? Применение vba и макросов в microsoft® excel
TRANSCRIPT
решения изнес- ?
Применение VBA и макросов в
Microsoft® Excel
Bill Jelen, Mr. ExcelTracy Syrstad
800 East 96th StreetIndianapolis, Indiana 46240
solutionsusiness ?
VBA and Macros for
Microsoft® Excel
Билл Джелен, “Мистер Excel”Трейси Сирстад
Москва • Санкт-Петербург • Киев 2006
решения изнес- ?
Применение VBA и макросов в
Microsoft® Excel
ББК 32.973.26�018.2.75
Д40
УДК 681.3.07
Издательский дом ‘‘Вильямс”
Главный редактор С.Н. Тригуб
Зав. редакцией В.Р. Гинзбург
Перевод с английского и редакция А.В. Журавлева
По общим вопросам обращайтесь в Издательский дом ‘‘Вильямс’’ по адресу:
[email protected], http://www.williamspublishing.com
115419, Москва, а/я 783; 03150, Киев, а/я 152
Джелен, Билл, Сирстад, Трейси.
Д40 Применение VBA и макросов в Microsoft Excel. : Пер. с англ. ���� М. : Из�
дательский дом ‘‘Вильямс’’, 2006. ���� 624 с. : ил. ���� Парал. тит. англ.
ISBN 5�8459�0882�5 (рус.)
В этой книге рассматривается автоматизация выполнения всевозможных задач
с помощью Excel VBA ���� от создания простого отчета до разработки полноценного
приложения Excel ‘‘с нуля’’. Авторы книги полагаются на достаточно высокий уро�
вень подготовки читателя, однако допускают, что материал каждой главы не зна�
ком ему в полном объеме. Особое внимание при изложении материала уделяется
таким высокоэффективным средствам Excel, как диаграмма, расширенный фильтр
и сводная таблица. Прежде чем продемонстрировать решение той или иной задачи
с помощью VBA, авторы кратко останавливаются на ее выполнении с помощью
пользовательского интерфейса Excel. Прочитав книгу, читатель получит знания,
необходимые для автоматизации выполнения повседневных задач и создания соб�
ственных решений в Excel с помощью VBA.
Книга предназначена для опытных пользователей Excel.
ББК 32.973.26�018.2.75
Все названия программных продуктов являются зарегистрированными торговыми марками
соответствующих фирм.
Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой
бы то ни было форме и какими бы то ни было средствами, будь то электронные или механиче�
ские, включая фотокопирование и запись на магнитный носитель, если на это нет письменного
разрешения издательства Que Corporation.
Authorized translation from the English language edition published by Sams Publishing, Copyright © 2004.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any
means, electronic or mechanical, including photocopying, recording or by any information storage re�
trieval system, without permission from the publisher.
Russian language edition is published by Williams Publishing House according to the Agreement with
R&I Enterprises International, Copyright © 2006.
ISBN 5�8459�0882�5 (рус.) © Издательский дом ‘‘Вильямс’’, 2006
ISBN 0�7897�3129�0 (англ.) © Sams Publishing, 2004
Îãëàâëåíèå
Об авторах 23
Посвящения 23
Благодарности 23
Введение 25
Часть I. Первые шаги 35
Глава 1. Excel и VBA — гремучая смесь 37
Глава 2. Знакомство с Visual Basic for Applications 59
Глава 3. Работа с диапазоном ячеек 95
Глава 4. Функции, определенные пользователем 111
Глава 5. Циклы и управление выполнением кода 141
Глава 6. Стиль записи ссылок R1C1 161
Глава 7. Имена 177
Глава 8. События 189
Глава 9. Введение в пользовательские формы 215
Часть II. Автоматизация Excel 227
Глава 10. Диаграммы 229
Глава 11. Анализ данных с помощью расширенного фильтра 267
Глава 12. Сводные таблицы 299
Глава 13. Excel всемогущий 363
Глава 14. Взаимодействие с Internet 407
Глава 15. Поддержка XML в профессиональном выпуске Excel 2003 427
Глава 16. Автоматизация Word 439
Часть III. Удивительные возможности Visual Basicfor Applications 461
Глава 17. Массивы 463
Глава 18. Работа с текстовыми файлами 473
Глава 19. Использование Microsoft Access 489
Глава 20. Создание пользовательских объектов, типов и коллекций 505
Глава 21. Пользовательские формы — профессиональный подход 525
Глава 22. Интерфейс прикладного программирования (API) Windows 547
6 Оглавление
Глава 23. Обработка ошибок 561
Глава 24. Создание пользовательских меню и панелей инструментов 575
Глава 25. Надстройки 593
Глава 26. Практикум: создание приложения Excel “с нуля” 603
Предметный указатель 615
Ñîäåðæàíèå
Об авторах 23
Посвящения 23
Благодарности 23
Введение 25
VBA — работа на результат 25
Как организована эта книга 27
Часть I, “Первые шаги” 27
Часть II, “Автоматизация Excel” 27
Часть III, “Удивительные возможности Visual Basic for Applications” 27
Для кого предназначена эта книга 28
История развития электронных таблиц и макросов 29
Будущее Excel и VBA 30
Соглашения, принятые в этой книге 31
Рассматриваемые версии Excel 32
Программный код 32
Следующий шаг 32
Ждем ваших отзывов! 33
Часть I. Первые шаги 35
Глава 1. Excel и VBA — гремучая смесь 37
Excel всемогущий 37
Камни преткновения 37
Средство записи макросов не работает! 37
Visual Basic — это не BASIC 38
Хорошие новости 38
Отличные новости 38
Панель инструментов “Visual Basic” 39
Безопасность макросов 40
Уровень безопасности “Очень высокая” 40
Уровень безопасности “Высокая” 40
Уровень безопасности “Средняя” 41
Уровень безопасности “Низкая” 41
Запись, хранение и выполнение макросов 42
Диалоговое окно “Запись макроса” 42
Выполнение макроса 43
Создание кнопки выполнения макроса 43
Назначение макроса элементу управления формы 44
Редактор Visual Basic 45
Параметры редактора Visual Basic 45
8 Содержание
Диспетчер проектов 46
Окно свойств 47
Изучение кода макроса 50
Непредвиденные результаты 52
Возможное решение: использование относительных ссылок 52
Отчаяние 57
Следующий шаг 57
Глава 2. Знакомство с Visual Basic for Applications 59
Загадочный код 59
Учимся понимать “речь” VBA 60
Справочная система VBA 63
Спасительная клавиша <F1> 63
Просмотр разделов справочной системы 65
Изучение кода записанного макроса 66
Необязательные параметры 68
Предопределенные константы 68
Возврат объектов свойством 73
Использование отладчика кода 74
Пошаговое выполнение кода 74
Точки прерывания 77
Перемещение по коду 78
Выполнение фрагмента кода 79
Вычисление значения переменной или выражения 79
Установка точки прерывания с помощью окна Watches 83
Отслеживание состояния объекта с помощью окна Watches 83
Диспетчер объектов 85
5 советов по исправлению и оптимизации автоматически
сгенерированного кода 87
Совет 1: ничего не выделяйте 87
Совет 2: перемещайтесь на последнюю строку данных с конца рабочего
листа 88
Совет 3: используйте переменные 89
Совет 4: используйте одно выражение для копирования и вставки
данных 89
Совет 5: используйте конструкцию With...End With 90
Исправление и оптимизация автоматически сгенерированного
кода 90
Следующий шаг 93
Глава 3. Работа с диапазоном ячеек 95
Объект Range 95
Обращение к диапазону ячеек с помощью указания адреса его
верхнего левого и нижнего правого угла 96
Сокращенная форма обращения к диапазону ячеек 96
Именованные диапазоны ячеек 96
Содержание 9
Обращение к диапазону ячеек, расположенному на другом
рабочем листе 97
Обращение к диапазону ячеек с помощью указания его
относительного адреса 98
Обращение к диапазону ячеек с помощью свойства Cells 99
Использование свойства Cells в качестве параметра свойства Range 100
Обращение к диапазону ячеек с помощью свойства Offset 100
Изменение размера диапазона ячеек с помощью свойства Resize 101
Обращение к диапазону ячеек с помощью свойств Columns и Rows 102
Объединение диапазонов ячеек с помощью метода Union 103
Создание нового диапазона ячеек из пересекающихся диапазонов с
помощью метода Intersect 103
Проверка пустых ячеек с помощью функции IsEmpty 104
Обращение к диапазону ячеек с помощью свойства CurrentRegion 105
Обращение к диапазону несмежных ячеек с помощью коллекции
Areas 108
Следующий шаг 109
Глава 4. Функции, определенные пользователем 111
Создание функций, определенных пользователем 111
Наиболее распространенные задачи программирования в Excel 113
Вывод имени файла текущей рабочей книги в ячейке 113
Вывод полного имени файла текущей рабочей книги в ячейке 113
Как проверить, открыта ли рабочая книга 114
Проверка существования рабочего листа в открытой книге 114
Подсчет количества файлов рабочих книг в папке 115
Получение имени пользователя, зарегистрировавшегося в системе 116
Получение даты и времени последнего сохранения рабочей книги 117
Получение постоянного значения даты и времени 118
Проверка адреса электронной почты 118
Суммирование значений ячеек на основе цвета заливки 120
Получение имени и номера цвета заливки ячейки 121
Получение номера цвета текста в ячейке 124
Подсчет количества уникальных значений 125
Удаление повторяющихся значений из диапазона ячеек 125
Поиск первой непустой ячейки в диапазоне 128
Замена нескольких символов в строке 128
Извлечение чисел из смешанного текста 130
Преобразование номера недели в дату 130
Разбор строки с символамиYразделителями 131
Сортировка и конкатенация значений ячеек из заданного диапазона 132
Сортировка числовых и строковых значений 134
Поиск строки в диапазоне ячеек 135
Запись содержимого ячейки в обратном порядке 136
Поиск наибольших значений в диапазоне ячеек 137
Получение адреса гиперссылки 138
10 Содержание
Получение адреса столбца ячейки 138
Генерация постоянных случайных чисел 138
Использование структуры Select...Case 139
Следующий шаг 140
Глава 5. Циклы и управление выполнением кода 141
Цикл For...Next 141
Использование переменных в выражении For 144
Изменение шага в цикле For...Next 144
Досрочное завершение выполнения цикла 145
Вложение циклов 146
Циклы Do...Loop 147
Использование операторов While и Until 150
Цикл While...Wend 152
Цикл For Each...Next 152
Объектные переменные 152
Управление выполнением кода: использование конструкций
If...Then...Else и Select Case 155
Знакомство с конструкцией If...Then...Else 155
Условие 156
Конструкция If...Then...End If 156
Конструкция If...Then...Else...End If 156
Конструкция If...ElseIf...End If 157
Конструкция Select Case...End Select 157
Использование сложных выражений Case 158
Вложение выражений If 158
Следующий шаг 160
Глава 6. Стиль записи ссылок R1C1 161
Сравнение стилей записи ссылок A1 и R1C1 161
R1C1 — дела давно минувших дней? 162
R1C1 — сильные стороны 162
Использование стиля ссылок R1C1 в Excel 162
Чудесный мир формул Excel 163
Как “размножаются” формулы 163
Разоблачение 164
Ссылки в стиле R1C1 166
Относительные ссылки в стиле R1C1 166
Абсолютные ссылки в стиле R1C1 167
Смешанные ссылки в стиле R1C1 167
Обращение к строке или столбцу с помощью ссылок в стиле R1C1 168
Замена нескольких A1Yформул одной R1C1Yформулой 169
Тренируем память 170
Использование ссылок в стиле R1C1 при условном
форматировании ячеек 171
Задание условного форматирования с помощью пользовательского
интерфейса 171
Содержание 11
Задание условного форматирования с помощью VBA 172
Использование ссылок в стиле R1C1 при создании формулы
массива 174
Следующий шаг 175
Глава 7. Имена 177
Глобальные и локальные имена 177
Создание имен 179
Удаление имен 180
Типы имен 180
Имена формул 181
Имена строк 181
Имена чисел 182
Имена массивов 183
Зарезервированные имена 183
Скрытие имен 185
Проверка существования имени 185
Следующий шаг 187
Глава 8. События 189
Использование событий 190
Параметры событий 190
Запрет обработки событий 191
События рабочей книги 191
Событие Workbook_Activate() 192
Событие Workbook_Deactivate() 192
Событие Workbook_Open() 192
Событие Workbook_BeforeSave(ByVal SaveAsUI As Boolean,
Cancel As Boolean) 193
Событие Workbook_BeforePrint(Cancel As Boolean) 193
Событие Workbook_BeforeClose(Cancel As Boolean) 194
Событие Workbook_NewSheet(ByVal Sh As Object) 195
Событие Workbook_WindowResize(ByVal Wn As Window) 195
Событие Workbook_WindowActivate(ByVal Wn As Window) 196
Событие Workbook_WindowDeactivate(ByVal Wn As Window) 196
Событие Workbook_AddinInstall() 196
Событие Workbook_AddinUninstall() 196
Событие Workbook_SheetActivate(ByVal Sh As Object) 197
Событие Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean) 197
Событие Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean) 197
Событие Workbook_SheetCalculate(ByVal Sh As Object) 197
Событие Workbook_SheetChange(ByVal Sh As Object, ByVal Target As
Range) 198
Событие Workbook_SheetDeactivate(ByVal Sh As Object) 198
12 Содержание
Событие Workbook_SheetFollowHyperlink(ByVal Sh As Object, ByVal
Target As Hyperlink) 198
Событие Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal
Target As Range) 198
События рабочего листа 199
Событие Worksheet_Activate() 199
Событие Worksheet_Deactivate() 199
Событие Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As
Boolean) 199
Событие Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As
Boolean) 200
Событие Worksheet_Calculate() 200
Событие Worksheet_Change(ByVal Target As Range) 202
Событие Worksheet_SelectionChange(ByVal Target As Range) 203
Событие Worksheet_FollowHyperlink(ByVal Target As Hyperlink) 203
События листа диаграммы 204
Встроенные диаграммы 204
Событие Chart_Activate() 205
Событие Chart_BeforeDoubleClick(ByVal ElementID As Long, ByVal Arg1
As Long, ByVal Arg2 As Long, Cancel As Boolean) 205
Событие Chart_BeforeRightClick(Cancel As Boolean) 206
Событие Chart_Calculate() 206
Событие Chart_Deactivate() 206
Событие Chart_DragOver() 206
Событие Chart_DragPlot() 206
Событие Chart_MouseDown(ByVal Button As Long, ByVal Shift As Long,
ByVal x As Long, ByVal y As Long) 206
Событие Chart_MouseMove(ByVal Button As Long, ByVal Shift As Long,
ByVal x As Long, ByVal y As Long) 207
Событие Chart_MouseUp(ByVal Button As Long, ByVal Shift As Long,
ByVal x As Long, ByVal y As Long) 207
Событие Chart_Resize() 207
Событие Chart_Select(ByVal ElementID As Long, ByVal Arg1 As Long,
ByVal Arg2 As Long) 207
Событие Chart_SeriesChange(ByVal SeriesIndex As Long, ByVal PointIndex
As Long) 208
События приложения 208
Событие AppEvent_NewWorkbook(ByVal Wb As Workbook) 210
Событие AppEvent_SheetActivate(ByVal Sh As Object) 210
Событие AppEvent_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean) 210
Событие AppEvent_SheetBeforeRightClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean) 210
Событие AppEvent_SheetCalculate(ByVal Sh As Object) 211
Событие AppEvent_SheetChange(ByVal Sh As Object, ByVal Target As
Range) 211
Событие AppEvent_SheetDeactivate(ByVal Sh As Object) 211
Содержание 13
Событие AppEvent_SheetFollowHyperlink(ByVal Sh As Object, ByVal
Target As Hyperlink) 211
Событие AppEvent_SheetSelectionChange(ByVal Sh As Object, ByVal Target
As Range) 211
Событие AppEvent_WindowActivate(ByVal Wb As Workbook, ByVal Wn As
Window) 211
Событие AppEvent_WindowDeactivate(ByVal Wb As Workbook, ByVal Wn
As Window) 211
Событие AppEvent_WindowResize(ByVal Wb As Workbook, ByVal Wn As
Window) 212
Событие AppEvent_WorkbookActivate(ByVal Wb As Workbook) 212
Событие AppEvent_WorkbookAddinInstall(ByVal Wb As Workbook) 212
Событие AppEvent_WorkbookAddinUninstall(ByVal Wb As Workbook) 212
Событие AppEvent_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel
As Boolean) 213
Событие AppEvent_WorkbookBeforePrint(ByVal Wb As Workbook, Cancel
As Boolean) 213
Событие AppEvent_WorkbookBeforeSave(ByVal Wb As Workbook, ByVal
SaveAsUI As Boolean, Cancel As Boolean) 213
Событие AppEvent_WorkbookDeactivate(ByVal Wb As Workbook) 213
Событие AppEvent_WorkbookNewSheet(ByVal Wb As Workbook, By Val Sh
As Object) 213
Событие AppEvent_WorkbookOpen(ByVal Wb As Workbook) 214
Следующий шаг 214
Глава 9. Введение в пользовательские формы 215
Способы взаимодействия с пользователем 215
Окно ввода 215
Окно сообщения 216
Создание пользовательской формы 216
Вызов и скрытие пользовательской формы 218
Программирование пользовательской формы 218
Основные элементы управления формы 220
Использование списков и комбинированных списков 221
Использование переключателей 222
Использование изображений 223
Использование счетчиков 224
Использование вкладок для объединения форм 225
Проверка ввода обязательных данных 225
Закрытие формы 225
Следующий шаг 226
Часть II. Автоматизация Excel 227
Глава 10. Диаграммы 229
Встроенные диаграммы и диаграммы, расположенные
на отдельном листе 229
14 Содержание
Встроенные диаграммы и контейнер ChartObject 230
Диаграммы, расположенные на отдельном листе 232
Создание диаграмм с помощью VBA 232
Изменение размещения диаграммы 235
Стандартный тип диаграмм 235
Использование объектных переменных для упрощения кода 236
“Анатомия” диаграммы 237
Область диаграммы (ChartArea) 237
Область построения диаграммы (PlotArea) 240
Ряды данных (Series) 242
Оси диаграммы (Axis) 243
Линии сетки (HasMajorGridlines и HasMinorGridlines) 245
Подписи данных (DataLabels и DataLabel) 246
Название диаграммы, легенда и таблица данных (ChartTitle, HasLegend
и HasDataTable) 247
Линии тренда и полосы погрешности (Trendlines и ErrorBar) 248
Типы диаграмм 251
Параметры трехмерных и круговых диаграмм 256
Параметры трехмерных диаграмм 256
Параметры круговых диаграмм 258
Интерактивные диаграммы 260
События диаграмм 260
Экспорт диаграммы в файл изображения 261
Удивительные возможности точечных диаграмм 262
Создание нестандартных диаграмм 262
Круговая пузырьковая диаграмма 262
Диаграмма с точками данных в виде спидометров 264
Диаграмма кривой предложения 264
Иерархическая кольцевая диаграмма 265
Следующий шаг 266
Глава 11. Анализ данных с помощью расширенного фильтра 267
Преимущества VBA перед пользовательским интерфейсом Excel 267
Использование расширенного фильтра для отбора уникальных
значений из заданного диапазона 268
Отбор уникальных значений из заданного столбца с помощью
пользовательского интерфейса 268
Отбор уникальных значений из заданного столбца с помощью VBA 271
Отбор уникальных значений из комбинации нескольких столбцов с
помощью VBA 275
Использование расширенного фильтра с указанием условия отбора
данных 276
Объединение нескольких условий с помощью
логической операции “ИЛИ” 278
Объединение нескольких условий с помощью логической операции “И” 278
Дополнительные аспекты объединения условий с помощью
логической операции “ИЛИ” 279
Содержание 15
Задание условия отбора с помощью формулы 279
Отбор пустого множества записей 286
Фильтрация диапазона исходных данных “на месте” 287
Отбор пустого множества записей 288
Отображение записей, скрытых в результате фильтрации “на месте” 288
Отбор только уникальных записей при фильтрации “на месте” 288
Использование расширенного фильтра для копирования всех
записей, удовлетворяющих заданному условию 289
Копирование всех столбцов исходного диапазона данных 289
Копирование и переупорядочивание подмножества столбцов исходного
диапазона данных 290
Автофильтр 297
Следующий шаг 298
Глава 12. Сводные таблицы 299
Сводные таблицы в различных версиях Excel 299
Создание сводных таблиц с помощью пользовательского
интерфейса Excel 300
Создание сводных таблиц с помощью VBA 303
Подсчет суммы чисел вместо количества значений 305
Перемещение или изменение части сводной таблицы 306
Определение размера сводной таблицы 307
Создание отчета о структуре спроса на товары 309
Заполнение значениями пустых ячеек в области данных 312
Изменение порядка сортировки списка заказчиков 313
Изменение порядка следования столбцов сводной таблицы вручную 314
Изменение формата отображения числовых значений 315
Запрет автоматического добавления промежуточных итогов 315
Запрет подсчета общей суммы по столбцам 316
Создание отчета о структуре спроса на товары:
завершающая стадия 317
Создание новой рабочей книги 318
Копирование содержимого сводной таблицы 318
Улучшение внешнего вида отчета 319
Стилевое форматирование отчета 321
Добавление промежуточных итогов 321
Результирующий код 323
Создание отчета о прибыльности товаров 326
Определение вычисляемых полей области данных 328
“Подводные камни” вычисляемых элементов 331
Суммирование значений полей области данных сводной таблицы
путем группирования 332
Группирование дат по неделям 336
Определение сроков выполнения заказов 338
Дополнительные возможности сводных таблиц 340
Отображение лучшей десятки заказчиков 340
Использование сводной таблицы для фильтрации исходных данных 344
16 Содержание
Использование полей области страницы сводной таблицы 346
Фильтрация элементов полей сводной таблицы вручную 351
Сумма, среднее, количество, минимум, максимум и др. 355
Дополнительные вычисления в полях области данных сводной
таблицы 356
Доля от общей суммы 358
Приведенное отличие от значения предыдущего элемента поля 358
Приведенное отличие от значения заданного элемента поля 359
Нарастающий итог 359
Следующий шаг 361
Глава 13. Excel всемогущий 363
Расширение возможностей Excel с помощью VBA 363
Условное форматирование с более чем тремя условиями 364
Расширенный фильтр с более чем двумя условиями 364
Файловые операции 365
Поиск файлов 366
Удаление рабочей книги после определенной даты 367
Создание команды меню “Закрыть и удалить” 368
Импорт CSVYфайлов 370
Считывание текстового файла в память и его последующий анализ 370
Объединение и разделение рабочих книг 372
Сохранение листов рабочей книги в виде отдельных рабочих книг 372
Объединение нескольких рабочих книг в одну 372
Фильтрация данных с последующим копированием полученного
результата в отдельные рабочие листы 373
Экспорт данных в Word 375
Работа с примечаниями 376
Вывод примечаний 376
Изменение размера области примечания 377
Изменение размера области примечания с помощью центрирования 378
Размещение диаграммы в примечании 379
Замечательные возможности Excel VBA 381
Выделение ячейки с помощью условного форматирования 381
Выделение ячейки без применения условного форматирования 382
Транспонирование данных 383
Выделение и отмена выделения несмежных ячеек 384
VBA для профессионалов 386
Установка параметров страницы 386
Вычисление времени выполнения кода макроса 389
Запрет/разрешение выполнения операций вырезания,
копирования и вставки 390
Определение порядка сортировки 392
Создание индикатора хода процесса 394
Создание защищенного поля для ввода пароля 395
Изменение регистра текста 397
Обработка события удаления строки или столбца 398
Поиск заданного текста с помощью свойства SpecialCells 399
Содержание 17
Условное удаление строк 400
Сокрытие строки формул 401
На закуску 402
Извлечение информации о курсах акций из Internet 402
Вставка программного кода во вновь созданную рабочую книгу 404
Следующий шаг 405
Глава 14. Взаимодействие с Internet 407
Извлечение данных из Internet 407
Создание WebYзапроса с помощью пользовательского интерфейса Excel 407
Обновление существующего WebYзапроса с помощью VBA 409
Создание WebYзапроса с помощью VBA 410
Извлечение данных из Internet в режиме реального времени 413
Анализ данных, извлеченных из Internet 414
Условия выполнения метода OnTime 414
Определение временного окна для выполнения макроса 415
Отмена назначенного задания 415
Отмена всех назначенных заданий 416
Выполнение макроса по прошествии заданного периода времени 416
Периодическое выполнение макроса через определенные
промежутки времени 416
Размещение данных на WebYстранице 418
Создание WebYстраниц с помощью VBA 420
Применение Excel в качестве системы управления содержимым 421
Загрузка WebYстраницы на FTPYсервер 425
Следующий шаг 425
Глава 15. Поддержка XML в профессиональном выпуске Excel 2003 427
Введение в XML 427
Правила XML 428
Универсальный формат файлов 429
XML набирает обороты 429
Схемы и сопоставления XML 429
Сохранение и считывание содержимого рабочей книги Excel в
формате XML 431
Следующий шаг 438
Глава 16. Автоматизация Word 439
Раннее связывание 439
Ошибка компиляции: отсутствие библиотеки 442
Позднее связывание 442
Работа с объектами 443
Ключевое слово New 443
Функция CreateObject 444
Функция GetObject 444
Объекты Word 445
Объект Document 446
18 Содержание
Объект Selection 448
Объект Range 449
Закладки 453
Следующий шаг 460
Часть III. Удивительные возможности Visual Basicfor Applications 461
Глава 17. Массивы 463
Объявление массива 463
Многомерные массивы 464
Заполнение массива 464
Манипулирование элементами массива 466
Еще одно преимущество массивов 467
Динамические массивы 469
Передача массива в качестве параметра 470
Следующий шаг 471
Глава 18. Работа с текстовыми файлами 473
Импорт данных из текстового файла 473
Импорт текстовых файлов, содержащих менее 65 536 записей 474
Импорт текстовых файлов, содержащих более 65 536 записей 482
Экспорт данных в текстовый файл 486
Следующий шаг 487
Глава 19. Использование Microsoft Access 489
ADO и DAO 490
Объекты ADO 492
Добавление записи в таблицу Access 493
Извлечение записей из таблицы Access 494
Обновление записей таблицы Access 496
Удаление записей таблицы Access 499
Создание итоговых запросов 499
Несколько полезных макросов 500
Проверка существования таблицы в базе данных Access 500
Проверка существования поля в таблице базы данных Access 501
Добавление таблицы в базу данных Access 502
Добавление поля в таблицу базы данных Access 503
Следующий шаг 503
Глава 20. Создание пользовательских объектов, типов и коллекций 505
Создание модуля класса 506
Обработка событий уровня приложения и встроенной диаграммы 506
События уровня приложения 507
События встроенной диаграммы 509
Содержание 19
Создание пользовательского объекта 510
Применение пользовательского объекта на практике 511
Использование выражений Property Let и Property Get 513
Коллекции 515
Создание коллекции в стандартном модуле 515
Создание коллекции в модуле класса 516
Создание пользовательских типов 520
Следующий шаг 524
Глава 21. Пользовательские формы //// профессиональный подход 525
Панель инструментов UserForm 525
Создание коллекций элементов управления формы 526
Дополнительные элементы управления формы 528
Переключатели 528
Набор вкладок 529
Поле ввода адреса диапазона ячеек 530
Немодальные формы 531
Гиперссылки в формах 531
Добавление элементов управления на форму во время выполнения
программного кода 532
Изменение размеров формы во время выполнения программного кода 534
Добавление элемента управления на форму во время выполнения
программного кода 534
Определение размера и положения элемента управления на форме во
время выполнения программного кода 535
Ограничения, связанные с добавлением элементов управления на форму
во время выполнения программного кода 535
Типы элементов управления 535
Добавление изображения на форму во время выполнения программного
кода 536
Результирующий код 537
Использование полосы прокрутки для выбора значений 539
Добавление подсказки к элементу управления 541
Использование сочетаний клавиш 541
Подсказка элемента управления 541
Порядок переноса фокуса 541
Изменение цвета фона активного элемента управления 542
Использование эффекта прозрачности формы 545
Следующий шаг 546
Глава 22. Интерфейс прикладного программирования (API) Windows 547
Знакомство с Windows API 547
Объявления Windows API 548
Использование объявлений Windows API 548
Примеры полезных объявлений Windows API 549
Определение имени компьютера 549
20 Содержание
Проверка возможности доступа к файлу 550
Определение разрешения экрана 550
Блокирование кнопки закрытия окна приложения 552
Блокирование кнопки закрытия окна формы 552
Часы 553
Создание гиперссылок 554
Воспроизведение звуковых файлов 555
Создание диалогового окна выбора файла 555
Дополнительные источники объявлений Windows API 559
Следующий шаг 559
Глава 23. Обработка ошибок 561
Отладка кода с помощью редактора VBA 561
Отладка кода пользовательской формы 562
Обработка ошибок с помощью выражения On Error GoTo 564
Использование нескольких обработчиков ошибок 565
Универсальные обработчики ошибок 566
Игнорирование ошибок 566
Игнорирование сообщений Excel 568
Извлечение пользы из ошибок 568
Общение с заказчиками 569
“Отложенные” ошибки 569
Ошибка времени выполнения 9: “Subscript out of range” 570
Ошибка времени выполнения 1004: “Method 'Range' of object '_Global'
failed” 571
Несовершенство защиты проекта VBA 572
Защита проекта VBA в различных версиях Excel 573
Совместимость различных версий Excel 573
Следующий шаг 574
Глава 24. Создание пользовательских меню и панелей инструментов 575
Создание пользовательского меню 575
Создание и удаление пользовательского меню 575
Добавление команд меню 577
Группирование команд меню 578
Создание подменю 579
Создание пользовательской панели инструментов 581
Создание и удаление пользовательской панели инструментов 582
Добавление кнопок на панель инструментов 582
Выбор значка кнопки панели инструментов 584
Добавление раскрывающегося списка на панель инструментов 584
Сохранение и восстановление координат панели инструментов 585
Другие способы запуска макросов 587
Запуск макроса с помощью сочетания клавиш 587
Запуск макроса с помощью кнопки 587
Запуск макроса с помощью элемента управления ActiveX 590
Следующий шаг 592
Содержание 21
Глава 25. Надстройки 593
Стандартные надстройки Excel 593
Преобразование рабочей книги Excel в надстройку 594
Преобразование рабочей книги Excel в надстройку с помощью
диалогового окна “Сохранение документа” (Save As) 594
Преобразование рабочей книги Excel в надстройку с помощью
редактора VBA 596
Использование надстроек 597
Безопасность стандартных надстроек Excel 598
Выгрузка надстроек 599
Удаление надстроек 599
Альтернативное решение: использование скрытой рабочей книги 599
Следующий шаг 601
Глава 26. Практикум: создание приложения Excel ‘‘с нуля’’ 603
О Тушаре Мехта 603
Постановка задачи 604
Решение 605
Реализация решения с помощью Excel и VBA 606
Этап 1а: нисходящее программирование 606
Этап 1б: создание ключевых компонентов 607
Этап 2а: нисходящее программирование 608
Этап 2б: создание ключевых компонентов 610
Этап 3а: нисходящее программирование 611
Этап 3б: создание ключевых компонентов 613
Резюме 614
Предметный указатель 615
Об авторахБилл Джелен, более известный под псевдонимом ‘‘Мистер Excel’’, YYYY рукоY
водитель популярнейшего WebYсайта MrExcel.com (свыше 10 млн. посещений
в год), а также автор многочисленных книг, посвященных Excel. Билл создает
решения, основанные на использовании Excel VBA, для сотен клиентов по
всему миру. До основания MrExcel.com Билл на протяжении 12 лет занимал
должность финансового аналитика в отделах финансов, маркетинга, бухгалY
терского учета и операций крупной корпорации. Билл проживает в окрестноY
стях города Акрон, штат Огайо, США, вместе со своей женой Мэри Эллен и
детьми Джошем и Зиком.
Трейси Сирстад работает программистом и консультантом в компании
Билла Джелена MrExcel Consulting. Трейси проживает в живописной местноY
сти Южной Дакоты вместе со своим мужем Джоном и собакой Генералом.
ПосвященияПосвящается Мэри Эллен Джелен.
Билл
Посвящается Джону Сирстаду, вера которого в меня помогает преодолеватьвсе трудности, встречающиеся на жизненном пути.
Трейси
БлагодарностиСпасибо Мале Сингху (Mala Singh) из XLSoft Consulting за помощь в написаY
нии главы 10; Тому Уртису (Tom Urtis) за техническое редактирование; Джерри
Колю (Jerry Kohl) за его великолепные идеи; Джанет Гарсиа (Jeanette Garcia),
Барбе Джелен (Barb Jelen), Дагу и Стейси Джеффериз за техническую помощь;
Зику, Джошу и Мэри Эллен Джелен за их терпение; Тому Мацешеку (Tom MaY
cioszek) за ‘‘дружескую’’ проверку; Чеду Ротшиллеру (Chad Rothschiller) из MiY
crosoft за неоценимую помощь в изучении Excel XML; Дейву Гейнеру (Dave
Gainer), Стиву Заске (Steve Zaske), Эрику Паттерсону (Eric Patterson) и Джо ЧиY
рилову (Joe Chirilov) из Microsoft; Лоретте Йейтс (Loretta Yates), Шону Диксону
(Sean Dixon), Марго Кэттс (Margo Catts), Энди Бистеру (Andy Beaster), Грегу
Виганду (Greg Wiegand), Эми Сорокас (Amy Sorokas), Ким Спилкер (Kim
Spilker), Эрике Миллен (Erika Millen), Кэти Бидуэл (Kathy Bidwell), Синди ТиY
терс (Cindy Teeters), Мишель Митчелл (Michelle Mitchell) и Гэри Адэру (Gary
Adair) из Pearson; Иване Тейлор (Ivana Taylor) за блестящий маркетинг; читатеY
лям MrExcel.com, нашим клиентам и всем MVP; Дэну Бриклину (Dan Bricklin),
Бобу Фрэнкстону (Bob Frankston) и Митчу Кейпору (Mitch Kapor) за создание
электронных таблиц; Уильяму Брауну (William Brown) из Waterside; Пэм Гензель
24 Благодарности
(Pam Gensel) за 1Yй урок по созданию макросов; Роберту Ф. Джелену (Robert
F. Jelen) за то, что он был первым поклонником моего таланта программиста;
Роберту К. Джелену (Robert K. Jelen) за вдохновение; Бонни Хильярд (Bonnie
Hilliard) за связь с общественностью; Лаурель Рииппу (Laurelle Riippa) из PW;
Лео Лапорте (Leo LaPorte) и Фон Луу (Fawn Luu) из TechTV; Дэну Пойнтеру
(Dan Poynter); Крейгу Кросмэну (Craig Crossman) из Computer America и УолтеY
ру Моссбергу (Walter Mossberg) из Wall Street Journal.
Билл
Спасибо Корту ЧиллдонуYХоффу (Cort ChilldonYHoff) за поддержку в трудY
ные минуты; Хуану Пабло Гонсалесу Руизу (Juan Pablo Gonzales Ruiz) за его
советы (в частности, касающиеся функций из главы 4); Даниелю Клэнну
(Daniel Klann), Деннису Валентайну (Dennis Wallentin), Ивану Ф. Моале (Ivan
F. Moala), Хуану Пабло Гонсалесу (Juan Pablo Gonzales), Масаре Каджи
(Masaru Kaji), Натану П. Оливеру (Nathan P. Oliver), Ричи Силлсу (Richie
Sills), Расселу Гауфу (Russell Hauf), Суату Мехмету Озгуру (Suat Mehmet
Ozgur), Тому Уртису (Tom Urtis), Томми Майлзу (Tommy Miles) и Вэю Цзянгу
(Wei Jiang) за их вклад в написание главы 13; Крису Лемэру (Chris Lemair) за
то, что он открыл для меня удивительный мир Excel и макросов, а также Энн
Трой (Anne Troy) за то, что она познакомила меня с Биллом.
Трейси
Ââåäåíèå
VBA — работана результат
Язык программирования Visual
Basic for Applications (VBA) позвоY
ляет существенно повысить произY
водительность труда пользователей
Microsoft Excel.
Не дожидаясь помощи от отдела
информационных технологий, польY
зователи Excel могут самостоятельно
создавать отчеты, необходимые для
выполнения своих повседневных
обязанностей. Это сулит как преY
имущества, так и недостатки. С одY
ной стороны, пользователи Excel
смогут повысить эффективность своеY
го труда. С другой, им придется раY
зобраться со всеми тонкостями исY
кусства создания макросов с помоY
щью Excel VBA.
Уверен, что в этот самый момент
вы или ктоYлибо из ваших коллег
все еще выполняете в Excel рутинY
ные операции, которые могут быть
автоматизированы с помощью VBA.
Случай, произошедший с Валери, —
весьма типичен для компаний, наY
считывающих 20 и более пользоватеY
лей Excel.
VBA — работа на результат ......... 25Как организована эта книга ........27Для кого предназначена этакнига................................................. 28История развитияэлектронных таблиц и макросов ...................................... 29Будущее Excel и VBA ..................... 30Соглашения, принятыев этой книге...................................... 31Рассматриваемыеверсии Excel .................................... 32Программный код......................... 32Следующий шаг............................. 32
 ÝÒÎÌ ÂÂÅÄÅÍÈÈ...
26 Введение
Практикум
Создание финансового отчетаЭтот практикум основан на реальных событиях. После внедрения дорогостоящейсистемы планирования и управления ресурсами (ERP) корпорация средних раз&меров осталась без средств для создания ежемесячных финансовых отчетов. Ва&лери, занимающая должность бизнес&аналитика в финансовом отделе корпора&ции, приняла решение создать требуемый отчет самостоятельно.
Прежде всего, Валери экспортировала главную бухгалтерскую книгу из ERP&системы в текстовый файл с разделителями&запятыми (CSV). Затем полученныйCSV&файл был импортирован в Excel.
Создание отчета оказалось делом не из легких. Некоторые счета необходимо былоклассифицировать как расходы, некоторые — полностью исключить из отчета.Шаг за шагом, Валери внесла все требуемые корректировки. Для получения пер&вой части отчета она создала сводную таблицу и скопировала итоговые значенияна новый рабочий лист. Аналогичным образом были получены остальные частиотчета. После трех часов кропотливого труда финансовый отчет был готов.
Звездный часВалери передала отчет своему начальнику, который уже отчаялся получить егов срок. Она тут же стала “героем дня” и пребывала на седьмом небе от счастья.
Неожиданный поворот событийНа следующий день состоялось ежемесячное собрание руководства корпорации, пол&ноценно подготовиться к которому смог лишь начальник финансового отдела. Он эф&фектно положил на стол отчет, чем привел в замешательство всех присутствующих. По&сле того как начальник Валери рассказал о происхождении отчета, президент корпора&ции попросил его помочь подготовить отчеты для всех остальных отделов.
Тяжкое бремя славыКорпорация, в которой работает Валери, насчитывает 46 отделов. Подготовка фи&нансового отчета для каждого отдела подразумевает импортирование данных изERP&системы, удаление определенных счетов, создание нескольких сводных таб&лиц и комбинацию полученных итоговых результатов. На подготовку первого от&чета Валери потратила 3 часа. Она подсчитала, что с учетом полученного опытасможет создать 46 отчетов не менее чем за 40 часов. Валери пришла в отчаяние.
VBA спешит на помощьК счастью, Валери решила поделиться своими заботами с возглавляемой мноюкомпанией MrExcel Consulting. Менее чем за неделю я создал набор VBA&макросов, реализующих действия, необходимые для подготовки отчета, — импортданных, удаление счетов, создание сводных таблиц, объединение полученныхитоговых сведений и стилевое форматирование отчета. Благодаря VBA 40&часовой процесс создания отчетов вручную был сведен к двум щелчкам мыши ичетырем минутам ожидания.
Введение 27
Как организована эта книгаЭта книга состоит из трех частей. Ее цель — научить читателя создавать
макросы VBA для автоматизации выполнения рутинных задач в Excel.
Часть I, “Первые шаги”Глава 1, ‘‘Excel и VBA YYYY гремучая смесь’’, акцентирует внимание на фундаменY
тальной проблеме средства записи макросов Excel — средство записи макросов не
работает. В главе 2, ‘‘Знакомство с Visual Basic for Applications’’, рассматриваются
основы синтаксиса языка программирования Visual Basic for Applications. Глава 3,
‘‘Работа с диапазоном ячеек’’, посвящена работе с диапазонами ячеек.
В главе 4, ‘‘Функции, определенные пользователем’’, рассматривается созY
дание функций, определенных пользователем, а также приводятся примеры
решения 25 наиболее распространенных задач, встречающихся при повсеY
дневном программировании в Excel.
Глава 5, ‘‘Циклы и управление выполнением кода’’, посвящена циклам —
фундаментальному компоненту любого языка программирования. РазрабатыY
вая решение для Валери, мы создали код, подготавливающий отчет для одного
отдела, а затем поместили его в цикл с 46 итерациями.
В главе 6, ‘‘Стиль записи ссылок R1C1’’, описывается стиль записи ссылок
R1C1, а в главе 7, ‘‘Имена’’, — использование имен. Глава 8, ‘‘События’’, поY
священа событиям, а глава 9, ‘‘Введение в пользовательские формы’’, — польY
зовательским формам.
Часть II, “Автоматизация Excel”В главе 10, ‘‘Диаграммы’’, рассматривается использование VBA при работе
с диаграммами. Глава 11, ‘‘Анализ данных с помощью расширенного фильтY
ра’’, посвящена анализу данных с помощью расширенного фильтра, а глаY
ва 12, ‘‘Сводные таблицы’’, — работе со сводными таблицами. В комбинации
с VBA диаграммы, расширенный фильтр и сводные таблицы образуют мощY
ную основу для создания всевозможных отчетов.
В главе 13, ‘‘Excel всемогущий’’, рассматриваются распространенные задаY
чи, встречающиеся при работе с Excel, и их решения с помощью VBA, предY
лагаемые опытными программистами со всех уголков мира.
Глава 14, ‘‘Взаимодействие с Internet’’, посвящена автоматизации WebYзаY
просов, глава 15, ‘‘Поддержка XML в профессиональном выпуске Excel 2003’’, —
работе с данными в формате XML, глава 16, ‘‘Автоматизация Word’’, — автоматиY
зации Microsoft Word.
Часть III, “Удивительные возможности Visual Basicfor Applications”
В главе 17, ‘‘Массивы’’, рассматриваются массивы. Основное предназнаY
чение массива заключается в упрощении обработки данных и повышении
28 Введение
скорости выполнения программного кода. Глава 18, ‘‘Работа с текстовыми файY
лами’’, посвящена работе с текстовыми файлами, а глава 19, ‘‘Использование
Microsoft Access’’, — использованию баз данных Microsoft Access. Применение
Excel в качестве пользовательского интерфейса, а MDBYфайла — в качестве
базы данных позволяет добиться оптимального использования возможностей
обеих программ.
В главе 20, ‘‘Создание пользовательских объектов, типов и коллекций’’, расY
сматривается создание модулей классов, предназначенных для размещения польY
зовательских объектов VBA. Глава 21, ‘‘Пользовательские формы YYYY профессиоY
нальный подход’’, посвящена сложным элементам управления, а также различY
ным приемам программирования пользовательских форм. В главе 22, ‘‘Интерфейс
прикладного программирования (API) Windows’’, рассматриваются основы исY
пользования функций интерфейса прикладного программирования (API) WinY
dows. Глава 23, ‘‘Обработка ошибок’’, посвящена обработке ошибок, глава 24,
‘‘Создание пользовательских меню и панелей инструментов’’, — созданию польY
зовательских меню и панелей инструментов, глава 25, ‘‘Надстройки’’, — применеY
нию надстроек. Наконец, глава 26, ‘‘Практикум: создание приложения Excel
‘‘с нуля’’, представляет собой практикум, демонстрирующий процесс создания
приложения Excel ‘‘с нуля’’.
Для кого предназначена эта книгаНа мероприятии, посвященном выходу на рынок пакета приложений MiY
crosoft Office 2003, корпорация Microsoft огласила результаты исследования,
согласно которым среднестатистический пользователь применяет только 10%
заложенных в Office возможностей. Эта книга предназначена для опытных
пользователей Excel. Опрос, проведенный среди 2000 посетителей WebYсайта
MrExcel.com, показал, что 42% опытных пользователей Excel применяют в поY
вседневной работе все наиболее эффективные средства этого приложения.
Компания MrExcel Consulting часто устраивает семинары для бухгалтеров. Как
правило, все они работают с Excel по 30YY40 часов в неделю. Практически на
каждом семинаре я демонстрирую слушателям возможности Excel, о которых
они ранее и не подозревали, и тем не менее практически на каждом семинаре
находится слушатель, который превосходит меня в знании того или иного
средства Excel. Что я хочу этим сказать? Вероятно, читатель этой книги велиY
колепно разбирается в Excel. Несмотря на это, я предполагаю, что материал
каждой главы незнаком в полном объеме для 58% опытных пользователей ExY
cel. Прежде чем продемонстрировать решение той или иной задачи с помоY
щью VBA, я кратко остановлюсь на ее выполнении с помощью пользовательY
ского интерфейса Excel.
Введение 29
История развития электронныхтаблиц и макросов
Вплоть до 1978 года каждый бухгалтер применял для создания отчета буY
магу формата ‘‘гроссбух’’, механический карандаш и ластик. Сведения о дневY
ном обороте записывались от руки, а промежуточный итог подсчитывался
с помощью счетной машины. Ошибка в расчетах или исходных данных стоила
многих часов работы с ластиком, счетной машиной и карандашом.
В 1979 году Дэн Бриклин (Dan Bricklin) и Боб Фрэнкстон (Bob Frankston)
(рис. 1) в буквальном смысле изменили мир. Они создали первую электронY
ную таблицу, предназначенную для выполнения на компьютерах Apple II, и
назвали ее VisiCalc (сокращение от англ. ‘‘visual calculator’’ — визуальный
калькулятор). Вскоре программа VisiCalc была перенесена на несколько разY
личных платформ, включая IBM PC. В 1981 году была выпущена расширенная
версия VisiCalc, предназначенная для выполнения на компьютерах Apple III и
поддерживающая макросы командной строки. Проект VisiCalc прекратил свое
существование в 1985 году в результате судебной тяжбы.
Рис. 1. Дэн Бриклин и Боб Фрэнкстон
В 1983 году Митч Кейпор (Mitch Kapor) создал программу Lotus 1Y2Y3.
По своим функциональным возможностям Lotus 1Y2Y3, изначально разработанY
ная для выполнения под управлением операционной системы DOS, намного
превзошла VisiCalc. В первый год объем продаж Lotus 1Y2Y3 достиг впечатляюY
щей цифры в 53 млн долларов. Вплоть до середины 90Yх годов Lotus 1Y2Y3 заниY
мала лидирующее положение на рынке программ для работы с электронными
30 Введение
таблицами. Несмотря на наличие конкурентов (Quattro, Multiplan и др.), Lotus
1Y2Y3 ‘‘деYфакто’’ оставалась стандартным инструментом бухгалтерского учета.
1985 год был ознаменован появлением на свет второго выпуска Lotus 1Y2Y3,
поддерживающего 8192 строки и 256 столбцов — более 2 млн ячеек! Кроме того,
пользователю была предоставлена возможность записывать простые макросы.
В 1990 году я был на 100% уверен в незыблемости позиций Lotus 1Y2Y3 на рынке
программного обеспечения для работы с электронными таблицами.
В начале 90Yх годов была выпущена версия Lotus 1Y2Y3 для операционной
системы CP/M. В то же самое время Microsoft направила усилия на улучшение
собственного продукта для работы с электронными таблицами — Excel. ПроY
грамма Excel 3.0, выпущенная в 1990 году, существенно проигрывала Lotus
1Y2Y3. Тем не менее, Microsoft продолжала упорствовать, выпуская новую,
улучшенную версию Excel каждые 1YY2 года. Excel 4, выпущенная в 1992 году,
уже пользовалась популярностью и предлагала возможность создавать макроY
сы с помощью языка XLM. Excel 5, выпущенная в 1993 году, поддерживала
создание нескольких рабочих листов в пределах одной рабочей книги,
а также запись макросов с помощью нового языка программирования —
VBA. Благодаря наличию обратной совместимости с Lotus 1Y2Y3 продажи
Excel стали стремительно возрастать. Середину 90Yх годов прошлого столеY
тия можно без преувеличения назвать ‘‘золотой эрой’’ в развитии Excel.
В Excel 95 и Excel 97 были представлены новые функциональные средства,
такие как сводные таблицы, автофильтр и автоматическое вычисление проY
межуточных итогов. Кроме того, в Excel 97 появилась новая среда разработY
ки VBA. Доминированию Lotus 1Y2Y3 на рынке программ для работы с элекY
тронными таблицами был положен конец. На момент написания этой книги
гремучая смесь в виде Excel и VBA прочно завоевала сердца более чем
400 млн пользователей по всему миру.
Будущее Excel и VBAС каждым новым выпуском программа для работы с электронными таблиY
цами StarOffice Calc приближается по предлагаемым возможностям к Excel.
Один из наиболее существенных недостатков пакета StarOffice заключается
в отсутствии поддержки VBA, равно как и любого другого языка создания
макросов. Этот факт позволяет не рассматривать StarOffice Calc в качестве
серьезного конкурента Excel.
Наверняка многие слышали о том, что Microsoft собирается отказаться от
VBA. На самом деле это маловероятно. По прошествии более чем 10 лет с моY
мента появления VBA Microsoft все еще поддерживает макросы XLM (язык
макросов, появившийся в Excel 4). К тому же Microsoft официально заявила о
поддержке VBA в следующей версии Excel. Учитывая, что большинство польY
зователей приобретают каждую вторую версию Office, VBA будет актуален по
меньшей мере до 2009 года.
Введение 31
Наконец, с точки зрения маркетинга было бы нелепо отказываться от VBA
как от ключевого компонента, обеспечивающего Excel тотальное преимущестY
во над StarOffice Calc.
В октябре 2003 года корпорация Microsoft официально объявила о новой
инициативе, направленной на повышение безопасности предлагаемых MiY
crosoft решений. Это заявление имеет весьма серьезное значение, поскольку
печально известный вирус Melissa использовал для своего распространения
макросы VBA текстового редактора Word. Пресса и власть имущие отреагироY
вали мгновенно, поместив VBA в список ‘‘вымирающих’’ технологий. Если
безопасность приложений пакета Office будет оставаться под угрозой, MicroY
soft может быть вынуждена отказаться от VBA.
Одной из отличительных особенностей пакета Office 2003 является подY
держка языка программирования Visual Basic .NET. Язык Visual Basic 6 позвоY
лял автоматизировать любую задачу в Excel XP с помощью VBA. В Excel 2003
некоторые задачи, такие как создание смартYдокумента или размещение соY
держимого на панели Справочные материалы (Research Pane), можно автоY
матизировать только из среды .NET.
Учитывая вопросы безопасности, возникающие при использовании VBA,
Microsoft может заменить его набором инструментов .NET Tools for Office. Это
было бы роковой ошибкой. На текущий момент свыше 400 млн пользователей
Office могут приобрести книгу, подобную этой, в течении недели изучить осноY
вы создания макросов и начать разрабатывать собственные решения с помощью
VBA. Производительность труда ‘‘белых воротничков’’ может существенно возY
расти, а их зависимость от отдела информационных технологий — уменьшитьY
ся. Если Microsoft заменит VBA набором инструментов .NET Tools for Office, коY
нечный пользователь будет лишен возможности создавать макросы с помощью
Excel. Кроме того, это нивелирует преимущество Excel перед конкурирующим
продуктом StarOffice Calc, поддержка языка создания макросов в котором должY
на появиться к 2007 году. Таким образом, инициатива по повышению безопасY
ности приложений пакета Office может обернуться для Microsoft утратой лидиY
рующих позиций на рынке программ для работы с электронными таблицами.
Тем не менее, я уверен, что VBA останется с нами по крайней мере до конY
ца этого десятилетия. К тому же навыки создания макросов с помощью Excel
VBA не утратят своей актуальности в результате перехода на среду .NET,
а синтаксис VBA не так уж сильно отличается от синтаксиса Visual Basic .NET.
Соглашения, принятые в этой книгеВ этой книге приняты следующие обозначения.
Курсив используется для выделения терминов, названий WebYсайтов,
а также акцентирования внимания читателя.
Моноширинным шрифтом выделяется код VBA, заголовки столбцов,
ссылки, формулы, имена макросов, модулей, функций, процедур,
32 Введение
переменных, констант, объектов, методов, свойств, файлов, адреY
сов URL и пр.
Для выделения названий элементов пользовательского интерфейса
применяется следующий шрифт: Меню.
Вдобавок к указанным обозначениям, каждая глава книги содержит специY
альные фрагменты текста: ‘‘Практикум’’, ‘‘На заметку’’, ‘‘Совет’’ и ‘‘Внимание’’.
‘‘Практикум’’ содержит примеры решений реальных задач с использованием
средств, описываемых в текущей главе.
На заметку Так помечается информация, которая не относится к основной теме главы, однакоявляется весьма интересной и полезной.
Совет В этом фрагменте содержатся методы и приемы, позволяющие сэкономить времяи усилия, которые потребуются для решения той или иной задачи.
Внимание Будьте осторожны, если встретите такой фрагмент. Приводящиеся в нем сведенияпомогут вам избежать ошибок, а также сберечь время и нервы.
Рассматриваемые версии ExcelНа момент написания этой книги текущей версией Excel была версия ExY
cel 2003. За исключением главы 15, ‘‘Поддержка XML в профессиональном
выпуске Excel 2003’’, большая часть приведенного в книге программного кода
совместима с Excel 2002 и Excel 2000. Более подробно о совместимости разY
личных версий Excel рассказывается в главе 23, ‘‘Обработка ошибок’’.
Программный кодПрилагаемый к книге программный код включает все рассматриваемые в
ней примеры. Его можно загрузить по адресу:
http://www.williamspublishing.com/Books/5-8459-0882-5.html.
(Чтобы загрузить код, прилагающийся к англоязычному изданию этой книги, поY
сетите WebYстраницу по адресу: http://www.mrexcel.com/getcode.html.)
Следующий шагВ главе 1, ‘‘Excel и VBA YYYY гремучая смесь’’, рассматривается редактор ViY
sual Basic и средство записи макросов Excel.
Введение 33
Ждем ваших отзывов!Вы, читатель этой книги, и есть главный ее критик и комментатор. Мы цеY
ним ваше мнение и хотим знать, что было сделано нами правильно, что можY
но было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам
интересно услышать и любые другие замечания, которые вам хотелось бы выY
сказать в наш адрес.
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам
бумажное или электронное письмо, либо просто посетить наш WebYсервер и
оставить свои замечания там. Одним словом, любым удобным для вас спосоY
бом дайте нам знать, нравится или нет вам эта книга, а также выскажите свое
мнение о том, как сделать наши книги более интересными для вас.
Посылая письмо или сообщение, не забудьте указать название книги и ее
авторов, а также ваш обратный адрес. Мы внимательно ознакомимся с вашим
мнением и обязательно учтем его при отборе и подготовке к изданию послеY
дующих книг. Наши координаты:
EYmail: [email protected]
WWW: http://www.williamspublishing.com
Адреса для писем:
из России: 115419, Москва, а/я 783
из Украины: 03150, Киев, а/я 152
IЧасть I
Первые шаги
1. Excel и VBA — гремучая смесь...............................................37
2. Знакомство с Visual Basic for Applications......................... 59
3. Работа с диапазоном ячеек ................................................. 95
4. Функции, определенные пользователем........................111
5. Циклы и управление выполнением кода........................ 141
6. Стиль записи ссылок R1C1.................................................... 161
7. Имена ....................................................................................... 177
8. События ...................................................................................189
9. Введение в пользовательские формы .............................215
1Excel всемогущий ...........................37Камни преткновения.....................37Панель инструментов“Visual Basic” ...................................39Безопасность макросов ............... 40Запись, хранение ивыполнение макросов ..................42Выполнение макроса ....................43Редактор Visual Basic .....................45Изучение кода макроса................50Непредвиденные результаты .....52Отчаяние ..........................................57Следующий шаг..............................57
Глава 1
Excel è VBA —ãðåìó÷àÿ ñìåñü
Excel всемогущий Сочетание языка программироваY
ния Visual Basic for Applications (VBA)
и Microsoft Excel таит в себе огромY
ные возможности. Удивительно, но об
этом задумывались лишь немногие из
почти что 400 миллионов пользоватеY
лей Microsoft Office. С помощью VBA
можно существенно упростить выY
полнение практически любой задачи в
Excel. К примеру, создание целого воY
роха квартальных диаграмм может
быть сведено всего лишь к нескольY
ким щелчкам мыши.
Камни преткновения Чтобы научиться программировать
с помощью VBA, вам придется преY
одолеть два барьера — несовершенное
средство записи макросов Excel и
чрезвычайно запутанный синтаксис
языка программирования VBA.
Средство записи макросовне работает!
С середины 90Yх годов прошлого
столетия корпорация Microsoft начаY
ла доминировать на рынке программ
для создания электронных таблиц.
Несмотря на то что в части работы с
электронными таблицами продукт
Microsoft оказался действительно
очень удачным (приверженцы Lotus
38 Часть I Первые шаги
1Y2Y3 быстро научились с ним работать), записать корректно работающий
макрос в Excel не удавалось практически никому. Неоспоримое преимущество
языка программирования Microsoft VBA перед языком макросов Lotus 1Y2Y3
нивелировалось низким качеством средства записи макросов.
Макрос, созданный накануне с помощью Lotus 1Y2Y3, прекрасно выполняY
ется и сегодня. Аналогичный макрос, созданный с помощью Microsoft Excel,
мог преподнести неприятный сюрприз. Многие из тех, кто пытался создать
свой первый макрос в Excel, приходили в отчаяние.
Visual Basic — это не BASIC
Код, сгенерированный в результате создания моего первого макроса, не
был похож ни на что, виденное мною ранее. Несмотря на то что я знал с полY
дюжины различных языков программирования, так называемый ‘‘Visual
Basic’’ оказался абсолютно неинтуитивным и даже приблизительно не напоY
минал тот BASIC, который я изучал в школе.
В 1995 году я уже в совершенстве освоил создание электронных таблиц.
И вот компания, в которой я работал, приняла решение о переходе с Lotus
1Y2Y3 на Excel. Без преувеличения, я оказался в сложном положении: с одной
стороны — средство записи макросов, которое не работает так, как надо,
с другой — язык программирования, в котором я ничего не понимал.
Эта книга задумывалась как пособие для тех, кто разбирается в создании
электронных таблиц больше, чем остальные 90% его сотрудников. Знание каY
когоYлибо языка программирования необязательно. Практика показывает, что
знание таких языков, как BASIC, может скорее навредить, чем принести пользу.
Нас должно объединять следующее: мы все пытались создать макрос в Excel и
остались недовольны полученным результатом.
Хорошие новости
Многочисленные недостатки средства записи макросов не являются неY
преодолимым препятствием на пути к постижению искусства программироY
вания в Excel. Далее в книге будет рассказано, как исправить ошибки автомаY
тически сгенерированного кода, а также как прочитать загадочный мануY
скрипт, написанный на языке Visual Basic.
Отличные новости
Microsoft Visual Basic for Applications (VBA) — чрезвычайно мощный язык
программирования. С его помощью можно продублировать абсолютно все
действия, выполняемые посредством пользовательского интерфейса Excel,
например, создание отчетов, построение диаграмм и т.п.
Авторы книги работают в компании MrExcel Consulting, предлагающей усY
луги по автоматизации процесса создания отчетов в Excel для огромного числа
клиентов. В ходе своей работы мы часто сталкиваемся с очень похожими задаY
Excel и VBA — гремучая смесь Глава 1 39
чами: успешно импортировав данные в Excel, наши клиенты хотели бы упроY
стить долгий и утомительный процесс создания одних и тех же еженедельных,
ежемесячных или ежеквартальных отчетов.
Именно это и предлагает VBA. Часы, потраченные на программирование
макросов, сводят создание отчетов к нескольким щелчкам мыши. Поистине
царская награда!
В этой главе будут рассмотрены причины некорректной работы средства заY
писи макросов. В частности, будет рассмотрен макрос, который начинает сбоY
ить на следующий день после своего создания. Не обращайте внимания на неY
понятный вам код. Цель этой главы — показать фундаментальную проблему
средства создания макросов Excel и познакомить вас со средой разработки VBA.
Панель инструментов “Visual Basic” Панель инструментов Visual Basic — одно из основных средств, необходиY
мых при написании макросов с помощью VBA. Чтобы отобразить ее на экраY
не, выберите в меню Excel команду Вид Панели инструментов Visual Basic(View Toolbars Visual Basic) (рис. 1.1).
Выполнить макрос Редактор сценариев
Записать макрос Режим конструктора
Редактор Visual Basic
Параметрыбезопасности
Элементы управления
Рис. 1.1. Панель инструментов Visual Basic предоставляет интерфейс для выполне&ния и записи макросов
Панель инструментов Visual Basic содержит несколько кнопок.
Выполнить макрос (Run Macro). Отображает список доступных макросов.
Записать макрос (Record Macro). Начинает процесс записи макроса и
отображает панель инструментов Остановить запись (Stop Recording)
(рис. 1.2).
Рис. 1.2. Одна из самых маленьких панелей инструментов в Excel содержит однуиз самых важных для записи работоспособного макроса кнопок (Относительнаяссылка)
Безопасность (Security). Отображает диалоговое окно Безопасность(Security) (см. раздел ‘‘Безопасность макросов’’, далее в этой главе).
Редактор Visual Basic (Visual Basic Editor). Открывает редактор Visual
Basic.
40 Часть I Первые шаги
Элементы управления (Control Toolbox). Отображает панель инструменY
тов с элементами управления, которые можно добавить на рабочий лист.
Режим конструктора (Design Mode). Режим конструктора позволяет
редактировать элементы управления, размещенные на рабочем листе.
Редактор сценариев (Microsoft Script Editor). Открывает редактор WebY
сценариев. Поскольку эта тема не имеет прямого отношения к VBA,
она не будет рассматриваться в этой книге.
Панель инструментов Остановить запись (см. рис. 1.2), которая отображаY
ется на экране в результате щелчка на кнопке Записать макрос, содержит
всего лишь 2 кнопки.
Остановить запись (Stop Recording). Останавливает текущий процесс
записи макроса.
Относительная ссылка (Relative Reference). Указывает Excel на необY
ходимость использования относительных ссылок вместо абсолютных.
Безопасность макросовПосле того как макросы VBA стали использоваться злоумышленниками
для распространения вирусов, Microsoft разработала новую политику безопасY
ности, по умолчанию запрещающую выполнение макросов. Чтобы продолY
жить изучение материала, нам потребуется изменить стандартную политику.
Откройте диалоговое окно Безопасность (Security) (рис. 1.3), выбрав коY
манду главного меню Excel Сервис Макрос Безопасность (Tools Macro
Security). Microsoft различает 4 уровня безопасности: Очень высокая (Very
High), Высокая (High) (используется по умолчанию), Средняя (Medium) и
Низкая (Low). При выборе уровня безопасности Высокая запрещается выполY
нение или редактирование всех неподписанных макросов. Чтобы начать создаY
ние собственных макросов, выберите уровень безопасности Средняя.
Уровень безопасности “Очень высокая”
В соответствии с парадигмой безопасности Microsoft системный админиY
стратор создает высокозащищенный сетевой каталог (так называемую
песочницу (sandbox)) и определяет его как доверенное размещение. Все макроY
сы, находящиеся в песочнице, считаются безопасными (их разрешается выY
полнять), остальные макросы таят в себе потенциальную угрозу. Ключевым
моментом этой парадигмы является предположение о невозможности комY
прометации доверенного размещения.
Уровень безопасности “Высокая”
На этом уровне безопасности разрешается выполнение только доверенных
макросов, т.е. макросов, имеющих цифровую подпись и происходящих из наY
Excel и VBA — гремучая смесь Глава 1 41
дежного источника. Поскольку подписывание макроса подразумевает необхоY
димость приобретения цифрового сертификата у уполномоченной на это орY
ганизации (такой как VeriSign), уровень безопасности Высокая (High) являетY
ся далеко не самым лучшим выбором при разработке собственных макросов.
При открытии рабочей книги все находящиеся в ней неподписанные макросы
будут попросту отключены.
Рис. 1.3. Уровень безопасности Высокая выбран поумолчанию
Уровень безопасности “Средняя”
На уровне безопасности Средняя (Medium) решение об отключении
потенциально опасных макросов принимается пользователем. Именно
этот уровень безопасности рекомендуется применять при разработке собY
ственных макросов. Конечно же, необходимость включать макросы при
каждом открытии рабочей книги может несколько раздражать. С другой
стороны, это последняя возможность защититься от разрушительного виY
руса, который таится в ничем не приметной рабочей книге, присланной
вам по электронной почте.
Уровень безопасности “Низкая”
На этом уровне безопасности защита от потенциально опасных макроY
сов отсутствует. Теперь уже ничто не защитит вас от вируса, хранящегося в
рабочей книге. Применение уровня безопасности Низкая (Low) крайне не
рекомендуется.
42 Часть I Первые шаги
Если необходимость включения собственных макросов при каждом открыY
тии рабочей книги начала приводить вас в бешенство, подумайте о приобреY
тении цифрового сертификата (см. выше).
Запись, хранение и выполнение макросовЗапись макросов весьма полезна на начальном этапе изучения языка макY
росов. По мере накопления знаний и опыта потребность в записи макросов
будет неуклонно уменьшаться.
Чтобы начать запись макроса, выберите команду главного меню Excel
Сервис Макрос Начать запись (Tools Macro Record New Macro) или
щелкните на кнопке Записать макрос (Record Macro) панели инструментов
Visual Basic. Перед тем как начать запись макроса, Excel отобразит диалоговое
окно Запись макроса (Record Macro), показанное на рис. 1.4.
Рис. 1.4. Задайте имя и сочетаниеклавиш для будущего макроса
Диалоговое окно “Запись макроса”
Введите имя макроса в поле Имя макроса (Macro name). В имени макроса
не допускается использование пробела (таким образом, имя Макрос1 являетY
ся допустимым, а имя Макрос 1 — нет). Старайтесь давать макросам значиY
мые имена, например, КвартальныйОтчет. Имена наподобие Макрос1 явY
ляются не слишком информативными.
Задайте сочетание клавиш с помощью одноименного поля. К примеру, если
ввести в поле Сочетание клавиш (Shortcut key) букву ‘‘п’’, записанный макрос
можно будет выполнить путем нажатия комбинации клавиш <Ctrl+п>.
С помощью раскрывающегося списка Сохранить в (Store macro in) выберите
место хранения записываемого макроса: Личная книга макросов (Personal
Macro Workbook), Новая книга (New Workbook) или Эта книга (This Workbook).
Макросы, имеющие непосредственное отношение к текущей рабочей книге,
рекомендуется сохранять в размещении Эта книга.
Личная книга макросов (PERSONAL.XLS) создается при первом сохранении
макроса в одноименном размещении. Это скрытая рабочая книга, которая
загружается автоматически при каждом запуске Excel. Чтобы отобразить личY
Excel и VBA — гремучая смесь Глава 1 43
ную книгу макросов на экране, выберите команду главного меню Excel
Окно Отобразить (Window Unhide).
Личная книга макросов подходит для хранения далеко не каждого макроса.
В ней рекомендуется хранить макросы общего назначения, не имеющие непоY
средственного отношения к конкретному рабочему листу или книге.
После выбора места хранения макроса щелкните на кнопке OK. Чтобы заY
кончить запись макроса, щелкните на кнопке Остановить запись (Stop ReY
cording) одноименной панели инструментов.
Выполнение макросаДля выполнения макроса достаточно нажать соответствующую комбинаY
цию клавиш (если она определена) на клавиатуре. Макрос можно назначить
также кнопке панели инструментов или элементу управления формы. Кроме
того, выполнить макрос можно с помощью уже рассмотренной панели инстY
рументов Visual Basic.
Создание кнопки выполнения макроса
Макросы общего назначения рекомендуется хранить в личной книге макY
росов и запускать с помощью кнопки, вынесенной на панель инструментов.
Чтобы создать кнопку выполнения макроса, следуйте приведенной ниже
процедуре.
1. Щелкните на панели инструментов правой кнопкой мыши и выберите
команду контекстного меню Настройка (Customize).
2. Перейдите во вкладку Команды (Commands) (рис. 1.5).
Рис. 1.5. Чтобы добавить кнопку выполнениямакроса на панель инструментов Excel, восполь&зуйтесь диалоговым окном Настройка
44 Часть I Первые шаги
3. Выберите категорию Макросы (Macros).
4. Выберите команду Настраиваемая кнопка (Custom Button) (со значком
улыбающейся рожицы) и перетащите ее на панель инструментов.
5. Щелкните на помещенном на панель инструментов значке с улыбаюY
щейся рожицей правой кнопкой мыши (не закрывайте диалоговое окно
Настройка (Customize)).
6. Выберите команду контекстного меню Назначить макрос (Assign
Macro), выберите макрос и щелкните на кнопке OK.
7. Закройте диалоговое окно Настройка.
Назначение макроса элементу управления формыМакросы, имеющие непосредственное отношение к конкретной рабочей
книге, рекомендуется хранить вместе с рабочей книгой и запускать с помоY
щью элемента управления формы, помещенного на рабочий лист.
Чтобы назначить макрос элементу управления формы, помещенному на
рабочий лист, выполните следующие действия.
1. Отобразите панель инструментов Формы (Forms), выбрав команду
главного меню Excel Вид Панели инструментов Формы (View
Toolbars Forms).
2. Щелкните на кнопке Кнопка (Button).
3. Щелкните на рабочем листе левой кнопкой мыши и, удерживая ее (кнопку)
нажатой, нарисуйте контур кнопки. Отпустите левую кнопку мыши.
4. Выберите требуемый макрос в диалоговом окне Назначить макрособъекту (Assign Macro) (рис. 1.6) и щелкните на кнопке OK.
Рис. 1.6. Макрос, хранящийся вместе с рабочейкнигой, рекомендуется назначить элементу управ&ления формы, помещенному на рабочий лист
5. Щелкните на только что созданной кнопке для выполнения макроса.
Excel и VBA — гремучая смесь Глава 1 45
Редактор Visual BasicНа рис. 1.7 показано типичное окно редактора Visual Basic, которое состоит
из трех основных частей. Не беспокойтесь, если ваше окно редактора Visual
Basic отличается от показанного на рисунке. Более подробно редактор Visual
Basic рассматривается в следующих разделах главы.
Рис. 1.7. Окно редактора Visual Basic
Параметры редактора Visual Basic
Редактор Visual Basic имеет несколько настраиваемых параметров. РасY
смотрим те из них, которые относятся непосредственно к написанию кода.
Настройка параметров редактора Visual Basic
Чтобы настроить параметры редактора Visual Basic, выберите команду меY
ню Tools Options (Сервис Параметры)1 и перейдите во вкладку Editor(Редактор). Из всех параметров, размещенных на этой вкладке, внимания заY
служивает только один — Require Variable Declaration (Требовать объявления
переменной). По умолчанию Excel не требует объявлять переменные, что споY
1 Примерный перевод. Редактор Visual Basic не русифицирован. YYYY Прим. ред.
46 Часть I Первые шаги
собствует более быстрому написанию кода. С другой стороны, с помощью
этого требования можно предотвратить ошибки ввода имен переменных. ПоY
ступайте так, как посчитаете нужным.
Использование цифровых подписей
Если вам надоело постоянно подтверждать безопасность собственных макY
росов, воспользуйтесь цифровой подписью, выбрав команду меню ToolsDigital Signature (Сервис Цифровая подпись).
Диспетчер проектов
Диспетчер проектов содержит список всех открытых рабочих книг и загруY
женных дополнительных модулей. Щелкнув на значке ‘‘плюс’’ рядом с узлом
VBAProject (Проект VBA), можно увидеть папки Microsoft Excel Objects(Объекты Microsoft Excel), Forms (Формы), Class Modules (Модули классов) и
Modules (Модули) (присутствует по умолчанию). Каждая папка содержит
один или несколько компонентов.
Чтобы просмотреть код компонента, щелкните на нем правой кнопкой
мыши и выберите команду контекстного меню View Code (Просмотр кода).
Такого же результата можно достичь путем двойного щелчка на названии
компонента (за исключением форм, двойной щелчок на названии которых
приводит к открытию формы в режиме конструктора).
Чтобы отобразить окно диспетчера проектов, выберите команду меню
Tools Project Explorer (Сервис Диспетчер проектов), нажмите комY
бинацию клавиш <Ctrl+R> или щелкните на кнопке Project Explorer(Диспетчер проектов), расположенной на панели инструментов.
Окно диспетчера проектов показано на рис. 1.8. Чтобы добавить к проекту
модуль, щелкните на названии проекта правой кнопкой мыши, выберите коY
манду контекстного меню Insert (Вставить), а затем — тип добавляемого модуля.
Объекты Microsoft Excel
По умолчанию проект состоит из модулей рабочих листов и модуля ЭтаКни-га (ThisWorkbook). Код, имеющий непосредственное отношение к рабочему
листу (например, код обработки событий листа), помещается в соответствуюY
щий этому листу модуль. Модуль ЭтаКнига содержит код обработки событий
рабочей книги. Об обработке событий речь идет в главе 8, ‘‘События’’.
Формы
Excel позволяет создавать формы для взаимодействия с пользователем.
О формах речь идет в главе 9, ‘‘Введение в пользовательские формы’’.
Модули
При записи макроса Excel автоматически создает модуль, куда помещает
код макроса. Именно в таких модулях хранится большая часть создаваемого
вами кода.
Excel и VBA — гремучая смесь Глава 1 47
Рис. 1.8. Диспетчер проектов содер&жит список всех модулей проекта
Модули классов
Модули классов Excel предназначены для создания пользовательских объY
ектов. Помимо этого, модули классов позволяют программистам обмениватьY
ся фрагментами кода, не вдаваясь в подробности работы последнего. О модуY
лях классов речь идет в главе 20, ‘‘Создание пользовательских объектов, типов
и коллекций’’.
Окно свойств
Окно свойств предназначено для редактирования параметров различных
компонентов — рабочих листов, книг, модулей или элементов управления
форм. Список параметров компонента зависит от его типа.
Чтобы открыть окно свойств, выберите команду меню View PropertiesWindow (Вид Окно свойств), нажмите клавишу <F4> или щелкните
на кнопке Project Properties (Свойства проекта), расположенной на
панели инструментов.
Практикум
Предположим, что вы работаете бухгалтером. Каждое утро вы получаете по элек&тронной почте текстовый файл с разделителями&запятыми, содержащий инфор&мацию о счетах за вчерашний день в столбцах СчетДата, СчетНомер, Продавец-Номер, КлиентНомер, ПродуктВыручка, СервисВыручка, ПродуктСтоимость(рис. 1.9).
48 Часть I Первые шаги
Рис. 1.9. Файл Счет.txt
Вы вручную импортируете этот файл в Excel, добавляете итоговый столбец, фор&матируете заголовки столбцов с помощью утолщенного шрифта и распечатываетеполученный отчет для передачи менеджерам.
Подготовка к записи макросаОписанная выше последовательность действий просто&таки напрашивается бытьоформленной в виде макроса. Прежде чем приступить к его записи, составьтеточный список выполняемых операций. В рассматриваемом случае он долженвыглядеть так.1. Выберите команду главного меню Excel Файл Открыть (File Open).
2. Отобразите содержимое папки, в которой хранится файл Счет.txt.
3. Выберите значение Все файлы (All Files) из раскрывающегося списка Типфайлов (Files of type).
4. Выберите файл Счет.txt.
5. Щелкните на кнопке Открыть (Open).
6. В группе Формат исходных данных (Original data type) диалогового окнаМастер текстов (импорт) — шаг 1 из 3 (Text Import Wizard — Step 1 of 3) устано&вите переключатель С разделителями (Delimited).
7. Щелкните на кнопке Далее (Next).
8. В группе Символом-разделителем является (Delimiters) диалогового окна Мастертекстов (импорт) — шаг 2 из 3 (Text Import Wizard — Step 2 of 3) сбросьте флажокЗнак табуляции (Tab) и установите флажок Запятая (Comma).
9. Щелкните на кнопке Далее.
10. В группе Формат данных столбца (Column data format) диалогового окнаМастер текстов (импорт) — шаг 3 из 3 (Text Import Wizard — Step 3 of 3) уста&новите переключатель Дата (Date) и выберите из раскрывающегося списказначение ДМГ (DMY).
11. Щелкните на кнопке Готово (Finish) для импортирования файла.
12. Нажмите клавишу <End>, а затем — клавишу <↓>, чтобы переместиться на по&следнюю строку импортированных данных.
Excel и VBA — гремучая смесь Глава 1 49
13. Нажмите клавишу <↓>, чтобы переместиться на итоговую строку.
14. Введите слово “Всего”.
15. Нажмите клавишу <→> 4 раза, чтобы переместиться в столбец E итоговой строки.
16. Щелкните на кнопке Автосумма (AutoSum) и нажмите комбинацию клавиш<Ctrl+Enter>, чтобы суммировать значения столбца ПродуктВыручка, остава&ясь при этом в той же ячейке.
17. Перетащите маркер заполнения по столбцам F и G, чтобы скопировать в нихформулу суммирования.
18. Выделите строку 1 и щелкните на кнопке Полужирный (Bold), чтобы выделитьзаголовки столбцов путем утолщения шрифта.
19. Выделите итоговую строку и щелкните на кнопке Полужирный, чтобы выделитьсуммарные значения столбцов путем утолщения шрифта.
20. Нажмите комбинацию клавиш <Ctrl+A>, чтобы выделить все ячейки рабочеголиста.
21. Выберите команду Формат Столбец Автоподбор ширины (Format ColumnAutoFit Selection).
Теперь вы готовы к записи своего первого макроса. Создайте пустую рабочую книгуи сохраните ее под каким&нибудь описательным именем, например Макрос-ИмпортаСчетов.xls. Щелкните в панели инструментов Visual Basic на кнопкеЗаписать макрос (Record Macro) или выберите команду меню Сервис МакросНачать запись (Tools Macro Record New Macro).Измените предлагаемое по умолчанию имя макроса Макрос1 на более инфор&мативное, например ИмпортСчета. Убедитесь, что макрос будет сохранен в раз&мещении Эта книга (This Workbook) и задайте сочетание клавиш для выполнениямакроса, к примеру <Ctrl+и>. По умолчанию в поле Описание (Description) зано&сится ваше имя и дата создания макроса. Добавьте сюда текст, кратко описываю&щий предназначение макроса (рис. 1.10), и щелкните на кнопке OK.
Рис. 1.10. Прежде чем начать запись макроса, задайте необходимые пара&метры в диалоговом окне Запись мак-роса (Record Macro)
50 Часть I Первые шаги
Запись макросаНачиная с этого момента средство записи макросов фиксирует каждое совершен&ное вами действие. Постарайтесь не отклоняться от намеченной ранее последо&вательности операций. Если, к примеру, вы случайно переместитесь в столбец Fвместо столбца E, а затем вернетесь обратно, созданный макрос будет старатель&но повторять эту ошибку при каждом своем запуске.
Внимание Сопротивляйтесь желанию убрать из виду панель инструментов Остановитьзапись (Stop Recording). Если она будет вам мешать, перетащите ее в безо&пасное место (действие по перетаскиванию панели Остановить запись не вклю&чается в записываемый макрос). Если вы все же закроете панель инструментовОстановить запись, то для того чтобы завершить запись макроса, вам потребует&ся выбрать команду меню Сервис Макрос Остановить запись (ToolsMacro Stop Recording).Выполните все действия, необходимые для создания отчета. Чтобы остановитьзапись макроса, щелкните на панели инструментов Остановить запись на одно&именной кнопке. Панель инструментов Остановить запись исчезнет из виду.
Внимание Закрытие панели инструментов Остановить запись не приводит к остановкезаписи макроса. Этим вы только усложните себе жизнь, так как теперь длязавершения записи макроса вам потребуется выбрать команду менюСервис Макрос Остановить запись.Пришло время взглянуть на сгенерированный код макроса. Для этого от&кройте окно редактора Visual Basic, выбрав команду меню Сервис МакросРедактор Visual Basic (Tools Macro Visual Basic Editor) или воспользовав&шись комбинацией клавиш <Alt+F11>.
Изучение кода макроса Рассмотрим код, сгенерированный Excel в результате записи макроса. ОтY
кройте редактор Visual Basic, воспользовавшись комбинацией клавиш
<Alt+F11>. Щелкните на названии модуля Module1 проекта МакросИмпор-таСчетов.xls правой кнопкой мыши и выберите команду контекстного меY
ню View Code (Просмотр кода). Строки кода, начинающиеся со знака апостY
рофа, являются комментариями и игнорируются Excel. Комментарии создаY
ются на основе информации, введенной в окне Запись макроса (Record
Macro) (сюда, в частности, относится сочетание клавиш, использующееся для
вызова макроса).
Excel и VBA — гремучая смесь Глава 1 51
Внимание Комментарий не определяет сочетание клавиш. Другими словами, изменив вкомментарии сочетание клавиш <Ctrl+и> на <Ctrl+с>, вы ничего не добьетесь.Изменить сочетание клавиш можно только с помощью диалогового окна Макрос(Macro).
Сгенерированный код макроса, как правило, выглядит достаточно опрятно
(рис. 1.11). Все строки кода, отличные от строк комментариев, сдвинуты на
4 символа вправо. Если длина строки превышает 100 символов, средство запиY
си макросов разбивает ее на несколько строк меньшей длины, дополнительно
сдвигая их еще на 4 символа вправо. В месте разрыва строки помещаются симY
волы пробела и знака подчеркивания. Поскольку физические размеры книги не
позволяют поместить на странице строку длиной 100 символов, в приводимых
далее примерах все строки будут разбиваться на границе в 60YY65 символов. ТаY
ким образом, код на экране компьютера может несколько отличаться от приY
водимого здесь.
Рис. 1.11. Сгенерированный код макроса выглядит очень аккуратно
Приведенные ниже 8 строк кода представляют собой 1 строку, разбитую на
несколько фрагментов для удобочитаемости.
Workbooks.OpenText Filename:= _ "C:\Счет.txt", Origin:=1251, StartRow:=1, _ DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, _ ConsecutiveDelimiter:=False, Tab:=False, Semicolon:=False, _ Comma:=True, Space:=False, Other:=False, _ FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), _
52 Часть I Первые шаги
Array(4, 1), Array(5, 1), Array(6, 1), Array(7, 1)), _ TrailingMinusNumbers:=True
Учитывая сказанное выше, средство записи макросов превратило 21Yшаговую
процедуру создания отчета в 14 строк кода. Весьма неплохо!
Совет Каждое действие, выполняемое посредством пользовательского интерфейса Excel,может быть описано с помощью одной или нескольких строк программного кода.
А теперь протестируем созданный макрос. Вернитесь к интерфейсу Excel,
воспользовавшись комбинацией клавиш <Alt+F11>. Закройте файл Счет.txt,
не сохранив внесенных в него изменений. При этом у вас должна остаться отY
крытой рабочая книга МакросИмпортаСчетов.xls.
Выполните сохраненный макрос, нажав комбинацию клавиш <Ctrl+и>.
Он должен сработать безукоризненно (рис. 1.12).
Рис. 1.12. Создание отчета прошло без сучка и задоринки
Непредвиденные результаты Предположим, что утром следующего дня вы получили по электронной
почте новый файл Счет.txt. Запустив созданный накануне макрос с помоY
щью сочетания клавиш <Ctrl+и>, вы были неприятно удивлены. Файл
Счет.txt от 5 июня содержал сведения о 12Yти счетах, а файл Счет.txt от
6 июня — о 16Yти. Тем не менее, макрос поместил итоговую информацию в
14Yю строку, тем самым в точности воспроизведя действия, выполненные при
его записи (рис. 1.13).
Возможное решение: использованиеотносительных ссылок
По умолчанию средство записи макросов рассматривает все действия, соY
вершаемые пользователем, как абсолютные. Если на определенном этапе
Excel и VBA — гремучая смесь Глава 1 53
пользователь введет данные в 14Yю строку, записанный макрос всегда будет
вводить эти данные в 14Yю строку. Поскольку исходная информация может
располагаться на разном количестве строк, использование при записи макроY
са абсолютных ссылок недопустимо.
Рис. 1.13. Записанный накануне макрос не выдержал проверки на прочность. Вместо того, что&бы добавить итоговые сведения сразу же после информации о счетах, макрос добавил их в14&ю строку
Одним из возможных решений в данной ситуации является использование
при записи макроса относительных ссылок.
Абсолютные ссылки основаны на действительных адресах ячеек, например
A1. Относительные ссылки основаны на позиции ячейки относительно другой
ячейки. Например, ссылка R[16]C[-1] указывает на ячейку, которая нахоY
дится на 16 строк ниже и на 1 столбец левее текущей ячейки.
Практикум
Запишем тот же макрос с использованием относительных ссылок. Закройте файлСчет.txt без сохранения изменений. В рабочей книге МакросИмпорта-Счетов.xls создайте новый макрос, выбрав команду меню СервисМакрос Начать запись (Tools Macro Record New Macro). Присвойте новомумакросу имя ИмпортСчетаОтносительно и назначьте другое сочетание клавиш,например <Ctrl+т> (рис. 1.14). Импортируйте данные из файла Счет.txt. Прежде чем переходить к последнейстроке данных с помощью последовательного нажатия клавиш <End> и <↓>,щелкните на кнопке Относительная ссылка (Relative Reference) в панели инстру&ментов Остановить запись (Stop Recording) (см. рис. 1.2). Выполните следующие действия.
1. Нажмите клавишу <End>, а затем — клавишу <↓>, чтобы переместиться на по&следнюю строку импортированных данных.
54 Часть I Первые шаги
2. Нажмите клавишу <↓>, чтобы переместиться на итоговую строку.
Рис. 1.14. Попытка номер 2
3. Введите слово “Всего”.
4. Нажмите клавишу <→> 4 раза, чтобы переместиться в столбец E итоговой строки.
5. Щелкните на кнопке Автосумма (AutoSum) и нажмите комбинацию клавиш<Ctrl+Enter>, чтобы суммировать значения столбца ПродуктВыручка, остава&ясь при этом в той же ячейке.
6. Перетащите маркер заполнения по столбцам F и G, чтобы скопировать в нихформулу суммирования.
7. Выделите итоговую строку с помощью комбинации клавиш <Shift+пробел> ищелкните на кнопке Полужирный (Bold), чтобы выделить суммарные значениястолбцов путем утолщения шрифта.
Не спешите перемещаться в ячейку A1 и выделять заголовки столбцов путемутолщения шрифта. Средство записи макросов зафиксирует это действие какперемещение на 17 строк вверх, а это не совсем корректно. Отключите режимотносительных ссылок, еще раз щелкнув на кнопке Относительная ссылка, ипродолжите запись макроса.
8. Выделите строку 1 и щелкните на кнопке Полужирный, чтобы выделить заголов&ки столбцов путем утолщения шрифта.
9. Нажмите комбинацию клавиш <Ctrl+A>, чтобы выделить все ячейки рабочеголиста.
10. Выберите команду Формат Столбец Автоподбор ширины (Format ColumnAutoFit Selection).
11. Остановите запись макроса.
Нажмите комбинацию клавиш <Alt+F11>, чтобы вернуться в окно редактораVisual Basic и просмотреть полученный на этот раз код. Текст макроса Импорт-СчетаОтносительно будет помещен в модуль Module1 сразу же после текстамакроса ИмпортСчета.
Excel и VBA — гремучая смесь Глава 1 55
Внимание Если между созданием первого и второго макроса вы завершали работу с Ex&cel, новый макрос будет помещен в модуль Module2.На рис. 1.15 показан код макроса ИмпортСчетаОтносительно с двумя ком&ментариями, указывающими на момент включения и отключения режима от&носительных ссылок.
Рис. 1.15. Код макроса, записанного с использованием режима относительных ссылок
Чтобы протестировать макрос, закройте файл Счет.txt без сохранения
изменений и нажмите комбинацию клавиш <Ctrl+т>. На этот раз работа макY
роса не должна вызывать какихYлибо нареканий.
Предположим, что файл Счет.txt от 7Yго июня содержит сведения о
21 счете (рис. 1.16).
Откройте рабочую книгу МакросИмпортаСчетов.xls и выполните ноY
вый макрос, нажав комбинацию клавиш <Ctrl+т>. На первый взгляд макрос
справился с поставленной перед ним задачей. Но взгляните на рис. 1.17 — не
кажется ли вам, что здесь чтоYто не так?
Передав подобный отчет менеджеру, вы, несомненно, навредили бы своей
репутации. Присмотритесь к ячейке E23. В левом верхнем углу ячейки нахоY
56 Часть I Первые шаги
дится маленький зеленый треугольник — верный признак ошибки. Следует
отметить, что возможность предупреждения ошибок появилась благодаря
смартYтегам — средству, недоступному в Excel 95 или Excel 97.
Рис. 1.16. Сможет ли новый макрос справиться с этими данными?
Рис. 1.17. Результат выполнения макроса, использующего относительные ссылки
Щелкните в ячейке E23 и подведите указатель к появившейся рядом с ячейY
кой кнопке примечания. На экране появится сообщение о том, что формула в
этой ячейке ссылается на диапазон, к которому прилегают другие значения.
Взглянув на строку формул, вы увидите, что макрос суммировал значения тольY
Excel и VBA — гремучая смесь Глава 1 57
ко с 7 по 22 строку. К сожалению, логику функции автоматического суммироваY
ния не может воспроизвести ни один автоматически созданный макрос.
Если же файл Счет.txt от 7Yго июня содержит сведения о меньшем колиY
честве счетов, чем 6Yго июня, Excel ‘‘наградит’’ вас аналогичной формулой
=СУММ(E10:E65531) (=SUM(E10:E65531)) и сообщением о наличии цикY
лических ссылок (рис. 1.18).
Рис. 1.18. Результат выполнения макроса, использующего относительные ссылки, при меньшемколичестве счетов
ОтчаяниеДочитав книгу до этого места, вы, вероятно, уже проклинаете Microsoft.
Представьте себе мое состояние после нескольких дней безуспешных попыток
написать хотя бы один работающий макрос. Ситуацию усугубляло знание тоY
го, что подобные макросы без проблем генерировались средством записи макY
росов Lotus 1Y2Y3, созданным в далеком 1983 году. То, что получилось у Мича
Кейпора (Mitch Kapor) 21 год назад, Microsoft не может повторить до сих пор.
Известно ли вам, что все ранние версии Excel вплоть до Excel 97 поддержиY
вали выполнение макросов командной строки Lotus? Этот факт стал известен
мне только после того, как Microsoft объявила об окончании поддержки
Excel 97. Многие компании, перешедшие на Excel XP (который уже не подY
держивал выполнение макросов Lotus 1Y2Y3), обратились к нам с просьбой пеY
реписать старые макросы Lotus на Excel VBA. Я не могу смириться с мыслью,
что начиная с Excel 5, Excel 95 и Excel 97 интерпретатор Microsoft мог выполY
нить макрос, корректно решавший поставленную нами задачу, однако средстY
во записи макросов было не в состоянии его создать.
Следующий шагЕдинственно правильное решение рассмотренной задачи заключается
в применении языка программирования Visual Basic. Первым приближением
58 Часть I Первые шаги
к цели можно считать автоматически сгенерированный макрос. Немного
здравого смысла, и он станет реальным подспорьем в решении повседневных
задач. В главе 2, ‘‘Знакомство с Visual Basic for Applications” мы попробуем
применить этот подход к двум записанным нами макросам. Научившись
‘‘читать’’ код VBA, вы с легкостью сможете подправить автоматически сгенеY
рированный код и даже написать макрос ‘‘с нуля’’.
2Загадочный код..............................59Учимся понимать “речь” VBA .....60Справочная система VBA .............63Изучение кода записанногомакроса ............................................66Использование отладчикакода ...................................................74Диспетчер объектов ......................855 советов по исправлению иоптимизации автоматическисгенерированного кода................87Исправление и оптимизацияавтоматическисгенерированного кода................90Следующий шаг..............................93
Глава 2
Çíàêîìñòâî ñ VisualBasic for Applications
Загадочный кодКод VBA способен смутить кажY
дого, кто изучал в школе один из
процедурных языков программироY
вания наподобие BASIC или COBOL.
Несмотря на то что VBA расшифроY
вывается как ‘‘Visual Basic for ApplicaY
tions’’, он представляет собой объект*но*ориентированную версию BASIC.
Рассмотрим небольшой фрагмент
кода VBA.
Selection.End(xlDown).SelectRange("A14").SelectActiveCell.FormulaR1C1 = "Всего"Range("E14").SelectSelection.FormulaR1C1 = _"=SUM(R[-12]C:R[-1]C)"Selection.AutoFillDestination:=Range("E14:G14"), _Type:=xlFillDefault
Бьюсь об заклад, что этот код не
будет иметь ни малейшего смысла
для тех, кто изучал только процедурY
ные языки программирования (к соY
жалению, практика изучения процеY
дурных языков еще весьма популярна
во многих учебных заведениях).
Ниже приведен фрагмент кода,
написанный на языке BASIC.
For x = 1 to 10 Print Rpt$(" ", x); Print "*";Next x
В результате его выполнения на экY
ране компьютера появится ‘‘лесенка’’
из символов звездочки.
60 Часть I Первые шаги
* * * * * * * * * *
Синтаксис процедурного языка программирования больше похож на синY
таксис английского языка, нежели синтаксис объектноYориентированного
языка программирования. К примеру, выражение Print "Hello World"записано в привычном формате ‘‘глаголYYобъект’’. А теперь постараемся заY
быть о программировании и рассмотрим один конкретный пример.
Учимся понимать “речь” VBAПопробуем сыграть в футбол на языке BASIC. Команда ‘‘ударить по мячу’’
будет выглядеть примерно следующим образом:
Kick the Ball
Именно так мы и говорим в повседневной жизни. Глагол ‘‘ударить’’ (kick)
следует перед существительным ‘‘мяч’’ (the ball). Аналогично, в приведенном
выше примере глагол Print следует перед существительным * (звездочка).
К сожалению, подобный синтаксис не употребляется ни в одном объектY
ноYориентированном языке, включая VBA. Исходя из самого названия этого
класса языков программирования, становится ясно, что центральное место
здесь отводится объекту, т.е. существительному. Команда ‘‘ударить по мячу’’,
записанная на языке VBA, будет выглядеть так:
Ball.Kick
В VBA существительное (объект) записывается перед глаголом (методом).
Базовая структура большинства строк VBA выглядит так:
Объект.МетодК сожалению, это не очень похоже на повседневную речь. Никто не говоY
рит ‘‘Вода.Пить’’, ‘‘Мяч.Ударить’’ или ‘‘Девушка.Целовать’’. Именно поэтому
VBA кажется очень сложным по сравнению с процедурными языками проY
граммирования.
Продолжим аналогию. Представьте, что вы стоите на зеленом газоне перед
тремя мячами: футбольным, баскетбольным и бейсбольным. Как сказать на
VBA ‘‘ударить футбольный мяч’’ члену школьной футбольной команды?
Выше была приведена команда ‘‘ударить по мячу’’ (Ball.Kick), однако в
данном случае этого недостаточно. Возможно, ребенок ударит мяч, который
находится ближе всех к нему (например, бейсбольный).
В VBA практически для каждого объекта (существительного) определяется
коллекция этих объектов. Рассмотрим электронную таблицу Excel. Строке соY
Знакомство с Visual Basic for Applications Глава 2 61
ответствует набор строк, столбцу YYYY набор столбцов, рабочему листу YYYY набор
рабочих листов. С точки зрения синтаксиса имя коллекции объектов составY
ляется из имени объекта и суффикса ‘‘s’’, например:
Row Rows,
Cell Cells,
Ball Balls.
Существует несколько способов обращения к элементу коллекции. ПерY
вый из них состоит в использовании порядкового номера элемента, например:
Balls(2).Kick
Несмотря на то что приведенная выше запись вполне корректна, переY
упорядочивание мячей в коллекции может привести к весьма плачевному
результату.
Второй способ обращения к элементу коллекции является более безопасY
ным и состоит в использовании имени элемента, например:
Balls("Soccer").Kick
Теперь можно быть уверенным, что ребенок ударит именно по футбольноY
му мячу.
Для большинства методов (глаголов) в Excel VBA определены параметры, хаY
рактеризующие способ выполнения метода (назовем их наречиями). Ниже привеY
дена команда ‘‘сильно ударить футбольный мяч так, чтобы он полетел влево’’:
Balls("Soccer").Kick Direction:=Left, Force:=Hard
Комбинации двоеточия и знака равенства в коде VBA всегда указывают на
параметр метода.
Методы могут иметь много параметров, как обязательных, так и нет. ПредY
положим, что у метода Kick есть параметр Elevation (‘‘поднятие’’). Ниже
приведена команда ‘‘сильно ударить футбольный мяч так, чтобы он полетел
высоко влево’’:
Balls("Soccer").Kick Direction:=Left, Force:=Hard, Elevation:=High
Для каждого метода существует определенный порядок следования его параY
метров. Некоторые программисты пропускают имена параметров, указывая тольY
ко их значения. Следующая строка кода полностью эквивалентна предыдущей:
Balls("Soccer").Kick Left, Hard, High
Практика пропуска имен параметров не вносит ясности в код, так как не
зная точного порядка следования параметров, сложно судить о предназначеY
нии той или иной строки. Значения параметров Left, Hard и High сами по
себе информативны, однако так бывает далеко не всегда. Рассмотрим слеY
дующую строку кода:
WordArt.Add Left:=10, Top:=20, Width:=100, Height:=200
Если пропустить имена параметров, она будет выглядеть так:
WordArt.Add 10, 20, 100, 200
Несмотря на то что приведенная выше строка кода вполне корректна, отY
сутствие имен параметров серьезно затрудняет восприятие ее смысла. Точный
62 Часть I Первые шаги
порядок следования параметров метода можно узнать, обратившись к разделу
справочной системы, посвященному этому методу.
Ситуацию усложняет еще и то, что имена параметров требуется указывать
только в случае нарушения стандартного порядка их следования. Ниже привеY
дены две эквивалентных строки кода, соответствующих команде ‘‘ударить
футбольный мяч так, чтобы он полетел высоко влево’’ (не важно, насколько
сильным будет сам удар):
Balls("Soccer").Kick Direction:=Left, Elevation:=HighBalls("Soccer").Kick Left, Elevation:=High
Указав имя одного параметра, следует указать также имена всех параметY
ров, которые последуют за ним в этой строке кода.
Некоторые методы не имеют параметров. Ниже приведен код, имитируюY
щий нажатие клавиши <F9>:
Application.Calculate
Другие методы выполняют действие и возвращают его результат. Ниже
приведен код, добавляющий рабочий лист:
Worksheet.Add Before:=Worksheets(1)
Поскольку метод Worksheet.Add создает новый объект, результат его
выполнения может быть присвоен переменной (параметры метода при этом
следует взять в скобки):
Set MyWorksheet = Worksheet.Add (Before:=Worksheets(1))
Напоследок рассмотрим еще одну важную составляющую языка VBA —
свойства. Свойства описывают объект наподобие того, как прилагательное
описывает существительное.
Обратимся к примеру. В Excel существует объект, соответствующий активY
ной ячейке YYYY ActiveCell. Предположим, что нам необходимо изменить
цвет активной ячейки на желтый. Цвет ячейки определяется значением свойY
ства Interior.ColorIndex объекта ActiveCell. Изменение цвета ячейки
на желтый описывается следующей строкой кода:
ActiveCell.Interior.ColorIndex = 6
Обратите внимание, что в приведенном выше коде используется конструкY
ция Объект.Свойство, похожая на уже рассмотренную нами конструкцию
Объект.Метод. На первый взгляд, их невозможно отличить друг от друга. Если
же присмотреться повнимательнее, то можно заметить отсутствие двоеточия
перед знаком равенства в строке с конструкцией Объект.Свойство. Обычно
свойство всегда присутствует в левой или правой части выражений, связанных
с присвоением значения.
Ниже приведена команда, изменяющая цвет текущей ячейки на цвет
ячейки A1:
ActiveCell.Interior.ColorIndex = Range("A1").Interior.ColorIndex
Итак, изменение значения свойства Interior.ColorIndex приводит к
изменению цвета ячейки. Сравнивая свойство с прилагательным, получаем
Знакомство с Visual Basic for Applications Глава 2 63
достаточно странный результат YYYY изменение прилагательного влечет за собой
выполнение действия.
В табл. 2.1 приведен краткий ‘‘словарь’’ терминов VBA.
Таблица 2.1. Словарь терминов VBA
Термин VBA Аналог Примечания
Объект Имя существительное YYYY
Коллекция Имя существительное
во множественном
числе
Обычно указывается элемент коллекY
ции, например Worksheets(1)
Метод Глагол Объект.Метод
Параметр Наречие Параметры указываются после имени
метода. Между именем параметра и его
значением ставится двоеточие и знак раY
венства (:=)
Свойство Имя прилагательное Обычно свойство присутствует в левой
или правой части выражения, связанY
ного с присвоением значения, например
ActiveCell.Height = 10 или x =ActiveCell.Height
Справочная система VBAНе беспокойтесь, если вы все еще не научились отличать метод от свойстY
ва. Именно здесь нам пригодится раскритикованное в предыдущей главе
средство записи макросов. Чтобы узнать, как запрограммировать то или иное
действие, запишите его в виде макроса и затем изучите сгенерированный код.
Спасительная клавиша <F1>
Приступая к написанию макросов, обязательно убедитесь в наличии на
вашем компьютере справочной системы VBA. К сожалению, она не входит в
стандартную установку Microsoft Office. Чтобы проверить наличие справочной
системы VBA, выполните следующие действия.
1. Запустите Excel и откройте окно редактора Visual Basic, воспользовавY
шись комбинацией клавиш <Alt+F11>. Выберите команду меню
Insert Module (Вставить Модуль) (рис. 2.1).
2. Введите 3 строки кода, как показано на рис. 2.2, и установите курсор
посредине слова MsgBox.
3. Нажмите клавишу <F1>. Если справочная система VBA установлена,
откроется окно, показанное на рис. 2.3.
64 Часть I Первые шаги
Рис. 2.1. Вставьте в рабочую книгуновый модуль
Рис. 2.2. Установите курсор посрединеслова MsgBox и нажмите клавишу <F1>
Рис. 2.3. Если справочная система VBA установлена, вы увидитена экране это окно
Если справочная система VBA не установлена, Excel выдаст сообщение об
ошибке. Установите справочную систему VBA, воспользовавшись установочY
ными компактYдисками Microsoft Office (при необходимости обратитесь за
помощью к системному администратору).
Знакомство с Visual Basic for Applications Глава 2 65
Просмотр разделов справочной системы
Раздел справочной системы, посвященный тому или иному методу, содерY
жит подробное описание всех его параметров. Под именем метода или функY
ции расположены три ссылки: See Also (См. также), Example (Пример) и
Specifics (Особенности). Одной из наиболее полезных является ссылка
Example, ведущая на страницу с примером использования метода или функY
ции (рис. 2.4).
Рис. 2.4. Большинство разделов справочной системы VBA со&держат ссылку на страницу с примерами
Код примера можно выделить (рис. 2.5), скопировать в буфер обмена с поY
мощью комбинации клавиш <Ctrl+C>, а затем вставить в модуль с помощью
комбинации клавиш <Ctrl+V>.
Код записанных макросов наверняка содержит много незнакомых объекY
тов и методов. Установите курсор посредине интересующего вас ключевого
66 Часть I Первые шаги
слова и нажмите клавишу <F1>, чтобы отобразить соответствующий раздел
справочной системы VBA.
Рис. 2.5. Выделите код примера и скопируйте его в буфер обмена с по&мощью комбинации клавиш <Ctrl+C>
Изучение кода записанного макросаРассмотрим код первого макроса, записанного в главе 1, “Excel и VBA —
гремучая смесь”, и попытаемся понять его смысл в контексте объектов,
свойств и методов (рис. 2.6).
Согласно концепции Объект.Метод (или, что то же самое, Существитель*ное.Глагол) в 1Yй строке кода Workbooks является объектом, а OpenText —
методом. Установите курсор внутри слова OpenText и нажмите клавишу
<F1>, чтобы открыть раздел справочной системы VBA, посвященный этому
методу (рис. 2.7).
В справочной системе указано, что OpenText — это метод. Его параметY
ры перечислены в стандартном порядке следования в области, выделенной
серым цветом. Обратите внимание, что метод OpenText имеет всего лишь
один обязательный аргумент YYYY FileName. Все остальные параметры могут
быть пропущены.
Знакомство с Visual Basic for Applications Глава 2 67
Рис. 2.6. Код записанного макроса
Рис. 2.7. Раздел справочной системы, посвященный методу OpenText. СсылкаApplies To (Применяется к) позволяет просмотреть список объектов, к которымможет быть применен этот метод
68 Часть I Первые шаги
Необязательные параметры
В справочной системе VBA можно найти информацию о стандартных знаY
чениях необязательных параметров. К примеру, стандартным значением параY
метра StartRow является 1, что весьма приемлемо. А вот пропустив параметр
Origin, вы рискуете попасть впросак. Дело в том, что по умолчанию Excel исY
пользует текущее значение этого параметра. Другими словами, если вы выполY
ните свой макрос после того, как ктоYто импортирует в Excel файл с китайскими
иероглифами, Excel предположит, что вы хотите сделать то же самое.
Предопределенные константы
Согласно разделу справочной системы VBA, посвященному методу Open-Text (см. рис. 2.7), DataType — это свойство, которое может иметь значение
xlDelimited или xlFixedWidth (предопределенные константы Excel VBA
типа XlTextParsingType). В редакторе Visual Basic нажмите комбинацию
клавиш <Ctrl+G>, чтобы открыть окно Immediate (Быстрое выполнение).
В окне Immediate введите следующую строку и нажмите клавишу <Enter>:
Print xlFixedWidth
Как показано на рис. 2.8, значением константы xlFixedWidth является 2.
Аналогичным образом можно узнать значение константы xlDelimited,
которое равно 1. Использование предопределенных констант с информаY
тивными именами вместо чисел значительно повышает удобочитаемость
программного кода.
Рис. 2.8. Воспользуйтесь окном Immediate, чтобы узнать значенияпредопределенных констант VBA, таких как xlFixedWidth
В большинстве случаев раздел справочной системы либо содержит допусY
тимые константы непосредственно в тексте справки, либо предлагает ссылку,
щелчок на которой приводит к их отображению (рис. 2.9).
К справочной системе VBA можно предъявить только одну претензию YYYY
она не позволяет узнать, является ли конкретный параметр нововведением теY
кущей версии Excel. К примеру, параметр TrailingMinusNumbers был
впервые представлен в Excel 2002. Попытка выполнения макроса, содержаY
щего этот параметр, в Excel 2000 завершится весьма плачевно. К сожалению,
эта проблема достаточно серьезна, поскольку решить ее можно только метоY
дом проб и ошибок.
Знакомство с Visual Basic for Applications Глава 2 69
Рис. 2.9. Щелкните на ссылке, чтобы увидеть все допустимые константы
Изучив раздел справочной системы, посвященный методу OpenText,
можно заметить, что этот метод является в некотором смысле эквивалентом
мастера импорта текстов. Так, на первом шаге мастера необходимо выбрать
формат исходных данных YYYY С разделителями (Delimited) или Фиксированнойширины (Fixed width), YYYY а также формат файла и строку, с которой необходимо
начать импорт (рис. 2.10).
Другими словами, первый шаг мастера импорта текстов можно описать
тремя параметрами метода OpenText:
Origin:=1251StartRow:=1DataType:=xlDelimited
70 Часть I Первые шаги
Рис. 2.10. Первый шаг мастера импорта текстов описывается тремя па&раметрами метода OpenText
На втором шаге мастера импорта текстов производится выбор разделителя
для текстовых данных. Чтобы Excel не считал две последовательные запятые
одной, флажок Считать последовательные разделители одним (Treat conY
secutive delimiters as one) снят. Поля, содержащие запятую как часть данных
(например, ‘‘XYZ, Inc.’’), должны быть ограничены символом, выбранным в
раскрывающемся списке Ограничитель строк (Text qualifier) (рис. 2.11).
Рис. 2.11. Второй шаг мастера импорта текстов описывается семью па&раметрами метода OpenText
Второй шаг мастера импорта текстов можно описать следующими параY
метрами метода OpenText:
Знакомство с Visual Basic for Applications Глава 2 71
TextQualifier:=xlDoubleQuoteConsecutiveDelimiter:=FalseTab:=FalseSemicolon:=FalseComma:=TrueSpace:=FalseOther:=False
На третьем шаге мастера импорта текстов определяется формат столбцов
данных. В рассмотренном примере мы оставили стандартный формат Общий(General) для всех столбцов, кроме первого, для которого был выбран формат
даты ДМГ (DMY) (рис. 2.12).
Рис. 2.12. Третий шаг мастера импорта текстов описывается всего лишьодним параметром метода OpenText
Третий шаг мастера импорта текстов полностью описывается параметром
FieldInfo метода OpenText.
Щелкнув на кнопке Подробнее (Advanced) диалогового окна Мастертекстов (импорт) — шаг 3 из 3 (Text Import Wizard — Step 3 of 3), можно выY
брать разделитель целой и дробной части, разделитель разрядов, а также укаY
зать на необходимость отображения знака ‘‘минус’’ в конце отрицательных
чисел (рис. 2.13).
Следует отметить, что средство записи макросов не генерирует код для паY
раметров DecimalSeparator и ThousandsSeparator до тех пор, пока не
будет выбран отличный от стандартного разделитель целой и дробной части и
разделитель разрядов, соответственно. В то же время, средство записи макроY
сов всегда генерирует код для параметра TrailingMinusNumbers.
Как видите, практически каждое действие, выполняемое с помощью польY
зовательского интерфейса Excel, находит отражение в фрагменте программY
ного кода макроса.
72 Часть I Первые шаги
Рис. 2.13. В диалоговом окне Дополнительнаянастройка импорта текста (Advanced Text Im&port Settings) можно определить три параметраметода OpenText
Рассмотрим следующую строку:
Selection.End(xlDown).Select
Щелкните на слове End и нажмите клавишу <F1>. На экране появится
диалоговое окне Context Help (Контекстная справка), предлагающее выбрать
один из двух разделов справочной системы, посвященный слову End. Один из
них находится в библиотеке Excel, а другой YYYY в библиотеке VBA (рис. 2.14).
Рис. 2.14. Иногда одному ключевому слову соответст&вует несколько разделов справочной системы
Чтобы не гадать, какой из двух разделов справочной системы вам нужен,
щелкните на кнопке Help (Справка). Как показано на рис. 2.15, раздел спраY
вочной системы из библиотеки VBA содержит сведения о выражении End. Это
не то, что нам нужно.
Закройте окно справочной системы, снова нажмите клавишу <F1> и выбеY
рите раздел, посвященный слову End, из библиотеки Excel. Свойство End возY
вращает объект Range, что эквивалентно последовательному нажатию клаY
виш <End> и <↑> или <End> и <↓> в пользовательском интерфейсе Excel.
Щелкнув на ссылке XlDirection, можно увидеть список параметров, допусY
тимых для передачи функции End (рис. 2.16).
Знакомство с Visual Basic for Applications Глава 2 73
Рис. 2.15. Поиск нужного раздела справочной системы можно проводитьметодом проб и ошибок
Возврат объектов свойством
Ранее неоднократно упоминалось, что базовый синтаксис языка VBA предY
ставлен конструкцией Объект.Метод. В рассмотренной выше строке кода меY
тодом, очевидно, является метод .Select. Несмотря на то, что End — это
свойство, оно возвращает объект Range, а метод, таким образом, применяется
непосредственно к свойству.
Открыв раздел справочной системы, посвященный слову Selection,
можно обнаружить, что это также свойство, а не объект. Полное обращение к
свойству Selection выглядит как Application.Selection, однако в конY
тексте использования объектной модели Excel префикс Application можно
опустить. Если бы данный макрос выполнялся в текстовом редакторе Word,
нам обязательно потребовалось бы указать перед свойством .Selection пеY
ременную объекта для идентификации вызываемого приложения.
74 Часть I Первые шаги
Рис. 2.16. Нужный раздел справочной системы, посвященный свойству End
Тип возвращаемого свойством Application.Selection объекта зависит
от текущего выделенного элемента. Если это ячейка, свойство Applica-tion.Selection возвращает объект Range.
Использование отладчика кодаРедактор Visual Basic содержит великолепный отладчик, предназначенный
для поиска и устранения недостатков программного кода.
Пошаговое выполнение кода
Обычно на выполнение макроса уходит всего лишь несколько секунд. Если
во время этого произойдет какойYто сбой, отследить его будет очень трудно.
К счастью, отладчик Excel поддерживает пошаговое выполнение кода.
Разместите курсор посредине имени процедуры ИмпортСчета и выберите
команду меню Debug Step Into (Отладка Пошаговое выполнение) (или наY
жмите клавишу <F8>) (рис. 2.17).
Сейчас редактор Visual Basic находится в режиме пошагового выполнеY
ния кода. Строка, которая будет выполнена следующей, выделена желтым
цветом. Кроме того, на нее указывает желтая стрелка, расположенная слеY
ва (рис. 2.18).
Знакомство с Visual Basic for Applications Глава 2 75
Рис. 2.17. Пошаговое выполнение кода позволяетобнаружить и устранить его недостатки
Рис. 2.18. Отладчик готов выполнить первую строку кода макроса
Выполнение строки Sub ИмпортСчета() приводит к входу в процедуру
ИмпортСчета(). Нажмите клавишу <F8>, чтобы выполнить эту строку и пеY
рейти к следующей. Редактор Visual Basic выделит желтым цветом фрагмент
кода, соответствующий методу OpenText. Нажмите клавишу <F8>. После
выполнения метода OpenText переключитесь в Excel с помощью комбинаY
ции клавиш <Alt+Tab> и убедитесь в успешном импорте файла Счет.txt.
Обратите внимание, что текущей выделенной ячейкой является ячейка A1(рис. 2.19).
76 Часть I Первые шаги
Рис. 2.19. Файл Счет.txt успешно импортирован в Excel
Переключитесь в редактор Visual Basic, воспользовавшись комбинацией
клавиш <Alt+Tab>. Нажмите клавишу <F8>, чтобы выполнить строку кода
макроса Selection.End(xlDown).Select. Переключившись в Excel,
можно увидеть, что теперь текущей выделенной ячейкой является ячейка A10(рис. 2.20).
Рис. 2.20. Выполнение команды Selection.End(xlDown).Select эквивалентно последовательному нажатию клавиш <End> и <↓>
Переключившись в редактор Visual Basic, нажмите клавишу <F8>, чтобы
выполнить команду Range("A14").Select. Вместо того чтобы выделить
ячейку в первой свободной строке после импортированных данных (A11),
макрос выделил ячейку A14, как показано на рис. 2.21.
Рис. 2.21. Записанный макрос допускает ошибку
Знакомство с Visual Basic for Applications Глава 2 77
Обнаружив проблемный участок кода, остановите выполнение макроса,
выбрав команду меню Run Reset (Выполнить Сброс) или щелкнув на
кнопке панели инструментов Reset (Сброс) (рис. 2.22). Вернитесь в Excel и
отмените все действия, которые успел выполнить макрос. В данном случае заY
кройте файл Счет.txt без сохранения изменений.
Рис. 2.22. Щелчок на кнопке Reset приводит к остановке выполнения макроса
Точки прерывания
Длина некоторых макросов может достигать сотен строк. Чтобы добраться
к проблемному участку кода, совсем необязательно пошагово выполнять все
предшествующие ему строки. Создайте точку прерывания, и выполнение макY
роса будет остановлено на ее границе.
Чтобы создать точку прерывания, щелкните на полосе слева от строки коY
да, перед выполнением которой необходимо сделать остановку. Строка кода
будет выделена красноYкоричневым цветом, а слева от нее появится такого же
цвета маркер (рис. 2.23).
Рис. 2.23. Красно&коричневый маркер слева от строки кода свидетельствует о наличии точкипрерывания
Выберите команду Run Run Sub/UserForm (Выполнить Выполнить подY
программу/Пользовательскую форму) или нажмите клавишу <F5>. ВыполнеY
ние макроса остановится на границе точки прерывания, а соответствующая
78 Часть I Первые шаги
строка кода будет выделена желтым цветом. Нажмите клавишу <F8>, чтобы
продолжить выполнение макроса в пошаговом режиме (рис. 2.24).
Рис. 2.24. Строка кода, на которой установлена точка прерывания, выделена желтым цветом
Завершив отладку кода, следует удалить все точки прерывания. Чтобы удаY
лить точку прерывания, щелкните на соответствующей ей точке на полосе
слева от строки кода. Чтобы удалить все точки прерывания в проекте, выбериY
те команду меню Debug Clear All Breakpoints (Отладка Удалить все точки
прерывания) или воспользуйтесь комбинацией клавиш <Ctrl+Shift+F9>.
Перемещение по коду
Пошаговый режим отладки позволяет изменить порядок выполнения строк
кода. Чтобы пропустить фрагмент кода или вернуться к уже выполнявшимся
строкам, перетащите желтую стрелку, расположенную на полосе слева от кода.
При подведении указателя мыши к стрелке он меняет свою форму, как показано
на рис. 2.25. Перетащите желтую стрелку на строку кода, которая должна быть
выполнена следующей, или разместите на этой строке курсор и выберите коY
манду меню Debug Set Next Statement (Отладка Выполнить следующей).
Рис. 2.25. При подведении указателямыши к желтой стрелке он меняетсвою форму
Знакомство с Visual Basic for Applications Глава 2 79
Выполнение фрагмента кодаИногда возникает необходимость в выполнении целого фрагмента кода, наY
пример, цикла. Вместо того чтобы возвращаться к одним и тем же строкам неY
сколько раз подряд, можно указать отладчику на необходимость выполнения
всего участка кода до указанной вами строки. Для этого разместите курсор на
требуемой строке и воспользуйтесь комбинацией клавиш <Ctrl+F8> или команY
дой меню Debug Run To Cursor (Отладка Выполнить до указанной строки).
Вычисление значения переменной или выраженияВ режиме пошагового выполнения кода можно просмотреть значение пеY
ременной или выражения (между прочим, средство записи кода никогда не
создает переменных).
Окно Immediate
Чтобы открыть окно Immediate (Быстрое выполнение) в редакторе Visual
Basic, нажмите комбинацию клавиш <Ctrl+G>. На рис. 2.26 приведен пример
вычисления различных выражений, таких как адрес текущей выделенной
ячейки, ее значение, а также имя активного рабочего листа.
Окно Immediate обычно располагается под окном просмотра программY
ного кода. Размер окна Immediate можно изменить, воспользовавшись маркеY
ром изменения размера окна (рис. 2.27).
Рис. 2.26. Пауза после выполнения каждойстроки кода позволяет узнать текущие значе&ния переменных или выражений
Рис. 2.27. Изменение размера окнаImmediate
Если содержимое окна Immediate не умещается на экране, его можно проY
смотреть с помощью полосы прокрутки, расположенной в правой части окна.
Выражение, значение которого необходимо вычислить с помощью окна
Immediate, не обязательно набирать каждый раз заново. К примеру, вычислим
значение выражения Selection.Address после выполнения нескольких
строк кода макроса (рис. 2.28).
Нажмите клавишу <F8>, чтобы выполнить следующую строку кода. ВмеY
сто повторного ввода выражения, установите курсор в конец содержащей это
выражение строки (рис. 2.29).
80 Часть I Первые шаги
Рис. 2.28. Вычисление значения выра&жения в окне Immediate
Рис. 2.29. Чтобы повторно вычис&лить результат выражения, устано&вите курсор в конец содержащей этовыражение строки и нажмите кла&вишу <Enter>
Чтобы повторно вычислить результат выражения, нажмите клавишу <Enter>.
Новый результат (в данном случае $1:$1) ‘‘сдвинет’’ старый ($E$14:$G$14) на
одну строку вниз (рис. 2.30).
Нажмите клавишу <F8> четыре раза, чтобы выполнить строку
Cells.Select. Снова расположите курсор в конце строки Print Selec-tion.Address в окне Immediate и нажмите клавишу <Enter>. Новый реY
зультат выражения Selection.Address сдвинет на одну строку вниз два
предыдущих (рис. 2.31).
Рис. 2.30. Старый результат выра&жения был сдвинут новым резуль&татом на одну строку вниз
Рис. 2.31. После выделения всехячеек текущий адрес выбранногодиапазона изменился на $1:$65536
Выражение, указанное в окне Immediate, можно изменить. Установите курсор
справа от слова Address и удалите его с помощью клавиши <Backspace>. Введите
выражение Rows.Count и нажмите клавишу <Enter>. В окне Immediate появится
значение, равное числу выделенных строк (рис. 2.32).
Изменение выражения в окне Immediate часто применяется при отладке
проблемных участков кода. В подобных ситуациях может пригодиться самая
различная информация YYYY имя активного рабочего листа (Print Active-
Знакомство с Visual Basic for Applications Глава 2 81
sheet.Name), адрес выбранного диапазона ячеек (Print Selection.Address), адрес активной ячейки (Print ActiveCell.Address), формула
активной ячейки (Print ActiveCell.Formula), значение активной ячейки
(Print ActiveCell.Value или же просто Print ActiveCell, так как
Value является стандартным свойством ячейки) и т.д.
Рис. 2.32. Измените выражение, ука&занное в окне Immediate, и нажмитеклавишу <Enter>
Вычисление значения с помощью указателя мыши
Чтобы узнать значение выражения, подведите к нему указатель мыши и заY
держите в таком положении пару секунд. На экране появится подсказка, соY
держащая текущее значение выражения. Как правило, этот прием оказываетY
ся наиболее полезным при отладке циклов (см. главу 5, ‘‘Циклы и управление
выполнением кода’’). Пригодится он и при работе с автоматически сгенериY
рованным кодом. Заметьте, что выражение, значение которого вычисляется
описанным выше способом, не обязано содержаться в только что выполненY
ной строке кода. Как показано на рис. 2.33, макрос только выделил все ячейки
(при этом текущей активной ячейкой является ячейка A1). Подведя указатель
мыши к выражению ActiveCell.FormulaR1C1, можно узнать, что его знаY
чением является строка СчетДата.
Рис. 2.33. Чтобы узнать значение выражения, задержите над ним указатель мыши
82 Часть I Первые шаги
Иногда окно просмотра кода редактора Visual Basic не реагирует на указаY
тель мыши. Поскольку некоторые выражения не имеют значения, назвать
причину отсутствия подсказки удается не сразу. Подведите указатель мыши к
выражению, которое всегда должно иметь значение, например, к переменной.
При отсутствии подсказки щелкните на имени переменной и задержите над
ним указатель мыши до появления подсказки. Как показывает практика, это
всегда выводит редактор Visual Basic из состояния ступора.
Вам все еще не нравится Visual Basic? Бьюсь об заклад, что после знакомства
с его рабочей средой вы настроены гораздо менее категорично. Эти средства
отладки просто потрясающи!
Окно Watches
Окно Watches (Просмотр) позволяет отслеживать значение любого выраY
жения во время выполнения кода. Отследим текущий адрес выделенного диаY
пазона ячеек (Selection.Address).
Выберите команду меню редактора Visual Basic Debug Add Watch (Отладка
Добавить в окно просмотра).
Введите Selection.Address в текстовом поле Expression (Выражение)
диалогового окна Add Watch (Добавить в окно просмотра) и щелкните на
кнопке OK (рис. 2.34).
Окно Watches обычно располагается под окном просмотра программного
кода. Запустите макрос ИмпортСчета в режиме пошагового выполнения и
остановитесь перед строкой Range("A14").Select. Текущее значение выY
ражения Selection.Address будет равно $A$10 (рис. 2.35).
Рис. 2.34. Добавление в окно просмотра теку&щего адреса выделенного диапазона ячеек
Рис. 2.35. Окно Watches позволяет от&слеживать текущее значение выраже&ния на протяжении всего времени вы&полнения кода
Нажмите клавишу <F8>, чтобы выполнить строку Range("A14").Select.
В окне Watches будет отображен новый адрес выделенного диапазона ячеек YYYY
$A$14 (рис. 2.36).
Знакомство с Visual Basic for Applications Глава 2 83
Рис. 2.36. Содержимое окна Watches обновляется после вы&полнения каждой строки кода
Установка точки прерывания с помощью окна Watches
Щелкните правой кнопкой мыши на значке с изображением очков в окне
Watches (Просмотр) и выберите команду контекстного меню Edit Watch(Изменить параметры просмотра). Установите переключатель Break WhenValue Changes (Приостановить при изменении значения) в группе переклюY
чателей Watch Type (Способ просмотра) диалогового окна Edit Watch(Изменить параметры просмотра) (рис. 2.37). Щелкните на кнопке OK.
Рис. 2.37. Установите переключатель BreakWhen Value Changes
Значок с изображением очков сменится на значок с изображением руки и
треугольника. Нажмите клавишу <F5> для выполнения макроса. Как только
значение выделенного диапазона ячеек изменится, выполнение макроса будет
приостановлено. Данная возможность является чрезвычайно полезной при
отладке кода.
Отслеживание состояния объекта с помощью окна Watches
Ранее было рассмотрено отслеживание значения свойства Selection.Address. Редактор Visual Basic позволяет также следить за состоянием целых
объектов, таких как объект Selection (рис. 2.38).
84 Часть I Первые шаги
Рис. 2.38. При отслеживании состояния объектарядом со значком с изображением очков появ&ляется значок с изображением знака “плюс”
Щелкните на значке с изображением знака ‘‘плюс’’, чтобы просмотреть все
свойства объекта Selection (рис. 2.39). Существование некоторых из них
окажется для вас настоящим сюрпризом. Кроме новых свойств наподобие
.AddIndent (значение False) и .AllowEdit (значение True), вы увидите
также уже знакомые свойства, такие как .Formula.
Рис. 2.39. Щелкните на значке с изображением знака “плюс”, чтобы просмотреть списоксвойств объекта и их текущих значений
Возле некоторых свойств объекта Selection, таких как коллекция Bor-ders, находится значок с изображением знака ‘‘плюс’’. Щелкните на нем,
чтобы получить более детальную информацию об объекте.
Знакомство с Visual Basic for Applications Глава 2 85
Диспетчер объектовЧтобы открыть окно диспетчера объектов редактора Visual Basic, нажмите
клавишу <F2> (рис. 2.40).
Рис. 2.40. Чтобы открыть окно диспетчера объектов, нажмите клавишу <F2>
Диспетчер объектов позволяет просматривать библиотеку объектов Excel и
проводить поиск в ней. Распечатка списка всех объектов из этой библиотеки
занимает порядка 409 страниц текста, однако благодаря диспетчеру объектов
работать с библиотекой совсем нетрудно.
Окно диспетчера объектов занимает пространство окна просмотра проY
граммного кода. С помощью верхнего раскрывающегося списка можно выY
брать все подключенные библиотеки (All Libraries (Все библиотеки)), библиоY
теку Excel, Office, VBA, библиотеку каждой открытой рабочей книги, а также
все остальные библиотеки, указанные с помощью диалогового окна
References (Ссылки) (чтобы открыть диалоговое окно References, выберите
команду меню редактора Visual Basic Tools References (Сервис Ссылки)).
Раскройте список и выберите библиотеку Excel.
В левой части окна диспетчера объектов содержится список классов бибY
лиотеки Excel. Щелкните на имени класса Application. В правой части окY
на диспетчера объектов появится список свойств и методов объекта Appli-cation (рис. 2.41).
86 Часть I Первые шаги
Рис. 2.41. Выберите класс, а затем — метод или свойство. В нижней части окна диспетчераобъектов появится краткое описание выбранного элемента. Рядом с именем метода в правойчасти окна диспетчера объектов находится значок с изображением зеленой книги, а рядом сименем свойства — изображение учетной карточки с указывающей на нее кистью руки
Щелкните на имени свойства ActiveCell. В нижней части окна диспетчера
объектов появится краткое описание свойства ActiveCell, из которого можно
узнать тип возвращаемого этим свойством значения YYYY Range. Кроме того,
свойство ActiveCell предназначено только для чтения, что делает невозможY
ным присвоение ему значения с целью сдвинуть указатель активной ячейки.
Щелкните на ссылке Range в нижней части окна диспетчера объектов,
чтобы увидеть список свойств и методов объекта Range, а значит и свойства
ActiveCell. Щелкните на имени любого свойства или метода объекта
Range, а затем YYYY на кнопке с изображением желтого вопросительного знака в
верхней части диспетчера объектов. В результате откроется окно справочной
системы с разделом, посвященным выбранному элементу.
Введите любое ключевое слово в поле ввода раскрывающегося списка, наY
ходящегося справа от кнопки с изображением бинокля, и щелкните на этой
кнопке, чтобы найти все подходящие под данное ключевое слово элементы
библиотеки Excel.
Чтобы закрыть окно диспетчера объектов и вернуться к окну просмотра
программного кода, щелкните на кнопке с изображением крестика в верхнем
правом углу окна диспетчера объектов (рис. 2.42).
Знакомство с Visual Basic for Applications Глава 2 87
Рис. 2.42. Чтобы закрыть окно диспетчера объектов, щелкните на кнопке с изображением кре&стика в верхнем правом углу окна
5 советов по исправлению и оптимизацииавтоматически сгенерированного кода
Приблизившись к концу второй главы, было бы неплохо исправить хотя
бы один из двух имеющихся у нас проблемных макросов. Ниже приведено
5 советов, направленных на оптимизацию и исправление автоматически сгеY
нерированного кода.
Совет 1: ничего не выделяйте
Отличительной особенностью автоматически сгенерированного кода являY
ется выделение элементов перед их дальнейшим использованием. В некотоY
ром смысле это подразумевает копирование действий, совершаемых с помоY
щью пользовательского интерфейса Excel. Так, чтобы сделать текст ячейки
утолщенным, ее необходимо сначала выделить.
Подобная практика является совершенно излишней в VBA. (Существуют
исключения, которые, однако же, обусловлены не вполне корректным повеY
дением некоторых методов, требующих для своего выполнения предварительY
ного выделения объекта диаграммы.) Чтобы сделать текст ячейки утолщенY
88 Часть I Первые шаги
ным, последнюю можно и не выделять. Ниже показан пример преобразования
двух строк автоматически сгенерированного кода макроса в одну.
Автоматически сгенерированный код:
Rows("1:1").SelectSelection.Font.Bold = True
Оптимизированный код:
Rows("1:1").Font.Bold = True
Подобное преобразование имеет несколько преимуществ. ВоYпервых, коY
личество строк кода уменьшается почти что вдвое. ВоYвторых, код выполняетY
ся быстрее.
Чтобы оптимизировать приведенный выше фрагмент кода, выделите
фрагмент Select в верхней строке кода и фрагмент Selection. — в нижY
ней, после чего щелкните на кнопке <Delete> (рис. 2.43 и 2.44).
Рис. 2.43. Выделите фрагмент ко&да так, как показано на рисунке...
Рис. 2.44. ...и нажмите клавишу<Delete>
Совет 2: перемещайтесь на последнюю строку данныхс конца рабочего листа
Никогда не доверяйте данным, поступившим из внешних источников. РаY
но или поздно вы столкнетесь с содержащимися в них ошибками, например,
с отсутствием номера счета. Вне зависимости от причины ошибок (сбой в
электропитании или человеческий фактор), следует запомнить одно — нет
никаких оснований полагать, что все ячейки содержат данные.
С учетом сказанного выше, последовательное нажатие клавиш <End> и
<↓> приводит не к перемещению на последнюю строку данных, а к перемеY
щению на последнюю строку данных в определенном диапазоне ячеек.
К примеру, на рис. 2.45 последовательное нажатие клавиш <End> и <↓> приY
ведет к перемещению в ячейку A6, а не в ячейку A10.
Рис. 2.45. Последовательное нажатие клавиш <End> и <↓>(выражение End(xlDown) в VBA) срабатывает некорректно приотсутствии значения в ячейке
Знакомство с Visual Basic for Applications Глава 2 89
Одним из возможных решений этой проблемы является перемещение в
конец рабочего листа Excel и последовательное нажатие клавиш <End> и <↑>.
В контексте пользовательского интерфейса Excel подобная процедура не имеY
ет смысла, однако она способна помочь макросу VBA переместиться на нужY
ную строку:
Range("A65536").End(xlUp)
Внимание Начиная с Excel 97 максимальное количество строк в Excel равно 65 536 (ранее оноравнялось 16 384). Чтобы обеспечить совместимость кода макроса с любой вер&сией Excel, жестко закодированное значение 65 535 рекомендуется заменить вы&ражением Rows.Count (максимальное число строк в текущей версии Excel).Строка Cells(Row.Count, 1).End(xlUp) гарантирует правильность работымакроса как в будущих, так и в предыдущих версиях Excel.
Совет 3: используйте переменные
Средство записи макросов никогда не создает переменные. О переменных
речь пойдет далее в этой книге, а пока что можно отметить, что, как и в BASIC,
переменные используются для хранения значений.
Создадим переменную для хранения номера последней строки данных. ПереY
менным рекомендуется давать информативные имена, например, FinalRow.
FinalRow = Range("A65536").End(xlUp).Row
Зная номер последней строки данных, разместить в столбце A следующей
строки слово ‘‘Всего’’ можно с помощью такого кода:
Range("A" & FinalRow + 1).Value = "Всего"
См. также Более простой способ обращения к этой ячейке рассматривается в разделе“Обращение к диапазону ячеек с помощью свойства Cells” главы 3 на с. 99.
Переменные можно использовать и при построении формулы. К примеру,
приведенная ниже формула суммирует все значения, начиная с ячейки E2и заканчивая ячейкой, находящейся на пересечении последней строки данных
и столбца E:
Range("E" & FinalRow + 1).Formula = "=SUM(E2:E" & FinalRow & ")"
Совет 4: используйте одно выражение для копированияи вставки данных
Автоматически сгенерированный код ‘‘славится’’ своей четырехшаговой
процедурой копирования и вставки данных, подразумевающей выделение исY
ходного диапазона ячеек, его копирование, выделение целевого диапазона
90 Часть I Первые шаги
ячеек и, наконец, вызов метода ActiveSheet.Paste. Метод Copy, примеY
няемый к диапазону ячеек, обладает намного более широкой функциональноY
стью, позволяя задать источник и назначение копируемых данных с помощью
одного выражения.
Ниже приведен фрагмент автоматически сгенерированного кода:
Range("E14").SelectSelection.CopyRange("F14:G14").SelectActiveSheet.Paste
А это YYYY тот же код после оптимизации:
Range("E14").Copy Destination:=Range("F14:G14")
Совет 5: используйте конструкцию With...End With
Ниже приведен автоматически сгенерированный код, изменяющий разY
личные параметры шрифта выделенного диапазона ячеек:
Range("A14:G14").SelectSelection.Font.Bold = TrueSelection.Font.Size = 12Selection.Font.ColorIndex = 5Selection.Font.Underline = xlUnderlineStyleDoubleAccounting
При выполнении этого кода макрос должен 4 раза подряд вычислить знаY
чение выражения Selection.Font. Поскольку каждый раз обращение проY
исходит к одному и тому же объекту, его имя рекомендуется указать в начале
блока With. Чтобы сослаться на объект внутри блока With, соответствующие
строки кода необходимо предварить символом точки, как показано ниже:
With Range("A14:G14").Font .Bold = True .Size = 12 .ColorIndex = 5 .Underline = xlUnderlineStyleDoubleAccountingEnd With
Исправление и оптимизация автоматическисгенерированного кода
Практикум
Изменение автоматически сгенерированного кодаИспользуя приведенные выше советы, превратим автоматически сгенерированныйкод макроса ИмпортСчета (см. ниже) в эффективный и профессиональный код.Sub ИмпортСчета()'' ИмпортСчета Макрос' Макрос записан 03.01.2005 (Александр Журавлев)
Знакомство с Visual Basic for Applications Глава 2 91
'' Сочетание клавиш: Ctrl+и' Workbooks.OpenText Filename:= _ "C:\Счет.txt", Origin:=1251, StartRow:=1, _ DataType:=xlDelimited, TextQualifier:= xlDoubleQuote, _ ConsecutiveDelimiter:=False, Tab:=False, Semicolon:= _ False, Comma:=True, Space:=False, Other:=False, _ FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), _ Array(4, 1), Array(5, 1), Array(6, 1), Array(7, 1)), _ TrailingMinusNumbers:=True Selection.End(xlDown).Select Range("A14").Select ActiveCell.FormulaR1C1 = "Всего" Range("E14").Select Selection.FormulaR1C1 = "=SUM(R[-12]C:R[-1]C)" Selection.AutoFill Destination:=Range("E14:G14"), _Type:=xlFillDefault Range("E14:G14").Select Rows("1:1").Select Selection.Font.Bold = True Rows("14:14").Select Selection.Font.Bold = True Cells.Select Selection.Columns.AutoFitEnd Sub
Чтобы исправить и оптимизировать код макроса, выполните следующие действия.1. Оставьте метод Workbook.OpenText без изменений.
2. В следующей строке кода осуществляется попытка перейти на последнюю стро&ку с данными:Selection.End(xlDown).Select
Ничего не выделяйте. Кроме того, создайте две переменные — для номера по&следней строки с данными и для номера итоговой строки. Чтобы избежать про&блемы пустой ячейки, переместитесь на последнюю строку с данными с концарабочего листа:
' Найти последнюю строку с даннымиFinalRow = Range("A65536").End(xlUp).RowTotalRow = FinalRow + 1
3. Следующие строки кода соответствуют вводу слова “Всего” в столбец A итого&вой строки:
Range("A14").SelectActiveCell.FormulaR1C1 = "Всего"
Воспользуйтесь созданной ранее переменной TotalRow и откажитесь от выде&ления ячейки, как показано ниже:
' Создание итоговой строкиRange("A" & TotalRow).Value = "Всего"
4. Приведенные ниже строки кода описывают ввод формулы суммы в столбец E иее копирование в столбцы F и G:
Range("E14").SelectSelection.FormulaR1C1 = "=SUM(R[-12]C:R[-1]C)"Selection.AutoFill Destination:=Range("E14:G14"), _
92 Часть I Первые шаги
Type:=xlFillDefaultRange("E14:G14").Select
Вы уже наверное догадались, что выделять здесь абсолютно нечего. Приведен&ный ниже код помещает формулу суммы в требуемые ячейки итоговой строки(формат ссылок R1C1 рассматривается в главе 6, “Стиль записи ссылок R1C1”):
Range("E" & TotalRow).Resize(1, 3).FormulaR1C1 = _"=SUM(R2C:R[-1]C)"
5. Ниже приведен код, сгенерированный средством записи макросов при фор&матировании строки заголовков столбцов и итоговой строки:
Rows("1:1").SelectSelection.Font.Bold = TrueRows("14:14").SelectSelection.Font.Bold = True
А вот и его оптимизированная версия: Rows("1:1").Font.Bold = True
Rows(TotalRow & ":" & TotalRow).Font.Bold = True
6. Перед вызовом метода AutoFit средство записи макросов выделяет все ячей&ки рабочего листа:
Cells.SelectSelection.Columns.AutoFit
Как вы уже догадались, это совершенно излишне: Cells.Columns.AutoFit
7. Ниже приведен комментарий, добавляемый к каждому макросу при его создании:' ИмпортСчета Макрос' Макрос записан 03.01.2005 (Александр Журавлев)'' Сочетание клавиш: Ctrl+и
Исправив и оптимизировав автоматически сгенерированный код, вы имеетеполное право заменить слово “записан” на “создан”, как показано ниже:' ИмпортСчета Макрос' Макрос создан 03.01.2005 (Александр Журавлев)'' Сочетание клавиш: Ctrl+и
Ниже приведен полный код исправленного и оптимизированного макроса.Sub ИмпортСчетаИсправленный()'' ИмпортСчета Макрос' Макрос создан 03.01.2005 (Александр Журавлев)'' Сочетание клавиш: Ctrl+и' Workbooks.OpenText Filename:= _ "C:\Счет.txt", Origin:=1251, StartRow:=1, _ DataType:=xlDelimited, TextQualifier:= xlDoubleQuote, _ ConsecutiveDelimiter:=False, Tab:=False, Semicolon:= _ False, Comma:=True, Space:=False, Other:=False, _ FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), _ Array(4, 1), Array(5, 1), Array(6, 1), Array(7, 1)), _ TrailingMinusNumbers:=True ' Найти последнюю строку с данными
Знакомство с Visual Basic for Applications Глава 2 93
FinalRow = Range("A65536").End(xlUp).Row TotalRow = FinalRow + 1 ' Создание итоговой строки Range("A" & TotalRow).Value = "Всего" Range("E" & TotalRow).Resize(1, 3).FormulaR1C1 = _ "=SUM(R2C:R[-1]C)" Rows("1:1").Font.Bold = True Rows(TotalRow & ":" & TotalRow).Font.Bold = True Cells.Columns.AutoFitEnd Sub
Следующий шагВ этой главе были рассмотрены основы синтаксиса языка программироваY
ния Visual Basic for Applications, использование справочной системы, средств
отладки, а также несколько советов по исправлению и оптимизации автомаY
тически сгенерированного кода.
Следующая глава посвящена более детальному изучению диапазонов ячеек.
3Объект Range ..................................95Обращение к диапазону ячеекс помощью указания адресаего верхнего левого инижнего правого угла ...................96Обращение к диапазонуячеек, расположенному надругом рабочем листе ..................97Обращение к диапазону ячеекс помощью указания егоотносительного адреса................ 98Обращение к диапазону ячеекс помощью свойства Cells.............99Обращение к диапазону ячеекс помощью свойства Offset........100Изменение размерадиапазона ячеек с помощьюсвойства Resize ..............................101Обращение к диапазону ячеекс помощью свойств Columns иRows ................................................ 102Объединение диапазоновячеек с помощью методаUnion................................................ 103Создание нового диапазонаячеек из пересекающихсядиапазонов с помощьюметода Intersect ............................ 103Проверка пустых ячеек спомощью функции IsEmpty .......104Обращение к диапазону ячеекс помощью свойстваCurrentRegion................................ 105Обращение к диапазонунесмежных ячеек с помощьюколлекции Areas...........................108Следующий шаг............................ 109
Глава 3
Ðàáîòàñ äèàïàçîíîì ÿ÷ååê
Диапазон ячеек представляет соY
бой любое их объединение в пределах
одного рабочего листа. Примерами
диапазона ячеек являются ячейка,
строка, столбец и т.п. Объект
Range YYYY один из наиболее популярY
ных объектов Excel VBA.
В этой главе будут рассмотрены
различные способы обращения к диаY
пазону ячеек в пределах как одного, так
и нескольких рабочих листов, объедиY
нение диапазонов ячеек, а также создаY
ние нового диапазона ячеек из неY
скольких пересекающихся диапазонов.
Объект RangeРассмотрим следующую иерархию
объектов Excel:
Application Workbook WorksheetRange
Объект Range является свойством
объекта Worksheet. Обе приведенY
ные ниже строки кода выполняют
одно и то же действие, если рабочий
лист Worksheets(1) является акY
тивным рабочим листом.
Range("A1")Worksheets(1).Range("A1")
Таким образом, существует неY
сколько способов обращения к диаY
пазону ячеек. Range("A1") является
наиболее распространенным из них в
основном за счет того, что это станY
дартный способ обращения к диапаY
зону ячеек, использующийся средстY
96 Часть I Первые шаги
вом записи макросов. Приведенные ниже строки кода полностью эквиваY
лентны:
Range("D5")[D5]Range("B3").Range("C3")Cells(5, 4)Range("A1").Offset(4, 3)Range("МойДиапазон") 'при условии что МойДиапазон - имя ячейки D5
Более подробно различные способы обращения к диапазону ячеек расY
сматриваются далее в этой главе.
Обращение к диапазону ячеек с помощьюуказания адреса его верхнего левого и нижнегоправого угла
Существует два различных синтаксиса команды Range. Согласно первому
из них обращение к диапазону ячеек осуществляется путем указания его полY
ного адреса, как это принято в формулах Excel:
Range("A1:B5").Select
Согласно второму синтаксису обращение к диапазону ячеек осуществляетY
ся путем указания адреса его верхнего левого и нижнего правого угла, как поY
казано ниже:
Range("A1", "B5").Select
Вместо адреса любого из углов можно подставить имя диапазона ячеек,
функцию Cells, а также свойство ActiveCell. В следующей строке кода
осуществляется выделение прямоугольного диапазона ячеек, в верхнем левом
углу которого находится ячейка A1, а в нижнем правом углу YYYY активная ячейка:
Range("A1", ActiveCell).Select
А вот как выделить диапазон ячеек, в верхнем левом углу которого нахоY
дится активная ячейка, а в нижнем правом углу YYYY ячейка, находящаяся на
5 строк ниже и на 2 столбца правее активной ячейки:
Range(ActiveCell, ActiveCell.Offset(5, 2)).Select
Сокращенная форма обращения к диапазону ячеек
Сокращенная форма обращения к диапазону ячеек предполагает использоY
вание квадратных скобок ([]), как показано в табл. 3.1.
Именованные диапазоны ячеек
Именованные диапазоны ячеек можно использовать не только на рабочих
листах и в формулах Excel, но также и в VBA.
Ниже приведен пример обращения к именованному диапазону ячеек
МойДиапазон на рабочем листе Лист1:
Worksheets("Лист1").Range("МойДиапазон").Select
Работа с диапазоном ячеек Глава 3 97
Обратите внимание, что имя диапазона ячеек взято в кавычки. Это отличиY
тельная особенность использования именованных диапазонов в VBA. Без каY
вычек Excel воспримет имя диапазона ячеек как объявленную в макросе переY
менную. Единственное исключение касается сокращенной формы обращения
к диапазону ячеек, которая не предусматривает заключение имени диапазона
в кавычки.
Таблица 3.1. Сокращенная форма обращения к диапазону ячеек
Стандартная форма Сокращенная форма
Range("D5") [D5]
Range("A1:D5") [A1:D5]
Range("A1:D5", "G6:I17") [A1:D5, G6:I17]
Range("МойДиапазон") [МойДиапазон]
Обращение к диапазону ячеек, расположенномуна другом рабочем листе
Переключение между рабочими листами может существенно замедлить
выполнение кода макроса. Чтобы избежать этого, можно обратиться непоY
средственно к объекту Worksheet, как показано ниже:
Worksheets("Лист1").Range("A1")
В приведенном выше коде происходит обращение к рабочему листу Лист1,
даже если активным рабочим листом на данный момент является лист Лист2.
Чтобы обратиться к диапазону ячеек в другой рабочей книге, воспользуйY
тесь объектами Workbook, Worksheet и Range, как показано ниже:
Workbooks("Счета.xls").Worksheets("Лист1").Range("A1")
Будьте внимательны, используя свойство Range в качестве аргумента друY
гого свойства Range. В подобных случаях необходима полная идентификация
диапазона ячеек. Предположим, что активным рабочим листом является лист
Лист1, а суммирование данных производится на листе Лист2 так, как покаY
зано ниже:
WorksheetFunction.Sum(Worksheets("Лист2").Range(Range("A1"), _Range("A7")))
Приведенная выше строка кода не будет выполняться, поскольку Excel не
распространит ссылку на объект Worksheet на вложенные объекты Range.
Чтобы исправить ситуацию, можно поступить так:
WorksheetFunction.Sum(Worksheets("Лист2").Range(Worksheets( _"Лист2").Range("A1"), Worksheets("Лист2").Range("A7")))
Однако еще лучше упростить эту достаточно длинную строку кода с
помощью конструкции With...End, позволяющей заменить выражения
98 Часть I Первые шаги
Worksheets("Лист2").Range более короткой формой .Range, как поY
казано ниже:
With Worksheets("Лист2") WorksheetFunction.Sum(.Range(.Range("A1"), .Range("A7")))End With
Обращение к диапазону ячеек с помощьюуказания его относительного адреса
Обычно объект Range выступает в качестве свойства рабочего листа. ВмеY
сте с тем, он может быть свойством другого объекта Range, внося неразбериху
в и без того непростой программный код. Рассмотрим пример:
Range("B5").Range("C3").Select
В результате выполнения приведенного выше кода выделяется ячейка D7.
Чтобы понять, почему так происходит, рассмотрим ячейку C3. Ячейка C3 расY
положена на две строки ниже и на два столбца правее ячейки A1. Однако в
указанном выше коде точкой отсчета является ячейка B5. Другими словами,
VBA выделит ячейку, которая находится на том же смещении относительно
ячейки B5, что и ячейка C3 относительно ячейки A1 (на две строки ниже и на
два столбца правее), а именно D7.
Подобный стиль записи программного кода является весьма неинтуитивY
ным. На первый взгляд указанные в строке кода адреса ячеек не имеют ни маY
лейшего отношения к адресу выделяемой ячейки!
Тем не менее, данный синтаксис может пригодиться при обращении к
ячейке, расположенной на определенном смещении относительно активной
ячейки. К примеру, в результате выполнения приведенной ниже строки кода
выделяется ячейка, расположенная на 3 строки ниже и на 4 столбца правее теY
кущей активной ячейки:
Selection.Range("E4").Select
Аналогичного результата (с применением куда более понятного синтаксиY
са) можно добиться путем использования свойства Offset, которое рассматY
ривается далее в этой главе.
Зачем же нужно знать о существовании такого неудобного способа обраY
щения к диапазону ячеек? Дело в том, что именно он пришелся ‘‘по душе’’
средству записи макросов. Ниже приведена одна из строк кода, сгенерированY
ных при записи макроса импорта счета с использованием относительных ссыY
лок (см. главу 1, ‘‘Excel и VBA YYYY гремучая смесь’’):
ActiveCell.Offset(0, 4).Range("A1").Select
Выполнение этого кода приведет к выделению ячейки, соответствующей
ячейке A1 с учетом смещения относительно активной ячейки на 4 столбца
вправо.
Работа с диапазоном ячеек Глава 3 99
Обращение к диапазону ячеек с помощьюсвойства Cells
Свойство Cells используется для обращения ко всем ячейкам объекта
Range, будь то целый рабочий лист или определенный диапазон ячеек.
К примеру, результатом выполнения приведенной ниже строки кода является
выделение всех ячеек активного рабочего листа:
Cells.Select
Использование свойства Cells вместе с объектом Range выглядит избыY
точным:
Range("A1:D5").Cells
Что делает объект Cells действительно полезным, так это его свойство
Item, которое позволяет обратиться к любой ячейке диапазона.
Ниже приведен синтаксис использования свойства Item с объектом Cells:
Cells.Item(Строка, Столбец)
Идентификацию строки разрешается проводить только с помощью числоY
вого значения, а идентификацию столбца YYYY с помощью числового или строY
кового значения. В обеих приведенных ниже строках кода осуществляется обY
ращение к ячейке C5:
Cells.Item(5, "C")Cells.Item(5, 3)
Поскольку свойство Item является свойством по умолчанию объекта
Range, справедлива следующая сокращенная запись:
Cells(5, "C")Cells(5, 3)
Возможность использования числовых значений при указании параметров
будет по достоинству оценена при создании циклов. Для выделения ячейки
средство записи макросов применяет выражение наподобие Range("A1").Select, а для выделения диапазона ячеек YYYY Range("A1:C5").Select.
Следующие строки выдержаны в стиле автоматически сгенерированного кода:
FinalRow = Range("A65536").End(xlUp).RowFor i = 1 To FinalRow Range("A" & i & ":E" & i).Font.Bold = TrueNext i
В результате использования ‘‘недружелюбного’’ синтаксиса цикл, выдеY
ляющий ячейки в столбцах A–E с помощью утолщения шрифта, оказался
весьма сложным для восприятия. Попробуем записать его несколько иначе:
FinalRow = Cells(65536, 1).End(xlUp).RowFor i = 1 To FinalRow Cells(i, "A").Resize(, 5).Font.Bold = TrueNext i
Использование свойств Cells и Resize вместо адреса диапазона ячеек
делает код цикла более наглядным.
100 Часть I Первые шаги
Использование свойства Cells в качестве параметрасвойства Range
Свойство Cells можно использовать в качестве параметра свойства Range.
Приведенная ниже строка кода описывает диапазон ячеек A1:E5:
Range(Cells(1, 1), Cells(5, 5))
Применение подобного подхода оправдано в случае необходимости исY
пользования переменных, как в предыдущем примере кода цикла.
Обращение к диапазону ячеекс помощью свойства Offset
Свойство Offset используется средством записи макросов при генерироY
вании кода в режиме относительных ссылок. Это свойство позволяет обраY
щаться к ячейке с помощью относительного адреса, отсчитываемого от адреса
активной ячейки.
Ниже приведен синтаксис использования свойства Offset:
Range.Offset(СмещениеПоСтрокам, СмещениеПоСтолбцам)
Чтобы обратиться к ячейке F5 при условии, что текущей активной ячейкой
является ячейка A1, используйте выражение
Range("A1").Offset(RowOffset:=4, ColumnOffset:=5)
или его сокращенную форму
Range("A1").Offset(4, 5)
Отсчет адресов ячеек ведется с адреса ячейки A1. Сама ячейка A1 при этом
не учитывается.
Одна из замечательных особенностей свойства Offset заключается в отY
сутствии необходимости указывать оба параметра одновременно. Чтобы обраY
титься к ячейке, расположенной на один столбец правее ячейки A1, испольY
зуйте любое из следующих выражений:
Range("A1").Offset(ColumnOffset:=1)Range("A1").Offset(, 1)
А вот как обратиться к ячейке, расположенной на одну строку выше
ячейки B2:
Range("B2").Offset(RowOffset:=-1)Range("B2").Offset(-1)
Рассмотрим таблицу с двумя столбцами, в одном из которых перечислены проY
дукты питания, а в другом YYYY их запасы. Чтобы найти продукт, запасы которого
подошли к концу, и отметить это путем размещения в следующей ячейке слова
‘‘ПОПОЛНИТЬ’’, можно воспользоваться следующим макросом:
Set Rng = Range("B1:B16").Find(What:="0", LookAt:=xlWhole, _LookIn:=xlValues)Rng.Offset(, 1).Value = "ПОПОЛНИТЬ"
Работа с диапазоном ячеек Глава 3 101
Результат выполнения макроса показан на рис. 3.1.
Свойство Offset позволяет смещать не только отдельные ячейки, но даже
целые диапазоны. Приведенная ниже строка кода смещает диапазон ячеек
A1:C3 на одну строку вниз и на один столбец правее так, что он переходит в
диапазон ячеек B2:D4 (рис. 3.2).
Range("A1:C3").Offset(1, 1)
Рис. 3.1. Результат выполнения мак&роса, находящего продукты с ис&текшими запасами
Рис. 3.2. Сдвиг диапазона ячеек с помо&щью команды Range("A1:C3").Offset(1, 1).Select
Изменение размера диапазона ячеек с помощьюсвойства Resize
Свойство Resize позволяет изменять размер диапазона ячеек, используя в
качестве отправной точки текущую активную ячейку.
Ниже приведен синтаксис использования свойства Resize:
Range.Resize(КоличествоСтрок, КоличествоСтолбцов)
Чтобы создать диапазон ячеек B3:D13, используйте выражение
Range("B3").Resize(RowSize:=11, ColumnSize:=3)
или его сокращенную форму
Range("B3").Resize(11, 3)
Как и свойство Offset, свойство Resize не требует указания обоих параY
метров одновременно. Чтобы увеличить размер диапазона ячеек до двух
столбцов, используйте любое из следующих выражений:
Range("B3").Resize(ColumnSize:=2)Range("B3").Resize(, 2)
А вот как увеличить размер диапазона ячеек до двух строк:
Range("B3").Resize(RowSize:=2)Range("B3").Resize(2)
102 Часть I Первые шаги
Возвратимся к таблице с продуктами питания. Чтобы найти продукт, запаY
сы которого заканчиваются, и отметить это путем выделения цветом ячеек
с названием продукта и его запасами, воспользуйтесь следующим макросом
(рис. 3.3):
Set Rng = Range("B1:B16").Find(What:="0", LookAt:=xlWhole, _LookIn:=xlValues)Rng.Offset(, -1).Resize(, 2).Interior.ColorIndex = 15
Рис. 3.3. Изменение размерадиапазона ячеек в действии
Здесь свойство Offset используется для изменения активной ячейки,
а свойство Resize — для увеличения размера диапазона до двух столбцов.
Точно так же можно изменить и размер диапазона, состоящего из нескольY
ких ячеек. К примеру, чтобы увеличить размер именованного диапазона до
двух столбцов, воспользуйтесь следующим выражением:
Range("Продукты").Resize(, 2)
Помните, что параметры свойства Resize обозначают размер целевого
диапазона ячеек, который необходимо создать.
Обращение к диапазону ячеек с помощью свойствColumns и Rows
Свойства Columns и Rows используются для обращения к столбцам и
строкам диапазона ячеек и возвращают соответствующий объект Range.
Ранее мы рассматривали следующую строку кода макроса:
FinalRow = Range("A65536").End(xlUp).Row
В результате ее выполнения переменной FinalRow присваивается номер
последней строки (объект Range), столбец A которой содержит какиеYлибо
данные. Зная номер последней строки с данными, можно создать цикл, поY
очередно обрабатывающий все значащие строки рабочего листа.
Работа с диапазоном ячеек Глава 3 103
Внимание Для корректного использования некоторых свойств объектов Columns и Rows не&обходимо наличие непрерывного диапазона ячеек. К примеру, результат следую&щего выражения будет равен 9, так как подсчет строк будет проведен только попервому диапазону ячеек:
Range("A1:B9, C10:D19").Rows.Count
Если же не группировать несмежные диапазоны ячеек (как показано ниже), то ре&зультат подсчета количества строк будет равен 19:
Range("A1:B9", "C10:D19").Rows.Count
Объединение диапазонов ячеек с помощьюметода Union
Метод Union позволяет объединить два или более несоприкасающихся
диапазона ячеек. Он возвращает временный объект, предназначенный для
манипулирования объединенным диапазоном:
Application.Union(аргумент1 ,аргумент2 ,...)
В результате выполнения приведенного ниже кода два именованных диаY
пазона ячеек будут объединены, заполнены случайными числовыми значеY
ниями и выделены путем утолщения шрифта:
Set UnionRange = Union(Range("Диапазон1"), Range("Диапазон2"))With UnionRange' В англоязычной версии Excel:' .Formula = "=RAND()" .FormulaLocal = "=СЛЧИС()" .Font.Bold = TrueEnd With
Создание нового диапазона ячеекиз пересекающихся диапазонов с помощьюметода Intersect
Метод Intersect возвращает диапазон ячеек, полученный в результате
пересечения нескольких диапазонов:
Application.Intersect(аргумент1 ,аргумент2 ,...)
В результате выполнения приведенного ниже кода будет создан новый диапаY
зон ячеек, полученный в результате пересечения двух существующих диапазонов.
Ячейки нового диапазона выделены цветом, как показано на рис. 3.4.
Set IntersectRange = Intersect(Range("Диапазон1"), _Range("Диапазон2"))IntersectRange.Interior.ColorIndex = 6
104 Часть I Первые шаги
Рис. 3.4. Метод Intersect возвращает диапазон ячеек, по&лученный в результате пересечения нескольких диапазонов
Проверка пустых ячеек с помощьюфункции IsEmpty
Функция IsEmpty возвращает булево значение, определяющее, является
ячейка пустой (True) или нет (False). Ячейка является пустой, если она не
содержит какихYлибо данных (даже символов пробела).
IsEmpty(Ячейка)
На рис. 3.5 показана таблица с несколькими группами данных, разделенY
ными пустой строкой.
Рис. 3.5. Группы данных разделены пустой строкой
С помощью следующего кода проведем поиск пустых строк (точнее, пусY
тых ячеек в столбце A) и выделим цветом их первые 4 ячейки (рис. 3.6):
LastRow = Range("A65536").End(xlUp).RowFor i = 1 To LastRow If IsEmpty(Cells(i, 1)) Then Cells(i, 1).Resize(1, 4).Interior.ColorIndex = 1
Работа с диапазоном ячеек Глава 3 105
End IfNext i
Рис. 3.6. Первые 4 ячейки строк&разделителейвыделены черным цветом
Обращение к диапазону ячеек с помощьюсвойства CurrentRegion
Свойство CurrentRegion возвращает объект, представляющий непреY
рывный диапазон ячеек. С помощью этого свойства можно обратиться к диаY
пазону ячеек, ограниченному по крайней мере одной пустой строкой или одY
ним пустым столбцом:
ДиапазонЯчеек.CurrentRegion
В результате выполнения приведенной ниже строки кода будет выделен
диапазон ячеек A1:D3 — непрерывный диапазон ячеек, включающий в себя
ячейку A1 (рис. 3.7):
Range("A1").CurrentRegion.Select
Рис. 3.7. Используйте свойство CurrentRe-gion для обращения к непрерывному диапа&зону ячеек, включающему в себя текущую ак&тивную ячейку
106 Часть I Первые шаги
Свойство CurrentRegion рекомендуется использовать для обращения к
таблицам, размер которых постоянно меняется.
Практикум
Выделение ячеек, соответствующих определенномукритерию, с помощью метода SpecialCellsДалеко не все пользователи Excel знают о существовании диалогового окнаВыделение группы ячеек (Go To Special). Нажмите клавишу <F5>, чтобы открытьдиалоговое окно Переход (Go To) (рис. 3.8).
Рис. 3.8. Чтобы открыть диалоговое окно Выделение группы ячеек, щелкните на кноп&
ке Выделить
Щелкните на кнопке Выделить (Special) в левом нижнем углу диалогового окнаПереход, чтобы открыть диалоговое окно Выделение группы ячеек (рис. 3.9).Диалоговое окно Выделение группы ячеек позволяет выделить только пустыеячейки, только видимые ячейки или же только ячейки, содержащие формулы.Возможность выделения только видимых ячеек очень полезна при автоматиче&ской фильтрации данных.Возможности диалогового окна Выделение группы ячеек могут быть реализованыс помощью метода VBA SpecialCells. Этот метод позволяет работать с ячейка&ми, соответствующими определенному критерию:ДиапазонЯчеек.SpecialCells(Тип, Значение)Метод SpecialCells имеет два параметра: Тип и Значение (необязательныйпараметр). Тип ячейки может быть описан одной из констант xlCellType:xlCellTypeAllFormatConditionsxlCellTypeAllValidationxlCellTypeBlanksxlCellTypeComments
Работа с диапазоном ячеек Глава 3 107
xlCellTypeConstantsxlCellTypeFormulasxlCellTypeLastCellxlCellTypeSameFormatConditionsxlCellTypeSameValidationxlCellTypeVisible
Рис. 3.9. Диалоговое окно Выделение группы ячеек предлагает широкие возможности повыделению ячеек
Предусмотрено также 4 различных значения ячейки:xlErrorsxlLogicalxlNumbersxlTextValues
В результате выполнения приведенного ниже кода вокруг всех непрерывных диа&пазонов ячеек с условным форматированием будет создана граница. При отсутст&вии таких диапазонов будет выдано сообщение об ошибке:Set rngCond = ActiveSheet.Cells.SpecialCells( _xlCellTypeAllFormatConditions)If Not rngCond Is Nothing Then rngCond.BorderAround xlContinuousEnd If
В таблице, показанной на рис. 3.10, отсутствуют данные в некоторых ячейках.Несмотря на эстетическую привлекательность такого решения, оно сводит на нетвозможность сортировки данных таблицы. Подобный формат принят и в сводныхтаблицах Excel.К счастью, метод SpecialCells позволяет выделить все пустые ячейки в диапа&зоне и заполнить их нужными данными:Sub FillIn() Range("A1").CurrentRegion.SpecialCells( _xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
108 Часть I Первые шаги
Range("A1").CurrentRegion.Value = _Range("A1").CurrentRegion.ValueEnd Sub
Рис. 3.10. Отсутствие данных в некото&
рых ячейках делает невозможной сор&тировку таблицы
В приведенном выше коде выражение Range("A1").CurrentRegion соответст&вует непрерывному диапазону ячеек рабочего листа. Свойство SpecialCellsвозвращает только пустые ячейки этого диапазона. Формула в стиле R1C1 (см. гла&ву 6, “Стиль записи ссылок R1C1”) заполняет каждую пустую ячейку данными изячейки, расположенной на одну строку выше. Вторая строка кода представляетсобой быстрый способ выполнения команд Копирование (Copy) и Специальнаявставка (Paste Special). Результат выполнения кода показан на рис. 3.11.
Рис. 3.11. После выполнения макроса
пустые ячейки в таблице заполнилисьнужными данными
Обращение к диапазону несмежных ячеекс помощью коллекции Areas
Коллекция Areas используется для представления множества диапазонов
несмежных ячеек. Она состоит из объектов Range, соответствующих непрерывY
ным диапазонам ячеек в выделенной области. Если последняя состоит из одного
непрерывного диапазона ячеек, коллекция Areas содержит один объект Range.
Работа с диапазоном ячеек Глава 3 109
Рассмотрим задачу копирования данных о запасах продуктов (ячейки, выY
деленные серым цветом) в другую часть рабочего листа (рис. 3.12).
Рис. 3.12. Содержимое ячеек, выделенных серымцветом, необходимо скопировать в другую частьрабочего листа
Наиболее очевидное решение заключается в создании цикла, поочередно
копирующего значения всех необходимых ячеек. Однако существует и более
эффективный подход (рис. 3.13):
Set NewDestination = ActiveSheet.Range("I1") For Each Rng In Cells.SpecialCells(xlCellTypeConstants, 1).Areas Rng.Copy Destination:=NewDestination Set NewDestination = NewDestination.Offset(Rng.Rows.Count)Next Rng
Рис. 3.13. Коллекция Areas предоставляет возможность эффективного мани&пулирования диапазонами несмежных ячеек
Следующий шагВ следующей главе рассматриваются функции, определенные пользоватеY
лем, а также наиболее распространенные задачи программирования в Excel.
4Создание функций,определенных пользователем .. 111Наиболее распространенныезадачи программированияв Excel ...............................................113Следующий шаг............................140
Глава 4
Ôóíêöèè,îïðåäåëåííûåïîëüçîâàòåëåì
Создание функций,определенныхпользователем
Иногда огромного количества
встроенных функций Excel бывает
недостаточно. В частности, Excel не
содержит готового решения для задаY
чи суммирования значений в ячейках,
выделенных определенным цветом.
Что же делать? Вручную скопироY
вать все нужные ячейки в другую
часть рабочего листа? Или взять
калькулятор и провести подсчет саY
мому? Оба способа отнимают много
времени и не гарантируют отсутствие
ошибок. Одно из возможных решений
заключается в написании процедуY
ры YYYY в конечном итоге, именно проY
цедурам посвящена большая часть
этой книги. Однако единственно праY
вильным решением является создание
функции, определенной пользователем.
VBA позволяет создавать функY
ции, которые могут использоваться
аналогично встроенным функциям
Excel, таким как СУММ (SUM). Чтобы
применить подобную функцию, неY
обходимо знать только ее имя и арY
гументы.
112 Часть I Первые шаги
На заметку Функции, определенные пользователем, должны храниться в стандартных моду&лях. Модули рабочих листов и модуль ЭтаКнига (ThisWorkbook) являются специ&альными модулями. Функция, размещенная в одном из таких модулей, не будетвоспринята Excel как функция, определенная пользователем.
Практикум
Практикум: пример создания и применения функции,определенной пользователемСоздадим функцию, суммирующую значения двух ячеек, и применим ее на рабо&чем листе Excel. С помощью редактора Visual Basic добавьте к проекту новый модуль и введите внего текст функции суммирования значений двух ячеек Add (см. ниже). Эта функ&ция принимает два аргумента: Add(Number1, Number2)
Здесь Number1 — это первое слагаемое, а Number2 — второе: Function Add(Number1, Number2) As IntegerAdd = Number1 + Number2End Function
Попытаемся разобраться в приведенном выше коде: имя функции — Add; аргументы функции Add — Number1 и Number2 — перечислены в скобках по&
сле ее имени; возвращаемый функцией Add результат является целым числом (As Integer) и вычисляется по формуле Add = Number1 + Number2.
Чтобы применить функцию Add на рабочем листе, выполните следующие действия.1. Введите любые два числа в ячейки A1 и A2.
2. Выделите ячейку A3.
3. Нажмите комбинацию клавиш <Shift+F3> или выберите команду меню ExcelВставка Функция (Insert Function), чтобы открыть диалоговое окно мастерафункций.
4. В раскрывающемся списке Категория (Or select a category) выберите значениеОпределенные пользователем (User Defined).
5. Выберите функцию Add и щелкните на кнопке OK.
6. В качестве первого аргумента укажите ячейку A1.
7. В качестве второго аргумента укажите ячейку A2.
8. Щелкните на кнопке OK.
Поздравляем! Вы только что создали собственную функцию и применили ее нарабочем листе.
Функции, определенные пользователем Глава 4 113
Большинство функций, используемых на рабочих листах, могут с успехом
применяться в VBA, и наоборот. Тем не менее, VBA требует, чтобы функция,
определенная пользователем (Add), вызывалась из процедуры (Addition),
как показано ниже:
Sub Addition ()Dim Total as IntegerTotal = Add (1, 10) 'вызов функции, определенной пользователемMsgBox "Ответ: " & TotalEnd Sub
Наиболее распространенные задачипрограммирования в Excel
В следующих разделах этой главы рассматриваются решения наиболее
распространенных задач, встречающихся при повседневном программиY
ровании в Excel.
Вывод имени файла текущей рабочей книги в ячейкеПредназначение следующей функции заключается в выводе имени файла
активной рабочей книги в ячейке, как показано на рис. 4.1:
MyName()
Рис. 4.1. Функции MyName и MyFullName используются для выводав ячейке имени и полного имени файла активной рабочей книги,соответственно
Функция MyName не имеет аргументов.
Function MyName() As String MyName = ThisWorkbook.NameEnd Function
Вывод полного имени файла текущей рабочейкниги в ячейке
Предназначение следующей функции заключается в выводе имени файла
активной рабочей книги в ячейке (см. рис. 4.1):
MyFullName()
Функция MyFullName не имеет аргументов.
Function MyFullName() As String MyFullName = ThisWorkbook.FullNameEnd Function
114 Часть I Первые шаги
Как проверить, открыта ли рабочая книга
Иногда требуется проверить, открыта ли определенная рабочая книга.
Следующая функция возвращает значение True, если рабочая книга открыта,
и False YYYY в противном случае:
BookOpen(Bk)
Функция BookOpen имеет один аргумент:
Bk — имя файла рабочей книги.
Function BookOpen(Bk As String) As Boolean Dim T As Excel.Workbook'Удалить информацию об ошибках. Err.Clear'Если при выполнении кода возникнет ошибка, она будет пропущена. On Error Resume Next Set T = Application.Workbooks(Bk) BookOpen = Not (T Is Nothing)'Если рабочая книга открыта, переменная T будет содержать'объект рабочей книги и, таким образом, не будет пустой. Err.Clear On Error GoTo 0End Function
Ниже приведен пример использования функции BookOpen:
Sub OpenAWorkbook() Dim IsOpen As Boolean Dim BookName As String BookName = "Chapter 4 samples.xls"'Вызов функции BookOpen - не забудьте указать значение параметра. IsOpen = BookOpen(BookName) If IsOpen Then MsgBox BookName & " открыта!!" Else Workbooks.Open (BookName) End IfEnd Sub
Проверка существования рабочего листа в открытой книге
Следующая функция возвращает значение True, если указанный рабочий
лист существует, и False — в противном случае. Подобная проверка возможY
на только при условии, что соответствующая рабочая книга открыта.
SheetExists(SName, WBName)
Функция SheetExists имеет 2 аргумента:
SName — имя рабочего листа;
WBName — имя рабочей книги (необязательный параметр).
Function SheetExists(SName As String, Optional WBName As _String) As Boolean Dim WS As Worksheet Dim WB As Workbook
Функции, определенные пользователем Глава 4 115
On Error Resume Next'Проверить, задано ли имя файла рабочей книги. If Len(WBName) > 0 Then Set WB = Workbooks(WBName)'Завершить выполнение, если рабочая книга не открыта. If WB Is Nothing Then Exit Function Else Set WB = ActiveWorkbook End If Set WS = WB.Sheets(SName)'Если рабочий лист существует, переменная WS хранит'соответствующий объект. Если рабочий лист отсутствует,'переменная WS хранит значение Nothing.
'Если переменная WS НЕ хранит Nothing, значение'выражения Not (WS Is Nothing) будет равно True. SheetExists = Not (WS Is Nothing)End Function
Ниже приведен пример использования функции SheetExists:
Sub CheckForSheet() Dim ShtExists As Boolean ShtExists = SheetExists("Sheet9")'Обратите внимание, что функции был передан только один параметр. If ShtExists Then MsgBox "Рабочий лист существует!" Else MsgBox "Рабочий лист НЕ существует!" End IfEnd Sub
Подсчет количества файлов рабочих книг в папке
Следующая функция просматривает папку (и при необходимости ее
подпапки) и, в зависимости от переданных параметров, подсчитывает лиY
бо общее количество хранящихся в ней файлов рабочих книг Excel, либо
количество файлов рабочих книг Excel, имена которых включают в себя
заданную строку.
NumFilesInCurDir(LikeText, Subfolders)
Функция NumFilesInCurDir имеет 2 аргумента:
LikeText — строка, которую должно включать в себя имя файла рабоY
чей книги (необязательный параметр);
Subfolders — булево значение, определяющее необходимость провеY
дения поиска в подпапках; по умолчанию поиск в подпапках не провоY
дится (False) (необязательный параметр).
Function NumFilesInCurDir(Optional LikeText As String, _Optional Subfolders As Boolean = False) With Application.FileSearch .NewSearch'Строка, которую должно включать в себя имя файла рабочей книги. If Len(LikeText) > 0 Then .Filename = LikeText
116 Часть I Первые шаги
End If'Выбрать тип файла - рабочие книги Excel. .FileType = msoFileTypeExcelWorkbooks'Указать на необходимость проведения поиска в текущей папке. .LookIn = CurDir'Указать на необходимость проведения поиска в подпапках. .SearchSubFolders = Subfolders .Execute NumFilesInCurDir = .FoundFiles.Count End WithEnd Function
Ниже приведен пример использования функции NumFilesInCurDir:
Sub CountMyWkbks() Dim MyFiles As Integer MyFiles = NumFilesInCurDir("Глава*", True) MsgBox MyFiles & " файл(ов) найден(о)"End Sub
Получение имени пользователя,зарегистрировавшегося в системе
Следующая функция возвращает имя пользователя, зарегистрировавшегоY
ся в системе. Вместе с функцией, возвращающей постоянное значение даты и
времени (рассматривается далее в этой главе), она может быть применена для
создания файла журнала. Кроме того, с ее помощью можно узнать имеющиеся
у пользователя права на доступ к рабочей книге.
WinUsername()
Функция WinUsername не имеет аргументов.
На заметку Функция WinUsername использует функции интерфейса прикладного програм&мирования (API), который рассматривается в главе 22, “Интерфейс прикладногопрограммирования (API) Windows”.
Следующий фрагмент кода должен быть помещен в верхнюю часть модуля:
Private Declare Function WNetGetUser Lib "mpr.dll" Alias _ "WNetGetUserA" (ByVal lpName As String, ByVal lpUserName _ As String, lpnLength As Long) As LongPrivate Const NO_ERROR = 0Private Const ERROR_NOT_CONNECTED = 2250&Private Const ERROR_MORE_DATA = 234Private Const ERROR_NO_NETWORK = 1222&Private Const ERROR_EXTENDED_ERROR = 1208&Private Const ERROR_NO_NET_OR_BAD_PATH = 1203&
Текст функции WinUsername может быть помещен в любую часть модуля
при условии, что он будет находиться ниже объявлений Private:
Функции, определенные пользователем Глава 4 117
Function WinUsername() As String'Переменные: Dim strBuf As String, lngUser As Long, strUn As String'Подготовка строковой переменной для использования в функции API. strBuf = Space$(255)'Использование функции WNetGetUser, возвращающей имя пользователя.'Сохранение возвращенного функцией кода в переменной lngUser. lngUser = WNetGetUser("", strBuf, 255)'Если выполнение функции API прошло успешно, If lngUser = NO_ERROR Then'убрать пробелы из переменной strBuf и возвратить результат'выполнения функции WinUsername. strUn = Left(strBuf, InStr(strBuf, vbNullChar) - 1) WinUsername = strUn Else'Ошибка, завершение работы функции. WinUsername = "Ошибка :" & lngUser End IfEnd Function
Ниже приведен пример использования функции WinUsername:
Sub CheckUserRights() Dim UserName As String UserName = WinUsername Select Case UserName Case "Administrator" MsgBox "Полные права" Case "Guest" MsgBox "Вы не можете вносить изменения в рабочую книгу" Case Else MsgBox "Ограниченные права" End SelectEnd Sub
Получение даты и времени последнегосохранения рабочей книги
Следующая функция возвращает дату и время последнего сохранения раY
бочей книги, как показано на рис. 4.2.
LastSaved(FullPath)
Рис. 4.2. Функция LastSaved возвращает дату и время последнегосохранения рабочей книги
Функция LastSaved имеет один аргумент:
FullPath — полный путь к файлу рабочей книги.
118 Часть I Первые шаги
Function LastSaved(FullPath As String) As Date LastSaved = FileDateTime(FullPath)End Function
Получение постоянного значения даты и времени
Поскольку значение, возвращаемое функцией Now, обновляется при кажY
дом открытии рабочей книги, его не рекомендуется использовать для указаY
ния даты и времени создания или изменения рабочей книги. Несмотря на то
что следующая функция основана на функции Now, ее результат куда менее
динамичен, так как он обновляется только при обновлении соответствующей
ячейки (рис. 4.3).
DateTime()
Рис. 4.3. Функция DateTime возвращает постоянное значение датыи времени
Функция DateTime не имеет аргументов.
На заметку Результат выполнения функции DateTime должен быть размещен в соответст&вующим образом отформатированной ячейке.
Function DateTime() DateTime = NowEnd Function
Проверка адреса электронной почты
Следующая функция проверяет корректность написания адреса электронY
ной почты (рис. 4.4).
IsEmailValid(StrEmail)
Рис. 4.4. Проверка корректности написания адреса электронной почты
Функции, определенные пользователем Глава 4 119
Внимание Функция IsEmailValid проверяет только корректность написания адреса элек&тронной почты, а не факт его существования.
Функция IsEmailValid имеет один аргумент:
StrEmail — адрес электронной почты.
Function IsEmailValid(strEmail As String) As Boolean Dim strArray As Variant Dim strItem As Variant Dim i As Long Dim c As String Dim blnIsItValid As Boolean blnIsItValid = True'Подсчет количества знаков @ в строке. i = Len(strEmail) - Len(Application.Substitute(strEmail, _"@", ""))'Если знаков @ больше, чем 1, адрес электронной почты неверный. If i <> 1 Then IsEmailValid = False: Exit Function ReDim strArray(1 To 2)'Текст слева и справа от знака @ помещается в 2 разные переменные. strArray(1) = Left(strEmail, InStr(1, strEmail, "@", 1) - 1) strArray(2) = Application.Substitute(Right(strEmail, _Len(strEmail) - Len(strArray(1))), "@", "") For Each strItem In strArray'Если хотя бы одна из переменных оказалась пустой,'адрес электронной почты неверный. If Len(strItem) <= 0 Then blnIsItValid = False IsEmailValid = blnIsItValid Exit Function End If'Проверка использования только допустимых символов. For i = 1 To Len(strItem)'Чтобы упростить проверку, все символы переводятся в нижний регистр. c = LCase(Mid(strItem, i, 1)) If InStr("abcdefghijklmnopqrstuvwxyz_-.", c) <= 0 _ And Not IsNumeric(c) Then blnIsItValid = False IsEmailValid = blnIsItValid Exit Function End If Next i'Проверка, что первым символом строк слева и справа от @'не является символ точки (.). If Left(strItem, 1) = "." Or Right(strItem, 1) = "." Then blnIsItValid = False IsEmailValid = blnIsItValid Exit Function End If Next strItem
120 Часть I Первые шаги
'Проверка, что в строке справа от @ есть символ точки (.). If InStr(strArray(2), ".") <= 0 Then blnIsItValid = False IsEmailValid = blnIsItValid Exit Function End If i = Len(strArray(2)) - InStrRev(strArray(2), ".")'Проверка длины имени домена. If i <> 2 And i <> 3 And i <> 4 Then blnIsItValid = False IsEmailValid = blnIsItValid Exit Function End If
'Проверка отсутствия двух символов точки подряд (..). If InStr(strEmail, "..") > 0 Then blnIsItValid = False IsEmailValid = blnIsItValid Exit Function End If
IsEmailValid = blnIsItValidEnd Function
Суммирование значений ячеек на основе цвета заливки
Следующая функция суммирует значения ячеек на основе цвета заливки.
SumByColor(CellColor, SumRange)
На заметку Функция SumByColor не поддерживает ячейки с условным форматированием.Таким образом, наличие заливки является ключевым условием, необходимым длявыполнения функции.
Функция SumByColor имеет 2 аргумента:
CellColor — адрес ячейки, имеющей заливку нужного цвета;
SumRange — диапазон ячеек, в котором необходимо провести поиск
ячеек с определенным цветом заливки.
Function SumByColor(CellColor As Range, SumRange As Range) Dim myCell As Range Dim iCol As Integer Dim myTotal'Определение цвета заливки ячейки. iCol = CellColor.Interior.ColorIndex'Просмотр ячеек в указанном диапазоне. For Each myCell In SumRange'Если цвет заливки ячейки совпадает с указанным цветом, If myCell.Interior.ColorIndex = iCol Then'добавить ее значение к сумме. myTotal = WorksheetFunction.Sum(myCell) + myTotal End If Next myCell
Функции, определенные пользователем Глава 4 121
SumByColor = myTotalEnd Function
Пример использования функции SumByColor на рабочем листе можно
увидеть на рис. 4.5.
Рис. 4.5. Пример суммирования значений ячеек на основе цвета заливки
Получение имени и номера цвета заливки ячейки
Следующая функция возвращает имя и номер цвета заливки ячейки:
CellColor(myCell, ColorIndex)
На заметку Функция CellColor не поддерживает ячейки с условным форматированием. На&личие заливки является ключевым условием, необходимым для выполненияфункции.
Функция CellColor имеет 2 аргумента:
myCell — адрес ячейки;
ColorIndex — если данный необязательный параметр имеет значение
True, функция CellColor возвратит номер цвета заливки ячейки (по
умолчанию функция CellColor возвращает имя цвета заливки).
Function CellColor(myCell As Range, Optional ColorIndex As Boolean) Dim myColor As String, IndexNum As Integer Select Case myCell.Interior.ColorIndex Case 1 myColor = "Черный" IndexNum = 1 Case 2 myColor = "Белый"
122 Часть I Первые шаги
IndexNum = 2 Case 3 myColor = "Красный" IndexNum = 3 Case 4 myColor = "Ярко-зеленый" IndexNum = 4 Case 5 myColor = "Синий" IndexNum = 5 Case 6 myColor = "Желтый" IndexNum = 6 Case 7 myColor = "Лиловый" IndexNum = 7 Case 8 myColor = "Бирюзовый" IndexNum = 8 Case 9 myColor = "Темно-красный" IndexNum = 9 Case 10 myColor = "Зеленый" IndexNum = 10 Case 11 myColor = "Темно-синий" IndexNum = 11 Case 12 myColor = "Коричнево-зеленый" IndexNum = 12 Case 13 myColor = "Фиолетовый" IndexNum = 13 Case 14 myColor = "Сине-зеленый" IndexNum = 14 Case 15 myColor = "Серый 25%" IndexNum = 15 Case 16 myColor = "Серый 50%" IndexNum = 16 Case 33 myColor = "Голубой" IndexNum = 33 Case 34 myColor = "Светло-бирюзовый" IndexNum = 34 Case 35 myColor = "Бледно-зеленый" IndexNum = 35 Case 36 myColor = "Светло-желтый" IndexNum = 36 Case 37 myColor = "Бледно-голубой"
Функции, определенные пользователем Глава 4 123
IndexNum = 37 Case 38 myColor = "Розовый" IndexNum = 38 Case 39 myColor = "Сиреневый" IndexNum = 39 Case 40 myColor = "Светло-коричневый" IndexNum = 40 Case 41 myColor = "Темно-голубой" IndexNum = 41 Case 42 myColor = "Темно-бирюзовый" IndexNum = 42 Case 43 myColor = "Травяной" IndexNum = 43 Case 44 myColor = "Золотистый" IndexNum = 44 Case 45 myColor = "Светло-оранжевый" IndexNum = 45 Case 46 myColor = "Оранжевый" IndexNum = 46 Case 47 myColor = "Сизый" IndexNum = 47 Case 48 myColor = "Серый 40%" IndexNum = 48 Case 49 myColor = "Светло-сизый" IndexNum = 49 Case 50 myColor = "Изумрудный" IndexNum = 50 Case 51 myColor = "Темно-зеленый" IndexNum = 51 Case 52 myColor = "Оливковый" IndexNum = 52 Case 53 myColor = "Коричневый" IndexNum = 53 Case 54 myColor = "Вишневый" IndexNum = 54 Case 55 myColor = "Индиго" IndexNum = 55 Case 56 myColor = "Серый 80%"
124 Часть I Первые шаги
IndexNum = 56 Case Else myColor = "Другой цвет или отсутствие заливки" End Select'Возвратить номер цвета заливки ячейки, если это указано'при вызове функции или невозможно возвратить имя цвета заливки. If ColorIndex = True Or myColor = "Другой цвет или _отсутствие заливки" Then CellColor = IndexNum Else CellColor = myColor End IfEnd Function
Пример использования функции CellColor на рабочем листе показан на
рис. 4.6.
Рис. 4.6. Пример определения имени или номера цвета заливки ячейки спомощью функции CellColor
Получение номера цвета текста в ячейке
Следующая функция возвращает номер цвета текста в ячейке:
TextColor(Rng)
На заметку Функция TextColor не поддерживает ячейки с условным или автоматическимформатированием текста (примером последнего является выделение отрицатель&ных числовых значений красным цветом).
Домашнее задание Изучив функцию TextColor, измените рассматривавшуюся в предыдущем раз&деле функцию CellColor таким образом, чтобы она возвращала имя цвета текстав ячейке.
Функция TextColor имеет один аргумент:
Rng — адрес ячейки.
Function TextColor(Rng As Range) As Long TextColor = Rng.Range("A1").Font.ColorIndexEnd Function
Функции, определенные пользователем Глава 4 125
Подсчет количества уникальных значений
Следующая функция возвращает количество уникальных значений в укаY
занном диапазоне ячеек (рис. 4.7):
NumUniqueValues(Rng)
Рис. 4.7. Пример подсчета количества уникальных значений в диапазонеячеек с помощью функции NumUniqueValues
Функция NumUniqueValues имеет один аргумент:
Rng — адрес диапазона ячеек.
Function NumUniqueValues(Rng As Range) As Long Dim myCell As Range, UniqueVals As New Collection'Произвести пересчет результата при изменении диапазона ячеек. Application.Volatile'Поместить значения всех ячеек диапазона в коллекцию'(в коллекции могут находиться только уникальные значения).'Продолжить выполнение функции при возникновении ошибки'(помещение в коллекцию двух одинаковых элементов). On Error Resume Next For Each myCell In Rng UniqueVals.Add myCell.Value, CStr(myCell.Value) Next myCell'Вернуться к стандартному режиму обработки ошибок. On Error GoTo 0'Возвратить количество элементов в коллекции. NumUniqueValues = UniqueVals.CountEnd Function
Удаление повторяющихся значений из диапазона ячеек Следующая функция удаляет повторяющиеся значения из указанного диаY
пазона ячеек:
UniqueValues(OrigArray)
126 Часть I Первые шаги
Функция UniqueValues имеет один аргумент:
OrigArray — массив, из которого необходимо удалить повторяющиеY
ся значения.
Следующий фрагмент кода должен быть помещен в верхнюю часть модуля:
Const ERR_BAD_PARAMETER = "Не указан исходный массив"Const ERR_BAD_TYPE = "Неверный тип"Const ERR_BP_NUMBER = 20000Const ERR_BT_NUMBER = 20001
Текст функции UniqueValues может быть помещен в любую часть модуY
ля при условии, что он будет находиться ниже объявлений Const:
Public Function UniqueValues(ByVal OrigArray As Variant) As Variant Dim vAns() As Variant Dim lStartPoint As Long Dim lEndPoint As Long Dim lCtr As Long, lCount As Long Dim iCtr As Integer Dim col As New Collection Dim sIndex As String Dim vTest As Variant, vItem As Variant Dim iBadVarTypes(4) As Integer
'Завершить выполнение функции, если массив содержит'элемент одного из следующих типов. iBadVarTypes(0) = vbObject iBadVarTypes(1) = vbError iBadVarTypes(2) = vbDataObject iBadVarTypes(3) = vbUserDefinedType iBadVarTypes(4) = vbArray
'Проверить, является ли переданное функции значение массивом. If Not IsArray(OrigArray) Then Err.Raise ERR_BP_NUMBER, , ERR_BAD_PARAMETER Exit Function End If
lStartPoint = LBound(OrigArray) lEndPoint = UBound(OrigArray) For lCtr = lStartPoint To lEndPoint vItem = OrigArray(lCtr)'Проверить допустимость значений элементов массива. For iCtr = 0 To UBound(iBadVarTypes) If VarType(vItem) = iBadVarTypes(iCtr) Or _ VarType(vItem) = iBadVarTypes(iCtr) + vbVariant Then Err.Raise ERR_BT_NUMBER, , ERR_BAD_TYPE Exit Function End If Next iCtr
'Добавить элемент в коллекцию, используя его значение'в качестве индекса. При добавлении в коллекцию'уже существующего элемента возникнет ошибка. sIndex = CStr(vItem)
Функции, определенные пользователем Глава 4 127
'Автоматически добавить первый элемент в коллекцию. If lCtr = lStartPoint Then col.Add vItem, sIndex ReDim vAns(lStartPoint To lStartPoint) As Variant vAns(lStartPoint) = vItem Else On Error Resume Next col.Add vItem, sIndex If Err.Number = 0 Then lCount = UBound(vAns) + 1 ReDim Preserve vAns(lStartPoint To lCount) vAns(lCount) = vItem End If End If Err.Clear Next lCtr
UniqueValues = vAnsEnd Function
Ниже приведен пример использования функции UniqueValues:
Function NoDupsArray(Rng As Range) As Variant Dim arr1() As Variant If Rng.Columns.Count > 1 Then Exit Function arr1 = Application.Transpose(Rng) arr1 = UniqueValues(arr1) NoDupsArray = Application.Transpose(arr1)End Function
Результат применения функции NoDupsArray на рабочем листе показан
на рис. 4.8.
Рис. 4.8. Создание диапазона ячеек, содержащего только уникальные зна&чения из исходного диапазона
128 Часть I Первые шаги
Поиск первой непустой ячейки в диапазоне
Следующая функция возвращает значение первой непустой ячейке в укаY
занном диапазоне:
FirstNonZeroLength(Rng)
Функция FirstNonZeroLength имеет один аргумент:
Rng — адрес диапазона ячеек.
Function FirstNonZeroLength(Rng As Range) Dim myCell As Range FirstNonZeroLength = 0# For Each myCell In Rng If Not IsNull(myCell) And myCell <> "" Then FirstNonZeroLength = myCell.Value Exit Function End If Next myCell FirstNonZeroLength = myCell.ValueEnd Function
На рис. 4.9 показан пример использования функции FirstNonZeroLengthна рабочем листе.
Рис. 4.9. Пример нахождения значения первой непустой ячейки вдиапазоне с помощью функции FirstNonZeroLength
Замена нескольких символов в строке Следующая функция используется для замены нескольких символов в
строке (рис. 4.10):
MSubstitute(trStr, frStr, toStr)
Рис. 4.10. Пример замены нескольких символов в строке с помощьюфункции MSubstitute
Функции, определенные пользователем Глава 4 129
Функция MSubstitute имеет 3 аргумента:
trStr — исходная строка;
frStr — символы строки, подлежащие замене;
toStr — символыYзаменители.
Внимание Функция MSubstitute предполагает, что длина строки toStr совпадает с дли&ной строки frStr. Если длина строки toStr меньше длины строки frStr, не&достающие символы считаются пустыми ("").Функция MSubstitute учитываеттакже регистр символов. Так, чтобы заменить все вхождения в строку буквы “А”,в строке frStr следует указать символы а и A. Замена одного символа двумя неподдерживается. Результатом выражения =MSubstitute("Тестовая строка"; "о"; "$@") будет Тест$вая стр$ка
Ниже приведен текст функции MSubstitute:
Function MSubstitute(ByVal trStr As Variant, frStr As String, _toStr As String) As Variant
Dim iRow As Integer Dim iCol As Integer Dim j As Integer Dim Ar As Variant Dim vfr() As String Dim vto() As String ReDim vfr(1 To Len(frStr)) ReDim vto(1 To Len(frStr))
'Помещение строк в массивы. For j = 1 To Len(frStr) vfr(j) = Mid(frStr, j, 1) If Mid(toStr, j, 1) <> "" Then vto(j) = Mid(toStr, j, 1) Else vto(j) = "" End If Next j'Сравнивание каждого символа и, при необходимости, его замена. If IsArray(trStr) Then Ar = trStr For iRow = LBound(Ar, 1) To UBound(Ar, 1) For iCol = LBound(Ar, 2) To UBound(Ar, 2) For j = 1 To Len(frStr) Ar(iRow, iCol) = Application.Substitute( _Ar(iRow, iCol), vfr(j), vto(j)) Next j Next iCol Next iRow Else Ar = trStr For j = 1 To Len(frStr)
130 Часть I Первые шаги
Ar = Application.Substitute(Ar, vfr(j), vto(j)) Next j End If MSubstitute = ArEnd Function
Извлечение чисел из смешанного текста
Следующая функция извлекает числа из смешанного текста (текста, соY
держащего числа и буквы):
RetrieveNumbers(myString)
Пример использования функции RetrieveNumbers на рабочем листе поY
казан на рис. 4.11.
Рис. 4.11. Пример извлечения чисел из смешанного текста с помощью функ&ции RetrieveNumbers
Функция RetrieveNumbers имеет один аргумент:
myString — строка смешанного текста.
Function RetrieveNumbers(myString As String)
Dim i As Integer, j As Integer Dim OnlyNums As String
'Просмотр строки, начиная с ее конца (с шагом -1). For i = Len(myString) To 1 Step -1'IsNumeric - это функция VBA, возвращающая True,'если значение переменной является числом.'Все найденные таким образом числа помещаются в строку OnlyNums. If IsNumeric(Mid(myString, i, 1)) Then j = j + 1 OnlyNums = Mid(myString, i, 1) & OnlyNums End If If j = 1 Then OnlyNums = CInt(Mid(OnlyNums, 1, 1)) Next i RetrieveNumbers = CLng(OnlyNums)End Function
Преобразование номера недели в дату Следующая функция преобразовывает строку вида ‘‘Неделя НН ГГГГ’’ (где
НН YYYY это номер недели, а ГГГГ YYYY номер года) в дату, соответствующую поY
недельнику этой недели:
Weekday(Str)
Функции, определенные пользователем Глава 4 131
На заметку Результат выполнения функции Weekday должен быть помещен в ячейку, отфор&матированную для отображения даты.
Пример использования функции Weekday на рабочем листе показан на
рис. 4.12.
Рис. 4.12. Пример преобразования номера недели в дату с помощьюфункции Weekday
Функция Weekday имеет один аргумент:
Str — строка вида ‘‘Неделя НН ГГГГ’’.
Function ConvertWeekDay(str As String) As Date
Dim Week As Long Dim FirstMon As Date Dim TStr As String FirstMon = DateSerial(Right(str, 4), 1, 1) FirstMon = FirstMon - FirstMon Mod 7 + 2 TStr = Right(str, Len(str) - 7) Week = Left(TStr, InStr(1, TStr, " ", 1)) + 0 ConvertWeekDay = FirstMon + (Week - 1) * 7End Function
Разбор строки с символамиLразделителями
Следующая функция извлекает элемент с заданным номером из строки с
символамиYразделителями:
StringElement(str, chr, ind)
Пример использования функции StringElement на рабочем листе покаY
зан на рис. 4.13.
Рис. 4.13. Пример извлечения элемента с заданным номером из стро&ки с символами&разделителями с помощью функции StringElement
132 Часть I Первые шаги
Функция StringElement имеет 3 аргумента:
str — строка с символамиYразделителями;
chr — символYразделитель;
ind — номер элемента, который нужно извлечь из строки.
Function StringElement(str As String, chr As String, ind As Integer)
Dim arr_str As Variant arr_str = Split(str, chr) StringElement = arr_str(ind - 1)End Function
Сортировка и конкатенация значений ячеекиз заданного диапазона
Следующая функция сортирует значения ячеек из заданного диапазона и
проводит их конкатенацию с помощью символаYразделителя ,:
SortConcat(Rng)
Пример использования функции SortConcat на рабочем листе показан
на рис. 4.14.
Рис. 4.14. Пример сортировки и конкатенации значений ячеек из заданногодиапазона с помощью функции SortConcat
Функция SortConcat имеет один аргумент:
Rng — адрес диапазона ячеек.
На заметку Для работы функции SortConcat используется процедура сортировки массиваBubbleSort.
Функции, определенные пользователем Глава 4 133
Function SortConcat(Rng As Range) As Variant
Dim MySum As String, arr1() As String Dim j As Integer, i As Integer Dim cl As Range Dim concat As Variant On Error GoTo FuncFail:'Инициализация результата функции. SortConcat = 0#'Завершить выполнение функции, если диапазон ячеек пуст. If Rng.Count = 0 Then Exit Function'Создать массив с размером, равным размеру диапазона ячеек. ReDim arr1(1 To Rng.Count)'Заполнить массив. i = 1 For Each cl In Rng arr1(i) = cl.Value i = i + 1 Next'Отсортировать элементы массива. Call BubbleSort(arr1)'Создать строку из элементов массива. For j = UBound(arr1) To 1 Step -1 If Not IsEmpty(arr1(j)) Then MySum = arr1(j) & "," & MySum End If Next j'Присвоить значение функции. SortConcat = Left(MySum, Len(MySum) - 2)'Точка выхода из функции SortConcat.concat_exit: Exit Function'Вывести в ячейке номер ошибки и ее описание.FuncFail: SortConcat = Err.Number & "-" & Err.Description Resume concat_exitEnd Function
Следующая процедура реализует один из наиболее популярных методов сорY
тировки массива, получившего название ‘‘метода пузырьковой сортировки’’:
Sub BubbleSort(List() As String)'Данная процедура сортирует содержимое массива по возрастанию. Dim First As Integer, Last As Integer Dim i As Integer, j As Integer Dim Temp
First = LBound(List) Last = UBound(List) For i = First To Last - 1 For j = i + 1 To Last If UCase(List(i)) > UCase(List(j)) Then Temp = List(j) List(j) = List(i) List(i) = Temp End If Next j
134 Часть I Первые шаги
Next iEnd Sub
Сортировка числовых и строковых значений
Следующая функция сортирует значения ячеек из смешанного диапазона
(диапазона, содержащего как числовые, так и строковые значения) YYYY сперва
в числовом, а затем в алфавитном порядке. Результат помещается в массив, коY
торый может быть отображен на рабочем листе с помощью формулы массива.
Sorter(Rng)
Пример использования функции Sorter показан на рис. 4.15.
Рис. 4.15. Сортировка значений ячеек из смешанного диапазона спомощью функции Sorter
Функция Sorter имеет один аргумент:
Rng — адрес диапазона ячеек.
Function Sorter(Rng As Range) As Variant
Dim arr1() As Variant If Rng.Columns.Count > 1 Then Exit Function arr1 = Application.Transpose(Rng) QuickSort arr1'Возвратить массив. Sorter = Application.Transpose(arr1)End Function
Для сортировки значений ячеек в смешанном диапазоне функция Sorterиспользует две процедуры.
Public Sub QuickSort(ByRef vntArr As Variant, _Optional ByVal lngLeft As Long = -2, _Optional ByVal lngRight As Long = -2) Dim i, j, lngMid As Long Dim vntTestVal As Variant If lngLeft = -2 Then lngLeft = LBound(vntArr) If lngRight = -2 Then lngRight = UBound(vntArr)
Функции, определенные пользователем Глава 4 135
If lngLeft < lngRight Then lngMid = (lngLeft + lngRight) \ 2 vntTestVal = vntArr(lngMid) i = lngLeft j = lngRight
Do Do While vntArr(i) < vntTestVal i = i + 1 Loop Do While vntArr(j) > vntTestVal j = j - 1 Loop If i <= j Then Call SwapElements(vntArr, i, j) i = i + 1 j = j - 1 End If Loop Until i > j
If j <= lngMid Then Call QuickSort(vntArr, lngLeft, j) Call QuickSort(vntArr, i, lngRight) Else Call QuickSort(vntArr, i, lngRight) Call QuickSort(vntArr, lngLeft, j) End If End IfEnd Sub
Private Sub SwapElements(ByRef vntItems As Variant, _ ByVal lngItem1 As Long, _ ByVal lngItem2 As Long) Dim vntTemp As Variant vntTemp = vntItems(lngItem2) vntItems(lngItem2) = vntItems(lngItem1) vntItems(lngItem1) = vntTempEnd Sub
Поиск строки в диапазоне ячеек
Следующая функция проводит поиск строки в указанном диапазоне ячеек.
Результатом выполнения функции являются адреса ячеек, в которых была
найдена заданная строка.
ContainsText(Rng, Text)
Пример использования функции ContainsText показан на рис. 4.16.
Рис. 4.16. Пример поиска строки в диапазоне ячеек с помощью функцииContainsText
136 Часть I Первые шаги
Функция ContainsText имеет 2 аргумента:
Rng — адрес диапазона ячеек;
Text — строка, которую необходимо найти.
Function ContainsText(Rng As Range, Text As String) As String
Dim T As String Dim myCell As Range'Просмотр каждой ячейки в диапазоне. For Each myCell In Rng'Поиск указанной строки. If InStr(myCell.Text, Text) > 0 Then'Если строка найдена, добавить ее адрес к результату'выполнения функции. If Len(T) = 0 Then T = myCell.Address(False, False) Else T = T & "," & myCell.Address(False, False) End If End If Next myCell ContainsText = TEnd Function
Запись содержимого ячейки в обратном порядке
Следующая функция записывает содержимое ячейки в обратном порядке:
ReverseContents(myCell, IsText)
Функция ReverseContents имеет 2 аргумента:
myCell — адрес ячейки;
IsText — необязательный булев параметр, определяющий, является
значение ячейки текстом (True, используется по умолчанию) или чисY
лом (False).
Function ReverseContents(myCell As Range, Optional IsText _As Boolean = True)
Dim i As Integer Dim OrigString As String, NewString As String'Удалить символы пробела в начале и конце строки. OrigString = Trim(myCell) For i = 1 To Len(OrigString)'Запись исходной строки в обратном порядке путем добавления'строки NewString после символа исходной строки. NewString = Mid(OrigString, i, 1) & NewString Next i If IsText = False Then ReverseContents = CLng(NewString) Else ReverseContents = NewString End IfEnd Function
Функции, определенные пользователем Глава 4 137
Поиск наибольших значений в диапазоне ячеек
Следующая функция возвращает адреса ячеек диапазона, содержащих наиY
большее значение:
ReturnMaxs(Rng)
Пример использования функции ReturnMaxs на рабочем листе показан
на рис. 4.17.
Рис. 4.17. Пример нахождения адресов ячеек диапазона, содержа&щих наибольшее значение, с помощью функции ReturnMaxs
Функция ReturnMaxs имеет один аргумент:
Rng — адрес диапазона ячеек.
Function ReturnMaxs(Rng As Range) As String
Dim Mx As Double Dim myCell As Range'Если диапазон состоит из одной ячейки, вернуть ее адрес'в качестве результата выполнения функции. If Rng.Count = 1 Then ReturnMaxs = Rng.Address(False, _False): Exit Function'Использование встроенной функции Max для нахождения'наибольшего значения в диапазоне ячеек. Mx = Application.Max(Rng)'Зная максимальное значение в диапазоне ячеек, найти все'ячейки, содержащие это значение, и возвратить их адреса. For Each myCell In Rng If myCell = Mx Then If Len(ReturnMaxs) = 0 Then ReturnMaxs = myCell.Address(False, False) Else ReturnMaxs = ReturnMaxs & "," & _myCell.Address(False, False)
138 Часть I Первые шаги
End If End If Next myCellEnd Function
Получение адреса гиперссылки
Следующая функция возвращает адрес гиперссылки:
GetAddress(Hyperlink)
Пример использования функции GetAddress на рабочем листе показан
на рис. 4.18.
Рис. 4.18. Пример получения адреса гиперссылки с помощью функцииGetAddress
Функция GetAddress имеет один аргумент:
Hyperlink — адрес ячейки, содержащей гиперссылку.
Function GetAddress(HyperlinkCell As Range)
GetAddress = Replace(HyperlinkCell.Hyperlinks(1).Address, _"mailto:", "")End Function
Получение адреса столбца ячейки
Следующая функция возвращает адрес столбца ячейки:
ColName(Rng)
Функция ColName имеет один аргумент:
Rng — адрес ячейки.
Function ColName(Rng As Range) As String
ColName = Left(Rng.Range("A1").Address(True, False), _ InStr(1, Rng.Range("A1").Address(True, False),"$", 1) - 1)End Function
Генерация постоянных случайных чисел
Следующая функция используется для помещения в ячейку случайного
числа.
StaticRAND()
В отличие от встроенной функции СЛЧИС (RAND), значение которой измеY
няется при каждом открытии рабочей книги, значение функции StaticRAND
Функции, определенные пользователем Глава 4 139
изменяется только при принудительном пересчете значения ячейки. Пример
использования функции StaticRAND на рабочем листе показан на рис. 4.19.
Функция StaticRAND не имеет аргументов.
Function StaticRAND() As Double Randomize StaticRAND = RndEnd Function
Рис. 4.19. Пример генерации постоянного случайного числа с по&мощью функции StaticRAND
Использование структуры Select...Case
Следующая функция демонстрирует пример использования структуры Se-lect...Case для замены вложенных выражений If...Then...Else(рис. 4.20).
Рис. 4.20. Пример замены вложенных выражений If...Then...Else с помощью структуры Select...Case
Function state_period(mth As Integer, yr As Integer) Select Case mth Case 1 state_period = "C 1 июля " & yr - 1 & " по _31 июля " & yr - 1 Case 2 state_period = "С 1 августа " & yr - 1 & " по _31 августа " & yr - 1 Case 3 state_period = "С 1 сентября " & yr - 1 & " по _30 сентября " & yr - 1 Case 4
140 Часть I Первые шаги
state_period = "С 1 октября " & yr - 1 & " по _31 октября " & yr - 1 Case 5 state_period = "С 1 ноября " & yr - 1 & " по _30 ноября " & yr - 1 Case 6 state_period = "С 1 декабря " & yr - 1 & " по _31 декабря " & yr - 1 Case 7 state_period = "С 1 января " & yr & " по _31 января " & yr Case 8 state_period = "С 1 февраля " & yr & " по _28 февраля " & yr Case 9 state_period = "С 1 марта " & yr & " по _31 марта " & yr Case 10 state_period = "С 1 апреля " & yr & " по _30 апреля " & yr Case 11 state_period = "С 1 мая " & yr & " по _31 мая " & yr Case 12 state_period = "С 1 июня " & yr & " по _30 июня " & yr Case 13 state_period = "Подготовка к распродаже" Case 14 state_period = "Распродажа" End SelectEnd Function
Следующий шагВ следующей главе будет рассмотрен один из фундаментальных компонентов
любого языка программирования YYYY цикл. Помимо базовых циклов, присутстY
вующих практически в каждом языке программирования, будет рассмотрен
цикл For Each...Next, являющийся исключительной особенностью VBA.
5Цикл For...Next............................... 141Циклы Do...Loop ............................ 147Цикл For Each...Next...................... 152Управление выполнениемкода: использованиеконструкций If...Then...Else иSelect Case ...................................... 155Следующий шаг............................ 160
Глава 5
Öèêëû è óïðàâëåíèåâûïîëíåíèåì êîäà
Цикл YYYY это фундаментальный
компонент любого языка програмY
мирования. VBA поддерживает все
наиболее распространенные виды
циклов, а также специальный цикл, явY
ляющийся исключительной особенноY
стью VBA как представителя класY
са объектноYориентированных языков
программирования.
В этой главе рассматриваются слеY
дующие базовые конструкции циклов:
For...Next;
Do...While;
Do...Until;
While...Loop;
Until...Loop.
Также будет рассмотрен специальY
ный цикл, уникальный для объектY
ноYориентированных языков прогY
раммирования:
For Each...Next.
Цикл For...Next For...Next — один из самых
распространенных видов цикла, приY
сутствующий практически в каждом
языке программирования. Суть данY
ного цикла заключается во множестY
венном выполнении фрагмента кода,
заключенного между выражениями
For и Next, с различным значением
переменнойYсчетчика (указывается в
выражении For).
142 Часть I Первые шаги
Рассмотрим следующий фрагмент кода:
For I = 1 To 10 Cells(I, I).Value = iNext I
ПеременнаяYсчетчик носит имя I. При первом выполнении цикла значеY
ние переменной I равно 1. Это приводит к тому, что ячейке, расположенной в
1Yй строке 1Yго столбца, будет присвоено значение 1 (рис. 5.1).
Рассмотрим действия VBA при достижении строки Next I. Перед выполY
нением этой строки значение переменной I равно 1. После выполнения строY
ки Next I VBA необходимо принять решение. Если после добавления к пеY
ременнойYсчетчику 1 ее значение не превысило максимально допустимое знаY
чение, заданное с помощью оператора To, цикл следует продолжить. В данном
случае значение переменной I увеличится до 2 и выполнение кода будет проY
должено с первой строки после выражения For. Значение переменной I до и
после выполнения строки Next показано на рис. 5.2 и 5.3, соответственно.
Рис. 5.1. При первом выполнениицикла ячейке, расположенной в 1&йстроке 1&го столбца, будет присвое&но значение 1
Рис. 5.2. Перед выполнением строки NextI значение переменной I равно 1. Увеличе&ние переменной I на 1 не приведет к пре&вышению максимального значения, задан&ного с помощью оператора To
Во время второго выполнения цикла значение переменной I равно 2, в реY
зультате чего ячейке, расположенной во 2Yй строке 2Yго столбца, будет приY
своено значение 2 (рис. 5.4).
Рис. 5.3. После выполнения строки Next Iзначение переменной I равно 2. Выполне&ние кода будет продолжено с первой строкипосле выражения For
Рис. 5.4. Во время второго выпол&нения цикла ячейке, расположен&ной во 2&й строке 2&го столбца, бу&дет присвоено значение 2
При последующих выполнениях цикла значение переменной I будет увеY
личено до 3, 4 и т.д. На 10Yм шаге ячейке, расположенной в 10Yй строке 10Yго
столбца, будет присвоено значение 10.
Циклы и управление выполнением кода Глава 5 143
Рассмотрим, что произойдет с переменной I после выполнения строки
Next I в 10Yй раз. Как показано на рис. 5.5, перед выполнением строки NextI в 10Yй раз значение переменной I равно 10.
Как всегда, после увеличения значения переменнойYсчетчика VBA предY
стоит принять решение относительно дальнейшего выполнения цикла. ВыY
полнение строки Next I в 10Yй раз приводит к увеличению значения переY
менной I до 10, что больше, чем максимальное значение, заданное с помощью
оператора To. VBA завершает цикл и переходит к выполнению первой строки
кода после выражения Next (рис. 5.6).
Рис. 5.5. Перед выполнением строкиNext I в 10&й раз значение переменнойI равно 10
Рис. 5.6. После увеличения значения пе&ременной I до 11 VBA выходит из циклаи продолжает выполнение кода с пер&вой строки после выражения Next
При намерении использовать переменную I после выполнения цикла слеY
дует помнить, что ее значение может превысить максимально допустимое знаY
чение, заданное с помощью оператора To.
Результат выполнения цикла после 10 итераций показан на рис. 5.7.
Рис. 5.7. Результат выполнения цикла после 10 итераций
Наиболее распространенное применение цикла For...Next заключается
в обработке строк заданного диапазона на основе некоторого критерия. ПриY
веденный ниже цикл используется для выделения всех строк, содержащих поY
ложительное число в столбце F:
For i = 2 To 10 If Cells(i, 6).Value > 0 Then Cells(i, 8).Value = "Выручка от сервиса" Cells(i, 1).Resize(1, 8).Interior.ColorIndex = 4 End IfNext i
144 Часть I Первые шаги
Данный цикл обрабатывает 2YY10 строки рабочего листа. Если в столбце Fстроки находится положительное число, в столбец H помещается надпись
‘‘Выручка от сервиса’’, а все ячейки данной строки, расположенные в столбY
цах A–H, выделяются зеленым цветом (рис. 5.8).
Рис. 5.8. Пример использования цикла For...Next для обработки строкзаданного диапазона
Использование переменных в выражении For
Предыдущий пример не очень практичен, поскольку он рассчитан на рабоY
ту с фиксированным диапазоном ячеек. Для указания максимального значеY
ния счетчика в выражении For рекомендуется использовать переменные, как
показано ниже:
FinalRow = Cells(65536, 1).End(xlUp).RowFor i = 2 To FinalRow If Cells(i, 6).Value > 0 Then Cells(i, 8).Value = "Выручка от сервиса" Cells(i, 1).Resize(1, 8).Interior.ColorIndex = 4 End IfNext I
Использование переменных имеет определенные особенности, которые
необходимо учитывать. Если импортированный файл счетов будет содержать
только одну строку заголовка, значение переменной FinalRow окажется равY
ным 1, а первая строка цикла примет вид For I = 2 to 1. Поскольку наY
чальное значение счетчика больше его максимально допустимого значения,
цикл будет пропущен и выполнение кода начнется со строки, следующей за
строкой Next I.
Изменение шага в цикле For...Next
Цикл For...Next предусматривает возможность изменения значения пеY
ременнойYсчетчика с шагом, отличным от 1. Рассмотрим задачу выделения
цветом каждой второй строки в заданном диапазоне ячеек. Чтобы добиться
этого, следует изменить шаг приращения значения переменнойYсчетчика,
воспользовавшись оператором Step:
FinalRow = Cells(65536, 1).End(xlUp).RowFor I = 2 To FinalRow Step 2 Cells(I, 1).Resize(1, 8).Interior.ColorIndex = 35Next I
Циклы и управление выполнением кода Глава 5 145
В результате выполнения приведенного выше кода строки 2, 4, 6 и т.д. буY
дут выделены бледноYзеленым цветом (рис. 5.9).
Рис. 5.9. Пример изменения шага приращения значения переменной&счетчика цикла For...Next с помощью оператора Step
Значение переменнойYсчетчика может изменяться практически с любым
шагом. Ниже приведен пример извлечения каждой десятой строки (Step 10)
из заданного диапазона ячеек:
FinalRow = Cells(65536, 1).End(xlUp).RowNextRow = FinalRow + 5Cells(NextRow - 1, 1).Value = "Выборка из приведенных выше данных"For I = 2 To FinalRow Step 10 Cells(I, 1).Resize(1, 8).Copy Destination:=Cells(NextRow, 1) NextRow = NextRow + 1Next I
Значение переменнойYсчетчика может изменяться и в направлении от
большего к меньшему. В частности, это может пригодиться при выборочном
удалении строк, как показано ниже:
'Удаление строк со значением S54 в столбце C.FinalRow = Cells(65536, 1).End(xlUp).RowFor I = FinalRow To 2 Step -1 If Cells(I, 3).Value = "S54" Then Cells(I, 1).EntireRow.Delete End IfNext I
Досрочное завершение выполнения цикла
Иногда выполнение цикла можно завершить досрочно. Рассмотрим задачу
поиска строки, удовлетворяющей определенному критерию. Как только данY
ная строка будет найдена, выполнение оставшейся части цикла теряет смысл.
Для досрочного выхода из цикла применяется выражение Exit For.
Следующий код используется для поиска строки с положительным числом
в столбце F и нулем в столбце E. При нахождении такой строки выдается соY
146 Часть I Первые шаги
общение об ошибке, а указатель помещается в ячейку проблемной строки,
расположенную в столбце F:
'Следующий код используется для поиска ошибок в исходных данных.FinalRow = Cells(65536, 1).End(xlUp).RowProblemFound = FalseFor I = 2 To FinalRow If Cells(I, 6).Value > 0 Then If Cells(I, 5).Value = 0 Then Cells(I, 6).Select ProblemFound = True Exit For End If End IfNext I
If ProblemFound Then MsgBox "Ошибка в строке " & I Exit SubEnd If
Вложение циклов
Цикл может выполняться внутри другого цикла. Одним из наиболее наглядY
ных примеров вложения циклов является цикл, обрабатывающий строки в заY
данном диапазоне ячеек, внутри которого выполняется цикл, обрабатывающий
столбцы этих строк. Рассмотрим набор данных, представленный на рис. 5.10.
Рис. 5.10. Вложение циклов позволяет реализовать последовательную обработку всехячеек этого диапазона
FinalRow = Cells(65536, 1).End(xlUp).RowFinalCol = Cells(1, 255).End(xlToLeft).ColumnFor I = 2 To FinalRow'Если номер строки - четный, начать с 1-го столбца.'Если номер строки - нечетный, начать со 2-го столбца. If I Mod 2 = 1 Then StartCol = 1 Else StartCol = 2 End If For j = StartCol To FinalCol Step 2 Cells(I, j).Interior.ColorIndex = 35 Next jNext I
Для обработки строк набора данных используется внешний цикл с переY
меннойYсчетчиком I, а для обработки столбцов этих строк YYYY внутренний
Циклы и управление выполнением кода Глава 5 147
цикл с переменнойYсчетчиком J. Поскольку набор данных состоит из 7 строк
(см. рис. 5.10), внешний цикл проходит 7 итераций. Каждой итерации внешY
него цикла соответствует 6 или 7 итераций внутреннего цикла (это зависит от
номера обрабатываемой строки). Результат выполнения приведенного выше
кода показан на рис. 5.11.
Рис. 5.11. Результат выполнения вложенных циклов
Циклы Do...Loop Существует несколько разновидностей цикла Do...Loop. Его наиболее проY
стой вариант используется для выполнения большого числа однообразных операY
ций. Рассмотрим задачу преобразования списка адресов, показанного на рис. 5.12.
Рис. 5.12. Преобразование этого списка адресов вформат базы данных позволит автоматизироватьпроцесс рассылки стандартных писем
148 Часть I Первые шаги
Чтобы преобразовать подобный список адресов в формат базы данных
(имя в столбце B, название улицы в столбце C, город и почтовый индекс в
столбце D), можно записать макрос, включив режим относительных ссылок
(см. главу 1, ‘‘Excel и VBA YYYY гремучая смесь’’). Предназначение макроса соY
стоит в преобразовании в формат базы данных одного адреса и установке укаY
зателя на ячейку, содержащую имя следующего адресата в списке:
Sub Macro3()' Макрос3 Макрос' Макрос записан 29.01.2005 (Александр Журавлев)'' Преобразовать в формат базы данных один адрес.' Установить указатель на ячейку, содержащую имя следующего' адресата в списке.'' Сочетание клавиш: Ctrl+Shift+A' Selection.Copy ActiveCell.Offset(0, 1).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(1, -1).Range("A1").Select Application.CutCopyMode = False Selection.Copy ActiveCell.Offset(-1, 2).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(2, -2).Range("A1").Select Application.CutCopyMode = False Selection.Copy ActiveCell.Offset(-2, 3).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(4, -3).Range("A1").SelectEnd Sub
См. также Об относительных ссылках рассказывается в разделе “Возможное решение:использование относительных ссылок” главы 1 на с. 52.
Внимание Приведенный выше макрос не предназначен для профессионального примененияи является примером решения единовременной задачи.
С помощью макроса преобразование в формат базы данных одного адреса
сводится к установке указателя на ячейку, содержащую имя адресата, и нажаY
тию комбинации клавиш <Ctrl+Shift+A>. После копирования составляющих
адреса в столбцы B, C и D указатель устанавливается на ячейку, содержащую
имя следующего адресата в списке (рис. 5.13).
Использование макроса позволяет преобразовывать список адресов в форY
мат базы данных со скоростью 1 адрес в секунду. Однако эффективно ли данY
ное решение при условии, что список состоит из 5000 адресов?
Циклы и управление выполнением кода Глава 5 149
Рис. 5.13. После преобразования в формат базы данных одного адреса указатель устанавли&вается на ячейку, содержащую имя следующего адресата
Поместив код макроса между выражениями Do и Loop, его можно выполY
нять бесконечно. Таким образом, часы монотонной работы можно свести
к нескольким минутам наблюдения за ходом выполнения макроса.
Чтобы остановить выполнение макроса, следует воспользоваться комбиY
нацией клавиш <Ctrl+Break>. Очевидно, подобное решение также не являетY
ся оптимальным, поскольку оно все еще требует непосредственного участия
человека.
Sub Macro3()' Макрос3 Макрос' Макрос записан 29.01.2005 (Александр Журавлев)'' Преобразовать в формат базы данных один адрес.' Установить указатель на ячейку, содержащую имя следующего' адресата в списке.'' Сочетание клавиш: Ctrl+Shift+A'Do Selection.Copy ActiveCell.Offset(0, 1).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(1, -1).Range("A1").Select Application.CutCopyMode = False Selection.Copy ActiveCell.Offset(-1, 2).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(2, -2).Range("A1").Select Application.CutCopyMode = False Selection.Copy ActiveCell.Offset(-2, 3).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(4, -3).Range("A1").SelectLoopEnd Sub
Приведенный выше цикл представляет собой компромиссное решение,
направленное на быстрое выполнение поставленной задачи. К счастью, цикл
Do...Loop поддерживает возможность своего досрочного завершения.
Логичным условием выхода из приведенного выше цикла является достиY
жение конца набора данных, признаком чего может служить выделение пусY
той ячейки:
150 Часть I Первые шаги
Sub Macro3()' Макрос3 Макрос' Макрос записан 29.01.2005 (Александр Журавлев)'' Преобразовать в формат базы данных один адрес.' Установить указатель на ячейку, содержащую имя следующего' адресата в списке.'' Сочетание клавиш: Ctrl+Shift+A'Do If Not Selection.Value > "" then Exit Do Selection.Copy ActiveCell.Offset(0, 1).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(1, -1).Range("A1").Select Application.CutCopyMode = False Selection.Copy ActiveCell.Offset(-1, 2).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(2, -2).Range("A1").Select Application.CutCopyMode = False Selection.Copy ActiveCell.Offset(-2, 3).Range("A1").Select ActiveSheet.Paste ActiveCell.Offset(4, -3).Range("A1").SelectLoopEnd Sub
Использование операторов While и Until
Операторы While и Until могут использоваться как в выражении Do, так
и в выражении Loop. Единственным обязательным условием является налиY
чие некоторого критерия, принимающего значение True или False.
При использовании конструкции Do While <критерий>...Loop цикл
не выполняется, если <критерий> равен False. К примеру, при чтении соY
держимого текстового файла цикл не должен выполняться, если был достигнут
конец файла (об этом свидетельствует значение функции EOF, равное True):
'Считать содержимое текстового файла,'за исключением итоговых строк. Open "C:\Счет.txt" For Input As #1 r = 1 Do While Not EOF(1) Line Input #FileNumber, Data If Not Left(Data, 5) = "ВСЕГО" Then'Импортировать строку. r = r + 1 Cells(r, 1).Value = Data End If Loop Close #1
В приведенном выше коде было использовано ключевое слово NOT. При досY
тижении конца файла значение функции EOF(1) становится равным True. НеY
Циклы и управление выполнением кода Глава 5 151
которые программисты считают, что частое использование ключевых слов NOTзатрудняет восприятие кода. Чтобы избавиться от NOT, можно воспользоваться
альтернативной конструкцией Do Until <критерий>...Loop:
'Считать содержимое текстового файла,'за исключением итоговых строк. Open "C:\Счет.txt" For Input As #1 r = 1 Do Until EOF(1) Line Input #FileNumber, Data If Not Left(Data, 5) = "ВСЕГО" Then'Импортировать строку. r = r + 1 Cells(r, 1).Value = Data End If Loop Close #1
Иногда цикл необходимо выполнить хотя бы один раз. Для этого оператоY
ры While и Until помещают в конец цикла, в выражение Loop. В результате
выполнения приведенного ниже кода пользователю предлагается ввести некоY
торое число (сумму, указанную в счете) до тех пор, пока он не введет 0:
TotalSales = 0Do x = InputBox(Prompt:="Введите сумму следующего счета _или 0 для завершения.") TotalSales = TotalSales + xLoop Until x = 0MsgBox "Общая сумма сегодняшних продаж составила $" & TotalSales
В следующем примере пользователю предлагается ввести сумму, указанY
ную в чеке. Оплата нескольких счетов одним чеком — весьма распространенY
ная практика. Макрос последовательно ‘‘погашает’’ счета (начиная с самых
ранних) до тех пор, пока не исчерпает сумму чека.
'Ввести сумму, указанную в чеке.AmtToApply = InputBox("Введите сумму, указанную в чеке")'Погасить счета, начиная с самых ранних,'уменьшая при этом значение переменной AmtToApply.NextRow = 2Do While AmtToApply > 0 OpenAmt = Cells(NextRow, 3) If OpenAmt > AmtToApply Then'Погасить счет с помощью чека. Cells(NextRow, 4).Value = AmtToApply AmtToApply = 0 Else Cells(NextRow, 4).Value = OpenAmt AmtToApply = AmtToApply - OpenAmt End If NextRow = NextRow + 1Loop
Возможность использования операторов While и Until как в начале, так
и в конце конструкции Do...Loop, позволяет осуществлять тонкий контроль
над ходом выполнения цикла.
152 Часть I Первые шаги
Цикл While...Wend
Цикл While...Wend был включен в VBA для обеспечения обратной соY
вместимости. В справочной системе VBA Microsoft рекомендует использоY
вать циклы Do...Loop как обладающие более широкими возможностями.
Чтобы объяснить принцип работы цикла While...Wend, приведем неY
большой пример.
'Считывание информации о счетах и вычисление общей суммы продаж.Open "C:\Счет.txt" For Input As #1 TotalSales = 0 While Not EOF(1) Line Input #1, Data TotalSales = TotalSales + Data Wend MsgBox "Общая сумма продаж = " & TotalSales Close #1
Первой строкой цикла While...Wend всегда является строка While<критерий>, последней YYYY строка Wend. Возможность досрочного выхода из
цикла не предусмотрена. За счет наличия операторов While и Until, котоY
рые могут применяться как в выражении Do, так и в выражении Loop,
а также возможности досрочного выхода из цикла, конструкция Do...Loopзаслуженно считается более надежной и гибкой, нежели конструкция
While...Wend.
Цикл For Each...Next Цикл For Each...Next является одним из наиболее полезных циклов
объектноYориентированного языка программирования. К сожалению, этот
цикл не поддерживается средством записи макросов.
Рабочая книга Excel переполнена всевозможными коллекциями объекY
тов YYYY рабочие листы в рабочей книге, ячейки в диапазоне, сводные таблицы
на рабочем листе, последовательности данных на диаграмме и т.п. Цикл ForEach...Next предназначен для последовательной обработки элементов колY
лекции. Прежде чем перейти к его более подробному изучению, рассмотрим
понятие объектной переменной.
Объектные переменные
Обычные переменные хранят только одно значение. В отличие от них, объект*ные переменные хранят много значений, которые являются значениями
свойств соответствующего объекта.
Существует мнение, согласно которому все переменные, используемые в
процедуре, необходимо объявлять в ее начале с помощью ключевого слова
Dim. Это позволяет указать тип переменной, например Integer или Double.
Несмотря на то что таким образом удается сэкономить немного оперативной
памяти, вам следует заранее знать весь список переменных, которые вы собиY
Циклы и управление выполнением кода Глава 5 153
раетесь использовать в процедуре. В отличие от обычных переменных, объявY
ление объектных переменных имеет множество преимуществ, одним из котоY
рых является возможность автоматического завершения ввода. Следующий
код содержит объявления трех объектных переменных, соответствующих раY
бочему листу, диапазону ячеек и сводной таблице:
Sub Test() Dim WSD As Worksheet Dim MyCell As Range Dim PT As PivotTable Set WSD = ThisWorkbook.Worksheets("Данные") Set MyCell = WSD.Cells(65536, 1).End(xlUp).Offset(1, 0) Set PT = WSD.PivotTables(1) ...
Чтобы присвоить значение объектной переменной, помимо знака равенстY
ва следует воспользоваться ключевым словом Set, как показано выше.
Одним из наиболее существенных преимуществ использования объектных
переменных является возможность быстрого обращения к нужному объекту,
например, WSD вместо ThisWorkbook.Worksheets("Данные").
Кроме того, как уже отмечалось выше, объектная переменная предоставляY
ет доступ ко всем свойствам соответствующего объекта.
Вместо переменнойYсчетчика в цикле For Each...Next используется
объектная переменная. В приведенном ниже коде такой переменной является
переменная Cell:
For Each Cell In Range("A1").CurrentRegion.Resize(, 1) If Left(Cell.Value, 5) = "Всего" Then Cell.Resize(1, 8).Font.Bold = True End IfNext Cell
Свойство CurrentRegion используется для выделения непрерывного диаY
пазона ячеек, а свойство Resize — для ограничения диапазона столбцом A.
С помощью следующего кода производится поиск рабочего листа с заданY
ным именем в коллекции всех открытых рабочих книг:
For Each wb in Workbooks If wb.Worksheet(1).Name = "Menu" Then WBFound = True WBName = wb.NameEnd IfNext wb
В результате выполнения приведенного ниже кода с текущего рабочего
листа будут удалены все находящиеся на нем фигуры:
For Each Sh In ActiveSheet.Shapes Sh.DeleteNext Sh
Выполнение следующего кода приведет к удалению с текущего рабочего
листа всех сводных таблиц:
For Each PT In ActiveSheet.PivotTables PT.TableRange2.ClearNext PT
154 Часть I Первые шаги
Обработка всех файлов в папке Рассмотрим несколько полезных процедур, построенных на применении циклов.Первая процедура использует объект VBA FileSearch для нахождения всех JPG&файлов в указанной папке и вывода их списка на рабочем листе Excel. Внешний цикл, использующий переменную&счетчик I, обрабатывает список най&денных файлов. На каждой итерации цикла полное имя файла помещается в пе&ременную ThisEntry. Чтобы отделить путь к файлу от его имени, применяетсявнутренний цикл, использующий переменную&счетчик J. Sub ListJpgFiles()
'Этот макрос находит все JPG-файлы в заданной'папке и выводит их список на рабочем листе Excel.
'Очистить все ячейки рабочего листа. Cells.Clear'Создать заголовки столбцов. Range("A1:D1").Value = Array("FileName", "Path", _"FileName", "NewPath") NextRow = 2'Поиск файлов осуществляется с помощью объекта FileSearch. With Application.FileSearch .NewSearch .LookIn = "C:\" .SearchSubFolders = True .Filename = "*.jpg" .Execute FilesToProcess = .FoundFiles.Count'Обработать список найденных файлов. For I = 1 To .FoundFiles.Count ThisEntry = .FoundFiles(I) Cells(NextRow, 1).Value = ThisEntry'Отделить путь к файлу от его имени. For j = Len(ThisEntry) To 1 Step -1 If Mid(ThisEntry, j, 1) = _Application.PathSeparator Then Cells(NextRow, 2) = Left(ThisEntry, j) Cells(NextRow, 3) = Mid(ThisEntry, j + 1) Exit For End If Next j NextRow = NextRow + 1 Next I End WithEnd Sub
Приведенная выше процедура может быть использована для перемещения JPG&файлов. Введите в столбце D полный путь к папке, в которую необходимо пере&местить соответствующий файл. На каждой итерации приведенного ниже циклаFor Each...Next объектная переменная Cell содержит ссылку на ячейку встолбце A (исходное имя файла), а выражение Cell.Offset(0, 3) — ссылку насоответствующую ячейку в столбце D (полный путь к папке, в которую необходимопереместить файл).
Циклы и управление выполнением кода Глава 5 155
Sub CopyToNewFolder() FinalRow = Range("A65536").End(xlUp).Row For Each Cell In Range("A2:A" & FinalRow) OrigFile = Cell.Value If Cell.Offset(0, 3).Value > "" Then NewFile = Cell.Offset(0, 3) & _Application.PathSeparator & Cell.Offset(0, 2) FileCopy OrigFile, NewFile End If Next CellEnd Sub
Управление выполнением кода: использованиеконструкций If...Then...Else и Select Case
Управление выполнением кода YYYY это еще один фундаментальный аспект
программирования, игнорируемый средством записи макросов. VBA поддерY
живает две конструкции, реализующие концепцию управления выполнением
кода, — If...Then...Else и Select Case.
Знакомство с конструкцией If...Then...Else
Краеугольным камнем концепции управления выполнением кода является
выражение If. Рассмотрим задачу копирования списка продуктов, показанY
ного на рис. 5.14, в два списка YYYY ‘‘Фрукты’’ и ‘‘Овощи’’.
Рис. 5.14. Задача разделения спи&ска продуктов на два списка мо&жет быть решена с помощью од&ного цикла
156 Часть I Первые шаги
Начинающему программисту может придти в голову идея создания двух
циклов YYYY по одному для составления каждого списка. Тем не менее, данная
задача решается с помощью всего лишь одного цикла и конструкции
If...Then...Else.
Условие Обязательной частью выражения If является условие, имеющее значение
True или False. Ниже приведены примеры простых и сложных условий:
If Range("A1").Value = "Товар";
If Not Range("A1").Value = "Товар";
If Range("A1").Value = "Товар" And Range("B1").Value= "Фрукт";
If Range("A1").Value = "Товар" Or Range("B1").Value ="Фрукт".
Конструкция If...Then...End If Строки кода, размещенные после выражения If, будут выполнены только
при соблюдении указанного условия. Чтобы завершить блок If, следует восY
пользоваться выражением End If, как показано ниже:
Sub ColorFruitRedBold() FinalRow = Cells(65536, 1).End(xlUp).Row
For I = 2 To FinalRow If Cells(I, 1).Value = "Фрукт" Then Cells(I, 1).Resize(1, 3).Font.Bold = True Cells(I, 1).Resize(1, 3).Font.ColorIndex = 3 End If Next I
MsgBox "Все фрукты выделены красным цветом и утолщением _шрифта"End Sub
Конструкция If...Then...Else...End If Иногда необходимо выполнить один фрагмент кода, если условие равно
True, и другой YYYY если оно равно False. В VBA для этого следует указать втоY
рой фрагмент кода после ключевого слова Else. Для завершения блока
If...Then...Else используется выражение End If:
Sub FruitRedVegGreen() FinalRow = Cells(65536, 1).End(xlUp).Row
For I = 2 To FinalRow If Cells(I, 1).Value = "Фрукт" Then Cells(I, 1).Resize(1, 3).Font.ColorIndex = 3 Else Cells(I, 1).Resize(1, 3).Font.ColorIndex = 50
Циклы и управление выполнением кода Глава 5 157
End If Next I
MsgBox "Все фрукты выделены красным цветом, _а все овощи - изумрудным"End Sub
Конструкция If...ElseIf...End If Структура If...End If поддерживает возможность проверки
нескольких условий с помощью ключевого слова ElseIf. Как показано на
рис. 5.14, список продуктов содержит одно травянистое растение, что
наводит на мысль о необходимости проверки трех условий — является ли
элемент списка фруктом, овощем или травянистым растением? При
негативном результате всех трех проверок можно сделать вывод, что элемент
списка содержит ошибку:
Sub MultipleIf() FinalRow = Cells(65536, 1).End(xlUp).Row
For I = 2 To FinalRow If Cells(I, 1).Value = "Фрукт" Then Cells(I, 1).Resize(1, 3).Font.ColorIndex = 3 ElseIf Cells(I, 1).Value = "Овощ" Then Cells(I, 1).Resize(1, 3).Font.ColorIndex = 50 ElseIf Cells(I, 1).Value = "Растение" Then Cells(I, 1).Resize(1, 3).Font.ColorIndex = 5 Else 'Элемент списка содержит ошибку. Cells(I, 1).Resize(1, 3).Interior.ColorIndex = 6 End If Next I
MsgBox "Фрукты выделены красным цветом, овощи - изумрудным, _а травянистые растения - синим"End Sub
Конструкция Select Case...End Select Когда условий становится слишком много, использование структуры
If...ElseIf теряет свою привлекательность. Для таких случаев VBA распоY
лагает конструкцией Select Case, первая строка которой содержит так наY
зываемое условное выражение:
Select Case Cells(I, 1).Value
После строки с условным выражением перечислены его возможные значеY
ния, записанные после ключевого слова Case. Для каждого значения должен
быть указан фрагмент кода, который будет выполнен, если условное выражеY
ние примет это значение.
Если необходимо предусмотреть возможность принятия условным выY
ражением значения, отличного от всех перечисленных, воспользуйтесь
ключевыми словами Case Else. Завершает блок Select Case выражеY
ние End Select.
158 Часть I Первые шаги
Единственное отличие следующего кода от приведенного ранее заключаетY
ся в использовании конструкции Case Select вместо конструкции
If...ElseIf:
Sub SelectCase() FinalRow = Cells(65536, 1).End(xlUp).Row
For I = 2 To FinalRow Select Case Cells(I, 1).Value Case "Fruit" Cells(I, 1).Resize(1, 3).Font.ColorIndex = 3 Case "Vegetable" Cells(I, 1).Resize(1, 3).Font.ColorIndex = 50 Case "Herbs" Cells(I, 1).Resize(1, 3).Font.ColorIndex = 5 Case Else End Select Next I
MsgBox "Фрукты выделены красным цветом, овощи - изумрудным, _а травянистые растения - синим"End Sub
Использование сложных выражений Case Выражения Case могут быть как простыми (рассматривались в предыY
дущем разделе), так и сложными. Следующее выражение определяет единое
действие для ячеек, содержащих значения ‘‘Клубника’’, ‘‘Голубика’’ и
‘‘Малина’’:
Case "Клубника", "Голубика", "Малина" AdCode = 1
В качестве значения условного выражения можно указать диапазон, как
показано ниже:
Case 1 To 20 Discount = 0.05Case 21 To 100 Discount = 0.1
Кроме того, воспользовавшись ключевым словом Is и оператором сравнеY
ния (например, > или <), можно задать значение условного выражения как
открытый диапазон:
Case Is < 10 Discount = 0Case Is > 100 Discount = 0.2Case Else Discount = 0
Вложение выражений If Выражение If может находиться внутри другого выражения If. Вложение
выражений If требует от программиста аккуратности при оформлении проY
граммного кода. Поскольку в конце подобных конструкций скапливается неY
Циклы и управление выполнением кода Глава 5 159
сколько строк End If, соблюдение отступов поможет определить, к какому
выражению If относится та или иная строка End If.
Итоговый макрос содержит большое количество правил, описывающих
политику скидок.
Если объем заказа фруктов составляет менее 5 ящиков, скидка не преY
доставляется.
Если объем заказа фруктов составляет от 5 до 20 ящиков, предоставляY
ется скидка в размере 10%.
Если объем заказа фруктов составляет более 20 ящиков, предоставляетY
ся скидка в размере 15%.
Если объем заказа травянистых растений составляет менее 10 ящиков,
скидка не предоставляется.
Если объем заказа травянистых растений составляет от 10 до
15 ящиков, предоставляется скидка в размере 3%.
Если объем заказа травянистых растений составляет более 15 ящиков,
предоставляется скидка в размере 6%.
Если объем заказа овощей, за исключением спаржи, составляет
5 ящиков и более, предоставляется скидка в размере 12%.
Если объем заказа спаржи составляет 20 ящиков и более, предоставляY
ется скидка в размере 12%.
Во время распродажи продукта на него предоставляется скидка в разY
мере 25%. Никакие другие скидки при этом не предоставляются.
На этой неделе проводится распродажа клубники, салата и помидоров.
Ниже приведен код, реализующий указанную политику скидок.
Sub ComplexIf() FinalRow = Cells(65536, 1).End(xlUp).Row
For I = 2 To FinalRow ThisClass = Cells(I, 1).Value ThisProduct = Cells(I, 2).Value ThisQty = Cells(I, 3).Value
'Определение продуктов, находящихся на распродаже. Select Case ThisProduct Case "Клубника", "Салат", "Помидоры" Sale = True Case Else Sale = False End Select
'Расчет скидки. If Sale Then Discount = 0.25 Else If ThisClass = "Фрукт" Then Select Case ThisQty Case Is < 5
160 Часть I Первые шаги
Discount = 0 Case 5 To 20 Discount = 0.1 Case Is > 20 Discount = 0.15 End Select ElseIf ThisClass = "Растение" Then Select Case ThisQty Case Is < 10 Discount = 0 Case 10 To 15 Discount = 0.03 Case Is > 15 Discount = 0.05 End Select ElseIf ThisClass = "Овощ" Then'Расчет скидки на спаржу. If ThisProduct = "Спаржа" Then If ThisQty < 20 Then Discount = 0 Else Discount = 0.12 End If Else If ThisQty < 5 Then Discount = 0 Else Discount = 0.12 End If End If 'Является ли продукт спаржей? End If 'Является ли продукт овощем? End If 'Проводится ли распродажа продукта?
Cells(I, 4).Value = Discount
If Sale Then Cells(I, 4).Font.Bold = True End If
Next I
Range("D1").Value = "Скидка"
MsgBox "Расчет скидок завершен"
End Sub
Следующий шагЦикл YYYY это фундаментальный компонент любого языка программирования.
VBA поддерживает как традиционные циклы For...Next и Do...Loop, так и
цикл For Each...Next, характерный для объектноYориентированных языY
ков. В следующей главе будет рассмотрен очень важный для Excel VBA стиль
записи ссылок с таинственным названием R1C1.
6Сравнение стилей записиссылок A1 и R1C1.............................161Использование стиля ссылокR1C1 в Excel ..................................... 162Чудесный мир формул Excel ..... 163Ссылки в стиле R1C1 ..................... 166Использование ссылок в стилеR1C1 при условномформатировании ячеек...............171Использование ссылок в стилеR1C1 при создании формулымассива........................................... 174Следующий шаг............................ 175
Глава 6
Ñòèëü çàïèñèññûëîê R1C1
Сравнение стилейзаписи ссылок A1 и R1C1
Стиль записи ссылок A1 берет
свое начало от приложения VisiCalc.
Для обращения к ячейке, располоY
женной в верхнем левом углу элекY
тронной таблицы, Дэн Бриклин (Dan
Bricklin) и Боб Фрэнкстон (Bob
Frankston) предложили использовать
запись ‘‘A1’’. Аналогичную адресную
схему взял на вооружение Мич КейY
пор (Mitch Kapor) в своем легендарY
ном продукте Lotus 1Y2Y3. Вскоре заY
конодательницей мод попыталась
стать Microsoft, предложив рынку
программу Multiplan и стиль записи
ссылок R1C1. Согласно этому стилю
для обращения к ячейке, располоY
женной в верхнем левом углу элекY
тронной таблицы, использовалась
запись ‘‘R1C1’’, указывающая на то,
что ячейка находится в 1Yй строке
(Row) 1Yго столбца (Column).
Благодаря лидирующему положеY
нию, которое занимал на рынке проY
дукт Lotus 1Y2Y3 в 1980Yх и начале
1990Yх годов, стиль записи ссылок A1
стал общепризнанным стандартом.
Осознав всю невыгодность своей
стратегии, Microsoft добавила в Excel
поддержку адресации A1, сделав ее
используемой по умолчанию. СледуY
ет отметить, что официально MicroY
soft поддерживает оба способа записи
ссылок.
162 Часть I Первые шаги
R1C1 — дела давно минувших дней?
Подавляющее большинство пользователей Excel единогласны во мнении,
что стиль записи ссылок R1C1 давно утратил свою актуальность. К сожалеY
нию, именно этот стиль ‘‘пришелся по душе’’ средству записи макросов. ТаY
ким образом, на первый взгляд знание адресной схемы R1C1 вызвано необхоY
димостью уметь ‘‘читать’’ автоматически сгенерированный код.
R1C1 — сильные стороны
И все же необходимо отдать должное Microsoft. Познакомившись с R1C1Y
формулами, вы поймете, что они намного полезнее обычных формул
(особенно ярко это проявляется при использовании формул в VBA). ПримеY
нение ссылок в стиле R1C1 позволяет создавать более эффективный код.
Кроме того, употребление адресации R1C1 является обязательным требованиY
ем при создании формул массивов и при задании условного форматирования
ячеек. Именно последнее и обуславливает необходимость ознакомления с таY
инственным стилем записи ссылок R1C1.
Использование стиля ссылок R1C1 в Excel Чтобы указать на необходимость использования в Excel адресации R1C1,
выберите команду главного меню Сервис Параметры (Tools Options), пеY
рейдите во вкладку Общие (General) и установите флажок Стиль ссылокR1C1 (R1C1 reference style) (рис. 6.1).
Рис. 6.1. Чтобы выбрать адресацию R1C1, установите флажок Стиль ссылокR1C1 на вкладке Общие диалогового окна Параметры
Стиль записи ссылок R1C1 Глава 6 163
После перехода к стилю ссылок R1C1 буквы в заголовках столбцов (A, B,
C и т.д.) будут заменены цифрами (1, 2, 3 и т.д.), как показано на рис. 6.2.
Рис. 6.2. После перехода к стилю ссылок R1C1 буквы взаголовках столбцов будут заменены цифрами
В соответствии с новой адресацией ячейка B5 будет называться R5C2, поY
скольку она расположена в 5Yй строке 2Yго столбца.
Чудесный мир формул Excel Возможность автоматического пересчета значений тысяч ячеек стала осY
новным преимуществом электронных таблиц перед перфокартами, испольY
зуемыми вплоть до 1979 года. В наши дни одной из наиболее востребованных
функций электронных таблиц является функция автоматического копироваY
ния формулы из одной ячейки в другую.
Как “размножаются” формулы
Рассмотрим достаточно простую электронную таблицу, показанную на
рис. 6.3.
Рис. 6.3. С помощью маркера заполнения формула можетбыть скопирована из одной ячейки в другую
Введите в ячейку D4 формулу =C4*B4 и с помощью маркера заполнения
скопируйте ее в ячейки D5:D9.
164 Часть I Первые шаги
Формула в ячейке F4 включает в себя как абсолютные, так и относительY
ные ссылки: =ЕСЛИ(E4;ОКРУГЛ(D4*$B$1;2);0) (=IF(E4,ROUND(D4*$B$1,2),0)). Благодаря наличию знаков доллара ($) эта формула всегда вычисляет
произведение общей цены товара, находящейся в столбце D текущей строки,
на величину налога, внесенную в ячейку B1.
Результат вычисления значений ячеек, показанный на рис. 6.4, был достигнут с
помощью формул, показанных на рис. 6.5. (Чтобы переключиться в режим отоY
бражения формул в Excel, воспользуйтесь комбинацией клавиш <Ctrl+~>.)
Рис. 6.4. Результат вычисления значений ячеек в столбцах D,F и G достигнут с помощью формул, показанных на рис. 6.5
Рис. 6.5. Чтобы переключиться в режим отображения формул в Excel, воспользуйтеськомбинацией клавиш <Ctrl+~>. Возможность автоматического копирования форму&лы из одной ячейки в другую поистине удивительна!
Обратите внимание, что для достижения показанного на рис. 6.5 результаY
та необходимо ввести вручную всего лишь 4 формулы: 3 в ячейки D4, F4, G4и 1 в ячейку G10. В остальные ячейки формулы могут быть скопированы авY
томатически. Превращение формулы =F4+D4 в ячейке G4 при ее копировании
в ячейку G5 в формулу =F5+D5 приводит начинающих пользователей Excel
в настоящий восторг!
Разоблачение
На самом деле Excel оперирует только R1C1Yформулами. Поддержка
формул в стиле A1 для совместимости со стандартом VisiCalc и Lotus реализоY
вана исключительно на уровне интерфейса Excel.
Стиль записи ссылок R1C1 Глава 6 165
Перейдя к стилю ссылок R1C1, можно заметить, что ‘‘различные’’ формуY
лы в ячейках D4:D9 на самом деле являются одной и той же R1C1Yформулой.
Это же справедливо и по отношению к ячейкам F4:F9 и G4:G9 (рис. 6.6).
Рис. 6.6. Переход к стилю ссылок R1C1 дает неожиданный результат — все формулы,расположенные в столбцах 4 и 6, одинаковые
Поскольку поддержка формул в стиле A1 реализована исключительно на уровY
не интерфейса Excel, становится ясно, что автоматическое копирование формулы
из одной ячейки в другую отнюдь не является чемYто экстраординарным.
Поддержка ссылок в стиле R1C1 делает возможным более эффективное
применение формул в макросах VBA. В частности, это позволяет ввести одну
и ту же формулу в целый диапазон ячеек с помощью одной строки кода.
Практикум
Использование ссылок в стиле A1 и R1C1 в VBA Заполним ячейки рассмотренной выше таблицы с помощью VBA, используя ссыл&ки в стиле A1. Прежде всего, введем формулы в ячейки D4, F4 и G4. Далее, скопи&руем их и вставим в требуемые ячейки, как показано ниже: Sub BookA1Style()' Нахождение последней строки с данными. FinalRow = Range("B65536").End(xlUp).Row' Ввод формул. Range("D4").Formula = "=B4*C4"' В англоязычной версии Excel:' Range("F4").Formula = "=IF(E4,ROUND(D4*$B$1,2),0)" Range("F4").FormulaLocal = "=ЕСЛИ(E4;ОКРУГЛ(D4*$B$1;2);0)" Range("G4").Formula = "=F4+D4"' Копирование формул в строке 4 в требуемые ячейки. Range("D4").Copy Destination:=Range("D5:D" & FinalRow) Range("F4:G4").Copy Destination:=Range("F5:G" & FinalRow)' Формирование итоговой строки. Cells(FinalRow + 1, 1).Value = "Всего"' В англоязычной версии Excel:' Cells(FinalRow + 1, 6).Formula = "=SUM(G4:G" & FinalRow & ")" Cells(FinalRow + 1, 6).FormulaLocal = "=СУММ(G4:G" & _FinalRow & ")"End Sub
166 Часть I Первые шаги
Как видим, понадобилось 3 строки для ввода формул и еще 2 для их копированияв требуемые ячейки. Эквивалентный код, в котором используются ссылки в стиле R1C1, позволяет вве&сти формулу сразу в целый диапазон ячеек с помощью одной строки кода, как по&казано ниже: Sub BookR1C1Style()' Нахождение последней строки с данными. FinalRow = Range("B65536").End(xlUp).Row' Ввод формул. Range("D4:D" & FinalRow).FormulaR1C1 = "=RC[-1]*RC[-2]"' В англоязычной версии Excel: ' Range("F4:F" & FinalRow).FormulaR1C1 = _"=IF(RC[-1],ROUND(RC[-2]*R1C2,2),0)" Range("F4:F" & FinalRow).FormulaR1C1Local = _"=ЕСЛИ(RC[-1];ОКРУГЛ(RC[-2]*R1C2;2);0)" Range("G4:G" & FinalRow).FormulaR1C1 = "=+RC[-1]+RC[-3]"' Формирование итоговой строки. Cells(FinalRow + 1, 1).Value = "Всего"' В англоязычной версии Excel:' Cells(FinalRow + 1, 6).Formula = "=SUM(G4:G" & FinalRow & ")" Cells(FinalRow + 1, 6).FormulaLocal = _"=СУММ(G4:G" & FinalRow & ")"End Sub
Ссылки в стиле R1C1 Согласно стилю R1C1, обращение к ячейке осуществляется по номеру ее
строки (R) и столбца (С). Подробное рассмотрение ссылок в стиле R1C1 начY
нем с относительных ссылок как наиболее часто используемых в формулах на
рабочем листе и в VBA.
Относительные ссылки в стиле R1C1
Рассмотрим задачу обращения к ячейке в формуле Excel. Чтобы определить
адрес ячейки, следует задать смещение по строкам и столбцам в квадратных
скобках после букв ‘‘R’’ и ‘‘C’’, соответственно.
Положительное смещение по столбцам обозначает смещение вправо на
указанное число позиций, а отрицательное смещение по столбцам YYYY смещеY
ние влево на указанное число позиций. К примеру, адрес ячейки F5 относиY
тельно ячейки E5 выглядит как RC[1], а адрес ячейки D5 относительно ячейY
ки E5 — как RC[-1].
Положительное смещение по строкам обозначает смещение вниз на укаY
занное число позиций, а отрицательное смещение по строкам YYYY смещение
вверх на указанное число позиций. К примеру, адрес ячейки E6 относительно
ячейки E5 выглядит как R[1]C, а адрес ячейки E4 относительно ячейки E5 —
как R[-1]C.
Нулевое смещение по строкам или столбцам (обозначается отсутствием
соответствующих квадратных скобок) используется для обращения к ячейкам,
Стиль записи ссылок R1C1 Глава 6 167
расположенным в той же строке или столбце, что и ячейка с формулой. РасY
смотрим несколько примеров.
Чтобы обратиться к ячейке D4 из ячейки E5, следует воспользоваться
формулой =R[-1]C[-1].
Чтобы обратиться к ячейке D5 из ячейки E5, следует воспользоваться
формулой =RC[-1].
Чтобы обратиться к ячейке F5 из ячейки E5, следует воспользоваться
формулой =RC[1].
Чтобы обратиться к ячейке E5 из ячейки E5, следует воспользоваться
формулой =RC. Последняя формула определяет так называемую цикY
лическую ссылку и, как правило, никогда не применяется на практике.
Более наглядно обращение к различным ячейкам из ячейки E5 продемонY
стрировано на рис. 6.7.
Рис. 6.7. Пример использования относительных ссылок встиле R1C1
Относительные ссылки в стиле R1C1 можно применять и для обращения к
диапазону ячеек. К примеру, следующая формула суммирует значение
12 ячеек, расположенных слева от ячейки с формулой:
=СУММ(RC[-12]:RC[-1])
(В англоязычной версии Excel эта формула записывается как =SUM(RC[-12]:RC[-1]).)
Абсолютные ссылки в стиле R1C1
Абсолютная ссылка всегда указывает на ячейку, расположенную в опредеY
ленном месте. При использовании адресации в стиле A1 абсолютные ссылки
задаются путем предварения буквы столбца и номера строки знаком доллара
($), например $B$2.
При использовании адресации в стиле R1C1 абсолютные ссылки задаются
путем опускания квадратных скобок, например R2C2.
Смешанные ссылки в стиле R1C1
Смешанная ссылка содержит либо абсолютный столбец и относительную
строку, либо абсолютную строку и относительный столбец.
168 Часть I Первые шаги
Рассмотрим макрос, импортирующий в Excel файл Счет.txt. Адрес итоY
говой строки вычисляется с помощью выражения .End(xlUp). В определенY
ные ячейки итоговой строки необходимо поместить сумму значений всех ячеек,
расположенных выше ячейки с формулой вплоть до 2Yй строки включительно:
Sub MixedReference() TotalRow = Cells(65536, 1).End(xlUp).Row + 1 Cells(TotalRow, 1).Value = "Всего"' В англоязычной версии Excel:' Cells(TotalRow, 5).Resize(1, 3).FormulaR1C1 = _'"=SUM(R2C:R[-1]C)" Cells(TotalRow, 5).Resize(1, 3).FormulaR1C1Local = _"=СУММ(R2C:R[-1]C)"End Sub
Как показано в приведенном выше коде, упомянутый диапазон ячеек задаY
ется как R2C:R[-1]C. Таким образом, единственная R1C1Yформула может
быть использована для обращения к диапазону ячеек неопределенного размеY
ра, что еще раз подтверждает эффективность использования ссылок в стиле
R1C1 (рис. 6.8).
Рис. 6.8. Пример использования смешанной ссылки в стиле R1C1
Обращение к строке или столбцу с помощью ссылокв стиле R1C1
Рассмотрим задачу нахождения максимального значения в столбце G. ПоY
скольку точное число строк с данными заранее неизвестно, для этого можно
применить A1Yформулу =МАКС($G:$G) (=MAX($G:$G)) или R1C1Yформулу
=МАКС(C7) (=MAX(C7)). Аналогично, для того чтобы найти минимальное
значение в строке 1, можно применить A1Yформулу =МИН($1:$1)(=MIN($1:$1)) или R1C1Yформулу =МИН(R1) (=MIN(R1)). Обратиться к
строке или столбцу можно также с помощью относительной ссылки в стиле
R1C1. Например, чтобы найти среднее всех значений в строке, расположенной
над текущей ячейкой, можно воспользоваться формулой =СРЗНАЧ(R[-1]) (=AVERAGE(R[-1])).
Стиль записи ссылок R1C1 Глава 6 169
Замена нескольких A1Lформул одной R1C1Lформулой
Формулы R1C1 намного эффективнее A1Yформул. Рассмотрим классичеY
ский пример построения таблицы умножения с помощью однойYединственной
формулы, использующей смешанные ссылки в стиле R1C1.
Построение таблицы умножения с помощью единственнойR1C1Lформулы
Введите числа от 1 до 12 в ячейки B1:M1. Выделите эти ячейки и скопируйY
те их с применением транспонирования в ячейки A2:A13. Создадим формулу,
вычисляющую для каждой ячейки из диапазона B2:M13 произведение соотY
ветствующих чисел из 1Yго столбца и 1Yй строки. Естественно, здесь не обойY
тись без ссылок в стиле R1C1:
Sub MultiplicationTable()' Построение таблицы умножения с помощью одной R1C1-формулы. Range("B1:M1").Value = Array(1, 2, 3, 4, 5, 6, 7, _8, 9, 10, 11, 12) Range("B1:M1").Font.Bold = True Range("B1:M1").Copy Range("A2:A13").PasteSpecial Transpose:=True Range("B2:M13").FormulaR1C1 = "=RC1*R1C" Cells.EntireColumn.AutoFitEnd Sub
Формула =RC1*R1C проста до гениальности. Результатом выполнения приY
веденного выше кода является таблица умножения, показанная на рис. 6.9.
Рис. 6.9. Таблица умножения, построенная с по&мощью формулы =RC1*R1C
Внимание Обратите внимание, что после выполнения макроса диапазон ячеек B1:M1 остаетсяактивным элементом буфера обмена. Если нажать клавишу <Enter>, содержимоеэтого диапазона будет скопировано в текущую выделенную область рабочего листа,что, вообще говоря, нежелательно. Чтобы выйти из режима копирования/вставки,добавьте в конце макроса строку Application.CutCopyMode = False.
170 Часть I Первые шаги
Интересный факт
Проведем небольшой эксперимент. Выделите ячейку F6 и начните запись
нового макроса, выбрав в меню Excel команду Сервис Макрос Начать запись(Tools Macro Record New Macro). Щелкните на кнопке Относительнаяссылка (Relative Reference), расположенной на панели инструментов
Остановить запись (Stop Recording). Введите формулу =A1 и нажмите комбиY
нацию клавиш <Ctrl+Enter>, чтобы остаться в ячейке F6. Щелкните на кнопY
ке Остановить запись, расположенной на одноименной панели инструменY
тов, чтобы остановить запись макроса.
Код, сгенерированный средством записи макросов, фактически будет
представлять собой одну формулу, ссылающуюся на ячейку, расположенную
на 5 строк выше и на 5 столбцов левее текущей активной ячейки:
Sub PointFiveRowsUp() ActiveCell.FormulaR1C1 = "=R[-5]C[-5]"End Sub
А теперь выделите ячейку A1 и выполните только что созданный макрос.
Вероятно, вы ожидаете получить сообщение о распространенной ошибке вреY
мени выполнения 1004? Как бы не так! В результате выполнения макроса
формула в ячейке A1 будет ссылаться на ячейку IR65532, что фактически озY
начает переход из левой верхней в правую нижнюю часть рабочего листа. НеY
смотря на то, что этот весьма любопытный факт не имеет какойYлибо практиY
ческой ценности, его знание может пригодиться при написании программY
ного кода.
Тренируем память
Преимущество использования R1C1Yформул в коде VBA частично нивелиY
руется необходимостью переключения интерфейса Excel в режим ссылок в
стиле R1C1. Многие пользователи предпочитают не делать этого и стараются
запомнить соответствие между буквами, применяемыми в качестве заголовка
столбцов, и порядковыми номерами последних.
К сожалению, запомнить, что буква ‘‘U’’ является 21Yй буквой латинского
алфавита, получается далеко не сразу. Следующая игра поможет вам быстрее
натренировать свою память:
Sub QuizColumnNumbers() Do i = Int(Rnd() * 26) + 1 Ans = InputBox("Заголовком какого столбца является _буква " & Chr(64 + i) & "?") If Ans = "" Then Exit Do If Not (Ans + 0) = i Then MsgBox "Буква " & Chr(64 + i) & " является _заголовком столбца # " & i End If LoopEnd Sub
Стиль записи ссылок R1C1 Глава 6 171
Если вы не считаете подобную игру забавной или же вам необходимо узY
нать порядковый номер какогоYнибудь ‘‘удаленного’’ столбца (например, DG),
обратитесь к интерфейсу Excel. Выделите ячейку A1 и, удерживая нажатой
клавишу <Shift>, нажмите несколько раз клавишу <→>. Пока длина выделенY
ного диапазона ячеек не вышла за пределы экрана, порядковый номер поY
следнего столбца можно узнать с помощью поля Имя (Name Box), располоY
женного слева от поля ввода формулы, как показано на рис. 6.10.
Рис. 6.10. Пока длина выделенного диапазона ячеек невышла за пределы экрана, количество выделенных строк истолбцов отображается в поле Имя слева от поля вводаформулы
Когда длина выделенного диапазона ячеек выйдет за пределы экрана, коY
личество выделенных строк и столбцов будет отображаться с помощью всплыY
вающей подсказки. В частности, порядковый номер столбца DG равен 111, как
показано на рис. 6.11.
Рис. 6.11. Когда длина выделенного диапазона ячеек выходит запределы экрана, количество выделенных строк и столбцов ото&бражается с помощью всплывающей подсказки
Использование ссылок в стиле R1C1 при условномформатировании ячеек
При условном форматировании ячеек рекомендуется использовать R1C1Y
формулы. Применение A1Yформул является нежелательным (по имеющимся
наблюдениям, примерно 1 из 50 ячеек с условным форматированием, опредеY
ленным с помощью A1Yформул, содержит ошибку).
Задание условного форматирования с помощьюпользовательского интерфейса
Существует два вида условного форматирования. Открыв диалоговое окно
Условное форматирование (Conditional Formatting) с помощью команды меY
ню Формат Условное форматирование (Format Conditional Formatting),
можно заметить, что по умолчанию в качестве критерия форматирования исY
пользуется значение ячейки. Например, вы можете определить особый форY
172 Часть I Первые шаги
мат для ячеек, содержащих отрицательные числа. Согласно второму типу усY
ловного форматирования критерий форматирования определяется с помощью
формулы, принимающей значение True или False. Формула может содерY
жать ссылки на любые ячейки рабочего листа, а также объединять несколько
критериев с помощью функций ИЛИ (OR) и И (AND). Для каждой ячейки можно
определить 3 различных критерия форматирования.
Рассмотрим задачу сокрытия текста ячеек, содержащих сообщения об
ошибке, и выделения текста ячеек с отрицательными числами красным цвеY
том. Наиболее очевидным ее решением является задание условного форматиY
рования. Первый критерий будет основываться на формуле, второй YYYY на знаY
чении ячейки. Пример установки подобного условного форматирования с поY
мощью интерфейса Excel показан на рис. 6.12.
Рис. 6.12. В качестве первого критерия форматирования ячейки ис&пользуется формула, в качестве второго — значение ячейки
Задание условного форматирования с помощью VBA
Задача сокрытия текста ячеек, содержащих сообщения об ошибке, может
быть решена путем установки цвета заливки ячейки равным цвету ее текста.
Для реализации такого решения создадим небольшой макрос VBA.
Задание условного форматирования ячеек осуществляется посредством
использования объекта FormatConditions. Поскольку для каждой ячейки
может быть определено три критерия форматирования, следующий код сперва
отменяет любое условное форматирование в пределах рабочего листа, после
чего применяет два критерия форматирования к каждой непустой ячейке.
Первый критерий форматирования основывается на R1C1Yформуле (тип
xlExpression), второй YYYY на значении ячейки (тип xlCellValue). ОбратиY
те внимание, что кроме значения ячейки необходимо указать также и операY
тор сравнения. После задания критериев форматирования определяется сам
формат ячеек (в данном случае YYYY цвет текста), как показано ниже:
Sub ApplySpecialFormattingAll()' Сокрытие текста ячеек, содержащих сообщения об ошибке. For Each ws In ThisWorkbook.Worksheets
Стиль записи ссылок R1C1 Глава 6 173
ws.UsedRange.FormatConditions.Delete For Each cell In ws.UsedRange.Cells If Not IsEmpty(cell) Then' В англоязычной версии Excel:' cell.FormatConditions.Add Type:=xlExpression, _' Formula1:="=OR(ISERR(RC),ISNA(RC))" cell.FormatConditions.Add Type:=xlExpression, _ Formula1:="=ИЛИ(ЕОШИБКА(RC);ЕНД(RC))" cell.FormatConditions(1).Font.Color = _cell.Interior.Color cell.FormatConditions.Add Type:=xlCellValue, _Operator:=xlLess, Formula1:="0" cell.FormatConditions(2).Font.ColorIndex = 3 End If Next cell Next wsEnd Sub
Внимание Не используйте A1&формулы для задания критерия условного форматированияв VBA. Соответствующий код может выполняться корректно лишь для небольшогочисла ячеек, однако при попытке его применения к 50 и более ячейкам существуетвероятность сбоя (более подробно об этом рассказывается в следующем практи&куме). Использование R1C1&формул полностью лишено подобного недостатка.
Практикум
Поиск минимального и максимальногозначения в столбцеРассмотрим классический пример использования условного форматированияячеек. На рис. 6.13 показано, как выделить строки, содержащие минимальное имаксимальное значение в определенном столбце, с помощью интерфейса Excel.
Рис. 6.13. Классический пример, позволяющий выявить одну из про&
блем задания условного форматирования ячеек с помощью VBA
174 Часть I Первые шаги
Ниже приведен соответствующий код VBA:Sub FindMinMax()' Выделить строку, содержащую наибольший доход, зеленым цветом.' Выделить строку, содержащую наименьший доход, желтым цветом. FinalRow = Cells(Application.Rows.Count, 1).End(xlUp).Row With Range("A2:I" & FinalRow) .FormatConditions.Delete' В англоязычной версии Excel:' .FormatConditions.Add Type:=xlExpression, _'Formula1:="=RC7=MAX(C7)" .FormatConditions.Add Type:=xlExpression, _Formula1:="=RC7=МАКС(C7)" .FormatConditions(1).Interior.ColorIndex = 4' В англоязычной версии Excel:' .FormatConditions.Add Type:=xlExpression, _'Formula1:="=RC7=MIN(C7)" .FormatConditions.Add Type:=xlExpression, _Formula1:="=RC7=МИН(C7)" .FormatConditions(2).Interior.ColorIndex = 6 End WithEnd Sub
Для нахождения строки с наибольшим значением в столбце G используется фор&мула МАКС(C7) (MAX(C7)), синтаксис которой позволяет считать ее и A1&, и R1C1&формулой. Вспомним, что Microsoft изначально использовала ссылки в стиле R1C1и ввела поддержку ссылок в стиле A1 только для совместимости с Lotus 1&2&3. По&скольку в первую очередь Excel пытается проинтерпретировать формулу с исполь&зованием ссылок в стиле R1C1 (это недокументированная особенность Excel), вы&полнение приведенного выше кода приведет к желаемому результату.А теперь представьте себе, что случится, если вы зададите условное форматиро&вание с помощью формулы, ссылающейся на ячейку C7 или R22. Вы получите со&всем не тот результат, на который рассчитывали, поскольку Excel воспримет этиссылки как ссылки на 7&й столбец и 22&ю строку, соответственно. Именно поэтомупри задании условного форматирования рекомендуется использовать толькоR1C1&формулы.
Использование ссылок в стиле R1C1при создании формулы массива
Формулы массива YYYY это одни из наиболее мощных формул в Excel. ИноY
гда их называют CSEYформулами, поскольку при вводе формулы массива исY
пользуется комбинация клавиш <Ctrl+Shift+Enter>.
Формула массива, введенная в ячейке E20 (рис. 6.14), реализует 18 операций
умножения и суммирование полученных при каждом умножении результатов.
Если ввести формулу массива без применения комбинации клавиш
<Ctrl+Shift+Enter>, в ячейке будет выведено сообщение об ошибке #ЗНАЧ!(#VALUE!). Обратите внимание, что при вводе формулы массива брать ее в
фигурные скобки не нужно.
Стиль записи ссылок R1C1 Глава 6 175
Рис. 6.14. Формула массива, введенная в ячейке E20, реали&зует 18 операций умножения и суммирование полученных прикаждом умножении результатов. Для ввода формулы необхо&димо использовать комбинацию клавиш <Ctrl+Shift+Enter>
Ниже приведен код, использующийся для ввода формулы массива:
Sub EnterArrayFormulas()' Ввод формулы массива.FinalRow = Cells(65536, 1).End(xlUp).RowCells(FinalRow + 1, 5).FormulaArray = _"=SUM(R2C[-1]:R[-1]C[-1]*R2C:R[-1]C)"End Sub
Несмотря на то что в интерфейсе Excel отображаются ссылки в стиле A1,
помните, что формулу массива необходимо вводить с использованием ссылок
в формате R1C1.
Следующий шагСогласно стилю R1C1, обращение к ячейке осуществляется по номеру ее
строки (R) и столбца (С). Использование R1C1Yформул позволяет создавать
более эффективный и корректный код VBA. Кроме того, употребление адреY
сации R1C1 является обязательным требованием при создании формул массиY
вов и при задании условного форматирования ячеек. В следующей главе будут
рассмотрены именованные диапазоны ячеек YYYY еще одно средство, упроY
щающее разработку макросов в Excel.
7Глобальные и локальныеимена .............................................. 177Создание имен.............................. 179Удаление имен .............................180Типы имен......................................180Скрытие имен ............................... 185Проверка существованияимени.............................................. 185Следующий шаг............................ 187
Глава 7
Èìåíà
Поле для ввода имени диапазона
ячеек, формулы, массива и т.п. распоY
лагается на рабочем листе слева от поY
ля ввода формулы. Присваивание
имен различным объектам Excel сущеY
ственно упрощает их использование.
Возможность создавать имена и
манипулировать ими доступна также
и в VBA. Далее в этой главе будут расY
смотрены различные типы имен и
способы их применения на практике.
Глобальные илокальные имена
Все имена можно разделить на две
категории YYYY глобальные имена (область
действия распространяется на всю раY
бочую книгу) и локальные имена
(область действия ограничена одним
рабочим листом). В рабочей книге моY
жет содержаться несколько одинакоY
вых локальных имен, однако только
одно уникальное глобальное имя. При
обращении к локальному имени необY
ходимо указывать соответствующий
рабочий лист (Лист1!Фрукты), тогда
как при обращении к глобальному
имени никаких дополнительных треY
бований не ставится (Фрукты).
Показанное на рис. 7.1 диалогоY
вое окно Присвоение имени (Define
Name) содержит смешанный спиY
сок, состоящий из глобальных и лоY
кальных имен.
Имя рабочего листа, соответстY
вующего локальному имени, вывоY
дится справа от последнего.
178 Часть I Первые шаги
Рис. 7.1. Диалоговое окно Присвоение имени со&держит список глобальных и локальных имен
При совпадении глобального и локального имени в окне Присвоениеимени выводится только локальное имя при условии, что соответствующий
ему рабочий лист является активным (рис. 7.2).
Рис. 7.2. При совпадении глобального и локаль&ного имени в списке имен выводится только ло&кальное имя при условии, что соответствующийему рабочий лист является активным
Если же активным является другой рабочий лист, в окне Присвоениеимени выводится только глобальное имя, как показано на рис. 7.3.
Совет Средство проверки вводимых значений (чтобы воспользоваться этим средством,выберите команду меню Excel Данные Проверка (Data Validation)) позволяетвыделить диапазон ячеек только на активном рабочем листе. Чтобы обойти этоограничение, присвойте имя диапазону данных, которые вы хотите проверить.
Имена Глава 7 179
Рис. 7.3. При совпадении глобального и локаль&ного имени в списке имен выводится только гло&бальное имя при условии, что соответствующийлокальному имени рабочий лист НЕ являетсяактивным
Создание имен Рассмотрим пример кода, сгенерированного средством записи макросов
при создании именованного диапазона ячеек:
ActiveWorkbook.Names.Add Name:="Фрукты", _RefersToR1C1:="=Лист2!R1C1:R6C6"
Как следует из приведенного выше кода, именованный диапазон ячеек ноY
сит имя Фрукты и охватывает ячейки A1:F6 (R1C1:R6C6). Формулу, опредеY
ляющую диапазон ячеек, следует предварить знаком равенства и взять в каY
вычки. Адрес диапазона задается с помощью абсолютной ссылки (с использоY
ванием знаков $) или с помощью ссылки в стиле R1C1. Если диапазон ячеек
находится на текущем активном листе, имя последнего можно не указывать
(тем не менее, указание полного адреса диапазона ячеек повышает читабельY
ность кода).
Чтобы создать локальное имя, предварите его именем рабочего листа, как
показано ниже:
ActiveWorkbook.Names.Add Name:="Лист2!Фрукты", _RefersToR1C1:="=Лист2!R1C1:R6C6"
или воспользуйтесь коллекцией Names данного рабочего листа:
Worksheets("Лист1").Names.Add Name:="Фрукты", _RefersToR1C1:="=Лист1!R1C1:R6C6"
Существует и более простой способ создания имен. Ниже приведены приY
меры создания глобального и локального имени, соответственно:
Range("A1:F6").Name = "Фрукты"Range("A1:F6").Name = "Лист1!Фрукты"
К сожалению, указанный выше способ имеет одно существенное ограниY
чение YYYY он позволяет создавать только имена диапазонов ячеек. Для создания
имен формул, строк, чисел и массивов необходимо использовать метод Add.
180 Часть I Первые шаги
Чтобы изменить существующее имя, воспользуйтесь свойством Name, как
показано ниже:
Names("Фрукты").Name = "Товар"
В результате выполнения приведенного выше кода имя Фрукты становится
недействительным; к соответствующему диапазону ячеек можно обратиться
по новому имени Товар.
Если строка Range("A1:F6").Name = "Фрукты" расположена выше
строки Range("A1:F6").Name = "Товар", имя Товар переопределяет имя
Фрукты, как показано на рис. 7.4.
Рис. 7.4. Локальное имя Фрукты было замененолокальным именем Товар. Обратите вниманиена то, что глобальное имя Фрукты по&прежнемудоступно
Попытка обращения к локальному имени Фрукты приведет к возникновеY
нию ошибки, поскольку такое имя больше не существует.
Удаление имен Чтобы удалить имя, воспользуйтесь методом Delete, как показано ниже:
Names("ТоварНомер").Delete
Попытка удаления несуществующего имени приведет к возникновению
ошибки.
Внимание При совпадении глобального и локального имени следует обратить особое вни&мание на необходимость корректного указания удаляемого имени.
Типы имен Наиболее распространенный способ использования имен заключается в
создании именованных диапазонов ячеек. Тем не менее, предназначение
имен гораздо шире YYYY они позволяют упростить доступ к большим объемам
Имена Глава 7 181
информации практически любого рода. В отличие от переменных, имена храY
нят данные и после завершения выполнения программного кода.
В следующих разделах рассматриваются имена формул, строк, чисел и
массивов.
Имена формул
Синтаксис создания имени формулы аналогичен синтаксису создания
имени диапазона ячеек, поскольку при определении последнего используется
формула:
Names.Add Name:="ТоварСписок", RefersTo:="=СМЕЩ(_Лист2!A2;0;0;СЧЁТЗ(Лист2!$A:$A))"
(В англоязычной версии Excel функции СМЕЩ соответствует функция OFFSET.)
В результате выполнения приведенной выше строки кода будет определено
имя формулы, ссылающейся на динамический столбец (рис. 7.5).
Рис. 7.5. Пример создания имени формулы
Имена строк
При создании имени строки значение последней заключается в кавычки
(знак равенства, как при создании имени диапазона и формулы, не ставится):
Names.Add Name:="Компания", RefersTo:="КомпанияА"
На рис. 7.6 показано диалоговое окно Присвоение имени (Define Name),
в списке имен которого содержится имя строки.
Совет Поскольку имена не теряют свои значения между сеансами работы в Excel, онибольше подходят для хранения данных, чем ячейки. Рассмотрим задачу храненияназвания компании, ставшей лучшим поставщиком за определенный промежутоквремени. Одним из решений этой задачи является создание имени ЛучшийПо-ставщик. Альтернативный способ хранения имени лучшего поставщика заключа&ется в использовании ячейки на отдельном рабочем листе, что менее удобно.
182 Часть I Первые шаги
Рис. 7.6. Пример создания имени строки
Следующая процедура демонстрирует пример использования ячеек для
хранения данных между сеансами работы в Excel:
Sub NoNames(ByRef CurrentTop As String) TopSeller = Worksheets("Переменные").Range("A1").Value If CurrentTop = TopSeller Then MsgBox ("Лучшим поставщиком снова стал ' &&TopSeller &'!") Else MsgBox ("Лучшим поставщиком стал " & CurrentTop) End IfEnd Sub
Ниже приведен код аналогичной процедуры, использующий для хранения
данных между сеансами работы в Excel имена:
Sub WithNames() If Evaluate("Текущий") = Evaluate("Предыдущий") Then MsgBox ("Лучшим поставщиком снова стал " & _Evaluate("Предыдущий") & "!") Else MsgBox ("Лучшим поставщиком стал " & Evaluate("Текущий")) End IfEnd Sub
Поскольку Текущий и Предыдущий являются заранее созданными именаY
ми, доступ к их значениям осуществляется напрямую, без необходимости созY
дания переменных. Обратите внимание, что для доступа к значению имени
используется метод Evaluate.
Внимание Длина строки не может превышать 255 символов.
Имена чисел
Имена могут использоваться и для хранения чисел. Ниже приведены два
примера создания имени числа YYYY с участием переменной и без нее:
NumofSales = 5123Names.Add Name:="ПродажиВсего", RefersTo:=NumofSales
Names.Add Name:="ПродажиВсего", RefersTo:=5123
Имена Глава 7 183
Обратите внимание на отсутствие кавычек и знака равенства. Кавычки
способны превратить число в строку, а знак равенства YYYY в формулу.
Чтобы извлечь значение имени числа, можно воспользоваться двумя метоY
дами YYYY простым и сокращенным:
NumofSales = Names("ПродажиВсего").Value
NumofSales = [ПродажиВсего]
Следует отметить, что использование квадратных кавычек равносильно
вызову метода Evaluate и способно в определенной степени затруднить поY
нимание программного кода. Чтобы сделать код более читабельным, рекоY
мендуется воздержаться от использования метода Evaluate или снабдить соY
ответствующие фрагменты кода комментариями.
Имена массивов
Имена могут использоваться для хранения целых массивов. Размер массиY
ва ограничен 256 столбцами и 5461 элементами (более подробно массивы расY
сматриваются в главе 17, ‘‘Массивы’’).
Создание имени массива похоже на создание имени числа:
Sub NamedArray() Dim myArray(10, 5) Dim i As Integer, j As Integer' Заполнение массива myArray значениями. For i = 1 To 10 For j = 1 To 5 myArray(i, j) = i + j Next j Next i' Создание имени массива. Names.Add Name:="Массив", RefersTo:=myArrayEnd Sub
Как и при создании имени числа, обратите внимание на отсутствие кавыY
чек и знака равенства.
Зарезервированные имена
Excel содержит несколько зарезервированных имен, которые не рекоменY
дуется использовать при создании собственных имен.
Рассмотрим небольшой пример. Выделите произвольный диапазон ячеек
на рабочем листе и выберите команду меню Excel Файл Область печатиЗадать (File Print Area Set Print Area).
Как показано на рис. 7.7, Excel автоматически присваивает выделенному
диапазону ячеек имя Область_печати (Print_Area).
Это одно из зарезервированных имен, значение которого сохраняется межY
ду сеансами работы в Excel (попробуйте сохранить, закрыть, а затем снова отY
крыть рабочую книгу, и вы убедитесь, что имя Область_печати соответствуY
ет все тому же диапазону ячеек).
184 Часть I Первые шаги
Рис. 7.7. Область_печати — одно из заре&зервированных имен Excel
Внимание Каждый рабочий лист имеет собственную область печати. Назначение новой об&ласти печати переопределяет текущее значение имени Область_печати для дан&ного рабочего листа.
Ниже приведен список зарезервированных имен Excel:
Критерии (Criteria);
База_данных (Database);
Извлечь (Extract);
Область_печати (Print_Area);
Заголовки_для_печати (Print_Titles).
Имена Критерии и Извлечь используются при копировании результата
применения расширенного фильтра в новое место (чтобы применить расY
ширенный фильтр, выберите команду меню Excel Данные ФильтрРасширенный фильтр (Data Filter Advanced Filter)).
Имя База_данных использовалось в предыдущих версиях Excel для обоY
значения данных, которыми манипулировали определенные функции. Сейчас
имя База_данных используется разве что при создании форм ввода данных
(чтобы создать форму ввода данных, выберите команду меню Excel ДанныеФорма (Data Form)).
Имя Область_печати используется при задании области печати (выберите
команду Файл Область печати Задать) или ее изменении посредством
диалогового окна Параметры страницы (Page Setup) (выберите команду
Файл Параметры страницы (File Page Setup)).
Имя Заголовки_для_печати используется при установке заголовков пеY
чати (выберите команду Файл Параметры страницы, перейдите во вкладку
Лист (Sheet) и установите флажок Заголовки строк и столбцов (Row and coY
lumn headings)).
Зарезервированные имена (а также похожие на них) не рекомендуется исY
пользовать при создании собственных имен. Предположим, что вы создали
имя ОбластьПечати и по ошибке написали следующий код:
Worksheets("Лист4").Names("Область_печати").Delete
Имена Глава 7 185
В результате выполнения приведенного выше кода вместо имени Область-Печати будет удалено зарезервированное имя Excel.
Скрытие именБлагодаря наличию у объекта Name свойства Visible имя можно скрыть.
Чтобы скрыть имя, установите значение свойства Visible равным False;
чтобы вновь сделать имя видимым, установите значение свойства Visibleравным True:
Names.Add Name:="ТоварНомер", RefersTo:="=$A$1", Visible:=False
Внимание Если пользователь создаст имя, совпадающее с существующим скрытым именем,последнее будет переопределено безо всякого предупреждения. Чтобы избежатьэтого, защитите рабочий лист.
Проверка существования имениСледующая функция проверяет существование имени (включая скрытые
имена), за исключением зарезервированных имен Excel:
Function NameExists(FindName As String) As Boolean Dim Rng As Range Dim myName As String On Error Resume Next myName = ActiveWorkbook.Names(FindName).Name If Err.Number = 0 Then NameExists = True Else NameExists = FalseEnd Function
Предыдущий код также является великолепным примером того, как можно
извлечь выгоду из ошибок. Если переданное функции имя не существует, генеY
рируется ошибка, однако благодаря строке On Error Resume Next код проY
должает выполняться как ни в чем не бывало. Определить факт наличия ошибки
поможет свойство Err.Number. Если значение этого свойства равно 0, переY
данное функции имя существует (при выполнении кода не было сгенерировано
ошибки), в противном случае YYYY нет (была сгенерирована ошибка).
Практикум
Использование именованных диапазонов ячеекпри поиске значений в столбце данныхРассмотрим следующую задачу. Каждый день в Excel импортируется файл со све&дениями о продажах товара в сети розничных магазинов. Файл содержит номера
186 Часть I Первые шаги
магазинов, но не их названия. Необходимо реализовать автоматическое добавле&ние названий магазинов с целью последующего создания отчетов.Типичное решение этой задачи заключается в создании таблицы с названиямивсех магазинов на дополнительном рабочем листе и применении макроса для до&бавления названий магазинов на основном рабочем листе.Всю процедуру можно разбить на 6 этапов.1. Импортирование файла данных.
2. Нахождение всех уникальных номеров магазинов.
3. Проверка, все ли номера магазинов внесены в таблицу соответствия номеров иназваний магазинов.
4. Если необходимо, занести в таблицу соответствия номеров и названий магази&нов сведения о новых магазинах.
5. Переопределить именованный диапазон ячеек так, чтобы он включал в себяобновленную таблицу соответствия номеров и названий магазинов.
6. Добавить названия магазинов на основном рабочем листе.
Представленный далее исходный код макроса реализует приведенную выше по&следовательность действий:Sub ImportData()
Dim WSD As Worksheet Dim WSM As Worksheet Dim WB As Workbook
Set WB = ThisWorkbook' Основной рабочий лист - "Данные". Set WSD = ThisWorkbook.Worksheets("Данные")' Вспомогательный рабочий лист - "Таблица". Set WSM = ThisWorkbook.Worksheets("Таблица")
' Код импорта файла закомментирован.' Предполагается, что данные уже импортированы.' Импортировать файл. ' Workbooks.Open Filename:="C:\Sales.csv"' Скопировать данные и закрыть файл. 'Range("A1").CurrentRegion.Copy Destination:=WSD.Range("A1") 'ActiveWorkbook.Close SaveChanges:=False
' Активизировать рабочий лист с данными.' Составить список уникальных номеров магазинов. WSD.Activate FinalRow = Cells(65536, 1).End(xlUp).Row Range("A1").Resize(FinalRow, 1).AdvancedFilter _Action:=xlFilterCopy, CopyToRange:=Range("Z1"), Unique:=True
' Проверить, все ли номера магазинов находятся в таблице' соответствия номеров и названий магазинов. FinalStore = Range("Z65536").End(xlUp).Row Range("AA1").Value = "В списке?"' В англоязычной версии Excel:' Range("AA2:AA" & FinalStore).FormulaR1C1 = _' "=ISNA(VLOOKUP(RC[-1],СписокМагазинов,1,False))"
Имена Глава 7 187
Range("AA2:AA" & FinalStore).FormulaR1C1Local = _ "=ЕНД(ВПР(RC[-1];СписокМагазинов;1;ЛОЖЬ))"
' Найти строку для ввода сведений о новом магазине. NextRow = WSM.Range("A65536").End(xlUp).Row + 1
' Добавить сведения о новых магазинах. For i = 2 To FinalStore If Cells(i, 27).Value = True Then ThisStore = Cells(i, 26).Value WSM.Cells(NextRow, 1).Value = ThisStore WSM.Cells(NextRow, 2).Value = InputBox(Prompt:=" _Введите имя магазина " & ThisStore, Title:="Найден новый магазин") NextRow = NextRow + 1 End If Next i
' Удалить вспомогательный список магазинов. Range("Z1:AA" & FinalStore).Clear
' Переопределить имя "СписокМагазинов". FinalStore = WSM.Range("A65536").End(xlUp).Row WSM.Range("A1:B" & FinalStore).Name = "СписокМагазинов"
' Добавить названия магазинов. Range("B1").EntireColumn.Insert Range("B1").Value = "МагазинНазвание"' В англоязычной версии Excel:' Range("B2:B" & FinalRow).FormulaR1C1 = _"=VLOOKUP(RC1,СписокМагазинов,2,False)" Range("B2:B" & FinalRow).FormulaR1C1Local = _"=ВПР(RC1;СписокМагазинов;2;ЛОЖЬ)"
'Заменить формулы значениями. Range("B2:B" & FinalRow).Value = Range("B2:B" &FinalRow).ValueEnd Sub
Следующий шагСледующая глава посвящена событиям YYYY концепции, позволяющей VBA
реагировать на действия пользователя, такие как выделение ячейки, смена теY
кущего активного рабочего листа и т.п.
8Использование событий............. 190События рабочей книги...............191События рабочего листа............. 199События листа диаграммы....... 204События приложения................. 208Следующий шаг............................214
Глава 8
Ñîáûòèÿ
События позволяют реагировать
на всевозможные действия, происхоY
дящие в пределах текущего сеанса
работы с Excel.
Различают следующие уровни соY
бытий.
Уровень приложения. ОхватыY
вает действия, имеющие неY
посредственное отношение к
Excel, например Applica-tion_NewWorkbook.
Уровень рабочей книги. ОхваY
тывает действия, имеющие
непосредственное отношение
к рабочей книге, например
Workbook_Open.
Уровень рабочего листа. ОхватыY
вает действия, имеющие непоY
средственное отношение к раY
бочему листу, например Work-sheet_SelectionChange.
Уровень листа диаграммы. ОхY
ватывает действия, имеющие
непосредственное отношение
к листу диаграммы, например
Chart_Activate.
Код обработки событий рабочей
книги размещается в модуле Эта-Книга (ThisWorkbook), код обработY
ки событий рабочего листа YYYY в модуY
ле этого рабочего листа (например,
Лист1), код обработки событий листа
диаграммы и Excel — в соответствуюY
щих модулях классов. При обработке
событий можно вызывать процедуры
и функции из других модулей.
190 Часть I Первые шаги
Таким образом, нет необходимости дублировать один и тот же код для неY
скольких рабочих листов YYYY его можно поместить в отдельный модуль и вызыY
вать при обработке события каждого листа.
В этой главе рассматриваются события различных уровней и их использоY
вание на практике.
Использование событий Запомнить синтаксис всех событий достаточно сложно. К счастью, редакY
тор Visual Basic позволяет быстро выбрать требуемое событие и добавить код
его обработки в соответствующий модуль.
При выборе модуля ЭтаКнига (ThisWorkbook), модуля листа или модуля
класса соответствующие события становятся доступны посредством расY
крывающихся списков Object (Объект) и Procedure (Процедура), как поY
казано на рис. 8.1.
Рис. 8.1. Редактор Visual Basic позволяет выбрать требуемое событие с помощью раскрываю&щихся списков Object и Procedure
После выбора объекта из раскрывающегося списка Object в раскрывающемY
ся списке Procedure появляются соответствующие этому объекту события. ВыY
бор события приводит к автоматической генерации верхнего (Private Sub) и
нижнего (End Sub) заголовка процедуры, как показано на рис. 8.2.
Рис. 8.2. Редактор Visual Basic автоматически генерирует верхний и нижний заголовок процедуры
Параметры событий
Некоторые события имеют параметры (например, Target и Cancel),
предназначенные для передачи коду обработки события определенной инY
формации. Рассмотрим событие Worksheet_BeforeRightClick, срабатыY
вающее перед выполнением действия, соответствующего щелчку правой
кнопкой мыши в пользовательском интерфейсе Excel. Присвоение параметру
События Глава 8 191
Cancel значения True предотвращает выполнение стандартного действия,
в данном случае YYYY отображения контекстного меню:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, _Cancel As Boolean) Cancel = TrueEnd Sub
Запрет обработки событий
Некоторые события могут вызывать другие события, включая самих себя.
Например, событие Worksheet_Change срабатывает при изменении ячейки
на рабочем листе. Если при обработке этого события вновь будет изменена
ячейка, событие сработает снова, что приведет к зацикливанию.
Чтобы избежать описанной выше ситуации, запретите обработку событий
в начале кода процедуры и разрешите ее в конце, как показано ниже:
Private Sub Worksheet_Change(ByVal Target As Range) Application.EnableEvents = False Range("A1").Value = Target.Value Application.EnableEvents = TrueEnd Sub
На заметку Чтобы прервать выполнение макроса, нажмите клавишу <Esc> или воспользуй&тесь комбинацией клавиш <Ctrl+Break>.
События рабочей книги Ниже приведен список событий, существующих на уровне рабочей
книги Excel:
Activate;
Deactivate;
Open;
BeforeSave;
BeforePrint;
BeforeClose;
NewSheet;
SheetActivate;
SheetDeactivate;
SheetCalculate;
SheetBeforeDoubleClick;
SheetSelectionChange;
192 Часть I Первые шаги
SheetBeforeRightClick;
SheetChange;
WindowResize;
WindowActivate;
WindowDeactivate;
AddinInstall;
AddinUninstall.
Событие Workbook_Activate()
Событие Workbook_Activate срабатывает, когда содержащая это собыY
тие рабочая книга становится активной.
Событие Workbook_Deactivate()
Событие Workbook_Deactivate срабатывает, когда содержащая это соY
бытие рабочая книга перестает быть активной.
Событие Workbook_Open()
Событие Workbook_Open является стандартным событием рабочей книги.
Код его обработки выполняется непосредственно при открытии файла рабочей
книги, перед выводом на экран пользовательского интерфейса. Одним из наиY
более распространенных применений события Workbook_Open является проY
верка имени пользователя и определение его прав на доступ к рабочей книге.
Следующий код проверяет имя пользователя и, если оно не равно
‘‘Администратор’’, защищает рабочий лист от внесения изменений со стороY
ны пользователя (за это отвечает параметр UserInterfaceOnly):
Private Sub Workbook_Open() Dim sht As Worksheet If Application.UserName <> "Администратор" Then For Each sht In Worksheets sht.Protect UserInterfaceOnly:=True Next sht End IfEnd Sub
Событие Workbook_Open можно применять и для создания пользовательY
ских меню или панелей управления. Следующий код создает меню МенюMrExcel с двумя пунктами, как показано на рис. 8.3.
См. также Более подробно создание пользовательского меню рассматривается в разделе“Создание пользовательского меню” главы 24 на с. 575.
События Глава 8 193
Private Sub Workbook_Open() Dim cbWSMenuBar As CommandBar Dim Ctrl As CommandBarControl, muCustom As CommandBarControl Dim iHelpIndex As Integer Dim sht As Worksheet
' Инициализация событий приложения и листа диаграммы. InitializeAppEvent InitializeChart
CreateToolbar
Set cbWSMenuBar = Application.CommandBars("Worksheet menu bar") iHelpIndex = cbWSMenuBar.Controls("Справка").Index Set muCustom = cbWSMenuBar.Controls.Add(Type:=msoControlPopup, _Before:=iHelpIndex, Temporary:=True) For Each Ctrl In cbWSMenuBar.Controls If Ctrl.Caption = "Меню &MrExcel" Then cbWSMenuBar.Controls("Меню MrExcel").Delete End If Next Ctrl With muCustom .Caption = "Меню &MrExcel" With .Controls.Add(Type:=msoControlButton) .Caption = "&Импорт" .OnAction = "Import" End With With .Controls.Add(Type:=msoControlButton) .Caption = "&Экспорт" .OnAction = "Export" End With End WithEnd Sub
Рис. 8.3. Событие Workbook_Open можно применять для создания пользова&тельского меню
Событие Workbook_BeforeSave(ByVal SaveAsUI As Boolean,Cancel As Boolean)
Событие Workbook_BeforeSave срабатывает при попытке сохранения
рабочей книги. Чтобы отобразить диалоговое окно Сохранение документа(Save As), установите значение параметра SaveAsUI равным True. Чтобы заY
претить сохранение рабочей книги, установите равным True значение параY
метра Cancel.
Событие Workbook_BeforePrint(Cancel As Boolean)
Событие Workbook_BeforePrint срабатывает при попытке печати раY
бочей книги (способ инициирования процесса печати YYYY с помощью команды
меню, кнопки панели инструментов, комбинации клавиш или программного
194 Часть I Первые шаги
кода YYYY не играет роли). Чтобы запретить печать, установите значение параY
метра Cancel равным True.
Приведенный ниже код отслеживает попытки печати рабочего листа, заноY
ся в журнал дату и время печати, имя пользователя и имя рабочего листа
(рис. 8.4):
Private Sub Workbook_BeforePrint(Cancel As Boolean) Dim LastRow As Long Dim PrintLog As Worksheet
Set PrintLog = Worksheets("PrintLog") LastRow = PrintLog.Range("A65536").End(xlUp).Row + 1 With PrintLog .Cells(LastRow, 1).Value = Now() .Cells(LastRow, 2).Value = Application.UserName .Cells(LastRow, 3).Value = ActiveSheet.Name End WithEnd Sub
Рис. 8.4. Событие Workbook_BeforePrint можно ис&пользовать для ведения журнала печати
Событие Workbook_BeforePrint можно использовать для создания
верхнего и нижнего колонтитула печатаемой страницы. Несмотря на то, что
теперь это можно сделать с помощью диалогового окна Параметры страницы(Page Setup), раньше подобная возможность реализовывалась только с помоY
щью программного кода:
Private Sub Workbook_BeforePrint(Cancel As Boolean) ActiveSheet.PageSetup.RightFooter = ActiveWorkbook.FullNameEnd Sub
Событие Workbook_BeforeClose(Cancel As Boolean)
Событие Workbook_BeforeClose срабатывает при попытке закрыть раY
бочую книгу. Чтобы сделать закрытие рабочей книги невозможным, устаноY
вите значение параметра Cancel равным True.
Событие Workbook_BeforeClose можно применить для удаления польY
зовательского меню:
Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim cbWSMenuBar As CommandBar On Error Resume Next Set cbWSMenuBar = Application.CommandBars("Worksheet menu bar") cbWSMenuBar.Controls("Меню MrExcel").DeleteEnd Sub
Приведенный выше код имеет один недостаток. Если в рабочую книгу быY
ли внесены изменения, Excel предложит сохранить их, отобразив соответстY
События Глава 8 195
вующее диалоговое окно после выполнения кода обработки события Work-book_BeforeClose. Если вы передумаете закрывать книгу и щелкните на
кнопке Отмена (Cancel), то уже не сможете вернуть удаленное меню.
Решение этой проблемы заключается в выводе на экран собственного диаY
логового окна сохранения рабочей книги:
Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim Msg As String Dim Response Dim cbWSMenuBar As CommandBar
If Not ThisWorkbook.Saved Then Msg = "Сохранить изменения, внесенные в " & Me.Name & "?" Response = MsgBox(Msg, vbQuestion + vbYesNoCancel)
Select Case Response Case vbYes ThisWorkbook.Save Case vbNo ThisWorkbook.Saved = True Case vbCancel Cancel = True Exit Sub End Select End If
Set myAppEvent = Nothing Set myClassModule = Nothing
On Error Resume Next Set cbWSMenuBar = Application.CommandBars("Worksheet menu bar") cbWSMenuBar.Controls("Меню MrExcel").DeleteEnd Sub
Событие Workbook_NewSheet(ByVal Sh As Object)
Событие Workbook_NewSheet срабатывает при добавлении в текущую
активную рабочую книгу нового листа. Sh — это объект нового рабочего листа
или листа диаграммы.
Событие Workbook_WindowResize(ByVal Wn As Window)
Событие Workbook_WindowResize срабатывает при изменении размера
окна текущей активной рабочей книги. Wn — это объект окна.
На заметку Событие Workbook_WindowResize срабатывает только при изменении размераокна текущей активной рабочей книги. Изменение размера окна Excel является со&бытием уровня приложения и не имеет никакого отношения к событиям уровнярабочей книги.
196 Часть I Первые шаги
Следующий код делает невозможным изменение размера окна текущей акY
тивной рабочей книги:
Private Sub Workbook_WindowResize(ByVal Wn As Window) Wn.EnableResize = FalseEnd Sub
Внимание Запрет изменения размера окна рабочей книги приводит к удалению кнопок окнаСвернуть (Minimize) и Развернуть (Maximize). Чтобы вернуть кнопки на место,выполните в окне Immediate (Быстрое выполнение) строку ActiveWin-dow.EnableResize = True.
Событие Workbook_WindowActivate(ByVal Wn As Window)
Событие Workbook_WindowActivate срабатывает при активизации окY
на любой рабочей книги. Wn — это объект окна.
На заметку Событие Workbook_WindowActivate срабатывает только при активизации окнарабочей книги.
Событие Workbook_WindowDeactivate(ByValWn As Window)
Событие Workbook_WindowDeactivate срабатывает при деактивизации
окна любой рабочей книги. Wn — это объект окна.
На заметку Событие Workbook_WindowDeactivate срабатывает только при деактивизацииокна рабочей книги.
Событие Workbook_AddinInstall()
Событие Workbook_AddinInstall срабатывает при установке рабочей
книги в качестве надстройки (чтобы установить надстройку, выберите команY
ду меню Excel Сервис Надстройки (Tools AddYIns)). Обратите внимание,
что событие Workbook_AddinInstall не срабатывает при двойном щелчке
на значке файла надстройки (файл с расширением .xla).
Событие Workbook_AddinUninstall()
Событие Workbook_AddinUninstall срабатывает при удалении рабочей
книги, используемой в качестве надстройки. Обратите внимание, что надY
стройка не закрывается автоматически.
События Глава 8 197
Событие Workbook_SheetActivate(ByVal Sh As Object)
Событие Workbook_SheetActivate срабатывает при активизации люY
бого рабочего листа или листа диаграммы в рабочей книге. Sh — это объект
активного листа.
Кроме того, активизация рабочего листа или листа диаграммы приводит к
срабатыванию событий активного рабочего листа (Worksheet_Activate)
или листа диаграммы (Chart_Activate).
Событие Workbook_SheetBeforeDoubleClick(ByVal Sh AsObject, ByVal Target As Range, Cancel As Boolean)
Событие Workbook_SheetBeforeDoubleClick срабатывает при выY
полнении двойного щелчка на рабочем листе или листе диаграммы текущей
активной рабочей книги. Sh — это объект активного рабочего листа или листа
диаграммы, а Target — объект, на котором был выполнен двойной щелчок.
Чтобы запретить стандартное действие, предпринимаемое при обработке
двойного щелчка, установите значение параметра Cancel равным True.
Кроме того, двойной щелчок на рабочем листе или листе диаграммы приводит
к срабатыванию событий активного рабочего листа (Worksheet_Before-DoubleClick) или листа диаграммы (Chart_BeforeDoubleClick).
Событие Workbook_SheetBeforeRightClick(ByVal Sh AsObject, ByVal Target As Range, Cancel As Boolean)
Событие Workbook_SheetBeforeRightClick срабатывает при выполY
нении щелчка правой кнопкой мыши на рабочем листе или листе диаграммы
текущей активной рабочей книги. Sh — это объект активного рабочего листа
или листа диаграммы, а Target — объект, на котором был выполнен щелчок
правой кнопкой мыши. Чтобы запретить стандартное действие, предприниY
маемое при обработке щелчка правой кнопкой мыши, установите значение
параметра Cancel равным True.
Кроме того, щелчок правой кнопкой мыши на рабочем листе или листе
диаграммы приводит к срабатыванию событий активного рабочего листа
(Worksheet_BeforeRightClick) или листа диаграммы (Chart_Before-RightClick).
Событие Workbook_SheetCalculate(ByVal Sh As Object)
Событие Workbook_SheetCalculate срабатывает при пересчете рабоY
чего листа или обновлении листа диаграммы. Sh — это объект активного раY
бочего листа или листа диаграммы.
198 Часть I Первые шаги
Кроме того, пересчет рабочего листа или обновление листа диаграммы
приводит к срабатыванию событий активного рабочего листа (Worksheet_Calculate) или листа диаграммы (Chart_Calculate).
Событие Workbook_SheetChange(ByVal Sh As Object, ByValTarget As Range)
Событие Workbook_SheetChange срабатывает при изменении любого
диапазона ячеек на рабочем листе. Sh — это объект рабочего листа, Target —
объект измененного диапазона ячеек.
Кроме того, изменение диапазона ячеек рабочего листа приводит к срабаY
тыванию события конкретного рабочего листа (Worksheet_Change).
Событие Workbook_SheetDeactivate(ByVal Sh As Object)
Событие Workbook_SheetDeactivate срабатывает при деактивизации
любого рабочего листа или листа диаграммы в текущей активной рабочей
книге. Sh — это объект рабочего листа или листа диаграммы, который был деY
активизирован.
Кроме того, деактивизация рабочего листа или листа диаграммы приводит к
срабатыванию событий конкретного рабочего листа (Worksheet_Deactivate)
или листа диаграммы (Chart_Deactivate).
Событие Workbook_SheetFollowHyperlink(ByVal Sh AsObject, ByVal Target As Hyperlink)
Событие Workbook_SheetFollowHyperlink срабатывает при щелчке
на гиперссылке, расположенной на любом рабочем листе активной рабочей
книги. Sh — это объект активного рабочего листа, Target — объект гиперY
ссылки.
Кроме того, щелчок на гиперссылке, расположенной на рабочем листе,
приводит к срабатыванию события активного рабочего листа (Worksheet_FollowHyperlink).
Событие Workbook_SheetSelectionChange(ByVal Sh AsObject, ByVal Target As Range)
Событие Workbook_SheetSelectionChange срабатывает при выделеY
нии нового диапазона ячеек на любом рабочем листе активной рабочей книги.
Sh — это объект активного рабочего листа, Target — объект выделенного
диапазона ячеек.
Кроме того, выделение нового диапазона ячеек приводит к срабатыванию
события активного рабочего листа (Worksheet_SelectionChange).
События Глава 8 199
События рабочего листа Ниже приведен список событий, существующих на уровне рабочего
листа Excel:
Activate;
BeforeDoubleClick;
BeforeRightClick;
Calculate;
Change;
Deactivate;
FollowHyperlink;
SelectionChange.
Событие Worksheet_Activate()
Событие Worksheet_Activate срабатывает при активизации соответстY
вующего этому событию рабочего листа. Примером использования события
Worksheet_Activate является создание плавающей панели инструментов:
Private Sub Worksheet_Activate() On Error Resume Next Application.CommandBars("Панель инструментов _MrExcel").Visible = TrueEnd Sub
Событие Worksheet_Deactivate()
Событие Worksheet_Deactivate срабатывает при деактивизации соотY
ветствующего этому событию рабочего листа:
Private Sub Worksheet_Deactivate() On Error Resume Next Application.CommandBars("Панель инструментов _MrExcel").Visible = FalseEnd Sub
На заметку При переключении между рабочими листами первым срабатывает событие Work-sheet_Deactivate исходного рабочего листа, и только затем — событие Work-sheet_Activate активизированного рабочего листа.
Событие Worksheet_BeforeDoubleClick(ByVal TargetAs Range, Cancel As Boolean)
Событие Worksheet_BeforeDoubleClick срабатывает в результате
двойного щелчка на соответствующем этому событию рабочем листе. Tar-
200 Часть I Первые шаги
get — это объект выделенного диапазона ячеек. По умолчанию параметр
Cancel имеет значение False. Присвоение этому параметру значения Trueприведет к отмене стандартного действия, предпринимаемого при обработке
двойного щелчка (в данном случае YYYY переход в режим изменения содержиY
мого ячейки).
Следующий код делает невозможным переход в режим изменения содерY
жимого ячейки путем двойного щелчка на ней:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, _Cancel As Boolean) Cancel = TrueEnd Sub
На заметку Приведенный выше код не запрещает применять двойной щелчок для изменениявысоты строки и ширины столбца.
Запретив использование двойного щелчка для перехода в режим изменеY
ния содержимого ячейки, ему можно найти другое применение. Выполнение
следующего кода приведет к смене цвета заливки ячейки:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, _Cancel As Boolean) Target.Interior.ColorIndex = 3End Sub
Событие Worksheet_BeforeRightClick(ByVal Target AsRange, Cancel As Boolean)
Событие Worksheet_BeforeRightClick срабатывает при щелчке праY
вой кнопкой мыши на рабочем листе. Target — это объект, на котором
щелкнули правой кнопкой мыши. Установка значения параметра Cancelравным True запрещает выполнение стандартного действия, предпринимаеY
мого при обработке щелчка правой кнопкой мыши.
Событие Worksheet_Calculate()
Событие Worksheet_Calculate срабатывает при пересчете рабочего
листа.
Рассмотрим небольшой пример. Если прибыль, полученная за определенY
ный месяц текущего года, превысила прибыль, полученную за аналогичный
период в прошлом году, под названием месяца выводится зеленая стрелка,
указывающая вверх; в противном случае выводится красная стрелка, указыY
вающая вниз, как показано на рис. 8.5:
Private Sub Worksheet_Calculate() Select Case Range("C3").Value Case Is > Range("C4").Value SetArrow 10, msoShapeDownArrow Case Is < Range("C4").Value
События Глава 8 201
SetArrow 3, msoShapeUpArrow End SelectEnd Sub
Private Sub SetArrow(ByVal ArrowColor As Integer, ByVal ArrowDegree)' Удалить все существующие фигуры. For Each Sh In ActiveSheet.Shapes If Left(Sh.Name, 4) = "Auto" Then Sh.Delete End If Next Sh ActiveSheet.Shapes.AddShape(ArrowDegree, 17.25, _43.5, 5, 10).Select With Selection.ShapeRange With .Fill .Visible = msoTrue .Solid .ForeColor.SchemeColor = ArrowColor .Transparency = 0# End With With .Line .Weight = 0.75 .DashStyle = msoLineSolid .Style = msoLineSingle .Transparency = 0# .Visible = msoTrue .ForeColor.SchemeColor = 64 .BackColor.RGB = RGB(255, 255, 255) End With End With Range("A3").SelectEnd Sub
Рис. 8.5. Пример использования события Work-sheet_Calculate для добавления на рабочийлист фигуры
202 Часть I Первые шаги
Событие Worksheet_Change(ByVal Target As Range)
Событие Worksheet_Change срабатывает при изменении содержимого
ячейки, например, при вводе, редактировании или удалении текста. Tar-get — это объект ячейки, содержимое которой было изменено.
На заметку Событие Worksheet_Change срабатывает также при вставке значения в ячейку,однако оно не срабатывает при пересчете ячейки. При пересчете ячейки срабаты&вает событие Worksheet_Calculate.
Практикум
Рассмотрим задачу введения времени отправки и прибытия авиарейсов в 24&часовом формате (например, 23:45). Ее можно упростить, поручив Excel вводсимвола двоеточия. Вот как это сделать с помощью события Worksheet_Change: Private Sub Worksheet_Change(ByVal Target As Range) Dim ThisColumn As Integer Dim UserInput As String, NewInput As String
ThisColumn = Target.Column If ThisColumn < 3 Then UserInput = Target.Value If IsNumeric(UserInput) Then If UserInput > 1 Then NewInput = Left(UserInput, Len(UserInput) - 2) _& ":" & Right(UserInput, 2) Application.EnableEvents = False Target = NewInput Application.EnableEvents = True End If End If End If
End Sub
Приведенный выше код будет автоматически преобразовывать числа, введенныев ячейки столбцов A и B рабочего листа (If ThisColumn < 3), во время в 24&часовом формате (например, 2345 в 23:45).
Внимание Чтобы избежать зацикливания в результате изменения содержимогоячейки, запретите обработку событий с помощью строки Applicati-on.EnableEvents = False.
События Глава 8 203
Событие Worksheet_SelectionChange(ByValTarget As Range)
Событие Worksheet_SelectionChange срабатывает при выделении ноY
вого диапазона ячеек. Target — это объект выделенного диапазона ячеек.
Приведенный ниже код применяется для изменения цвета заливки столбца
и строки, на пересечении которых находится выделенная ячейка:
Private Sub Worksheet_SelectionChange(ByVal Target As Range) Dim iColor As Integer
On Error Resume Next iColor = Target.Interior.ColorIndex If iColor < 0 Then iColor = 36 Else iColor = iColor + 1 End If If iColor = Target.Font.ColorIndex Then iColor = iColor + 1 Cells.FormatConditions.Delete With Range("A" & Target.Row, Target.Address)' В англоязычной версии Excel:' .FormatConditions.Add Type:=2, Formula1:="TRUE" .FormatConditions.Add Type:=2, Formula1:="ИСТИНА" .FormatConditions(1).Interior.ColorIndex = iColor End With With Range(Target.Offset(1 - Target.Row, 0).Address & ":" & _Target.Offset(-1, 0).Address)' В англоязычной версии Excel:' .FormatConditions.Add Type:=2, Formula1:="TRUE" .FormatConditions.Add Type:=2, Formula1:="ИСТИНА" .FormatConditions(1).Interior.ColorIndex = iColor End WithEnd Sub
Внимание Указанный код обработки события Worksheet_SelectionChange отменяет лю&бое условное форматирование, заданное на рабочем листе. Кроме того, он можеточистить буфер обмена, затрудняя тем самым выполнение операций копированияи вставки в пределах данного рабочего листа.
Событие Worksheet_FollowHyperlink(ByVal TargetAs Hyperlink)
Событие Worksheet_FollowHyperlink срабатывает при щелчке на гиY
перссылке. Target — это объект гиперссылки.
204 Часть I Первые шаги
События листа диаграммы События листа диаграммы срабатывают при изменении или активизации
диаграммы. Для доступа к событиям встроенных диаграмм используются моY
дули классов.
См. также Более подробно модули классов рассматриваются в главе 20, “Создание пользо&вательских объектов, типов и коллекций”.
Ниже приведен список событий, существующих на уровне листа диаграмY
мы Excel:
Activate;
BeforeDoubleClick;
BeforeRightClick;
Calculate;
Deactivate;
DragOver;
DragPlot;
MouseDown;
MouseMove;
MouseUp;
Resize;
Select;
SeriesChange.
Встроенные диаграммы
Поскольку встроенные диаграммы не имеют отдельного листа, для доступа
к их событиям необходимо использовать модуль класса.
1. Добавьте к проекту модуль класса.
2. Переименуйте его в cl_ChartEvents.
3. Разместите в модуле класса следующую строку кода:
Public WithEvents myChartClass As Excel.Chart
Это сделает события встроенной диаграммы доступными через модуль
класса, как показано на рис. 8.6.
4. Добавьте к проекту стандартный модуль.
5. Разместите в стандартном модуле следующие строки кода:
События Глава 8 205
Dim myClassModule As New cl_ChartEventsSub InitializeChart() Set myClassModule.myChartClass = _Worksheets(1).ChartObjects(1).ChartEnd Sub
Рис. 8.6. События встроенной диаграммы доступны посредством модуля класса
Тем самым вы сделаете объект встроенной диаграммы доступным как
обычный объект диаграммы. Процедуру InitializeChart необходиY
мо выполнить один раз при открытии рабочей книги (воспользуйтесь
событием Workbook_Open).
Внимание Первая часть имени события встроенной диаграммы будет совпадать с именемобъекта диаграммы, заданном в модуле класса (в данном случае — myChart-Class).
Событие Chart_Activate()
Событие Chart_Activate срабатывает при активизации листа диаграммы.
Событие Chart_BeforeDoubleClick(ByVal ElementID As Long,ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean)
Событие Chart_BeforeDoubleClick срабатывает в результате двойного
щелчка на любом элементе диаграммы. ElementID — это объект, представY
ляющий элемент диаграммы, на котором был сделан двойной щелчок
(например, легенду). Параметры Arg1 и Arg2 зависят от значения параметра
ElementID. Чтобы запретить выполнение стандартного действия, предприY
нимаемого при обработке двойного щелчка, установите значение параметра
Cancel равным True.
Согласно приведенному ниже коду, двойной щелчок на легенде диаграммы
приводит к скрытию легенды. Чтобы вернуть легенду на место, дважды щелкY
ните на области диаграммы.
Private Sub myChartClass_BeforeDoubleClick(ByVal ElementID As _Long, ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean) Select Case ElementID
206 Часть I Первые шаги
Case xlLegend myChartClass.HasLegend = False Cancel = True Case xlChartArea myChartClass.HasLegend = True Cancel = True End SelectEnd Sub
Событие Chart_BeforeRightClick(Cancel As Boolean)
Событие Chart_BeforeRightClick срабатывает в результате щелчка на
диаграмме правой кнопкой мыши. Чтобы запретить выполнение стандартного
действия, предпринимаемого при обработке щелчка правой кнопкой мыши,
установите значение параметра Cancel равным True.
Событие Chart_Calculate()
Событие Chart_Calculate срабатывает при изменении исходных данY
ных диаграммы.
Событие Chart_Deactivate()
Событие Chart_Deactivate срабатывает при деактивизации листа диаY
граммы.
Событие Chart_DragOver()
Событие Chart_DragOver срабатывает при перетаскивании диапазона
ячеек на диаграмму.
Событие Chart_DragPlot()
Событие Chart_DragPlot срабатывает в результате перетаскивания с поY
следующим опусканием диапазона ячеек на диаграмму.
Событие Chart_MouseDown(ByVal Button As Long, ByValShift As Long, ByVal x As Long, ByVal y As Long)
Событие Chart_MouseDown срабатывает в результате нажатия любой кнопки
мыши при условии, что указатель мыши находится над диаграммой. Параметр
Button определяет кнопку мыши, которая была нажата; параметр Shift — были
ли нажаты при этом кнопки <Shift>, <Ctrl> и <Alt>; параметр x — координату
указателя мыши по горизонтали; параметр y — координату указателя мыши
по вертикали.
Рассмотрим пример. При щелчке на диаграмме левой кнопкой мыши знаY
чения по оси Y сместятся на 50 единиц вверх, а при щелчке правой кнопкой
мыши YYYY на 50 единиц вниз. Для запрета выполнения стандартного действия,
События Глава 8 207
предпринимаемого при обработке щелчка правой кнопкой мыши (в данном
случае YYYY отображения контекстного меню) используется параметр Cancelсобытия Chart_BeforeRightClick.
Private Sub myChartClass_MouseDown(ByVal Button As Long, ByVal _Shift As Long, ByVal x As Long, ByVal y As Long) If Button = 1 Then ActiveChart.Axes(xlValue).MaximumScale = _ ActiveChart.Axes(xlValue).MaximumScale - 50 End If If Button = 2 Then ActiveChart.Axes(xlValue).MaximumScale = _ ActiveChart.Axes(xlValue).MaximumScale + 50 End IfEnd Sub
Событие Chart_MouseMove(ByVal Button As Long,ByVal Shift As Long, ByVal x As Long, ByVal y As Long)
Событие Chart_MouseMove срабатывает при перемещении указателя
мыши над диаграммой. Параметр Button определяет кнопку мыши, которая
могла быть при этом нажата; параметр Shift — были ли нажаты кнопки
<Shift>, <Ctrl> и <Alt>; параметр x — координату указателя мыши по гориY
зонтали; параметр y — координату указателя мыши по вертикали.
Событие Chart_MouseUp(ByVal Button As Long, ByVal ShiftAs Long, ByVal x As Long, ByVal y As Long)
Событие Chart_MouseUp срабатывает в результате отпускания нажатой
кнопки мыши при условии, что указатель мыши находится над диаграммой.
Параметр Button определяет кнопку мыши, которая была нажата, а затем отY
пущена; параметр Shift — были ли нажаты при этом кнопки <Shift>, <Ctrl> и
<Alt>; параметр x — координату указателя мыши по горизонтали; параметр y —
координату указателя мыши по вертикали.
Событие Chart_Resize()
Событие Chart_Resize срабатывает при изменении размера диаграммы.
Если диаграмма находится на отдельном листе, событие Chart_Resize сраY
батывает при условии установки параметра Вид По размеру окна (View
Sized with Window) в главном меню Excel.
Событие Chart_Select(ByVal ElementID As Long, ByValArg1 As Long, ByVal Arg2 As Long)
Событие Chart_Select срабатывает при выделении элемента диаграмY
мы. ElementID — это объект, представляющий выделенный элемент диаY
208 Часть I Первые шаги
граммы (например, легенда). Параметры Arg1 и Arg2 зависят от значения
параметра ElementID.
В соответствии со следующим кодом выделение элемента диаграммы приY
водит к выделению соответствующего этому элементу набора данных:
Private Sub myChartClass_Select(ByVal ElementID As Long, _ByVal Arg1 As Long, ByVal Arg2 As Long)
If Arg1 = 0 Then Exit Sub
Sheets("Встроенная диаграмма").Cells.Interior.ColorIndex = xlNone
If ElementID = 3 Then If Arg2 = -1 Then' Выделить всю последовательность данных. Sheets("Встроенная диаграмма").Range("A2:A22").Offset(0, _Arg1).Interior.ColorIndex = 19 Else' Выделить значение, соответствующее указанному маркеру. Sheets("Встроенная диаграмма").Range("A1").Offset(Arg2, _Arg1).Interior.ColorIndex = 19 End If End IfEnd Sub
Событие Chart_SeriesChange(ByVal SeriesIndex As Long,ByVal PointIndex As Long)
Событие Chart_SeriesChange срабатывает при обновлении ряда данY
ных на диаграмме. SeriesIndex — это смещение в коллекции Seriesобновленного ряда данных, PointIndex — смещение в коллекции Pointsобновленной точки данных.
События приложенияСобытия приложения охватывают все рабочие книги, открытые в рамках
текущего сеанса работы с Excel. Для доступа к событиям приложения необхоY
димо использовать модуль класса (в этом смысле события приложения имеют
много общего с событиями встроенной диаграммы).
1. Добавьте к проекту модуль класса.
2. Переименуйте его в cl_AppEvents.
3. Разместите в модуле класса следующую строку кода:
Public WithEvents AppEvent As Application
Это сделает события приложения доступными через модуль класса, как
показано на рис. 8.7.
4. Добавьте к проекту стандартный модуль.
События Глава 8 209
Рис. 8.7. События приложения доступны посредством модуля класса
5. Разместите в стандартном модуле следующие строки кода:
Dim myAppEvent As New cl_AppEventsSub InitializeAppEvent() Set myAppEvent.AppEvent = ApplicationEnd Sub
Приведенный выше код делает объект AppEvent доступным как объект
приложения. Процедуру InitializeAppEvent необходимо выполY
нить один раз при открытии рабочей книги (воспользуйтесь событием
Workbook_Open).
Внимание Первая часть имени события приложения будет совпадать с именем объекта при&ложения, заданном в модуле класса (в данном случае — AppEvent).
Ниже приведен список событий, существующих на уровне приложения Excel:
NewWorkbook;
SheetActivate;
SheetBeforeDoubleClick;
SheetBeforeRightClick;
SheetCalculate;
SheetChange;
SheetDeactivate;
SheetFollowHyperlink;
SheetSelectionChange;
WindowActivate;
WindowDeactivate;
WindowResize;
WorkbookActivate;
210 Часть I Первые шаги
WorkbookAddinInstall;
WorkbookAddinUninstall;
WorkbookBeforeClose;
WorkbookBeforePrint;
WorkbookBeforeSave;
WorkbookDeactivate;
WorkbookNewSheet;
WorkbookOpen.
Событие AppEvent_NewWorkbook(ByVal Wb As Workbook)
Событие AppEvent_NewWorkbook срабатывает при создании новой рабоY
чей книги. Wb — это объект созданной рабочей книги. Следующий код упоряY
дочивает на экране окна открытых рабочих книг:
Private Sub AppEvent_NewWorkbook(ByVal Wb As Workbook) Application.Windows.Arrange xlArrangeStyleTiledEnd Sub
Событие AppEvent_SheetActivate(ByVal Sh As Object)
Событие AppEvent_SheetActivate срабатывает при активизации люY
бого рабочего листа или листа диаграммы. Sh — это объект активизированY
ного рабочего листа или листа диаграммы.
Событие AppEvent_SheetBeforeDoubleClick(ByVal ShAs Object, ByVal Target As Range, Cancel As Boolean)
Событие AppEvent_SheetBeforeDoubleClick срабатывает в результаY
те двойного щелчка на любом рабочем листе. Sh — это объект активного рабоY
чего листа, Target — объект выделенного диапазона ячеек. Чтобы запретить
выполнение стандартного действия, предпринимаемого при обработке двойY
ного щелчка, установите значение параметра Cancel равным True (по умолY
чанию значение параметра Cancel равно False).
Событие AppEvent_SheetBeforeRightClick(ByVal ShAs Object, ByVal Target As Range, Cancel As Boolean)
Событие AppEvent_SheetBeforeRightClick срабатывает в результате
щелчка правой кнопкой мыши на любом рабочем листе. Sh — это объект акY
тивного рабочего листа, Target — объект, на котором был выполнен щелчок
правой кнопкой мыши. Чтобы запретить выполнение стандартного действия,
предпринимаемого при обработке щелчка правой кнопкой мыши, установите
значение параметра Cancel равным True.
События Глава 8 211
Событие AppEvent_SheetCalculate(ByVal Sh As Object)
Событие AppEvent_SheetCalculate срабатывает при пересчете любого
рабочего листа или изменении данных любой диаграммы. Sh — это объект акY
тивного листа.
Событие AppEvent_SheetChange(ByVal Sh As Object,ByVal Target As Range)
Событие AppEvent_SheetChange срабатывает при изменении содержиY
мого любой ячейки. Sh — это объект рабочего листа, Target — объект измеY
ненного диапазона ячеек.
Событие AppEvent_SheetDeactivate(ByVal Sh As Object)
Событие AppEvent_SheetDeactivate срабатывает при деактивизации
любого рабочего листа или листа диаграммы. Sh — это объект деактивизироY
ванного листа.
Событие AppEvent_SheetFollowHyperlink(ByVal ShAs Object, ByVal Target As Hyperlink)
Событие AppEvent_SheetFollowHyperlink срабатывает при щелчке
на любой гиперссылке. Sh — это объект активного рабочего листа, Target —
объект гиперссылки.
Событие AppEvent_SheetSelectionChange(ByVal ShAs Object, ByVal Target As Range)
Событие AppEvent_SheetSelectionChange срабатывает при выделеY
нии нового диапазона ячеек на любом листе. Sh — это объект активного лисY
та, Target — объект выделенного диапазона ячеек.
Событие AppEvent_WindowActivate(ByVal WbAs Workbook, ByVal Wn As Window)
Событие AppEvent_WindowActivate срабатывает при активизации окY
на любой рабочей книги. Wb — это объект рабочей книги, отображаемой в акY
тивизированном окне, Wn — объект окна.
Событие AppEvent_WindowDeactivate(ByVal WbAs Workbook, ByVal Wn As Window)
Событие AppEvent_WindowDeactivate срабатывает при деактивизации
окна любой рабочей книги. Wb — это объект рабочей книги, отображаемой в
деактивизированном окне, Wn — объект окна.
212 Часть I Первые шаги
Событие AppEvent_WindowResize(ByVal Wb As Workbook,ByVal Wn As Window)
Событие AppEvent_WindowResize срабатывает при изменении размеров
окна активной рабочей книги. Wb — это объект активной рабочей книги, Wn —
объект окна.
Внимание Запрет изменения размера окна рабочей книги (EnableResize = False) при&водит к удалению кнопок окна Свернуть (Minimize) и Развернуть (Maximize). Что&бы вернуть кнопки на место, выполните в окне Immediate (Быстрое выполнение)строку ActiveWindow.EnableResize = True.
Событие AppEvent_WorkbookActivate(ByVal WbAs Workbook)
Событие AppEvent_WorkbookActivate срабатывает при активизации
любой рабочей книги. Wb — это объект активизированной рабочей книги.
Следующий код указывает на необходимость развернуть окно рабочей книги
при ее активизации:
Private Sub AppEvent_WorkbookActivate(ByVal Wb As Workbook) Wb.WindowState = xlMaximizedEnd Sub
Событие AppEvent_WorkbookAddinInstall(ByVal WbAs Workbook)
Событие AppEvent_WorkbookAddinInstall срабатывает при установке
рабочей книги в качестве надстройки (чтобы установить надстройку, выбериY
те команду меню Excel Сервис Надстройки (Tools AddYIns)). Обратите
внимание, что событие AppEvent_WorkbookAddinInstall не срабатывает
при двойном щелчке на значке файла надстройки (файл с расширением
.xla). Wb — это объект рабочей книги, установленной в качестве надстройки.
Событие AppEvent_WorkbookAddinUninstall(ByVal WbAs Workbook)
Событие AppEvent_WorkbookAddinUninstall срабатывает при удалеY
нии рабочей книги, используемой в качестве надстройки. Обратите внимание,
что надстройка не закрывается автоматически. Wb YYYY это объект удаляемой
рабочей книги, использовавшейся в качестве надстройки.
События Глава 8 213
Событие AppEvent_WorkbookBeforeClose(ByVal Wb AsWorkbook, Cancel As Boolean)
Событие AppEvent_WorkbookBeforeClose срабатывает при закрытии
рабочей книги. Wb — это объект рабочей книги. Чтобы запретить закрытие раY
бочей книги, установите значение параметра Cancel равным True.
Событие AppEvent_WorkbookBeforePrint(ByVal Wb AsWorkbook, Cancel As Boolean)
Событие AppEvent_WorkbookBeforePrint срабатывает при попытке
печати рабочей книги (способ инициирования процесса печати YYYY с помощью
команды меню, кнопки панели инструментов, комбинации клавиш или проY
граммного кода YYYY не играет роли). Wb — это объект рабочей книги. Чтобы заY
претить печать, установите значение параметра Cancel равным True.
Следующий код помещает имя пользователя в нижний колонтитул печаY
таемых страниц:
Private Sub AppEvent_WorkbookBeforePrint(ByVal Wb As Workbook, _Cancel As Boolean) Wb.ActiveSheet.PageSetup.LeftFooter = Application.UserNameEnd Sub
Событие AppEvent_WorkbookBeforeSave(ByVal Wb AsWorkbook, ByVal SaveAsUI As Boolean, Cancel As Boolean)
Событие AppEvent_WorkbookBeforeSave срабатывает при попытке соY
хранения рабочей книги. Wb — это объект рабочей книги. Чтобы отобразить
диалоговое окно Сохранение документа (Save As), установите значение параY
метра SaveAsUI равным True. Чтобы запретить сохранение рабочей книги,
установите значение параметра Cancel равным True.
Событие AppEvent_WorkbookDeactivate(ByVal Wb AsWorkbook)
Событие AppEvent_WorkbookDeactivate срабатывает при деактивизаY
ции любой рабочей книги. Wb — это объект деактивизированной рабочей
книги.
Событие AppEvent_WorkbookNewSheet(ByVal Wb AsWorkbook, By Val Sh As Object)
Событие AppEvent_WorkbookNewSheet срабатывает при добавлении в
активную рабочую книгу нового листа. Wb — это объект активной рабочей
книги, Sh — объект нового рабочего листа или листа диаграммы.
214 Часть I Первые шаги
Событие AppEvent_WorkbookOpen(ByVal Wb As Workbook)
Событие AppEvent_WorkbookOpen срабатывает при открытии рабочей
книги. Wb — это объект только что открытой рабочей книги.
Следующий шагВ этой главе были рассмотрены события, которые позволяют реагировать
на всевозможные действия, происходящие в пределах текущего сеанса рабоY
ты с Excel. Следующая глава посвящена взаимодействию Excel с пользоватеY
лем. В частности, в ней будут рассмотрены такие вопросы, как запрос инY
формации у пользователя, вывод предупреждающих сообщений, а также
предоставление графического интерфейса для реализации дополнительных
способов взаимодействия.
9Способы взаимодействияс пользователем ........................... 215Создание пользовательскойформы ............................................ 216Вызов и скрытиепользовательской формы..........218Основные элементыуправления формы..................... 220Использование вкладок дляобъединения форм......................225Следующий шаг........................... 226
Глава 9
Ââåäåíèå âïîëüçîâàòåëüñêèåôîðìû
Способывзаимодействияс пользователем
В отличие от окна ввода и окна соY
общения, формы Excel выводят взаиY
модействие с пользователем на соверY
шенно иной качественный уровень.
В этой главе будут рассмотрены
базовые интерфейсы взаимодействия
с пользователем, такие как окно ввоY
да и окно сообщения, а также основы
создания пользовательских форм.
Более подробно пользовательские
формы рассматриваются в главе 21,
‘‘Пользовательские формы YYYY проY
фессиональный подход’’.
Окно ввода Окно ввода данных представляет
собой один из основных интерфейсов
взаимодействия с пользователем. НаY
страиваемым является текст сообщеY
ния, заголовок окна, значение по
умолчанию, положение окна на экраY
не, а также файл справки. Окно ввода
имеет две кнопки YYYY OK и Отмена(Cancel). Функция InputBox возвраY
щает значение типа String. В резульY
тате выполнения приведенного ниже
кода пользователю будет предложено
ввести количество месяцев, для котоY
рого необходимо подсчитать некотоY
рые статистические данные:
216 Часть I Первые шаги
AveMonths = InputBox(Prompt:="Введите количество месяцев", _ Title:="Введите количество месяцев", Default:="3")
Соответствующее окно ввода показано на рис. 9.1.
Рис. 9.1. Простейшее окно ввода данных
Окно сообщения Не менее важным интерфейсом взаимодействия с пользователем является
окно сообщения. В отличие от окна ввода, окно сообщения может иметь люY
бую комбинацию из кнопок Да (Yes), Нет (No), OK и Отмена (Cancel). НаY
страиваемым также является текст сообщения, заголовок окна и файл справY
ки. В результате выполнения приведенного ниже кода пользователю будет
предложено закрыть рабочую книгу и сохранить внесенные в нее изменения:
MyMsg = "Сохранить изменения?"MyTitle = "Закрытие рабочей книги"Response = MsgBox(MyMsg, vbExclamation + vbYesNoCancel, MyTitle)Select Case Response Case Is = vbYes ActiveWorkbook.Close SaveChanges:=False Case Is = vbNo ActiveWorkbook.Close SaveChanges:=True Case Is = vbCancel Exit SubEnd Select
Выражение Select Case используется для принятия решения относительY
но хода выполнения программного кода в зависимости от сделанного пользоваY
телем выбора. Соответствующее окно сообщения показано на рис. 9.2.
Рис. 9.2. Окно сообщения — это один изспособов базового взаимодействия спользователем
Создание пользовательской формы По сравнению с окном ввода и окном сообщения пользовательская форма
имеет гораздо более широкие возможности. К примеру, ее можно применять
для ввода пользователем личной информации, как показано на рис. 9.3.
Введение в пользовательские формы Глава 9 217
Чтобы добавить к проекту пользовательскую форму, выберите команду меY
ню редактора Visual Basic Insert UserForm (Вставить Пользовательская
форма). В результате этого к проекту будет добавлен модуль новой формы,
на месте области ввода программного кода будет отображена пустая форма и
на экране появится окно панели инструментов, как показано на рис. 9.4.
Рис. 9.3. Пример пользо&вательской формы
Рис. 9.4. Пустая пользовательская формаи панель инструментов
Изменить размер формы можно с помощью маркеров, расположенных по
бокам и в углах окна формы. Чтобы добавить на форму элемент управления,
щелкните на соответствующей ему кнопке на панели инструментов и нариY
суйте его на форме. Помещенные на форму элементы управления можно пеY
ремещать, а также изменять их размер.
На заметку По умолчанию на панели инструментов находятся только наиболее часто используе&мые элементы управления. Чтобы получить доступ к остальным элементам управления,щелкните на панели инструментов правой кнопкой мыши и выберите команду контек&стного меню Additional Controls (Дополнительные элементы управления).
После добавления элемента управления на форму его свойства становятся досY
тупны посредством окна свойств редактора Visual Basic. Свойства элемента управY
ления можно настроить как вручную, так и с помощью программного кода.
Совет Пользовательской форме, а также элементам управления рекомендуется назна&чать описательные имена. Примером стандартного имени пользовательскойформы является имя UserForm1. Измените его на что&нибудь более значащее,например, frm_AddEmp.
218 Часть I Первые шаги
Вызов и скрытие пользовательской формы Пользовательскую форму можно вызвать из любого модуля. Следующий
код выводит на экран форму frm_AddEmp:
frm_AddEmp.Show
Для вызова формы может применяться также метод Load, однако в этом
случае форма будет лишь загружена, но не выведена на экран.
Для скрытия формы применяется метод Hide. Форма будет поYпрежнему
активна, однако не видна на экране. Все элементы управления формы доступY
ны посредством программного кода.
Метод Unload выгружает форму из памяти и убирает ее с экрана. В резульY
тате выполнения приведенного ниже кода форма Me становится недоступной
как посредством пользовательского интерфейса, так и посредством проY
граммного кода:
Unload Me
Программирование пользовательской формы
Код обработки событий элементов управления формы помещается в моY
дуль этой формы. В отличие от других модулей, двойной щелчок на модуле
формы приводит к ее открытию в режиме конструктора. Чтобы просмотреть
код формы, щелкните правой кнопкой мыши на названии модуля формы или
на самой форме в режиме конструктора и выберите команду контекстного меY
ню View Code (Просмотр кода).
Чтобы ввести код обработки стандартного события элемента управления
формы, выделите требуемый элемент управления и выберите команду меню
View Code (Вид Код). Редактор Visual Basic автоматически сгенерирует заY
головки процедуры обработки стандартного события. Чтобы отобразить спиY
сок других событий данного элемента управления, выберите соответствуюY
щий ему объект из раскрывающегося списка Object (Объект) и откройте расY
крывающийся список Properties (Свойства), как показано на рис. 9.5.
Рис. 9.5. Раскрывающийся список Properties содержит все события, доступные для элементауправления формы, выбранного из списка Object
Введение в пользовательские формы Глава 9 219
Все элементы управления являются объектами с соответствующими свойY
ствами и методами. Обычно весь код, относящийся к элементу управления,
размещается в модуле формы. При обращении к элементу управления извне
этого модуля имя соответствующего объекта необходимо предварить именем
объекта формы.
Private Sub btn_EmpCancel_Click() Unload MeEnd Sub
Рассмотрим некоторые из составляющих элементов приведенного выше кода:
btn_EmpCancel — имя элемента управления;
Click — событие элемента управления;
Unload Me — код обработки события элемента управления (в данном
случае форма будет убрана с экрана и выгружена из памяти).
Практикум
Добавление элемента управленияк существующей формеДобавление элемента управления к существующей форме представляет собойвесьма непростую задачу. Щелкнув на новом элементе управления правой кноп&кой мыши и выбрав команду контекстного меню View Code (Просмотр кода), выобнаружите, что редактор Visual Basic даже не подозревает о существовании та&кого элемента. Имени объекта элемента управления нет и в раскрывающемся спи&ске Object (Объект).Чтобы добавить элемент управления к существующей форме, выполните следую&щие действия.
1. Добавьте к существующей форме все необходимые элементы управления.
2. Щелкните на названии модуля формы в диспетчере проектов правой кнопкоймыши и выберите команду контекстного меню Export File (Экспорт в файл).В открывшемся диалоговом окне Export File (Экспорт в файл) щелкните накнопке Сохранить (Save), чтобы сохранить файл формы в стандартном разме&щении.
3. Снова щелкните на названии модуля формы в диспетчере проектов правой кноп&кой мыши и выберите команду контекстного меню Remove (Удалить). В ответ напредложение экспортировать файл формы щелкните на кнопке Нет (No).
4. Щелкните правой кнопкой мыши на незанятом участке окна диспетчера про&ектов и выберите из контекстного меню команду Import File (Импорт из файла).Выберите созданный выше файл формы и щелкните на кнопке Открыть (Open).
В результате выполнения приведенной выше последовательности действий ре&дактор Visual Basic распознает все элементы управления, добавленные к сущест&вующей форме.
220 Часть I Первые шаги
Основные элементы управления формы
Форма, показанная на рис. 9.6, состоит из надписей, полей ввода и
командных кнопок.
После ввода требуемой информации пользователь щелкает на кнопке OK,
что приводит к заполнению соответствующих ячеек на рабочем листе, как поY
казано на рис. 9.7.
Рис. 9.6. Простая форма, пред&назначенная для ввода пользо&вательских данных
Рис. 9.7. Информация, введенная в форму, помещает&ся на рабочий лист Excel
Private Sub btn_EmpOK_Click() Dim LastRow As Long LastRow = Worksheets("Лист2").Range("A65536").End(xlUp).Row + 1 Cells(LastRow, 1).Value = tb_EmpName.Value Cells(LastRow, 2).Value = tb_EmpPosition.Value Cells(LastRow, 3).Value = tb_EmpHireDate.ValueEnd Sub
Примечательно, что одна и та же форма может использоваться как для ввода,
так и для извлечения информации. Чтобы приспособить показанную на рис. 9.6
форму для извлечения сведений о должности служащего и дате приема на рабоY
ту, измените код обработки события btn_EmpOK_Click, как показано ниже:
Private Sub btn_EmpOK_Click() Dim EmpFound As Range With Range("EmpList") Set EmpFound = .Find(tb_EmpName.Value) If EmpFound Is Nothing Then MsgBox("Служащий не найден!") tb_EmpName.Value = "" Exit Sub Else With Range(EmpFound.Address) tb_EmpPosition = .Offset(0, 1) tb_HireDate = .Offset(0, 2) End With End If End WithEnd Sub
Введение в пользовательские формы Глава 9 221
Использование списков и комбинированных списков
При вводе имени служащего можно допустить ошибку. Чтобы этого не
произошло, предложите пользователю выбрать имя служащего из списка или
комбинированного списка.
Список позволяет выбрать одно или несколько значений.
Комбинированный список позволяет выбрать одно значение или
ввести новое.
Заменим поле ввода имени служащего списком, как показано на рис. 9.8.
Элементы списка определяются значением свойства RowSource объекта
списка. Поскольку список служащих может меняться, для обращения к нему
рекомендуется использовать динамический именованный диапазон ячеек.
Private Sub btn_EmpOK_Click() Dim EmpFound As Range With Range("EmpList") Set EmpFound = .Find(lb_EmpName.Value) If EmpFound Is Nothing Then MsgBox("Служащий не найден!") lb_EmpName.Value = "" Exit Sub Else With Range(EmpFound.Address) tb_EmpPosition = .Offset(0, 1) tb_HireDate = .Offset(0, 2) End With End If End WithEnd Sub
Выбор нескольких значений из списка
Объект списка имеет свойство MultiSelect, позволяющее выбирать из
списка несколько значений одновременно (рис. 9.9).
Рис. 9.8. Список позволяет избе&жать ошибок ввода
Рис. 9.9. Список поддерживаетвозможность выбора несколькихзначений одновременно
222 Часть I Первые шаги
Ниже перечислены возможные значения этого свойства:
fmMultiSelectSingle — значение по умолчанию, разрешающее выY
бор из списка только одного элемента;
fmMultiSelectMulti — разрешает выбор из списка нескольких элеY
ментов одновременно, а также отмену выбора элемента путем повторY
ного щелчка на нем;
fmMultiSelectExtended — разрешает выбор из списка нескольких
элементов одновременно с применением клавиш <Ctrl> и <Shift>,
а также отмену выбора элемента путем повторного щелчка на нем.
Следующий код демонстрирует способ обращения к элементам списка,
поддерживающего множественный выбор:
Private Sub btn_EmpOK_Click() Dim LastRow As Long, i As Integer LastRow = Worksheets("Лист2").Range("A65536").End(xlUp).Row + 1 Cells(LastRow, 1).Value = tb_EmpName.Value'Какие элементы из списка выбраны? For i = 0 To lb_EmpPosition.ListCount - 1'Если элемент выбран, добавить его на рабочий лист. If lb_EmpPosition.Selected(i) = True Then Cells(LastRow, 2).Value = Cells(LastRow, 2).Value & _lb_EmpPosition.List(i) & "," End If Next i Cells(LastRow, 3).Value = tb_HireDate.ValueEnd Sub
Поскольку первый элемент списка имеет порядковый номер 0, при опреY
делении верхней границы значения переменнойYсчетчика от значения свойY
ства ListCount необходимо отнять 1:
For i = 0 To lb_EmpPosition.ListCount - 1
Использование переключателей
Переключатели должны быть отделены от остальных элеменY
тов пользовательской формы с помощью панели, как показано
на рис. 9.10.
Все переключатели одной группы должны иметь одинаковое значение
свойства GroupName, чтобы гарантировать возможность выбора только одY
ного переключателя в группе.
Совет Некоторые пользователи предпочитают выбирать переключатель путем щелчка насоответствующей ему надписи (рис. 9.11). Ниже приведен пример кода, реали&зующего такую возможность: Private Sub Lbl_Bldg1_Click() Obtn_Bldg1.Value = TrueEnd Sub
Введение в пользовательские формы Глава 9 223
Рис. 9.10. Для группирования пере&ключателей используется панель
Рис. 9.11. Некоторые пользователипредпочитают выбирать переклю&чатель путем щелчка на соответст&вующей надписи
Использование изображений
Изображения позволяют придать списку более наглядный вид, как
показано на рис. 9.12.
Рис. 9.12. Изображения придают списку более на&глядный вид
Согласно приведенному ниже коду, выбор имени служащего из списка буY
дет сопровождаться выводом фотографии этого служащего:
Private Sub lb_EmpName_Change() Dim EmpFound As Range With Range("EmpList") Set EmpFound = .Find(lb_EmpName.Value) If EmpFound Is Nothing Then MsgBox("Служащий не найден!") lb_EmpName.Value = "" Exit Sub Else
224 Часть I Первые шаги
With Range(EmpFound.Address) tb_EmpPosition = .Offset(0, 1) tb_HireDate = .Offset(0, 2) On Error Resume Next Img_Employee.Picture = LoadPicture _(ThisWorkbook.Path & Application.PathSeparator & EmpFound & ".jpg") On Error GoTo 0 End With End If End WithEnd Sub
Использование счетчиков
Как показано на рис. 9.12, поле Дата приема на работу позволяет вводить
данные в произвольном формате, например, 1/1/1 или 1 января 2001 года.
Чтобы унифицировать ввод даты приема служащего на работу, следует восY
пользоваться счетчиками.
Счетчик ограничивает пользователя вводом численного значения и
содержит кнопки увеличения и уменьшения последнего.
Рассмотрим создание счетчика для ввода месяца. Поместите счетчик
на форму. Установите свойство объекта счетчика Min равным 1 (январь),
свойство Max YYYY 12 (декабрь), свойство Value YYYY 1 (начальное значение).
Разместите рядом со счетчиком поле ввода, которое будет использоваться для
отображения текущего значения счетчика (вместо поля ввода можно примеY
нить надпись). Ниже приведен код обработки события, срабатывающего при
изменении значения счетчика:
Private Sub SpBtn_Month_Change() tb_Month.Value = SpBtn_Month.ValueEnd Sub
Добавьте на форму еще два счетчика и два поля ввода, как показано на
рис. 9.13.
Private Sub btn_EmpOK_Click() Dim LastRow As Long, i As Integer LastRow = Worksheets("Лист2").Range("A65536").End(xlUp).Row + 1 Cells(LastRow, 1).Value = tb_EmpName.Value For i = 0 To lb_EmpPosition.ListCount - 1 If lb_EmpPosition.Selected(i) = True Then Cells(LastRow, 2).Value = Cells(LastRow, 2).Value & _lb_EmpPosition.List(i) & "," End If Next i'Создание даты путем конкатенации значений полей ввода. Cells(LastRow, 3).Value = tb_Month.Value & "/" & _tb_Day.Value & "/" & tb_Year.ValueEnd Sub
Введение в пользовательские формы Глава 9 225
Использование вкладок для объединения формВкладки позволяют объединить воедино несколько различных форм.
На рис. 9.14 показан пример объединения форм для ввода служебной
и личной информации о сотруднике.
Рис. 9.13. Чтобы унифициро&вать ввод даты приема слу&жащего на работу, восполь&зуйтесь счетчиками
Рис. 9.14. Используйте вкладки для объединения несколь&ких различных форм
Совет Старайтесь планировать формы с вкладками заранее. Чтобы создать форму свкладками на основе уже существующих форм, необходимо создать новую фор&му, добавить в нее требуемое число вкладок и скопировать на них элементыуправления с имеющихся форм.
Проверка ввода обязательных данных Одним из преимуществ электронной формы является возможность проY
верки ввода обязательных данных:
If tb_EmpName.Value = "" Then frm_AddEmp.Hide MsgBox("Пожалуйста, введите имя служащего") frm_AddEmp.Show Exit SubEnd If
Закрытие формы Как и большинство окон Windows, окно пользовательской формы имеет
кнопку Закрыть (Close) (кнопка с изображением крестика), расположенную в
его правом верхнем углу. В зависимости от предназначения формы ее закрыY
тие путем щелчка на этой кнопке может оказаться весьма нежелательным.
Определить способ закрытия окна формы и, при необходимости, соответстY
вующим образом среагировать на него поможет событие QueryClose:
226 Часть I Первые шаги
Private Sub UserForm_QueryClose(Cancel As Integer, _CloseMode As Integer) If CloseMode = vbFormControlMenu Then MsgBox "Для закрытия формы щелкните на кнопке OK _или Отмена", vbCritical Cancel = True End IfEnd Sub
Согласно приведенному выше коду попытка закрытия формы с помощью
недозволенного способа приведет к выводу окна сообщения, показанного на
рис. 9.15.
Рис. 9.15. Событие QueryClose позволяет опре&делить способ закрытия окна формы и соответ&ствующим образом среагировать на него
Ниже перечислены оставшиеся способы закрытия окна формы:
vbFormCode — форма была закрыта с помощью метода Unload;
vbAppWindows — форма была закрыта в результате завершения рабоY
ты Windows;
vbTaskManager — форма была закрыта с помощью диспетчера задач.
Следующий шагВ этой главе были рассмотрены базовые интерфейсы взаимодействия с пользоY
вателем, такие как окно ввода и окно сообщения, а также основы создания польY
зовательских форм Excel. Следующая глава посвящена диаграммам YYYY одному из
наиболее эффективных средств наглядного представления данных.
IIЧасть II
Автоматизация Excel
10. Диаграммы .......................................................................... 229
11. Анализ данных с помощью расширенного фильтра ..... 267
12. Сводные таблицы............................................................... 299
13. Excel всемогущий................................................................ 363
14. Взаимодействие с Internet ............................................... 407
15. Поддержка XML в профессиональномвыпуске Excel 2003.............................................................. 427
16. Автоматизация Word ........................................................439
10Встроенные диаграммы идиаграммы, расположенныена отдельном листе .................... 229Создание диаграмм спомощью VBA ...............................232Использование объектныхпеременных для упрощениякода ................................................ 236“Анатомия” диаграммы ............237Типы диаграмм ............................ 251Параметры трехмерных икруговых диаграмм.................... 256Интерактивные диаграммы..... 260Экспорт диаграммы в файлизображения................................. 261Удивительные возможноститочечных диаграмм ................... 262Создание нестандартныхдиаграмм ...................................... 262Следующий шаг........................... 266
Глава 10
Äèàãðàììû
Говорят, лучше один раз увидеть,
чем сто раз услышать. Наглядное предY
ставление данных имеет неоспоримое
преимущество перед сухими цифрами.
Именно поэтому диаграммы являются
неотъемлемой частью всех программ
для работы с электронными таблицаY
ми, пройдя вместе с ними долгий эвоY
люционный путь развития.
В этой главе рассматривается исY
пользование VBA при выполнении
следующих базовых задач:
создание встроенных диаграмм
и диаграмм, расположенных на
отдельном листе;
выбор типа диаграммы;
изменение типа диаграммы;
форматирование, перемещение
и удаление диаграммы и ее элеY
ментов;
создание нестандартных диаY
грамм.
Одной из особенностей програмY
мирования диаграмм в Excel является
наличие двух объектных моделей,
каждая из которых соответствует разY
личному типу диаграмм.
Встроенные диаграммыи диаграммы,расположенныена отдельном листе
Изначально все диаграммы создаY
вались на отдельном листе. В середиY
230 Часть II Автоматизация Excel
не 1990Yх годов в Excel была добавлена возможность встраивать диаграмму в
существующий рабочий лист.
Наличие двух различных типов диаграмм вызвало необходимость создания
двух объектных моделей. Диаграмме, расположенной на отдельном листе, соY
ответствует объект Chart, в то время как для работы со встроенной диаграмY
мой следует использовать объект ChartObject.
Встроенные диаграммы и контейнер ChartObject
Объект ChartObject является своеобразным ‘‘контейнером’’ встроенной
диаграммы. Его основное предназначение заключается в обеспечении способа
определения размера встроенной диаграммы и ее положения на рабочем лисY
те. Эти параметры распространяются на все внедренные в диаграмму объекты,
такие как автофигуры и изображения.
Откройте любой рабочий лист, содержащий встроенную диаграмму.
Щелкните на диаграмме, удерживая нажатой клавишу <Ctrl> или <Shift>.
По бокам и в углах области диаграммы появятся круглые маркеры управления
размером белого цвета, как показано на рис. 10.1.
Рис. 10.1. Чтобы выделить контейнер ChartObject, щелкните на встроен&ной диаграмме, удерживая нажатой клавишу <Ctrl> или <Shift>. Имя кон&тейнера диаграммы появится в поле Имя слева от поля ввода формулы
В поле Имя (Name Box) слева от поля ввода формулы появится имя контейY
нера встроенной диаграммы. Это имя используется для обращения к объекту
ChartObject, как показано ниже:
ActiveSheet.ChartObjects("Диаграмма 1").Select
Диаграммы Глава 10 231
Чтобы определить смещение контейнера встроенной диаграммы от верхY
ней границы рабочего листа, выделите объект ChartObject и введите в окне
Immediate (Быстрое выполнение) редактора Visual Basic строку Print Se-lection.Top.
Отмените выделение контейнера диаграммы, после чего щелкните на неY
занятом пространстве между границей диаграммы и областью ее построения.
По бокам и в углах области диаграммы появятся квадратные маркеры управY
ления размером черного цвета (рис. 10.2), что свидетельствует о выделении
области диаграммы.
Рис. 10.2. Чтобы выделить область диаграммы, щелкните на незанятомпространстве между границей диаграммы и областью ее построения. В по&ле Имя появится стандартное имя области всех диаграмм — Областьдиаграммы
Области всех диаграмм имеют стандартное имя Область диаграммы(Chart Area), которое выводится в поле Имя слева от поля ввода формулы.
Ниже приведен код VBA, соответствующий выделению области диаграммы:
ActiveSheet.ChartObjects("Диаграмма 1").ActivateActiveChart.ChartArea.Select
Чтобы узнать смещение области диаграммы от верхней границы контейнеY
ра, выделите область диаграммы и введите в окне Immediate редактора Visual
Basic строку Print Selection.Top.
Ниже приведен пример изменения цвета области диаграммы, встроенной в
рабочий лист Excel:
Worksheets("Лист3").ChartObjects("Диаграмма 2").Chart.ChartArea. _Interior.ColorIndex = 2
232 Часть II Автоматизация Excel
Диаграммы, расположенные на отдельном листе При работе с диаграммами, расположенными на отдельном листе, примеY
няется объектная модель, отличная от той, что применялась при работе со
встроенными диаграммами. В частности, объект диаграммы принадлежит не
объекту контейнера, а объекту листа. Ниже приведен пример изменения цвета
области диаграммы, расположенной на отдельном листе:
Sheets("Диаграмма 2").ChartArea.Interior.ColorIndex = 2
Создание диаграмм с помощью VBAНаиболее простой способ создания диаграммы с
помощью пользовательского интерфейса Excel заY
ключается в выделении исходных данных и нажаY
тии клавиши <F11>. Рассмотрим код, сгенерироY
ванный средством записи макросов при построеY
нии диаграммы на основе исходных данных,
показанных на рис. 10.3.
На рис. 10.4 показана диаграмма, созданная Excel
в результате нажатия клавиши <F11>.
Рис. 10.4. В результате нажатия клавиши <F11> Excel создаст новую диаграмму, расположеннуюна отдельном листе
Рис. 10.3. Чтобы создатьдиаграмму, выделите ис&ходные данные и нажмитеклавишу <F11>
Диаграммы Глава 10 233
Ниже приведен код, сгенерированный средством записи макросов:
Charts.AddActiveChart.SetSourceData Source:=Sheets("Лист1").Range("A1:B5")ActiveChart.Location Where:=xlLocationAsNewSheet
Проанализируем первую строку кода:
Charts.Add
Коллекция Charts представляет собой коллекцию всех листов диаграмм в раY
бочей книге. Каждая коллекция имеет метод добавления нового элемента YYYY Add.
Таким образом, в результате выполнения строки Charts.Add в коллекцию
Charts будет добавлен новый лист диаграммы (пока еще пустой).
Добавив новый лист диаграммы, Excel автоматически делает его активным.
Для обращения к текущей активной диаграмме можно использовать как
объект Chart (например, Charts("Диаграмма 1")), так и объект VBA Ac-tiveChart.
Внимание Если текущим активным объектом является объект, отличный от диаграммы, по&пытка использования объекта ActiveChart приведет к возникновению ошибки.
ActiveChart.SetSourceData Source:=Sheets("Лист4").Range("A1:B5")
Вторая строка автоматически сгенерированного кода определяет диапазон
исходных данных для диаграммы с помощью метода SetSourceData. СледуY
ет отметить, что VBA содержит соответствующие методы для всех действий,
которые можно выполнить посредством пользовательского интерфейса. Так,
для того чтобы задать диапазон исходных данных диаграммы, необходимо
щелкнуть на ней правой кнопкой мыши и выбрать команду контекстного меY
ню Исходные данные (Source Data). На экране появится диалоговое окно
Исходные данные (Source Data) (рис. 10.5), вкладка Диапазон данных (Data
Range) которого позволяет указать диапазон исходных данных.
Ниже приведен полный синтаксис метода SetSourceData:
SetSourceData(Source, PlotBy)
Здесь Source YYYY это ссылка на диапазон ячеек, а PlotBy YYYY константа,
принимающая значение xlColumns или xlRows.
Ниже приведен пример вызова метода SetSourceData с указанием всех
аргументов:
ActiveChart.SetSourceData Source:=Sheets("Лист4").Range("A1:B5"), _PlotBy:=xlColumns
Обратите внимание, что автоматически сгенерированный код не содержит арY
гумент PlotBy. Вероятно, средство записи макросов сочло возможным опустить
его, поскольку структура исходных данных (имена заголовков столбцов в ячейках
A1 и B1) предполагает, что ряды данных расположены в столбцах. К сожалению,
подобная эффективность является скорее исключением, чем правилом.
ActiveChart.Location Where:=xlLocationAsNewSheet
234 Часть II Автоматизация Excel
Рис. 10.5. Для определения исходных данных диаграммы можно восполь&зоваться диалоговым окном Исходные данные или методом VBA Set-SourceData
Приведенная выше строка соответствует последнему шагу мастера создаY
ния диаграмм (рис. 10.6).
Рис. 10.6. Выбор размещения диаграммы
Ниже приведен полный синтаксис метода Location:
Location(Where, Name)
Диаграммы Глава 10 235
Константа Where может принимать значения xlLocationAsNewSheet,
xlLocationAsNewObject и xlLocationAutomatic.
Name — это строка, которая определяет
имя нового листа, на котором будет размещена диаграмма (параметр
Where принимает значение xlLocationAsNewSheet);
имя рабочего листа, на котором будет размещена встроенная диаграмY
ма (параметр Where принимает значение xlLocationAsNewObject).
Как уже отмечалось, средство записи макросов генерирует много избыточY
ного кода. Поскольку использование метода Charts.Add подразумевает, что
новая диаграмма будет размещаться на отдельном листе, последняя строка
сгенерированного кода является лишней и ее можно удалить.
Изменение размещения диаграммы
Метод Location позволяет изменить тип диаграммы, преобразовав ее из
встроенной на размещенную на отдельном листе и наоборот. (Чтобы изменить
размещение диаграммы с помощью пользовательского интерфейса, щелкните
на диаграмме правой кнопкой мыши и выберите команды контекстного меню
Размещение (Location).)
Рассмотрим следующий код:
Worksheets("Лист1").ChartObjects("Диаграмма 1").ActivateActiveChart.Location Where:=xlLocationAsNewSheet,Name:="МояДиаграмма"
Его выполнение приведет к преобразованию диаграммы Диаграмма 1,
встроенной в рабочий лист Лист1, в диаграмму, размещенную на отдельном
листе МояДиаграмма.
Стандартный тип диаграмм
Обратите внимание, что в автоматически сгенерированном коде не был
указан тип создаваемой диаграммы. Одна из особенностей Excel VBA заклюY
чается в возможности неявного использования стандартных значений параY
метров Excel (в данном случае параметра, определяющего тип диаграммы). По
умолчанию стандартной диаграммой Excel является обычная гистограмма.
Эта особенность Excel может сыграть весьма неоднозначную роль. С одной
стороны, вы можете изменить стандартную диаграмму, чтобы создать неY
сколько однотипных диаграмм. С другой стороны, нельзя быть на 100% увеY
ренным в том, что один и тот же тип диаграммы используется в качестве станY
дартного на всех компьютерах, куда может попасть данная рабочая книга.
Переопределить стандартное значение параметра можно с помощью кода
VBA. Следующая строка кода изменяет цвет заливки области построения
диаграммы с серого на белый:
ActiveChart.PlotArea.Interior.ColorIndex = xlNone
236 Часть II Автоматизация Excel
Совет Чтобы изменить стандартный тип диаграммы с помощью пользовательского ин&терфейса, щелкните на диаграмме правой кнопкой мыши и выберите командуконтекстного меню Тип диаграммы (Chart Type). Выберите требуемый тип диа&граммы и щелкните на кнопке Сделать стандартной (Set As Default Chart).
Использование объектных переменныхдля упрощения кода
Объектные переменные позволяют упростить код и сделать его более эфY
фективным. В частности, использование объектной переменной делает возY
можным обращение к диаграмме без активизации последней. Ниже приведен
пример создания объектной переменной типа Chart:
Dim Cht As ChartSet Cht = Charts.AddCht.SourceData = Source:=Sheets("Лист4").Range("A1:B5")
Объектные переменные будут использоваться на протяжении оставшейся
части этой главы.
Еще одним преимуществом объектных переменных является поддержка
редактором Visual Basic автозаполнения. Чтобы включить автозаполнение,
выберите команду меню редактора Visual Basic Tools Options (Сервис
Параметры) и установите флажок Auto List Members (Автозаполнение) на
вкладке Editor (Редактор), как показано на рис. 10.7.
Рис. 10.7. Автозаполнение позволяет редактору Vi&sual Basic автоматически предлагать подходящийспособ продолжения ввода программного кода
Автозаполнение позволяет редактору Visual Basic автоматически предY
лагать подходящий способ продолжения ввода программного кода. Все,
Диаграммы Глава 10 237
что требуется от пользователя YYYY это выбрать нужный элемент из списка,
как показано на рис. 10.8.
Рис. 10.8. Автозаполнение в действии — после вводавыражения Cht.ChartType = редактор Visual Basicпредлагает выбрать требуемую константу из списка
Внимание Сбой в работе средства автозаполнения может быть вызван наличием ошибкикомпилирования в программном коде. Чтобы обнаружить ошибку, выберите ко&манду меню редактора Visual Basic Debug Compile VBA Project (ОтладкаКомпилировать проект VBA). Устранение ошибки компилирования должно при&вести к восстановлению работы средства автозаполнения.
“Анатомия” диаграммы В этом разделе рассматриваются VBAYэквиваленты различных элементов
диаграммы, их свойства и методы.
Все элементы диаграмм можно разделить на две категории: элементы,
общие для всех диаграмм (например, область диаграммы, область поY
строения диаграммы, название диаграммы и легенда), и элементы, харакY
терные только для диаграмм определенного типа (например, угол повороY
та круговой диаграммы и параметры проекции трехмерных диаграмм).
График, точечная диаграмма и диаграмма с областями имеют по две оси
данных; лепестковая диаграмма YYYY одну ось для каждой категории данных;
круговая и кольцевая диаграммы не имеют осей как таковых.
Область диаграммы (ChartArea)
Объект ChartArea представляет собой контейнер для всех остальных элеY
ментов диаграммы, таких как область построения диаграммы, оси, легенда,
ряды данных, подписи данных и т.д. Наиболее распространенными изменеY
ниями, вносимыми в область диаграммы, являются определение формата обY
ласти диаграммы (выбор границы, цвета заливки и текстуры) и выбор параY
метров шрифта.
Рассмотрим пример форматирования области диаграммы, для чего запишем
небольшой макрос. Щелкните правой кнопкой мыши на области диаграммы и
238 Часть II Автоматизация Excel
выберите команду контекстного меню Формат области диаграммы (Format
Chart Area). На вкладке Вид (Patterns) диалогового окна Формат областидиаграммы (Format Chart Area) выберите светлоYбирюзовый цвет заливки,
красный цвет рамки, третью по толщине линию и установите флажок С тенью(Shadow). На вкладке Шрифт (Font) выберите размер шрифта 14 и снимите
флажок Автомасштабирование (Auto scale). Ниже приведен код, сгенерированY
ный средством записи макроса в результате выполнения указанных действий:
Sub Macro2AsRecorded() Sheets("Диаграмма 1").Activate ActiveChart.ChartArea.Select With Selection.Border .ColorIndex = 3 .Weight = xlMedium .LineStyle = xlContinuous End With Selection.Shadow = True With Selection.Interior .ColorIndex = 34 .PatternColorIndex = 1 .Pattern = xlSolid End With Selection.AutoScaleFont = False With Selection.Font .Name = "Arial" .FontStyle = "Regular" .Size = 14 .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .ColorIndex = xlAutomatic .Background = xlAutomatic End WithEnd Sub
Средство записи макросов зафиксировало все действия, выполненные поY
средством пользовательского интерфейса (и даже больше). Следующий шаг
состоит в оптимизации полученного кода. Строки, набранные полужирным
шрифтом, являются избыточными:
Sub Macro2AsRecorded() Sheets("Диаграмма 1").Activate ActiveChart.ChartArea.Select With Selection.Border .ColorIndex = 3 .Weight = xlMedium' Значение по умолчанию. .LineStyle = xlContinuous End With Selection.Shadow = True With Selection.Interior' Светло-бирюзовый цвет заливки. .ColorIndex = 34
Диаграммы Глава 10 239
' Значение по умолчанию. .PatternColorIndex = 1' Значение по умолчанию. .Pattern = xlSolid End With Selection.AutoScaleFont = False With Selection.Font' Значение не изменилось. .Name = "Arial"' Значение не изменилось. .FontStyle = "Regular" .Size = 14' Значение не изменилось. .Strikethrough = False' Значение не изменилось. .Superscript = False' Значение не изменилось. .Subscript = False' Значение не изменилось. .OutlineFont = False' Значение не изменилось. .Shadow = False' Значение не изменилось. .Underline = xlUnderlineStyleNone' Значение не изменилось. .ColorIndex = xlAutomatic' Значение не изменилось. .Background = xlAutomatic End WithEnd Sub
Ниже приведен оптимизированный код макроса:
Sub Macro2Shortened() Sheets("Диаграмма 1").Activate ActiveChart.ChartArea.Select With Selection.Border .ColorIndex = 3 .Weight = xlMedium End With Selection.Shadow = True With Selection.Interior .ColorIndex = 34 End With Selection.AutoScaleFont = False With Selection.Font .Size = 14 End WithEnd Sub
С целью дальнейшего упрощения кода создадим объектную переменную,
представляющую область диаграммы. Обратите внимание, что при использоY
вании объектной переменной к области диаграммы можно обращаться без ее
предварительного выделения. Более того, следующий код будет выполняться
корректно даже в том случае, когда лист диаграммы не будет активным:
240 Часть II Автоматизация Excel
Sub ChartArDemo() Dim ChtArea As ChartArea Set ChtArea = Charts("Диаграмма 1").ChartArea With ChtArea .Shadow = True With .Border .ColorIndex = 3 .Weight = xlMedium End With .Interior.ColorIndex = 34 .AutoScaleFont = False .Font.Size = 14 End WithEnd Sub
Поговорим о цвете
При выборе цвета линии, заливки или шрифта Excel предлагает использовать
стандартную палитру, состоящую из 56 цветов. Чтобы изменить любой цвет станY
дартной палитры, выберите команду меню Excel Сервис Параметры(Tools Options), перейдите во вкладку Цвет (Color) и, указав требуемый цвет,
щелкните на кнопке Изменить (Modify). Измененная палитра сохраняется
вместе с рабочей книгой. Для доступа к цветам палитры можно использовать
свойство рабочей книги Colors.
Следующие строки кода полностью эквивалентны:
.Border.ColorIndex = 3.Border.Color = ThisWorkbook.Colors(3)
Обратите внимание, что порядок цветов в палитре не соответствует их инY
дексу, т.е. значению свойства ColorIndex. Например, в стандартной палитре
красный цвет (1Yй столбец 3Yй строки) имеет индекс 3, а бирюзовый (5Yй
столбец 4Yй строки) YYYY 8. Наиболее простой способ определения индекса цвеY
та заключается в записи простого макроса, устанавливающего требуемый цвет
для произвольного элемента интерфейса.
Любой цвет на экране может быть получен путем смешивания трех основY
ных цветов YYYY красного, зеленого и синего. В VBA есть функция RGB, позвоY
ляющая создать практически любой цвет путем указания интенсивности его
составляющих. Ниже приведен синтаксис функции RGB:
RGB(red, green, blue)
Каждый из аргументов принимает значения в диапазоне от 0 до 255. СлеY
дующие строки кода полностью эквивалентны и используются для установки
красного цвета границы:
.Border.ColorIndex = 3.Border.Color = RGB(255, 0, 0)
Область построения диаграммы (PlotArea)
Область построения диаграммы содержит визуализированные ряды данY
ных, оси и подписи осей. К ней применимы те же операции форматирования,
Диаграммы Глава 10 241
что и к области диаграммы. Вдобавок, вы можете изменять размеры области
построения диаграммы и ее размещение в пределах области диаграммы с поY
мощью свойств Top, Left, Height и Width объекта PlotArea.
Изменение размера и размещения объекта
Каждый объект рисунка или диаграммы имеет контейнер. К примеру, конY
тейнером автофигуры является рабочий лист, на котором она размещена. ПоY
добным образом, контейнером области построения диаграммы является область
самой диаграммы. Объект, заключенный в контейнер, имеет ограничивающийпрямоугольник YYYY наименьший прямоугольник, в который полностью вписываY
ется данный объект.
За единицу измерения в Excel VBA принята точка, составляющая 1/72
дюйма.
Размещение объекта полностью определяется расстоянием по вертикали и
горизонтали от верхнего левого угла объекта до верхнего левого угла его контейY
нера. Расстоянию по вертикали соответствует свойство объекта Top, а расстояY
нию по горизонтали YYYY свойство объекта Left. Высота и ширина объекта совY
падают с высотой и шириной его ограничивающего прямоугольника (рис. 10.9).
Рис. 10.9. Размещение объекта полностью опреде&ляется расстоянием по вертикали и горизонтали отверхнего левого угла объекта до верхнего левогоугла его контейнера. Высота и ширина объектасовпадают с высотой и шириной его ограничи&вающего прямоугольника
Свойства Top, Left, Height и Width поддерживают как считывание, так
и установку значения. Ниже приведен пример определения высоты объекта:
ObjHt = obj.Height
и изменения его размещения:
obj.Top = 75obj.Left = 80
Рассмотрим следующий код:
Sub PlotArDemo() Dim PltArea As PlotArea
242 Часть II Автоматизация Excel
Set PltArea = Charts("Диаграмма 3").PlotArea With PltArea .Top = 100 .Left = 100 .Height = 300 .Width = 400 End WithEnd Sub
Результат его выполнения представлен на рис. 10.10.
Рис. 10.10. Свойства объекта Top, Left, Height и Width позволяют изменить его размери размещение
Наличие свойств Top, Left, Height и Width позволяет задать размер
и размещение объекта с большей точностью, чем это можно было бы сделать
с помощью пользовательского интерфейса. Изменяя значения этих свойств,
следует помнить об их естественных ограничениях. К примеру, сумма значеY
ний свойств объекта Left и Width не может превысить значение свойства
Width контейнера объекта.
Ряды данных (Series)
Ряды данных диаграммы входят в коллекцию SeriesCollection. РасY
сматриваемая в качестве примера диаграмма имеет два ряда данных YYYY Xdataи Ydata. Ниже приведен синтаксис обращения к ряду данных:
Cht.SeriesCollection(Index)
Диаграммы Глава 10 243
Index — это номер (начиная с 1) или имя ряда данных. Щелкнув на точке
данных (столбце) из ряда Xdata, вы увидите в строке формул следующее выY
ражение:
=РЯД(Лист1!$A$1;;Лист1!$A$2:$A$5;1)
(В англоязычной версии Excel: =SERIES(Лист1!$A$1,,Лист1!$A$2:$A$5,1).)
Лист1!$A$1 YYYY это имя ряда данных (Xdata).
По умолчанию второй параметр пропущен, так как ось категорий гистоY
граммы содержит последовательность порядковых чисел (начиная с 1), соотY
ветствующих точкам данных. При необходимости этот параметр может содерY
жать ссылку на диапазон ячеек, определяющий подписи оси категорий.
Лист1!$A$2:$A$5 YYYY это диапазон ячеек, в котором находится ряд данных.
Наконец, 1 YYYY это индекс ряда данных в коллекции. Чтобы изменить инY
декс ряда данных с помощью пользовательского интерфейса, щелкните на
точке данных правой кнопкой мыши, выберите команду контекстного меню
Формат рядов данных (Format Data Series) и перейдите во вкладку Порядокрядов (Series Order).
Ниже приведены два эквивалентных способа обращения к ряду данных
Xdata:
Charts("Диаграмма 1").SeriesCollection("Xdata")Charts("Диаграмма 1").SeriesCollection(1)
Следующий макрос позволяет создать комбинированную диаграмму, в коY
торой ряд данных Xdata будет представлен в виде графика:
Sub SeriesDemo() Dim Ser As Series Set Ser = Charts("Диаграмма 3").SeriesCollection("Xdata") With Ser .ChartType = xlLine .Border.Weight = xlThick .MarkerStyle = xlMarkerStyleCircle .MarkerBackgroundColorIndex = xlAutomatic .MarkerForegroundColorIndex = xlAutomatic .MarkerSize = 10 End WithEnd Sub
Результат выполнения макроса представлен на рис. 10.11.
Оси диаграммы (Axis)
Оси диаграммы входят в коллекцию Axes. Рассматриваемая в качестве
примера диаграмма имеет две оси YYYY ось категорий (X) и ось значений (Y).
Ниже приведен сокращенный синтаксис обращения к оси диаграммы:
Cht.Axes(Type)
Type — это константа Excel VBA, определяющая тип оси. Ось категорий (X)
имеет тип xlCategory, а ось значений (Y) YYYY тип xlValue.
244 Часть II Автоматизация Excel
Рис. 10.11. Пример создания комбинированной диаграммы путем изменения спосо&ба представления (ChartType) ряда данных
Чтобы обратиться ко всей коллекции осей диаграммы, пропустите параY
метр Type, как показано ниже:
Cht.Axes
Следующий макрос добавляет к диаграмме подписи осей и изменяет форY
мат данных оси Y (оси значений):
Sub AxisDemo() Dim Axs As Axis Set Axs = Charts("Диаграмма 3").Axes(xlValue) With Axs .HasTitle = True .AxisTitle.Caption = "Эффективность производства" .TickLabels.NumberFormat = "0.00" End With Set Axs = Charts("Диаграмма 3").Axes(xlCategory) With Axs .HasTitle = True .AxisTitle.Caption = "Год" End WithEnd Sub
Результат выполнения макроса представлен на рис. 10.12.
Обратите внимание, что добавление подписей осей привело к автоматичеY
скому изменению размера области построения диаграммы. Чтобы задать подY
пись диаграммы, необходимо установить значение свойства оси HasTitleравным True.
Диаграммы Глава 10 245
Рис. 10.12. Пример добавления к диаграмме подписей осей и измененияформата данных оси значений
Добавление вспомогательных осей
Если масштаб рядов данных сильно отличается, может возникнуть необхоY
димость добавления вспомогательной оси (горизонтальной или вертикальY
ной). Ниже приведен полный синтаксис обращения к оси диаграммы:
Cht.Axes(Type, AxisGroup)
AxisGroup — это константа Excel VBA, определяющая группу оси. ОсновY
ные оси входят в группу xlPrimary, а вспомогательные YYYY в группу xlSe-condary.
Следующий макрос определяет вспомогательную ось значений (Y) для ряда
данных Xdata.
Sub SecondaryAxisDemo() Dim Cht As Chart Set Cht = Charts("Диаграмма 3") Cht.SeriesCollection("Xdata").AxisGroup = 2End Sub
Результат выполнения макроса представлен на рис. 10.13.
Линии сетки (HasMajorGridlines и HasMinorGridlines)
Линии сетки являются расширением меток делений оси и предназначены
для улучшения восприятия и оценки отображаемых данных. Основным
и промежуточным меткам делений оси соответствуют основные и промежуY
точные линии сетки, которые можно скрыть или отобразить независимо друг
246 Часть II Автоматизация Excel
от друга. Линия сетки имеет настраиваемые параметры, такие как тип, цвет
и толщина.
Рис. 10.13. Если масштаб рядов данных сильно отличается, добавьте вспомо&гательную ось
Следующий макрос удаляет все линии сетки диаграммы:
Sub GridlineDemo() Dim Cht As Chart Set Cht = Charts("Диаграмма 3") With Cht With .Axes(xlValue) .HasMajorGridlines = False .HasMinorGridlines = False End With With Cht.Axes(xlValue) .HasMajorGridlines = False .HasMinorGridlines = False End With End WithEnd Sub
Подписи данных (DataLabels и DataLabel)
Ряд состоит из точек данных. Каждая точка может иметь свою собственную
подпись, включающую значение по оси X (категорий), оси Y (значений) или
значение, определенное пользователем (строковая константа или ссылка на
ячейку). Ниже перечислены различные способы создания подписи данных с
помощью VBA.
Все точки данных диаграммы имеют подписи одного и того же типа:
ActiveChart.ApplyDataLabels Type:=xlDataLabelsShowNone
Диаграммы Глава 10 247
Точки данных определенного ряда имеют подписи одного и того же типа:
With ActiveChart.SeriesCollection("Xdata") .HasDataLabels = True .ApplyDataLabels Type:=xlDataLabelsShowValueEnd With
Определенная точка в ряде данных имеет подпись в виде строковой
константы:
With ActiveChart.SeriesCollection("Xdata").Points(1) .HasDataLabel = True .DataLabel.Text="Подпись точки данных"End With
Определенная точка в ряде данных имеет подпись в виде R1C1Y
формулы (ссылки на ячейку):
With ActiveChart.SeriesCollection("Xdata").Points(1) .HasDataLabel = True .DataLabel.Text="=Лист1!R1C1"End With
Подпись данных имеет настраиваемые параметры, такие как положение,
ориентация и т.п.
Следующий макрос создает подписи для точек данных ряда Xdata.
Sub DataLabelDemo() With Charts("Диаграмма 3").SeriesCollection("Xdata") .HasDataLabels = True .ApplyDataLabels Type:=xlDataLabelsShowValue With .DataLabels .HorizontalAlignment = xlCenter .VerticalAlignment = xlCenter .Position = xlLabelPositionAbove .Orientation = xlUpward End With End WithEnd Sub
Название диаграммы, легенда и таблица данных(ChartTitle, HasLegend и HasDataTable)
Название диаграммы и легенда имеют такие настраиваемые параметры,
как шрифт и размещение. Кроме того, рядом с каждой диаграммой можно
отобразить таблицу ее исходных данных. Следующий макрос добавляет к
диаграмме ее название (отформатированное полужирным шрифтом 16Yго
размера и размещенное в верхнем левом углу), легенду (расположенную внизу
и посередине диаграммы) и таблицу данных.
Обратите внимание, что установка параметров названия диаграммы, леY
генды и таблицы данных становится возможной только после присвоения
значения True свойствам HasTitle, HasLegend и HasDataTable, соответY
ственно.
Sub DemoMisc() With Charts("Диаграмма 3")
248 Часть II Автоматизация Excel
.HasTitle = True With .ChartTitle .Text = "Эффективность работы компании" .Font.Size = 16 .Top = 0 .Left = 0 End With .HasLegend = True .Legend.Position = xlBottom .HasDataTable = True End WithEnd Sub
Линии тренда и полосы погрешности (Trendlines и ErrorBar)
Линия тренда дает наглядное представление о направлении изменения ряY
да данных. Excel содержит несколько типов линий тренда: линейная, логаY
рифмическая, полиномиальная, степенная, экспоненциальная и линейная
фильтрация. Полосы погрешности позволяют оценить отклонение фактичеY
ских данных от тренда.
На рис. 10.14 показана точечная диаграмма годовых продаж.
Следующий макрос добавляет к диаграмме линию тренда с прогнозом на
5 периодов (лет) вперед, выводит уравнение линии тренда и величину достоY
верности аппроксимации (R2):
Sub AddTrendLine() Dim Ser As Series Dim Trnd As Trendline Dim Cht As Chart Set Cht = Worksheets("Тренд и погрешности").ChartObjects(" _Диаграмма 1").Chart Set Ser = Cht.SeriesCollection(1) Set Trnd = Ser.Trendlines.Add(Type:=xlLinear, Forward:=5, _Backward:=0, DisplayEquation:=True, DisplayRSquared:=True) Trnd.Border.LineStyle = xlDot With Cht .HasTitle = True .ChartTitle.Characters.Text = "Прогнозируемые продажи" End WithEnd Sub
Результат выполнения макроса показан на рис. 10.15. Величина достоверY
ности аппроксимации (R2), близкая к 1, означает, что линия тренда соответстY
вует фактическим данным.
Следующий макрос добавляет к диаграмме полосы погрешности по обе
стороны точек данных с фиксированным значением величины погрешности,
равным 25 единицам.
Sub AddErrorBars() Dim Ser As Series Dim Trnd As Trendline Dim Cht As Chart Set Cht = Worksheets("Тренд и погрешности").ChartObjects(" _Диаграмма 1").Chart
Диаграммы Глава 10 249
Set Ser = Cht.SeriesCollection(1) Ser.ErrorBar Direction:=xlY, Include:=xlBoth, _Type:=xlFixedValue, Amount:=25End Sub
Рис. 10.14. Чтобы добавить линию тренда, щелкните на любой точке данных диаграммыгодовых продаж правой кнопкой мыши и выберите команду контекстного менюДобавить линию тренда (Add Trendline)
Полосы погрешности могут размещаться по обе стороны точки данных,
выше или ниже ее. Погрешность может определяться как фиксированное знаY
чение, относительное значение, заданное число стандартных отклонений,
стандартная погрешность, а также с помощью пользовательской формулы.
Как показано на рис. 10.16, прогнозируемый уровень продаж выходит за рамY
ки допустимой погрешности только в 1987 году.
250 Часть II Автоматизация Excel
Рис. 10.15. Добавление линии тренда с прогнозом на 5 периодов (лет) вперед
Рис. 10.16. Добавление полос погрешности
Диаграммы Глава 10 251
Типы диаграмм В состав Excel входит множество встроенных диаграмм различных типов.
Типы и виды стандартных диаграмм Excel перечислены в табл. 10.1.
Таблица 10.1. Стандартные диаграммы Excel
Типдиаграммы
Вид диаграммы Константа VBA
Гистограмма Обычная гистограмма xlColumnClustered
Объемный вариант обычной
гистограммы
xl3DColumnClustered
Гистограмма с накоплением xlColumnStacked
Объемный вариант гистограммы
с накоплением
xl3DColumnStacked
Нормированная гистограмма с
накоплением
xlColumnStacked100
Объемный вариант нормироY
ванной гистограммы с накоплеY
нием
xl3DColumnStacked100
Трехмерная гистограмма xl3DColumn
Линейчатая
диаграмма
Обычная линейчатая диаграмма xlBarClustered
Объемный вариант обычной лиY
нейчатой диаграммы
xl3DBarClustered
Линейчатая диаграмма с накопY
лением
xlBarStacked
Объемный вариант линейчатой
диаграммы с накоплением
xl3DBarStacked
Нормированная линейчатая
диаграмма с накоплением
xlBarStacked100
Объемный вариант нормироY
ванной линейчатой диаграммы с
накоплением
xl3DBarStacked100
График Обычный график xlLine
Обычный график с маркерами xlLineMarkers
График с накоплением
xlLineStacked
252 Часть II Автоматизация Excel
Продолжение табл. 10.1
Типдиаграммы
Вид диаграммы Константа VBA
График с накоплением, на котоY
ром отдельные значения помеY
чены маркерами
xlLineMarkersStacked
Нормированный график с накоY
плением
xlLineStacked100
Нормированный график с накоY
плением, на котором отдельные
значения помечены маркерами
xlLineMarkersStacked100
Трехмерный график xl3DLine
Круговая
диаграмма
Обычная круговая диаграмма xlPie
Разрезанная круговая диаграмма xlPieExploded
Объемный вариант обычной
круговой диаграммы
xl3DPie
Объемный вариант разрезанной
круговой диаграммы
xl3DPieExploded
Вторичная круговая диаграмма
(обычная круговая диаграмма с
частью значений, вынесенными
во вторую круговую диаграмму)
xlPieOfPie
Вторичная гистограмма с накопY
лением (обычная круговая диаY
грамма с частью значений, выY
несенными в гистограмму с наY
коплением)
xlBarOfPie
Точечная
диаграмма
Обычная точечная диаграмма xlXYScatter
Точечная диаграмма со значеY
ниями, соединенными сглажиY
вающими линиями
xlXYScatterSmooth
Точечная диаграмма со значеY
ниями, соединенными сглажиY
вающими линиями без маркеров
xlXYScatterSmoothNoMarkers
Точечная диаграмма со значеY
ниями, соединенными отрезY
ками
xlXYScatterLines
Диаграммы Глава 10 253
Продолжение табл. 10.1
Типдиаграммы
Вид диаграммы Константа VBA
Точечная диаграмма со значеY
ниями, соединенными отрезкаY
ми без маркеров
xlXYScatterLinesNoMarkers
Пузырьковая
диаграмма
Обычная пузырьковая диаграмма xlBubble
Объемный вариант обычной пуY
зырьковой диаграммы
xlBubble3DEffect
Диаграмма с
областями
Обычная диаграмма с областями xlArea
Объемный вариант обычной
диаграммы с областями
xl3DArea
Диаграмма с областями с накопY
лением
xlAreaStacked
Объемный вариант диаграммы с
областями с накоплением
xl3DAreaStacked
Нормированная диаграмма с обY
ластями с накоплением
xlAreaStacked100
Объемный вариант нормироY
ванной диаграммы с областями с
накоплением
xl3DAreaStacked100
Кольцевая
диаграмма
Обычная кольцевая диаграмма xlDoughnut
Разрезанная кольцевая диаY
грамма
xlDoughnutExploded
Лепестковая
диаграмма
Обычная лепестковая диаграмма xlRadar
Лепестковая диаграмма с маркеY
рами, которыми помечены знаY
чения данных
xlRadarMarkers
Заполненная лепестковая диаY
грамма
xlRadarFilled
Поверхностная
диаграмма
Обычная поверхностная диаY
грамма
xlSurface
Контурная диаграмма (вид сверY
ху на обычную поверхностную
диаграмму)
xlSurfaceTopView
254 Часть II Автоматизация Excel
Продолжение табл. 10.1
Типдиаграммы
Вид диаграммы Константа VBA
Проволочная (прозрачная) поY
верхностная диаграмма
xlSurfaceWireframe
Проволочная (прозрачная) конY
турная диаграмма (вид сверху на
проволочную (прозрачную) поY
верхностную диаграмму)
xlSurfaceTopViewWireframe
Биржевая
диаграмма
Биржевая диаграмма для набоY
ров из трех значений (самый выY
сокий курс, самый низкий курс,
курс закрытия)
xlStockHLC
Биржевая диаграмма для набоY
ров из четырех значений (объем,
самый высокий курс, самый
низкий курс, курс закрытия)
xlStockVHLC
Биржевая диаграмма для набоY
ров из четырех значений (курс
открытия, самый высокий курс,
самый низкий курс, курс закрыY
тия)
xlStockOHLC
Биржевая диаграмма для набоY
ров из пяти значений (объем,
курс открытия, самый высокий
курс, самый низкий курс, курс
закрытия)
xlStockVOHLC
ЦилиндриY
ческая диаY
грамма
Обычная гистограмма со столбY
цами в виде цилиндров
xlCylinderColClustered
Обычная линейчатая диаграмма
со столбцами в виде цилиндров
xlCylinderBarClustered
Гистограмма с накоплением со
столбцами в виде цилиндров
xlCylinderColStacked
Линейчатая диаграмма с накопY
лением со столбцами в виде циY
линдров
xlCylinderBarStacked
Нормированная гистограмма с
накоплением со столбцами в виY
де цилиндров
xlCylinderColStacked100
Диаграммы Глава 10 255
Окончание табл. 10.1
Типдиаграммы
Вид диаграммы Константа VBA
Нормированная линейчатая диаY
грамма с накоплением со столбY
цами в виде цилиндров
xlCylinderBarStacked100
Трехмерная гистограмма со
столбцами в виде цилиндров
xlCylinderCol
Коническая
диаграмма
Обычная гистограмма со столбY
цами в виде конусов
xlConeColClustered
Обычная линейчатая диаграмма
со столбцами в виде конусов
xlConeBarClustered
Гистограмма с накоплением со
столбцами в виде конусов
xlConeColStacked
Линейчатая диаграмма с накоплеY
нием со столбцами в виде конусов
xlConeBarStacked
Нормированная гистограмма с
накоплением со столбцами в виде
конусов
xlConeColStacked100
Нормированная линейчатая диаY
грамма с накоплением со столбY
цами в виде конусов
xlConeBarStacked100
Трехмерная гистограмма со
столбцами в виде конусов
xlConeCol
ПирамидальY
ная диаграмма
Обычная гистограмма со столбY
цами в виде пирамид
xlPyramidColClustered
Обычная линейчатая диаграмма
со столбцами в виде пирамид
xlPyramidBarClustered
Гистограмма с накоплением со
столбцами в виде пирамид
xlPyramidColStacked
Линейчатая диаграмма с накоплеY
нием со столбцами в виде пирамид
xlPyramidBarStacked
Нормированная гистограмма с
накоплением со столбцами в виY
де пирамид
xlPyramidColStacked100
Нормированная линейчатая
диаграмма с накоплением со
столбцами в виде пирамид
xlPyramidBarStacked100
Трехмерная гистограмма со
столбцами в виде пирамид
xlPyramidCol
256 Часть II Автоматизация Excel
Не отчаивайтесь, взглянув на размеры этой таблицы. С практической точY
ки зрения цилиндрические, конические и пирамидальные диаграммы аналоY
гичны гистограммам, а линейчатые диаграммы YYYY это гистограммы, повернуY
тые на 90° по часовой стрелке.
В большинстве случаев разные виды диаграмм в пределах одного типа
отличаются значениями нескольких параметров. К примеру, единственное
отличие обычной точечной диаграммы (xlXYScatter) от точечной диаграмY
мы со значениями, соединенными отрезками (xlXYScatterLines), заклюY
чается в том, что параметр SeriesCollection(1).Border.LineStyleпоследней имеет значение xlAutomatic.
Параметры трехмерных и круговых диаграмм В этом разделе рассматриваются параметры, применимые только к трехY
мерным или круговым диаграммам.
Параметры трехмерных диаграмм
Все трехмерные диаграммы имеют параметры объемного вида, являющиеY
ся свойствами объекта Chart.
Elevation. Возвышение, с которого наблюдатель смотрит на диаY
грамму. Если значение параметра Elevation равно 0, наблюдатель не
видит верхнюю поверхность фигур. Если значение параметра Eleva-tion равно 90, наблюдатель смотрит на диаграмму сверху вниз.
Rotation. Этот параметр принимает значения в диапазоне от 0 до 359.
При небольшом значении угла поворота наблюдатель смотрит на диаY
грамму так, как если бы он находился справа от нее, а при значении угY
ла поворота, равном 330YY350, YYYY так, как если бы он находился слева от
нее. Чтобы развернуть диаграмму и посмотреть на нее сзади, испольY
зуйте значения параметра Rotation в диапазоне от 150 до 210.
Perspective. Этот параметр принимает значения в диапазоне от 0
до 100. Установка больших значений параметра Perspective привоY
дит к искривлению основания диаграммы.
DepthPercent. Глубина диаграммы в процентах от стандартной
глубины.
HeightPercent. Высота диаграммы в процентах от стандартной
высоты.
RightAngleAxes. Установка значения этого параметра равным Trueисключает возможность изменения перспективы диаграммы, что хаY
рактерно для линейчатых диаграмм.
Диаграммы Глава 10 257
Объекты Walls (стенки) и Floor (основание) доступны только для трехY
мерных диаграмм. Эти объекты имеют одинаковые наборы настраиваемых
параметров, таких как цвет заливки, тип рамки и т.п.
Ниже перечислены параметры ряда данных трехмерной диаграммы.
GapWidth. Если ширина зазора равна 0, фигуры, представляющие на
диаграмме один ряд данных, соприкасаются друг с другом. Чем выше
ширина зазора, тем больше расстояние между фигурами. GapWidth —
это свойство объекта ChartGroup, представляющего все ряды данных
одинакового типа. Если один ряд данных на диаграмме представлен
трехмерными столбцами, а другой YYYY трехмерным графиком, то шириY
на зазора между столбцами будет определяться значением свойства
GapWidth объекта Columns3DGroup.
GapDepth. Если глубина зазора равна 0, фигуры, представляющие на
диаграмме соседние ряды данных, соприкасаются друг с другом. Чем
выше глубина зазора, тем больше расстояние между фигурами. НеY
смотря на то что глубину зазора можно определить посредством диаY
логового окна Формат ряда данных (Format Data Series), GapDepthявляется свойством объекта Chart.
ChartDepth. Если значение параметра ChartDepth равно 20, диаY
грамма выглядит очень плоской. Чем больше значение этого параметY
ра, тем больше глубина диаграммы. ChartDepth является свойством
объекта Chart.
Следующий макрос оперирует всеми рассмотренными выше параметрами.
Sub Format3D() Dim Cht As Chart Set Cht = Worksheets("Трехмерная _диаграмма").ChartObjects(1).Chart With Cht .Elevation = 30 .Perspective = 25 .Rotation = 30 .RightAngleAxes = False .HeightPercent = 150 .AutoScaling = True .DepthPercent = 280 .GapDepth = 160 End With Cht.Column3DGroup.GapWidth = 0 With Cht.Walls.Fill .TwoColorGradient Style:=msoGradientHorizontal, Variant:=1 .Visible = True .ForeColor.SchemeColor = 2 .BackColor.SchemeColor = 1 End With With Cht.Floor.Fill .PresetGradient Style:=msoGradientHorizontal, _Variant:=1, PresetGradientType:=msoGradientCalmWater .Visible = True
258 Часть II Автоматизация Excel
End WithEnd Sub
Результат выполнения макроса показан на рис. 10.17.
Рис. 10.17. Пример изменения параметров трехмерной диаграммы
Параметры круговых диаграмм
Один из недостатков круговых диаграмм состоит в возможности перекрыY
вания подписей данных, как показано на рис. 10.18.
К счастью, уникальные свойства круговой диаграммы могут помочь в реY
шении этой проблемы.
Если друг друга перекрывают всего две подписи данных, попробуйте измеY
нить угол поворота первой доли круговой диаграммы с помощью свойства
FirstSliceAngle. При этом становится очевидным фундаментальный неY
достаток создания диаграмм с помощью VBA. В VBA нет метода или свойства,
позволяющего судить о внешней привлекательности диаграммы. Выяснить
это можно, только взглянув на нее. Следующий макрос поворачивает диаY
грамму на 60° по часовой стрелке, в результате чего взаимное расположение
подписей данных становится вполне приемлемым.
Sub RotateFirstSlice() Dim Cht As Chart Set Cht = Worksheets("Круговая диаграмма").ChartObjects(1).Chart Cht.PieGroups(1).FirstSliceAngle = 60End Sub
Диаграммы Глава 10 259
Рис. 10.18. Подписи данных круговой диаграммы могут перекрывать друг друга
Если же друг друга перекрывает много подписей данных, вынесите все доY
ли, не превышающие 5%, во вторичную гистограмму с накоплением:
Sub CreateBarOfPie() Dim Cht As Chart Dim CG As ChartGroup Set Cht = Worksheets("Круговая диаграмма").ChartObjects(1).Chart Cht.ChartType = xlBarOfPie Set CG = Cht.PieGroups(1) With CG .SplitType = xlSplitByPercentValue' Доли менее 5%. .SplitValue = 5' Зазор между основной и вторичной диаграммой. .GapWidth = 200' Размер вторичной диаграммы в % от основной. .SecondPlotSize = 55 End WithEnd Sub
Результат выполнения приведенного выше кода показан на рис. 10.19.
Совет Линии выносок, показанные на рис. 10.19, автоматически добавляются Excel, еслирасстояние между подписью данных и соответствующей ей долей на диаграммепревышает некоторую заданную величину. Чтобы запретить добавление линий выно&сок, установите значение свойства диаграммы HasLeaderLines равным False.
260 Часть II Автоматизация Excel
Рис. 10.19. Вторичная гистограмма с накоплением используется для выне&сения из круговой диаграммы долей, не превышающих 5%. В большинствеслучаев это позволяет избежать перекрывания подписей данных
Интерактивные диаграммы Рассмотрим использование VBA для создания интерактивных диаграмм.
События диаграмм
Одним из недостатков диаграмм является автоматическое изменение
внешнего вида диаграммы при обновлении исходных данных. Управлять изY
менением внешнего вида диаграммы помогут события. (Более подробно соY
бытия рассматривались в главе 8, ‘‘События’’.) Код обработки событий диаY
граммы, расположенной на отдельном рабочем листе, помещается в модуль
этого листа. Для обработки событий встроенной диаграммы необходимо созY
дать модуль класса. Ниже перечислены некоторые из наиболее часто испольY
зуемых событий диаграммы:
SeriesChange — срабатывает при обновлении ряда данных на диаY
грамме;
Calculate — срабатывает при изменении исходных данных диаграммы;
Activate — срабатывает при активизации диаграммы;
Deactivate — срабатывает при деактивизации диаграммы.
Диаграммы Глава 10 261
Следующий макрос выполняется при каждом пересчете диаграммы
(например, при обновлении ряда данных). Если количество точек в ряде данY
ных с индексом 1 больше 5, отрезки, соединяющие точки данных, окрашиваY
ются в красный цвет, в противном случае YYYY в синий.
Private Sub Chart_Calculate() Dim Ser As Series Set Ser = Me.SeriesCollection(1) If Ser.Points.Count > 5 Then Ser.Border.ColorIndex = 5 Else Ser.Border.ColorIndex = 3 End IfEnd Sub
Экспорт диаграммы в файл изображения Экспортировать диаграмму в файл изображения формата GIF не составляY
ет никакого труда:
Sub SaveChart() Dim Cht As Chart Set Cht = Worksheets("Круговая диаграмма").ChartObjects(1).Chart Cht.Export Filename:=ThisWorkbook.Path & _Application.PathSeparator & "pie.gif", FilterName:="GIF"End Sub
Подобный шаг может иметь следующие мотивы.
Необходимость помещения диаграммы на Web/страницу. Создайте диаY
грамму, экспортируйте ее в файл и сошлитесь на него в тексте WebY
страницы, например, <Img src="MyChart.gif">.
Экономия системных ресурсов. Диаграммы занимают много места в
оперативной памяти компьютера. Если вам нужно отобразить 100 или
больше диаграмм, воспользуйтесь средствами VBA для создания кажY
дой отдельной диаграммы, ее сохранения в файле формата GIF и заY
грузки полученного файла в рабочую книгу. Ниже приведен код загрузY
ки файла изображения в рабочую книгу:
ActiveSheet.Pictures.Insert (ThisWorkbook.Path & _Application.PathSeparator & "pie.gif")
Необходимость помещения диаграммы на пользовательскую форму.Единственный способ помещения диаграммы на пользовательскую
форму заключается в ее загрузке из файла изображения. Ниже привеY
ден код загрузки файла изображения в элемент управления Image:
Me.Image1.Picture = LoadPicture(ThisWorkbook.Path & _Application.PathSeparator & "pie.gif")
262 Часть II Автоматизация Excel
Удивительные возможности точечных диаграммНесмотря на то, что по части построения чертежей Excel существенно усY
тупает таким ‘‘монстрам’’, как AutoCAD, вы будете приятно поражены, отY
крыв для себя удивительные возможности точечных диаграмм. На рис. 10.20
показан пример использования точечной диаграммы для построения логотипа
компании MrExcel Consulting.
Рис. 10.20. Для построения логотипа компании MrExcel Consulting с помощью точечнойдиаграммы понадобилось 15 точек данных
Идея использования точечных диаграмм для построения чертежей была
доведена до совершенства Малой Сингхом (Mala Singh) из компании XLSoft
Consulting (Индия). На рис. 10.21 показан один из его шедевров.
Создание нестандартных диаграммК сожалению, создание нестандартных диаграмм Excel с помощью VBA
выходит за рамки этой книги. Все диаграммы, приведенные в этом разделе,
были созданы с помощью VBA и используются с разрешения компании
XLSoft Consulting.
Круговая пузырьковая диаграмма
Внешний вид круговой пузырьковой диаграммы представлен на рис. 10.22.
Диаграммы Глава 10 263
Рис. 10.21. Этот чертеж на самом деле является точечной диаграммойExcel. На его построение уходит около 25 с
Рис. 10.22. Круговая пузырьковая диаграмма
Пузырьковая диаграмма похожа на точечную диаграмму с добавлением
третьего ряда данных в виде диаметра пузырька. В свою очередь диаграмма,
показанная на рис. 10.22, является расширением пузырьковой диаграммы с
добавлением четвертого ряда данных, представленного в виде обычной кругоY
вой диаграммы. Круговая пузырьковая диаграмма строится с помощью цикла
264 Часть II Автоматизация Excel
по всем точкам четвертого ряда данных. Для каждой точки создается скрытая
круговая диаграмма, которая затем используется в качестве изображения соY
ответствующего пузырька.
Диаграмма с точками данных в виде спидометров
На рис. 10.23 показана диаграмма с точками данных в виде спидометров.
Рис. 10.23. Диаграмма с точками данных в виде спидометров
Диаграмма с точками данных в виде спидометров представляет собой изY
мененную точечную диаграмму с двумя автофигурами YYYY кругом для установY
ки внешнего периметра и циферблатом. Оставшаяся часть диаграммы предY
ставлена точкой и подписями данных. Шкала циферблата и цветовые зоны
являются полностью настраиваемыми. Несколько размещенных рядом спиY
дометров создают эффект приборной доски.
Каждый из спидометров, показанных на рис. 10.23, на самом деле является
изображением отдельной диаграммы. Макрос, создающий приборную доску,
генерирует диаграмму на основе данных таблицы Excel (одной строке данных
соответствует одна приборная доска), а затем использует ее для создания стаY
тического изображения. Наконец, полученные изображения спидометров
упорядочиваются на рабочем листе.
Диаграмма кривой предложения
Excel не позволяет создавать гистограммы со столбцами разной ширины.
В диаграмме, показанной на рис. 10.24, высота столбца определяет стоимость
товара, а ширина YYYY предлагаемое количество.
Диаграмма кривой предложения представляет собой точечную диаY
грамму с помещенными на область построения диаграммы цветными пряY
моугольниками, имитирующими столбцы данных. Ширина и размещение
прямоугольников подобраны так, чтобы они корректно отражали исходY
ные данные диаграммы.
Диаграммы Глава 10 265
Рис. 10.24. Диаграмма кривой предложения
Иерархическая кольцевая диаграмма
Иерархическая кольцевая диаграмма представляет собой комбинацию
круговой и кольцевой диаграммы. Отличительная особенность иерархической
кольцевой диаграммы заключается в том, что каждый ее уровень хранит инY
формацию о пропорции вложенного уровня. Подписи данных содержат знаY
чение и, при необходимости, его вклад (в процентах) в соответствующую долю
предыдущего уровня (рис. 10.25).
Рис. 10.25. Иерархическая кольцевая диаграмма
266 Часть II Автоматизация Excel
Следующий шагДиаграммы являются неотъемлемой частью всех программ для работы с
электронными таблицами, поскольку они позволяют получить наглядное
представление об исходных данных. Следующая глава посвящена анализу
данных с помощью расширенного фильтра.
Преимущества VBA передпользовательскиминтерфейсом Excel.......................267Использование расширенногофильтра для отборауникальных значений иззаданного диапазона................. 268Использование расширенногофильтра с указанием условияотбора данных..............................276Фильтрация диапазонаисходных данных “на месте” ...287Использование расширенногофильтра для копированиявсех записей,удовлетворяющих заданномуусловию ......................................... 289Автофильтр ...................................297Следующий шаг........................... 298
11Глава 11
Àíàëèç äàííûõñ ïîìîùüþðàñøèðåííîãîôèëüòðà
Преимущества VBAпередпользовательскиминтерфейсом Excel
Диалоговое окно Excel Расши-ренный фильтр (Advanced Filter) наY
столько неинтуитивно, что большинY
ство пользователей предпочитают
вообще не связываться с ним. ВероY
ятно, Microsoft полностью изменит
интерфейс расширенного фильтра в
следующей версии Excel.
С другой стороны, работа с расY
ширенным фильтром посредством
VBA может доставить истинное наY
слаждение. Всего одной строки кода
достаточно для извлечения подмноY
жества строк исходных данных или
отбора уникальных значений из заY
данного столбца!
При рассмотрении расширенных
фильтров в этой главе внимание буY
дет уделено как первому, так и втоY
рому способу их создания.
Одна из причин излишней сложноY
сти диалогового окна Расширенныйфильтр (рис. 11.1) обусловлена налиY
чием параметров фильтра.
268 Часть II Автоматизация Excel
Рис. 11.1. Диалоговое окно Расширенныйфильтр излишне сложно в использова&нии. К счастью, у нас есть VBA!
Способ обработки исходных данных. Чтобы показать результат фильтраY
ции, скрыв ненужные строки, установите переключатель Фильтроватьсписок на месте (Filter the list, inYplace). Чтобы скопировать отфильтY
рованные строки в другую область листа, установите переключатель
Скопировать результат в другое место (Copy to another location).
Условие отбора. Фильтрация с условием позволяет отобрать подмножеY
ство строк, а фильтрация без условия YYYY подмножество столбцов исY
ходного диапазона. Фильтрация без условия применяется также при
отборе только уникальных записей.
Отбор только уникальных записей. Установите флажок Толькоуникальные записи (Unique records only), для того чтобы отобрать только
уникальные значения из заданного диапазона.
Использование расширенного фильтра дляотбора уникальных значений из заданногодиапазона
Классический пример использования расширенного фильтра заключается
в отборе уникальных значений из заданного диапазона. Предположим, что наY
звания компанийYзаказчиков расположены в столбце D исходных данных.
Общее число записей неизвестно, однако известно, что данные начинаются с
ячейки A2 (1Yя строка используется в качестве строки заголовка). Справа от
исходных данных на рабочем листе находится пустое пространство.
Отбор уникальных значений из заданного столбца спомощью пользовательского интерфейса
Установите указатель ячейки в любом месте исходного диапазона и выберите
команду меню Данные Фильтр Расширенный фильтр (Data Filter Advanced
Анализ данных с помощью расширенного фильтра Глава 11 269
Filter). При первом вызове этой команды Excel автоматически подставляет в поле
Исходный диапазон (List range) адрес исходного диапазона данных. При послеY
дующих вызовах команды Данные Фильтр Расширенный фильтр Excel подY
ставляет в поле Исходный диапазон его предыдущее значение.
Установите флажок Только уникальные записи (Unique records only), расY
положенный внизу диалогового окна Расширенный фильтр (Advanced Filter).
Установите переключатель Скопировать результат в другое место (Copy
to another location) и введите $J$1 в поле Поместить результат в диапазон(Copy to).
По умолчанию Excel копирует все столбцы исходного диапазона. Чтобы
ограничиться только столбцом D, можно сузить до него исходный диапазон
данных или скопировать заголовок столбца D в первую строку области вставки
результата. Каждый из способов имеет свои недостатки.
Сужение диапазона исходных данных до одного столбца
Введите в поле Исходный диапазон (List range) адрес диапазона исходных
данных, ограниченного столбцом D. В рассматриваемом случае это означает
замену адреса $A$1:$H$1127 адресом $D$1:$D$1127, как показано на
рис. 11.2.
Рис. 11.2. Сузьте диапазон исходных дан&ных до столбца D
Недостаток этого метода заключается в том, что Excel запоминает значение поY
ля Исходный диапазон и автоматически подставляет его при следующем вызове
команды Данные Фильтр Расширенный фильтр (Data Filter Advanced
Filter). Если позже вам понадобится отобрать уникальные значения из столбца C,
вам придется изменить адрес исходного диапазона.
Копирование заголовка столбца исходных данных в первую строкуобласти вставки результатов
Не спешите менять адрес исходного диапазона с $A$1:$H$1127 на
$D$1:$D$1127. Вместо этого введите в ячейке J1 заголовок столбца D, в данY
ном случае YYYY Заказчик (рис. 11.3).
270 Часть II Автоматизация Excel
Рис. 11.3. Чтобы не менять адрес исходного диапазона, скопируйте заголовок столбца D в ячейку J1
Обнаружив в первой строке области вставки результатов заголовок столбY
ца D, Excel скопирует исходные данные только из этого столбца. Этот способ
ограничения результата фильтрации рекомендуется применять при многоY
кратном использовании фильтра. Поскольку Excel запоминает значение поля
Исходный диапазон (List range) и автоматически подставляет его при слеY
дующем вызове команды Данные Фильтр Расширенный фильтр (Data
Filter Advanced Filter), вам не придется каждый раз изменять значение исY
ходного диапазона.
Результат отбора уникальных значений из столбца D показан на рис. 11.4.
Рис. 11.4. Отбор уникальных значений из заданного столбца — классический пример использо&вания расширенного фильтра
Анализ данных с помощью расширенного фильтра Глава 11 271
Отбор уникальных значений из заданного столбцас помощью VBA
Команде Данные Фильтр Расширенный фильтр (Data Filter Advanced
Filter) соответствует метод VBA .AdvancedFilter. Этот метод имеет
3 параметра.
Способ обработки исходных данных. Чтобы показать результат фильтраY
ции, скрыв ненужные строки, установите значение параметра Actionравным xlFilterInPlace. Чтобы скопировать отфильтрованные строY
ки в другую область листа, установите значение параметра Action равY
ным xlFilterCopy. В последнем случае установите также значение паY
раметра CopyToRange, например, CopyToRange:=Range("J1").
Условие отбора. Чтобы задать условие фильтрации, установите значение
параметра CriteriaRange, например, CriteriaRange:=Range("L1:L2"). Для фильтрации без условия не указывайте значение
этого параметра.
Отбор только уникальных записей. Чтобы отобрать только уникальные
значения из заданного диапазона, установите значение параметра
Unique равным True.
Следующий код находит результат отбора уникальных значений из столбY
ца D и помещает его на два столбца правее последнего столбца исходного диаY
пазона данных.
Sub GetUniqueCustomers()
' Выделить рабочий лист. Worksheets("Данные").Select' Очистить результат предыдущего выполнения макроса. Range("J1:AZ1").EntireColumn.Delete
Dim IRange As Range Dim ORange As Range
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Копирование заголовка столбца D в 1-ю строку 1-го столбца' области вставки результатов.' Определение целевого диапазона данных. Range("D1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора
' уникальных значений из столбца D. IRange.AdvancedFilter Action:=xlFilterCopy, _
272 Часть II Автоматизация Excel
CopyToRange:=ORange, Unique:=True
End Sub
По умолчанию расширенный фильтр копирует все столбцы исходного
диапазона. Чтобы ограничиться только столбцом D, скопируйте его заголовок
в первую строку области вставки результата.
В первой части кода определяется размер исходного диапазона данных,
точнее YYYY последняя строка исходной области и первый столбец области
вставки результата. Несмотря на то, что в этом нет прямой необходимости,
адрес исходного и целевого диапазона сохраняется в объектных переменных
IRange и ORange, соответственно.
Макрос GetUniqueCustomers не требует внесения изменений в свой код
при добавлении к исходному диапазону данных новых столбцов. Основное
предназначение объектных переменных IRange и Orange состоит в повышеY
нии читабельности программного кода. Ниже приведен код макроса, не исY
пользующего объектные переменные и не обладающего универсальностью:
Sub UniqueCustomerRedux()' Копирование заголовка столбца D в ячейку J1. Range("J1").Value = Range("D1").Value' Применение расширенного фильтра для отбора' уникальных значений из столбца D. Range("A1").CurrentRegion.AdvancedFilter xlFilterCopy, _CopyToRange:=Range("J1"), Unique:=TrueEnd Sub
Результат выполнения обоих макросов одинаков YYYY справа от исходных
данных размещается список уникальных значений из столбца D (см. рис. 11.4).
Отсортируем полученный список и подсчитаем объем выручки, приходяY
щейся на каждого заказчика. Для этого воспользуемся формулой массива, как
показано ниже:
Sub RevenueByCustomers() Dim IRange As Range Dim ORange As Range
' Выделить рабочий лист. Worksheets("Данные").Select' Очистить результат предыдущего выполнения макроса. Range("J1:AZ1").EntireColumn.Delete
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Копирование заголовка столбца D в 1-ю строку 1-го столбца' области вставки результатов.' Определение целевого диапазона данных. Range("D1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора' уникальных значений из столбца D.
Анализ данных с помощью расширенного фильтра Глава 11 273
IRange.AdvancedFilter Action:=xlFilterCopy, _CopyToRange:=ORange, Unique:=True
' Определение размера списка заказчиков. LastRow = Cells(65536, NextCol).End(xlUp).Row
' Сортировка списка заказчиков. Cells(1, NextCol).Resize(LastRow, 1).Sort Key1:=Cells(1, _NextCol), Order1:=xlAscending, Header:=xlYes
' Подсчет выручки, приходящейся на каждого заказчика,' с помощью формулы массива. Cells(1, NextCol + 1).Value = "Выручка" Cells(2, NextCol + 1).FormulaArray = "=SUM((R2C4:R" & _FinalRow & "C4=RC[-1])*R2C6:R" & FinalRow & "C6)" If LastRow > 2 Then Cells(2, NextCol + 1).Copy Cells(3, _NextCol + 1).Resize(LastRow - 2, 1) End If
End Sub
Результат выполнения макроса представлен на рис. 11.5.
Список заказчиков может служить источником данY
ных для списка или комбинированного списка, располоY
женного на пользовательской форме. Создадим макрос,
позволяющий генерировать отчет о сделках для выбранY
ных заказчиков. Добавьте к проекту форму (назовем ее
frmReport), разместите на ней список (установите знаY
чение свойства списка MultiSelect равным frmMul-tiSelectMulti) и 4 кнопки YYYY OK, Отмена, Выбратьвсе и Очистить. Процедура UserForm_Initialize исY
пользуется для заполнения списка на форме данными,
полученными в результате отбора уникальных значений
из столбца D и их последующей сортировки.
Private Sub CancelButton_Click() Unload MeEnd Sub
Private Sub cbSubAll_Click() For i = 0 To lbCust.ListCount - 1 Me.lbCust.Selected(i) = True Next iEnd Sub
Private Sub cbSubClear_Click() For i = 0 To lbCust.ListCount - 1 Me.lbCust.Selected(i) = False Next iEnd Sub
Private Sub OKButton_Click() For i = 0 To lbCust.ListCount - 1 If Me.lbCust.Selected(i) = True Then' Создание отчета. RunCustReport WhichCust:=Me.lbCust.List(i)
Рис. 11.5. Подсчет вы&ручки, приходящейсяна каждого заказчика
274 Часть II Автоматизация Excel
End If Next i Unload MeEnd Sub
Private Sub UserForm_Initialize()
Dim IRange As Range Dim ORange As Range
' Выделить рабочий лист. Worksheets("Данные").Select' Очистить результат предыдущего выполнения макроса. Range("J1:AZ1").EntireColumn.Delete
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Копирование заголовка столбца D в 1-ю строку 1-го столбца' области вставки результатов.' Определение целевого диапазона данных. Range("D1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора' уникальных значений из столбца D. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:="", CopyToRange:=ORange, Unique:=True
' Определение размера списка заказчиков. LastRow = Cells(65536, NextCol).End(xlUp).Row
' Сортировка списка заказчиков. Cells(1, NextCol).Resize(LastRow, 1).Sort _Key1:=Cells(1, NextCol), Order1:=xlAscending, Header:=xlYes
With Me.lbCust .RowSource = "" FinalRow = Range("J65536").End(xlUp).Row For Each cell In Cells(2, NextCol).Resize(LastRow - 1, 1) .AddItem cell.Value Next cell End With
' Удаление списка заказчиков. Cells(1, NextCol).Resize(LastRow, 1).ClearEnd Sub
Ниже приведен код вывода формы frmReport на экран:
Sub ShowCustForm() frmReport.ShowEnd Sub
Как показано на рис. 11.6, список заказчиков поддерживает множественY
ный выбор.
Анализ данных с помощью расширенного фильтра Глава 11 275
Рис. 11.6. Использование расширенного фильтра —один из наиболее эффективных способов запол&нения списка на форме
Отбор уникальных значений из комбинации несколькихстолбцов с помощью VBA
Чтобы отобрать уникальные значения из комбинации нескольких столбY
цов, скопируйте заголовки этих столбцов в первую строку области вставки реY
зультата. Ниже приведен пример отбора уникальных значений из комбинации
столбцов B и D.
Sub UniqueCustomerProduct() Dim IRange As Range Dim ORange As Range
' Выделить рабочий лист. Worksheets("Данные").Select' Очистить результат предыдущего выполнения макроса. Range("J1:AZ1").EntireColumn.Delete
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Копирование заголовков столбцов B и D во 2-й и 1-й столбец' 1-й строки области вставки результатов, соответственно.' Определение целевого диапазона данных. Range("D1").Copy Destination:=Cells(1, NextCol) Range("B1").Copy Destination:=Cells(1, NextCol + 1)
276 Часть II Автоматизация Excel
Set ORange = Cells(1, NextCol).Resize(1, 2)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора уникальных' значений из комбинации столбцов B и D. IRange.AdvancedFilter Action:=xlFilterCopy, _CopyToRange:=ORange, Unique:=True
' Определение количества уникальных значений. LastRow = Cells(65536, NextCol).End(xlUp).Row
' Сортировка полученного результата. Cells(1, NextCol).Resize(LastRow, 2).Sort Key1:=Cells(1, _NextCol), Order1:=xlAscending, Key2:=Cells(1, NextCol + 1), _Order2:=xlAscending, Header:=xlYes
End Sub
Как показано на рис. 11.7, компания BCD LTD приY
обрела всего один товар, зато компания CDE INC —
целых три.
На заметку Отбор уникальных значений из заданного диапазона —единственный пример использования расширенногофильтра без указания критерия фильтрации данных.
Использование расширенногофильтра с указанием условия отбораданных
Основное предназначение расширенного фильтра заY
ключается в фильтрации, т.е. отборе подмножества исY
ходных данных. Это достигается путем задания
диапазона условий.
Диапазон условий всегда включает в себя нескольY
ко строк. Первая строка содержит заголовки столбцов
исходного диапазона данных, вторая YYYY значения этих
столбцов, удовлетворяющие критерию отбора. На
рис. 11.8 показан диапазон условий J1:J2 и диапазон
вставки результата L1.
Рис. 11.7. Результат от&бора всех уникальныхкомбинаций значенийстолбцов B и D
Анализ данных с помощью расширенного фильтра Глава 11 277
Рис. 11.8. Пример задания параметров расширенного фильтра, сортирующеготовары, приобретенные заказчиком CDE INC.
Чтобы отобрать товары, приобретенные заказчиком CDE INC., с помоY
щью пользовательского интерфейса Excel, выберите команду меню ДанныеФильтр Расширенный фильтр (Data Filter Advanced Filter) и заполните
поля открывшегося диалогового окна так, как показано на рис. 11.8. Результат
отбора представлен на рис. 11.9.
Рис. 11.9. Результат применения расширенного фильтра, сортирующего товары, приобретен&ные заказчиком CDE INC.
Аналогичных результатов можно достичь с помощью следующего макроса.
Sub UniqueProductsOneCustomer() Dim IRange As Range Dim ORange As Range Dim CRange As Range
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Определение столбца, по которому будет проводиться фильтрация. Cells(1, NextCol).Value = Range("D1").Value' В действительности, значение CDE INC. должно' вводиться посредством пользовательской формы.
278 Часть II Автоматизация Excel
Cells(2, NextCol).Value = "CDE INC." Set CRange = Cells(1, NextCol).Resize(2, 1)
' Определение целевого диапазона данных.' Копирование заголовка столбца B1 в столбец L1. Range("B1").Copy Destination:=Cells(1, NextCol + 2) Set ORange = Cells(1, NextCol + 2)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора уникальных' комбинаций товаров и заданного заказчика. IRange.AdvancedFilter Action:=xlFilterCopy, CriteriaRange:= _CRange, CopyToRange:=ORange, Unique:=True' Приведенная выше строка может быть записана так:'IRange.AdvancedFilter xlFilterCopy, CRange, ORange, True
' Определение количества уникальных значений. LastRow = Cells(65536, NextCol + 2).End(xlUp).Row
' Сортировка полученного результата. Cells(1, NextCol + 2).Resize(LastRow, 1).Sort Key1:=Cells(1, _NextCol + 2), Order1:=xlAscending, Header:=xlYes
End Sub
Объединение нескольких условий с помощьюлогической операции “ИЛИ”
Расширенный фильтр позволяет отбирать значения, удовлетворяющие одY
ному из двух условий, с помощью логической операции ИЛИ. Примером поY
добного объединения условий является отбор заказчиков, которые приобрели
товар ABC или товар XYZ.
Чтобы объединить условия с помощью операции ‘‘ИЛИ’’, разместите их в
последовательных строках диапазона условий, как показано на рис. 11.10.
Объединение нескольких условий с помощью логическойоперации “И”
Расширенный фильтр позволяет отбирать значения, удовлетворяющие одY
новременно двум условиям, с помощью логической операции И. Примером
подобного объединения условий является отбор заказчиков, которые приобY
рели товар XYZ в западном регионе.
Чтобы объединить условия с помощью операции ‘‘И’’, разместите их в одY
ной строке диапазона условий, как показано на рис. 11.11.
Анализ данных с помощью расширенного фильтра Глава 11 279
Рис. 11.10. Диапазон условий J1:J3 ис&пользуется для отбора заказчиков, ко&торые приобрели товар ABC или XYZ
Рис. 11.11. Диапазон условий J1:K2 использу&ется для отбора заказчиков, которые приоб&рели товар XYZ в западном регионе
Дополнительные аспекты объединения условийс помощью логической операции “ИЛИ”
Диапазон условий, показанный на рис. 11.12, основан на значении двух
различных полей, объединенных с помощью логической операции ‘‘ИЛИ’’.
Рис. 11.12. Диапазон условий J1:K3 использует&ся для отбора заказчиков из западного региона изаказчиков, которые приобрели товар XYZ
В результате применения расширенного фильтра будут отобраны заказчиY
ки из западного региона и заказчики, которые приобрели товар XYZ.
Задание условия отбора с помощью формулы
Диапазон условий может состоять из множества критериев, объединенных
с помощью логических операций. Неэффективность такого подхода становитY
ся все более очевидной при увеличении числа критериев. Однако Excel позвоY
ляет задавать условие отбора с помощью формулы.
Практикум
Задание сложного условия отбораСоздадим усовершенствованный вариант формы создания отчета на базе формыfrmReport. Новая форма позволяет создавать отчет о сделках для выбранногозаказчика, товара, региона или их комбинации, как показано на рис. 11.13.Предположим, что пользователь выбрал двух заказчиков и два товара. Соответст&вующий диапазон условий состоит из 5 строк, что вполне приемлемо (рис. 11.14).А теперь представьте, что на некотором диапазоне исходных данных пользова&тель выбрал 10 товаров, 9 регионов и 499 заказчиков. Поскольку диапазон ус&ловий должен содержать все возможные комбинации значений полей, по кото&рым проводится отбор, его размер превысит 44 000 строк. Попробуйте создать
280 Часть II Автоматизация Excel
подобный фильтр, и вы вскоре поймете, что на его применение может уйти це&лая вечность.Чтобы не ждать так долго, задайте условие отбора с помощью формулы.
Рис. 11.13. Создание диапазона условий для такой формы может превратить&
ся в настоящий кошмар
Рис. 11.14. Диапазон условий J1:K5 исполь&
зуется для отбора заказчиков, которые при&обрели либо товар DEF, либо товар XYZ
Использование формул в качестве условия отборарасширенного фильтра
Существует альтернативная форма диапазона условий, в соответствии
с которой его первая строка (строка заголовка) остается пустой, а во второй
строке размещается булева формула. Если последняя содержит относительные
ссылки на вторую строку диапазона исходных данных, Excel автоматически
применяет формулу ко всем строкам диапазона.
Анализ данных с помощью расширенного фильтра Глава 11 281
Рассмотрим задачу отбора всех записей, для которых процент валовой приY
были не превышает 53%. Оставим ячейку J1 пустой, а в ячейку J2 поместим
булеву формулу =(H2/F2)<0.53. Диапазон условий расширенного фильтра
при этом нужно задать как J1:J2.
Применяя фильтр, Excel вычислит формулу для каждой строки исходного диаY
пазона и отберет те из них, для которых значение формулы будет равным True.
Использование формулы в качестве условия отбора расширенного
фильтра чрезвычайно эффективно. К тому же, формулы можно объединять
с помощью логических операций ‘‘И’’ и ‘‘ИЛИ’’ подобно объединению
обычных условий отбора.
Практикум
Задание диапазона условий на основе формулс помощью пользовательского интерфейсаПродемонстрируем создание диапазона условий на основе формул для задачи,рассмотренной в предыдущем практикуме.Скопируйте несколько названий фирм&заказчиков в столбец, расположенныйсправа от диапазона условий (например, в столбец O). Выделите полученный спи&сок и присвойте ему имя (например, MyCust). Введите в ячейку J2, принадлежа&щую диапазону условий, следующую формулу:=НЕ(ЕНД(ПОИСКПОЗ(D2;MyCust;ЛОЖЬ)))(В англоязычной версии Excel следует использовать формулу =NOT(ISNA(Match(D2,MyCust,False))).)Скопируйте несколько названий товаров в столбец, расположенный справа отдиапазона MyCust (например, в столбец P). Выделите полученный список и при&свойте ему имя (например, MyProd). Введите в ячейку K2, принадлежащую диа&пазону условий, следующую формулу:=НЕ(ЕНД(ПОИСКПОЗ(B2;MyProd;ЛОЖЬ)))(В англоязычной версии Excel следует использовать формулу =NOT(ISNA(Match(B2,MyProd,False))).)Наконец, скопируйте несколько названий регионов в столбец, расположенныйсправа от диапазона MyProd (например, в столбец Q). Выделите полученный спи&сок и присвойте ему имя (например, MyRegion). Введите в ячейку L2, принадле&жащую диапазону условий, следующую формулу:=НЕ(ЕНД(ПОИСКПОЗ(A2;MyRegion;ЛОЖЬ)))(В англоязычной версии Excel следует использовать формулу =NOT(ISNA(Match(A2,MyRegion,False))).)Задав диапазон условий расширенного фильтра как J1:L2, вы сможете отобратьстроки исходного диапазона, соответствующие любой комбинации значений диа&пазонов MyCust, MyProd и MyRegion.
282 Часть II Автоматизация Excel
Задание диапазона условий на основе формул с помощью VBA
Ниже приведен код усовершенствованного варианта формы для создания
отчета. Обратите внимание на метод OKButton_Click, создающий диапазон
условий на основе формул.
Private Sub CancelButton_Click() Unload MeEnd Sub
Private Sub cbSubAll_Click()' Выделить всех заказчиков. For i = 0 To lbCust.ListCount - 1 Me.lbCust.Selected(i) = True Next iEnd Sub
Private Sub cbSubClear_Click()' Отменить выделение заказчиков. For i = 0 To lbCust.ListCount - 1 Me.lbCust.Selected(i) = False Next iEnd Sub
Private Sub CommandButton1_Click()' Отменить выделение товаров. For i = 0 To lbProduct.ListCount - 1 Me.lbProduct.Selected(i) = False Next iEnd Sub
Private Sub CommandButton2_Click()' Выделить все товары. For i = 0 To lbProduct.ListCount - 1 Me.lbProduct.Selected(i) = True Next iEnd Sub
Private Sub CommandButton3_Click()' Отменить выделение регионов. For i = 0 To lbRegion.ListCount - 1 Me.lbRegion.Selected(i) = False Next iEnd Sub
Private Sub CommandButton4_Click()' Выделить все регионы. For i = 0 To lbRegion.ListCount - 1 Me.lbRegion.Selected(i) = True Next iEnd Sub
Private Sub OKButton_Click() Dim CRange As Range, IRange As Range, ORange As Range' Создание сложного условия, состоящего из нескольких' условий, объединенных с помощью логического И. NextCCol = 10 NextTCol = 15
Анализ данных с помощью расширенного фильтра Глава 11 283
For j = 1 To 3 Select Case j Case 1 MyControl = "lbCust" MyColumn = 4 Case 2 MyControl = "lbProduct" MyColumn = 2 Case 3 MyControl = "lbRegion" MyColumn = 1 End Select NextRow = 2' Проверка выбора пользователя. For i = 0 To Me.Controls(MyControl).ListCount - 1 If Me.Controls(MyControl).Selected(i) = True Then Cells(NextRow, NextTCol).Value = _Me.Controls(MyControl).List(i) NextRow = NextRow + 1 End If Next i' Создание новой формулы условия. If NextRow > 2 Then' Использование относительных ссылок на строку R2 обязательно.' В англоязычной версии Excel:' MyFormula = "=NOT(ISNA(MATCH(RC" & MyColumn & ",R2C" & _' NextTCol & ":R" & NextRow - 1 & "C" & NextTCol & ",False)))"' Cells(2, NextCCol).FormulaR1C1 = MyFormula MyFormula = "=НЕ(ЕНД(ПОИСКПОЗ(RC" & _MyColumn & ";R2C" & NextTCol & ":R" & NextRow - 1 & "C" & _NextTCol & ";Ложь)))" Cells(2, NextCCol).FormulaR1C1Local = MyFormula NextTCol = NextTCol + 1 NextCCol = NextCCol + 1 End If Next j Unload Me
' На рис. 11.15 показано текущее содержимое рабочего листа.' Закрыть форму и создать расширенный фильтр с диапазоном' условий на основе построенных выше формул. If NextCCol > 10 Then Set CRange = Range(Cells(1, 10), Cells(2, NextCCol - 1)) Set IRange = Range("A1").CurrentRegion Set ORange = Cells(1, 20) IRange.AdvancedFilter xlFilterCopy, CRange, ORange
' Очистить диапазон условий. Cells(1, 10).Resize(1, 10).EntireColumn.Clear End If
' Вывести сообщение. MsgBox "Область вставки результата применения фильтра _начинается с ячейки T1"
End Sub
284 Часть II Автоматизация Excel
Private Sub UserForm_Initialize() Dim IRange As Range Dim ORange As Range
' Определение размера диапазона исходных данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Определение целевого диапазона данных.' Копирование заголовка столбца D1 в столбец J1. Range("D1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Применение расширенного фильтра для отбора' уникальных значений из столбца D. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:="", CopyToRange:=ORange, Unique:=True
' Определение размера списка заказчиков. LastRow = Cells(65536, NextCol).End(xlUp).Row
' Сортировка списка заказчиков. Cells(1, NextCol).Resize(LastRow, 1).Sort Key1:=Cells(1, _NextCol), Order1:=xlAscending, Header:=xlYes
With Me.lbCust .RowSource = "" FinalRow = Range("J65536").End(xlUp).Row For Each cell In Cells(2, NextCol).Resize(LastRow - 1, 1) .AddItem cell.Value Next cell End With
' Удаление списка заказчиков. Cells(1, NextCol).Resize(LastRow, 1).Clear
' Определение целевого диапазона данных.' Копирование заголовка столбца B1 в столбец J1. Range("B1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Применение расширенного фильтра для отбора' уникальных значений из столбца B. IRange.AdvancedFilter Action:=xlFilterCopy, _CopyToRange:=ORange, Unique:=True
' Определение размера списка товаров. LastRow = Cells(65536, NextCol).End(xlUp).Row
' Сортировка списка товаров. Cells(1, NextCol).Resize(LastRow, 1).Sort Key1:=Cells(1, _NextCol), Order1:=xlAscending, Header:=xlYes
With Me.lbProduct .RowSource = ""
Анализ данных с помощью расширенного фильтра Глава 11 285
FinalRow = Range("J65536").End(xlUp).Row For Each cell In Cells(2, NextCol).Resize(LastRow - 1, 1) .AddItem cell.Value Next cell End With
' Удаление списка товаров. Cells(1, NextCol).Resize(LastRow, 1).Clear
' Определение целевого диапазона данных.' Копирование заголовка столбца A1 в столбец J1. Range("A1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Применение расширенного фильтра для отбора' уникальных значений из столбца A. IRange.AdvancedFilter Action:=xlFilterCopy, _CopyToRange:=ORange, Unique:=True
' Определение размера списка регионов. LastRow = Cells(65536, NextCol).End(xlUp).Row
' Сортировка списка регионов. Cells(1, NextCol).Resize(LastRow, 1).Sort Key1:=Cells(1, _NextCol), Order1:=xlAscending, Header:=xlYes
With Me.lbRegion .RowSource = "" FinalRow = Range("J65536").End(xlUp).Row For Each cell In Cells(2, NextCol).Resize(LastRow - 1, 1) .AddItem cell.Value Next cell End With
' Удаление списка регионов. Cells(1, NextCol).Resize(LastRow, 1).Clear
End Sub
На рис. 11.15 показано содержимое рабочего листа перед выполнением меY
тода AdvancedFilter.
Макрос помещает выбранные пользователем данные (заказчиков, товары и
регионы) в столбцы O, P и Q, а затем определяет диапазон условий как J1:L2.
Формула в ячейке J2 проверяет, входит ли значение в ячейке $D2 в список заY
казчиков в столбце O. Формулы в ячейках K2 и L2 осуществляют аналогичную
проверку для ячеек $B2, $A2 и столбцов P, Q, соответственно.
Внимание В справочной системе Excel VBA сказано, что для отбора данных без примененияусловия достаточно не задать диапазон условий. В Excel 2003 это не так — если выне определите диапазон условий, метод AdvancedFilter будет использоватьзначение CriteriaRange, заданное при предыдущем вызове этого метода. Что&бы избежать недоразумений, очистите значение CriteriaRange, например,укажите CriteriaRange="" при вызове метода AdvancedFilter.
286 Часть II Автоматизация Excel
Рис. 11.15. Содержимое рабочего листа перед применением расширенного фильтра
Использование условия на основе формулы при решенииэкономических задач
Следует признать, что задание диапазона условий расширенного фильтра
с помощью формулы YYYY эффективное, но редко используемое решение.
В свете этого необходимо упомянуть об одном его весьма интересном приY
менении. Ниже приведена формула, позволяющая отобрать строки, значеY
ние в столбце A которых больше среднего значения по этому столбцу на всем
диапазоне исходных данных:
=$A2>СРЗНАЧ($A$2:$A$60000)
(В англоязычной версии Excel следует использовать формулу =$A2>AVERAGE($A$2:$A$60000).)
Отбор пустого множества записей
Условие расширенного фильтра может быть задано таким образом, что в
результате применения последнего будет отобрано пустое множество записей.
Чтобы определить данную ситуацию, достаточно найти номер последней
строки области вставки результата YYYY если он равен 1 (другими словами, обY
ласть вставки результата содержит только строку заголовков столбцов), сообY
щите пользователю о том, что его запрос оказался безуспешным и выйдите из
процедуры.
Анализ данных с помощью расширенного фильтра Глава 11 287
Фильтрация диапазона исходных данных“на месте”
При фильтрации диапазона исходных данных ‘‘на месте’’ нет нужды укаY
зывать область вставки результата применения фильтра. А вот задание диапаY
зона условий является обязательным YYYY в противном случае фильтр отберет
100% исходных строк.
Обычно фильтрация ‘‘на месте’’ выполняется с помощью пользовательY
ского интерфейса Excel. Чтобы выделить строки, отобранные в результате
фильтрации ‘‘на месте’’, необходимо использовать метод VBA SpecialCellsс параметром xlCellTypeVisible. Аналогичное действие в пользовательY
ском интерфейсе Excel заключается в выборе команды Правка Перейти(Edit Go To), щелчке на кнопке Выделить (Special) в диалоговом окне
Переход (Go To) и установке переключателя Только видимые ячейки (Visible
cells only) в диалоговом окне Выделение группы ячеек (Go To Special)
(рис. 11.16).
Рис. 11.16. Фильтрация “на месте” позволяет скрыть строки, не удовлетворяю&щие заданному условию. Чтобы выделить строки, отобранные в результатефильтрации “на месте”, необходимо использовать метод VBA SpecialCells спараметром xlCellTypeVisible
Чтобы применить фильтр ‘‘на месте’’, вызовите метод AdvancedFilter,
установив значение параметра Action равным xlFilterInPlace и опустив
параметр CopyToRange, как показано ниже:
IRange.AdvancedFilter Action:=xlFilterInPlace, _CriteriaRange:=CRange, Unique:=False
288 Часть II Автоматизация Excel
Следующий код подсчитывает количество видимых строк в исходном диаY
пазоне данных после применения фильтрации ‘‘на месте’’:
For Each cell In Range("A2:A" & FinalRow).SpecialCells( _xlCellTypeVisible) Ctr = Ctr + 1Next cellMsgBox Ctr & " строк удовлетворяют заданному критерию"
Отбор пустого множества записей
Условие расширенного фильтра может быть задано таким образом, что в реY
зультате применения последнего будет отобрано пустое множество записей.
Чтобы определить данную ситуацию при фильтрации ‘‘на месте’’, следует проY
верить, возвращает ли метод SpecialCells ошибку времени выполнения 1004
(не найдено ни одной ячейки, удовлетворяющей заданным условиям).
Для этого воспользуемся универсальной ловушкой ошибок, как показано
ниже. (Более подробно обработка ошибок рассматривается в главе 23,
‘‘Обработка ошибок’’.)
On Error GoTo NoRecs For Each cell In Range("A2:A" & FinalRow).SpecialCells( _xlCellTypeVisible) Ctr = Ctr + 1 Next cell On Error GoTo 0 MsgBox Ctr & " строк удовлетворяют заданному критерию" Range("A1").Select Exit SubNoRecs: MsgBox "Нет строк, удовлетворяющих заданному критерию"
Чтобы подобная ловушка сработала, следует исключить строку заголовка
из диапазона ячеек, передаваемого методу SpecialCells. В противном слуY
чае метод SpecialCells не сгенерирует ошибку 1004, поскольку строка заY
головка остается видимой и после применения расширенного фильтра.
Отображение записей, скрытых в результатефильтрации “на месте”
Чтобы отобразить все строки исходного диапазона, скрытые в результате
применения расширенного фильтра ‘‘на месте’’, воспользуйтесь методом
ShowAllData, как показано ниже:
ActiveSheet.ShowAllData
Отбор только уникальных записейпри фильтрации “на месте”
В результате отбора только уникальных записей при фильтрации ‘‘на месY
те’’ расширенный фильтр скроет строки, в которых одинаковыми являются
Анализ данных с помощью расширенного фильтра Глава 11 289
значения всех столбцов исходного диапазона. Другими словами, фильтрация
‘‘на месте’’ не позволяет отобрать строки с уникальной комбинацией только
некоторого подмножества столбцов исходного диапазона, например, столбца
с названием фирмыYзаказчика и столбца с наименованием товара.
Использование расширенного фильтра длякопирования всех записей, удовлетворяющихзаданному условию
Ранее в этой главе рассматривался отбор уникальных значений из исходY
ного диапазона данных и их копирование в другую область рабочего листа.
В частности, составлялись списки заказчиков, регионов и товаров, которые
затем использовались при заполнении соответствующих списков на форме.
Тем не менее, в повседневной жизни расширенный фильтр обычно исY
пользуется для отбора всех записей, удовлетворяющих определенному услоY
вию. Например, при генерации отчета о сделках фильтр возвращает все запиY
си, соответствующие выбранным пользователем заказчикам.
Чтобы отобрать все записи, удовлетворяющие заданному критерию,
сбросьте флажок Только уникальные записи (Unique records only) в диалогоY
вом окне Расширенный фильтр (Advanced Filter) (если расширенный фильтр
создается с помощью пользовательского интерфейса Excel) или установите
значение параметра Unique метода AdvancedFilter равным False (если
расширенный фильтр создается с помощью VBA).
Если требуется отобрать только подмножество столбцов исходного диапаY
зона данных, скопируйте их заголовки в первую строку области вставки реY
зультата фильтрации, при необходимости изменив порядок их следования.
В следующих разделах рассматриваются различные примеры использоваY
ния расширенного фильтра.
Копирование всех столбцов исходного диапазона данных
Чтобы скопировать все столбцы строк, удовлетворяющих заданному услоY
вию, укажите в качестве области вставки результата пустую ячейку.
Sub AllColumnsOneCustomer() Dim IRange As Range Dim ORange As Range Dim CRange As Range
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Определение столбца, по которому будет проводиться фильтрация. Cells(1, NextCol).Value = Range("D1").Value' В действительности, значение CDE INC. должно' вводиться посредством пользовательской формы.
290 Часть II Автоматизация Excel
Cells(2, NextCol).Value = "CDE INC." Set CRange = Cells(1, NextCol).Resize(2, 1)
' Определение целевого диапазона данных (пустая ячейка). Set ORange = Cells(1, NextCol + 2)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора строк,' удовлетворяющих заданному условию. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:=CRange, CopyToRange:=ORange Range("L1").Select
End Sub
Результат выполнения приведенного выше макроса показан на рис. 11.17.
Рис. 11.17. Указав пустую ячейку в качестве области вставки результата приме&нения расширенного фильтра, вы получите все столбцы строк, удовлетворяю&щих заданному условию
Копирование и переупорядочивание подмножествастолбцов исходного диапазона данных
Применяя расширенный фильтр для создания отчета, вы, вероятно, захоY
тите включить в последний только некоторые столбцы исходного диапазона
данных.
Возвратимся к форме frmReport, рассматривавшейся ранее в этой главе.
Форма frmReport предназначается для создания отчета о сделках для выY
бранных пользователем заказчиков. Создание отчета осуществляется с помоY
щью процедуры RunCustReport, которая принимает в качестве параметра
имя заказчика.
Предположим, что по определенным соображениям в отчет нужно
включить только столбцы Дата, Количество, Товар и Выручка (в укаY
занном порядке).
Следующий код копирует соответствующие заголовки столбцов в первую
строку области вставки результата применения расширенного фильтра. Метод
AdvancedFilter отбирает строки, удовлетворяющие заданному условию,
как показано на рис. 11.18.
Анализ данных с помощью расширенного фильтра Глава 11 291
Рис. 11.18. Содержимое рабочего листа после применения расширенного фильтра
После этого процедура RunCustReport копирует отобранные строки в
новую рабочую книгу, добавляет заголовок отчета, итоговую строку и сохраY
няет рабочую книгу в файле с именем, совпадающим с названием соответстY
вующей фирмыYзаказчика. Пример отчета о сделках для заказчика CDE INC.
показан на рис. 11.19.
Рис. 11.19. Отчет о сделках для заказчика CDE INC.
Sub RunCustReport(WhichCust As Variant) Dim IRange As Range Dim ORange As Range Dim CRange As Range Dim WBN As Workbook Dim WSN As Worksheet Dim WSO As Worksheet
Set WSO = ActiveSheet' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Определение условия отбора. Cells(1, NextCol).Value = Range("D1").Value Cells(2, NextCol).Value = WhichCust Set CRange = Cells(1, NextCol).Resize(2, 1)
292 Часть II Автоматизация Excel
' Определение целевого диапазона данных.' В целевой диапазон войдут столбцы C (Дата),' E (Количество), B (Товар) и F (Выручка). Cells(1, NextCol + 2).Resize(1, 4).Value = Array(Cells(1, _3), Cells(1, 5), Cells(1, 2), Cells(1, 6)) Set ORange = Cells(1, NextCol + 2).Resize(1, 4)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора строк,' удовлетворяющих заданному условию. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:=CRange, CopyToRange:=ORange
' Содержимое рабочего листа на текущий
' момент показано на рис. 11.18.
' Создание новой рабочей книги для размещения' результата применения расширенного фильтра. Set WBN = Workbooks.Add(xlWBATWorksheet) Set WSN = WBN.Worksheets(1)
' Определение заголовка отчета. WSN.Cells(1, 1).Value = "Отчет о сделках для заказчика " _& WhichCust
' Копирование данных с текущего активного' рабочего листа в новую рабочую книгу. WSO.Cells(1, NextCol + 2).CurrentRegion.Copy _Destination:=WSN.Cells(3, 1) TotalRow = WSN.Cells(65536, 1).End(xlUp).Row + 1 WSN.Cells(TotalRow, 1).Value = "Всего"' В англоязычной версии Excel: ' WSN.Cells(TotalRow, 2).FormulaR1C1 = "=SUM(R2C:R[-1]C)" ' WSN.Cells(TotalRow, 4).FormulaR1C1 = "=SUM(R2C:R[-1]C)" WSN.Cells(TotalRow, 2).FormulaR1C1Local = "=СУММ(R2C:R[-1]C)" WSN.Cells(TotalRow, 4).FormulaR1C1Local = "=СУММ(R2C:R[-1]C)"
' Стилевое форматирование отчета. WSN.Cells(3, 1).Resize(1, 4).Font.Bold = True WSN.Cells(TotalRow, 1).Resize(1, 4).Font.Bold = True WSN.Cells(1, 1).Font.Size = 18
WBN.SaveAs "C:\" & WhichCust & ".xls" WBN.Close SaveChanges:=False
WSO.Select
' Очистить область вставки результата' применения расширенного фильтра. Range("J1:Z1").EntireColumn.Clear
End Sub
Анализ данных с помощью расширенного фильтра Глава 11 293
Процедура RunCustReport — это простой, однако весьма эффективный
способ создания отчетов, который может применить на практике любой польY
зователь Excel.
Практикум
Использование двух расширенных фильтровдля создания отчетов по каждому заказчикуРассмотрим итоговый макрос, применяющий два расширенных фильтра различ&ного типа для создания отчетов по каждому заказчику.1. Первый расширенный фильтр используется для создания списка заказчиков
в столбце J. Параметр Unique метода AdvancedFilter имеет значение True,а параметр CopyToRange содержит ссылку на ячейку J1, содержащую заголо&вок столбца D.' Первый расширенный фильтр - создание' списка заказчиков в столбце J.' Определение целевого диапазона.' Копирование заголовка столбца D в ячейку J1.Range("D1").Copy Destination:=Cells(1, NextCol)Set ORange = Cells(1, NextCol)
' Определение исходного диапазона данных.Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора' уникальных значений из столбца D.IRange.AdvancedFilter Action:=xlFilterCopy, CriteriaRange:="", _CopyToRange:=ORange, Unique:=True
2. Для каждого заказчика из списка выполняются действия, описанные в пп. 3–7.Приведенный ниже код определяет размер списка заказчиков и реализует со&ответствующий цикл.
' Цикл по списку заказчиков.FinalCust = Cells(65536, NextCol).End(xlUp).RowFor Each cell In Cells(2, NextCol).Resize(FinalCust - 1, 1) ThisCust = cell.Value' Выполнение действий, описанных в пп. 3-7.Next Cell
3. Условие отбора второго расширенного фильтра содержится в ячейках L1:L2(заголовок столбца D в ячейке L1, имя заказчика — в ячейке L2).
' Определение условия отбора.Cells(1, NextCol + 2).Value = Range("D1").ValueCells(2, NextCol + 2).Value = ThisCustSet CRange = Cells(1, NextCol + 2).Resize(2, 1)
4. Второй расширенный фильтр используется для копирования строк, удовлетво&ряющих заданному условию, в область, начинающуюся со столбца N. ПараметрUnique метода AdvancedFilter имеет значение False, а параметр CopyTo-Range представляет собой ссылку на диапазон ячеек N1:Q1, содержащий заго&ловки необходимых столбцов исходного диапазона данных.
294 Часть II Автоматизация Excel
' Определение целевого диапазона данных.' В целевой диапазон войдут столбцы C (Дата),' E (Количество), B (Товар) и F (Выручка).Cells(1, NextCol + 4).Resize(1, 4).Value = Array(Cells(1, 3), _Cells(1, 5), Cells(1, 2), Cells(1, 6))Set ORange = Cells(1, NextCol + 4).Resize(1, 4)
' Второй расширенный фильтр - отбор строк,' удовлетворяющих заданному условию.IRange.AdvancedFilter Action:=xlFilterCopy,CriteriaRange:=CRange, CopyToRange:=ORange
5. Результат применения второго расширенного фильтра копируется в новую ра&бочую книгу. Для создания рабочей книги используется метод Work-books.Add.
' Создание новой рабочей книги для размещения' результата применения расширенного фильтра.Set WBN = Workbooks.Add(xlWBATWorksheet)Set WSN = WBN.Worksheets(1)
' Определение заголовка отчета.WSN.Cells(1, 1).Value = "Отчет о сделках заказчика " & ThisCust
' Копирование данных с текущего активного' рабочего листа в новую рабочую книгу.WSO.Cells(1, NextCol + 4).CurrentRegion.Copy _Destination:=WSN.Cells(3, 1)
6. Последний штрих — добавление заголовка отчета и итоговой строки. Вдобавок,строка заголовков столбцов и итоговая строка выделяются полужирнымшрифтом.
' Определение заголовка отчета.WSN.Cells(1, 1).Value = "Отчет о сделках заказчика " & ThisCustTotalRow = WSN.Cells(65536, 1).End(xlUp).Row + 1WSN.Cells(TotalRow, 1).Value = "Всего"' В англоязычной версии Excel:' WSN.Cells(TotalRow, 2).FormulaR1C1 = "=SUM(R2C:R[-1]C)"' WSN.Cells(TotalRow, 4).FormulaR1C1 = "=SUM(R2C:R[-1]C)"WSN.Cells(TotalRow, 2).FormulaR1C1Local = "=СУММ(R2C:R[-1]C)"WSN.Cells(TotalRow, 4).FormulaR1C1Local = "=СУММ(R2C:R[-1]C)"
' Стилевое форматирование отчета.WSN.Cells(3, 1).Resize(1, 4).Font.Bold = TrueWSN.Cells(TotalRow, 1).Resize(1, 4).Font.Bold = TrueWSN.Cells(1, 1).Font.Size = 18
7. Новая рабочая книга сохраняется в файле с именем, совпадающим с названиемсоответствующей фирмы&заказчика, после чего эта книга закрывается. Передпереходом к следующей итерации цикла макрос очищает область вставки ре&зультата выполнения обоих расширенных фильтров.WBN.SaveAs "C:\" & ThisCust & ".xls"WBN.Close SaveChanges:=False
WSO.SelectSet WSN = NothingSet WBN = Nothing
Анализ данных с помощью расширенного фильтра Глава 11 295
' Очистить область вставки результата' применения расширенных фильтров.Cells(1, NextCol + 2).Resize(1, 10).EntireColumn.Clear
Ниже приведен полный код макроса RunReportForEachCustomer.Sub RunReportForEachCustomer() Dim IRange As Range Dim ORange As Range Dim CRange As Range Dim WBN As Workbook Dim WSN As Worksheet Dim WSO As Worksheet
Set WSO = ActiveSheet' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Первый расширенный фильтр - создание' списка заказчиков в столбце J.' Определение целевого диапазона.' Копирование заголовка столбца D в ячейку J1. Range("D1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора' уникальных значений из столбца D. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:="", CopyToRange:=ORange, Unique:=True
FinalCust = Cells(65536, NextCol).End(xlUp).Row' Цикл по списку заказчиков. For Each cell In Cells(2, NextCol).Resize(FinalCust - 1, 1) ThisCust = cell.Value
' Определение условия отбора. Cells(1, NextCol + 2).Value = Range("D1").Value Cells(2, NextCol + 2).Value = ThisCust Set CRange = Cells(1, NextCol + 2).Resize(2, 1)
' Определение целевого диапазона данных.' В целевой диапазон войдут столбцы C (Дата),' E (Количество), B (Товар) и F (Выручка). Cells(1, NextCol + 4).Resize(1, 4).Value = _Array(Cells(1, 3), Cells(1, 5), Cells(1, 2), Cells(1, 6)) Set ORange = Cells(1, NextCol + 4).Resize(1, 4)
' Второй расширенный фильтр - отбор строк,' удовлетворяющих заданному условию. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:=CRange, CopyToRange:=ORange
' Создание новой рабочей книги для размещения
296 Часть II Автоматизация Excel
' результата применения расширенного фильтра. Set WBN = Workbooks.Add(xlWBATWorksheet) Set WSN = WBN.Worksheets(1)
' Определение заголовка отчета. WSN.Cells(1, 1).Value = "Отчет о сделках заказчика " _& ThisCust
' Копирование данных с текущего активного' рабочего листа в новую рабочую книгу. WSO.Cells(1, NextCol + 4).CurrentRegion.Copy _Destination:=WSN.Cells(3, 1) TotalRow = WSN.Cells(65536, 1).End(xlUp).Row + 1 WSN.Cells(TotalRow, 1).Value = "Всего"' В англоязычной версии Excel:' WSN.Cells(TotalRow, 2).FormulaR1C1 = "=SUM(R2C:R[-1]C)"' WSN.Cells(TotalRow, 4).FormulaR1C1 = "=SUM(R2C:R[-1]C)" WSN.Cells(TotalRow, 2).FormulaR1C1Local = _"=СУММ(R2C:R[-1]C)" WSN.Cells(TotalRow, 4).FormulaR1C1Local = _"=СУММ(R2C:R[-1]C)"
' Стилевое форматирование отчета. WSN.Cells(3, 1).Resize(1, 4).Font.Bold = True WSN.Cells(TotalRow, 1).Resize(1, 4).Font.Bold = True WSN.Cells(1, 1).Font.Size = 18
WBN.SaveAs "C:\" & ThisCust & ".xls" WBN.Close SaveChanges:=False
WSO.Select Set WSN = Nothing Set WBN = Nothing
' Очистить область вставки результата' применения расширенных фильтров. Cells(1, NextCol + 2).Resize(1, 10).EntireColumn.Clear Next cell
Cells(1, NextCol).EntireColumn.Clear MsgBox FinalCust - 1 & " отчетов были успешно созданы!"End Sub
Подведем итог. Комбинация двух расширенных фильтров позволила создать27 отчетов менее чем за 1 минуту (рис. 11.20).С учетом того, что опытные пользователи Excel создают один отчет в среднем за 2–3 минуты, макрос RunReportForEachCustomer позволяет сэкономить около1 часа рабочего времени.
Анализ данных с помощью расширенного фильтра Глава 11 297
Рис. 11.20. Создание 27 отчетов менее чем за одну минуту — весьма непло&
хой результат для комбинации двух расширенных фильтров!
АвтофильтрАвтофильтр является упрощенным вариантом расширенного фильтра и
обычно применяется посредством пользовательского интерфейса.
Тем не менее, существует один аспект автофильтра, доступный исключиY
тельно посредством VBA. При выборе команды Данные Фильтр Авто-фильтр (Data Filter AutoFilter) справа от названий столбцов в фильтруемом
диапазоне появляются кнопки с изображением указывающей вниз стрелки.
Чтобы скрыть кнопки для столбцов, по которым не нужно проводить фильтY
рацию, воспользуйтесь VBA. Ниже приведен пример скрытия кнопок раскрыY
вающегося списка для столбцов C (дата), E (количество), F (выручка),
G (себестоимость) и H (прибыль):
Sub AutoFilterCustom() Range("A1").AutoFilter Field:=3, VisibleDropDown:=False Range("A1").AutoFilter Field:=5, VisibleDropDown:=False Range("A1").AutoFilter Field:=6, VisibleDropDown:=False Range("A1").AutoFilter Field:=7, VisibleDropDown:=False Range("A1").AutoFilter Field:=8, VisibleDropDown:=FalseEnd Sub
Параметр VisibleDropDown уникален тем, что он доступен только через
программный код. Выполнить аналогичное действие посредством пользоваY
298 Часть II Автоматизация Excel
тельского интерфейса Excel не представляется возможным. ‘‘Модифицируйте’’
подобным образом автофильтр, и вы прославитесь как знаток своего дела в глаY
зах коллег, ничего не подозревающих об удивительных возможностях VBA. РеY
зультат выполнения макроса AutoFilterCustom показан на рис. 11.21.
Рис. 11.21. Скрыть кнопки раскрывающихся списков для столбцов, по которымне нужно проводить фильтрацию, возможно исключительно посредством VBA
Следующий шагРасширенный фильтр Excel предоставляет впечатляющие возможности по
манипулированию исходными данными и созданию отчетов. Следующая глаY
ва посвящена одному из краеугольных камней Excel — сводным таблицам.
Комбинация расширенного фильтра и сводной таблицы YYYY это настоящая
гремучая смесь, позволяющая делать с исходными данными все, что вам заY
благорассудится!
Сводные таблицыв различных версиях Excel ........ 299Создание сводных таблицс помощью пользовательскогоинтерфейса Excel......................... 300Создание сводных таблицс помощью VBA ........................... 303Создание отчета о структуреспроса на товары......................... 309Создание отчета о структуреспроса на товары:завершающая стадия.................. 317Создание отчетао прибыльности товаров ........... 326Суммирование значенийполей области данныхсводной таблицы путемгруппирования .............................332Дополнительные возможностисводных таблиц........................... 340Сумма, среднее, количество,минимум, максимум и др. .........355Дополнительные вычисленияв полях области данныхсводной таблицы ........................ 356Следующий шаг............................ 361
12Глава 12
Ñâîäíûå òàáëèöû
Впервые концепция сводных табY
лиц была представлена компанией
Lotus в продукте Improv.
Одна из ключевых особенностей
сводных таблиц состоит в возможноY
сти быстрого суммирования больших
объемов данных. Тем не менее, это
далеко не единственное их применеY
ние. В частности, сводные таблицы
можно использовать для создания
всевозможных отчетов.
Сводные таблицыв различныхверсиях Excel
Впервые сводные таблицы были
представлены в Excel 95. В Excel 97 реаY
лизация сводных таблиц была значиY
тельно улучшена, а в Excel 2000 —
кардинальным образом изменена.
В Excel 2002 к сводным таблицам
были добавлены несколько новых
параметров.
Создавая программный код в ExY
cel 2003, следует уделить особое вниY
мание его совместимости с Excel 2000
и Excel 97. И если совместимость с
Excel 2000 достигается за счет внесеY
ния в код нескольких небольших изY
менений, то совместимость с Excel 97
требует его полного пересмотра. УчиY
тывая, что Microsoft прекратила подY
держку Excel 97, а возраст самого
продукта превышает 7 лет, в этой
главе будет использоваться кэш
сводных таблиц, впервые представY
300 Часть II Автоматизация Excel
ленный в Excel 2000. Кроме того, в конце главы будет рассмотрен метод Pivot-TableWizard YYYY единственный способ создания кода, совместимого с Excel 97.
Создание сводных таблиц с помощьюпользовательского интерфейса Excel
По имеющейся у Microsoft информации, сводные таблицы применяют
около 7% пользователей Excel. В то же время, исследование компании MrExY
cel Consulting показало, что сводные таблицы применяют около 42% опытных
пользователей Excel. Как бы то ни было, о том, что такое сводная таблица,
знают далеко не все. Рассмотрим создание простой сводной таблицы с помоY
щью пользовательского интерфейса Excel.
Предположим, что исходные данные занимают на рабочем листе свыше
12 000 строк (рис. 12.1).
Рис. 12.1. Сводная таблица позволяет подсчитать суммарные значения длябольшого объема исходных данных
Задача заключается в подсчете суммарного дохода по регионам (строки цеY
левой таблицы) и товарам (столбцы целевой таблицы). Для ее решения создаY
дим сводную таблицу, выполнив следующие действия.
Сводные таблицы Глава 12 301
1. Выделите ячейку, принадлежащую диапазону исходных данных, и выY
берите команду меню Excel Данные Сводная таблица (Data PivotY
Table and PivotChart Report).
2. В открывшемся диалоговом окне Мастер сводных таблиц идиаграмм — шаг 1 из 3 (PivotTable and PivotChart Wizard — Step 1 of 3)
установите переключатели В списке или базе данных Microsoft OfficeExcel (Microsoft Office Excel list or database) и Сводная таблица(PivotTable).
3. Убедитесь, что в поле Диапазон (Range) диалогового окна Мастерсводных таблиц и диаграмм — шаг 2 из 3 (PivotTable and PivotChart
Wizard YYYY Step 2 of 3) указан верный адрес диапазона исходных данных.
4. В диалоговом окне Мастер сводных таблиц и диаграмм — шаг 3 из 3(PivotTable and PivotChart Wizard — Step 3 of 3) установите переключаY
тель Существующий лист (Existing worksheet) и укажите адрес первой
ячейки диапазона, в который необходимо поместить сводную таблицу.
Щелкните на кнопке Макет (Layout) (рис. 12.2).
Рис. 12.2. Определите макет сводной таблицы
5. В диалоговом окне Мастер сводных таблиц и диаграмм — макет(PivotTable and PivotChart Wizard YYYY Layout) перетащите кнопку Региони отпустите ее над областью Строка (ROW). Аналогичным образом пеY
ретащите кнопку Товар и отпустите ее над областью Столбец(COLUMN). Наконец, перетащите кнопку Выручка и отпустите ее над
областью Данные (DATA). Если столбец Выручка содержит только чиY
словые сведения, при отпускании над областью Данные надпись на
кнопке Выручка изменится на Сумма по полю Выручка (Sum of ВыручY
ка), как показано на рис. 12.3. Щелкните на кнопке OK, для того чтобы
вернуться к диалоговому окну Мастер сводных таблиц и диаграмм —шаг 3 из 3.
6. Щелкните на кнопке Готово (Finish). Практически мгновенно Excel
сгенерирует сводную таблицу на основе исходных данных, как показано
на рис. 12.4.
302 Часть II Автоматизация Excel
Рис. 12.3. Чтобы определить макет сводной таблицы, перетащитекнопки, соответствующие требуемым столбцам исходных данных, иотпустите их над областями Строка, Столбец и Данные диалоговогоокна Мастер сводных таблиц и диаграмм — макет
Рис. 12.4. Сводная таблица предельно лаконична
Сводные таблицы Глава 12 303
Внимание В Excel 97 мастер сводных таблиц предполагает выполнение четырех шагов вместотрех. Шаг 3 мастера сводных таблиц Excel 97 заключается в определении макетасводной таблицы. Чтобы определить макет сводной таблицы в Excel 2003, щелк&ните на кнопке Макет (Layout) диалогового окна Мастер сводных таблиц идиаграмм — шаг 3 из 3 (PivotTable and PivotChart Wizard — Step 3 of 3).
Поместив сводную таблицу на рабочий лист, вы можете изменять ее макет
путем перетаскивания полей из окна Список полей сводной таблицы(PivotTable Field List) в сводную таблицу, и наоборот. Например, на рис. 12.5
показана сводная таблица, полученная в результате перетаскивания поля Ре-гион в область столбцов, поля Товар YYYY в область строк, и добавления в обY
ласть строк поля Заказчик.
Рис. 12.5. Сводная таблица, полученная в результате перетаскивания поля Регион вобласть столбцов, поля Товар — в область строк, и добавления в область строк по&ля Заказчик
Создание сводных таблиц с помощью VBAЧтобы создать сводную таблицу с помощью VBA в Excel 2000 и более поздY
них версиях Excel, сперва необходимо создать объект кэша сводных таблиц,
как показано далее:
304 Часть II Автоматизация Excel
Dim WSD As WorksheetDim PTCache As PivotCacheDim PT As PivotTableDim PRange As RangeDim FinalRow As LongSet WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы.For Each PT In WSD.PivotTables PT.TableRange2.ClearNext PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц.FinalRow = WSD.Cells(65536, 1).End(xlUp).RowSet PRange = WSD.Cells(1, 1).Resize(FinalRow, 8)Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
После создания кэша сводных таблиц в него можно добавить новую сводY
ную таблицу с помощью метода CreatePivotTable:
Set PT = PTCache.CreatePivotTable(TableDestination:=WSD.Range( _"J2"), TableName:="PivotTable1")
Параметр TableDestination метода CreatePivotTable определяет
размещение сводной таблицы, а параметр TableName — ее имя. Результат
выполнения приведенной выше строки кода показан на рис. 12.6.
Рис. 12.6. В результате выполнения метода CreatePivotTable Excel создает пустую свод&ную таблицу, состоящую из четырех ячеек
Сводные таблицы Глава 12 305
Excel не пересчитывает сводную таблицу при добавлении полей к ее макету
с помощью пользовательского интерфейса, однако по умолчанию пересчитыY
вает сводную таблицу на каждом шаге ее построения с помощью VBA. Чтобы
повысить эффективность программного кода, временно запретите пересчет
сводной таблицы с помощью свойства ManualUpdate:
PT.ManualUpdate = True
Теперь все готово для создания макета сводной таблицы с помощью VBA.
Воспользовавшись методом AddFields, добавьте поля к области строк, обY
ласти столбцов или области страницы сводной таблицы:
' Добавить поля к области строк и области столбцов сводной таблицы.PT.AddFields RowFields:=Array("Товар", "Заказчик"), _ColumnFields:="Регион"
Чтобы добавить поле Выручка к области данных сводной таблицы, устаY
новите значение свойства Orientation этого поля равным xlDataField(рассматривается в следующем разделе).
Подсчет суммы чисел вместо количества значенийПри добавлении поля Выручка к области данных сводной таблицы Excel
вполне логично предполагает, что вы хотите подсчитать сумму выручки. К соY
жалению, это справедливо только при условии, что все без исключения ячейY
ки столбца Выручка содержат числовые данные. Если хотя бы одна ячейка
будет содержать нечисловые данные (например, окажется пустой), вместо
суммы чисел Excel подсчитает количество значений.
Определяя макет сводной таблицы с помощью пользовательского интерY
фейса, убедитесь, что название кнопки Выручка изменилось в результате ее
перетаскивания в область Данные (DATA) на Сумма по полю Выручка (Sum
of Выручка), а не на Количество по полю Выручка (Count of Выручка). Чтобы
изменить функцию подсчета количества значений на функцию подсчета сумY
мы чисел, исправьте исходные данные или же дважды щелкните на кнопке
Количество по полю Выручка, после чего ее название изменится на Сумма пополю Выручка.
Для того чтобы указать на необходимость применения функции подсчета
суммы чисел в коде VBA, установите значение свойства Function поля Вы-ручка равным xlSum, как показано ниже:
' Определить область данных.With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1End With
Задав все необходимые параметры сводной таблицы, пересчитайте ее, усY
тановив значение свойства ManualUpdate равным False, а затем вновь заY
претите автоматический пересчет, установив значение свойства ManualUp-date равным True, как показано далее:
306 Часть II Автоматизация Excel
' Пересчитать сводную таблицу.PT.ManualUpdate = FalsePT.ManualUpdate = True
Ниже приведен полный код макроса, создающего сводную таблицу, анаY
логичную показанной на рис. 12.5:
Sub CreatePivot() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range Dim FinalRow As Long Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Добавить поля к области строк и области столбцов сводной таблицы. PT.AddFields RowFields:=Array("Товар", "Заказчик"), _ColumnFields:="Регион"
' Определить область данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 End With
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = TrueEnd Sub
Перемещение или изменение части сводной таблицы
Excel не разрешает перемещать или изменять часть сводной таблицы.
К примеру, создайте макрос, удаляющий столбец, который содержит общую
сумму по строкам сводной таблицы (в данном случае YYYY столбец O). В резульY
тате выполнения макроса будет сгенерирована ошибка времени выполнеY
ния 1004, как показано на рис. 12.7.
Сводные таблицы Глава 12 307
Рис. 12.7. Excel не разрешает перемещать илиизменять часть сводной таблицы. Чтобыобойти это ограничение, скопируйте содер&жимое ячеек сводной таблицы в другое место
Определение размера сводной таблицы
Определить размер сводной таблицы заранее достаточно сложно. Например,
рассматриваемая сводная таблица может содержать как 5, так и 6 столбцов в заY
висимости от того, была ли заключена в западном регионе хотя бы одна сделка.
Чтобы обратиться ко всей сводной таблице, необходимо воспользоваться ее
свойством TableRange2.
Поскольку Excel не разрешает перемещать или изменять часть сводной
таблицы, рекомендуется скопировать содержимое ячеек сводной таблицы в
другое место и удалить исходную таблицу. Рассмотрим макрос CreateSum-maryReportUsingPivot, который создает небольшую сводную таблицу.
Чтобы запретить добавление к сводной таблице столбца Общий итог (Grand
Total) и одноименной строки, установите равными False значения свойств
ColumnGrand и RowGrand, соответственно.
Диапазон ячеек PT.TableRange2 охватывает всю сводную таблицу,
включая строку с кнопкой Сумма по полю Выручка (Sum of Выручка). Чтобы
избавиться от этой строки, необходимо сместить ссылку PT.TableRange2 на
одну строку вниз (Offset(1, 0)). Если сводная таблица содержит нескольY
ко строк с избыточной информацией, сместите ссылку PT.TableRange2 на
соответствующее число строк.
Для вставки содержимого ячеек диапазона PT.TableRange2.Offset(1, 0)в область, начинающуюся ячейкой J10, применяется метод PasteSpecial. На
рис. 12.8 показано содержимое рабочего листа после копирования ячеек сводY
ной таблицы.
Чтобы удалить сводную таблицу, ‘‘очистите’’ диапазон ячеек PT.Table-Range2 с помощью метода Clear. Если вы затем собираетесь форматировать
рабочий лист, удалите объект PivotCache из памяти, присвоив соответстY
вующей переменной (в данном случае PTCache) значение Nothing.
308 Часть II Автоматизация Excel
Рис. 12.8. Содержимое рабочего листа перед удалением свод&ной таблицы
Sub CreateSummaryReportUsingPivot()' Этот макрос создает статический отчет' на основе сводной таблицы.
Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить область строк и область столбцов сводной таблицы. PT.AddFields RowFields:="Регион", ColumnFields:="Товар"
' Определить область данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 End With
' Запретить создание итогового столбца и итоговой строки. With PT .ColumnGrand = False .RowGrand = False .NullString = "0" End With
Сводные таблицы Глава 12 309
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Скопировать содержимое ячеек диапазона' PT.TableRange2.Offset(1, 0) в область,' начинающуюся ячейкой J10. PT.TableRange2.Offset(1, 0).Copy WSD.Range("J10").PasteSpecial xlPasteValues
' Содержимое рабочего листа на текущий' момент показано на рис. 12.8.
' Удалить сводную таблицу и объект кэша сводных таблиц. PT.TableRange2.Clear Set PTCache = NothingEnd Sub
Далее в этой главе рассматриваются более сложные примеры использоваY
ния сводных таблиц.
Создание отчета о структуре спроса на товарыОтчет о структуре спроса на товары может быть полезен менеджерам по проY
дажам, поскольку он позволяет получить наглядное представление об объеY
мах закупок товаров разными заказчиками. Отчет, показанный на рис. 12.9,
содержит список заказчиков в порядке убывания общего объема закупок в
трех регионах: центральном, восточном и западном.
Рис. 12.9. 90% этого отчета создано с помощью своднойтаблицы
310 Часть II Автоматизация Excel
Чтобы создать такой отчет, следует воспользоваться сводной таблицей.
К сожалению, конечный результат применения сводной таблицы вряд ли поY
нравится менеджерам по продажам. В частности, сводная таблица не предуY
сматривает вставку разрыва страницы между частями отчета, соответствуюY
щими разным товарам.
Тем не менее, первый шаг в подготовке отчета о структуре спроса на товаY
ры состоит в создании сводной таблицы с полями Товар и Заказчик в обласY
ти строк, полем Регион в области столбцов и полем Выручка в области данY
ных (рис. 12.10).
Рис. 12.10. В основе отчета о структуре спроса на товары лежит сводная таблица
Ниже перечислены очевидные недостатки стандартной сводной таблицы с
двумя полями в области строк.
Внешний вид стандартной сводной таблицы оставляет желать лучшего.
Как показано на рис. 12.10, значение ‘‘ABC’’ встречается в столбце То-вар всего один раз, а следующие за ним 26 ячеек и вовсе оставлены
пустыми. Это одна из наиболее серьезных недоработок сводных табY
лиц, устранить которую весьма непросто. Безусловно, большинство
пользователей догадаются, о каком товаре идет речь, однако если часть
отчета, посвященная товару ABC, будет занимать 2 и более страницы,
могут возникнуть определенные трудности. Кроме того, подобный
способ представления данных затрудняет их повторное использование.
Сводные таблицы Глава 12 311
Область данных отчета содержит пустые ячейки вместо нулей. Как поY
казано на рис. 12.10, заказчик BCD LTD. приобрел товар ABC только
в западном регионе, что побудило Excel оставить пустыми ячейки, соY
ответствующие объемам закупок товара ABC заказчиком BCD LTD.
В восточном и центральном регионах. Это может не понравиться
опытным пользователям Excel, привыкшим перемещаться по блокам
данных с помощью последовательного нажатия клавиши <End> и одY
ной из клавиш со стрелками.
Название отчета заслуживает отдельного внимания. Вряд ли комуY
нибудь понравится, чтобы предоставленный ему отчет имел заголовок
Сумма по полю Выручка (Sum of Выручка).
Заголовки некоторых столбцов сводной таблицы избыточны. К примеY
ру, слово ‘‘Регион’’, помещенное в ячейку L2 (см. рис. 12.10), и вовсе
не относится к отчету.
Стандартный порядок сортировки (по алфавиту), применяемый в сводной
таблице, не всегда используется на практике. В частности, менеджеры по
продажам предпочитают, чтобы список заказчиков был отсортирован в поY
рядке убывания суммы закупок товара. Кроме того, последовательность
регионов ‘‘ВостокYYЗападYYЦентр’’ не соответствует их естественному геоY
графическому расположению (‘‘ЗападYYЦентрYYВосток’’).
Автоматически сгенерированные границы определенно не красят отчет.
При отображении числовых значений используется формат Общий(General), хотя для подобных отчетов больше подходит формат с раздеY
лителем групп разрядов и округлением чисел до тысяч или миллионов.
Сводные таблицы не поддерживают вставку разрыва страницы между
различными частями отчета. Если отчет предназначен для нескольких
менеджеров по продажам, каждый из которых курирует определенный
товар, разрывы страниц придется добавлять самостоятельно.
Ввиду невозможности вставки разрывов страниц между различными
частями отчета, целесообразно отказаться от автоматического создания
строк с промежуточными итогами сводной таблицы и добавить строки
с промежуточными итогами и разрывы страниц вручную с помощью
метода Subtotal. Excel генерирует строки с промежуточными итогами
при наличии в области строк сводной таблицы двух и более полей (на
рис. 12.10 показана строка с промежуточными итогами для поля То-вар). Например, при наличии в области строк 4Yх полей Excel сгенериY
рует 3 строки с промежуточными итогами для 3Yх первых полей.
К счастью, каждый из перечисленных выше недостатков можно устранить
либо путем изменения параметров сводной таблицы, либо с помощью проY
граммного кода. В последнем случае содержимое сводной таблицы следует
предварительно скопировать в другую область рабочего листа или на новый
рабочий лист.
312 Часть II Автоматизация Excel
Заполнение значениями пустых ячеек в области данных
Пустые ячейки начали досаждать пользователям со времен первой реалиY
зации сводных таблиц в Excel 95. Начиная с Excel 97, Microsoft предоставляет
возможность указать значение, которое будет отображаться в пустых ячейках.
Чтобы задать значение, которое будет отображаться в пустых ячейках, откройY
те диалоговое окно Параметры сводной таблицы (PivotTable Options). Для
этого щелкните на кнопке Параметры (Options) диалогового окна Мастерсводных таблиц и диаграмм — шаг 3 из 3 (PivotTable and PivotChart Wizard —
Step 3 of 3) или выберите команду Параметры таблицы (Table Options) из расY
крывающегося списка Сводная таблица (PivotTable) панели инструментов
Сводные таблицы (PivotTable). Установите флажок Для пустых ячеекотображать (For empty cells, show) и введите 0 в расположенном справа от
флажка текстовом поле (рис. 12.11).
Рис. 12.11. Установите флажок Для пустых ячеек отображатьи введите значение, которое будет отображаться в пустыхячейках области данных сводной таблицы, в расположенномсправа от флажка текстовом поле
Чтобы выполнить эквивалентное действие в VBA, установите значение
свойства объекта сводной таблицы NullString равным "0".
На заметку Несмотря на то что в коде VBA значение, которое будет отображаться в пустыхячейках, задается как текстовый нуль, Excel помещает в пустые ячейки число&вой нуль.
Сводные таблицы Глава 12 313
Изменение порядка сортировки списка заказчиковСредства пользовательского интерфейса Excel позволяют отсортировать
список заказчиков в порядке убывания суммы закупок товара. Дважды щелкY
ните на кнопке сводной таблицы Заказчик, в результате чего откроется диаY
логовое окно Вычисление поля сводной таблицы (PivotTable Field), показанY
ное на рис. 12.12.
Рис. 12.12. Чтобы добраться до параметров сортировкисводной таблицы, щелкните на кнопке Дополнительно
Щелкните на кнопке Дополнительно (Advanced), в результате чего откроY
ется диалоговое окно Дополнительные параметры поля сводной таблицы(PivotTable Field Advanced Options). Установите переключатель По убыванию(Descending) и выберите из расположенного под переключателем раскрываюY
щегося списка значение Сумма по полю Выручка (Sum of Выручка), как покаY
зано на рис. 12.13.
Рис. 12.13. Диалоговое окно Дополнительные параметрыполя сводной таблицы позволяет выбрать порядок сорти&ровки содержимого поля сводной таблицы
Чтобы выполнить эквивалентное действие в VBA, воспользуйтесь методом
AutoSort:
PT.PivotFields("Заказчик").AutoSort Order:=xlDescending, _Field:="Сумма по полю Выручка"
314 Часть II Автоматизация Excel
Изменение порядка следования столбцовсводной таблицы вручную
Последовательность регионов YYYY столбцов сводной таблицы YYYY ‘‘ВостокYY
ЗападYYЦентр’’ не соответствует их естественному географическому распоY
ложению (‘‘ЗападYYЦентрYYВосток’’), что может смутить конечных пользоY
вателей отчета.
Microsoft предлагает весьма экстравагантный способ изменения порядка
следования столбцов сводной таблицы, получивший название ручной сортиY
ровки. Как показано на рис. 12.10, по умолчанию Excel выстраивает столбцы
сводной таблицы по алфавиту: Восток, Запад, Центр (заголовки столбцов
находятся в ячейках L3:N3, соответственно). Чтобы изменить порядок следоY
вания столбцов с помощью пользовательского интерфейса, введите в ячейке
N3 слово ‘‘Восток’’. Как по мановению волшебной палочки, Excel сместит
столбцы Запад и Центр на одну позицию влево и перенесет столбец Востокна место бывшего столбца Центр (рис. 12.14).
Рис. 12.14. Чтобы изменить стандартный порядок следования столб&цов сводной таблицы Восток, Запад, Центр с помощью пользова&тельского интерфейса, введите в ячейке N3 слово “Восток”
Чтобы выполнить эквивалентное действие в VBA, измените значение
свойства Position соответствующего столбца сводной таблицы. Поскольку
Сводные таблицы Глава 12 315
гарантии того, что столбец с заданным заголовком будет присутствовать в исY
ходных данных, нет, отключите обработку ошибок, как показано ниже
(обработке ошибок посвящена глава 23, ‘‘Обработка ошибок’’):
On Error Resume NextPT.PivotFields("Регион").PivotItems("Восток").Position = 3On Error GoTo 0
Изменение формата отображения числовых значений
Чтобы изменить формат отображения числовых значений сводной таблиY
цы с помощью пользовательского интерфейса, дважды щелкните на кнопке
Сумма по полю Выручка (Sum of Выручка). В открывшемся диалоговом окне
Вычисление поля сводной таблицы (PivotTable Field) щелкните на кнопке
Формат (Number) и выберите требуемый формат с помощью диалогового окна
Формат ячеек (Format Cells).
При работе с большими числами рекомендуется использовать формат с
разделителем групп разрядов. Ниже приведен пример задания маски формата
с разделителем групп разрядов в VBA:
PT.PivotFields("Сумма по полю Выручка").NumberFormat = "#,##0"
Зачастую суммы заказов товаров исчисляются в тысячах, а то и в миллиоY
нах. Чтобы округлить числовое значение до тысяч, добавьте в конец маски
формата запятую и, при желании, аббревиатуру ‘‘K’’:
PT.PivotFields("Сумма по полю Выручка").NumberFormat = "#,##0,K"
Некоторые компании по старой привычке используют в качестве показатеY
ля тысяч аббревиатуру ‘‘M’’, а в качестве показателя миллионов YYYY аббревиаY
туру ‘‘MM’’. Чтобы использовать в качестве показателя тысяч аббревиатуру
‘‘M’’, предварите ее символом обратной косой черты:
PT.PivotFields("Сумма по полю Выручка").NumberFormat = "#,##0,\M"
Символ обратной косой черты можно заменить двойными кавычками.
Особенность употребления двойных кавычек внутри строки в кавычках в коде
VBA заключается в необходимости дублировать каждый символ двойной каY
вычки. Ниже приведен пример задания маски #,##0.0,,"MM", используюY
щейся для округления числовых значений до десятков миллионов с аббревиаY
турой ‘‘MM’’:
PT.PivotFields("Сумма по полю Выручка").NumberFormat = _"#,##0.0,,""MM"""
Запрет автоматического добавленияпромежуточных итогов
Excel автоматически генерирует промежуточные итоги при наличии в обY
ласти строк сводной таблицы двух и более полей. Строки с промежуточными
итогами создаются для всех полей, за исключением последнего. Ввиду невозY
можности вставки разрывов страниц между частями отчета, относящимися к
316 Часть II Автоматизация Excel
разным товарам, целесообразно отказаться от автоматического добавления
промежуточных итогов сводной таблицы и добавить промежуточные итоги и
разрывы страниц вручную с помощью метода Subtotal.
Чтобы запретить автоматическое добавление промежуточных итогов для
поля Товар с помощью пользовательского интерфейса Excel, дважды щелкY
ните на кнопке Товар и установите переключатель Нет (None) в открывшемся
диалоговом окне Вычисление поля сводной таблицы (PivotTable Field), как
показано на рис. 12.15.
Рис. 12.15. Строки с промежуточными итогами созда&ются автоматически при наличии в области строксводной таблицы двух и более полей. Чтобы запретитьдобавление промежуточных итогов для некоторогополя, дважды щелкните на кнопке, соответствующейэтому полю, и установите переключатель Нет в от&крывшемся диалоговом окне
Чтобы выполнить аналогичное действие в VBA, придется изрядно потрудитьY
ся, а именно установить значение свойства поля Subtotals равным массиву, соY
стоящему из 12 значений False. Первое значение False запрещает автоматичеY
ское добавление промежуточных итогов, второе значение False — подсчет сумY
мы, третье значение False — подсчет количества и т.д. (полный синтаксис
свойства Subtotals приводится в справочной системе VBA). Следующий код заY
прещает создание строки с промежуточными итогами для поля Товар:
PT.PivotFields("Товар").Subtotals = Array(False, False, False, _False, False, False, False, False, False, False, False, False)
Чтобы сделать код более удобочитаемым, можно создать переменную, храY
нящую массив из 12 значений False:
NoSubtotalArray = Array(False, False, False, False, False, _False, False, False, False, False, False, False)PT.PivotFields("Товар").Subtotals = NoSubtotalArrayPT.PivotFields("Дата").Subtotals = NoSubtotalArray
Запрет подсчета общей суммы по столбцам
Логичным продолжением запрета автоматического добавления промежуY
точных итогов сводной таблицы будет запрет подсчета общей суммы по
Сводные таблицы Глава 12 317
столбцам, помещаемой в строку Общий итог (Grand Total). Откройте диалоY
говое окно Параметры сводной таблицы (PivotTable Options) (см. рис. 12.11).
Чтобы запретить создание строки Общий итог, снимите флажок Общаясумма по столбцам (Grand totals for columns) (чтобы запретить создание одY
ноименного столбца, сбросьте флажок Общая сумма по строкам (Grand totals
for rows)). Ниже приведен соответствующий код VBA:
PT.ColumnGrand = False
Создание отчета о структуре спроса на товары:завершающая стадия
В предыдущем разделе были рассмотрены изменения, которые можно вноY
сить непосредственно в сводную таблицу Excel. Все оставшиеся действия по
созданию отчета о структуре спроса на товары требуют копирования значений
сводной таблицы (но не саму таблицу!) в другое место.
На рис. 12.16 показана сводная таблица после выполнения всех действий,
описанных в предыдущем разделе.
Рис. 12.16. Менее 1 секунды и 30 строк программного кода понадобилосьдля того, чтобы преодолеть более 90% пути к отчету о структуре спроса натовары
318 Часть II Автоматизация Excel
Создание новой рабочей книги
Предположим, что отчет о структуре спроса на товары необходимо размесY
тить в новой рабочей книге, чтобы отправить ее затем по электронной почте.
С целью сделать код более универсальным, создадим объектные переменные
для текущей рабочей книги, новой рабочей книги и первого рабочего листа в
новой рабочей книге (см. практикум ‘‘Использование двух расширенных
фильтров для создания отчетов по каждому заказчику’’ на с. 293). Добавьте
следующий код в начало макроса:
Dim WSD As WorksheetDim WSR As WorksheetDim WBO As WorkbookDim WBN As WorkbookDim PTCache As PivotCacheDim PT As PivotTableDim PRange As Range
Set WBO = ActiveWorkbookSet WSD = Worksheets("Данные")
После построения и пересчета сводной таблицы создайте новую рабочую
книгу, как показано ниже:
' Создать новую рабочую книгу с одним листом.Set WBN = Workbooks.Add(xlWBATWorksheet)Set WSR = WBN.Worksheets(1)WSR.Name = "Отчет"' Создать заголовок отчета.With WSR.[A1] .Value = "Отчет о структуре спроса на товары" .Font.Size = 14End With
Копирование содержимого сводной таблицы
Среди недостатков сводной таблицы, показанной на рис. 12.16, следует отY
метить наличие границ, неудачный заголовок и совершенно ненужное слово
‘‘Регион’’ в ячейке L2. Все это можно устранить путем копирования содержиY
мого диапазона ячеек PT.TableRange2 (за исключением первой строки) и
его вставки в новый рабочий лист с помощью метода PasteSpecial со знаY
чением параметра Paste, равным xlPasteValuesAndNumberFormats.
Как уже отмечалось, диапазон ячеек PT.TableRange2 содержит одну изY
быточную строку — строку 2 (см. рис. 12.16). Более сложные сводные таблицы
с несколькими полями в области столбцов или области страницы будут соY
держать несколько таких строк (определить точное количество ненужных
строк поможет анализ макета сводной таблицы). Чтобы исключить избыточY
ные строки из копируемого диапазона ячеек, воспользуйтесь его свойством
Offset. Можно справедливо заметить, что смещение ссылки на диапазон
ячеек PT.TableRange2 приведет к захвату лишних строк под сводной таблиY
цей, однако это несущественно, так как эти строки пустые. После копироваY
Сводные таблицы Глава 12 319
ния содержимого сводной таблицы ее можно удалить с рабочего листа, а соотY
ветствующий объект PivotCache — из памяти компьютера:
' Скопировать содержимое сводной таблицы и поместить' его на рабочий лист "Отчет". Свойство Offset используется' для исключения из копируемого диапазона ячеек строки с' заголовком сводной таблицы.PT.TableRange2.Offset(1, 0).CopyWSR.[A3].PasteSpecial Paste:=xlPasteValuesAndNumberFormatsPT.TableRange2.ClearSet PTCache = Nothing
Обратите внимание, что метод PasteSpecial копирует только значения и
форматы чисел, что позволяет избавиться как от границ, так и от самой струкY
туры сводной таблицы. Последнее необходимо для возможности вставки в
скопированные данные новых строк.
Улучшение внешнего вида отчета
Как показано на рис. 12.17, большинство ячеек в столбце A пусты.
Рис. 12.17. Пустые ячейки — это одна из наиболее сущест&венных недоработок сводных таблиц, устранить которуювесьма непросто
Чтобы заполнить пустые ячейки столбца A соответствующими значениями с
помощью пользовательского интерфейса Excel, выполните следующие действия.
320 Часть II Автоматизация Excel
1. Выделите все ячейки столбца A, в которых должно содержаться наимеY
нование товара.
2. Выберите команду меню Правка Перейти (Edit Go To), в результате
чего откроется диалоговое окно Переход (Go To). Щелкните на кнопке
Выделить (Special) и установите в открывшемся диалоговом окне
Выделение группы ячеек (Go To Special) переключатель Пустыеячейки (Blanks) (рис. 12.18).
Рис. 12.18. Выделите пустые ячейки и заполните их форму&лой, ссылающейся на ячейку выше
3. Заполните пустые ячейки R1C1Yформулой, ссылающейся на ячейку
выше (=R[-1]C). Чтобы сделать это с помощью пользовательского инY
терфейса, введите знак равенства, нажмите клавишу <↑>, а затем YYYY
комбинацию клавиш <Ctrl+Enter>.
4. Выделите все ячейки столбца A, в которых содержится наименование
товара. Необходимость этого шага обусловлена тем, что функция спеY
циальной вставки (см. следующий шаг) не поддерживает работу с неY
смежными диапазонами ячеек.
5. Скопируйте выделенные ячейки и вставьте их с помощью команды
Правка Специальная вставка (Edit Paste Special), установив в отY
крывшемся диалоговом окне Специальная вставка (Paste Special) переY
ключатель Значения (Values).
Сводные таблицы Глава 12 321
Достижение аналогичного результата с помощью VBA можно разбить
на 3 этапа.
1. Вычисление номера последней строки отчета.
2. Вставка формулы =R[-1]C в пустые ячейки столбца A.
3. Замена формул значениями.
Ниже приведен соответствующий код VBA:
' Вычислить номер последней строки отчета по столбцу B.FinalReportRow = WSR.Range("B65536").End(xlUp).RowWith Range("A3").Resize(FinalReportRow - 2, 1) With .SpecialCells(xlCellTypeBlanks) .FormulaR1C1 = "=R[-1]C" End With .Value = .ValueEnd With
Стилевое форматирование отчета
Прежде чем добавить строки с промежуточными итогами, применим к отY
чету стилевое форматирование. Для этого выделим полужирным шрифтом
заголовки столбцов отчета (строка 3) и выровняем их по правому краю ячейки
(за исключением столбцов A и B, которые нужно выровнять по левому краю
ячейки). Последняя строка приведенного ниже кода указывает на необходиY
мость печати на каждой странице трех первых строк отчета:
' Стилевое форматирование отчета:' - выделение полужирным шрифтом заголовков столбцов;' - выравнивание заголовков столбцов по правому краю' (за исключением столбцов A-B, которые' выравниваются по правому краю).Selection.Columns.AutoFitRange("A3").EntireRow.Font.Bold = TrueRange("A3").EntireRow.HorizontalAlignment = xlRightRange("A3:B3").HorizontalAlignment = xlLeft
' Печатать на каждой странице строки 1-3 отчета.WSR.PageSetup.PrintTitleRows = "$1:$3"
Добавление промежуточных итогов
Чтобы добавить промежуточные итоги с помощью пользовательского инY
терфейса Excel, выделите область данных отчета (включая заголовки столбY
цов) и выберите команду меню Данные Итоги (Data Subtotals). Проверьте,
чтобы в открывшемся диалоговом окне Промежуточные итоги (Subtotals) был
установлен флажок Конец страницы между группами (Page breaks between
groups), как показано на рис. 12.19.
322 Часть II Автоматизация Excel
Рис. 12.19. При добавлении промежуточныхитогов с помощью команды Данные Итоги Excelпозволяет вставить разрыв страницы междугруппами данных
Если отчет о структуре спроса на товары всегда содержит столбцы Запад,
Центр и Восток, для добавления промежуточных итогов можно воспользоY
ваться следующим кодом:
' Добавление промежуточных итогов со вставкой' разрыва страницы между группами данных.Selection.Subtotal GroupBy:=1, Function:=xlSum, _TotalList:=Array(3, 4, 5, 6), Replace:=True, PageBreaks:=True, _SummaryBelowData:=True
К сожалению, выполнение этого кода приведет к ошибке при изменении
количества столбцов, соответствующих регионам. Решение этой проблемы
состоит в динамическом создании массива столбцов, по которым нужно подY
водить промежуточные итоги:
FinalCol = Cells(3, 255).End(xlToLeft).ColumnReDim Preserve TotColumns(1 To FinalCol - 2)For i = 3 To FinalCol TotColumns(i - 2) = iNext iSelection.Subtotal GroupBy:=1, Function:=xlSum, _TotalList:=TotColumns, Replace:=True, PageBreaks:=True, _SummaryBelowData:=True
Наконец, необходимо подобрать ширину столбцов отчета в соответствии с
добавленными в него строками:
' Автоподбор ширины столбцов отчета.GrandRow = Range("A65536").End(xlUp).RowCells(GrandRow, 3).Resize(1, 4).Columns.AutoFitCells(GrandRow, 3).Resize(1, 4).NumberFormat = "#,##0,K"' Добавить разрыв страницы перед' строкой "Общий итог" (Grand Total).WSR.HPageBreaks.Add Before:=Cells(GrandRow, 1)
Сводные таблицы Глава 12 323
Результирующий код
Ниже приведен полный код макроса, создающего отчет о структуре спроса
на товары:
Sub ProductLineManagerReport() Dim WSD As Worksheet Dim WSR As Worksheet Dim WBO As Workbook Dim WBN As Workbook Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range Dim TotColumns() Dim FinalRow As Long Dim FinalReportRow As Long Dim FinalCol As Integer Dim i As Integer Dim GrandRow As Long Dim NoSubtotalArray As Variant
Set WBO = ActiveWorkbook Set WSD = Worksheets("Данные")
' Удалить все существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк. PT.AddFields RowFields:=Array("Товар", "Заказчик"), _ColumnFields:="Регион"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Запретить вставку строки "Общий итог" (Grand Total). PT.ColumnGrand = False
324 Часть II Автоматизация Excel
' Запретить Excel автоматически вставлять' строки с промежуточными итогами. NoSubtotalArray = Array(False, False, False, False, False, _False, False, False, False, False, False, False) PT.PivotFields("Товар").Subtotals = NoSubtotalArray
' Отсортировать список заказчиков' в порядке убывания суммы закупок товара. PT.PivotFields("Заказчик").AutoSort Order:=xlDescending, _Field:="Сумма по полю Выручка"
' Изменить порядок следования столбцов сводной таблицы так,' чтобы был соблюден естественный порядок следования регионов' "Запад"-"Центр"-"Восток". Отключить обработку ошибок' на случай, если столбец "Восток" не существует. On Error Resume Next PT.PivotFields("Регион").PivotItems("Восток").Position = 3 On Error GoTo 0
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Создать новую рабочую книгу с одним листом. Set WBN = Workbooks.Add(xlWBATWorksheet) Set WSR = WBN.Worksheets(1) WSR.Name = "Отчет"' Создать заголовок отчета. With WSR.[A1] .Value = "Отчет о структуре спроса на товары" .Font.Size = 14 End With
' Скопировать содержимое сводной таблицы и поместить' его на рабочий лист "Отчет". Свойство Offset используется' для исключения из копируемого диапазона ячеек строки с' заголовком сводной таблицы. PT.TableRange2.Offset(1, 0).Copy WSR.[A3].PasteSpecial Paste:=xlPasteValuesAndNumberFormats PT.TableRange2.Clear Set PTCache = Nothing
' Вычислить номер последней строки отчета по столбцу B. FinalReportRow = WSR.Range("B65536").End(xlUp).Row With Range("A3").Resize(FinalReportRow - 2, 1) With .SpecialCells(xlCellTypeBlanks) .FormulaR1C1 = "=R[-1]C" End With .Value = .Value End With
' Стилевое форматирование отчета:' - выделение полужирным шрифтом заголовков столбцов;' - выравнивание заголовков столбцов по правому краю' (за исключением столбцов A-B, которые' выравниваются по правому краю). Selection.Columns.AutoFit
Сводные таблицы Глава 12 325
Range("A3").EntireRow.Font.Bold = True Range("A3").EntireRow.HorizontalAlignment = xlRight Range("A3:B3").HorizontalAlignment = xlLeft
' Печатать на каждой странице строки 1-3 отчета. WSR.PageSetup.PrintTitleRows = "$1:$3"
' Добавление промежуточных итогов со вставкой' разрыва страницы между группами данных. 'Selection.Subtotal GroupBy:=1, Function:=xlSum, _ ' TotalList:=Array(3, 4, 5, 6), Replace:=True, _ ' PageBreaks:=True, SummaryBelowData:=True
' Выполнение приведенного выше кода приведет к ошибке' при изменении количества столбцов, соответствующих регионам.' Решение этой проблемы состоит в динамическом создании массива' столбцов, по которым нужно подводить промежуточные итоги. FinalCol = Cells(3, 255).End(xlToLeft).Column ReDim Preserve TotColumns(1 To FinalCol - 2) For i = 3 To FinalCol TotColumns(i - 2) = i Next i Selection.Subtotal GroupBy:=1, Function:=xlSum, _TotalList:=TotColumns, Replace:=True, PageBreaks:=True, _SummaryBelowData:=True
' Автоподбор ширины столбцов отчета. GrandRow = Range("A65536").End(xlUp).Row Cells(GrandRow, 3).Resize(1, 4).Columns.AutoFit Cells(GrandRow, 3).Resize(1, 4).NumberFormat = "#,##0,K"' Добавить разрыв страницы' перед строкой "Общий итог" (Grand Total). WSR.HPageBreaks.Add Before:=Cells(GrandRow, 1)' Изменить заголовок последнего столбца' отчета с "Общий итог" (Grand Total) на "Всего". Cells(3, FinalCol).Value = "Всего"
MsgBox "Отчет о структуре спроса на товары успешно создан."End Sub
На рис. 12.20 показана первая страница отчета, созданного с помощью
макроса ProductLineManagerReport.
Рис. 12.20. Без применения сводных таблиц для создания подоб&ного отчета понадобилось бы разработать гораздо более сложныйкод VBA
326 Часть II Автоматизация Excel
Создание отчета о прибыльности товаровОтчет о структуре спроса на товары продемонстрировал лишь часть возY
можностей сводных таблиц. В частности, область данных рассмотренной выY
ше сводной таблицы содержала всего одно поле YYYY Выручка.
Область данных сводной таблицы, использующейся для создания отчета о
прибыльности товаров, содержит четыре поля YYYY Выручка, Количество, Се-бестоимость и Прибыль. В итоговом отчете должна быть отражена следуюY
щая информация: общее количество проданного товара, общая выручка от
продажи товара, средняя цена единицы товара, общая себестоимость проданY
ного товара, средняя себестоимость единицы товара, валовая прибыль и валоY
вая прибыль в процентах.
На рис. 12.21 показан макет сводной таблицы, созданный с помощью польY
зовательского интерфейса Excel.
Рис. 12.21. Область данных своднойтаблицы может включать несколькополей
По умолчанию Excel помещает поля области данных сводной таблицы в
последовательные ячейки столбца, как показано на рис. 12.22.
Рис. 12.22. Стандартный способ представления по&лей данных сводной таблицы не подходит для соз&дания отчета
Сводные таблицы Глава 12 327
С помощью пользовательского интерфейса Excel перетащите кнопку
Данные на кнопку Товар и отпустите ее. К сожалению, сводная таблица, поY
казанная на рис. 12.23, все еще не пригодна для создания отчета.
Выходом из сложившейся ситуации является перенесение поля Данные в
область столбцов сводной таблицы. Начав перетаскивать кнопку Данные, обY
ратите внимание на форму курсора мыши. Область сводной таблицы, в котоY
рую будет перенесено поле, обозначена синим цветом (рис. 12.24).
Областьстрок
Областьданных
Удалитьполе
Областьстолбцов
Областьстраницы
Рис. 12.23. Перемена мест полейДанные и Товар не привела кжелаемому результату
Рис. 12.24. Область сводной таблицы,в которую будет перенесено поле, обо&значена синим цветом
Результат перенесения поля Данные в область столбцов сводной таблицы
показан на рис. 12.25.
Рис. 12.25. После перенесения поля Данные в область столбцов своднаятаблица стала напоминать вполне привычный финансовый отчет
Если в область данных сводной таблицы помещается несколько полей, ExY
cel автоматически объединяет их в виртуальное поле Данные. Чтобы опредеY
лить внешний вид сводной таблицы с помощью VBA, воспользуйтесь методом
AddFields.
Сводная таблица, показанная на рис. 12.22, может быть получена в резульY
тате выполнения следующей строки кода:
PT.AddFields RowFields:=Array("Товар", "Данные")
А вот как добиться результата, показанного на рис. 12.23:
PT.AddFields RowFields:=Array("Данные", "Товар")
328 Часть II Автоматизация Excel
Чтобы поместить поле Данные в область столбцов сводной таблицы, восY
пользуйтесь приведенным ниже кодом:
PT.AddFields RowFields:="Товар", ColumnFields:="Данные"
После добавления в область столбцов сводной таблицы поля Данные опреY
делите поля области данных:
' Определение полей области данных.With PT.PivotFields("Количество") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0" .Name = "Общее количество"End With
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 2 .NumberFormat = "#,##0" .Name = "Общая выручка"End With
With PT.PivotFields("Себестоимость") .Orientation = xlDataField .Function = xlSum .Position = 4 .NumberFormat = "#,##0" .Name = "Общая себестоимость"End With
With PT.PivotFields("Прибыль") .Orientation = xlDataField .Function = xlSum .Position = 6 .NumberFormat = "#,##0" .Name = "Валовая прибыль"End With
Определение вычисляемых полей области данных
Сводные таблицы поддерживают два типа формул. Наиболее часто испольY
зуемый тип формул предназначен для добавления к сводной таблице вычисY
ляемых полей. Подсчет значений вычисляемого поля осуществляется на осноY
ве итоговых значений полей, входящих в формулу вычисляемого поля. НаY
пример, при создании поля СредняяЦена с помощью формулы Выручка/Количество Excel вычислит общую сумму по полю Выручка, общую сумму
по полю Количество и поделит первое значение на второе. В большинстве
случаев это именно то, что нужно.
Чтобы добавить вычисляемое поле с помощью VBA, воспользуйтесь метоY
дом Add объекта CalculatedFields. Метод Add имеет два параметра: имя
вычисляемого поля (Name) и его формула (Formula). Обратите внимание, что
Сводные таблицы Глава 12 329
при определении вычисляемого поля с именем СредняяЦена Excel автоматиY
чески создаст поле сводной таблицы Сумма по полю СредняяЦена (Sum of
СредняяЦена), что выглядит весьма нелепо. Устранить недоразумение позвоY
ляет свойство Name поля сводной таблицы. Также следует отметить, что имя
поля сводной таблицы должно отличаться от имени соответствующего вычисY
ляемого поля.
Ниже приведен полный код макроса, создающего отчет о прибыльности
товаров.
Sub AccountingReport() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
WSD.Range("J1:M1").EntireColumn.Clear
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Товар", ColumnFields:="Данные"
' Определить вычисляемые поля. PT.CalculatedFields.Add Name:="СредняяЦена", _Formula:="=Выручка/Количество" PT.CalculatedFields.Add Name:="СредняяСебестоимость", _Formula:="=Себестоимость/Количество" PT.CalculatedFields.Add Name:="ВаловаяПрибыль_%", _Formula:="=Прибыль/Выручка"
' Определить поля области данных. With PT.PivotFields("Количество") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0" .Name = "Общее количество" End With
With PT.PivotFields("Выручка")
330 Часть II Автоматизация Excel
.Orientation = xlDataField .Function = xlSum .Position = 2 .NumberFormat = "#,##0" .Name = "Общая выручка" End With
With PT.PivotFields("СредняяЦена") .Orientation = xlDataField .Function = xlSum .Position = 3 .NumberFormat = "#,##0.00" .Name = "Средняя цена" End With
With PT.PivotFields("Себестоимость") .Orientation = xlDataField .Function = xlSum .Position = 4 .NumberFormat = "#,##0" .Name = "Общая себестоимость" End With
With PT.PivotFields("СредняяСебестоимость") .Orientation = xlDataField .Function = xlSum .Position = 5 .NumberFormat = "#,##0.00" .Name = "Средняя себестоимость" End With
With PT.PivotFields("Прибыль") .Orientation = xlDataField .Function = xlSum .Position = 6 .NumberFormat = "#,##0" .Name = "Валовая прибыль" End With
With PT.PivotFields("ВаловаяПрибыль_%") .Orientation = xlDataField .Function = xlSum .Position = 7 .NumberFormat = "#0.0%" .Name = "Валовая прибыль, %" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
End Sub
Результат выполнения макроса AccountingReport показан на рис. 12.26.
Сводные таблицы Глава 12 331
Рис. 12.26. Полученный отчет позволяет составить мнение о прибыльности товаров
“Подводные камни” вычисляемых элементов
В отличие от вычисляемых полей, вычисляемые элементы сводной таблицы
редко используются на практике. Главная особенность вычисляемого элемента
состоит в возможности его добавления к существующему полю сводной таблицы.
Рассмотрим пример использования вычисляемых элементов. ПредполоY
жим, что одно подразделение компании занимается продажами товаров ABC
и DEF, а другое подразделение YYYY продажами товара XYZ. Добавим к полю
Товар элемент, вычисляющий сумму содержимого элементов ABC и DEF.
Sub CalculatedItemsAreEvil() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range Dim FinalRow As Long
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
WSD.Range("J1:M1").EntireColumn.Clear
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Товар", ColumnFields:="Данные"
' Создать вычисляемый элемент в поле "Товар". PT.PivotFields("Товар").CalculatedItems.Add _"Подразделение1", "=ABC+DEF"' Переместить вычисляемый элемент на 3-ю позицию. PT.PivotFields("Товар").PivotItems( _"Подразделение1").Position = 3
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0"
332 Часть II Автоматизация Excel
.Name = "Общая выручка" End With
With PT.PivotFields("Прибыль") .Orientation = xlDataField .Function = xlSum .Position = 2 .NumberFormat = "#,##0" .Name = "Валовая прибыль" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
End Sub
Результат выполнения макроса CalculatedItemsAreEvil показан на
рис. 12.27.
Проанализируем содержимое поля Общая выручка полученной сводной
таблицы. Подсчет значения вычисляемого элемента вполне корректен: 46 млн
(товар ABC) плюс 47 млн (товар DEF) примерно равняются 93 млн. Однако
итоговое значение общей выручки должно равняться 146 млн (93 млн плюс
53 млн), но никак не 241 млн! Очевидно, что при подсчете строки Общийитог (Grand Total) Excel не делает различие между обычными и вычисляемыY
ми элементами. Единственный способ исправить положение заключается в
сокрытии строк, соответствующих товарам ABC и DEF:
With PT.PivotFields("Товар") .PivotItems("ABC").Visible = False .PivotItems("DEF").Visible = FalseEnd With
Результат выполнения измененного макроса показан на рис. 12.28.
Рис. 12.27. Использование вычисляемых эле&ментов чревато весьма неприятными послед&ствиями
Рис. 12.28. После сокрытия строк, соответст&вующих товарам ABC и DEF, сводная таблицавновь содержит корректные сведения
Суммирование значений полей области данныхсводной таблицы путем группирования
Сводная таблица, показанная на рис. 12.29, содержит огромное число
строк — по одной на каждый день отгрузки товара.
Сводные таблицы Глава 12 333
Рис. 12.29. До группирования сводная таблица со&держит огромное число строк — по одной на каждыйдень отгрузки товара
В большинстве случаев такая детализация отчета избыточна YYYY гораздо боY
лее удобно анализировать итоговые объемы продаж за определенный промеY
жуток времени (например, месяц или квартал).
К счастью, Excel поддерживает группирование дат в сводной таблице. Это
гораздо эффективнее использования загадочной формулы =A2+1-ДЕНЬ(A2)(=A2+1-DAY(A2)), преобразующей произвольную дату в дату, соответствуюY
щую первому дню исходного месяца и года.
Чтобы сгруппировать даты с помощью пользовательского интерфейса ExY
cel, выделите любую ячейку в столбце ДатаОтгрузки и выберите команду
Группа и структура Группировать (Group and Show Detail) из раскрываюY
щегося списка Сводная таблица (PivotTable) панели инструментов Сводныетаблицы (PivotTable). В открывшемся диалоговом окне Группирование(Grouping) выберите значения Месяцы (Months), Кварталы (Quarters), Годы(Years) и щелкните на кнопке ОК (рис. 12.30).
Перед выполнением указанных выше действий сводная таблица содержала
поле ДатаОтгрузки с группированием дат по дням. После изменения спосоY
ба группирования дат сводная таблица также содержит поле ДатаОтгрузки,
однако теперь даты в этом поле сгруппированы по месяцам. Кроме того, в
сводную таблицу было добавлено два новых поля YYYY Годы (Years) и Кварталы
334 Часть II Автоматизация Excel
(Quarters). При группировании дат поле с наибольшей детализацией всегда
перенимает имя исходного поля, а остальные поля добавляются к сводной
таблице по мере необходимости.
Рис. 12.30. Диалоговое окно Группирование позволя&ет выбрать шаг группирования дат в сводной таблице
Результат группирования дат сводной таблицы по месяцам, кварталам и
годам показан на рис. 12.31.
Рис. 12.31. Сводные таблицы Excel поддерживают группирование дат помесяцам, кварталам и годам
Сводные таблицы Глава 12 335
Чтобы выполнить аналогичное действие в VBA, следует воспользоваться
методом Group. Метод Group должен быть применен к диапазону, состоящеY
му из одной ячейки, YYYY заголовка столбца ДатаОтгрузки или любой его
ячейки с датой. Обратите внимание, что впервые в этой главе Excel позволяетY
ся пересчитать промежуточную сводную таблицу.
Прежде всего, создадим сводную таблицу с полем ДатаОтгрузки в обласY
ти столбцов и пересчитаем ее (это необходимо для заполнения поля ДатаОт-грузки данными). При группировании дат обратимся к свойству Label-Range, возвращающему заголовок соответствующего поля сводной таблицы.
Sub ReportByMonth() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="ДатаОтгрузки", ColumnFields:="Регион"
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0" .Name = "Общая выручка" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Сгруппировать содержимое столбца "ДатаОтгрузки"' по месяцам, кварталам и годам. PT.PivotFields("ДатаОтгрузки").LabelRange.Group _Start:=True, End:=True, Periods:=Array(False, False, False, _
336 Часть II Автоматизация Excel
False, True, True, True)
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
End Sub
Группирование дат по неделям
Excel позволяет группировать даты по дням, месяцам, кварталам, годам, но
не по неделям. Этот недочет легко исправить путем определения единицы
группирования, состоящей из 7 дней.
По умолчанию Excel начинает группирование с первой даты исходных
данных, в рассматриваемом случае YYYY с четверга 1 января 2004 года. Чтобы наY
чать группирование с понедельника, следует воспользоваться параметром
Start метода Group. Функция Weekday поможет определить смещение перY
вой даты исходных данных от начала недели.
Группирование по неделям имеет один существенный недостаток YYYY оно
исключает возможность какогоYлибо иного группирования. Другими словами,
Excel не разрешает группировать даты по неделям и, к примеру, кварталам.
Ниже приведен код, реализующий группирование дат сводной таблицы по
неделям.
Sub ReportByWeek() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="ДатаОтгрузки", ColumnFields:="Регион"
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1
Сводные таблицы Глава 12 337
.NumberFormat = "#,##0" .Name = "Общая выручка" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Сгруппировать содержимое столбца "ДатаОтгрузки" по неделям.' Определить дату понедельника 1-й недели. FirstDate = PT.PivotFields("ДатаОтгрузки").LabelRange.Offset( _1, 0).Value WhichDay = Application.WorksheetFunction.Weekday(FirstDate, 3) StartDate = FirstDate - WhichDay PT.PivotFields("ДатаОтгрузки").LabelRange.Group _Start:=StartDate, End:=True, By:=7, _ Periods:=Array(False, False, False, True, False, _False, False)
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
End Sub
Результат выполнения макроса ReportByWeek показан на рис. 12.32.
Рис. 12.32. При необходимости даты сводной таблицыможно сгруппировать по неделям
338 Часть II Автоматизация Excel
Определение сроков выполнения заказов
Как упоминалось выше, при группировании дат поле с наибольшей детаY
лизацией всегда перенимает имя исходного поля, а остальные поля
(например, Годы (Years) или Кварталы (Quarters)) добавляются к сводной
таблице по мере необходимости.
Предположим, что предприятию необходимо определить сроки выполнеY
ния заказов. Полный цикл производства товара включает 12Yнедельную заY
держку, связанную с поставкой необходимых компонентов. В идеальной сиY
туации на выполнение заказа отводится не менее 13 недель. В противном слуY
чае следует реализовать систему прогнозирования поступления заказов.
Добавив к исходным данным поле ДатаЗаказа, создадим сводную таблиY
цу, показывающую поступление выручки от выполнения заказов по месяцам.
1. Создайте сводную таблицу с полем ДатаОтгрузки в области строк и
полем Выручка в области данных. Сгруппируйте поле ДатаОтгрузкипо месяцам и годам, в результате чего Excel создаст два новых поля YYYY
ДатаОтгрузки и Годы (Years).
2. Поместите поля ДатаОтгрузки и Годы в область столбцов сводной
таблицы.
3. Добавьте поле ДатаЗаказа к области строк сводной таблицы.
4. Сгруппируйте поле ДатаЗаказа по месяцам и годам, в результате чего
Excel создаст два новых поля YYYY ДатаЗаказа и Годы2 (Years2).
Следует отметить, что новые версии Excel корректно справляются с групY
пировкой дат в нескольких полях сводной таблицы, подтверждением чего моY
жет служить факт создания поля Годы2 вместо поля Годы.
Ниже приведен полный код макроса, создающего отчет о поступлении выY
ручки от выполнения заказов по месяцам.
Sub MeasureLeadtime() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные (2)")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Сводные таблицы Глава 12 339
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("K2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк. PT.AddFields RowFields:="ДатаОтгрузки"
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0" .Name = "Общая выручка" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Сгруппировать содержимое поля "ДатаОтгрузки" по месяцам и годам. PT.PivotFields("ДатаОтгрузки").LabelRange.Group _Start:=True, End:=True, Periods:=Array(False, False, False, _False, True, False, True)
' Поместить поля "Годы" и "ДатаОтгрузки" в область столбцов. With PT.PivotFields("Годы") .Orientation = xlColumnField .Position = 1 End With With PT.PivotFields("ДатаОтгрузки") .Orientation = xlColumnField .Position = 2 End With
' Добавить поле "ДатаЗаказа" в область строк. With PT.PivotFields("ДатаЗаказа") .Orientation = xlRowField .Position = 1 End With
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Сгруппировать содержимое поля "ДатаЗаказа" по месяцам и годам. PT.PivotFields("ДатаЗаказа").LabelRange.Group _Start:=True, End:=True, Periods:=Array(False, False, False, _False, True, False, True)
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
End Sub
340 Часть II Автоматизация Excel
Результат выполнения макроса MeasureLeadtime показан на рис. 12.33.
Рис. 12.33. Данный отчет свидетельствует о том, что пред&приятие нуждается в гибкой системе прогнозирования по&ступления заказов
Дополнительные возможности сводных таблицЗачастую даже опытные пользователи Excel не знакомы со всеми возможY
ностями сводных таблиц. Восполним же этот пробел!
Отображение лучшей десятки заказчиков
Согласно широко известному правилу 80/20, 80% дохода компании приноY
сят 20% ее клиентов. В реальных условиях это не так уж далеко от истины.
Создавая отчет для руководства компании, разумно включить в него данY
ные о 5YY10 лучших заказчиках.
Чтобы отобразить заданное число наибольших (наименьших) элементов поY
ля сводной таблицы с помощью пользовательского интерфейса Excel, щелкните
на кнопке Дополнительно (Advanced) диалогового окна Вычисление полясводной таблицы (PivotTable Field). Установите переключатель Включено (On),
выберите из раскрывающегося списка Отображать (Show) значение
Наибольших (Top), установите значение расположенного справа от списка
Отображать счетчика равным 6 и выберите из раскрывающегося списка
Сводные таблицы Глава 12 341
С помощью поля (Using field) значение Сумма по полю Выручка (Sum of ВыY
ручка), как показано на рис. 12.34.
Рис. 12.34. Функция Автоотображение лучшей десятки(Top 10 AutoShow) позволяет отобразить заданное число наи&больших (наименьших) элементов поля сводной таблицы
На заметку Название функции Автоотображение лучшей десятки (Top 10 AutoShow) можетввести в заблуждение. На самом деле, Excel позволяет отобразить заданное число(от 1 до 500) как наибольших, так и наименьших значений поля сводной таблицы.
Чтобы отобразить 6 наибольших элементов поля Заказчик с расчетом по
полю Общая выручка в VBA, воспользуйтесь методом AutoShow:
' Вывести 6 лучших заказчиков.PT.PivotFields("Заказчик").AutoShow Type:=xlAutomatic, _Range:=xlTop, Count:=6, Field:="Общая выручка"
Вызвав метод AutoShow и пересчитав сводную таблицу, рекомендуется
скопировать полученный отчет, после чего вернуться к исходной сводной
таблице, чтобы подсчитать значение поля Общая выручка для всех заказY
чиков. В приведенном ниже коде это достигается путем удаления поля За-казчик из сводной таблицы, ее пересчета и копирования в отчет полученY
ной итоговой строки.
Sub Top6CEOReport()' Этот макрос создает отчет о 6 лучших заказчиках' с полями "Общая прибыль", "Валовая прибыль" и' "Валовая прибыль, %". Dim WSD As Worksheet Dim WSR As Worksheet Dim WBN As Workbook Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
342 Часть II Автоматизация Excel
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Заказчик", ColumnFields:="Данные"
' Определить вычисляемые поля. PT.CalculatedFields.Add Name:="ВаловаяПрибыль_%", _Formula:="=Прибыль/Выручка"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K" .Name = "Общая выручка" End With
With PT.PivotFields("Прибыль") .Orientation = xlDataField .Function = xlSum .Position = 2 .NumberFormat = "#,##0,K" .Name = "Валовая прибыль" End With
With PT.PivotFields("ВаловаяПрибыль_%") .Orientation = xlDataField .Function = xlSum .Position = 3 .NumberFormat = "#0.0%" .Name = "Валовая прибыль, %" End With
' Отсортировать поле "Заказчик" по убыванию,' используя значение поля "Общая выручка". PT.PivotFields("Заказчик").AutoSort Order:=xlDescending, _Field:="Общая выручка"
' Отобразить 6 наибольших значений поля "Заказчик"' с расчетом по полю "Общая выручка". PT.PivotFields("Заказчик").AutoShow Type:=xlAutomatic, _Range:=xlTop, Count:=6, Field:="Общая выручка"
Сводные таблицы Глава 12 343
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Создать новую рабочую книгу' с одним рабочим листом. Set WBN = Workbooks.Add(xlWBATWorksheet) Set WSR = WBN.Worksheets(1) WSR.Name = "Отчет"' Создать заголовок отчета. With WSR.[A1] .Value = "6 лучших заказчиков" .Font.Size = 14 End With
' Скопировать диапазон ячеек TableRange2.Offset(1, 0)' в рабочий лист "Отчет" новой рабочей книги. PT.TableRange2.Offset(1, 0).Copy WSR.[A3].PasteSpecial Paste:=xlPasteValuesAndNumberFormats LastRow = WSR.Cells(65536, 1).End(xlUp).Row WSR.Cells(LastRow, 1).Value = "Всего (по 6 заказчикам)"
' Подсчитать значение поля "Общая выручка" для всех заказчиков. PT.PivotFields("Заказчик").Orientation = xlHidden PT.ManualUpdate = False PT.ManualUpdate = True PT.TableRange2.Offset(2, 0).Copy WSR.Cells(LastRow + 2, 1).PasteSpecial Paste:= _xlPasteValuesAndNumberFormats WSR.Cells(LastRow + 2, 1).Value = "Всего (по всем заказчикам)"
' Удалить сводную таблицу и объект PivotCache. PT.TableRange2.Clear Set PTCache = Nothing
' Применить стилевое форматирование:' - выделить заголовки столбцов полужирным шрифтом;' - выровнять заголовки столбцов по правому краю;' - выполнить автоподбор ширины столбцов отчета. Range("A3").EntireRow.Font.Bold = True Range("A3").EntireRow.HorizontalAlignment = xlRight Range("A3").HorizontalAlignment = xlLeft Range("B3").Value = "Общая выручка" WSR.Range(WSR.Range("A2"), WSR.Cells(LastRow + 2,4)).Columns.AutoFit
Range("A2").Select MsgBox "Отчет для руководства компании успешно создан"
End Sub
Результат выполнения макроса Top6CEOReport показан на рис. 12.35.
344 Часть II Автоматизация Excel
Рис. 12.35. Отчет о 6 лучших заказчиках компании создан на основе двухсводных таблиц
Обратите внимание, что отчет о 6 лучших заказчиках компании был создан
на основе двух сводных таблиц YYYY таблицы с полем Заказчик в области строк,
которая позволила получить список 6 лучших заказчиков, и таблицы без полей в
области строк, которая использовалась для подсчета итоговой выручки.
Использование сводной таблицы для фильтрацииисходных данных
Проведем небольшой эксперимент. Дважды щелкните на любом числовом
значении сводной таблицы, в результате чего Excel создаст новый рабочий лист и
скопирует в него все записи исходных данных, на основании которых было полуY
чено это числовое значение. По существу, эта особенность сводных таблиц предY
ставляет собой простейший способ выполнения запросов к исходным данным.
Чтобы добиться аналогичного результата в VBA, следует установить значеY
ние свойства диапазона ячеек ShowDetail равным True, как показано ниже:
PT.TableRange2.Offset(2, 1).Resize(1, 1).ShowDetail = True
Следующий макрос создает сводную таблицу, содержащую сведения об
общей выручке, приходящейся на трех лучших заказчиков. Подробная инY
формация обо всех сделках каждого заказчика выводится на отдельном рабоY
чем листе. Похожая задача рассматривалась в практикуме ‘‘Использование
двух расширенных фильтров для создания отчетов по каждому заказчику’’ на
с. 293, где для ее решения предлагалось использовать расширенный фильтр.
Sub RetrieveTop3CustomerDetail() Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
Сводные таблицы Глава 12 345
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Заказчик", ColumnFields:="Данные"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K" .Name = "Общая выручка" End With
' Отсортировать поле "Заказчик" по убыванию,' используя значение поля "Общая выручка". PT.PivotFields("Заказчик").AutoSort Order:=xlDescending, _Field:="Общая выручка"
' Отобразить 3 наибольших значения поля "Заказчик"' с расчетом по полю "Общая выручка". PT.PivotFields("Заказчик").AutoShow Type:=xlAutomatic, _Range:=xlTop, Count:=3, Field:="Общая выручка"
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Создать отчеты о сделках каждого заказчика. For i = 1 To 3 PT.TableRange2.Offset(i + 1, 1).Resize(1, 1).ShowDetail =True Range("A1:A2").EntireRow.Insert Range("A1").Value = "Информация о сделках с заказчиком " _& PT.TableRange2.Offset(i + 1, 0).Resize(1, 1).Value & " _(порядковый номер в списке лучших заказчиков: " & i & ")"
Next i
MsgBox "Отчеты о сделках с 3-мя лучшими заказчиками _успешно созданы"
End Sub
346 Часть II Автоматизация Excel
Отчет о сделках самого крупного заказчика, полученный в результате выY
полнения макроса RetrieveTop3CustomerDetail, показан на рис. 12.36.
Рис. 12.36. В результате выполнения макроса была создана сводная таблица, со&держащая сведения о 3&х лучших заказчиках, а также подробные отчеты о сделкахкаждого из заказчиков
Использование полей области страницы сводной таблицы
В добавок к полям в области строк, области столбцов и области данных,
сводная таблица может содержать одно или несколько полей в области страY
ницы. Поля области страницы выводятся в строках, расположенных над отчеY
том сводной таблицы. Они могут использоваться для фильтрации отчета по
различным критериям, например, по товару, региону или комбинации товара
и региона. На рис. 12.37 показана сводная таблица, отображающая 10 лучших
заказчиков товара ABC в западном регионе.
Чтобы определить поле области страницы в VBA, воспользуйтесь параметY
ром PageFields метода AddFields. Следующий код определяет сводную
таблицу с полем Регион в области страницы:
PT.AddFields RowFields:="Заказчик", ColumnFields:="Данные", _PageFields:="Регион"
По умолчанию значение поля области страницы Регион устанавливается
равным (Все) ((All)). Чтобы ограничиться данными только по какомуYлибо
определенному региону (например, западному), воспользуйтесь свойством
CurrentPage объекта PivotField:
PT.PivotFields("Регион").CurrentPage = "Запад"
Сводные таблицы Глава 12 347
Рис. 12.37. Область страницы сводной таблицы содержит по&ля Регион и Товар, которые могут использоваться дляфильтрации отчета по товару, региону или их комбинации
Поля области страницы часто применяются при создании пользовательY
ской формы, позволяющей выбрать требуемый регион или товар. Присвоив
выбранное значение свойству CurrentPage, можно быстро получить требуеY
мый отчет.
Другое применение поля области страницы заключается в создании отчеY
тов для всех значений этого поля. К примеру, чтобы определить общее число
регионов, воспользуйтесь свойством Count объекта PivotItems:
PT.PivotFields("Регион").PivotItems.Count
Ниже приведено два равноценных цикла по всем значениям поля Регион:
For i = 1 To PT.PivotFields("Регион").PivotItems.Count PT.PivotFields("Регион").CurrentPage = _PT.PivotFields("Регион").PivotItems(i).Name PT.ManualUpdate = False PT.ManualUpdate = TrueNext i
For Each PivItem In PT.PivotFields("Регион").PivotItems PT.PivotFields("Регион").CurrentPage = PivItem.Name PT.ManualUpdate = False PT.ManualUpdate = TrueNext PivItem
Конечно же, поочередный вывод отчетов на экран имеет немного практиY
ческого смысла. Обычно отчет создается для его последующего сохранения.
Ранее в этой главе для копирования содержимого сводной таблицы приY
менялось свойство TableRange2 объекта PivotTable. Свойство Table-Range2 ссылается на все строки сводной таблицы, включая строки полей обY
ласти страницы. Чтобы исключить строки полей области страницы, воспольY
зуйтесь свойством TableRange1 объекта PivotTable. Следующие строки
ссылаются на одинаковый диапазон ячеек рабочего листа, показанного на
рис. 12.37:
348 Часть II Автоматизация Excel
PT.TableRange2.Offset(4, 0)PT.TableRange1.Offset(1, 0)
Выбор того или иного свойства YYYY дело личного предпочтения. Тем не меY
нее, свойство TableRange2 имеет некоторое преимущество, так как вызов
метода TableRange2.Clear позволяет удалить сводную таблицу с рабочего
листа. А вот попытка вызова метода TableRange1.Clear неминуемо заверY
шится выводом сообщения об ошибке времени выполнения 1004, напомиY
нающего о невозможности перемещения или изменения части сводной табY
лицы Excel.
Следующий макрос создает отчеты о 10 лучших заказчиках в каждом из
регионов. Каждый отчет помещается в новую рабочую книгу.
Sub Top10ByRegionReport()' Этот макрос создает отчет о 10 лучших' заказчиках в каждом из регионов. Dim WSD As Worksheet Dim WSR As Worksheet Dim WBN As Workbook Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк, области' столбцов и области страницы. PT.AddFields RowFields:="Заказчик", ColumnFields:="Данные", _PageFields:="Регион"
' Определить вычисляемые поля. PT.CalculatedFields.Add Name:="ВаловаяПрибыль_%", _Formula:="=Прибыль/Выручка"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K"
Сводные таблицы Глава 12 349
.Name = "Общая выручка" End With
With PT.PivotFields("Прибыль") .Orientation = xlDataField .Function = xlSum .Position = 2 .NumberFormat = "#,##0,K" .Name = "Валовая прибыль" End With
With PT.PivotFields("ВаловаяПрибыль_%") .Orientation = xlDataField .Function = xlSum .Position = 3 .NumberFormat = "#0.0%" .Name = "Валовая прибыль, %" End With
' Отсортировать поле "Заказчик" по убыванию,' используя значение поля "Общая выручка". PT.PivotFields("Заказчик").AutoSort Order:=xlDescending, _Field:="Общая выручка"
' Отобразить 10 наибольших значений поля "Заказчик"' с расчетом по полю "Общая выручка". PT.PivotFields("Заказчик").AutoShow Type:=xlAutomatic, _Range:=xlTop, Count:=10, Field:="Общая выручка"
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True Ctr = 0
' Создать цикл по всем значениям поля "Регион". For Each PivItem In PT.PivotFields("Регион").PivotItems Ctr = Ctr + 1 PT.PivotFields("Регион").CurrentPage = PivItem.Name PT.ManualUpdate = False PT.ManualUpdate = True
' Создать новую рабочую книгу с одним рабочим листом. Set WBN = Workbooks.Add(xlWBATWorksheet) Set WSR = WBN.Worksheets(1) WSR.Name = PivItem.Name' Создать заголовок отчета. With WSR.[A1] .Value = "10 лучших заказчиков в регионе " & _PivItem.Name .Font.Size = 14 End With
' Скопировать диапазон ячеек TableRange2.Offset(3, 0)' в рабочий лист новой рабочей книги. PT.TableRange2.Offset(3, 0).Copy WSR.[A3].PasteSpecial Paste:=xlPasteValuesAndNumberFormats
350 Часть II Автоматизация Excel
LastRow = WSR.Cells(65536, 1).End(xlUp).Row WSR.Cells(LastRow, 1).Value = "Всего ( _по 10 лучшим заказчикам)"
' Применить стилевое форматирование:' - выделить заголовки столбцов полужирным шрифтом;' - выровнять заголовки столбцов по правому краю;' - выполнить автоподбор ширины столбцов отчета. Range("A3").EntireRow.Font.Bold = True Range("A3").EntireRow.HorizontalAlignment = xlRight Range("A3").HorizontalAlignment = xlLeft Range("B3").Value = "Выручка" WSR.Range(WSR.Range("A3"), WSR.Cells( _LastRow, 4)).Columns.AutoFit
Range("A2").Select
Next PivItem
' Удалить сводную таблицу и объект PivotCache. PT.TableRange2.Clear Set PTCache = Nothing
MsgBox Ctr & " отчета о лучших заказчиках в регионах _успешно созданы"
End Sub
Отчеты, созданные в результате выполнения макроса Top10ByRegion-Report, показаны на рис. 12.38.
Рис. 12.38. Отчеты о 10 лучших заказчиках в каждом регионе созданы с помощью цикла повсем значениям поля Регион
Сводные таблицы Глава 12 351
Фильтрация элементов полей сводной таблицы вручную
Вдобавок к созданию вычисляемых элементов, Excel позволяет фильтроY
вать элементы полей сводной таблицы вручную.
Рассмотрим следующую задачу. К примеру, некий продавец обуви желает
получить отчет о продажах сандалий в регионах с теплым климатом. СледуюY
щая строка кода позволяет скрыть определенный элемент поля Магазин:
PT.PivotFields("Магазин").PivotItems("Миннеаполис").Visible = False
Присвоение значения False свойству Visible всех элементов поля приY
ведет к возникновению ошибки времени выполнения. К примеру, на 1Yй итеY
рации цикла макрос может отобразить товары A и B, а на 2Yй итерации YYYY тоY
вары C и D. Если скрыть товары A и B до того, как будут отображены товаY
ры C и D, возникнет ошибка. Чтобы избежать подобного недоразумения,
присвойте значение True свойству Visible всех элементов поля перед его
фильтрацией.
Создадим отчет, включающий информацию о выручке, прибыли и валовой
прибыли (%) от продаж товаров ABC и DEF по регионам.
Чтобы создать такой отчет с помощью пользовательского интерфейса
Excel, необходимо выполнить следующие действия.
1. Создать сводную таблицу с полем Регион в области строк, полем То-вар в области столбцов и полем Выручка в области данных.
2. Вручную отфильтровать поле Товар, скрыв все элементы за исключеY
нием ABC и DEF.
3. Переместить поле Товар из области столбцов в область страницы.
4. Поместить поля Прибыль и ВаловаяПрибыль_% (вычисляемое поле) в
область данных.
Сводная таблица, полученная в результате выполнения указанных выше
действий, показана на рис. 12.39.
Рис. 12.39. Элемент XYZ поля Товар скрыт, однако обэтом нет никаких уведомлений
Узнать о том, что элемент XYZ поля Товар скрыт, позволяет лишь раскрыY
вающийся список, расположенный справа от названия поля Товар (рис. 12.40).
Создание подобного отчета рекомендуется автоматизировать с помощью
VBA. Это позволит не только добавить заголовок и стилевое форматирование,
352 Часть II Автоматизация Excel
но и поместить поле Товар непосредственно в область страницы, отфильтроY
вав нужные элементы с помощью свойства Visible.
Рис. 12.40. Факт фильтрации поля Товар обнаруживаетсялишь при раскрытии соответствующего списка
Ниже приведен полный код макроса, создающего отчет о продажах товаров
ABC и DEF по регионам.
Sub ProduceReportOfTwoProducts()' Этот макрос создает отчет о продажах' товаров ABC и DEF в регионах. Dim WSD As Worksheet Dim WSR As Worksheet Dim WBN As Workbook Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Регион", ColumnFields:=Array( _"Данные", "Товар")
' Определить вычисляемые поля. PT.CalculatedFields.Add Name:="ВаловаяПрибыль_%", _
Сводные таблицы Глава 12 353
Formula:="=Прибыль/Выручка"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K" .Name = "Общая выручка" End With
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True
' Убедиться, что поле "Товар" не содержит скрытых элементов. For Each PivItem In PT.PivotFields("Товар").PivotItems PivItem.Visible = True Next PivItem
' Отфильтровать поле "Товар". For Each PivItem In PT.PivotFields("Товар").PivotItems Select Case PivItem.Name Case "ABC", "DEF" PivItem.Visible = True Case Else PivItem.Visible = False End Select Next PivItem
' Переместить поле "Товар" в область страницы. PT.PivotFields("Товар").Orientation = xlPageField
' Добавить оставшиеся поля области данных. With PT.PivotFields("Прибыль") .Orientation = xlDataField .Function = xlSum .Position = 2 .NumberFormat = "#,##0,K" .Name = "Валовая прибыль" End With
With PT.PivotFields("ВаловаяПрибыль_%") .Orientation = xlDataField .Function = xlSum .Position = 3 .NumberFormat = "#0.0%" .Name = "Валовая прибыль, %" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True Ctr = 0
354 Часть II Автоматизация Excel
' Создать новую рабочую книгу с одним рабочим листом. Set WBN = Workbooks.Add(xlWBATWorksheet) Set WSR = WBN.Worksheets(1) WSR.Name = "Отчет"' Создать заголовок отчета. With WSR.[A1] .Value = "Продажи по регионам - только товары ABC и DEF" .Font.Size = 14 End With
' Скопировать диапазон ячеек TableRange2.Offset(3, 0)' в рабочий лист новой рабочей книги. PT.TableRange2.Offset(3, 0).Copy WSR.[A3].PasteSpecial Paste:=xlPasteValuesAndNumberFormats LastRow = WSR.Cells(65536, 1).End(xlUp).Row WSR.Cells(LastRow, 1).Value = "Всего (по товарам ABC и DEF)"
' Применить стилевое форматирование:' - выделить заголовки столбцов полужирным шрифтом;' - выровнять заголовки столбцов по правому краю;' - выполнить автоподбор ширины столбцов отчета. Range("A3").EntireRow.Font.Bold = True Range("A3").EntireRow.HorizontalAlignment = xlRight Range("A3").HorizontalAlignment = xlLeft Range("B3").Value = "Выручка" WSR.Range(WSR.Range("A3"), WSR.Cells(LastRow, _4)).Columns.AutoFit
Range("A2").Select
' Удалить сводную таблицу и объект PivotCache. PT.TableRange2.Clear Set PTCache = Nothing
MsgBox "Отчет о продажах товаров ABC и DEF успешно создан"
End Sub
Результат выполнения макроса ProduceReportOfTwoProducts показан
на рис. 12.41.
Рис. 12.41. VBA позволяет гарантировать отсутствие ошибки временивыполнения и упростить создание отчета о продажах товаров ABC и DEFпо регионам
Сводные таблицы Глава 12 355
Сумма, среднее, количество, минимум,максимум и др.
Единственной статистикой, подсчитываемой для поля сводной таблицы
ранее в этой главе, была сумма значений элементов поля. Помимо суммы, ExY
cel позволяет вычислить среднее значение всех элементов поля, минимальное,
максимальное значения и т.п. Чтобы подсчитать требуемую статистику в VBA,
создайте поле области данных с уникальным именем и установите соответстY
вующее значение свойства xlFunction. Следующий макрос демонстрирует
пример вычисления 5Yти различных статистик для поля Выручка по каждому
заказчику.
Sub ReportManyDetailsByCustomer()' Макрос, вычисляющий общую сумму выручки, количество' заказов, среднюю выручку, минимальный и максимальный' заказ для каждого клиента. Dim WSD As Worksheet Dim PTCache As PivotCache Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных и создать' объект кэша сводных таблиц. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8) Set PTCache = ActiveWorkbook.PivotCaches.Add( _SourceType:=xlDatabase, SourceData:=PRange.Address)
Set PT = PTCache.CreatePivotTable(TableDestination:= _WSD.Range("J2"), TableName:="PivotTable1") PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Заказчик", ColumnFields:="Данные"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K" .Name = "Общая выручка" End With
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlCount .Position = 2
356 Часть II Автоматизация Excel
.NumberFormat = "#,##0" .Name = "Кол-во заказов" End With
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlAverage .Position = 3 .NumberFormat = "#,##0" .Name = "Средняя выручка" End With
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlMin .Position = 4 .NumberFormat = "#,##0" .Name = "Минимальный заказ" End With
With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlMax .Position = 5 .NumberFormat = "#,##0" .Name = "Максимальный заказ" End With
' Заполнить нулями пустые ячейки в области данных. PT.NullString = "0"
' Пересчитать сводную таблицу. PT.ManualUpdate = False PT.ManualUpdate = True Ctr = 0
WSD.Select
End Sub
Результат выполнения макроса ReportManyDetailsByCustomer покаY
зан на рис. 12.42.
Дополнительные вычисления в полях областиданных сводной таблицы
Excel позволяет проводить так называемые дополнительные вычисления в
полях области данных сводной таблицы. В частности, значения элементов поY
ля можно отображать в виде доли от общей суммы, доли от суммы по строке,
доли от суммы по столбцу или в виде разницы по отношению к значению
другого элемента (рис. 12.43).
Сводные таблицы Глава 12 357
Рис. 12.42. Каждое поле области данных сводной таблицы содержит различную статисти&ку поля Выручка (слева направо): общая сумма выручки, количество заказов, средняясумма выручки, минимальная сумма заказа, максимальная сумма заказа
Рис. 12.43. Excel предлагает разнообразные способыпроведения дополнительных вычислений в поляхобласти данных сводной таблицы
358 Часть II Автоматизация Excel
В VBA вид дополнительного вычисления определяется с помощью свойстY
ва Calculation объекта PivotField, которое может принимать следующие
значения: xlPercentOf, xlPercentOfColumn, xlPercentOfRow, xlPer-centOfTotal, xlRunningTotal, xlPercentDifferenceFrom, xlDif-ferenceFrom, xlIndex и xlNoAdditionalCalculation. Некоторые тиY
пы вычислений требуют указания базового поля (свойство BaseField объекY
та PivotField) или комбинации базового поля и базового элемента
(свойство BaseItem объекта PivotField). Более подробно дополнительные
вычисления в полях области данных сводной таблицы рассматриваются в слеY
дующих разделах этой главы.
Доля от общей суммы
Чтобы отобразить значения элементов поля в виде доли от общей суммы
по этому полю, установите значение свойства Calculation равным xlPer-centOfTotal, как показано ниже:
' Подсчитать долю от общей суммы.With PT.PivotFields("Выручка") .Orientation = xlDataField .Caption = "Доля от общей суммы" .Function = xlSum .Position = 2 .NumberFormat = "#0.0%" .Calculation = xlPercentOfTotalEnd With
Приведенное отличие от значенияпредыдущего элемента поля
Сгруппировав даты поля ДатаОтгрузки по месяцам, можно составить отY
чет о процентном изменении выручки относительно предыдущего месяца.
Для этого установите значение параметра Calculation равным xlPer-centDifferenceFrom, параметра BaseField — ДатаОтгрузки, а параY
метра BaseItem — (назад) ((previous)).
' Подсчитать процентное отличие от предыдущего месяца.With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Caption = "Отличие, %" .Calculation = xlPercentDifferenceFrom .BaseField = "ДатаОтгрузки" .BaseItem = "(назад)" .Position = 3 .NumberFormat = "#0.0%"End With
Одно из наиболее существенных ограничений, касающееся вычислений,
содержащих позиционные ссылки, заключается в невозможности использоY
вания методов AutoSort и AutoShow. В частности, это не позволяет сравY
Сводные таблицы Глава 12 359
нить общий объем закупок товаров заказчиками, предварительно отсортироY
вав поле Заказчик по убыванию с помощью поля Общая выручка.
Приведенное отличие от значениязаданного элемента поля
Предположим, что компания предлагает своим клиентам товары трех каY
тегорий: аппаратное обеспечение, программное обеспечение и договора на
обслуживание компьютерной техники. Менеджеры по продажам получили заY
дание увеличить выручку от продажи программного обеспечения и заключеY
ния договоров на обслуживание на 10% по сравнению с выручкой от продажи
аппаратного обеспечения. Чтобы создать соответствующую сводную таблицу
с помощью VBA, установите значение параметра Calculation равным
xlPercentDifferenceFrom, как показано ниже:
' Подсчитать процентное отличие от выручки, полученной' в результате продажи аппаратного обеспечения.With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Caption = "Отличие (%) от продаж аппаратного обеспечения" .Calculation = xlPercentDifferenceFrom .BaseField = "Товар" .BaseItem = "Аппаратное обеспечение" .Position = 3 .NumberFormat = "#0.0%"End With
Нарастающий итог
Для вычисления нарастающего итога по полю сводной таблицы необходиY
мо указать базовое поле. В рассматриваемом примере таким полем является
поле области строк сводной таблицы ДатаОтгрузки. Таким образом, чтобы
вычислить нарастающий итог по полю Выручка, необходимо установить знаY
чение свойства BaseField равным ДатаОтгрузки, как показано ниже:
' Подсчитать нарастающий итог.With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Caption = "Нарастающий итог" .Calculation = xlRunningTotal .Position = 4 .NumberFormat = "#,##0,K" .BaseField = "ДатаОтгрузки"End With
На рис. 12.44 показана сводная таблица с тремя полями области данных,
полученными в результате описанных выше дополнительных вычислений, а
именно: вычисления доли от общей суммы, приведенного отличия от значеY
ния предыдущего элемента поля и нарастающего итога.
360 Часть II Автоматизация Excel
Рис. 12.44. Каждое из полей области данных сводной таблицы содержитразличные вычисления на основе суммы по полю Выручка: собственносумма по полю Выручка, доля от общей суммы по полю Выручка, при&веденное отличие от значения предыдущего элемента поля Общая вы-ручка и нарастающий итог по полю Общая выручка
Практикум
Создание сводных таблиц в Excel 97с помощью VBAВпервые сводные таблицы были представлены в Excel 95. В Excel 97 реализациясводных таблиц была значительно улучшена, а в Excel 2000 — кардинальным об&разом изменена за счет добавления объекта кэша сводных таблиц PivotCache.Несмотря на то что Microsoft официально прекратила поддержку Excel 97 несколь&ко лет назад, этим продуктом до сих пор пользуются все еще достаточно многокомпаний. Чтобы обеспечить совместимость кода VBA, созданного с помощью Ex&cel 2003, с Excel 97, его придется изрядно переработать.Для создания сводной таблицы в Excel 97 используется метод PivotTable-Wizard. Рассмотрим пример построения простой сводной таблицы, отображаю&щей выручку от продажи товаров по регионам.Sub PivotExcel97Compatible()' Этот макрос полностью совместим с Excel 97. Dim WSD As Worksheet Dim PT As PivotTable Dim PRange As Range
Set WSD = Worksheets("Данные")
' Удалить существующие сводные таблицы. For Each PT In WSD.PivotTables PT.TableRange2.Clear Next PT
' Задать диапазон исходных данных. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row
Сводные таблицы Глава 12 361
Set PRange = WSD.Cells(1, 1).Resize(FinalRow, 8)
' Создать сводную таблицу с помощью метода PivotTableWizard. Set PT = WSD.PivotTableWizard(SourceType:=xlDatabase, _SourceData:=PRange.Address, _ TableDestination:="R2C10", TableName:="PivotTable1")
PT.ManualUpdate = True' Определить поля области строк и области столбцов. PT.AddFields RowFields:="Регион", ColumnFields:="Товар"
' Определить поля области данных. With PT.PivotFields("Выручка") .Orientation = xlDataField .Function = xlSum .Position = 1 .NumberFormat = "#,##0,K" .Name = "Общая выручка" End With
PT.ManualUpdate = False PT.ManualUpdate = True
WSD.Select
End Sub
Следующий шагСводные таблицы YYYY чрезвычайно гибкое и мощное средство в арсенале
пользователя Excel. В комбинации с VBA они предоставляют превосходный
вычислительный механизм и основу для создания всевозможных отчетов.
В следующей главе рассматриваются распространенные задачи, встречающиеY
ся при работе с Excel, и их решения с помощью VBA, предлагаемые опытными
программистами со всех уголков мира.
Расширение возможностейExcel с помощью VBA.................. 363Файловые операции .................. 365Объединение и разделениерабочих книг .................................372Работа с примечаниями .............376Замечательные возможностиExcel VBA ........................................381VBA для профессионалов.......... 386На закуску ..................................... 402Следующий шаг........................... 405
13Глава 13
Excel âñåìîãóùèé
Первый принцип удачного проY
граммиста состоит в том, чтобы ниY
когда не писать один и тот же код
дважды. Второй принцип удачного
программиста требует создавать код
для каждой рутинной операции.
В настоящей главе рассматриваY
ются макросы, присланные опытY
ными пользователями Excel со
всего мира. Эти программы не
только помогут сэкономить время,
но и прольют свет на новые спосоY
бы решения наиболее распростраY
ненных задач.
Различные программисты приY
держиваются различных подходов к
созданию программного кода. НаY
глядным подтверждением этого явY
ляются приведенные в данной главе
макросы.
Расширениевозможностей Excelс помощью VBA
VBA позволяет раскрыть возможY
ности Excel, недоступные посредстY
вом пользовательского интерфейса.
В следующих разделах рассматриваY
ется использование VBA для реалиY
зации условного форматирования с
более чем тремя условиями, и расY
ширенного фильтра с более чем двуY
мя условиями.
364 Часть II Автоматизация Excel
Условное форматирование с более чем тремя условиями
Макрос Worksheet_Change любезно предоставлен Расселом Гауфом
(Russell Hauf), проживающим в Бивертоне, штат Орегон, США.
По сути, макрос Worksheet_Change дублирует функцию условного форY
матирования Excel. Увеличение максимального числа условий достигается за
счет наличия управляющего листа, содержащего 2 столбца YYYY допустимых
значений ячеек (столбец A) и соответствующих индексов цвета заливки
(столбец B).
Private Sub Worksheet_Change(ByVal Target As Range)' Этот макрос реализует условное форматирование' с более чем 3-мя условиями. Dim rng As Range' Исходный диапазон может содержать более чем 1 ячейку,' поскольку он задается как пересечение диапазонов Target' и D:D. Благодаря этому макрос будет выполняться корректно' при удалении содержимого нескольких ячеек столбца D или' вводе значений нескольких ячеек столбца D в виде массива. Set rng = Intersect(Target, Range("D:D")) If rng Is Nothing Then Exit Sub Else Dim cl As Range For Each cl In rng On Error Resume Next' Если содержимое ячейки отсутствует' в диапазоне rngColors, ее заливка удаляется. cl.Interior.ColorIndex = _ Application.WorksheetFunction.VLookup(cl.Value, _ThisWorkbook.Sheets("УФ (управляющий лист)").Range("rngColors"), _2, False) If Err.Number <> 0 Then cl.Interior.ColorIndex = xlNone End If Next cl End IfEnd Sub
Расширенный фильтр с более чем двумя условиями
Макрос MultiFilter любезно предоставлен Ричи Силлсом (Richie Sills),
проживающим в Вустере, Англия. Ричи работает консультантом по налогам в
аудиторской компании.
Стандартный расширенный фильтр Excel позволяет задавать не более двух
условий. Следующий макрос позволяет обойти это ограничение.
Sub MultiFilter()' Обратите внимание, что Ричи размещает комментарии после кода.' Другими словами, комментарий на строке 20 относится к коду' на строке 19. Dim rngTarget As Range, rng1 As Range, rng2 As Range, _rngMyRange As Range Const Crit1 As String = "DEF, LLC"
Excel всемогущий Глава 13 365
Const Crit2 As String = "FGH LTD." Const Crit3 As String = "QRS INC."' Критерии расширенного фильтра.
Application.ScreenUpdating = False With Worksheets("Расширенный фильтр") .Rows(1).Insert .Range("A1").Value = "dummy"' Создать заголовок "dummy".
Set rngTarget = .Range("A1:A" & .Cells(Rows.Count, _1).End(xlUp).Row)' Задать исходный диапазон.
rngTarget.AutoFilter Field:=1, Criteria1:=Crit1, _Operator:=xlOr, Criteria2:=Crit2' Применить стандартный расширенный фильтр с 2-мя критериями.
Set rng1 = .AutoFilter.Range.Offset(1, 0).Resize( _.AutoFilter.Range.Rows.Count - 1).SpecialCells(xlCellTypeVisible)' Создать диапазон ячеек, ссылающийся' на результат применения расширенного фильтра.
rngTarget.AutoFilter' Вернуться к исходным данным.
rngTarget.AutoFilter Field:=1, Criteria1:=Crit3' Применить стандартный расширенный фильтр с 3-м критерием.
Set rng2 = .AutoFilter.Range.Offset(1, 0).Resize( _.AutoFilter.Range.Rows.Count - 1).SpecialCells(xlCellTypeVisible)' Создать диапазон ячеек, ссылающийся' на результат применения расширенного фильтра.
rngTarget.AutoFilter .Rows(1).Delete' Удалить заголовок "dummy".
Set rngMyRange = Union(rng1, rng2)' Объединить диапазоны ячеек rng1 и rng2.
rngMyRange.EntireRow.Copy Destination:=Worksheets( _"РФ (результат)").Range("A2")' Скопировать полученный результат в рабочий лист "РФ (результат)". End With Worksheets("РФ (результат)").Select Application.ScreenUpdating = True MsgBox "Фильтр успешно применен"End Sub
Файловые операцииМакросы, приведенные в следующих разделах, используются для выполY
нения различных файловых операций.
366 Часть II Автоматизация Excel
Поиск файлов
Макрос Srch и функция BrowseForFolderShell любезно предоставлеY
ны Натаном П. Оливером (Nathan P. Oliver), проживающим в Миннеаполисе,
штат Миннесота, США. Натан занимает должности финансового консультанY
та и разработчика приложений.
Следующий макрос проводит поиск файлов, в имени которых встречается
заданная строка, в указанной папке и ее подпапках. По результатам поиска
создается список, в котором для каждого найденного файла выводится ссылка
на файл, размер файла и дата его последнего изменения.
Sub Srch() Dim i As Long, z As Long, ws As Worksheet, y As Variant Dim fLdr As String y = Application.InputBox("Пожалуйста, введите строку, _встречающуюся в имени файла", "Ввод информации") If y = False And Not TypeName(y) = "String" Then Exit Sub Application.ScreenUpdating = False fLdr = BrowseForFolderShell With Application.FileSearch .NewSearch .LookIn = fLdr .SearchSubFolders = True .Filename = y Set ws = ThisWorkbook.Worksheets.Add(Sheets(1)) On Error GoTo 12: ws.Name = "Результат поиска файлов" On Error GoTo 0 If .Execute() > 0 Then For i = 1 To .FoundFiles.Count If Left$(.FoundFiles(i), 1) = Left$(fLdr, 1) Then If CBool(Len(Dir(.FoundFiles(i)))) Then z = z + 1 ws.Cells(z + 1, 1).Resize(, 3) = _ Array(Dir(.FoundFiles(i)), _ FileLen(.FoundFiles(i)) \ 1000, _ FileDateTime(.FoundFiles(i))) ws.Hyperlinks.Add Anchor:=Cells(z + 1, _1), Address:=.FoundFiles(i) End If End If Next i End If End With
ActiveWindow.DisplayHeadings = False
With ws With .[a1:c1 ] .Value = [{"Ссылка на файл","Размер (Kb)","Дата _последнего изменения"}] .Font.Underline = xlUnderlineStyleSingle .EntireColumn.AutoFit .HorizontalAlignment = xlCenter End With
Excel всемогущий Глава 13 367
.[d1:iv1 ].EntireColumn.Hidden = True Range(.[a65536 ].End(3)(2), _ .[a65536 ]).EntireRow.Hidden = True Range(.[a2 ], .[c65536 ]).Sort [a2 ], xlAscending, _Header:=xlNo End With
Application.ScreenUpdating = True Exit Sub
1: Application.DisplayAlerts = False Worksheets("Результат поиска файлов").Delete Application.DisplayAlerts = True GoTo 2End Sub
Function BrowseForFolderShell() As String Dim objShell As Object, objFolder As Object Set objShell = CreateObject("Shell.Application")' Расскомментируйте следующую строку, чтобы начать' обзор папок с рабочего стола Windows. 'Set objFolder =objShell.BrowseForFolder(0,"Пожалуйста, _выберите папку", 0, 0)
' Укажите папку, с которой нужно начать обзор. Set objFolder = objShell.BrowseForFolder(0, "Пожалуйста, _выберите папку", 0, "c:\") If (Not objFolder Is Nothing) Then On Error Resume Next If IsError(objFolder.Items.Item.Path) Then _BrowseForFolderShell = CStr(objFolder): GoTo Here On Error GoTo 0 If Len(objFolder.Items.Item.Path) > 3 Then BrowseForFolderShell = objFolder.Items.Item.Path _& Application.PathSeparator Else BrowseForFolderShell = objFolder.Items.Item.Path End If Else: Application.ScreenUpdating = True: End End If
Here: Set objFolder = Nothing: Set objShell = NothingEnd Function
Удаление рабочей книги после определенной даты
Макрос Workbook_Open любезно предоставлен Томом Уртисом (Tom UrY
tis), проживающим в СанYФранциско, штат Калифорния, США. Том YYYY глава
консалтинговой компании Atlas Programming Management, расположенной в
районе Залива.
Приведенный ниже макрос удаляет активную рабочую книгу после
31 декабря 2004 года.
368 Часть II Автоматизация Excel
Внимание Файл рабочей книги не помещается в Корзину (Recycle Bin), а навсегда удаляется скомпьютера.
Sub Workbook_Open() If Date <= #12/31/2004# Then Exit Sub MsgBox "Сейчас рабочая книга будет удалена!" With ThisWorkbook .Saved = True .ChangeFileAccess xlReadOnly Kill .FullName .Close False End WithEnd Sub
Создание команды меню “Закрыть и удалить”Макросы Workbook_AddinInstall, Workbook_AddinnUninstall и
CloseAndKill любезно предоставлены Томми Майлзом (Tommy Miles),
проживающем в Хьюстоне, штат Техас, США.
Одной из наиболее распространенных операций, выполняемых в Excel, являY
ется открытие временного файла, копирование из него нужной информации и заY
крытие этого файла. Подобные файлы ‘‘разового использования’’ имеют свойство
накапливаться, захламляя пространство жесткого диска компьютера.
Макрос Workbook_AddinInstall добавляет к меню Excel Файл (File) коY
манду Закрыть и удалить (рис. 13.1), выполнение которой приводит к закрытию
активной рабочей книги и удалению ее файла. Помимо этого, имя файла активY
ной рабочей книги удаляется из списка ранее открывавшихся файлов.
Рис. 13.1. Команда Закрыть и удалить использу&ется для закрытия активной рабочей книги и уда&ления ее файла
Excel всемогущий Глава 13 369
Public Const CONTROLNAME As String = "Закрыть и у&далить"
Sub Workbook_AddinInstall() Dim cmdControl As CommandBarButton On Error Resume Next Set cmdControl =Application.CommandBars(1).Controls("Файл").Controls(CONTROLNAME) If cmdControl Is Nothing Then Set cmdControl =Application.CommandBars(1).Controls("Файл").Controls.Add( _Type:=msoControlButton, Before:=Application.CommandBars( _1).Controls("Файл").Controls("Сохранить").Index) With cmdControl .Caption = CONTROLNAME .FaceId = 67 .Style = msoButtonIconAndCaption .DescriptionText = "Закрыть активную рабочую книгу _и удалить ее файл" .OnAction = "CloseAndKill" End With Set cmdControl = Nothing End If On Error GoTo 0 MsgBox "Команда ""Закрыть и удалить"" доступна из меню ""Файл"""End Sub
Sub Workbook_AddinUninstall() On Error Resume Next Application.CommandBars(1).Controls("Файл").Controls( _CONTROLNAME).Delete MsgBox "Команда ""Закрыть и удалить"" успешно удалена _из меню ""Файл"""End Sub
Sub CloseAndKill() Dim tmpAnswer As Variant If ActiveWorkbook Is Nothing Then Exit Sub
tmpAnswer = MsgBox("Вы действительно хотите удалить _рабочую книгу " & ActiveWorkbook.FullName & """?", _vbYesNoCancel + vbInformation) If tmpAnswer = vbYes Then Dim tmpFileName As String Dim RecentFle As RecentFile tmpFileName = ActiveWorkbook.FullName' Удалить имя файла активной рабочей книги из списка' ранее открывавшихся файлов. For Each RecentFle In Application.RecentFiles If RecentFle.Path = tmpFileName Then RecentFle.Delete Next ActiveWorkbook.Close SaveChanges:=False On Error Resume Next Kill tmpFileName ' Удалить файл. If Err.Number <> 0 Then MsgBox "Невозможно удалить файл """ & tmpFileName &"""." End If End IfEnd Sub
370 Часть II Автоматизация Excel
Импорт CSVLфайлов
Макрос OpenLargeCSVFast любезно предоставлен Масару Каджи
(Masaru Kaji), проживающим в КобеYСити, Япония. Масару автор WebYсайта
Colo’s Excel Junk Room (http://www.puremis.net/excel/), посвященного
Excel и VBA.
Следующий макрос импортирует CSVYфайл в Excel, а затем удаляет его.
Option Base 1
Sub OpenLargeCSVFast() Dim buf(1 To 256) As Variant Dim i As Long Const strFilePath As String = "C:\temp\Test.CSV" 'Подставьте _сюда путь к нужному файлу. Dim strRenamedPath As String strRenamedPath = Split(strFilePath, ".")(0) & "txt" With Application .ScreenUpdating = False .DisplayAlerts = False End With
'Создание массива для параметра FieldInfo метода OpenText. For i = 1 To 256 buf(i) = Array(i, 2) Next
Name strFilePath As strRenamedPath Workbooks.OpenText Filename:=strRenamedPath, _DataType:=xlDelimited, Comma:=True, FieldInfo:=buf Erase buf ActiveSheet.UsedRange.Copy ThisWorkbook.Sheets( _"CSV").Range("A5") ActiveWorkbook.Close False Kill strRenamedPath With Application .ScreenUpdating = True .DisplayAlerts = True End WithEnd Sub
Считывание текстового файла в памятьи его последующий анализ
Макрос ReadTxtLines любезно предоставлен Суатом Мехметом Озгуром
(Suat Mehmet Ozgur), проживающим в Стамбуле, Турция. Суат разрабатывает
ExcelY, AccessY и Visual BasicYприложения для компаний MrExcel Consulting и
TheOfficeExperts.com.
Следующий макрос реализует весьма необычный подход к считыванию соY
держимого текстового файла. Вместо построчного чтения, макрос загружает
весь файл в память и сохраняет его в строковой переменной. Затем полученY
ная переменная разбивается на строки. Преимущество этого метода заключаY
Excel всемогущий Глава 13 371
ется в единственном обращении к файлу на диске. Последующая обработка
файла осуществляется в памяти компьютера.
Sub ReadTxtLines()'Нет необходимости устанавливать библиотеку'Microsoft Scripting Runtime, поскольку'в этом макросе используется позднее связывание. Dim sht As Worksheet Dim fso As Object Dim fil As Object Dim txt As Object Dim strtxt As String Dim tmpLoc As Long
'Работаем с активным рабочим листом. Set sht = ActiveSheet
'Очистить содержимое активного рабочего листа. sht.UsedRange.ClearContents
'Создать объект файловой системы. Set fso = CreateObject("Scripting.FileSystemObject")
'Создать объект требуемого файла. Set fil = fso.GetFile("c:\test.csv")
'Открыть файл как текст. Set txt = fil.OpenAsTextStream(1)
'Считать содержимое файла в строковую переменную. strtxt = txt.ReadAll
'Закрыть файл. txt.Close
'Найти позицию 1-го символа новой строки. tmpLoc = InStr(1, strtxt, vbCrLf)
'Выполнять до тех пор, пока в переменной strtxt'будут оставаться символы новой строки. Do Until tmpLoc = 0'Сохранить строку в следующей пустой ячейке столбца А. sht.Cells(65536, 1).End(xlUp).Offset(1).Value = _Left(strtxt, tmpLoc - 1)' Удалить "отработанную" строку из переменной. strtxt = Right(strtxt, Len(strtxt) - tmpLoc - 1)'Найти позицию следующего символа новой строки. tmpLoc = InStr(1, strtxt, vbCrLf) Loop'Последняя строка не содержит символа новой строки. sht.Cells(65536, 1).End(xlUp).Offset(1).Value = strtxt'Несмотря на то что файл уже закрыт, правила "хорошего тона"'требуют установить значение переменной fso равным Nothing. Set fso = NothingEnd Sub
372 Часть II Автоматизация Excel
Объединение и разделение рабочих книгСледующие 4 макроса демонстрируют возможность объединения нескольY
ких рабочих книг в одну, а также сохранения листов рабочей книги в виде отY
дельных рабочих книг или документов Word.
Сохранение листов рабочей книги в виде отдельныхрабочих книг
Макрос SplitWorkbook любезно предоставлен Томми Майлзом (Tommy
Miles), проживающим в Хьюстоне, штат Техас, США.
Следующий макрос сохраняет все листы активной рабочей книги в виде
отдельных рабочих книг, файлы которых носят имена соответствующих рабоY
чих листов и размещаются в той же папке, что и исходная рабочая книга. При
попытке перезаписать существующий файл выводится предупреждение.
Sub SplitWorkbook() Dim ws As Worksheet Dim DisplayStatusBar As Boolean DisplayStatusBar = Application.DisplayStatusBar Application.DisplayStatusBar = True Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Sheets Dim NewFileName As String Application.StatusBar = "Осталось рабочих листов: " & _ThisWorkbook.Sheets.Count If ThisWorkbook.Sheets.Count <> 1 Then NewFileName = ThisWorkbook.Path & "\" & ws.Name & _".xls" ws.Copy ActiveWorkbook.Sheets(1).Name = "Sheet1" ActiveWorkbook.SaveAs Filename:=NewFileName ActiveWorkbook.Close SaveChanges:=False Else NewFileName = ThisWorkbook.Path & "\" & ws.Name & _".xls" ws.Name = "Sheet1" ThisWorkbook.SaveAs Filename:=NewFileName End If Next Application.StatusBar = False Application.DisplayStatusBar = DisplayStatusBar Application.ScreenUpdating = TrueEnd Sub
Объединение нескольких рабочих книг в одну
Макрос CombineWorkbooks также любезно предоставлен Томми Майлзом.
Следующий макрос объединяет все рабочие книги, расположенные в заY
данной в папке, в одну. Рабочие листы полученной книги будут названы по
именам соответствующих исходных рабочих книг.
Excel всемогущий Глава 13 373
Sub CombineWorkbooks() Dim CurFile As String
Dim DestWB As Workbook Dim ws As Object 'Рабочие листы могут быть произвольного типа.
Const DirLoc As String = "C:\Data\" 'Местоположение _исходных файлов.
Application.ScreenUpdating = False
Set DestWB = Workbooks.Add(xlWorksheet)
CurFile = Dir(DirLoc & "*.xls")
Do While CurFile <> vbNullString Dim OrigWB As Workbook
Set OrigWB = Workbooks.Open(Filename:=DirLoc & CurFile, _ReadOnly:=True)
CurFile = Left(Left(CurFile, Len(CurFile) - 4), 29) 'Получение базового имени 'рабочего листа путем отсечения 'последних 4-х символов имени 'исходного файла (".xls").
For Each ws In OrigWB.Sheets ws.Copy After:=DestWB.Sheets(DestWB.Sheets.Count)
If OrigWB.Sheets.Count > 1 Then DestWB.Sheets(DestWB.Sheets.Count).Name = _CurFile & ws.Index Else DestWB.Sheets(DestWB.Sheets.Count).Name = CurFile End If Next
OrigWB.Close SaveChanges:=False
CurFile = Dir Loop
Application.DisplayAlerts = False DestWB.Sheets(1).Delete Application.DisplayAlerts = True
Application.ScreenUpdating = True
Set DestWB = NothingEnd Sub
Фильтрация данных с последующим копированиемполученного результата в отдельные рабочие листы
Макрос Filter_NewSheet любезно предоставлен Деннисом ВалентайY
ном (Dennis Wallentin), проживающим в Остерсунде, Швеция. Деннис дает
советы, касающиеся использования Excel и VBA, на своем собственном WebY
сайте по адресу: www.xldennis.com.
374 Часть II Автоматизация Excel
Следующий макрос фильтрует исходные данные (рис. 13.2) и копирует поY
лученные результаты в отдельные рабочие листы (рис. 13.3).
Рис. 13.2. Исходные данные Рис. 13.3. Результат примененияфильтра скопирован в новый ра&бочий лист
Sub Filter_NewSheet()Dim wbBook As WorkbookDim wsSheet As WorksheetDim rnStart As Range, rnData As RangeDim i As Long
Set wbBook = ThisWorkbookSet wsSheet = wbBook.Worksheets("Фильтр и копирование")
With wsSheet'Убедитесь, что 1-я строка содержит заголовки столбцов. Set rnStart = .Range("A2") Set rnData = .Range(.Range("A2"), .Range("C65536").End(xlUp))End With
Application.ScreenUpdating = True
For i = 1 To 5'Применение расширенного фильтра. rnStart.AutoFilter Field:=1, Criteria1:="AA" & i'Копирование результата фильтрации. rnData.SpecialCells(xlCellTypeVisible).Copy'Добавление нового рабочего листа. Worksheets.Add Before:=wsSheet'Присвоение имени новому рабочему листу. ActiveSheet.Name = "AA" & i'Вставка результата фильтрации'в новый рабочий лист. Range("A2").PasteSpecial xlPasteValuesNext i
'Вернуться к исходным данным.rnStart.AutoFilter Field:=1
Excel всемогущий Глава 13 375
With Application'Очистить буфер обмена. .CutCopyMode = False .ScreenUpdating = FalseEnd With
End Sub
Экспорт данных в Word
Макрос Export_Data_Word_Table также любезно предоставлен ДенниY
сом Валентайном.
Следующий макрос экспортирует данные с рабочего листа Excel в докуY
мент Word. Поскольку используется раннее связывание, необходимо добавить
ссылку (команда меню редактора Visual Basic Tools References (Сервис
Ссылки)) на библиотеку Microsoft Word Object Library.
Sub Export_Data_Word_Table()Dim wdApp As Word.ApplicationDim wdDoc As Word.DocumentDim wdCell As Word.CellDim i As LongDim wbBook As WorkbookDim wsSheet As WorksheetDim rnData As RangeDim vaData As Variant
Set wbBook = ThisWorkbookSet wsSheet = wbBook.Worksheets("Экспорт в Word")
With wsSheet Set rnData = .Range("A1:A10")End With
'Поместить данные из диапазона A1:A10 в одномерный массив Variant.vaData = rnData.Value
'Создать объект Word.Set wdApp = New Word.Application'Документ Test.doc должен находиться в той же папке,'что и рабочая книга.Set wdDoc = wdApp.Documents.Open(ThisWorkbook.Path & "\Test.doc")
'Импортировать данные в 1-й столбец 1-й таблицы.For Each wdCell In wdDoc.Tables(1).Columns(1).Cells i = i + 1 wdCell.Range.Text = vaData(i, 1)Next wdCell
'Сохранить и закрыть документ.With wdDoc .Save .CloseEnd With
376 Часть II Автоматизация Excel
'Завершить работу скрытой копии Microsoft Word.wdApp.Quit'Удалить внешние переменные из памяти.Set wdDoc = NothingSet wdApp = Nothing
MsgBox "Данные были успешно экспортированы в документ Test.doc.", _vbInformation
End Sub
Работа с примечаниямиВ большинстве случаев примечания ячеек Excel используются не достаточY
но эффективно. Рассматриваемые в следующих разделах макросы помогут исY
править это упущение.
Вывод примечаний
Макрос ListComments любезно предоставлен Томми Майлзом.
Excel позволяет печатать примечания, однако не позволяет выводить инY
формацию о рабочей книге и (или) рабочем листе, к которым относится то
или иное примечание. Следующий макрос помещает примечания, имена их
авторов и сведения о местоположении в новую рабочую книгу для последуюY
щего просмотра, сохранения и (или) печати.
Sub ListComments() Dim wb As Workbook Dim ws As Worksheet
Dim cmt As Comment
Dim cmtCount As Long
cmtCount = 2
On Error Resume Next Set ws = ActiveSheet If ws Is Nothing Then Exit Sub On Error GoTo 0
Application.ScreenUpdating = False
Set wb = Workbooks.Add(xlWorksheet)
With wb.Sheets(1) .Range("$A$1") = "Автор" .Range("$B$1") = "Рабочая книга" .Range("$C$1") = "Рабочий лист" .Range("$D$1") = "Диапазон" .Range("$E$1") = "Примечание" End With
For Each cmt In ws.Comments
Excel всемогущий Глава 13 377
With wb.Sheets(1) .Cells(cmtCount, 1) = cmt.author .Cells(cmtCount, 2) = cmt.Parent.Parent.Parent.Name .Cells(cmtCount, 3) = cmt.Parent.Parent.Name .Cells(cmtCount, 4) = cmt.Parent.Address .Cells(cmtCount, 5) = CleanComment(cmt.author, _cmt.Text) End With
cmtCount = cmtCount + 1 Next
wb.Sheets(1).UsedRange.WrapText = False
Application.ScreenUpdating = True
Set ws = Nothing Set wb = NothingEnd Sub
Private Function CleanComment(author As String, _cmt As String) As String Dim tmp As String
tmp = Application.WorksheetFunction.Substitute(cmt, _author & ":", "") tmp = Application.WorksheetFunction.Substitute(tmp, _Chr(10), "")
CleanComment = tmpEnd Function
Результат выполнения макроса ListComments показан на рис. 13.4.
Рис. 13.4. Макрос ListComments позволяет получить исчерпывающуюинформацию о примечаниях
Изменение размера области примечания
Макрос CommentFitter1 любезно предоставлен Томом Уртисом.
Следующий макрос изменяет размер области примечания так, чтобы она
вместила в себя весь текст примечания.
Sub CommentFitter1()Application.ScreenUpdating = FalseDim x As Range, y As Long
For Each x In Cells.SpecialCells(xlCellTypeComments) Select Case True Case Len(x.NoteText) <> 0 With x.Comment
378 Часть II Автоматизация Excel
.Shape.TextFrame.AutoSize = True If .Shape.Width > 250 Then y = .Shape.Width * .Shape.Height .Shape.Width = 150 .Shape.Height = (y / 200) * 1.2 End If End With End SelectNext xApplication.ScreenUpdating = TrueEnd Sub
Результат выполнения макроса CommentFitter1 показан на рис. 13.5.
Рис. 13.5. Теперь область примечания включает в себя весь его текст
Изменение размера области примечания с помощьюцентрирования
Макрос CommentFitter2 также любезно предоставлен Томом Уртисом.
Следующий макрос изменяет размер области примечания путем центрироY
вания текста примечания.
Sub CommentFitter2()Application.ScreenUpdating = FalseDim x As Range, y As Long
For Each x In Cells.SpecialCells(xlCellTypeComments) Select Case True Case Len(x.NoteText) <> 0 With x.Comment .Shape.TextFrame.AutoSize = True If .Shape.Width > 250 Then y = .Shape.Width * .Shape.Height .Shape.ScaleHeight 0.9, msoFalse, _msoScaleFromTopLeft .Shape.ScaleWidth 1#, msoFalse, _msoScaleFromTopLeft End If End With End SelectNext xApplication.ScreenUpdating = TrueEnd Sub
Результат выполнения макроса CommentFitter2 показан на рис. 13.6.
Excel всемогущий Глава 13 379
Рис. 13.6. Результат центрирования текста примечания
Размещение диаграммы в примечании
Макрос PlaceGraph любезно предоставлен Томом Уртисом.
Несмотря на то что Excel не позволяет разместить в примечании
‘‘настоящую’’ диаграмму, это можно сделать с ее изображением, как показано
на рис. 13.7.
Рис. 13.7. “Диаграмма”, размещенная в примечании
Чтобы поместить изображение диаграммы в примечание с помощью польY
зовательского интерфейса Excel, выполните следующие действия.
1. Создайте требуемое изображение.
2. Создайте примечание и выделите соответствующую ячейку.
380 Часть II Автоматизация Excel
3. Выберите команду меню Excel Вставка Изменить примечание (Insert
Edit Comment) или щелкните на ячейке правой кнопкой мыши и выбериY
те команду контекстного меню Изменить примечание (Edit Comment).
4. Щелкните правой кнопкой мыши на границе области примечания и
выберите команду контекстного меню Формат примечания (Format
Comment).
5. Перейдите во вкладку Цвета и линии (Colors and Lines) и раскройте
список Цвет (Color), расположенный в области Заливка (Fill).
6. Выберите команду Способы заливки (Fill Effects), перейдите во вкладку
Рисунок (Picture) открывшегося диалогового окна Способы заливки(Fill Effects) и щелкните на кнопке Рисунок (Picture).
7. Выберите требуемое изображение и щелкните на кнопке OK для закрыY
тия диалогового окна Способы заливки. Еще раз щелкните на кнопке
OK, для того чтобы закрыть диалоговое окно Формат примечания.
Чтобы ‘‘диаграмма’’, помещенная в примечание описанным выше образом,
всегда содержала текущие сведения, включите следующий код VBA в обработY
чик события SheetChange, срабатывающего при изменении исходных данных
диаграммы (укажите требуемое имя файла изображения, название диаграммы,
название рабочего листа, адрес ячейки и размер области примечания).
Sub PlaceGraph()Dim x As String, z As Range
Application.ScreenUpdating = Falsex = "C:\XWMJGraph.gif"
Set z = Worksheets("Диаграмма в примечании").Range("A3")
On Error Resume Nextz.Comment.DeleteOn Error GoTo 0
ActiveSheet.ChartObjects("Chart 1").ActivateActiveChart.Export x
With z.AddComment With .Shape .Height = 322 .Width = 465 .Fill.UserPicture x End WithEnd With
Range("A1").ActivateApplication.ScreenUpdating = True
Set z = NothingEnd Sub
Excel всемогущий Глава 13 381
Замечательные возможности Excel VBAВ следующих разделах рассматриваются макросы, демонстрирующие богаY
тые возможности Excel VBA.
Выделение ячейки с помощью условного форматирования
Макрос Worksheet_SelectionChange любезно предоставлен Иваном
Ф. Моалой (Ivan F. Moala), проживающим в Окленде, Новая Зеландия.
Иван YYYY автор WebYсайта The XcelFiles (www.xcelfiles.com), посвященного
задачам, решить которые с помощью Excel представляется невероятным или
невозможным.
Следующий макрос выделяет активную ячейку с помощью условного форY
матирования. Следует отметить, что макрос Worksheet_SelectionChangeудаляет любое существующее условное форматирование рабочего листа,
а также очищает буфер обмена, что приводит к затруднению выполнения опеY
раций вырезания, копирования и вставки данных.
'/////////////////////////////////////////////////////'// Исправлено 14 февраля 2003 - с комментариями'// Хуана Пабло Г. (Juan Pablo G.).'// Локализованные версии Excel могут неправильно'// интерпретировать значение TRUE.'// Воспользуемся фактом, что TRUE можно'// заменить любым целым числом, не равным 0.'////////////////////////////////////////////////////
Const iInternational As Integer = Not (0)
Private Sub Worksheet_SelectionChange(ByVal Target As Range)Dim iColor As Integer'// Примечание: этот макрос удаляет любое'// существующее условное форматирование!
'// Продолжить выполнение макроса, если'// пользователь выделит диапазон ячеек.On Error Resume NextiColor = Target.Interior.ColorIndex'// Оставить действие строки On Error Resume Next'// в силе на случай возникновения ошибок смещения строк.
If iColor < 0 Then iColor = 36Else iColor = iColor + 1End If
'// Проверить, совпадает ли цвет текста с цветом заливки.If iColor = Target.Font.ColorIndex Then iColor = iColor + 1
Cells.FormatConditions.Delete
'// Выделение с помощью горизонтальной полосы.With Range("A" & Target.Row, Target.Address) 'Rows(Target.Row) .FormatConditions.Add Type:=2, Formula1:=iInternational ' "TRUE" .FormatConditions(1).Interior.ColorIndex = iColor
382 Часть II Автоматизация Excel
End With
'// Выделение с помощью вертикальной полосы.With Range(Target.Offset(1 - Target.Row, 0).Address & ":" & _Target.Offset(-1, 0).Address) .FormatConditions.Add Type:=2, Formula1:=iInternational ' "TRUE" .FormatConditions(1).Interior.ColorIndex = iColorEnd With
End Sub
Выделение ячейки без применения условногоформатирования
Макрос HighLight и вспомогательные макросы HighlightRight,
HighlightLeft, HighlightUp, HighlightDown, DisableDelete и Re-Set также любезно предоставлены Иваном Ф. Моалой.
Следующий макрос выделяет ячейку, которая была сделана активной в реY
зультате использования клавиш управления курсором, без применения условY
ного форматирования.
'// Альтернативный вариант макроса, выделяющего активную ячейку:'// - не использует условное форматирование;'// - сохраняет существующее условное форматирование рабочего листа;'// - позволяет выполнять операции вырезания,'// копирования и вставки данных.'//'// Макрос создан Нейтом Оливером (Nate Oliver) для Альдо (Aldo).'//'// Альдо предложил обработку нажатия клавиши <Del>.'//'// Спасибо, друзья!!'//'// См. дополнительные комментарии в коде.
'// Размещено в стандартном модуле.
Dim strCol As StringDim iCol As IntegerDim dblRow As Double
Sub HighlightRight() HighLight 0, 1End Sub
Sub HighlightLeft() HighLight 0, -1End Sub
Sub HighlightUp() HighLight -1, 0, -1End Sub
Sub HighlightDown() HighLight 1, 0, 1End Sub
Sub HighLight(dblxRow As Double, iyCol As Integer, _
Excel всемогущий Глава 13 383
Optional dblZ As Double = 0)
On Error GoTo NoGostrCol = Mid(ActiveCell.Offset(dblxRow, iyCol).Address, _InStr(ActiveCell.Offset(dblxRow, iyCol).Address, "$") + 1, _InStr(2, ActiveCell.Offset(dblxRow, iyCol).Address, "$") - 2)iCol = ActiveCell.ColumndblRow = ActiveCell.Row
'// Позволяет избежать мерцания экрана.Application.ScreenUpdating = False
With Range(strCol & ":" & strCol & "," & dblRow + dblZ & _":" & dblRow + dblZ) .Select'// Сейчас экран следует обновить. Application.ScreenUpdating = True .Item(dblRow + dblxRow).ActivateEnd With
NoGo:End Sub
Sub DisableDelete() Cells(ActiveCell.Row, ActiveCell.Column).Select Application.OnKey "{DEL}"End Sub
Sub ReSet() Application.OnKey "{RIGHT}" Application.OnKey "{LEFT}" Application.OnKey "{UP}" Application.OnKey "{DOWN}"End Sub
Транспонирование данных
Макрос TransposeData любезно предоставлен Масару Каджи.
Следующий макрос транспонирует данные по заданному столбцу.
Sub TransposeData() Dim shOrg As Worksheet, shRes As Worksheet Dim rngStart As Range, rngPaste As Range Dim lngData As Long
Application.ScreenUpdating = False On Error Resume Next Application.DisplayAlerts = False Sheets("Транспонирование (результат)").Delete Application.DisplayAlerts = True On Error GoTo 0
On Error GoTo terminate
Set shOrg = Sheets("Транспонирование") Set shRes = Sheets.Add(After:=shOrg) shRes.Name = "Транспонирование (результат)" With shOrg'Отсортировать. .Cells.CurrentRegion.Sort Key1:=.[B2], Order1:=1, _
384 Часть II Автоматизация Excel
Key2:=.[C2], Order2:=1, Key3:=.[E2], Order3:=1, Header:=xlYes'Скопировать заголовок. .Rows(1).Copy shRes.Rows(1)'Задать начальный диапазон. Set rngStart = .[C2] Do Until IsEmpty(rngStart) Set rngPaste = shRes.[A65536].End(xlUp).Offset(1) lngData = GetNextRange(rngStart) rngStart.Offset(, -2).Resize(, 5).Copy rngPaste
'Копировать V1 в V14. rngStart.Offset(, 2).Resize(lngData).Copy rngPaste.Offset(, 5).PasteSpecial Paste:=xlAll, _Operation:=xlNone, SkipBlanks:=False, Transpose:=True'Копировать V1FP в V14FP. rngStart.Offset(, 1).Resize(lngData).Copy rngPaste.Offset(, 19).PasteSpecial Paste:=xlAll, _Operation:=xlNone, SkipBlanks:=False, Transpose:=True Set rngStart = rngStart.Offset(lngData) Loop End With Application.GoTo shRes.[A1] shRes.Cells.Columns.AutoFit Application.ScreenUpdating = True Application.CutCopyMode = False If MsgBox("Удалить исходный рабочий лист?", 36) = 6 Then Application.DisplayAlerts = False Sheets("Транспонирование").Delete Application.DisplayAlerts = True End If Set rngPaste = Nothing Set rngStart = Nothing Set shRes = Nothing Exit Subterminate:
End Sub
Function GetNextRange(ByVal rngSt As Range) As Long Dim i As Long i = 0 Do Until rngSt.Value <> rngSt.Offset(i).Value i = i + 1 Loop GetNextRange = iEnd Function
Выделение и отмена выделения несмежных ячеек
Макросы ModifyRightClick, DeselectActiveCell и DeselectAc-tiveArea любезно предоставлены Томом Уртисом.
Чтобы отменить выделение ячейки или диапазона ячеек на рабочем листе,
нужно щелкнуть на произвольной невыделенной ячейке. После выполнения
этой операции выделение требуемых ячеек необходимо начинать заново, что
весьма проблематично, если речь идет о большом количестве несмежных ячеек.
Excel всемогущий Глава 13 385
Следующий макрос добавляет в контекстное меню 2 новые команды:
Отменить выделение активной ячейки и Отменить выделение активнойобласти (рис. 13.8).
Рис. 13.8. Процедура Modi-fyRightClick создает 2 новыекоманды контекстного меню
Удерживая нажатой клавишу <Ctrl>, щелкните левой кнопкой мыши на
ячейке несмежного диапазона, выделение которой следует отменить. ЩелкY
ните правой кнопкой мыши и выберите команду контекстного меню
Отменить выделение активной ячейки (для отмены выделения только одной
активной ячейки) или Отменить выделение активной области (для отмены
выделения области, которой принадлежит активная ячейка).
Поместите приведенный ниже код в стандартный модуль.
Sub ModifyRightClick() Dim O1 As Object, O2 As Object On Error Resume Next With CommandBars("Cell") .Controls("Отменить выделение активной ячейки").Delete .Controls("Отменить выделение активной области").Delete End With On Error GoTo 0 Set O1 = CommandBars("Cell").Controls.Add With O1 .Caption = "Отменить выделение активной ячейки" .OnAction = "DeselectActiveCell" End With Set O2 = CommandBars("Cell").Controls.Add With O2 .Caption = "Отменить выделение активной области" .OnAction = "DeselectActiveArea" End WithEnd Sub
386 Часть II Автоматизация Excel
Sub DeselectActiveCell() Dim x As Range, y As Range If Selection.Cells.Count > 1 Then For Each y In Selection.Cells If y.Address <> ActiveCell.Address Then If x Is Nothing Then Set x = y Else Set x = Application.Union(x, y) End If End If Next y If x.Cells.Count > 0 Then x.Select End If End IfEnd Sub
Sub DeselectActiveArea() Dim x As Range, y As Range If Selection.Areas.Count > 1 Then For Each y In Selection.Areas If Application.Intersect(ActiveCell, y) Is Nothing Then If x Is Nothing Then Set x = y Else Set x = Application.Union(x, y) End If End If Next y x.Select End IfEnd Sub
Разместите следующий код в модуле ЭтаКнига (ThisWorkbook).
Private Sub Workbook_Deactivate() Application.CommandBars("Cell").ResetEnd Sub
Private Sub Workbook_Activate() ModifyRightClickEnd Sub
VBA для профессионаловVBAYпрограммисты находятся в постоянном поиске более эффективных
решений тех или иных задач. Размещая результаты своей работы в Internet,
они оказывают неоценимую услугу всему сообществу VBAYпрограммистов.
Установка параметров страницы
Макросы Macro1, Macro1_Version2, Macro1_Version3 и Macro1_Version4 любезно предоставлены Хуаном Пабло Гонсалесом (Juan Pablo
Excel всемогущий Глава 13 387
Gonzales), проживающим в Боготе, Колумбия. Хуан Пабло YYYY разработчик
программы F&I Menu Wizard, он также выполняет для Mr.Excel.com все закаY
зы, поступающие от испаноязычных клиентов.
Следующие макросы выполняют одно и то же действие: устанавливают разY
меры верхнего, нижнего, левого и правого полей страницы равными 1,5 дюйма
(3,8 см), а размеры полей верхнего колонтитула и нижнего колонтитула YYYY равY
ными 1 дюйму (2,5 см). Макрос Macro1 создан средством записи макросов.
Макросы Macro1_Version2, Macro1_Version3 и Macro1_Version4 деY
монстрируют возможность улучшения автоматически сгенерированного кода с
целью повышения его производительности. На рис. 13.9 показана сравниY
тельная таблица скорости выполнения всех четырех макросов.
Рис. 13.9. Сравнительная таблица скорости выполнениямакросов, устанавливающих параметры страницы
Sub Macro1()'' Macro1 Macro' Macro recorded 12/2/2003 by Juan Pablo Gonzalez' With ActiveSheet.PageSetup .PrintTitleRows = "" .PrintTitleColumns = "" End With ActiveSheet.PageSetup.PrintArea = "" With ActiveSheet.PageSetup .LeftHeader = "" .CenterHeader = "" .RightHeader = "" .LeftFooter = "" .CenterFooter = "" .RightFooter = ""
388 Часть II Автоматизация Excel
.LeftMargin = Application.InchesToPoints(1.5) .RightMargin = Application.InchesToPoints(1.5) .TopMargin = Application.InchesToPoints(1.5) .BottomMargin = Application.InchesToPoints(1.5) .HeaderMargin = Application.InchesToPoints(1) .FooterMargin = Application.InchesToPoints(1) .PrintHeadings = False .PrintGridlines = False .PrintComments = xlPrintNoComments .CenterHorizontally = False .CenterVertically = False .Orientation = xlPortrait .Draft = False .PaperSize = xlPaperLetter .FirstPageNumber = xlAutomatic .Order = xlDownThenOver .BlackAndWhite = False .Zoom = 100 End WithEnd Sub
Как обычно, средство записи макросов создало весьма громоздкий код.
Учитывая низкую скорость обновления объекта PageSetup, макрос Macro1остро нуждается в оптимизации, как показано ниже:
Sub Macro1_Version2() With ActiveSheet.PageSetup .LeftMargin = Application.InchesToPoints(1.5) .RightMargin = Application.InchesToPoints(1.5) .TopMargin = Application.InchesToPoints(1.5) .BottomMargin = Application.InchesToPoints(1.5) .HeaderMargin = Application.InchesToPoints(1) .FooterMargin = Application.InchesToPoints(1) End WithEnd Sub
Среднее время выполнения макроса Macro1_Version2 более чем на 70%
меньше среднего времени выполнения макроса Macro1, однако это еще далеко
не все. Принимая во внимание низкую скорость обновления объекта Page-Setup, воспользуемся выражением If для установки значения только тех паY
раметров страницы, которые этого требуют.
В приведенном ниже макросе вызов функции Application.Inches-ToPoints был заменен фактическими значениями параметров страницы.
Sub Macro1_Version3() With ActiveSheet.PageSetup If .LeftMargin <> 108 Then .LeftMargin = 108 If .RightMargin <> 108 Then .RightMargin = 108 If .TopMargin <> 108 Then .TopMargin = 108 If .BottomMargin <> 108 Then .BottomMargin = 108 If .HeaderMargin <> 72 Then .HeaderMargin = 72 If .FooterMargin <> 72 Then .FooterMargin = 72 End WithEnd Sub
Excel всемогущий Глава 13 389
Разница во времени выполнения макросов Macro1_Version2 и Macro1_Version3 становится заметной при условии, что некоторые параметры страY
ницы уже содержат нужные значения.
Макрос Macro1_Version4 вызывает XLMYметод PAGE.SETUP, позволяя
тем самым сократить среднее время своего выполнения более чем на 95% по
сравнению со средним временем выполнения макроса Macro1. Параметры
метода PAGE.SETUP left, right, top, bot, head_margin и foot_marginизмеряются не в точках, а в дюймах.
Sub Macro1_Version4() Dim St As String St = "PAGE.SETUP(, , " & _ "1.5, 1.5, 1.5, 1.5" & _ ", 0, False, False, False, 1, 1, True, 1, 1,False, , " & _ "1, 1" & _ ", False)" Application.ExecuteExcel4Macro StEnd Sub
Параметрам left, right, top и bot соответствует 4Yя строка приведенY
ного выше кода, параметрам head_margin и foot_margin YYYY 6Yя строка.
К сожалению, макрос Macro1_Version4 имеет два существенных недостатY
ка. ВоYпервых, он основан на языке XLM, включенном в Excel для обеспечеY
ния обратной совместимости. Неизвестно, как долго Microsoft намерена подY
держивать этот язык. ВоYвторых, ошибка при указании параметров метода
PAGE.SETUP приведет к тому, что этот метод не будет выполнен без какогоY
либо уведомления.
Вычисление времени выполнения кода макроса
Ниже приведен код, использующийся для вычисления времени выполнения
макросов Macro1, Macro1_Version2, Macro1_Version3 и Macro1_Version4 (см. рис. 13.9).
Public Declare Function QueryPerformanceFrequency Lib _ "kernel32" (lpFrequency As Currency) As Long
Public Declare Function QueryPerformanceCounter Lib _ "kernel32.dll" (lpPerformanceCount As Currency) As Long
Sub Test() Dim Ar(1 To 20, 1 To 4) As Currency, WS As Worksheet Dim n As Currency, str As Currency, fin As Currency Dim y As Currency
Dim i As Long, j As Long
Application.ScreenUpdating = False For i = 1 To 4 For j = 1 To 20 Set WS = ThisWorkbook.Sheets.Add WS.Range("A1").Value = 1
390 Часть II Автоматизация Excel
QueryPerformanceFrequency y QueryPerformanceCounter str Select Case i Case 1: Macro1 Case 2: Macro1_Version2 Case 3: Macro1_Version3 Case 4: Macro1_Version4 End Select QueryPerformanceCounter fin Application.DisplayAlerts = False WS.Delete Application.DisplayAlerts = True n = (fin - str) Ar(j, i) = CCur(Format(n, _"##########.############") / y) Next j Next i With Range("A8").Resize(1, 4) .Value = Array("Macro1", "Macro1_Version2", _"Macro1_Version3", "Macro1_Version4") .Font.Bold = True End With Range("A9").Resize(20, 4).Value = Ar
With Range("A29").Resize(1, 4)' В англоязычной версии Excel:' .FormulaR1C1 = "=AVERAGE(R2C:R21C)" .FormulaR1C1Local = "=СРЗНАЧ(R2C:R21C)"' В англоязычной версии Excel:' .Offset(1).FormulaR1C1 = "=RANK(R22C, R22C1:R22C4, 1)" .Offset(1).FormulaR1C1Local = "=РАНГ(R22C; R22C1:R22C4; 1)" .Resize(2).Font.Bold = True End With Application.ScreenUpdating = TrueEnd Sub
Запрет/разрешение выполнения операций вырезания,копирования и вставки
Макросы EnableAllClear и DisAbleAllCLear любезно предоставлены
Иваном Ф. Моалой.
Иногда изменение пользователями содержимого рабочего листа крайне
нежелательно. Следующий код запрещает/разрешает все способы выполнения
операций вырезания, копирования и вставки данных.
Option Private Module
Dim ComBar As CommandBarDim ComBarCtrl As CommandBarControl
Sub EnableAllClear()'// Команда Вставка (Insert).. EnableControl 295, True '// ..Ячейки (Cells) EnableControl 296, True '// ..Строки (Rows) EnableControl 297, True '// ..Столбцы (Columns)
Excel всемогущий Глава 13 391
'// Команда... EnableControl 478, True '// Правка->Удалить (Edit->Delete) EnableControl 292, True '// Удалить (Delete), контекстное '// меню ячейки EnableControl 293, True '// Удалить строки (Delete Rows), '// контекстное меню строки EnableControl 294, True '// Удалить (Delete), контекстное '// меню столбца EnableControl 847, True '// Правка->Удалить лист '// (Edit->Delete Sheet)'// Команда... EnableControl 3125, True '// Очистить содержимое (Clear '// Contents), контекстное меню EnableControl 1964, True '// Правка->Очистить->Все (All) EnableControl 872, True '// Правка->Очистить->Форматы '// (Edit->Clear->Formats) EnableControl 873, True '// Правка->Очистить->Содержимое '// (Edit->Clear->Contents) EnableControl 874, True '// Правка->Очистить->Примечания '// (Edit->Clear->Comments)'// Команда... EnableControl 21, True '// Вырезать (Cut) EnableControl 19, True '// Копировать (Copy) EnableControl 22, True '// Вставить (Paste) EnableControl 755, True '// Специальная вставка '// (Paste Special)'// Комбинации клавиш. With Application .OnKey "^c" .OnKey "^v" .OnKey "+{DEL}" .OnKey "+{INSERT}" .CellDragAndDrop = True .OnDoubleClick = "" End With'// Панели инструментов. CommandBars("ToolBar List").Enabled = True
End Sub
Sub DisAbleAllCLear()'// Команда Вставка (Insert).. EnableControl 295, False '// ..Ячейки (Cells) EnableControl 296, False '// ..Строки (Rows) EnableControl 297, False '// ..Столбцы (Columns)'// Команда... EnableControl 478, False '// Правка->Удалить (Edit->Delete) EnableControl 292, False '// Удалить (Delete), контекстное '// меню ячейки EnableControl 293, False '// Удалить строки (Delete Rows), '// контекстное меню строки EnableControl 294, False '// Удалить (Delete), контекстное '// меню столбца EnableControl 847, False '// Правка->Удалить лист '// (Edit->Delete Sheet)'// Команда... EnableControl 21, False '// Вырезать (Cut)
392 Часть II Автоматизация Excel
EnableControl 19, False '// Копировать (Copy) EnableControl 22, False '// Вставить (Paste) EnableControl 755, False '// Специальная вставка '// (Paste Special)'// Команда... EnableControl 3125, False '// Очистить содержимое (Clear '// Contents), контекстное меню EnableControl 1964, False '// Правка->Очистить->Все (All) EnableControl 872, False '// Правка->Очистить->Форматы '// (Edit->Clear->Formats) EnableControl 873, False '// Правка->Очистить->Содержимое '// (Edit->Clear->Contents) EnableControl 874, False '// Правка->Очистить->Примечания '// (Edit->Clear->Comments)'// Комбинации клавиш. With Application .OnKey "^c", "Dummy" .OnKey "^v", "Dummy" .OnKey "+{DEL}", "Dummy" .OnKey "+{INSERT}", "Dummy" .CellDragAndDrop = False .OnDoubleClick = "Dummy" End With'// Панели инструментов. CommandBars("ToolBar List").Enabled = False
End Sub
Sub EnableControl(iId As Integer, blnState As Boolean)Dim ComBar As CommandBarDim ComBarCtrl As CommandBarControl
On Error Resume NextFor Each ComBar In Application.CommandBars Set ComBarCtrl = ComBar.FindControl(Id:=iId, recursive:=True) If Not ComBarCtrl Is Nothing Then ComBarCtrl.Enabled = blnStateNext
End Sub
Sub Dummy() '// Вывод сообщения. MsgBox "Команда недоступна!"End Sub
Определение порядка сортировки
Макрос CustomSort любезно предоставлен Вэем Цзянгом (Wei Jiang),
проживающим в г. Шиян, Китай. Цзянг работает на должности консультанта
в компании MrExcel Consulting.
Excel поддерживает сортировку списков в числовом или алфавитном поY
рядке. Иногда этого оказывается недостаточно. Как показано на рис. 13.10,
желаемый порядок сортировки списка выглядит так: ‘‘Пояса’’, ‘‘Сумки’’,
‘‘Часы’’, ‘‘Бумажники’’, ‘‘Все остальное’’.
Excel всемогущий Глава 13 393
Рис. 13.10. Желаемый порядок сортировки списка вячейках A2:C16 указан в столбце I
Следующий макрос сортирует список с учетом заданного порядка сортиY
ровки.
Sub CustomSort() ' Задать желаемый порядок сортировки. Application.AddCustomList ListArray:=Range("I1:I5")
' Определить номер списка, задающего порядок сортировки. nIndex = Application.GetCustomListNum(Range("I1:I5").Value)
' Отсортировать список, используя заданный порядок сортировки.' Номер списка, задающего порядок сортировки, равен nIndex + 1,' поскольку обычный порядок сортировки имеет номер 1. Range("A2:C16").Sort Key1:=Range("B2"), Order1:=xlAscending, _Header:=xlNo, Orientation:=xlSortColumns, OrderCustom:=nIndex + 1 Range("A2:C16").Sort Key1:=Range("A2"), Order1:=xlAscending, _Header:=xlNo, Orientation:=xlSortColumns
' Удалить список, задающий порядок сортировки. Application.DeleteCustomList nIndexEnd Sub
Результат выполнения макроса CustomSort показан на рис. 13.11.
Рис. 13.11. В результате выполнения макроса список вячейках A2:C16 отсортирован сперва по дате, а затем —в соответствии с порядком, заданным в столбце I
394 Часть II Автоматизация Excel
Создание индикатора хода процесса
Макрос Worksheet_Change любезно предоставлен Томом Уртисом.
Следующий макрос создает индикатор хода процесса в столбце C, основыY
ваясь на данных в столбцах A и B (рис. 13.12).
Рис. 13.12. Индикатор хода процесса
Private Sub Worksheet_Change(ByVal Target As Range)If Target.Column > 2 Or Target.Cells.Count > 1 Then Exit SubIf Application.IsNumber(Target.Value) = False Then Application.EnableEvents = False Application.Undo Application.EnableEvents = True MsgBox "Введите число." Exit SubEnd IfSelect Case Target.Column Case 1 If Target.Value > Target.Offset(0, 1).Value Then Application.EnableEvents = False Application.Undo Application.EnableEvents = True MsgBox "Значение в столбце A не может быть больше _значения в столбце B." Exit Sub End If Case 2 If Target.Value < Target.Offset(0, -1).Value Then Application.EnableEvents = False Application.Undo Application.EnableEvents = True MsgBox "Значение в столбце B не может быть меньше _значения в столбце A." Exit Sub End IfEnd SelectDim x As Long
Excel всемогущий Глава 13 395
x = Target.RowDim z As Stringz = Range("B" & x).Value - Range("A" & x).ValueWith Range("C" & x)' В англоязычной версии Excel:' .Formula = "=IF(RC[-1]<=RC[-2],REPT(""n"",RC[-1])&REPT(""n"", _RC[-2]-RC[-1]),REPT(""n"",RC[-2])&REPT(""o"",RC[-1]-RC[-2]))" .FormulaLocal = "=ЕСЛИ(RC[-1]<=RC[-2];ПОВТОР(""n""; _RC[-1])&ПОВТОР(""n"";RC[-2]-RC[-1]);ПОВТОР(""n""; _RC[-2])&ПОВТОР(""o"";RC[-1]-RC[-2]))" .Value = .Value .Font.Name = "Wingdings" .Font.ColorIndex = 1 .Font.Size = 10 If Len(Range("A" & x)) <> 0 Then .Characters(1, (.Characters.Count - z)).Font.ColorIndex = 3 .Characters(1, (.Characters.Count - z)).Font.Size = 12 End IfEnd WithEnd Sub
Создание защищенного поля для ввода пароля
Макрос HiddenPassword любезно предоставлен Даниелем Клэнном
(Daniel Klann), проживающим в Сиднее, Австралия. Даниель знаком со мноY
жеством различных языков программирования, однако предпочитает разраY
ботку приложений на VBA для Excel и Access. Клэнн YYYY автор собственного
WebYсайта, который находится по адресу: www.danielklann.com.
Использование для ввода пароля обычного текстового поля крайне нежеY
лательно, поскольку все вводимые символы отображаются на экране. СлеY
дующий макрос заменяет символы пароля знаками звездочки (*), превращая
обычное текстовое поле в защищенное поле ввода пароля (рис. 13.13).
Рис. 13.13. Макрос HiddenPassword пре&вращает обычное текстовое поле в защищен&ное поля ввода пароля
'Используемые функции API.Private Declare Function CallNextHookEx Lib "user32" _(ByVal hHook As Long, ByVal ncode As Long, _ByVal wParam As Long, lParam As Any) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias _"GetModuleHandleA" (ByVal lpModuleName As String) As Long
396 Часть II Автоматизация Excel
Private Declare Function SetWindowsHookEx Lib "user32" Alias _"SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, _ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" _(ByVal hHook As Long) As Long
Private Declare Function SendDlgItemMessage Lib "user32" Alias _"SendDlgItemMessageA" (ByVal hDlg As Long, _ByVal nIDDlgItem As Long, ByVal wMsg As Long, _ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" _Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName _As String, ByVal nMaxCount As Long) As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" () _As Long
'Константы, используемые в функциях API.Private Const EM_SETPASSWORDCHAR = &HCCPrivate Const WH_CBT = 5Private Const HCBT_ACTIVATE = 5Private Const HC_ACTION = 0
Private hHook As Long
Sub HiddenPassword()
If InputBoxDK("Введите пароль", "Ввод пароля") <> _"password" Then
MsgBox "Неверный пароль! Доступ запрещен."
Else
MsgBox "Верный пароль! Добро пожаловать!"
End If
End Sub
Public Function NewProc(ByVal lngCode As Long, _ByVal wParam As Long, ByVal lParam As Long) As Long Dim RetVal Dim strClassName As String, lngBuffer As Long
If lngCode < HC_ACTION Then NewProc = CallNextHookEx(hHook, lngCode, wParam, lParam) Exit Function End If
strClassName = String$(256, " ") lngBuffer = 255
If lngCode = HCBT_ACTIVATE Then 'Окно активировано.
Excel всемогущий Глава 13 397
RetVal = GetClassName(wParam, strClassName, lngBuffer)
'Имя класса обычного текстового поля. If Left$(strClassName, RetVal) = "#32770" Then
'Следующая строка превращает обычное текстовое поле в защищенное'поле ввода пароля. Символ "*" можно заменить на любой другой. SendDlgItemMessage wParam, &H1324, _EM_SETPASSWORDCHAR, Asc("*"), &H0 End If
End If
'Следующая строка гарантирует корректный вызов'всех обработчиков прерываний. CallNextHookEx hHook, lngCode, wParam, lParam
End Function
Public Function InputBoxDK(Prompt, Optional Title, _Optional Default, Optional XPos, Optional YPos, _Optional HelpFile, Optional Context) As String Dim lngModHwnd As Long, lngThreadID As Long
lngThreadID = GetCurrentThreadId lngModHwnd = GetModuleHandle(vbNullString)
hHook = SetWindowsHookEx(WH_CBT, AddressOf NewProc, _lngModHwnd, lngThreadID) On Error Resume Next InputBoxDK = InputBox(Prompt, Title, Default, XPos, YPos, _HelpFile, Context) UnhookWindowsHookEx hHook
End Function
Изменение регистра текста
Макрос TextCaseChange любезно предоставлен Иваном Ф. Моалой.
Word позволяет изменять регистр выделенного текста. Следующий макрос
делает возможным изменение регистра текста, находящегося в выделенном
диапазоне рабочего листа Excel.
Sub TextCaseChange()Dim RgText As RangeDim oCell As RangeDim Ans As StringDim strTest As StringDim sCap As Integer, _ lCap As Integer, _ i As Integer
'// Предполагается, что перед вызовом макроса'// пользователь выделил требуемый диапазон текста.
Again:
398 Часть II Автоматизация Excel
Ans = Application.InputBox("[С]трочные" & vbCr & "[П]рописные" _& vbCr & "[К]ак в предложениях" & vbCr & "[Н]ачинать с прописных" _& vbCr & "[М]алые прописные", "Введите букву", Type:=2)
If Ans = "False" Then Exit SubIf InStr(1, "СПКНМ", UCase(Ans), vbTextCompare) = 0 Or _Len(Ans) > 1 Then GoTo Again
On Error GoTo NoTextIf Selection.Count = 1 Then Set RgText = SelectionElse Set RgText = Selection.SpecialCells(xlCellTypeConstants, 2)End IfOn Error GoTo 0
For Each oCell In RgText Select Case UCase(Ans) Case "С": oCell = LCase(oCell.Text) Case "П": oCell = UCase(oCell.Text) Case "К": oCell = UCase(Left(oCell.Text, 1)) & _LCase(Right(oCell.Text, Len(oCell.Text) - 1)) Case "Н": oCell = Application.WorksheetFunction.Proper( _oCell.Text) Case "М" lCap = oCell.Characters(1, 1).Font.Size sCap = Int(lCap * 0.85)'Малые прописные для всех букв. oCell.Font.Size = sCap oCell.Value = UCase(oCell.Text) strTest = oCell.Value'Большие прописные для 1-х букв слов. strTest = Application.Proper(strTest) For i = 1 To Len(strTest) If Mid(strTest, i, 1) = UCase(Mid(strTest, _i, 1)) Then oCell.Characters(i, 1).Font.Size = lCap End If Next i End SelectNext
Exit SubNoText:MsgBox "Текст в диапазоне " & Selection.Address & " отсутствует"
End Sub
Обработка события удаления строки или столбца
Макросы EventHack и EventReset, а также вспомогательные процедуры
AssignMacro, JudgeRng и DelExecute любезно предоставлены Масару
Каджи (Masaru Kaji).
Excel не предусматривает возможность обработки события удаления строY
ки или столбца. Следующий макрос устраняет этот недостаток: при удалении
Excel всемогущий Глава 13 399
строки или столбца на экран выводится сообщение, содержащее номер удаY
ленной строки или удаленного столбца.
Sub EventHack() AssignMacro "JudgeRng"End SubSub EventReset() AssignMacro ""End Sub
Private Sub AssignMacro(ByVal strProc As String) Dim lngId As Long Dim CtrlCbc As CommandBarControl Dim CtrlCbcRet As CommandBarControls Dim arrIdNum As Variant
'// 293: команда Удалить строки (Delete Rows)'// контекстного меню строки.'// 294: команда Удалить (Delete) контекстного'// меню столбца.'// 478: команда Удалить (Delete) меню Правка (Edit). arrIdNum = Array(293, 294, 478)
For lngId = LBound(arrIdNum) To UBound(arrIdNum) Set CtrlCbcRet = CommandBars.FindControls( _ID:=arrIdNum(lngId)) For Each CtrlCbc In CtrlCbcRet CtrlCbc.OnAction = strProc Next Set CtrlCbcRet = Nothing NextEnd Sub
Private Sub JudgeRng() If Not TypeOf Selection Is Range Then Exit Sub With Selection If .Address = .EntireRow.Address Then Call DelExecute("строка: " & .Row, xlUp) ElseIf .Address = .EntireColumn.Address Then Call DelExecute("столбец: " & .Column, xlToLeft) Else Application.Dialogs(xlDialogEditDelete).Show End If End WithEnd Sub
Private Sub DelExecute(ByVal str, ByVal lngDerec As Long) MsgBox "Удален(а): " & str Selection.Delete lngDerecEnd Sub
Поиск заданного текста с помощью свойства SpecialCells
Макросы CheckAllCells и CheckSpecialCellsOnly любезно предосY
тавлены Иваном Ф. Моалой.
При поиске значения, текста или формулы в заданном диапазоне Excel
проверяет каждую ячейку этого диапазона. Макрос CheckAllCells провоY
400 Часть II Автоматизация Excel
дит поиск заданного текста в диапазоне A1:Z20000, проверяя каждую его
ячейку. Макрос CheckSpecialCellsOnly проводит поиск заданного текста
в диапазоне A1:Z20000, проверяя только ячейки, содержащие текстовые
константы. По окончании поиска оба макроса выводят сведения о результатах
поиска и затраченном на это времени.
Sub CheckAllCells()Dim TheRange As RangeDim oCell As Range
StartTime = Now Ctr = 0 Set TheRange = Range("A1:Z20000") For Each oCell In TheRange If oCell.Text = "Ваш текст" Then Ctr = Ctr + 1 End If Next oCell EndTime = Now
Msg = "Проверено " & TheRange.Cells.Count & " ячеек. _Найдено " & Ctr & " совпадений. Время поиска = " & _Format(EndTime - StartTime, "h:mm:ss") MsgBox Msg
End Sub
Sub CheckSpecialCellsOnly()Dim TheRange As RangeDim oCell As Range
StartTime = Now Ctr = 0
Set TheRange = Range("A1:Z20000").SpecialCells( _xlCellTypeConstants, xlTextValues)
For Each oCell In TheRange If oCell.Text = "Ваш текст" Then Ctr = Ctr + 1 End If Next oCell EndTime = Now
Msg = "Проверено " & TheRange.Cells.Count & " ячеек. _Найдено " & Ctr & " совпадений. Время поиска = " _& Format(EndTime - StartTime, "h:mm:ss") MsgBox Msg
End Sub
Условное удаление строк
Макрос Delete_rows_with_conditions любезно предоставлен ДенниY
сом Валентайном (Dennis Wallentin), проживающим в Остерсунде, Швеция.
Excel всемогущий Глава 13 401
Следующий макрос удаляет определенное число строк при выполнении усY
ловия ‘‘ячейка в столбце A пуста’’.
Sub Delete_rows_with_conditions()Dim wbBook As WorkbookDim wsSheet As WorksheetDim rnArea As RangeDim lnLastRow As Long, lnMoreRows As LongDim i As Long, j As Long, k As Long
Set wbBook = ThisWorkbookSet wsSheet = wbBook.Worksheets("Условное удаление строк")
Application.ScreenUpdating = False
'Определение числа строк, которые нужно удалить.j = 5
With wsSheet'Определение номера последней строки по столбцу A. lnLastRow = .Cells(Rows.Count, 1).End(xlUp).Row + 1
'Определение номера последней строки по столбцам A, B и C. For k = 1 To 3 lnMoreRows = .Cells(Rows.Count, k).End(xlUp).Row + 1 If lnLastRow < lnMoreRows Then lnLastRow = lnMoreRows End If Next k
'Удаление строк с пустой ячейкой в столбце A. For i = lnLastRow To 1 Step -1 If IsEmpty(.Cells(i, 1)) Then .Cells(i, 1).Resize(j, 1).EntireRow.Delete End If Next iEnd With
Application.ScreenUpdating = True
End Sub
Сокрытие строки формул
Макрос Worksheet_SelectionChange любезно предоставлен Томом
Уртисом.
Если активная ячейка содержит более 50 символов, строка формул автомаY
тически увеличивается в размере и перекрывает часть рабочего листа Excel.
Следующий макрос скрывает строку формул при выделении ячейки, содерY
жащей более 50 символов.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)If Target.Cells.Count > 1 Then Exit SubOn Error Resume NextIf Len(Target.Text) > 50 Or Len(Target.Formula) > 50 Then
402 Часть II Автоматизация Excel
Application.DisplayFormulaBar = FalseElse Application.DisplayFormulaBar = TrueEnd IfEnd Sub
Результат выполнения макроса Worksheet_SelectionChange показан
на рис. 13.14.
Рис. 13.14. Макрос Worksheet_SelectionChange скрываетстроку формул при выделении ячейки, содержащей более50 символов
На закускуВ этом разделе рассматривается несколько занятных макросов, которым
можно найти применение в самых различных приложениях.
Извлечение информации о курсах акций из Internet
Процедура GetQuote любезно предоставлена Натаном П. Оливером.
Следующая процедура извлекает из Internet информацию о курсе акций
компании на заданную дату.
Excel всемогущий Глава 13 403
Private Sub GetQuote()Dim ie As Object, lCharPos As Long, sHTML As StringDim HistDate As Date, HighVal As String, LowVal As StringDim cl As Range
Set cl = ActiveCellHistDate = cl(, 0)
If Intersect(cl, [c2:c65366]) Is Nothing Then MsgBox "Выберите ячейку в столбце C." Exit SubEnd If
If Not CBool(Len(cl(, -1))) Or Not CBool(Len(cl(, 0))) Then MsgBox "Введите символ акций и требуемую дату." Exit SubEnd If
Set ie = CreateObject("InternetExplorer.Application")
With ie .Navigate "http://bigcharts.marketwatch.com/historical/ _default.asp?detect=1&symbol=" & cl(, -1) & "&close_date=" & _Month(HistDate) & "%2F" & Day(HistDate) & "%2F" & _Year(HistDate) & "&x=31&y=26" Do While .Busy And .ReadyState <> 4 DoEvents Loop sHTML = .Document.body.innertext .QuitEnd With
Set ie = Nothing
lCharPos = InStr(1, sHTML, "High:", vbTextCompare)If lCharPos Then HighVal = Mid$(sHTML, lCharPos + 5, 15)
If Not Left$(HighVal, 3) = "n/a" Then lCharPos = InStr(1, sHTML, "Low:", vbTextCompare) If lCharPos Then LowVal = Mid$(sHTML, lCharPos + 4, 15) cl.Value = (Val(LowVal) + Val(HighVal)) / 2Else: lCharPos = InStr(1, sHTML, "Closing Price:", vbTextCompare) cl.Value = Val(Mid$(sHTML, lCharPos + 14, 15))End If
Set cl = NothingEnd Sub
Результат выполнения процедуры GetQuote показан на рис. 13.15.
Рис. 13.15. Процедура GetQuote извлекаетиз Internet информацию о курсе акций ком&пании на заданную дату
404 Часть II Автоматизация Excel
Вставка программного кода во вновьсозданную рабочую книгу
Ранее в этой главе был рассмотрен макрос, создающий отчеты для региоY
нальных менеджеров в виде отдельных рабочих книг. Усложним задачу, поY
требовав скопировать в новые рабочие книги программный код. Для этого обY
ратимся к объектной модели Microsoft Visual Basic for Applications Extensibility,
позволяющей не только импортировать в рабочую книгу модули с программY
ным кодом, но и создавать код непосредственно в рабочей книге.
Прежде чем выполнить какойYлибо из приведенных ниже макросов, подY
ключите библиотеку Microsoft Visual Basic for Applications Extensibility 5.3, выбрав
команду меню редактора Visual Basic Tools References (Сервис Ссылки) и усY
тановив соответствующий флажок в открывшемся диалоговом окне.
Наиболее простой способ переноса программного кода заключается в
экспорте модуля и пользовательской формы из текущей рабочей книги и их
последующем импорте в новую рабочую книгу. Предположим, что региоY
нальному менеджеру необходимо передать отчет с данными о его регионе
и 3 макроса, реализующие форматирование и печать отчета. Поместите все
3 макроса в модуль modToRegion. Предположим также, что макросы модуля
modToRegion вызывают пользовательскую форму frmRegion. Следующий
макрос перенесет программный код и пользовательскую форму в новую рабоY
чую книгу.
Sub MoveDataAndMacro() Dim WSD As Worksheet Set WSD = Worksheets("Отчет")' Скопировать отчет в новую рабочую книгу. WSD.Copy' Сделать новую рабочую книгу активной.' Удалить старые копии промежуточных файлов с диска. On Error Resume Next Kill ("C:\ModToRegion.bas") Kill ("C:\frmRegion.frm") On Error GoTo 0' Экспортировать модуль и форму из исходной рабочей книги. ThisWorkbook.VBProject.VBComponents("ModPics").Export _("C:\ModToRegion.bas") ThisWorkbook.VBProject.VBComponents("frmModPics").Export _("C:\frmRegion.frm")' Импортировать модуль и форму в новую рабочую книгу. ActiveWorkbook.VBProject.VBComponents.Import _("C:\ModToRegion.bas") ActiveWorkbook.VBProject.VBComponents.Import _("C:\frmRegion.frm") On Error Resume Next Kill ("C:\ModToRegion.bas") Kill ("C:\frmRegion.frm") On Error GoTo 0End Sub
Excel всемогущий Глава 13 405
Чтобы создать программный код непосредственно в рабочей книге, следует
обратить внимание на два инструмента: метод Lines и метод InsertLines.
Метод Lines позволяет извлечь заданное число строк из указанного модуля,
а метод InsertLines — вставить код в модуль.
Внимание Код, который добавляется в рабочую книгу с помощью метода InsertLines, тутже компилируется. Сбой при компиляции кода может привести к ошибке общегонарушения защиты (General Protection Failure, GPF), чего нужно всячески избегать.
Следующий макрос копирует весь код модуля ЭтаКнига исходной рабочей
книги в модуль ЭтаКнига новой рабочей книги.
Sub MoveDataAndMacro() Dim WSD As Worksheet Dim WBN As Workbook Set WSD = Worksheets("Отчет")' Скопировать отчет в новую рабочую книгу. WSD.Copy' Сделать новую рабочую книгу активной. Set WBN = ActiveWorkbook' Скопировать обработчики событий уровня рабочей книги. Set WBCodeMod1 = ThisWorkbook.VBProject.VBComponents( _"ЭтаКнига").CodeModule Set WBCodeMod2 = WBN.VBProject.VBComponents( _"ЭтаКнига").CodeModule WBCodeMod2.InsertLines 1, WBCodeMod1.Lines(1, _WBCodeMod1.CountOfLines)
Следующий шагВ следующей главе рассматриваются WebYзапросы, позволяющие импорY
тировать данные из Internet в приложения Excel.
Извлечение данных изInternet........................................... 407Извлечение данных изInternet в режиме реальноговремени..........................................413Анализ данных, извлеченныхиз Internet.......................................414Размещение данных на WebLстранице.........................................418Следующий шаг........................... 425
14Глава 14
Âçàèìîäåéñòâèåñ Internet
В этой главе рассматривается авY
томатизация WebYзапросов, целью
которых является помещение инY
формации из Internet в электронную
таблицу, и наоборот.
Извлечение данныхиз Internet
На рис. 14.1 показана страница
WebYсайта Finance.Yahoo.com, содерY
жащая сведения о курсах акций разY
личных компаний.
Понимая всю важность электронY
ных таблиц, разработчики поместили
внизу страницы ссылку, позволяюY
щую загрузить данные, которые были
получены в результате WebYзапроса,
в файле формата CSV.
Создание WebLзапросас помощьюпользовательскогоинтерфейса Excel
Рассмотрим создание WebYзапроса
вручную с помощью пользовательского
интерфейса Excel. Запустите WebY
обозреватель и откройте страницу, соY
держащую требуемую информацию.
Ниже приведен адрес URL страницы,
изображенной на рис. 14.1:
http://finance.yahoo.com/q/cq?d=v1&s=PSO,+SJM,+KO,+MSFT,+CSCO,+INTC
408 Часть II Автоматизация Excel
Рис. 14.1. Web&сайт Finance.Yahoo.com позволяет получить информацию отекущих курсах акций
Откройте Excel и выберите на рабочем листе пустую область, предназначенY
ную для вставки данных из Internet. Выберите команду меню Данные Импортвнешних данных Создать веб-запрос (Data Import External Data New Web
Query). Скопируйте адрес WebYстраницы из окна обозревателя в поле Адрес(Address) открывшегося диалогового окна Создание веб-запроса (New Web
Query) и щелкните на кнопке Пуск (Go). Загрузившаяся WebYстраница будет
содержать значки с изображением черной стрелки в желтом квадрате, обознаY
чающие левые верхние углы имеющихся на странице таблиц. При подведении
указателя мыши к такому значку на экране появится рамка, ограничивающая
соответствующую таблицу. Щелкните на значке, чтобы выбрать таблицу. В реY
зультате этого стрелка сменится флажком, а цвет фона станет зеленым вместо
желтого (рис. 14.2).
Щелкните на кнопке Импорт (Import), а затем YYYY на кнопке OK в открывY
шемся диалоговом окне Импорт данных (Import Data). Через несколько сеY
кунд данные из Internet будут помещены на рабочий лист Excel, как показано
на рис. 14.3.
Взаимодействие с Internet Глава 14 409
Рис. 14.2. Выберите требуемую таблицу, щелкнув на соответствующем значке с изображе&нием черной стрелки в желтом квадрате
Рис. 14.3. Данные успешно скопированы с Web&страницы на рабочий лист Excel
Обновление существующего WebLзапроса с помощью VBA
Чтобы обновить все WebYзапросы на текущем рабочем листе, выполните
следующий код VBA, назначив его кнопке или комбинации клавиш:
Sub RefreshAllWebQueries() Dim QT As QueryTable For Each QT In ActiveSheet.QueryTables Application.StatusBar = "Обновление " & QT.Connection QT.Refresh
410 Часть II Автоматизация Excel
Next QT Application.StatusBar = FalseEnd Sub
Создание WebLзапроса с помощью VBAРассмотренный ранее пример имеет один существенный недостаток YYYY
при изменении портфеля акций соответствующим образом следует изменить
и жестко заданный WebYзапрос.
Создать WebYзапрос ‘‘на лету’’ совсем не сложно. Для этого нужно сформиY
ровать так называемую строку подключения, пример которой показан ниже:
URL;http://finance.yahoo.com/q/cq?d=v1&s=PSO,+SJM,+KO,+MSFT,+CSCO,+INTC
При формировании строки подключения не обойтись без символа соедиY
нения строк, реализующего ‘‘сцепление’’ основной части строки подключеY
ния с заданными пользователем символами акций.
На рис. 14.4 показан простейший интерфейс создания WebYзапросов.
Рис. 14.4. Введите символы акций в ячейки столбца A и щелкните на кнопке Узнатькурсы акций
Введите символы акций в ячейки столбца A и щелкните на кнопке Узнатькурсы акций, чтобы выполнить макрос CreateNewQuery.
Макрос CreateNewQuery создает строку подключения, объединяя ее осY
новную часть с символом акций, размещенном в ячейке A2:
ConnectString = "URL;http://finance.Yahoo.com/q/cq?d=v1&s=" & _WSD.Cells(i, 1).Value
По мере обнаружения новых символов акций макрос добавляет в конец
существующей строки подключения комбинацию ,+ и символ акции:
ConnectString = ConnectString & ",+" & WSD.Cells(i, 1).Value
Взаимодействие с Internet Глава 14 411
После формирования строки подключения макрос удаляет все ранее созY
данные WebYзапросы, выполняет новый WebYзапрос и помещает его результат
на рабочий лист Вспомогательный лист.
Следует отметить, что при выполнении WebYзапроса параметр Back-groundRefresh метода Refresh устанавливается равным False. Это поY
зволяет приостановить выполнение макроса на время, требующееся для изY
влечения информации из Internet.
После получения данных макрос присваивает имя соответствующему диаY
пазону ячеек рабочего листа Вспомогательный лист и переносит извлеY
ченную из Internet информацию на рабочий лист Портфель акций с помоY
щью формул ВПР (VLOOKUP).
Sub CreateNewQuery() Dim WSD As Worksheet Dim WSW As Worksheet Dim QT As QueryTable
Set WSD = Worksheets("Портфель акций") Set WSW = Worksheets("Вспомогательный лист")
' Создание строки подключения. FinalRow = WSD.Cells(65536, 1).End(xlUp).Row For i = 2 To FinalRow Select Case i Case 2 ConnectString = _"URL;http://finance.Yahoo.com/q/cq?d=v1&s=" & WSD.Cells(i, 1).Value Case Else ConnectString = _ConnectString & ",+" & WSD.Cells(i, 1).Value End Select Next i
' Удаление существующих запросов. For Each QT In WSW.QueryTables QT.Delete Next QT
WSW.Select WSW.Cells.Clear
' Создание нового запроса. Set QT = WSW.QueryTables.Add(Connection:=ConnectString, _Destination:=WSW.Range("A1")) With QT .Name = "portfolio" .FieldNames = True .RowNumbers = False .FillAdjacentFormulas = False .PreserveFormatting = True .RefreshOnFileOpen = False .BackgroundQuery = False .RefreshStyle = xlInsertDeleteCells .SavePassword = False
412 Часть II Автоматизация Excel
.SaveData = True .AdjustColumnWidth = True .RefreshPeriod = 0 .WebSelectionType = xlSpecifiedTables .WebFormatting = xlWebFormattingNone .WebTables = "20" .WebPreFormattedTextToColumns = True .WebConsecutiveDelimitersAsOne = True .WebSingleBlockTextImport = False .WebDisableDateRecognition = False .WebDisableRedirections = False End With
' Выполнение запроса. QT.Refresh BackgroundQuery:=True
' Создание именованного диапазона,' содержащего результаты запроса. WSW.Cells(1, 1).Resize(FinalRow, 6).Name = "WebInfo"
' Перенесение полученной информации' на рабочий лист "Портфель акций". RowCount = FinalRow - 1' В англоязычной версии Excel:'WSD.Cells(2, 2).Resize(RowCount, 1).FormulaR1C1 = _"=VLOOKUP(RC1,WebInfo,3,False)" WSD.Cells(2, 2).Resize(RowCount, 1).FormulaR1C1Local = _"=ВПР(RC1;WebInfo;3;ЛОЖЬ)"' В англоязычной версии Excel:'WSD.Cells(2, 3).Resize(RowCount, 1).FormulaR1C1 = _"=VLOOKUP(RC1,WebInfo,4,False)" WSD.Cells(2, 3).Resize(RowCount, 1).FormulaR1C1Local = _"=ВПР(RC1;WebInfo;4;ЛОЖЬ)"' В англоязычной версии Excel:'WSD.Cells(2, 4).Resize(RowCount, 1).FormulaR1C1 = _"=VLOOKUP(RC1,WebInfo,5,False)" WSD.Cells(2, 4).Resize(RowCount, 1).FormulaR1C1Local = _"=ВПР(RC1;WebInfo;5;ЛОЖЬ)"' В англоязычной версии Excel:'WSD.Cells(2, 5).Resize(RowCount, 1).FormulaR1C1 = _"=VLOOKUP(RC1,WebInfo,6,False)" WSD.Cells(2, 5).Resize(RowCount, 1).FormulaR1C1Local = _"=ВПР(RC1;WebInfo;6;ЛОЖЬ)" WSD.Cells(2, 6).Resize(RowCount, 1).Value = Time WSD.Cells(2, 6).Resize(RowCount, 1).NumberFormat = "h:m:s"
WSD.Select MsgBox "Подождите, пока будут обновлены данные"
End Sub
Результат выполнения макроса CreateNewQuery показан на рис. 14.5.
Взаимодействие с Internet Глава 14 413
Рис. 14.5. В результате выполнения макроса CreateNewQuery на рабочий листПортфель акций помещается только существенная информация. Необработанныйрезультат Web&запроса можно увидеть на листе Вспомогательный лист
Извлечение данных из Internet в режимереального времени
В Internet существует много служб, позволяющих доставлять данные непоY
средственно на рабочий лист Excel в режиме реального времени. Обычно для
доставки данных используется технология DDE.
Вызов типичной DDEYслужбы реального времени осуществляется посредY
ством формулы, идентифицирующей внешний исполняемый файл (.exe).
Чтобы передать удаленной программе входные данные, используется символ
перенаправления (|).
На рис. 14.6 показан пример вызова удаленной программы MktLink.exe,
возвращающей курс акций с символом AA.
Рис. 14.6. Вызов типичной DDE&службы ре&ального времени
Наблюдать за изменением курса акций в режиме реального времени, несоY
мненно, интересно. Однако еще более интересно и полезно анализировать
получаемую информацию с целью выявления трендов.
414 Часть II Автоматизация Excel
Анализ данных, извлеченных из InternetVBA содержит метод OnTime, позволяющий выполнить любую процедуY
ру в определенный момент времени или по прошествии заданного периода
времени.
Ниже приведен код VBA, извлекающий данные из Internet каждый час на
протяжении всего рабочего дня.
Sub ScheduleTheDay() Application.OnTime EarliestTime:=TimeValue("8:00 AM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("9:00 AM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("10:00 AM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("11:00 AM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("12:00 AM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("1:00 PM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("2:00 PM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("3:00 PM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("4:00 PM"), _Procedure:="CaptureData" Application.OnTime EarliestTime:=TimeValue("5:00 PM"), _Procedure:="CaptureData"End Sub
Sub CaptureData() Dim WSQ As Worksheet Set WSQ = Worksheets("MyQuery")' Выполнить запрос. WSQ.Range("A2").QueryTable.Refresh BackgroundQuery:=False' Приостановить выполнение макроса. Application.Wait (Now + TimeValue("0:00:05"))' Скопировать результаты запроса. NextRow = WSQ.Range("A65536").End(xlUp).Row + 1 WSQ.Range("A2:B2").Copy WSQ.Cells(NextRow, 1)' Зафиксировать дату и время. WSQ.Cells(NextRow, 2).Value = WSQ.Cells(NextRow, 2).ValueEnd Sub
Условия выполнения метода OnTime
Метод OnTime выполняется в заданный момент времени при условии преY
бывания Excel в режиме готовности (Ready), копирования (Copy), вырезания
(Cut) или поиска (Find). Excel не сможет выполнить макрос CaptureData в
08:00, если в это время какаяYлибо ячейка рабочего листа находится в режиме
редактирования.
Взаимодействие с Internet Глава 14 415
Обратите внимание, что в предыдущем примере было задано только наY
чальное время выполнения макроса CaptureData. Это означает, что Excel
будет терпеливо ожидать возвращения в режим готовности, чтобы выполнить
указанный макрос.
Предположим, что пользователь начал редактировать ячейку в 07:59, а заY
тем был вызван на экстренное совещание, продолжавшееся до 10:30. За это
время Excel должен был выполнить макрос CaptureData три раза: в 08:00,
09:00 и 10:00. Вернувшись на свое рабочее место и нажав клавишу <Enter>,
чтобы выйти из режима редактирования, пользователь обнаружит, что три
первых WebYзапроса были выполнены между 10:30 и 10:31.
Определение временного окна для выполнения макроса
Чтобы обойти описанную выше проблему, можно определить временные
рамки для выполнения макроса. Следующий код указывает Excel на необхоY
димость выполнения макроса CaptureData в промежутке времени между
08:00 и 08:05. Если на протяжении всего этого периода Excel будет пребывать
в режиме редактирования, выполнение назначенного задания отменяется.
Application.OnTime EarliestTime:=TimeValue("8:00 AM"), _Procedure:="CaptureData", LatestTime:=TimeValue("08:05 AM")
Отмена назначенного заданияОтменить ранее назначенное задание весьма непросто, так как для этого
необходимо знать точное время выполнения макроса. Чтобы отменить назнаY
ченное задание, вызовите метод OnTime, установив значение параметра
Schedule равным False. Ниже приведен код, отменяющий выполнение
макроса CaptureData, назначенное на 11:00.
Sub CancelEleven() Application.OnTime EarliestTime:=TimeValue("11:00 AM"), _ Procedure:="CaptureData", _ Schedule:=FalseEnd Sub
Обратите внимание, что расписание, заданное с помощью метода OnTime,
существует на уровне выполняющейся копии Excel. Другими словами, закрыY
тие рабочей книги не отменяет текущее расписание, если копия Excel остается
открытой. Рассмотрим приведенную ниже последовательность действий.
1. Открытие Excel в 07:30.
2. Открытие рабочей книги Schedule.xls и запуск макроса, назначаюY
щего выполнение процедуры на 08:00.
3. Закрытие рабочей книги Schedule.xls.
4. Открытие новой рабочей книги и начало ввода данных.
В 08:00 Excel откроет рабочую книгу Schedule.xls и выполнит назнаY
ченное задание. Естественно, это может стать полной неожиданностью для
416 Часть II Автоматизация Excel
пользователя. При большом числе назначенных заданий рекомендуется отY
крывать две копии Excel, одну из которых использовать для выполнения задаY
ний, а другую YYYY для работы.
Отмена всех назначенных заданий
Чтобы отменить все назначенные задания, закройте Excel с помощью коY
манды меню Файл Выход (File Exit). Обычно к подобному действию приY
бегают при необходимости отмены большого числа автоматически назначенY
ных заданий с заранее неизвестным точным временем выполнения.
Выполнение макроса по прошествии заданного периодавремени
Excel позволяет выполнить макрос по прошествии заданного периода вреY
мени. Например, макрос CaptureData будет запущен через 2 мин и 30 с поY
сле выполнения следующего кода.
Sub ScheduleAnything() WaitHours = 0 WaitMin = 2 WaitSec = 30 NameOfScheduledProc = "CaptureData"
' Определение времени выполнения назначенного задания. NextTime = Time + TimeSerial(WaitHours, WaitMin, WaitSec)
' Создание назначенного задания. Application.OnTime EarliestTime:=NextTime, _Procedure:=NameOfScheduledProc
End Sub
Периодическое выполнение макроса через определенныепромежутки времени
Предположим, что некий макрос нужно выполнять каждые 2 мин. Если в
заданное время Excel находится в режиме редактирования, выполнение макY
роса, назначенное на это время, следует отменить.
Одно из наиболее очевидных решений поставленной задачи заключается в
рекурсивном планировании макросом своего собственного выполнения.
Sub ScheduleRepeatedly() WaitHours = 0 WaitMin = 2 WaitSec = 0 NameOfThisProcedure = "ScheduleRepeatedly" NameOfScheduledProc = "CaptureData"
' Определение времени выполнения назначенного задания. NextTime = Time + TimeSerial(WaitHours, WaitMin, WaitSec)
Взаимодействие с Internet Глава 14 417
' Создание назначенного задания. Application.OnTime EarliestTime:=NextTime, _Procedure:=NameOfThisProcedure
' Выполнить макрос NameOfScheduledProc' (в данном случае - макрос "CaptureData"). Application.Run NameOfScheduledProc
End Sub
Подобный подход имеет неоспоримые преимущества. ВоYпервых, он поY
зволяет не планировать огромное число заданий наперед. В каждый момент
времени существует только одно назначенное задание. ВоYвторых, чтобы преY
кратить периодическое выполнение макроса CaptureData каждые 2 мин,
достаточно закомментировать строку Application.OnTime Earliest-Time:=NextTime, Procedure:=NameOfThisProcedure и дождаться поY
следнего выполнения этого макроса.
Практикум
Отслеживание размера национального долга СШАРассмотрим рабочий лист, показанный на рис. 14.7.
Рис. 14.7. Ячейка A2 содержит результат Web&запро&
са, а ячейка B2 — текущие дату и время. Цель мак&роса DebtClock — каждые 15 с получать из Internetданные о размере национального долга США исохранять их на рабочем листе Национальныйдолг США, начиная с 6&й строки
Ячейка A2 содержит результаты Web&запроса по адресу: http://brillig.com/debt_clock/, а ячейка B2 — функцию ТДАТА (NOW).Макрос DebtClock предназначен для отслеживания размера национальногодолга США. Следующий код выполняет Web&запрос, сохраняет полученный ре&зультат на рабочем листе и планирует свое выполнение через 15 с.Sub DebtClock() Dim WSQ As Worksheet Set WSQ = Worksheets("Национальный долг США")
' Ячейка A2 содержит Web-запрос по адресу: ' http://brillig.com/debt_clock WaitSec = 15
418 Часть II Автоматизация Excel
NameOfThisProcedure = "DebtClock"
' Определение времени выполнения назначенного задания. NextTime = Time + TimeSerial(0, 0, WaitSec)
' Создание назначенного задания. Application.OnTime EarliestTime:=NextTime, _Procedure:=NameOfThisProcedure
' Выполнение Web-запроса. WSQ.Range("A2").QueryTable.Refresh BackgroundQuery:=False' Приостановка выполнения макроса. Application.Wait (Now + TimeValue("0:00:05"))' Копирование результатов запроса. NextRow = WSQ.Range("A65536").End(xlUp).Row + 1 WSQ.Range("A2:B2").Copy WSQ.Cells(NextRow, 1)' Фиксирование даты и времени. WSQ.Cells(NextRow, 2).Value = WSQ.Cells(NextRow, 2).Value
End Sub
Запустив макрос и подождав несколько минут, можно увидеть, что каждую ми&нуту национальный долг США увеличивается примерно на 2 млн долларов(рис. 14.8).
Рис. 14.8. Спустя несколько минут после запуска
макроса DebtClock на рабочем листе будут содер&жаться данные, достаточные для анализа темповизменения национального долга США
Размещение данных на WebLстраницеРанее в этой главе были рассмотрены различные способы извлечения инY
формации из Internet. Вместе с тем, Excel поддерживает и обратную операY
цию YYYY размещение данных рабочего листа на WebYстранице.
Взаимодействие с Internet Глава 14 419
В главе 12, ‘‘Сводные таблицы’’, был описан макрос, создающий отчеты
для региональных менеджеров компании. Вместо отправки отчетов по факсу
или электронной почте их можно сохранить в формате HTML и разместить на
корпоративном сайте компании.
Рассмотрим отчет, показанный на рис. 14.9.
Рис. 14.9. Отчет, сгенерированный макросом из главы 12
Чтобы сохранить содержимое рабочего листа в формате HTML с помощью
пользовательского интерфейса Excel, выберите команду меню ФайлСохранить как (File Save As).
Выбрав формат сохранения Веб-страница (Web Page), убедитесь, что флаY
жок Добавить интерактивность (Add interactivity) сброшен (рис. 14.10). В проY
тивном случае созданную WebYстраницу можно будет просмотреть только при
условии наличия на компьютере установленной копии Excel.
Рис. 14.10. Сохраняя рабочий лист в формате Web&страницы, проверьте, чтобыфлажок Добавить интерактивность (Add interactivity) был сброшен — Web&страница будет доступна для гораздо большего числа пользователей
420 Часть II Автоматизация Excel
Файл, полученный в результате сохранения рабочего листа в формате
HTML, можно просмотреть с помощью любого обозревателя Web, как покаY
зано на рис. 14.11.
Рис. 14.11. Excel успешно справляется с задачей сохранения рабочеголиста в формате HTML
Новая концепция сохранения данных в формате WebYстраницы, представY
ленная Microsoft, предполагает возможность восстановления рабочего листа
на основе полученного HTMLYфайла. Платой за подобную гибкость является
чрезмерный объем генерируемого HTMLYкода. В частности, для представлеY
ния информации, показанной на рис. 14.11, достаточно 457 байт. Будучи пеY
реведенной в формат HTML, эта же информация занимает уже 9105 байт.
Создание WebLстраниц с помощью VBA
Лишенные возможности сохранения рабочего листа в формате WebY
страницы, пользователи предыдущих версий Excel были вынуждены создавать
HTMLYкод с помощью VBA. Следует отметить, что этот метод имеет одно весьY
ма существенное преимущество YYYY он позволяет создавать ‘‘полноценные’’ WebY
страницы (содержащие логотип компании, навигационные панели и т.п.), готоY
вые к немедленному размещению на сайте.
Шаблон типичной WebYстраницы содержит код для отображения логотипа
компании и навигационных панелей, данные этой страницы и код, заверY
шающий HTMLYфайл. Создайте подобный шаблон, поместив вместо данных
страницы текст ‘‘ДАННЫЕ WEBYСТРАНИЦЫ’’, и просмотрите полученный
код с помощью приложения Блокнот (Notepad), как показано на рис. 14.12.
Взаимодействие с Internet Глава 14 421
Рис. 14.12. Шаблон типичной Web&страницы можно условно разделить на три части: код дляотображения логотипа компании и навигационных панелей; данные Web&страницы; и код, за&вершающий HTML&файл
Анализ кода типичной WebYстраницы наталкивает на мысль о необходиY
мости поэтапного создания HTMLYкода с помощью VBA.
1. Создайте код для отображения логотипа компании и навигационных
панелей.
2. Создайте код, размещающий на WebYстранице данные с рабочего листа
Excel.
3. Создайте код, завершающий HTMLYфайл.
Применение Excel в качестве системы управлениясодержимым
Создадим простую систему управления содержимым, генерирующую WebY
страницы на основе имеющихся данных Excel.
На рис. 14.13 показано содержимое типичной базы данных.
Разработаем код VBA, генерирующий WebYстраницу на основе данных, поY
казанных на рис. 14.13, и на основе HTMLYкода, показанного на рис. 14.12.
422 Часть II Автоматизация Excel
Рис. 14.13. Базы данных, подобные этой, поддерживаются огромным числом всевозможныхкомпаний
Добавим к рабочей книге Excel, содержащей сведения из базы данных, два
рабочих листа. Первый рабочий лист (назовем его Top) будет содержать код,
отображающий логотип компании и навигационные панели. Второй рабочий
лист (назовем его Bottom) будет содержать код, завершающий HTMLYфайл
(рис. 14.14).
Конечным результатом выполнения макроса
WriteMembershipHTML является HTMLYфайл sam-pledirectory.html. Первым делом макрос копируY
ет в этот файл HTMLYкод, размещенный на рабочем
листе Top.
На следующем этапе макрос WriteMember-shipHTML экспортирует сведения из базы данных в
файл sampledirectory.html.
Наконец, макрос WriteMembershipHTML копируY
ет в файл sampledirectory.html HTMLYкод, разY
мещенный на рабочем листе Bottom.
Sub WriteMembershipHTML()' Этот макрос создает Web-страницу. Dim WST As Worksheet Dim WSB As Worksheet Dim WSM As Worksheet Set WSB = Worksheets("Bottom") Set WST = Worksheets("Top") Set WSM = Worksheets("База данных")
' Определение пути к текущей рабочей книге. MyPath = ThisWorkbook.Path
LineCtr = 0
Рис. 14.14. Рабочий листBottom содержит деск&рипторы, необходимыедля завершения HTML&файла
Взаимодействие с Internet Глава 14 423
FinalT = WST.Cells(65536, 1).End(xlUp).Row FinalB = WSB.Cells(65536, 1).End(xlUp).Row FinalM = WSM.Cells(65536, 1).End(xlUp).Row
MyFile = "sampledirectory.html"
ThisFile = MyPath & Application.PathSeparator & MyFile ThisHostFile = MyFile
' Удалить существующую Web-страницу. On Error Resume Next Kill (ThisFile) On Error GoTo 0
' Создать заголовок Web-страницы. ThisTitle = "<Title>Список членов организации ""Lake _Township Chamber of Commerce""</Title>" WST.Cells(3, 2).Value = ThisTitle
' Открыть файл для записи. Open ThisFile For Output As #1
' Скопировать HTML-код с рабочего листа "Top". For j = 2 To FinalT Print #1, WST.Cells(j, 2).Value Next j
' Экспортировать сведения из базы данных в HTML-файл. For j = 2 To FinalM' Название компании, выделенное полужирным шрифтом. Print #1, "<b>" & WSM.Cells(j, 1).Value & "</b><br>"' Адрес компании. Print #1, WSM.Cells(j, 2).Value & "<br>"' Город, штат и Zip-код. Addr = WSM.Cells(j, 3) & " " & WSM.Cells(j, 4) & " " _& WSM.Cells(j, 5) Print #1, Addr & "<br>"' Номер телефона и 2 символа разрыва строки. Print #1, WSM.Cells(j, 6).Value & "<br><br>" Next j
' 20 символов разрыва страницы. For j = LineCtr To 20 Print #1, "<br>" Next j
' Дата последнего изменения Web-страницы. Print #1, "<br>" Print #1, "Последнее изменение " & Format(Date, "mmmm dd, _yyyy") & " " & Format(Time, "h:mm AM/PM")
' Скопировать HTML-код с рабочего листа "Bottom". For j = 2 To FinalB Print #1, WSB.Cells(j, 2).Value Next j' Закрыть файл. Close #1
424 Часть II Автоматизация Excel
Application.StatusBar = False Application.CutCopyMode = False MsgBox "Web-страница успешно создана"
End Sub
Результат выполнения макроса WriteMembershipHTML показан на
рис. 14.15.
Рис. 14.15. Web&страница, созданная с помощью макроса WriteMembershipHTML
Подобная система управления содержимым имеет много преимуществ.
Сотруднику, поддерживающему базу данных, достаточно щелкнуть на кнопке,
чтобы обновить содержимое WebYстраницы.
С другой стороны, WebYдизайнеру, решившему изменить внешний вид
шаблона WebYстраницы, достаточно скопировать обновленный HTMLYкод на
рабочие листы Top и Bottom.
Также следует отметить, что размер WebYстраницы, созданной с помощью
макроса WriteMembershipHTML, в несколько раз меньше размера WebY
страницы, сгенерированной с помощью встроенного средства сохранения
файлов Excel.
Рассмотренная система управления содержимым может быть с успехом
взята за основу более сложной системы, создающей не одну, а несколько деY
сятков различных WebYстраниц.
Взаимодействие с Internet Глава 14 425
Загрузка WebLстраницы на FTPLсервер
После создания WebYстраницы ее нужно загрузить с жесткого диска комY
пьютера на FTPYсервер. К сожалению, далеко не все пользователи Excel могут
справиться с этой задачей.
Воспользуемся бесплатным FTPYклиентом WCL_FTP, созданным Кеном
Андерсоном (Ken Anderson), по адресу: http://www.pacific.net/~ken/software/. Разместите файл WCL_FTP.exe в корневой папке жесткого дисY
ка C: и примените следующий код для загрузки созданной ранее WebY
страницы на ваш FTPYсервер.
Sub DoFTP(fname, pathfname)' Скопируйте файл wcl_ftp.exe в корневую папку диска C:.' FTP-клиент WCL_FTP находится по адресу:http://www.pacific.net/~ken/software/.
' Создать командную строку со следующим синтаксисом:' WCL_FTP.exe "Заголовок окна" АдресСервераFTP ИмяПользователя' Пароль КаталогНаСервереFTP ИмяФайлаНаСервереFTP ЛокальноеИмяФайла' Операция (get или put) РежимПередачи (0 - Ascii, 1 - двоичный)' Журнал (0 - нет, 1 - да) ФоновыйРежим (0 - нет, 1 - да)' ЗакрытьПоОкончанииРаботы (0 - нет, 1 - да)' ПассивныйРежим (0 - нет, 1 - да) ЖурналОшибок (0 - нет, 1 - да)
s = """c:\wcl_ftp.exe"" " _ & """Загрузка файла на FTP-сервер"" " _ & "ftp.АдресСервераFTP.com ИмяПользователя Пароль _КаталогНаСервереFTP " _ & fname & " " _ & """" & pathfname & """ " _ & "put " _ & "0 0 0 1 1 1"
Shell s, vbMinimizedNoFocusEnd Sub
Следующий шагСледующая глава посвящена передаче данных между приложениями с поY
мощью XML. Использование XML намного эффективнее WebYзапросов. НеY
которые сайты (например, Amazon.com) предлагают такую услугу уже сегодня.
Введение в XML ............................427Правила XML ................................ 428Универсальный форматфайлов........................................... 429XML набирает обороты ............. 429Схемы и сопоставления XML.... 429Сохранение и считываниесодержимого рабочей книгиExcel в формате XML....................431Следующий шаг........................... 438
15Глава 15
ÏîääåðæêàXML âïðîôåññèîíàëü-íîì âûïóñêåExcel 2003
Одним из наиболее существенных
нововведений профессионального выY
пуска Office 2003 является улучшенная
поддержка работы с данными в формаY
те XML. Для тех, кто еще не сталкиY
вался с XML в своей повседневной
работе, в конце главы имеется пракY
тикум, демонстрирующий получение
данных в формате XML с WebYсайта
Amazon.com.
Введение в XML Исходный код WebYстраницы
‘‘усыпан’’ так называемыми HTMLY
тегами. Например, в самом начале
страницы обычно присутствует тег,
определяющий ее заголовок:
<TITLE>Заголовок Web-страницы</TITLE>
Типичная WebYстраница содерY
жит также теги, определяющие абзаY
цы, таблицы, строки в таблицах и т.п.
Синтаксис тегов описан в специфиY
кации HTML.
XML имеет много общего с HTML.
Одно из ключевых отличий XML от
HTML состоит в том, что XML позвоY
ляет определять любой тег. Ниже приY
428 Часть II Автоматизация Excel
веден код XML, содержащий информацию о заключенных за день сделках (для
создания подобного кода можно воспользоваться приложением Блокнот(Notepad)):
<TodaysOrders> <SalesOrder> <Customer>BCA Co</Customer> <Address>123 North</Address> <City>Stow</City> <State>OH</State> <Zip>44224</Zip> <ItemSKU>23456</ItemSKU> <Quantity>500</Quantity> <UnitPrice>21.75</UnitPrice> </SalesOrder> <SalesOrder> <Customer>DEF Co</Customer> <Address>234 Carapace Lane</Address> <City>South Bend</City> <State>IN</State> <Zip>44685</Zip> <ItemSKU>34567</ItemSKU> <Quantity>20</Quantity> <UnitPrice>50.00</UnitPrice> </SalesOrder></TodaysOrders>
Правила XML Ниже приведено несколько простых правил, подчеркивающих отличия
между XML и HTML.
Каждый элемент данных должен быть заключен в идентичные теги.
Поскольку теги XML чувствительны к регистру, запись
<TagName>Data</Tagname> некорректна. Примером правильной заY
писи является запись <TagName>Data</TagName>.
XMLYфайл должен начинаться и заканчиваться так называемым корY
невым тегом. В каждом XMLYфайле может существовать только один
корневой тег. В рассмотренном выше примере таким тегом является тег
<TodaysOrders>.
XML допускает наличие пустых тегов, отличающихся символом косой
черты после названия тега. Например, чтобы указать на отсутствие в
данной записи информации о ZipYкоде, используйте тег <Zip/>.
При вложении внутренние теги должны закрываться раньше, чем внешY
ние теги. Например, в HTML допускается такая запись: <b>XML - этоочень <i>круто</b></i>. В XML подобная запись была бы некорY
ректной. Примером корректного закрытия тегов в XML является запись
<Item><a>data</a></Item>.
Поддержка XML в профессиональном выпуске Excel 2003 Глава 15 429
Универсальный формат файловНа протяжении многих лет универсальным форматом файлов считался
формат CSV. Практически все приложения для работы с электронными табY
лицами обладали возможностью сохранения и считывания данных в виде знаY
чений с разделителямиYзапятыми. На сегодняшний момент наиболее вероятY
ным претендентом на звание универсального является формат файлов XML.
При обмене данными в формате CSV обе стороны должны иметь одинакоY
вое представление об их структуре. В рассмотренном выше примере поле ZipY
кода имеет порядковый номер 5, а поле кода SKU — порядковый номер 6. НаY
рушение очередности следования этих полей может привести к нежелательY
ным последствиям. На рис. 15.1 показан результат открытия в Excel типичного
CSVYфайла.
Рис. 15.1. Формат CSV не содержит никакой информации о структуреданных
XML набирает оборотыФормат XML предлагает гораздо более широкие возможности. На рис. 15.2
показан результат открытия в Excel XMLYфайла, содержащего сведения о
сделках, заключенных в продолжение рабочего дня.
На основе информации, хранящейся в XMLYфайле, Excel отображает загоY
ловки столбцов и даже может вывести так называемую схему данных. Схема
хранится в отдельном файле и описывает столбцы данных и существующие
между ними отношения.
Excel поддерживает сохранение данных в формате XML. Добавьте новые
записи в XMLYсписок и выберите команду меню Файл Сохранить как (File
Save As). Из раскрывающегося списка Тип файла (Save as type) выберите знаY
чение XML-данные (XML Data) и щелкните на кнопке Сохранить (Save), как
показано на рис. 15.3.
Схемы и сопоставления XMLВ предыдущем разделе было рассмотрено считывание, редактирование и
сохранение XMLYданных с помощью Excel.
Существует два дополнительных типа файлов, предназначенных для рабоY
ты с XMLYданными YYYY файл схемы и файл сопоставления.
В то время как XMLYфайл содержит данные и имена полей, схема XML опY
ределяет взаимоотношения между полями и правила проверки корректности
данных (например, значение, представляющее ZipYкод, должно состоять из
пяти цифр). Схема XML хранится в файле с расширением .XSD.
430 Часть II Автоматизация Excel
Рис. 15.2. Формат XML содержит сведения о структуре данных, корректно интерпретируемые Excel
Рис. 15.3. Excel поддерживает два способа сохранения XML&данных: в виде элек&тронной таблицы XML и в виде обычного XML&файла
Сопоставление описывает способ отображения XMLYданных на документ
Excel. В файле сопоставления можно определить поля XMLYфайла, которые
Поддержка XML в профессиональном выпуске Excel 2003 Глава 15 431
будут отображены на рабочем листе. Схеме XML могут соответствовать неY
сколько сопоставлений, каждое из которых будет определять некоторое предY
ставление одних и тех же данных. Сопоставление XML хранится в файле с
расширением .XLS.
XSDY и XLSYфайлы обычно поставляются вместе с XMLYданными. При неY
обходимости, Excel может вывести схему XML на основе существующего XMLY
файла. Откройте созданный ранее файл TodaysOrders.xml и введите в окне
Immediate (Быстрое выполнение) редактора Visual Basic следующую строку:
Print ActiveWorkbook.XmlMaps(1).Schemas(1).XML
Скопируйте полученный результат в Блокнот (Notepad) и сохраните его в
файле с именем TodaysOrders.xsd. Сгенерированная схема XML с добавY
ленными для повышения удобочитаемости переносами строк и отступами поY
казана на рис. 15.4.
Рис. 15.4. Схема XML, выведенная на основе содержимого файла TodaysOr-ders.xml
В отличие от схемы, Excel не поддерживает автоматического генерироY
вания сопоставления XML. Более подробную информацию о создании
XSLYфайлов вы сможете найти по адресу: http://www.mrexcel.com/tip064.shtml.
Сохранение и считывание содержимого рабочейкниги Excel в формате XML
Microsoft рассматривает формат XML как стандартный формат файлов доY
кументов Office. Это означает, что, теоретически, любая программа, способY
ная сохранять данные в формате XML, может быть использована для создания
файла Excel.
432 Часть II Автоматизация Excel
На рис. 15.5 показан результат сохранения и последующего считывания соY
держимого файла Excel в формате XML.
Рис. 15.5. Формат XML можно применять в качестве стандартного формата файлов Excel 2003
На рис. 15.5 слева показан исходный файл Excel, содержащий стилевое
форматирование и диаграмму, а на рис. 15.5 справа YYYY результат открытия
XMLYфайла, созданного при сохранении исходного файла в виде электронной
таблицы XML. При сохранении файла рабочей книги в виде электронной табY
лицы XML Excel предупреждает о невозможности сохранения диаграмм и
проектов VBA. Во всем остальном полученный файл XML ничем не отличаетY
ся от исходного файла Excel.
Фрагмент XMLYкода, сгенерированного при сохранении файла Excel в виY
де электронной таблицы XML, представлен на рис. 15.6.
Поддержка XML в профессиональном выпуске Excel 2003 Глава 15 433
Рис. 15.6. Придерживаясь спецификаций Microsoft, любая программа, способная сохранятьданные в формате XML, может быть использована для создания файла Excel
Практикум
Извлечение XML-данных с сайта Amazon.comРассмотрим пример создания VBA&макроса, извлекающего XML&данные с сайтаAmazon.com.Прежде чем приступить к созданию макроса, загрузите с сайта Amazon.com наборинструментальных средств разработки, выполнив следующие действия.1. С помощью обозревателя Internet посетите страницу http://amazon.com/
webservices.
2. Щелкните на ссылке E-Commerce Service (Служба электронной коммерции),расположенной на левой навигационной панели.
3. В разделе Other Versions Available (Другие версии) щелкните на ссылке Here(Здесь) для загрузки файла kit.zip на свой компьютер.
4. Распакуйте файл kit.zip, чтобы извлечь его содержимое.
5. В наборе инструментальных средств разработки содержится описание двухсхем XSD: упрощенной и полной. Откройте файл READMEFIRST.txt и отыщитев нем ссылку на полную XSD&схему. На момент написания этой книги полнаяXSD&схема была доступна по адресу: http://xml.amazon.com/schemas3/dev-heavy.xsd.
434 Часть II Автоматизация Excel
6. Откройте ссылку в обозревателе Internet и сохраните XSD&схему на жесткомдиске компьютера.
Создайте новую рабочую книгу Excel и присоедините к ней XSD&схему, выполнивследующие действия.
1. Выберите в меню Excel команду Данные XML Источник XML(Data XML XML Source).
2. Щелкните на кнопке Карты XML (XML Maps), расположенной в области задачИсточник XML (XML Source).
3. В открывшемся диалоговом окне Карты XML (XML Maps) щелкните на кнопкеДобавить (Add).
4. Выберите сохраненный ранее файл dev-heavy.xsd и щелкните на кнопкеОткрыть (Open).
5. Выберите элемент ProductInfo в списке Выберите корень (Please select aroot) диалогового окна Несколько корней (Multiple Roots) и щелкните на кноп&ке OK.
6. Щелкните на кнопке OK, чтобы закрыть диалоговое окно Карты XML.
В области задач Источник XML появится список полей схемы ProductInfo. Пере&несите нужные поля на рабочий лист, как показано на рис. 15.7.
Рис. 15.7. Присоединив XSD&схему к рабочей книге, перетащите требуемые поля из областизадач Источник XML (XML Source) на рабочий лист Excel
Поддержка XML в профессиональном выпуске Excel 2003 Глава 15 435
Завершив подготовку рабочего листа, откройте редактор Visual Basic, чтобы соз&дать код, извлекающий XML&данные с сайта Amazon.com.Выберите в меню редактора Visual Basic команду Tools References (СервисСсылки) и установите флажок, соответствующий библиотеке Microsoft WinHTTPServices. Щелкните на кнопке OK, чтобы закрыть диалоговое окно References(Ссылки).Набор инструментальных средств разработки Amazon.com содержит примеры за&просов в виде строк URL по имени автора, издателю, коду ISBN и т.п.Следующий макрос формирует строку запроса URL и отправляет ее в виде HTTP&запроса серверу Amazon.com. Результат запроса копируется на рабочий лист спомощью метода ImportXML.Ниже приведен код макроса, запрашивающего у Amazon.com все книги БиллаДжелена (Bill Jelen), изданные в 2002 году.Sub QuerybyAuthor()' Запрос к Amazon.com возвращает все книги' Билла Джелена (Bill Jelen), изданные в 2002 году. Dim ws As Worksheet Dim whr As New WinHttpRequest Dim lobj As ListObject Dim nCount As Integer Dim objSelected As Object
Dim sURI As String
Set objSelected = Selection Set ws = Worksheets("Sheet1") Set lobj = ws.ListObjects(1)
Application.Cursor = xlWait
sURI = "http://xml.amazon.com/onca/xml2?t=webservices-20 _&dev-t=D3VCCO47XZEQFA" _ & "&PowerSearch=author:Bill Jelen and pubdate:2002" _ & "&mode=books&type=heavy&page=1&f=xml"
whr.Open "GET", sURI whr.Send
ActiveWorkbook.XmlMaps(1).ImportXml whr.ResponseText, _Overwrite:=True
Application.Cursor = xlDefault
End Sub
Результат запроса помещается в объект Список (List) на рабочем листе Excel. Осо&бенности структуры XML&данных, полученных с Amazon.com, обуславливают на&личие более чем одной строки для книг, написанных несколькими авторами (поодной на каждого автора). Подобный запрос может использоваться для отслежи&вания рейтинга книг и текущей стоимости их подержанных экземпляров(рис. 15.8).
436 Часть II Автоматизация Excel
Рис. 15.8. Метод ImportXML используется для копирования результатов запроса на рабочийлист. Обратите внимание, что на экране будут видны только те поля XML&данных, которые бы&ли сопоставлены с рабочим листом путем перетаскивания из области задач Источник XML
Извлечение нескольких страниц XML-данныхДля извлечения XML&данных, занимающих несколько страниц, необходимо вы&полнить соответствующее число запросов. Номер запрашиваемой страницы ука&зывается в строке URL с помощью параметра page (например, page=2). Чтобыуказать на необходимость добавления извлеченных XML&данных к существующе&му объекту Список (List), установите параметр Overwrite метода ImportXMLравным False. Следующий макрос запрашивает у Amazon.com две страницыXML&данных, сгенерированных в результате запроса книг, опубликованных из&дательством Sams в 2003 году.Sub QuerybyPublisher() Dim ws As Worksheet Dim whr As New WinHttpRequest Dim lobj As ListObject Dim nCount As Integer Dim objSelected As Object
Dim sURI As String
Set objSelected = Selection Set ws = Worksheets("Sheet1") Set lobj = ws.ListObjects(1)
Поддержка XML в профессиональном выпуске Excel 2003 Глава 15 437
Application.Cursor = xlWait
sURI = "http://xml.amazon.com/onca/xml2?t=webservices-20 _&dev-t=D3VCCO47XZEQFA" _ & "&PowerSearch=publisher:Sams and pubdate:2003" _ & "&mode=books&type=heavy&page=1&f=xml"
' Получить первые 20 строк XML-данных. whr.Open "GET", sURI whr.Send ActiveWorkbook.XmlMaps(1).ImportXml whr.ResponseText, _Overwrite:=True
' Получить следующие 20 строк XML-данных. sURI = "http://xml.amazon.com/onca/xml2?t=webservices-20 _&dev-t=D3VCCO47XZEQFA" _ & "&PowerSearch=publisher:Sams and pubdate:2003" _ & "&mode=books&type=heavy&page=2&f=xml"
whr.Open "GET", sURI whr.Send ActiveWorkbook.XmlMaps(1).ImportXml whr.ResponseText, _Overwrite:=False
Application.Cursor = xlDefault
End Sub
Результат выполнения макроса QuerybyPublisher показан на рис. 15.9.
Рис. 15.9. Если XML&данные занимают несколько страниц, для их извлечения потребуетсявыполнить соответствующее число запросов
438 Часть II Автоматизация Excel
Следует отметить, что за счет наличия огромного числа полей карта Product-Info_Map позволяет легко манипулировать отображаемыми результатами запросабез необходимости внесения изменений в код VBA. Например, чтобы получить све&дения о дате выхода книги (ReleaseDate), ее номере в каталоге Amazon.com(ListID) или средней оценке книги читателями (AvgCustomerRating), перета&щите на рабочий лист соответствующие поля из области задач Источник XML(XML Source).
Следующий шагОсновная задача XML заключается в обеспечении возможности обмена
данными между разнородными приложениями. Программы, входящие в соY
став пакета Microsoft Office, обладают встроенной возможностью обмена данY
ными между собой. Следующая глава посвящена автоматизации управления
текстовым редактором Microsoft Word средствами Excel VBA.
Раннее связывание..................... 439Позднее связывание................... 442Работа с объектами .................... 443Объекты Word.............................. 445Следующий шаг........................... 460
16Глава 16
ÀâòîìàòèçàöèÿWord
Word, Excel, PowerPoint, Outlook и
Access используют один и тот же язык
программирования VBA, отличаясь
между собой только объектной модеY
лью (например, рабочая книга Excel
представлена объектом Workbook, а
документ Word — объектом Docu-ment). Каждое из перечисленных
приложений может получить доступ
к объектной модели другого прилоY
жения при условии, что последнее
установлено на компьютере.
Для доступа к объектной библиоY
теке Word из кода Excel VBA на нее
необходимо установить ссылку поY
средством раннего или позднего связы*вания. Ранее связывание подразумеY
вает создание ссылки на объект приY
ложения во время компиляции
программы, а позднее связывание YYYY
во время ее выполнения.
В этой главе рассматривается досY
туп к объектной модели Word средстY
вами Excel VBA. Чтобы познакомитьY
ся со структурой объектной модели
Word или другого приложения, вхоY
дящего в состав пакета Microsoft OfY
fice, воспользуйтесь диспетчером
объектов редактора Visual Basic соотY
ветствующего приложения.
Раннее связываниеКод, созданный с применением
раннего связывания, выполняется
быстрее, чем код, созданный с приY
менением позднего связывания.
440 Часть II Автоматизация Excel
Раннее связывание предполагает создание ссылки на объектную библиотеку
Word на этапе разработки программного кода. Это делает объекты, свойства и
методы Word доступными в диспетчере объектов Visual Basic, что, в частности,
позволяет отображать динамические подсказки, как показано на рис. 16.1.
Рис. 16.1. Раннее связывание позволяет существенно упроститьзнакомство с синтаксисом объектной модели Word
Недостатком раннего связывания является требование обязательного приY
сутствия в системе объектной библиотеки, на которую создается ссылка.
Именно поэтому макрос, использующий объектную модель Word 2003 и ранY
нее связывание, не удастся выполнить на компьютере с Word 2002.
Чтобы добавить ссылку на объектную библиотеку Word с помощью редакY
тора Visual Basic, выполните следующие действия.
1. Выберите в меню редактора Visual Basic команду Tools References(Сервис Ссылки).
2. В списке Available References (Доступные ссылки) установите флажок
рядом со ссылкой на библиотеку Microsoft Word 11.0 Object Library
(рис. 16.2).
3. Щелкните на кнопке OK.
На заметку Отсутствие объектной библиотеки Word в списке Available References означает,что приложение не установлено на компьютере. Если в списке доступных ссылоксодержится объектная библиотека другой версии Word (например, 10.0), значитна компьютере установлена другая версия этого текстового редактора.
Добавив ссылку на библиотеку, можно начинать объявлять переменные,
используя типы из объектной модели Word. Если в качестве типа переменной
указать тип Object, это приведет к использованию позднего связывания.
Автоматизация Word Глава 16 441
Рис. 16.2. Выберите требуемую объектную библиотеку всписке Available References
Совет В поисках ссылки на тип объекта Excel последовательно просматривает все под&ключенные библиотеки. Если нужная ссылка содержится более чем в одной биб&лиотеке, Excel выберет ссылку, встретившуюся первой. Повлиять на выбор той илииной библиотеки можно путем изменения ее приоритета в списке AvailableReferences.
Sub WordEarlyBinding()' Раннее связывание.
Dim wdApp As Word.Application Dim wdDoc As Document
Set wdApp = New Word.Application Set wdDoc = wdApp.Documents.Open(ThisWorkbook.Path & _"\Автоматизация Word.doc") wdApp.Visible = True Set wdApp = Nothing Set wdDoc = NothingEnd Sub
Макрос WordEarlyBinding открывает существующий документ Word. ПеY
ременная wdApp используется для обращения к объекту Word.Application(аналог объекту Application в Excel VBA), а переменная wdDoc — для обY
ращения к объекту Document. Для создания нового экземпляра Word испольY
зуется выражение New Word.Application.
После создания экземпляра Word освободите память, занимаемую переY
менными wdApp и wdDoc, присвоив каждой из них значение Nothing.
442 Часть II Автоматизация Excel
Совет Созданный экземпляр Word не отображается на экране, если в момент выполне&ния макроса в системе отсутствует другая активная копия Word. Чтобы вывестиокно Word на экран, присвойте свойству Visible объекта Word.Applicationзначение True.
Ошибка компиляции: отсутствие библиотеки
Если объектная библиотека Word, на которую была создана ссылка, отсутY
ствует в системе, Excel не сможет откомпилировать макрос и отобразит сообY
щение об ошибке. В списке Available References (Доступные ссылки) отсутY
ствующая библиотека будет отмечена словом ‘‘MISSING’’ (‘‘Отсутствует’’),
как показано на рис. 16.3.
Рис. 16.3. Excel не сможет откомпилировать макрос, еслитребуемая объектная библиотека отсутствует в системе
Если в системе присутствует другая версия объектной библиотеки Word,
можно попытаться откомпилировать макрос, создав ссылку на эту библиотеY
ку. Различные версии библиотек Word содержат много одинаковых объектов.
Позднее связываниеПозднее связывание предполагает создание объектов Word до установки
ссылки на соответствующую объектную библиотеку. Это позволяет использоY
вать любую версию библиотеки Word, содержащую требуемые объекты, метоY
ды и свойства. К тому же, версию библиотеки Word можно определить в коде
макроса и создавать экземпляры только содержащихся в ней объектов.
Недостаток позднего связывания заключается в полной неосведомленноY
сти Excel об обращении к объектной модели Word. Помимо невозможности
Автоматизация Word Глава 16 443
отображения динамических подсказок, Excel не позволяет использовать
встроенные константы Word и не может проверить корректность создаваемых
ссылок. В результате этого все ошибки, связанные с использованием библиоY
теки Word, обнаруживаются во время выполнения программного кода.
Следующий макрос открывает существующий документ Word и выводит
его на экран.
Sub WordLateBinding()' Позднее связывание.
Dim wdApp As Object, wdDoc As Object Set wdApp = CreateObject("Word.Application") Set wdDoc = wdApp.Documents.Open(ThisWorkbook.Path & _"\Автоматизация Word.doc") wdApp.Visible = True Set wdApp = Nothing Set wdDoc = NothingEnd Sub
Переменная wdApp содержит ссылку на объект приложения (CreateObject("Word.Application")) и используется при создании переменной wdDoc,
ссылающейся на объект библиотеки Word.
На заметку Необходимость использования позднего связывания обусловлена выбором типапеременных wdApp и wdDoc (Object). Макрос не может создать требуемые ссыл&ки на объекты библиотеки Word до выполнения функции CreateObject.
Работа с объектамиВ следующих разделах рассматривается создание новых объектов, а также
обращение к уже существующим объектам.
Ключевое слово New
Ключевое слово New можно использовать для создания объекта приложеY
ния при раннем связывании (см. макрос WordEarlyBinding выше в этой
главе). Аналогичного результата позволяет добиться также функция Cre-ateObject, а вот функция GetObject предназначена для получения ссылки
на уже существующий объект. Использовать ключевое слово New при позднем
связывании нельзя.
Внимание Если окно созданного экземпляра Word не отображается на экране, откройте при&ложение Диспетчер задач Windows (Windows Task Manager) и проверьте наличиев памяти компьютера процесса WINWORD.EXE. Если процесс запущен, выполнитеследующую строку кода с помощью окна Immediate (Быстрое выполнение) ре&дактора Visual Basic: Word.Application.Visible = True
444 Часть II Автоматизация Excel
При наличии в памяти компьютера нескольких экземпляров процесса WIN-WORD.EXE выполняйте приведенную выше строку до тех пор, пока не доберетесьдо нужной копии Word, попутно закрывая окна “лишних” экземпляров.
Функция CreateObject
Функцию CreateObject можно использовать для создания нового экY
земпляра объекта как при позднем (см. макрос WordLateBinding выше в
этой главе), так и при раннем связывании. В качестве обязательного параметY
ра функции CreateObject необходимо передать имя приложения и тип созY
даваемого объекта (например, Word.Application).
Функция GetObject
Функция GetObject возвращает ссылку на уже существующий экземпляр
объекта. Если обнаружить указанный экземпляр объекта не удалось, генериY
руется сообщение об ошибке.
Первый параметр функции GetObject определяет полный путь к файлу,
содержащему требуемый объект. Второй параметр функции GetObject задаY
ет класс объекта. Несмотря на то что по отдельности каждый из параметров
является необязательным, пропуск первого из них автоматически означает
необходимость указания второго.
Sub UseGetObject() Dim wdDoc As Object Set wdDoc = GetObject(ThisWorkbook.Path & _"\Автоматизация Word.doc") wdDoc.Application.Visible = True Set wdDoc = NothingEnd Sub
Макрос UseGetObject открывает документ внутри существующего экземпY
ляра приложения Word и выводит окно последнего на экран. Поскольку переменY
ная wdDoc ссылается на объект документа, для вывода окна Word на экран исY
пользуется свойство Application объекта Document (wdDoc.Applicati-on.Visible = True).
На заметку Несмотря на установку значения свойства Visible объекта Application рав&ным True, макрос UseGetObject не делает приложение Word активным.
Следующий код пытается вставить диаграмму Excel в конец открытого доY
кумента Word, предварительно отключив обработку ошибок. Если на момент
выполнения кода ни один документ Word не был открыт, создается новый доY
кумент.
Sub IsWordOpen() Dim wdApp As Object ActiveChart.ChartArea.Copy
Автоматизация Word Глава 16 445
On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 With wdApp.Selection .EndKey Unit:=wdStory .TypeParagraph .PasteSpecial link:=False, DataType:=wdPasteOLEObject, _ Placement:=wdInLine, DisplayAsIcon:=False End With Set wdApp = NothingEnd Sub
Обработка ошибок отключается с помощью строки On Error ResumeNext. Ошибка возникает при попытке связать переменную wdApp с несущеY
ствующим объектом. В этом случае переменная wdApp не будет иметь значеY
ния, чем можно воспользоваться (If wdApp Is Nothing Then) для открыY
тия нового экземпляра Word, создания пустого документа и отображения окна
Word на экране. Чтобы открыть новый экземпляр Word, значение первого паY
раметра функции GetObject задается как "". Возвратиться к обычному реY
жиму обработки ошибок позволяет строка On Error GoTo 0.
Объекты WordПолучить первичное представление об объектной модели Word поможет
средство записи макросов Word. Помня обо всех недостатках средства записи
макросов Excel, рассматривавшегося в начале этой книги, обращайте внимаY
ние только на использованные в сгенерированном коде объекты, методы и
свойства.
Внимание Средство записи макросов Word накладывает некоторые ограничения на действияпользователя. В частности, перемещать курсор и выделять объекты разрешаетсятолько с помощью клавиатуры. Использование мыши для выполнения указанныхопераций не предусмотрено.
Ниже приведен код, сгенерированный средством записи макросов Word
при добавлении нового документа:
Documents.Add Template:="Normal", NewTemplate:=False, _DocumentType:=0
446 Часть II Автоматизация Excel
На самом деле, добавить новый документ Word можно с помощью гораздо
более простого и эффективного кода:
Documents.Add
Свойства Template, NewTemplate и DocumentType необязательны и
обычно используются для гарантированного задания значений параметров.
Чтобы выполнить приведенный выше код в Excel, необходимо создать
ссылку на объектную библиотеку Word. В следующих разделах рассматриваY
ются основные объекты Word. Для более подробного изучения объектной моY
дели Word обратитесь к диспетчеру объектов редактора Visual Basic.
Объект Document
Объект Word Document является эквивалентом объекта Excel Workbook.
Документ Word состоит из символов (свойство Characters объекта Docu-ment), слов (свойство Words объекта Document), предложений (свойство
Sentences объекта Document), абзацев (свойство Paragraphs объекта
Document), разделов (свойство Sections объекта Document), а также верхY
них (свойство Headers объекта Section) и нижних колонтитулов (свойство
Footers объекта Section). Свойства и методы объекта Document позволяY
ют создавать новые документы Word, закрывать существующие документы,
осуществлять печать, редактирование и многое другое.
Создание документа Word
Чтобы создать новый документ внутри существующего экземпляра Word,
воспользуйтесь методом Add (для создания нового документа с открытием экY
земпляра Word примените функцию CreateObject или GetObject, о чем
уже говорилось ранее в этой главе).
Sub NewDocument() Dim wdApp As Word.Application On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 Set wdApp = NothingEnd Sub
Макрос NewDocument создает новый документ Word на основе стандартY
ного шаблона. Ниже приведен пример создания нового документа Word на
основе шаблона Современная записка (Contemporary Memo).
wdApp.Documents.Add Template:="Современная записка.dot"
Автоматизация Word Глава 16 447
В качестве значения параметра Template следует указать либо полный
путь к файлу шаблона, либо имя файла одного из шаблонов, поставляющихся
вместе с Word.
Открытие и сохранение документа Word
Чтобы открыть существующий документ Word, воспользуйтесь методом
Open. Этот метод имеет несколько параметров, таких как ReadOnly и
AddtoRecentFiles. В результате выполнения следующего кода документ
Автоматизация Word.doc будет открыт в режиме только для чтения и не
будет добавлен в список ранее открывавшихся файлов:
wdApp.Documents.Open Filename:="C:\Автоматизация Word.doc", _ReadOnly:=True, AddtoRecentFiles:=False
Чтобы сохранить документ под его текущим именем, воспользуйтесь метоY
дом Save:
wdApp.Documents.Save
Чтобы сохранить документ под новым именем (в том числе новый докуY
мент), воспользуйтесь методом SaveAs:
wdApp.ActiveDocument.SaveAs "C:\Служебная записка.doc "
На заметку Метод SaveAs предполагает использование объекта ActiveDocument.
Закрытие документа Word
Чтобы закрыть определенный документ Word или все открытые докуменY
ты, воспользуйтесь методом Close. По умолчанию при попытке закрытия
документа с несохраненными изменениями выводится окно сообщения.
Чтобы закрыть все открытые документы без сохранения изменений, устаноY
вите значение параметра SaveChanges метода Close равным wdDoNot-SaveChanges:
wdApp.Documents.Close SaveChanges:=wdDoNotSaveChanges
Ниже приведен пример закрытия текущего документа:
wdApp.ActiveDocument.Close
и документа с указанным именем:
wdApp.Documents("Автоматизация Word.doc").Close
Печать документа Word
Чтобы отправить на печать документ Word или его часть, воспользуйтесь
методом PrintOut. Ниже приведен пример печати текущего документа со
стандартными параметрами печати:
wdApp.ActiveDocument.PrintOut
448 Часть II Автоматизация Excel
По умолчанию на печать выводится весь документ целиком. Чтобы напечаY
тать только определенный диапазон страниц, воспользуйтесь параметрами
Range и Pages метода PrintOut:
wdApp.ActiveDocument.PrintOut Range:=wdPrintRangeOfPages, Pages:="2"
Объект Selection
Объект Selection используется для представления выделенной области в
документе Word (слова, предложения, места вставки и т.п.). Тип выделенной
области хранится в свойстве Type объекта Selection (wdSelectionIP,
wdSelectionColumn, wdSelectionShape и т.п.).
Методы HomeKey и EndKey
Методы HomeKey и EndKey используются для изменения выделенной обY
ласти и эквивалентны нажатию клавиш <Home> и <End>, соответственно.
Каждый метод принимает 2 параметра: Unit и Extend. Параметр Unit опреY
деляет диапазон смещения или расширения выделенной области: начало
(метод HomeKey) или конец (метод EndKey) строки текста (wdLine), докуY
мента (wdStory), столбца таблицы (wdColumn) или строки таблицы (wdRow).
Параметр Extend определяет тип действия: смещение выделенной области
(wdMove) или ее расширение (wdExtend).
Следующий код перемещает курсор в начало документа:
wdApp.Selection.HomeKey Unit:=wdStory, Extend:=wdMove
В результате выполнения приведенной ниже строки кода будет выделен
фрагмент от текущего места вставки до конца документа:
wdApp.Selection.EndKey Unit:=wdStory, Extend:=wdExtend
Метод TypeText
Метод TypeText используется для вставки текста в документ Word. СпоY
соб вставки текста определяется значением различных параметров приложеY
ния, таких как параметр Overtype.
Sub InsertText() Dim wdApp As Word.Application Dim wdDoc As Document Dim wdSln As Selection On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0
Автоматизация Word Глава 16 449
Set wdDoc = wdApp.ActiveDocument Set wdSln = wdApp.Selection wdDoc.Application.Options.Overtype = False With wdSln If .Type = wdSelectionIP Then .TypeText ("Вставка текста в месте текущего _положения курсора.") ElseIf .Type = wdSelectionNormal Then If wdApp.Options.ReplaceSelection Then .Collapse Direction:=wdCollapseStart End If .TypeText ("Вставка текста перед выделенным блоком.") End If .TypeText vbCrLf End With Set wdApp = Nothing Set wdDoc = NothingEnd Sub
Объект Range
Объект Range используется для представления непрерывной области
(диапазона) в документе Word. Диапазон определяется позицией первого и
последнего входящего в него символа. Примером диапазона является место
вставки, фрагмент текста, а также весь документ Word вместе с непечатаемыY
ми символами, такими как символ пробела и символ абзаца.
Несмотря на кажущуюся схожесть, объект Range выгодно отличается от
объекта Selection. В частности, использование объекта Range позволяет
создавать более эффективный код за счет упрощенного способа обращения к
требуемым элементам документа Word.
Определение диапазона
Чтобы определить диапазон, укажите позицию первого и последнего вхоY
дящего в него символа:
Sub RangeText() Dim wdApp As Word.Application Dim wdDoc As Document Dim wdRng As Word.Range On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 InsertText Set wdDoc = wdApp.ActiveDocument
450 Часть II Автоматизация Excel
Set wdRng = wdDoc.Range(0, 22) wdRng.Select Set wdApp = Nothing Set wdDoc = Nothing Set wdRng = NothingEnd Sub
Результат выполнения макроса RangeText показан на рис. 16.4.
Рис. 16.4. Пример выделения фрагмента текста с помо&щью объекта Range
Диапазон, представленный объектом Range, включает 22 символа, в том
числе символы абзаца.
На заметку Выделение диапазона wdRng в макросе RangeText было предпринято для повы&шения наглядности рассматриваемого примера. Работать с диапазоном можно ибез его предварительного выделения.
Позиция первого символа в документе Word всегда равна нулю. Позиция
последнего символа в документе Word равна общему числу символов в доY
кументе.
Объект Range можно использовать для выделения абзацев. Следующий
макрос выделяет третий абзац документа Word, копирует его и помещает на
рабочий лист Excel как объект Word (рис. 16.5) и как обычный текст
(рис. 16.6).
Sub SelectSentence() Dim wdApp As Word.Application Dim wdRng As Word.Range On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 InsertText InsertText InsertText With wdApp.ActiveDocument If .Paragraphs.count >= 3 Then Set wdRng = .Paragraphs(3).Range
Автоматизация Word Глава 16 451
wdRng.Copy End If End With' Вставить скопированный абзац как объект Word. Worksheets("Объекты Word").Select Range("B22").Select ActiveSheet.PasteSpecial' Вставить скопированный абзац в ячейку B24 как обычный текст.' Обратите внимание, что перед вставкой абзаца нужно выделить' соответствующий диапазон рабочего листа Excel. Range("B24").Select ActiveSheet.Paste Set wdApp = Nothing Set wdRng = NothingEnd Sub
Рис. 16.5. Скопированный абзац помещается на рабочий лист Excel как объект Word
Рис. 16.6. Скопированный абзац помещается на ра&бочий лист Excel как обычный текст
Стилевое форматирование диапазона
Следующий макрос добавляет полужирное начертание к первому слову
всех абзацев документа Word.
Sub ChangeFormat() Dim wdApp As Word.Application Dim wdRng As Word.Range Dim count As Integer On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 InsertText InsertText InsertText With wdApp.ActiveDocument For count = 1 To .Paragraphs.count Set wdRng = .Paragraphs(count).Range With wdRng .Words(1).Font.Bold = True .Collapse
452 Часть II Автоматизация Excel
End With Next count End With Set wdApp = NothingEnd Sub
Результат выполнения макроса ChangeFormat показан на рис. 16.7.
Рис. 16.7. Макрос ChangeFormat добавляет полужир&ное начертание к первому слову всех абзацев доку&мента Word
Один из наиболее быстрых способов изменения форматирования абзаца
заключается в применении к нему другого стиля. Следующий макрос изменяY
ет стиль абзацев документа Word с Обычный на Заголовок 1.
Sub ChangeStyle() Dim wdApp As Word.Application Dim wdRng As Word.Range Dim count As Integer On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 InsertText InsertText InsertText With wdApp.ActiveDocument For count = 1 To .Paragraphs.count Set wdRng = .Paragraphs(count).Range With wdRng If .Style = "Обычный" Then .Style = "Заголовок 1" .Collapse End If End With Next count End With
Автоматизация Word Глава 16 453
Set wdApp = Nothing Set wdRng = NothingEnd Sub
Результат выполнения макроса ChangeStyle показан на рис. 16.8.
Рис. 16.8. Макрос ChangeStyle изменяет стиль абзацев документа сОбычный на Заголовок 1
Закладки
Закладки предназначены для упрощения навигации по документу Word.
Объект Bookmarks доступен в виде свойства объектов Document, Selec-tion и Range.
На заметку Закладки можно создать непосредственно в программном коде.
Закладка, назначенная некоторой позиции в документе Word, имеет
IYобразный вид. Чтобы отобразить закладки, выберите в меню Word команду
Сервис Параметры (Tools Options) и установите флажок Закладки(Bookmarks), расположенный во вкладке Вид (View) (рис. 16.9).
Следующий макрос создает закладки Кому, Копия, От и Тема, а затем поY
мещает после каждой из них соответствующий текст.
454 Часть II Автоматизация Excel
Рис. 16.9. Чтобы отобразить закладки, установите флажокЗакладки
Sub UseBookmarks() Dim myArray() Dim wdBkmk As String Dim wdApp As Word.Application Dim wdRng As Word.Range Dim wdSln As Selection
myArray = Array("Кому", "Копия", "От", "Тема")
On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 Set wdSln = wdApp.Selection With wdSln .TypeText "Кому: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Кому", wdRng .TypeText vbCrLf .TypeText "Копия: "
Автоматизация Word Глава 16 455
Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Копия", wdRng .TypeText vbCrLf .TypeText "От: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "От", wdRng .TypeText vbCrLf .TypeText "Тема: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Тема", wdRng End With
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(0)).Range wdRng.InsertBefore ("Билл Джелен")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(1)).Range wdRng.InsertBefore ("Трейси Сирстад")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(2)).Range wdRng.InsertBefore ("MrExcel")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(3)).Range wdRng.InsertBefore ("Продажи фруктов")
Set wdApp = Nothing Set wdRng = NothingEnd Sub
Результат выполнения макроса UseBookmarks показан на рис. 16.10.
Рис. 16.10. Закладки существенно упро&щают перемещение по документу Word
Закладки можно использовать не только для вставки текста, но и для
вставки других объектов, таких как диаграммы.
Sub CreateMemo() Dim myArray() Dim wdBkmk As String Dim wdApp As Word.Application Dim wdRng As Word.Range
myArray = Array("Кому", "Копия", "От", "Тема", "Диаграмма")
On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") End If If wdApp.ActiveDocument Is Nothing Then With wdApp
456 Часть II Автоматизация Excel
.Documents.Add .Visible = True End With End If On Error GoTo 0 Set wdSln = wdApp.Selection With wdSln .TypeText "Кому: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Кому", wdRng .TypeText vbCrLf .TypeText "Копия: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Копия", wdRng .TypeText vbCrLf .TypeText "От: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "От", wdRng .TypeText vbCrLf .TypeText "Тема: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Тема", wdRng .TypeText vbCrLf .TypeText "Диаграмма: " Set wdRng = wdApp.Selection.Range wdApp.ActiveDocument.Bookmarks.Add "Диаграмма", wdRng End With
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(0)).Range wdRng.InsertBefore ("Билл Джелен")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(1)).Range wdRng.InsertBefore ("Трейси Сирстад")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(2)).Range wdRng.InsertBefore ("MrExcel")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(3)).Range wdRng.InsertBefore ("Продажи овощей и фруктов")
Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(4)).Range ActiveSheet.ChartObjects("Chart 2").Copy wdRng.Select wdRng.Application.Selection.PasteAndFormat Type:=2
wdApp.Activate
Set wdApp = Nothing Set wdRng = NothingEnd Sub
Результат выполнения макроса CreateMemo показан на рис. 16.11.
Автоматизация Word Глава 16 457
Рис. 16.11. Закладки можно использовать для помещения в документ Word диаграммы
Практикум
Создание отчетов в Word с помощьюрасширенного фильтраВ главе 11, “Анализ данных с помощью расширенного фильтра”, рассматривалсямакрос, использующий расширенный фильтр для создания отчетов по каждомузаказчику и сохраняющий их в виде отдельных рабочих книг. Изменим условиезадачи, потребовав сохранения полученных отчетов в виде документов Word. Ни&же приведена последовательность действий, которые нужно выполнить для дос&тижения поставленной цели.1. Создайте новый документ Word. Добавьте в него закладки Заказчик и
Таблица, а также, при необходимости, дополнительный текст, который нужнопоместить в отчет. Сохраните документ в виде шаблона с именем SalesRe-port.dot.
2. Внесите изменения в макрос RunReportForEachCustomer (см. главу 11). По&сле применения расширенного фильтра для генерирования отчета в Excel соз&дайте документ Word на основе шаблона SalesReport.dot.
3. С помощью метода InsertBefore поместите имя заказчика в позицию за&кладки Заказчик в новом документе Word.
4. Поместите отчет в позицию закладки Таблица в новом документе Word. До&бавьте полужирное начертание к первой строке таблицы (строке заголовков).
Ниже приведен код макроса RunReportForEachCustomer со всеми необходи&мыми изменениями.Sub RunReportForEachCustomer() Dim IRange As Range Dim ORange As Range Dim CRange As Range Dim WBN As Workbook Dim WSN As Worksheet Dim WSO As Worksheet
458 Часть II Автоматизация Excel
Dim wdApp As Word.Application Dim wdDoc As Word.Document Dim wdRng As Word.Range
Application.ScreenUpdating = False
Set WSO = ActiveSheet
' Определение размера исходного диапазона данных. FinalRow = Cells(65536, 1).End(xlUp).Row NextCol = Cells(1, 255).End(xlToLeft).Column + 2
' Первый расширенный фильтр - создание' списка заказчиков в столбце J.' Определение целевого диапазона.' Копирование заголовка столбца A в ячейку J1. Range("A1").Copy Destination:=Cells(1, NextCol) Set ORange = Cells(1, NextCol)
' Определение исходного диапазона данных. Set IRange = Range("A1").Resize(FinalRow, NextCol - 2)
' Применение расширенного фильтра для отбора' уникальных значений из столбца A. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:="", CopyToRange:=ORange, Unique:=True FinalCust = Cells(65536, NextCol).End(xlUp).Row
' Цикл по списку заказчиков. For Each cell In Cells(2, NextCol).Resize(FinalCust - 1, 1) ThisCust = cell.Value
' Определение условия отбора. Cells(1, NextCol + 2).Value = Range("A1").Value Cells(2, NextCol + 2).Value = ThisCust Set CRange = Cells(1, NextCol + 2).Resize(2, 1)
' Определение целевого диапазона данных.' В целевой диапазон войдут столбцы B (Код),' C (Розничная цена), D (Продано) и E (Остаток). Cells(1, NextCol + 4).Resize(1, 4).Value = _Array(Cells(1, 2), Cells(1, 3), Cells(1, 4), Cells(1, 5)) Set ORange = Cells(1, NextCol + 4).Resize(1, 4)
' Второй расширенный фильтр - отбор строк,' удовлетворяющих заданному условию. IRange.AdvancedFilter Action:=xlFilterCopy, _CriteriaRange:=CRange, CopyToRange:=ORange
' Добавить итоговую строку. TotalRow = WSO.Cells(65536, _ORange.Columns(1).Column).End(xlUp).Row + 1 WSO.Cells(TotalRow, ORange.Columns(1).Column).Value = _"Всего" ' В англоязычной версии Excel: 'WSO.Cells(TotalRow, _ORange.Columns(2).Column).FormulaR1C1 = "=SUM(R2C:R[-1]C)"
Автоматизация Word Глава 16 459
WSO.Cells(TotalRow, _ORange.Columns(2).Column).FormulaR1C1Local = "=СУММ(R2C:R[-1]C)" ' В англоязычной версии Excel: 'WSO.Cells(TotalRow, _ORange.Columns(4).Column).FormulaR1C1 = "=SUM(R2C:R[-1]C)" WSO.Cells(TotalRow, _ORange.Columns(4).Column).FormulaR1C1Local = "=СУММ(R2C:R[-1]C)"
' Создание нового документа Word. On Error Resume Next
Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", _"Word.Application") Set wdDoc = wdApp.Documents.Add(Template:= _ThisWorkbook.Path & "\SalesTemplate.dot") wdDoc.Activate
' Создание заголовка отчета. wdDoc.Bookmarks("Заказчик").Range.InsertBefore (ThisCust)
' Копирование отчета с рабочего листа Excel в документ Word. WSO.Cells(1, NextCol + 4).CurrentRegion.Copy Set wdRng =wdApp.ActiveDocument.Bookmarks("Таблица").Range wdRng.Select
' Форматирование таблицы. With wdDoc.Application.Selection .Paste With wdDoc.Application.ActiveDocument.Tables(1) .Rows.Alignment = wdAlignRowCenter With .Rows(1) .HeadingFormat = True .Select wdApp.Selection.Font.Bold = True End With End With .HomeKey Unit:=wdStory, Extend:=Move End With
' Сохранение документа Word с последующим закрытием. wdDoc.SaveAs ThisWorkbook.Path & "\" & ThisCust & ".doc" wdDoc.Close savechanges:=False WSO.Select
Set wdApp = Nothing Set wdDoc = Nothing Set wdRng = Nothing
' Очистить область вставки результата' применения расширенных фильтров. Cells(1, NextCol + 2).Resize(1, 10).EntireColumn.Clear Next cell
Application.ScreenUpdating = True
460 Часть II Автоматизация Excel
Cells(1, NextCol).EntireColumn.Clear MsgBox FinalCust - 1 & " отчетов были успешно созданы!!"
Set wdRng = NothingEnd Sub
Результат выполнения макроса RunReportForEachCustomer показан нарис. 16.12.
Рис. 16.12. Пример создания отчета в Microsoft Word
Следующий шагСледующая глава посвящена многомерным массивам. Один из наиболее
эффективных способов ускорения обработки данных на рабочем листе заклюY
чается в их считывании в многомерный массив, проведении необходимых выY
числений и копировании полученных результатов обратно на рабочий лист.
IIIЧасть III
Удивительныевозможности Visual Basicfor Applications
17. Массивы ................................................................................463
18. Работа с текстовыми файлами ....................................... 473
19. Использование Microsoft Access .....................................489
20. Создание пользовательских объектов, типови коллекций ........................................................................ 505
21. Пользовательские формы — профессиональLный подход.......................................................................... 525
22. Интерфейс прикладного программирования(API) Windows ..................................................................... 547
23. Обработка ошибок .............................................................561
24. Создание пользовательских меню и панелейинструментов...................................................................... 575
25. Надстройки.......................................................................... 593
26. Практикум: создание приложения Excel“с нуля”................................................................................. 603
Объявление массива .................. 463Заполнение массива .................. 464Манипулированиеэлементами массива .................. 466Еще одно преимуществомассивов.........................................467Динамические массивы ............ 469Передача массива в качествепараметра..................................... 470Следующий шаг............................ 471
17Глава 17
Ìàññèâû
Массив YYYY это переменная специY
ального типа, которая может хранить
более одного элемента данных. Без
использования массива для запомиY
нания имени и адреса заказчика поY
требуется создать две обычные переY
менные. В то же время, для хранения
целого списка, состоящего из имен и
адресов сотен заказчиков, достаточно
одного массива.
Объявление массиваЧтобы объявить массив, укажите
число его элементов в скобках после
имени переменной, как показано
ниже:
Dim myArray(2)
Приведенный выше код создает
массив myArray, состоящий из трех
элементов. ДаYда, именно из трех,
потому что по умолчанию первый
элемент массива имеет индекс 0:
myArray(0) = 10myArray(1) = 20myArray(2) = 30
Чтобы назначить первому элеменY
ту массива индекс 1, воспользуйтесь
выражением Option Base 1. ВыY
ражение Option Base размещается
в разделе объявлений модуля:
Option Base 1Dim myArray(2)
Теперь массив myArray будет соY
стоять из двух элементов.
При создании массива можно заY
дать как верхнюю, так и нижнюю его
границу:
464 Часть III Удивительные возможности Visual Basic for Applications
Dim myArray(1 to 10)Dim BigArray(100 to 200)
Узнать верхнюю и нижнюю границу массива можно с помощью функций
UBound и LBound, соответственно. Выражение Dim myArray(2) определяет
только верхнюю границу массива, в то время как выражение Dim myArray(1 to 10) — как верхнюю (10), так и нижнюю (1) границы.
Многомерные массивыРассмотренные выше массивы являются одномерными массивами, положеY
ние элементов которых определяется всего одним числом. Одномерный масY
сив можно представить в виде строки данных, единственной значимой коорY
динатой которой является номер столбца. Ниже приведен пример обращения
ко второму элементу одномерного массива (Option Base 0):
myArray(1)
Во многих ситуациях для представления данных используются многомерныемассивы, положение элементов которых определяется несколькими числами
(например, двумерный массив можно сравнить с набором строк, значимыми
координатами которого являются номер столбца и номер строки).
На заметку Двумерный массив часто называют матрицей, что по своей сути близко к понятиюэлектронной таблицы. Действительно, объект Cells определяет положение эле&мента таблицы, т.е. номер его строки и столбца.
Ниже приведен пример создания двумерного массива, состоящего из
10 строк и 20 столбцов:
Dim myArray(1 to 10, 1 to 20)
Следующий код заполняет элементы первой строки массива myArray:
myArray(1, 1) = 10myArray(1, 2) = 20
Аналогичным образом заполняются элементы второй строки массива:
myArray(2, 1) = 20myArray(2, 2) = 40
Конечно же, существуют и более эффективные способы заполнения масY
сива, которые рассматриваются далее в этой главе.
Заполнение массиваСамый простой способ заполнения массива был продемонстрирован в
предыдущем разделе. Существуют гораздо более эффективные методы выполY
нения аналогичной операции, один из которых приведен ниже:
Option Base 1
Sub ColumnHeaders()
Массивы Глава 17 465
Dim myArray As Variant Dim myCount As Integer' Заполнить массив. myArray = Array("Имя", "Адрес", "Телефон", "Адрес эл. почты")' Скопировать содержимое массива на рабочий лист. With Worksheets("Заполнение массива") For myCount = 1 To UBound(myArray) .Cells(1, myCount).Value = myArray(myCount) Next myCount End WithEnd Sub
Результат выполнения макроса ColumnHeaders показан на рис. 17.1.
Обратите внимание, что в приведенном выше коде для создания массива
была использована переменная myArray типа Variant, которая может храY
нить информацию любого рода. Переменная myArray принимает свойства
массива после присвоения ей соответствующего значения:
myArray = Array("Имя", "Адрес", "Телефон", "Адрес эл. почты")
Ниже приведен пример заполнения массива данными, взятыми с рабочего
листа Excel (см. рис. 17.2):
Dim myArray As VariantmyArray = Worksheets("Sheet1").Range("B2:C17")
Рис. 17.1. Пример использования массивадля создания строки заголовков
Рис. 17.2. Массив можно за&полнить данными, взятыми срабочего листа Excel
Следующий макрос заполняет массив данными, взятыми с рабочего листа
Excel через одну строку:
Sub EveryOtherRow() Dim myArray(1 To 8, 1 To 2) Dim i As Integer, j As Integer, myCount As Integer
' Заполнить массив данными, взятыми' с рабочего листа Excel через одну строку. For i = 1 To 8
466 Часть III Удивительные возможности Visual Basic for Applications
For j = 1 To 2 myArray(i, j) = _Worksheets("Через один").Cells(i * 2, j + 1).Value Next j Next i
' Скопировать содержимое массива на рабочий лист. For myCount = LBound(myArray) To UBound(myArray) Worksheets("Через один").Cells(myCount * 2, 4) = _WorksheetFunction.Sum(myArray(myCount, 1), myArray(myCount, 2)) Next myCountEnd Sub
Результат выполнения макроса EveryOtherRow показан на рис. 17.3.
Рис. 17.3. Макрос EveryOtherRowподсчитывает сумму данных, взя&тых с рабочего листа Excel через од&ну строку
Манипулирование элементами массиваОдно из основных предназначений массива заключается в манипулироваY
нии его элементами. Следующий макрос вычисляет наибольший элемент масY
сива, заполненного данными, взятыми с рабочего листа Excel.
Sub QuickFillMax() Dim myArray As Variant myArray = Worksheets("Через один").Range("B2:C17") MsgBox "Максимальное целое число в диапазоне B2:C17 равно " _& WorksheetFunction.Max(myArray)End Sub
Результат выполнения макроса QuickFillMax показан на рис. 17.4.
Зачастую результат манипулирования элементами массива помещается на
рабочий лист Excel. Следующий макрос вычисляет среднее значение каждой
строки массива и помещает полученный результат на рабочий лист.
Массивы Глава 17 467
Рис. 17.4. Макрос QuickFillMax вычисляетнаибольшее значение в диапазоне B2:C17
Sub QuickFillAverage() Dim myArray As Variant Dim myCount As Integer
myArray = Worksheets("Через один").Range("B2:C17") For myCount = LBound(myArray) To UBound(myArray) Worksheets("Через один").Cells(myCount + 1, 5).Value = _WorksheetFunction.Average(myArray(myCount, 1), myArray(myCount, 2)) Next myCountEnd Sub
На заметку При помещении данных на рабочий лист Excel в качестве адреса строки использу&ется значение myCount + 1. Это обусловлено тем, что нижняя граница массива(LBound) равна 1 (Option Base 1), а данные на рабочем листе размещаются на&чиная со второй строки.
Результат выполнения макроса QuickFillAverage показан на рис. 17.5.
Еще одно преимущество массивовНеужели все преимущество массивов сводится к упрощению манипулироY
вания данными рабочего листа? Конечно же, нет. Основная выгода, получаеY
мая от использования массивов, заключается в существенном повышении ско*рости выполнения программного кода.
468 Часть III Удивительные возможности Visual Basic for Applications
Рис. 17.5. Макрос QuickFillAverage вы&числяет среднее значение строк массива ипомещает полученные результаты на рабо&чий лист Excel
Следующий макрос вычисляет среднее значение столбцов B и C без исY
пользования массива.
Sub SlowAverage() Dim myCount As Integer, LastRow As Integer LastRow = Worksheets("Через один").Range("A65536").End(xlUp).Row For myCount = 2 To LastRow With Worksheets("Через один") .Cells(myCount, 5).Value = _WorksheetFunction.Average(.Cells(myCount, 2), .Cells(myCount, 3)) End With Next myCountEnd Sub
Макрос SlowAverage обрабатывает каждую строку в области данных раY
бочего листа, вычисляя среднее столбцов B и C и помещая полученный реY
зультат в столбец E. Не проще ли работать с данными как с единым целым,
обращаясь к ячейкам рабочего листа только для заполнения массива и помеY
щения на рабочий лист полученного результата?
При заполнении массива рекомендуется использовать именованный диаY
пазон ячеек, так как это позволяет создавать более универсальный код.
Ниже приведены примеры заполнения массива с помощью обычного и
именованного диапазона ячеек.
myArray = Range("B2:C17")myArray = Range("myData")
Совет Присвоение переменной типа Variant содержимого столбца рабочего листа Ex&cel приведет к созданию двумерного массива, для обращения к элементам кото&рого необходимо указывать номер строки и номер столбца.
Массивы Глава 17 469
Чтобы упростить работу с подобным массивом, транспонируйте столбец с помо&щью функции Transpose, превратив его тем самым в строку. Присвоение пере&менной типа Variant содержимого строки рабочего листа Excel приведет к соз&данию одномерного массива, что наглядно демонстрирует следующий код. Sub TransposeArray() Dim myArray As Variant
myArray = WorksheetFunction.Transpose(Range("myTran"))
' Узнать значение 5-го элемента массива. MsgBox "5-й элемент массива равен " & myArray(5)End Sub
Результат выполнения макроса TransposeArray показан на рис. 17.6.
Рис. 17.6. Чтобы создать одномерный массив,
преобразуйте столбец в строку с помощьюфункции Transpose
Динамические массивыОчень часто размер массива заранее неизвестен. Теоретически можно созY
дать массив очень большого размера ‘‘на все случаи жизни’’, однако это решение
приведет к напрасной трате ресурсов и не гарантирует 100%Yго результата. ВыY
ходом из подобной ситуации является использование динамического массива.
Объявление динамического массива аналогично объявлению обычного
массива без указания размера последнего:
Dim myArray()
470 Часть III Удивительные возможности Visual Basic for Applications
Когда размер динамического массива станет известен, инициализируйте
его с помощью выражения ReDim:
Sub MySheets() Dim myArray() As String Dim myCount As Integer, NumShts As Integer
NumShts = ActiveWorkbook.Worksheets.Count' Инициализация динамического массива. ReDim myArray(1 To NumShts) For myCount = 1 To NumShts myArray(myCount) = ActiveWorkbook.Sheets(myCount).Name Next myCount Worksheets("Через один").Range("J33").Resize(, _NumShts).Value = myArray
End Sub
Повторная инициализация массива с помощью выражения ReDim привоY
дит к полному уничтожению его предыдущего содержимого. Чтобы предотY
вратить потерю текущих значений элементов массива, воспользуйтесь ключеY
вым словом Preserve. Следующий макрос создает массив, состоящий из
имен файлов рабочих книг Excel, находящихся в заданной папке.
Sub XLFiles() Dim FName As String Dim arNames() As String Dim myCount As Integer
FName = Dir(ThisWorkbook.Path & "\*.xls") Do Until FName = "" myCount = myCount + 1 ReDim Preserve arNames(1 To myCount) arNames(myCount) = FName FName = Dir Loop On Error Resume Next Worksheets("Через один").Range("J38").Resize(, _myCount).Value = arNamesEnd Sub
Внимание При работе с большими объемами данных использование ключевого слова Pre-serve внутри цикла приводит к замедлению выполнения программного кода. Ес&ли это возможно, постарайтесь определить размер динамического массива довхождения в цикл.
Передача массива в качестве параметраМассив можно передавать в качестве параметра, подобно обычной переY
менной. Это повышает эффективность и читабельность кода. Следующий
макрос передает массив myArray в качестве параметра функции Region-Sales, которая подсчитывает сумму элементов массива и возвращает полуY
ченный результат.
Массивы Глава 17 471
Sub PassAnArray() Dim myArray() As Variant Dim myRegion As String
myArray = Range("mySalesData") myRegion = InputBox("Введите регион - Запад, Центр или Восток") MsgBox "Продажи в регионе " & myRegion & " составили " &Format(RegionSales(myArray, _ myRegion), "$#,#00.00 ")End Sub
Function RegionSales(ByRef BigArray As Variant, _sRegion As String) As Long Dim myCount As Integer
RegionSales = 0 For myCount = LBound(BigArray) To UBound(BigArray) If BigArray(myCount, 1) = sRegion Then RegionSales = BigArray(myCount, _4) * BigArray(myCount, 3) + RegionSales End If Next myCountEnd Function
Результат выполнения макроса PassAnArray показан на рис. 17.7.
Рис. 17.7. Массив можно передавать в качествепараметра функции или процедуре
Следующий шагМассив YYYY это переменная специального типа, которая может хранить неY
сколько элементов данных. Основное предназначение массива заключается в
упрощении обработки данных и повышении скорости выполнения проY
граммного кода. Следующая глава посвящена работе с текстовыми файлами.
Экспортирование данных в текстовый файл может понадобиться для передачи
информации в другую систему или создания HTMLYфайла.
Импорт данных из текстовогофайла ..............................................473Экспорт данных в текстовыйфайл ............................................... 486Следующий шаг........................... 487
18Глава 18
Ðàáîòàñ òåêñòîâûìèôàéëàìè
Прежде чем формат XML окончаY
тельно утвердится в качестве станY
дартного формата файлов, некоторое
время наряду с форматом XML шиY
рокое распространение будут иметь
форматы CSV и TXT.
В этой главе рассматривается имY
порт и экспорт текстовых файлов поY
средством VBA. Экспорт данных в
текстовый файл может понадобиться
для передачи информации в другую
систему или создания HTMLYфайла.
Импорт данных изтекстового файла
Рассмотрим три сценария имY
порта данных из текстовых файлов.
Если в файле содержится менее
65 536 записей, его можно импорY
тировать с помощью метода Work-books.OpenText. Если в файле
содержится более 65 536 записей, но
менее 98 304 записей, его можно имY
портировать с помощью двух вызовов
метода Workbooks.OpenText. Если
же в файле содержится более
98 304 записей, его придется имY
портировать путем последовательY
ного считывания строк.
474 Часть III Удивительные возможности Visual Basic for Applications
Импорт текстовых файлов, содержащих менее65 536 записей
Существует два основных формата текстовых файлов. Первый формат
предполагает разделение полей каждой записи с помощью некоторого симвоY
ла, например, запятой, точки с запятой, знака табуляции и т.п. Второй формат
подразумевает наличие фиксированной ширины полей каждой записи.
Excel одинаково успешно справляется с файлами обоих форматов с помоY
щью метода OpenText. При разработке программного кода рекомендуется
записать макрос и использовать уже готовые фрагменты кода.
Импорт текстового файла с полями фиксированной ширины
На рис. 18.1 показан пример текстового файла с полями фиксированной
ширины.
Рис. 18.1. При открытии текстового файла с полями фиксированной ши&рины необходимо указать точный размер полей
При открытии подобного файла необходимо указать точный размер его
полей. Чтобы создать соответствующий программный код, воспользуемся
средством записи макросов.
Начните запись макроса, выбрав в меню Excel команду Сервис МакросНачать запись (Tools Macros Record New Macro). Выберите команду ФайлОткрыть (File Open) и откройте нужный файл с помощью диалогового окна
Открытие документа (Open).
Работа с текстовыми файлами Глава 18 475
В диалоговом окне Мастер текстов (импорт) — шаг 1 из 3 (Text Import
Wizard YYYY Step 1 of 3) установите переключатель Фиксированной ширины(Fixed width) и щелкните на кнопке Далее (Next).
На рис. 18.2 показан результат попытки автоматического определения шиY
рины полей текстового файла.
Рис. 18.2. Результат попытки автоматического определения ширины по&лей текстового файла. Очевидно, что Excel затрудняется определитьширину соприкасающихся полей
Excel затрудняется определить ширину соприкасающихся полей, таких как
Дата и Заказчик.
Добавьте требуемые линии, обозначающие конец поля, в области Образецразбора данных (Data preview) диалогового окна Мастер текстов (импорт) —шаг 2 из 3 (Text Import Wizard YYYY Step 2 of 3). Чтобы добавить линию, щелкниY
те в нужной позиции в области Образец разбора данных. Чтобы переместить
линию, щелкните на ней и перетащите в требуемое место. Чтобы удалить лиY
нию, дважды щелкните на ней. Конечный результат определения ширины поY
лей текстового файла показан на рис. 18.3.
Обратите внимание на линейку, расположенную над областью Образецразбора данных. Каждое ее деление соответствует одному символу в текстоY
вом файле. К примеру, поле Заказчик начинается с 25Yй позиции и имеет
длину 11 символов.
По умолчанию Excel предполагает, что все поля текстового файла имеют
формат Общий (General). Измените формат полей, требующих особой обраY
ботки. Для этого щелкните на столбце и выберите требуемый формат с помоY
щью области Формат данных столбца (Column data format) диалогового окна
Мастер текстов (импорт) — шаг 3 из 3 (Text Import Wizard YYYY Step 3 of 3), как
показано на рис. 18.4.
476 Часть III Удивительные возможности Visual Basic for Applications
Рис. 18.3. Образец разбора данных после добавления двух линий, обо&значающих конец поля, и перемещения линии, разграничивающей по&ля Товар и Дата
Рис. 18.4. Измените тип столбца Дата на МДГ (MDY). Откажитесь отимпорта столбцов С-ть и Пр-ль, изменив их тип на Пропустить(Skip). Чтобы определить разделитель целой и дробной части, а такжеразделитель разрядов, щелкните на кнопке Подробнее (Advanced)
Например, чтобы изменить тип столбца Дата, щелкните на нем и установите
переключатель Дата (Date) в области Формат данных столбца. Укажите формат
даты (например, ‘‘деньYYмесяцYYгод’’ или ‘‘месяцYYденьYYгод’’) с помощью раскрыY
вающегося списка, расположенного справа от переключателя Дата.
Чтобы отказаться от импорта определенного столбца, щелкните на нем и
установите переключатель Пропустить столбец (Do not import column (skip)).
Работа с текстовыми файлами Глава 18 477
Пропуск столбцов может пригодиться при импортировании текстового файY
ла, содержащего нежелательную для разглашения информацию (например,
себестоимость товара и прибыль, получаемая от его продажи). Иногда текY
стовый файл с полями фиксированной ширины содержит также символыY
разделители. Установите переключатель Пропустить столбец для всех столбY
цов, содержащих символыYразделители, как показано на рис. 18.5.
Рис. 18.5. Переключатель Пропустить столбец рекомендуется устано&вить для всех столбцов текстового файла с полями фиксированной ши&рины, содержащих символы&разделители
Полям, содержащим алфавитные символы, можно назначить тип Общий.
Назначьте тип Текст (Text) числовым полям, значения которых необходимо
импортировать как текст. Примером таких полей является поле почтового инY
декса и поле номера банковского счета (оба поля могут содержать значения с
ведущими нулями, например, 01234).
Внимание Excel воспринимает формулу, введенную в столбец текстового формата, как обыч&ную строку. Чтобы ввести формулу, формат столбца придется изменить на Общий(General).
Ниже приведен код импорта текстового файла с полями фиксированной
ширины, сгенерированный средством записи макросов.
Workbooks.OpenText Filename:="sales.prn", Origin:=1251, _StartRow:=1, DataType:=xlFixedWidth, FieldInfo:=Array( _Array(0, 2), Array(8, 1), Array(17, 3), Array(25, 1), _Array(36, 1), Array(46, 1), Array(56, 9), Array(61, 9)), _TrailingMinusNumbers:=True
478 Часть III Удивительные возможности Visual Basic for Applications
Значение параметра FieldInfo представляет массив двухэлементных
массивов, определяющих позицию первого символа поля (с отсчетом от нуля)
и его тип.
Тип поля представлен числом, соответствующим одной из констант Excel
xlColumnDataType (табл. 18.1). Например, массив Array(0, 1) определяY
ет поле общего формата, отстающее от начала строки на 0 символов (поле
Регион), массив Array(8, 1) — поле общего формата, отстающее от начала
строки на 8 символов (поле Товар), а массив Array(17, 3) — поле даты в
формате ‘‘месяцYYденьYYгод’’, отстающее от начала строки на 17 символов (поле
Дата).
Таблица 18.1. Значения констант xlColumnDataType
Значение Константа Тип данных
1 xlGeneralFormat Общий
2 xlTextFormat Текстовый
3 xlMDYFormat Дата в формате ‘‘месяцYYденьYYгод’’
4 xlDMYFormat Дата в формате ‘‘деньYYмесяцYYгод’’
5 xlYMDFormat Дата в формате ‘‘годYYмесяцYYдень’’
6 xlMYDFormat Дата в формате ‘‘месяцYYгодYYдень ’’
7 xlDYMFormat Дата в формате ‘‘деньYYгодYYмесяц’’
8 xlYDMFormat Дата в формате ‘‘годYYденьYYмесяц’’
9 xlSkipColumn Пропуск столбца
10 xlEMDFormat Дата в тайском летоисчислении
Ввиду относительной сложности кодирования параметра FieldInfo реY
комендуется записать макрос и скопировать автоматически сгенерированный
фрагмент кода.
Внимание Впервые параметр TrailingMinusNumbers был представлен в Excel 2002. Не ис&пользуйте его в макросах, которые могут выполняться в более ранних версиях Ex&cel, например, Excel 97 или Excel 2000, так как это приведет к возникновениюошибки компиляции. Отсутствие параметра TrailingMinusNumbers не скажетсяна результате выполнения макроса в новых версиях Excel.
Импорт текстового файла с символамиLразделителями
На рис. 18.6 показан текстовый файл с разделителямиYзапятыми.
При открытии подобного файла в Excel укажите используемый символY
разделитель и, при необходимости, способ обработки полей. В рассматриваеY
Работа с текстовыми файлами Глава 18 479
мом примере значение третьего поля должно быть интерпретировано как дата
в формате ‘‘месяцYYденьYYгод’’.
Рис. 18.6. При открытии текстового файла с символами&разделителямиукажите используемый символ&разделитель (в данном случае это запя&тая) и, при необходимости, способ обработки полей
Внимание При открытии текстового файла с расширением .csv, содержащего разделители&запятые, средство записи макросов Excel создаст код, вызывающий метод Work-books.Open. Чтобы иметь возможность указать способ обработки полей, изме&ните расширение файла на .txt.
Начните запись макроса, выбрав в меню Excel команду Сервис МакросНачать запись (Tools Macros Record New Macro). Выберите команду
Файл Открыть (File Open) и откройте нужный файл с помощью диалогоY
вого окна Открытие документа (Open).
В диалоговом окне Мастер текстов (импорт) — шаг 1 из 3 (Text Import
Wizard YYYY Step 1 of 3) установите переключатель С разделителями (Delimited)
и щелкните на кнопке Далее (Next).
Первоначальный результат автоматического разбора данных, показанный
в области Образец разбора данных (Data preview) диалогового окна Мастертекстов (импорт) — шаг 2 из 3 (Text Import Wizard YYYY Step 2 of 3), будет выY
глядеть крайне непривлекательно. Это вызвано тем, что Excel по умолчанию
использует в качестве символаYразделителя знак табуляции (рис. 18.7).
480 Часть III Удивительные возможности Visual Basic for Applications
Рис. 18.7. Результат первоначального разбора данных выглядит как чья&то неудавшаяся шутка. А все из&за того, что Excel по умолчанию исполь&зует в качестве символа&разделителя знак табуляции
Сбросьте флажок Знак табуляции (Tab) и установите флажок, соответствуюY
щий требуемому символуYразделителю (в данном случае это флажок Запятая(Comma)). Результат повторного разбора данных показан на рис. 18.8.
Рис. 18.8. Результат повторного разбора данных после изменения ис&пользуемого символа&разделителя не вызывает никаких нареканий.В целом, разбор текстового файла с символами&разделителями требуетвыполнения меньшего количества действий, чем разбор текстовогофайла с полями фиксированной ширины
Задайте формат полей, требующих особой обработки, с помощью диалогоY
вого окна Мастер текстов (импорт) — шаг 3 из 3 (Text Import Wizard YYYY Step 3
Работа с текстовыми файлами Глава 18 481
of 3). Ниже приведен код импорта текстового файла с символамиY
разделителями, сгенерированный средством записи макросов.
Workbooks.OpenText Filename:="sales.csv.txt", Origin:=1251, _StartRow:=1, DataType:=xlDelimited, TextQualifier:= _xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _Semicolon:=False, Comma:=True, Space:=False, Other:=False, _FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 3), _Array(4, 1), Array(5, 1), Array(6, 1), Array(7, 1), _Array(8, 1)), TrailingMinusNumbers:=True
Несмотря на то что этот код длиннее кода импорта текстового файла с поY
лями фиксированной ширины, он менее сложен для восприятия. Параметр
FieldInfo представляет массив двухэлементных массивов, определяющих
порядковый номер поля (с отсчетом от единицы) и его тип (см. табл. 18.1).
Например, массив Array(2, 1) определяет второе поле как поле общего
формата, а массив Array(3, 3) — третье поле как поле даты в формате
‘‘месяцYYденьYYгод’’. Длина кода обусловлена явным заданием значений всех
параметров, определяющих символыYразделители. Поскольку по умолчанию
значение этих параметров равно False, код можно оптимизировать, как поY
казано ниже:
Workbooks.OpenText Filename:="sales.csv.txt", Origin:=1251, _StartRow:=1, DataType:=xlDelimited, Comma:=True, _FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 3), _Array(4, 1), Array(5, 1), Array(6, 1), Array(7, 1), Array(8, 1))
Чтобы сделать код более удобочитаемым, числовые значения, определяюY
щие тип поля, можно заменить соответствующими константами:
Workbooks.OpenText Filename:="sales.csv.txt", Origin:=1251, _StartRow:=1, DataType:=xlDelimited, Comma:=True, _FieldInfo:=Array(Array(1, xlGeneralFormat), Array(2, _xlGeneralFormat), Array(3, xlMDYFormat), Array(4, _xlGeneralFormat), Array(5, xlGeneralFormat), Array(6, _xlGeneralFormat), Array(7, xlGeneralFormat), Array(8, _xlGeneralFormat))
Внимание Значения параметров, определяющих символы&разделители, остаются действи&тельными на протяжении всего сеанса работы с Excel. При вставке текстовых дан&ных, содержащихся в буфере обмена, Excel осуществит их разбор в соответствиисо значениями параметров, заданными во время последнего вызова методаOpenText. К примеру, название фирмы заказчика “ABC, Inc.” будет разбито надве части (“ABC” и “Inc.”) и помещено в два соседних столбца, если в качествесимвола&разделителя использовалась запятая.
Excel позволяет использовать любой символYразделитель, а не только симY
вол табуляции, запятую, точку с запятой и пробел. Чтобы указать на необхоY
димость использования в качестве символаYразделителя знака перенаправлеY
ния (|), установите значение параметра Other метода OpenText равным
True, а значение параметра OtherChar равным "|", как показано далее:
482 Часть III Удивительные возможности Visual Basic for Applications
Workbooks.OpenText Filename:="sales.csv.txt", Origin:=1251, _StartRow:=1, DataType:=xlDelimited, Other:=True, OtherChar:="|" _FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 3), _Array(4, 1), Array(5, 1), Array(6, 1), Array(7, 1), Array(8, 1))
Импорт текстовых файлов, содержащих более65 536 записей
Импорт файла, содержащего более 65 536 записей, с помощью мастера текY
стов завершится выдачей сообщения об ошибке Файл загружен неполностью (File not loaded completely) и загрузкой первых 65 536 строк.
Импорт того же файла с помощью метода Workbooks.OpenText заверY
шится загрузкой первых 65 536 строк без уведомления о внештатной ситуации.
Проверьте содержимое ячейки A65536 после импорта файла. Если эта ячейка
не пуста, вероятно, файл был загружен некорректно.
Импорт текстового файла, содержащего не более 98 304 записей
На самом деле, Excel позволяет импортировать текстовый файл, содержаY
щий более 65 536 записей. В диалоговом окне Мастер текстов (импорт) —шаг 1 из 3 (Text Import Wizard YYYY Step 1 of 3) есть счетчик Начать импорт состроки (Start Import At Row), являющийся аналогом параметра StartRow меY
тода OpenText. Казалось бы, достаточно присвоить параметру StartRowзначение 65 537 и продолжить импорт файла. Как бы не так! Попытка присвоY
ить параметру StartRow значение, превышающее 32 767, завершится ошибY
кой времени выполнения 1004. Дело в том, что в ранних версиях Excel для
хранения значения параметра StartRow использовалась переменная типа
Integer, максимально допустимое значение которой составляло 32 767.
Складывается впечатление, что Microsoft не проводила генеральную ревизию
кода импорта текстовых файлов со времен, когда рабочий лист Excel был споY
собен вмещать чуть более 16 000 строк!
Рассмотрим задачу импорта текстового файла, содержащего сведения о
складских запасах 35 магазинов розничной торговли. Известно, что перечень
наименований товаров в каждом из магазинов не превышает 2000 позиций.
Следовательно, максимальное число записей импортируемого файла составY
ляет 70 000. Ниже приведен код импорта текстового файла, содержащего не
более 98 304 записей.
Sub ImportFile() Dim WBO As Workbook Dim WBC As Workbook
Set WBO = ActiveWorkbook ChDir ThisWorkbook.Path ThisFile = "inventory.txt" Workbooks.OpenText Filename:=ThisFile Set WBC = ActiveWorkbook WBO.Worksheets("Данные").Cells.Clear Cells.Copy WBO.Worksheets("Данные").Range("A1")
Работа с текстовыми файлами Глава 18 483
WBC.Close False
If WBO.Worksheets("Данные").Cells(65536, 1).Value <> "" Then' Файл содержит более 65536 записей.' Импортировать заново, начиная со строки 32767. Workbooks.OpenText Filename:=ThisFile, StartRow:=32767 Set WBC = ActiveWorkbook
' Некоторые строки (а именно строки 32767-65536)' будут скопированы повторно.' Первые 32770 строк нужно удалить. RowsToSkip = Application.Rows.Count + 1 - 32767 Cells(1, 1).Resize(RowsToSkip, 1).EntireRow.Delete FinalRow = Cells(65536, 1).End(xlUp).Row Cells(1, 1).Resize(FinalRow, 1).Copy _WBO.Worksheets("Данные").Range("AA1") WBC.Close False
End IfEnd Sub
Импорт текстового файла построчно
Изменим условие задачи, увеличив число розничных магазинов до 50. При
этом максимальный размер импортируемого файла возрастет до 100 000 записей и
его придется загружать построчно.
Импорт текстового файла, содержащего менее 65 536 записей, построчноОткройте файл для чтения с помощью выражения Input As #1. Чтобы
считать строку файла в переменную, воспользуйтесь выражением Line In-put #1, имя_переменной. Следующий макрос открывает файл inven-tory.txt, считывает первые 10 строк, помещая их содержимое в ячейки раY
бочего листа, после чего закрывает файл.
Sub Import10() ChDir ThisWorkbook.Path ThisFile = "inventory.txt" Open ThisFile For Input As #1 For i = 1 To 10 Line Input #1, Data Cells(i, 1).Value = Data Next i Close #1End Sub
Чтобы считать все содержимое файла, воспользуйтесь циклом
Do...While и функцией EOF. Значение выражения EOF(1) позволяет опреY
делить, был ли достигнут конец файла с номером #1. Следующий макрос поY
строчно импортирует содержимое файла inventory.txt (чтобы обеспечить
корректное выполнение макроса ImportAll, файл inventory.txt не долY
жен содержать более 65 536 записей).
Sub ImportAll() ThisFile = "inventory.txt" Open ThisFile For Input As #1
484 Часть III Удивительные возможности Visual Basic for Applications
Ctr = 0 Do Line Input #1, Data Ctr = Ctr + 1 Worksheets("Данные").Cells(Ctr, 1).Value = Data Loop While EOF(1) = False Close #1 Worksheets("Данные").SelectEnd Sub
Результат импорта текстового файла описанным выше способом показан
на рис. 18.9.
Рис. 18.9. Построчное считывание текстового файла требует последующей обработки данныхна рабочем листе
Очевидным недостатком такого подхода является помещение содержимого
каждой строки файла в ячейку столбца A.
Чтобы провести разбор импортированных данных, воспользуйтесь метоY
дом TextToColumns. Параметры метода TextToColumns практически иденY
тичны параметрам метода OpenText.
Cells(1, 1).Resize(Ctr, 1).TextToColumns Destination:= _Range("A1"), DataType:=xlDelimited, comma:=True, FieldInfo:= _Array(Array(1, xlGeneralFormat), Array(2, xlGeneralFormat), _Array(3, xlGeneralFormat), Array(4, xlMDYFormat))
Работа с текстовыми файлами Глава 18 485
Вместо жесткой привязки к конкретному номеру файла рекомендуется исY
пользовать функцию FreeFile. Функция FreeFile возвращает целое число,
представляющее номер файла, доступный для использования в выражении
Open. Ниже приведен полный код макроса, считывающего текстовый файл с
менее чем 65 536 записями, построчно.
Sub ImportAllAndParse() ThisFile = "inventory.txt" FileNumber = FreeFile
Open ThisFile For Input As #FileNumber Ctr = 0 Do Line Input #FileNumber, Data Ctr = Ctr + 1 Worksheets("Данные").Cells(Ctr, 1).Value = Data Loop While EOF(FileNumber) = False Close #FileNumber Worksheets("Данные").Select' Провести разбор данных. Cells(1, 1).Resize(Ctr, 1).TextToColumns Destination:= _Range("A1"), DataType:=xlDelimited, comma:=True, FieldInfo:= _Array(Array(1, xlGeneralFormat), Array(2, xlGeneralFormat), _Array(3, xlGeneralFormat), Array(4, xlMDYFormat))End Sub
Импорт текстового файла, содержащего более 65 536 записей, построчноДля построчного импорта текстового файла, содержащего более
65 536 записей, можно использовать уже знакомое выражение Line Input.
Считаем часть файла в ячейки A1:A65534, продолжив считывание оставшихY
ся строк, начиная с ячейки AA2 (первая строка отводится для размещения заY
головков столбцов). Если и этого окажется недостаточно, продолжим считыY
вание, начиная с ячейки BA2, CA2 и т.д. Наличие двух пустых строк в конце
каждого столбца с данными обеспечивает корректное определение номера поY
следней строки с помощью выражения Range("A65536").End(xlUp).Row.
Sub ReadLargeFile() ThisFile = "inventory.txt" FileNumber = FreeFile
Open ThisFile For Input As #FileNumber NextRow = 1 NextCol = 1' Добавлено для увеличения скорости выполнения макроса. Application.ScreenUpdating = False Application.StatusBar = "Считывание данных из текстового файла" Do While Not EOF(FileNumber) Line Input #FileNumber, Data Cells(NextRow, NextCol).Value = Data NextRow = NextRow + 1 If NextRow = 65535 Then' Провести разбор данных. Application.StatusBar = "Разбор данных" Range(Cells(1, NextCol), Cells(65536, _NextCol)).TextToColumns Destination:=Cells(1, NextCol), _
486 Часть III Удивительные возможности Visual Basic for Applications
DataType:=xlDelimited, comma:=True, FieldInfo:=Array(Array(1, _xlGeneralFormat), Array(2, xlGeneralFormat), Array(3, _xlGeneralFormat), Array(4, xlMDYFormat))' Скопировать заголовки столбцов. If NextCol > 1 Then Range("A1:D1").Copy Destination:=Cells(1, NextCol) End If' Начать считывание следующего набора данных. NextCol = NextCol + 26 NextRow = 2 Application.StatusBar = "Считывание следующего _набора данных"
End If Loop Close #FileNumber' Разбор последнего набора данных. FinalRow = NextRow - 1 If FinalRow = 1 Then' Обработка файла, содержащего ровно 65534 строки. NextCol = NextCol - 26 Else Application.StatusBar = "Разбор последнего набора данных" Range(Cells(2, NextCol), Cells(FinalRow, _NextCol)).TextToColumns Destination:=Cells(2, NextCol), _DataType:=xlDelimited, comma:=True, FieldInfo:=Array(Array(1, _xlGeneralFormat), Array(2, xlGeneralFormat), Array(3, _xlGeneralFormat), Array(4, xlMDYFormat)) If NextCol > 1 Then Range("A1:D1").Copy Destination:=Cells(1, NextCol) End If End If Cells(1, NextCol).Select
DataSets = (NextCol - 1) / 26 + 1 Application.StatusBar = False Application.ScreenUpdating = True
End Sub
Число наборов данных, созданных в результате выполнения макроса
ReadLargeFile, хранится в переменной DataSet.
Описанный выше метод позволяет разместить на рабочем листе более
655 000 строк, импортированных из текстового файла. Чтобы применить фильтр
или создать отчет на основе нескольких наборов данных, в код макросов, рассмотY
ренных в предыдущих главах, потребуется внести соответствующие изменения.
Например, можно создать по одной сводной таблице для каждого набора данных,
подытожив полученные результаты с помощью еще одной сводной таблицы.
Экспорт данных в текстовый файлЧтобы экспортировать данные в текстовый файл, откройте его для записи с
помощью выражения Output As #1. Сохраните все требуемые строки в
файле с помощью выражения Print #1.
Работа с текстовыми файлами Глава 18 487
Прежде чем открыть файл для записи, убедитесь, что на диске не существуY
ет копии этого файла. Для этого попытайтесь удалить файл с помощью выраY
жения Kill. Чтобы проигнорировать сообщение об ошибке, которое будет
сгенерировано, если файл с заданным именем не существует, воспользуйтесь
выражением On Error Resume Next.
Следующий макрос экспортирует данные рабочего листа в текстовый файл
Results.txt.
Sub WriteFile() ThisFile = ThisWorkbook.Path & Application.PathSeparator & _"Results.txt"' Удалить копию файла. On Error Resume Next Kill (ThisFile) On Error GoTo 0' Открыть файл. Open ThisFile For Output As #1 FinalRow = Range("A65536").End(xlUp).Row' Экспортировать в файл данные рабочего листа. For j = 1 To FinalRow Print #1, Cells(j, 1).Value Next j Close #1 MsgBox "Файл " & ThisFile & " успешно сохранен."End Sub
Аналогичный способ экспорта данных в текстовый файл применялся в
главе 14, ‘‘Взаимодействие с Internet’’, при создании WebYстраниц.
Следующий шагСкорость импорта и экспорта текстовых файлов оставляет желать лучшего.
В следующей главе рассматривается использование Microsoft Access для обесY
печения быстрого доступа к данным, предусматривающего индексацию и
многопользовательский режим.
ADO и DAO .................................... 490Объекты ADO ............................... 492Добавление записи втаблицу Access ............................. 493Извлечение записей изтаблицы Access ............................ 494Обновление записей таблицыAccess ............................................. 496Удаление записей таблицыAccess ............................................. 499Создание итоговыхзапросов ........................................ 499Несколько полезныхмакросов ....................................... 500Следующий шаг........................... 503
19Глава 19
ÈñïîëüçîâàíèåMicrosoft Access
В предыдущей главе был предлоY
жен метод, позволяющий хранить на
одном рабочем листе Excel свыше
650 000 строк данных. Тем не менее,
на некотором этапе становится очеY
видно, что для хранения больших
объемов данных следует применять
специализированный инструмент,
такой как Microsoft Access. ОсновY
ным форматом файлов Microsoft AcY
cess является формат MDB (MulY
tidimensional Database — многомерY
ная база данных).
Использование MDBYфайлов моY
жет быть оправдано необходимостью
обеспечения совместного доступа к
данным. Предоставление общего досY
тупа к рабочей книге связано с множеY
ством ограничений. В частности, досY
туп к следующим функциям возможен
только при запрете совместного досY
тупа к книге:
автоматическая вставка проY
межуточных итогов;
создание сводных таблиц;
группировка и структурироваY
ние данных;
создание, изменение и проY
смотр сценариев;
защита листов и книг, а также
снятие защиты;
добавление и изменение усY
ловного форматирования;
вставка и изменение рисунков
и других объектов;
490 Часть III Удивительные возможности Visual Basic for Applications
создание и изменение диаграмм или отчетов сводных диаграмм;
удаление рабочих листов.
Применение Excel в качестве пользовательского интерфейса, а MDBY
файла YYYY в качестве базы данных позволяет добиться оптимального использоY
вания возможностей обоих программ.
На заметку Формат файлов MDB является официальным форматом файлов как Microsoft Ac&cess, так и Microsoft Visual Basic for Applications. Это позволяет разрабатыватьMDB&ориентированные макросы, предназначенные для выполнения на компью&терах без установленной копии Microsoft Access. Тем не менее, создание таблиц изапросов Access возможно только с помощью пользовательского интерфейса этойпрограммы.
ADO и DAOНа протяжении нескольких лет для доступа к данным, хранящимся во
внешних источниках, Microsoft рекомендовала использовать объекты DAO
(Data Access Objects — объекты доступа к данным). Начиная с Excel 2000 и
VBA 6 Microsoft смещает акцент на объекты ADO (ActiveX Data Objects —
объекты данных ActiveX), при этом продолжая обеспечивать поддержку DAO.
Обе парадигмы доступа к данным весьма похожи и имеют незначительно отY
личающийся синтаксис. Во всех примерах этой главы доступ к данным будет
осуществляться с помощью объектов ADO. Более подробно отличия между
ADO и DAO рассматриваются в статье базы знаний Microsoft, которую вы
найдете по адресу:
http://support.microsoft.com/default.aspx?scid=KB;EN-US;q225048&
Прежде чем перейти к созданию макросов, использующих ADO, выберите
в меню редактора Visual Basic команду Tools References (Сервис Ссылки) и
установите флажок, соответствующий библиотеке Microsoft ActiveX Data ObY
jects, как показано на рис. 19.1. Щелкните на кнопке OK, чтобы закрыть диаY
логовое окно References (Ссылки).
Практикум
Совместный доступ к MDB-файлуРассмотрим следующую задачу. Предположим, что пользователь А и пользова&тель Б ответственны за закупку товаров для сети розничных магазинов. Каждое ут&ро они импортируют данные из журнала кассовых операций, чтобы получить ин&формацию о вчерашних продажах и, при необходимости, перераспределить ос&таток товаров между магазинами. Необходимо сделать так, чтобы пользователь Амог видеть перемещения товаров, инициированные пользователем Б, и наоборот.
Использование Microsoft Access Глава 19 491
Рис. 19.1. Для доступа к содержимому MDB&файла с по&мощью объектов ADO используется библиотека Micro&soft ActiveX Data Objects
На компьютерах обоих пользователей установлена копия Excel с поддержкой VBA.Импортированные данные обрабатываются макросами, в результате выполнениякоторых создаются сводные таблицы, облегчающие принятие решений о необхо&димости закупки товаров.Попытка хранения данных о перемещениях товаров в рабочей книге Excel обрече&на на провал. Монопольный режим доступа не позволяет изменять файл рабочейкниги нескольким пользователям одновременно. С другой стороны, режим со&вместного доступа исключает возможность создания сводных таблиц.Следует отметить, что на компьютерах обоих пользователей нет установленнойкопии Microsoft Access.Решение поставленной задачи приведено ниже.1. С помощью копии Microsoft Access, установленной на другом компьютере, создайте
базу данных Transfers.mdb и добавьте в нее таблицу tblTransfer, как показанона рис. 19.2.
2. Разместите файл Transfers.mdb на сетевом диске, доступном с компьютеровобоих пользователей.
3. Подключите библиотеку Microsoft ActiveX Data Objects на компьютерах обоихпользователей.
4. Поместите путь к файлу Transfers.mdb в рабочие книги пользователей А и Б,присвоив соответствующей ячейке имя TPath.
5. Для работы с данными, хранящимися в таблице tblTransfer, используйтекод, приводящийся в следующих разделах этой главы.
Access поддерживает общий доступ к данным. Это означает, что пользователь А ипользователь Б могут просматривать или изменять таблицу tblTransfer одно&временно. Единственным условием для возникновения конфликтной ситуации яв&
492 Часть III Удивительные возможности Visual Basic for Applications
ляется попытка совместного изменения пользователями А и Б одной и той же за&писи таблицы.
Рис. 19.2. Таблица tblTransfer предназначена для хранения информации о перемещени&ях товаров и будет доступна для изменения несколькими пользователями одновременно
Объекты ADOДвумя основными объектами ADO являются объект соединения Connec-
tion и объект набора данных Recordset.
Объект соединения определяет путь к базе данных и ее тип. База данных
Microsoft Access основана на ядре Microsoft Jet.
Объект набора данных создается на основе существующего объекта соедиY
нения для представления таблицы, некоторого ее подмножества или предоY
пределенного запроса базы данных Access. Помимо объекта соединения, при
создании объекта набора данных указываются такие параметры, как Cur-sorType, CursorLocation, LockType и Options. Для обеспечения одноY
временного доступа к базе данных двух пользователей рекомендуется примеY
нять динамический курсор и оптимистическую блокировку. При работе с
большими объемами данных установите значение параметра CursorLoca-tion равным adUseServer, что позволит обрабатывать записи в оперативY
ной памяти сервера. В противном случае установите значение параметра Cur-sorLocation равным adUseClient. При создании объекта набора данных
все записи будут скопированы в оперативную память компьютера пользоватеY
ля, что способно существенно ускорить их обработку.
Использование Microsoft Access Глава 19 493
Чтобы скопировать записи из таблицы Access на рабочий лист Excel, восY
пользуйтесь методом Excel VBA CopyFromRecordset.
Чтобы добавить новую запись в таблицу Access, воспользуйтесь методом
AddNew объекта Recordset. Укажите значение каждого поля таблицы и выY
зовите метод Update объекта Recordset для внесения изменений в базу
данных.
Чтобы удалить запись из таблицы Access, отправьте запрос на удаление заY
писи, удовлетворяющий заданному критерию.
Кроме того, средства VBA позволяют проверить существование таблицы
или ее поля, а также добавить новые поля к существующей таблице.
Добавление записи в таблицу AccessПользователи А и Б вводят информацию о перемещениях товаров в форму.
Фактическое добавление записи в таблицу Access возложено на макрос
AddTransfer.
При добавлении записи в таблицу Access макрос AddTransfer выполняет
следующую последовательность действий.
1. Создание объекта соединения.
2. Создание объекта набора данных для обращения к таблице tblTransfer.
3. Вызов метода AddNew для добавления новой записи в таблицу
tblTransfer.
4. Установка значения каждого поля новой записи таблицы tblTransfer.
5. Вызов метода Update для внесения изменений в базу данных.
6. Закрытие объектов набора данных и соединения.
Ниже приведен код метода AddTransfer.
Sub AddTransfer(Style As Variant, FromStore As Variant, _ToStore As Variant, Qty As Integer) Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset
MyConn = ThisWorkbook.Path & Application.PathSeparator & _"transfers.mdb"
' Открыть соединение. Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With
' Создать объект набора данных. Set rst = New ADODB.Recordset rst.CursorLocation = adUseServer
494 Часть III Удивительные возможности Visual Basic for Applications
' Открыть набор данных. rst.Open Source:="tblTransfer", _ ActiveConnection:=cnn, _ CursorType:=adOpenDynamic, _ LockType:=adLockOptimistic, _ Options:=adCmdTable
' Добавить новую запись. rst.AddNew
' Определить значения полей новой записи. Первые 4 поля' задаются пользователем с помощью формы. В поле "Дата"' вносится текущее значение даты. rst("Товар") = Style rst("ИзМагазина") = FromStore rst("ВМагазин") = ToStore rst("Количество") = Qty rst("Дата") = Date rst("Отправлен") = False rst("Получен") = False
' Внести изменения в базу данных. rst.Update
' Закрыть объекты набора данных и соединения. rst.Close cnn.CloseEnd Sub
Извлечение записей из таблицы AccessЧтобы извлечь записи из таблицы Access, определите набор данных, указав в
качестве источника строку SQLYзапроса, задающую требуемый критерий отбора.
Скопировать записи из таблицы Access на рабочий лист Excel поможет меY
тод Excel VBA CopyFromRecordset.
Следующий макрос извлекает из таблицы tblTransfer все записи, в коY
торых значение поля Отправлен равно False, и помещает их на рабочий
лист. Строка frmTransConf.Show выводит на экран пользовательскую форY
му, которая применяется для обновления записей и рассматривается в слеY
дующем разделе.
Sub GetUnsentTransfers() Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset Dim WSOrig As Worksheet Dim WSTemp As Worksheet Dim sSQL As String Dim FinalRow As Long
Set WSOrig = ActiveSheet
' Создать SQL-запрос для извлечения записей,' соответствующих неотправленным товарам. sSQL = "SELECT Идентификатор, Товар, ИзМагазина, ВМагазин, _
Использование Microsoft Access Глава 19 495
Количество, Дата FROM tblTransfer" sSQL = sSQL & " WHERE Отправлен=FALSE"
' Задать путь к файлу Transfers.mdb. MyConn = ThisWorkbook.Path & Application.PathSeparator _& "transfers.mdb"
Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With Set rst = New ADODB.Recordset rst.CursorLocation = adUseServer rst.Open Source:=sSQL, ActiveConnection:=cnn, _ CursorType:=AdForwardOnly, LockType:=adLockOptimistic, _ Options:=adCmdText
' Создать новый рабочий лист. Set WSTemp = Worksheets.Add WSTemp.Select Range("A1:F1").EntireColumn.Clear
' Создать строку заголовков. Range("A1:F1").Value = Array("Идентификатор", "Товар", _"ИзМагазина", "ВМагазин", "Количество", "Дата")
' Скопировать записи из набора данных на рабочий лист. Range("A2").CopyFromRecordset rst
' Закрыть набор данных и соединение. rst.Close cnn.Close
' Отформатировать отчет. FinalRow = Range("A65536").End(xlUp).Row
' Удалить временный рабочий лист, если в результате' запроса было возвращено пустое множество записей. If FinalRow = 1 Then Application.DisplayAlerts = False WSTemp.Delete Application.DisplayAlerts = True WSOrig.Activate MsgBox "Неподтвержденных перемещений товаров нет" Exit Sub End If
' Задать формат ячеек столбца F как М.Д.ГГ. Range("F2:F" & FinalRow).NumberFormat = "m/d/y"
' Отобразить форму. frmTransConf.Show
' Удалить временный рабочий лист. Application.DisplayAlerts = False WSTemp.Delete
496 Часть III Удивительные возможности Visual Basic for Applications
Application.DisplayAlerts = True
End Sub
Метод CopyFromRecordset копирует на рабочий лист Excel только строY
ки с данными, а строку заголовка необходимо создавать вручную. Результат
выполнения макроса GetUnsentTransfers до отображения пользовательY
ской формы показан на рис. 19.3.
Рис. 19.3. Метод CopyFromRecordset копи&рует на рабочий лист Excel результат извлече&ния записей из таблицы tblTransfer
Обновление записей таблицы AccessЧтобы обновить существующую запись таблицы Access, создайте набор
данных, содержащий однуYединственную запись. Обычно для этого необхоY
димо, чтобы пользователь выбрал требуемую запись (а значит и ее уникальY
ный идентификатор YYYY ключ) с помощью формы. Измените значение требуеY
мого поля с помощью свойства Fields и внесите обновленную запись в базу
данных с помощью метода Update.
Рассмотренный ранее макрос GetUnsentTransfers помещает записи
таблицы tblTransfer на рабочий лист, а затем выводит на экран пользоваY
тельскую форму frmTransConf. Метод инициализации формы User-Form_Initialize загружает все записи с текущего рабочего листа в список,
Использование Microsoft Access Глава 19 497
поддерживающий множественный выбор (значение свойства MultiSelectравно True):
Private Sub UserForm_Initialize() OrigBook = ActiveWorkbook.Name
' Загрузить записи в список. FinalRow = Cells(65536, 1).End(xlUp).Row If FinalRow > 1 Then Me.lbXlt.RowSource = "A2:F" & FinalRow End IfEnd Sub
Форма, показанная на рис. 19.4, отображает записи, соответствующие неY
отправленным товарам. Чтобы изменить значение поля Отправлен требуеY
мых записей с False на True, выделите их и щелкните на кнопке
Подтвердить.
Рис. 19.4. Пользовательская форма отобра&жает записи, соответствующие неотправлен&ным товарам (значение поля Отправленравно False). Чтобы отразить факт отправкитоваров в базе данных, выделите требуемыезаписи и щелкните на кнопке Подтвердить
Ниже приведен код, выполняющийся в результате щелчка на кнопке
Подтвердить. Ключевым фрагментом этого кода является создание SQLY
запроса, использующегося для отбора единственной записи таблицы
tblTransfer с помощью поля Идентификатор.
498 Часть III Удивительные возможности Visual Basic for Applications
Private Sub cbConfirm_Click() Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset
' Если в списке не содержится' ни одной записи, вывести сообщение. CountSelect = 0 For x = 0 To Me.lbXlt.ListCount - 1 If Me.lbXlt.Selected(x) Then CountSelect = CountSelect + 1 End If Next x
If CountSelect = 0 Then MsgBox "Перемещаемых товаров нет. Чтобы закрыть форму, _щелкните на кнопке Выход." Exit Sub End If
' Установить соединение с базой данных transfers.mdb. MyConn = ThisWorkbook.Path & Application.PathSeparator & _"transfers.mdb"
Set cnn = New ADODB.Connection
With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With
' Обновить записи таблицы tblTransfer. For x = 0 To Me.lbXlt.ListCount - 1 If Me.lbXlt.Selected(x) Then ThisID = Cells(2 + x, 1).Value' Обновить запись с идентификатором ThisID.' Создать SQL-запрос. sSQL = "SELECT * FROM tblTransfer Where _Идентификатор=" & ThisID Set rst = New ADODB.Recordset With rst .Open Source:=sSQL, ActiveConnection:=cnn, _CursorType:=adOpenKeyset, LockType:=adLockOptimistic' Обновить поле. .Fields("Отправлен").Value = True .Update .Close End With End If Next x
' Закрыть объекты набора данных и соединения. cnn.Close Set rst = Nothing Set cnn = Nothing
' Закрыть пользовательскую форму. Unload Me
End Sub
Использование Microsoft Access Глава 19 499
Удаление записей таблицы AccessЧтобы удалить существующую запись таблицы Access, создайте SQLYкод,
удаляющий запись на основе значения ее уникального идентификатора. УстаY
новите соединение с базой данных и выполните SQLYкод с помощью метода
Execute, как показано ниже:
Public Sub DeleteRecord(RecID)' Установить соединение с базой данных transfers.mdb. MyConn = ThisWorkbook.Path & Application.PathSeparator _& "transfers.mdb"
With New ADODB.Connection .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn .Execute "Delete From tblTransfer Where _Идентификатор = " & RecID .Close End WithEnd Sub
Создание итоговых запросовОдна из многочисленных возможностей Access заключается в создании
итоговых запросов с объединением записей с одинаковыми значениями в укаY
занном списке полей в одну запись. Попробуйте создать итоговый запрос и
просмотрите полученный SQLYкод. Аналогичный запрос можно создать с поY
мощью Excel VBA и передать его на обработку Access с помощью средств бибY
лиотеки ADO.
Следующий макрос выполняет сложный SQLYзапрос, возвращающий чисY
тый итог перемещения товара B10894 по магазинам.
Sub NetTransfers(Style As Variant)' Этот макрос подсчитывает чистый итог' перемещения заданного товара по магазинам. Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset
' Создать сложный SQL-запрос.' Логика запроса: объединить результаты запроса, возвращающего' сумму входящих перемещений указанного товара по магазинам,' с результатами запроса, возвращающего отрицательную сумму' исходящих перемещений указанного товара по магазинам. sSQL = "Select Магазин, Sum(Количество), Min(мДата) From _(SELECT ВМагазин AS Магазин, Sum(Количество) AS Количество, _Min(Дата) AS мДата FROM tblTransfer where Товар='" & Style & "' _AND Получен=FALSE GROUP BY ВМагазин " sSQL = sSQL & " Union All SELECT ИзМагазина AS Магазин, _Sum(-1*Количество) AS Количество, Min(Дата) AS мДата FROM _tblTransfer where Товар='" & Style & "' AND Отправлен=FALSE _GROUP BY ИзМагазина)" sSQL = sSQL & " Group by Магазин"
500 Часть III Удивительные возможности Visual Basic for Applications
MyConn = ThisWorkbook.Path & Application.PathSeparator & _"transfers.mdb"
' Открыть соединение. Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With Set rst = New ADODB.Recordset rst.CursorLocation = adUseServer
' Выполнить SQL-запрос. rst.Open Source:=sSQL, _ ActiveConnection:=cnn, _ CursorType:=AdForwardOnly, _ LockType:=adLockOptimistic, _ Options:=adCmdText Range("A1:C1").Value = Array("Магазин", "Количество", "Дата")
' Скопировать результаты запроса на рабочий лист. Range("A2").CopyFromRecordset rst
rst.Close cnn.CloseEnd Sub
Несколько полезных макросовВ оставшихся разделах главы рассматриваются несколько полезных функY
ций и макросов, использующих возможности библиотеки ADO. Как отмечаY
лось ранее, пользователи А и Б имеют доступ к базе данных Access, располоY
женной на сетевом диске, но не имеют установленной копии Microsoft Access.
В связи с этим возникает потребность обновления базы данных средствами
VBA, что удобно осуществлять при открытии рабочей книги Excel. Макрос
обновления базы данных можно разместить в надстройке и выполнить при
открытии рабочей книги с помощью обработчика события Workbook_Open(см. главу 25, “Надстройки”).
Проверка существования таблицы в базе данных Access
В одном из следующих разделов рассматривается добавление новой таблиY
цы в существующую базу данных Access. Естественно, что добавить новую
таблицу сможет только один пользователь, тот, кто первым откроет рабочую
книгу и тем самым инициирует выполнение соответствующего макроса.
Функция TableExists проверяет существование таблицы с указанным
именем в базе данных Transfers.mdb с помощью метода OpenSchema.
Function TableExists(WhichTable) Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset Dim fld As ADODB.Field
Использование Microsoft Access Глава 19 501
TableExists = False
' Задать путь к базе данных Transfers.mdb. MyConn = ThisWorkbook.Path & Application.PathSeparator _& "transfers.mdb"
Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With
Set rst = cnn.OpenSchema(adSchemaTables)
Do Until rst.EOF If LCase(rst!Table_Name) = LCase(WhichTable) Then TableExists = True GoTo ExitMe End If rst.MoveNext Loop
ExitMe: rst.Close Set rst = Nothing
' Закрыть соединение. cnn.CloseEnd Function
Проверка существования поля в таблицебазы данных Access
В одном из следующих разделов рассматривается добавление нового поля в
таблицу базы данных Access. Функция ColumnExists проверяет существоваY
ние поля с указанным именем в заданной таблице базы данных Trans-fers.mdb.
Function ColumnExists(WhichColumn, WhichTable) Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset Dim WSOrig As Worksheet Dim WSTemp As Worksheet Dim fld As ADODB.Field ColumnExists = False
' Задать путь к базе данных Transfers.mdb. MyConn = ThisWorkbook.Path & Application.PathSeparator _& "transfers.mdb"
Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With
502 Часть III Удивительные возможности Visual Basic for Applications
Set rst = cnn.OpenSchema(adSchemaColumns)
Do Until rst.EOF If LCase(rst!Column_Name) = LCase(WhichColumn) And _ LCase(rst!Table_Name) = LCase(WhichTable) Then ColumnExists = True GoTo ExitMe End If rst.MoveNext Loop
ExitMe: rst.Close Set rst = Nothing' Закрыть соединение. cnn.CloseEnd Function
Добавление таблицы в базу данных Access
Следующий макрос добавляет таблицу tblReplenish в базу данных
Transfers.mdb с помощью команды SQL CREATE TABLE.
Sub ADOCreateReplenish()' Этот макрос создает таблицу tblReplenish' в базе данных Transfers.mdb.' Таблица tblReplenish имеет 5 полей.' 1. Товар.' 2. Кат1 - минимальный уровень запасов товара' магазинов 1-й категории.' 3. Кат2 - минимальный уровень запасов товара' магазинов 2-й категории.' 4. Кат3 - минимальный уровень запасов товара' магазинов 3-й категории.' 5. Активна - поле "Да/Нет". Dim cnn As ADODB.Connection Dim cmd As ADODB.Command
' Задать путь к базе данных. MyConn = ThisWorkbook.Path & Application.PathSeparator _& "transfers.mdb"
' Открыть соединение. Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With Set cmd = New ADODB.Command Set cmd.ActiveConnection = cnn
' Создать таблицу. cmd.CommandText = "CREATE TABLE tblReplenish (Товар Char(10) _Primary Key, Кат1 int, Кат2 int, Кат3 Int, Активна YesNo)" cmd.Execute , , adCmdText Set cmd = Nothing
Использование Microsoft Access Глава 19 503
Set cnn = Nothing Exit SubEnd Sub
Добавление поля в таблицу базы данных Access
Следующий макрос добавляет поле Группа в таблицу tblReplenish баY
зы данных Transfers.mdb с помощью команды SQL ALTER TABLE.
Sub ADOAddField()' Этот макрос добавляет поле Группа к таблице tblReplenish. Dim cnn As ADODB.Connection Dim cmd As ADODB.Command' Задать путь к базе данных. MyConn = ThisWorkbook.Path & Application.PathSeparator _& "transfers.mdb"
' Открыть соединение. Set cnn = New ADODB.Connection With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .Open MyConn End With
Set cmd = New ADODB.Command Set cmd.ActiveConnection = cnn' Добавить поле в таблицу tblReplenish. cmd.CommandText = "ALTER TABLE tblReplenish Add Column _Группа Char(25)" cmd.Execute , , adCmdText Set cmd = Nothing Set cnn = NothingEnd Sub
Следующий шагСледующая глава посвящена созданию модулей классов, предназначенных
для размещения пользовательских объектов.
Создание модуля класса ........... 506Обработка событий уровняприложения и встроеннойдиаграммы ................................... 506Создание пользовательскогообъекта ........................................... 510Применениепользовательского объектана практике ....................................511Использование выраженийProperty Let и Property Get.......... 513Коллекции ..................................... 515Создание пользовательскихтипов .............................................. 520Следующий шаг........................... 524
20Глава 20
Ñîçäàíèåïîëüçîâàòåëü-ñêèõ îáúåêòîâ,òèïîâè êîëëåêöèé
Несмотря на все разнообразие
встроенных объектов Excel, в некоY
торых ситуациях наиболее оптиY
мальным решением является создаY
ние собственного объекта, преднаY
значенного для выполнения узкоY
специализированной задачи. Excel
поддерживает создание пользоваY
тельских объектов в так называеY
мых модулях классов.
Ранее в этой книге рассматриваY
лись ‘‘улучшенные’’ модули классов,
такие как модуль рабочей книги, раY
бочего листа, диаграммы и пользоваY
тельской формы. Каждому из этих
модулей свойственны характеристиY
ки, уникальные для соответствуюY
щего типа объектов. В частности, это
касается обработчиков событий.
Кроме размещения пользовательY
ских объектов с соответствующими
свойствами и методами, классы моY
дулей могут включать в себя код обY
работчиков событий уровня прилоY
жения, встроенной диаграммы, элеY
мента управления ActiveX и др.
506 Часть III Удивительные возможности Visual Basic for Applications
Создание модуля класса Чтобы добавить модуль класса к текущему проекту, выберите в меню реY
дактора Visual Basic команду Insert Class Module (Вставить Модуль класса).
Excel добавит новый модуль класса Class1, разместив его в папке ClassModules (Модули классов) текущего проекта, как показано на рис. 20.1.
Рис. 20.1. Пользовательские объектыразмещаются в модулях классов
При работе с пользовательскими объектами необходимо помнить следуюY
щие ключевые моменты.
Каждый пользовательский объект должен размещаться в отдельном
модуле класса. (Обработчики событий пользовательских объектов моY
гут размещаться в одном модуле класса.)
Модуль класса необходимо переименовать в соответствии с именем
размещенного в нем пользовательского объекта.
Обработка событий уровня приложенияи встроенной диаграммы
В главе 8, ‘‘События’’, рассматривалась обработка событий уровня рабочей
книги, рабочего листа и листа диаграммы. Там же описывалось создание моY
дуля класса для размещения кода обработки событий приложения и встроенY
ной диаграммы. Более подробно тема обработки событий уровня приложения
и встроенной диаграммы рассматривается в следующих разделах.
Создание пользовательских объектов, типов и коллекций Глава 20 507
События уровня приложения
Событие уровня рабочей книги Workbook_BeforePrint срабатывает при
попытке печати конкретной рабочей книги. Чтобы обеспечить одинаковую
обработку события Workbook_BeforePrint в нескольких рабочих книгах,
скопируйте соответствующий программный код в каждую книгу или воспольY
зуйтесь событием уровня приложения WorkbookBeforePrint.
События уровня приложения охватывают все рабочие книги, открытые в
рамках текущего сеанса работы с Excel. Для доступа к событиям уровня приY
ложения необходимо использовать модуль класса, как показано ниже.
1. Добавьте модуль класса к текущему проекту. Присвойте модулю какоеY
нибудь значащее имя, например, clsAppEvents.
На заметку Чтобы переименовать модуль, выберите в меню редактора Visual Basic командуView Properties Window (Вид Окно свойств).
2. Разместите в модуле класса следующую строку кода:
Public WithEvents xlApp As Application
Имя используемой переменной (в данном случае xlApp) может быть
любым. Ключевое слово WithEvents делает доступными события
объекта Application на уровне модуля clsAppEvents.
3. В результате выполнения предыдущего шага переменная xlApp станет
доступной из раскрывающегося списка Object (Объект) редактора Visual
Basic. После выбора переменной xlApp из раскрывающегося списка
Object в расположенном рядом раскрывающемся списке Procedure(Процедура) появятся события, соответствующие типу объекта этой пеY
ременной (в данном случае Application), как показано на рис. 20.2.
Рис. 20.2. Выбор объекта из раскрывающегося списка Object делает доступными соответст&вующие типу этого объекта события
См. также События уровня приложения рассматриваются в главе 8, “События”, на с. 208.
508 Часть III Удивительные возможности Visual Basic for Applications
Для каждого события, перечисленного в раскрывающемся списке Procedure,
можно создать собственный код обработки. Ниже приведен код обработки соY
бытия NewWorkbook, создающий нижний колонтитул рабочей книги. Код обY
работчика xlApp_NewWorkbook следует поместить в модуль класса clsApp-Events после строки, содержащей объявление переменной xlApp.
Private Sub xlApp_NewWorkbook(ByVal Wb As Workbook) Dim wks As Worksheet On Error Resume Next With Wb For Each wks In .Worksheets wks.PageSetup.LeftFooter = "Создано: " & _.Application.UserName wks.PageSetup.RightFooter = Now Next wks End WithEnd Sub
В отличие от обработчиков событий уровня рабочей книги или рабочего
листа, процедура, размещенная в модуле класса, не вызывается автоматичеY
ски. Создайте экземпляр класса clsAppEvents и установите значение его
свойства xlApp равным Application, как показано ниже.
Public myAppEvent As clsAppEvents
Sub TrapAppEvent() Set myAppEvent.xlApp = ApplicationEnd Sub
После выполнения процедуры TrapAppEvent обработчик xlApp_New-Workbook будет формировать нижний колонтитул для каждой новой рабочей
книги, созданной в рамках текущего сеанса работы с Excel (рис. 20.3).
Рис. 20.3. Обработка события уровня приложения NewWorkbook позволяет формироватьнижний колонтитул для каждой новой рабочей книги, созданной в рамках текущего сеан&са работы с Excel
Внимание Обработка событий уровня приложения может быть приостановлена вследствиевыполнения действий, приводящих к обнулению значений переменных модуля.В частности, к таким действиям относится изменение программного кода в ре&дакторе Visual Basic. Чтобы возобновить обработку событий уровня приложения,воссоздайте соответствующие объекты (в данном случае для этого достаточно вы&полнить процедуру TrapAppEvent).
Создание пользовательских объектов, типов и коллекций Глава 20 509
Объявление переменной myAppEvent, а также процедура TrapAppEventразмещены в стандартном модуле. Чтобы автоматизировать обработку собыY
тия уровня приложения, модули классов можно перенести в личную книгу мак*росов (PERSONAL.XLS), а вызов процедуры TrapAppEvent — в обработчик
события Workbook_Open. Объявление переменной myAppEvent должно остаY
ваться в стандартном модуле, чтобы переменная была доступна другим модулям.
События встроенной диаграммы
Обработка событий встроенной диаграммы аналогична обработке событий
уровня приложения. Добавьте модуль класса и разместите в нем объявление
общедоступной переменной типа Char. Затем создайте код обработки требуеY
мого события и разместите в стандартном модуле процедуру, инициирующую
обработку событий встроенной диаграммы.
На заметку Код обработки событий уровня приложения и событий встроенной диаграммыможно размещать в одном и том же модуле класса.
Разместите в модуле класса следующую строку кода:
Public WithEvents xlChart As Chart
Переменная xlChart станет доступной из раскрывающегося списка
Object (Объект) редактора Visual Basic. После выбора переменной xlChart из
раскрывающегося списка Object в расположенном рядом раскрывающемся
списке Procedure (Процедура) появятся события, соответствующие типу
объекта этой переменной (в данном случае Chart), как показано на рис. 20.4.
Рис. 20.4. Выбор переменной xlChart из раскрывающегося списка Object делает доступнымисобытия встроенной диаграммы
См. также События уровня листа диаграммы рассматриваются в главе 8, “События”, на с. 204.
Создадим обработчик события MouseDown, изменяющий масштаб диаY
граммы при щелчке на ее области кнопкой мыши. Для этого потребуется пеY
510 Часть III Удивительные возможности Visual Basic for Applications
реопределить стандартный способ обработки еще двух событий YYYY Before-RightClick и BeforeDoubleClick.
Следующий код предотвращает выполнение стандартных действий, предY
принимаемых при обработке двойного щелчка и щелчка правой кнопкой мыши:
Private Sub xlChart_BeforeDoubleClick(ByVal ElementID As Long, _ ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean) Cancel = TrueEnd Sub
Private Sub xlChart_BeforeRightClick(Cancel As Boolean) Cancel = TrueEnd Sub
Определим способ обработки события, срабатывающего в результате нажаY
тия любой кнопки мыши.
Private Sub xlChart_MouseDown(ByVal Button As Long, _ByVal Shift As Long, ByVal x As Long, ByVal y As Long) If Button = 1 Then ActiveChart.Axes(xlValue).MaximumScale = _ ActiveChart.Axes(xlValue).MaximumScale - 50 End If If Button = 2 Then ActiveChart.Axes(xlValue).MaximumScale = _ ActiveChart.Axes(xlValue).MaximumScale + 50 End IfEnd Sub
Создайте экземпляр класса clsChartEvents и установите значение его
свойства xlChart, как показано ниже (рис. 20.5).
Public myChartEvent As clsChartEvents
Sub TrapChartEvent() Set myChartEvent.xlChart = Worksheets("Встроенная _диаграмма").ChartObjects("Chart 1").ChartEnd Sub
Создание пользовательского объектаКроме кода обработчиков событий, модули классов могут содержать польY
зовательские объекты с определением соответствующих свойств и методов.
Создадим объект служащего с такими характеристиками, как имя, идентифиY
катор, тарифная ставка и количество рабочих часов.
Добавьте модуль класса и переименуйте его в clsEmployee. Ниже переY
числены свойства объекта clsEmployee.
EmpName — имя служащего;
EmpID — идентификатор служащего;
EmpRate — тарифная ставка;
EmpWeeklyHrs — количество рабочих часов в неделю.
Создание пользовательских объектов, типов и коллекций Глава 20 511
Рис. 20.5. Обработка событий встроенной диаграммы реализуется посредством использова&ния модуля класса
При объявлении свойств и переменных указывается область видимости YYYY
обычно Private или Public. Поскольку свойства объекта clsEmployeeдолжны быть доступны из стандартного модуля, сделайте их общими, размесY
тив следующие строки в начале модуля класса:
Public EmpName As StringPublic EmpID As StringPublic EmpRate As DoublePublic EmpWeeklyHrs As Double
Методы объекта YYYY это ‘‘действия’’, которые он может выполнять. В модуY
ле класса ‘‘действия’’ объекта представлены процедурами и функциями. СлеY
дующий метод подсчитывает недельную заработную плату служащего:
Public Function EmpWeeklyPay() As Double EmpWeeklyPay = EmpRate*EmpWeeklyHrsEnd Function
Создание объекта clsEmployee, содержащего четыре свойства и один меY
тод, можно считать завершенным. В следующем разделе рассматривается
применение пользовательского объекта на практике.
Применение пользовательского объектана практике
Пользовательский объект, корректно заданный в модуле класса, становитY
ся доступным для других модулей. Ниже приведен пример объявления переY
менной типа clsEmployee:
Dim Employee As clsEmployee
Следующий код создает экземпляр пользовательского объекта:
Set Employee = New clsEmployee
512 Часть III Удивительные возможности Visual Basic for Applications
При разработке программного кода Excel отображает динамические подY
сказки, упрощающие обращение к свойствам и методам пользовательского
объекта (рис. 20.6).
Рис. 20.6. В аспекте отображения динамическихподсказок пользовательские объекты ничем неотличаются от стандартных объектов Excel
Приведенный ниже код наглядно демонстрирует применение пользоваY
тельского объекта на практике.
Option Explicit
Dim Employee As clsEmployee
Sub EmpPay()
Set Employee = New clsEmployee
With Employee .EmpName = "Трейси Сирстад" .EmpID = "1651" .EmpRate = 25 .EmpWeeklyHrs = 40 MsgBox .EmpName & " зарабатывает в неделю " & _.EmpWeeklyPay & " долларов."End With
End Sub
Макрос EmpPay создает экземпляр пользовательского объекта clsEm-ployee и задает значения всех его свойств. После этого макрос выводит на
экран сообщение, содержащее сведения о недельной заработной плате слуY
жащего, как показано на рис. 20.7.
Создание пользовательских объектов, типов и коллекций Глава 20 513
Рис. 20.7. При подсчете недельной за&работной платы служащего использует&ся метод EmpWeeklyPay объекта cls-Employee
Использование выражений Property Letи Property Get
Значения общедоступных переменных (таких как EmpName, EmpID, Emp-Rate и EmpWeeklyHrs) можно считывать и изменять произвольным образом.
Если доступ к свойствам объекта необходимо ограничить, воспользуйтесь выY
ражениями Property Let и Property Get.
Выражение Property Let позволяет управлять изменением свойств объекY
та, а выражение Property Get — считыванием этих свойств. В рассмотренY
ном ранее примере для хранения информации о количестве рабочих часов в
неделю использовалась общедоступная переменная EmpWeeklyHrs, что не
позволяло вести учет сверхурочных часов.
Внесем изменения в модуль класса, добавив к объекту clsEmployee два
новых свойства YYYY EmpNormalHrs и EmpOverTimeHrs. Поскольку эти свойY
ства должны быть доступны только для чтения, их нельзя реализовать с поY
мощью переменных с областью видимости Public. Вместо этого воспользуY
емся выражением Property Get.
Установка значения свойств EmpNormalHrs и EmpOverTimeHrs будет
производиться с помощью свойства EmpWeeklyHrs, которое также нельзя
реализовать в виде общедоступной переменной. Создадим две закрытые переY
менные NormalHrs и OverHrs, доступные для считывания и изменения исY
ключительно в пределах модуля класса.
Public EmpName As StringPublic EmpID As StringPublic EmpRate As Double
Private NormalHrs As DoublePrivate OverHrs As Double
Выражение Property Let позволяет использовать свойство EmpWeek-lyHrs для установки значения закрытых переменных NormalHrs и OverHrs.
Property Let EmpWeeklyHrs(Hrs As Double) NormalHrs = WorksheetFunction.Min(40, Hrs) OverHrs = WorksheetFunction.Max(0, Hrs - 40)End Property
514 Часть III Удивительные возможности Visual Basic for Applications
Выражение Property Get EmpWeeklyHrs определяет результат считыY
вания значения свойства EmpWeeklyHrs как сумму переменных NormalHrsи OverHrs.
Property Get EmpWeeklyHrs() As Double EmpWeeklyHrs = NormalHrs + OverHrsEnd Property
Выражение Property Get используется для определения результатов
считывания значений свойств EmpNormalHrs и EmpOverTimeHrs, как покаY
зано ниже:
Property Get EmpNormalHrs() As Double EmpNormalHrs = NormalHrsEnd Property
Property Get EmpOverTimeHrs() As Double EmpOverTimeHrs = OverHrsEnd Property
Поскольку свойства EmpNormalHrs и EmpOverTimeHrs должны быть
доступны только для чтения, их значения устанавливаются неявно посредстY
вом свойства EmpWeeklyHrs.
Наконец, ниже приведен код обновленного метода EmpWeeklyPay:
Public Function EmpWeeklyPay() As Double EmpWeeklyPay = (EmpNormalHrs * EmpRate) + _(EmpOverTimeHrs * EmpRate * 1.5)End Function
Макрос EmpPayOverTime использует преимущества, полученные в реY
зультате внесения изменений в модуль класса.
Option Explicit
Dim Employee As clsEmployee
Sub EmpPayOverTime()
Set Employee = New clsEmployee
With Employee .EmpName = "Трейси Сирстад" .EmpID = "1651" .EmpRate = 25 .EmpWeeklyHrs = 45 MsgBox .EmpName & Chr(10) & Chr(9) & _ "Стандартные часы: " & .EmpNormalHrs & Chr(10) & Chr(9) & _ "Сверхурочные часы: " & .EmpOverTimeHrs & Chr(10) & Chr(9) & _ "Заработная плата за неделю (долларов): " & .EmpWeeklyPayEnd With
End Sub
Результат выполнения макроса EmpPayOverTime показан на рис. 20.8.
Создание пользовательских объектов, типов и коллекций Глава 20 515
Рис. 20.8. Выражения Property Let иProperty Get позволяют ограничитьдоступ к свойствам пользовательскогообъекта
КоллекцииКоллекция предназначена для представления нескольких экземпляров
объекта. Например, объект Worksheet входит в коллекцию Worksheets.
Коллекция позволяет обращаться к любому ее элементу, подсчитывать общее
количество элементов в коллекции, а также удалять и добавлять элементы.
Аналогичная функциональность доступна для пользовательских объектов.
Существует два способа создания коллекции: в стандартном модуле и в моY
дуле класса.
Создание коллекции в стандартном модуле
Самый простой способ создания коллекции заключается в ее размещении
в стандартном модуле и использовании встроенного объекта VBA Collec-tion. Это позволяет получить доступ к четырем основным методам и свойстY
вам коллекции: Add, Remove, Count и Item.
Следующий макрос считывает список служащих с рабочего листа и помеY
щает их в массив. В результате обработки полученного массива создается колY
лекция пользовательских объектов clsEmployee.
Sub EmpPayCollection() Dim colEmployees As New Collection Dim recEmployee As New clsEmployee Dim LastRow As Integer, myCount As Integer Dim EmpArray As Variant
LastRow = ActiveSheet.Range("A65536").End(xlUp).Row EmpArray = ActiveSheet.Range(Cells(1, 1), Cells(LastRow, 4))
For myCount = 1 To UBound(EmpArray) With recEmployee .EmpName = EmpArray(myCount, 1) .EmpID = EmpArray(myCount, 2) .EmpRate = EmpArray(myCount, 3) .EmpWeeklyHrs = EmpArray(myCount, 4) colEmployees.Add recEmployee, .EmpID End With Next myCount
516 Часть III Удивительные возможности Visual Basic for Applications
MsgBox "Общее число служащих: " & colEmployees.Count & _Chr(10) & "Имя 2-го служащего в коллекции: " & _colEmployees(2).EmpName
MsgBox "Недельная заработная плата Трейси (долларов): " & _colEmployees("1651").EmpWeeklyPay
Set recEmployee = Nothing
End Sub
Коллекция colEmployees создается как экземпляр объекта Collection,
а ее элементы (recEmployee) YYYY как экземпляры пользовательского объекта
clsEmployee.
После задания значений всех свойств объект recEmployee добавляется в
коллекцию. Второй параметр метода Add определяет уникальный ключ запиY
си в коллекции YYYY в данном случае это идентификатор служащего. Наличие
уникального ключа позволяет использовать сокращенную форму обращения к
записи, например, colEmployees("1651").EmpWeeklyPay (рис. 20.9).
Рис. 20.9. Уникальный ключ позволяет использо&вать сокращенную форму обращения к записи вколлекции
На заметку Уникальный ключ записи является необязательным параметром метода Add. В од&ной коллекции не могут существовать две записи с одинаковым значением уни&кального ключа. При попытке добавления записи, приводящей к дублированиюуникальных ключей, будет сгенерировано сообщение об ошибке.
Создание коллекции в модуле класса
При создании коллекции в модуле класса ее основные методы и свойства
(Add, Remove, Count и Item) необходимо определять самостоятельно. ПреY
имущества этого способа создания коллекции заключаются в размещении коY
да коллекции в одном модуле, возможности полного контроля над ее
‘‘поведением’’, а также возможности управления доступом к коллекции.
Добавьте к текущему проекту новый модуль класса и переименуйте его в
clsEmployees. Объявите закрытую переменную типа Collection, которая
будет использоваться только в пределах данного модуля класса.
Private AllEmployees As New Collection
Создание пользовательских объектов, типов и коллекций Глава 20 517
Определите основные свойства и методы коллекции. Для этого воспольY
зуйтесь соответствующими свойствами и методами объекта Collection,
доступными в пределах модуля clsEmployees.
Следующий код определяет метод добавления новых элементов в коллекY
цию Add.
Public Sub Add(recEmployee As clsEmployee) AllEmployees.Add recEmployee, recEmployee.EmpIDEnd Sub
Свойство Count возвращает общее число элементов в коллекции.
Public Property Get Count() As Long Count = AllEmployees.CountEnd Property
Свойство Items используется для обращения ко всей коллекции.
Public Property Get Items() As Collection Set Items = AllEmployeesEnd Property
Свойство Item предназначено для обращения к отдельному элементу колY
лекции.
Public Property Get Item(myItem As Variant) As clsEmployee Set Item = AllEmployees(myItem)End Property
Наконец, метод Remove предназначен для удаления из коллекции заданY
ного элемента.
Public Sub Remove(myItem As Variant) AllEmployees.Remove myItemEnd Sub
При необходимости функциональность основных методов коллекции
можно улучшить, например, реализовать автоматическое создание уникальY
ного ключа записи в методе Add.
Предназначенные только для чтения свойства Count, Items и Item опреY
деляются с помощью выражения Property Get. Свойство Item позволяет
обратиться к одному элементу коллекции, а свойство Items — ко всей колY
лекции целиком (в частности, это свойство удобно применять в цикле ForEach...Next).
Следующий макрос аналогичен рассмотренному ранее макросу EmpPayCol-lection за исключением того, что теперь коллекция пользовательских объектов
clsEmployee размещается не в стандартном модуле, а в модуле класса.
Sub EmpAddCollection() Dim colEmployees As New clsEmployees Dim recEmployee As New clsEmployee Dim LastRow As Integer, myCount As Integer Dim EmpArray As Variant
LastRow = ActiveSheet.Range("A65536").End(xlUp).Row EmpArray = ActiveSheet.Range(Cells(1, 1), Cells(LastRow, 4))
For myCount = 1 To UBound(EmpArray)
518 Часть III Удивительные возможности Visual Basic for Applications
With recEmployee .EmpName = EmpArray(myCount, 1) .EmpID = EmpArray(myCount, 2) .EmpRate = EmpArray(myCount, 3) .EmpWeeklyHrs = EmpArray(myCount, 4) End With colEmployees.Add recEmployee Next myCount
MsgBox "Общее число служащих: " & colEmployees.Count & _Chr(10) & "Имя 2-го служащего в коллекции: " & _colEmployees.Item(2).EmpName
MsgBox "Недельная заработная плата Трейси (долларов): " & _colEmployees.Item("1651").EmpWeeklyPay
For Each recEmployee In colEmployees.Items recEmployee.EmpRate = recEmployee.EmpRate * 1.5 Next recEmployee MsgBox "Недельная заработная плата Трейси с премиальными _(долларов): " & colEmployees.Item("1651").EmpWeeklyPayEnd Sub
Обратите внимание, что переменная colEmployees имеет тип clsEm-ployees, а не тип Collection. Способ заполнения массива и коллекции осY
тался прежним, а вот способ обращения к элементам коллекции претерпел
некоторые изменения. В частности, для обращения ко второму элементу колY
лекции необходимо использовать свойство Item. Сравните вызовы метода
MsgBox в макросах EmpPayCollection и EmpAddCollection.
Цикл For Each...Next используется для увеличения значения свойства
EmpRate всех записей коллекции в 1,5 раза. Результат последнего вызова меY
тода MsgBox в макросе EmpAddCollection показан на рис. 20.10.
Рис. 20.10. При создании коллекции в модуле класса ееметоды и свойства необходимо определять самостоятельно
Практикум
Кнопки получения справочной информацииРассмотрим задачу предоставления справочной информации по содержимомурабочего листа. Несмотря на то, что ее наиболее очевидное решение заключаетсяв использовании примечаний ячеек, это может смутить новичка Excel. Другой спо&
Создание пользовательских объектов, типов и коллекций Глава 20 519
соб состоит в создании кнопок, щелчок на каждой из которых приводит к отобра&жению формы, содержащей справочные сведения.Разместите на рабочем листе три небольшие надписи, содержащие знак вопроса(по одной надписи на строку). Внешний вид надписей, показанных на рис. 20.11,был достигнут за счет установки значения свойства надписи SpecialEffect рав&ным fmSpecialEffectRaised, а свойства BackColor — равным Button Face.Поместите текст справки на два столбца правее соответствующей этому текстукнопки (надписи), после чего скройте столбец справочной информации.
Рис. 20.11. Разместите на рабочем листе кнопки (надписи) и соответствующую им справоч&ную информацию
Создайте пользовательскую форму, содержащую надпись и кнопку Закрытьсправку, как показано на рис. 20.12.
Рис. 20.12. Создайте пользовательскую
форму, которая будет применяться длявывода справочной информации на экран
Переименуйте форму в HelpForm, кнопку — в CloseHelp, а надпись — вHelpText. Измените размер надписи таким образом, чтобы в ней мог поместить&ся текст справки. Следующий макрос выгружает форму HelpForm из памяти прищелчке на кнопке CloseHelp.Private Sub CloseHelp_Click() Unload MeEnd Sub
Добавьте к проекту модуль класса clsLabel. Объявите переменную Lbl для об&ращения к событиям объекта Label.Public WithEvents Lbl As MSForms.Label
Следующая процедура выводит на экран пользовательскую форму с соответст&вующим текстом справки.Private Sub Lbl_Click() Dim Rng As Range
520 Часть III Удивительные возможности Visual Basic for Applications
Set Rng = Lbl.TopLeftCell If Lbl.Caption = "?" Then HelpForm.Caption = "Надпись в ячейке " & Rng.Address(0, 0) HelpForm.HelpText.Caption = Rng.Offset(, 2).Value HelpForm.Show End IfEnd Sub
Разместите в модуле ЭтаКнига (ThisWorkbook) следующий код:Dim col As Collection
Private Sub DefineLabels() Dim WS As Worksheet Dim cLbl As clsLabel Dim OleObj As OLEObject
Set col = New Collection For Each WS In ThisWorkbook.Worksheets For Each OleObj In WS.OLEObjects If OleObj.OLEType = xlOLEControl Then If TypeName(OleObj.Object) = "Label" Then Set cLbl = New clsLabel Set cLbl.Lbl = OleObj.Object col.Add cLbl End If End If Next OleObj Next WSEnd Sub
Выполните процедуру DefineLabels для создания коллекции кнопок получениясправочной информации. Результат щелчка на одной из таких кнопок показан нарис. 20.13.
Рис. 20.13. Результат щелчка на кнопке получения справочной информации
Создание пользовательских типовВ отличие от пользовательских объектов, пользовательские типы не требуY
ют своего размещения в модуле класса. Модуль класса позволяет определить
свойства и методы объекта, тогда как пользовательский тип характеризуется
только свойствами.
Создание пользовательских объектов, типов и коллекций Глава 20 521
Пользовательский тип объявляется с помощью выражения Type...EndType и может быть общедоступным (Public) или закрытым (Private). ПоY
добно объекту, у пользовательского типа есть имя и свойства, которые задаются
с помощью переменных, объявленных внутри выражения Type...End Type.
Применение пользовательских типов на практике ничем не отличается от
применения стандартных типов VBA. Как показано на рис. 20.14, Excel отоY
бражает динамическую подсказку, упрощающую обращение к свойствам пеY
ременной пользовательского типа при наборе программного кода.
Рис. 20.14. Применение пользовательских ти&пов на практике ничем не отличается от приме&нения стандартных типов VBA
Рассмотрим применение двух пользовательских типов для создания отчета
о продажах товаров в сети розничных магазинов.
Тип Style используется для представления товара.
Public Type Style StyleName As String Price As Single UnitsSold As Long UnitsOnHand As LongEnd Type
Тип Store имеет два свойства YYYY имя магазина и массив товаров, каждый
из которых представлен переменной типа Style.
Public Type Store Name As String Styles() As StyleEnd Type
Следующий макрос создает итоговый отчет о продажах товаров в сети розY
ничных магазинов. В приведенном ниже коде используется только переменY
ная типа Store, так как этот тип включает в себя свойство типа Style.
522 Часть III Удивительные возможности Visual Basic for Applications
Sub UDTMain() Dim FinalRow As Long, ThisRow As Long, ThisStore As Long Dim CurrRow As Long, TotalDollarsSold As Long, _TotalUnitsSold As Long Dim TotalDollarsOnHand As Long, TotalUnitsOnHand As Long Dim ThisStyle As Long Dim StoreName As String' Объявление переменной пользовательского типа. ReDim Stores(0 To 0) As Store
FinalRow = Range("A65536").End(xlUp).Row' Следующий цикл For...Next заполняет два массива.' Внешний массив (массив переменных типа Store)' состоит из названий магазинов и перечня имеющихся в них товаров.' Внутренний массив (массив переменных типа Style)' состоит из подробных сведений о товарах. For ThisRow = 2 To FinalRow StoreName = Range("A" & ThisRow).Value' Проверить, есть ли во внешнем массиве хотя бы один элемент. If LBound(Stores) = 0 Then ThisStore = 1 ReDim Stores(1 To 1) As Store Stores(1).Name = StoreName ReDim Stores(1).Styles(0 To 0) As Style Else For ThisStore = LBound(Stores) To UBound(Stores) If Stores(ThisStore).Name = StoreName Then Exit For Next ThisStore If ThisStore > UBound(Stores) Then ReDim Preserve Stores(LBound(Stores) To _UBound(Stores) + 1) As Store Stores(ThisStore).Name = StoreName ReDim Stores(ThisStore).Styles(0 To 0) As Style End If End If With Stores(ThisStore) If LBound(.Styles) = 0 Then ReDim .Styles(1 To 1) As Style Else ReDim Preserve .Styles(LBound(.Styles) To _ UBound(.Styles) + 1) As Style End If With .Styles(UBound(.Styles)) .StyleName = Range("B" & ThisRow).Value .Price = Range("C" & ThisRow).Value .UnitsSold = Range("D" & ThisRow).Value .UnitsOnHand = Range("E" & ThisRow).Value End With End With Next ThisRow
' Создание отчета на новом рабочем листе. Sheets.Add Range("A1:E1").Value = Array("Магазин", "Продано (шт.)", _ "Продано (ден. ед.)", "Остаток (шт.)", "Остаток (ден. ед.)") CurrRow = 2 For ThisStore = LBound(Stores) To UBound(Stores) With Stores(ThisStore) TotalDollarsSold = 0
Создание пользовательских объектов, типов и коллекций Глава 20 523
TotalUnitsSold = 0 TotalDollarsOnHand = 0 TotalUnitsOnHand = 0' Подсчет суммарных сведений о продажах товаров. For ThisStyle = LBound(.Styles) To UBound(.Styles) With .Styles(ThisStyle) TotalDollarsSold = TotalDollarsSold + _.UnitsSold * .Price TotalUnitsSold = TotalUnitsSold + .UnitsSold TotalDollarsOnHand = TotalDollarsOnHand + _.UnitsOnHand * .Price TotalUnitsOnHand = TotalUnitsOnHand + _.UnitsOnHand End With Next ThisStyle Range("A" & CurrRow & ":E" & CurrRow).Value = _ Array(.Name, TotalUnitsSold, TotalDollarsSold, _ TotalUnitsOnHand, TotalDollarsOnHand) End With CurrRow = CurrRow + 1 Next ThisStoreEnd Sub
Результат выполнения макроса UDTMain показан на рис. 20.15.
Рис. 20.15. Пользовательские типы позволяют упростить разработкудостаточно сложного программного кода. (Примечание: для наглядно&сти результаты выполнения макроса UDTMain совмещены с исходны&ми данными.)
524 Часть III Удивительные возможности Visual Basic for Applications
Следующий шагСледующая глава посвящена пользовательским формам. В ней описываютY
ся различные элементы управления, а также приемы программирования польY
зовательских форм.
Панель инструментовUserForm .........................................525Создание коллекцийэлементов управленияформы ........................................... 526Дополнительные элементыуправления формы..................... 528Немодальные формы ................. 531Гиперссылки в формах ............... 531Добавление элементовуправления на формуво время выполненияпрограммного кода.....................532Использование полосыпрокрутки для выборазначений ....................................... 539Добавление подсказки кэлементу управления..................541Порядок переноса фокуса..........541Изменение цвета фонаактивного элементауправления ................................... 542Использование эффектапрозрачности формы................. 545Следующий шаг........................... 546
21Глава 21
Ïîëüçîâàòåëü-ñêèå ôîðìû —ïðîôåññèîíàëü-íûé ïîäõîä
В главе 9, ‘‘Введение в пользоваY
тельские формы’’, рассматривались
основы создания пользовательских
форм. Продолжим знакомство с польY
зовательскими формами, уделив
особое внимание сложным элеменY
там управления, а также различным
приемам программирования польY
зовательских форм.
Панель инструментовUserForm
Чтобы отобразить панель инструY
ментов UserForm, выберите в меню
редактора Visual Basic команду ViewToolbars UserForm (Вид Панели
инструментов UserForm). Панель
инструментов UserForm содержит неY
сколько элементов управления, как поY
казано на рис. 21.1.
На передний планГруппировать
Выравнивание
На задний планОтменить группирование
Центрирование
Масштаб
Уравнять размеры
Рис. 21.1. Панель инструментов UserForm
526 Часть III Удивительные возможности Visual Basic for Applications
Кнопка Bring To Front (На передний план). Вынести выбранный элемент
управления поверх всех остальных элементов управления формы.
Кнопка Send To Back (На задний план). Спрятать выбранный элемент
управления за всеми остальными элементами управления формы.
Кнопка Group (Группировать). Объединить выбранные элементы управY
ления в группу.
Кнопка Ungroup (Отменить группирование). Отменить объединение выY
бранных элементов управления в группу.
Раскрывающийся список Align (Выравнивание). Выровнять выбранные
элементы управления по левому краю (Lefts), по центру (Centers), по
правому краю (Rights), по верхнему краю (Tops), посередине (Middles),
по нижнему краю (Bottoms) или по сетке (To Grid).
Раскрывающийся список Center (Центрирование). Центрировать выY
бранные элементы управления относительно формы по горизонтали
(Horizontally) или по вертикали (Vertically).
Раскрывающийся список Make Same Size (Уравнять размеры). Уравнять
ширину (Width), высоту (Height) или оба измерения (Both) выбранных
элементов управления.
Раскрывающийся список Zoom (Масштаб). Масштабировать элементы
управления формы.
Совет Чтобы выбрать несколько смежных элементов управления, щелкните на первом ина последнем из них, удерживая нажатой клавишу <Shift>. Чтобы выбрать не&сколько несмежных элементов управления, щелкните на каждом из них, удержи&вая нажатой клавишу <Ctrl>.
Создание коллекций элементовуправления формы
В главе 20, ‘‘Создание пользовательских объектов, типов и коллекций’’,
описывалось создание коллекции надписей, использующихся в качестве кноY
пок получения справочной информации. Рассмотрим пример создания колY
лекции других элементов управления формы YYYY флажков.
Разместите следующий код в модуле класса clsFormCtl.
Public WithEvents chb As MSForms.CheckBox
Public Sub SelectAll() chb.Value = TrueEnd Sub
Public Sub UnselectAll()
Пользовательские формы — профессиональный подход Глава 21 527
chb.Value = FalseEnd Sub
Метод SelectAll выделяет флажок путем установки значения свойства
Value равным True. Метод UnselectAll отменяет выделение флажка путем
установки значения свойства Value равным False.
Объекты флажков помещаются в коллекцию при инициализации формы
frmSelectAll. Создание коллекции упрощается за счет того, что все флажки
являются частью панели frm_Selection.
Dim col_Selection As New Collection
Private Sub UserForm_Initialize() For Each ctl In frm_Selection.Controls Set clFormCtl = New clsFormCtl Set clFormCtl.chb = ctl col_Selection.Add clFormCtl Next ctlEnd Sub
Следующий код выделяет все флажки коллекции при щелчке на надписи
Выделить все.
Private Sub lbl_SelectAll_Click() For Each clFormCtl In col_Selection clFormCtl.SelectAll Next clFormCtlEnd Sub
Процедура lbl_UnselectAll_Click, выполняющаяся при щелчке на
кнопке Отменить выделение, отменяет выделение флажков коллекции.
Private Sub lbl_UnselectAll_Click() For Each clFormCtl In col_Selection clFormCtl.UnselectAll Next clFormCtlEnd Sub
Пользовательская форма frmSelectAll показана на рис. 21.2.
Рис. 21.2. Использование коллекций и панелей упро&щает работу с элементами управления формы
528 Часть III Удивительные возможности Visual Basic for Applications
На заметку Объединение флажков в коллекцию никоим образом не сказывается на их функ&циональности. Другими словами, флажки формы frmSelectAll по&прежнемуможно устанавливать/сбрасывать по отдельности.
На заметку Каждый элемент управления формы имеет свойство Tag типа String, содержа&щее дополнительную информацию об элементе управления. Это свойство можноиспользовать для неформального группирования элементов управления, входя&щих в другие группы.
Дополнительные элементы управления формыВ этом разделе продолжается рассмотрение элементов управления пользоY
вательской формы, начатое в главе 9, ‘‘Введение в пользовательские формы’’.
Переключатели
В отличие от флажков, переключатели поддерживают возможность выбора
только одного элемента из группы, как показано на рис. 21.3.
Рис. 21.3. Переключатели поддерживают возможностьвыбора только одного элемента из группы
Возможность выбора только одного элемента из группы может быть реалиY
зована с помощью программного кода, однако переключатели изначально
поддерживают такую функциональность благодаря наличию свойства Group-Name. Все переключатели с одинаковым значением свойства GroupNameпринадлежат к одной группе. Выделение одного из переключателей группы
автоматически приводит к отмене выделения остальных переключателей
группы. Чтобы иметь возможность выбора нескольких переключателей одноY
временно, присвойте им разное значение свойства GroupName или же оставьY
те его незаданным.
Пользовательские формы — профессиональный подход Глава 21 529
Набор вкладок
Вкладки, рассматривавшиеся в главе 9, ‘‘Введение в пользовательские
формы’’, позволяют объединить воедино несколько страниц с различнымиэлементами управления. Набор вкладок (tabstrip) позволяет объединить неY
сколько страниц с одинаковыми элементами управления, общими для всех
входящих в набор вкладок. При переключении вкладок значения элементов
управления меняются, как показано на рис. 21.4.
Рис. 21.4. Набор вкладок позволяет объединитьнесколько страниц с одинаковыми элементамиуправления
См. также Более подробно вкладки рассматриваются в разделе “Использование вкладок дляобъединения форм” главы 9 на с. 225.
По умолчанию набор состоит из двух вкладок. Чтобы добавить, удалить,
переименовать или переместить вкладку, щелкните на ней правой кнопкой
мыши. Измените размер набора вкладок так, чтобы он мог вместить все необY
ходимые элементы управления.
На заметку Кнопку закрытия пользовательской формы следует разместить за пределами на&бора вкладок.
Свойство набора вкладок TabOrientation позволяет определить распоY
ложение корешков вкладок, которые могут располагаться вдоль верхней
(fmTabOrientationTop), нижней (fmTabOrientationBottom), левой
(fmTabOrientationLeft) или правой (fmTabOrientationRight) граниY
цы пользовательской формы.
Следующий код создает набор вкладок, показанный на рис. 21.4. Для
установки значений элементов управления вкладки используется процедура
SetValuesToTabStrip.
Private Sub UserForm_Initialize()' По умолчанию при инициализации формы
530 Часть III Удивительные возможности Visual Basic for Applications
' отображается содержимое первой вкладки. SetValuesToTabStrip 1End Sub
Процедура TabStrip1_Change выполняется при выборе новой вкладки,
что приводит к автоматическому изменению значений элементов управления
вкладки в соответствии с ее порядковым номером.
Private Sub TabStrip1_Change() Dim lngRow As Long lngRow = TabStrip1.Value + 1 SetValuesToTabStrip lngRowEnd Sub
Ниже приведен код процедуры SetValuesToTabStrip.
Private Sub SetValuesToTabStrip(ByVal lngRow As Long) lbl_Address.Caption = Cells(lngRow, 2).Value lbl_Phone.Caption = Cells(lngRow, 3).Value lbl_Fax.Caption = Cells(lngRow, 4).Value lbl_Email.Caption = Cells(lngRow, 5).Value lbl_Website.Caption = Cells(lngRow, 6).ValueEnd Sub
Совет Добавить дополнительный элемент управления к вкладке, входящей в набор,можно только программным путем в момент активизации этой вкладки.
Поле ввода адреса диапазона ячеекПоле ввода адреса диапазона ячеек можно разместить на любой пользоваY
тельской форме. Щелчок на кнопке, расположенной справа от поля ввода,
приводит к скрытию формы и отображению окна ввода адреса диапазона ячеY
ек, знакомого большинству пользователей по многочисленным мастерам ExY
cel. Чтобы вернуть форму на экран, щелкните на кнопке с изображением
формы, расположенной в правой части окна.
Следующая процедура форматирует содержимое диапазона ячеек, адрес
которого был задан с помощью поля ввода, изображенного на рис. 21.5, путем
утолщения шрифта.
Private Sub cb1_Click() Range(RefEdit1.Value).Font.Bold = TrueEnd Sub
Рис. 21.5. Окно ввода адреса диапазона ячеек знакомобольшинству пользователей Excel
Пользовательские формы — профессиональный подход Глава 21 531
Немодальные формыПо умолчанию пользовательская форма является модальной, т.е. такой, коY
торая не позволяет вернуться к просмотру или изменению содержимого рабоY
чего листа Excel до тех пор, пока она не будет закрыта. Чтобы сделать форму
немодальной, установите значение ее свойства ShowModal равным False.
При отображении на экране немодальной формы пользователь может проY
должать работу с содержимым листа Excel без какихYлибо ограничений, как
показано на рис. 21.6.
Рис. 21.6. Немодальная форма позволяет продолжатьработу с листом Excel
Гиперссылки в формахПользовательская форма, показанная на рис. 21.4, содержит надписи с адY
ресом электронной почты и адресом WebYсайта. Рассмотрим код, необходиY
мый для запуска соответствующего приложения при щелчке на каждой из
этих надписей.
Разместите в верхней части модуля объявления требуемых функций API и
переменных.
Private Declare Function ShellExecute Lib "shell32.dll" _Alias "ShellExecuteA" (ByVal hWnd As Long, ByVal lpOperation _As String, ByVal lpFile As String, ByVal lpParameters As String, _ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Const SWNormal = 1
532 Часть III Удивительные возможности Visual Basic for Applications
Процедура lbl_Email_Click обрабатывает щелчок на надписи с адресом
электронной почты (рис. 21.7).
Private Sub lbl_Email_Click() Dim lngRow As Long lngRow = TabStrip1.Value + 1 ShellExecute 0&, "open", "mailto:" & Cells(lngRow, 5).Value, _vbNullString, vbNullString, SWNormalEnd Sub
Рис. 21.7. Всего лишь несколько строк кода способны превратить надписи садресом электронной почты и адресом Web&сайта в гиперссылки
Процедура lbl_Website_Click обрабатывает щелчок на надписи с адреY
сом WebYсайта.
Private Sub lbl_Website_Click() Dim lngRow As Long lngRow = TabStrip1.Value + 1 ShellExecute 0&, "open", Cells(lngRow, 6).Value, _vbNullString, vbNullString, SWNormalEnd Sub
Добавление элементов управления на формуво время выполнения программного кода
Рассмотрим простую форму, показанную на рис. 21.8.
Эта форма, содержащая всего лишь одну кнопку Закрыть, будет использоY
вана для отображения рисунков из некоторого каталога. Рисунки и соответстY
вующие им надписи добавляются на форму во время выполнения программY
ного кода.
Пользовательские формы — профессиональный подход Глава 21 533
Рис. 21.8. Эта форма будет использованадля добавления на нее элементов управ&ления во время выполнения программ&ного кода
Подобная форма может применяться торговыми представителями для выY
вода на экран изображений товаров из каталога. Для этого достаточно выбрать
названия или коды нужных товаров на рабочем листе Excel и щелкнуть на
кнопке, отображающей форму. На рис. 21.9 приведен пример вывода на экран
нескольких фотографий.
Рис. 21.9. Рисунки и соответствующие им надписи добавляются на форму при выполнениипроцедуры инициализации UserForm_Initialize
534 Часть III Удивительные возможности Visual Basic for Applications
При выборе меньшего числа фотографий их изображения будут иметь
больший размер, как показано на рис. 21.10.
Рис. 21.10. Процедура UserForm_Initialize определяет размер рисунков, исходя из ихобщего числа
В следующих разделах рассматриваются приемы программирования, деY
лающие возможным добавление на форму элементов управления во время
выполнения программного кода.
Изменение размеров формы во время выполненияпрограммного кода
Приведенный ниже код использует свойства формы Height и Width для
изменения ее размеров во время выполнения программного кода.
' Изменить размер формы.Me.Height = Int(0.98 * ActiveWindow.Height)Me.Width = Int(0.98 * ActiveWindow.Width)
Добавление элемента управления на форму во времявыполнения программного кода
Ниже приведен пример обращения к свойству элемента управления, доY
бавленного на форму во время ее проектирования.
Me.cbSave.Left = 100
Пользовательские формы — профессиональный подход Глава 21 535
Чтобы обратиться к свойству элемента управления, добавленного на форму
во время выполнения программного кода, необходимо использовать коллекY
цию Controls. Также рекомендуется создать переменную, хранящую имя
элемента управления. Для добавления элемента управления на форму испольY
зуется метод Add коллекции Controls. Параметр bstrProgId метода Addопределяет тип элемента управления.
Приведенный ниже код добавляет на форму надпись. Переменная Pic-Count используется для присвоения элементу управления уникального имеY
ни. Свойства надписи Top и Left определяют ее положение на форме, а
свойства Height и Width — размер надписи.
LC = "LabelA" & PicCountMe.Controls.Add bstrProgId:="forms.label.1", Name:=LC, Visible:=TrueMe.Controls(LC).Top = 25Me.Controls(LC).Left = 50Me.Controls(LC).Height = 18Me.Controls(LC).Width = 60Me.Controls(LC).Caption = Cell.Value
Определение размера и положения элемента управленияна форме во время выполнения программного кода
Определение размера (свойства Height и Width) и положения (Top и Left)
элемента управления на форме осуществляется с учетом высоты и ширины
формы, а также общего числа помещенных на форму элементов управления.
Ограничения, связанные с добавлением элементовуправления на форму во время выполненияпрограммного кода
Excel не сможет отобразить динамическую подсказку при обращении к
элементам управления, добавляемым на форму во время выполнения проY
граммного кода. К примеру, при наборе выражения Me.cbClose. Excel выY
водит на экран подсказку, содержащую доступные методы, свойства и собыY
тия соответствующего объекта. С другой стороны, при наборе выражения
Me.Controls(LC). тип элемента управления заранее неизвестен. В связи с
этим, в частности, следует помнить о том, что текст надписи задается свойстY
вом Caption, а не свойством Value.
Типы элементов управления
Как уже упоминалось, тип добавляемого на форму элемента управления
определяется параметром bstrProgId метода Controls.Add. Возможные
значения этого параметра перечислены в табл. 21.1.
536 Часть III Удивительные возможности Visual Basic for Applications
Таблица 21.1. Типы элементов управления
Элемент управления Значение параметра bstrProgId
Флажок (CheckBox) Forms.CheckBox.1
Комбинированный список (ComboBox) Forms.ComboBox.1
Кнопка (CommandButton) Forms.CommandButton.1
Панель (Frame) Forms.Frame.1
Изображение (Image) Forms.Image.1
Надпись (Label) Forms.Label.1
Список (ListBox) Forms.ListBox.1
Вкладка (MultiPage) Forms.MultiPage.1
Переключатель (OptionButton) Forms.OptionButton.1
Полоса прокрутки (ScrollBar) Forms.ScrollBar.1
Счетчик (SpinButton) Forms.SpinButton.1
Набор вкладок (TabStrip) Forms.TabStrip.1
Поле ввода (TextBox) Forms.TextBox.1
Выключатель (ToggleButton) Forms.ToggleButton.1
Добавление изображения на форму во время выполненияпрограммного кода
Добавление изображения на форму во время выполнения программного
кода связано с определенными трудностями, поскольку ни ориентация изоY
бражения YYYY альбомная или портретная, YYYY ни его размер заранее неизвестны.
Решение этой задачи рекомендуется разбить на три этапа. Загрузите изобраY
жение в натуральную величину, установив значение параметра AutoSizeравным True, как показано ниже:
TC = "Image" & PicCountMe.Controls.Add bstrProgId:="forms.image.1", Name:=TC, Visible:=TrueMe.Controls(TC).Top = LastTopMe.Controls(TC).Left = LastLeftMe.Controls(TC).AutoSize = True
On Error Resume NextMe.Controls(TC).Picture = LoadPicture(fname)On Error GoTo 0
Загрузив изображение, определите его размер и ориентацию с помощью
свойств Height и Width:
' Определить размер изображения.Wid = Me.Controls(TC).WidthHt = Me.Controls(TC).HeightWidRedux = CellWid / WidHtRedux = CellHt / Ht
Пользовательские формы — профессиональный подход Глава 21 537
If WidRedux < HtRedux Then Redux = WidReduxElse Redux = HtReduxEnd If
NewHt = Int(Ht * Redux)NewWid = Int(Wid * Redux)
Определив исходный размер изображения, установите значение свойства
AutoSize равным False и задайте требуемые значения свойств Height и
Width.
' Изменить размер изображения.Me.Controls(TC).AutoSize = FalseMe.Controls(TC).Height = NewHtMe.Controls(TC).Width = NewWidMe.Controls(TC).PictureSizeMode = fmPictureSizeModeStretch
Результирующий код
Ниже приведен полный текст процедуры UserForm_Initialize, доY
бавляющей элементы управления на форму во время выполнения проY
граммного кода.
Private Sub UserForm_Initialize()' Эта процедура помещает на форму фотографии, имена' файлов которых выбираются с помощью рабочего листа Excel. PicPath = ThisWorkbook.Path & Application.PathSeparator
' Изменить размер формы. Me.Height = Int(0.98 * ActiveWindow.Height) Me.Width = Int(0.98 * ActiveWindow.Width)
' Определить число выбранных ячеек. CellCount = Selection.Cells.Count ReDim Preserve Pics(1 To CellCount)
' Определить размер пользовательской формы. TempHt = Me.Height TempWid = Me.Width
' Определить число столбцов и строк с изображениями. NumCol = Int(0.99 + Sqr(CellCount)) NumRow = Int(0.99 + CellCount / NumCol)
' Зарезервировать 2 точки слева и' справа от каждого изображения. CellWid = Application.WorksheetFunction.Max(Int(TempWid _/ NumCol) - 4, 1)' Зарезервировать 33 точки под каждым' изображением для размещения надписи. CellHt = Application.WorksheetFunction.Max(Int(TempHt _/ NumRow) - 33, 1)
' Переменная-счетчик. PicCount = 0
538 Часть III Удивительные возможности Visual Basic for Applications
LastTop = 2 MaxBottom = 1' Создать строку изображений. For X = 1 To NumRow LastLeft = 3' Создать столбец изображений. For Y = 1 To NumCol PicCount = PicCount + 1 If PicCount > CellCount Then' Изменить размер формы, чтобы' вместить все изображения. Me.Height = MaxBottom + 100 Me.cbClose.Top = MaxBottom + 25 Me.cbClose.Left = Me.Width - 50
Repaint
Exit Sub End If
ThisStyle = Selection.Cells(PicCount).Value ThisDesc = Selection.Cells(PicCount).Offset(0, 1).Value fname = PicPath & ThisStyle & ".jpg "
TC = "Image" & PicCount Me.Controls.Add bstrProgId:="forms.image.1", _Name:=TC, Visible:=True Me.Controls(TC).Top = LastTop Me.Controls(TC).Left = LastLeft Me.Controls(TC).AutoSize = True
On Error Resume Next Me.Controls(TC).Picture = LoadPicture(fname) On Error GoTo 0
' Определить размер изображения. Wid = Me.Controls(TC).Width Ht = Me.Controls(TC).Height WidRedux = CellWid / Wid HtRedux = CellHt / Ht If WidRedux < HtRedux Then Redux = WidRedux Else Redux = HtRedux End If
NewHt = Int(Ht * Redux) NewWid = Int(Wid * Redux)
' Изменить размер изображения. Me.Controls(TC).AutoSize = False Me.Controls(TC).Height = NewHt Me.Controls(TC).Width = NewWid Me.Controls(TC).PictureSizeMode = _fmPictureSizeModeStretch
Me.Controls(TC).ControlTipText = ThisStyle & ".jpg"
Пользовательские формы — профессиональный подход Глава 21 539
Me.Controls(TC).Tag = fname
' Определить координаты нижнего правого угла изображения. ThisRight = Me.Controls(TC).Left + _Me.Controls(TC).Width ThisBottom = Me.Controls(TC).Top + _Me.Controls(TC).Height If ThisBottom > MaxBottom Then MaxBottom = ThisBottom
' Добавить подрисуночную надпись. LC = "LabelA" & PicCount Me.Controls.Add bstrProgId:="forms.label.1", _Name:=LC, Visible:=True Me.Controls(LC).Top = ThisBottom + 1 Me.Controls(LC).Left = LastLeft Me.Controls(LC).Height = 18 Me.Controls(LC).Width = CellWid Me.Controls(LC).Caption = ThisDesc
' Эта строка позволяет увеличить' изображение при щелчке на нем кнопкой мыши. Set Pics(PicCount).PictureGroup = Me.Controls(TC)
' Определить координаты следующего изображения. LastLeft = LastLeft + CellWid + 4' Конец строки. Next Y LastTop = MaxBottom + 21 + 16 Next X
Me.Height = MaxBottom + 100 Me.cbClose.Top = MaxBottom + 25 Me.cbClose.Left = Me.Width - 50
RepaintEnd Sub
Использование полосы прокруткидля выбора значений
В главе 9, “Введение в пользовательские формы”, описывалось использоY
вание счетчика для выбора даты. Недостаток счетчика состоит в том, что
единственный способ изменения его значения заключается в щелчке на одной
из клавиш счетчика. Альтернативный подход к выбору значения состоит в исY
пользовании полосы прокрутки. Помимо щелчка на клавишах полосы проY
крутки, ее значение можно выбрать путем перемещения ползунка.
Пользовательская форма, показанная на рис. 21.11 и 21.12, содержит надY
пись Label1 и полосу прокрутки ScrollBar1.
540 Часть III Удивительные возможности Visual Basic for Applications
Рис. 21.11. Полоса прокрутки можетприменяться в качестве альтернативысчетчику
Рис. 21.12. Преимущество полосы про&крутки заключается в возможности бы&строго изменения ее значения с помо&щью ползунка
Процедура UserForm_Initialize задает начальное, минимальное и
максимальное значение полосы прокрутки, а также текст надписи.
Private Sub UserForm_Initialize() Me.ScrollBar1.Min = 0 Me.ScrollBar1.Max = 100 Me.ScrollBar1.Value = Range("A1").Value Me.Label1.Caption = Me.ScrollBar1.ValueEnd Sub
Обработчик события ScrollBar1_Change выполняется при щелчке на
одной из кнопок полосы прокрутки.
Private Sub ScrollBar1_Change()' Событие ScrollBar1_Change срабатывает' при щелчке на одной из кнопок полосы прокрутки. Me.Label1.Caption = Me.ScrollBar1.ValueEnd Sub
Обработчик события ScrollBar1_Scroll выполняется при щелчке на
ползунке полосы прокрутки.
Private Sub ScrollBar1_Scroll()' Событие ScrollBar1_Scroll срабатывает' при щелчке на ползунке полосы прокрутки. Me.Label1.Caption = Me.ScrollBar1.ValueEnd Sub
Обработчик события CommandButton1_Click выполняется при щелчке
на кнопке OK. Следующий код копирует значение полосы прокрутки в ячейку
A1 рабочего листа Excel и закрывает пользовательскую форму.
Private Sub CommandButton1_Click() Range("A1").Value = Me.ScrollBar1.Value Unload MeEnd Sub
Пользовательские формы — профессиональный подход Глава 21 541
Добавление подсказки к элементу управленияИспользование сочетаний клавиш
Сочетания клавиш позволяют инициировать выполнение действий, наY
пример, нажатие клавиши или установку флажка на форме. Как правило, соY
четание клавиш определяется подчеркнутой буквой в названии кнопки или
тексте надписи.
Чтобы назначить сочетание клавиш для элемента управления пользоваY
тельской формы, задайте значение свойства Accelerator. Действие, связанY
ное с этим элементом управления, будет выполняться при нажатии комбинаY
ции клавиш <Alt+значение свойства Accelerator>. Как показано на
рис. 21.13, для установки/снятия флажка VHS можно применять сочетание
клавиш <Alt+H>.
Рис. 21.13. Сочетания клавиш используются для вы&полнения различных действий, связанных с элемен&тами управления формы
Подсказка элемента управления
При подведении указателя мыши к кнопке панели инструментов Excel на
экране отображается подсказка, описывающая предназначение этой кнопки.
Аналогичную функциональность можно реализовать для любого элемента
управления формы путем установки значения свойства ControlTipText.
На рис. 21.14 показана подсказка, отображающаяся на экране при подведении
указателя мыши к переключателю Комедия.
Порядок переноса фокусаВсе пользовательские формы поддерживают перенос фокуса с одного элеY
мента управления на другой при нажатии клавиши <Tab>. Порядок переноса
фокуса определяется свойствами элемента управления TabStop и TabIndex.
542 Часть III Удивительные возможности Visual Basic for Applications
Рис. 21.14. Подсказки упрощают работу с пользова&тельской формой
Булево свойство TabStop определяет саму возможность переноса фокуса
на элемент управления при нажатии клавиши <Tab>, а свойство TabIndex —
порядковый номер элемента управления в группе (с отсчетом от нуля). Для
создания группы элементов управления можно использовать панель. Два элеY
мента управления в группе не могут иметь одинаковое значение свойства
TabIndex. После переноса фокуса на флажок или переключатель значения
последних можно устанавливать с помощью клавиши пробела (рис. 21.15).
Рис. 21.15. Значения элементов управления этой фор&мы можно устанавливать с помощью клавиши <Tab> иклавиши пробела
Изменение цвета фона активного элементауправления
Изменение цвета фона активного элемента управления способно упростить
взаимодействие пользователя с формой. Рассмотрим пример изменения цвета
фона поля ввода и комбинированного списка, как показано на рис. 21.16.
Пользовательские формы — профессиональный подход Глава 21 543
Рис. 21.16. Изменение цвета фо&на активного элемента управле&ния способно упростить взаи&модействие пользователя сформой
Разместите следующий код в модуле класса clsCtlColor.
Public Event GetFocus()Public Event LostFocus(ByVal strCtrl As String)
Private strPreCtr As String
Public Sub CheckActiveCtrl(objForm As MSForms.UserForm) With objForm If TypeName(.ActiveControl) = "ComboBox" Or _ TypeName(.ActiveControl) = "TextBox" Then strPreCtr = .ActiveControl.Name On Error GoTo Terminate Do DoEvents If .ActiveControl.Name <> strPreCtr Then If TypeName(.ActiveControl) = "ComboBox" Or _ TypeName(.ActiveControl) = "TextBox" Then RaiseEvent LostFocus(strPreCtr) strPreCtr = .ActiveControl.Name RaiseEvent GetFocus End If End If Loop End If End WithTerminate:End Sub
Ниже приведен код, который необходимо поместить в модуль формы.
Private WithEvents objForm As clsCtlColor
Private Sub UserForm_Initialize() Set objForm = New clsCtlColorEnd Sub
Процедура UserForm_Activate изменяет цвет фона (свойство Back-Color) активного элемента управления при отображении формы на экране.
Private Sub UserForm_Activate() If TypeName(ActiveControl) = "ComboBox" Or _ TypeName(ActiveControl) = "TextBox" Then ActiveControl.BackColor = &HC0E0FF
544 Часть III Удивительные возможности Visual Basic for Applications
End If objForm.CheckActiveCtrl MeEnd Sub
Обработчик события objForm_GetFocus изменяет цвет фона элемента
управления, на который был перенесен фокус.
Private Sub objForm_GetFocus() ActiveControl.BackColor = &HC0E0FFEnd Sub
Обработчик события objForm_LostFocus изменяет цвет фона элемента
управления, с которого был перенесен фокус.
Private Sub objForm_LostFocus(ByVal strCtrl As String) Controls(strCtrl).BackColor = &HFFFFFFEnd Sub
Процедура UserForm_QueryClose удаляет объект objForm из памяти
при закрытии формы.
Private Sub UserForm_QueryClose(Cancel As Integer, _CloseMode As Integer) Set objForm = NothingEnd Sub
Практикум
Список с несколькими столбцамиРассмотрим следующую задачу. На рабочем листе Excel содержится информация омагазинах, включающая их названия и коды. Необходимо создать форму, позволяю&щую выбрать магазин по его названию и возвращающую в качестве результата кодмагазина. Несмотря на то, что подобная задача может быть решена с помощью функ&ций ВПР (VLOOKUP) и ПОИСКПОЗ (MATCH), рассмотрим альтернативный метод.Элемент управления ListBox (список) может содержать несколько столбцов,лишь часть из которых будет видна пользователю. К тому же список позволяетвыбрать столбец, значение которого будет возвращено в качестве результата.Разместите на форме список и установите значение его свойства ColumnCountравным 2. Установите значение свойства RowSource равным Магазины, гдеМагазины — это имя диапазона ячеек, включающего список кодов (первый стол&бец) и названий (второй столбец) магазинов. Чтобы скрыть первый столбец, уста&новите значение свойства ColumnWidth равным 0 pt;100 pt, как показано нарис. 21.17.Теперь при отображении формы на экране пользователь увидит вполне привыч&ный список с одним столбцом. Чтобы возвратить значение первого столбца(столбца кодов магазинов), установите значение свойства списка BoundColumnравным 1. Это можно сделать с помощью окна свойств списка или с помощьюпрограммного кода, как показано ниже:Private Sub lb_StoreName_Click() lb_StoreName.BoundColumn = 1 lbl_StoreNum.Caption = lb_StoreNameEnd Sub
Пользовательские формы — профессиональный подход Глава 21 545
Рис. 21.17. Свойство списка ColumnWidth позволяет скрыть столбцы,не предназначенные для отображения на экране
На рис. 21.18 показан результат решения поставленной задачи с помощью списка снесколькими столбцами.
Рис. 21.18. Список с несколькими
столбцами позволяет отображатьна экране один столбец, а возвращатьв качестве результата значение друго&го столбца
Использование эффекта прозрачности формыКак показано на рис. 21.19, эффект прозрачности формы позволяет видеть
содержимое расположенного под ней рабочего листа Excel.
Разместите следующий код в начале модуля формы:
Private Declare Function GetActiveWindow Lib "USER32" () As LongPrivate Declare Function SetWindowLong Lib "USER32" Alias _ "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, _ ByVal dwNewLong As Long) As LongPrivate Declare Function GetWindowLong Lib "USER32" Alias _ "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) _ As LongPrivate Declare Function SetLayeredWindowAttributes Lib "USER32" _ (ByVal hWnd As Long, ByVal crKey As Integer, _ ByVal bAlpha As Integer, ByVal dwFlags As Long) As Long
546 Часть III Удивительные возможности Visual Basic for Applications
Private Const WS_EX_LAYERED = &H80000Private Const LWA_COLORKEY = &H1Private Const LWA_ALPHA = &H2Private Const GWL_EXSTYLE = &HFFEC
Рис. 21.19. Полупрозрачная форма
Обработчик события UserForm_Activate вызывает функции Windows
API, необходимые для достижения эффекта прозрачности.
Private Sub UserForm_Activate() Dim nIndex As Long
hWnd = GetActiveWindow nIndex = GetWindowLong(hWnd, GWL_EXSTYLE) SetWindowLong hWnd, GWL_EXSTYLE, nIndex Or WS_EX_LAYERED' Вызов этой функции позволяет достичь эффекта прозрачности. SetLayeredWindowAttributes hWnd, 0, (255 * (100 - rt)) / _100, LWA_ALPHAEnd Sub
Следующий шагСледующая глава посвящена основам использования функций интерфейса
прикладного программирования (API) Windows.
Знакомство с Windows API.........547Объявления Windows API.......... 548Использование объявленийWindows API ................................. 548Примеры полезныхобъявлений Windows API.......... 549Дополнительные источникиобъявлений Windows API.......... 559Следующий шаг........................... 559
22Ãëàâà 22
Èíòåðôåéñïðèêëàäíîãîïðîãðàììèðî-âàíèÿ (API)Windows
Знакомствос Windows API
Несмотря на разнообразие встроY
енных функций Excel VBA, сущестY
вуют задачи, которые можно выполY
нить только с помощью средств инY
терфейса прикладного программироY
вания (API) Windows.
Заглянув в папку \Windows\System32 (или \Winnt\System32)
на системном диске компьютера,
можно увидеть множество файлов с
расширением .dll. Эти файлы наY
зываются динамически подключаемы*ми библиотеками, содержащими проY
цедуры и функции, доступные друY
гим программам (например, Excel).
Следует помнить, что средства WinY
dows API доступны только на компьюY
терах, работающих под управлением
операционной системы семейства
Microsoft Windows.
В этой главе рассматриваются осY
новы использования объявлений
Windows API, а также приводится неY
сколько полезных примеров.
548 Часть III Удивительные возможности Visual Basic for Applications
Объявления Windows API Рассмотрим пример объявления Windows API.
Private Declare Function GetUserName Lib "advapi32.dll" Alias _"GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Существует два типа объявлений Windows API — функции, возвращающие
информацию, и процедуры, выполняющие какиеYлибо действия.
Разберем структуру приведенного выше объявления Windows API.
Private. Ключевое слово, определяющее переменную, функцию или
процедуру, использующуюся в пределах одного модуля. Чтобы объяY
вить общедоступную переменную, функцию или процедуру, примеY
няйте ключевое слово Public.
Внимание Объявления Windows API, размещенные в стандартном модуле, могут иметь об&ласть видимости Public или Private. Объявления Windows API, размещенные вмодуле класса, должны иметь область видимости Private.
Declare Function GetUserName. Объявление функции GetUser-Name. Имя функции может быть произвольным.
Lib "advapi32.dll". Функция API находится в библиотеке ad-vapi32.dll.
Alias "GetUserNameA". Псевдоним функции в библиотеке DLL.
Псевдоним чувствителен к регистру и должен быть набран в том виде, в
котором он существует в библиотеке DLL. Обычно каждая функция
API имеет два псевдонима. Псевдоним функции API, использующей
набор символов ANSI, заканчивается буквой A, а псевдоним функции
API, использующей набор символов Unicode, — буквой W.
ByVal lpBuffer As String, nSize As Long. Функция API приY
нимает два параметра YYYY lpBuffer типа String и nSize типа Long.
Использование объявлений Windows APIИспользование объявления Windows API ничем не отличается от вызова
функции или процедуры VBA. Ниже приведен пример использования объявY
ления Windows API GetUserName.
Private Function UserName() As String Dim sName As String * 256 Dim cChars As Long
cChars = 256 If GetUserName(sName, cChars) Then UserName = Left$(sName, cChars - 1) End If
Интерфейс прикладного программирования (API) Windows Глава 22 549
End Function
Sub ProgramRights() Dim NameofUser As String
NameofUser = UserName Select Case NameofUser Case Is = "Администратор" MsgBox "Вы имеете права администратора на этом компьютере" Case Else MsgBox "Вы имеете ограниченные права на этом компьютере" End SelectEnd Sub
Макрос ProgramRights позволяет узнать, имеет ли текущий пользоваY
тель права администратора компьютера (рис. 22.1).
Примеры полезных объявлений Windows APIВ этом разделе рассматриваются полезные объявления Windows API вместе
с краткими описаниями и примерами использования.
Определение имени компьютера
Объявление Windows API GetComputerName возвращает имя компьютера.
Private Declare Function GetComputerName Lib "kernel32" _Alias "GetComputerNameA" (ByVal lpBuffer As String, _ByRef nSize As Long) As Long
Private Function ComputerName() As String Dim stBuff As String * 255, lAPIResult As Long Dim lBuffLen As Long
lBuffLen = 255 lAPIResult = GetComputerName(stBuff, lBuffLen) If lBuffLen > 0 Then ComputerName = Left(stBuff, lBuffLen)End Function
Sub ComputerCheck() Dim CompName As String
CompName = ComputerName MsgBox Prompt:=CompName, Buttons:=vbOKOnly, _Title:="Имя компьютера"End Sub
Макрос ComputerCheck выводит на экран окно сообщения, содержащего
имя компьютера. Результат выполнения макроса ComputerCheck показан на
рис. 22.2.
550 Часть III Удивительные возможности Visual Basic for Applications
Рис. 22.1. Объявление Windows APIGetUserName позволяет узнать имяпользователя компьютера и опреде&лить, имеет ли пользователь праваадминистратора
Рис. 22.2. Объявление WindowsAPI GetComputerName позволяетузнать имя компьютера
Проверка возможности доступа к файлу
Объявления Windows API lOpen и lClose позволяют узнать, открыт или
закрыт заданный файл.
Private Declare Function lOpen Lib "kernel32" Alias "_lopen" _ (ByVal lpPathName As String, ByVal iReadWrite As Long) As LongPrivate Declare Function lClose Lib "kernel32" _ Alias "_lclose" (ByVal hFile As Long) As Long
Private Function FileIsOpen(strFullPath_FileName _As String) As Boolean Dim hdlFile As Long Dim lastErr As Long
hdlFile = -1 hdlFile = lOpen(strFullPath_FileName, OF_SHARE_EXCLUSIVE) If hdlFile = -1 Then lastErr = Err.LastDllError Else lClose hdlFile End If FileIsOpen = (hdlFile = -1) And (lastErr = 32)End Function
Sub CheckFileOpen() Dim txtFile As String txtFile = GetFileName("c:\") If txtFile = "" Then Exit Sub If FileIsOpen(txtFile) Then MsgBox "Файл занят (открыт)" Else MsgBox "Файл не занят" End IfEnd Sub
Макрос CheckFileOpen позволяет выбрать файл и узнать, открыт он или
закрыт (рис. 22.3).
Определение разрешения экрана
Объявление Windows API DisplaySize позволяет определить разрешение
экрана компьютера.
Интерфейс прикладного программирования (API) Windows Глава 22 551
Private Declare Function DisplaySize Lib "user32" Alias _ "GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Function VideoRes() As String Dim vidWidth Dim vidHeight
vidWidth = DisplaySize(SM_CXSCREEN) vidHeight = DisplaySize(SM_CYSCREEN) Select Case (vidWidth * vidHeight) Case 307200 VideoRes = "640 x 480" Case 480000 VideoRes = "800 x 600" Case 786432 VideoRes = "1024 x 768" Case Else VideoRes = "Другое разрешение" End SelectEnd Function
Sub CheckDisplayRes() Dim VideoInfo As String Dim Msg1 As String, Msg2 As String, Msg3 As String
VideoInfo = VideoRes Msg1 = "Текущее разрешение экрана: " & VideoInfo & Chr(10) Msg2 = "Оптимальное разрешение экрана для этого приложения: _1024 x 768 " & Chr(10) Msg3 = "Пожалуйста, увеличьте разрешение экрана" Select Case VideoInfo Case Is = "640 x 480" MsgBox Msg1 & Msg2 & Msg3 Case Is = "800 x 600" MsgBox Msg1 & Msg2 & Msg3 Case Is = "1024 x 768" MsgBox Msg1 Case Else MsgBox Msg2 & Msg3 End SelectEnd Sub
Макрос CheckDisplayRes проверяет текущее разрешение экрана и при
необходимости рекомендует его увеличить, как показано на рис. 22.4.
Рис. 22.3. Объявления Windows APIlOpen и lClose могут быть ис&пользованы для того, чтобы узнать,открыт ли заданный файл
Рис. 22.4. Объявление Windows API Display-Size позволяет определить разрешение экранакомпьютера
552 Часть III Удивительные возможности Visual Basic for Applications
Блокирование кнопки закрытия окна приложения
В правом верхнем углу окна приложения находится кнопка, предназначенY
ная для его закрытия (кнопка с изображением знака ‘‘×’’). Блокировать кнопY
ку закрытия окна приложения можно с помощью объявлений Windows API
FindWindow, GetSystemMenu и DeleteMenu.
На заметку Не забудьте разблокировать кнопку закрытия окна приложения с помощью выра&жения XButtonEnabled True.
Private Declare Function FindWindow Lib "user32" Alias _"FindWindowA" (ByVal lpClassName As String, _ByVal lpWindowName As String) As LongPrivate Declare Function GetSystemMenu Lib "user32" _(ByVal hwnd As Long, ByVal bRevert As Long) As LongPrivate Declare Function DeleteMenu Lib "user32" (ByVal hMenu _As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Private Sub XButtonEnabled(ByVal bEnabled As Boolean) Dim hWndForm As Long Dim hMenu As Long
hWndForm = FindWindow("XLMAIN", Application.Caption) hMenu = GetSystemMenu(hWndForm, bEnabled) DeleteMenu hMenu, SC_CLOSE, 0&End Sub
Sub DisableExcelXButton() XButtonEnabled False MsgBox "Кнопка закрытия окна приложения заблокирована"End Sub
Sub EnableExcelXButton() XButtonEnabled True MsgBox "Кнопка закрытия окна приложения разблокирована"End Sub
Обратите внимание, что псевдоним функции DLL указан только в первом
объявлении Windows API. На самом деле, псевдоним можно и не указывать,
однако тогда имя объявляемой функции должно полностью совпадать с ее
именем в библиотеке DLL.
На рис. 22.5 показан результат выполнения макроса DisableExcelXButton.
Блокирование кнопки закрытия окна формы
По аналогии с предыдущим примером создадим код, блокирующий кнопку
закрытия окна формы при инициализации последней.
Private Declare Function FindWindow Lib "user32" Alias _"FindWindowA" (ByVal lpClassName As String, _ByVal lpWindowName As String) As LongPrivate Declare Function GetSystemMenu Lib "user32" _
Интерфейс прикладного программирования (API) Windows Глава 22 553
(ByVal hwnd As Long, ByVal bRevert As Long) As LongPrivate Declare Function DeleteMenu Lib "user32" (ByVal hMenu _As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Private Const SC_CLOSE As Long = &HF060
Private Sub UserForm_Initialize() Dim hWndForm As Long Dim hMenu As Long
hWndForm = FindWindow("ThunderDFrame", Me.Caption) hMenu = GetSystemMenu(hWndForm, 0) DeleteMenu hMenu, SC_CLOSE, 0&End Sub
Результат выполнения обработчика события UserForm_Initialize поY
казан на рис. 22.6. Пользователь может закрыть форму только с помощью
щелчка на кнопке Выход.
Рис. 22.5. Для блокирования кнопки за&крытия окна приложения используютсяобъявления Windows API FindWindow,GetSystemMenu и DeleteMenu
Рис. 22.6. Единственный спо&соб закрыть форму заключа&ется в щелчке на кнопкеВыход
Часы
Функция Excel VBA ТДАТА (NOW) возвращает текущие дату и время, однако
ее возможностей оказывается недостаточно для реализации простых часов.
Данную функциональность можно получить с помощью объявлений Windows
API SetTimer, KillTimer и FindWindow, как показано ниже.
Private Declare Function SetTimer Lib "user32" _ (ByVal hwnd As Long, ByVal nIDEvent As Long, _ ByVal uElapse As Long, ByVal lpTimerFunc As Long) As LongPrivate Declare Function KillTimer Lib "user32" _ (ByVal hwnd As Long, ByVal nIDEvent As Long) As LongPrivate Declare Function FindWindow Lib "user32" _ Alias "FindWindowA" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long
Private lngTimerID
Sub StartTimer()' Остановить текущий таймер. StopTimer
554 Часть III Удивительные возможности Visual Basic for Applications
lngTimerID = SetTimer(0, 1, 10, AddressOf RunTimer)End Sub
Sub StopTimer() Dim lRet As Long, lngTID As Long
If IsEmpty(lngTimerID) Then Exit Sub
lngTID = lngTimerID lRet = KillTimer(0, lngTID) lngTimerID = EmptyEnd Sub
Private Sub RunTimer(ByVal hwnd As Long, _ ByVal uint1 As Long, ByVal nEventId As Long, _ ByVal dwParam As Long)
On Error Resume Next Sheet1.Range("A1").Value = Format(Now, "hh:mm:ss")End Sub
Макрос StartTimer размещает часы в ячейке A1, как показано на
рис. 22.7.
Рис. 22.7. Часы, созданные с помощью объявлений Windows API Set-Timer, KillTimer и FindWindow
Создание гиперссылок
Объявление Windows API ShellExecute позволяет разместить на форме
гиперссылки YYYY надписи с адресом электронной почты или адресом WebY
сайта, щелчок на которых приводит к запуску соответствующего приложения.
Private Declare Function ShellExecute Lib "shell32.dll" _ Alias "ShellExecuteA" (ByVal hwnd As Long, _ ByVal lpOperation As String, ByVal lpFile As String, _ ByVal lpParameters As String, ByVal lpDirectory As String, _ ByVal nShowCmd As Long) As Long
Const SWNormal = 1
Private Sub lbl_Email_Click() Dim lngRow As Long
ShellExecute 0&, "open", "mailto:" & lbl_Email.Caption, _ vbNullString, vbNullString, SWNormalEnd Sub
Private Sub lbl_Website_Click() Dim lngRow As Long
ShellExecute 0&, "open", lbl_Website.Caption, _ vbNullString, vbNullString, SWNormalEnd Sub
Интерфейс прикладного программирования (API) Windows Глава 22 555
Щелчок на надписи [email protected] приводит к открытию программы,
предназначенной для работы с электронной почтой, а щелчок на надписи
www.mrexcel.com — к запуску обозревателя Internet, как показано на рис. 22.8.
Рис. 22.8. Объявление Windows API ShellExecute позволяет разместить на фор&ме гиперссылку надпись, щелчок на которой приводит к запуску внешнего при&ложения
Воспроизведение звуковых файлов
Желаете предупредить пользователя об опасности или поздравить его с усY
пешным выполнением задачи посредством воспроизведения звукового файла?
Воспользуйтесь объявлением Windows API PlayWavSound, как показано ниже.
Private Declare Function PlayWavSound Lib "winmm.dll" _ Alias "sndPlaySoundA" (ByVal LpszSoundName As String, _ ByVal uFlags As Long) As Long
Private Sub CommandButton2_Click() SoundName = Application.GetOpenFilename("Формат WAV _(*.wav), *.wav", , "Выберите файл формата WAV", "Воспроизвести") If SoundName = False Then Exit Sub
PlayWavSound SoundName, 0End Sub
Создание диалогового окна выбора файла
Рассмотрим код, использующий объявления Windows API для создания
диалогового окна выбора файла.
556 Часть III Удивительные возможности Visual Basic for Applications
Type tagOPENFILENAME lStructSize As Long hwndOwner As Long hInstance As Long strFilter As String strCustomFilter As String nMaxCustFilter As Long nFilterIndex As Long strFile As String nMaxFile As Long strFileTitle As String nMaxFileTitle As Long strInitialDir As String strTitle As String Flags As Long nFileOffset As Integer nFileExtension As Integer strDefExt As String lCustData As Long lpfnHook As Long lpTemplateName As StringEnd Type
Declare Function aht_apiGetOpenFileName Lib "comdlg32.dll" _ Alias "GetOpenFileNameA" (OFN As tagOPENFILENAME) As BooleanDeclare Function aht_apiGetSaveFileName Lib "comdlg32.dll" _ Alias "GetSaveFileNameA" (OFN As tagOPENFILENAME) As BooleanDeclare Function CommDlgExtendedError Lib "comdlg32.dll" () As Long
Global Const ahtOFN_READONLY = &H1Global Const ahtOFN_OVERWRITEPROMPT = &H2Global Const ahtOFN_HIDEREADONLY = &H4Global Const ahtOFN_NOCHANGEDIR = &H8Global Const ahtOFN_SHOWHELP = &H10Global Const ahtOFN_NOVALIDATE = &H100Global Const ahtOFN_ALLOWMULTISELECT = &H200Global Const ahtOFN_EXTENSIONDIFFERENT = &H400Global Const ahtOFN_PATHMUSTEXIST = &H800Global Const ahtOFN_FILEMUSTEXIST = &H1000Global Const ahtOFN_CREATEPROMPT = &H2000Global Const ahtOFN_SHAREAWARE = &H4000Global Const ahtOFN_NOREADONLYRETURN = &H8000Global Const ahtOFN_NOTESTFILECREATE = &H10000Global Const ahtOFN_NONETWORKBUTTON = &H20000Global Const ahtOFN_NOLONGNAMES = &H40000Global Const ahtOFN_EXPLORER = &H80000Global Const ahtOFN_NODEREFERENCELINKS = &H100000Global Const ahtOFN_LONGNAMES = &H200000
Function ahtCommonFileOpenSave( _ Optional ByRef Flags As Variant, _ Optional ByVal InitialDir As Variant, _ Optional ByVal Filter As Variant, _ Optional ByVal FilterIndex As Variant, _ Optional ByVal DefaultExt As Variant, _ Optional ByVal FileName As Variant, _ Optional ByVal DialogTitle As Variant, _ Optional ByVal hwnd As Variant, _ Optional ByVal OpenFile As Variant) As Variant
Интерфейс прикладного программирования (API) Windows Глава 22 557
' Эта функция вызывает стандартное диалоговое окно' выбора файла. Все перечисленные выше параметры' являются необязательными.'' Входящие параметры:' - Flags. Одна или несколько ahtOFN-констант,' объединенных оператором OR.' - InitialDir. Начальная папка.' - Filter. Набор фильтров отбора файлов' (для создания фильтров используется' функция ahtAddFilterItem).' - FilterIndex. Номер используемого фильтра.' По умолчанию 1.' - DefaultExt. Расширение файла, используемое' по умолчанию. Применяется при сохранении файла.' - FileName. Значение по умолчанию поля ввода имени файла.' - DialogTitle. Заголовок диалогового окна.' - hWnd. Дескриптор родительского окна.' - OpenFile. Булево значение: True - открыть файл,' False - сохранить файл.' Возвращаемый результат:' - Null или имя выбранного файла. Dim OFN As tagOPENFILENAME Dim strFileName As String Dim strFileTitle As String Dim fResult As Boolean
' Задать параметры диалогового окна. If IsMissing(InitialDir) Then InitialDir = CurDir If IsMissing(Filter) Then Filter = "" If IsMissing(FilterIndex) Then FilterIndex = 1 If IsMissing(Flags) Then Flags = 0& If IsMissing(DefaultExt) Then DefaultExt = "" If IsMissing(DialogTitle) Then DialogTitle = "" If IsMissing(OpenFile) Then OpenFile = True
' Создать строковые переменные для хранения' возвращаемых значений. strFileName = Left(FileName & String(256, 0), 256) strFileTitle = String(256, 0)
' Определить значение полей структуры OFN. With OFN .lStructSize = Len(OFN) .strFilter = Filter .nFilterIndex = FilterIndex .strFile = strFileName .nMaxFile = Len(strFileName) .strFileTitle = strFileTitle .nMaxFileTitle = Len(strFileTitle) .strTitle = DialogTitle .Flags = Flags .strDefExt = DefaultExt .strInitialDir = InitialDir .hInstance = 0 .lpfnHook = 0 .strCustomFilter = String(255, 0) .nMaxCustFilter = 255 End With
558 Часть III Удивительные возможности Visual Basic for Applications
' Передать структуру OFN функции Windows API,' отображающей диалоговое окно выбора файла. If OpenFile Then fResult = aht_apiGetOpenFileName(OFN) Else fResult = aht_apiGetSaveFileName(OFN) End If
' В результате вызова функции API значение поля strFile' структуры OFN было изменено. Следующий код извлекает' полученное значение поля strFile. If fResult Then' Информация о выбранном файле хранится в поле Flags' структуры OFN. Следующий код извлекает полученное' значение этого поля для последующего анализа. If Not IsMissing(Flags) Then Flags = OFN.Flags ahtCommonFileOpenSave = TrimNull(OFN.strFile) Else ahtCommonFileOpenSave = vbNullString End IfEnd Function
Private Function ahtAddFilterItem(strDescription As String, _strFilter As String, Optional varItem As Variant) As String' Эта функция создает фильтр файлов.' Фильтр файлов = описание фильтра файлов (например,' "Базы данных ") + символ NULL + шаблон фильтра файлов' (например, "*.mdb;*.mda ") + символ NULL. If IsMissing(varItem) Then varItem = "*.*" ahtAddFilterItem = strDescription & vbNullChar & strFilter & _ vbNullChar & varItem & vbNullCharEnd Function
Private Function TrimNull(ByVal strItem As String) As String Dim intPos As Integer
intPos = InStr(strItem, vbNullChar) If intPos > 0 Then TrimNull = Left(strItem, intPos - 1) Else TrimNull = strItem End IfEnd Function
Следующая функция отображает диалоговое окно выбора файла и возвраY
щает имя последнего.
Function GetFileName(strPath As String) Dim strFilter As String Dim lngFlags As Long
strFilter = "*.xls" strFilter = ahtAddFilterItem("Файлы Excel (*.xls)", strFilter)
GetFileName = ahtCommonFileOpenSave(InitialDir:=strPath, _ Filter:=strFilter, FilterIndex:=3, Flags:=lngFlags, _ DialogTitle:="Пожалуйста, выберите файл", FileName:="")End Function
Создайте пользовательскую форму. Ниже приведен код, выполняющийся в
результате щелчка на кнопке Выбрать файл, как показано на рис. 22.9. ОбраY
Интерфейс прикладного программирования (API) Windows Глава 22 559
тите внимание, что функция GetFileName принимает в качестве параметра
путь к папке, с которой будет начат обзор файловой системы.
Private Sub CommandButton2_Click() Dim txtFile As String
txtFile = GetFileName("C:\") If txtFile <> "" Then ListBox1.AddItem (txtFile) End IfEnd Sub
Рис. 22.9. Диалоговое окно, возвращающее имя выбранного файла
Дополнительные источники объявленийWindows API
Многие программисты не прочь поделиться опытом в создании и использоваY
нии объявлений Windows API. В частности, на WebYсайте Ивана Ф. Моалы (Ivan
F. Moala) The XcelFiles (www.xcelfiles.com) содержится множество примеров
объявлений Windows API с соответствующими пояснениями (посетите WebY
страницу по адресу: http://www.xcelfiles.com/APIIndex.html).
Следующий шагСледующая глава посвящена обработке ошибок.
Отладка кода с помощьюредактора VBA.............................. 561Обработка ошибокс помощью выраженияOn Error GoTo ................................ 564Универсальныеобработчики ошибок ................. 566Общение с заказчиками............ 569“Отложенные” ошибки ............. 569Несовершенство защитыпроекта VBA ..................................572Защита проекта VBA вразличных версиях Excel............573Совместимость различныхверсий Excel ...................................573Следующий шаг............................574
23Глава 23
Îáðàáîòêà îøèáîê
Сколько бы усилий ни было заY
трачено на тестирование и отладку
программного кода, рано или поздно
он даст сбой. Единственная защита
от непредвиденных ситуаций заклюY
чается в их планировании.
Отладка кодас помощьюредактора VBA
Обнаружив необработанную ошибY
ку в программном коде, редактор VBA
отображает диалоговое окно, пример
которого показан на рис. 23.1.
Щелкните на кнопке Debug(Отладка). Редактор VBA выделяет
строку кода, выполнение которой
привело к возникновению ошибки,
желтым цветом, как показано на
рис. 23.2.
Рис. 23.1. Результат обнаружения необрабо&танной ошибки в незащищенном модуле
562 Часть III Удивительные возможности Visual Basic for Applications
Рис. 23.2. Чтобы узнать текущее значение переменной, подве&дите к ней указатель мыши — и вы получите дополнительнуюинформацию о причине сбоя
К сожалению, редактор VBA не слишком разборчив в том, что касается опY
ределения типа ошибки. В частности, огромное число сбойных ситуаций
классифицируются как ошибка времени выполнения 1004. Таким образом,
единственный способ определения причины сбоя заключается в изучении
строки, на которой было остановлено выполнение программного кода, и теY
кущих значений переменных.
Установив возможные причины возникновения ошибки, щелкните на
кнопке Reset (Сброс), чтобы прекратить выполнение макроса (рис. 23.3).
Рис. 23.3. Кнопка Reset напоминает кнопку Стоп, располо&женную на пульте дистанционного управления CD& илиDVD&плейера
Внимание Попытка выполнения макроса в режиме отладки другого макроса приводит к ото&бражению сообщения об ошибке, показанного на рис. 23.4. Если запуск макросабыл инициирован с помощью интерфейса Excel, на экране появится окно редакто&ра VBA. Тем не менее, после щелчка на кнопке OK на экране вновь появится окноExcel, что весьма неудобно.
Отладка кода пользовательской формы
Иногда редактор VBA неверно определяет строку кода, выполнение котоY
рой привело к возникновению ошибки. Рассмотрим следующую ситуацию.
Предположим, что сбой произошел при выполнении кода формы, отображаеY
мой на экране в результате выполнения некоторого макроса. После перехода в
режим отладки редактор VBA выделяет желтым цветом строку вызова формы,
что может ввести в заблуждение. Чтобы отыскать настоящий источник ошибY
ки, выполните следующие действия.
Обработка ошибок Глава 23 563
Рис. 23.4. Это сообщение выводится редактором VBA при попыткезапуска макроса в режиме отладки другого макроса
1. В окне сообщения об ошибке щелкните на кнопке Debug (Отладка),
как показано на рис. 23.5.
Рис. 23.5. Щелкните на кнопке Debug
2. Редактор VBA выделит желтым цветом строку вызова пользовательской
формы frmChoose.Show, как показано на рис. 23.6. Это неверное реY
шение, поскольку сбой произошел при выполнении кода пользовательY
ской формы.
Рис. 23.6. Редактор VBA неверно определяет строку, выполнениекоторой привело к возникновению ошибки
564 Часть III Удивительные возможности Visual Basic for Applications
3. Нажмите клавишу <F8>, чтобы выполнить метод frmChoose.Show.
Вместо выдачи сообщения об ошибке редактор VBA приступит к выY
полнению кода процедуры инициализации формы UserForm_Initialize.
4. Нажимайте клавишу <F8> до тех пор, пока редактор VBA вновь не отоY
бразит сообщение об ошибке. Поиск проблемного участка кода может
быть затруднен наличием длинного цикла, как показано на рис. 23.7.
Рис. 23.7. Число нажатий клавиши <F8>, необходимое для прохожденияцикла For...Next, прямо пропорционально числу его итераций
Для прохождения первой итерации цикла For...Next достаточно трех
нажатий клавиши <F8> (см. рис. 23.7). Каждая следующая итерация цикла
требует нажатия клавиши <F8> два раза. В частности, чтобы добавить в спиY
сок 25 элементов, клавишу <F8> придется нажать 51 раз.
Другими словами, отладка кода пользовательской формы представляет соY
бой весьма утомительное занятие. Запаситесь терпением и не забывайте слеY
дить за выполняемой на следующем шаге строкой.
Обработка ошибок с помощью выраженияOn Error GoTo
Логика обработки ошибок в VBA заключается в передаче выполнения кода
некоторому заранее подготовленному участку макроса.
Чтобы создать код обработки ошибки, выполните следующие действия.
1. Разместите строку Exit Sub после основного кода макроса, чтобы
предотвратить несанкционированное выполнение кода обработки
ошибки.
2. Добавьте метку после строки Exit Sub, например, MyErrorHandler:.
3. Разместите после метки код обработки ошибки. Чтобы передать выполY
нение кода строке макроса, следующей за строкой, которая привела к
возникновению ошибки, воспользуйтесь выражением Resume Next.
Обработка ошибок Глава 23 565
4. Разместите строку On Error GoTo MyErrorHandler перед строкой,
выполнение которой может привести к возникновению ошибки. Обратите
внимание, что на этот раз двоеточие после имени метки ставить не нужно.
5. Разместите строку On Error GoTo 0 после строки кода, выполнение
которой может привести к возникновению ошибки. На самом деле,
метки с именем 0 не существует. Выражение On Error GoTo 0 поY
зволяет вернуться в обычный режим обработки ошибок.
Рассмотрим пример создания кода обработки ошибки открытия файла.
Sub HandleAnError() Dim MyFile As Variant
' Определить обработчик ошибок. On Error GoTo FileNotThere Workbooks.Open Filename:="C:\NotHere.xls"
' Вернуться в обычный режим обработки ошибок. On Error GoTo 0 MsgBox "Выполнение макроса завершено"
' Строка Exit Sub позволяет предотвратить' несанкционированное выполнение обработчика ошибки. Exit Sub
' Создать метку.FileNotThere: MyPrompt = "При открытии файла произошла ошибка. Вероятно, " MyPrompt = MyPrompt & "файл не существует. Щелкните на кнопке " MyPrompt = MyPrompt & "OK, чтобы указать расположение файла, " MyPrompt = MyPrompt & "или на кнопке Cancel, чтобы завершить " MyPrompt = MyPrompt & "выполнение макроса" Ans = MsgBox(Prompt:=MyPrompt, Buttons:=vbOKCancel) If Ans = vbCancel Then Exit Sub
' Отобразить диалоговое окно выбора файла. MyFile = Application.GetOpenFilename
' Если пользователь ничего не выбрал, завершить выполнение макроса. If MyFile = False Then Exit Sub
On Error GoTo 0 Workbooks.Open MyFile
' Передать выполнение кода строке макроса, следующей' за строкой, которая привела к возникновению ошибки. Resume NextEnd Sub
Использование нескольких обработчиков ошибок
В коде макроса можно разместить несколько различных обработчиков
ошибок. Единственным требованием при этом является наличие строки Re-sume Next или Exit Sub в конце кода каждого обработчика.
566 Часть III Удивительные возможности Visual Basic for Applications
Универсальные обработчики ошибокНекоторые программисты отдают предпочтение универсальному обработY
чику ошибок, позволяющему скрыть от пользователей сообщение об ошибке,
генерируемое редактором VBA. Как правило, универсальный обработчик
ошибок обращается к свойствам объекта Err, таким как номер ошибки и ее
описание.
Sub GenericHandler() On Error GoTo HandleAny
Sheets(9).Select
Exit SubHandleAny: Msg = "Произошла ошибка номер " & Err.Number & ". Описание _ошибки: " & Err.Description MsgBox Msg Exit Sub
End Sub
Игнорирование ошибок
Некоторые ошибки можно безболезненно проигнорировать. Вспомним
макрос создания WebYстраницы, рассматривавшийся в главе 14, ‘‘ВзаимодейY
ствие с Internet’’. Перед созданием HTMLYфайла sampledirectory.htmlмакрос WriteMembershipHTML удаляет существующую копию этого файла.
Выражение Kill (Файл) возвращает ошибку, если файл с именем Файлне существует. Однако эта ошибка столь несущественна, что ее можно проигY
норировать и продолжить выполнение макроса со следующей строки.
Sub WriteMembershipHTML() ... MyFile = "sampledirectory.html"
ThisFile = MyPath & Application.PathSeparator & MyFile ThisHostFile = MyFile
' Удалить существующую Web-страницу. On Error Resume Next Kill (ThisFile) On Error GoTo 0
' Открыть файл для записи. Open ThisFile For Output As #1 ...End Sub
Ни в коем случае не злоупотребляйте выражением On Error ResumeNext. Также не забывайте размещать строку On Error GoTo 0 сразу же поY
сле строки кода, выполнение которой может привести к возникновению
ошибки.
Обработка ошибок Глава 23 567
Попытка проигнорировать критическую ошибку приводит к завершению
выполнения макроса. Если макрос А вызывает макрос Б и выполнение поY
следнего завершается аварийно, выполнение кода продолжается со строки
макроса А, следующей за строкой вызова макроса Б. Это чревато непредскаY
зуемыми последствиями.
Игнорирование ошибок задания параметров печати страницы
Запишите макрос, задающий параметры печати страницы. Установка всего
лишь одного значения в диалоговом окне Параметры страницы (Page Setup)
приводит к созданию множества строк кода. Проблема заключается в том, что
параметры печати страницы отличаются от принтера к принтеру. Например,
при создании макроса на компьютере с принтером, поддерживающим цветY
ную печать, средство записи макросов сгенерирует строку .BlackAndWhi-te = False. Выполнение этой строки на компьютере с принтером, поддерY
живающим только черноYбелую печать, приведет к возникновению ошибки.
Еще одной причиной сбоя может стать установка недопустимых значений паY
раметров, например, параметра, определяющего качество печати. Если макY
рос был создан на компьютере с принтером, поддерживающим разрешение
600 точек на дюйм, средство записи макросов сгенерирует строку
.PrintQuality = 600. Выполнение этой строки на компьютере с принтеY
ром, поддерживающим максимальное разрешение 300 точек на дюйм, привеY
дет к сбою. В данной ситуации рекомендуется разместить выражение On Er-ror Resume Next перед фрагментом кода, устанавливающим параметры
печати страницы, а выражение On Error GoTo 0 — после него.
On Error Resume NextWith ActiveSheet.PageSetup .PrintTitleRows = "" .PrintTitleColumns = ""End WithActiveSheet.PageSetup.PrintArea = "$A$1:$L$27"With ActiveSheet.PageSetup .LeftHeader = "" .CenterHeader = "" .RightHeader = "" .LeftFooter = "" .CenterFooter = "" .RightFooter = "" .LeftMargin = Application.InchesToPoints(0.25) .RightMargin = Application.InchesToPoints(0.25) .TopMargin = Application.InchesToPoints(0.75) .BottomMargin = Application.InchesToPoints(0.5) .HeaderMargin = Application.InchesToPoints(0.5) .FooterMargin = Application.InchesToPoints(0.5) .PrintHeadings = False .PrintGridlines = False .PrintComments = xlPrintNoComments .PrintQuality = 300 .CenterHorizontally = False .CenterVertically = False
568 Часть III Удивительные возможности Visual Basic for Applications
.Orientation = xlLandscape .Draft = False .Pauperize = xlPaperLetter .FirstPageNumber = xlAutomatic .Order = xlDownThenOver .BlackAndWhite = False .Zoom = False .FitToPagesWide = 1 .FitToPagesTall = False .PrintErrors = xlPrintErrorsDisplayedEnd WithOn Error GoTo 0
Игнорирование сообщений Excel
Некоторые сообщения Excel выводятся на экран вне зависимости от текуY
щего режима обработки ошибок. Например, при попытке удаления рабочего
листа на экране появится сообщение В листах, выбранных дляудаления, могут существовать данные. Чтобы удалить данные,нажмите кнопку "Удалить". (Data may exist in the sheet(s) selected for deleY
tion. To permanently delete the data, click Delete.). Это смущает многих пользоY
вателей, так как они полагают, что произошла какаяYлибо ошибка. Чтобы отY
ключить вывод сообщений Excel, воспользуйтесь выражением Applica-tion.DisplayAlerts = False.
Sub DeleteSheet() Application.DisplayAlerts = False Worksheets("Лист2").Delete Application.DisplayAlerts = TrueEnd Sub
Извлечение пользы из ошибок
Звучит весьма странно, однако даже ошибки могут приносить определенY
ную пользу. В частности, ошибки помогают разрабатывать более быстрый и
эффективный код.
Рассмотрим задачу проверки наличия в активной рабочей книге листа с
именем Данные. Ниже приведен код, решающий эту задачу без использоваY
ния ошибок.
Sub AvoidTheErrorLongerCode() DataFound = False For Each WS In ActiveWorkbook.Worksheets If WS.Name = "Данные" Then DataFound = True Exit For End If Next WS If Not DataFound Then Sheets.Add.Name = "Данные"End Sub
Обработка ошибок Глава 23 569
Если активная рабочая книга содержит 128 листов, тело цикла ForEach...Next будет выполнено 128 раз при условии, что листа с именем
Данные в книге нет.
Альтернативный подход заключается в попытке обращения к листу
Данные. Проигнорируйте обработку ошибок с помощью выражения On Er-ror Resume Next и проверьте номер ошибки Err.Number. Если номер
ошибки отличен от нуля, листа Данные в рабочей книге нет.
Sub ErrorOnPurposeShorter() On Error Resume Next x = Worksheets("Данные").Name If Not Err.Number = 0 Then Sheets.Add.Name = "Данные" On Error GoTo 0End Sub
Очевидно, что макрос ErrorOnPurposeShorter выполняется быстрее
макроса AvoidTheErrorLongerCode. Иногда даже ошибки способны приY
носить пользу.
Общение с заказчикамиОдним из наиболее важных аспектов общения с заказчиками является окаY
зание поддержки в вопросах, возникающих при использовании разработанY
ных для них макросов.
Постарайтесь объяснить заказчикам разницу между сообщением об ошибY
ке и обычным сообщением Excel. К сожалению, сообщения обоих типов имеY
ют свойство сваливаться как снег на голову и сопровождаются звуковым сигY
налом. Конечно же, сообщение об ошибке не сулит ничего положительного,
однако далеко не каждое сообщение является сообщением об ошибке. Один
офисный работник постоянно жаловался своему начальнику на сбои, которые
возникают при выполнении разработанного нами макроса. На самом же деле
это были обычные информационные сообщения.
Попросите заказчика связаться с вами по телефону, если на экране возникY
нет окно сообщения об ошибке. Узнайте номер ошибки и ее описание, после
чего попросите заказчика щелкнуть на кнопке Debug (Отладка), сообщить имя
модуля, процедуры и содержимое строки, выделенной желтым цветом. ВооруY
жившись полученной информацией, постарайтесь определить причину сбоя.
“Отложенные” ошибкиЗапуская программный код в первый раз, его разработчик ожидает возникY
новения внештатных ситуаций. Для обнаружения ошибок на этапе тестироваY
ния кода последний часто выполняют в пошаговом режиме.
К сожалению, тестирование и отладка не гарантируют отсутствие ошибок
на этапе эксплуатации программы. Иногда первый сбой может произойти поY
сле нескольких месяцев ее успешного применения.
570 Часть III Удивительные возможности Visual Basic for Applications
Не спешите перекладывать ответственность за неработоспособность кода
на заказчика. Зачастую пристальное изучение ошибки приводит к выявлению
слабых мест, не учтенных на этапе разработки.
В следующих разделах рассматриваются примеры внештатных ситуаций,
способных возникнуть на этапе эксплуатации программного кода.
Ошибка времени выполнения 9: “Subscript out of range”Предположим, что переданная заказчику рабочая книга содержит лист
Меню. Через некоторое время заказчик сообщает о сбое и присылает вам коY
пию экрана, показанную на рис. 23.8.
Рис. 23.8. Одной из распространенных причин возникновения ошибкивремени выполнения 9 является попытка обращения к несуществующе&му рабочему листу
Приведенный ниже код предполагает наличие рабочего листа Меню. Если
заказчик удалил или переименовал рабочий лист Меню, возникнет ошибка.
Sub GetSettings() ThisWorkbook.Worksheets("Меню").Select x = Range("A1").ValueEnd Sub
К сожалению, описанная ситуация достаточно типична. Следующий код
позволяет заменить недружественное сообщение об ошибке редактора VBA
сообщением об отсутствии рабочего листа Меню.
Sub GetSettingsLonger() On Error Resume Next x = ThisWorkbook.Worksheets("Меню").Name If Not Err.Number = 0 Then MsgBox "Рабочий лист ""Меню"" не обнаружен" Exit Sub End If On Error GoTo 0
Обработка ошибок Глава 23 571
ThisWorkbook.Worksheets("Меню").Select x = Range("A1").ValueEnd Sub
Ошибка времени выполнения 1004: “Method 'Range'of object '_Global' failed”
Предположим, что программный код ежедневно импортирует в Excel соY
держимое некоторого текстового файла, загружаемого с FTPYсервера. Макрос
SetReportInItalics выделяет курсивом последнюю строку полученного
результата, как показано ниже.
Sub SetReportInItalics()' Выполнение этого макроса вызовет сбой при условии,' что текстовый файл не был импортирован в Excel. TotalRow = Worksheets("Лист1").Range("F65536").End(xlUp).Row FinalRow = TotalRow - 1 Range("A1:A" & FinalRow).Font.Italic = TrueEnd Sub
Через некоторое время, прошедшее с начала эксплуатации программного
кода, заказчик сообщает об ошибке времени выполнения 1004 (рис. 23.9).
Рис. 23.9. Ошибка времени выполнения 1004может быть вызвана 1001 причиной
Подобная ошибка может возникнуть при условии, что импортированный
текстовый файл не содержал данных. В этом случае значение переменной To-talRow будет равно 1, а переменной FinalRow — 0. Попытка обращения к
несуществующей строке приведет к сбою.
Следующий код позволяет заменить сообщение об ошибке редактора VBA
сообщением о необходимости проверки правильности загрузки текстового
файла с FTPYсервера.
Sub SetReportInItalicsNoError() TotalRow = Worksheets("Лист1").Range("A65536").End(xlUp).Row FinalRow = TotalRow - 1 If FinalRow > 0 Then Range("A1:A" & FinalRow).Font.Italic = True Else MsgBox "Импортированный файл не содержит данных. _Попробуйте загрузить файл с FTP-сервера еще раз" End IfEnd Sub
572 Часть III Удивительные возможности Visual Basic for Applications
Несовершенство защиты проекта VBAПроект VBA можно защитить от просмотра. В этом случае окно сообщения
об ошибке редактора VBA будет содержать нефункциональную кнопку Debug(Отладка), лишающую пользователя возможности сообщить разработчику доY
полнительную информацию о произошедшем сбое.
Вдобавок, защита, обеспечиваемая редактором VBA, далеко не совершенY
на. Программисты из Эстонии создали приложение, которое взламывает заY
щиту любого проекта Excel. Другими словами, не усложняйте себе жизнь,
прибегая к защите проектов VBA.
Практикум
Взлом паролейВзлом паролей в Excel 97 и Excel 2000 не представлял никаких трудностей. Специ&альные программы в мгновение ока сообщали пользователю пароль проекта VBA.В Excel 2002 корпорация Microsoft предложила схему защиты, стойкую к атакампрограмм для взлома паролей. Благодаря применению шифрования единствен&ным способом взлома пароля проекта VBA на протяжении нескольких месяцев по&сле выхода Excel 2002 оставался способ простого подбора. И если для взлома4&значного пароля наподобие blue требовалось 10 минут, то взлом 24&значногопароля наподобие *A6%kJJ542(9$GgU44#2drt8 занимал около 20 часов. Безус&ловно, это затрудняло доступ к коду программистам Excel, желавшим воспользо&ваться чужой интеллектуальной собственностью.К сожалению, следующее поколение программ для взлома паролей справилось с24&значным паролем Excel 2002 за 2 секунды. Поначалу сообщение программыдля взлома пароля о том, что она определила искомый 24&значный пароль какXVII, кажется ошибкой. Проверив полученный результат, убеждаешься в обрат&ном. Дело в том, что вместо применения метода простого подбора программа ге&нерирует новый 4&значный пароль проекта VBA и сохраняет его в файле рабочейкниги Excel.Подобный подход к взлому пароля может породить весьма пикантную ситуацию.Предположим, что разработчик устанавливает пароль проекта VBA*A6%kJJ542(9$GgU44#2drt8 и передает файл заказчику. Если заказчик изменитпароль, а затем обратится за помощью к разработчику, он будет вынужден пере&слать ему файл рабочей книги Excel, защищенный новым паролем. Единственный,кто способен извлечь выгоду из такой ситуации, — это талантливый программистиз Эстонии.На момент написания этой книги число VBA&проектов Excel превышало число раз&работчиков, способных их реализовать. Большинство разработчиков отдает себеотчет в том, что созданный ими код может быть доступен любому человеку, у ко&торого имеется файл рабочей книги.Тем не менее, многие начинающие программисты все еще продолжают защищатьпаролем созданные ими проекты VBA. Рассмотрим следующую ситуацию.
Обработка ошибок Глава 23 573
Предположим, что заказчик желает внести изменения в существующий VBA&проект. Он связывается с разработчиком, который выполняет всю необходимуюработу. Спустя месяц заказчик вновь обращается к разработчику, который на этотраз не может выполнить задание своевременно. Заказчик не хочет ждать и обра&щается к другому программисту.Поскольку проект VBA защищен паролем, новому разработчику придется еговзломать, чтобы получить доступ к программному коду. Если в будущем заказчикпланирует сотрудничать с первоначальным создателем кода, может возникнутьситуация, в которой два разработчика будут иметь два разных пароля для доступак проекту VBA. В подобной ситуации рекомендуется полностью отказаться от за&щиты программного кода паролем.
Защита проекта VBA в различных версиях ExcelСхема защиты проекта VBA паролем в Excel 2002 несовместима со схемой
защиты проекта VBA паролем в Excel 97. Другими словами, защиту проекта
VBA, установленную с помощью Excel 2002, нельзя снять средствами Excel 97.
В частности, это приводит к тому, что пользователь Excel 97 не сможет ни отY
ладить сбойный код, ни передать разработчику подробную информацию о
возникнувшей ошибке.
Защита проекта VBA паролем приносит больше неприятностей, чем пользы.
Совместимость различных версий ExcelКорпорация Microsoft совершенствует VBA в каждой новой версии Excel.
В частности, переход от Excel 97 к Excel 2000 был отмечен изменением кода
создания сводных таблиц, а также некоторых аспектов работы с диаграммами.
Одним из нововведений Excel 2002 является параметр TrailingMinus-Numbers метода OpenText. Попытка выполнения кода, расположенного
в одном модуле с параметром TrailingMinusNumbers, приведет к ошибке
компиляции в Excel 2000. Рассмотрим пример.
Предположим, что модуль Module1 содержит макросы ProcA, ProcBи ProcC, а модуль Module2 — макросы ProcD и ProcE. Макрос ProcEвключает вызов метода OpenText с параметром TrailingMinusNumbers.
Благодаря отсутствию кода, специфичного для Excel 2002, макросы ProcA,
ProcB и ProcC могут быть выполнены в Excel 2000 без необходимости внесеY
ния какихYлибо изменений, а вот попытка выполнения макроса ProcD приY
ведет к ошибке компиляции модуля Module2. Окончательно запутать ситуаY
цию способен тот факт, что причина сбоя находится не в макросе ProcD,
а в макросе ProcE.
Одно из возможных решений описанной выше проблемы заключается
в тестировании кода VBA во всех версиях Excel, включая Excel 97. Следует
отметить, что Excel 97 с набором исправлений SRY2 намного устойчивее
574 Часть III Удивительные возможности Visual Basic for Applications
первоначального выпуска Excel 97. Поскольку далеко еще не все пользоваY
тели Excel 97 установили набор исправлений SRY2, тестировать придется оба
выпуска.
Пользователи Macintosh ошибочно полагают, что версия Excel для MacinY
tosh и версия Excel для Windows одинаковы. На самом деле совместимость ExY
cel для Macintosh и Excel для Windows заканчивается на формате файлов и
пользовательском интерфейсе. Код VBA Windows и Macintosh не совместим.
Несмотря на внешнюю схожесть, код VBA Windows и Macintosh отличается
множеством мелких аспектов.
Следующий шагСколько бы усилий ни было затрачено на тестирование и отладку проY
граммного кода, рано или поздно он даст сбой. В этой главе были рассмотреY
ны различные способы обработки ошибок, отладка кода и другие вопросы,
связанные с возникновением непредвиденных ситуаций.
Следующая глава посвящена созданию пользовательских меню и панелей
инструментов.
Создание пользовательскогоменю ...............................................575Создание пользовательскойпанели инструментов .................581Другие способы запускамакросов ........................................587Следующий шаг........................... 592
24Глава 24
Ñîçäàíèåïîëüçîâàòåëüñêèõìåíþ è ïàíåëåéèíñòðóìåíòîâ
В этой главе рассматривается созY
дание пользовательских меню и паY
нелей инструментов, предназначенY
ных для запуска макросов. В конце
главы будут описаны способы запусY
ка макросов с помощью сочетания
клавиш, кнопки и элемента управлеY
ния ActiveX.
Созданиепользовательскогоменю
Пользовательское меню прекрасY
но подходит для размещения команд
запуска макросов, созданных для заY
казчика. На рис. 24.1 показано польY
зовательское меню, разработанное
для вымышленной компании XYZ.
Создание и удалениепользовательского меню
Строка главного меню Excel предY
ставлена объектом VBA Applica-tion.CommandBars(1) и включает
такие меню, как Файл (File), Правка(Edit), Вид (View) и т.д. Чтобы добаY
вить к главному меню новый пункт,
воспользуйтесь методом Add. При
добавлении нового пункта меню неY
обходимо указать его имя и располоY
жение.
576 Часть III Удивительные возможности Visual Basic for Applications
Рис. 24.1. Пользовательское меню, названное по именизаказчика, подчеркивает уникальность разработанногодля него приложения
Имя пункта меню (например, XYZ Co) отображается в строке меню. БольY
шинству пунктов меню сопоставлено некоторое сочетание клавиш, такое как
<Alt+В> (<Alt+V>) для меню Вид (View). Сочетание клавиш, назначенное тоY
му или иному пункту меню, определяет подчеркнутая буква в его имени. ЧтоY
бы задать сочетание клавиш для пункта меню, поставьте символ амперсанда
(&) перед требуемой буквой, например, &XYZ Co.
Следует помнить, что сочетания клавиш <Alt+А> (<Alt+I>), <Alt+В>
(<Alt+V>), <Alt+Д> (<Alt+D>), <Alt+Е> (<Alt+T>), <Alt+М> (<Alt+O>),
<Alt+О> (<Alt+W>), <Alt+П> (<Alt+E>), <Alt+С> (<Alt+H>) и <Alt+Ф>
(<Alt+F>) зарезервированы для стандартных меню Excel. Несмотря на то, что
одно и то же сочетание клавиш может быть назначено двум различным пункY
там меню, оно не будет функциональным.
По умолчанию строка меню Excel содержит 10 пунктов: значок Excel, Файл(File), Правка (Edit), Вид (View), Вставка (Insert), Формат (Format), Сервис(Tools), Данные (Data), Окно (Window) и Справка (Help). Чтобы добавить ноY
вый пункт меню перед пунктом Справка, установите значение параметра Be-fore метода Add равным 10. Чтобы добавить новый пункт меню после пункта
Справка, установите значение параметра Before метода Add равным 11. ДоY
бавление нового пункта меню после пункта Справка в Excel для Macintosh
приведет к возникновению ошибки, поскольку в MacintoshYприложениях
пункт Справка всегда должен оставаться последним пунктом меню.
Рекомендуется предусмотреть процедуры создания и удаления меню. ПроY
цедуру создания меню следует вызвать в обработчике события Work-book_Open, Workbook_Activate или Worksheet_Activate, а процедуру
удаления меню YYYY в обработчике события Workbook_BeforeClose.
Чтобы не допустить дублирования меню, вызовите в самом начале процеY
дуры создания меню процедуру его удаления.
Создание пользовательских меню и панелей инструментов Глава 24 577
Ниже приведен код макроса, создающего меню (CreateMenu), и макроса,
удаляющего меню (DeleteMenu). Объектная переменная MenuObject имеет
тип CommandBarPopup. Макрос CreateMenu вызывает макрос DeleteMenu,
после чего добавляет в строку главного меню Excel пункт XYZ Co перед пункY
том Справка.
Sub DeleteMenu()' Этот макрос должен быть выполнен' перед закрытием рабочей книги. On Error Resume Next Application.CommandBars(1).Controls("&XYZ Co").Delete On Error GoTo 0End Sub
Sub CreateMenu() Dim MenuObject As CommandBarPopup Dim MenuItem As Object Dim SubMenuItem As Object
' Удалить существующее меню. Call DeleteMenu
Set MenuObject = Application.CommandBars(1). _ Controls.Add(Type:=msoControlPopup, _ Before:=10, Temporary:=True) MenuObject.Caption = "&XYZ Co"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ImportData" MenuItem.Caption = "&Импорт данных"
End Sub
Добавление команд меню
Чтобы добавить команду меню, воспользуйтесь методом MenuOb-ject.Controls.Add, установив значение параметра Type равным msoCon-trolButton. MenuObject — это созданный ранее объект меню типа Com-mandBarPopup. Объект команды меню имеет свойство Caption, опредеY
ляющее текст команды, и OnAction, определяющее макрос, который будет
выполнен при выборе этой команды.
Следующий код создает меню XYZ Co, содержащее четыре команды, как
показано на рис. 24.2.
Sub CreateSimpleMenu() Dim MenuObject As CommandBarPopup Dim MenuItem As Object Dim SubMenuItem As Object
' Удалить существующее меню. Call DeleteMenu
Set MenuObject = Application.CommandBars(1). _
578 Часть III Удивительные возможности Visual Basic for Applications
Controls.Add(Type:=msoControlPopup, _ Before:=10, Temporary:=True) MenuObject.Caption = "&XYZ Co"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ImportData" MenuItem.Caption = "&Импорт данных"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ExportData" MenuItem.Caption = "&Экспорт данных"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ProduceReport" MenuItem.Caption = "От&чет"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "AboutMe" MenuItem.Caption = "О про&грамме"End Sub
Группирование команд меню
Команды меню можно группировать. Чтобы создать линию раздела двух
групп команд меню, установите значение свойства BeginGroup первой коY
манды меню, расположенной за линией, равным True. Следующий код поY
мещает линию раздела между третьей и четвертой командой меню, как покаY
зано на рис. 24.3.
Рис. 24.2. Меню, со&держащее четыре ко&манды
Рис. 24.3. Чтобы создать линиюраздела двух групп команд ме&ню, воспользуйтесь свойствомBeginGroup
Sub CreateGroupedMenu()' Этот макрос помещает линию раздела двух групп' команд меню перед командой "О программе". Dim MenuObject As CommandBarPopup Dim MenuItem As Object Dim SubMenuItem As Object
' Удалить существующее меню. Call DeleteMenu
Set MenuObject = Application.CommandBars(1). _ Controls.Add(Type:=msoControlPopup, _ Before:=10, Temporary:=True)
Создание пользовательских меню и панелей инструментов Глава 24 579
MenuObject.Caption = "&XYZ Co"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ImportData" MenuItem.Caption = "&Импорт данных"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ExportData" MenuItem.Caption = "&Экспорт данных"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ProduceReport" MenuItem.Caption = "От&чет"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "AboutMe" MenuItem.Caption = "О про&грамме"' Следующая строка создает линию раздела. MenuItem.BeginGroup = TrueEnd Sub
Создание подменю
Подменю позволяет сократить число команд меню путем вынесения неY
скольких команд в отдельный пункт. На рис. 24.4 показано подменю Отчет,
содержащее команды для создания всевозможных отчетов.
Рис. 24.4. Как правило, подменю используется для ло&гического группирования команд меню
Чтобы создать подменю, воспользуйтесь методом MenuObject.Con-trols.Add, установив значение параметра Type равным msoControl-Popup. MenuObject — это созданный ранее объект меню типа CommandBar-Popup.
Чтобы добавить команду подменю, воспользуйтесь методом MenuItem.Con-trols.Add, установив значение параметра Type равным msoControlBut-ton. MenuItem — это созданный ранее объект подменю типа Object.
580 Часть III Удивительные возможности Visual Basic for Applications
Приведенный ниже код создает подменю, показанное на рис. 24.4.
Sub CreateFullMenu() Dim MenuObject As CommandBarPopup Dim MenuItem As Object Dim SubMenuItem As Object
' Удалить существующее меню. Call DeleteMenu
Set MenuObject = Application.CommandBars(1). _ Controls.Add(Type:=msoControlPopup, _ Before:=10, Temporary:=True) MenuObject.Caption = "&XYZ Co"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ImportData" MenuItem.Caption = "&Импорт данных"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "ExportData" MenuItem.Caption = "&Экспорт данных"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlPopup) MenuItem.Caption = "От&чет" MenuItem.BeginGroup = True
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "StyleRptNew" SubMenuItem.Caption = "Отчет о товарах"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "CategoryRptNew" SubMenuItem.Caption = "Отчет о группах товаров"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "ReportCard" SubMenuItem.Caption = "Карточка отчета"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "OverRpt" SubMenuItem.Caption = "Отчет о товарах за год" SubMenuItem.BeginGroup = True
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "CreateReportCard" SubMenuItem.Caption = "Отчет о магазинах"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "InventoryAgingReportNew" SubMenuItem.Caption = "Отчет о складских запасах"
Создание пользовательских меню и панелей инструментов Глава 24 581
SubMenuItem.BeginGroup = True
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "ShowStockReport" SubMenuItem.Caption = "Магазины с критическим _уровнем запасов"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "ShowRecTransfer" SubMenuItem.Caption = "Рекомендуемые перемещения товаров"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "ShowSummaryNew" SubMenuItem.Caption = "Итоговый отчет"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlPopup) MenuItem.Caption = "&Диаграмма"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "GraphOne" SubMenuItem.Caption = "Диаграмма &продаж"
Set SubMenuItem = MenuItem.Controls.Add(Type:= _msoControlButton) SubMenuItem.OnAction = "GraphTwo" SubMenuItem.Caption = "Диаграмма &запасов"
Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton) MenuItem.OnAction = "AboutMe" MenuItem.Caption = "О про&грамме" MenuItem.BeginGroup = TrueEnd Sub
Создание пользовательской панели инструментовКоманды запуска макросов, созданных для заказчика, можно также разY
мещать на панели инструментов. На рис. 24.5 показан пример пользовательY
ской панели, содержащей несколько различных элементов управления.
Рис. 24.5. Кнопка, размещенная на панели инстру&ментов, может содержать только текст, только значокили же и значок, и текст. Чтобы задать текст всплы&вающей подсказки, воспользуйтесь свойством эле&мента управления ToolTipText
582 Часть III Удивительные возможности Visual Basic for Applications
Создание и удаление пользовательской панелиинструментов
По аналогии с пользовательским меню, предусмотрите процедуры для созY
дания и удаления панели инструментов. Процедуру создания панели инструY
ментов следует вызвать в обработчике события Workbook_Open, Work-book_Activate или Worksheet_Activate, а процедуру удаления панели
инструментов YYYY в обработчике события Workbook_BeforeClose.
Макрос CreateToolbar создает плавающую панель инструментов. ПреY
дыдущие версии Excel позволяли закреплять панель инструментов, однако деY
лать этого не рекомендуется.
Чтобы создать панель инструментов, воспользуйтесь методом Command-Bars.Add, указав имя и тип панели инструментов. Ниже приведен код макY
росов CreateToolbar и DeleteToolbar.
Sub DeleteToolbar() On Error Resume Next CommandBars("XYZ").Delete On Error GoTo 0End Sub
Sub CreateToolbar() Dim Tbar As CommandBar Dim NewDD As CommandBarControl Dim NewBtn As CommandBarButton
' Удалить существующую панель инструментов. DeleteToolbar
' Создать панель инструментов. Set Tbar = CommandBars.Add With Tbar .Name = "XYZ" .Visible = True .Position = msoBarFloating End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .OnAction = "PrintCard" .Caption = "Печать..." .TooltipText = "Выберите страницы для печати" .Style = msoButtonCaption End WithEnd Sub
Добавление кнопок на панель инструментов
Чтобы добавить кнопку на созданную ранее панель инструментов TBar,
воспользуйтесь методом TBar.Controls.Add. Макрос, который будет выY
полнен в результате щелчка на кнопке, определяет ее свойство OnAction.
Создание пользовательских меню и панелей инструментов Глава 24 583
Кроме того, кнопке можно назначить текст (свойство Caption), значок
(свойство FaceID) и всплывающую подсказку (свойство ToolTipText).
Свойство Style определяет содержимое кнопки YYYY только текст
(msoButtonCaption), только значок (msoButtonIcon) или же и текст, и
значок (msoButtonIconAndCaption). Как показано на рис. 24.5, кнопка
Печать содержит только текст, кнопка Фотографии YYYY текст и значок, а кнопY
ка Отчеты YYYY только значок.
Следующий код создает панель инструментов, содержащую кнопки Пе-чать, Фотографии и Отчеты.
Sub CreateToolbar() Dim Tbar As CommandBar Dim NewDD As CommandBarControl Dim NewBtn As CommandBarButton
' Удалить существующую панель инструментов. DeleteToolbar
' Создать панель инструментов. Set Tbar = CommandBars.Add With Tbar .Name = "XYZ" .Visible = True .Position = msoBarFloating End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .OnAction = "PrintCard" .Caption = "Печать..." .TooltipText = "Выберите страницы для печати" .Style = msoButtonCaption End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .FaceId = 682 .OnAction = "DispPictures" .Caption = "Фотографии" .TooltipText = "Показать фотографии выбранных товаров" .Style = msoButtonIconAndCaption End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .FaceId = 461 .OnAction = "ProduceReport" .Caption = "Отчеты" .TooltipText = "Создать отчеты" .Style = msoButtonIcon End WithEnd Sub
584 Часть III Удивительные возможности Visual Basic for Applications
Выбор значка кнопки панели инструментов
Значок кнопки панели инструментов определяется значением свойства
кнопки FaceID. При выборе значения свойства FaceID рекомендуется восY
пользоваться обозревателем значков. Один из бесплатных обозревателей
значков доступен по адресу: http://skp.mvps.org/faceid.htm.
Добавление раскрывающегося списка на панельинструментов
Добавим на панель инструментов раскрывающийся список, содержащий
имена всех видимых листов рабочей книги Excel. При выборе имени рабочего
листа из списка выполняется макрос GoToSheet, делающий выбранный раY
бочий лист активным. Следующий код создает панель инструментов, содерY
жащую кнопки Печать, Фотографии, Отчеты, а также раскрывающийся спиY
сок Перейти к рабочему листу.
Sub CreateToolbar() Dim Tbar As CommandBar Dim NewDD As CommandBarControl Dim NewBtn As CommandBarButton Dim Sh As Worksheet
' Удалить существующую панель инструментов. DeleteToolbar
' Создать панель инструментов. Set Tbar = CommandBars.Add With Tbar .Name = "XYZ" .Visible = True .Position = msoBarFloating End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .OnAction = "PrintCard" .Caption = "Печать..." .TooltipText = "Выберите страницы для печати" .Style = msoButtonCaption End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .FaceId = 682 .OnAction = "DispPictures" .Caption = "Фотографии" .TooltipText = "Показать фотографии выбранных товаров" .Style = msoButtonIconAndCaption End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .FaceId = 461
Создание пользовательских меню и панелей инструментов Глава 24 585
.OnAction = "ProduceReport" .Caption = "Отчеты" .TooltipText = "Создать отчеты" .Style = msoButtonIcon End With Set NewDD = Tbar.Controls.Add(Type:=msoControlDropdown) With NewDD .Caption = "Перейти к рабочему листу" .OnAction = "GoToSheet" .Style = msoButtonAutomatic For Each Sh In ActiveWorkbook.Worksheets If Sh.Visible = True Then .AddItem Sh.Name End If Next Sh .ListIndex = 1 .Width = 110 End WithEnd Sub
Ниже приведен код макроса GoToSheet.
Sub GoToSheet() Dim ThisItem As Integer Dim ThisName As String
With CommandBars("XYZ").Controls("Перейти к рабочему листу") ThisItem = .ListIndex ThisName = .List(.ListIndex) End With' Сделать лист активным. Worksheets(ThisName).ActivateEnd Sub
Сохранение и восстановление координатпанели инструментов
Координаты панели инструментов изменяются каждый раз при ее создаY
нии. Добавим к макросам DeleteToolbar и CreateToolbar код, позвоY
ляющий сохранить и восстановить координаты панели инструментов.
Public Sub DeleteToolbar() Dim obj As CommandBar
On Error Resume Next Set obj = CommandBars("XYZ") If Err.Number = 0 Then' Запомнить текущие координаты' панели инструментов. With ThisWorkbook.Sheets("Параметры") .[I5].Value = obj.Top .[I6].Value = obj.Left End With End If obj.DeleteEnd Sub
586 Часть III Удивительные возможности Visual Basic for Applications
Sub CreateToolbar() Dim nTop As Variant Dim nLeft As Variant Dim Tbar As CommandBar Dim NewDD As CommandBarControl Dim NewBtn As CommandBarButton Dim Sh As Worksheet
' Удалить существующую панель инструментов. DeleteToolbar
With ThisWorkbook.Sheets("Параметры") nTop = .[I5].Value nLeft = .[I6].Value End With
' Создать панель инструментов. Set Tbar = CommandBars.Add With Tbar .Name = "XYZ" .Visible = True .Position = msoBarFloating If Not IsEmpty(nTop) Then .Top = nTop If Not IsEmpty(nLeft) Then .Left = nLeft End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .OnAction = "PrintCard" .Caption = "Печать..." .TooltipText = "Выберите страницы для печати" .Style = msoButtonCaption End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .FaceId = 682 .OnAction = "DispPictures" .Caption = "Фотографии" .TooltipText = "Показать фотографии выбранных товаров" .Style = msoButtonIconAndCaption End With
Set NewBtn = Tbar.Controls.Add(Type:=msoControlButton) With NewBtn .FaceId = 461 .OnAction = "ProduceReport" .Caption = "Отчеты" .TooltipText = "Создать отчеты" .Style = msoButtonIcon End With Set NewDD = Tbar.Controls.Add(Type:=msoControlDropdown) With NewDD .Caption = "Перейти к рабочему листу" .OnAction = "GoToSheet" .Style = msoButtonAutomatic
Создание пользовательских меню и панелей инструментов Глава 24 587
For Each Sh In ActiveWorkbook.Worksheets If Sh.Visible = True Then .AddItem Sh.Name End If Next Sh .ListIndex = 1 .Width = 110 End WithEnd Sub
Другие способы запуска макросовПомимо меню и панели инструментов, для запуска макроса можно испольY
зовать сочетание клавиш, кнопку и элемент управления ActiveX.
Запуск макроса с помощью сочетания клавиш
Чтобы назначить сочетание клавиш для запуска макроса, выберите имя
макроса в диалоговом окне Макрос (Macro) и щелкните на кнопке Параметры(Options). (Чтобы открыть диалоговое окно Макрос, выберите команду главY
ного меню Excel Сервис Макрос Макросы (Tools Macro Macros) или наY
жмите комбинацию клавиш <Alt+F8>.) Задайте сочетание клавиш, как покаY
зано на рис. 24.6.
Рис. 24.6. Чтобы выполнить макросJump, нажмите сочетание клавиш<Ctrl+J>
Внимание Будьте внимательны при выборе сочетания клавиш для запуска макроса. Многие со&четания клавиш (например, <Ctrl+C>) зарезервированы для выполнения стандартныхопераций в Windows. Относительно безопасными сочетаниями клавиш в Excel 2003являются сочетания <Ctrl+E>, <Ctrl+J>, <Ctrl+M>, <Ctrl+Q> и <Ctrl+T>.
Запуск макроса с помощью кнопки
На рабочем листе Excel можно разместить два типа кнопок: стандартную
кнопку пользовательской формы и кнопку элемента управления ActiveX.
588 Часть III Удивительные возможности Visual Basic for Applications
Выполните следующие действия, чтобы вынести команду добавления
кнопки пользовательской формы на панель управления Excel и разместить на
рабочем листе кнопку запуска макроса.
1. Щелкните на панели инструментов Excel правой кнопкой мыши и выY
берите команду контекстного меню Настройка (Customize).
2. Выберите категорию Формы (Forms) из списка Категории (Categories)
вкладки Команды (Commands) диалогового окна Настройка (Customize).
3. Перетащите команду Кнопка (Button) из списка Команды на панель инY
струментов Excel.
4. Щелкните на кнопке Закрыть (Close), чтобы закрыть диалоговое окно
Настройка.
5. Щелкните на кнопке Кнопка, вынесенной на панель инструментов.
6. С помощью указателя мыши нарисуйте на рабочем листе контур кнопки.
7. Выберите имя макроса в открывшемся диалоговом окне Назначьтемакрос объекту (Assign Macro) и щелкните на кнопке OK.
8. Выделите текст кнопки и измените его на более значащий.
9. Чтобы изменить цвет, шрифт и другие параметры внешнего вида кнопY
ки, щелкните на ней правой кнопкой мыши и выберите команду конY
текстного меню Формат объекта (Format Control).
10. Чтобы назначить кнопке другой макрос, щелкните на ней правой кнопY
кой мыши и выберите команду контекстного меню Назначить макрос(Assign Macro).
Внимание Существует две версии диалогового окна Формат элемента управления (FormatControl). Одна из них содержит семь вкладок (Шрифт (Font), Выравнивание(Alignment), Размер (Size), Защита (Protection), Свойства (Properties), Поля(Margins) и Веб (Web)), другая — только одну (Шрифт (Font)). В результате щелчка правой кнопкой мыши над элементом управления вокруг негопоявляется рамка, заполненная диагональными отрезками (рис. 24.7) или точками(рис. 24.8). В первом случае выбор команды контекстного меню Формат объектаприводит к отображению диалогового окна Формат элемента управления с однойвкладкой, во втором — с семью. Чтобы изменить заливку рамки элемента управления с диагональных отрезков наточки, щелкните на рамке левой кнопкой мыши.
Помимо кнопки, макрос можно назначить любому графическому объекту,
расположенному на рабочем листе Excel. Например, чтобы назначить макрос
автофигуре, щелкните на ней правой кнопкой мыши и выберите команду конY
текстного меню Назначить макрос (Assign Macro), как показано на рис. 24.9.
Создание пользовательских меню и панелей инструментов Глава 24 589
Рис. 24.7. Выбор команды контекстного менюФормат объекта приведет к отображению диа&логового окна Формат элемента управления,содержащего одну вкладку
Рис. 24.8. Выбор команды контекстного менюФормат объекта приведет к отображению диа&логового окна Формат элемента управления,содержащего семь вкладок
Рис. 24.9. Макрос можно назначить любомуграфическому объекту, расположенному на ра&бочем листе Excel
590 Часть III Удивительные возможности Visual Basic for Applications
Назначение макроса автофигуре можно реализовать с помощью программY
ного кода, создав автофигуру и присвоив значение ее свойству OnAction. Тем
не менее, этот метод имеет один существенный недостаток: если назначаемый
макрос находится в другой рабочей книге Excel, то при ее сохранении и заY
крытии Excel изменит значение свойства OnAction, включив в него полный
путь к содержащей макрос рабочей книге.
Запуск макроса с помощью элемента управления ActiveX
Чтобы запустить макрос с помощью элемента управления ActiveX, создайте
процедуру ИмяЭлементаУправленияActiveX_Click, содержащую вызов
макроса или его код. Для этого выполните следующие действия.
1. Выберите команду главного меню Excel Вид Панели инструментовЭлементы управления (View Toolbars Control Toolbox).
2. Щелкните на кнопке Кнопка (Command Button), расположенной на паY
нели инструментов Элементы управления. С помощью указателя мыY
ши нарисуйте на рабочем листе контур кнопки.
3. Чтобы изменить параметры элемента управления ActiveX, щелкните на
кнопке Свойства (Properties), расположенной на панели инструментов
Элементы управления. На экране появится окно свойств, позволяюY
щее задать такие параметры элемента управления, как текст, цвет и др.
(рис. 24.10).
Рис. 24.10. Окно свойств элемента управления ActiveX позволяет на&строить его параметры
Создание пользовательских меню и панелей инструментов Глава 24 591
На заметку Закрытие окна свойств элемента управления ActiveX приводит к закрытию окнасвойств редактора VBA.
4. Чтобы назначить макрос элементу управления ActiveX, щелкните на
кнопке Исходный текст (View Code), расположенной на панели инстY
рументов Элементы управления. Разместите в тексте обработчика соY
бытия ИмяЭлементаУправленияActiveX_Click вызов требуемого
макроса или его код, как показано на рис. 24.11.
Рис. 24.11. Чтобы ввести код обработчика собы&тия ИмяЭлементаУправленияActiveX_Click,щелкните на кнопке Исходный текст, располо&женной на панели инструментов Элементыуправления
Практикум
Запуск макроса с помощью гиперссылкиЧтобы запустить макрос с помощью гиперссылки, следует прибегнуть к неболь&шой хитрости. Выделите ячейку, в которой требуется разместить гиперссылку, по&сле чего выберите команду главного меню Excel Вставка Гиперссылка (InsertHyperlink) или нажмите комбинацию клавиш <Ctrl+K>. В открывшемся диалого&вом окне Добавление гиперссылки (Insert Hyperlink) щелкните на кнопке Местомв документе (Place in This Document). В поле Введите адрес ячейки (Type the cellreference) введите адрес ячейки, в которой находится гиперссылка. Таким обра&зом, гиперссылка будет указывать сама на себя (рис. 24.12).Для запуска требуемого макроса воспользуйтесь обработчиком события Work-sheet_FollowHyperlink, как показано ниже:Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink) Select Case Target.TextToDisplay Case "Widgets" RunWidgetReport Case "Gadgets" RunGadgetReport Case "Gizmos" RunGizmoReport Case "Doodads" RunDooDadReport End SelectEnd Sub
592 Часть III Удивительные возможности Visual Basic for Applications
Рис. 24.12. Чтобы назначить макрос гиперссылке, создайте гиперссылку, ука&зывающую саму на себя, и воспользуйтесь обработчиком события
Worksheet_FollowHyperlink для запуска требуемого макроса
Следующий шагСледующая глава посвящена использованию надстроек в качестве контейY
неров для размещения макросов.
Стандартные надстройкиExcel ................................................ 593Преобразование рабочейкниги Excel в надстройку ........... 594Использование надстроек .........597Альтернативное решение:использование скрытойрабочей книги.............................. 599Следующий шаг............................601
25Глава 25
Íàäñòðîéêè
Надстройки YYYY это программы,
добавляющие в Excel дополнительY
ные команды и возможности.
В этой главе рассматриваются
стандартные надстройки Excel, котоY
рые можно создать средствами VBA.
Существует также два других типа
надстроек: надстройки COM и надY
стройки DLL, однако эти надстройки
можно создать только с помощью ViY
sual Basic (например, Visual Basic 6 или
Visual Basic .NET) или Visual C++.
Стандартныенадстройки Excel
Стандартные надстройки Excel
(.xla) могут использоваться в качеY
стве контейнера макросов. Ниже пеY
речислены основные преимущества
надстроек.
Выполнение обработчика соY
бытия Workbook_Open можY
но отменить путем удерживаY
ния нажатой клавиши <Shift>
при открытии рабочей книги
Excel. Этот трюк невозможно
проделать с обработчиком соY
бытия Workbook_Open, разY
мещенным в надстройке.
После установки надстройки с
помощью диалогового окна
Надстройки (AddYIns) она будет
загружаться при каждом запуске
Excel. (Чтобы открыть диалогоY
вое окно Надстройки, выберите
команду главного меню Excel
Сервис Надстройки (Tools
AddYIns).)
594 Часть III Удивительные возможности Visual Basic for Applications
Размещенный в надстройке код выполняется вне зависимости от выY
бранного уровня безопасности макросов.
Обычно область выполнения макросов ограничивается рабочей книY
гой, в которой они были объявлены. Код, размещенный в надстройке,
доступен всем открытым рабочим книгам Excel.
Надстройки не отображаются в списке открытых файлов меню Окно(Window) и не могут быть выведены на экран с помощью команды
Окно Отобразить (Window Unhide).
Поскольку надстройка является скрытой рабочей книгой, которую нельзя
вывести на экран, не существует возможности выделить или активизировать
ячейки надстройки посредством программного кода. Вдобавок, сохранение
файла надстройки необходимо реализовать в коде самой надстройки, наприY
мер, вызвав метод ThisWorkbook.Save в обработчике события Work-book_BeforeClose.
Внимание Впервые возможность сохранения файла надстройки была реализована в Excel 97.
Преобразование рабочей книги Excelв надстройку
Диалоговое окно Надстройки (AddYIns) содержит список доступных надY
строек. Имя и описание надстройки задается с помощью диалогового окна
свойств файла рабочей книги Excel.
Чтобы задать имя и описание надстройки, выберите команду главного меY
ню Excel Файл Свойства (File Properties) и перейдите во вкладку Документ(Summary), как показано на рис. 25.1.
Введите имя надстройки в поле Название (Title), а ее описание YYYY в поле
Заметки (Comments).
Существует два способа преобразования рабочей книги Excel в надстройку.
Первый способ проще, однако он не лишен некоторых недостатков. Второй
способ требует выполнения большего числа действий, но позволяет лучше
контролировать процесс создания надстройки.
Преобразование рабочей книги Excel в надстройку спомощью диалогового окна “Сохранение документа”(Save As)
Чтобы преобразовать рабочую книгу Excel в надстройку с помощью диаY
логового окна Сохранение документа, выберите команду главного меню ExY
cel Файл Сохранить как (File Save As). Выберите формат НадстройкаMicrosoft Office Excel (Microsoft Office Excel AddYIn (*.xla)) из раскрывающегоY
Надстройки Глава 25 595
ся списка Тип файла (Save as type). Обратите внимание, что папка, в которой
будет сохранена надстройка, изменится на AddIns, как показано на рис. 25.2.
Рис. 25.1. Заполните поля Название и Заметкиперед преобразованием рабочей книги Excel внадстройку
Рис. 25.2. По умолчанию надстройки Excel сохраняются в папке AddIns
Полный путь к папке AddIns зависит от операционной системы, установY
ленной на компьютере. Например, в Windows XP это может быть папка
596 Часть III Удивительные возможности Visual Basic for Applications
C:\Documents and Settings\ИмяПользователя\Application Data\Microsoft\AddIns. После преобразования рабочей книги Excel (.xls) в
надстройку (.xla) файл XLS все еще остается открытым. Хранить этот файл
не имеет смысла, так как надстройку легко преобразовать в рабочую книгу.
Внимание При преобразовании рабочей книги Excel в надстройку с помощью диалоговогоокна Сохранение документа текущим активным листом должен быть рабочийлист. Если текущим активным листом является лист диаграммы, форматНадстройка Microsoft Office Excel окажется недоступен.
Преобразование рабочей книги Excel в надстройкус помощью редактора VBA
Откройте рабочую книгу Excel, которую необходимо преобразовать в надY
стройку. В окне редактора VBA щелкните на модуле ЭтаКнига (ThisWorkbook) и
присвойте параметру IsAddin значение True, как показано на рис. 25.3.
Рис. 25.3. Чтобы преобразовать рабочую книгу внадстройку, присвойте параметру IsAddin модуляЭтаКнига значение True
Нажмите комбинацию клавиш <Ctrl+G>, чтобы открыть окно Immediate(Быстрое выполнение). В окне Immediate введите следующую строку и наY
жмите клавишу <Enter>, чтобы сохранить файл с расширением .xla.
ThisWorkbook.SaveAs FileName:="C:\ClientFiles\Chap25.xla", _FileFormat:=xlAddIn
Надстройки Глава 25 597
Использование надстроекЧтобы установить надстройку, выполните следующие действия.
1. Запустите Excel и выберите команду главного меню СервисНадстройки (Tools AddYIns).
2. Щелкните на кнопке Обзор (Browse), расположенной в открывшемся
диалоговом окне Надстройки (AddYIns) (рис. 25.4).
Рис. 25.4. Щелкните на кнопке Обзор,чтобы выбрать файл надстройки
3. Выберите файл надстройки с помощью диалогового окна Обзор(Browse) и щелкните на кнопке OK (рис. 25.5).
Рис. 25.5. Выберите файл надстройки и щелкните на кнопке OK
598 Часть III Удивительные возможности Visual Basic for Applications
Excel скопирует указанную надстройку в папку AddIns. Имя и описание
надстройки будут совпадать со значениями, введенными в поле Название(Title) и Заметки (Comments) вкладки Документ (Summary) диалогового окна
свойств файла рабочей книги Excel (рис. 25.6).
Рис. 25.6. Надстройка готова к использованию
Безопасность стандартных надстроек Excel
Чтобы отобразить стандартную надстройку Excel, достаточно открыть окно
редактора VBA и присвоить параметру IsAddin модуля ЭтаКнига(ThisWorkbook) значение True. Для защиты проекта VBA выполните следуюY
щие действия.
1. Откройте окно редактора VBA.
2. Выберите команду Tools VBAProject Properties (Сервис Свойства
проекта VBA).
3. Перейдите во вкладку Protection (Защита).
4. Установите флажок Lock project for viewing (Запретить просмотр проекта).
5. Введите пароль и подтверждение пароля.
На заметку Современные программы для взлома паролей справляются с защитой проектаVBA в считанные секунды. Более подробно этот вопрос рассматривается в разделе“Взлом паролей” главы 23 на с. 572.
Надстройки Глава 25 599
Выгрузка надстроек
Чтобы выгрузить надстройку из памяти, выполните одно из следующих
действий.
1. Снимите флажок надстройки в диалоговом окне Надстройки (AddYIns).
В результате выполнения этого действия надстройка становится недосY
тупной в рамках текущего и всех последующих сеансов работы с Excel.
2. Откройте окно Immediate (Быстрое выполнение) редактора VBA. В окне
Immediate введите следующую строку и нажмите клавишу <Enter>:
Workbooks("ИмяФайлаНадстройки.xla").Close
3. Закройте Excel. Завершение текущего сеанса работы с Excel приводит к
автоматической выгрузке из памяти всех надстроек.
Удаление надстроек
Чтобы удалить надстройку из списка доступных надстроек диалогового окY
на Надстройки (AddYIns), выполните следующие действия.
1. Закройте все открытые сеансы работы с Excel.
2. Отыщите файл надстройки с помощью средства Проводник (Windows
Explorer). На компьютере с Windows XP файл надстройки может нахоY
диться в папке C:\Documents and Settings\ИмяПользователя\Application Data\Microsoft\AddIns.
3. Переименуйте файл надстройки или переместите его в другую папку.
4. Запустите Excel. На экране появится сообщение о том, что Excel не моY
жет найти файл надстройки. Щелкните на кнопке OK, чтобы закрыть
окно сообщения.
5. Выберите команду главного меню Excel Сервис Надстройки (Tools
AddYIns). В окне Надстройки снимите флажок надстройки, которую неY
обходимо удалить. Excel предупредит об отсутствии надстройки и предY
ложит ее удалить. Щелкните на кнопке Да (Yes), чтобы удалить надY
стройку из списка доступных надстроек.
Альтернативное решение: использованиескрытой рабочей книги
Одна из ключевых характеристик надстроек состоит в том, что надстройки
являются скрытыми рабочими книгами. Однако для создания скрытой рабоY
чей книги ее совсем не обязательно преобразовывать в надстройку.
Чтобы скрыть рабочую книгу, выберите команду главного меню Excel
Окно Скрыть (Window Hide). Сохранить скрытую рабочую книгу можно
только с помощью редактора VBA, поскольку стандартные средства Excel сдеY
600 Часть III Удивительные возможности Visual Basic for Applications
лать этого не позволяют. Выделите рабочую книгу в окне диспетчера проекY
тов, введите следующую строку в окне Immediate (Быстрое выполнение) и
нажмите клавишу <Enter>:
ThisWorkbook.Save
Практикум
Размещение программного кодаи пользовательских форм в скрытойрабочей книгеРазработчики приложений Access размещают данные и программный код в раз&ных базах данных, объединяя их с помощью средства связывания таблиц.Аналогичный подход рекомендуется применять при создании крупных проектов вExcel. Все данные можно разместить в рабочей книге Data.xls, связав ее с рабо&чей книгой Code.xls с помощью небольшого фрагмента программного кода.Преимущество разделения данных и кода заключается в возможности обновленияпоследнего без необходимости изменения пользовательских данных.Рассмотрим следующую ситуацию. Предположим, что некоторое приложение Ex&cel, реализованное в виде одного файла рабочей книги, было передано50 агентам по продаже товаров, каждый из которых, в свою очередь, распростра&нил его среди 10 крупных заказчиков. Если в приложении будет обнаруженаошибка, ее потребуется исправить во всех 500 копиях рабочей книги.Очевидно, что исходное приложение нуждается в существенной переработке.Создадим две рабочие книги — для хранения данных (Data.xls) и программногокода (Code.xls). Рабочая книга Data.xls будет содержать около 20 строк кода,открывающего рабочую книгу Code.xls и передающего ей управление при от&крытии рабочей книги Data.xls, а также закрывающего рабочую книгуCode.xls при закрытии рабочей книги Data.xls.Разделение данных и кода имеет несколько преимуществ. Во&первых, существен&но уменьшится размер файла данных. Во&вторых, для внесения изменений в при&ложение достаточно исправить код рабочей книги Code.xls и распространить еесреди всех пользователей.Будьте особенно внимательны при разработке и тестировании кода, помещае&мого в рабочую книгу с данными. Ошибка, допущенная в этом коде, может по&влечь за собой необходимость исправления сотен файлов рабочих книг.Ниже приведен пример кода, помещаемого в рабочую книгу Data.xls.Private Sub Workbook_Open()' Проверить, открыт ли файл Code.xls. On Error Resume Next x = Workbooks("Code.xls").Name ErrHolder = Err.Number On Error GoTo 0 If ErrHolder <> 0 Then' Открыть рабочую книгу, содержащую программный код. CodeFile = ThisWorkbook.Path & _
Надстройки Глава 25 601
Application.PathSeparator & "Code.xls" On Error Resume Next Workbooks.Open CodeFile ErrHolder = Err.Number On Error GoTo 0 End If
' Выполнить макрос, содержащийся в рабочей книге Code.xls. If ErrHolder = 0 Then Application.Run "'Code.xls'!CustFileOpen" End IfEnd Sub
Процедура CustFileOpen может использоваться для создания меню, внесенияизменений в файл данных и т.п.Разделение данных и программного кода является ключевым моментом при раз&работке сложного приложения Excel. Для внесения изменений в приложениедостаточно исправить код рабочей книги Code.xls и распространить ее средивсех пользователей.
Следующий шагПоследняя глава книги представляет собой практикум Тушара Мехты
(Tushar Mehta) YYYY независимого консультанта, имеющего статус Microsoft
MVP. Тушар демонстрирует процесс создания приложения Excel ‘‘с нуля’’.
О Тушаре Мехта........................... 603Постановка задачи ..................... 604Решение......................................... 605Реализация решенияс помощью Excel и VBA .............. 606Резюме............................................614
26Глава 26
Ïðàêòèêóì:ñîçäàíèåïðèëîæåíèÿExcel “ñ íóëÿ”
Эта глава представляет собой
практикум Тушара Мехты (Tushar
Mehta) YYYY независимого консультанY
та, имеющего статус Microsoft MVP.
Тушар демонстрирует процесс создаY
ния приложения Excel ‘‘с нуля’’, заY
трагивая множество различных воY
просов проектирования.
О Тушаре Мехта Тушар Мехта, магистр менеджY
мента, доктор философии, проживаY
ет в Рочестере, штат НьюYЙорк,
США. ГYн Тушар YYYY независимый
консультант, специализирующийся
на разработке бизнесYрешений, объеY
диняющих в себе технологию, оргаY
низационную структуру, маркетинг и
производство. Финансовые и эконоY
мические результаты предлагаемых
Тушаром решений заключаются в
повышении производительности, боY
лее эффективном маркетинге, росте
дохода, снижении операционных
расходов и улучшении потребительY
ской ценности.
Начиная с 2000 года, Тушар ежеY
годно удостаивается звания MVP
(Most Valuable Professional) от корпоY
рации Microsoft. Подобного успеха
удалось добиться только 65 разработY
604 Часть III Удивительные возможности Visual Basic for Applications
чикам Excel. С некоторыми из созданных Тушаром решений вы можете ознаY
комиться на его личном WebYсайте по адресу: http://www.tushar-mehta.com.
Имея степень доктора делового администрирования, полученную в униY
верситете Рочестера, Тушар также является магистром менеджмента бизнесY
школы Йельского университета и бакалавром технологии в области электроY
техники (специальность YYYY вычислительная техника) Индийского технологиY
ческого института в Бомбее, Индия.
Постановка задачи Рассмотрим применение Excel в качестве платформы, используемой для
документирования конфигурации системы электрического управления,
состоящей из 6000 контактных точек. Каждая контактная точка может быть
соединена с другой контактной точкой с помощью некоторого проводника.
Предложенное решение должно предусматривать возможность одновременY
ного использования всех контактных точек.
Идентификация контактных точек осуществляется по следующей схеме.
Каждой точке ставится в соответствие комбинация из двух чисел m/nnn, где mпринимает значения в диапазоне от 1 до 10 (по горизонтали), а nnn — от 101 до
700 (по вертикали).
Каждая из 6000 контактных точек представлена одной ячейкой рабочего
листа Excel. Если контактная точка соединена с другой контактной точкой, в
соответствующую ей ячейку заносится адрес контактной точки на другом конY
це соединения.
Контактная точка не может быть использована более чем в одном соединеY
нии. Помимо создания, соединения можно разрывать и изменять. Очевидно,
что поддержание подобной системы документации вручную представляет соY
бой чрезвычайно утомительное занятие с большой вероятностью внесения неY
верных данных.
Процесс разработки соответствующего решения можно разделить на слеY
дующие этапы.
Отображение терминологии проблемной области (терминологии заY
казчика) на терминологию Excel.
Разработка визуального представления проблемной области.
Разработка функциональных процедур, соответствующих ключевым
возможностям приложения.
Разработка процедур VBA, реализующих предусмотренные на предыY
дущем этапе функциональные процедуры.
Возможно ли автоматизировать подобную систему? И если да, то каким
образом? Очевидно, что для решения задачи достаточно реализовать автомаY
тическое занесение кода контактной точки А в ячейку контактной точки Б
Практикум: создание приложения Excel “с нуля” Глава 26 605
при вводе кода последней в ячейку контактной точки А. И конечно же, нужно
запретить установку соединения с контактной точкой Б, если она уже испольY
зуется в другом соединении.
Чтобы разорвать соединение, пользователь должен удалить содержимое
любой из ячеек, соответствующих участвующим в соединении контактным
точкам. Например, чтобы разорвать соединение между контактной точкой
1/101 и контактной точкой 10/700, пользователю необходимо удалить содерY
жимое ячейки, соответствующей контактной точке 1/101, или ячейки, соотY
ветствующей контактной точке 10/700. Система восстановит начальное знаY
чение и формат ячеек.
Процесс изменения соединения состоит из двух этапов. Первый этап заY
ключается в удалении существующего соединения, второй этап YYYY в создании
нового соединения.
Решение Матрица электрических контактов состоит из 100 элементов по горизонтаY
ли и 60 элементов по вертикали. Рабочий лист Excel удовлетворяет этим параY
метрам, так как содержит 256 столбцов и 65 536 строк.
Создадим матрицу контактных точек путем введения их кодов в формате
m/nnn в ячейки рабочего листа, начиная с ячейки A1. Код контактной точки,
не участвующей в соединении, выделен светлоYсерым шрифтом, как показано
на рис. 26.1.
При попытке создания соединения система анализирует предыдущее соY
стояние ячейки и ее новое содержимое. Если код соответствующей контактY
ной точки был выделен светлоYсерым цветом (‘‘нет соединения’’), проверяетY
ся возможность создания указанного соединения. При благоприятном резульY
тате содержимое ячеек на обоих концах соединения обновляется и выделяется
синим цветом. На рис. 26.2 показан результат соединения контактных точек
1/101 и 1/175.
Рис. 26.1. Код контактной точки, не участвую&щей в соединении, выделен светло&серымшрифтом
Рис. 26.2. Коды контактных точек, участвую&щих в соединении, выделяются синим цветом
Помимо создания нового соединения, система должна обеспечить разрыв и
изменение существующего соединения. Разрыв соединения соответствует удаY
лению содержимого ячейки с помощью клавиши <Delete> или <Backspace>.
606 Часть III Удивительные возможности Visual Basic for Applications
Чтобы изменить соединение, достаточно ввести в ячейку новый код контактной
точки. Система разорвет существующее соединение и создаст новое.
Предлагаемое решение не обеспечивает защиты от непреднамеренного
разрушения матрицы контактных точек. Например, вставка в рабочий лист
матрицы контактных точек диапазона ячеек с другого рабочего листа приведет
к разрушению матрицы. Целостность матрицы может быть нарушена также в
результате удаления ячеек рабочего листа с помощью команды меню Excel.
Реализация защиты целостности матрицы контактных точек выходит за рамки
данного практикума.
Реализация решения с помощью Excel и VBA Один из ключевых моментов этого практикума состоит в использовании
обработчиков событий. Известно, что большое число обработчиков событий
затрудняет тестирование и отладку программного кода. Таким образом, горазY
до проще потратить чуть больше времени на создание надежного кода, чем
пытаться отладить сбойный код путем тестирования.
Разделим процесс реализации решения на два параллельных потока. С одY
ной стороны, при создании кода будет использоваться принцип нисходящего
программирования. С другой стороны, по мере конкретизации кода к проекту
будут добавляться так называемые ключевые компоненты, выполняющие
роль ‘‘мостов’’ между моделью приложения и моделью Excel.
Этап 1а: нисходящее программирование
Любой обработчик события может быть размещен на одном из трех уровY
ней: уровне рабочего листа, уровне рабочей книги или уровне приложения.
Воспользуемся обработчиками событий уровня рабочей книги, поскольку, воY
первых, это позволит обеспечить поддержку нескольких матриц электричеY
ских контактов (каждая матрица размещается на отдельном рабочем листе), а,
воYвторых, реализовать обработчики событий уровня рабочей книги проще,
чем обработчики событий уровня приложения. Наиболее существенный неY
достаток подобного подхода заключается в совмещении кода и данных в одY
ной и той же рабочей книге Excel. Поскольку обработчики событий будут выY
зываться вне зависимости от того, содержит рабочий лист матрицу электричеY
ских контактов или нет, необходимо также создать специальный дескрипторрабочего листа матрицы контактов.
Разместите код обработчика события Workbook_SheetChange в модуле
ЭтаКнига (ThisWorkbook).
Private Sub Workbook_SheetChange( _ ByVal Sh As Object, ByVal Target As Range) If Not TypeOf Sh Is Worksheet Then Exit Sub If Not (Sh.Range(cWSWiringTagAddr).Value = _cWSWiringTagID) Then Exit Sub '<<<<<End Sub
Практикум: создание приложения Excel “с нуля” Глава 26 607
Действие, предпринимаемое в результате изменения содержимого ячейки
рабочего листа матрицы контактов (создание нового соединения или разY
рыв/изменение существующего), на данном этапе не конкретизируется.
Процедура, инициализирующая матрицу, заполняет ячейки рабочего листа
соответствующими кодами контактных точек в формате m/nnn, изменяя цвет
шрифта ячейки на светлоYсерый. Кроме того, процедура инициализации
предлагает удалить существующее содержимое рабочего листа и добавляет к
нему дескриптор рабочего листа матрицы контактов. Разместите следующий
код в стандартном модуле.
Sub createWiringData(aWS As Worksheet)End Sub
Sub addWiringWSTag(aWS As Worksheet)End Sub
Sub initializeSystem() If Not TypeOf ActiveSheet Is Worksheet Then MsgBox "Активным листом должен быть рабочий лист" Exit Sub '<<<<< End If If Not (ActiveSheet.UsedRange.Address = "$A$1" And _ IsEmpty(Range("a1"))) Then If MsgBox("Рабочий лист содержит данные. " _& "Щелкните на кнопке OK, чтобы удалить их", vbOKCancel) _= vbCancel Then Exit Sub '<<<<< End If createWiringData ActiveSheet addWiringWSTag ActiveSheetEnd Sub
Код процедур createWiringData и addWiringWSTag будет создан на
следующих этапах разработки приложения.
Этап 1б: создание ключевых компонентов
Создадим ключевой компонент, преобразующий коды контактных точек
m/nnn в адреса ячеек рабочего листа Excel, и наоборот. Адрес ячейки рабочего
листа Excel состоит из двух частей: номера строки и номера столбца. Функция
XLtoWiringCoords преобразует адрес ячейки в код контактной точки, а
функция WiringToExcelCoords YYYY код контактной точки в адрес ячейки.
Для представления адреса ячейки применяется пользовательский тип Excel-Coords. Разместите следующий код в стандартном модуле.
Public Type ExcelCoords Row As Long Col As Long End Type
Function XLtoWiringCoords(aRow As Long, aCol As Long) XLtoWiringCoords = CStr((aCol - 1) \ 10 + 1) & "/" _ & CStr(100 + (aRow - 1) * 10 + (aCol - 1) Mod 10 + 1)End Function
608 Часть III Удивительные возможности Visual Basic for Applications
Function WiringToExcelCoords(WiringCode As String) As ExcelCoords Dim FirstPart As Long, SecondPart As Long, SlashLoc As Long SlashLoc = InStr(1, WiringCode, "/") If SlashLoc = 0 Then Exit Function On Error Resume Next FirstPart = CLng(Left(WiringCode, SlashLoc - 1)) SecondPart = CLng(Right(WiringCode, Len(WiringCode) - SlashLoc)) On Error GoTo 0 If FirstPart < 1 Or FirstPart > 10 _Then Exit Function '<<<<< If SecondPart < 101 Or SecondPart > 700 _Then Exit Function '<<<<< WiringToExcelCoords.Row = (SecondPart - 101) \ 10 + 1 WiringToExcelCoords.Col = (FirstPart - 1) * 10 + _ (SecondPart - 101) Mod 10 + 1End Function
Объявим несколько глобальных констант. В стандартной цветовой палитре
Excel светлоYсерый цвет, использующийся для представления свободных конY
тактных точек, имеет индекс 15, а синий цвет, использующийся для представY
ления занятых контактных точек, YYYY индекс 5. Дескриптор рабочего листа
матрицы контактов будет представлен глобальной константой cWSWiring-TagID, размещенной в ячейке cWSWiringTagAddr. Поместите следующий
код в начало стандартного модуля, содержащего процедуру initializeSys-tem, а также функции преобразования XLtoWiringCoords и WiringToEx-celCoords.
Public Const _ cUnusedColorIdx As Integer = 15, _ cInUseColorIdx As Integer = 5, _ cWSWiringTagID As String = "Матрица электрических контактов", _ cWSWiringTagAddr As String = "A61"
Первый этап разработки приложения завершен. Следует отметить, что созY
данный код не только компилируется, но даже выполняется!
Этап 2а: нисходящее программирование
Создадим код процедуры addWiringWSTag, объявленной на предыдущем
этапе разработки.
Sub addWiringWSTag(aWS As Worksheet) Application.EnableEvents = False On Error GoTo ErrHandler aWS.Range(cWSWiringTagAddr).Value = cWSWiringTagID Application.EnableEvents = True Exit SubErrHandler: MsgBox "Непредвиденная ошибка в процедуре addWiringWSTag:" _& vbNewLine & "Ошибка=" & Err.Description & " (" & Err.Number & ")" Application.EnableEvents = TrueEnd Sub
Практикум: создание приложения Excel “с нуля” Глава 26 609
Процедура createWiringData создает матрицу кодов контактных точек
(значений в формате m/nnn), начиная со значения 1/101 в ячейке A1 и заканY
чивая значением 10/700 в ячейке CV60. Указанная матрица состоит из
10 блоков значений от m/101 до m/700 (m=1, 2,..., 10), каждый из которых
представлен в Excel диапазоном ячеек, содержащим 60 строк и 10 столбцов.
Следующий код создает блок значений nnn, копирует его 9 раз (таким обраY
зом, всего будет создано 10 таких блоков) и добавляет соответствующее значеY
ние m/ к каждому блоку.
Sub createWiringData(aWS As Worksheet) Dim Dest As Range, i As Long'Процедура createWiringData создает матрицу кодов _контактных точек (значений в формате m/nnn), начиная _со значения 1/101 в ячейке A1 и заканчивая значением 10/700 _в ячейке CV60. Указанная матрица состоит из 10 блоков значений _от m/101 до m/700 (m=1, 2,..., 10), каждый из которых представлен _в Excel диапазоном ячеек, содержащим 60 строк и 10 столбцов. _Следующий код создает блок значений nnn, копирует его 9 раз (таким _образом, всего будет создано 10 таких блоков) и добавляет _соответствующее значение m/ к каждому блоку. При создании _nnn-блоков используется общий формат чисел, а при добавлении _значения m/ - текстовый формат ячеек, иначе выражение m/nnn будет _интерпретировано как деление двух чисел. Application.EnableEvents = False On Error GoTo ErrHandler resetFormatting aWS createOneDataBlock aWS cloneDataBlocks aWS addMSlash aWS Application.EnableEvents = True Exit SubErrHandler: MsgBox "Непредвиденная ошибка в процедуре createWiringData:" _& vbNewLine & "Ошибка=" & Err.Description & " (" & Err.Number & ")" Application.EnableEvents = TrueEnd Sub
Последней процедурой, созданной на первом этапе и нуждающейся в конY
кретизации, является обработчик события Workbook_SheetChange, срабаY
тывающий при изменении содержимого ячейки рабочего листа Excel. Если
новое значение ячейки отличается от старого, необходимо определить тип
действия: создание нового соединения, разрыв существующего соединения
или изменения существующего соединения.
Изменение значения ячейки, соответствующей незанятой контактной точке
(светлоYсерый цвет шрифта), свидетельствует о создании нового соединения.
Разрыв соединения осуществляется путем удаления содержимого ячейки с
помощью нажатия клавиши <Delete> или <Backspace>. Таким образом, о разY
рыве соединения свидетельствует пустая ячейка.
Все остальные действия классифицируются как попытка изменения сущеY
ствующего соединения.
Если пользователь попытается нарушить целостность матрицы, его нужно
уведомить об этом и восстановить предыдущее значение ячейки.
610 Часть III Удивительные возможности Visual Basic for Applications
Чтобы сохранить предыдущее значение ячейки, воспользуемся обработчиY
ком события Workbook_SheetSelectionChange, срабатывающим при выY
делении ячейки.
Этап 2б: создание ключевых компонентов
Создадим обработчик события Workbook_SheetSelectionChange, соY
храняющий предыдущее значение ячейки при ее выделении. Возможность выY
деления пользователем нескольких ячеек следует запретить, так как это привеY
дет к существенному усложнению программного кода. Ниже приведен код проY
цедур Workbook_SheetChange, Workbook_SheetSelectionChange, ‘‘загоY
товки’’ процедур создания и разрыва соединения (addAConnection и de-leteAConnection, соответственно), а также полный код процедуры изменеY
ния соединения (changeAConnection). Последняя проверяет корректность
введенного пользователем значения, сохраняет его, после чего вызывает проY
цедуры удаления и создания нового соединения. Весь следующий код должен
быть помещен в модуль ЭтаКнига (ThisWorkbook).
Dim OldVal As String
Function addAConnection(Sh As Worksheet, Target As Range)End Function
Sub deleteAConnection(Sh As Worksheet, Target As Range, _OldVal As String)End Sub
Sub changeAConnection(Sh As Worksheet, Target As Range, _OldVal As String) Dim Conn2 As ExcelCoords Dim SavedVal As String Conn2 = WiringToExcelCoords(Target.Value) If Conn2.Row = 0 Then MsgBox Target.Value & " : недопустимое значение" Application.EnableEvents = False Target.Value = OldVal Application.EnableEvents = True Exit Sub '<<<<< End If SavedVal = Target.Value deleteAConnection Sh, Target, OldVal Application.EnableEvents = False Target.Value = SavedVal Application.EnableEvents = True addAConnection Sh, TargetEnd Sub
Private Sub Workbook_SheetChange( _ ByVal Sh As Object, ByVal Target As Range) If Not TypeOf Sh Is Worksheet Then Exit Sub If Not (Sh.Range(cWSWiringTagAddr).Value = cWSWiringTagID) _Then Exit Sub '<<<<< If Target.Cells.Count > 1 Then
Практикум: создание приложения Excel “с нуля” Глава 26 611
MsgBox "Версия 1 этого приложения не поддерживает" & vbNewLine _& "одновременное изменение нескольких соединений." & vbNewLine _& "Данное изменение нарушает целостность матрицы." & vbNewLine _& "Матрица электрических контактов разрушена! " & vbNewLine _& "Автоматическое изменение матрицы отменено." Exit Sub End If If OldVal = Target.Value Then Exit Sub '<<<<< If Target.Font.ColorIndex = cUnusedColorIdx Then addAConnection Sh, Target ElseIf IsEmpty(Target) Then deleteAConnection Sh, Target, OldVal Else changeAConnection Sh, Target, OldVal End IfEnd Sub
Private Sub Workbook_SheetSelectionChange( _ ByVal Sh As Object, ByVal Target As Range) If Not TypeOf Sh Is Worksheet Then Exit Sub If Not (Sh.Range(cWSWiringTagAddr).Value = cWSWiringTagID) _Then Exit Sub '<<<<< If Target.Cells.Count > 1 ThenMsgBox "Версия 1 этого приложения не поддерживает" & vbNewLine _& "одновременное изменение нескольких соединений." & vbNewLine _& "Пожалуйста, выделите только одну ячейку."'Предотвратить рекурсивный вызов процедуры! Target.Cells(1).Select Exit Sub End If OldVal = Target.ValueEnd Sub
Этап 3а: нисходящее программирование
Разработка приложения практически завершена. Осталось создать код
процедур, имеющих непосредственное отношение к инициализации системы,
а также код процедур создания и разрыва соединения.
Следующие четыре процедуры используются при инициализации системы.
Процедура resetFormatting удаляет существующее содержимое раY
бочего листа, задает общий формат чисел и светлоYсерый цвет шрифта.
Sub resetFormatting(aWS As Worksheet) With aWS.Cells .ClearContents .NumberFormat = "General" .Font.ColorIndex = cUnusedColorIdx End WithEnd Sub
Процедура createOneDataBlock использует эквивалент команды
главного меню Excel Правка Заполнить Прогрессия (Edit Fill
Series) для заполнения значениями от 101 до 700 блока, состоящего из
60 строк и 10 столбцов.
612 Часть III Удивительные возможности Visual Basic for Applications
Sub createOneDataBlock(aWS As Worksheet) With aWS.Range("A1") .FormulaR1C1 = "101" .DataSeries Rowcol:=xlRows, Type:=xlLinear, _ Date:=xlDay, _ Step:=1, Stop:=110, Trend:=False .Resize(1, 10).DataSeries Rowcol:=xlColumns, _ Type:=xlLinear, Date:=xlDay, _ Step:=10, Stop:=700, Trend:=False End WithEnd Sub
Процедура cloneDataBlocks создает 9 копий полученного блока
значений nnn, каждый раз определяя его новые координаты.
Sub cloneDataBlocks(aWS As Worksheet) Dim Dest As Range, i As Integer With aWS.Range("A1") Set Dest = .Offset(, 10) For i = 2 To 9 Set Dest = Union(Dest, .Offset(, i * 10)) Next i .CurrentRegion.Copy Dest End WithEnd Sub
Процедура addMSlash заносит формулу Excel в диапазон, состоящий
из 60 строк и 100 столбцов и расположенный непосредственно под суY
ществующим содержимым рабочего листа. Формула Excel отображает
значения nnn на соответствующие им коды контактных точек m/nnn.
После этого процедура addMSlash копирует полученную матрицу на
место диапазона значений nnn и удаляет исходный диапазон формул.
Sub addMSlash(aWS As Worksheet) With aWS.Range("A1").Offset(60, 0).Resize(60, 100)' В англоязычной версии Excel:' .FormulaR1C1 = _' "=TRUNC((COLUMN()-1)/10,0)+1 &""/""&R[-60]C" .FormulaR1C1Local = _ "=ОТБР((СТОЛБЕЦ()-1)/10,0)+1 &""/""&R[-60]C" aWS.Cells.NumberFormat = "@" .Copy aWS.Range("a1").PasteSpecial xlPasteValues .EntireRow.Delete End WithEnd Sub
Процедура addAConnection создает соединение между указанными конY
тактными точками. При возникновении ошибки вызывается процедура re-setValue, которая восстанавливает начальное содержимое ячейки рабочего
листа Excel.
Function addAConnection(Sh As Worksheet, Target As Range) Dim Conn2 As ExcelCoords Conn2 = WiringToExcelCoords(Target.Value) If Conn2.Row = 0 Then MsgBox Target.Value & " : недопустимое значение"
Практикум: создание приложения Excel “с нуля” Глава 26 613
addAConnection = True resetValue Target Exit Function '<<<<< End If With Sh.Cells(Conn2.Row, Conn2.Col) If .Font.ColorIndex <> cUnusedColorIdx Then MsgBox Target.Value & " занята!" & vbNewLine _ & "Она соединена с контактной точкой " & _Cells(Conn2.Row, Conn2.Col).Value resetValue Target addAConnection = True Exit Function '<<<<< End If Application.EnableEvents = False On Error GoTo ErrHandler .Value = XLtoWiringCoords(Target.Row, Target.Column) .Font.ColorIndex = cInUseColorIdx End With Target.Font.ColorIndex = cInUseColorIdx Application.EnableEvents = True Exit FunctionErrHandler: MsgBox "Непредвиденная ошибка в функции addAConnection: _Ячейка=" & Target.Address & vbNewLine _ & "Ошибка=" & Err.Description & " (" & Err.Number & ")" Application.EnableEvents = True End Function
Процедура deleteAConnection разрывает соединение между указанныY
ми контактными точками путем восстановления начальных значений соответY
ствующих ячеек рабочего листа Excel.
Sub deleteAConnection(Sh As Worksheet, Target As Range, _OldVal As String) Dim Conn2 As ExcelCoords Conn2 = WiringToExcelCoords(OldVal) Application.EnableEvents = False On Error GoTo ErrHandler With Conn2 resetValue Sh.Cells(.Row, .Col) End With resetValue Target Application.EnableEvents = True Exit SubErrHandler: MsgBox "Непредвиденная ошибка в процедуре _deleteAConnection: Ячейка=" & Target.Address & vbNewLine _& "Ошибка=" & Err.Description & " (" & Err.Number & ")" Application.EnableEvents = True End Sub
Этап 3б: создание ключевых компонентов
Ниже приведен код процедуры resetValue, восстанавливающей начальY
ное значение и форматирование ячейки рабочего листа Excel.
614 Часть III Удивительные возможности Visual Basic for Applications
Sub resetValue(Target As Range) Application.EnableEvents = False On Error GoTo ErrHandler With Target .Value = XLtoWiringCoords(.Row, .Column) .Font.ColorIndex = cUnusedColorIdx End With Application.EnableEvents = True Exit SubErrHandler: MsgBox "Непредвиденная ошибка в процедуре resetValue: _Ячейка=" & Target.Address & vbNewLine & "Ошибка=" & _Err.Description & " (" & Err.Number & ")" Application.EnableEvents = TrueEnd Sub
РезюмеВ этом практикуме был продемонстрирован процесс разработки реального
приложения Excel ‘‘с нуля’’ с использованием различных методик программиY
рования: структурного анализа, принципа распараллеливания и принципа
нисходящего программирования.
Предметный указатель
..NET Tools for Office, 31
AADO (ActiveX Data Objects), 490
объект
Connection, 492
Recordset, 492
API (Application Programming
Interface). См. Windows API
CCSV, 429
DDAO (Data Access Objects), 490
LLotus 1Y2Y3, 29
MMDB (Multidimensional Database), 489
Microsoft Access, 489
Microsoft Jet, 492
Microsoft Word, 439
SStarOffice, 30
VVBA. См. Visual Basic for Applications
VisiCalc, 29
Visual Basic for Applications, 59
синтаксис, 60
справочная система, 63
WWebYзапрос
обновление, 409
создание, 407; 410
Windows API, 547
объявление, 548
использование, 548
примеры, 549
структура, 548
XXML, 427
правила, 428
сопоставление данных, 430
схема данных, 429
ААвтозаполнение, 236
Автофильтр, 297
ББриклин, Дэн, 29
ВВкладка, 225
Выражение
On Error GoTo, 564
Property Get, 513
Property Let, 513
ДДиаграмма, 229
биржевая, 254
616 Предметный указатель
встроенная, 204; 230
контейнер, 230
гистограмма, 251
график, 251
иерархическая кольцевая, 265
изменение размещения, 235
кольцевая, 253
коническая, 255
кривой предложения, 264
круговая, 252; 258
круговая пузырьковая, 262
легенда, 247
лепестковая, 253
линейчатая, 251
линии сетки, 245
линия тренда, 248
название, 247
область диаграммы, 231; 237
область построения
диаграммы, 240
ось диаграммы, 243
вспомогательная, 245
пирамидальная, 255
поверхностная, 253
подписи данных, 246
полосы погрешности, 248
пузырьковая, 253
расположенная на отдельном
листе, 232
ряд данных, 242
с областями, 253
с точками данных в виде
спидометров, 264
таблица данных, 247
тип, 251
точечная, 252; 262
трехмерная, 256
цилиндрическая, 254
Диапазон ячеек, 95
именованный, 96
Динамически подключаемая
библиотека, 547
Диспетчер
объектов, 85
проектов, 46
Документ Word
закрытие, 447
открытие, 447
печать, 447
создание, 446
сохранение, 447
ИИзображение, 223
Имя
глобальное, 177
зарезервированное, 183
локальное, 177
массива, 183
создание, 179
строки, 181
удаление, 180
формулы, 181
числа, 182
ККейпор, Мич, 29
Кнопка, 220
Код
оптимизация, 90
отладка, 561
Коллекция, 60; 63; 515
Areas, 108
Axes, 243
Charts, 233
SeriesCollection, 242
создание
в модуле класса, 516
в стандартном модуле, 515
Константа
предопределенная, 68
Конструкция
Do Until...Loop, 151
Do While...Loop, 150
Do...Loop, 147
For Each...Next, 152
For...Next, 141
If...ElseIf...End If, 157
If...Then...Else, 155
If...Then...Else...End If, 156
If...Then...End If, 156
Select Case...End Select, 157
Type...End Type, 521
Предметный указатель 617
While...Wend, 152
With...End With, 90
ЛЛичная книга макросов, 42
ММакрос
безопасность, 40
выполнение, 43
с помощью гиперссылки, 591
с помощью кнопки, 587
с помощью сочетания
клавиш, 587
с помощью элемента
управления ActiveX, 590
запись, 42; 48; 53
сохранение, 42
Массив, 463
динамический, 469
заполнение, 464
манипулирование элементами, 466
многомерный, 464
объявление, 463
одномерный, 464
Матрица, 464
Меню
пользовательское
группирование команд, 578
добавление команд, 577
создание, 575
создание подменю, 579
удаление, 575
Метод, 60; 63
Intersect, 103
Microsoft Word
EndKey, 448
HomeKey, 448
TypeText, 448
OpenText, 66
SpecialCells, 106
Union, 103
параметр, 61; 63
Модуль, 46
класса, 47; 505
создание, 506
ННабор вкладок, 529
Надпись, 220
Надстройка, 593
безопасность, 598
выгрузка, 599
создание, 594
стандартная, 593
удаление, 599
установка, 597
ООбъект, 60; 63
ADO
Connection, 492
Recordset, 492
ChartArea, 237
ChartObject, 230
Microsoft Word
Document, 446
Range, 449
Selection, 448
PlotArea, 241
Range, 95
пользовательский
применение, 511
создание, 510
Ограничивающий прямоугольник, 241
Окно
Immediate (Быстрое
выполнение), 79
Watches (Просмотр), 82
ввода данных, 215
свойств, 47
сообщения, 216
Ошибка
времени выполнения
‘‘Method 'Range' of object
'_Global' failed’’, 571
‘‘Subscript out of range’’, 570
игнорирование, 566
извлечение пользы, 568
обработка, 561
выражение On Error GoTo, 564
универсальный обработчик, 566
618 Предметный указатель
ППанель инструментов, 222
UserForm, 525
Visual Basic, 39
пользовательская
выбор значка кнопки, 584
добавление кнопок, 582
добавление раскрывающегося
списка, 584
создание, 582
сохранение и восстановление
координат, 585
удаление, 582
Параметр, 61; 63
TrailingMinusNumbers, 68; 478
необязательный, 68
события, 190
Переключатель, 222; 528
Переменная, 89
объектная, 152; 236
цикла, 144
Песочница, 40
Поле ввода, 220
адреса диапазона ячеек, 530
Полоса прокрутки, 539
Пользовательская форма
вкладка, 225
вызов, 218
добавление элемента
управления, 219
закрытие, 218; 225
изображение, 223
кнопка, 220
модальная, 531
набор вкладок, 529
надпись, 220
немодальная, 531
панель, 222
переключатель, 222; 528
поле ввода, 220
адреса диапазона ячеек, 530
полоса прокрутки, 539
создание, 216
список, 221
комбинированный, 221
счетчик, 224
эффект прозрачности, 545
Примечания ячеек, 376
РРасширенный фильтр, 267; 364
автофильтр, 297
диапазон условий, 276
Редактор Visual Basic, 45
автозаполнение, 236
диспетчер объектов, 85
диспетчер проектов, 46
окно Immediate (Быстрое
выполнение), 79
окно Watches (Просмотр), 82
окно свойств, 47
отладчик, 74
параметры, 45
ССводная таблица, 299
автоотображение лучшей
десятки, 340
вычисляемое поле, 328
вычисляемый элемент, 331
группирование дат, 333
определение размера, 307
перемещение и изменение, 306
создание, 300; 303
удаление, 307
фильтрация данных, 344
Свойство, 62; 63
Cells, 99
Columns, 102
CurrentRegion, 105
Offset, 100
Resize, 101
Rows, 102
Связывание
позднее, 442
раннее, 439
Событие, 189
AppEvent_NewWorkbook, 210
AppEvent_SheetActivate, 210
AppEvent_SheetBeforeY
DoubleClick, 210
AppEvent_SheetBeforeY
RightClick, 210
AppEvent_SheetCalculate, 211
AppEvent_SheetChange, 211
Предметный указатель 619
AppEvent_SheetDeactivate, 211
AppEvent_SheetFollowHyperlink,
211
AppEvent_SheetSelectionChange,
211
AppEvent_WindowActivate, 211
AppEvent_WindowDeactivate, 211
AppEvent_WindowResize, 212
AppEvent_WorkbookActivate, 212
AppEvent_WorkbookAddinInstall,
212
AppEvent_WorkbookAddinUninstall,
212
AppEvent_WorkbookBeforeClose,
213
AppEvent_WorkbookBeforePrint,
213
AppEvent_WorkbookBeforeSave, 213
AppEvent_WorkbookDeactivate, 213
AppEvent_WorkbookNewSheet, 213
AppEvent_WorkbookOpen, 214
Chart_Activate, 205
Chart_BeforeDoubleClick, 205
Chart_BeforeRightClick, 206
Chart_Calculate, 206
Chart_Deactivate, 206
Chart_DragOver, 206
Chart_DragPlot, 206
Chart_MouseDown, 206
Chart_MouseMove, 207
Chart_MouseUp, 207
Chart_Resize, 207
Chart_Select, 207
Chart_SeriesChange, 208
Workbook_Activate, 192
Workbook_AddinInstall, 196
Workbook_AddinUninstall, 196
Workbook_BeforeClose, 194
Workbook_BeforePrint, 193
Workbook_BeforeSave, 193
Workbook_Deactivate, 192
Workbook_NewSheet, 195
Workbook_Open, 192
Workbook_SheetActivate, 197
Workbook_SheetBeforeY
DoubleClick, 197
Workbook_SheetBeforeRightClick,
197
Workbook_SheetCalculate, 197
Workbook_SheetChange, 198
Workbook_SheetDeactivate, 198
Workbook_SheetFollowHyperlink,
198
Workbook_SheetSelectionChange,
198
Workbook_WindowActivate, 196
Workbook_WindowDeactivate, 196
Workbook_WindowResize, 195
Worksheet_Activate, 199
Worksheet_BeforeDoubleClick, 199
Worksheet_BeforeRightClick, 200
Worksheet_Calculate, 200
Worksheet_Change, 202
Worksheet_Deactivate, 199
Worksheet_FollowHyperlink, 203
Worksheet_SelectionChange, 203
встроенной диаграммы, 204; 509
листа диаграммы, 204
параметры, 190
приложения, 208; 507
рабочего листа, 199
рабочей книги, 191
Список, 221
комбинированный, 221
Ссылка
абсолютная, 52; 167
относительная, 53; 166
смешанная, 167
Стиль записи ссылок
A1, 161; 165
R1C1, 162; 165; 166
Счетчик, 224
ТТаблица Access
запись
добавление, 493
извлечение, 494
обновление, 496
удаление, 499
поле
проверка существования, 501
создание, 503
проверка существования, 500
создание, 502
620 Предметный указатель
Точка прерывания, 77
создание, 77; 83
удаление, 78
УУсловное форматирование ячеек, 171;
364; 381
ФФайл
.dll, 547
.mdb, 489
.xls, 431
.xml, 431
.xsd, 431
текстовый
импорт, 473
экспорт, 486
Форма. См. Пользовательская форма
Формула
A1, 164; 168
R1C1, 164; 168; 169
массива, 174
Фрэнкстон, Боб, 29
Функция
CreateObject, 444
GetObject, 444
IsEmpty, 104
определенная пользователем, 111
создание, 112
ЦЦикл, 141
Do Until...Loop, 151
Do While...Loop, 150
Do...Loop, 147
For Each...Next, 152
For...Next, 141
While...Wend, 152
вложение циклов, 146
досрочное завершение, 145
изменение шага, 144
Научно�популярное издание
Билл Джелен, Трейси Сирстад
Применение VBA и макросов в Microsoft Excel
Литературный редактор П.Н. МачугаВерстка О.В. Линник
Художественный редактор С.А. ЧернокозинскийКорректор О.В. Мишутина
Издательский дом ‘‘Вильямс”
101509, г. Москва, ул. Лесная, д. 43, стр. 1
Подписано в печать 30.09.2005. Формат 70x100/16.
Гарнитура Times. Печать офсетная
Усл. печ. л. 50,3. Уч.$изд. л. 30,4.
Тираж 3000 экз. Заказ № .
Отпечатано с диапозитивов в ФГУП ‘‘Печатный двор”
Министерства РФ по делам печати,
телерадиовещания и средств массовых коммуникаций
197110, С.$Петербург, Чкаловский пр., 15