Применение VBA и макросов в Microsoft® Excel

advertisement
?изнес-решения
Применение VBA
и макросов в
Microsoft® Excel
?usiness solutions
VBA and Macros
for
Microsoft® Excel
Bill Jelen, Mr. Excel
Tracy Syrstad
800 East 96th Street
Indianapolis, Indiana 46240
?изнес-решения
Применение VBA
и макросов в
Microsoft® Excel
Билл Джелен, “Мистер Excel”
Трейси Сирстад
Москва • Санкт-Петербург • Киев
2006
ББК 32.973.26018.2.75
Д40
УДК 681.3.07
Издательский дом ‘‘Вильямс”
Главный редактор С.Н. Тригуб
Зав. редакцией В.Р. Гинзбург
Перевод с английского и редакция А.В. Журавлева
По общим вопросам обращайтесь в Издательский дом ‘‘Вильямс’’ по адресу:
info@williamspublishing.com, http://www.williamspublishing.com
115419, Москва, а/я 783; 03150, Киев, а/я 152
Джелен, Билл, Сирстад, Трейси.
Д40 Применение VBA и макросов в Microsoft Excel. : Пер. с англ. М. : Из
дательский дом ‘‘Вильямс’’, 2006. 624 с. : ил. Парал. тит. англ.
ISBN 5845908825 (рус.)
В этой книге рассматривается автоматизация выполнения всевозможных задач
с помощью Excel VBA от создания простого отчета до разработки полноценного
приложения Excel ‘‘с нуля’’. Авторы книги полагаются на достаточно высокий уро
вень подготовки читателя, однако допускают, что материал каждой главы не зна
ком ему в полном объеме. Особое внимание при изложении материала уделяется
таким высокоэффективным средствам Excel, как диаграмма, расширенный фильтр
и сводная таблица. Прежде чем продемонстрировать решение той или иной задачи
с помощью VBA, авторы кратко останавливаются на ее выполнении с помощью
пользовательского интерфейса Excel. Прочитав книгу, читатель получит знания,
необходимые для автоматизации выполнения повседневных задач и создания соб
ственных решений в Excel с помощью VBA.
Книга предназначена для опытных пользователей Excel.
ББК 32.973.26018.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 5845908825 (рус.)
ISBN 0789731290 (англ.)
© Издательский дом ‘‘Вильямс’’, 2006
© Sams Publishing, 2004
Îãëàâëåíèå
Об авторах
Посвящения
Благодарности
Введение
23
23
23
25
Часть I. Первые шаги
35
Глава 1. Excel и VBA — гремучая смесь
Глава 2. Знакомство с Visual Basic for Applications
Глава 3. Работа с диапазоном ячеек
Глава 4. Функции, определенные пользователем
Глава 5. Циклы и управление выполнением кода
Глава 6. Стиль записи ссылок R1C1
Глава 7. Имена
Глава 8. События
Глава 9. Введение в пользовательские формы
37
59
95
111
141
161
177
189
215
Часть II. Автоматизация Excel
227
Глава 10. Диаграммы
Глава 11. Анализ данных с помощью расширенного фильтра
Глава 12. Сводные таблицы
Глава 13. Excel всемогущий
Глава 14. Взаимодействие с Internet
Глава 15. Поддержка XML в профессиональном выпуске Excel 2003
Глава 16. Автоматизация Word
229
267
299
363
407
427
439
Часть III. Удивительные возможности Visual Basic
for Applications
Глава 17. Массивы
Глава 18. Работа с текстовыми файлами
Глава 19. Использование Microsoft Access
Глава 20. Создание пользовательских объектов, типов и коллекций
Глава 21. Пользовательские формы — профессиональный подход
Глава 22. Интерфейс прикладного программирования (API) Windows
461
463
473
489
505
525
547
6
Оглавление
Глава 23. Обработка ошибок
Глава 24. Создание пользовательских меню и панелей инструментов
Глава 25. Надстройки
Глава 26. Практикум: создание приложения Excel “с нуля”
Предметный указатель
561
575
593
603
615
Ñîäåðæàíèå
Об авторах
Посвящения
Благодарности
Введение
VBA — работа на результат
Как организована эта книга
Часть I, “Первые шаги”
Часть II, “Автоматизация Excel”
Часть III, “Удивительные возможности Visual Basic for Applications”
Для кого предназначена эта книга
История развития электронных таблиц и макросов
Будущее Excel и VBA
Соглашения, принятые в этой книге
Рассматриваемые версии Excel
Программный код
Следующий шаг
Ждем ваших отзывов!
Часть I. Первые шаги
Глава 1. Excel и VBA — гремучая смесь
Excel всемогущий
Камни преткновения
Средство записи макросов не работает!
Visual Basic — это не BASIC
Хорошие новости
Отличные новости
Панель инструментов “Visual Basic”
Безопасность макросов
Уровень безопасности “Очень высокая”
Уровень безопасности “Высокая”
Уровень безопасности “Средняя”
Уровень безопасности “Низкая”
Запись, хранение и выполнение макросов
Диалоговое окно “Запись макроса”
Выполнение макроса
Создание кнопки выполнения макроса
Назначение макроса элементу управления формы
Редактор Visual Basic
Параметры редактора Visual Basic
23
23
23
25
25
27
27
27
27
28
29
30
31
32
32
32
33
35
37
37
37
37
38
38
38
39
40
40
40
41
41
42
42
43
43
44
45
45
8
Содержание
Диспетчер проектов
Окно свойств
Изучение кода макроса
Непредвиденные результаты
Возможное решение: использование относительных ссылок
Отчаяние
Следующий шаг
Глава 2. Знакомство с Visual Basic for Applications
Загадочный код
Учимся понимать “речь” VBA
Справочная система VBA
Спасительная клавиша <F1>
Просмотр разделов справочной системы
Изучение кода записанного макроса
46
47
50
52
52
57
57
59
59
60
63
63
65
66
Необязательные параметры
Предопределенные константы
Возврат объектов свойством
68
68
73
Использование отладчика кода
74
Пошаговое выполнение кода
Точки прерывания
Перемещение по коду
Выполнение фрагмента кода
Вычисление значения переменной или выражения
Установка точки прерывания с помощью окна Watches
Отслеживание состояния объекта с помощью окна Watches
74
77
78
79
79
83
83
Диспетчер объектов
5 советов по исправлению и оптимизации автоматически
сгенерированного кода
85
Совет 1: ничего не выделяйте
Совет 2: перемещайтесь на последнюю строку данных с конца рабочего
листа
Совет 3: используйте переменные
Совет 4: используйте одно выражение для копирования и вставки
данных
Совет 5: используйте конструкцию With...End With
Исправление и оптимизация автоматически сгенерированного
кода
Следующий шаг
Глава 3. Работа с диапазоном ячеек
Объект Range
Обращение к диапазону ячеек с помощью указания адреса его
верхнего левого и нижнего правого угла
Сокращенная форма обращения к диапазону ячеек
Именованные диапазоны ячеек
87
87
88
89
89
90
90
93
95
95
96
96
96
Содержание
Обращение к диапазону ячеек, расположенному на другом
рабочем листе
Обращение к диапазону ячеек с помощью указания его
относительного адреса
Обращение к диапазону ячеек с помощью свойства Cells
Использование свойства Cells в качестве параметра свойства Range
Обращение к диапазону ячеек с помощью свойства Offset
Изменение размера диапазона ячеек с помощью свойства Resize
Обращение к диапазону ячеек с помощью свойств Columns и Rows
Объединение диапазонов ячеек с помощью метода Union
Создание нового диапазона ячеек из пересекающихся диапазонов с
помощью метода Intersect
Проверка пустых ячеек с помощью функции IsEmpty
Обращение к диапазону ячеек с помощью свойства CurrentRegion
Обращение к диапазону несмежных ячеек с помощью коллекции
Areas
Следующий шаг
97
98
99
100
100
101
102
103
103
104
105
108
109
Глава 4. Функции, определенные пользователем
Создание функций, определенных пользователем
Наиболее распространенные задачи программирования в Excel
111
111
113
Вывод имени файла текущей рабочей книги в ячейке
Вывод полного имени файла текущей рабочей книги в ячейке
Как проверить, открыта ли рабочая книга
Проверка существования рабочего листа в открытой книге
Подсчет количества файлов рабочих книг в папке
Получение имени пользователя, зарегистрировавшегося в системе
Получение даты и времени последнего сохранения рабочей книги
Получение постоянного значения даты и времени
Проверка адреса электронной почты
Суммирование значений ячеек на основе цвета заливки
Получение имени и номера цвета заливки ячейки
Получение номера цвета текста в ячейке
Подсчет количества уникальных значений
Удаление повторяющихся значений из диапазона ячеек
Поиск первой непустой ячейки в диапазоне
Замена нескольких символов в строке
Извлечение чисел из смешанного текста
Преобразование номера недели в дату
Разбор строки с символамиYразделителями
Сортировка и конкатенация значений ячеек из заданного диапазона
Сортировка числовых и строковых значений
Поиск строки в диапазоне ячеек
Запись содержимого ячейки в обратном порядке
Поиск наибольших значений в диапазоне ячеек
Получение адреса гиперссылки
113
113
114
114
115
116
117
118
118
120
121
124
125
125
128
128
130
130
131
132
134
135
136
137
138
9
10
Содержание
Получение адреса столбца ячейки
Генерация постоянных случайных чисел
Использование структуры Select...Case
Следующий шаг
Глава 5. Циклы и управление выполнением кода
Цикл For...Next
Использование переменных в выражении For
Изменение шага в цикле For...Next
Досрочное завершение выполнения цикла
Вложение циклов
Циклы Do...Loop
Использование операторов While и Until
Цикл While...Wend
Цикл For Each...Next
Объектные переменные
Управление выполнением кода: использование конструкций
If...Then...Else и Select Case
Знакомство с конструкцией If...Then...Else
Условие
Конструкция If...Then...End If
Конструкция If...Then...Else...End If
Конструкция If...ElseIf...End If
Конструкция Select Case...End Select
Использование сложных выражений Case
Вложение выражений If
Следующий шаг
Глава 6. Стиль записи ссылок R1C1
Сравнение стилей записи ссылок A1 и R1C1
R1C1 — дела давно минувших дней?
R1C1 — сильные стороны
Использование стиля ссылок R1C1 в Excel
Чудесный мир формул Excel
Как “размножаются” формулы
Разоблачение
Ссылки в стиле R1C1
Относительные ссылки в стиле R1C1
Абсолютные ссылки в стиле R1C1
Смешанные ссылки в стиле R1C1
Обращение к строке или столбцу с помощью ссылок в стиле R1C1
Замена нескольких A1Yформул одной R1C1Yформулой
Тренируем память
Использование ссылок в стиле R1C1 при условном
форматировании ячеек
Задание условного форматирования с помощью пользовательского
интерфейса
138
138
139
140
141
141
144
144
145
146
147
150
152
152
152
155
155
156
156
156
157
157
158
158
160
161
161
162
162
162
163
163
164
166
166
167
167
168
169
170
171
171
Содержание
Задание условного форматирования с помощью VBA
Использование ссылок в стиле R1C1 при создании формулы
массива
Следующий шаг
Глава 7. Имена
Глобальные и локальные имена
Создание имен
Удаление имен
Типы имен
Имена формул
Имена строк
Имена чисел
Имена массивов
Зарезервированные имена
Скрытие имен
Проверка существования имени
Следующий шаг
Глава 8. События
Использование событий
Параметры событий
Запрет обработки событий
События рабочей книги
Событие Workbook_Activate()
Событие Workbook_Deactivate()
Событие Workbook_Open()
Событие Workbook_BeforeSave(ByVal SaveAsUI As Boolean,
Cancel As Boolean)
Событие Workbook_BeforePrint(Cancel As Boolean)
Событие Workbook_BeforeClose(Cancel As Boolean)
Событие Workbook_NewSheet(ByVal Sh As Object)
Событие Workbook_WindowResize(ByVal Wn As Window)
Событие Workbook_WindowActivate(ByVal Wn As Window)
Событие Workbook_WindowDeactivate(ByVal Wn As Window)
Событие Workbook_AddinInstall()
Событие Workbook_AddinUninstall()
Событие Workbook_SheetActivate(ByVal Sh As Object)
Событие Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean)
Событие Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean)
Событие Workbook_SheetCalculate(ByVal Sh As Object)
Событие Workbook_SheetChange(ByVal Sh As Object, ByVal Target As
Range)
Событие Workbook_SheetDeactivate(ByVal Sh As Object)
172
174
175
177
177
179
180
180
181
181
182
183
183
185
185
187
189
190
190
191
191
192
192
192
193
193
194
195
195
196
196
196
196
197
197
197
197
198
198
11
12
Содержание
Событие Workbook_SheetFollowHyperlink(ByVal Sh As Object, ByVal
Target As Hyperlink)
Событие Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal
Target As Range)
События рабочего листа
Событие Worksheet_Activate()
Событие Worksheet_Deactivate()
Событие Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As
Boolean)
Событие Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As
Boolean)
Событие Worksheet_Calculate()
Событие Worksheet_Change(ByVal Target As Range)
Событие Worksheet_SelectionChange(ByVal Target As Range)
Событие Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
События листа диаграммы
Встроенные диаграммы
Событие Chart_Activate()
Событие Chart_BeforeDoubleClick(ByVal ElementID As Long, ByVal Arg1
As Long, ByVal Arg2 As Long, Cancel As Boolean)
Событие Chart_BeforeRightClick(Cancel As Boolean)
Событие Chart_Calculate()
Событие Chart_Deactivate()
Событие Chart_DragOver()
Событие Chart_DragPlot()
Событие Chart_MouseDown(ByVal Button As Long, ByVal Shift As Long,
ByVal x As Long, ByVal y As Long)
Событие Chart_MouseMove(ByVal Button As Long, ByVal Shift As Long,
ByVal x As Long, ByVal y As Long)
Событие Chart_MouseUp(ByVal Button As Long, ByVal Shift As Long,
ByVal x As Long, ByVal y As Long)
Событие Chart_Resize()
Событие Chart_Select(ByVal ElementID As Long, ByVal Arg1 As Long,
ByVal Arg2 As Long)
Событие Chart_SeriesChange(ByVal SeriesIndex As Long, ByVal PointIndex
As Long)
События приложения
Событие AppEvent_NewWorkbook(ByVal Wb As Workbook)
Событие AppEvent_SheetActivate(ByVal Sh As Object)
Событие AppEvent_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean)
Событие AppEvent_SheetBeforeRightClick(ByVal Sh As Object, ByVal
Target As Range, Cancel As Boolean)
Событие AppEvent_SheetCalculate(ByVal Sh As Object)
Событие AppEvent_SheetChange(ByVal Sh As Object, ByVal Target As
Range)
Событие AppEvent_SheetDeactivate(ByVal Sh As Object)
198
198
199
199
199
199
200
200
202
203
203
204
204
205
205
206
206
206
206
206
206
207
207
207
207
208
208
210
210
210
210
211
211
211
Содержание
Событие AppEvent_SheetFollowHyperlink(ByVal Sh As Object, ByVal
Target As Hyperlink)
Событие AppEvent_SheetSelectionChange(ByVal Sh As Object, ByVal Target
As Range)
Событие AppEvent_WindowActivate(ByVal Wb As Workbook, ByVal Wn As
Window)
Событие AppEvent_WindowDeactivate(ByVal Wb As Workbook, ByVal Wn
As Window)
Событие AppEvent_WindowResize(ByVal Wb As Workbook, ByVal Wn As
Window)
Событие AppEvent_WorkbookActivate(ByVal Wb As Workbook)
Событие AppEvent_WorkbookAddinInstall(ByVal Wb As Workbook)
Событие AppEvent_WorkbookAddinUninstall(ByVal Wb As Workbook)
Событие AppEvent_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel
As Boolean)
Событие AppEvent_WorkbookBeforePrint(ByVal Wb As Workbook, Cancel
As Boolean)
Событие AppEvent_WorkbookBeforeSave(ByVal Wb As Workbook, ByVal
SaveAsUI As Boolean, Cancel As Boolean)
Событие AppEvent_WorkbookDeactivate(ByVal Wb As Workbook)
Событие AppEvent_WorkbookNewSheet(ByVal Wb As Workbook, By Val Sh
As Object)
Событие AppEvent_WorkbookOpen(ByVal Wb As Workbook)
Следующий шаг
Глава 9. Введение в пользовательские формы
Способы взаимодействия с пользователем
Окно ввода
Окно сообщения
Создание пользовательской формы
Вызов и скрытие пользовательской формы
Программирование пользовательской формы
Основные элементы управления формы
Использование списков и комбинированных списков
Использование переключателей
Использование изображений
Использование счетчиков
Использование вкладок для объединения форм
Проверка ввода обязательных данных
Закрытие формы
Следующий шаг
Часть II. Автоматизация Excel
Глава 10. Диаграммы
Встроенные диаграммы и диаграммы, расположенные
на отдельном листе
211
211
211
211
212
212
212
212
213
213
213
213
213
214
214
215
215
215
216
216
218
218
220
221
222
223
224
225
225
225
226
227
229
229
13
14
Содержание
Встроенные диаграммы и контейнер ChartObject
Диаграммы, расположенные на отдельном листе
230
232
Создание диаграмм с помощью VBA
232
Изменение размещения диаграммы
Стандартный тип диаграмм
235
235
Использование объектных переменных для упрощения кода
“Анатомия” диаграммы
Область диаграммы (ChartArea)
Область построения диаграммы (PlotArea)
Ряды данных (Series)
Оси диаграммы (Axis)
Линии сетки (HasMajorGridlines и HasMinorGridlines)
Подписи данных (DataLabels и DataLabel)
Название диаграммы, легенда и таблица данных (ChartTitle, HasLegend
и HasDataTable)
Линии тренда и полосы погрешности (Trendlines и ErrorBar)
Типы диаграмм
Параметры трехмерных и круговых диаграмм
Параметры трехмерных диаграмм
Параметры круговых диаграмм
Интерактивные диаграммы
События диаграмм
236
237
237
240
242
243
245
246
247
248
251
256
256
258
260
260
Экспорт диаграммы в файл изображения
Удивительные возможности точечных диаграмм
Создание нестандартных диаграмм
261
262
262
Круговая пузырьковая диаграмма
Диаграмма с точками данных в виде спидометров
Диаграмма кривой предложения
Иерархическая кольцевая диаграмма
262
264
264
265
Следующий шаг
Глава 11. Анализ данных с помощью расширенного фильтра
Преимущества VBA перед пользовательским интерфейсом Excel
Использование расширенного фильтра для отбора уникальных
значений из заданного диапазона
Отбор уникальных значений из заданного столбца с помощью
пользовательского интерфейса
Отбор уникальных значений из заданного столбца с помощью VBA
Отбор уникальных значений из комбинации нескольких столбцов с
помощью VBA
Использование расширенного фильтра с указанием условия отбора
данных
Объединение нескольких условий с помощью
логической операции “ИЛИ”
Объединение нескольких условий с помощью логической операции “И”
Дополнительные аспекты объединения условий с помощью
логической операции “ИЛИ”
266
267
267
268
268
271
275
276
278
278
279
Содержание
Задание условия отбора с помощью формулы
Отбор пустого множества записей
Фильтрация диапазона исходных данных “на месте”
Отбор пустого множества записей
Отображение записей, скрытых в результате фильтрации “на месте”
Отбор только уникальных записей при фильтрации “на месте”
Использование расширенного фильтра для копирования всех
записей, удовлетворяющих заданному условию
Копирование всех столбцов исходного диапазона данных
Копирование и переупорядочивание подмножества столбцов исходного
диапазона данных
Автофильтр
Следующий шаг
Глава 12. Сводные таблицы
Сводные таблицы в различных версиях Excel
Создание сводных таблиц с помощью пользовательского
интерфейса Excel
Создание сводных таблиц с помощью VBA
Подсчет суммы чисел вместо количества значений
Перемещение или изменение части сводной таблицы
Определение размера сводной таблицы
Создание отчета о структуре спроса на товары
Заполнение значениями пустых ячеек в области данных
Изменение порядка сортировки списка заказчиков
Изменение порядка следования столбцов сводной таблицы вручную
Изменение формата отображения числовых значений
Запрет автоматического добавления промежуточных итогов
Запрет подсчета общей суммы по столбцам
Создание отчета о структуре спроса на товары:
завершающая стадия
Создание новой рабочей книги
Копирование содержимого сводной таблицы
Улучшение внешнего вида отчета
Стилевое форматирование отчета
Добавление промежуточных итогов
Результирующий код
Создание отчета о прибыльности товаров
Определение вычисляемых полей области данных
“Подводные камни” вычисляемых элементов
Суммирование значений полей области данных сводной таблицы
путем группирования
Группирование дат по неделям
Определение сроков выполнения заказов
Дополнительные возможности сводных таблиц
Отображение лучшей десятки заказчиков
Использование сводной таблицы для фильтрации исходных данных
279
286
287
288
288
288
289
289
290
297
298
299
299
300
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
15
16
Содержание
Использование полей области страницы сводной таблицы
Фильтрация элементов полей сводной таблицы вручную
Сумма, среднее, количество, минимум, максимум и др.
Дополнительные вычисления в полях области данных сводной
таблицы
Доля от общей суммы
Приведенное отличие от значения предыдущего элемента поля
Приведенное отличие от значения заданного элемента поля
Нарастающий итог
Следующий шаг
Глава 13. Excel всемогущий
Расширение возможностей Excel с помощью VBA
Условное форматирование с более чем тремя условиями
Расширенный фильтр с более чем двумя условиями
Файловые операции
Поиск файлов
Удаление рабочей книги после определенной даты
Создание команды меню “Закрыть и удалить”
Импорт CSVYфайлов
Считывание текстового файла в память и его последующий анализ
Объединение и разделение рабочих книг
Сохранение листов рабочей книги в виде отдельных рабочих книг
Объединение нескольких рабочих книг в одну
Фильтрация данных с последующим копированием полученного
результата в отдельные рабочие листы
Экспорт данных в Word
Работа с примечаниями
Вывод примечаний
Изменение размера области примечания
Изменение размера области примечания с помощью центрирования
Размещение диаграммы в примечании
Замечательные возможности Excel VBA
Выделение ячейки с помощью условного форматирования
Выделение ячейки без применения условного форматирования
Транспонирование данных
Выделение и отмена выделения несмежных ячеек
VBA для профессионалов
Установка параметров страницы
Вычисление времени выполнения кода макроса
Запрет/разрешение выполнения операций вырезания,
копирования и вставки
Определение порядка сортировки
Создание индикатора хода процесса
Создание защищенного поля для ввода пароля
Изменение регистра текста
Обработка события удаления строки или столбца
Поиск заданного текста с помощью свойства SpecialCells
346
351
355
356
358
358
359
359
361
363
363
364
364
365
366
367
368
370
370
372
372
372
373
375
376
376
377
378
379
381
381
382
383
384
386
386
389
390
392
394
395
397
398
399
Содержание
Условное удаление строк
Сокрытие строки формул
На закуску
Извлечение информации о курсах акций из Internet
Вставка программного кода во вновь созданную рабочую книгу
Следующий шаг
Глава 14. Взаимодействие с Internet
Извлечение данных из Internet
Создание WebYзапроса с помощью пользовательского интерфейса Excel
Обновление существующего WebYзапроса с помощью VBA
Создание WebYзапроса с помощью VBA
Извлечение данных из Internet в режиме реального времени
Анализ данных, извлеченных из Internet
Условия выполнения метода OnTime
Определение временного окна для выполнения макроса
Отмена назначенного задания
Отмена всех назначенных заданий
Выполнение макроса по прошествии заданного периода времени
Периодическое выполнение макроса через определенные
промежутки времени
Размещение данных на WebYстранице
Создание WebYстраниц с помощью VBA
Применение Excel в качестве системы управления содержимым
Загрузка WebYстраницы на FTPYсервер
Следующий шаг
400
401
402
402
404
405
407
407
407
409
410
413
414
414
415
415
416
416
416
418
420
421
425
425
Глава 15. Поддержка XML в профессиональном выпуске Excel 2003
Введение в XML
Правила XML
Универсальный формат файлов
XML набирает обороты
Схемы и сопоставления XML
Сохранение и считывание содержимого рабочей книги Excel в
формате XML
Следующий шаг
427
427
428
429
429
429
Глава 16. Автоматизация Word
Раннее связывание
439
439
Ошибка компиляции: отсутствие библиотеки
Позднее связывание
Работа с объектами
Ключевое слово New
Функция CreateObject
Функция GetObject
Объекты Word
Объект Document
431
438
442
442
443
443
444
444
445
446
17
18
Содержание
Объект Selection
Объект Range
Закладки
448
449
453
Следующий шаг
460
Часть III. Удивительные возможности Visual Basic
for Applications
Глава 17. Массивы
Объявление массива
Многомерные массивы
Заполнение массива
Манипулирование элементами массива
Еще одно преимущество массивов
Динамические массивы
Передача массива в качестве параметра
Следующий шаг
Глава 18. Работа с текстовыми файлами
Импорт данных из текстового файла
Импорт текстовых файлов, содержащих менее 65 536 записей
Импорт текстовых файлов, содержащих более 65 536 записей
Экспорт данных в текстовый файл
Следующий шаг
Глава 19. Использование Microsoft Access
ADO и DAO
Объекты ADO
Добавление записи в таблицу Access
Извлечение записей из таблицы Access
Обновление записей таблицы Access
Удаление записей таблицы Access
Создание итоговых запросов
Несколько полезных макросов
Проверка существования таблицы в базе данных Access
Проверка существования поля в таблице базы данных Access
Добавление таблицы в базу данных Access
Добавление поля в таблицу базы данных Access
Следующий шаг
Глава 20. Создание пользовательских объектов, типов и коллекций
Создание модуля класса
Обработка событий уровня приложения и встроенной диаграммы
События уровня приложения
События встроенной диаграммы
461
463
463
464
464
466
467
469
470
471
473
473
474
482
486
487
489
490
492
493
494
496
499
499
500
500
501
502
503
503
505
506
506
507
509
Содержание
Создание пользовательского объекта
Применение пользовательского объекта на практике
Использование выражений Property Let и Property Get
Коллекции
Создание коллекции в стандартном модуле
Создание коллекции в модуле класса
Создание пользовательских типов
Следующий шаг
Глава 21. Пользовательские формы //// профессиональный подход
Панель инструментов UserForm
Создание коллекций элементов управления формы
Дополнительные элементы управления формы
Переключатели
Набор вкладок
Поле ввода адреса диапазона ячеек
Немодальные формы
Гиперссылки в формах
Добавление элементов управления на форму во время выполнения
программного кода
Изменение размеров формы во время выполнения программного кода
Добавление элемента управления на форму во время выполнения
программного кода
Определение размера и положения элемента управления на форме во
время выполнения программного кода
Ограничения, связанные с добавлением элементов управления на форму
во время выполнения программного кода
Типы элементов управления
Добавление изображения на форму во время выполнения программного
кода
Результирующий код
Использование полосы прокрутки для выбора значений
Добавление подсказки к элементу управления
Использование сочетаний клавиш
Подсказка элемента управления
Порядок переноса фокуса
Изменение цвета фона активного элемента управления
Использование эффекта прозрачности формы
Следующий шаг
Глава 22. Интерфейс прикладного программирования (API) Windows
Знакомство с Windows API
Объявления Windows API
Использование объявлений Windows API
Примеры полезных объявлений Windows API
Определение имени компьютера
510
511
513
515
515
516
520
524
525
525
526
528
528
529
530
531
531
532
534
534
535
535
535
536
537
539
541
541
541
541
542
545
546
547
547
548
548
549
549
19
20
Содержание
Проверка возможности доступа к файлу
Определение разрешения экрана
Блокирование кнопки закрытия окна приложения
Блокирование кнопки закрытия окна формы
Часы
Создание гиперссылок
Воспроизведение звуковых файлов
Создание диалогового окна выбора файла
Дополнительные источники объявлений Windows API
Следующий шаг
Глава 23. Обработка ошибок
Отладка кода с помощью редактора VBA
Отладка кода пользовательской формы
Обработка ошибок с помощью выражения On Error GoTo
Использование нескольких обработчиков ошибок
Универсальные обработчики ошибок
Игнорирование ошибок
Игнорирование сообщений Excel
Извлечение пользы из ошибок
Общение с заказчиками
“Отложенные” ошибки
Ошибка времени выполнения 9: “Subscript out of range”
Ошибка времени выполнения 1004: “Method 'Range' of object '_Global'
failed”
Несовершенство защиты проекта VBA
Защита проекта VBA в различных версиях Excel
Совместимость различных версий Excel
Следующий шаг
Глава 24. Создание пользовательских меню и панелей инструментов
Создание пользовательского меню
Создание и удаление пользовательского меню
Добавление команд меню
Группирование команд меню
Создание подменю
Создание пользовательской панели инструментов
Создание и удаление пользовательской панели инструментов
Добавление кнопок на панель инструментов
Выбор значка кнопки панели инструментов
Добавление раскрывающегося списка на панель инструментов
Сохранение и восстановление координат панели инструментов
Другие способы запуска макросов
Запуск макроса с помощью сочетания клавиш
Запуск макроса с помощью кнопки
Запуск макроса с помощью элемента управления ActiveX
Следующий шаг
550
550
552
552
553
554
555
555
559
559
561
561
562
564
565
566
566
568
568
569
569
570
571
572
573
573
574
575
575
575
577
578
579
581
582
582
584
584
585
587
587
587
590
592
Содержание
Глава 25. Надстройки
Стандартные надстройки Excel
Преобразование рабочей книги Excel в надстройку
Преобразование рабочей книги Excel в надстройку с помощью
диалогового окна “Сохранение документа” (Save As)
Преобразование рабочей книги Excel в надстройку с помощью
редактора VBA
Использование надстроек
Безопасность стандартных надстроек Excel
Выгрузка надстроек
Удаление надстроек
Альтернативное решение: использование скрытой рабочей книги
Следующий шаг
Глава 26. Практикум: создание приложения Excel ‘‘с нуля’’
О Тушаре Мехта
Постановка задачи
Решение
Реализация решения с помощью Excel и VBA
Этап 1а: нисходящее программирование
Этап 1б: создание ключевых компонентов
Этап 2а: нисходящее программирование
Этап 2б: создание ключевых компонентов
Этап 3а: нисходящее программирование
Этап 3б: создание ключевых компонентов
Резюме
Предметный указатель
593
593
594
594
596
597
598
599
599
599
601
603
603
604
605
606
606
607
608
610
611
613
614
615
21
Об авторах
Билл Джелен, более известный под псевдонимом ‘‘Мистер 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&
часовой процесс создания отчетов вручную был сведен к двум щелчкам мыши и
четырем минутам ожидания.
Введение
Как организована эта книга
Эта книга состоит из трех частей. Ее цель — научить читателя создавать
макросы 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 Basic
for Applications”
В главе 17, ‘‘Массивы’’, рассматриваются массивы. Основное предназнаY
чение массива заключается в упрощении обработки данных и повышении
27
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.
Введение
История развития электронных
таблиц и макросов
Вплоть до 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
мала лидирующее положение на рынке программ для работы с электронными
29
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 года.
Введение
Наконец, с точки зрения маркетинга было бы нелепо отказываться от 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, заголовки столбцов,
ссылки, формулы, имена макросов, модулей, функций, процедур,
31
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.
Введение
Ждем ваших отзывов!
Вы, читатель этой книги, и есть главный ее критик и комментатор. Мы цеY
ним ваше мнение и хотим знать, что было сделано нами правильно, что можY
но было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам
интересно услышать и любые другие замечания, которые вам хотелось бы выY
сказать в наш адрес.
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам
бумажное или электронное письмо, либо просто посетить наш WebYсервер и
оставить свои замечания там. Одним словом, любым удобным для вас спосоY
бом дайте нам знать, нравится или нет вам эта книга, а также выскажите свое
мнение о том, как сделать наши книги более интересными для вас.
Посылая письмо или сообщение, не забудьте указать название книги и ее
авторов, а также ваш обратный адрес. Мы внимательно ознакомимся с вашим
мнением и обязательно учтем его при отборе и подготовке к изданию послеY
дующих книг. Наши координаты:
info@williamspublishing.com
EYmail:
http://www.williamspublishing.com
WWW:
Адреса для писем:
из России:
из Украины:
115419, Москва, а/я 783
03150, Киев, а/я 152
33
Часть 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
Глава 1
Excel è VBA —
ãðåìó÷àÿ ñìåñü
1
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
Excel всемогущий ........................... 37
Камни преткновения..................... 37
Панель инструментов
“Visual Basic” ...................................39
Безопасность макросов ............... 40
Запись, хранение и
выполнение макросов ..................42
Выполнение макроса ....................43
Редактор Visual Basic .....................45
Изучение кода макроса ................50
Непредвиденные результаты .....52
Отчаяние .......................................... 57
Следующий шаг.............................. 57
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
чами: успешно импортировав данные в 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.
39
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
дежного источника. Поскольку подписывание макроса подразумевает необхоY
димость приобретения цифрового сертификата у уполномоченной на это орY
ганизации (такой как VeriSign), уровень безопасности Высокая (High) являетY
ся далеко не самым лучшим выбором при разработке собственных макросов.
При открытии рабочей книги все находящиеся в ней неподписанные макросы
будут попросту отключены.
Рис. 1.3. Уровень безопасности Высокая выбран по
умолчанию
Уровень безопасности “Средняя”
На уровне безопасности Средняя (Medium) решение об отключении
потенциально опасных макросов принимается пользователем. Именно
этот уровень безопасности рекомендуется применять при разработке собY
ственных макросов. Конечно же, необходимость включать макросы при
каждом открытии рабочей книги может несколько раздражать. С другой
стороны, это последняя возможность защититься от разрушительного виY
руса, который таится в ничем не приметной рабочей книге, присланной
вам по электронной почте.
Уровень безопасности “Низкая”
На этом уровне безопасности защита от потенциально опасных макроY
сов отсутствует. Теперь уже ничто не защитит вас от вируса, хранящегося в
рабочей книге. Применение уровня безопасности Низкая (Low) крайне не
рекомендуется.
41
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
ную книгу макросов на экране, выберите команду главного меню Excel
Окно Отобразить (Window Unhide).
Личная книга макросов подходит для хранения далеко не каждого макроса.
В ней рекомендуется хранить макросы общего назначения, не имеющие непоY
средственного отношения к конкретному рабочему листу или книге.
После выбора места хранения макроса щелкните на кнопке OK. Чтобы заY
кончить запись макроса, щелкните на кнопке Остановить запись (Stop ReY
cording) одноименной панели инструментов.
Выполнение макроса
Для выполнения макроса достаточно нажать соответствующую комбинаY
цию клавиш (если она определена) на клавиатуре. Макрос можно назначить
также кнопке панели инструментов или элементу управления формы. Кроме
того, выполнить макрос можно с помощью уже рассмотренной панели инстY
рументов Visual Basic.
Создание кнопки выполнения макроса
Макросы общего назначения рекомендуется хранить в личной книге макY
росов и запускать с помощью кнопки, вынесенной на панель инструментов.
Чтобы создать кнопку выполнения макроса, следуйте приведенной ниже
процедуре.
1. Щелкните на панели инструментов правой кнопкой мыши и выберите
команду контекстного меню Настройка (Customize).
2. Перейдите во вкладку Команды (Commands) (рис. 1.5).
Рис. 1.5. Чтобы добавить кнопку выполнения
макроса на панель инструментов Excel, восполь&
зуйтесь диалоговым окном Настройка
43
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
Редактор 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 Прим. ред.
45
46
Часть I
Первые шаги
собствует более быстрому написанию кода. С другой стороны, с помощью
этого требования можно предотвратить ошибки ввода имен переменных. ПоY
ступайте так, как посчитаете нужным.
Использование цифровых подписей
Если вам надоело постоянно подтверждать безопасность собственных макY
росов, воспользуйтесь цифровой подписью, выбрав команду меню Tools
Digital 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
Рис. 1.8. Диспетчер проектов содер&
жит список всех модулей проекта
Модули классов
Модули классов Excel предназначены для создания пользовательских объY
ектов. Помимо этого, модули классов позволяют программистам обмениватьY
ся фрагментами кода, не вдаваясь в подробности работы последнего. О модуY
лях классов речь идет в главе 20, ‘‘Создание пользовательских объектов, типов
и коллекций’’.
Окно свойств
Окно свойств предназначено для редактирования параметров различных
компонентов — рабочих листов, книг, модулей или элементов управления
форм. Список параметров компонента зависит от его типа.
Чтобы открыть окно свойств, выберите команду меню View Properties
Window (Вид Окно свойств), нажмите клавишу <F4> или щелкните
на кнопке Project Properties (Свойства проекта), расположенной на
панели инструментов.
Практикум
Предположим, что вы работаете бухгалтером. Каждое утро вы получаете по элек&
тронной почте текстовый файл с разделителями&запятыми, содержащий инфор&
мацию о счетах за вчерашний день в столбцах СчетДата, СчетНомер, ПродавецНомер, КлиентНомер, ПродуктВыручка, СервисВыручка, ПродуктСтоимость
(рис. 1.9).
47
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
13.Нажмите клавишу <↓>, чтобы переместиться на итоговую строку.
14.Введите слово “Всего”.
15. Нажмите клавишу <→> 4 раза, чтобы переместиться в столбец E итоговой строки.
16.Щелкните на кнопке Автосумма (AutoSum) и нажмите комбинацию клавиш
<Ctrl+Enter>, чтобы суммировать значения столбца ПродуктВыручка, остава&
ясь при этом в той же ячейке.
17. Перетащите маркер заполнения по столбцам F и G, чтобы скопировать в них
формулу суммирования.
18.Выделите строку 1 и щелкните на кнопке Полужирный (Bold), чтобы выделить
заголовки столбцов путем утолщения шрифта.
19.Выделите итоговую строку и щелкните на кнопке Полужирный, чтобы выделить
суммарные значения столбцов путем утолщения шрифта.
20.Нажмите комбинацию клавиш <Ctrl+A>, чтобы выделить все ячейки рабочего
листа.
21.Выберите команду Формат Столбец Автоподбор ширины (Format Column
AutoFit Selection).
Теперь вы готовы к записи своего первого макроса. Создайте пустую рабочую книгу
и сохраните ее под каким&нибудь описательным именем, например МакросИмпортаСчетов.xls. Щелкните в панели инструментов Visual Basic на кнопке
Записать макрос (Record Macro) или выберите команду меню Сервис Макрос
Начать запись (Tools Macro Record New Macro).
Измените предлагаемое по умолчанию имя макроса Макрос1 на более инфор&
мативное, например ИмпортСчета. Убедитесь, что макрос будет сохранен в раз&
мещении Эта книга (This Workbook) и задайте сочетание клавиш для выполнения
макроса, к примеру <Ctrl+и>. По умолчанию в поле Описание (Description) зано&
сится ваше имя и дата создания макроса. Добавьте сюда текст, кратко описываю&
щий предназначение макроса (рис. 1.10), и щелкните на кнопке OK.
Рис. 1.10. Прежде чем начать запись
макроса, задайте необходимые пара&
метры в диалоговом окне Запись макроса (Record Macro)
49
50
Часть I
Первые шаги
Запись макроса
Начиная с этого момента средство записи макросов фиксирует каждое совершен&
ное вами действие. Постарайтесь не отклоняться от намеченной ранее последо&
вательности операций. Если, к примеру, вы случайно переместитесь в столбец F
вместо столбца E, а затем вернетесь обратно, созданный макрос будет старатель&
но повторять эту ошибку при каждом своем запуске.
Внимание
Сопротивляйтесь желанию убрать из виду панель инструментов Остановить
запись (Stop Recording). Если она будет вам мешать, перетащите ее в безо&
пасное место (действие по перетаскиванию панели Остановить запись не вклю&
чается в записываемый макрос). Если вы все же закроете панель инструментов
Остановить запись, то для того чтобы завершить запись макроса, вам потребует&
ся выбрать команду меню Сервис Макрос Остановить запись (Tools
Macro 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
Внимание
Комментарий не определяет сочетание клавиш. Другими словами, изменив в
комментарии сочетание клавиш <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), _
51
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
пользователь введет данные в 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>, а затем — клавишу <↓>, чтобы переместиться на по&
следнюю строку импортированных данных.
53
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 Column
AutoFit Selection).
11. Остановите запись макроса.
Нажмите комбинацию клавиш <Alt+F11>, чтобы вернуться в окно редактора
Visual Basic и просмотреть полученный на этот раз код. Текст макроса ИмпортСчетаОтносительно будет помещен в модуль Module1 сразу же после текста
макроса ИмпортСчета.
Excel и VBA — гремучая смесь
Глава 1
Внимание
Если между созданием первого и второго макроса вы завершали работу с Ex&
cel, новый макрос будет помещен в модуль Module2.
На рис. 1.15 показан код макроса ИмпортСчетаОтносительно с двумя ком&
ментариями, указывающими на момент включения и отключения режима от&
носительных ссылок.
Рис. 1.15. Код макроса, записанного с использованием режима относительных ссылок
Чтобы протестировать макрос, закройте файл Счет.txt без сохранения
изменений и нажмите комбинацию клавиш <Ctrl+т>. На этот раз работа макY
роса не должна вызывать какихYлибо нареканий.
Предположим, что файл Счет.txt от 7Yго июня содержит сведения о
21 счете (рис. 1.16).
Откройте рабочую книгу МакросИмпортаСчетов.xls и выполните ноY
вый макрос, нажав комбинацию клавиш <Ctrl+т>. На первый взгляд макрос
справился с поставленной перед ним задачей. Но взгляните на рис. 1.17 — не
кажется ли вам, что здесь чтоYто не так?
Передав подобный отчет менеджеру, вы, несомненно, навредили бы своей
репутации. Присмотритесь к ячейке E23. В левом верхнем углу ячейки нахоY
55
56
Часть I
Первые шаги
дится маленький зеленый треугольник — верный признак ошибки. Следует
отметить, что возможность предупреждения ошибок появилась благодаря
смартYтегам — средству, недоступному в Excel 95 или Excel 97.
Рис. 1.16. Сможет ли новый макрос справиться с этими данными?
Рис. 1.17. Результат выполнения макроса, использующего относительные ссылки
Щелкните в ячейке E23 и подведите указатель к появившейся рядом с ячейY
кой кнопке примечания. На экране появится сообщение о том, что формула в
этой ячейке ссылается на диапазон, к которому прилегают другие значения.
Взглянув на строку формул, вы увидите, что макрос суммировал значения тольY
Excel и VBA — гремучая смесь
Глава 1
ко с 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. Первым приближением
57
58
Часть I
Первые шаги
к цели можно считать автоматически сгенерированный макрос. Немного
здравого смысла, и он станет реальным подспорьем в решении повседневных
задач. В главе 2, ‘‘Знакомство с Visual Basic for Applications” мы попробуем
применить этот подход к двум записанным нами макросам. Научившись
‘‘читать’’ код VBA, вы с легкостью сможете подправить автоматически сгенеY
рированный код и даже написать макрос ‘‘с нуля’’.
Глава 2
Çíàêîìñòâî ñ Visual
Basic for Applications
2
Загадочный код
Код VBA способен смутить кажY
дого, кто изучал в школе один из
процедурных языков программироY
вания наподобие BASIC или COBOL.
Несмотря на то что VBA расшифроY
вывается как ‘‘Visual Basic for ApplicaY
tions’’, он представляет собой объект*
но*ориентированную версию BASIC.
Рассмотрим небольшой фрагмент
кода VBA.
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
Бьюсь об заклад, что этот код не
будет иметь ни малейшего смысла
для тех, кто изучал только процедурY
ные языки программирования (к соY
жалению, практика изучения процеY
дурных языков еще весьма популярна
во многих учебных заведениях).
Ниже приведен фрагмент кода,
написанный на языке BASIC.
For x = 1 to 10
Print Rpt$(" ", x);
Print "*";
Next x
В результате его выполнения на экY
ране компьютера появится ‘‘лесенка’’
из символов звездочки.
Загадочный код..............................59
Учимся понимать “речь” VBA .....60
Справочная система VBA .............63
Изучение кода записанного
макроса ............................................66
Использование отладчика
кода ...................................................74
Диспетчер объектов ......................85
5 советов по исправлению и
оптимизации автоматически
сгенерированного кода ................87
Исправление и оптимизация
автоматически
сгенерированного кода ................90
Следующий шаг..............................93
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
ответствует набор строк, столбцу 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
сутствие имен параметров серьезно затрудняет восприятие ее смысла. Точный
61
62
Часть I
Первые шаги
порядок следования параметров метода можно узнать, обратившись к разделу
справочной системы, посвященному этому методу.
Ситуацию усложняет еще и то, что имена параметров требуется указывать
только в случае нарушения стандартного порядка их следования. Ниже привеY
дены две эквивалентных строки кода, соответствующих команде ‘‘ударить
футбольный мяч так, чтобы он полетел высоко влево’’ (не важно, насколько
сильным будет сам удар):
Balls("Soccer").Kick Direction:=Left, Elevation:=High
Balls("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
достаточно странный результат 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.
63
64
Часть I
Первые шаги
Рис. 2.1. Вставьте в рабочую книгу
новый модуль
Рис. 2.2. Установите курсор посредине
слова MsgBox и нажмите клавишу <F1>
Рис. 2.3. Если справочная система VBA установлена, вы увидите
на экране это окно
Если справочная система VBA не установлена, Excel выдаст сообщение об
ошибке. Установите справочную систему VBA, воспользовавшись установочY
ными компактYдисками Microsoft Office (при необходимости обратитесь за
помощью к системному администратору).
Знакомство с Visual Basic for Applications
Глава 2
Просмотр разделов справочной системы
Раздел справочной системы, посвященный тому или иному методу, содерY
жит подробное описание всех его параметров. Под именем метода или функY
ции расположены три ссылки: See Also (См. также), Example (Пример) и
Specifics (Особенности). Одной из наиболее полезных является ссылка
Example, ведущая на страницу с примером использования метода или функY
ции (рис. 2.4).
Рис. 2.4. Большинство разделов справочной системы VBA со&
держат ссылку на страницу с примерами
Код примера можно выделить (рис. 2.5), скопировать в буфер обмена с поY
мощью комбинации клавиш <Ctrl+C>, а затем вставить в модуль с помощью
комбинации клавиш <Ctrl+V>.
Код записанных макросов наверняка содержит много незнакомых объекY
тов и методов. Установите курсор посредине интересующего вас ключевого
65
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
Рис. 2.6. Код записанного макроса
Рис. 2.7. Раздел справочной системы, посвященный методу OpenText. Ссылка
Applies To (Применяется к) позволяет просмотреть список объектов, к которым
может быть применен этот метод
67
68
Часть I
Первые шаги
Необязательные параметры
В справочной системе VBA можно найти информацию о стандартных знаY
чениях необязательных параметров. К примеру, стандартным значением параY
метра StartRow является 1, что весьма приемлемо. А вот пропустив параметр
Origin, вы рискуете попасть впросак. Дело в том, что по умолчанию Excel исY
пользует текущее значение этого параметра. Другими словами, если вы выполY
ните свой макрос после того, как ктоYто импортирует в Excel файл с китайскими
иероглифами, Excel предположит, что вы хотите сделать то же самое.
Предопределенные константы
Согласно разделу справочной системы VBA, посвященному методу OpenText (см. рис. 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
Рис. 2.9. Щелкните на ссылке, чтобы увидеть все допустимые константы
Изучив раздел справочной системы, посвященный методу OpenText,
можно заметить, что этот метод является в некотором смысле эквивалентом
мастера импорта текстов. Так, на первом шаге мастера необходимо выбрать
формат исходных данных YYYY С разделителями (Delimited) или Фиксированной
ширины (Fixed width), YYYY а также формат файла и строку, с которой необходимо
начать импорт (рис. 2.10).
Другими словами, первый шаг мастера импорта текстов можно описать
тремя параметрами метода OpenText:
Origin:=1251
StartRow:=1
DataType:=xlDelimited
69
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
TextQualifier:=xlDoubleQuote
ConsecutiveDelimiter:=False
Tab:=False
Semicolon:=False
Comma:=True
Space:=False
Other:=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
ного кода макроса.
71
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
Рис. 2.15. Поиск нужного раздела справочной системы можно проводить
методом проб и ошибок
Возврат объектов свойством
Ранее неоднократно упоминалось, что базовый синтаксис языка VBA предY
ставлен конструкцией Объект.Метод. В рассмотренной выше строке кода меY
тодом, очевидно, является метод .Select. Несмотря на то, что End — это
свойство, оно возвращает объект Range, а метод, таким образом, применяется
непосредственно к свойству.
Открыв раздел справочной системы, посвященный слову Selection,
можно обнаружить, что это также свойство, а не объект. Полное обращение к
свойству Selection выглядит как Application.Selection, однако в конY
тексте использования объектной модели Excel префикс Application можно
опустить. Если бы данный макрос выполнялся в текстовом редакторе Word,
нам обязательно потребовалось бы указать перед свойством .Selection пеY
ременную объекта для идентификации вызываемого приложения.
73
74
Часть I
Первые шаги
Рис. 2.16. Нужный раздел справочной системы, посвященный свойству End
Тип возвращаемого свойством Application.Selection объекта зависит
от текущего выделенного элемента. Если это ячейка, свойство Application.Selection возвращает объект Range.
Использование отладчика кода
Редактор Visual Basic содержит великолепный отладчик, предназначенный
для поиска и устранения недостатков программного кода.
Пошаговое выполнение кода
Обычно на выполнение макроса уходит всего лишь несколько секунд. Если
во время этого произойдет какойYто сбой, отследить его будет очень трудно.
К счастью, отладчик Excel поддерживает пошаговое выполнение кода.
Разместите курсор посредине имени процедуры ИмпортСчета и выберите
команду меню Debug Step Into (Отладка Пошаговое выполнение) (или наY
жмите клавишу <F8>) (рис. 2.17).
Сейчас редактор Visual Basic находится в режиме пошагового выполнеY
ния кода. Строка, которая будет выполнена следующей, выделена желтым
цветом. Кроме того, на нее указывает желтая стрелка, расположенная слеY
ва (рис. 2.18).
Знакомство с Visual Basic for Applications
Глава 2
Рис. 2.17. Пошаговое выполнение кода позволяет
обнаружить и устранить его недостатки
Рис. 2.18. Отладчик готов выполнить первую строку кода макроса
Выполнение строки Sub ИмпортСчета() приводит к входу в процедуру
ИмпортСчета(). Нажмите клавишу <F8>, чтобы выполнить эту строку и пеY
рейти к следующей. Редактор Visual Basic выделит желтым цветом фрагмент
кода, соответствующий методу OpenText. Нажмите клавишу <F8>. После
выполнения метода OpenText переключитесь в Excel с помощью комбинаY
ции клавиш <Alt+Tab> и убедитесь в успешном импорте файла Счет.txt.
Обратите внимание, что текущей выделенной ячейкой является ячейка A1
(рис. 2.19).
75
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
Обнаружив проблемный участок кода, остановите выполнение макроса,
выбрав команду меню 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
ние макроса остановится на границе точки прерывания, а соответствующая
77
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
Выполнение фрагмента кода
Иногда возникает необходимость в выполнении целого фрагмента кода, на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).
79
80
Часть I
Первые шаги
Рис. 2.28. Вычисление значения выра&
жения в окне Immediate
Рис. 2.29. Чтобы повторно вычис&
лить результат выражения, устано&
вите курсор в конец содержащей это
выражение строки и нажмите кла&
вишу <Enter>
Чтобы повторно вычислить результат выражения, нажмите клавишу <Enter>.
Новый результат (в данном случае $1:$1) ‘‘сдвинет’’ старый ($E$14:$G$14) на
одну строку вниз (рис. 2.30).
Нажмите клавишу <F8> четыре раза, чтобы выполнить строку
Cells.Select. Снова расположите курсор в конце строки Print Selection.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
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. Чтобы узнать значение выражения, задержите над ним указатель мыши
81
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
Рис. 2.36. Содержимое окна Watches обновляется после вы&
полнения каждой строки кода
Установка точки прерывания с помощью окна Watches
Щелкните правой кнопкой мыши на значке с изображением очков в окне
Watches (Просмотр) и выберите команду контекстного меню Edit Watch
(Изменить параметры просмотра). Установите переключатель Break When
Value Changes (Приостановить при изменении значения) в группе переклюY
чателей Watch Type (Способ просмотра) диалогового окна Edit Watch
(Изменить параметры просмотра) (рис. 2.37). Щелкните на кнопке OK.
Рис. 2.37. Установите
When Value Changes
переключатель
Break
Значок с изображением очков сменится на значок с изображением руки и
треугольника. Нажмите клавишу <F5> для выполнения макроса. Как только
значение выделенного диапазона ячеек изменится, выполнение макроса будет
приостановлено. Данная возможность является чрезвычайно полезной при
отладке кода.
Отслеживание состояния объекта с помощью окна Watches
Ранее было рассмотрено отслеживание значения свойства Selection.
Address. Редактор Visual Basic позволяет также следить за состоянием целых
объектов, таких как объект Selection (рис. 2.38).
83
84
Часть I
Первые шаги
Рис. 2.38. При отслеживании состояния объекта
рядом со значком с изображением очков появ&
ляется значок с изображением знака “плюс”
Щелкните на значке с изображением знака ‘‘плюс’’, чтобы просмотреть все
свойства объекта Selection (рис. 2.39). Существование некоторых из них
окажется для вас настоящим сюрпризом. Кроме новых свойств наподобие
.AddIndent (значение False) и .AllowEdit (значение True), вы увидите
также уже знакомые свойства, такие как .Formula.
Рис. 2.39. Щелкните на значке с изображением знака “плюс”, чтобы просмотреть список
свойств объекта и их текущих значений
Возле некоторых свойств объекта Selection, таких как коллекция Borders, находится значок с изображением знака ‘‘плюс’’. Щелкните на нем,
чтобы получить более детальную информацию об объекте.
Знакомство с Visual Basic for Applications
Глава 2
Диспетчер объектов
Чтобы открыть окно диспетчера объектов редактора 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
на диспетчера объектов появится список свойств и методов объекта Application (рис. 2.41).
85
86
Часть I
Первые шаги
Рис. 2.41. Выберите класс, а затем — метод или свойство. В нижней части окна диспетчера
объектов появится краткое описание выбранного элемента. Рядом с именем метода в правой
части окна диспетчера объектов находится значок с изображением зеленой книги, а рядом с
именем свойства — изображение учетной карточки с указывающей на нее кистью руки
Щелкните на имени свойства ActiveCell. В нижней части окна диспетчера
объектов появится краткое описание свойства ActiveCell, из которого можно
узнать тип возвращаемого этим свойством значения YYYY Range. Кроме того,
свойство ActiveCell предназначено только для чтения, что делает невозможY
ным присвоение ему значения с целью сдвинуть указатель активной ячейки.
Щелкните на ссылке Range в нижней части окна диспетчера объектов,
чтобы увидеть список свойств и методов объекта Range, а значит и свойства
ActiveCell. Щелкните на имени любого свойства или метода объекта
Range, а затем YYYY на кнопке с изображением желтого вопросительного знака в
верхней части диспетчера объектов. В результате откроется окно справочной
системы с разделом, посвященным выбранному элементу.
Введите любое ключевое слово в поле ввода раскрывающегося списка, наY
ходящегося справа от кнопки с изображением бинокля, и щелкните на этой
кнопке, чтобы найти все подходящие под данное ключевое слово элементы
библиотеки Excel.
Чтобы закрыть окно диспетчера объектов и вернуться к окну просмотра
программного кода, щелкните на кнопке с изображением крестика в верхнем
правом углу окна диспетчера объектов (рис. 2.42).
Знакомство с Visual Basic for Applications
Глава 2
Рис. 2.42. Чтобы закрыть окно диспетчера объектов, щелкните на кнопке с изображением кре&
стика в верхнем правом углу окна
5 советов по исправлению и оптимизации
автоматически сгенерированного кода
Приблизившись к концу второй главы, было бы неплохо исправить хотя
бы один из двух имеющихся у нас проблемных макросов. Ниже приведено
5 советов, направленных на оптимизацию и исправление автоматически сгеY
нерированного кода.
Совет 1: ничего не выделяйте
Отличительной особенностью автоматически сгенерированного кода являY
ется выделение элементов перед их дальнейшим использованием. В некотоY
ром смысле это подразумевает копирование действий, совершаемых с помоY
щью пользовательского интерфейса Excel. Так, чтобы сделать текст ячейки
утолщенным, ее необходимо сначала выделить.
Подобная практика является совершенно излишней в VBA. (Существуют
исключения, которые, однако же, обусловлены не вполне корректным повеY
дением некоторых методов, требующих для своего выполнения предварительY
ного выделения объекта диаграммы.) Чтобы сделать текст ячейки утолщенY
87
88
Часть I
Первые шаги
ным, последнюю можно и не выделять. Ниже показан пример преобразования
двух строк автоматически сгенерированного кода макроса в одну.
Автоматически сгенерированный код:
Rows("1:1").Select
Selection.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
Одним из возможных решений этой проблемы является перемещение в
конец рабочего листа 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
ходного диапазона ячеек, его копирование, выделение целевого диапазона
89
90
Часть I
Первые шаги
ячеек и, наконец, вызов метода ActiveSheet.Paste. Метод Copy, примеY
няемый к диапазону ячеек, обладает намного более широкой функциональноY
стью, позволяя задать источник и назначение копируемых данных с помощью
одного выражения.
Ниже приведен фрагмент автоматически сгенерированного кода:
Range("E14").Select
Selection.Copy
Range("F14:G14").Select
ActiveSheet.Paste
А это YYYY тот же код после оптимизации:
Range("E14").Copy Destination:=Range("F14:G14")
Совет 5: используйте конструкцию With...End With
Ниже приведен автоматически сгенерированный код, изменяющий разY
личные параметры шрифта выделенного диапазона ячеек:
Range("A14:G14").Select
Selection.Font.Bold = True
Selection.Font.Size = 12
Selection.Font.ColorIndex = 5
Selection.Font.Underline = xlUnderlineStyleDoubleAccounting
При выполнении этого кода макрос должен 4 раза подряд вычислить знаY
чение выражения Selection.Font. Поскольку каждый раз обращение проY
исходит к одному и тому же объекту, его имя рекомендуется указать в начале
блока With. Чтобы сослаться на объект внутри блока With, соответствующие
строки кода необходимо предварить символом точки, как показано ниже:
With Range("A14:G14").Font
.Bold = True
.Size = 12
.ColorIndex = 5
.Underline = xlUnderlineStyleDoubleAccounting
End With
Исправление и оптимизация автоматически
сгенерированного кода
Практикум
Изменение автоматически сгенерированного кода
Используя приведенные выше советы, превратим автоматически сгенерированный
код макроса ИмпортСчета (см. ниже) в эффективный и профессиональный код.
Sub ИмпортСчета()
'
' ИмпортСчета Макрос
' Макрос записан 03.01.2005 (Александр Журавлев)
Знакомство с Visual Basic for Applications
Глава 2
'
' Сочетание клавиш: 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.AutoFit
End Sub
Чтобы исправить и оптимизировать код макроса, выполните следующие действия.
1. Оставьте метод Workbook.OpenText без изменений.
2. В следующей строке кода осуществляется попытка перейти на последнюю стро&
ку с данными:
Selection.End(xlDown).Select
Ничего не выделяйте. Кроме того, создайте две переменные — для номера по&
следней строки с данными и для номера итоговой строки. Чтобы избежать про&
блемы пустой ячейки, переместитесь на последнюю строку с данными с конца
рабочего листа:
' Найти последнюю строку с данными
FinalRow = Range("A65536").End(xlUp).Row
TotalRow = FinalRow + 1
3. Следующие строки кода соответствуют вводу слова “Всего” в столбец A итого&
вой строки:
Range("A14").Select
ActiveCell.FormulaR1C1 = "Всего"
Воспользуйтесь созданной ранее переменной TotalRow и откажитесь от выде&
ления ячейки, как показано ниже:
' Создание итоговой строки
Range("A" & TotalRow).Value = "Всего"
4. Приведенные ниже строки кода описывают ввод формулы суммы в столбец E и
ее копирование в столбцы F и G:
Range("E14").Select
Selection.FormulaR1C1 = "=SUM(R[-12]C:R[-1]C)"
Selection.AutoFill Destination:=Range("E14:G14"), _
91
92
Часть I
Первые шаги
Type:=xlFillDefault
Range("E14:G14").Select
Вы уже наверное догадались, что выделять здесь абсолютно нечего. Приведен&
ный ниже код помещает формулу суммы в требуемые ячейки итоговой строки
(формат ссылок R1C1 рассматривается в главе 6, “Стиль записи ссылок R1C1”):
Range("E" & TotalRow).Resize(1, 3).FormulaR1C1 = _
"=SUM(R2C:R[-1]C)"
5. Ниже приведен код, сгенерированный средством записи макросов при фор&
матировании строки заголовков столбцов и итоговой строки:
Rows("1:1").Select
Selection.Font.Bold = True
Rows("14:14").Select
Selection.Font.Bold = True
А вот и его оптимизированная версия:
Rows("1:1").Font.Bold = True
Rows(TotalRow & ":" & TotalRow).Font.Bold = True
6. Перед вызовом метода AutoFit средство записи макросов выделяет все ячей&
ки рабочего листа:
Cells.Select
Selection.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
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.AutoFit
End Sub
Следующий шаг
В этой главе были рассмотрены основы синтаксиса языка программироваY
ния Visual Basic for Applications, использование справочной системы, средств
отладки, а также несколько советов по исправлению и оптимизации автомаY
тически сгенерированного кода.
Следующая глава посвящена более детальному изучению диапазонов ячеек.
93
Глава 3
Ðàáîòà
ñ äèàïàçîíîì ÿ÷ååê
Диапазон ячеек представляет соY
бой любое их объединение в пределах
одного рабочего листа. Примерами
диапазона ячеек являются ячейка,
строка, столбец и т.п. Объект
Range YYYY один из наиболее популярY
ных объектов Excel VBA.
В этой главе будут рассмотрены
различные способы обращения к диаY
пазону ячеек в пределах как одного, так
и нескольких рабочих листов, объедиY
нение диапазонов ячеек, а также создаY
ние нового диапазона ячеек из неY
скольких пересекающихся диапазонов.
Объект Range
Рассмотрим следующую иерархию
объектов Excel:
Application Workbook Worksheet
Range
Объект Range является свойством
объекта Worksheet. Обе приведенY
ные ниже строки кода выполняют
одно и то же действие, если рабочий
лист Worksheets(1) является акY
тивным рабочим листом.
Range("A1")
Worksheets(1).Range("A1")
Таким образом, существует неY
сколько способов обращения к диаY
пазону ячеек. Range("A1") является
наиболее распространенным из них в
основном за счет того, что это станY
дартный способ обращения к диапаY
зону ячеек, использующийся средст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
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
Обратите внимание, что имя диапазона ячеек взято в кавычки. Это отличи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, позволяющей заменить выражения
97
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
Обращение к диапазону ячеек с помощью
свойства 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).Row
For i = 1 To FinalRow
Range("A" & i & ":E" & i).Font.Bold = True
Next i
В результате использования ‘‘недружелюбного’’ синтаксиса цикл, выдеY
ляющий ячейки в столбцах A–E с помощью утолщения шрифта, оказался
весьма сложным для восприятия. Попробуем записать его несколько иначе:
FinalRow = Cells(65536, 1).End(xlUp).Row
For i = 1 To FinalRow
Cells(i, "A").Resize(, 5).Font.Bold = True
Next i
Использование свойств Cells и Resize вместо адреса диапазона ячеек
делает код цикла более наглядным.
99
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
Результат выполнения макроса показан на рис. 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)
101
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 = True
End 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).Row
For i = 1 To LastRow
If IsEmpty(Cells(i, 1)) Then
Cells(i, 1).Resize(1, 4).Interior.ColorIndex = 1
Работа с диапазоном ячеек
Глава 3 105
End If
Next i
Рис. 3.6. Первые 4 ячейки строк&разделителей
выделены черным цветом
Обращение к диапазону ячеек с помощью
свойства CurrentRegion
Свойство CurrentRegion возвращает объект, представляющий непреY
рывный диапазон ячеек. С помощью этого свойства можно обратиться к диаY
пазону ячеек, ограниченному по крайней мере одной пустой строкой или одY
ним пустым столбцом:
ДиапазонЯчеек.CurrentRegion
В результате выполнения приведенной ниже строки кода будет выделен
диапазон ячеек A1:D3 — непрерывный диапазон ячеек, включающий в себя
ячейку A1 (рис. 3.7):
Range("A1").CurrentRegion.Select
Рис. 3.7. Используйте свойство CurrentRegion для обращения к непрерывному диапа&
зону ячеек, включающему в себя текущую ак&
тивную ячейку
106 Часть I
Первые шаги
Свойство CurrentRegion рекомендуется использовать для обращения к
таблицам, размер которых постоянно меняется.
Практикум
Выделение ячеек, соответствующих определенному
критерию, с помощью метода SpecialCells
Далеко не все пользователи Excel знают о существовании диалогового окна
Выделение группы ячеек (Go To Special). Нажмите клавишу <F5>, чтобы открыть
диалоговое окно Переход (Go To) (рис. 3.8).
Рис. 3.8. Чтобы открыть диалоговое окно Выделение группы ячеек, щелкните на кноп&
ке Выделить
Щелкните на кнопке Выделить (Special) в левом нижнем углу диалогового окна
Переход, чтобы открыть диалоговое окно Выделение группы ячеек (рис. 3.9).
Диалоговое окно Выделение группы ячеек позволяет выделить только пустые
ячейки, только видимые ячейки или же только ячейки, содержащие формулы.
Возможность выделения только видимых ячеек очень полезна при автоматиче&
ской фильтрации данных.
Возможности диалогового окна Выделение группы ячеек могут быть реализованы
с помощью метода VBA SpecialCells. Этот метод позволяет работать с ячейка&
ми, соответствующими определенному критерию:
ДиапазонЯчеек.SpecialCells(Тип, Значение)
Метод SpecialCells имеет два параметра: Тип и Значение (необязательный
параметр). Тип ячейки может быть описан одной из констант xlCellType:
xlCellTypeAllFormatConditions
xlCellTypeAllValidation
xlCellTypeBlanks
xlCellTypeComments
Работа с диапазоном ячеек
Глава 3 107
xlCellTypeConstants
xlCellTypeFormulas
xlCellTypeLastCell
xlCellTypeSameFormatConditions
xlCellTypeSameValidation
xlCellTypeVisible
Рис. 3.9. Диалоговое окно Выделение группы ячеек предлагает широкие возможности по
выделению ячеек
Предусмотрено также 4 различных значения ячейки:
xlErrors
xlLogical
xlNumbers
xlTextValues
В результате выполнения приведенного ниже кода вокруг всех непрерывных диа&
пазонов ячеек с условным форматированием будет создана граница. При отсутст&
вии таких диапазонов будет выдано сообщение об ошибке:
Set rngCond = ActiveSheet.Cells.SpecialCells( _
xlCellTypeAllFormatConditions)
If Not rngCond Is Nothing Then
rngCond.BorderAround xlContinuous
End 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.Value
End 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
Ôóíêöèè,
îïðåäåëåííûå
ïîëüçîâàòåëåì
Создание функций,
определенных
пользователем
Иногда огромного количества
встроенных функций Excel бывает
недостаточно. В частности, Excel не
содержит готового решения для задаY
чи суммирования значений в ячейках,
выделенных определенным цветом.
Что же делать? Вручную скопироY
вать все нужные ячейки в другую
часть рабочего листа? Или взять
калькулятор и провести подсчет саY
мому? Оба способа отнимают много
времени и не гарантируют отсутствие
ошибок. Одно из возможных решений
заключается в написании процедуY
ры YYYY в конечном итоге, именно проY
цедурам посвящена большая часть
этой книги. Однако единственно праY
вильным решением является создание
функции, определенной пользователем.
VBA позволяет создавать функY
ции, которые могут использоваться
аналогично встроенным функциям
Excel, таким как СУММ (SUM). Чтобы
применить подобную функцию, неY
обходимо знать только ее имя и арY
гументы.
4
Создание функций,
определенных пользователем .. 111
Наиболее распространенные
задачи программирования
в Excel ...............................................113
Следующий шаг............................140
112
Часть I
Первые шаги
На заметку
Функции, определенные пользователем, должны храниться в стандартных моду&
лях. Модули рабочих листов и модуль ЭтаКнига (ThisWorkbook) являются специ&
альными модулями. Функция, размещенная в одном из таких модулей, не будет
воспринята Excel как функция, определенная пользователем.
Практикум
Практикум: пример создания и применения функции,
определенной пользователем
Создадим функцию, суммирующую значения двух ячеек, и применим ее на рабо&
чем листе Excel.
С помощью редактора Visual Basic добавьте к проекту новый модуль и введите в
него текст функции суммирования значений двух ячеек Add (см. ниже). Эта функ&
ция принимает два аргумента:
Add(Number1, Number2)
Здесь Number1 — это первое слагаемое, а Number2 — второе:
Function Add(Number1, Number2) As Integer
Add = Number1 + Number2
End 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
Большинство функций, используемых на рабочих листах, могут с успехом
применяться в VBA, и наоборот. Тем не менее, VBA требует, чтобы функция,
определенная пользователем (Add), вызывалась из процедуры (Addition),
как показано ниже:
Sub Addition ()
Dim Total as Integer
Total = Add (1, 10) 'вызов функции, определенной пользователем
MsgBox "Ответ: " & Total
End Sub
Наиболее распространенные задачи
программирования в Excel
В следующих разделах этой главы рассматриваются решения наиболее
распространенных задач, встречающихся при повседневном программиY
ровании в Excel.
Вывод имени файла текущей рабочей книги в ячейке
Предназначение следующей функции заключается в выводе имени файла
активной рабочей книги в ячейке, как показано на рис. 4.1:
MyName()
Рис. 4.1. Функции MyName и MyFullName используются для вывода
в ячейке имени и полного имени файла активной рабочей книги,
соответственно
Функция MyName не имеет аргументов.
Function MyName() As String
MyName = ThisWorkbook.Name
End Function
Вывод полного имени файла текущей рабочей
книги в ячейке
Предназначение следующей функции заключается в выводе имени файла
активной рабочей книги в ячейке (см. рис. 4.1):
MyFullName()
Функция MyFullName не имеет аргументов.
Function MyFullName() As String
MyFullName = ThisWorkbook.FullName
End Function
113
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 0
End 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 If
End 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
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 If
End 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
115
116
Часть I
Первые шаги
End If
'Выбрать тип файла - рабочие книги Excel.
.FileType = msoFileTypeExcelWorkbooks
'Указать на необходимость проведения поиска в текущей папке.
.LookIn = CurDir
'Указать на необходимость проведения поиска в подпапках.
.SearchSubFolders = Subfolders
.Execute
NumFilesInCurDir = .FoundFiles.Count
End With
End 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 Long
Private Const NO_ERROR = 0
Private Const ERROR_NOT_CONNECTED = 2250&
Private Const ERROR_MORE_DATA = 234
Private Const ERROR_NO_NETWORK = 1222&
Private Const ERROR_EXTENDED_ERROR = 1208&
Private Const ERROR_NO_NET_OR_BAD_PATH = 1203&
Текст функции WinUsername может быть помещен в любую часть модуля
при условии, что он будет находиться ниже объявлений Private:
Функции, определенные пользователем
Глава 4
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 If
End Function
Ниже приведен пример использования функции WinUsername:
Sub CheckUserRights()
Dim UserName As String
UserName = WinUsername
Select Case UserName
Case "Administrator"
MsgBox "Полные права"
Case "Guest"
MsgBox "Вы не можете вносить изменения в рабочую книгу"
Case Else
MsgBox "Ограниченные права"
End Select
End Sub
Получение даты и времени последнего
сохранения рабочей книги
Следующая функция возвращает дату и время последнего сохранения раY
бочей книги, как показано на рис. 4.2.
LastSaved(FullPath)
Рис. 4.2. Функция LastSaved возвращает дату и время последнего
сохранения рабочей книги
Функция LastSaved имеет один аргумент:
FullPath — полный путь к файлу рабочей книги.
117
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 = Now
End Function
Проверка адреса электронной почты
Следующая функция проверяет корректность написания адреса электронY
ной почты (рис. 4.4).
IsEmailValid(StrEmail)
Рис. 4.4. Проверка корректности написания адреса электронной почты
Функции, определенные пользователем
Глава 4
Внимание
Функция 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
119
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 = blnIsItValid
End 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
SumByColor = myTotal
End 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 = "Белый"
121
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 = "Бледно-голубой"
Функции, определенные пользователем
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%"
Глава 4 123
124 Часть I
Первые шаги
IndexNum = 56
Case Else
myColor = "Другой цвет или отсутствие заливки"
End Select
'Возвратить номер цвета заливки ячейки, если это указано
'при вызове функции или невозможно возвратить имя цвета заливки.
If ColorIndex = True Or myColor = "Другой цвет или _
отсутствие заливки" Then
CellColor = IndexNum
Else
CellColor = myColor
End If
End 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.ColorIndex
End 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.Count
End Function
Удаление повторяющихся значений из диапазона ячеек
Следующая функция удаляет повторяющиеся значения из указанного диаY
пазона ячеек:
UniqueValues(OrigArray)
126 Часть I
Первые шаги
Функция UniqueValues имеет один аргумент:
OrigArray — массив, из которого необходимо удалить повторяющиеY
ся значения.
Следующий фрагмент кода должен быть помещен в верхнюю часть модуля:
Const
Const
Const
Const
ERR_BAD_PARAMETER = "Не указан исходный массив"
ERR_BAD_TYPE = "Неверный тип"
ERR_BP_NUMBER = 20000
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 = vAns
End 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.Value
End 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 = Ar
End 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
На заметку
Результат выполнения функции 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) * 7
End Function
Разбор строки с символамиLразделителями
Следующая функция извлекает элемент с заданным номером из строки с
символамиYразделителями:
StringElement(str, chr, ind)
Пример использования функции StringElement на рабочем листе покаY
зан на рис. 4.13.
Рис. 4.13. Пример извлечения элемента с заданным номером из стро&
ки с символами&разделителями с помощью функции StringElement
131
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_exit
End 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 i
End 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,
Call QuickSort(vntArr,
Else
Call QuickSort(vntArr,
Call QuickSort(vntArr,
End If
End If
End Sub
lngLeft, j)
i, lngRight)
i, lngRight)
lngLeft, j)
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) = vntTemp
End 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 = T
End 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 If
End 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 myCell
End 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 = Rnd
End Function
Рис. 4.19. Пример генерации постоянного случайного числа с по&
мощью функции StaticRAND
Использование структуры Select...Case
Следующая функция демонстрирует пример использования структуры Select...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
31 октября " & yr - 1
Case 5
state_period
30 ноября " & yr - 1
Case 6
state_period
31 декабря " & yr - 1
Case 7
state_period
31 января " & yr
Case 8
state_period
28 февраля " & yr
Case 9
state_period
31 марта " & yr
Case 10
state_period
30 апреля " & yr
Case 11
state_period
31 мая " & yr
Case 12
state_period
30 июня " & yr
Case 13
state_period
Case 14
state_period
End Select
End Function
= "С 1 октября " & yr - 1 & " по _
= "С 1 ноября " & yr - 1 & " по _
= "С 1 декабря " & yr - 1 & " по _
= "С 1 января " & yr & " по _
= "С 1 февраля " & yr & " по _
= "С 1 марта " & yr & " по _
= "С 1 апреля " & yr & " по _
= "С 1 мая " & yr & " по _
= "С 1 июня " & yr & " по _
= "Подготовка к распродаже"
= "Распродажа"
Следующий шаг
В следующей главе будет рассмотрен один из фундаментальных компонентов
любого языка программирования YYYY цикл. Помимо базовых циклов, присутстY
вующих практически в каждом языке программирования, будет рассмотрен
цикл For Each...Next, являющийся исключительной особенностью VBA.
Глава 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).
5
Цикл For...Next............................... 141
Циклы Do...Loop ............................ 147
Цикл For Each...Next...................... 152
Управление выполнением
кода: использование
конструкций If...Then...Else и
Select Case ...................................... 155
Следующий шаг............................ 160
142 Часть I
Первые шаги
Рассмотрим следующий фрагмент кода:
For I = 1 To 10
Cells(I, I).Value = i
Next 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. Перед выполнением строки Next
I значение переменной 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, перед выполнением строки Next
I в 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 If
Next 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).Row
For 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 If
Next 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).Row
For I = 2 To FinalRow Step 2
Cells(I, 1).Resize(1, 8).Interior.ColorIndex = 35
Next I
Циклы и управление выполнением кода
Глава 5 145
В результате выполнения приведенного выше кода строки 2, 4, 6 и т.д. буY
дут выделены бледноYзеленым цветом (рис. 5.9).
Рис. 5.9. Пример изменения шага приращения значения переменной&
счетчика цикла For...Next с помощью оператора Step
Значение переменнойYсчетчика может изменяться практически с любым
шагом. Ниже приведен пример извлечения каждой десятой строки (Step 10)
из заданного диапазона ячеек:
FinalRow = Cells(65536, 1).End(xlUp).Row
NextRow = FinalRow + 5
Cells(NextRow - 1, 1).Value = "Выборка из приведенных выше данных"
For I = 2 To FinalRow Step 10
Cells(I, 1).Resize(1, 8).Copy Destination:=Cells(NextRow, 1)
NextRow = NextRow + 1
Next I
Значение переменнойYсчетчика может изменяться и в направлении от
большего к меньшему. В частности, это может пригодиться при выборочном
удалении строк, как показано ниже:
'Удаление строк со значением S54 в столбце C.
FinalRow = Cells(65536, 1).End(xlUp).Row
For I = FinalRow To 2 Step -1
If Cells(I, 3).Value = "S54" Then
Cells(I, 1).EntireRow.Delete
End If
Next I
Досрочное завершение выполнения цикла
Иногда выполнение цикла можно завершить досрочно. Рассмотрим задачу
поиска строки, удовлетворяющей определенному критерию. Как только данY
ная строка будет найдена, выполнение оставшейся части цикла теряет смысл.
Для досрочного выхода из цикла применяется выражение Exit For.
Следующий код используется для поиска строки с положительным числом
в столбце F и нулем в столбце E. При нахождении такой строки выдается соY
146 Часть I
Первые шаги
общение об ошибке, а указатель помещается в ячейку проблемной строки,
расположенную в столбце F:
'Следующий код используется для поиска ошибок в исходных данных.
FinalRow = Cells(65536, 1).End(xlUp).Row
ProblemFound = False
For 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 If
Next I
If ProblemFound Then
MsgBox "Ошибка в строке " & I
Exit Sub
End If
Вложение циклов
Цикл может выполняться внутри другого цикла. Одним из наиболее наглядY
ных примеров вложения циклов является цикл, обрабатывающий строки в заY
данном диапазоне ячеек, внутри которого выполняется цикл, обрабатывающий
столбцы этих строк. Рассмотрим набор данных, представленный на рис. 5.10.
Рис. 5.10. Вложение циклов позволяет реализовать последовательную обработку всех
ячеек этого диапазона
FinalRow = Cells(65536, 1).End(xlUp).Row
FinalCol = Cells(1, 255).End(xlToLeft).Column
For 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 j
Next 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").Select
End 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").Select
Loop
End 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").Select
Loop
End 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
которые программисты считают, что частое использование ключевых слов 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 = 0
Do
x = InputBox(Prompt:="Введите сумму следующего счета _
или 0 для завершения.")
TotalSales = TotalSales + x
Loop Until x = 0
MsgBox "Общая сумма сегодняшних продаж составила $" & TotalSales
В следующем примере пользователю предлагается ввести сумму, указанY
ную в чеке. Оплата нескольких счетов одним чеком — весьма распространенY
ная практика. Макрос последовательно ‘‘погашает’’ счета (начиная с самых
ранних) до тех пор, пока не исчерпает сумму чека.
'Ввести сумму, указанную в чеке.
AmtToApply = InputBox("Введите сумму, указанную в чеке")
'Погасить счета, начиная с самых ранних,
'уменьшая при этом значение переменной AmtToApply.
NextRow = 2
Do 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 + 1
Loop
Возможность использования операторов While и Until как в начале, так
и в конце конструкции Do...Loop, позволяет осуществлять тонкий контроль
над ходом выполнения цикла.
151
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 рабочие листы в рабочей книге, ячейки в диапазоне, сводные таблицы
на рабочем листе, последовательности данных на диаграмме и т.п. Цикл For
Each...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 If
Next Cell
Свойство CurrentRegion используется для выделения непрерывного диаY
пазона ячеек, а свойство Resize — для ограничения диапазона столбцом A.
С помощью следующего кода производится поиск рабочего листа с заданY
ным именем в коллекции всех открытых рабочих книг:
For Each wb in Workbooks
If wb.Worksheet(1).Name = "Menu" Then
WBFound = True
WBName = wb.Name
End If
Next wb
В результате выполнения приведенного ниже кода с текущего рабочего
листа будут удалены все находящиеся на нем фигуры:
For Each Sh In ActiveSheet.Shapes
Sh.Delete
Next Sh
Выполнение следующего кода приведет к удалению с текущего рабочего
листа всех сводных таблиц:
For Each PT In ActiveSheet.PivotTables
PT.TableRange2.Clear
Next 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 With
End 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 Cell
End 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
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.
157
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.05
Case 21 To 100
Discount = 0.1
Кроме того, воспользовавшись ключевым словом Is и оператором сравнеY
ния (например, > или <), можно задать значение условного выражения как
открытый диапазон:
Case Is < 10
Discount = 0
Case Is > 100
Discount = 0.2
Case 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
Ñòèëü çàïèñè
ññûëîê R1C1
6
Сравнение стилей
записи ссылок 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 поддерживает оба способа записи
ссылок.
Сравнение стилей записи
ссылок A1 и R1C1.............................161
Использование стиля ссылок
R1C1 в Excel ..................................... 162
Чудесный мир формул Excel ..... 163
Ссылки в стиле R1C1 ..................... 166
Использование ссылок в стиле
R1C1 при условном
форматировании ячеек...............171
Использование ссылок в стиле
R1C1 при создании формулы
массива........................................... 174
Следующий шаг............................ 175
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.AutoFit
End 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
Loop
End Sub
Стиль записи ссылок R1C1
Глава 6
Если вы не считаете подобную игру забавной или же вам необходимо уз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
171
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
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 ws
End Sub
Внимание
Не используйте A1&формулы для задания критерия условного форматирования
в VBA. Соответствующий код может выполняться корректно лишь для небольшого
числа ячеек, однако при попытке его применения к 50 и более ячейкам существует
вероятность сбоя (более подробно об этом рассказывается в следующем практи&
куме). Использование R1C1&формул полностью лишено подобного недостатка.
Практикум
Поиск минимального и максимального
значения в столбце
Рассмотрим классический пример использования условного форматирования
ячеек. На рис. 6.13 показано, как выделить строки, содержащие минимальное и
максимальное значение в определенном столбце, с помощью интерфейса Excel.
Рис. 6.13. Классический пример, позволяющий выявить одну из про&
блем задания условного форматирования ячеек с помощью VBA
173
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 With
End 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
Рис. 6.14. Формула массива, введенная в ячейке E20, реали&
зует 18 операций умножения и суммирование полученных при
каждом умножении результатов. Для ввода формулы необхо&
димо использовать комбинацию клавиш <Ctrl+Shift+Enter>
Ниже приведен код, использующийся для ввода формулы массива:
Sub EnterArrayFormulas()
' Ввод формулы массива.
FinalRow = Cells(65536, 1).End(xlUp).Row
Cells(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.
175
Глава 7
Èìåíà
Поле для ввода имени диапазона
ячеек, формулы, массива и т.п. распоY
лагается на рабочем листе слева от поY
ля ввода формулы. Присваивание
имен различным объектам Excel сущеY
ственно упрощает их использование.
Возможность создавать имена и
манипулировать ими доступна также
и в VBA. Далее в этой главе будут расY
смотрены различные типы имен и
способы их применения на практике.
Глобальные и
локальные имена
Все имена можно разделить на две
категории YYYY глобальные имена (область
действия распространяется на всю раY
бочую книгу) и локальные имена
(область действия ограничена одним
рабочим листом). В рабочей книге моY
жет содержаться несколько одинакоY
вых локальных имен, однако только
одно уникальное глобальное имя. При
обращении к локальному имени необY
ходимо указывать соответствующий
рабочий лист (Лист1!Фрукты), тогда
как при обращении к глобальному
имени никаких дополнительных треY
бований не ставится (Фрукты).
Показанное на рис. 7.1 диалогоY
вое окно Присвоение имени (Define
Name) содержит смешанный спиY
сок, состоящий из глобальных и лоY
кальных имен.
Имя рабочего листа, соответстY
вующего локальному имени, вывоY
дится справа от последнего.
7
Глобальные и локальные
имена .............................................. 177
Создание имен.............................. 179
Удаление имен .............................180
Типы имен......................................180
Скрытие имен ............................... 185
Проверка существования
имени.............................................. 185
Следующий шаг............................ 187
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
информации практически любого рода. В отличие от переменных, имена хра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, они
больше подходят для хранения данных, чем ячейки. Рассмотрим задачу хранения
названия компании, ставшей лучшим поставщиком за определенный промежуток
времени. Одним из решений этой задачи является создание имени ЛучшийПоставщик. Альтернативный способ хранения имени лучшего поставщика заключа&
ется в использовании ячейки на отдельном рабочем листе, что менее удобно.
181
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 If
End Sub
Ниже приведен код аналогичной процедуры, использующий для хранения
данных между сеансами работы в Excel имена:
Sub WithNames()
If Evaluate("Текущий") = Evaluate("Предыдущий") Then
MsgBox ("Лучшим поставщиком снова стал " & _
Evaluate("Предыдущий") & "!")
Else
MsgBox ("Лучшим поставщиком стал " & Evaluate("Текущий"))
End If
End Sub
Поскольку Текущий и Предыдущий являются заранее созданными именаY
ми, доступ к их значениям осуществляется напрямую, без необходимости созY
дания переменных. Обратите внимание, что для доступа к значению имени
используется метод Evaluate.
Внимание
Длина строки не может превышать 255 символов.
Имена чисел
Имена могут использоваться и для хранения чисел. Ниже приведены два
примера создания имени числа YYYY с участием переменной и без нее:
NumofSales = 5123
Names.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:=myArray
End 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 = False
End 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).Value
End Sub
Следующий шаг
Следующая глава посвящена событиям YYYY концепции, позволяющей VBA
реагировать на действия пользователя, такие как выделение ячейки, смена теY
кущего активного рабочего листа и т.п.
Глава 8
Ñîáûòèÿ
События позволяют реагировать
на всевозможные действия, происхоY
дящие в пределах текущего сеанса
работы с Excel.
Различают следующие уровни соY
бытий.
Уровень приложения. ОхватыY
вает действия, имеющие неY
посредственное отношение к
Excel, например Application_NewWorkbook.
Уровень рабочей книги. ОхваY
тывает действия, имеющие
непосредственное отношение
к рабочей книге, например
Workbook_Open.
Уровень рабочего листа. ОхватыY
вает действия, имеющие непоY
средственное отношение к раY
бочему листу, например Worksheet_SelectionChange.
Уровень листа диаграммы. ОхY
ватывает действия, имеющие
непосредственное отношение
к листу диаграммы, например
Chart_Activate.
Код обработки событий рабочей
книги размещается в модуле ЭтаКнига (ThisWorkbook), код обработY
ки событий рабочего листа YYYY в модуY
ле этого рабочего листа (например,
Лист1), код обработки событий листа
диаграммы и Excel — в соответствуюY
щих модулях классов. При обработке
событий можно вызывать процедуры
и функции из других модулей.
8
Использование событий............. 190
События рабочей книги...............191
События рабочего листа............. 199
События листа диаграммы....... 204
События приложения................. 208
Следующий шаг............................ 214
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
Cancel значения True предотвращает выполнение стандартного действия,
в данном случае YYYY отображения контекстного меню:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, _
Cancel As Boolean)
Cancel = True
End Sub
Запрет обработки событий
Некоторые события могут вызывать другие события, включая самих себя.
Например, событие Worksheet_Change срабатывает при изменении ячейки
на рабочем листе. Если при обработке этого события вновь будет изменена
ячейка, событие сработает снова, что приведет к зацикливанию.
Чтобы избежать описанной выше ситуации, запретите обработку событий
в начале кода процедуры и разрешите ее в конце, как показано ниже:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Range("A1").Value = Target.Value
Application.EnableEvents = True
End Sub
На заметку
Чтобы прервать выполнение макроса, нажмите клавишу <Esc> или воспользуй&
тесь комбинацией клавиш <Ctrl+Break>.
События рабочей книги
Ниже приведен список событий, существующих на уровне рабочей
книги Excel:
Activate;
Deactivate;
Open;
BeforeSave;
BeforePrint;
BeforeClose;
NewSheet;
SheetActivate;
SheetDeactivate;
SheetCalculate;
SheetBeforeDoubleClick;
SheetSelectionChange;
191
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 If
End Sub
Событие Workbook_Open можно применять и для создания пользовательY
ских меню или панелей управления. Следующий код создает меню Меню
MrExcel с двумя пунктами, как показано на рис. 8.3.
См. также
Более подробно создание пользовательского меню рассматривается в разделе
“Создание пользовательского меню” главы 24 на с. 575.
События
Private
Dim
Dim
Dim
Dim
Глава 8 193
Sub Workbook_Open()
cbWSMenuBar As CommandBar
Ctrl As CommandBarControl, muCustom As CommandBarControl
iHelpIndex As Integer
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 With
End 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 With
End Sub
Рис. 8.4. Событие Workbook_BeforePrint можно ис&
пользовать для ведения журнала печати
Событие Workbook_BeforePrint можно использовать для создания
верхнего и нижнего колонтитула печатаемой страницы. Несмотря на то, что
теперь это можно сделать с помощью диалогового окна Параметры страницы
(Page Setup), раньше подобная возможность реализовывалась только с помоY
щью программного кода:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
ActiveSheet.PageSetup.RightFooter = ActiveWorkbook.FullName
End 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").Delete
End Sub
Приведенный выше код имеет один недостаток. Если в рабочую книгу быY
ли внесены изменения, Excel предложит сохранить их, отобразив соответстY
События
Глава 8 195
вующее диалоговое окно после выполнения кода обработки события Workbook_BeforeClose. Если вы передумаете закрывать книгу и щелкните на
кнопке Отмена (Cancel), то уже не сможете вернуть удаленное меню.
Решение этой проблемы заключается в выводе на экран собственного диаY
логового окна сохранения рабочей книги:
Private
Dim
Dim
Dim
Sub Workbook_BeforeClose(Cancel As Boolean)
Msg As String
Response
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").Delete
End 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 = False
End Sub
Внимание
Запрет изменения размера окна рабочей книги приводит к удалению кнопок окна
Свернуть (Minimize) и Развернуть (Maximize). Чтобы вернуть кнопки на место,
выполните в окне Immediate (Быстрое выполнение) строку ActiveWindow.EnableResize = True.
Событие Workbook_WindowActivate(ByVal Wn As Window)
Событие Workbook_WindowActivate срабатывает при активизации окY
на любой рабочей книги. Wn — это объект окна.
На заметку
Событие Workbook_WindowActivate срабатывает только при активизации окна
рабочей книги.
Событие Workbook_WindowDeactivate(ByVal
Wn 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 As
Object, ByVal Target As Range, Cancel As Boolean)
Событие Workbook_SheetBeforeDoubleClick срабатывает при выY
полнении двойного щелчка на рабочем листе или листе диаграммы текущей
активной рабочей книги. Sh — это объект активного рабочего листа или листа
диаграммы, а Target — объект, на котором был выполнен двойной щелчок.
Чтобы запретить стандартное действие, предпринимаемое при обработке
двойного щелчка, установите значение параметра Cancel равным True.
Кроме того, двойной щелчок на рабочем листе или листе диаграммы приводит
к срабатыванию событий активного рабочего листа (Worksheet_BeforeDoubleClick) или листа диаграммы (Chart_BeforeDoubleClick).
Событие Workbook_SheetBeforeRightClick(ByVal Sh As
Object, ByVal Target As Range, Cancel As Boolean)
Событие Workbook_SheetBeforeRightClick срабатывает при выполY
нении щелчка правой кнопкой мыши на рабочем листе или листе диаграммы
текущей активной рабочей книги. Sh — это объект активного рабочего листа
или листа диаграммы, а Target — объект, на котором был выполнен щелчок
правой кнопкой мыши. Чтобы запретить стандартное действие, предприниY
маемое при обработке щелчка правой кнопкой мыши, установите значение
параметра Cancel равным True.
Кроме того, щелчок правой кнопкой мыши на рабочем листе или листе
диаграммы приводит к срабатыванию событий активного рабочего листа
(Worksheet_BeforeRightClick) или листа диаграммы (Chart_BeforeRightClick).
Событие Workbook_SheetCalculate(ByVal Sh As Object)
Событие Workbook_SheetCalculate срабатывает при пересчете рабоY
чего листа или обновлении листа диаграммы. Sh — это объект активного раY
бочего листа или листа диаграммы.
198 Часть I
Первые шаги
Кроме того, пересчет рабочего листа или обновление листа диаграммы
приводит к срабатыванию событий активного рабочего листа (Worksheet_
Calculate) или листа диаграммы (Chart_Calculate).
Событие Workbook_SheetChange(ByVal Sh As Object, ByVal
Target 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 As
Object, ByVal Target As Hyperlink)
Событие Workbook_SheetFollowHyperlink срабатывает при щелчке
на гиперссылке, расположенной на любом рабочем листе активной рабочей
книги. Sh — это объект активного рабочего листа, Target — объект гиперY
ссылки.
Кроме того, щелчок на гиперссылке, расположенной на рабочем листе,
приводит к срабатыванию события активного рабочего листа (Worksheet_
FollowHyperlink).
Событие Workbook_SheetSelectionChange(ByVal Sh As
Object, 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 = True
End Sub
Событие Worksheet_Deactivate()
Событие Worksheet_Deactivate срабатывает при деактивизации соотY
ветствующего этому событию рабочего листа:
Private Sub Worksheet_Deactivate()
On Error Resume Next
Application.CommandBars("Панель инструментов _
MrExcel").Visible = False
End Sub
На заметку
При переключении между рабочими листами первым срабатывает событие Worksheet_Deactivate исходного рабочего листа, и только затем — событие Worksheet_Activate активизированного рабочего листа.
Событие Worksheet_BeforeDoubleClick(ByVal Target
As 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 = True
End Sub
На заметку
Приведенный выше код не запрещает применять двойной щелчок для изменения
высоты строки и ширины столбца.
Запретив использование двойного щелчка для перехода в режим изменеY
ния содержимого ячейки, ему можно найти другое применение. Выполнение
следующего кода приведет к смене цвета заливки ячейки:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, _
Cancel As Boolean)
Target.Interior.ColorIndex = 3
End Sub
Событие Worksheet_BeforeRightClick(ByVal Target As
Range, 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 Select
End 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").Select
End Sub
Рис. 8.5. Пример использования события Worksheet_Calculate для добавления на рабочий
лист фигуры
202 Часть I
Первые шаги
Событие Worksheet_Change(ByVal Target As Range)
Событие Worksheet_Change срабатывает при изменении содержимого
ячейки, например, при вводе, редактировании или удалении текста. Target — это объект ячейки, содержимое которой было изменено.
На заметку
Событие 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).
Внимание
Чтобы избежать зацикливания в результате изменения содержимого
ячейки, запретите обработку событий с помощью строки Application.EnableEvents = False.
События
Глава 8 203
Событие Worksheet_SelectionChange(ByVal
Target 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 With
End Sub
Внимание
Указанный код обработки события Worksheet_SelectionChange отменяет лю&
бое условное форматирование, заданное на рабочем листе. Кроме того, он может
очистить буфер обмена, затрудняя тем самым выполнение операций копирования
и вставки в пределах данного рабочего листа.
Событие Worksheet_FollowHyperlink(ByVal Target
As 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_ChartEvents
Sub InitializeChart()
Set myClassModule.myChartClass = _
Worksheets(1).ChartObjects(1).Chart
End Sub
Рис. 8.6. События встроенной диаграммы доступны посредством модуля класса
Тем самым вы сделаете объект встроенной диаграммы доступным как
обычный объект диаграммы. Процедуру InitializeChart необходиY
мо выполнить один раз при открытии рабочей книги (воспользуйтесь
событием Workbook_Open).
Внимание
Первая часть имени события встроенной диаграммы будет совпадать с именем
объекта диаграммы, заданном в модуле класса (в данном случае — myChartClass).
Событие 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 Select
End 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, ByVal
Shift 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 If
End 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 Shift
As 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, ByVal
Arg1 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 If
End 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_AppEvents
Sub InitializeAppEvent()
Set myAppEvent.AppEvent = Application
End 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 xlArrangeStyleTiled
End Sub
Событие AppEvent_SheetActivate(ByVal Sh As Object)
Событие AppEvent_SheetActivate срабатывает при активизации люY
бого рабочего листа или листа диаграммы. Sh — это объект активизированY
ного рабочего листа или листа диаграммы.
Событие AppEvent_SheetBeforeDoubleClick(ByVal Sh
As Object, ByVal Target As Range, Cancel As Boolean)
Событие AppEvent_SheetBeforeDoubleClick срабатывает в результаY
те двойного щелчка на любом рабочем листе. Sh — это объект активного рабоY
чего листа, Target — объект выделенного диапазона ячеек. Чтобы запретить
выполнение стандартного действия, предпринимаемого при обработке двойY
ного щелчка, установите значение параметра Cancel равным True (по умолY
чанию значение параметра Cancel равно False).
Событие AppEvent_SheetBeforeRightClick(ByVal Sh
As Object, ByVal Target As Range, Cancel As Boolean)
Событие AppEvent_SheetBeforeRightClick срабатывает в результате
щелчка правой кнопкой мыши на любом рабочем листе. Sh — это объект акY
тивного рабочего листа, Target — объект, на котором был выполнен щелчок
правой кнопкой мыши. Чтобы запретить выполнение стандартного действия,
предпринимаемого при обработке щелчка правой кнопкой мыши, установите
значение параметра Cancel равным True.
События
Глава 8
Событие 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 Sh
As Object, ByVal Target As Hyperlink)
Событие AppEvent_SheetFollowHyperlink срабатывает при щелчке
на любой гиперссылке. Sh — это объект активного рабочего листа, Target —
объект гиперссылки.
Событие AppEvent_SheetSelectionChange(ByVal Sh
As Object, ByVal Target As Range)
Событие AppEvent_SheetSelectionChange срабатывает при выделеY
нии нового диапазона ячеек на любом листе. Sh — это объект активного лисY
та, Target — объект выделенного диапазона ячеек.
Событие AppEvent_WindowActivate(ByVal Wb
As Workbook, ByVal Wn As Window)
Событие AppEvent_WindowActivate срабатывает при активизации окY
на любой рабочей книги. Wb — это объект рабочей книги, отображаемой в акY
тивизированном окне, Wn — объект окна.
Событие AppEvent_WindowDeactivate(ByVal Wb
As Workbook, ByVal Wn As Window)
Событие AppEvent_WindowDeactivate срабатывает при деактивизации
окна любой рабочей книги. Wb — это объект рабочей книги, отображаемой в
деактивизированном окне, Wn — объект окна.
211
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 Wb
As Workbook)
Событие AppEvent_WorkbookActivate срабатывает при активизации
любой рабочей книги. Wb — это объект активизированной рабочей книги.
Следующий код указывает на необходимость развернуть окно рабочей книги
при ее активизации:
Private Sub AppEvent_WorkbookActivate(ByVal Wb As Workbook)
Wb.WindowState = xlMaximized
End Sub
Событие AppEvent_WorkbookAddinInstall(ByVal Wb
As Workbook)
Событие AppEvent_WorkbookAddinInstall срабатывает при установке
рабочей книги в качестве надстройки (чтобы установить надстройку, выбериY
те команду меню Excel Сервис Надстройки (Tools AddYIns)). Обратите
внимание, что событие AppEvent_WorkbookAddinInstall не срабатывает
при двойном щелчке на значке файла надстройки (файл с расширением
.xla). Wb — это объект рабочей книги, установленной в качестве надстройки.
Событие AppEvent_WorkbookAddinUninstall(ByVal Wb
As Workbook)
Событие AppEvent_WorkbookAddinUninstall срабатывает при удалеY
нии рабочей книги, используемой в качестве надстройки. Обратите внимание,
что надстройка не закрывается автоматически. Wb YYYY это объект удаляемой
рабочей книги, использовавшейся в качестве надстройки.
События
Глава 8 213
Событие AppEvent_WorkbookBeforeClose(ByVal Wb As
Workbook, Cancel As Boolean)
Событие AppEvent_WorkbookBeforeClose срабатывает при закрытии
рабочей книги. Wb — это объект рабочей книги. Чтобы запретить закрытие раY
бочей книги, установите значение параметра Cancel равным True.
Событие AppEvent_WorkbookBeforePrint(ByVal Wb As
Workbook, 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.UserName
End Sub
Событие AppEvent_WorkbookBeforeSave(ByVal Wb As
Workbook, ByVal SaveAsUI As Boolean, Cancel As Boolean)
Событие AppEvent_WorkbookBeforeSave срабатывает при попытке соY
хранения рабочей книги. Wb — это объект рабочей книги. Чтобы отобразить
диалоговое окно Сохранение документа (Save As), установите значение параY
метра SaveAsUI равным True. Чтобы запретить сохранение рабочей книги,
установите значение параметра Cancel равным True.
Событие AppEvent_WorkbookDeactivate(ByVal Wb As
Workbook)
Событие AppEvent_WorkbookDeactivate срабатывает при деактивизаY
ции любой рабочей книги. Wb — это объект деактивизированной рабочей
книги.
Событие AppEvent_WorkbookNewSheet(ByVal Wb As
Workbook, 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
Ââåäåíèå â
ïîëüçîâàòåëüñêèå
ôîðìû
Способы
взаимодействия
с пользователем
В отличие от окна ввода и окна соY
общения, формы Excel выводят взаиY
модействие с пользователем на соверY
шенно иной качественный уровень.
В этой главе будут рассмотрены
базовые интерфейсы взаимодействия
с пользователем, такие как окно ввоY
да и окно сообщения, а также основы
создания пользовательских форм.
Более подробно пользовательские
формы рассматриваются в главе 21,
‘‘Пользовательские формы YYYY проY
фессиональный подход’’.
Окно ввода
Окно ввода данных представляет
собой один из основных интерфейсов
взаимодействия с пользователем. НаY
страиваемым является текст сообщеY
ния, заголовок окна, значение по
умолчанию, положение окна на экраY
не, а также файл справки. Окно ввода
имеет две кнопки YYYY OK и Отмена
(Cancel). Функция InputBox возвраY
щает значение типа String. В резульY
тате выполнения приведенного ниже
кода пользователю будет предложено
ввести количество месяцев, для котоY
рого необходимо подсчитать некотоY
рые статистические данные:
9
Способы взаимодействия
с пользователем ........................... 215
Создание пользовательской
формы ............................................ 216
Вызов и скрытие
пользовательской формы.......... 218
Основные элементы
управления формы..................... 220
Использование вкладок для
объединения форм......................225
Следующий шаг........................... 226
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 Sub
End 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 Me
End 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.Value
End 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 With
End 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 With
End 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.Value
End 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 = True
End Sub
Введение в пользовательские формы
Рис. 9.10. Для группирования пере&
ключателей используется панель
Глава 9 223
Рис. 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 With
End 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.Value
End 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.Value
End Sub
Введение в пользовательские формы
Глава 9 225
Использование вкладок для объединения форм
Вкладки позволяют объединить воедино несколько различных форм.
На рис. 9.14 показан пример объединения форм для ввода служебной
и личной информации о сотруднике.
Рис. 9.13. Чтобы унифициро&
вать ввод даты приема слу&
жащего на работу, восполь&
зуйтесь счетчиками
Рис. 9.14. Используйте вкладки для объединения несколь&
ких различных форм
Совет
Старайтесь планировать формы с вкладками заранее. Чтобы создать форму с
вкладками на основе уже существующих форм, необходимо создать новую фор&
му, добавить в нее требуемое число вкладок и скопировать на них элементы
управления с имеющихся форм.
Проверка ввода обязательных данных
Одним из преимуществ электронной формы является возможность проY
верки ввода обязательных данных:
If tb_EmpName.Value = "" Then
frm_AddEmp.Hide
MsgBox("Пожалуйста, введите имя служащего")
frm_AddEmp.Show
Exit Sub
End 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 If
End 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
Äèàãðàììû
Говорят, лучше один раз увидеть,
чем сто раз услышать. Наглядное предY
ставление данных имеет неоспоримое
преимущество перед сухими цифрами.
Именно поэтому диаграммы являются
неотъемлемой частью всех программ
для работы с электронными таблицаY
ми, пройдя вместе с ними долгий эвоY
люционный путь развития.
В этой главе рассматривается исY
пользование VBA при выполнении
следующих базовых задач:
создание встроенных диаграмм
и диаграмм, расположенных на
отдельном листе;
выбор типа диаграммы;
изменение типа диаграммы;
форматирование, перемещение
и удаление диаграммы и ее элеY
ментов;
создание нестандартных диаY
грамм.
Одной из особенностей програмY
мирования диаграмм в Excel является
наличие двух объектных моделей,
каждая из которых соответствует разY
личному типу диаграмм.
Встроенные диаграммы
и диаграммы,
расположенные
на отдельном листе
Изначально все диаграммы создаY
вались на отдельном листе. В середиY
10
Встроенные диаграммы и
диаграммы, расположенные
на отдельном листе .................... 229
Создание диаграмм с
помощью VBA ...............................232
Использование объектных
переменных для упрощения
кода ................................................ 236
“Анатомия” диаграммы ............237
Типы диаграмм ............................ 251
Параметры трехмерных и
круговых диаграмм.................... 256
Интерактивные диаграммы..... 260
Экспорт диаграммы в файл
изображения................................. 261
Удивительные возможности
точечных диаграмм ................... 262
Создание нестандартных
диаграмм ...................................... 262
Следующий шаг........................... 266
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 Selection.Top.
Отмените выделение контейнера диаграммы, после чего щелкните на неY
занятом пространстве между границей диаграммы и областью ее построения.
По бокам и в углах области диаграммы появятся квадратные маркеры управY
ления размером черного цвета (рис. 10.2), что свидетельствует о выделении
области диаграммы.
Рис. 10.2. Чтобы выделить область диаграммы, щелкните на незанятом
пространстве между границей диаграммы и областью ее построения. В по&
ле Имя появится стандартное имя области всех диаграмм — Область
диаграммы
Области всех диаграмм имеют стандартное имя Область диаграммы
(Chart Area), которое выводится в поле Имя слева от поля ввода формулы.
Ниже приведен код VBA, соответствующий выделению области диаграммы:
ActiveSheet.ChartObjects("Диаграмма 1").Activate
ActiveChart.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.3.
диаграмму, выделите ис&
На рис. 10.4 показана диаграмма, созданная Excel
ходные данные и нажмите
в
результате
нажатия клавиши <F11>.
клавишу <F11>
Рис. 10.4. В результате нажатия клавиши <F11> Excel создаст новую диаграмму, расположенную
на отдельном листе
Диаграммы
Глава 10 233
Ниже приведен код, сгенерированный средством записи макросов:
Charts.Add
ActiveChart.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 ActiveChart.
Внимание
Если текущим активным объектом является объект, отличный от диаграммы, по&
пытка использования объекта 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 SetSourceData
Приведенная выше строка соответствует последнему шагу мастера созда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").Activate
ActiveChart.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 Chart
Set Cht = Charts.Add
Cht.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 With
End 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 With
End 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 With
End 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 With
End 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 = 75
obj.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 With
End 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 With
End 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 With
End Sub
Результат выполнения макроса представлен на рис. 10.12.
Обратите внимание, что добавление подписей осей привело к автоматичеY
скому изменению размера области построения диаграммы. Чтобы задать подY
пись диаграммы, необходимо установить значение свойства оси HasTitle
равным True.
Диаграммы
Глава 10 245
Рис. 10.12. Пример добавления к диаграмме подписей осей и изменения
формата данных оси значений
Добавление вспомогательных осей
Если масштаб рядов данных сильно отличается, может возникнуть необхоY
димость добавления вспомогательной оси (горизонтальной или вертикальY
ной). Ниже приведен полный синтаксис обращения к оси диаграммы:
Cht.Axes(Type, AxisGroup)
AxisGroup — это константа Excel VBA, определяющая группу оси. ОсновY
ные оси входят в группу xlPrimary, а вспомогательные YYYY в группу xlSecondary.
Следующий макрос определяет вспомогательную ось значений (Y) для ряда
данных Xdata.
Sub SecondaryAxisDemo()
Dim Cht As Chart
Set Cht = Charts("Диаграмма 3")
Cht.SeriesCollection("Xdata").AxisGroup = 2
End 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 With
End Sub
Подписи данных (DataLabels и DataLabel)
Ряд состоит из точек данных. Каждая точка может иметь свою собственную
подпись, включающую значение по оси X (категорий), оси Y (значений) или
значение, определенное пользователем (строковая константа или ссылка на
ячейку). Ниже перечислены различные способы создания подписи данных с
помощью VBA.
Все точки данных диаграммы имеют подписи одного и того же типа:
ActiveChart.ApplyDataLabels Type:=xlDataLabelsShowNone
Диаграммы
Глава 10 247
Точки данных определенного ряда имеют подписи одного и того же типа:
With ActiveChart.SeriesCollection("Xdata")
.HasDataLabels = True
.ApplyDataLabels Type:=xlDataLabelsShowValue
End 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 With
End 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 With
End Sub
Линии тренда и полосы погрешности (Trendlines и ErrorBar)
Линия тренда дает наглядное представление о направлении изменения ряY
да данных. Excel содержит несколько типов линий тренда: линейная, логаY
рифмическая, полиномиальная, степенная, экспоненциальная и линейная
фильтрация. Полосы погрешности позволяют оценить отклонение фактичеY
ских данных от тренда.
На рис. 10.14 показана точечная диаграмма годовых продаж.
Следующий макрос добавляет к диаграмме линию тренда с прогнозом на
5 периодов (лет) вперед, выводит уравнение линии тренда и величину достоY
2
верности аппроксимации (R ):
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 With
End Sub
Результат выполнения макроса показан на рис. 10.15. Величина достоверY
2
ности аппроксимации (R ), близкая к 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:=25
End 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
вающими линиями без маркеров
xlXYScatterSmoothNoMarke
rs
Точечная диаграмма со значе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
диаграмма
грамма
Контурная диаграмма (вид сверY
ху на обычную поверхностную
диаграмму)
xlSurface
xlSurfaceTopView
254 Часть II
Автоматизация Excel
Продолжение табл. 10.1
Тип
диаграммы
Биржевая
диаграмма
ЦилиндриY
ческая диаY
грамма
Вид диаграммы
Константа VBA
Проволочная (прозрачная) поY
верхностная диаграмма
xlSurfaceWireframe
Проволочная (прозрачная) конY
турная диаграмма (вид сверху на
проволочную (прозрачную) поY
верхностную диаграмму)
xlSurfaceTopViewWireframe
Биржевая диаграмма для набоY
ров из трех значений (самый выY
сокий курс, самый низкий курс,
курс закрытия)
xlStockHLC
Биржевая диаграмма для набоY
ров из четырех значений (объем,
самый высокий курс, самый
низкий курс, курс закрытия)
xlStockVHLC
Биржевая диаграмма для набоY
ров из четырех значений (курс
открытия, самый высокий курс,
самый низкий курс, курс закрыY
тия)
xlStockOHLC
Биржевая диаграмма для набоY
ров из пяти значений (объем,
курс открытия, самый высокий
курс, самый низкий курс, курс
закрытия)
xlStockVOHLC
Обычная гистограмма со столб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
грамма с накоплением со столбY
цами в виде конусов
xlConeBarStacked100
Трехмерная гистограмма со
столбцами в виде конусов
xlConeCol
Обычная гистограмма со столб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, наблюдатель не
видит верхнюю поверхность фигур. Если значение параметра Elevation равно 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 With
End 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 = 60
End 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 With
End 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 If
End 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
Следующий шаг
Диаграммы являются неотъемлемой частью всех программ для работы с
электронными таблицами, поскольку они позволяют получить наглядное
представление об исходных данных. Следующая глава посвящена анализу
данных с помощью расширенного фильтра.
Глава 11
Àíàëèç äàííûõ
ñ ïîìîùüþ
ðàñøèðåííîãî
ôèëüòðà
Преимущества VBA
перед
пользовательским
интерфейсом Excel
Диалоговое окно Excel Расширенный фильтр (Advanced Filter) наY
столько неинтуитивно, что большинY
ство пользователей предпочитают
вообще не связываться с ним. ВероY
ятно, Microsoft полностью изменит
интерфейс расширенного фильтра в
следующей версии Excel.
С другой стороны, работа с расY
ширенным фильтром посредством
VBA может доставить истинное наY
слаждение. Всего одной строки кода
достаточно для извлечения подмноY
жества строк исходных данных или
отбора уникальных значений из заY
данного столбца!
При рассмотрении расширенных
фильтров в этой главе внимание буY
дет уделено как первому, так и втоY
рому способу их создания.
Одна из причин излишней сложноY
сти диалогового окна Расширенный
фильтр (рис. 11.1) обусловлена налиY
чием параметров фильтра.
11
Преимущества VBA перед
пользовательским
интерфейсом Excel.......................267
Использование расширенного
фильтра для отбора
уникальных значений из
заданного диапазона................. 268
Использование расширенного
фильтра с указанием условия
отбора данных..............................276
Фильтрация диапазона
исходных данных “на месте” ...287
Использование расширенного
фильтра для копирования
всех записей,
удовлетворяющих заданному
условию ......................................... 289
Автофильтр ...................................297
Следующий шаг........................... 298
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
Отбор уникальных значений из заданного столбца
с помощью 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, _
271
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:=True
End 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 равным frmMultiSelectMulti) и 4 кнопки YYYY OK, Отмена, Выбрать
все и Очистить. Процедура UserForm_Initialize исY
пользуется для заполнения списка на форме данными,
полученными в результате отбора уникальных значений
из столбца D и их последующей сортировки.
Private Sub CancelButton_Click()
Unload Me
End Sub
Private Sub cbSubAll_Click()
For i = 0 To lbCust.ListCount - 1
Me.lbCust.Selected(i) = True
Next i
End Sub
Private Sub cbSubClear_Click()
For i = 0 To lbCust.ListCount - 1
Me.lbCust.Selected(i) = False
Next i
End Sub
Рис. 11.5. Подсчет вы&
ручки, приходящейся
на каждого заказчика
Private Sub OKButton_Click()
For i = 0 To lbCust.ListCount - 1
If Me.lbCust.Selected(i) = True Then
' Создание отчета.
RunCustReport WhichCust:=Me.lbCust.List(i)
274 Часть II
Автоматизация Excel
End If
Next i
Unload Me
End 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).Clear
End Sub
Ниже приведен код вывода формы frmReport на экран:
Sub ShowCustForm()
frmReport.Show
End 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.7. Результат от&
бора всех уникальных столбцов, удовлетворяющие критерию отбора. На
комбинаций значений рис. 11.8 показан диапазон условий J1:J2 и диапазон
столбцов B и D
вставки результата L1.
Анализ данных с помощью расширенного фильтра
Глава 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.10. Диапазон условий J1:J3 ис&
пользуется для отбора заказчиков, ко&
торые приобрели товар ABC или XYZ
Глава 11 279
Рис. 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 Me
End Sub
Private Sub cbSubAll_Click()
' Выделить всех заказчиков.
For i = 0 To lbCust.ListCount - 1
Me.lbCust.Selected(i) = True
Next i
End Sub
Private Sub cbSubClear_Click()
' Отменить выделение заказчиков.
For i = 0 To lbCust.ListCount - 1
Me.lbCust.Selected(i) = False
Next i
End Sub
Private Sub CommandButton1_Click()
' Отменить выделение товаров.
For i = 0 To lbProduct.ListCount - 1
Me.lbProduct.Selected(i) = False
Next i
End Sub
Private Sub CommandButton2_Click()
' Выделить все товары.
For i = 0 To lbProduct.ListCount - 1
Me.lbProduct.Selected(i) = True
Next i
End Sub
Private Sub CommandButton3_Click()
' Отменить выделение регионов.
For i = 0 To lbRegion.ListCount - 1
Me.lbRegion.Selected(i) = False
Next i
End Sub
Private Sub CommandButton4_Click()
' Выделить все регионы.
For i = 0 To lbRegion.ListCount - 1
Me.lbRegion.Selected(i) = True
Next i
End 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 + 1
Next cell
MsgBox 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 Sub
NoRecs:
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).Row
For 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").Value
Cells(2, NextCol + 2).Value = ThisCust
Set CRange = Cells(1, NextCol + 2).Resize(2, 1)
4. Второй расширенный фильтр используется для копирования строк, удовлетво&
ряющих заданному условию, в область, начинающуюся со столбца N. Параметр
Unique метода AdvancedFilter имеет значение False, а параметр CopyToRange представляет собой ссылку на диапазон ячеек 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. Результат применения второго расширенного фильтра копируется в новую ра&
бочую книгу. Для создания рабочей книги используется метод Workbooks.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 = "Отчет о сделках заказчика " & ThisCust
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
7. Новая рабочая книга сохраняется в файле с именем, совпадающим с названием
соответствующей фирмы&заказчика, после чего эта книга закрывается. Перед
переходом к следующей итерации цикла макрос очищает область вставки ре&
зультата выполнения обоих расширенных фильтров.
WBN.SaveAs "C:\" & ThisCust & ".xls"
WBN.Close SaveChanges:=False
WSO.Select
Set WSN = Nothing
Set 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
Range("A1").AutoFilter
Range("A1").AutoFilter
Range("A1").AutoFilter
Range("A1").AutoFilter
End Sub
Field:=3,
Field:=5,
Field:=6,
Field:=7,
Field:=8,
VisibleDropDown:=False
VisibleDropDown:=False
VisibleDropDown:=False
VisibleDropDown:=False
VisibleDropDown:=False
Параметр VisibleDropDown уникален тем, что он доступен только через
программный код. Выполнить аналогичное действие посредством пользоваY
298 Часть II
Автоматизация Excel
тельского интерфейса Excel не представляется возможным. ‘‘Модифицируйте’’
подобным образом автофильтр, и вы прославитесь как знаток своего дела в глаY
зах коллег, ничего не подозревающих об удивительных возможностях VBA. РеY
зультат выполнения макроса AutoFilterCustom показан на рис. 11.21.
Рис. 11.21. Скрыть кнопки раскрывающихся списков для столбцов, по которым
не нужно проводить фильтрацию, возможно исключительно посредством VBA
Следующий шаг
Расширенный фильтр Excel предоставляет впечатляющие возможности по
манипулированию исходными данными и созданию отчетов. Следующая глаY
ва посвящена одному из краеугольных камней Excel — сводным таблицам.
Комбинация расширенного фильтра и сводной таблицы YYYY это настоящая
гремучая смесь, позволяющая делать с исходными данными все, что вам заY
благорассудится!
Глава 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
12
Сводные таблицы
в различных версиях Excel ........ 299
Создание сводных таблиц
с помощью пользовательского
интерфейса Excel......................... 300
Создание сводных таблиц
с помощью VBA ........................... 303
Создание отчета о структуре
спроса на товары......................... 309
Создание отчета о структуре
спроса на товары:
завершающая стадия.................. 317
Создание отчета
о прибыльности товаров ........... 326
Суммирование значений
полей области данных
сводной таблицы путем
группирования .............................332
Дополнительные возможности
сводных таблиц........................... 340
Сумма, среднее, количество,
минимум, максимум и др. .........355
Дополнительные вычисления
в полях области данных
сводной таблицы ........................ 356
Следующий шаг............................ 361
300 Часть II
Автоматизация Excel
ленный в Excel 2000. Кроме того, в конце главы будет рассмотрен метод PivotTableWizard 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 Office
Excel (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
Dim
Dim
Dim
Dim
Dim
Set
Автоматизация Excel
WSD As Worksheet
PTCache As PivotCache
PT As PivotTable
PRange As Range
FinalRow As Long
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)
После создания кэша сводных таблиц в него можно добавить новую свод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 = 1
End With
Задав все необходимые параметры сводной таблицы, пересчитайте ее, усY
тановив значение свойства ManualUpdate равным False, а затем вновь заY
претите автоматический пересчет, установив значение свойства ManualUpdate равным True, как показано далее:
306 Часть II
Автоматизация Excel
' Пересчитать сводную таблицу.
PT.ManualUpdate = False
PT.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 = True
End Sub
Перемещение или изменение части сводной таблицы
Excel не разрешает перемещать или изменять часть сводной таблицы.
К примеру, создайте макрос, удаляющий столбец, который содержит общую
сумму по строкам сводной таблицы (в данном случае YYYY столбец O). В резульY
тате выполнения макроса будет сгенерирована ошибка времени выполнеY
ния 1004, как показано на рис. 12.7.
Сводные таблицы
Глава 12 307
Рис. 12.7. Excel не разрешает перемещать или
изменять часть сводной таблицы. Чтобы
обойти это ограничение, скопируйте содер&
жимое ячеек сводной таблицы в другое место
Определение размера сводной таблицы
Определить размер сводной таблицы заранее достаточно сложно. Например,
рассматриваемая сводная таблица может содержать как 5, так и 6 столбцов в заY
висимости от того, была ли заключена в западном регионе хотя бы одна сделка.
Чтобы обратиться ко всей сводной таблице, необходимо воспользоваться ее
свойством TableRange2.
Поскольку Excel не разрешает перемещать или изменять часть сводной
таблицы, рекомендуется скопировать содержимое ячеек сводной таблицы в
другое место и удалить исходную таблицу. Рассмотрим макрос CreateSummaryReportUsingPivot, который создает небольшую сводную таблицу.
Чтобы запретить добавление к сводной таблице столбца Общий итог (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.TableRange2 с помощью метода Clear. Если вы затем собираетесь форматировать
рабочий лист, удалите объект PivotCache из памяти, присвоив соответстY
вующей переменной (в данном случае PTCache) значение Nothing.
308 Часть II
Автоматизация Excel
Рис. 12.8. Содержимое рабочего листа перед удалением свод&
ной таблицы
Sub CreateSummaryReportUsingPivot()
' Этот макрос создает статический отчет
' на основе сводной таблицы.
Dim
Dim
Dim
Dim
Set
WSD As Worksheet
PTCache As PivotCache
PT As PivotTable
PRange As Range
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 = Nothing
End 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
Область данных отчета содержит пустые ячейки вместо нулей. Как по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
граммного кода. В последнем случае содержимое сводной таблицы следует
предварительно скопировать в другую область рабочего листа или на новый
рабочий лист.
311
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 Next
PT.PivotFields("Регион").PivotItems("Восток").Position = 3
On 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 = NoSubtotalArray
PT.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
Dim
Dim
Dim
Dim
Dim
Dim
WSD As Worksheet
WSR As Worksheet
WBO As Workbook
WBN As Workbook
PTCache As PivotCache
PT As PivotTable
PRange As Range
Set WBO = ActiveWorkbook
Set WSD = Worksheets("Данные")
После построения и пересчета сводной таблицы создайте новую рабочую
книгу, как показано ниже:
' Создать новую рабочую книгу с одним листом.
Set WBN = Workbooks.Add(xlWBATWorksheet)
Set WSR = WBN.Worksheets(1)
WSR.Name = "Отчет"
' Создать заголовок отчета.
With WSR.[A1]
.Value = "Отчет о структуре спроса на товары"
.Font.Size = 14
End 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).Copy
WSR.[A3].PasteSpecial Paste:=xlPasteValuesAndNumberFormats
PT.TableRange2.Clear
Set 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).Row
With Range("A3").Resize(FinalReportRow - 2, 1)
With .SpecialCells(xlCellTypeBlanks)
.FormulaR1C1 = "=R[-1]C"
End With
.Value = .Value
End With
Стилевое форматирование отчета
Прежде чем добавить строки с промежуточными итогами, применим к отY
чету стилевое форматирование. Для этого выделим полужирным шрифтом
заголовки столбцов отчета (строка 3) и выровняем их по правому краю ячейки
(за исключением столбцов A и B, которые нужно выровнять по левому краю
ячейки). Последняя строка приведенного ниже кода указывает на необходиY
мость печати на каждой странице трех первых строк отчета:
' Стилевое форматирование отчета:
' - выделение полужирным шрифтом заголовков столбцов;
' - выравнивание заголовков столбцов по правому краю
'
(за исключением столбцов A-B, которые
'
выравниваются по правому краю).
Selection.Columns.AutoFit
Range("A3").EntireRow.Font.Bold = True
Range("A3").EntireRow.HorizontalAlignment = xlRight
Range("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).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)
Сводные таблицы
Глава 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 = False
End 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
ти столбцов и пересчитаем ее (это необходимо для заполнения поля ДатаОтгрузки данными). При группировании дат обратимся к свойству LabelRange, возвращающему заголовок соответствующего поля сводной таблицы.
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 = True
Next i
For Each PivItem In PT.PivotFields("Регион").PivotItems
PT.PivotFields("Регион").CurrentPage = PivItem.Name
PT.ManualUpdate = False
PT.ManualUpdate = True
Next PivItem
Конечно же, поочередный вывод отчетов на экран имеет немного практиY
ческого смысла. Обычно отчет создается для его последующего сохранения.
Ранее в этой главе для копирования содержимого сводной таблицы приY
менялось свойство TableRange2 объекта PivotTable. Свойство TableRange2 ссылается на все строки сводной таблицы, включая строки полей об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
Отчеты, созданные в результате выполнения макроса Top10ByRegionReport, показаны на рис. 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:="ВаловаяПрибыль_%", _
Сводные таблицы
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
Глава 12 353
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, xlPercentOfTotal, xlRunningTotal, xlPercentDifferenceFrom, xlDifferenceFrom, xlIndex и xlNoAdditionalCalculation. Некоторые тиY
пы вычислений требуют указания базового поля (свойство BaseField объекY
та PivotField) или комбинации базового поля и базового элемента
(свойство BaseItem объекта PivotField). Более подробно дополнительные
вычисления в полях области данных сводной таблицы рассматриваются в слеY
дующих разделах этой главы.
Доля от общей суммы
Чтобы отобразить значения элементов поля в виде доли от общей суммы
по этому полю, установите значение свойства Calculation равным xlPercentOfTotal, как показано ниже:
' Подсчитать долю от общей суммы.
With PT.PivotFields("Выручка")
.Orientation = xlDataField
.Caption = "Доля от общей суммы"
.Function = xlSum
.Position = 2
.NumberFormat = "#0.0%"
.Calculation = xlPercentOfTotal
End With
Приведенное отличие от значения
предыдущего элемента поля
Сгруппировав даты поля ДатаОтгрузки по месяцам, можно составить отY
чет о процентном изменении выручки относительно предыдущего месяца.
Для этого установите значение параметра Calculation равным xlPercentDifferenceFrom, параметра 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 используется метод PivotTableWizard. Рассмотрим пример построения простой сводной таблицы, отображаю&
щей выручку от продажи товаров по регионам.
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, предлагаемые опытными
программистами со всех уголков мира.
Глава 13
Excel âñåìîãóùèé
Первый принцип удачного проY
граммиста состоит в том, чтобы ниY
когда не писать один и тот же код
дважды. Второй принцип удачного
программиста требует создавать код
для каждой рутинной операции.
В настоящей главе рассматриваY
ются макросы, присланные опытY
ными пользователями Excel со
всего мира. Эти программы не
только помогут сэкономить время,
но и прольют свет на новые спосоY
бы решения наиболее распростраY
ненных задач.
Различные программисты приY
держиваются различных подходов к
созданию программного кода. НаY
глядным подтверждением этого явY
ляются приведенные в данной главе
макросы.
Расширение
возможностей Excel
с помощью VBA
VBA позволяет раскрыть возможY
ности Excel, недоступные посредстY
вом пользовательского интерфейса.
В следующих разделах рассматриваY
ется использование VBA для реалиY
зации условного форматирования с
более чем тремя условиями, и расY
ширенного фильтра с более чем двуY
мя условиями.
13
Расширение возможностей
Excel с помощью VBA.................. 363
Файловые операции .................. 365
Объединение и разделение
рабочих книг .................................372
Работа с примечаниями .............376
Замечательные возможности
Excel VBA ........................................ 381
VBA для профессионалов.......... 386
На закуску ..................................... 402
Следующий шаг........................... 405
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 If
End 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 1
2:
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 2
End 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 = Nothing
End 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 With
End 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 If
End 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 With
End Sub
Считывание текстового файла в память
и его последующий анализ
Макрос ReadTxtLines любезно предоставлен Суатом Мехметом Озгуром
(Suat Mehmet Ozgur), проживающим в Стамбуле, Турция. Суат разрабатывает
ExcelY, AccessY и Visual BasicYприложения для компаний MrExcel Consulting и
TheOfficeExperts.com.
Следующий макрос реализует весьма необычный подход к считыванию соY
держимого текстового файла. Вместо построчного чтения, макрос загружает
весь файл в память и сохраняет его в строковой переменной. Затем полученY
ная переменная разбивается на строки. Преимущество этого метода заключаY
Excel всемогущий
Глава 13
ется в единственном обращении к файлу на диске. Последующая обработка
файла осуществляется в памяти компьютера.
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 = Nothing
End Sub
371
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 = True
End 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 = Nothing
End Sub
Фильтрация данных с последующим копированием
полученного результата в отдельные рабочие листы
Макрос Filter_NewSheet любезно предоставлен Деннисом ВалентайY
ном (Dennis Wallentin), проживающим в Остерсунде, Швеция. Деннис дает
советы, касающиеся использования Excel и VBA, на своем собственном WebY
сайте по адресу: www.xldennis.com.
374 Часть II
Автоматизация Excel
Следующий макрос фильтрует исходные данные (рис. 13.2) и копирует поY
лученные результаты в отдельные рабочие листы (рис. 13.3).
Рис. 13.2. Исходные данные
Sub
Dim
Dim
Dim
Dim
Рис. 13.3. Результат применения
фильтра скопирован в новый ра&
бочий лист
Filter_NewSheet()
wbBook As Workbook
wsSheet As Worksheet
rnStart As Range, rnData As Range
i As Long
Set wbBook = ThisWorkbook
Set 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 xlPasteValues
Next i
'Вернуться к исходным данным.
rnStart.AutoFilter Field:=1
Excel всемогущий
Глава 13 375
With Application
'Очистить буфер обмена.
.CutCopyMode = False
.ScreenUpdating = False
End With
End Sub
Экспорт данных в Word
Макрос Export_Data_Word_Table также любезно предоставлен ДенниY
сом Валентайном.
Следующий макрос экспортирует данные с рабочего листа Excel в докуY
мент Word. Поскольку используется раннее связывание, необходимо добавить
ссылку (команда меню редактора Visual Basic Tools References (Сервис
Ссылки)) на библиотеку Microsoft Word Object Library.
Sub
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Export_Data_Word_Table()
wdApp As Word.Application
wdDoc As Word.Document
wdCell As Word.Cell
i As Long
wbBook As Workbook
wsSheet As Worksheet
rnData As Range
vaData As Variant
Set wbBook = ThisWorkbook
Set 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
.Close
End With
376 Часть II
Автоматизация Excel
'Завершить работу скрытой копии Microsoft Word.
wdApp.Quit
'Удалить внешние переменные из памяти.
Set wdDoc = Nothing
Set 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 всемогущий
With wb.Sheets(1)
.Cells(cmtCount,
.Cells(cmtCount,
.Cells(cmtCount,
.Cells(cmtCount,
.Cells(cmtCount,
cmt.Text)
End With
1)
2)
3)
4)
5)
=
=
=
=
=
Глава 13 377
cmt.author
cmt.Parent.Parent.Parent.Name
cmt.Parent.Parent.Name
cmt.Parent.Address
CleanComment(cmt.author, _
cmtCount = cmtCount + 1
Next
wb.Sheets(1).UsedRange.WrapText = False
Application.ScreenUpdating = True
Set ws = Nothing
Set wb = Nothing
End 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 = tmp
End Function
Результат выполнения макроса ListComments показан на рис. 13.4.
Рис. 13.4. Макрос ListComments позволяет получить исчерпывающую
информацию о примечаниях
Изменение размера области примечания
Макрос CommentFitter1 любезно предоставлен Томом Уртисом.
Следующий макрос изменяет размер области примечания так, чтобы она
вместила в себя весь текст примечания.
Sub CommentFitter1()
Application.ScreenUpdating = False
Dim 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 Select
Next x
Application.ScreenUpdating = True
End Sub
Результат выполнения макроса CommentFitter1 показан на рис. 13.5.
Рис. 13.5. Теперь область примечания включает в себя весь его текст
Изменение размера области примечания с помощью
центрирования
Макрос CommentFitter2 также любезно предоставлен Томом Уртисом.
Следующий макрос изменяет размер области примечания путем центрироY
вания текста примечания.
Sub CommentFitter2()
Application.ScreenUpdating = False
Dim 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 Select
Next x
Application.ScreenUpdating = True
End 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 = False
x = "C:\XWMJGraph.gif"
Set z = Worksheets("Диаграмма в примечании").Range("A3")
On Error Resume Next
z.Comment.Delete
On Error GoTo 0
ActiveSheet.ChartObjects("Chart 1").Activate
ActiveChart.Export x
With z.AddComment
With .Shape
.Height = 322
.Width = 465
.Fill.UserPicture x
End With
End With
Range("A1").Activate
Application.ScreenUpdating = True
Set z = Nothing
End 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 Next
iColor = Target.Interior.ColorIndex
'// Оставить действие строки On Error Resume Next
'// в силе на случай возникновения ошибок смещения строк.
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) '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 = iColor
End With
End Sub
Выделение ячейки без применения условного
форматирования
Макрос HighLight и вспомогательные макросы HighlightRight,
HighlightLeft, HighlightUp, HighlightDown, DisableDelete и ReSet также любезно предоставлены Иваном Ф. Моалой.
Следующий макрос выделяет ячейку, которая была сделана активной в реY
зультате использования клавиш управления курсором, без применения условY
ного форматирования.
'//
'//
'//
'//
'//
'//
'//
'//
'//
'//
'//
'//
'//
Альтернативный вариант макроса, выделяющего активную ячейку:
- не использует условное форматирование;
- сохраняет существующее условное форматирование рабочего листа;
- позволяет выполнять операции вырезания,
копирования и вставки данных.
Макрос создан Нейтом Оливером (Nate Oliver) для Альдо (Aldo).
Альдо предложил обработку нажатия клавиши <Del>.
Спасибо, друзья!!
См. дополнительные комментарии в коде.
'// Размещено в стандартном модуле.
Dim strCol As String
Dim iCol As Integer
Dim dblRow As Double
Sub HighlightRight()
HighLight 0, 1
End Sub
Sub HighlightLeft()
HighLight 0, -1
End Sub
Sub HighlightUp()
HighLight -1, 0, -1
End Sub
Sub HighlightDown()
HighLight 1, 0, 1
End Sub
Sub HighLight(dblxRow As Double, iyCol As Integer, _
Excel всемогущий
Optional dblZ As Double = 0)
On Error GoTo NoGo
strCol = Mid(ActiveCell.Offset(dblxRow, iyCol).Address, _
InStr(ActiveCell.Offset(dblxRow, iyCol).Address, "$") + 1, _
InStr(2, ActiveCell.Offset(dblxRow, iyCol).Address, "$") - 2)
iCol = ActiveCell.Column
dblRow = ActiveCell.Row
'// Позволяет избежать мерцания экрана.
Application.ScreenUpdating = False
With Range(strCol & ":" & strCol & "," & dblRow + dblZ & _
":" & dblRow + dblZ)
.Select
'// Сейчас экран следует обновить.
Application.ScreenUpdating = True
.Item(dblRow + dblxRow).Activate
End With
NoGo:
End Sub
Sub DisableDelete()
Cells(ActiveCell.Row, ActiveCell.Column).Select
Application.OnKey "{DEL}"
End Sub
Sub ReSet()
Application.OnKey
Application.OnKey
Application.OnKey
Application.OnKey
End Sub
"{RIGHT}"
"{LEFT}"
"{UP}"
"{DOWN}"
Транспонирование данных
Макрос 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, _
Глава 13 383
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 Sub
terminate:
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 = i
End Function
Выделение и отмена выделения несмежных ячеек
Макросы ModifyRightClick, DeselectActiveCell и DeselectActiveArea любезно предоставлены Томом Уртисом.
Чтобы отменить выделение ячейки или диапазона ячеек на рабочем листе,
нужно щелкнуть на произвольной невыделенной ячейке. После выполнения
этой операции выделение требуемых ячеек необходимо начинать заново, что
весьма проблематично, если речь идет о большом количестве несмежных ячеек.
Excel всемогущий
Глава 13 385
Следующий макрос добавляет в контекстное меню 2 новые команды:
Отменить выделение активной ячейки и Отменить выделение активной
области (рис. 13.8).
Рис. 13.8. Процедура ModifyRightClick создает 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 With
End 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 If
End 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 If
End Sub
Разместите следующий код в модуле ЭтаКнига (ThisWorkbook).
Private Sub Workbook_Deactivate()
Application.CommandBars("Cell").Reset
End Sub
Private Sub Workbook_Activate()
ModifyRightClick
End 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 With
End 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 With
End Sub
Среднее время выполнения макроса Macro1_Version2 более чем на 70%
меньше среднего времени выполнения макроса Macro1, однако это еще далеко
не все. Принимая во внимание низкую скорость обновления объекта PageSetup, воспользуемся выражением If для установки значения только тех паY
раметров страницы, которые этого требуют.
В приведенном ниже макросе вызов функции Application.InchesToPoints был заменен фактическими значениями параметров страницы.
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 With
End 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 St
End 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 = True
End Sub
Запрет/разрешение выполнения операций вырезания,
копирования и вставки
Макросы EnableAllClear и DisAbleAllCLear любезно предоставлены
Иваном Ф. Моалой.
Иногда изменение пользователями содержимого рабочего листа крайне
нежелательно. Следующий код запрещает/разрешает все способы выполнения
операций вырезания, копирования и вставки данных.
Option Private Module
Dim ComBar As CommandBar
Dim ComBarCtrl As CommandBarControl
Sub EnableAllClear()
'// Команда Вставка (Insert)..
EnableControl 295, True '// ..Ячейки (Cells)
EnableControl 296, True '// ..Строки (Rows)
EnableControl 297, True '// ..Столбцы (Columns)
Excel всемогущий
'// Команда...
EnableControl 478, True
EnableControl 292, True
EnableControl 293, True
EnableControl 294, True
EnableControl 847, True
'// Команда...
EnableControl 3125, True
EnableControl 1964, True
EnableControl 872, True
EnableControl 873, True
EnableControl 874, True
'// Команда...
EnableControl
EnableControl
EnableControl
EnableControl
21, True
19, True
22, True
755, True
'//
'//
'//
'//
'//
'//
'//
'//
'//
Глава 13 391
Правка->Удалить (Edit->Delete)
Удалить (Delete), контекстное
меню ячейки
Удалить строки (Delete Rows),
контекстное меню строки
Удалить (Delete), контекстное
меню столбца
Правка->Удалить лист
(Edit->Delete Sheet)
'//
'//
'//
'//
'//
'//
'//
'//
'//
Очистить содержимое (Clear
Contents), контекстное меню
Правка->Очистить->Все (All)
Правка->Очистить->Форматы
(Edit->Clear->Formats)
Правка->Очистить->Содержимое
(Edit->Clear->Contents)
Правка->Очистить->Примечания
(Edit->Clear->Comments)
'//
'//
'//
'//
'//
Вырезать (Cut)
Копировать (Copy)
Вставить (Paste)
Специальная вставка
(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 '//
EnableControl 296, False '//
EnableControl 297, False '//
'// Команда...
EnableControl 478, False '//
EnableControl 292, False '//
'//
EnableControl 293, False '//
'//
EnableControl 294, False '//
'//
EnableControl 847, False '//
'//
'// Команда...
EnableControl 21, False
'//
..Ячейки (Cells)
..Строки (Rows)
..Столбцы (Columns)
Правка->Удалить (Edit->Delete)
Удалить (Delete), контекстное
меню ячейки
Удалить строки (Delete Rows),
контекстное меню строки
Удалить (Delete), контекстное
меню столбца
Правка->Удалить лист
(Edit->Delete Sheet)
Вырезать (Cut)
392 Часть II
Автоматизация Excel
EnableControl 19, False
EnableControl 22, False
EnableControl 755, False
'//
'//
'//
'//
Копировать (Copy)
Вставить (Paste)
Специальная вставка
(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 CommandBar
Dim ComBarCtrl As CommandBarControl
On Error Resume Next
For Each ComBar In Application.CommandBars
Set ComBarCtrl = ComBar.FindControl(Id:=iId, recursive:=True)
If Not ComBarCtrl Is Nothing Then ComBarCtrl.Enabled = blnState
Next
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 nIndex
End 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 Sub
If Application.IsNumber(Target.Value) = False Then
Application.EnableEvents = False
Application.Undo
Application.EnableEvents = True
MsgBox "Введите число."
Exit Sub
End If
Select 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 If
End Select
Dim x As Long
Excel всемогущий
Глава 13 395
x = Target.Row
Dim z As String
z = Range("B" & x).Value - Range("A" & x).Value
With 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 If
End With
End 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 = &HCC
Private Const WH_CBT = 5
Private Const HCBT_ACTIVATE = 5
Private 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
Dim
Dim
Dim
Dim
Dim
TextCaseChange()
RgText As Range
oCell As Range
Ans As String
strTest As String
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 Sub
If InStr(1, "СПКНМ", UCase(Ans), vbTextCompare) = 0 Or _
Len(Ans) > 1 Then GoTo Again
On Error GoTo NoText
If Selection.Count = 1 Then
Set RgText = Selection
Else
Set RgText = Selection.SpecialCells(xlCellTypeConstants, 2)
End If
On 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 Select
Next
Exit Sub
NoText:
MsgBox "Текст в диапазоне " & Selection.Address & " отсутствует"
End Sub
Обработка события удаления строки или столбца
Макросы EventHack и EventReset, а также вспомогательные процедуры
AssignMacro, JudgeRng и DelExecute любезно предоставлены Масару
Каджи (Masaru Kaji).
Excel не предусматривает возможность обработки события удаления строY
ки или столбца. Следующий макрос устраняет этот недостаток: при удалении
Excel всемогущий
Глава 13 399
строки или столбца на экран выводится сообщение, содержащее номер удаY
ленной строки или удаленного столбца.
Sub EventHack()
AssignMacro "JudgeRng"
End Sub
Sub EventReset()
AssignMacro ""
End Sub
Private
Dim
Dim
Dim
Dim
Sub AssignMacro(ByVal strProc As String)
lngId As Long
CtrlCbc As CommandBarControl
CtrlCbcRet As CommandBarControls
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
Next
End 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 With
End Sub
Private Sub DelExecute(ByVal str, ByVal lngDerec As Long)
MsgBox "Удален(а): " & str
Selection.Delete lngDerec
End Sub
Поиск заданного текста с помощью свойства SpecialCells
Макросы CheckAllCells и CheckSpecialCellsOnly любезно предосY
тавлены Иваном Ф. Моалой.
При поиске значения, текста или формулы в заданном диапазоне Excel
проверяет каждую ячейку этого диапазона. Макрос CheckAllCells провоY
400 Часть II
Автоматизация Excel
дит поиск заданного текста в диапазоне A1:Z20000, проверяя каждую его
ячейку. Макрос CheckSpecialCellsOnly проводит поиск заданного текста
в диапазоне A1:Z20000, проверяя только ячейки, содержащие текстовые
константы. По окончании поиска оба макроса выводят сведения о результатах
поиска и затраченном на это времени.
Sub CheckAllCells()
Dim TheRange As Range
Dim 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 Range
Dim 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
Dim
Dim
Dim
Dim
Dim
Delete_rows_with_conditions()
wbBook As Workbook
wsSheet As Worksheet
rnArea As Range
lnLastRow As Long, lnMoreRows As Long
i As Long, j As Long, k As Long
Set wbBook = ThisWorkbook
Set 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 i
End 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 Sub
On Error Resume Next
If Len(Target.Text) > 50 Or Len(Target.Formula) > 50 Then
402 Часть II
Автоматизация Excel
Application.DisplayFormulaBar = False
Else
Application.DisplayFormulaBar = True
End If
End 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 String
Dim HistDate As Date, HighVal As String, LowVal As String
Dim cl As Range
Set cl = ActiveCell
HistDate = cl(, 0)
If Intersect(cl, [c2:c65366]) Is Nothing Then
MsgBox "Выберите ячейку в столбце C."
Exit Sub
End If
If Not CBool(Len(cl(, -1))) Or Not CBool(Len(cl(, 0))) Then
MsgBox "Введите символ акций и требуемую дату."
Exit Sub
End 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
.Quit
End 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)) / 2
Else: lCharPos = InStr(1, sHTML, "Closing Price:", vbTextCompare)
cl.Value = Val(Mid$(sHTML, lCharPos + 14, 15))
End If
Set cl = Nothing
End 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 0
End 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.
Глава 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
14
Извлечение данных из
Internet........................................... 407
Извлечение данных из
Internet в режиме реального
времени.......................................... 413
Анализ данных, извлеченных
из Internet.......................................414
Размещение данных на WebL
странице.........................................418
Следующий шаг........................... 425
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 = False
End 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
После формирования строки подключения макрос удаляет все ранее созY
данные WebYзапросы, выполняет новый WebYзапрос и помещает его результат
на рабочий лист Вспомогательный лист.
Следует отметить, что при выполнении WebYзапроса параметр BackgroundRefresh метода 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
411
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
Анализ данных, извлеченных из Internet
VBA содержит метод 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).Value
End 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:=False
End 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 EarliestTime:=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файл sampledirectory.html. Первым делом макрос копируY
ет в этот файл HTMLYкод, размещенный на рабочем
листе Top.
На следующем этапе макрос WriteMembershipHTML экспортирует сведения из базы данных в
Рис. 14.14. Рабочий лист файл sampledirectory.html.
Bottom содержит деск&
Наконец, макрос WriteMembershipHTML копируY
рипторы, необходимые
ет
в
файл sampledirectory.html HTMLYкод, разY
для завершения HTML&
файла
мещенный на рабочем листе 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
Взаимодействие с 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, vbMinimizedNoFocus
End Sub
Следующий шаг
Следующая глава посвящена передаче данных между приложениями с поY
мощью XML. Использование XML намного эффективнее WebYзапросов. НеY
которые сайты (например, Amazon.com) предлагают такую услугу уже сегодня.
Глава 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
15
Введение в XML ............................427
Правила XML ................................ 428
Универсальный формат
файлов........................................... 429
XML набирает обороты ............. 429
Схемы и сопоставления XML.... 429
Сохранение и считывание
содержимого рабочей книги
Excel в формате XML.................... 431
Следующий шаг........................... 438
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, выведенная на основе содержимого файла TodaysOrders.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
(Data XML XML Source).
команду
Данные XML Источник
XML
2. Щелкните на кнопке Карты XML (XML Maps), расположенной в области задач
Источник XML (XML Source).
3. В открывшемся диалоговом окне Карты XML (XML Maps) щелкните на кнопке
Добавить (Add).
4. Выберите сохраненный ранее файл dev-heavy.xsd и щелкните на кнопке
Открыть (Open).
5. Выберите элемент ProductInfo в списке Выберите корень (Please select a
root) диалогового окна Несколько корней (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 WinHTTP
Services. Щелкните на кнопке 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
Следует отметить, что за счет наличия огромного числа полей карта ProductInfo_Map позволяет легко манипулировать отображаемыми результатами запроса
без необходимости внесения изменений в код VBA. Например, чтобы получить све&
дения о дате выхода книги (ReleaseDate), ее номере в каталоге Amazon.com
(ListID) или средней оценке книги читателями (AvgCustomerRating), перета&
щите на рабочий лист соответствующие поля из области задач Источник XML
(XML Source).
Следующий шаг
Основная задача XML заключается в обеспечении возможности обмена
данными между разнородными приложениями. Программы, входящие в соY
став пакета Microsoft Office, обладают встроенной возможностью обмена данY
ными между собой. Следующая глава посвящена автоматизации управления
текстовым редактором Microsoft Word средствами Excel VBA.
Глава 16
Àâòîìàòèçàöèÿ
Word
Word, Excel, PowerPoint, Outlook и
Access используют один и тот же язык
программирования VBA, отличаясь
между собой только объектной модеY
лью (например, рабочая книга Excel
представлена объектом Workbook, а
документ Word — объектом Document). Каждое из перечисленных
приложений может получить доступ
к объектной модели другого прилоY
жения при условии, что последнее
установлено на компьютере.
Для доступа к объектной библиоY
теке Word из кода Excel VBA на нее
необходимо установить ссылку поY
средством раннего или позднего связы*
вания. Ранее связывание подразумеY
вает создание ссылки на объект приY
ложения во время компиляции
программы, а позднее связывание YYYY
во время ее выполнения.
В этой главе рассматривается досY
туп к объектной модели Word средстY
вами Excel VBA. Чтобы познакомитьY
ся со структурой объектной модели
Word или другого приложения, вхоY
дящего в состав пакета Microsoft OfY
fice, воспользуйтесь диспетчером
объектов редактора Visual Basic соотY
ветствующего приложения.
Раннее связывание
Код, созданный с применением
раннего связывания, выполняется
быстрее, чем код, созданный с приY
менением позднего связывания.
16
Раннее связывание ..................... 439
Позднее связывание................... 442
Работа с объектами .................... 443
Объекты Word.............................. 445
Следующий шаг........................... 460
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 выберет ссылку, встретившуюся первой. Повлиять на выбор той или
иной библиотеки можно путем изменения ее приоритета в списке Available
References.
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 = Nothing
End 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 = Nothing
End Sub
Переменная wdApp содержит ссылку на объект приложения (CreateObject
("Word.Application")) и используется при создании переменной wdDoc,
ссылающейся на объект библиотеки Word.
На заметку
Необходимость использования позднего связывания обусловлена выбором типа
переменных wdApp и wdDoc (Object). Макрос не может создать требуемые ссыл&
ки на объекты библиотеки Word до выполнения функции CreateObject.
Работа с объектами
В следующих разделах рассматривается создание новых объектов, а также
обращение к уже существующим объектам.
Ключевое слово New
Ключевое слово New можно использовать для создания объекта приложеY
ния при раннем связывании (см. макрос WordEarlyBinding выше в этой
главе). Аналогичного результата позволяет добиться также функция CreateObject, а вот функция GetObject предназначена для получения ссылки
на уже существующий объект. Использовать ключевое слово New при позднем
связывании нельзя.
Внимание
Если окно созданного экземпляра Word не отображается на экране, откройте при&
ложение Диспетчер задач Windows (Windows Task Manager) и проверьте наличие
в памяти компьютера процесса WINWORD.EXE. Если процесс запущен, выполните
следующую строку кода с помощью окна Immediate (Быстрое выполнение) ре&
дактора Visual Basic:
Word.Application.Visible = True
444 Часть II
Автоматизация Excel
При наличии в памяти компьютера нескольких экземпляров процесса WINWORD.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 = Nothing
End Sub
Макрос UseGetObject открывает документ внутри существующего экземпY
ляра приложения Word и выводит окно последнего на экран. Поскольку переменY
ная wdDoc ссылается на объект документа, для вывода окна Word на экран исY
пользуется свойство Application объекта Document (wdDoc.Application.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 = Nothing
End Sub
Обработка ошибок отключается с помощью строки On Error Resume
Next. Ошибка возникает при попытке связать переменную 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 объекта Document), слов (свойство 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 = Nothing
End 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 равным wdDoNotSaveChanges:
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 = Nothing
End 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 = Nothing
End 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 = Nothing
End 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 = Nothing
End 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 = Nothing
End Sub
Результат выполнения макроса ChangeStyle показан на рис. 16.8.
Рис. 16.8. Макрос ChangeStyle изменяет стиль абзацев документа с
Обычный на Заголовок 1
Закладки
Закладки предназначены для упрощения навигации по документу Word.
Объект Bookmarks доступен в виде свойства объектов Document, Selection и 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 = Nothing
End 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
.TypeText vbCrLf
.TypeText "Копия: "
Set wdRng = wdApp.Selection.Range
wdApp.ActiveDocument.Bookmarks.Add
.TypeText vbCrLf
.TypeText "От: "
Set wdRng = wdApp.Selection.Range
wdApp.ActiveDocument.Bookmarks.Add
.TypeText vbCrLf
.TypeText "Тема: "
Set wdRng = wdApp.Selection.Range
wdApp.ActiveDocument.Bookmarks.Add
.TypeText vbCrLf
.TypeText "Диаграмма: "
Set wdRng = wdApp.Selection.Range
wdApp.ActiveDocument.Bookmarks.Add
End With
"Кому", wdRng
"Копия", wdRng
"От", wdRng
"Тема", wdRng
"Диаграмма", wdRng
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 = Nothing
End Sub
Результат выполнения макроса CreateMemo показан на рис. 16.11.
Автоматизация Word
Глава 16 457
Рис. 16.11. Закладки можно использовать для помещения в документ Word диаграммы
Практикум
Создание отчетов в Word с помощью
расширенного фильтра
В главе 11, “Анализ данных с помощью расширенного фильтра”, рассматривался
макрос, использующий расширенный фильтр для создания отчетов по каждому
заказчику и сохраняющий их в виде отдельных рабочих книг. Изменим условие
задачи, потребовав сохранения полученных отчетов в виде документов Word. Ни&
же приведена последовательность действий, которые нужно выполнить для дос&
тижения поставленной цели.
1. Создайте новый документ Word. Добавьте в него закладки Заказчик и
Таблица, а также, при необходимости, дополнительный текст, который нужно
поместить в отчет. Сохраните документ в виде шаблона с именем SalesReport.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 = Nothing
End Sub
Результат выполнения
рис. 16.12.
макроса
RunReportForEachCustomer
показан
на
Рис. 16.12. Пример создания отчета в Microsoft Word
Следующий шаг
Следующая глава посвящена многомерным массивам. Один из наиболее
эффективных способов ускорения обработки данных на рабочем листе заклюY
чается в их считывании в многомерный массив, проведении необходимых выY
числений и копировании полученных результатов обратно на рабочий лист.
Часть III
III
Удивительные
возможности Visual Basic
for 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
Глава 17
Ìàññèâû
Массив YYYY это переменная специY
ального типа, которая может хранить
более одного элемента данных. Без
использования массива для запомиY
нания имени и адреса заказчика поY
требуется создать две обычные переY
менные. В то же время, для хранения
целого списка, состоящего из имен и
адресов сотен заказчиков, достаточно
одного массива.
Объявление массива
Чтобы объявить массив, укажите
число его элементов в скобках после
имени переменной, как показано
ниже:
Dim myArray(2)
Приведенный выше код создает
массив myArray, состоящий из трех
элементов. ДаYда, именно из трех,
потому что по умолчанию первый
элемент массива имеет индекс 0:
myArray(0) = 10
myArray(1) = 20
myArray(2) = 30
Чтобы назначить первому элеменY
ту массива индекс 1, воспользуйтесь
выражением Option Base 1. ВыY
ражение Option Base размещается
в разделе объявлений модуля:
Option Base 1
Dim myArray(2)
Теперь массив myArray будет соY
стоять из двух элементов.
При создании массива можно заY
дать как верхнюю, так и нижнюю его
границу:
17
Объявление массива .................. 463
Заполнение массива .................. 464
Манипулирование
элементами массива .................. 466
Еще одно преимущество
массивов.........................................467
Динамические массивы ............ 469
Передача массива в качестве
параметра..................................... 470
Следующий шаг............................ 471
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) = 10
myArray(1, 2) = 20
Аналогичным образом заполняются элементы второй строки массива:
myArray(2, 1) = 20
myArray(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 With
End Sub
Результат выполнения макроса ColumnHeaders показан на рис. 17.1.
Обратите внимание, что в приведенном выше коде для создания массива
была использована переменная myArray типа Variant, которая может храY
нить информацию любого рода. Переменная myArray принимает свойства
массива после присвоения ей соответствующего значения:
myArray = Array("Имя", "Адрес", "Телефон", "Адрес эл. почты")
Ниже приведен пример заполнения массива данными, взятыми с рабочего
листа Excel (см. рис. 17.2):
Dim myArray As Variant
myArray = 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 myCount
End 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 myCount
End 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 myCount
End 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 = arNames
End Sub
Внимание
При работе с большими объемами данных использование ключевого слова Preserve внутри цикла приводит к замедлению выполнения программного кода. Ес&
ли это возможно, постарайтесь определить размер динамического массива до
вхождения в цикл.
Передача массива в качестве параметра
Массив можно передавать в качестве параметра, подобно обычной переY
менной. Это повышает эффективность и читабельность кода. Следующий
макрос передает массив myArray в качестве параметра функции RegionSales, которая подсчитывает сумму элементов массива и возвращает полу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 myCount
End Function
Результат выполнения макроса PassAnArray показан на рис. 17.7.
Рис. 17.7. Массив можно передавать в качестве
параметра функции или процедуре
Следующий шаг
Массив YYYY это переменная специального типа, которая может хранить неY
сколько элементов данных. Основное предназначение массива заключается в
упрощении обработки данных и повышении скорости выполнения проY
граммного кода. Следующая глава посвящена работе с текстовыми файлами.
Экспортирование данных в текстовый файл может понадобиться для передачи
информации в другую систему или создания HTMLYфайла.
Глава 18
Ðàáîòà
ñ òåêñòîâûìè
ôàéëàìè
Прежде чем формат XML окончаY
тельно утвердится в качестве станY
дартного формата файлов, некоторое
время наряду с форматом XML шиY
рокое распространение будут иметь
форматы CSV и TXT.
В этой главе рассматривается имY
порт и экспорт текстовых файлов поY
средством VBA. Экспорт данных в
текстовый файл может понадобиться
для передачи информации в другую
систему или создания HTMLYфайла.
Импорт данных из
текстового файла
Рассмотрим три сценария имY
порта данных из текстовых файлов.
Если в файле содержится менее
65 536 записей, его можно импорY
тировать с помощью метода Workbooks.OpenText. Если в файле
содержится более 65 536 записей, но
менее 98 304 записей, его можно имY
портировать с помощью двух вызовов
метода Workbooks.OpenText. Если
же в файле содержится более
98 304 записей, его придется имY
портировать путем последовательY
ного считывания строк.
18
Импорт данных из текстового
файла ..............................................473
Экспорт данных в текстовый
файл ............................................... 486
Следующий шаг........................... 487
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 создаст код, вызывающий метод Workbooks.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 If
End Sub
Импорт текстового файла построчно
Изменим условие задачи, увеличив число розничных магазинов до 50. При
этом максимальный размер импортируемого файла возрастет до 100 000 записей и
его придется загружать построчно.
Импорт текстового файла, содержащего менее 65 536 записей, построчно
Откройте файл для чтения с помощью выражения Input As #1. Чтобы
считать строку файла в переменную, воспользуйтесь выражением Line Input #1, имя_переменной. Следующий макрос открывает файл inventory.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 #1
End 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("Данные ").Select
End 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
печения быстрого доступа к данным, предусматривающего индексацию и
многопользовательский режим.
Глава 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
ловного форматирования;
вставка и изменение рисунков
и других объектов;
19
ADO и DAO .................................... 490
Объекты ADO ............................... 492
Добавление записи в
таблицу Access ............................. 493
Извлечение записей из
таблицы Access ............................ 494
Обновление записей таблицы
Access ............................................. 496
Удаление записей таблицы
Access ............................................. 499
Создание итоговых
запросов ........................................ 499
Несколько полезных
макросов ....................................... 500
Следующий шаг........................... 503
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 являются объект соединения Connection и объект набора данных Recordset.
Объект соединения определяет путь к базе данных и ее тип. База данных
Microsoft Access основана на ядре Microsoft Jet.
Объект набора данных создается на основе существующего объекта соедиY
нения для представления таблицы, некоторого ее подмножества или предоY
пределенного запроса базы данных Access. Помимо объекта соединения, при
создании объекта набора данных указываются такие параметры, как CursorType, CursorLocation, LockType и Options. Для обеспечения одноY
временного доступа к базе данных двух пользователей рекомендуется примеY
нять динамический курсор и оптимистическую блокировку. При работе с
большими объемами данных установите значение параметра CursorLocation равным adUseServer, что позволит обрабатывать записи в оперативY
ной памяти сервера. В противном случае установите значение параметра CursorLocation равным 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.Close
End 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
Количество, Дата 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
Глава 19 495
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. Метод инициализации формы UserForm_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 If
End 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 With
End 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.Close
End 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.Close
End Function
Проверка существования поля в таблице
базы данных Access
В одном из следующих разделов рассматривается добавление нового поля в
таблицу базы данных Access. Функция ColumnExists проверяет существоваY
ние поля с указанным именем в заданной таблице базы данных Transfers.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.Close
End 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 Sub
End 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 = Nothing
End Sub
Следующий шаг
Следующая глава посвящена созданию модулей классов, предназначенных
для размещения пользовательских объектов.
Глава 20
Ñîçäàíèå
ïîëüçîâàòåëüñêèõ îáúåêòîâ,
òèïîâ
è êîëëåêöèé
Несмотря на все разнообразие
встроенных объектов Excel, в некоY
торых ситуациях наиболее оптиY
мальным решением является создаY
ние собственного объекта, преднаY
значенного для выполнения узкоY
специализированной задачи. Excel
поддерживает создание пользоваY
тельских объектов в так называеY
мых модулях классов.
Ранее в этой книге рассматриваY
лись ‘‘улучшенные’’ модули классов,
такие как модуль рабочей книги, раY
бочего листа, диаграммы и пользоваY
тельской формы. Каждому из этих
модулей свойственны характеристиY
ки, уникальные для соответствуюY
щего типа объектов. В частности, это
касается обработчиков событий.
Кроме размещения пользовательY
ских объектов с соответствующими
свойствами и методами, классы моY
дулей могут включать в себя код обY
работчиков событий уровня прилоY
жения, встроенной диаграммы, элеY
мента управления ActiveX и др.
20
Создание модуля класса ........... 506
Обработка событий уровня
приложения и встроенной
диаграммы ................................... 506
Создание пользовательского
объекта ........................................... 510
Применение
пользовательского объекта
на практике ....................................511
Использование выражений
Property Let и Property Get.......... 513
Коллекции ..................................... 515
Создание пользовательских
типов .............................................. 520
Следующий шаг........................... 524
506 Часть III
Удивительные возможности Visual Basic for Applications
Создание модуля класса
Чтобы добавить модуль класса к текущему проекту, выберите в меню реY
дактора Visual Basic команду Insert Class Module (Вставить Модуль класса).
Excel добавит новый модуль класса Class1, разместив его в папке Class
Modules (Модули классов) текущего проекта, как показано на рис. 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 следует поместить в модуль класса clsAppEvents после строки, содержащей объявление переменной 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 With
End Sub
В отличие от обработчиков событий уровня рабочей книги или рабочего
листа, процедура, размещенная в модуле класса, не вызывается автоматичеY
ски. Создайте экземпляр класса clsAppEvents и установите значение его
свойства xlApp равным Application, как показано ниже.
Public myAppEvent As clsAppEvents
Sub TrapAppEvent()
Set myAppEvent.xlApp = Application
End Sub
После выполнения процедуры TrapAppEvent обработчик xlApp_NewWorkbook будет формировать нижний колонтитул для каждой новой рабочей
книги, созданной в рамках текущего сеанса работы с 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 BeforeRightClick и BeforeDoubleClick.
Следующий код предотвращает выполнение стандартных действий, предY
принимаемых при обработке двойного щелчка и щелчка правой кнопкой мыши:
Private Sub xlChart_BeforeDoubleClick(ByVal ElementID As Long, _
ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean)
Cancel = True
End Sub
Private Sub xlChart_BeforeRightClick(Cancel As Boolean)
Cancel = True
End 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 If
End Sub
Создайте экземпляр класса clsChartEvents и установите значение его
свойства xlChart, как показано ниже (рис. 20.5).
Public myChartEvent As clsChartEvents
Sub TrapChartEvent()
Set myChartEvent.xlChart = Worksheets("Встроенная _
диаграмма").ChartObjects("Chart 1").Chart
End Sub
Создание пользовательского объекта
Кроме кода обработчиков событий, модули классов могут содержать польY
зовательские объекты с определением соответствующих свойств и методов.
Создадим объект служащего с такими характеристиками, как имя, идентифиY
катор, тарифная ставка и количество рабочих часов.
Добавьте модуль класса и переименуйте его в clsEmployee. Ниже переY
числены свойства объекта clsEmployee.
EmpName — имя служащего;
EmpID — идентификатор служащего;
EmpRate — тарифная ставка;
EmpWeeklyHrs — количество рабочих часов в неделю.
Создание пользовательских объектов, типов и коллекций
Глава 20
Рис. 20.5. Обработка событий встроенной диаграммы реализуется посредством использова&
ния модуля класса
При объявлении свойств и переменных указывается область видимости YYYY
обычно Private или Public. Поскольку свойства объекта clsEmployee
должны быть доступны из стандартного модуля, сделайте их общими, размесY
тив следующие строки в начале модуля класса:
Public
Public
Public
Public
EmpName As String
EmpID As String
EmpRate As Double
EmpWeeklyHrs As Double
Методы объекта YYYY это ‘‘действия’’, которые он может выполнять. В модуY
ле класса ‘‘действия’’ объекта представлены процедурами и функциями. СлеY
дующий метод подсчитывает недельную заработную плату служащего:
Public Function EmpWeeklyPay() As Double
EmpWeeklyPay = EmpRate*EmpWeeklyHrs
End Function
Создание объекта clsEmployee, содержащего четыре свойства и один меY
тод, можно считать завершенным. В следующем разделе рассматривается
применение пользовательского объекта на практике.
Применение пользовательского объекта
на практике
Пользовательский объект, корректно заданный в модуле класса, становитY
ся доступным для других модулей. Ниже приведен пример объявления переY
менной типа clsEmployee:
Dim Employee As clsEmployee
Следующий код создает экземпляр пользовательского объекта:
Set Employee = New clsEmployee
511
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 создает экземпляр пользовательского объекта clsEmployee и задает значения всех его свойств. После этого макрос выводит на
экран сообщение, содержащее сведения о недельной заработной плате слуY
жащего, как показано на рис. 20.7.
Создание пользовательских объектов, типов и коллекций
Глава 20 513
Рис. 20.7. При подсчете недельной за&
работной платы служащего использует&
ся метод EmpWeeklyPay объекта clsEmployee
Использование выражений Property Let
и Property Get
Значения общедоступных переменных (таких как EmpName, EmpID, EmpRate и 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 String
Public EmpID As String
Public EmpRate As Double
Private NormalHrs As Double
Private OverHrs As Double
Выражение Property Let позволяет использовать свойство EmpWeeklyHrs для установки значения закрытых переменных 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 + OverHrs
End Property
Выражение Property Get используется для определения результатов
считывания значений свойств EmpNormalHrs и EmpOverTimeHrs, как покаY
зано ниже:
Property Get EmpNormalHrs() As Double
EmpNormalHrs = NormalHrs
End Property
Property Get EmpOverTimeHrs() As Double
EmpOverTimeHrs = OverHrs
End 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) & _
"Заработная плата за неделю (долларов): " & .EmpWeeklyPay
End With
End Sub
Результат выполнения макроса EmpPayOverTime показан на рис. 20.8.
Создание пользовательских объектов, типов и коллекций
Глава 20 515
Рис. 20.8. Выражения Property Let и
Property Get позволяют ограничить
доступ к свойствам пользовательского
объекта
Коллекции
Коллекция предназначена для представления нескольких экземпляров
объекта. Например, объект Worksheet входит в коллекцию Worksheets.
Коллекция позволяет обращаться к любому ее элементу, подсчитывать общее
количество элементов в коллекции, а также удалять и добавлять элементы.
Аналогичная функциональность доступна для пользовательских объектов.
Существует два способа создания коллекции: в стандартном модуле и в моY
дуле класса.
Создание коллекции в стандартном модуле
Самый простой способ создания коллекции заключается в ее размещении
в стандартном модуле и использовании встроенного объекта VBA Collection. Это позволяет получить доступ к четырем основным методам и свойст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.EmpID
End Sub
Свойство Count возвращает общее число элементов в коллекции.
Public Property Get Count() As Long
Count = AllEmployees.Count
End Property
Свойство Items используется для обращения ко всей коллекции.
Public Property Get Items() As Collection
Set Items = AllEmployees
End 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 myItem
End Sub
При необходимости функциональность основных методов коллекции
можно улучшить, например, реализовать автоматическое создание уникальY
ного ключа записи в методе Add.
Предназначенные только для чтения свойства Count, Items и Item опреY
деляются с помощью выражения Property Get. Свойство Item позволяет
обратиться к одному элементу коллекции, а свойство Items — ко всей колY
лекции целиком (в частности, это свойство удобно применять в цикле For
Each...Next).
Следующий макрос аналогичен рассмотренному ранее макросу EmpPayCollection за исключением того, что теперь коллекция пользовательских объектов
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").EmpWeeklyPay
End Sub
Обратите внимание, что переменная colEmployees имеет тип clsEmployees, а не тип 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 Me
End 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 If
End Sub
Разместите в модуле ЭтаКнига (ThisWorkbook) следующий код:
Dim col As Collection
Private
Dim
Dim
Dim
Sub DefineLabels()
WS As Worksheet
cLbl As clsLabel
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 WS
End Sub
Выполните процедуру DefineLabels для создания коллекции кнопок получения
справочной информации. Результат щелчка на одной из таких кнопок показан на
рис. 20.13.
Рис. 20.13. Результат щелчка на кнопке получения справочной информации
Создание пользовательских типов
В отличие от пользовательских объектов, пользовательские типы не требуY
ют своего размещения в модуле класса. Модуль класса позволяет определить
свойства и методы объекта, тогда как пользовательский тип характеризуется
только свойствами.
Создание пользовательских объектов, типов и коллекций
Глава 20 521
Пользовательский тип объявляется с помощью выражения Type...End
Type и может быть общедоступным (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 Long
End Type
Тип Store имеет два свойства YYYY имя магазина и массив товаров, каждый
из которых представлен переменной типа Style.
Public Type Store
Name As String
Styles() As Style
End 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 ThisStore
End Sub
Результат выполнения макроса UDTMain показан на рис. 20.15.
Рис. 20.15. Пользовательские типы позволяют упростить разработку
достаточно сложного программного кода. (Примечание: для наглядно&
сти результаты выполнения макроса UDTMain совмещены с исходны&
ми данными.)
524 Часть III
Удивительные возможности Visual Basic for Applications
Следующий шаг
Следующая глава посвящена пользовательским формам. В ней описываютY
ся различные элементы управления, а также приемы программирования польY
зовательских форм.
Глава 21
Ïîëüçîâàòåëüñêèå ôîðìû —
ïðîôåññèîíàëüíûé ïîäõîä
В главе 9, ‘‘Введение в пользоваY
тельские формы’’, рассматривались
основы создания пользовательских
форм. Продолжим знакомство с польY
зовательскими формами, уделив
особое внимание сложным элеменY
там управления, а также различным
приемам программирования польY
зовательских форм.
Панель инструментов
UserForm
Чтобы отобразить панель инструY
ментов UserForm, выберите в меню
редактора Visual Basic команду View
Toolbars UserForm (Вид Панели
инструментов UserForm).
Панель
инструментов UserForm содержит неY
сколько элементов управления, как поY
казано на рис. 21.1.
На передний план
Группировать
Выравнивание
Уравнять размеры
Масштаб
Центрирование
Отменить группирование
На задний план
Рис. 21.1. Панель инструментов UserForm
21
Панель инструментов
UserForm .........................................525
Создание коллекций
элементов управления
формы ........................................... 526
Дополнительные элементы
управления формы..................... 528
Немодальные формы ................. 531
Гиперссылки в формах ............... 531
Добавление элементов
управления на форму
во время выполнения
программного кода.....................532
Использование полосы
прокрутки для выбора
значений ....................................... 539
Добавление подсказки к
элементу управления.................. 541
Порядок переноса фокуса.......... 541
Изменение цвета фона
активного элемента
управления ................................... 542
Использование эффекта
прозрачности формы................. 545
Следующий шаг........................... 546
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 = True
End Sub
Public Sub UnselectAll()
Пользовательские формы — профессиональный подход
Глава 21 527
chb.Value = False
End 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 ctl
End Sub
Следующий код выделяет все флажки коллекции при щелчке на надписи
Выделить все.
Private Sub lbl_SelectAll_Click()
For Each clFormCtl In col_Selection
clFormCtl.SelectAll
Next clFormCtl
End Sub
Процедура lbl_UnselectAll_Click, выполняющаяся при щелчке на
кнопке Отменить выделение, отменяет выделение флажков коллекции.
Private Sub lbl_UnselectAll_Click()
For Each clFormCtl In col_Selection
clFormCtl.UnselectAll
Next clFormCtl
End Sub
Пользовательская форма frmSelectAll показана на рис. 21.2.
Рис. 21.2. Использование коллекций и панелей упро&
щает работу с элементами управления формы
528 Часть III
Удивительные возможности Visual Basic for Applications
На заметку
Объединение флажков в коллекцию никоим образом не сказывается на их функ&
циональности. Другими словами, флажки формы frmSelectAll по&прежнему
можно устанавливать/сбрасывать по отдельности.
На заметку
Каждый элемент управления формы имеет свойство Tag типа String, содержа&
щее дополнительную информацию об элементе управления. Это свойство можно
использовать для неформального группирования элементов управления, входя&
щих в другие группы.
Дополнительные элементы управления формы
В этом разделе продолжается рассмотрение элементов управления пользоY
вательской формы, начатое в главе 9, ‘‘Введение в пользовательские формы’’.
Переключатели
В отличие от флажков, переключатели поддерживают возможность выбора
только одного элемента из группы, как показано на рис. 21.3.
Рис. 21.3. Переключатели поддерживают возможность
выбора только одного элемента из группы
Возможность выбора только одного элемента из группы может быть реалиY
зована с помощью программного кода, однако переключатели изначально
поддерживают такую функциональность благодаря наличию свойства GroupName. Все переключатели с одинаковым значением свойства 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 1
End Sub
Процедура TabStrip1_Change выполняется при выборе новой вкладки,
что приводит к автоматическому изменению значений элементов управления
вкладки в соответствии с ее порядковым номером.
Private Sub TabStrip1_Change()
Dim lngRow As Long
lngRow = TabStrip1.Value + 1
SetValuesToTabStrip lngRow
End 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).Value
End Sub
Совет
Добавить дополнительный элемент управления к вкладке, входящей в набор,
можно только программным путем в момент активизации этой вкладки.
Поле ввода адреса диапазона ячеек
Поле ввода адреса диапазона ячеек можно разместить на любой пользоваY
тельской форме. Щелчок на кнопке, расположенной справа от поля ввода,
приводит к скрытию формы и отображению окна ввода адреса диапазона ячеY
ек, знакомого большинству пользователей по многочисленным мастерам ExY
cel. Чтобы вернуть форму на экран, щелкните на кнопке с изображением
формы, расположенной в правой части окна.
Следующая процедура форматирует содержимое диапазона ячеек, адрес
которого был задан с помощью поля ввода, изображенного на рис. 21.5, путем
утолщения шрифта.
Private Sub cb1_Click()
Range(RefEdit1.Value).Font.Bold = True
End 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, SWNormal
End 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, SWNormal
End 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
определяет тип элемента управления.
Приведенный ниже код добавляет на форму надпись. Переменная PicCount используется для присвоения элементу управления уникального имеY
ни. Свойства надписи Top и Left определяют ее положение на форме, а
свойства Height и Width — размер надписи.
LC = "LabelA" & PicCount
Me.Controls.Add bstrProgId:="forms.label.1", Name:=LC, Visible:=True
Me.Controls(LC).Top = 25
Me.Controls(LC).Left = 50
Me.Controls(LC).Height = 18
Me.Controls(LC).Width = 60
Me.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" & 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
Загрузив изображение, определите его размер и ориентацию с помощью
свойств Height и Width:
' Определить размер изображения.
Wid = Me.Controls(TC).Width
Ht = Me.Controls(TC).Height
WidRedux = CellWid / Wid
HtRedux = CellHt / Ht
Пользовательские формы — профессиональный подход
Глава 21 537
If WidRedux < HtRedux Then
Redux = WidRedux
Else
Redux = HtRedux
End If
NewHt = Int(Ht * Redux)
NewWid = Int(Wid * Redux)
Определив исходный размер изображения, установите значение свойства
AutoSize равным False и задайте требуемые значения свойств Height и
Width.
' Изменить размер изображения.
Me.Controls(TC).AutoSize = False
Me.Controls(TC).Height = NewHt
Me.Controls(TC).Width = NewWid
Me.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
Repaint
End 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.Value
End Sub
Обработчик события ScrollBar1_Change выполняется при щелчке на
одной из кнопок полосы прокрутки.
Private Sub ScrollBar1_Change()
' Событие ScrollBar1_Change срабатывает
' при щелчке на одной из кнопок полосы прокрутки.
Me.Label1.Caption = Me.ScrollBar1.Value
End Sub
Обработчик события ScrollBar1_Scroll выполняется при щелчке на
ползунке полосы прокрутки.
Private Sub ScrollBar1_Scroll()
' Событие ScrollBar1_Scroll срабатывает
' при щелчке на ползунке полосы прокрутки.
Me.Label1.Caption = Me.ScrollBar1.Value
End Sub
Обработчик события CommandButton1_Click выполняется при щелчке
на кнопке OK. Следующий код копирует значение полосы прокрутки в ячейку
A1 рабочего листа Excel и закрывает пользовательскую форму.
Private Sub CommandButton1_Click()
Range("A1").Value = Me.ScrollBar1.Value
Unload Me
End 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 With
Terminate:
End Sub
Ниже приведен код, который необходимо поместить в модуль формы.
Private WithEvents objForm As clsCtlColor
Private Sub UserForm_Initialize()
Set objForm = New clsCtlColor
End Sub
Процедура UserForm_Activate изменяет цвет фона (свойство BackColor) активного элемента управления при отображении формы на экране.
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 Me
End Sub
Обработчик события objForm_GetFocus изменяет цвет фона элемента
управления, на который был перенесен фокус.
Private Sub objForm_GetFocus()
ActiveControl.BackColor = &HC0E0FF
End Sub
Обработчик события objForm_LostFocus изменяет цвет фона элемента
управления, с которого был перенесен фокус.
Private Sub objForm_LostFocus(ByVal strCtrl As String)
Controls(strCtrl).BackColor = &HFFFFFF
End Sub
Процедура UserForm_QueryClose удаляет объект objForm из памяти
при закрытии формы.
Private Sub UserForm_QueryClose(Cancel As Integer, _
CloseMode As Integer)
Set objForm = Nothing
End 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_StoreName
End Sub
Пользовательские формы — профессиональный подход
Глава 21 545
Рис. 21.17. Свойство списка ColumnWidth позволяет скрыть столбцы,
не предназначенные для отображения на экране
На рис. 21.18 показан результат решения поставленной задачи с помощью списка с
несколькими столбцами.
Рис. 21.18. Список с несколькими
столбцами позволяет отображать
на экране один столбец, а возвращать
в качестве результата значение друго&
го столбца
Использование эффекта прозрачности формы
Как показано на рис. 21.19, эффект прозрачности формы позволяет видеть
содержимое расположенного под ней рабочего листа Excel.
Разместите следующий код в начале модуля формы:
Private Declare Function GetActiveWindow Lib "USER32" () As Long
Private Declare Function SetWindowLong Lib "USER32" Alias _
"SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "USER32" Alias _
"GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) _
As Long
Private 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
Private
Private
Private
Private
Удивительные возможности Visual Basic for Applications
Const
Const
Const
Const
WS_EX_LAYERED = &H80000
LWA_COLORKEY = &H1
LWA_ALPHA = &H2
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_ALPHA
End Sub
Следующий шаг
Следующая глава посвящена основам использования функций интерфейса
прикладного программирования (API) Windows.
Ãëàâà 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
сколько полезных примеров.
22
Знакомство с Windows API.........547
Объявления Windows API.......... 548
Использование объявлений
Windows API ................................. 548
Примеры полезных
объявлений Windows API.......... 549
Дополнительные источники
объявлений Windows API.......... 559
Следующий шаг........................... 559
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. Объявление функции GetUserName. Имя функции может быть произвольным.
Lib "advapi32.dll". Функция API находится в библиотеке advapi32.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 Select
End 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 API
GetUserName позволяет узнать имя
пользователя компьютера и опреде&
лить, имеет ли пользователь права
администратора
Рис. 22.2. Объявление Windows
API GetComputerName позволяет
узнать имя компьютера
Проверка возможности доступа к файлу
Объявления Windows API lOpen и lClose позволяют узнать, открыт или
закрыт заданный файл.
Private Declare Function lOpen Lib "kernel32" Alias "_lopen" _
(ByVal lpPathName As String, ByVal iReadWrite As Long) As Long
Private 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 If
End 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 Select
End 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 Select
End Sub
Макрос CheckDisplayRes проверяет текущее разрешение экрана и при
необходимости рекомендует его увеличить, как показано на рис. 22.4.
Рис. 22.3. Объявления Windows API
lOpen и lClose могут быть ис&
пользованы для того, чтобы узнать,
открыт ли заданный файл
Рис. 22.4. Объявление Windows API DisplaySize позволяет определить разрешение экрана
компьютера
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 Long
Private Declare Function GetSystemMenu Lib "user32" _
(ByVal hwnd As Long, ByVal bRevert As Long) As Long
Private 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 Long
Private Declare Function GetSystemMenu Lib "user32" _
Интерфейс прикладного программирования (API) Windows
Глава 22 553
(ByVal hwnd As Long, ByVal bRevert As Long) As Long
Private 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 Long
Private Declare Function KillTimer Lib "user32" _
(ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private 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 = Empty
End 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 SetTimer, 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, SWNormal
End Sub
Private Sub lbl_Website_Click()
Dim lngRow As Long
ShellExecute 0&, "open", lbl_Website.Caption, _
vbNullString, vbNullString, SWNormal
End Sub
Интерфейс прикладного программирования (API) Windows
Глава 22 555
Щелчок на надписи Consult@MrExcel.com приводит к открытию программы,
предназначенной для работы с электронной почтой, а щелчок на надписи
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, 0
End 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 String
End Type
Declare Function aht_apiGetOpenFileName Lib "comdlg32.dll" _
Alias "GetOpenFileNameA" (OFN As tagOPENFILENAME) As Boolean
Declare Function aht_apiGetSaveFileName Lib "comdlg32.dll" _
Alias "GetSaveFileNameA" (OFN As tagOPENFILENAME) As Boolean
Declare Function CommDlgExtendedError Lib "comdlg32.dll" () As Long
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Global
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
ahtOFN_READONLY = &H1
ahtOFN_OVERWRITEPROMPT = &H2
ahtOFN_HIDEREADONLY = &H4
ahtOFN_NOCHANGEDIR = &H8
ahtOFN_SHOWHELP = &H10
ahtOFN_NOVALIDATE = &H100
ahtOFN_ALLOWMULTISELECT = &H200
ahtOFN_EXTENSIONDIFFERENT = &H400
ahtOFN_PATHMUSTEXIST = &H800
ahtOFN_FILEMUSTEXIST = &H1000
ahtOFN_CREATEPROMPT = &H2000
ahtOFN_SHAREAWARE = &H4000
ahtOFN_NOREADONLYRETURN = &H8000
ahtOFN_NOTESTFILECREATE = &H10000
ahtOFN_NONETWORKBUTTON = &H20000
ahtOFN_NOLONGNAMES = &H40000
ahtOFN_EXPLORER = &H80000
ahtOFN_NODEREFERENCELINKS = &H100000
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
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
Эта функция вызывает стандартное диалоговое окно
выбора файла. Все перечисленные выше параметры
являются необязательными.
Входящие параметры:
- 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
Глава 22 557
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 If
End 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 & vbNullChar
End 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 If
End 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 If
End 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).
Следующий шаг
Следующая глава посвящена обработке ошибок.
Глава 23
Îáðàáîòêà îøèáîê
Сколько бы усилий ни было заY
трачено на тестирование и отладку
программного кода, рано или поздно
он даст сбой. Единственная защита
от непредвиденных ситуаций заклюY
чается в их планировании.
Отладка кода
с помощью
редактора VBA
Обнаружив необработанную ошибY
ку в программном коде, редактор VBA
отображает диалоговое окно, пример
которого показан на рис. 23.1.
Щелкните на кнопке Debug
(Отладка). Редактор VBA выделяет
строку кода, выполнение которой
привело к возникновению ошибки,
желтым цветом, как показано на
рис. 23.2.
Рис. 23.1. Результат обнаружения необрабо&
танной ошибки в незащищенном модуле
23
Отладка кода с помощью
редактора VBA.............................. 561
Обработка ошибок
с помощью выражения
On Error GoTo ................................ 564
Универсальные
обработчики ошибок ................. 566
Общение с заказчиками............ 569
“Отложенные” ошибки ............. 569
Несовершенство защиты
проекта VBA ..................................572
Защита проекта VBA в
различных версиях Excel............573
Совместимость различных
версий Excel ...................................573
Следующий шаг............................574
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 Next
End Sub
Использование нескольких обработчиков ошибок
В коде макроса можно разместить несколько различных обработчиков
ошибок. Единственным требованием при этом является наличие строки Resume Next или Exit Sub в конце кода каждого обработчика.
566 Часть III
Удивительные возможности Visual Basic for Applications
Универсальные обработчики ошибок
Некоторые программисты отдают предпочтение универсальному обработY
чику ошибок, позволяющему скрыть от пользователей сообщение об ошибке,
генерируемое редактором VBA. Как правило, универсальный обработчик
ошибок обращается к свойствам объекта Err, таким как номер ошибки и ее
описание.
Sub GenericHandler()
On Error GoTo HandleAny
Sheets(9).Select
Exit Sub
HandleAny:
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 Resume
Next. Также не забывайте размещать строку On Error GoTo 0 сразу же поY
сле строки кода, выполнение которой может привести к возникновению
ошибки.
Обработка ошибок
Глава 23 567
Попытка проигнорировать критическую ошибку приводит к завершению
выполнения макроса. Если макрос А вызывает макрос Б и выполнение поY
следнего завершается аварийно, выполнение кода продолжается со строки
макроса А, следующей за строкой вызова макроса Б. Это чревато непредскаY
зуемыми последствиями.
Игнорирование ошибок задания параметров печати страницы
Запишите макрос, задающий параметры печати страницы. Установка всего
лишь одного значения в диалоговом окне Параметры страницы (Page Setup)
приводит к созданию множества строк кода. Проблема заключается в том, что
параметры печати страницы отличаются от принтера к принтеру. Например,
при создании макроса на компьютере с принтером, поддерживающим цветY
ную печать, средство записи макросов сгенерирует строку .BlackAndWhite = False. Выполнение этой строки на компьютере с принтером, поддерY
живающим только черноYбелую печать, приведет к возникновению ошибки.
Еще одной причиной сбоя может стать установка недопустимых значений паY
раметров, например, параметра, определяющего качество печати. Если макY
рос был создан на компьютере с принтером, поддерживающим разрешение
600 точек на дюйм, средство записи макросов сгенерирует строку
.PrintQuality = 600. Выполнение этой строки на компьютере с принтеY
ром, поддерживающим максимальное разрешение 300 точек на дюйм, привеY
дет к сбою. В данной ситуации рекомендуется разместить выражение On Error Resume Next перед фрагментом кода, устанавливающим параметры
печати страницы, а выражение On Error GoTo 0 — после него.
On Error Resume Next
With ActiveSheet.PageSetup
.PrintTitleRows = ""
.PrintTitleColumns = ""
End With
ActiveSheet.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 = xlPrintErrorsDisplayed
End With
On 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, воспользуйтесь выражением Application.DisplayAlerts = False.
Sub DeleteSheet()
Application.DisplayAlerts = False
Worksheets("Лист2").Delete
Application.DisplayAlerts = True
End 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 листов, тело цикла For
Each...Next будет выполнено 128 раз при условии, что листа с именем
Данные в книге нет.
Альтернативный подход заключается в попытке обращения к листу
Данные. Проигнорируйте обработку ошибок с помощью выражения On Error 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 0
End 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").Value
End 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").Value
End 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 = True
End Sub
Через некоторое время, прошедшее с начала эксплуатации программного
кода, заказчик сообщает об ошибке времени выполнения 1004 (рис. 23.9).
Рис. 23.9. Ошибка времени выполнения 1004
может быть вызвана 1001 причиной
Подобная ошибка может возникнуть при условии, что импортированный
текстовый файл не содержал данных. В этом случае значение переменной TotalRow будет равно 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 If
End 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 является параметр TrailingMinusNumbers метода 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
ны различные способы обработки ошибок, отладка кода и другие вопросы,
связанные с возникновением непредвиденных ситуаций.
Следующая глава посвящена созданию пользовательских меню и панелей
инструментов.
Глава 24
Ñîçäàíèå
ïîëüçîâàòåëüñêèõ
ìåíþ è ïàíåëåé
èíñòðóìåíòîâ
В этой главе рассматривается созY
дание пользовательских меню и паY
нелей инструментов, предназначенY
ных для запуска макросов. В конце
главы будут описаны способы запусY
ка макросов с помощью сочетания
клавиш, кнопки и элемента управлеY
ния ActiveX.
Создание
пользовательского
меню
Пользовательское меню прекрасY
но подходит для размещения команд
запуска макросов, созданных для заY
казчика. На рис. 24.1 показано польY
зовательское меню, разработанное
для вымышленной компании XYZ.
Создание и удаление
пользовательского меню
Строка главного меню Excel предY
ставлена объектом VBA Application.CommandBars(1) и включает
такие меню, как Файл (File), Правка
(Edit), Вид (View) и т.д. Чтобы добаY
вить к главному меню новый пункт,
воспользуйтесь методом Add. При
добавлении нового пункта меню неY
обходимо указать его имя и располоY
жение.
24
Создание пользовательского
меню ...............................................575
Создание пользовательской
панели инструментов ................. 581
Другие способы запуска
макросов ........................................587
Следующий шаг........................... 592
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
вый пункт меню перед пунктом Справка, установите значение параметра Before метода Add равным 10. Чтобы добавить новый пункт меню после пункта
Справка, установите значение параметра Before метода Add равным 11. ДоY
бавление нового пункта меню после пункта Справка в Excel для Macintosh
приведет к возникновению ошибки, поскольку в MacintoshYприложениях
пункт Справка всегда должен оставаться последним пунктом меню.
Рекомендуется предусмотреть процедуры создания и удаления меню. ПроY
цедуру создания меню следует вызвать в обработчике события Workbook_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 0
End 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
Добавление команд меню
Чтобы добавить команду меню, воспользуйтесь методом MenuObject.Controls.Add, установив значение параметра Type равным msoControlButton. MenuObject — это созданный ранее объект меню типа CommandBarPopup. Объект команды меню имеет свойство 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 = True
End Sub
Создание подменю
Подменю позволяет сократить число команд меню путем вынесения неY
скольких команд в отдельный пункт. На рис. 24.4 показано подменю Отчет,
содержащее команды для создания всевозможных отчетов.
Рис. 24.4. Как правило, подменю используется для ло&
гического группирования команд меню
Чтобы создать подменю, воспользуйтесь методом MenuObject.Controls.Add, установив значение параметра Type равным msoControlPopup. MenuObject — это созданный ранее объект меню типа CommandBarPopup.
Чтобы добавить команду подменю, воспользуйтесь методом MenuItem.Controls.Add, установив значение параметра Type равным msoControlButton. 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 = True
End Sub
Создание пользовательской панели инструментов
Команды запуска макросов, созданных для заказчика, можно также разY
мещать на панели инструментов. На рис. 24.5 показан пример пользовательY
ской панели, содержащей несколько различных элементов управления.
Рис. 24.5. Кнопка, размещенная на панели инстру&
ментов, может содержать только текст, только значок
или же и значок, и текст. Чтобы задать текст всплы&
вающей подсказки, воспользуйтесь свойством эле&
мента управления ToolTipText
582 Часть III
Удивительные возможности Visual Basic for Applications
Создание и удаление пользовательской панели
инструментов
По аналогии с пользовательским меню, предусмотрите процедуры для созY
дания и удаления панели инструментов. Процедуру создания панели инструY
ментов следует вызвать в обработчике события Workbook_Open, Workbook_Activate или Worksheet_Activate, а процедуру удаления панели
инструментов YYYY в обработчике события Workbook_BeforeClose.
Макрос CreateToolbar создает плавающую панель инструментов. ПреY
дыдущие версии Excel позволяли закреплять панель инструментов, однако деY
лать этого не рекомендуется.
Чтобы создать панель инструментов, воспользуйтесь методом CommandBars.Add, указав имя и тип панели инструментов. Ниже приведен код макY
росов CreateToolbar и DeleteToolbar.
Sub DeleteToolbar()
On Error Resume Next
CommandBars("XYZ").Delete
On Error GoTo 0
End 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 With
End 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 With
End 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 With
End 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).Activate
End 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.Delete
End 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 With
End 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).
Внимание
Существует две версии диалогового окна Формат элемента управления (Format
Control). Одна из них содержит семь вкладок (Шрифт (Font), Выравнивание
(Alignment), Размер (Size), Защита (Protection), Свойства (Properties), Поля
(Margins) и Веб (Web)), другая — только одну (Шрифт (Font)).
В результате щелчка правой кнопкой мыши над элементом управления вокруг него
появляется рамка, заполненная диагональными отрезками (рис. 24.7) или точками
(рис. 24.8). В первом случае выбор команды контекстного меню Формат объекта
приводит к отображению диалогового окна Формат элемента управления с одной
вкладкой, во втором — с семью.
Чтобы изменить заливку рамки элемента управления с диагональных отрезков на
точки, щелкните на рамке левой кнопкой мыши.
Помимо кнопки, макрос можно назначить любому графическому объекту,
расположенному на рабочем листе Excel. Например, чтобы назначить макрос
автофигуре, щелкните на ней правой кнопкой мыши и выберите команду конY
текстного меню Назначить макрос (Assign Macro), как показано на рис. 24.9.
Создание пользовательских меню и панелей инструментов
Рис. 24.7. Выбор команды контекстного меню
Формат объекта приведет к отображению диа&
логового окна Формат элемента управления,
содержащего одну вкладку
Рис. 24.8. Выбор команды контекстного меню
Формат объекта приведет к отображению диа&
логового окна Формат элемента управления,
содержащего семь вкладок
Рис. 24.9. Макрос можно назначить любому
графическому объекту, расположенному на ра&
бочем листе Excel
Глава 24 589
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 Вставка Гиперссылка (Insert
Hyperlink) или нажмите комбинацию клавиш <Ctrl+K>. В открывшемся диалого&
вом окне Добавление гиперссылки (Insert Hyperlink) щелкните на кнопке Местом
в документе (Place in This Document). В поле Введите адрес ячейки (Type the cell
reference) введите адрес ячейки, в которой находится гиперссылка. Таким обра&
зом, гиперссылка будет указывать сама на себя (рис. 24.12).
Для запуска требуемого макроса воспользуйтесь обработчиком события Worksheet_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 Select
End Sub
592 Часть III
Удивительные возможности Visual Basic for Applications
Рис. 24.12. Чтобы назначить макрос гиперссылке, создайте гиперссылку, ука&
зывающую саму на себя, и воспользуйтесь обработчиком события
Worksheet_FollowHyperlink для запуска требуемого макроса
Следующий шаг
Следующая глава посвящена использованию надстроек в качестве контейY
неров для размещения макросов.
Глава 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).)
25
Стандартные надстройки
Excel ................................................ 593
Преобразование рабочей
книги Excel в надстройку ........... 594
Использование надстроек .........597
Альтернативное решение:
использование скрытой
рабочей книги.............................. 599
Следующий шаг............................ 601
594 Часть III
Удивительные возможности Visual Basic for Applications
Размещенный в надстройке код выполняется вне зависимости от выY
бранного уровня безопасности макросов.
Обычно область выполнения макросов ограничивается рабочей книY
гой, в которой они были объявлены. Код, размещенный в надстройке,
доступен всем открытым рабочим книгам Excel.
Надстройки не отображаются в списке открытых файлов меню Окно
(Window) и не могут быть выведены на экран с помощью команды
Окно Отобразить (Window Unhide).
Поскольку надстройка является скрытой рабочей книгой, которую нельзя
вывести на экран, не существует возможности выделить или активизировать
ячейки надстройки посредством программного кода. Вдобавок, сохранение
файла надстройки необходимо реализовать в коде самой надстройки, наприY
мер, вызвав метод ThisWorkbook.Save в обработчике события Workbook_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 If
End Sub
Процедура CustFileOpen может использоваться для создания меню, внесения
изменений в файл данных и т.п.
Разделение данных и программного кода является ключевым моментом при раз&
работке сложного приложения Excel. Для внесения изменений в приложение
достаточно исправить код рабочей книги Code.xls и распространить ее среди
всех пользователей.
Следующий шаг
Последняя глава книги представляет собой практикум Тушара Мехты
(Tushar Mehta) YYYY независимого консультанта, имеющего статус Microsoft
MVP. Тушар демонстрирует процесс создания приложения Excel ‘‘с нуля’’.
Глава 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
26
О Тушаре Мехта........................... 603
Постановка задачи ..................... 604
Решение......................................... 605
Реализация решения
с помощью Excel и VBA .............. 606
Резюме............................................ 614
604 Часть III
Удивительные возможности Visual Basic for Applications
чикам Excel. С некоторыми из созданных Тушаром решений вы можете ознаY
комиться на его личном WebYсайте по адресу: http://www.tusharmehta.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 ActiveSheet
End Sub
Код процедур createWiringData и addWiringWSTag будет создан на
следующих этапах разработки приложения.
Этап 1б: создание ключевых компонентов
Создадим ключевой компонент, преобразующий коды контактных точек
m/nnn в адреса ячеек рабочего листа Excel, и наоборот. Адрес ячейки рабочего
листа Excel состоит из двух частей: номера строки и номера столбца. Функция
XLtoWiringCoords преобразует адрес ячейки в код контактной точки, а
функция WiringToExcelCoords YYYY код контактной точки в адрес ячейки.
Для представления адреса ячейки применяется пользовательский тип ExcelCoords. Разместите следующий код в стандартном модуле.
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 + 1
End Function
Объявим несколько глобальных констант. В стандартной цветовой палитре
Excel светлоYсерый цвет, использующийся для представления свободных конY
тактных точек, имеет индекс 15, а синий цвет, использующийся для представY
ления занятых контактных точек, YYYY индекс 5. Дескриптор рабочего листа
матрицы контактов будет представлен глобальной константой cWSWiringTagID, размещенной в ячейке cWSWiringTagAddr. Поместите следующий
код в начало стандартного модуля, содержащего процедуру initializeSystem, а также функции преобразования XLtoWiringCoords и WiringToExcelCoords.
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 Sub
ErrHandler:
MsgBox "Непредвиденная ошибка в процедуре addWiringWSTag:" _
& vbNewLine & "Ошибка=" & Err.Description & " (" & Err.Number & ")"
Application.EnableEvents = True
End 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 Sub
ErrHandler:
MsgBox "Непредвиденная ошибка в процедуре createWiringData:" _
& vbNewLine & "Ошибка=" & Err.Description & " (" & Err.Number & ")"
Application.EnableEvents = True
End 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 и deleteAConnection, соответственно), а также полный код процедуры измене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, Target
End 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
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 If
End 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 Then
MsgBox "Версия 1 этого приложения не поддерживает" & vbNewLine _
& "одновременное изменение нескольких соединений." & vbNewLine _
& "Пожалуйста, выделите только одну ячейку."
'Предотвратить рекурсивный вызов процедуры!
Target.Cells(1).Select
Exit Sub
End If
OldVal = Target.Value
End Sub
Этап 3а: нисходящее программирование
Разработка приложения практически завершена. Осталось создать код
процедур, имеющих непосредственное отношение к инициализации системы,
а также код процедур создания и разрыва соединения.
Следующие четыре процедуры используются при инициализации системы.
Процедура resetFormatting удаляет существующее содержимое раY
бочего листа, задает общий формат чисел и светлоYсерый цвет шрифта.
Sub resetFormatting(aWS As Worksheet)
With aWS.Cells
.ClearContents
.NumberFormat = "General"
.Font.ColorIndex = cUnusedColorIdx
End With
End Sub
Процедура createOneDataBlock использует эквивалент команды
главного меню Excel Правка Заполнить Прогрессия (Edit Fill
Series) для заполнения значениями от 101 до 700 блока, состоящего из
60 строк и 10 столбцов.
611
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 With
End 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 With
End 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 With
End Sub
Процедура addAConnection создает соединение между указанными конY
тактными точками. При возникновении ошибки вызывается процедура resetValue, которая восстанавливает начальное содержимое ячейки рабочего
листа 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 Function
ErrHandler:
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 Sub
ErrHandler:
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 Sub
ErrHandler:
MsgBox "Непредвиденная ошибка в процедуре resetValue: _
Ячейка=" & Target.Address & vbNewLine & "Ошибка=" & _
Err.Description & " (" & Err.Number & ")"
Application.EnableEvents = True
End Sub
Резюме
В этом практикуме был продемонстрирован процесс разработки реального
приложения Excel ‘‘с нуля’’ с использованием различных методик программиY
рования: структурного анализа, принципа распараллеливания и принципа
нисходящего программирования.
Предметный указатель
.
синтаксис, 60
справочная система, 63
.NET Tools for Office, 31
A
ADO (ActiveX Data Objects), 490
объект
Connection, 492
Recordset, 492
API (Application Programming
Interface). См. Windows API
W
WebYзапрос
обновление, 409
создание, 407; 410
Windows API, 547
объявление, 548
использование, 548
примеры, 549
структура, 548
X
C
CSV, 429
D
XML, 427
правила, 428
сопоставление данных, 430
схема данных, 429
DAO (Data Access Objects), 490
L
Lotus 1Y2Y3, 29
А
Автозаполнение, 236
Автофильтр, 297
Б
M
MDB (Multidimensional Database), 489
Microsoft Access, 489
Microsoft Jet, 492
Microsoft Word, 439
S
StarOffice, 30
Бриклин, Дэн, 29
В
Вкладка, 225
Выражение
On Error GoTo, 564
Property Get, 513
Property Let, 513
V
VBA. См. Visual Basic for Applications
VisiCalc, 29
Visual Basic for Applications, 59
Д
Диаграмма, 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
Download