Текст
                    основных понятий и приемов программирования для Excel 2000 за 21 день
ммшние
полученных знаний на практике
sAms
Ocioi самчиишлш
программирование для Microsoft Excel 2000
Мэтью Харрис
SAMS
Освой самостоятельна
программирование для Microsoft
Excel 2000
за 21 лень
Matthew Harris
SAMS
Teach Yourself Microsoft Excel 2000 Programming in J] Dags
SAMS
A Division of Macmillan Computer Publishing 201 West 103rd St., Indianapolis, Indiana, 46290 USA
Мэтью Харрис
S^MS
Освой самостоятельно программирование для Microsoft*
Excel 2000 за 21 день
М№ШК
м
Издательский дом “Вильямс ” Москва » Санкт-Петербург ♦ Киев 2000
ББК 32.973.26-018.2Я75
Х19
УДК 681.3.07
Издательский дом “Вильямс”
По общим вопросам обращайтесь в Издательский дом “Вильямс” по адресу: info@williamspublishing.com, http://www.williamspublishing.com
Харрис, Мэтью.
Х19 Освой самостоятельно программирование для Microsoft Excel 2000 за 21 день. : Пер. с англ. : Уч. пос. — М. : Издательский дом “Вильямс”, 2000. — 880 с. : ил. — Парад, тит. англ.
ISBN 5-8459-0061-1 (рус.)
В книге рассматривается написание макросов и процедур на языке Visual Basic for Applications. Подробно изложена современная технология объектно-ориентированного программирования. Значительное внимание уделено таким вопросам как создание диалоговых окон и форм пользователя, обработка ошибок, управление файлами, обработка событий и пр. Изложенное проиллюстрировано многочисленными примерами.
Книга рассчитана как на начинающих, так и на опытных программистов.
ББК 32.973.26-018.2Я75
Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм. Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательства Sams Publishing.
Authorized translation from the English language edition published by Sams Publishing, Copyright © 1999.
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 retrieval system, without permission from the Publisher.
Russian language edition published by Williams Publishing House according to the Agreement with R&I Enterprises International, Copyright © 2000.
ISBN 5-8459-0061-1 (рус.)	© Издательский дом “Вильямс", 2000
ISBN 0-672-31543-2 (англ.)	© Sams Publishing, 1999
Содержание
Об авторе	17
Введение	19
НЕДЕЛЯ 10Б30Р	21
День1*й. Начинаем	23
День 2-й. Создаем и редактируем простой макрос	39
День 3-й. Типы данных,	переменные и константы	83
День 4-й. Операторы и выражения	113
День 5-й. Функции Visual Basic и Excel	140
День 6-й. Процедуры-функции и нестандартные функции	182
День 7-й. Объекты и коллекции	212
НЕДЕЛЯ 2. ОБЗОР	247
День 8-й. Принятие решения в Visual Basic for Applications	249
День 9-й. Циклы	281
День 10-й. Типы данных и переменных	318
День 11 -й. Типы данных и классы объектов, определяемые пользователем	349
День 12-й. Модульное программирование	376
День 13-й. Управление файлами с помощью Visual Basic	410
День 14-й. Массивы	444
НЕДЕЛЯ 3. ОБЗОР	507
День 15-й. Отладка VBA-программы	508
День 16-й. Создание настраиваемых диалоговых окон	537
День 17-й. Меню и панели инструментов	591
День 18-й. Обработка ошибок	649
День 19-й. Управление приложением Excel с помощью VBA	686
День 20-й. Работа с другими приложениями	715
День 21 -й. Процедуры обработки событий и надстройки	768
ИРИЛОЖЕНИЕ. ОТВЕТЫ	819
Содержание	5
Оглавление
Введение	19
НЕДЕЛЯ 1. ОБЗОР	21
День 1-й. Начинаем	23
Макросы и языки программирования	23
Что такое макрос	23
Краткая история языка VBA	24
Зачем изучать язык VBA	26
Как записать новый макрос	27
Создание начальных условий	27
Начало записи макроса	28
Пробуем записать первый макрос	29
Создаем начальные условия	29
Выбираем имя и место хранения макроса	29
Записываем действия	31
Останавливаем запись	33
Текст макроса	34
Запуск макроса	35
Резюме	36
Вопросы и ответы	36
Коллоквиум	37
Тест	37
Упражнения	38
День 2-й. Создаем и редактируем простой макрос	39
Знакомство со средой Visual Basic For Applications	39
Что такое модули	39
Знакомство с редактором Visual Basic	40
Редактируем макрос	57
Открываем модуль	57
Как найти записанный макрос	58
Из чего состоит записанный макрос	61
Редактируем текст макроса	64
Перемещение или копирование макроса из одного модуля в другой	67
Сохранение и импорт модулей в виде текстового файла	67
Экспорт модуля	67
Импорт модуля	68
Удаление модулей из проекта	69
Пишем макрос и процедуру	70
Создание и переименование модуля	70
Выбор существующего модуля	71
Пишем текст процедуры	71
Запуск процедуры при редактировании	74
Вывод сообщений пользователю	75
Сообщения об ошибках при написании, редактировании и запуске процедуры	76
Синтаксические ошибки	76
Ошибки при выполнении	78
Печать текста программы	79
Резюме	80
Вопросы и ответы	80
6
Оглавление
Коллоквиум	81
Тест	81
Упражнения	82
День 3-й. Типы данных, переменные и константы	83
Познакомимся с типами данных	83
Дата	85
Числа	86
Текстовые строки	87
Логические значения	87
Данные типа Variant	88
Понятие о переменных	88
Что такое переменная	88
Выбор имени переменной	89
Создание переменных	91
Область видимости: какие переменные доступны	93
Время жизни переменной: как долго сохраняется значение	97
Запрет неявного определения переменных	97
Указание типа переменной	98
Понятие о константах	102
Создаем поименованную константу	ЮЗ
Область видимости констант	ЮЗ
Правила написания непоименованных констант	104
Указание типа константы	106
Предопределенные константы	106
Доступ к внутренним константам с помощью окна Object Browser	107
Ввод данных пользователя в вашу процедуру	108
Резюме	1Ю
Вопросы и ответы	НО
Коллоквиум	111
Тест	111
Упражнения	112
День 4-й. Операторы и выражения	113
Понятие об операторах и выражениях	113
Совместимость типов данных	115
Автоматическое преобразование типов	116
Преобразование числовых типов	117
Преобразование строк и чисел	117
Преобразования булевых величин	118
Преобразование дат	118
Оператор присваивания	118
Арифметические операторы	120
Сложение	121
Вычитание	121
Умножение	122
Деление	122
Целочисленное деление	123
Деление по модулю	123
Возведение в степень	124
Операторы сравнения	124
Сравнение строк	125
Оператор Like	127
Оператор Is	129
Логические операторы	129
Оглавление	7
Таблицы истинности	130
Оператор And	130
Оператор Or	131
Оператор Not	131
Оператор Хог	132
Оператор Eqv	132
Оператор Imp	132
Операторы конкатенации строк	133
Использование конкатенации строк	133
Операторы конкатенации строк	134
Старшинство операторов и вычисление сложных выражений	135
Резюме	138
Вопросы и ответы	138
Коллоквиум	138
Тест	138
Упражнения	139
День 5-й. Функции Visual Basic и Excel	140
Понятие о функциях	140
Использование функций в выражениях	141
Понятие об аргументах функции и о результате функции	144
Игнорирование результата функции	144
Использование поименованных аргументов функции	146
Использование функций VBA	148
Математические функции	148
Функции преобразования данных	150
Строковые функции	156
Функции для получения информации о диске, каталоге и другие	158
Использование функций Excel	159
Использование окна CM^ect Browser для вызова функции	160
Просмотр и вставка функций Visual Basic	161
Просмотр и использование функций Excel	163
Использование функций для управления строками	164
Удаление лишних символов пробелов	165
Определение длины строки	166
Сравнение и поиск строк	167
Разбиение строки на части	169
Использование символов, которые нельзя набрать на клавиатуре	171
Резюме	178
Вопросы и ответы	178
Коллоквиум	180
Тест	180
Упражнения	180
День 6-й. Процедуры-функции и нестандартные функции	182
Понятие о процедуре-функции и о нестандартной функции	182
Создание процедуры-функции	184
Пишем процедуру-функцию	184
Создание нестандартных функций для Excel	186
Объявление типа данных для результата функции	187
Объявление типа данных для аргументов функции	189
Создание необязательных аргументов	189
Определение значения по умолчанию для необязательных аргументов	193
Как VBA передает аргументы	193
Использование процедуры функции в Visual Basic for Application	197
8
Оглавление
Использование окна Object Browser для поиска вашей процедуры функции	198
Вставка описания процедуры-функции в окне Object Browser	200
Использование определенных пользователем функций в рабочих листах Excel	201
Создание процедур функций	203
Разработка функции для Excel	204
Понятие о рекурсии	206
Анализ работы рекурсивной функции	206
Как избежать нежелательной рекурсии, а также других проблем	208
Резюме	208
Вопросы и ответы	209
Коллоквиум	210
Тест	210
Упражнения	210
День 7. Объекты и коллекции	212
Понятие об объектах	212
Свойства объекта	213
Методы объектов	214
Классы объектов	215
Использование объектов	217
Использование свойств объекта	218
Как пользоваться методами объектов	221
Объявление объектных переменных	223
Использование объектов в выражениях	224
Доступ к объектам с помощью конструкции With... End With	227
Коллекции объектов и контейнеры объектов	229
Включение в коллекцию нового элемента	232
Как обратиться к объекту в коллекции или в контейнере	232
Объекты, методы и свойства в окне Object Browser	233
Резюме	235
Вопросы и ответы	235
Мастерская	237
Контрольные вопросы	237
Упражнения	238
НЕДЕЛЯ 2. ОБЗОР	247
День 8-й. Принятие решения в Visual Basic for Applications	249
Изучение команд принятия решений в VBA	249
Осуществление простого выбора	251
Выбор с помощью оператора If...Then	251
Выбор между ветвями с помощью If...Then...Else	253
Осуществление сложного выбора	256
Вложенные инструкции If...Then	256
Использование If...Then...ElseIf	258
Применение инструкции Select Case	259
Безусловное ветвление	263
Раннее завершение процедур, функций и программ	267
Использование инструкции Exit	267
Использование инструкции End	269
Выбор с помощью функции MsgBox	272
Резюме	276
Вопросы и ответы	277
Коллоквиум	278
Тест	278
Упражнения	279
Оглавление	9
День 9-й. Циклы	281
Что такое циклические действия	281
Повторение фиксированное число раз: циклы For	284
Применение цикла For...Next	284
Применение цикла For Each...Next	292
Повторение неопределенное число раз: циклы Do	295
Как VBA проверяет определитель цикла	295
Циклы с проверкой условий перед выполнением	298
Циклы с проверкой условий после выполнения	303
Немедленное завершение циклов	307
Вложенные циклы	310
Вложенные циклы For	310
Вложенные циклы Do	312
Резюме	315
Вопросы и ответы	315
Коллоквиум	316
Тест	316
Упражнения	316
День 10-й. Типы данных и переменных	318
Получение информации о переменных и выражениях	318
Применение информационных функций Visual Basic for Applications	320
Получение информации о типе данных в переменной или в выражения	324
Специальные значения Empty и Null	331
Защитное программирование: предотвращение ошибок	333
Проверка аргументов и других внутренних значений	334
Проверка данных пользователя	334
Проверка данных, полученных не от пользователя	339
Сохранение переменных в промежутках между вызовами процедур и функций	339
Резюме	346
Вопросы и ответы	347
Коллоквиум	347
Тест	347
Упражнения	348
День 11 -й. Типы данных и классы объектов, определяемые пользователем	349
Создание новых типов данных	349
Определение нового типа	350
Описание переменных определенного пользователем типа	351
Использование переменных определенного пользователем типа	352
Создание новых классов объектов	359
Что такое модули классов	360
Проектирование класса объектов	360
Создание определяемого пользователем класса	364
Применение нестандартного класса	372
Резюме	374
Вопросы и ответы	374
Коллоквиум	375
Тест	375
Упражнения	375
10
Оглавление
День 12-й. Модульное программирование Эффективное применение модулей Создание библиотечной книги Доступ к библиотеке процедур и функций Расширенные правила видимости для многомодульных программ Области видимости Private и Public Переопределение правил видимости языка Visual Basic Как избежать замкнутых ссылок Префикс модуля Структурное программирование Вызов одной процедуры из другой Программирование сверху вниз, или Пошаговая детализация Модульная организация программного проекта Обмен данными между процедурами с помощью аргументов Когда и с какой целью используются процедуры со списком аргументов Как создать список аргументов процедуры Применение процедур с аргументами Резюме Вопросы и ответы Коллоквиум Тест	. Упражнения	376 376 378 379 385 385 386 391 392 393 393 395 397 398 398 399 400 406 407 408 408 408
День 13-й. Управление файлами с помощью Visual Basic Понятие об управлении файлами Что такое управление файлами Средства Visual Basic для управления файлами Атрибуты файла Знакомство с атрибутами файлов Чтение атрибута файла Изменение атрибута файла Как получить имя файла Поиск файлов с помощью функции Dir Ввод имени файла с помощью стандартных окон Excel Диски и папки Определение пути к текущей папке и буквы дисковода Изменение текущей папки Изменение дисковода Создание папки Удаление папки Копирование и удаление файлов Копирование файла Удаление файла Переименование или перемещение файлов Получение информации о файле Дата и время изменения Длина файла Резюме Вопросы и ответы Коллоквиум Тест Упражнения	410 410 410 411 412 412 414 417 419 419 423 429 429 430 432 433 433 434 435 436 437 439 439 440 441 442 442 442 443
Оглавление	11
День 14-й. Массивы Понятие о массивах Что такое одномерные массивы Что такое многомерные массивы Статические и динамические массивы Инструкция Option Base Описание массивов Использование массивов Использование ReDim с динамическими массивами Функции LBound и UBound Использование Erase для чистки и удаления массива Использование массивов в качестве аргументов процедур и функций Сортировка массивов Поиск в массивах Последовательный поиск Бинарный поиск Резюме Вопросы и ответы Коллоквиум Тест Упражнения	444 444 445 445 448 449 450 451 458 466 467 470 471 478 479 482 488 489 490 490 491
НЕДЕЛЯ 3. ОБЗОР	507
День 15-й. Отладка VBA-программы Распространенные типы ошибок Режим прерывания Переход в режим прерывания из окна сообщения об ошибке Точки останова Инструкция Stop Прерывание программы командой Step Into Прерывание программы нажатием клавиши Выход из режима прерывания Команда Step Into Команда Step Over Команды Step Out и Run To Cursor Как пользоваться контрольными значениями переменных Как добавить контрольное выражение Редактирование контрольного выражения Удаление контрольного выражения Быстрая проверка контрольного значения Подсказки значений данных Окно локальных переменных Последовательность вызова процедур Как пользоваться окном отладки Использование инструкции Debug.Print Резюме Вопросы и ответы Коллоквиум Тест Упражнения	508 508 510 511 512 514 514 515 515 515 520 521 522 523 525 526 526 527 528 528 530 530 533 534 535 535 535
12
Оглавление
День 16-й. Создание настраиваемых диалоговых окон	537
Знакомство с формами	538
Элементы управления	541
Использование форм для создания диалоговых окон	547
Добавление нового объекта формы	547
Использование панели инструментов Toolbox	549
Добавление элементов управления в форму	552
Редактирование элементов управления	555
Управление последовательностью перехода	556
Определение свойств формы и элемента управления в режиме конструктора	558
Отображение форм с помощью VBA	560
Использование VBA с элементами управления в ферме	561
Создание процедур событий и других программ формы	562
Создаем диалоговое окно	564
Диалоговое окно и его программа в стандартном модуле	574
Использование элемента списка	582
Резюме	586
Вопросы и ответы	587
Коллоквиум	588
Тест	588
Упражнения	589
День 17-й. Меню и панели инструментов	591
Управляющие панели	591
Типы управляющих панелей	593
Части управляющей панели	593
Типы элементов управления	595
Методы и свойства управляющей панели	597
Коллекция CommandBars	597
Объект CommandBar	599
Встроенные и нестандартные управляющие панели	601
Нестандартные управляющие панели	602
Встроенные управляющие панели	603
Отображение списка встроенных управляющих панелей	603
Объекты, методы и свойства элементов управления	605
Коллекция CommandBarControls	605
Объект CommandBarControl	606
Встроенные элементы управления	611
Лицевые поверхности элементов управления	614
Управление нестандартными и встроенными управляющими панелями	619
Добавление новой управляющей панели	619
Сокрытие, отображение и позиционирование управляющей панели	620
Удаление управляющей панели	624
Переустановка встроенной управляющей панели	625
Работа с элементами управления	626
Добавление элемента управления в управляющую панель	626
Именование или переименование элемента управления	627
Указание процедуры события элемента управления	628
Удаление элементов управления	628
Включение или отключение элемента управления	629
Состояние элемента управления	630
Отыскание конкретных элементов управления	630
Применение на практике: управляющая панель типа меню	631
Применение на практике: управляющая панель типа панели инструментов	638
Резюме	647
Оглавление
13
Вопросы и	ответы	647
Коллоквиум	647
Тест	648
Упражнения	648
День 18. Обработка ошибок	649
Стратегии обработки ошибок	649
Инструкция On Error GoTo	651
Инструкция Resume	652
Определение типа ошибки, ее местонахождения и текста сообщения	655
Искусственное создание ошибки и создание собственных кодов ошибки: инструкция Error	661
Использование объекта Err	663
Подведем итоги: примеры обработки ошибок	665
Резюме	681
Вопросы и ответы	682
Коллоквиум	683
Тест	683
Упражнения	683
День 19-й. Управление приложением Excel с помощью VBA	686
Работа с объектами Workbook	686
Обращение к объекту Workbook	687
Открытие рабочей книги	688
Создание рабочей книги	689
Активизация рабочей книги	690
Сохранение рабочей книги	691
Закрытие рабочей книги	692
Работа с объектами Worksheet	694
Обращение к объекту Worksheet	694
Активизация рабочего листа	695
Создание нового рабочего листа	696
Переименование рабочего листа	697
Копирование и перемещение рабочего листа	697
Удаление рабочего листа	698
Методы для обращения к диапазону	699
Использование метода Range	700
Использование метода Cells	701
Использование метода Offset	703
Другие методы и свойства для указания диапазона	705
Работа с ячейками и диапазонами	705
Выбор ячейки или диапазона	705
Работа со значениями и формулами	706
Создание имени диапазона	708
Вырезание, копирование и очистка данных	709
Резюме	711
Вопросы и ответы	712
Коллоквиум	713
Тест	713
Упражнения	714
День 20-й. Работа с другими приложениями	715
Что такое Automation и OLE	715
Краткая история OLE	715
Как к этому приспособить VBA?	718
14
Оглавление
Определение типов объектных классов	718
Использование базы данных реестра	719
Использование утилиты Сведения о системе	722
Добавление связанных и внедренных объектов	724
Объекты Shape и OLEFormat	724
Использование метода AddOLEObject и коллекции Shapes	725
Вставка нового внедренного объекта	726
Вставка существующего файла как внедренного объекта	728
Вставка существующего файла как связанного объекта	729
Коллекция OLEObjects	731
Работа со связанными и внедренными объектами	732
Доступ к OLE-объектам	732
Использование свойств OLE-объекта	733
Использование методов OLE-объекта	737
Метод Update	737
Метод Verb	738
Использование автоматизации	741
Доступ к объектам автоматизации	742
Прямой доступ к объектам	742
Создание нового объекта автоматизации	745
Доступ к существующему объекту автоматизации	750
Доступ к DLL из Visual Basic for Application	752
Объявление DLL-процедур	752
Некоторые примеры использования DLL	753
Работа с приложениями, которые не поддерживают OLE или автоматизации	757
Запуск другого приложения	757
Активизация запущенного приложения	759
Отправка кодов клавиш другому приложению	761
Резюме	764
Вопросы и ответы	765
Коллоквиум	766
Тест	766
Упражнения	767
День 21-й. Процедуры обработки событий и надстройки	768
Что такое события и процедуры обработки событий	768
Где хранятся процедуры обработки событий	770
Имя и описание процедуры обработки события	771
Аргументы процедуры обработки события	772
События объекта Application	772
События объектов приложения Excel	774
Событие Open	777
Событие BeforeClose	779
Событие Activate	780
Событие DeActivate	782
Событие BeforeDoubleQick	783
Событие Change	785
Событие Calculate	787
Другие события	788
Свойства и методы, связанные с событиями	789
Свойство OnWindow	790
Метод ОпКеу	791
Метод OnTime	794
Надстройки	796
Создание надстройки	796
Управление надстройками из Visual Basic	798
Оглавление	15
Резюме	800
Вопросы и ответы	801
Коллоквиум	802
Тест	802
Упражнения	802
ВИА1ЖЕИЕ. ОТВЕТЫ	819
День 1-й	819
День 2-й	821
День 3-й	824
День 4-й	828
День 5-й	830
День 6-й	832
День 7-й	835
День 8-й	839
День 9-й	842
День 10-й	848
День 11-й	850
День 12-й	853
День 13-й	857
День 14-й	859
День 15-й	862
День 16-й	864
День 17-й	867
День 18-й	871
День 19-й	872
День 20-й	876
День 21-й	878
Предметный указатель	881

16
Оглавление
Об авторе
Мэтью Харрис (Mathew Harris) живет в г. Окленд, Калифорния. С микрокомпьютерной индустрией он связан с 1980 года. Мэтью обеспечивает техническую поддержку и консультации по программированию и обучению персонала для некоторых известных организаций, таких как Калифорнийский университет в Сан-Франциско, и многих мелких компаний. В 1990 г. он работал в качестве консультанта на международном конгрессе, посвященном проблемам СПИД. Дипломированный специалист по аппаратному обеспечению начал заниматься программированием для IBM-совместимых компьютеров в 1983 году. С тех пор он создал немало коммерческих программных продуктов и программ для внутреннего применения по заказу многих компаний. Долгое время Мэтью Харрис преподавал программирование на языках Basic и Pascal и обучал слушателей работе с MS DOS. Он является автором и соавтором книг The Disk Compression Book и Using FileMaker Pro 2.0 for Windows. Кроме того, он участвовал в создании таких книг, как Using Word for Windows 6.0, Using Excel 5, Excel Professional Techniques, Using Paradox 4.5 for DOS, Using Paradox for Windows 5.0, The Paradox Developer's Guide, Using MS DOS 6, Unveiling Windows 95, и Using Access 7 for Windows. Вы можете связаться с Мэтью Харрисом по электронной почте; его адрес 74017.766@compuserv.com.
Посвящение
Посвяшается Роджеру Дженнингсу (Roger Jennings), который дал мне шанс.
Благодарности
Пользуясь случаем, хочу поблагодарить всех сотрудников издательства Sams, которые участвовали в работе над этой книгой и внесли свой вклад в ее создание. Особую благодарность хотелось бы выразить Крису Денни (Chris Denny) за его неоценимую помощь. Снимаю шляпу перед Сюзанной Mop (Susan Moore), Хазер Аршел (Heather Urschel), Тони Амико (Tony Amico) и Пер Бломквист (Per Blomquist) за их самоотверженную работу.
Введение
Что нового в этой книге?
При работе над этой книгой часто приходилось сталкиваться с изменениями, сделанными в программе Excel 9 из пакета Microsoft Office 2000 по сравнению с программой Excel 8 из Office 97. При этом я использовал любую возможность для того, чтобы сделать новую книгу лучше и понятнее.
•	Были учтены отклики читателей на первые три издания книги. В результате добавлены многие советы и примечания.
•	Все главы и особенно тексты программ были пересмотрены и исправлены в соответствии с новыми свойствами Excel.
•	Урок 11-го дня представляет собой совершенно новую главу, посвященную созданию новых классов объектов и типов данных. Кроме того, в ходе этого урока рассматривается применение этих классов для изменения поведения встроенных в VBA и Excel объектов.
•	Урок 13-го дня был существенно расширен для рассмотрения примеров того, как с помощью инструкций Open, Write и Read создавать и использовать файлы данных специальных типов.
•	Урок 20-го дня был существенно переработан с целью рассмотрения новых свойств элементов ActiveX и более полного описания автоматизации и технологии OLE. В материал этого урока была включена информация по обработке нажатий клавиш и использованию библиотек динамической компоновки (DLL) других приложений. Эти механизмы позволяют управлять приложениями, которые не поддерживают функции автоматизации, или управлять диалоговыми окнами, которые не поддерживают технологии ActiveX.
•	Урок 21-го дня был переработан для того, чтобы лучшего объяснить, что такое программы-надстройки и как они разрабатываются.
Как пользоваться этой книгой
На страницах этой книги тексты программ и примеров, в отличие от простого текста, набраны моноширинным шрифтом (т.е. шрифтом, символы которого имеют постоянную ширину), как в следующей строке:
MsgBox "Hello, World!"
Текст, который вам будет предложено ввести в поле ввода, выделен полужирным, например, “в качестве имени файла введите FormatArialBold”.
Названия окон, их управляющих элементов и пунктов меню выделяются следующим образом:
В окне Макрос щелкните на кнопке Выполнить.
Введение
19
ПРИМЕЧАНИЕ
Предупреждение
Так мы отмечаем места, на которые вам следует обратить особое внимание.
В этом месте вы найдете совет или рекомендацию.
Здесь вас предупреждают о возможной опасности или подвохе.
Кроме того, вам встретятся врезки, имеющие следующий вид:
Так надо
Не забывайте чистить зубы после еды.
Так не надо
Никогда не берите конфеты у незнакомцев.
Вы, конечно, заметите, что книга изобилует листингами программ. Каждый листинг, в отличие от фрагментов в тексте, представляет собой завершенную программу, как правило, полный модуль, который вы можете ввести в редактор VBA. Файлы листингов вы можете взять по адресу www.williamspublishing.com в разделе Материалы.
20
Введение
<sAms
Освой самостоятельно
Неделя 1
Краткий оОзор
Приступая к изучению языка Visual Basic for Applications, вы должны иметь только две вещи: эту книгу и установленную программу Excel 2000. Прочесть эту книгу вы можете и если у вас нет программы Excel 2000, но в таком случае польза от нее будет несколько ограниченной. (В крайнем случае, вы можете при чтении обращаться к программе Excel 95 или Excel 97.) Вряд ли можно изучить язык программирования, только читая книгу, пусть даже и очень хорошую. Лучше всего изучать Visual Basic, создавая и редактируя макросы, запуская процедуры, написанные собственноручно в процессе работы с книгой. Изучив предложенные здесь примеры и упражнения, вы получите неоценимый опыт и научитесь программировать на языке Visual Basic for Applications. Приобретенные знания помогут вам также создавать программы на Visual Basic для приложений Microsoft Access, Microsoft Word, Microsoft Outlook и для любого приложения, включенного в состав Microsoft Office 2000 (профессиональное или стандартное издание).
Предлагаемая вашему вниманию книга устроена таким образом, что в конце каждого дня обучения приводятся несколько вопросов и несколько упражнений. Закончив урок, вы должны уметь ответить на все вопросы и выполнить упражнения. В приложении (в конце книги) вы найдете ответы на все вопросы и способы выполнения упражнений. Кроме того, для упражнений из начальных уроков вы найдете объяснения программных решений и советы там, где это уместно.
Старайтесь не пропустить ни одного упражнения, так как многие из них используются на протяжении книги неоднократно. Например, в упражнении вр второй главе используется макрос, созданный в первой главе.
Что дальше
Материал первой недели обучения посвящен в основном тому, как записывать, редактировать или создавать вручную программы на языке Visual Basic for Applications (сокращенно VBA). Здесь же вы узнаете, как создавать определенные пользователем функции для листов Excel.
На первых двух уроках мы выясним, как записывать макросы на VBA, изучим основные элементы процедуры VBA и ознакомимся с процессом создания макросов, не требующим средств записи. Здесь же вы узнаете, что такое Object Browser и как им пользоваться при программировании на Visual Basic. Кроме того, на втором занятии вы научитесь выводить на экран сообщения из своих макросов.
День 1-й. Начинаем
21
Третий урок посвящен переменным для временного хранения значений данных в макросах и процедурах. Вы познакомитесь с различными типами данных, с которыми может работать VBA, и с тем, как пользоваться константами для хранения неизменных значений в программах. На этом же занятии вы узнаете, как осуществлять ввод данных пользователя. В ходе четвертого урока вы научитесь составлять вычисляемые выражения из переменных и констант.
Пятый урок посвящен функциям, входящим в состав VBA. Мы поговорим о том, что такое функция и как пользоваться функциями в выражениях. Функции VBA позволяют преобразовывать данные из одного типа в другой, получать информацию о VBA, выполнять математические вычисления и многие другие задания. На этом же занятии вы узнаете, как в своих программах на VBA пользоваться функциями, встроенными в Excel.
Тема шестого дня — создание функций и использование их в программах и макросах, написанных на VBA, а также в листах Excel.
Седьмой урок подводит итоги первой недели учебного курса. На этом занятии вы узнаете, что такое программный объект, научитесь распознавать эти объекты в записанных вами макросах и понимать их смысл. Здесь же вы познакомитесь со свойствами и методами объекта, научитесь изменять свойства обьекта и пользоваться его методами.
Не так уж мало материала для того, чтобы изучить его за одну неделю. Но если вы прилежно будете проходить по одному уроку в день, тщательно выполнять все предложенные упражнения и отвечать на контрольные вопросы, можете быть уверены в успехе.
22
Неделя 1
****-*
|И ял
ДЕНЬ |И
Начинаем
Примите поздравления! Вы начали трехнедельный курс обучения программированию для Excel 2000. На первом уроке мы рассмотрим следующие вопросы.
•	Что такое макросы и для чего они применяются.
•	Как язык VBA взаимодействует с макросами.
•	Почему иногда приходится добавлять команды VBA к записанным макросам и для чего нужно уметь создавать VBA-макросы без использования средств записи.
•	Как записать и запустить макрос.
Макросы о языки программирования
Перед тем как приступать к созданию собственных макросов, вы должны ясно понять, что такое макрос вообще и в чем заключается разница между макросом, написанным на VBA, и записанным макросом.
Что такое макрос
Наверняка вам не раз приходилось замечать, что независимо от используемой операционной системы или программы, с которой вы работаете, очень часто при выполнении тех или иных заданий вы применяете одни и те же последовательности команд. Вместо того чтобы каждый раз выполнять одну и ту же последовательность команд, вы можете создать макрос с тем, чтобы программа самостоятельно выполняла его в случае нужды. Запускается макрос одной-единственной командой.
(ПРИМЕЧАНИЕ
В начале такая предварительно записанная последовательность команд называлась макрокомандой. В современном компьютерном языке слово сократилось до простого макрос, или, по-английски, macro. Приставка “макро” часто встречается в техническом и научном языке, она происходит от греческого слова и означает “большой” или “длинный”, например макромир, макроклимат.
Макросы используются не только для удобства. Компьютеры вообще справляются с повторяющимися заданиями лучше, чем люди, поэтому применение макросов повышает скорость работы и, главное, точность и надежность. После того как вы запи
День 1-й. Начинаем	23
сали последовательность команд в виде макроса, вы можете быть уверены в том, что компьютер выполнит эту последовательность однозначно и безошибочно. Кроме того, выполнение макроса, как правило, не требует участия оператора, поэтому вы можете запустить макрос, требующий значительного времени, например, запрос к базе данных. или просто сортировку данных, а сами в это время можете отдохнуть или переключиться на другое задание.
При записи макроса программа записывает все действия оператора, включая и ошибочные. Когда приложение, такое как Excel, активизирует макрос, все действия оператора выполняются точно в том порядке, в каком они были записаны. Старые программы для записи макросов грешили одним существенным недостатком: если при записи последовательности команд вы допускали ошибку, единственным способом исправить эту ошибку была повторная запись всего макроса. Более того, даже в правильно записанном макросе внести небольшое изменение можно было, только повторив всю последовательность команд. Очень часто каждое повторение длинной последовательности порождало новые ошибки. Учитывая все это, разработчики программного обеспечения создали средства для редактирования записанных макросов, и теперь вы можете исправлять ошибки, не повторяя всей последовательности команд.
ПРИМЕЧАНИЕ
Не в каждом приложении есть инструмент для записи макросов, такой как в программах Microsoft Word и Microsoft Excel. Например, в программе Microsoft Access вместо этого существует конструктор макросов. Работая с конструктором макросов, вы выбираете из предлагаемого вам списка те действия, которые хотите включить в создаваемый макрос:
Краткая история языка VBA
Несмотря на новизну языка Visual Basic for Applications, история его появления почти так же стара, как и вся компьютерная промышленность. Про язык VBA можно сказать, что он является диалектом языка BASIC, который появился в начале 60-х. (BASIC — это сокращение от английского Beginner’s All Purpose Symbolic Instruction Code, или символьный язык программирования общего назначения для начинающих.)
Хотя по сегодняшним понятиям язык BASIC был довольно ограниченным и, как теперь говорят, варварским, он был прост для изучения и очень скоро получил широкое распространение. Версии BASIC выпускались (и выпускаются по сей день) для всех типов компьютеров. Язык GWBAS1C производства компании Microsoft (GW означало Graphics Workshop, Коллоквиум графики) был одним из первых языков программирования для современных персональных компьютеров. Он поставлялся со всеми операционными системами MS DOS до 5-й версии. Ранние персональные компьютеры производства компании IBM даже имели версию BASIC, встроенную в ПЗУ.
С годами первоначальная версия BASIC была существенно доработана. Менялась технология программирования, и вместе с ней под влиянием разработчиков программного обеспечения менялся сам BASIC. Современный его диалект включает многие черты и свойства, характерные для более поздних и совершенных языков, таких как Pascal, С и C++.
В конце 80-х Microsoft выпускает существенно улучшенную версию BASIC, названную QuickBASIC. Эта версия вобрала в себя все достижения современных систем для разработки программного обеспечения. Теперь компания Microsoft включает QuickBASIC во все версии MS DOS, начиная с 6-й (но не в Windows 95).
После нескольких версий QuickBASIC в 1992 г. Microsoft выпускает Visual Basic for Windows. Как и QuickBASIC, Visual Basic for Windows включает в себя все современные достижения технологии программирования и очень хорошо интегрируется со средой Windows. В состав Visual Basic for Windows входят команды и функции, необходимые для программирования под Windows: диалоговые окна, панели меню, списки,
24
Неделя 1
кнопки, флажки и многое другое. В частности, в состав Visual Basic входят функции для обеспечения технологии OLE, т.е. для обмена данными между несколькими Windows-приложениями. Современная версия Visual Basic for Windows предусматривает поддержку управляющих элементов ActiveX и автоматизации (automation), что позволяет вам получать доступ к функциям других приложений из вашей программы, написанной на Visual Basic. Таким образом, Visual Basic — это современный язык программирования под Windows, уходящий своими корнями в старинный BASIC.
Параллельно с развитием и улучшением языка BASIC развивались и средства записи макросов в приложениях. С годами макросы приложений становились все более совершенными и сложными, поспевая за потребностями пользователей в большей гибкости и простоте применения. Многие языки макросов стали включать в себя возможности, которые ранее встречались только в языках программирования.
Однако языки макросов производства разных компаний могли существенно отличаться один от другого. Нередко пользователю приходилось изучать несколько таких языков, что неминуемо приводило к снижению производительности. Чтобы избавить разработчиков от необходимости изучения нескольких языков, компания Microsoft начала строить свои языки макросов для приложений на основе элементов языка BASIC. В результате язык макросов для программы Microsoft Word для Windows (предшествовавшей Word 97) получил название WordBASIC, а соответствующий язык для Microsoft Access 2.0 — AccessBasic.
Для унификации языков макросов различных приложений и для интеграции макросов в технологию ActiveX и автоматизацию компания Microsoft создала специальную версию языка Visual Basic, назвав ее Visual Basic for Applications (сокращенно VBA). Excel 5 был первым коммерческим продуктом, включавшим в себя поддержку языка VBA. С появлением на рынке комплекта Microsoft Office 97 язык VBA был включен в программы Microsoft Word, Access, Excel, PowerPoint и Outlook.
Язык VBA в основном совпадает с Visual Basic for Windows, но имеет и существенные отличия. В частности, макросы VBA хранятся в файле документа того приложения, в котором вы создаете этот макрос, а не в отдельном текстовом файле. Например, VBA-макрос, созданный в Excel, хранится в файле книги, VBA-макрос приложения Word — в документе Word, а макрос для программы Access — в файле базы данных.
Запустить VBA-макрос можно только из программы, в которой он был написан. Например, вы не можете запускать макрос, написанный для Excel, из другой программы, хотя ничто не мешает вам заставить Excel выполнить нужный макрос, использую автоматизацию. Хотя основные свойства VBA остаются неизменными для каждого приложения, каждое приложение вносит в свои макросы специфические команды и объекты. В частности, VBA для Excel имеет много команд, которые можно применять только к таблицам Excel. Точно так же, VBA для Word содержит много команд и функций, которые имеют смысл только применительно к тексту, a VBA для Access имеет инструменты, воздействующие исключительно на базы данных.
Поскольку каждое приложение, поддерживающее VBA, привносит в этот язык некото----------- рые специфические черты, существует как бы несколько разновидностей языка VBA. (ПРИМЕЧАНИЕ Когда мы буДем говорить о VBA, относящемся к конкретному приложению, мы будем \---------прибавлять название этого приложения, например, Excel VBA, Word VBA и т.д. Говоря о
чертах, свойственных всем этим разновидностям, т.е. об общих свойствах, мы будем говорить просто VBA.
Внедрив один язык макросов во все свои приложения, Microsoft гарантирует, что большая часть того, что вы выучите о VBA применительно к одному приложению, будет справедлива и для остальных. Фактически язык Visual Basic версии 4 и выше тоже поддерживает VBA, и вы можете без труда преобразовать свои макросы в полноценную программу на Visual Basic.
День 1-й. Начинаем
25
Зачем изучать язык VBA
На первый взгляд может показаться, что если вы можете записать макрос средствами программы Excel и потом воспроизвести его, то вам совсем не нужно учить язык VBA. Однако это не так. Записанный макрос не универсален, поскольку он может только повторить однажды выполненную последовательность команд, и только в том же самом порядке, в каком вы ее записали. С помощью VBA вы можете усовершенствовать записанные макросы, придавая им недостающую гибкость и универсальность.
Записанный макрос не гибок, поскольку он не учитывает изменения обстоятельств и условий. Однако с помощью VBA можно создать макрос, который будет проверять некоторые заданные вами условия и в зависимости от них менять последовательность или состав выполняемых команд.
Например, вы пытаетесь выполнить в программе Excel макрос, который должен открыть лист с названием Продажи. Если такого листа в текущей книге нет, макрос будет остановлен и Excel выдаст сообщение об ошибке. Но если вы отредактируете свой макрос с помощью VBA, он может сначала проверить наличие нужного листа, а в случае его отсутствия может даже его создать.
ПРИМЕЧАНИЕ
Если вы немного знакомы с Microsoft Access, вам может показаться, что использование конструктора макросов - это почти что программирование, снимающее указанные выше проблемы, но это не так. Конструктор макросов позволяет лишь выбирать команды из заданного списка, и полученный макрос получается таким же не гибким, как и записанный, т.е. он не способен проверять условия или выполнять циклические действия.
Если вам нужно циклически повторить несколько раз некоторую последовательность команд, записанный макрос — плохой помощник. Ведь при выполнении он повторит эту последовательность ровно столько раз, сколько раз вы повторили ее при записи — по крайней мере, до тех пор, пока вы не отредактируете его или не перезапишете. Другое дело VBA-макрос, который может проверить заданное вами условие или просто перед выполнением запросить информацию о количестве повторений и о том, нужно ли вообще выполнять то или иное действие.
Представьте себе, что вы записали макрос, который меняет ширину столбцов в листе Excel. Если вам требуется, чтобы он менял ширину первых трех столбцов, то при записи вы должны менять ширину всех трех столбцов вручную. Записанный макрос всегда будет менять ширину только первых трех столбцов, вы не сможет с его помощью изменить ширину столбцов со второго по четвертый. Отредактировав записанный макрос с помощью VBA, вы добьетесь того, чтобы он спрашивал вас, сколько столбцов нужно обработать и какие именно. Вы даже сможете задать новую ширину.
Это только самые простые примеры того, что вы можете делать с макросами с помощью VBA. На самом деле существует очень много обстоятельств, требующих проверки условий для принятия решения или выполнения циклических операций. И единственный способ сделать это — включить в записанный макрос операторы языка VBA.
Помимо редактирования записанных макросов, вы можете применять VBA для согласованного управления несколькими простыми макросами, каждый из которых выполняет некоторую часть большого задания. Например, вы можете регулярно импортировать данные из базы данных в лист Excel, форматировать эти данные, строить на их основании диаграмму, а затем посылать и диаграмму, и отформатированный отчет на печать.
Для того чтобы собрать все эти, вполне независимые задания, в одно, и оформить их в виде макроса, вам понадобится записать по макросу для каждого задания — макрос для импорта данных, макрос для форматирования, макрос для построения диа
26
Неделя 1
граммы и для вывода на печать. Потом вы сможете запускать эти макросы на выполнение с помощью VBA в нужной последовательности.
Кроме того, с помощью VBA вы можете управлять выполнением других программ, воспользовавшись таким средством, как автоматизация, и сможете организовывать обмен данными с помощью OLE. Речь об этом пойдет на занятии 20-го дня.
Как записать новый макрос
Перед тем как редактировать макрос с помощью VBA, вам нужно сначала его записать. В этом разделе мы рассмотрим основные понятия, необходимые для того, чтобы научиться записывать макросы. Позже, в следующем разделе, мы разберем собственно запись макроса.
Как правило, запись макроса состоит из четырех шагов:
1.	Создание начальных условий для макроса подразумевает приведение вашей программной среды в точно такое состояние, в котором вы предполагаете выполнять записываемый макрос.
2.	Запустить запись макроса и задать ему имя. Как только вы запустите запись макроса, вы должны будете дать ему подходящее имя и указать, где сохранить этот макрос. Записывая макрос в программе Excel 2000, вы можете также назначить ему комбинацию клавиш.
3.	Выполнить действия, которые вы хотите записать. Могут быть записаны любые действия, которые вы выполняете с помощью мыши или клавиатуры, включая выполнение ранее записанных макросов. Что именно вы запишете, зависит от того, для какого задания вы предполагаете использовать данный макрос.
4.	Остановить запись макроса. После остановки записи ваши действия уже не фиксируются. Новый макрос готов к применению сразу после остановки записи.
^ПРИМЕЧАНИЕ
В этой книге мы концентрируем внимание на применении макросов в программе Excel 2000, и на это есть две причины. Во-первых, Excel - одна из наиболее часто используемых программ из комплекта Microsoft Office. (В стандартный комплект Microsoft Office не входит программа Access, она поставляется только с комплектом Premium.) Во-вторых, Excel - одна из двух программ Microsoft Office, которые имеют средство для записи макросов. (Вторая - Microsoft Word.) Изучать программирование на VBA намного удобнее и легче, если в качестве заготовки брать предварительно записанный макрос.
Давайте рассмотрим эти действия подробнее.
Создание начальных деланий
Перед тем как записывать макрос, вы должны создать точно такие же условия, в каких потом вы будете его выполнять. Предположим, например, что вы хотите создать макрос, который будет задавать шрифт, кегль и цвет текста в выделенных ячейках листа Excel. Начальными условиями для такого макроса будет открытый лист Excel с выделенной одной или несколькими ячейками.
Вы обязательно должны создать начальные условия перед началом записи, потому что, если вы начнете запись, а потом откроете лист и станете выделять ячейки, все эти действия будут записаны в макросе. В результате ваш макрос будет уж очень специфичен — он всегда будет открывать одну и ту же книгу, один и тот же лист и форматировать в нем одни и те же ячейки. Для того чтобы создать универсальный макрос
День 1-й. Начинаем
27
для форматирования клеток, вам нужно открыть книгу, выбрать в ней лист и выделить ячейки до начала записи.
Когда начальные условия созданы, можно приступать к записи макроса.
Начало заноси макроса
Для того чтобы начать запись макроса в программе Excel 2000, выберите в меню Сервис-Макрос-Начать запись. Excel раскроет диалоговое окно Запись макроса. В этом окне вы должны задать имя макроса и указать, где его сохранить. Указать имя и место сохранения нужно до того, как вы начнете запись макроса. В этом же окне вы можете задать комбинацию клавиш, которой макрос будет запускаться на выполнение.
НРИМЕЧАНИЕ
Имя макроса должно начинаться буквой, но потом может содержать и цифры. Оно не должно содержать пробелов и знаков пунктуации. Длина имени макроса не должна превышать 63 символов.
В диалоговом окне Запись макроса есть четыре управляющих элемента (само окно вы можете видеть на рис. 1.2).
•	Текстовое поле Имя макроса. Это первый параметр, который вам нужно указать в этом диалоговом окне. По умолчанию VBA предлагает вам имя, состоящее из слова Macro с последующей цифрой, соответствующей порядковому номеру создаваемого в этом сеансе работы макроса. Лучше заменить это имя на какое-нибудь осмысленное. Например, если вы создаете макрос, строящий диаграмму продаж на основании данных текущего листа, можете назвать этот макрос Диагр_продаж.
•	Текстовое поле Сочетание клавиш. В этом поле вы можете задать комбинацию клавиш, с помощью которой ваш макрос будет запускаться на выполнение. Просто введите в поле подходящий символ на клавиатуре. Этим есть смысл пользоваться только в том случае, если вы собираетесь применять свой макрос часто. Все комбинации клавиш для вызова макросов в программе Excel состоят из клавиши <Ctrl> и какого-нибудь символа. Если вы введете в этом поле букву <а>, комбинацией для вызова макроса будет <Ctrl+a>, если вы введете <А>, комбинацией будет <Ctrl+Shift+A>.
•	Список Сохранить в. В этом списке вы указываете, где должен быть сохранен новый макрос. Возможные варианты таковы: Личная книга макросов, Новая книга, Эта книга. Если вы выберете Личная книга макросов, ваш новый макрос будет записан в специальной книге с названием Personal.xls, которая открывается при каждом запуске программы Excel. Такой выбор уместен тогда, когда вы хотите, чтобы новый макрос был доступен при любом сеансе работы с Excel. Если вы выберете Эта книга, ваш макрос запишется в текущей открытой книге. Этот вариант годится, если вы хотите, чтобы макрос был доступен только при работе с этой книгой. Выбрав Новая книга, вы заставите Excel создать новую книгу и сохранить макрос в ней. При этом активной останется та книга, которая была у вас открыта перед записью макроса, и все ваши действия будут применены именно к ней, а не к новой книге. Независимо от того, в какой книге вы решите сохранить макрос, он будет записан в ней в виде модуля. Что такое модули, мы выясним на следующем уроке.
•	Текстовое поле Описание. Информация в этом поле макросом не используется, а служит для вас и тех, кто будет пользоваться вашим макросом. Здесь очень полезно указать, для чего макрос предназначен и что он делает. В начале записи Excel помещает сюда текущую дату и имя пользователя, которое указано на вкладке Общие диалогового окна Параметры в меню Сервис.
28
Неделя 1
Так надо
Всегда выбирайте для своих макросов осмысленные имена, чтобы по имени можно было судить о том, что делает макрос или для чего он предназначен. Такое имя, как FormatArialBoldl2 говорит больше, чем какое-нибудь Насго7.
Всегда вводите в поле Описание необходимые комментарии, например сведения о том, нужно ли перед за-! пуском макроса открыть определенную книгу или выделить какие-то ячейки.
Пробуем записать первый макрос
Давайте предположим, что вам часто приходится применять к некоторым ячейкам, к которым вы хотите привлечь внимание, особый формат шрифта, например Arial, полужирный, 12 пунктов. Вы не хотите пользоваться ни одним из стандартных стилей (доступных в меню Формат-Стиль), так как это повлияет на другие атрибуты ячеек, такие как дата или представление денежных величин, а вам нужно только менять вид символов. Приходится каждый раз делать это вручную.
Выбор шрифта Arial, указание его размера, применение атрибута полужирный — все это требует нескольких движений мышью или нажатий клавиш. Для экономии времени, нужного для форматирования текста, вы решили записать макрос, который выбирает шрифт Arial и делает все остальное в выделенных ячейках.
Давайте шаг за шагом рассмотрим все действия, необходимые для создания такого макроса.
Создаем начальные условия
Поскольку вы хотите, чтобы этот макрос работал с любыми выделенными ячейками, начальными условиями для него будут открытая книга и выделенный диапазон ячеек на активном листе. Для создания начальных условий нашего примера сделайте следующее.
1.	Запустите программу Excel 2000, если она еще не запущена.
2.	Откройте любую книгу.
3.	Выберите любой лист.
4.	Выделите любую ячейку. На рис. 1.1 вы видите пример такого листа с единственной выделенной ячейкой.
Таким образом, вы создали начальные условия для записи макроса, форматирующего текст в выделенных ячейках.
Выбираем имя и мести хранения макроса
Вы готовы к тому, чтобы начать запись макроса, задать для него имя и прочие необходимые параметры. Сделайте следующее.
1.	Выберите в меню Сервис-Макрос-Начать запись. Excel раскроет диалоговое окно Запись макроса.
2.	В текстовом поле Имя макроса введите в качестве имени макроса FormatArialBoldl2. Такое имя сразу скажет любому, для чего предназначен макрос.
День 1-й. Начинаем
29
£3 Microsoft Excel - ДеньО! xls

jEJSiefln Правка £ис Вставка Формат Сервис Денные £<но Д'равка jojrsfSasFiмИшы...............
___;•__ C-i		. ..—	,	«»	- *	КЯ-.»мх B-i_________________"«-. " Филиал
-adSj-Xj
- k • Ж X s ? __	.	~ф
г-А.	В	1 с	’ .‘D .
1 BLUE SKY AIRLINES			
»2ц Продажи ш	Филиал	JЯнварь	Февраль
S ;	Север	10111	13400
6 1	Юг	22100	24050
7	Восток	13270	15670
8	Запад	10800	21500
S JOj fij 12	’
13 14 15 16 17 18
М 4, >;M\sheetl/sheets/Sheets/	|«|
Готово
I иг NUM
Рис. 1.1. Начальные условия для записи макроса, форматирующего текст в ячейках
Запись макроса
Имя макроса:
Макрос 1
Сочетание клавиш: Сохранить^. ;	;
CtrH-| ' |эта книга
Описание:
1макрос записан 15.11.1999 (Alexander Kovalevsky)
OK I Отмена
Рис. 1.2. Диалоговое окно Запись макроса
3.	Оставьте без изменений текст, который Excel вставила в поле Описание, но добавьте следующее. Форматирует текст в ячейке шрифтом Arial, полужирный, 12 пунктов.
4.	В списке Сохранить в укажите, где должен быть сохранен макрос. Поскольку вы хотите, чтобы макрос был доступен при любом сеансе работы, выберите Личная книга макросов.
5.	Если вы уверены, что часто будете пользоваться этим макросом, можете присвоить ему комбинацию клавиш для быстрого вызова. В таком случае введите клавишу в поле Сочетание клавиш диалогового окна Запись макроса.
6.	Щелкните на кнопке ОК.
На рис. 1.3 вы видите окно Запись макроса с заполненными полями.
30
Неделя 1
(mewie
Когда вы выбираете в качестве места для хранения макроса Личная книга макросов, Excel сохраняет макрос в файле специальной книги с именем Personal.xls в папке, в которую установлена Excel. Excel автоматически открывает эту книгу каждый раз в начале работы. Поскольку вам всегда доступны макросы из всех открытых книг, макрос, сохраненный в книге Personal.xls, также будет доступен вам всегда. Если книги Personal.xls не существует, Excel создаст ее. Если при установке программы Excel вы принимали все стандартные параметры, то книга Personal.xls находится в папке C:\Program Files\Microsoft Office\Office\Xlstart.
Вы можете не заметить, что книга Personal.xls открыта, потому что Excel по умолчанию скрывает эту книгу после ее создания. (О скрытых книгах можете почитать в справочной системе Excel.)
Запись макроса
макроса:
|FormatAnalBoldl2|
Сочетание клавиш: Сохранить £:
Личная книга макросов
|Макрос записан 15.11.1999 (Alexander Kovalevsky) ^Форматирует текст в ячейке шрифтом Arial,
Рис. 1.3. Диалоговое окно Запись макроса с заполненными полями
Так надо
Сохраняйте макросы общего назначения в книге Personal.xls.
Сохраняйте макросы, относящиеся только к определенной книге, в самой этой книге.
Помните, что комбинация клавиш для вызова макроса - это сочетание клавиши <Crtl> и того символа, который вы указали при создании макроса. Если вы указали символ а, то комбинацией клавиш будет <Crtl+a>, а если вы указали А, то комбинацией клавиш будет <Crtl+Shift+a>.

Не присваивайте макросу комбинацию клавиш, если вы не уверены в том, что будете часто им пользоваться. Если вы станете присваивать комбинацию клавиш каждому макросу, у вас очень скоро не останется свободных комбинаций.
Записываем действия
Как только вы щелкнете на кнопке OK, Excel запустит программу записи макроса, откроет панель Остановить запись и запишет ваши действия. При этом будет записываться каждое ваше действие до тех пор, пока вы не остановите запись.
День 1-Й. Начинаем
31
На рис. 1.4 вы видите окно программы Excel и панель Остановить запись, которую Excel открывает при записи. (На этом рисунке записывается макрос FormatArialBoldl2.) Обратите внимание на то, что в левом нижнем углу экрана, на панели состояния, появляется слово Запись. Это должно напоминать вам, что записывается каждое ваше действие.
Q;Microsoft LxctJl - ДеньО! xls

у £>айл Праею Дид Вставке Формат Сервис Данные Qkho Справка 
2
3
А BLUE SKY AIRLINES Продажи
4 |	|Филиаг!	Январь	Февраль
5 .	Север	10111	13400
6	Юг	22100	24050
7	Восток	13270	15670
	Запад	10800	21500
D
В
9 ю"
11 1?
13 ’
14 И
16
18.
ИО М \sheetl <" Sheets / Sheets /
Готово Запись
Рис. 1.4. При записи макроса появляется панель Остановить запись и слово Запись на панели состояния
Панель Остановить запись (рис. 1.4) имеет две кнопки. Левая кнопка, Остановить запись, служит для того чтобы останавливать запись. Щелкните на ней, когда записываемые действия закончены. Правая кнопка — Относительная ссылка. По умолчанию Excel записывает в макросах абсолютные номера ячеек. Например, если вы начали запись макроса с ячейки В4, а потом выделили ячейку справа от нее, С4, то при работе макроса каждый раз так и будет выделяться ячейка С4.
Если же вы нажмете кнопку Относительная ссылка, то будут записываться относительные номера ячеек, т.е. в нашем случае запишется не С4, а сдвиг на 1 столбец и на О строк. Когда вы запустите макрос, выделится ячейка справа от текущей.
Кнопка Относительная ссылка — это переключатель, т.е. она имеет два положения: утопленное и приподнятое. Когда записываются абсолютные номера ячеек, кнопка Относительная ссылка выглядит плоской, и только при наведении на нее указателя приобретает рельефные очертания. Когда записываются относительные номера, кнопка утоплена. Каждый щелчок на этой кнопке переводит ее в противозположное положение. Переключать запись между абсолютной и относительной можно в любой момент на протяжении записи.
32
Неделя 1
Чтобы закончить с нашим макросом FormatArialBoldl2, сделайте следующее.
1.	Раскройте диалоговое окно Формат ячеек, выбрав в меню Формат-Ячейки.
2.	Щелкните на вкладке Шрифт. На рис. 1.5 показано окно Формат ячеек с открытой вкладкой Шрифт.
Формат ячеек
Шрифт; [Arial
Ж Albertus Extra Bold Ж Albertus Medium M Antique Olive
Начертание:
[полужирный (обычный курсив
Aria!
полужирный
Вздчеркмвание:
|Нет
I	курсив
Црет:
| Защита ] Еаэмер: '
 Видоизменение Г зачеркнутый П в^осний индекс Г нфкнийиндас
Образец
АаВЬБбЯя
Шрифт типа TrueType. Шрифт будет использован как для вывода на экран, таки для печати.
Рис. 1.5. Диалоговое окно Формат ячеек, заполненное
для записи макроса Format Arial Bold 12
3.	В списке Шрифт выберите Arial. Сделайте это, даже если этот шрифт уже у вас выбран.
4.	В списке Начертание выберите Полужирный.
5.	Выберите 12 в списке Размер.
6.	Примените все это к выделенной ячейке, щелкнув на кнопке ОК.
Вы можете проделать то же самое, т.е. выбрать шрифт, размер и начертание на панели инструментов. Как и ранее, проделать нужно все, даже если у вас в ячейке уже установлен нужный шрифт.
Останавливаем запись
Выполнив все, что вы хотели записать, вы должны остановить запись. Для этого нужно щелкнуть на кнопке Остановить запись на панели Остановить запись или выбрать в меню Сервис-Макрос-Остановить запись.
В случае с нашим макросом вы должны остановить запись сразу после щелчка на кнопке ОК, т.е. после закрытия окна Формат ячеек. Ваш новый макрос готов к употреблению. Как запускать макросы на выполнение, вы узнаете позже в этом уроке
День 1-й. Начинаем
33
Так надо
Не забывайте остановить запись макроса сразу, как только вы выполнили все действия, подлежащие записи. Если этого не сделать, все ваши действия будут продолжать записываться.
Так не надо
Не забывайте, что Excel записывает ваши макросы там. где вы указали при заполнении диалогового окна Запись макроса
Текст макроса
Когда вы записывает макрос в программе Excel, программа записи макросов записывает на языке Visual Basic все ваши действия, которые вы выполнили во время записи. Эту запись называют текстом макроса, или исходным текстом. Потом, когда вы запускаете макрос на выполнение, программа VBA считывает эти команды и выполняет их в том порядке, в каком вы их записали.
Ниже приведен текст, полученный при записи макроса FormatArialBoldl2. Обратите внимание на то, что в начале текста приведено имя макроса и его описание.
Sub FormatArialBoldl2()
'	FormatArialBoldl2 Макрос
'	Макрос записан 11.11.99 (Alexander Kovalevsky), Форматирует текст в ячейке шрифтом Arial,
'	полужирный, 12 пунктов.
With Selection.Font
.Name = "Arial"
.Fontstyle = "полужирный"
.Size = 12
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.Colorindex = xlAutomatic
End With
End Sub
He расстраивайтесь, если сейчас вы ничего не можете понять в этом тексте. Вы можете пользоваться макросом, даже не заглядывая в его текст и не зная, где он находится. На следующем уроке мы рассмотрим разные части макроса и вы узнаете, где и как искать его текст и как его редактировать.
ПРИМЕЧАНИЕ
Если при записи своего макроса вы делали что-то еще, кроме того, что было указано в пунктах нашего списка, текст вашего макроса будет отличаться от приведенного здесь. Кроме того, если вы делали то же самое, но с помощью панели инструментов, текст тоже будет отличаться, а именно будет заметно длиннее.
34
Неделя 1
Запуск макроса
После того как макрос записан, вы можете им пользоваться. Запуск макроса заставляет программу Excel выполнять все те инструкции, которые записаны в тексте макроса.
Для того чтобы в программе Excel запустить макрос, вы должны выбрать в меню Сервис-Макрос-Макросы. При этом раскроется окно, показанное на рис. 1.6. Выберите в списке Имя макроса нужный макрос и щелкните на кнопке Выполнить. Назначение остальных кнопок мы обсудим на следующих уроках.
|Макрос
Имя макроса г
Форматирует тест s ячей® шрифтом Arial,
Рис. 1.6. Выберите нужный макрос в окне Макрос. В этом списке перечислены только те макросы, которые сохранены в открытых книгах
Например, для того чтобы запустить только что записанный макрос, выделите ячейки, к которым вы хотите применить форматирование. Потом выберите в меню Зервис-Макрос-Макросы, раскрыв диалоговое окно Макрос. Выберите в списке Имя макроса макрос Personal.xls!FormatArialBoldl2. И наконец, запустите макрос, щелкнув на кнопке Выполнить. Текст в ячейках, которые были отмечены перед запуском макроса, будет отформатирован указанным шрифтом.
В списке диалогового окна Макрос приведены только макросы из открытых в дан-‘.ый момент книг, даже если эти книги скрыты (см. рис. 1.6). Если макрос находится не в той книге, с которой вы в данный момент работаете, то перед его именем в списке Имя макроса указывается имя книги, в которой он сохранен, как в случае с макросом Personal.xls!FormatArialBoldl2. Если в списке нет нужного вам макроса, вы должны перед раскрытием списка Имя макроса открыть книгу, в которой он сохранен.
Если в диалоговом окне Запись макроса вы назначили макросу комбинацию клавиш, то можете запустить этот макрос, нажав соответствующую комбинацию. Как и в :лучае с окном Макрос, через комбинацию клавиш доступны только те макросы, которые сохранены в одной из открытых книг (не обязательно в активной).
у ПРИМЕЧАНИЕ
Макрею программы Excel доступен только тогда, когда открыта книга, в которой он записан. Для этого книга не обязательно должна быть активной и не обязательно должна быть видимой.
2ень 1-й. Начинаем
35
Вы, наверно, уже знаете, что макрос в Excel можно запустить, создав для него кнопку на панели инструментов или соответствующий пункт меню. Вы можете также связать с макросом кнопку или другой графический объект, расположенный непосредственно в листе Excel. На уроке 17-го дня обучения мы поговорим о том, как создать для макроса кнопку панели инструментов или пункт меню и как связать макрос с объектом, расположенным в листе Excel. Кроме того, вы можете почитать об этом в справочной системе программы Excel.
Резюме
На этом уроке вы узнали кое-что из истории языка Visual Basic for Applications и о том, почему этот язык макросов развился настолько, что его уже можно считать полноценным языком программирования. Компания Microsoft значительно улучшила и расширила возможности языка макросов, приняв на вооружение для своих продуктов семейства Microsoft Office 2000 язык Visual Basic. Мощь языка макросов особенно возросла благодаря средствам проверки условий и циклических вычислений. Используя язык VBA, вы можете достичь в своих программах настоящей гибкости и универсальности.
Кроме того, в этом уроке вы научились записывать макрос и запускать его на выполнение. Этого уже достаточно для того, чтобы приступить к изучению языка VBA. Большую часть макросов, на примере которых вы будете изучать программирование на VBA, вы предварительно будете записывать. На следующем уроке вы узнаете, как редактировать записанный макрос.
Вопросы и ответы
Что такое приложение, поддерживающее VBA?
Так называют приложение, в которое встроена поддержка языка Visual Basic for Applications. Программы Microsoft Word, Excel, Access, Outlook и Project являются приложениями, поддерживающими язык VBA
У всех ли приложений, поддерживающих VBA, есть программа для записи макросов?
Нет. Например, в приложении Microsoft Access нет средства для записи макросов
Правда ли, что запись — это единственный способ создать макрос для программы Excel?
Нет. Вы можете создать макрос, просто написав его текст в модуле VBA. На следующем уроке речь пойдет о создании макросов без записи
При записи макроса в программе Excel обязательно ли держать на экране панель Остановить запись?
Нет. Если она вам мешает, щелкните в ее правом верхнем углу, и она закроется. Можно также выбрать в меню Вид-Панели инструментов и снять соответствующий флажок. В таком случае вам придется останавливать запись макроса через меню Сервис-Макрос-Остановить запись
Можно ли изменить имя макроса после того, как ои записан?
Можно, но для этого вам придется непосредственно отредактировать его текст. Речь об этом пойдет на следующем уроке. Но имейте в виду, что переименование макроса может повлечь за собой некоторые проблемы. Если вы успели создать для своего макроса кнопку, или пункт меню, или связанный с ним графический объект, то такой инструмент вызова будет по-прежнему искать макрос со старым именем. Переименовав макрос, вы должны будете внести изменения во все связанные с ним элементы. Поэтому лучше не полениться и дать макросу хорошее имя сразу при его создании. а не откладывать это на потом
36
Неделя 1
Можно ли назначить макросу комбинацию клавиш после того, как ои записан?
Можно. Для этого нужно раскрыть диалоговое окно Макрос. Выберите Сервис-Макрос-Макросы, укажите в списке Имя макроса нужный макрос и раскройте диалоговое окно Параметры макроса, щелкнув на кнопке Параметры. Введите комбинацию клавиш и щелкните на кнопке ОК
Могу ли я поместить свой макрос в меню или иа панели инструментов?
Конечно. Для того чтобы поместить макрос на панель инструментов Excel, выберите в меню Сервис-Настройка. Откроется диалоговое окно Настройка. Щелкните на вкладке Панели инструментов. Отметьте флажком нужную панель инструментов, чтобы она была видима в окне Excel. Потом щелкните на вкладке Команды. Отметьте категорию Макросы и перетащите свой макрос на нужную панель инструментов. Подробнее мы рассмотрим настройку меню и панелей инструментов на уроке 17-го дня
Если при записи макроса я назначил ему комбинацию клавиш, могу ли я позже удалить или изменить это назначение?
Можете. Для этого нужно раскрыть окно Параметры макроса соответствующего макроса и удалить данные в текстовом поле Сочетание клавиш. Вы можете также удалить макрос с панели инструментов, раскрыв окно Настройка и перетащив кнопку вашего макроса из панели инструментов куда угодно
Коллоквиум
В этом разделе вы найдете контрольные вопросы, которые помогут вам закрепить пройденный материал, а также упражнения, в ходе выполнения которых вы приобретете необходимый опыт. Постарайтесь разобраться и в вопросах, и в упражнениях, прежде чем переходить к следующему уроку. Ответы вы найдете в приложении в конце книги.
Твсш
1.	Почему постепенно пришлось перейти от простой записи макросов к языку программирования?
2.	Чем отличается Visual Basic for Applications от Visual Basic?
3.	Назовите три причины, по которым имеет смысл добавлять программные конструкции к записанному макросу.
4.	Можете ли вы поместить макрос на панель инструментов сразу же при его записи?
5.	Где Excel хранит записанные макросы?
6.	Как в программе Excel выполняется записанный ранее макрос?
7.	Как вы запускаете программу записи макросов?
8.	Где вы сохраните макрос, если вы хотите, чтобы он был доступен для всех открытых книг Excel в любом сеансе работы?
9.	Какие макросы вы увидите в списке диалогового окна Макрос?
2е-1ь 1-й. Начинаем
37
Упражнения
1.	Запишите новый макрос, сделав следующее.
В начале записи настройте диалоговое окно так, чтобы макрос сохранился в личной книге макросов.
Назовите макрос NewBook.
Запишите следующие действия. Создайте новую книгу, выбрав в меню Файл-Создать, затем сохраните эту книгу под именем NewWorkbook, выбрав в меню команды Файл-Сохранить как. Сразу после сохранения закройте книгу, выполнив команды Файл-Закрыть.
Остановите запись.
2.	С помощью программы Windows Explorer или просто в папке на рабочем столе удалите файл NewWorkbook.xls, который вы создали при записи макроса в упражнении 1. Теперь запустите записанный макрос. Что вы видите? {Подсказка. С помощью программы Explorer или просто выбрав Файл-Открыть посмотрите содержимое папки, в которой вы сохранили файл новой книги в упражнении 1.)
3.	Запустите макрос NewBook еще раз. Что произойдет? {Подсказка. Во всех диалоговых окнах шелкайте на кнопке Отмена или Закрыть.)
38
Неделя 1
Создаем а редактируем простой макрос
Вы уже умеете записывать и запускать макросы и понимаете, как важно уметь добавлять к макросам конструкции языка VBA. Теперь самое время научиться редактировать существующие макросы и создавать их без предварительной записи. На этом уроке мы рассмотрим следующие вопросы.
•	Как пользоваться редактором Visual Basic для создания и редактирования макросов.
•	С какими типами ошибок вы встретитесь при редактировании и выполнении макросов и как устранять ситуации, приведшие к этим ошибкам.
•	Как создавать в своих макросах простейшие сообщения для пользователя.
Знакомство со средой Visual Basic For Applications
На первом занятии вы научились записывать и выполнять макросы. Прежде чем приступать к созданию или редактированию макросов, следует узнать еще кое-что о том, где хранятся макросы. Вам необходимо также получить представление о редакторе VBA, его меню, командах и панелях инструментов и о том, как все это связано с программой Excel.
(ЯРИМЕЧЛИИЕ
В этой книге мы говорим исключительно о программировании VBA для Excel, поскольку Excel - очень распространенное, удобное и, как сейчас говорят, дружественное приложение для VBA-программирования. Изучить язык VBA для Microsoft Access вы можете по книге Роджера Дженнингса (Roger Jennings) Access Developer’s Guide (Руководство программирования для Access/
Что такое модули
Из предыдущего урока вы знаете, что макросы языка VBA хранятся в тех же файлах, что и данные приложения, в частности макросы программы Excel хранятся в файлах рабочих книг, а именно в особой части книги, называемой модулем. VBA-макрос представляет собой текст, в котором записаны инструкции макроса. Рабочая книга Excel может содержать несколько модулей. Модули, хранящиеся в одной книге, обычно называются проектом. Каждый модуль может содержать несколько макросов.
День 2-й. Создаем и редактируем простой макрос
39
ПРИМЕЧАНИЕ
Все приложения Microsoft Office, которые поддерживают VBA, такие как MS Word, Access, Project и им подобные, хранят текст макросов в стандартных файлах документов, MS Word, например, хранит текст макросов в документах или в шаблонах, Access хранит макросы в файлах базы данных.
Понимание того, как Excel дет модулям имена, поможет вам находить записанные макросы, когда вы захотите просмотреть их или отредактировать.
При создании макроса в программе Excel вы можете указать только книгу, в которой он должен быть сохранен, — текущую, новую или книгу Personal.xls. Модуль для сохранения файла Excel выбирает сама, и если нужно, создает его.
Когда Excel создает модуль для сохранения записанного макроса, она дает ему имя ModuleN, где N — порядковый номер модуля, созданного в данном сеансе работы. Например, когда вы первый раз сохраняете записанный макрос в личной книге макросов (с именем Personal.xls), Excel создает модуль с именем Modulel. Если вы продолжает создавать макросы и сохранять их в личной книге макросов, все они попадают в модуль Modulel, и так происходит до тех пор, пока вы не выберете для сохранения макроса другую книгу. Если позже в этом же сеансе работы вы опять выберете книгу Personal.xls, Excel создаст в этой книге новый модуль с именем Module2.
Если в книге уже есть модуль с таким именем, Excel увеличивает номер нового имени. Например, если вы открыли новый сеанс работы с Excel и создаете новый макрос, Excel попытается создать модуль с именем Modulel, поскольку это первый сохраняемый макрос в данном сеансе работы с Excel. Если же в этой книге уже есть модули с именем Modulel и Module2, Excel увеличивает номер нового модуля и записывает его под номером Module3.
ПРИМЕЧАНИЕ
По такому же правилу Excel выбирает имя для макроса, когда вы вставляете его через меню редактора Visual Basic командами Insert-Module (Вставка-Модуль). Ручная вставка модулей рассмотрена дальше в этом уроке.
Знакомство с редактором Visual Basic
Для того чтобы узнать, какие модули записаны в определенной книге и посмотреть текст этих модулей, вам понадобится редактор языка Visual Basic. Редактор Visual Basic — это инструмент для создания модулей и просмотра их содержимого, создания и редактирования текста макросов, создания диалоговых окон и решения других задач, с которыми вам приходится сталкиваться при работе с программами на языке Visual Basic. В дальнейшем мы будем называть этот редактор просто VB-редактором.
VB-редактор приложения Excel совершенно аналогичен VB-редакторам других приложений, поддерживающих язык VBA. Фактически, работая с Excel, вы пользуетесь тем же самым VB-редактором, что и при работе с Microsoft Word, Access и другими приложениями Office. Ниже мы расскажем, как запустить редактор Visual Basic, объясним назначение его окон и команд меню.
Запускаем редактор VB
Для того чтобы просмотреть текст модулей VBA, вы должны запустить редактор Visual Basic. Сделать это можно двумя способами.
•	Выберите в меню Tools-Macro-Visual Basic Editor (Сервис-Макрос-Редактор Visual Basic).
•	Нажмите <Alt+Fl 1>.
40
Неделя 1
Какой бы способ вы ни выбрали, запустится редактор Visual Basic. На рис. 2.1 вы видите окно этого редактора. (Окно с текстом макроса с правой стороны появится только после того, как вы откроете модуль, содержащий этот текст.)
Microsoft Visual Basic - PERSONAL.XLS - (Moduli (Code))
>4^ Die £dit У&Н hsert FQnrat Qebug Bun Tools Add-Ins Window Help т в	n .к хгч 
-~Jx!
(Project - VBAPfOject
j (General)
Окно проекта
в Sheetj (Shee
Q ThrsWorkbod J
- & VRAProject (PERS |
*j й Microsoft Excel оЖ1:
- ':~y Modules
[Modulel
Bw:-
(NameV
jModuiel Module Alphabetic | categorized |
Окно свойств
Окно программы
* j |FormatAfialBold12
Sub FormatArialBoldl2()
With Selection.Font .Name = "Arial" .Size = 12 .Strikethrough = False .Superscript = False .Subscript = False -OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .Colorindex = xlAutomatic
End With
Selection.Font.Bold = True
End Sub
□ т > ез
И
Puc. 2.1. Типичное окно редактора Visual Basic
Познакомимся с окном редактора Visual Basic
Окно редактора Visual Basic содержит три дочерних окна. Каждое из дочерних окон предоставляет некоторую информацию о вашем проекте. (Проектом называются модули и другие объекты, сохраненные в рабочей книге Excel или в шаблоне книги.) Обычно дочерние окна размещены у границы родительского окна {docked position) — правой, левой, верхней или нижней. По умолчанию дочерние окна редактора Visual Basic расположены у левой границы.
При желании, вы можете переместить любое окно в новое положение, просто перетаскивая его за панель заголовка — точно так же, как любое окно на рабочем столе Windows. Перетащив прижатое окно от границы, вы получите плавающее окно. (Плавающим называется окно, которое всегда находится поверх других окон.) Кроме того, вы можете менять размеры дочерних окон, перетаскивая их границы — так же, как вы это делаете с остальными окнами. Приведем описание окон редактора Visual Basic.
•	Окно Project (Проект). В окне Project вы видите древообразную структуру, описывающую ваш проект: открытые в данный момент книги, объекты, содержащиеся в этих книгах (модули, ссылки, формы и т.д.). Это окно предназначено для поиска модулей и других объектов в вашем проекте.
•	Окно Properties (Свойства). В окне Properties приведены свойства выбранного в данный момент объекта. Иногда, как на рис. 2.1, свойства объекта состоят только из его имени. (Вы узнаете больше о свойствах объектов на уроке 7-го дня.) На вкладке Alphabetic (По алфавиту) объекты приведены в алфавитном порядке. На вкладке Categorized (По категориям) объекты отсортированы по категориям. Сейчас вы можете не особенно напрягаться, пытаясь понять свойства объектов. Вы узнаете об этом больше, продвигаясь в естественном темпе.
День 2-й. Создаем и редактируем простой макрос
41
•	Окно Code (Программа). В окне Code вы видите текст макросов. Именно в этом окне вы будете читать, создавать и редактировать свои макросы.
Окна Project и Code требуют дополнительных пояснений, помимо приведенных выше описаний. В следующих разделах мы расскажем об окнах Project и Code.
Окно Project
По умолчанию окно Project выглядит подобно окну программы Explorer и отображает папки вашего проекта, как вы можете видеть на рис. 2.1. Щелчок на знаке “+” слева от элемента разворачивает данную ветвь дерева, а щелчок на знаке ” сворачивает эту ветвь.
Для того чтобы выбрать объект в окне Project, нужно щелкнуть на этом объекте. В режиме отображения, приведенном на рис. 2.1, объекты сгруппированы в папках в соответствии с их типом — объекты Excel, модули и ссылки (ссылки на рис. 2.1 не видны). В режиме отображения, приведенном на рис. 2.2, просто показаны все объекты, содержащиеся в проекте, без группировки. Для переключения режимов отображения щелкните на кнопке Toggle Folders (Папки).
Кнопка View Code
Project- День02 -
йй	———...........
VBAProject (PERSONAL.XLS) l-j День02 (День02.х15)
FtrstProgram SecondProgram
H] Sheetl (Sheetl)
О Sheets (Sheets)
B) Sheets (Sheets)
ThisWorkbook!
Кнопка View Object
Кнопка Toggle Folders
Puc. 2.2. Окно проекта при отключенном режиме отображения Folders
В окне Project, помимо кнопки Toggle Folders, есть еще две кнопки: View Code (Программа) и View Object (Объект). Кнопка View Object отображает объект, выбранный в окне Project, а кнопка View Code отображает текст модуля в окне Code.
(^РИМЕШИЕ	Работая с текстом макроса в окне Code, вы можете закрыть окна Project и Properties для того, чтобы иметь больше свободного места на экране для просмотра текста макроса. Любое из дочерних окон редактора Visual Basic - Project, Properties и Code - можно закрыть, щелкнув на кнопке закрытия окна в его правом верхнем углу (см. рис. 2.1 и 2.2). Потом можно повторно вывести на экран окно Project или Properties, щелкнув на соответствующей кнопке панели инструментов Visual Basic или выбрав в меню View-Project или View-Properties.
Окно Code	
На рис. 2.3 вы видите окно Code, размер которого увеличен по сравнению со стандартным так, чтобы можно было разместить в нем как можно больше текста и лучше были видны различные его свойства. (В данном случае в этом окне отображен модуль
42
Неделя 1
Module2 книги Personal.xls, в котором вы видите текст макросов, записанных вами на предыдущем уроке.)
По умолчанию модуль отображается в режиме Full Module View (Представление полного модуля). В этом режиме просмотра в окне отображается весь модуль со всеми входящими в него макросами. Каждый макрос отделяется от соседних тонкой чертой. Редактор Visual Basic также позволяет вам просматривать модуль в режиме Procedure View (Представление процедуры). Макрос иногда называют процедурой. На рис. 2.4 вы видите тот же самый модуль, но уже в режиме просмотра Procedure View. Режим просмотра можно менять, щелкая на кнопках в левом нижнем углу окна Code.
.	-lolxj,
[(General)	~y~] [l oimalAiialBrilrlV?r| w
With Selection.Font .Name = "Arial" .Fontstyle = "полужирный" .Size = 12 .Strikethrough = False .Superscript = False .Subscript = False -OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .Colorindex = xlAutomatic
End With
End Sub
Список Object
Список Procedure
Режим Full Module View
Режим Procedure View
Sub NewBook ()
’ NewBook Макрос
Рис. 2.3. Окно Code в режиме Full Module View
4 PERSONALXLS-Модуль! (Code)
j(Generai)
Sub FormatArialBoldi; ()
▼ | |FormatArialBold12 [(Declarations)
Forni3tArialBoid12
Mi
[NewBook
With Selection.Font "Ari< 1" полужирный
.Name
.Fontstyle = ---------.aiae - 1-2—
.Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False
_________.Underline = XlUnderlineStyleNone .Colorindex = xlAutomatic
End With
End Sub
й
Puc. 2.4. Тот же модуль в режиме Procedure View
День 2-й. Создаем и редактируем простой макрос
43
Глядя на рис. 2.4, обратите внимание на то, что в режиме Procedure View в окне Code виден текст только одного макроса. Для того чтобы посмотреть текст другого макроса, воспользуйтесь списком Procedure (Процедура), который показан раскрытым на рис. 2.4. В режиме Full Module View вы тоже можете воспользоваться этим списком для того, чтобы быстро перепрыгивать с одного макроса на другой.
Для выбора объекта, макросы которого вы хотите просмотреть или отредактировать, можете воспользоваться списком Object (Объект). В обычном случае, как с макросами, которые вы записали в ходе предыдущего урока, в списке Object у вас есть только один элемент — Generali. Вы узнаете больше об этом списке, читая книгу дальше.
Теперь, познакомившись с окном редактора Visual Basic, мы перейдем к описанию его панелей инструментов и меню, которые часто используются при работе с макросами.
Знакомимся с меню редактора Visual Basic
В этом и в следующем разделах мы рассмотрим меню и панель инструментов редактора Visual Basic. Не расстраивайтесь, если назначение некоторых команд и функций вам сначала не будет понятно. Почти все команды, о которых сейчас пойдет речь, выполняют действия, смысл которых вам пока не понятен. Позже, все эти команды будут рассмотрены в соответствующих уроках, а сейчас мы только познакомимся с существующими командами. Если вы любите приключения, можете попробовать на свой страх и риск выполнить некоторые из этих команд, но сначала убедитесь, что в текущей книге нет никаких важных данных.
^ПРИМЕЧАНИЕ
Не пытайтесь запомнить все команды и пункты меню, описанные в этом разделе. Большая часть этих команд вам пока еще незнакома. Вместо того чтобы пытаться все запомнить, постарайтесь лучше понять основные возможности редактора Visual Basic. Все рассматриваемые здесь команды будут изучены подробнее в соответствующем уроке. Воспринимайте изложенный здесь материал как обзор или справочное пособие, которое может пригодиться при работе с более сложными вещами.
Меню File (Файл). В меню File, как и во всех приложениях для Windows, вы найдете команды, относящиеся к открытию и сохранению файлов. В меню File редактора Visual Basic находятся команды, которые позволяют открывать макросы, сохранять изменения и печатать текст макросов. В табл. 2.1 приведены краткое описание команд, их назначение и соответствующие им комбинации клавиш.
Таблица 2.1. Команды меню File
Команда	Комбинация клавиш	Действие
Save (Сохранить)	<Ctrl+S>	Сохраняет на диске текущий проект, включая все модули и формы
Import File (Импорт файла)	<Ctrl+M>	Добавляет существующий модуль, форму или класс к текущему проекту. Добавлять можно только те модули, формы или классы, которые были ранее сохранены командой Export File из другого проекта
Export File (Экспорт файла)	<Ctrl+E>	Сохраняет модуль, форму или класс из текущего проекта в текстовом формате для последующего импортирования или для архивирования
44
Неделя 1
Окончание табл. 2.1
Команда	Комбинация Действие клавиш
Remove (Удалить)	Навсегда удаляет из текущего проекта отмеченный модуль или форму. Эта команда недоступна, если ни один элемент не выделен
Print (Печать)	<Ctrl+P>	Печатает модуль или форму для отчета и архи- вирования
Close and Return (Закрыть и вернуться)	<Alt+Q>	Закрывает редактор Visual Basic и возвращает вас в программу, из которой он был вызван
ПРИМЕЧАНИЕ
О том, что такое формы и как они применяются для создания диалоговых окон, вы узнаете на уроке 16-го дня.
Меню Edit (Правка). В меню Edit собраны команды, предназначенные для редактирования текста макросов в окне Code. В табл. 2.2 собраны команды меню Edit и приведено их описание и указаны соответствующие комбинации клавиш.
Таблица 2.2. Команды меню Edit		
Команда	Комбинация клавиш	Действие
Undo (Отменить) Redo (Повторить)	<Ctrl+Z>	Отменяет последнюю команду. Отменить можно не всякую команду Повторяет последнюю выполненную команду
Cut (Вырезать)	<Ctrl+X>	Вырезает выделенный текст или объект и помещает его в буфер обмена. Выделенный текст удаляется из модуля или формы
Сору (Копировать)	<Ctrl+C>	Копирует выделенный текст или объект и помещает его в буфер обмена. Выделенный текст остается в модуле или форме без изменений
Paste (Вставить)	<Ctrl+V>	Вставляет текст или объект из буфера обмена в текущий модуль или форму
Clear (Очистить)	<Dei>	Удаляет выделенный текст или объект из модуля или формы
Select АП (Выделить все)	<Ctrl+A>	Выделяет весь текст в модуле или объекты в форме
Find (Найти)	<Ctrl+F>	Подобно команде Find в программе Excel, позволяет найти нужный фрагмент в тексте
Find Next (Найти следующий)	<F3>	Повторяет последнюю операцию поиска
Replace (Заменить)	<Ctrl+H>	Подобно команде Replace в программе Excel, позволяет найти нужный фрагмент в тексте и заменить его новым
День 2-й. Создаем и редактируем простой макрос
45
Окончание табл. 2.2
Команда	Комбинация клавиш	Действие
Indent (Увеличить отступ)	<ТаЬ>	Увеличивает отступ выделенного текста на одну позицию табуляции
Outdent (Уменьшить отступ)	<Shift+Tab>	Уменьшает отступ выделенного текста на одну позицию табуляции
List (Список свойств/методов)	<Ctrl+J>	Раскрывает список Properties/Methods в окне Code, отображая свойства и методы объекта, имя которого вы ввели. Если курсор находится на пустом месте в окне Code, выводится список всех свойств и методов
List Constants (Список констант)	<Ctri+Shift+J>	Раскрывает список всех возможных значений свойства, имя которого вы ввели перед знаком равенства
Quick Info (Сведения)	<Ctrl+I>	Выводит окно подсказки, содержащее правильный синтаксис процедуры, функции или метода, имя которого вы ввели в окне Code
Parameter Info (Параметры)	<Ctrl+Z>	Выводит окно подсказки, содержащее список параметров (аргументов) процедуры, функции или метода, имя которого вы ввели в окне Code
Complete Word (Завершить слово)	<Ctri+npo6en>	Завершает не до конца набранное ключевое слово, если вы ввели достаточно символов для того, чтобы Visual Basic смог распознать это слово
Bookmarks (Закладки)		Раскрывает дополнительное меню, позволяющее вставить, удалить или перейти на закладку, ранее вставленную в вашем модуле. Закладки в Visual Basic не имеют имен
ПРИМЕЧАНИЕ
Вы подробно познакомитесь со свойствами и методами на уроках 7-го и 19-го дней.
Меню View (Вид). В меню View собраны команды, позволяющие выбирать, какой объект или элемент редактора Visual Basic вы хотите посмотреть, а также режим просмотра. В табл. 2.3 собраны команды меню View, приведено их описание и указаны соответствующие комбинации клавиш.
46
Неделя 1
Таблица 2.3. Команды меню View
Команда	Комбинация Действие
клавиш
Code (Программа)	<F7>	Активизирует окно Code, содержащее исходный текст выделенного модуля или формы
Object (Объект)	<Shift+F7>	Отображает объект, выделенный в окне Project
Definition (Описание)	<Shift+F2>	Отображает текст процедуры или функции, на имени которой стоит курсор
Last Position	<Ctrl+Shift	Возвращается в последнюю позицию в модуле
(Вернуться к последней позиции)	+F2>	после использования команды Definition или после редактирования текста
Object Browser (Просмотр объектов)	<F2>	Раскрывает окно Object Browser, в котором вы можете видеть все доступные в данный момент макросы. Позже в ходе этого урока вы узнаете, как пользоваться окном Object Browser. На следующих уроках вы познакомитесь с этим окном подробнее
Immediate Window (Окно отладки) Locals Window (Окно локальных переменных) Watch Window (Окно контрольных значений)	<Ctrl+G>	Раскрывает окно отладчика редактора Visual Basic Раскрывает окно локальных переменных редактора Visual Basic Раскрывает окно для просмотра значений переменных
Call Stack (Стек вызова)	<Ctrl+L>	Отображает стек вызовов данной функции или процедуры
Project Explorer (Окно проекта)	<Ctrl+R>	Раскрывает окно Project
Properties Window (Окно свойств) Toolbox (Панель элементов) Tab Order (Последовательность перехода) Toolbars (Панели инструментов)	<F4>	Раскрывает окно Properties Раскрывает панель Toolbox. Панель Toolbox применяется для создания управляющих элементов в диалоговых окнах. О том, как пользоваться этой панелью, вы узнаете на уроке 16-го дня обучения Раскрывает диалоговое окно Tab Order. Диалоговое окно Tab Order применяется для создания управляющих элементов в диалоговых окнах. О том, как пользоваться этим окном, вы узнаете на уроке 16-го дня нашего учебного курса Раскрывает дополнительное меню, с помощью которого вы можете убирать с экрана или выводить на экран различные панели инструментов или раскрыть диалоговое окно Customize (Настройка) для настройки панелей инструментов
День 2-й. Создаем и редактируем простой макрос
47
Окончание табл.2.3
Команда	Комбинация Действие клавиш
<Исходное приложение>	<Alt+Fll> Возвращает вас в приложение, из которого вы вызвали редактор Visual Basic, сам редактор при этом остается открытым. Название этого пункта меню зависит от того, из какого приложения вы вызвали редактор Visual Basic
Меню Insert (Вставка). Команды меню Insert предназначены для вставки различных объектов в ваш проект, таких как модули или формы. В табл. 2.4 собраны команды меню Insert и приведено их описание.
Таблица 2.4. Команды меню Insert
Команда	Действие
Procedure (Процедура) UserForm	Вставляет процедуру Sub, Function или Property в текущий модуль. (Процедура — это то же самое, что и макрос) Добавляет новую форму в проект. (Формы применяются для создания диалоговых окон.) Создание диалоговых окон мы рассмотрим на уроке 16-го дня
Module (Модуль)	Добавляет новый модуль в проект. Редактор Visual Basic дает этому модулю имя в соответствии с правилами, оговоренными выше
Class Module (Модуль класса) File	Добавляет новый модуль класса в проект. Модуль класса применяется для создания новых объектов Позволяет вставить в модуль часть исходного текста из текстового файла
Меню Format (Формат). Команды меню Format используются, когда вы создаете собственные диалоговые окна и другие формы. Эти команды применяются для того, чтобы выравнивать объекты на форме относительно друг друга, менять размеры управляющих элементов и для многого другого. В этом разделе мы рассматриваем команды меню Format просто для последовательности изложения; вы не будете пользоваться этими командами, пока не научитесь создавать собственные диалоговые окна (на уроке 16-го дня). В табл. 2.5 собраны команды меню Format и приведено их описание. Командам этого меню не соответствуют никакие комбинации клавиш.
Таблица 2.5. Команды меню Format
Команда	Действие
Align (Выровнять)	Раскрывает дополнительное меню, содержащее команды для выравнивания выделенных объектов на форме друг относительно друга. Вы можете выровнять объекты по правому краю, по левому, по верхнему или нижнему или по центру выбранного объекта
Make Same Size (Выровнять размер)	Раскрывает дополнительное меню, содержащее команды для придания выделенным объектам нужного размера
48
Неделя 1
Окончание табл.
Команда	Действие
Size to Fit (Подогнать размер) Size to Grid (Выровнять размер по сетке)	Автоматически подбирает высоту и ширину выделенных объектов в соответствии с размером их надписей Автоматически подбирает высоту и ширину выделенных объектов в соответствии с размером ячейки. (При создании форм редактор Visual Basic отображает сетку на форме, в ячейках которой располагаются управляющие элементы)
Horizontal Spacing (Интервал по горизонтали)	Раскрывает дополнительное меню, содержащее команды для подбора горизонтального интервала между выделенными элементами. Вы можете сделать горизонтальный интервал равномерным, увеличить его, уменьшить или убрать его вовсе
Vertical Spacing (Интервал по вертикали)	Раскрывает дополнительное меню, содержащее команды для подбора вертикального интервала между выделенными элементами. Вы можете сделать вертикальный интервал равномерным, увеличить его, уменьшить или отказаться от него вовсе
Center in Form (Разместить по центру в форме) Arrange Buttons (Разместить кнопки) Group (Группировать)	Раскрывает дополнительное меню, содержащее команды для центрирования выделенных объектов на форме по горизонтали или вертикали Раскрывает дополнительное меню, содержащее команды для равномерного выравнивания кнопок на форме вдоль нижнего или правого края формы Объединяет выделенные объекты в группу для того, чтобы их можно было совместно перемещать, менять их размеры, копировать или удалять как единый объект
Ungroup (Разделить) Order (Порядок)	Разъединяет группу, созданную предыдущей командой, на независимые объекты Раскрывает дополнительное меню, содержащее команды для изменения порядка перекрывающих друг друга объектов (этот порядок называют Z-порядком). С помощью этих команд можно сделать, например, так, чтобы кнопка всегда отображалась сверху некоторого графического объекта
Меню Debug (Отладка). Команды меню Debug используются для проверки и отладки макросов. (Отладкой называется процесс поиска и исправления ошибок в программе.) Вы узнаете, как пользоваться этими командами и как отлаживать макросы, на уроке 16-го дня. В табл. 2.6 собраны команды меню Debug, приведено их описание и указаны соответствующие комбинации клавиш.
Таблица 2.6. Команды меню Debug
Команда	Комбинация Действие клавиш
Compile (Компилировать) Step Into (Шаг с заходом)	Компилирует проект, выделенный в окне Project <F8>	Выполняет одну инструкцию вашего макроса с заходом в процедуру, если такая встретится
День 2-й. Создаем и редактируем простой макрос
49
Окончание талбл. 2.6
Команда	Комбинация клавиш	Действие
Step Over (Шаг с обходом)	<Shift+F8>	Выполняет одну инструкцию вашего макроса без захода в процедуру
Step Out (Шаг с выходом)	<Ctrl+Shift+ F8>	Выходит из процедуры в место ее вызова
Run to Cursor (Выполнить до текущей позиции) Add Watch (Добавить контрольное значение)	<Ctrl+F8>	Выполняет текст макроса от текущей позиции до курсора Позволяет вам указать переменную или выражение, значения которых вы хотите контролировать
Edit Watch (Изменить контрольное значение)	<Ctrl+W>	Позволяет вам изменить выражение, значения которого вы хотите контролировать
Quick Watch (Контрольное значение)	<Shift+F9>	Показывает значение выделенного выражения
Toggle Breakpoint (Точка останова)	<F9>	Отмечает (или снимает отмеченное) место в вашем тексте, в котором вы хотите приостановить выполнение программы (такое место называют точкой останова)
Clear All Breakpoints (Снять все точки останова)	<Ctrl4-Shift+ F9>	Снимает все точки останова в вашем модуле
Set Next Statement (Задать следующую инструкцию) Show Next Statement (Показать следующую инструкцию)	<Ctrl+F9>	Позволяет вам изменить порядок выполнения текста программы, указав следующую подлежащую выполнению инструкцию Подсвечивает (выделяет цветом) инструкцию, которая будет выполнена при следующем шаге
Команды меню Debug позволяют вам строго контролировать выполнение программы, останавливая ее в указанных точках, выполняя программу в пошаговом режиме, следя за значениями выбранных переменных. Подробнее эти команды будут рассмотрены на уроке 15-го дня.
Меню Run (Запуск). Команды меню Run служат для запуска макроса на выполнение, для прерывания и возобновления работы макроса, для восстановления начальных условий перед повторным выполнением. Некоторые команды этого меню вы изучите в ходе этого урока, а остальные — на уроке 15-го дня. В табл. 2.7 собраны команды меню Run, приведено их описание и указаны соответствующие комбинации клавиш.
50
Неделя 1
Таблица 2.7. Команды меню Run
Команда	Комбинация Действие клавиш
Run Sub/User Form (Запуск подпро-граммы/User Form)	<F5>	Редактор Visual Basic запускает макрос, кото- рый вы сейчас редактируете, т.е. тот макрос, в тексте которого находится текстовый курсор. Если активна форма, Visual Basic запускает форму
Break (Прервать)	<Ctrl+Break> Прерывается выполнение вашего макроса, и редактор Visual Basic переходит в режим останова (break mode). Режим останова мы рассмотрим подробнее на уроке 15-го дня
Reset (Сброс)	Сбрасывает значения всех переменных модуля и очищает стек вызовов. Переменные модуля будут рассмотрены на уроке 3-го дня учебного курса, а стек вызовов — на уроке 15-го дня
Design Mode (Конструктор)	Включает или отключает режим конструктора для всего проекта. В режиме конструктора не выполняются никакие макросы и не обрабатываются никакие системные события
Меню Tools (Сервис). Команды меню Tools позволяют вам выбрать макрос для выполнения и, кроме того, получить доступ к внешним библиотекам макросов и форм. В дополнение к этому, из меню Tools вы можете вызвать диалоговое окно Options для настройки параметров проекта, выделенного в окне Project. В табл. 2.8 собраны команды меню Tools и приведены их описание и соответствующие комбинации клавиш.
Таблица 2.8. Команды меню Tools
Команда	Действие
References (Ссылки)	Раскрывает диалоговое окно References, в котором вы можете установить ссылки на библиотеки объектов, библиотеки типов и на другой VBA-проект. После установления ссылок объекты, методы, свойства, процедуры и функции становятся доступными в окне Object Browser
Additional Controls (Дополнительные элементы )	Раскрывает диалоговое окно Additional Controls, с помощью которого вы можете добавить на панель инструментов дополнительные кнопки, предназначенные для включения в форму новых управляющих элементов. С помощью таких же кнопок вы можете вводить в форму вставляемые объекты, такие как лист Excel или документ Word
Macros (Макросы)	Раскрывает диалоговое окно Macro, с помощью которого вы можете создавать, редактировать, запускать или удалять макросы
Options (Параметры)	Раскрывает диалоговое окно Options, с помощью которого вы можете устанавливать различные параметры редактора Visual Basic, такие как ширина интервала табуляции, проверка синтаксиса инструкций, размеры ячейки в форме и пр. Мы будем знакомиться с окном Options по мере изучения новых тем
День 2-й. Создаем и редактируем простой макрос
51
Окончание табл. 2.8
Команда	Действие
Properties (Свойства)	Раскрывает диалоговое окно Object Properties, с помощью которого вы можете устанавливать различные свойства проекта, такие как его имя, описание, файл справки и т.д. В этом же окне вы можете установить защиту своего проекта с помощью пароля, запретив его редактирование
Digital Signature (Цифровая подпись)	Раскрывает диалоговое окно Digital Signature, с помощью которого вы можете посмотреть цифровую подпись проекта или установить ее
(Примечание
Список дополнительных управляющих элементов, которые вы можете установить с помощью диалогового окна Additional Controls, зависит от того, какие программы установлены на вашем компьютере. Обычно в этом списке вам доступны управляющие элементы ActiveX, полученные от независимых производителей или входящие в состав какого-либо приложения.
Цифровая подпись и сертификат - это новые средства, включенные в Microsoft Office, с помощью которых разработчик может защитить свой программный продукт, а пользователь — проверить подлинность продукта и запускать на выполнение только программы и макросы, полученные из надежных источников. Подробнее речь об этом пойдет на уроке 21-го дня.
Другие меню. Имеется еще три меню в редакторе Visual Basic: Add-Ins (Надстройки), Window (Окно) и Help (Справка). В меню Add-Ins есть только один пункт — Add-In Manager. Эта команда раскрывает диалоговое окно Add-In Manager. С помощью этого окна вы можете добавлять или удалять программы-надстройки редактора Visual Basic. Подобно программе Excel, редактор Visual Basic в составе Office 2000 позволяет использовать программы-надстройки, расширяющие и улучшающие свойства Visual Basic. Такие программы-надстройки вы можете получить у независимых разработчиков программного обеспечения.
Меню Window и Help совершенно аналогичны соответствующим меню других приложений Windows. Команды меню Window позволяют выбрать активное окно, разделить текущее окно на части, расположить дочерние окна горизонтально, вертикально или каскадом либо расположить значки свернутых окон вдоль границы главного окна. Мы не будем рассматривать подробно меню Window, поскольку оно работает совершенно так же, как в программах Word, Excel и в других приложениях Windows.
Меню Help также мало чем отличается от аналогичного меню программ Word, Excel и других. Работая с редактором Visual Basic, вы можете воспользоваться помощником Office или просто получить справку по работе с Visual Basic для того приложения, из которого вы его вызвали. Если у вас есть модем и подключение к Internet, вы можете через меню Help-MSDN on the Web получить доступ ко многим Web-страницам, содержащим важную и полезную информацию о работе с продуктами Microsoft, в том числе и о программировании на VBA. Последняя команда в меню справки — это About Microsoft Visual Basic (О программе). По этой команде раскрывается диалоговое окно, содержащее информацию об авторских правах на программу Visual Basic. Кроме того, в этом окне имеется кнопка с надписью System Info (О системе). Щелкнув на этой кнопке, вы раскроете окно, содержащее информацию о
52
Неделя 1
вашем компьютере, такую как версия операционной системы, тип видеосистемы, поддержка звука, какие принтеры подключены к компьютеру, какие программы установлены, и какие из них загружены в память в данный момент. Мы подробнее познакомимся с этим окном, когда будем изучать технологию OLE и автоматизацию, а именно на уроке 20-го дня.
Панели инструментов редактора Visual Basic
Большинству пользователей легче щелкнуть мышкой на кнопке, чем выбрать команду из меню. Поэтому в редакторе Visual Basic для наиболее часто используемых команд предусмотрены кнопки на панели инструментов. Если вы интенсивно работаете с Visual Basic, то наверняка заметили, что кнопки намного ускоряют работу.
Обычно Visual Basic отображает только одну панель инструментов — стандартную, которую вы можете видеть на рис. 2.5. Но, помимо стандартной панели инструментов, есть еще три панели — Edit, Debug и UserForm. С панелью Debug вы познакомитесь на уроке 15-го дня, а с панелью UserForm — на уроке 16-го дня.
На панели инструментов Edit имеется несколько кнопок, которые могут очень пригодиться при работе с исходным текстом макроса в окне Code. Более того, на этой панели есть даже несколько кнопок, которых вы не найдете в меню Edit. Мы рассмотрим панели инструментов чуть позже в ходе этого урока.
Для того чтобы выбрать, какую панель инструментов отображать, а какую нет, вы должны воспользоваться командой меню View-Toolbars. Поскольку панель Edit по умолчанию не отображается, вам придется вывести ее на экран самостоятельно. Может случиться так, что кто-то уберет с экрана стандартную панель инструментов, тогда вам вручную придется восстановить на месте и ее. Для того чтобы убрать или вывести на экран какую-то панель инструментов, сделайте следующее.
1. Выберите в меню View-Toolbars. При этом вы увидите дополнительное меню со списком всех доступных панелей инструментов.
2. Щелкните на имени нужной панели инструментов. Например, щелкните на строке Edit. Visual Basic отобразит выбранную панель.
Есть и другой способ добраться до списка панелей инструментов. Просто щелкните правой кнопкой на любой видимой панели инструментов, и вы увидите вспомогательное меню, в котором сможете сделать свой выбор.
Обычно редактор Visual Basic отображает стандартную панель инструментов прижатой к верхней границе окна (посмотрите на рис. 2.1, где показано окно редактора со стандартной панелью инструментов). Но вы можете расположить любую панель инструментов где угодно, в любом месте окна. Такую панель инструментов называют плавающей. Она выглядит как полноценное окно, имеет границы и строку заголовка. Вы можете видеть плавающую стандартную панель на рис. 2.6. В редакторе Visual Basic с плавающими панелями обращаются точно так же, как в программе Excel или в любой другой — ее можно перетаскивать, менять размеры и форму. Вы можете прочитать об этом в любом учебнике по работе с программой Excel.
Сейчас мы рассмотрим команды стандартной панели инструментов, а позже — команды панели Edit.
Стандартная панель инструментов. На стандартной панели инструментов в редакторе Visual Basic имеется 18 кнопок. Каждой кнопке соответствует некоторая команда меню. В табл. 2.9 приведено описание команд, связанных с кнопками стандартной панели инструментов. Изучая табл. 2.9, смотрите на рис. 2.5, чтобы понять, какой рисунок на кнопке соответствует той или иной команде.
День 2-й. Создаем и редактируем простой макрос
53
Вставка объекта
Запуск подпрограммы Вырезать
Отменить
Вставить
Найти
Помощник по Office проекта
Панель
Уй'вЯ’ G
| оА
Окно Сброс
!но
Дополнительные кнопки элементов	I
свойств Позиция курсора
Сохранить
Копировать
Главное приложение	Повторить " Просмотр объектов
Рис. 2.5. На стандартной панели инструментов расположены кнопки, соответствующие часто используемым командам
Прервать
Конструктор
М
(Примечание	Вид и состав любой панели инструментов настраивается так же, как вы это делаете в программе Excel. В табл. 2.9 и 2.10 приведено описание кнопок, а на рис. 2.5 и 2.6 -внешний вид стандартной панели и панели Edit в их обычной, неизмененной конфигурации. Если вы или кто-то другой изменили конфигурацию одной из этих панелей, ее вид может не соответствовать приведенному на рисунке. Читайте справку по программе Visual Basic для получения подробной информации.
Таблица 2.9. Кнопки стандартной панели инструментов	
Кнопка	Действие
View (Вид) <приложение>	Переключает вас на исходное приложение, из которого был вызван редактор Visual Basic. Рисунок на этой кнопке зависит от того, из какого приложения вы запускали Visual Basic. На рис. 2.5 Visual Basic был вызван из программы Excel
Insert (Вставить) <объект>	Щелкните на стрелке справа от кнопки, и вы увидите список объектов, которые можно вставить в ваш проект. Это объекты UserForm, Module, Class Module (Модуль класса) или Procedure. Действует аналогично меню Insert
Save (Сохранить)	Сохраняет текущий проект. Действует аналогично меню File-Save
Cut (Вырезать)	Вырезает выделенный текст и помещает его в буфер обмена. Действует аналогично меню Edit-Cut
Сору (Копировать)	Копирует выделенный текст и помещает его в буфер обме- на. Действует аналогично меню Edit-Copy	
Paste (Вставить)	Вставляет текст из буфера обмена в позицию курсора. Действует аналогично меню Edit-Paste
Find (Найти)	Раскрывает диалоговое окно для поиска текста. Действует аналогично меню Edit-Find
Undo (Отменить)	Отменяет последнюю команду. Отменить можно не всякую команду. Действует аналогично меню Edit-Undo
Redo (Повторить)	Повторяет последнюю выполненную команду. Действует аналогично меню Edit-Redo
Run (Запуск подпрограммы)	Запускает макрос, который вы сейчас редактируете, т.е. тот макрос, в тексте которого находится текстовый курсор. Действует аналогично меню Run-Sub/UserForm
54
Неделя 1
Окончание табл. 2.9
Кнопка	Действие
Break (Прервать)	Прерывается выполнение макроса. Действует аналогично меню Run-Break
Reset (Сброс)	Сбрасывает значения всех переменных модуля и очищает стек вызовов. Действует аналогично меню Run-Reset
Design Mode (Конструктор) Project Explorer (Окно проекта) Properties Window (Окно свойств) Object Browser (Просмотр объектов) Toolbox (Панель элементов) Office Assistant (Помощник по Office) Cursor Position (Позиция курсора)	Включает или отключает режим конструктора. Действует аналогично меню Run-Design Mode Раскрывает окно Project. Действует аналогично меню View-Project Explorer Раскрывает окно Properties. Действует аналогично меню View-Properties Window Раскрывает окно Object Browser. Действует аналогично меню View-Object Browser Раскрывает панель инструментов Toolbox. Действует аналогично меню View-Toolbox Вызывает окно справки. Действует аналогично меню Help-Microsoft Visual Basic Help Эта область на панели не является командной кнопкой. Здесь просто указывается позиция курсора в текстовом окне Code, а именно номер строки и столбца. На рис. 2.5 позиция курсора соответствует 3-й строке и 4-му столбцу
More Buttons (Дополнительные кнопки)	Щелкнув на стрелке справа от кнопки, вы увидите дополнительное меню с командами, для которых вы можно создать кнопки на панели инструментов
ПРИМЕЧАНИЕ
Как и в случае с меню, не расстраивайтесь, если вам пока не понятны назначения некоторых кнопок редактора Visual Basic. На каждом уроке полностью рассматриваются все относящиеся к этому уроку команды и кнопки. Цель настоящего урока состоит только в том, чтобы познакомить вас с тем, что доступно при работе с редактором Visual Basic.
Панель инструментов Edit (Правка). По мере того как вы будете набираться знаний по программированию в Visual Basic и работать с текстом макросов в окне Code, вы убедитесь, что панель инструментов Edit значительно экономит время и силы, создавая вам доступ к часто используемым командам. Редактор Visual Basic не показывает эту панель автоматически, вам следует вывести ее на экран, выполнив действия, описанные выше. В табл. 2.10 приведено описание команд, связанных с кнопками панели инструментов Edit. Изучая табл. 2.10, смотрите на рис. 2.6, чтобы понять, какой рисунок на кнопке соответствует той или иной команде.
Вы можете изменить стандартное расположение панели инструментов Edit или любой другой, перетаскивая ее по экрану к любой из его границ.
День 2-й. Создаем и редактируем простой макрос
55
Увеличить отступ
Раскомментировать блок
Список констант
Уменьшить отступ
Параметры I
—I	|_______I
Edii
Закладка	„
I |—— ------Предыдущая закладка
Л •-	:----Снять все закладки
Сведения
Список свойств/методов
Точка останова
Завершить слово
Закомментировать блок
Следующая закладка
Рис. 2.6. На панели инструментов Edit расположены кнопки, соответствующие часто используемым командам
Таблица 2.10. Команды панели инструментов Edit
Кнопка	Действие
List Properties/Methods (Список свойств/методов) List Constants (Список констант) Quick Info (Сведения) Parameter Info (Параметры)	Раскрывает список Properties/Methods. Действует аналогично меню Edit-List Properties/Methods Раскрывает список всех возможных значений свойства. Действует аналогично меню Edit-List Constants Выводит окно подсказки, содержащее правильный синтаксис процедуры. Действует аналогично меню Edit-Quick Info Выводит список параметров (аргументов) процедуры, функции или метода. Действует аналогично меню Edit- Parameter Info
Complete Word (Завершить слово) Indent(Увеличить отступ) Outdent (Уменьшить отступ) Toggle Breakpoint (Точка останова) Comment Block (Закомментировать блок)	Завершает не до конца набранное ключевое слово. Действует аналогично меню Edit-Complete Word Увеличивает отступ выделенного текста на одну позицию табуляции. Действует аналогично меню Edit-Indent Уменьшает отступ выделенного текста на одну позицию табуляции. Действует аналогично меню Edit-Outdent Отмечает точку останова в тексте. Действует аналогично меню Debug-Toggle Breakpoint Преобразует выделенный текст в окне Code в комментарии, добавляя в начале каждой строки знак комментария. Аналогичной команды нет ни в одном другом меню. Комментарии — в следующих разделах урока
Uncomment Block (Раскомментировать блок ) Toggle Bookmark (Закладка)	Удаляет знак комментария в начале каждой строки выделенного текста. Аналогичной команды нет ни в каком другом меню Устанавливает или снимает закладку в тексте макроса. Действует аналогично меню Edit-Bookmarks-Toggle Bookmark
56
Неделя 1
Окончание табл. 2.10
Кнопка
Next Bookmark (Следующая закладка)
Previous Bookmark (Предыдущая закладка)
Clear All Bookmarks
(Снять все закладки)
Действие
Перемещает курсор в окне Code к следующей закладке. Действует аналогично меню Edit-Bookmarks-Next Bookmark
Перемещает курсор в окне Code к предыдущей закладке. Действует аналогично меню Edit-Bookmarks-Previous Bookmark
Удаляет все закладки в модуле. Действует аналогично меню
Edit-Bookmarks-Clear All Bookmarks
ПРИМЕЧАНИЕ
Все приложения, поддерживающие программирование на Visual Basic, в частности MS Excel и MS Word, используют практически один и тот же редактор, который мы рассматриваем в этом уроке. Почти все, что вы узнаете в этой книге, применимо к любому из этих приложений. Это один из главных аргументов в пользу Visual Basic.
Редактируем макрос
Для того чтобы начать редактировать макрос, вы должны сначала открыть модуль, содержащий этот макрос, и найти в модуле текст макроса. Например, для того, чтобы редактировать макрос FormarArialBoldl2, который вы записали на прошлом уроке, вы должны сначала открыть модуль, сохраненный в книге Personal.xls, в котором хранится исходный текст макроса FormarArialBoldl2.
В следующем разделе мы узнаем, как открыть VBA-модуль, рассмотрим несколько способов найти в нем нужный макрос и познакомимся с тем, из каких частей он состоит, после чего и займемся собственно редактированием макроса.
Открываем модуль
Для того чтобы начать редактировать конкретный макрос, вы должны сначала открыть модуль, содержащий этот макрос. Для этого выполните следующее.
1.	Откройте редактор Visual Basic, нажав <Alt+Fl 1>, если он еще не открыт.
2.	Раскройте окно Project, если оно еще неоткрыто. Для этого выберите в меню View-Project.
3.	Найдите в древообразном списке окна Project модуль, который вы хотите отобразить. Если в окне Project включен режим отображения Folders, вы найдете все модули своего проекта в папке Modules. (Посмотрите на рис. 2.1 или 2.7, как выглядит дерево папок в окне Project.)
4.	Дважды щелкните на нужном модуле. При этом его текст раскроется в окне Code.
После этого вы можете воспользоваться списком Procedure, в котором вы найдете нужный макрос.
Есть и другой способ найти нужный макрос, не открывая предварительно модуля. Этот способ описан в следующем разделе.
День 2-й. Создаем и редактируем простой макрос
57
Как найти записанный макрос
В начале этого урока вы узнали, как Excel создает модули по мере необходимости записи ваших макросов. Поскольку Excel создает новые модули в одной и той же книге и дает им новые имена, вы можете обнаружить, что в одной книге записано несколько макросов с весьма схожими именами. В таком случае может оказаться непросто найти модуль, содержащий нужный макрос. Например, когда вы записали макрос "crir.arArialBoldl2 на прошлом уроке, вы сохранили его в книге Personal.xls и Excel дала ему имя Modulel. Если после этого вы в том же сеансе работы записали макрос NewBook в упражнении 1.1, он был сохранен в том же модуле. В случае же, если между двумя этими записями макросов вы закрывали программу Excel, второй макрос будет сохранен в новом модуле с именем, скорее всего, Module2. Таким образом, собравшись редактировать макрос NewBook, вы не всегда можете с уверенностью знать, в каком модуле он сохранен, пока не просмотрите все модули в книге.
Поиск с помощью Object Browser
Вместо того чтобы рыться в текстах всех модулей в поисках нужного макроса, лучше воспользоваться специальным средством, называемым Object Browser.
Вот как с ним работают.
^ПРИМЕЧАНИЕ
Хотя данная процедура является универсальной, в ней для примера мы будем искать конкретный макрос, а именно FormarArialBoldl2, записанный вами на уроке 1-го дня. Если вы строго следуете этапам этой процедуры, вы должны сначала запустить программу Excel.
1.	Запустите редактор Visual Basic, если он еще не запущен. Для этого нажмите <Alt+Fll>.
2.	В меню View выберите команду Project, если это окно еше не раскрыто (см. рис. 2.7).
3.	В окне Project выделите проект, в котором вы собираетесь искать макрос. Например, для того чтобы найти макрос FormarArialBoldl2, который вы записали на предыдущем занятии, отметьте книгу Personal.xls, как показано на рис. 2.7.
Project- VBAProject
“Но	" ' !
[vBAProject (PERSONAL .XLS;
+ M icrosoft Exce I Objects
* * Modules
н День02 (День02.х<е)
+ . Microsoft Excel Objects
+ » Modules
Puc. 2,7. Выберите проект в окне Project, перед тем как искать макрос
58
Неделя 1
ПРИМЕЧАНИЕ
Перед тем как искать макрос с помощью Object Browser, вы должны отметить соответствующий проект в окне Project. Макросы вашего проекта появляются в окне Object Browser только после того, как в окне Project отмечен один из проектов.
4.	Выберите в меню команды View-Object Browser. Редактор Visual Basic откроет окно Object Browser, которое вы видите на рис. 2.8.
Раскрыть окно Object Browser можно также, щелкнув на кнопке Object Browser на панели инструментов.
Список Проект/Библиотека Описания
I
Object Browser
Образец поиска
|<Alt Libraries* *	< I * I ' I I T I
C-=ss-s
® |<giobals>
Л Addin	’
<S Addins
SI Adjustments
AnswerWizard
>3 AnswerWizardFi ,
>IS Application
Areas
’© Assistant ।
Meml-.nrdcf '«gbsals»'
I ®Abs I ______________
I Й? ActiveCell
Й? ActiveChart iff ActivePnnter tS ActiveSheet Й? ActiveWmdow Й? Activeworkbook Й? Addins
* AppActivate
Результаты поиска
Поиск
j «All Libraries»
Рис. 2.8. Диалоговое окно Object Browser поможет вам найти нужные макросы и модули, в которых они хранятся
5.	Убедитесь, что в списке Project/Library отмечен пункт <AII Libraries* (см. рис. 2.8). В этом случае окно Object Browser отображает все модули и объекты в списке Classes.
6.	Введите имя нужного макроса в текстовом поле Search Text. Например, для того чтобы найти макрос под названием FormatArialBoldl2, введите в текстовом поле Search Text FormatArialBoldl2.
7.	Щелкните на кнопке Search в окне Object Browser (см. рис. 2.8). Будет произведен поиск введенного текста во всех проектах (и всех доступных библиотеках).
8.	Щелкните на имени макроса в списке Search Results, а потом на кнопке View Definition в окне Object Browser (см. рис. 2.8). Редактор Visual Basic откроет окно Code и отобразит текст нужного макроса.
После щелчка на кнопке View Definition редактор Visual Basic раскроет окно Code и отобразит макрос, поместив курсор в начале его текста.
День 2-й. Создаем и редактируем простой макрос
59
Тй Object Browser	‘ ПД-
|<AII Libraries*	’] <J_J t|
|FoimatArlalBold12 V] Я
Search Results	:
Classes
«8 LegendEntry
LegendKey «51 LineFormat <8 LmkFormat «8 ListBox
Members of'Modulel' ’>FormatArialBold12
PublK f ill FormatArialBold12()
Member of VBAProject Module! • '
Макрос записан 16.11.1999 (Alexander Kovalevsky)
Puc. 2.9. Окно Object Browser после поиска макроса с именем FormatArialBoldl2 в проекте Personal.xls
Просмотр модулей с помощью Object Browser
Если в вашем проекте не слишком много разных модулей, может оказаться проще найти нужный макрос следующим способом.
1.	Запустите редактор Visual Basic, нажав <Alt+Fll>.
2.	В меню View выберите команду Project, если это окно еще не раскрыто.
3.	В окне Project выделите проект, в котором вы собираетесь искать макрос.
4.	Выберите в меню команды View-Object Browser. Редактор Visual Basic откроет окно Object Browser.
5.	Выберите в списке Project/Library имя своего проекта. Если вы не меняли его имя, оно, скорее всего, будет VBAProject. Вы узнаете, как менять имя проекта, чуть ниже.
По умолчанию имя проекта, ассоциированного с книгой Personal.xls, будет Personal.
6.	В списке Classes выберите модуль. Object Browser отобразит все макросы в списке Компонент <объект>. (Название этого списка меняется в зависимости от того, какой объект вы выбрали в списке Classes.)
7.	Выберите имя нужного макроса в списке Компонент.
8.	Щелкните на кнопке View Definition в окне Object Browser. Редактор Visual Basic откроет окно Code и отобразит текст нужного макроса.
9.	Когда вы выбираете конкретный проект в списке Project/Library в окне Object Browser, в списке Classes появляются только те объекты, которые являются частью вашего проекта — обычно это несколько модулей, книга Excel и входящие в нее листы. На рис. 2.10 вы видите окно Object Browser с проектом Personal из книги Personal.xls. В списке Classes на рис. 2.10 вы видите книгу ThisWorkBook (которая является ссылкой на Personal.xls), два модуля и три листа.
60
Неделя 1
Object Browser
[VBAProject	3 4 I ‘ I Tt
3 wk I
Members of ,<globafs>'. * Broken
ExcelVBAHelp________
sj
*NewBook
]FormatArialBold12 Classes
I ® i<globals>
Л Modulel Л ThisWorkbook •£> Лист1 •3 Модули
Модуль2 МодульЗ
FormatArialBold12
Public Sub FormatAria!Bold12Q
.Member of VBAPrqieetModutel
; Макрос записан 15.11.1999 (Alexander Kovalevsky)
Рис. 2.10. В окне Object Browser вы можете видеть содержимое разных объектов своего проекта
После того как вы отобразили макрос в окне Code, независимо от того, какой способ вы применили, вы можете отредактировать текст этого макроса. На рис. 2.11 вы видите окно Code с текстом макроса NewBook, записанного вами при выполнении упражнения 1.1 на первом уроке. На этом рисунке окна Project и Properties закрыты, поэтому окно Code может быть развернуто достаточно широко. Отобразив текст макроса на экране, вы можете прокручивать его с помощью полос прокрутки — как горизонтальной, так и вертикальной. Вы можете пользоваться средствами поиска текста так же, как вы делаете это при работе в программе Excel.
Так надо
Учтите, что вы можете открыть модуль, просто дважды щелкнув на нем в окне Project.
Запомните, что вы можете искать макросы в окне Code с помощью списка Процедура, выбирая в нем нужный макрос.
Не забывайте, что в окне Project видны проекты только из открытых книг Excel. Если файл проекта, который вам нужен, не виден в окне Project, вам придется вернуться в Excel и открыть этот проект.
Не забывайте, что в окне Object Browser видны только те объекты, которые содержатся в проектах, отмеченных в окне Project.
Из чего состоит записанный макрос
Если вы просмотрите текст записанного макроса, то заметите, что разные макросы имеют много общего. В листинге 2.1 приведен текст макроса FormatArialBoldl2, записанного вами на первом уроке. В листинге 2.2 приведен текст макроса NewBook, записанного вами при выполнении упражнения 1.1.
День 2-Й. Создаем и редактируем простой макрос
61
Microsoft Visual Basic - PERSONAL.XLS - [Модуль? (Code)]
___________ Qsld
file Edit View Insert Fijrmot Debug fiun Tools Add-Ins y/indow Help ' ‘ \	‘	_ jg| x[
ga-e - ; ем »ос*: » и sЭ;ing.coii •'	J_________
i General)	~ir]: jNewBook
Ж NewBookO	“J
VcvBook Макрос	~
 ' макрос записан 11.11.99 (Alexander Kovalevsky)
Workbooks.Add
Activeworkbook.SaveAs FileName:= _
"C:\Doc\Excel_2000\NewWorkBook.xls", _
FileFormat:=xlNormal, _ Passwords"", WriteResPassword : = ”" , _	J.
ReadOnlyReconnended:=False, CreateBackup:=False	Ж
Д ActiveWorkbook.Close
« End Sub	Ж
Рис. 2.11. Так выглядит текст макроса в окне Code
ПРИМЕЧАНИЕ
В действительности текст макросов в окне Code не имеет номеров в начале каждой строки. В листингах этой книги такие номера приведены только для удобства рассмотрения отдельных строк.
Листинг 2.1. Текст макроса FormafftrialBalrtf2
1:	Sub FormatArialBoldl2()
2:	' FormatArialBoldl2 Макрос
3:	' Макрос записан 11.11.99 (Alexander Kovalevsky), Форматирует текст в ячейке
шрифтом Arial,
4:	' полужирный, 12 пунктов.
J;
6:	With Selection.Font
7:	.Name = "Arial"
8:	.Fontstyle = "полужирный"
9:	.Size = 12
10:	.Strikethrough = False
11:	.Superscript	= False
12:	.Subscript =	False
13:	.OutlineFont	= False
14:	.Shadow = False
15:	.Underline =	xlUnderlineStyleNone
16:	.ColorIndex = xlAutomatic
17:	End With
18:	End Sub
62
Неделя 1
Листинг 2.2. Текст макроса NewBnok
1:	Sub NewBook()
2:	' NewBook Макрос
3:	' Макрос записан 11.11.99 (Alexander Kovalevsky)
4:	'
5:	Workbooks.Add
6:	Activeworkbook.SaveAs	FileName:=
7:	"C:\Doc\Excel_2000\NewWorkBook.xls",
8:	FileFormat:=xlNormal,
9:	Passwords"", WriteResPassword:="",
10:	ReadOnlyRecommended:=False, CreateBackup:=False
11:	ActiveWorkbook.Close
12:	End Sub
В обоих приведенных примерах первая строка — это начало записанного макроса. Каждый VBA-макрос начинается с ключевого слова Sub, за которым следует имя макроса. Строка, содержащая слово Sub и имя макроса, называется объявлением макроса, поскольку она существует специально для того, чтобы сообщить редактору VBA о существовании этого макроса. За именем макроса следует пара пустых скобок. О назначении этих скобок и о том, как ими пользоваться, вы узнаете на уроке 6-го дня, а пока просто запомните, что после имени макроса они должны быть.
Ключевое слово - это слово, зарезервированное в языке Visual Basic для специального использования, в отличие от таких элементов, как имя макроса, которые вы можете выбирать по своему усмотрению. Ключевые слова выделяются в тексте этой книги, кроме тех случаев, когда они встречаются в листингах программ.
Строки с номерами 2 и 4 в листинге 2.1. и с номерами 2 и 3 в листинге 2.2 — это комментарии. Комментариями называют фрагменты текста, которые не являются частью языка программирования и не содержат инструкций. Они применяются просто для того, чтобы пояснить текст макроса. Обратите внимание на то, что каждая строка комментария начинается с символа апострофа (*)• Visual Basic считает комментарием каждую такую строку от апострофа до конца строки.
В записанном макросе в начале всегда есть строки, содержащие имя макроса и его описание, которое вы ввели при записи в поле Description (Описание) диалогового окна Record Macro. Конкретно число таких строк и их содержание зависит от того, какое описание макроса вы ввели при записи.
Сразу за этим следует тело макроса, в котором могут присутствовать строки комментария. В листинге 2.1 строки 2-17 содержат тело макроса FormarArialBoldl2, а в листинге 2.2 строки 2-11 — тело макроса NewBook. Каждая строка состоит из одной или нескольких инструкций языка Visual Basic. Инструкция языка состоит из ключевых слов и других символов, вроде того, как слова обычного языка образуют предложение. Подобно абзацу языка, состоящему из нескольких фраз, макрос состоит из нескольких инструкций.
VBA-инструкции в макросе содержат команды, соответствующие тем действиям, которые вы выполняли при записи макроса. Например, строка 7 в листинге 2.1 выбирает шрифт Arial, строка 9 устанавливает его размер и т.д. В листинге 2.2 строка 5 создает новую книгу, а строка 11 — закрывает эту книгу. Каждая строка соответствует некоторому выполненному вами действию.
День 2-Й. Создаем и редактируем простой макрос
63
После тела макроса следует строка, содержащая ключевое слово End Sub, которое говорит о том, что выполнение макроса закончено. Макрос в листинге 2.1 заканчивается в строке 18, а макрос в листинге 2.2 — в строке 12.
При запуске макроса VBA начинает выполнять первую инструкцию его тела, слезающую сразу за объявлением макроса, и выполняет их последовательно слева направо. Потом VBA выполняет следующую строку и так далее, пока не достигнет конца макроса, обозначенного ключевым словом End Sub.
VBA игнорирует все строки комментариев, которые встречаются в теле макроса.
Вы, наверное, уже заметили, что некоторые строки макроса даны с отступом от левого края. Разные величины отступа способствуют визуальному восприятию текста и отделению одной части макроса от другой. Все тело макроса выполнено с отступами относительно строк со словами Sub и EndSub. Это помогает глазам выделить тело макроса. В некоторых строках (строки с 7-й по 16-ю в листинге 2.1) отступ еще больше. Это сделано для выделения строк, заключенных между инструкциями With и End With. О том, что такое инструкции with и End with, вы узнаете на уроках 7-го и 11-го дней.
Программа записи макроса автоматически делает такие отступы для того, чтобы человеку легче было читать текст макроса. Когда вы будете писать свои макросы вручную, вам тоже следует делать такие отступы для выделения различных логических разделов. Не существует никаких определенных требований в отношении того, как именно делать отступы и делать ли их вообще. Макросы в листингах 2.1.и 2.2 будут работать точно так же, если вы сдвинете все строки к левому краю. Отступы строк — это просто традиция, поддерживаемая программистами для лучшего восприятия текста.
Если при редактировании макроса вы используете цветной монитор, то заметите, что разные части макроса выделены разными цветами. Комментарии выделены зеленым, ключевые слова Sub, End Sub и другие — синим, а остальные инструкции — черным цветом. VBA расцвечивает части макроса для того, чтобы вы могли определить, какую именно часть вы в данный момент редактируете.
Редактируем текст макроса
Редактирование текста макроса в окне Code основано на той же технике и приемах, с которыми вы уже знакомы как пользователь Windows и Excel. Все делается точно так же, как в программах Блокнот или WordPad. Вы пользуетесь клавиатурой, мышью, командами меню Edit, вводите текст, удаляете его, выделяете, вырезаете, копируете или вставляет из буфера обмена — все как в известных вам программах. (В табл. 2.2 и 2.10 вы найдете описание команд редактирования и панели инструментов редактора Visual Basic.)
Вы можете пользоваться командами Edit-Find или Edit-Replace для поиска фрагментов текста и замены отдельных слов и инструкций в модуле.
Сохранить изменения можно с помощью меню File-Save или щелкнув на кнопке Save панели инструментов. Все изменения будут также записаны при сохранении книги Excel или при выходе из программы (Excel предложит вам сохранить изменения в книге Personal.xls.)
Давайте в качестве примера редактирования попробуем добавить к макросу, записанному на уроке 1-го дня, нескольких строк комментариев.
Посмотрите на листинг 2.1. Приблизительно так может выглядеть ваш макрос, записанный при выполнении упражнения 1.1. Текст макроса в листинге был отформатирован так, чтобы его строки помещались в поля книги и чтобы его было удобнее
64
Неделя 1
читать. У вас на экране текст может выглядеть несколько иначе, в частности строки 6-10 могут выглядеть как на рис. 2.11.
Разберемся с символом продолжение строки
Посмотрите еще раз на строки 6-9 в листинге 2.2. Обратите внимание на символ подчеркивания (_), который стоит в конце каждой строки. Перед каждым таким символом подчеркивания вы видите пробел, отделяющий его от остального текста строки. Такая специальная комбинация пробела с последующим символом подчеркивания называется символом продолжения строки и служит для того, чтобы указать VBA на то, что следующая строка является продолжением данной и образует с ней единую инструкцию.
В листинге строки 6-10 фактически являются одной инструкцией, которая сохраняет текущую книгу и устанавливает для нее некоторые параметры. Для лучшего восприятия текста программа записи макроса разделила эту инструкцию на несколько строк с помощью символа продолжения строки. (Несколько таких символов были добавлены вручную.)
Если бы инструкция не была разделена на несколько строк, она заняла бы так много места на экране, что вам пришлось бы очень долго ее прокручивать, пытаясь добраться до конца. Когда вы будете писать свои макросы вручную, вы тоже будете использовать символ продолжения строки для большей наглядности текста.
Обратите также внимание на то, как сдвинуты строки, образованные из одной инструкции символом продолжения.
Для того чтобы добавить к тексту макроса комментарии, сделайте следующее.
1.	Установите курсор в конец строки 4 (см. листинг 2.2).
2.	Вставьте новую строку, нажав клавишу <Enter>.
3.	Введите символ апострофа (') —- все комментарии начинаются с этого знака.
4.	Введите текст комментария, например такой: В следующей строке создается новая книга.
5.	Повторите пп. 1 и 2, вставив пустую строку перед строкой 6.
6.	Введите апостроф и текст комментария, например: В следующей строке сохраняется книга.
В листинге 2.3 вы видите измененный макрос NewBook, в который добавлены строки комментария, а именно 5-я и 6-я. Ваш макрос на экране должен выглядеть, как на рис. 2.12.
Дисшина 2.3. Макрос NewBook с добавленными комментариями
1:	Sub NewBook()
2:	' NewBook Макрос
3:	' Макрос записан 11.11.99 (Alexander Kovalevsky)
4:	'
5:	'В следующей строке создается новая книга
6:	Workbooks.Add
7:	'В следующей строке сохраняется книга
8:	ActiveWorkbook.SaveAs FileName:=
9:	"C:\Doc\Excel_2000\NewWorkBook.xls",
10:	FileFormat:=xlNormal,
11:	Passwords"", WriteResPassword:="",
12:	ReadOnlyReconunended:=False, CreateBackup:=False
13:	ActiveWorkbook.Close
14:	End Sub
День 2-й. Создаем и редактируем простой макрос
65
Добавляя комментарии к тексту макроса, обратите внимание на то, что при вводе новой строки ее символы выводятся на экран черным цветом, но при переходе на другую строку, либо с помощью нажатия клавиши управления курсором, либо с помощью щелчка мыши, символы строки комментария становятся зелеными. Объясняется это тем, что Visual Basic проверяет тип строки в тот момент, когда курсор покидает эту строку.
Visual Basic проверяет каждую строку для того, чтобы определить, правильно ли она набрана с точки зрения синтаксиса. (Синтаксис — это правила, по которым слова объединяются в предложения языка программирования или простого, человеческого языка.) Если строка набрана правильно, VBA раскрашивает ее символы в соответствии с правилами, описанными выше, если же в строке содержится ошибка, VBA красит всю строку в красный цвет и выводит сообщение об ошибке. Сообщения об ошибках мы рассмотрим чуть ниже.
Microsoft Visual Basic - PERSONAL.XLS - [Модуль? (Code)]
Be Edit yiew Insert Farmer! Qebug gun Tools AdcHns y^ndow Help	' _ |g|
jjjga-H Л	; » tl  fe£'%ilg'Wz%. ^: bi1.Con4 '	-,j :________
((General)	(NewBook
Sub	NewBook ()	"J
'	NewBook Макрос	Ц
' Макрос записан j.1 -11.Г-9 (Alexander Kovalevsky)
’	В	следующей строке создается новая	книга	(11
Workbooks.Add	||
' В следующей строке сохраняется	созданная книга	||j
Activeworkbook. SaveAs FileName:=	_	(IK
"C:\Doc\Excel_2000\NewWorkBook.xls", _
FileFormat:=xlNormal, _
Password: = " ", WriteResPassword:	_	(Ж
ReadOnlyRecommended:«False, CreateBackup:«False	g
Activeworkbook.Close
End Sub
Рас. 2.12. Макрос NewBook после редактирования в окне Code
Так надо
Всегда добавляйте комментарии к макросу, пока вы еще хорошо помните все, что делает этот макрос, особенно когда текст макроса достаточно длинен. Впоследствии комментарии существенно помогут вам вносить изменения в текст.
Всегда добавляйте комментарии, которые объясняют, что вы меняли в тексте макроса и зачем. Потом такие комментарии помогут вам разобраться, что в макросе добавлено вами, а что осталось неизменным.
66
Неделя 1
Перемещение или копиривание макриса из идноги модуля в другпй
Перед тем как редактировать макрос, рекомендуется создать резервную копию, особенно, если вы планируете делать в нем большие изменения или если макрос большой и выполняет сложные действия. Сделав копию, вы предохраните себя на тот случай, если из ваших переделок ничего не выйдет или выйдет не то, что вы ожидали.
Скопировать макрос можно через буфер обмена следующим образом.
1.	Раскройте макрос, который вы хотите копировать.
2.	Отметьте весь текст макроса. Убедитесь, что отмечено все, включая строки со словами Sub и EndSub.
3.	Выберите в меню команду Edit-Copy. При этом макрос скопируется в буфер обмена.
4.	Теперь раскройте модуль, в который вы хотите вставить макрос.
5.	Выберите команду Edit-Paste.
Описанным выше способом вы можете вставить макрос в тот же самый модуль, в другой модуль в том же проекте, в другой модуль в другом проекте или в документ другой программы, например в Блокнот.
Так надо
Обязательно делайте резервную копию макроса, если вы не уверены в том, что после изменений он будет работать правильно или просто, если его было трудно записывать.
Так не надо
Не следует делать резервную копию в том же модуле, в котором хранится исходный макрос. Если в одном модуле окажется два макроса с одинаковыми менами, то при попытке запуска макроса VBA выдаст сообщение, об
Сохранение и импорт модулей в виде текстового файла
Если вы хотите сделать резервную копию всего модуля, можете сохранить его в виде текстового файла. Кроме того, это полезно при импорте модуля в другой проект, при создании архива вашей работы, для преобразования модуля в формат Visual Basic версии 6. Сохранение модуля в текстовом виде называется экспортированием модуля. После того как модуль экспортирован, вы можете импортировать его в любой VBA-или Visual Basic-проект.
Эксперт модуля
Для того чтобы экспортировать модуль в текстовый файл, сделайте следующее.
1.	Отметьте модуль, который вы хотите экспортировать, в окне Project.
2.	Выберите в меню команды File-Export File. Редактор VBA раскроет диалоговое окно Export File, показанное на рис. 2.13. Это диалоговое окно работает так же, как и любое окно Сохранение документа.
3.	Убедитесь, что в списке Save As Туре выбран тип файлов Basic Files (*.bas).
День 2-Й. Создаем и редактируем простой макрос
67
Рис. 2.13. Для экспортирования файла в текстовый формат служит окно Export File
4.	В списке Save In выберите диск и папку, в которой вы хотите сохранить текстовый файл.
5.	Введите имя файла в текстовом поле File Name. Редактор Visual Basic по умолчанию предлагает вам имя экспортируемого модуля.
6.	Щелкните на кнопке Save.
При экспортировании файла редактор Visual Basic создает текстовый файл, содержащий все макросы исходного модуля. При желании вы можете редактировать файлы типа *.bas с помощью программы Блокнот. На рис. 2.14 вы видите модуль, содержащий макрос NewBook из листинга 2.3, открытый в программе Блокнот после экспортирования его в текстовый формат. Обратите внимание на то, что в файле типа *.bas есть несколько строк, начинающихся со слов Attribute, которые не видны при просмотре модуля в окне Code. Редактор Visual Basic добавляет эти строки в файл для того, чтоб потом этот файл можно было импортировать в другой проект.
Импорт модуля
Вы можете присоединить экспортированный файл типа *.bas к любому VBA-проекту. Это делается путем импортирования файла. Для того чтобы импортировать файл, сделайте следующее.
1.	Отметьте модуль, в который вы хотите импортировать файл типа *.bas, в окне Project.
2.	Выберите в меню команды File-Import File. Редактор VBA раскроет диалоговое окно Import File. Это диалоговое окно работает так же, как и окно Открытие документа.
3.	В списке Look In выберите диск и папку, в которой следует искать файл, ранее экспортированный в формат *,bas.
4.	Убедитесь, что в списке Save As Туре выбрана строка VB Files (*.frm, *.bas, *.cls)
5.	Дважды щелкните на имени файла, который вы хотите импортировать. Редактор Visual Basic прочитает файл и создаст в вашем проекте новый модуль. Если в проекте уже есть модуль с таким же именем, как у импортируемого, Visual Basic добавит единицу к имени нового модуля.
68
Неделя 1
ЛЦ Модуль?.bas Блокнот
<Ёайл Оравка Поиск Справка	•
Attribute VB_Name = "Модуль2"	Д
Sub NewBook()	|||
Attribute NewBook. VB_Description = "Макрос записан 11.11.99 (Alexander
Kovalevsky)1'
Attribute NewBook.VB_ProcData.VBJnvoke_Func = " \n14"
' NewBook Макрос
' Макрос записан 11.11 99 (Alexander Kovalevsky)
' В следующей строке создается новая книга
Workbooks Add
' В следующей строке сохраняется созданная книга
ActiveWorkbook.SaveAs FileName:= _
"С \Doc\Excel_2000\NewWorkBook.xls", _
FileFormat:=xlNormal, _
Passwords"", WriteResPassword:-"’, _	|И
ReadOnlyRecommended:=False, CreateBackup:=False
ActiveWorkbook.Close	...
End Sub
 d|
Puc. 2.14. Экспортированный файл в окне программы Блокнот
Так надо
На уроке 16-го дня вы узнаете, что точно так же, как модули, можно импортировать текстовые описания форм при создании диалоговых окон. Текстовые описания форм хранятся в файлах с именами *. f rm.
Запомните, что файлы с расширением 4. cis содержат модули классов, предназначенные для создания новых объектов VBA. Модули классов рассмотрены на уроке 11 -го дня.
Удалвнпв модулей из проекта
Может статься, что вы захотите удалить макросы, сохраненные в некотором модуле. Причины могут быть разные: макросы устарели и у них появились новые, более совершенные версии; это был модуль с экспериментальными, созданными только для обучения макросами. Как бы то ни было, для того чтобы удалить модуль, сделайте следующее.
1.	Выделите в окне Project модуль, который вы хотите удалить.
2.	Выберите команду File-Remove <объект>. Visual Basic спросит вас, хотите ли вы экспортировать модуль, перед тем как удалить его.
Можно также удалить модуль, щелкнув на нем правой кнопкой и выбрав во вспомогательном меню команду Remove.
3.	Щелкните на кнопке Yes, если считаете нужным экспортировать модуль перед удалением. Рекомендуется поступать именно так, если только вы не абсолютно уверены в том, что никогда не захотите воспользоваться одним из макросов, сохраненных в этом модуле. Если уверены, щелкните на кнопке No.
День 2-й. Создаем и редактируем простой макрос
69
4.	Если вы решите экспортировать модуль, Visual Basic откроет диалоговое окно Export File, описанное выше в этом уроке. Сделайте все как надо и щелкните на кнопке Save.
Так надо
Будьте внимательны, выделяя модуль для удаления. Удаление объекта нельзя отменить.
Имейте в виду, что точно так же, как модули, вы можете удалять из проекта другие объекты, например формы. Обязательно экспортируйте модули перед удалением, если есть хоть малейшее сомнение в том, что они никогда вам не понадобятся.
Пишем макрос и процедуру
Для того чтобы написать макрос без применения средств записи, вы можете поместить новый макрос в существующий модуль или создать новый.
Что такое макрос о процедура
; До сих пор в этой книге мы называли макросом и то, что было записано с помощью средств записи макросов, и то, что было написано вручную. Сейчас мы внесем в терминологию некоторую ясность. Строго говоря, термин макрос должен применяться только для программы, записанной с помощью средств записи макросов. То же, что пишется вручную, так сказать,*с нуля, мы будем называть процедурой. В дальнейшем в этой книге под .
' процедурой мы будем иметь в виду программу, написанную вручную.
Создание и переименование модуля
Если книга, в которой вы хотите создать процедуру, не содержит ни одного модуля, вам нужно перед написанием процедуры создать такой модуль в своем проекте. Кроме того, вам может понадобиться создать новый модуль, если существующий модуль переполнен (в модуле может быть не более чем 4000 строк) или просто если вы хотите поместить новую процедуру в отдельный модуль.
Если вы решили поместить процедуру в новый модуль, сделайте следующее.
1.	Откройте в основном приложении (в нашем случае в Excel) документ, шаблон или книгу, в которой вы хотите создать процедуру.
2.	Нажмите клавиши <Alt+Fll>. В результате откроется редактор Visual Basic.
3.	В окне Project отметьте проект, к которому вы хотите добавить модуль.
4.	Выберите в меню Insert-Module (или щелкните на кнопке Insert панели инструментов). Visual Basic создаст новый модуль и откроет окно Code.
При создании модуля Visual Basic дает ему имя в соответствии с правилами, описанными выше. Ваш новый модуль будет иметь имя Module с некоторым номером. Создав новый модуль, вы должны дать ему содержательное имя. Для переименования модуля сделайте следующее.
1.	В редакторе Visual Basic отметьте модуль, который вы хотите переименовать.
2.	Если окно Properties не открыто, выберите в меню View-Properties или щелкните на кнопке Properties Window панели задач. На рис. 2.15 вы видите окно Properties, в котором единственный модуль имеет только одно свойство — свое имя.
70
Неделя 1
3.	В текстовом поле Name введите новое имя для модуля. Как только курсор покинет это текстовое поле, модуль будет переименован.
Так надо
Переименовывайте модули немедленно после создания. Давайте модулям содержательный имена, чтобы вы могли по имени определить назначение процедур в этом модуле. Такие имена, как Modulel 2, не много могут сказать о назначении модуля.
Properties - Мсдуль2
)модуль2 Module Alphabetic ] categorized |
(Name)
|Модуль2
И
Рис. 2.15. Переименуйте модуль, введя в текстовое поле новое имя
Выбор существующего моууля
на имени модуля в окне Project,
Чтобы создать процедуру в существующем модуле, вы должны открыть окно Code для этого модуля. Для этого либо дважды щелкните либо отметьте модуль и выберите в меню View-Code.
в модуль, будь то новый или су-курсор в окно Code в то место,
Пишем текст процеууры
Перед тем как начинать запись текста процедуры шествовавший ранее модуль, вы должны поместить куда хотите вставить текст процедуры.
Вы можете вводить текст процедуры в любое место в модуле, лишь бы это было после слова End Sub, которое заканчивает предыдущую процедуру, и перед словом Sub, которое начинает следующую процедуру. Большинство программистов помещают новую процедуру в конец модуля.
Когда вы пишете процедуру, вы должны прежде всего указать ее имя и поместить в текст слова Sub в начале процедуры и End Sub в конце. Если хоть одно из этих требований не будет соблюдено, синтаксические правила будут нарушены и редактор Visual Basic выдаст сообщение об ошибке при попытке запустить процедуру.
В любом языке программирования классическим примером простой программы является программа, выводящая на экран слова Hello, World! В листинге 2.4 вы видите такую программу, состоящую из единственной процедуры.
День 2-й. Создаем и редактируем простой макрос
71
Для того что бы ввести такую программу самостоятельно, сделайте следующее.
1.	Откройте книгу Excel или создайте новую книгу.
2.	Вызовите редактор Visual Basic, нажав <Flt+Fll>.
3.	В окне Project отметьте документ или книгу, в которой вы хотите создать свою первую программу.
4.	Выберите команды Insert-Module. Visual Basic создаст новый модуль и откроет для него окно Code.
5.	Переименуйте новый модуль, дав ему содержательное имя.
6.	Убедитесь, что курсор находится в начале первой строки в окне Code и введите текст из листинга 2.4. В конце каждой строки нажимайте <Enter>, начиная новую строку.
Вводите текст точно так же, как он приведен в листинге 2.4, но без номеров строк. Помните, что номера строк не являются частью программы, приведены только для того, чтобы нам было удобнее обсуждать конкретные строки и части процедуры.
Visual Basic во многом помогает вам писать программу. Например, после того как вы введете строку, содержащую слово Sub и имя процедуры, он автоматически введет строку со словами End Sub. Таким образом, уменьшается вероятность того, что вы забудете ввести нечто важное.
Кроме того, редактор Visual Basic имеет свойство, называемое Auto Quick Info. Как только вы введете MsgBox и нажмете пробел (строка 2 в листинге 2.4), появится вспомогательное окно со списком аргументов функции, имя которой вы только что ввели, в данном случае функции MsgBox. На рис. 2.16 вы видите это окно с информацией об аргументах функции. Аргумент, значение которого вы должны сейчас вводить, выделяется полужирным шрифтом. Вспомогательное окно Quick Info закрывается, как только вы переместите курсор на другую строку. Его можно закрыть также, нажав клавишу <Esc>.
Если свойство Quick Info покажется вам не заслуживающим внимания, вы можете отключить его, воспользовавшись командой Tools-Options. Читайте справочную систему редактора Visual Basic для полной информации об окне Options.
Листинг 2,4. Процеддра Hello, World
1:	Sub HelloWorld()
2:	MsgBox "Hello, World!"
3:	End Sub
Первая строка в листинге 2.4 — это объявление процедуры. Объявление процедуры служит для того, чтобы сообщить Visual Basic о существовании процедуры и о месте начала ее исходного текста.
Каждое объявление процедуры состоит из слова Sub, пробела и имени процедуры. В листинге 2.4 имя процедуры — HelloWorld. Заканчивается объявление процедуры парой пустых скобок. Эти скобки являются обязательным элементом (об их назначении вы узнаете из урока 6-го дня). Если вы не поставите скобок, Visual Basic сделает это за вас, когда вы нажмете <Enter> в конце строки.
(Ipimiie
Вы можете добавить комментарии в конце строки, содержащей VBA-инструкцию, поставив апостроф и введя сам комментарий. Такие комментарии называются концевыми. Следующий фрагмент текста показывает пример концевого комментария.
ChDir "Е:\" 'Меняем текущий директорий на Е:\
72
Неделя 1
Microsoft Visual Basic - fleHbO2.xls - [FirstProgram (tode)}
6le Edit ytew Insert Format Qebug Bun Tools Add-Ins ^Vindcw Help
•ЙЗ’Й -'-®Л	- ► II	'©’LnlCblW
Д?Гх]
*1

G(>enur till
С C2
- VBAProject (PERSONAL
:+ Microsoft Excel Objects
-	 Modules
«« Modulei Модуль 1 Модуль2 МодульЗ
День02 (flenb02.xls)
+ Microsoft Excel objects ’
-	’ Modules
FrstProgram
SecondProgram
Sub HelioWorld()
MsgBox "Hello, World!"
MsgBox
En MsgBoxtFrampt, [Buffons As VbMsgBoxStyle - vbOKOnly]. [Fiffe]. |
[HeipFile], [ConfexQ) As VbMsgBaxResult |
Puc. 2.16. Вспомогательное окно Quick Info для функции MsgBox
Вторая строка в листинге 2.4 составляет тело процедуры и является единственной инструкцией, которая что-то делает. Тело процедуры может состоять из нескольких инструкций. Инструкция MsgBox выводит на экран сообщение пользователю. Ниже в этом разделе мы расскажем подробнее об инструкции MsgBox.
Третья и последняя строка в нашей процедуре — инструкция End Sub. Она служит для того, чтобы указать VBA конец процедуры. VBA, встретив такую инструкцию, заканчивает выполнение. Как и объявление процедуры, инструкция End Sub должна стоять в начале строки и быть единственной инструкцией в строке, но вы можете ставить комментарии после нее. Как уже говорилось, Visual Basic автоматически добавляет эту инструкцию в конце процедуры.
Процедура сохраняется каждый раз при сохранении файла рабочей книги, в которой она записана.
(^ПРИМЕЧАНИЕ
После того как вы ввели текст процедуры HelioWorld, запустите ее. Как запускать процедуру, вы знаете из урока 1-го дня.
1.	Откройте окно Macro, выбрав в меню Tools-Macros.
2.	В списке Macro Name выберите процедуру HelioWorld.
3.	Щелкните на кнопке Run.
В результате выполнения процедуры Visual Basic выведет окно, которое вы видите на рис. 2.17. Щелкните на кнопке ОК и закройте окно.
День 2-й. Создаем и редактируем простой макрос
73
Hello. World!
Рис. 2.17. Процедура, приведенная в листинге 2.4, выводит такое сообщение
Так не надо	'	~!
Следите за тем, чтобы случайно не удалить строку с инструкцией End Sub. Если такое произойдет, может возникнуть много проблем.
Обратите внимание на то, что даже в такой короткой программе тело процедуры введено с отступом от левого края, чтобы отделить его от объявления и окончания. Старайтесь придерживаться этого правила. Сравните листинг 2.4 с тем, что вы видите здесь; безусловно, отступы намного повышают читабельность текста.
1:	Sub HelloWorld()
2:	MsgBox "Hello, World!"
3:	End Sub
Автоматическое отстдпы
У редактора Visual Basic есть свойство, называемое автоматические отступы, которое помогает вам форматировать текст с разными уровнями отступов. Когда это свойство включено, при переходе на новую строку автоматически делается отступ того же уровня, что и предыдущей строки. Нажатие клавиши <Backspace> возвращает вас к предыдущему уровню отступа.
Для того чтобы включить или выключить это свойство, вы должны установить или снять флажок Auto Indent на вкладке Editor диалогового окна Options.
Запуск процедуры при редактировании
Пишете ли вы новую процедуру или редактируете написанную ранее, вам время от времени нужно запускать ее для того, чтобы проверить результаты своей работы. Вы уже знаете, как запускать процедуру с помощью диалогового окна Macro, сейчас вы научитесь делать это непосредственно из окна редактирования.
Чтобы запустить процедуру из окна редактора, сделайте следующее.
1.	Убедитесь, что курсор находится в процедуре, в ее теле, в строке объявления или в завершающей строке.
2.	Выберите Run-Run Sub/UserForm. VBA выполнит процедуру от начала до конца.
Вы можете также запустить процедуру, установив курсор внутри нее и нажав кнопку Run Sub/UserForm на панели инструментов или клавишу <F5>.
Например, для того чтобы запустить процедуру HelloWorld, вы должны установить курсор в ее тексте и выбрать команду Run-Run Sub/UserForm.
74	Неделя 1
Если при запуске процедуры курсор находится вне ее текста, Visual Basic не может определить, какую процедуру вы хотите запустить и вместо этого открывает окно Macro.
Вывод сообщений пользователю
В листинге 2.4 вы видите инструкцию MsgBox, которая выводит на экран сообщение пользователю. Сообщения, которые процедура выводит на экран или посылает на принтер или записывает в файл на диске, называются выводом процедуры. Инструкция MsgBox — это простейший способ вывода на экран.
Инструкция MsgBox — это как бы встроенная в язык Visual Basic процедура. Строка в процедуре HelioWorld, в которой содержится слово MsgBox, вызывает эту встроенную процедуру. Вот еще раз фрагмент процедуры HelloWoprld:
MsgBox "Hello, World!"
Текст в кавычках после имени процедуры — это текст того сообщения, которое вы хотите вывести на экран. Visual Basic передает этот текст в качестве дополнительной информации процедуре MsgBox. Такую дополнительную информацию, передаваемую процедуре для дальнейшего использования, называют аргументом процедуры. В нашем случае аргументом процедуры является текст Hello, World!. (На уроках 6-го и 12-го дней вы узнаете, как писать процедуры, использующие аргументы.) Кавычки, в которые обрамлен текст Hello, World!, указывают на то, что это аргумент, а не инструкция, подлежащая выполнению.
Посмотрите еще раз на рис. 2.17 и обратите внимание на то, что в заголовке окна сообщения выведено Microsoft Excel. По умолчанию в заголовке окна выводится имя того приложения, которое запускает процедуру или макрос, в нашем случае это Microsoft Excel.
Вы можете изменить заголовок окна. В листинге 2.5 измененная процедура, в которой функция MsgBox выводит сообщение с другим заголовком (рис. 2.18).
Рис. 2.18. Окно сообщения с измененным заголовком
Листинг 2.5. Изменение строки заголовка фднкции МздВвк
1:	Sub HelloWorld()
2:	MsgBox "Hello, World!", , "Окно приветствия"
3:	End Su
Инструкция MsgBox (строка 2 в листинге 2.5) в нашей процедуре выглядит теперь несколько иначе, хотя служит той же самой цели: выводит на экран сообщение для пользователя. В листинге 2.5 инструкция MsgBox содержит три аргумента, каждый аргумент отделен от соседних запятыми.
Первый аргумент — тот же, что и в листинге 2.4. Это текст сообщения, выводимого в окне. (Во вспомогательном окне сведений, которое выводится при нажатии
День 2-й. Создаем и редактируем простой макрос
75
<Ctrl+I>, ему соответствует аргумент Prompt.) Поскольку в данном варианте функции №;3ох аргументов несколько, за первым аргументом следует запятая; все аргументы в списке разделены запятыми.
Как только вы введете запятую после первого аргумента, вы увидите, что вид аргументов в окне сведений изменится: теперь второй аргумент Buttons выводится полужирным, a Prompt — нормальным шрифтом. В то же время в окне появляется список с возможными значениями аргумента Buttons. Этот список появляется в результате работы функции подсказки значений данных. Данная функция работает подобно функции Quick Info, но выводит список возможных значений аргументов и других элементов вашего текста программы.
Второй аргумент в списке необязательный. В нашем примере он опушен, и вместо него стоит пробел. Пробел указывает редактору VBA, что аргумент отсутствует. За пробелом следует запятая, отделяющая его от следующего аргумента. Если вы не поставите пробел, Visual Basic сделает это за вас, но запятые вы должны поставить обязательно.
Необязательный второй аргумент инструкции MsgBox называется Buttons. Этот аргумент появляется в окне сведений в квадратных скобках, что указывает на его необязательность. Аргумент Buttons служит для того чтобы указать, сколько и какие кнопки должны присутствовать в окне сообщения. Если вы опустите аргумент Buttons, как мы сделали в нашем примере, окно сообщения будет содержать только одну кнопку — кнопку ОК. (Подробнее о том, как обращаться с аргументом Buttons, вы узнаете на уроках 5-го и 8-го дней.)
Третий и последний аргумент в инструкции MsgBox указывает заголовок окна (см. рис. 2.18). Когда вы введете запятую, разделяющую второй и третий аргументы, окно сведений покажет, что вы должны вводить аргумент Title.
Подобно первому аргументу, текст заголовка окна заключен в кавычки. VBA всегда рассматривает текст в кавычках не как инструкции, а именно как текст для вывода на экран. Если вы пропустите такие кавычки, VBA выдаст сообщение об ошибке. Поскольку третий аргумент является последним, запятая после него не обязательна.
Сообщения об ошибках при написании, радактирсвании и запуске працеддры
При написании процедуры вы наверняка будете делать ошибки в инструкциях программы. VBA может распознать такие ошибки и сообщить вам о них при запуске процедуры.
Синтаксические ошибки
Синтаксис определяет специфический порядок слов и символов, составляющих правильную инструкцию VBA. Это самый распространенный вид ошибок, который встречаются в программах. Сообщения о синтаксических ошибках информируют вас о таких неприятностях, как пропущенные запятые, недостающие кавычки, отсутствующие аргументы и т.д.
Когда вы пишете или изменяете строку программного текста, VBA делает синтаксический анализ строки в тот момент, когда курсор покидает эту строку. (Синтаксическим анализом называют процесс, при котором часть текста раскладывается на составные элементы, такие как ключевые слова, элементы данных, разделяющие знаки, переменные и пр.) После синтаксического анализа VBA компилирует
76
Неделя 1
строку, т.е. приводит ее к такому виду, в котором он может ее выполнять, не повторяя синтаксического анализа.
Если синтаксический анализ и компилирование прошли успешно, VBA раскрашивает разные части строки. Ключевые слова выводятся голубым, комментарии — зеленым, а прочие части — черным цветом. Если же VBA находит в строке ошибку при синтаксическом анализе или при компиляции, он окрашивает всю строку в красный цвет и выводит сообщение об ошибке.
Рассмотрим следующий фрагмент текста, в котором инструкция MsgBox набран неправильно:
MsgBox "Hello, World!", , Окно приветствия
В этом примере кавычки, необходимые вокруг текста, составляющего заголовок окна, были пропущены. В результате VBA не может понять, что такое слово Greeting, считая его переменной, а не текстом. (Переменные — это специальные объекты для хранения значений в памяти. Мы рассмотрим переменные в следующей главе.)
Поскольку VBA считает, что Окно — это переменная, он ожидает встретить запятую после этого слова или конец списка аргументов. Вместо этого он находит пробел и еще одно слово, которое он тоже считает переменной. В таком случае VBA окрашиват всю строку в красный цвет, выделяет слово, в котором он нашел первую ошибку и выводит сообщение об ошибке (рис. 2.19). Вы видите, что выделено слово приветствия, которое VBA считает ошибочным.
Ж
'^1
£dit View insert Format Qebug Bw* Tools Add-Ins Window Help
.Й'л-Я,...► и  К'ЫtffW(’LintCol*
-|s| x|
ct
t VBAProject (PERSONAL i+' CL Microsoft Excel Objects-
С C:* Modules
«3$ Modulel
Модуль!
: «3$ Модуль2 МодульЗ
День02 (День02.х1$)
Ci* Microsoft Excel Objects в) Sheet! (Sheet!)
<1 Sheetz (Sheetz)
<] Sheets (Sheets) ThisWorkbook
t-A Modules
•*$ FirstProgram
SecondProgram
((General)
Sub
End
HelloKTorld ()
Sub
Microsoft Visual Basic
error
Id
V] I H e l lo Worl d
[приветствия)
Expected: end of statement
OK
Puc. 2.19. VBA выводит сообщение об ошибках
Если вы получили такое сообщение об ошибке, щелкните на кнопке Help и почитайте справку, в которой вы найдете дополнительную информацию о том, как эту ошибку устранить. Для того чтобы закрыть окно сообщения, щелкните на кнопке ОК.
День 2-Й. Создаем и редактируем простой макрос
77
Закрыв окно сообщения, попытайтесь исправить ошибку. Вы можете переводить курсор куда угодно, но строка с ошибкой останется красной. VBA не будет вновь разбирать строку до тех пор, пока вы не запустите процедуру на выполнение или не отредактируете строку. Синтаксический анализ строки выполняется только тогда, когда курсор покидает строку после внесения в нее изменений.
СОВЕТ
Вы можете отключить автоматическую проверку синтаксиса. Хотя нет сомнения в том, что автоматическая проверка сохранит вам много времени и сил, вы все же можете отказаться от нее, выбрав в меню Tools-Options и раскрыв диалоговое окно Options. Выберите вкладку Editor и снимите флажок Auto Syntax Check. После изменения параметров щелкните на кнопке ОК.
Отключение автоматической проверки просто запрещает вывод на экран сообщений о синтаксических ошибках. Фактически VBA продолжает проверять каждую строку в тот момент, когда курсор покидает ее, и при нахождении ошибки VBA окрашивает строку в красный цвет.
Когда при выполнении процедуры VBA обнаруживает синтаксическую ошибку, он прекращает выполнение процедуры, открывает модуль, содержащий данную процедуру, выделяют конкретную строку в модуле и открывает диалоговое окно.
Окно, которое VBA открывает, обнаружив ошибку при выполнении процедуры, содержит гораздо меньше информации, чем окно, открываемое при редактировании процедуры. По этой причине и для того, чтобы не прерывать выполнение процедуры, вы должны стараться исправлять ошибки сразу, при написании процедуры.
VBA может обнаруживать очень много различных синтаксических ошибок, таких как отсутствующие запятые, пропущенные кавычки и пр. Однако не каждая ошибка может быть выявлена и определена достаточно точно, иногда все, что VBA может сделать, — это просто сообщить, что в тексте есть ошибка.
Если вам нужна помощь при исправлении ошибки, вы должны поместить курсор на слово, содержащее ошибку, и нажать клавишу <F1 >. VBA откроет окно справки, в котором объяснит правильное использование данного слова.
Ошибки нри выполнении
Вполне возможно написать такую VBA-инструкцию, которая синтаксически будет верной, но правильно выполнить ее будет невозможно. Ошибки такого сорта называются ошибками времени выполнения (run time error). Подобные ошибки могут быть вызваны различными причинами: пропущенными аргументами процедуры, неправильным типом аргумента, пропущенным ключевым словом, попыткой доступа к несуществующему диску или к папке либо просто ошибкой в логике программы.
Рассмотрим следующую инструкцию VBA, которая содержит неправильный вызов функции MsgBox.
MsgBox "Hello, World!", "Окно приветствия"
В этом примере VBA не находит синтаксической ошибки: текстовые данные обрамлены кавычками, аргументы разделены запятыми. Поскольку все аргументы функции MsgBox, кроме первого, необязательные, VBA считает присутствие только двух из них вполне приемлемым. Однако при выполнении этой инструкции VBA выдает сообщение об ошибке, которое вы видите на рис. 2.20.
78
Неделя 1
Microsoft Visual Basic
Run-Яте error'134 "	; _ г
Type mismatch-	•’	--t  ‘	•	•>'*
' | вп |	- ВФ |
Puc. 2.20. Некоторые ошибки VBA обнаруживает только при выполнении и выдает при этом соответствующее сообщение
Это окно сообщает о том, что ошибка возникла при выполнении процедуры, и описывает тип ошибки. В данном случае произошла ошибка типа Type mismatch (Несовпадение типов). Если вы посмотрите на фрагмент программы, приведенный выше, то увидите, что пробел, заменяющий необязательный второй аргумент, отсутствует. Сравните этот фрагмент с листингом 2.5, строка 2.
Когда VBA компилирует этот текст, он рассматривает строку в кавычках "Окно приветствия" как второй аргумент функции MsgBox, а не как третий, что должно было бы быть при наличии пробела. Поскольку вторым аргументом функции MsgBox должно быть число, а не текст, VBA сообщает вам о несовпадении типов аргументов. Подробнее о типах аргументов мы поговорим в следующей главе.
В окне сообщения об ошибке выполнения есть несколько кнопок. Рассмотрим эти кнопки по отдельности.
•	Continue (Продолжить). Щелчок но этой кнопке позволяет продолжать выполнение процедуры. Некоторые ошибки допускают такое продолжение, но в большинстве случаев эта кнопка бывает заблокирована.
•	End (Завершить). Щелчок но этой кнопке заканчивает выполнение процедуры.
•	Debug (Отладка). Щелчок на этой кнопке переводит Visual Basic в режим останова и позволяет вам воспользоваться средствами отладки для того, чтобы попытаться найти и устранить ошибку. Отладку процедур мы рассмотрим на уроке 15-го дня.
•	Help. Щелчок на этой кнопке вызывает справочную систему VBA с описанием возникшей ошибки.
Если вы не можете понять, в чем причина ошибки при использовании конкретного слова или процедуры, наведите курсор на это слово и нажмите клавишу <F1>. VBA раскроет файл справки для этого ключевого слова, если такая справка есть.
Печать твиста программы
Обязательно наступит момент, когда вам понадобится распечатать тексты своих программ. Это может потребоваться для архива, для того, чтобы показать программу коллегам, или просто для того, чтобы изучить ее. Кстати, изучение текстов записанных макросов — неплохой способ изучения языка Visual Basic.
При печати текстов вы можете выбрать печать всех модулей своего проекта или только выделенного модуля. Вы можете выбрать для печати конкретные процедуры.
День 2-Й. Создаем и редактируем простой макрос	79
Для того чтобы напечатать текст программы, сделайте следующее.
1.	В окне Project выделите модуль или проект, тексты которого вы хотите печатать. Если вы хотите печатать только часть текста, выделите эту часть текста.
2.	Выберите команды File-Print. Откроется диалоговое окно Print (рис. 2.21).
Print - День02
Printer: н3 LaserJet 4М
- Range ------- -  г Print What 	;
. fcxrrent^pdjiej	P Code
Г Current groject	j
Print Quality: Fflh	3 Г Print to File
Puc. 2.21. В диалоговом окне Print вы можете указать различные параметры печати
3.	В группе параметров Range (Диапазон печати) укажите, хотите ли вы печатать только выделенный текст, весь модуль или весь проект.
4.	В группе параметров Print What (Печатать) убедитесь, что флажок Code установлен, если вы хотите печатать текст программы. Если вам нужно печатать формы, должен быть установлен флажок Form Image (Рисунок формы).
5.	Установите другие параметры так же, как вы это делаете при печати в программе Excel.
6.	Щелкните на кнопке ОК.
При печати проекта или модуля вы не можете воспользоваться средством предварительного просмотра. Не можете вы и форматировать текст; единственное, что вам доступно, — изменение ориентации станицы.
Резюме
На сегодняшнем уроке вы познакомились с командами и панелями инструментов редактора Visual Basic, которые вам необходимы для написания и редактирования процедур и макросов. Кроме того, вы научились копировать макросы и экспортировать их в текстовый файл.
Вы получили также основные сведения, необходимые для того, чтобы писать процедуры, не прибегая к помощи средств записи макросов; познакомились с понятием синтаксического контроля и с сообщениями об ошибках. И наконец, вы научились печатать свои тексты.
Вопросы н ответы
Мне не нравятся цвета, в которыми Visual Basic окрашивает тексты программ. Могу ли я изменить соответствующие настройки?
Конечно, и сделать это очень легко. Выберите в меню Tools-Options, при этом раскроется диалоговое окно Options. Выберите вкладку Editor Format (Формат). Выберите элемент, цвет которого вы хотите изменить, и укажите для него новый цвет. Лучше
80
Неделя 1
будет, если вы измените только цвет переднего плана, а цвет фона оставите выбранным автоматически Automatic (Авто). Закончив, щелкните на кнопке ОК
Я предпочел бы другой шрифт для отображения текстов программ. Как это сделать?
Как и цвет текста, шрифт и размер устанавливаются на вкладке Editor Format. Выберите в списке вид шрифта и его размер. Пример отображения приводится на этой же вкладке
Обязательно ли мне делать такие же отступы, какие делает программа записи макросов?
Нет, не обязательно. Можете вообще не делать отступов.
Visual Basic игнорирует ваши отступы, они делаются только для более удобного восприятия текста человеком. Но не следует этим пренебрегать, отступы очень помогают при чтении текста, и мы рекомендуем выработать стиль отступов, не очень отличающийся от того, который использует программа записи макросов
Когда следует создавать новый модуль для написания процедуры?
Создавайте новый модуль тогда, когда вы начинаете новую категорию процедур или новую тему. Кроме того, создавать новый модуль приходится тогда, когда размер старого превышает 4000 строк
Как изменить шрифт и форматирование при печати текста программ?
При печати текста программ вы можете менять только ориентацию страницы. Шрифт и размер символов остаются такими же, как и на экране. Если вам очень нужно вывести текст программ другим шрифтом или отформатировать как-то иначе, скопируйте текст в буфер обмена, потом вставьте его в какую-то другую программу, например в WordPad, и отформатируйте как угодно
Коллоквиум
В этом разделе вы найдете контрольные вопросы, которые помогут вам закрепить пройденный материал, а также упражнения, при выполнении которых вы приобретете необходимый опыт. Постарайтесь разобраться и в вопросах, и в упражнениях, прежде чем переходить к следующему уроку. Ответы вы найдете в приложении в конце книги.
Тест
1.	Что такое модуль VBA?
2.	Для чего вставляются комментарии в текст макроса? Должны ли вы вставлять комментарии в процедуры, которые вы пишете самостоятельно?
3.	Что такое ключевое слово VBA?
4.	Какие необходимые элементы содержит процедура?
5.	Что такое объявление процедуры?
6.	Что такое тело процедуры и где оно расположено?
7.	Какие специальные средства в редакторе Visual Basic помогают вам найти нужный макрос?
8.	Почему текст записанных макросов имеет отступы? Зачем делать отступы в текстах программ?
9.	Для чего предназначена процедура MsgBox?
10.	Что такое аргумент? Что такое список аргументов?
11.	Что такое символ продолжения строки и для чего он используется?
День 2-Й. Создаем и редактируем простой макрос
81
12.	Что делает редактор Visual Basic, когда вы переводите курсор на новую строку после редактирования предыдущей строки?
13.	Что такое синтаксическая ошибка?
14.	Что такое ошибка времени выполнения?
Упражнения
1.	Запустите Excel 2000, вызовите редактор Visual Basic и вставьте новый модуль в книгу Personal.xls. Измените имя модуля на ProgramHelp. Теперь введите следующую программу (номера строк опускайте).
Sub ExcelVBAHelp()
'Эта процедура открывает справочную систему VBA
'на статье Microsoft Excel Objects t
Application.Help "VBAX19.CHM"
End Sub
Закончив ввод, запустите программу. Вы должны увидеть файл справки для программы Excel.
2.	Напишите процедуру, выводящую сообщение: Это сообщение программы EXCEL. Окно сообщения должно иметь одну кнопку и заголовок — Сообщение VBA
3.	Поиск ошибок. Найдите ошибку в следующем фрагменте процедуры (введите процедуру и запустите ее)
Sub Broken()
MsgBox Yet Another Message
End Sub
82
Неделя 1
Типы данных, переменные и константы
В этом уроке вы познакомитесь с типами данных, которые поддерживает VBA и научитесь создавать временные хранилища для таких данных в VBA-процедурах. Кроме того, мы рассмотрим следующие вопросы.
•	Что такое типы данных и какие типы данных обрабатываются с помощью языка VBA.
•	Что такое переменная и как пользоваться переменными в процедурах.
•	Что такое константа и как пользоваться константами в процедурах.
•	Как получить информацию от пользователя вашей процедуры и сохранить эту информацию в переменной — первый и очень важный шаг в добавлении интерактивных свойств вашим программам.
Познакомимся с тисами данных
Перед тем как вы познакомитесь с переменными, вы должны понять, как Visual Basic хранит разного рода информацию. VBA использует различные способы хранения информации для разных типов данных, например для чисел, текста и дат. Тип данных — это термин, описывающий природу данных, которые Visual Basic различает и умеет с ними обращаться.
Дианке
Информация этой главы справедлива для любого приложения, поддерживающего язык VBA - для Excel 2000, Word 2000, Access 2000 и т.д. Фактически, все, сказанное в этой главе, применимо к языку Visual Basic вообще. Все, что вы прочтете в этой главе, относится в равной степени к любой разновидности Visual Basic.
В табл. 3.1 перечислены типы данных VBA, указано, сколько памяти потребляет тот или иной тип данных, дано краткое описание каждого типа и диапазон, в котором может находиться значение переменной. Для числовых типов данных такой диапазон означает минимальное и максимальное число, которое может быть записано в переменную этого типа.
День 3-Й. Типы данных, переменные и константы
83
ПРИМЕЧАНИЕ
Байт- это единица измерения памяти в компьютере и на жестком диске. Байт состоит из восьми битов (простейших двоичных единиц). Например, каждая буква, введенная вами в компьютер, занимает один байт памяти.
Таблица 3.1. Типы данных языка Visual Basic
Имя типа данных	Размер в байтах	Описание и диапазон значений
Byte	1 (8 бит)	Положительные числа от 0 до 255
Boolean	2 (16 бит)	Логические значения. Только значения True и False
Currency	8 (64 бит)	От -9223372036854775808 до 9223372036854775807
Date	8 (64 бит)	Хранит комбинацию даты и времени. Дата от 1 января 100 г. до 31 декабря 9999 г. Время от 00:00:00 до 23:59:59
Double	8 (64 бит)	Отрицательные числа от -1.79хЮ308до -4.9х10’324. Положительные числа от 4.9х10’324 до 1.79Х10308
Integer	2 (16 бит)	Числа от -32768 до 32767
Long	4 (32 бит)	Числа от -2147483648 до 2147483647
Object	4 (32 бит)	Используется для доступа к любому объекту VBA. Хранит адрес объекта
Single	4 (32 бит)	Отрицательные числа от -3.4х1038 до — 1.4х1045 Положительные числа от 1.4х1045до 3.4х1038
String	1 байт на символ	Служит для хранения текста. Может содержать до 2 миллионов символов
Variant	16 байт + 1 байт на символ	Служит для хранения данных любого типа
Позже на этом уроке мы рассмотрим каждый тип данных отдельно. Кроме того, типу данных Object будет посвяшен целый урок 7-го дня.
Вы можете преобразовывать большинство типов данных из одного вида в другой, кроме того, на следующем занятии мы рассмотрим, как Visual Basic автоматически преобразует данные. Сегодня мы сосредоточим все внимание на самих типах данных — на их свойствах, ограничениях и использовании.
Эксяменциальная запись
В табл. 3.1 вы, наверное, обратили внимание на необычную запись чисел, называемую экспоненциальной. Вполне возможно, что вам эта запись не знакома. Такой способ записи чисел используется для представления больших чисел в компактной форме. В экспоненциальной записи не записываются ни лидирующие, ни завершающие нули, кроме того, перед запятой указывается только одна цифра. Число умножается на 10 в указанной степени, что фактически означает сдвиг числа относительно десятичной точки.
Помните, что возведение числа в отрицательную степень означает уменьшение числа, а возведение в положительную степень - увеличение. В программном тексте нельзя использовать верхние индексы для показателя степени, поэтому в языке Visual Basic применяется несколько необычный вид записи, а именно вместо того, чтобы писать х10®, пишут Е38. Вот несколько примеров записи чисел в экспоненциальной и обычной форме записи.
84
Неделя 1
-1.23Е2	
-1.23Е-2	-0.0123
2.5Е10	25000000000
7Е9	7000000000
2.5E-IO	0.00000000025
1.7Е1	
1.7Е0	
Даша
VBA использует тип данных Date для хранения времени и даты. Этот тип данных занимает 8 байт памяти.
Работая с данными типа Date, помните, что в Visual Basic такие данные - это не то же самое, что дата в листе Excel или в базе данных Access, хотя у них много общего.
Вообще говоря, вам нет нужды заботиться о том, как Visual Basic хранит данные типа Date, вы просто манипулируете этими данными как обычной датой, а программа VBA все делает за вас.
При выводе даты на экран (например, с помощью процедуры MsgBox) VBA использует короткий формат — такой же, как и ваш компьютер. Аналогичным образом время выводится в 12-часовом или 24-часовом формате.
В Windows 95 и NT вы можете изменить формат представления времени в компьютере с помощью панели управления.
В VBA данные типа Date содержат абсолютную дату. Абсолютная дата — это коли-
чество дней, прошедших после так называемой начальной даты. Начальная дата в Visual Basic — 30 декабря 1899 г. Отрицательные числа соответствуют датам до 30 декабря 1899 г., а положительные — после этого дня. Число 0 соответствует начальной
дате. В такой схеме 1 января 1900 г. представляется числом 2; числу -2 соответствует
дата 28 декабря

1899 г.____________________________________________________________________
Excel и другие поддерживающие VBA приложения используют свои собственные форматы даты. Например, Excel для Windows использует абсолютную дату, но другое значение начальной даты, а именно 1 января 1900 г. Для совместимости с Macintosh вы можете установить начальную дату в Excel на 1 января 1904 г.
VBA всегда использует начальную дату 30 декабря 1899 г., независимо от приложения, для которого создана программа.
При абсолютном представлении даты целая часть числа (цифры слева от запятой) хранит количество дней, прошедших с начальной даты, а дробная часть (цифры справа от запятой) хранит время. Один час — это 1/24 часть суток, или приблизительно 0.0416. Соответственно, одна минута — это 1/1440 часть дня, а секунда — 1/86400.
Вы можете складывать и вычитать даты между собой, можете складывать и вычитать даты и числа. Например, если вам нужно узнать разницу в днях между двумя датами, просто отнимите от поздней даты более раннюю. VBA знает, что нужно выразить разницу в днях, поскольку тип данных является датой. Аналогично этому, если
День 3-й. Типы данных, переменные и константы
85
вам нужно отсчитать 60 дней от определенной даты, просто прибавьте к дате 60, и вы получите новую дату, отстоящую от исходной на 60 дней.
VBA имеет несколько встроенных процедур, позволяющих производить вычисления отдельно с часами, минутами, днями, годами и месяцами.
Числа
В языке VBA есть несколько различных числовых типов: Byte, Integer, Long, single, Double и Currency. Числа хранятся по-разному в зависимости от типа, но всегда компактно. Числа занимают больше всего памяти и имеют самый большой диапазон значений, но никакое число не занимает больше 8 байт. Рассмотрим числовые типы подробнее.
Целые числа типа Byte, Integer и Luuy
Целое число — это число без дробной части. Например, числа 1, 32, 465 — это целые числа, а 1.5, 3.14 и 16.2 — это дробные числа. Число 1.0 — это не целое число, несмотря на то, что его дробная часть равна нулю.
В языке VBA есть три типа числовых данных: Byte, Integer и Long. Самый малый из них — это тип Byte, в нем может быть записано только число от 0 до 255. Число такого типа занимает всего один байт памяти. Вы не можете хранить в числах типа Byte отрицательные величины.
Тип данных Integer имеет в длину два байта, диапазон числа в этом случае — от — 32768 до 32767. Поскольку диапазон этого типа не велик, VBA имеет еще один тип числовых данных — Long. Такое число занимает в памяти 4 байт и может принимать значения от -2147483648 до 2147483647.
Числовые типы Byte, Integer и Long имеют целый ряд преимуществ перед другими числовыми типами. Целые числа занимают меньше памяти, и операции над целыми числами выполняются гораздо быстрее, чем над числами с плавающей запятой, благодаря чему такие типы данных используются очень широко.
VBA автоматически преобразует эти данные в текст, когда вам нужно вывести данные на экран, например с помощью процедуры MsgBox. В следующем уроке мы рассмотрим автоматическое преобразование типов данных.
Числа с плавающей занятой
Числа с плавающей запятой могут иметь сколько угодно цифр как до десятичной запятой, так и после нее. Название этого типа данных отражает тот факт, что десятичная запятая перемешается с одного места на другое в зависимости от величины числа. Числа 12.345, -11.654, 123.00065456 и т.п. — это числа с плавающей запятой, еще их называют действительными числами. Вы будете использовать такие числа тогда, когда вам придется иметь дело с дробной частью.
Для этих чисел предусмотрено два типа данных — Single и Double. Число типа Single занимает 4 байт памяти и имеет диапазон значений для отрицательных чисел от -3.4х1038 до —1.4х1045, а для положительных — от 1.4х1045 до 3.4х1038. Числа типа Double могут принимать значения от -1.79х1О308 до -4.9х10‘324 и от 4.9х10‘324 до 1.79хЮ308. Числа типа Double также называю числами с двойной точностью. Число 0 можно хранить в данных любого типа.
Хотя числа типов Single и Double имеют большой диапазон значений, они имеет также некоторые недостатки. Например, арифметические операции над числами с плавающей запятой выполняются медленнее, чем над целыми числами; числа с плавающей запятой подвержены ошибкам округления. Как и целые числа, числа с пла-
86
Неделя 1
ваюшей запятой преобразуются в текст, когда вы выводите их на экран с помощью функции MsgBox. Если число слишком маленькое или слишком большое, VBA применяет экспоненциальную запись.
Tuu данных Currency
Тип данных Currency является типом с фиксированной запятой, так как запятая всегда находится на одном и том же месте, а справа от нее расположены четыре значащие цифры. Этот тип данных используется тогда, когда нужна особая точность, например в случае с денежными вычислениями.
Числа этого типа занимают 8 байт памяти и могут хранить значения от -9223372036854775808 до 9223372036854775807. Математические вычисления с числами типа Currency имеет малую ошибку округления или не имеют ее вовсе, поэтому их точность выше, чем у чисел с плавающей запятой. Ошибки округления возникают только тогда, когда вы умножаете или делите число типа Currency на какое-нибудь другое число. Как и другие числовые типы, числа типа Currency преобразуются в текст при выводе их на экран.
Текстовые строки
Любые текстовые данные в VBA-программе называются строкой; для хранения строк Visual Basic предназначен тип данных String. Такое название обусловлено тем, что текст в программах обычно представляется в виде строк символов. В строке могут быть записаны любые текстовые символы — буквы, цифры, знаки пунктуации и другие. Строки в VBA-программе заключаются в двойные кавычки, например "Это текстовая строка", "3.14", "Роберт Силверберг" и "143.00987" — все это текстовые строки. Существует две категории строк: строки переменной длины, которые расширяются по мере добавления к ним символов, и строки постоянной длины, число символов в которых неизменно. В языке VBA все строки имеют переменную длину, если вы не указали обратного. Как это сделать, будет рассказано ниже в этой главе.
Тип данных String играет очень важную роль в программах на VBA. Большинство данных, вводимых пользователем в диалоговых окнах или в ячейках листа Excel, являются текстовыми строками. Кроме того, поскольку выводить на экран можно только текстовые строки, все данные, подлежащие выводу на экран, должны быть преобразованы к типу String. Многие встроенные процедуры языка VBA, такие как MsgBox, имеют аргументы типа String.
В VBA есть несколько операторов для конкатенации (проще говоря, для слияния) и для сравнения строк, кроме того, имеется несколько процедур для извлечения из строки ее подстроки, для поиска символов или слов в строке, изменения регистра символов и т.д. На следующем занятии мы поговорим о строковых операторах, а в уроке 5-го дня рассмотрим процедуры для работы со строками.
Логические значения
Обычно при принятии решения VBA-программа проверяет истинность некоторого условия. Для упрощения такой проверки и для хранения результатов проверки используется логический тип данных, принимающий значения истины или лжи. Данные такого типа называются также булевыми. В VBA-программах такие данные обозначаются словом Boolean. Данные типа Boolean требуют двух байтов памяти и могут принимать только два значения — True и False. При выводе данных типа Boolean на экран VBA автоматически преобразует их в строку, содержащую слово True или слово
День 3-й. Типы данных, переменные и константы
87
False. Булевы величины приобретают свои значения в результате выполнения операций сравнения. (Сравнение — это операция, при которой сопоставляются две величины, такие как числа или текстовые строки. С операциями сравнения вы познакомитесь на следующем уроке.)
Данные типа Variant
Variant — это специальный тип данных, в котором можно хранить все типы данных, перечисленные в табл. 3.1, включая типы Object и Array. VBA использует тип данных Variant всегда, когда вы не указываете тип явно, как описано в следующих разделах.
Данные типа Variant имеют свойства тех данных, которые фактически в них записаны. Например, если в данных типа Variant записана текстовая строка, они имеют свойства данных типа String, а если там записано число, то они имеют свойства числового типа, чаще всего типа Double, хотя это может быть и Integer, и Long, и Single, и Currency.
Данные типа Variant используют самое компактное из возможных представлений, например, если в данных типа Variant записано целое число, то тот эти данные будут трактоваться как Integer или как Long, в зависимости от размера этого числа. Число 15 в переменной типа Variant будет трактоваться как Integer, а число 1000000 — как Long.
VBA хранит числа с плавающей запятой в переменных типа Variant как Double и обращается с ними так, что вы не обязаны всегда заботиться о точном типе переменных в своих программах. Любая переменная, для которой вы не указали тип явно, становится переменной типа Variant.
Хотя использование переменных типа Variant удобно и освобождает вас от многих забот при написании процедур, такие переменные требуют больше памяти и большинство операций выполняется на них медленнее, чем с другими типами. Вообще говоря. вам следует избегать неоправданного использования переменных типа Variant, так как это может сделать ваши программы медленными и затруднить их чтение, кроме того, это может привести к появлению трудно обнаруживаемых ошибок.
Понятие о переменных
Переменные — это очень важное понятие, так как вы пользуетесь ими для временного хранения и манипулирования данными в программах. В этом разделе вы узнаете, что такое переменные и как их создавать.
Что такое переменная
Переменная — это имя, которое вы, программист, дали участку компьютерной памяти для того, чтобы хранить в нем данные некоторого типа. Вы можете представить себе переменную как гнездо, в котором записано какое-то значение для дальнейшего использования. Имя переменной — это ярлык, по которому осуществляется доступ к участку памяти. Содержимое участка (значение переменной) может меняться, но имя остается неизменным. В переменной VBA могут храниться данные любого из типов, перечисленных в табл. 3.1.
В некотором смысле переменная подобна имени ячейки в листе Excel. Вы можете дать ячейке листа Excel имя и потом обращаться к этой ячейке по данному имени, не заботясь о том, какие номера столбца и строки имеет ячейка.
88
Неделя 1
Точно так же имя переменной позволяет вам обращаться к данным, записанным в участке памяти, не думая о том, какой именно адрес имеет этот участок. VBA берет на себя заботы по поиску нужного участка в памяти компьютера.
Переменные в инструкции языка VBA совершенно аналогичны переменным в алгебраическом уравнении — вы пишете имя переменной вместо ее значения, которое на момент написания инструкции вам может быть еще не известно, но которое будет известно на момент выполнения этой инструкции.
Когда в тексте программы встречается имя переменной, VBA вставляет в соответствующую инструкцию записанное в памяти значение, на которое указывает имя переменной. В следующем примере, если переменная AnyNun имеет значение 2, то все выражение приобретает значение 4.
AnyNun+2
Когда VBA выполняет эту инструкцию, число 2, записанное в переменной AnyNun, подставляется в выражение и складывается с числом 2, непосредственно записанным в нем, в итоге получается 4. Если бы в переменной AnyNun было записано число 4, все выражение получило бы значение 6.
Давайте рассмотрим более сложный пример использования переменных на формуле, которая вычисляет, какой процент от числа Whole составляет другое число Part (вам эта формула должна быть знакома из школьного курса).
Percent = (Part-f-Whole) х 100
В этом выражении переменные Part и Whole представляют собой два разных числа. Формула вычисляет, какой процент от числа Whole составляет число Part, и результат записывается в переменную Percent. Когда VBA вычисляет это выражение, значения переменных Part и Whole извлекаются из памяти компьютера и подставляются в формулу.
Если значение переменной Part равно 5, а значение Whole — 20, VBA подставляет эти значения в формулу и получается следующее выражение (эта подстановка делается внутри VBA, и вы никогда не видите выражения в таком виде):
Percent=(5-s-20)xl00
Потом VBA вычисляет выражение и записывает результат в переменную Percent: 5 разделить на 20 будет 0.25, и умножить на 100 будет 25, следовательно, в переменную Percent записывается число 25. Любое значение, записанное в эту переменную ранее, уничтожается.
В действительности в тексте программ VBA не используются приведенные здесь сим-
—-	волы умножения (х) и деления (Д, таких символов просто нет на клавиатуре, и в тек-
РИ МЕЧ АНИ Е сте пР°гРамм применяются символы * и /. На уроке 4-го дня вы узнаете, какие симво-X	лы применяются для обозначения арифметических операций, а сейчас мы использо-
вали стандартные символы, поскольку предметом нашего обсуждения являются переменные.
Выбор имени переменной
Вы можете давать переменным почти любое имя, хотя некоторые ограничения все же существуют. В этом разделе мы рассмотрим такие ограничения, а потом поговорим о том, как выбирать имена для переменных.
День 3-й. Типы данных, переменные и константы
89
Понятие ив идентификаторе
Идентификатор - это имя, которое вы даете элементам, создаваемым в ваших процедурах и модулях, например переменным. Термин идентификатор возник, как нетрудно догадаться, от слова идентифицировать, поскольку он предназначен для определения участка памяти, в котором записано значение переменной, если это идентификатор переменной, или группа инструкций, если речь идет о процедуре.
Идентификаторы подчиняются тем же правилам, что и имена переменных, которые мы рассматриваем в этом разделе. В дальнейшем мы часто будем ссылаться на эти правила.
Имена переменных должны подчиняться следующим правилам.
•	Имя переменной начинается с буквы.
•	За буквой может следовать любая комбинация букв, цифр и символов подчеркивания (_).
•	Имя переменной не должно содержать пробелов, точек и символов математических операций.
•	Имя переменной не должно превышать 255 символов в длину.
•	Имя переменной не должно совпадать ни с одним из ключевых слов (называемых также зарезервированными словами). Если такое совпадение будет иметь место, VBA выдаст сообщение о синтаксической ошибке.
•	Имя переменной должно быть уникальным в процедуре. Если вы дадите двум переменным одинаковые имена или имя переменной совпадет с именем процедуры в том же самом модуле, при выполнении процедуры VBA выдаст сообщение об ошибке.
Вот примеры правильных имен переменных.
MyVar
PayDate
New_Item
Percent
Whole
Part
Linel2
А это примеры неправильных имен переменных.
New Iten	'Не разрешен символ пробела
5thDimention 'Начинается с цифры
Dim	'Совпадает с зарезервированным	словом
Week/Day	'Содержит знак арифметической	операции
Имена переменных в VBA не чувствительны к регистру букв, т.е. не имеет значения, набрана буква в верхнем или нижнем регистре. Таким образом, MyVar и myvar — это одна и та же переменная. Более того, VBA выравнивает регистр всех вхождений имени переменной, беря за образец последнее написание. Например, если вы несколько раз написали MyVar, а потом написали myvar, VBA изменит все вхождения этого имени на myvar.
Выбирая имена переменных, старайтесь делать их описательными. Гораздо лучше дать переменной имя Percent, чем X или Y. Например, если в переменной записывается температура в градусах по Цельсию, назовите переменную CelsiusTemp или DegreesC. В предыдущем примере с вычислением процента имена переменных Part и Whole означали соответственно часть и целое.
90
Неделя 1
Так надо
Имейте в виду, что, хотя регистр имен переменных не имеет значения для работы программы, правильное его использование может улучшить читабельность ваших текстов для другого человека.
Обратите внимание на правильное использование символа подчеркивания, который может с успехом заменять пробелы, запрещенные в именах переменных. Например, второй и третий идентификаторы читать намного легче, чем первый.
verylongidentifiername
very_long_identifier_name
Very_Long_Identifier_Name
Создание переменных
Самый простой способ создать переменную — это просто упомянуть ее в выражении. VBA создает переменную и выделяет для нее память, как только обнаружит ее, обычно это происходит при записи в переменную некоторого значения.
Запись значения в переменную называется присваиванием значения переменной. Вы присваиваете значение переменной, используя оператор присваивания, который выглядит как знак равенства (=). Вот пример оператора присваивания.
MyVar=15
Этот оператор записывает число 15 в участок памяти, с которым связано имя переменной MyVar. Если это первое упоминание имени переменной, то VBA создает переменную, выделят для нее память и записывает в нее число 15.
Если такая переменная была создана ранее, то оператор присваивания просто записывает в участок памяти, на который указывает переменная, число 15, при этом все, что было записано в эту память ранее, безвозвратно стирается.
Такой способ создания переменной простым ее упоминанием называется неявным объявлением переменной. Упоминая переменную в выражении, вы косвенно говорите программе VBA о том, что хотите создать такую переменную. Все переменные, созданные таким способом, получают тип Variant. Неявное создание переменных называют также созданием переменных “на лету” (on-the-fly declaration).
Неявное объявление переменных на первый взгляд может показаться простым и удобным, но здесь скрывается потенциальная опасность. Например, представьте себе, что у вас есть переменная MyValue, но в очередной раз вы допустили описку и написали MValue. В зависимости от того, в каком месте программы это произошло, VBA может выдать сообщение об ошибке выполнения, а может создать новую переменную с таким именем. Если это случилось, логика работы программы будет нарушена, а найти такую ошибку будет очень трудно.
Еще хуже, если вы думаете, что в операторе присваивания неявно создаете новую переменную, а на самом деле по ошибке пишете имя существующей переменной. Это никогда не вызовет сообщения об ошибке выполнения, но старое значение переменной будет уничтожено, и найти такую ошибку тоже очень трудно.
По этим и некоторым другим причинам VBA предоставляет вам способ объявлять переменные явно. Явное объявление переменных имеет много преимуществ.
•	При явном объявлении переменных ваша программа работает заметно быстрее. VBA создает все переменные перед тем, как программа начнет работать, и на это не будет уходить время при работе программы.
День 3-Й. Типы данных, переменные и константы
91
•	Явное объявление переменных помогает избежать многих ошибок, которые подстерегают вас при неявном объявлении.
•	Явное объявление переменных облегчает чтение программы человеку. Видя в начале процедуры или модуля список всех переменных, человеку легче понять логику работы программы.
•	При явном объявлении переменных вам не нужно следить за правильным использованием верхнего и нижнего регистров, так как за вас это будет делать VBA. Вместо того чтобы подгонять все вхождения имени переменной под последнее ее упоминание, VBA будет исправлять регистр имени в соответствии с тем написанием, которое было использовано при явном объявлении.
(примечание
Хотя в переменной типа Variant может храниться дата, т.е. данные типа Date, переменная типа Variant может неправильно обрабатывать дату, полученную из листа Excel. Например, VBA не распознает правильно дату, если она не была отформатирована в листе Excel одним из стандартных форматов для даты. Однако если значение даты присваивается переменной типа Date, оно будет обработано правильно независимо от формата, примененного в листе Excel. Аналогично, если вы встааляете дату из VBA-программы в лист Excel, она не будет воспринята правильно из переменной типа Variant. По этим причинам всегда следует объявлять переменную, хранящую дату, как переменную типа Date.
Для того чтобы явно объявить переменную, используется инструкция Dim со следующим синтаксисом:
Dim имя
Имя — это любой идентификатор. Например:
Dim PentProfit
Такая конструкция создает переменную с именем PentProfit. (Ключевое слово Dim является сокращением от dimension — размерность.) Все переменные, которые создаются этим способом, имеют тип Variant. Позже на этом уроке вы узнаете, как создавать переменные других типов.
(примечание
Создавая новую переменную, VBA инициализирует ее, т.е. дает ей начальное значение. Строки при этом становятся пустыми, числа - равными нулю, а даты превращаются в дату 30 декабря 1899 г.
При желании вы можете создать несколько переменных в одной строке, разделяя их запятыми.
Dim имя1, имя2, имяЗ
Имя1, имя2 и имяЗ — это идентификаторы переменных. Таких идентификаторов может быть сколько угодно. В следующем примере создаются три переменные PentProfit, Gross_Income и Total_Costs.
Dim PentProfit, Gross_Income, Total_Costs
Объявлять переменную можно только один раз. Это может быть сделано в любом месте программы, но обязательно до первого упоминания переменной в процедуре.
92
Неделя 1
Однако хорошей практикой следует считать вынесение всех объявлений в один блок в начале программы.
В листинге 3.1 приведена процедура HelloWorld, описанная на прошлом уроке, преобразованная так, что переменная HelloMsg объявляется явно. Процедура HelloWorld выводит сообщение, показанное на рис. 3.1.
Листинг 3.1. Процедура HelloWorld с явным объявлением переменной
1:	Sub HelloWorld()
2:	Dim HelloMsg 'текст сообщения для функции MsgBox
3:	HelloMsg = "Hello, World!"
4:	MsgBox HelloMsg, , "Приветствие"
5:	End Sub
Инструкция Dim появляется во второй строке листинга 3.1. Когда VBA выполняет эту строку, он создает новую переменную и выделяет для нее память. (Переменная HelloMsg имеет тип Variant, поскольку специально ее тип не указан.) В этой же строке вы видите комментарии, объясняющие назначение этой переменной.
В строке 3 листинга 3.1 присваивается значение переменной HelloMsg. В этой переменной содержится текст "Hello, World!". В следующей строке листинга переменная HelloMsg передается процедуре MsgBox, которая выводит на экран окно сообщения.
Hello. World!
Окно приветствия
Рис. 3.1. Процедура MsgBox выводит на жран такое окно сообщения
Так надо
Всегда пишите комментарии, поясняющие назначение переменной, даже если у этой переменной достаточно описательное имя. Комментарии облегчают чтение программы, особенно если возникает потребность эту программу переделывать.
Всегда инициализируйте переменную с помощью оператора присваивания перед первым вхождением этой переменной.
Так не надо
Не думайте, что если VBA инициализирует переменную автоматически, то переменная содержит осмысленные данные.
Область видимости: какие переменные доступны
Говоря об области видимости идентификатора, имеют в виду процедуру или модуль, в которых доступен данный идентификатор переменной или другой процедуры. В этом разделе мы рассмотрим два уровня видимости — внутри процедуры и внутри модуля. Одни переменные доступны только внутри процедуры, другие — внутри всего модуля.
День 3-Й. Типы данных, переменные и константы
93
Область видимости: прицеддра
Переменные, объявленные в некоторой процедуре, доступны только внутри этой процедуры. Так, переменная HelioMsg, объявленная во второй строке листинга 3.1, доступна только в процедуре HelloWorld, никакая другая процедура не может ею воспользоваться. Фактически переменная HelioMsg существует только во время выполнения процедуры HelloWorld. Про такую переменную говорят, что областью ее видимости является процедура.
Хотя на первый взгляд это не очевидно, VBA ограничивает область видимости переменной в ваших же интересах, в интересах программиста. Поскольку переменная доступна только в одной процедуре, вы можете не особенно беспокоиться о том, чтобы ее имя было уникальным.
Правила выбора имен переменных требуют, чтобы переменная имела уникальное имя внутри области видимости. Это значит, что две переменные, объявленные в одной процедуре, не могут иметь одинаковые имена. В самом деле, ни человек, ни программа не могут понять, какую из переменных вы имели в виду, если их имена совпадают.
Однако, если область видимости переменных ограничена процедурой, ничто не мешает вам объявить две переменные с одинаковыми именами в разных процедурах. Посмотрите на листинг 3.2, в котором приведены две процедуры. Первая из них совпадает с процедурой из листинга 3.1.
Листинг 3.2. ООласть видимости: процедура
1:	Sub HelloWorld()
2:	Dim HelloMsg 'текст сообщения для функции MsgBox
3:	HelloMsg = "Hello, World!"
4:	MsgBox HelloMsg, , "Приветствие"
5:	End Sub
6:
7:	Sub HelioDave()
8:	Dim HelloMsg 'текст сообщения для функции MsgBox
9:	HelloMsg = "Привет, Дэйв!"
10:	MsgBox HelloMsg, , "Второе приветствие"
11:	End Sub
В 7-й строке начинается вторая процедура, HelloDave, которая была добавлена к этому модулю. Она работает точно так же, как и процедура HelloWorld, разница толь
ко в тексте сообщения, которое выводит эта процедура, и в строке ее заголовка. На
рис. 3.2 вы видите окно сообщения процедуры HelloDave.
Обратите внимание на то, что в строках 2 и 8 с помощью инструкции Dim объявлены две переменные с одним и тем же именем: HelloMsg. Это вполне естественно, потому что HelloMsg (Hello Massage, приветственное сообщение) — это хорошее описательное имя для переменной, содержащей текст приветственного сообщения.
Рис. 3.2. Процедура HelloDave из листинга 3.2 выводит такое сообщение
Поскольку эти переменные объявлены в разных процедурах, никакого конфликта не возникает: при выполнении процедуры вызывается та переменная, которая в ней объявлена.
94
Неделя 1
Область видимости: модуль
Иногда необходимо, чтобы одна переменная была доступна из нескольких процедур. Обычно это применяется для того, чтобы вычислить некоторое значение и сохранить его в переменной, доступной для многих процедур, вместо того чтобы вычислять его в каждой из процедур.
Например, вы можете написать несколько процедур, каждой из которых требуется величина объема продаж вашей компании. Одна процедура использует это значение для вычисления абсолютного роста дохода компании, другая — для вычисления процента роста дохода, третья — еще для чего-нибудь и т.д.
Если вы объявите переменную Sales внутри процедуры, то она будет недоступна процедурам, вычисляющим рост дохода и процент этого роста. В таком случае всем этим процедурам придется вычислять переменную Sales самостоятельно. Совершенно очевидно, что это напрасная трата времени и сил.
Вместо этого вы можете написать процедуру, вычисляющую значение Sales и записывающую его в переменную, доступную всем процедурам. Тогда такое вычисление будет проделано только один раз, а когда это значение понадобиться другим процедурам, они смогут обратиться к переменной Sales и получить вычисленное ранее значение.
VBA предоставляет вам способ объявить переменную так, что она будет доступна для нескольких процедур сразу. Про такую переменную говорят, что область ее видимости ограничена модулем, в котором объявлена переменная. (На самом деле область видимости переменной может быть даже больше, мы поговорим об этом уроке 10-го дня.)
Для того чтобы объявить переменную, доступную всем процедурам в модуле, поместите инструкцию Dim, объявляющую эту переменную, в самом начале модуля, перед объявлением всех процедур. В листинге 3.3 вы видите модуль, содержащий две процедуры и одно глобальное объявление. Процедура HelloWorld выводит окно сообщения, показанное на рис. 3.1, а процедура HelloDave выводит окно сообщения, показанное на рис. 3.2.
ПРИМЕЧАНИЕ
Область в начале модуля, перед всеми процедурами, называется областью объявлений, потому что в ней вы помещаете объявления общедоступных переменных и директивы языка VBA. Вы можете быстро перейти в область объявлений, выбрав Declarations (Объявления) в списке Procedure (Процедуры) в окне Code (Программа).
Листинг 3.3. ОВласть видимости: модуль
1:	Dim HelloMsg 'используется всеми процедурами этого модуля
2:
3:	Sub HelloWorld()
4:	HelloMsg = "Hello,	World!"
5:	MsgBox HelloMsg,	,	"Приветствие"
6:	End Sub
7:
8:	Sub HelloDave()
9:	HelloMsg = "Привет, Дэйв!"
10:	MsgBox HelloMsg, , "Второе приветствие"
11:	End Sub
В этом листинге строки 3-6 содержат процедуру HelloWorld, а строки 8-11 — процедуру HelloDave. Обратите внимание на то, что ни в одной из этих процедур нет инструкции Dim, вместо этого в 1-й строке объявлена переменная HelloMsg, областью видимости которой является весь модуль.
День 3-й. Типы данных, переменные и константы
95
Такая переменная доступна всем процедурам в этом модуле, поэтому в строках 4 и 5 VBA использует ее при работе процедуры HelloWorld, а в строках 9 и 10 эта же переменная используется при работе процедуры HelloDave.
Переменные с одинаковыми именами на разных уривнях видимисти
Имя переменной должно быть уникальным в области ее видимости. Вы не можете объявить две одноименные переменные в одной процедуре и точно так же вы не можете объявить две переменные в одном модуле, если областью их видимости является весь модуль.
Однако вы свободно можете объявить две переменные с одинаковыми именами, если у них разные уровни видимости. В этом случае VBA выбирает переменную с меньшей областью видимости. Вы уже видели это в листинге 3.2, где были объявлены две переменные с разными областями видимости, каждая в своей процедуре. В листинге 3.4 вы видите модуль, состоящий из трех процедур, и три переменные с одинаковыми именами.
Листинг 3.4. Сочетание процедурного и моцульного цровнвй вицимости
1:	Dim HelloMsg 'используется всеми процедурами этого модуля,
2:	'у которых нет собственной переменной HelloMsg
3:	Sub HelloWorld()
4:	HelloMsg = "Hello, World!"
5:	MsgBox HelloMsg, , "Приветствие"
6:	End Sub
7:
8:	Sub HelloDave()
9:	HelloMsg = " Привет, Дэйв!"
10:	MsgBox HelloMsg, , " Второе приветствие "
11:	End Sub
12:
13:	Sub AnotherMessage()
14:	Dim HelloMsg 'локальная переменная;
15:	в этой процедуре будет использоваться она
16:	HelloMsg = "Всем привет!"
17:	MsgBox HelloMsg, , "Еще одно приветствие"
18:	End Sub
В этом листинге вы видите три разные процедуры. Первые две — те же, что и в листинге 3.3. Третья процедура, AnotherMessage, начинается на строке 13 и заканчивается на строке 18. Она работает так же, как и предыдущие, т.е. выводит сообщение с помощью функции MsgBox.
Обратите внимание на то, что в 1-й строке объявлена переменная процедурного уровня видимости с именем HelloMsg. Когда VBA выполняет процедуру HelloWorld, используется переменная HelloMsg, объявленная в 1-й строке. В этой процедуре нет локальной переменной с таким именем, поэтому переменная уровня модуля, и никакой неоднозначности не возникает. Точно так же происходит при работе процедуры HelloDave.
Однако процедура AnotherMessage имеет свою локальную переменную с именем HelloMsg, объявленную в строке 14. Поэтому при выполнении строк 16 и 17 VBA использует переменную HelloMsg, объявленную в этой процедуре.
96
Неделя 1
Переменная HelloMsg модульного уровня видимости, объявленная в строке 1, процедуре AnotherMessage недоступна.
Примечание
Переменные, объявленные внутри процедуры, называются локальными по отношению к этой процедуре.
Время жизни переменной: как долго сохраняется значение
Говоря о времени жизни переменной (persistence), имеют в виду время, в течение которого переменная сохраняет присвоенное ей значение. Русский термин время жизни хотя и общепринят, не совсем точен: на самом деле речь идет не о времени, измеряемом в секундах или минутах, а об области программы. Обычно время жизни переменной совпадает с областью ее видимости, но не всегда.
Когда вы объявляете переменную в процедуре, значение этой переменной сохраняется только до тех пор, пока выполняется процедура. При выполнении процедуры VBA создает переменную, и независимо от того, объявлена она явно или неявно, по окончании процедуры освобождает память, занятую переменной. Значение переменной при этом безвозвратно теряется.
Например, при выполнении программы из листинга 3.1 резервируется память для переменной HelloMsg. По завершении этой процедуры память, отведенная под переменную HelloMsg, возвращается системе, и значение переменной перестает существовать.
Переменные, объявленные в процедуре, не определены до начала выполнения процедуры и по ее окончании.
Переменные уровня модуля сохраняют свое значение до тех пор, пока выполняется хоть одна процедура из этого модуля. Когда VBA выполняет процедуру, фактически просматривается весь модуль, и все переменные, объявленные в начале этого модуля, создаются и сохраняют свое значение, пока выполняется хоть одна процедура из этого модуля.
Запрет неявного определения переменных
Хотя неявное определение переменных на первый взгляд кажется простым и удобным, оно может породить некоторые проблемы. Если вы определяете переменные простым их упоминанием, вы рискуете создать новую переменную вместо использования существующей или, наоборот, использовать существующую вместо создания новой. И в том, и в другом случае возникает очень трудно обнаруживаемая ошибка, которая всегда приводит к неправильной работе программы.
Вы уже знаете, как с помощью инструкции Dim определять переменные явно для снижения такого риска, но это не всегда гарантирует вам отсутствие ошибки, поскольку VBA разрешает неявное определение переменных.
Для того чтобы гарантировать отсутствие ошибок, вызванных ложным неявным определением, в языке VBA существует команда Option Explicit. Когда вы применяете эту команду, VBA требует, чтобы все переменные были объявлены явно. По существу, VBA просто запрещает неявное определение переменных при использовании команды Option Explicit.
Для того чтобы запретить неявное определение переменных в модуле, добавьте в области описания модуля, т.е. в его начале, команду Option Explicit. В листинге 3.5 вы видите модуль из листинга 3.3, к которому добавлена команда Option Explicit.
День 3-Й. Типы данных, переменные и константы
97
Листинг 3.5. Нспользование команды optiOH Explicit.
1: Option Explicit 'Требует явного описания переменных
1: з этом модуле
2: Dim HelloMsg 'Используется всеми процедурами этого модуля
: Sub HelloWorld()
HelloMsg = "Helio, World!"
MsgBox HelloMsg, , "Приветствие"
= : End Sub
10:	Sub HelloDave()
11:	HelloMsg = " Привет, Дэйв!"
12:	MsgBox HelloMsg, , " Второе приветствие "
13:	End Sub
За исключением строки 1, этот модуль выглядит и работает точно так же, как модуль из листинга 3.3.
В 1-й строке этого модуля содержится команда Option Explicit. В результате все переменные модуля должны быть объявлены с использованием инструкции Dim. Если вы попытаетесь определить переменную неявно, при выполнении модуля VBA выдаст сообщение об ошибке.
Такие команды, как Option Explicit, называются директивами компилятора. Они не вызывают никаких конкретных действий, но служат указаниями компилятору, как выполнять этот модуль. Компилятор - это часть программы VBA, которая преобразует текст вашей программы в машинные инструкции, необходимые для выполнения конкретного задания. Директивы компилятора просто сообщают ему, как следует компилировать ваш исходный текст.
Команда Option Explicit воздействует только на тот модуль, в котором она появляется. Если в вашем проекте несколько модулей, вы должны вставлять команду Option Explicit в каждый модуль, в котором вы хотите запретить неявное объявление переменных.
Так надо
Всегда используйте команду Option Explicit в своих программах. Запрет неявного определения переменных значительно снижает риск ошибки и экономит ваше время и силы.
Указание типа переменной
Все переменные в VBA, независимо от того, объявлены они явно или неявно, имеют тип Variant, если не указан другой тип при объявлении этой переменной. До сих пор все переменные, с которыми вы встречались, имели неопределенный тип, поскольку он не был указан. А все переменные в VBA, тип которых не указан, всегда имеют тип Variant.
98
Неделя 1
Поскольку использование команды Option Explicit в модулях яаляется очень хорошей практикой, VBA предоставляет вам возможность включать эту команду автоматически во все вновь создаваемые модули. Для этого вы должны установить флажок Require Variable Declaration (Явное описание переменных) на вкладке Editor (Редактор) диалогового окна Options (Параметры). Сделать это можно следующим образом.
1.	Выберите в меню Tools-Options. Редактор VBA раскроет диалоговое окно Options.
2.	Щелкните на корешке вкладки Editor.
3.	Установите флажок Require Variable Declaration.
4.	Щелкните на кнопке OK. VBA закроет окно Options.
Теперь каждый раз, когда вы или программа записи макросов будет добавлять новый модуль в проект. В начале модуля будет появляться команда Option Explicit.
Проделанная операция окажет влияние только на вновь создаваемые модули; если вы хотите вставить команду Option Explicit во все модули, созданные ранее, вам придется сделать это вручную. Запрет неявного описания переменных повлияет на все приложения, использующие редактор Visual Basic, т.е. если вы запустите редактор Visual Basic из программы Excel и установите флажок Require Variable Declaration, команда Option Explicit будет появляться во всех модулях, создаваемых в программах NS Word, MS Access и других, использующих редактор Visual Basic.
Указание типа переменной дает вам несколько преимуществ.
•	Переменные, тип которых указан, требуют меньше времени на обработку. Поскольку тип переменной известен, программе не нужно тратить время на определение того, что именно записано в переменной типа Variant.
•	Переменные типа Variant занимают значительно больше памяти, чем переменные указанного типа, поскольку последние занимают ровно столько памяти, сколько необходимо. Использование переменных указанного типа сильно сокращает объем памяти, требуемой вашей программе. Иногда это может даже оказать решающее воздействие на работоспособность ваших программ.
•	Указание типа переменных облегчает чтение и понимание ваших программ.
•	Указание типа переменных предотвращает многие ошибки, такие как смешение несовместимых типов. В случае использования переменных типа Variant такую ошибку найти практически невозможно.
Есть и другие причины, по которым следует отдавать предпочтение указанию типа переменной. Например, хотя в переменной типа Variant может храниться дата, обработка такой даты будет неверной, если она была получена из листа Excel. Если же дата хранится в переменной типа Date, ошибки не произойдет.
Тип переменной объявляется в той же инструкции, в которой эта переменная создается, при этом не важно, создается ли переменная явно, с помощью инструкции Dim, или неявно. Объявляя строку, вы можете одновременно указать длину этой строки.
День 3-й. Типы данных, переменные и константы
99
Указание типа переменной в инструкции Dim
Для того чтобы объявить тип переменной в инструкции Dim, добавьте за именем переменной ключевое слово As и укажите тип переменной. Синтаксис этой конструкции следующий.
Dim имя As тип
где имя в данном случае —идентификатор переменной, а тип — один из типов переменных, приведенных в табл. 3.1.
В следующих строках приведены примеры правильного объявления типа переменных.
Dim PentProfit As Single
Dim Gross_Sales As Currency
Dim PayDay As Date
Dim Massage As String
Dim Counter As Integer
При желании можно объявлять несколько переменных в одной инструкции Dim, при этом объявления переменных нужно разделять запятыми, как в следующем примере.
Dim PentProfit As Single, Gross_Sales As Currency, PayDay As Date
Так надо
t. Объявляя несколько переменных в одной инструкции Dim, указывайте тип каждой переменной. Если вы пропустите тип какой-либо переменной, VBA создаст ее с типом Variant. Например, в следующей строке переменная NetValue имееттип Variant.
as
Объявляйте каждую переменную в отдельной строке. Это облегчит чтение и понимание программы.
Указание тина иврвмвннпй с иимпщью специальных символов
Объявляя переменную неявно, вы можете указать ее тип, добавляя в конце имени переменной специальный символ. Такие символы называются символами определения типа. В табл. 3.2 перечислены символы определения типа и соответствующие им типы.
Таблица 3.2. Символы определения типа
!	Single
@	Currency
#	Double
$	String
%	Integer
&Long
Обратите внимание на то, что таких символов всего шесть. Не существует символов определения типа для таких типов, как Byte, Boolean, Date, Object или Array. Символы определения типа могут находиться только в конце имени переменной.
(^ПРИМЕЧАНИЕ
Символы определения типа - это дань традиции VBA. В старых диалектах языка Basic это был единственный способ указать тип переменной. Вы, безусловно, должны знать, что это за символы и как ими пользоваться, но в действительности пользоваться ими нет необходимости; Инструкция Dim - гораздо более удобный и понятный способ. Большинство современных программистов не пользуются символами определения типа.
100
Неделя 1
В листинге 3.6 вы видите еще один вариант процедуры HelloWorld, с которой вы познакомились на втором занятии.
Дисшииг З.Б. Явиое в неявное определение шиппв
1:	Sub	HelloWorld))
2:	Dim HelloMsg As String
3:	HelloMsg = "Hello, World!"
4:	Title? = "Приветствие"
5:	MsgBox HelloMsg, , Title?
6:	End	Sub
ПРИМЕЧАНИЕ
Если вы введете текст листинга 3.6 в модуль, содержащий директиву Option Explicit, при попытке выполнить этот модуль вы получите сообщение об ошибке времени выполнения. В 4-й строке этой процедуры есть неявное определение переменной, которое запрещено директивой Option Explicit. Для того чтобы выполнить процедуру из листинга 3.6, вы не должны использовать директиву Option Explicit.
Эта версия процедуры HelloWorld работает точно так же, как и предыдущие версии. В сроке 1 содержится объявление процедуры. В стоке 2 инструкция Dim явно объявляет переменную HelloMsg. Поскольку в инструкции Dim есть ключевое слово As и указан тип переменной String, переменная HelloMsg имеет тип String. В 3-й сроке этой переменной присваивается значение.
В 4-й строке неявно объявляется переменная Titles? и сразу ей присваивается значение, содержащее текст заголовка. Имя этой переменной заканчивается символом $, следовательно, тип этой переменной — String. И наконец, в 5-й строке функция MsgBox выводит текст сообщения и заголовок окна, содержащиеся соответственно в переменных HelloMsg и Title?.
Если в 3-й или 4-й строках вы попытаетесь присвоить переменным HelloMsg или Title? числовое значение, при запуске процедуры вы получите сообщение о несоответствии типов и выполнение процедуры прекратится.
Если вы указали тип переменной с помощью символа определения типа, вы должны добавлять этот символ всякий раз при обращении к переменной. Например, опустив символ $ в имени переменной Title в строке 5, вы получите сообщение об ошибке, и выполнение программы будет остановлено.
Вы можете применять символы определения типа в инструкциях Dim. В следующих двух примерах результат выполнения инструкции Dim совершенно одинаков - создается переменная Count, имеющая тип Integer.
Dim Count As Integer
Dim Counts
(Если вы поместите обе эти инструкции в текст процедуры, вы получите сообщение об ошибке, а именно о том, что переменная объявлена дважды.)
ПРИМЕЧАНИЕ
После того как вы создали переменную, независимо от того, явно или неявно и с каким типом, эта переменная будет сохранять свой тип до конца существования. Вы не можете изменить имя или тип переменной.
День 3-й. Типы данных, переменные и константы
101
Создание строк фиксированной длины
Независимо от того, создали вы переменную типа String с использование символов определения типа или указали тип в инструкции Dim, по умолчанию эта переменная будет иметь переменную длину.
Это значит, что длина переменной будет меняться в зависимости от того, сколько символов вы записали в нее. Но может случиться так, что вам понадобится иметь переменную фиксированной длины. Переменная фиксированной длины всегда имеет постоянное число символов. Это обеспечивает уверенность в том, что в строке сообщения всегда находится правильное число символов.
Строки фиксированной длины удобны для вывода информации на экран в колонках, или просто для того, чтобы длина строки никогда не превысила определенного значения. Есть только один способ создать строку фиксированной длины: использование инструкции Dim. В следующей строке приведен синтаксис конструкции Dim, создающей строку фиксированной длины.
Dim Имя As String * N
Имя — идентификатор переменной, a N — число от 1 до максимальной длины строки, которая равна приблизительно 2000000.
Вот пример объявления строки фиксированной длины.
Dim LastName As String * 30
В этом случае создается строка с длиной 30 символов.
Понятие о константах
Константа — это такая величина в VBA-программе, которая никогда не меняется. До сих пор мы использовали такие константы, как "Hello, World!", "Greeting Box" и другие. Эти константы называются непоименованными, так как вы вносите их текст прямо в программу.
Бывают и другие виды констант в программах на VBA. Примером констант могут служить числа 36, 3.14, 212. Константы даты — это записи вроде #12/31/96# или #October 28, 1997#. Если вы просмотрите тексты записанных макросов, то увидите много примеров констант, в основном это будут текстовые константы. Вы можете менять их, только непосредственно редактируя текст программы.
Константы применяются для хранения таких данных, которые не меняются в течение выполнения программы, в отличие от переменных, которые могут варьироваться. Константы могут быть аргументами функций, могут входить в арифметические выражения и операторы сравнения.
Не все константы являются непоименованными. VBA позволяет вам создавать поименованные константы. Поименованная константа, как и переменная, имеет имя, по которому вы можете к ней обратиться. Это имя связано с конкретным, не изменяющимся значением. Как и в случае с переменной, вместо константы в выражение подставляется значение, связанное с константой. Разница лишь в том, что значение константы не может быть изменено программой, единственный способ изменить его — это отредактировать текст программы.
Константы используются для того, чтобы сделать текст программы более понятным и легким для восприятия. Например, при геометрических вычислениях гораздо понятнее, если в тексте встречается поименованная константа Pi вместо непоименованной 3.14.
102
Неделя 1
Кроме того использование констант облегчает модернизацию программ. Например, если вы пишете программу, вычисляющую доход вашей компании, в программе, скорее всего, будет фигурировать величина цены товара. Задав эту цену в виде непоименованной константы, вам придется переписывать программу, когда цена изменится, а именно вам придется менять все выражения, в которые входит цена. Если же вы вместо непоименованной константы создадите поименованную, и в выражениях будете использовать ее имя, то вам придется изменить значение этой константы только в одном месте.
В общем случае следует использовать поименованную константу всегда, когда в программе встречается повторяющееся значение, или значение, которое трудно запомнить, или если само значение на момент написания программы неизвестно.
Создаем пппмвппванпуш копсшапшд
Имя константы подчиняется тем же правилам, что и имя переменной. Правила создания имен переменных мы рассмотрели выше в этой главе.
Как и в случае с переменной, константа должна быть объявлена до того, как вы первый раз к ней обратитесь. Но в отличие от переменной, константа всегда объявляется явно, и служит для этого ключевое слово Const. Вот синтаксис объявления поименованной константы.
Const имя = значение
Значение может быть любого типа — числом, строкой или датой. Приведем несколько примеров правильного объявления констант.
Const BillingPoint = 212
Const Цена =8.25
Const Приветствие = "Привет, коллеги!"
При желании можно объявить несколько констант в одной строке, разделяя их запятыми. Следующая строка полностью эквивалентна предыдущим трем.
Const BillingPoint = 212, Цена = 8.25, Приветствие = "Привет, коллеги!"
При объявлении констант можно пользоваться ранее объявленными константами, арифметическими выражениями и операторами сравнения, но нельзя применять операцию конкатенации, использовать переменные и оператор Is. (Конкатенацию, арифметические операторы и оператор Is мы рассмотрим на следующем занятии.)
Вот пример правильного объявления констант.
Const BillingPoint = 212
Const DangerZone = BillingPoint+50
Область видпмости констант
Как и переменную, вы можете объявить константу как в процедуре, так и в области описаний модуля. Константа, объявленная в процедуре, имеет процедурный уровень видимости, а объявленная в модуле — модульный уровень. Область видимости поименованных констант определяется так же, как и у переменных.
Поскольку одна из главных причин использования констант — это хранение повторяющегося значения, вы, скорее всего, захотите, чтобы константы были доступны для всех процедур в модуле. Поэтому чаще всего определения констант размещаются в начале модуля, в области его описаний с тем, чтобы обеспечить максимальную доступность.
День 3-Й. Типы данных, переменные и константы
103
Просмотрите листинг 3.7. Эта процедура вычисляет площадь круга и записывает полученное значение в переменную.
Листинг 3.7. Использоваиие константы в оычислеииях
1:	Const Pi = 3.14
2:	Dim CircleArea As Single
3:
4:	Sub Calc_CircleArea()
5:	Dim Radius	As	Single
6:	Radius	= 5
7:	CircleArea = Pi * (Radius * Radius)
8:	MsgBox	CircleArea,	, "Площадь круга"
9:	End Sub
В первой строке листинга 3.7 объявлена константа, в которую записано приближенное значение числа л. В 7-й строке вычисляется арифметическое выражение, соответствующее формуле площади круга. Когда VBA вычисляет это выражение, значение 3.14 подставляется вместо поименованной константы Pi. В 8-й строке процедура MsgBox выводит на экран результат — 78.5. Обратите внимание на то, что переменная CircleArea объявлена в модуле, поэтому она доступна другим процедурам этого модуля.
Правила написания неноимепованных констант
Даже если вы никогда не пользуетесь в своих программах непоименованными константами, вам все равно нужно уметь их писать, хотя бы для того, чтобы объявлять с их помощью поименованные константы. В следующих разделах мы рассмотрим несколько правил написания констант типа String, Date, Boolean и числового типа.
Строковые константы
Строковые константы в программе VBA подчиняются следующим правилам.
•	Строковая константа должна быть заключена в кавычки. Ниже приведен пример неправильной константы.
•	Это неправильная константа.
•	Пустая строка (называемая иначе нулевой или Null-строкой) обозначается парой кавычек без символа между ними.
•	Строковая константа не должна содержать символа перевода строки или символа продолжения строки. Ни одна из приведенных ниже строк не является правильной.
"Это неправильная строковая константа"
“И это неправильная строковая константа"
Если вы попробуете вставить одну из этих строк в программу в качестве строковой константы, VBA выдаст сообщение об ошибке и прекратит выполнение программы. Вот пример правильной строковой константы:
"Это правильная строковая константа"
104
Неделя 1
Числовые константы
Числовые константы подчиняются следующим правилам.
•	Числовая константа может содержать только цифры от 0 до 9.
•	Числовая константа может начинаться с символа и содержать десятичную точку.
•	Для записи числовых констант можно применять экспоненциальную форму записи. Никаких других символов в числовой константе не должно быть. Вот пример правильных числовых констант.
12
-14.3
6.345Е2
•	Нельзя использовать вспомогательные знаки вроде доллара или разделяющей запятой. Вот пример неправильных числовых констант.
$656
6,560
Константы даты
VBA распознает несколько форматов даты, но все константы даты должны быть обрамлены знаками #. Вот несколько примеров констант даты, которые VBA понимает.
#2-5-98 21:17#
#	February 5, 1998 9:17#
#Mar-31-98#
#	15 April 1998#
Независимо от того, какой из указанных форматов вы применили, когда курсор перейдет на новую строку, VBA преобразует дату к следующему виду (в зависимости от того, есть ли в вашей константе данные времени или нет):
#	2/5/98 9:17 PM#
#2/5/98#
Если вы пропустили знак #, VBA не сможет правильно распознать дату' и попытается интерпретировать ее как арифметическое выражение с переменными и константами. Например, следующую неправильно введенную дату (без символа #) VBA попытается вычислить, как выражение, содержащее знаки деления:
3/15/98
Если же вы по ошибке заключите дату в кавычки, VBA распознает ее как строковую константу. Например, следующее выражение будет принято как строковая константа, а не как дата:
'3/15/98"
Логические константы
Существует только две логические константы — True и False. Вы всегда должны писать эти слова полностью и не заключать их в кавычки.
День 3-й. Типы данных, переменные и константы
105
Указанпв тппа кппстанты
Когда вы создаете поименованную константу, VBA приписывает ей тип в соответствии с типом выражения, которое ей присваивается. Например, считается, что константа, содержащая строку, имеет тип String.
Но иногда вы можете указать тип константы явно. Это может понадобиться для повышения точности вычислений: константа, тип которой указан как Double, использует более широкий диапазон значений. Вы можете указать тип константы Integer, Long, Currency или другой для того, чтобы результат вычислений имел, в свою очередь, нужный тип. (На следующем уроке вы узнаете, что тип результата вычислений зависит от типа выражений, из которых он вычисляется.)
Константа может иметь любой из типов, перечисленных для переменных, кроме Object и Array. Тип константы объявляется точно так же, как и тип переменной, просто объявление начинается со слова Const. Синтаксис объявления типа константы выглядит следующим образом.
Const имя As Тип = значение
Здесь имя — это идентификатор константы, тип — один из перечисленных в табл. 3.1 VBA-типов, значение — это то значение, которое вы присваиваете константе. Вот пример правильного объявления константы.
Consr Pi As Double = 3.14
Здесь объявлена константа Pi, имеющая тип Double, и ей присвоено значение 3.14.
Предопределенные копсшапшы
В VBA есть несколько констант, значение которых заранее определено для вас. Такие константы называют еще внутренними. Кроме внутренних констант VBA, существует несколько констант, определяемых приложением, из которого вызывается VBA. В частности, Excel 2000 определяет несколько констант для работы с книгами Excel, MS Word определяет константы для работы с документами и шаблонами, MS Access имеет несколько констант для работы с базами данных.
Все внутренние константы, определенные в VBA, имеют имя, начинающееся с букв vb. Например, такие константы, как vbOKOnly, vbOKCancel, vbAbortRetrylgnore определены в языке Visual Basic. В приложении Excel определены, например, такие константы: xlChart, xlCountrySetting, xlWorksheet. Как видите, их имена начинаются с букв xl. Имена констант, определенных в приложении MS Word, начинаются с букв wd.
Внутренние константы служат для упрощения работы с некоторыми встроенными процедурами VBA, такими как уже знакомая вам MsgBox или InputBox, с которой вы познакомитесь в следующих разделах. Хотя до сих пор мы не пользовались такими константами, из материала предыдущих уроков вы, наверное, помните, что процедура MsgBox имеет необязательный аргумент, указывающий количество кнопок в окне. Аргументы, соответствующие кнопкам, обычно задаются с помощью внутренних констант VBA. (На уроке 9-го дня вы узнаете, как пользоваться кнопками при вызове процедуры MsgBox.)
Аналогичным образом константы, определенные в программе Excel, облегчают работу со свойствами и методами объектов Excel. (Со свойствами и объектами вы познакомитесь на 7-м занятии.)
(^ПРИМЕЧАНИЕ
Вы можете использовать внутренние константы точно так же, как и те, что вы определили самостоятельно. Мы будем рассматривать внутренние константы VBA на протяжении всего курса обучения.
/06
Неделя 1
Доступ к внутренним кнпстаптам с ппмпщьш окна Object Browser
Для того чтобы увидеть полный список внутренних констант, объявленных как в VBA, так и в главном приложении, вы можете воспользоваться программой Object Browser (Просмотр Объектов). Делается это следующим образом.
1. Вызовите редактор Visual Basic, нажав комбинацию клавиш <Alt+Fl 1 >.
2. Выберите в меню View-Object Browser (или щелкните на кнопке Object Browser на панели инструментов). Редактор Visual Basic откроет диалоговое окно Object Browser со списком констант VBA. Вы видите это окно на рис. 3.3.
Открыть окно Object Browser можно также, нажав клавишу <F2>.
Когда окно Object Browser раскрыто, просмотреть константы вы можете, сделав следующее.
1.	В списке Project/Library (Проект/Библиотека) диалогового окна Object Browser выберите VBA.
2.	В списке Classes (Классы) выберите Constants.
3.	Для получения подробной информации о конкретной константе выберите эту константу в списке Members of (Компонент) ‘Constants’.
Когда вы выберете константу в списке Members of‘Constants', ее имя появится в нижней части окна Object Browser. Дополнительную информацию об этой константе вы можете получить, щелкнув на кнопке Help.
jvBA
jFormatArialBoIdl 2
Classes
° <globais*
’£i Collection
«St ColorConstants -’«SLConstants  j, i«® Conversion	।
«St DateTime
S3 ErrObject «St FileSystem
**<t Financial
Members of ’Constants'
sa vbBack
a vbCr
И vbCrLf
cd vbFormFeed
® vbLf
SB vbNewLine и vbNullChar и vbNullString vhCThiprtPrrn''
Mtdje Constants
Member of УВД
Рис. 3.3. С помощью окна Object Browser вы можете получить информацию о константе, определенной в VBA или в Excel
<1ЙЧ1МШНИЕ
Внутренние константы VBA одинаковы для всех приложений, из которых вы можете вызвать редактор Visual Basic. Внутренние константы приложения, например Excel, доступны в окне Object Browser только тогда, когда вы работаете с этим приложением. Вы не увидите константу xlWorksheet, если окно Object Browser было открыто из программы MS Word.
День 3-Й. Типы данных, переменные и константы
107
Если в справочной системе есть дополнительная информация по элементу, выбранному в списке Classes, вы можете получить ее, щелкнув на кнопке Help в окне Object Browser. Эта кнопка отмечена вопросительным знаком, вы видите ее на рис. 3.3. Дополнительная информация существует не для каждого объекта.
Для того чтобы просмотреть внутренние константы приложения, из которого вызван редактор Visual Basic, вы должны выбрать в списке Project/Library не VBA, как ранее, а библиотеку приложения. Например, просмотреть константы приложения Excel вы можете, выбрав в списке Project/Library строку Excel. Просмотреть внутренние константы можно только того приложения, из которого был вызван Visual Basic.
Ввод данных пользователя в вашу процедуру
На втором занятии нашего курса вы научились пользоваться процедурой MsgBox для вывода на экран сообщений пользователю; несколько примеров в книге также включали это процедуру. Кроме того, на этом уроке вы познакомились с переменными и константами и теперь готовы к тому, чтобы научиться получать информацию от пользователя.
Получение данных, сохранение их в переменной, обработка этих данных или другой информации на основе этих данных — вот главные составляющие умения писать интерактивные программы. (Интерактивной называется программа, которая интенсивно общается с пользователем, выводя на экран информацию и получая информацию от пользователя.) Такие программы часто оказываются более эффективными, чем просто записанные макросы, так как пользователь может управлять их работой в зависимости от изменившихся условий.
Данные, полученные от пользователя, называются введенными данными, или просто вводом. Для получения данных от пользователя процедуры применяется функция InputBox. (Функция — это особый вид процедуры, которая возвращает некоторое значение.) Функция InputBox выводит на экран окно с текстом, предлагающим ввести некоторое значение, и полем ввода для этого значения. Кроме того, в окне функции InputBox присутствуют кнопки ОК и Cancel.
Перед тем как вызывать функцию InputBox, вы должны сформировать текст приглашения. Кроме того, вы можете задать заголовок окна, которое выведет на экран InputBox.
Синтаксис вызова InputBox выглядит следующим образом.
Строка = InputBox (Приглашение [, Заголовок])
где строка — это любая переменная, в которой может храниться текстовая строка; может иметь тип String или Variant. Приглашение — поименованная или непоименованная константа или переменная, содержащая текст. Это обязательный аргумент, вы должны указывать его в вызове функции InputBox.
Заголовок — это необязательный второй аргумент функции InputBox. (Все необязательные элементы в описании синтаксиса берутся в квадратные скобки.) Заголовком также может быть поименованная или непоименованная константа или переменная, содержащая текст. Если вы опустите этот аргумент в вызове функции, в заголовке будет выведена строка Microsoft Excel. В листинге 3.8 вы видите модуль, содержащий единственную процедуру, и константу и переменную, объявленные на уровне модуля. Эта процедура, как и в листинге 3.7, вычисляет площадь круга. Но в отличие от процедуры из листинга 3.7, которая всегда вычисляет площадь круга радиусом в 5 единиц, эта процедура спрашивает у пользователя, для какого радиуса следует вычислять площадь.
/08
Неделя 1
Листинг 3.8. Подвчение ввода шьзввгтеи с номощьи функции lnpuIBnx
1:	Const Pi As Single = 3.14	'приблизительное значение числа Pi.
2:	Dim CircleArea As Single	'сюда запишем вычисленную площадь
3:
4:	Sub Calc_CircleArea()
5:	Const BoxTitle = "Площадь круга"
6:	Dim Radius As Single, Temp As String
7:	Temp =	InputBox("Введите	радиус круга", BoxTitle)
8:	Radius	= CSng(Temp)
9:	CircleArea = Pi * (Radius * Radius)
10:	MsgBox	CircleArea,	,	BoxTitle
11:	End Sub
В 1-й строке на уровне модуля объявляется константа Pi. Во 2-й строке тоже на уровне модуля объявляется переменная CircleArea. Оба эти элемента объявляются таким образом, чтобы быть доступными другим процедурам этого модуля — число Pi может им понадобиться для вычислений, а площадь круга — просто как результат, ведь для чего-то же ее вычисляли.
В 4-й строке собственно объявляется процедура; как и ранее, она называется Culc_CircleArea. В 5-й строке на уровне процедуры объявлена константа BoxTitle, доступная только этой процедуре. Это сделано потому, что константа BoxTitle вряд ли понадобится другим процедурам, ее назначение — только выводить заголовок в окна, создаваемые этой процедурой. В 6-й строке объявляются локальные переменные, которые тоже используются только в этой процедуре, и указывается их тип.
Обратите внимание на строку 7. В этой строке вызывается функция InputBox, которая выводит свой первый аргумент в качестве приглашения, поясняя пользователю, чего от него ждут. В данном случае выводится строка Введите радиус круга. Второй аргумент функции — это заголовок окна; в данном случае для заголовка окна используется переменная BoxTitle.
Так надо
Всегда старайтесь делать текст приглашения простым, позволяющим пользователю понять, что именно он должен ввести в текстовое поле.
При выполнении 7-й строки на экран выводится окно, которое вы видите на рис. 3.4. Пользователь вводит в текстовое поле нужное значение радиуса и щелкает на кнопке ОК или Cancel, как в любом окне Windows.
День 3-й. Типы данных, переменные и константы
109
После вызова функции вы должны каким-то образом использовать возвращаемое ею значение, называемое также результатом функции. Обычно возвращаемое значение присваивается некоторой переменной, как в строке 7 листинга 3.8, где возвращаемое значение присваивается переменной Тетр. (Более подробно мы рассмотрим функции на уроках 5-го и 6-го дней.)
Результат функции InputBox, или ее возвращаемое значение, — это всегда строка. Именно поэтому переменная Тетр была объявлена с типом String. Если пользователь щелкнет на кнопке ОК, функция InputBox вернет строку, которая была введена в текстовое поле. Если пользователь щелкнет на кнопке Cancel (нажмет клавишу <Esc> на клавиатуре или щелкнет на кнопке закрытия окна), функция InputBox вернет пустую строку.
Поскольку переменная Тетр была явно определена с типом String, перед тем как использовать результат функции InputBox в математических вычислениях, вы должны преобразовать его в число. Именно это и делается в строке 8. Ввод пользователя преобразуется в число с помощью встроенной функции CSng, одной из нескольких функций преобразования типов, которые более подробно будут описаны на пятом занятии.
И наконец, в строке 9 вычисляется собственно площадь круга и полученное значение присваивается переменной CircleArea. Для вывода этого значения используется функция MsgBox. Обратите внимание на то, что функция MsgBox использует в качестве заголовка окна ту же самую константу BoxTitle, что и функция InputBox.
Это хороший пример использования константы — вы можете быть уверены, что во всех окнах данной процедуры заголовки будут одинаковы, вам не нужно несколько раз вводить один и тот же текст, вы не создаете несколько непоименованных констант и не растрачиваете понапрасну память компьютера.
Резюме
На сегодняшнем уроке вы узнали, что такое типы данных и какие типы бывают в языке Visual Basic. Вы выяснили, что переменная — это поименованный участок памяти, где могут храниться некоторые данные. Вы научились описывать переменные и давать им имена. В этой главе мы рассмотрели такие понятия, как область видимости переменной и время ее жизни. Кроме того, вы узнали, что такое непоименованная константа и как записывать константы этого вида. Вы получили также представление о том, что такое поименованная константа, как они создаются и как определить их тип. И наконец, вы научились пользоваться функцией InputBox для получения ввода пользователя процедуры.
Вопросы о ответы
Обязан ли я указывать тип переменной или поименованной константы?
Нет, вы не обязаны этого делать. Если вы не укажете тип данных при создании переменной, VBA создаст ее с типом Variant. Если вы не укажете тип при создании константы, VBA выберет ее тип исходя из типа данных присваиваемого значения. Однако считается хорошей практикой указывать тип переменной и константы явно. По крайней мере, это облегчает чтение ваших программ и настраивает ваше мышление на более строгий лад, подчеркивая назначение каждой переменной и константы
Какой тип выбрать при создании переменной?
Выбирайте тип переменной исходя из типа данных, которые вы собираетесь в ней хранить. Например, если вы создаете переменную для хранения текста, выбирайте тип String; если переменная создается для хранения денежной величины, подходящим
ПО
Неделя 1
типом будет Currency. Если вы знаете, что в числовой переменной будут храниться небольшие числа, выбирайте тип переменной минимальной длины. Таким образом вы сэкономите память компьютера. Кроме того, это поможет вам находить ошибки в программе. Если вы точно знаете, что в переменной типа Integer не должно быть ничего, выходящего за соответствующий диапазон, но при работе программы получаете сообщение о переполнении, значит, в программе есть логическая ошибка
Когда и где объявлять переменные?
Вы должны выработать привычку объявлять переменные явно, лучше всего, если вы всегда будете применять директиву компилятора Option Explicit. (В дальнейшем в этой книге мы будем использовать только явное объявление переменных.) Это считается хорошей практикой не только из-за перечисленных выше преимуществ, но и потому, что такой подход дисциплинирует вас, программиста, заставляя планировать работу программы, что неминуемо сбережет ваши силы при отладке.
Объявляйте переменные в начале той процедуры, в которой вы хотите их использовать. Если вы хотите, чтобы переменная была доступна нескольким процедурам, объявляйте ее в начале модуля
Раз функция InputBox всегда возвращает данные типа String, означает ли это, что я не могу получить от пользователя данные другого типа?
И да, и нет. Действительно, функция InputBox всегда возвращает данные типа String, т.е. строку. Но в вашем распоряжении есть много средств для преобразования данных из одного типа в другой, как вы видели в листинге 3.8. Вы можете преобразовывать данные, возвращаемые функцией InputBox, к любому типу, кроме Object и Array. Таким способом можно получать от пользователя даты, денежные значения, целые числа или числа с плавающей запятой
Коллоквиум
В этом разделе вы найдете контрольные вопросы, которые помогут вам закрепить пройденный материал, а также упражнения, в ходе выполнения которых вы приобретете необходимый опыт. Постарайтесь разобраться и в вопросах, и в упражнениях, прежде чем переходить к следующему разделу. Ответы вы найдете в приложении в конце книги.
Тест
1.	Сколько числовых типов данных насчитывается в VBA?
2.	Какая разница между числами типа Integer и Single? Между Integer и Long?
3.	Что такое символы определения типа?
4.	Что означает выражение определить переменную неявно? А что такое явно?
5.	Каковы правила в языке VBA для имен переменных? Чем еще следует руководствоваться при выборе имен переменных?
6.	Какие преимущества дает явное описание переменных?
7.	Почему следует указывать тип переменной?
8.	Почему в процедурах лучше использовать поименованные константы?
9.	Для чего служит функция InputBox?
10.	Какие аргументы функции InputBox являются обязательными?
11.	Какого типа значение возвращает функция InputBox?
День 3-Й. Типы данных, переменные и константы
111
Упражнения
1.	Исходя из здравого смысла, решите, какая из перечисленных ниже величин должна быть переменной, а какая — константой. Выберите для них имена и напишите определение типа (если можно, сделайте это и с помощью ключевых слов, и с помощью символов определения типа).
А Вычисленное количество столбцов в листе Excel
Б Объем продаж для подразделения компании
В Предполагаемое количество респондентов в маркетинговом опросе
Г	Вычисленная площадь поверхности цилиндра
Д Коэффициент для перевода дюймов в сантиметры
Е	Рентабельность операции, выраженная в процентах
2.	Напишите вручную процедуру с названием EchoThis, в которой используется функция InputBox для ввода пользователем некоторой фразы, а потом эта фраза выводится на экран функцией MsgBox.
3.	С помощью программы записи макросов создайте в книге Personal.xls макрос, который будет открывать существующую книгу Excel и выбирать в ней лист с именем ЛистЗ. Остановите запись макроса. Отредактируйте записанный макрос так, чтобы он спрашивал у пользователя имя файла книги и затем открывал книгу с этим именем. (Подсказка. Сначала внимательно ознакомьтесь с текстом записанного макроса и поймите, в какой строке открывается файл книги. Поместите вызов функции InputBox непосредственно перед этой строкой. Присвойте введенное пользователем значение переменной, а затем подмените этой переменной непоименованную константу, созданную при записи макроса.)
4.	Измените макрос NewBook, который вы записали в упражнении 1 в 1-м уроке так, чтобы он принимал у пользователя имя книги, которую нужно создать и сохранить.
112
Неделя 1
Операторы о выражения
Итак, вы уже знаете, как записывать и редактировать макросы, умеете собственноручно писать простые процедуры, пользоваться константами и переменными. Значит, вы готовы к тому, чтобы научиться комбинировать эти элементы, создавая новые значения. На этом уроке мы рассмотрим следующие вопросы.
•	Что такое выражения и как они создаются.
•	Что такое арифметические, логические, строковые операции и как они используются в выражениях.
•	Что такое совместимость типов в выражении и как VBA иногда автоматически преобразует данные для обеспечения совместимости.
•	Как VBA определяет тип выражения и как подавить это определение.
•	В каком порядке VBA выполняет вычисления в выражении и как изменить этот порядок.
Материал этого урока не обязательно запоминать дословно. Вполне достаточно, если вы сможете ответить на контрольные вопросы в конце главы. Это будет означать, что вы усвоили материал на достаточном уровне. В дальнейшем, по мере чтения следующих глав и написания процедур на языке VBA, вы не раз вернетесь к этому уроку, чтобы освежить в памяти тот или иной факт.
Понятие об операторах и выражениях
Выражение — это величина или группа величин, объединенных в единую сущность. Каждое выражение имеет определенное и причем единственное значение. Например, выражение 2+2 имеет значение, равное 4. В состав выражения могут входить следующие элементы.
Константы (поименованные или непоименованные)
Переменные любого типа
Операторы
Массивы
Элементы массивов
Функции
День 4-й. Операторы и выражения
113
Выражение имеет единственное значение одного из типов, с которыми вы познакомились на предыдущем уроке. Кроме того, выражение может иметь одно из особых значений — Empty (пусто) и Null. Для обозначения этих особых значений в языке VBA существуют соответствующие зарезервированные слова. Значение Empty соответствует неинициализированной переменной типа Variant или выражению, в которое входит такая переменная. Значение Null соответствует выражению, содержащему неверные данные, т.е. неопределенному выражению. На уроке 10-го дня обучения эти ключевые слова и соответствующие им значения будут рассмотрены более подробно. Ниже приведено несколько примеров выражений.
Выражение	Пояснение
5	Значением является число 5
"5"	Значение — строка, состоящая из единственного символа 5
”сего"&"дня"	Значение — строка сегодня
С*(р/100)	Значение — число, полученное после умножения значения переменной С на значение, полученное после деления переменной Р на константу 100
CStr(1200)	Значение равно результату, возвращаемому функцией CStr. В данном случае это строка 1200 (функция CStr преобразует числа в строки)
MyValue <= 7	Логическое выражение. Его значение зависит от того, действительно ли значение переменной MyValue меньше или равно числу 7
Обратите внимание в первых двух примерах на то, что число 5 (типа Integer) и строка "5" (типа String) — это совершенно разные вещи, несмотря на то, что строка состоит из единственного символа.
Для объединения, сравнения или иного манипулирования значениями, входящими в выражение, используются операторы. Величины, входящие в выражение (это могут быть переменные или константы), называются операндами', большинство операторов требует наличия двух операндов. Например, в выражении 2+1 числа 2 и 1 — операнды, объединенные оператором сложения. В выражении может быть любое количество операторов.
Выражения могут входить в состав других выражений, сравниваться операторами сравнения или передаваться функциям и процедурам в качестве аргументов. Каждое выражение имеет единственное значение определенного типа. Выражения языка VBA в некотором смысле подобны предложениям человеческого языка. Каждая инструкция может содержать сколько угодно выражений.
Рассмотрим типы выражений, которые встречаются в языке Visual Basic.
•	Дата. Это любое выражение, которое имеет тип Date. Такие выражения могут содержать константы даты, переменные числового типа или типа Date, числовые константы и даты, возвращаемые функциями.
•	Числовое выражение. Это выражение одного из следующих типов: Byte, Integer, Long, Single, Double или Currency. В числовое выражение могут входить числовые переменные и константы, функции, возвращающие числа, и знаки арифметических операций. Кроме того, эти выражения могут включать строки, если их можно преобразовать в число. (Строку можно преобразовать в число, если в нее входят только цифры; например, строку "36" можно преобразовать в число 36.)
114
Неделя 1
•	Строковые выражения. Это выражения имеющие тип String. Такое выражение может включать строковые константы и переменные, функции, возвращающие строки, и операторы конкатенации строк. В строковые выражения могут также входить числовые выражения, если их можно преобразовать в строки.
•	Логические выражения. Это выражение, которое имеет одно из булевых значений, т.е. True или False. Такие выражения могут состоять из логических переменных и констант, функций, которые возвращают логическое значение, операторов сравнения и логических операторов.
•	Объектное выражение. Это выражение, значением которого является ссылка на объект. Объекты и объектные выражения описываются при изложении материала урока 7-го дня обучения.
Совместимость топов данных
Не все типы данных совместимы друг с другом. Нельзя комбинировать несовместимые типы в одном выражении. Например, нельзя складывать текстовую строку "Привет!'1 с числом — такая операция не имеет смысла, и ее значение не определено.
В то же время некоторые типы являются совместимыми. Вы можете свободно комбинировать различные числовые типы между собой, VBA автоматически выполнит необходимые преобразования. Иногда VBA может автоматически преобразовывать и другие типы данных, добиваясь их совместимости, но не всегда. Например, типы String и Date совместимы с числовыми данными только при некоторых условиях, которые мы обсудим позже на этом уроке.
Очень важно знать типы входящих в выражение значений, так как если в выражении встретятся несовместимые данные, то при выполнении программы VBA выдаст вам сообщение о несоответствии типов. Когда VBA встречает в выражении различные типы данных, он сначала пытается уладить конфликт, автоматически преобразуя данные к совместимым типам. Но если это не удается, вы получаете сообщение о несоответствии типов и выполнение программы прекращается.
То же самое произойдет, если вы попытаетесь присвоить переменной значение выражения, тип которого несовместим с типом переменной, т.е. VBA выдаст сообщение о несоответствии типов.
Аналогичным образом вы столкнетесь с подобной проблемой при попытке передать функции в качестве аргумента переменную, тип которой несовместим с типом аргумента функции.
Например, следующее выражение недопустимо, так как в нем сочетаются строка с числом.
"128" + 326
Если в вашей процедуре встретится нечто подобное, VBA выдаст вам сообщение о несоответствии типов и выполнение процедуры прекратится.
А вот примеры, в которых эта ошибка исправлена, и оба выражения верны, хотя и представляют собой совершенно разные значения.
"128" + "326" 'Значение - строка "128326"
128 + 326	'Значение - число 454
В языке VBA есть много функций, которые преобразуют данные из одного типа в другой, например строку в число или число в строку и т.п. (В предыдущем уроке вы встречались с функцией CSng, которая преобразует строку в число типа Single.) Подробно мы рассмотрим функции для преобразования типов в ходе пятого занятия нашего курса.
День 4-й. Операторы и выражения
115
Автоматическое преобразование типов
Автоматическое преобразование типов для обеспечения совместимости выполняется в VBA по-разному. Правила такого преобразования зависят от типов входящих в выражение величин и от объединяющих их операторов.
Проще всего это делается тогда, когда в выражение входят переменные типа Variant, поскольку этот тип не определен жестко. Если же в выражении содержатся непоименованные константы или константы и переменные заданных типов, правила преобразования становятся более строгими. (Напомним, что переменные и константы заданных типов — это те, которые вы объявили явно либо с помощью ключевого слова As, либо с помощью символов определения типа, как было описано в предыдущей главе.)
Когда в выражении есть величины заданных типов, VBA старается преобразовать другие величины так, что бы обеспечить совместимость; тип выражения в таком случае зависит от заданных типов.
Следующие выражения иллюстрируют, как типы составляющих и операторы влияют на правила преобразования и на тип всего выражения.
Выражение	Правила и тип результата
Число + строка	Выдает сообщение о несоответствии типов, если число имеет числовой тип, а строка — тип String. Выполняется конкатенация, если число имеет тип Variant, а строка — тип String. Результат имеет тип String. Выполняется арифметическое сложение, если число имеет числовой тип, а строка — тип Variant. Результат имеет числовой тип
Число + "3"	Всегда выполняется арифметическое сложение, независимо от того, имеет ли число числовой тип или тип Variant. Результат — число
3 + строка	Выполняется арифметическое сложение, если строка имеет числовой тип или тип Variant и в ней содержится текст, который можно преобразовать в число. Результат — число. Если в строке содержится текст, который VBA не может преобразовать в число, выдается сообщение о несоответствии типов
Число & строка	Всегда выполняется конкатенация, независимо от типа переменных. Результат — типа String
Обратите внимание на последнюю строку этого списка. В ней приведен оператор конкатенации (&), который объединяет строки, или сливает их. Поскольку этот оператор можно применять только к строкам, VBA преобразует все элементы выражения к типу String, независимо от исходных типов элементов. (Конкатенацию строк мы рассмотрим позже в ходе этого урока.) Далее в этой главе мы подробно рассмотрим все операторы и правила преобразования, связанные с каждым оператором.
Вы, наверное, заметили, что сообщение о несоответствии типов выдается только тогда, когда оба элемента выражения имеют определенный тип и эти типы не совпадают. Сообщение о несоответствии типов не выдается, если один из операндов имеет тип Variant. Как мы уже говорили, проще всего преобразование типов выполняется тогда, когда переменная имеет тип Variant, но не следует этим злоупотреблять.
//6
Неделя 1
Польза сообщения о несоатввтсшвпо попов
Не следует относиться к сообщению о несоответствии типов как к чему-то, отравляющему вашу жизнь. Напротив, от него может быть больше пользы, чем вреда. На уроке 3-го дня занятий мы говорили, что объявлять явно тип переменных нужно для того, чтобы избежать многих неприятных ошибок. В самом деле, смешение в одном выражении разных типов - это почти всегда следствие ошибки или непродуманное™ программы, кроме того, результат такого выражения часто бывает непредсказуем.
Предположим, написав выражение З+Str, вы имели в виду присоединить к символу 3 текст, содержащийся в переменной Str, но забыли взять в кавычки символ 3, т.е. вместо непоименованной строковой константы использовали число. Конкатенация строк в этом случае выполнена не будет, но если вы объявили переменную Str как имеющую тип String, вы получите сообщение о несоответствии типов, которое укажет вам на сделанную ошибку.
Преобразование числовых типов
VBA обычно преобразует все типы элементов числового выражения к самому точному, или, можно сказать, к самому старшему из упомянутых в этом выражении типов. Например, если в выражении есть переменные типа Single и Integer, результат выражения будет иметь тип Single, потому что точность этого типа выше.
^ПРИМЕЧАНИЕ
Говоря о точности, мы имеем в виду количество значащих цифр, которые можно сохранить в переменной. Значащие цифры - это те, которые влияют на величину числа. Например, нули впереди числа и в конце его дробной части - это незначащие цифры. Числа в экспоненциальной форме (см. 3-й урок) состоят только из значащих цифр.
Если вы присвоите значение выражения переменной, точность которой ниже, чем у выражения, VBA округлит значение до той точности, которая присуща переменной. Например, когда вы присвоите значение выражения типа Double переменной типа Integer, значение выражения будет округлено до точности переменной типа Integer. Рассмотрим пример:
Dim Num As Integer
Num =1+1.51 'В Num записывается целое число 3
Во второй строке в действительности получается число типа Double, равное 2.51, но VBA округляет его до числа типа Integer, и результат получается равным 3.
Првпбразование строк и чисел
Преобразуя число в строку, VBA создает строку, состоящую из цифр этого числа и десятичной точки, если она присутствует в числе. Например, число 412.72 преобразуется в строку "412.72". Если число очень большое или, наоборот, очень маленькое, строка может быть сформирована в экспоненциальном виде. Так, число 0.0000000003456 преобразуется в строку "3.456Е-10".
Строка может быть преобразована в число, если она содержит только цифры или соответствует экспоненциальной записи. Строки "12", "-16.43" и "3.657Е7" могут быть успешно преобразованы в числа. Строки "1,234", "$74.59” и "1000 и одна ночь" не могут быть преобразованы в числа, так как в них содержатся нечисловые символы.
День 4-й. Операторы и выражения
117
Преобразования булевых величин
При преобразовании булевых величин в числа значение True преобразуется в число — 1, а значение False — в 0. При преобразовании чисел в булевы величины число 0 преобразуется в значение False, а все остальные числа — в True. При преобразовании булевых величин в строки значение True преобразуется в строку "True", а значение False — в строку "False". VBA не может преобразовать строку в булево значение. Когда вы используете булевы величины в арифметических выражениях, они всегда преобразуются в числа.
Првобразованнв gam
При преобразовании даты в число получается число типа Double, содержащее количество дней, прошедших от 30 декабря 1899 г. до этой даты. Отрицательные числа соответствуют датам до 30 декабря 1899 г. Дробная часть числа, если она есть, выражает время в долях суток. 0 соответствует полночи, а 0.5 — полудню. Преобразование чисел в даты выполняется точно наоборот.
Если значение выражения превосходит диапазон значений для данного типа, VBA выдает сообщение об ошибке переполнения и прерывает выполнение программы.
Если в вашем выражении присутствует непоименованная константа, вы можете использовать символы определения типа и предотвратить переполнение, заставив вычислять это выражение с более высоким типом. Например, в следующем выражении переменная Num описана как Integer, поэтому все выражение получает тип Integer.
Num * 2
Если значение переменной Num настолько велико, что при умножении получается число больше чем 32767, вы получите переполнение, и выполнение программы прекратится. Однако со следующим выражением этого не произойдет, так как константе 2 с помощью символа определения типа приписан тип Long (напомним, что символ & соответствует типу Long).
Num * 2s
Благодаря тому, что VBA всегда придает выражению тип, соответствующий переменной с самым высоким типом, это выражение будет вычислено как Long.
Оператор присваивания
В материале предыдущих уроков вам уже встречался оператор присваивания (=). Этот оператор служит для того, чтобы записать значение выражения в переменную или значение одной переменной в другую. Точнее говоря, значение выражения, стоящего справа от оператора, присваивается переменной, стоящей слева от него.
Оператор присваивания, хотя и напоминает внешне знак равенства, который знаком вам из школьного курса алгебры, смысл его совсем другой. Знак равенства в алгебре служит для обозначения того факта, что две величины равны одна другой, и это позволяет вам решать уравнение, находя такие значения неизвестных, при которых это равенство соблюдается. Оператор присваивания служит другой цели - он записывает некоторое значение в участок памяти, на который указывает переменная слева от него.
118
Неделя 1
Оператор присваивания имеет две синтаксические формы, которые обе законны и совершенно эквивалентны. В первом случае используется ключевое слово Let и синтаксис имеет следующий вид.
Let имя = выражение
где имя — идентификатор переменной, а выражение — любое допустимое в VBA выражение. Такая форма оператора присваивания пришла из старого языка Basic. Вот пример использования оператора присваивания.
Let X = Y 'Значение Y записывается в переменную X
Вторая форма более общепринята и используется в этой книге повсеместно. Синтаксис ее таков:
имя = выражение
где имя — идентификатор переменной, а выражение — любое допустимое в VBA выражение. Вот пример использования оператора присваивания в этой простой и общепринятой форме.
X = Y
MyVar = 12+YourVar
В обеих формах оператора присваивания переменная слева от него получает значение выражения справа. При выполнении оператора присваивания сначала вычисляется выражение, стоящее справа от оператора, а потом полученное значение присваивается переменной, стоящей слева.
Во многих процедурах вы можете видеть такую конструкцию.
Count = Count + 1
GrossTotal = GrossTotal + SubTotal
Это часто применяется для накопления значения какого-нибудь счетчика. Смысл написанного не сразу очевиден; на первый взгляд даже может показаться, что это противоречит логике. Но вы поймете написанное, если вспомните, что при выполнении оператора присваивания сначала вычисляется выражение, стоящее справа от оператора, а потом полученное значение присваивается переменной, стоящей слева.
Например, предположим, что перед выполнением оператора присваивания переменная Count имеет значение 9.
Count = Count + 1
Сначала VBA вычисляет значение выражения справа от оператора присваивания. Для этого VBA берет текущее значение переменной Count и прибавляет к ней 1, а потом полученное значение, равное 10, присваивает переменной Count. Предыдущее значение переменной теряется.
Вы наверняка будете чаще писать и встречать в чужих текстах короткую форму оператора присваивания, хотя бы из экономии сил. Длинная форма, использующая слово Let, рекомендуется начинающим программистам, так как она делает более очевидной разницу между оператором присваивания и оператором сравнения, которые выглядят очень похоже.
Хотя оба эти оператора используют один знак (=), вы не должны их путать. В следующем примере в первой строке приведен оператор присваивания, который записывает значение 5 в переменную MyVal, а во второй строке — логическое выражение, которое принимает значение True или False в зависимости от того, равна или нет 5 переменная MyVal.
MyVal = 5 'Присваивает значение 5 переменной
(MyVal = 5) 'Сравнивает переменную и число
День 4-й. Операторы и выражения
119
Обратите внимание на то, что вторая строка в этом примере — это не полноценная инструкция, хотя и полноценное логическое выражение. Дело в том, что результат этого выражения никак не используется; такая строка в программе вызовет синтаксическую ошибку. В правильной инструкции результат выражения должен как-нибудь использоваться — быть присвоенным переменной, или быть аргументом функции или процедуры и т.п.
Когда вы присваиваете результат выражения переменной определенного типа, тип результата должен быть совместим с типом переменной. Во многих случаях VBA может преобразовать тип выражения к типу, совместимому с типом переменной, если их типы не указаны жестко. Переменной типа Variant можно присваивать результат любого типа.
Так надо
Помните, что всегда можно присваивать значение числовой переменной или числового выражения другой числовой переменной или переменной типа Variant. Если вы присвоите числовое значение переменной с недостаточно высокой точностью (например, значение типа Double переменной типа Long), VBA округлит это значение до точности переменной.
Так не надо
Если переменной типа String вы присвоите значение переменной типа Variant, содержащей число, VBA преобразует это число в строку. Присваивание переменной типа String числового значения (переменной или константы) вызывает ошибку несоответствия типов.
Арифметические операторы
VBA может выполнять все стандартные арифметические действия: сложение, вычитание, умножение и деление. Кроме того, есть оператор для возведения числа в степень и дополнительные операторы для целочисленного деления и для деления по модулю. В табл. 4.1 собраны арифметические операторы VBA.
Оператор	Синтаксис	Пояснение
+	Nl + N2	Складывает N1 и N2
-	Nl - N2 - N1	Вычитает N2 из N1 Унарный минус (отрицательное число)
*	Nl * N2	Умножает N1 на N2
/	Nl / N2	Делит N1 на N2
\	Nl \ N2	Целочисленное деление. Делит N1 на N2, отбрасывая дробную часть
Mod	Nl Mod N2	Деление по модулю. Делит N1 на N2, возвращая остаток от деления
А	Nl Л N2	Возводит N1 в степень N2
В следующих разделах мы рассмотрим эти операции в деталях.
120
Неделя 1
Сложение
Оба операнда оператора сложения должны быть числовыми выражениями или строками, которые VBA может преобразовать в числа. Кроме того, сложение можно применять к датам, т.е. к переменным типа Date. Вот примеры правильного синтаксиса.
MyVal + 1	'прибавляет 1 к переменной
MyVal + YourVal 'прибавляет одну переменную к другой
Тип результата сложения обычно совпадает с самым высоким типом его операндов. Если операнды — числа типа Integer и Long, результат будет иметь тип Long. Но это не всегда так, особенно если в выражении есть переменные типа Variant.
•	Результат сложения чисел типа Single и Long будет иметь тип Double.
•	Если вы складываете число типа Date с любым другим числом, результат имеет тип Date.
•	Если вы присваиваете результат сложения переменной типа Variant, в которой записано число типа Integer и результат сложения переполняет это число, VBA преобразует результат в тип Long, и переменная после этого имеет тип Long.
•	Если вы присваиваете результат сложения переменной типа Variant, в которой записано число типа Long, Single или Date, и результат сложения переполняет это число, VBA преобразует результат в тип Double и переменная после этого имеет тип Double.
•	Если хоть один из операндов сложения имеет значение Null, то и результат сложения имеет значение Null. (Null — это особое значение, которое вы можете присвоить только переменной типа Variant для того, чтобы указать, что в ней нет правильных данных. Подробнее мы поговорим об этом на 10-м уроке.)
ПРИМЕЧАНИЕ
Когда мы говорим о более высокой или низкой точности, то имеем в виду, что типы данных располагаются по точности от меньшей к большей в следующем порядке: Byte, Integer, Long, Single, Double и Currency.
Вот несколько примеров правильного использования оператора сложения.
Now + 1 'результат имеет тип Date
PentProfit +0.5
#1/1/99# + 60
В первой строке идентификатор Now — это имя функции, которая возвращает текущую дату в соответствии с часами вашего компьютера.
Вычитание
Знак вычитания служит двум разным целям: он обозначает либо операцию вычитания либо унарный минус.
Унарным минусом называется знак, который вы ставите перед числом или выражением, чтобы сделать эту величину отрицательной. Поставить минус перед переменной или выражением — это то же самое, что умножить его на -1. Вот примеры использования унарного минуса:
День 4-й. Операторы и выражения
121
-1
-MyVal
-(MyVal + 10)
В последней строке приведен пример того, как с помощью унарного минуса можно изменить знак выражения; если переменная MyVal имеет значение 5, то все выражение имеет значение -15.
В следующих примерах показано, как операция вычитания используется именно для вычитания чисел.
Now - 60	'результат имеет тип Date
40 - MyVal
Sales - Costs
Оба операнда при вычитании должны быть числами или строками, которые можно преобразовать в числа. Операцию вычитания можно применять к датам. Тип результата при вычитании обычно совпадает с самым высоким типом операнда.
При определении типа результата VBA следует тем же правилам, что и при сложении, но с некоторыми дополнениями.
•	Если один из операндов имеет тип Date, то и все выражение имеет тип Date.
•	Если оба операнда при вычитании имеют тип Date, то результат имеет тип Double.
ПРИМЕЧАНИЕ
Несмотря на то что для конкатенации (слияния) строк можно применять знак знак никак не может быть применен для разделения строки. Для этого существуют специальные функции Mid, Left и Right, которые мы рассмотрим в следующем уроке.
Умножение
Оба операнда при умножении должны быть числами или строками, которые можно преобразовать в числа. Вот примеры правильного использования оператора умножения.
4 * 10
NumVal * 2
NumVal * OtherVal
Тип результата при умножении обычно совпадает с самым высоким типом операнда. Правила при этом те же, что и при сложении. При умножении все переменные типа Variant, в которых записана величина типа Date, преобразуются в числовой тип.
Деление
Оператор деления, обозначаемый знаком иногда называют делением действительных чисел, чтобы отличать его от целочисленного деления, которое мы рассмотрим чуть ниже. Оба операнда при делении должны быть числами или строками, которые можно преобразовать в числа.
Вот примеры правильного использования оператора деления.
9 / 3
NumVal /17
Minutes / 60
/22
Неделя 1
Если хоть один из операндов имеет значение Null, то и все выражение имеет значение Null. Результат деления обычно имеет тип Double.
ПРИМЕЧАНИЕ
Деление на нуль всегда вызывает ошибку времени выполнения. Из курса математики известно, что деление на нуль запрещено, поэтому, следуя такому правилу, VBA всегда выдает ошибку времени выполнения и прерывает программу, обнаруживая деление на нуль.
Целочисленное деление
Целочисленное деление очень похоже на деление действительных чисел, с той разницей, что результат всегда является целым числом. Оба операнда при целочисленном делении должны быть числами или строками, которые можно преобразовать в числа. Вот примеры правильного использования целочисленного деления:
4 \ 2.5	'Результат равен 1
6 \ NumVal
MyVal \ NumVal
При выполнении целочисленного деления VBA всегда округляет операнды перед вычислением. Таким образом, если вы делите 19.5 на 2, VBA сначала округлит 19.5, получив 20, а затем разделит на 2. Немного неожиданно, но результат целочисленного деления 19.5 на 2 — 10.
VBA отбрасывает дробную часть результата при целочисленном делении. Например, результат обычного деления 19 на 5 — 3.8, но при целочисленном делении результат будет 3. VBA не округляет результат, а просто отбрасывает дробную часть.
Тип результата — Integer или Long; VBA выбирает наименьший тип, в который можно уместить результат. Если хотя бы один из операндов имеет значение Null, результат тоже имеет значение Null.
Деление по модулю
Результат деления по модулю двух целых чисел — это остаток от деления первого числа на второе. Оба операнда при делении по модулю должны быть числами или строками, которые можно преобразовать в числа. Вот примеры правильного использования деления по модулю.
4 Mod 2 'результат = О
5.4 Mod 3 'результат = 2
6 Mod MyVal
MyVal Mod NumVal
При выполнении деления по модулю VBA всегда округляет операнды перед вычислением. Таким образом, если вы делите 5.4 на 3, VBA сначала округлит 5.4, получив 5. а затем разделит на 2. Результат целочисленного деления 5.4 на 3 — 2.
Тип результата — Integer или Long; VBA выбирает наименьший тип, в который можно уместить результат. Если хотя бы один из операндов имеет значение Null, результат тоже имеет значение Null.
День 4-й. Операторы и выражения
123
Возведение в степень
Возведение числа в степень N — это N-кратное умножение его на само себя. Например, 23 — это то же самое, что и 2*2*2, т.е. 8. Для того чтобы записать в программе на Visual Basic 23, нужно написать:
2 А 3
Оба операнда при делении по модулю должны быть числами или строками, которые можно преобразовать в числа. Отрицательные числа можно возводить только в целую степень. Если хотя бы один из операндов имеет значение Null, результат тоже имеет значение Null, в противном случае тип результата Double.
Операторы сравнения
Операторы сравнения иногда называют операторами отношения. Обычно их применяют для задания критерия на принятие решения или для формирования условия выполнения некоторой последовательности операций. (На уроке 8-го дня нашего курса описывается процесс принятия решений, а на уроке 9-го дня — циклические вычисления.)
Результат оператора сравнения имеет тип Boolean и принимает только два значения - True и False. Сравнивать можно поименованные и непоименованные константы и переменные сходных типов.
В табл. 4.2 приведены операторы сравнения и описано их действие (буквой Е обозначено любое допустимое в VBA выражение).
Таблица 4.2. Операторы сравнения
Оператор	Синтаксис		Описание
=	El	= E2	Равно. True, если El равно E2, False в противном случае
<	El	< E2	Меньше. True, если El меньше Е2, False в противном случае
< =	El	<= E2	True, если Е1 меньше или равно Е2, False в противном случае
>	El	> E2	True, если Е1 больше Е2, False в противном случае
>=	El	>= E2	True, если Е1 больше или равно Е2, False в противном случае
о	El	О E2	Не равно. True, если Е1 не равно Е2, False в противном случае
Is	El	Is E2	Является. Оба операнда должны иметь тип Object. True, если El и Е2 указывают на один и тот же объект, False в противном случае
Like	El Like E2		Подобно. Оба операнда должны иметь тип String. True, если El соответствует образцу, содержащемуся в Е2, False в противном случае
124
Неделя 1
Если оба операнда имеют один тип, VBA выполняет простое сравнение, например, если оба операнда — строки, то выполняется их сравнение по алфавитному принципу; если операнды — даты, то они сравниваются как числа.
Вот пример использования операторов сравнения.
MyVal = 2 4 о 5
NumVal <17 "Sam” < "Joe" "Fred" < "Freddy" Now > *5.30.1999*
'True - 4 не равно 5
'False - S больше, чем J 'True - Freddy длиннее
'True после 5-го мая 1999г.
ПРИМЕЧАНИЕ
Помните, что в переменной типа Date кроме даты может содержаться и время (в дробной части числа). При сравнении двух дат VBA фактически сравнивает и записанное в них время. Поэтому, две переменные могут быть не равны, даже если в них записана одна и та же дата. В частности, *1/2/97* меньше, чем *1/2/97 9:00:00 AM*. VBA считает, что в первой константе время равно 0, поэтому вторая константа больше первой.
Для того чтобы сравнить только даты, игнорируя время, можно использовать функции CInt и CLng, преобразующие абсолютную дату в целое число. В ходе преобразования абсолютной даты в целое число информация о времени отбрасывается и при сравнении учитываются только даты. (О том, что такое абсолютная дата, мы говорили на 3-м уроке; функции преобразования типов будут рассмотрены на 5-м уроке.)
При сравнении числовых величин их тип не играет большой роли. Однако, если вы попытаетесь сравнить две величины разных типов, таких как Integer и string или Variant с любым другим типом, может возникнуть ошибка несоответствия типов или результат будет не таким, как вы ожидали.
Если операнды в операторе сравнения имеют разные типы и эти типы несовместимы (неважно, константы это или переменные), VBA выдаст сообщение об ошибке несоответствия типов. Например, следующие выражения вызывают такую ошибку из-за того, что в них сравниваются несравнимые величины.
1 > "zero"
NumVal1 <= StrVal$
Если один или оба операнда в выражении сравнения являются переменными типа Variant, VBA попытается преобразовать их к совместимым типам, но в случае невозможности такого преобразования будет выдана ошибка времени выполнения.
Не всегда можно с уверенностью сказать, какое сравнение будет делать VBA — числовое или алфавитное, — когда в выражении сравнения смешаны числовые и символьные величины, поэтому следует применять функции преобразования типов, такие как CStr, для того чтобы обе сравниваемые величины имели один тип и не было сомнений в том, как они будут сравниваться. (Функции преобразования типов мы рассмотрим на следующем уроке.)
Сравнение строк
Сравнение строк происходит не так просто, как сравнение чисел. В языке VBA две строки считаются равными тогда и только тогда, когда они состоят в точности из одних и тех же символов, следующих в том же порядке, и имеют одинаковую длину.
День 4-й. Операторы и выражения
125
Посмотрите на следующие выражения:
"abc" = "abc"	'Верно, строки полностью совпадают
"abc" = "abc " 'Неверно, это разные строки
В первом примере строки действительно равны, они состоят из одних и тех же символов, введенных в том же порядке, и имеют одинаковую длину. Во втором примере строки не совпадают, в конце правой строки есть пробел, которого нет в левой. При сравнении строк VBA не игнорирует ни лидирующие, ни концевые пробелы.
При сравнении строк с помощью операторов отношения VBA проверяет каждую строку слева направо, символ за символом. Фактически VBA сортирует строки в алфавитном порядке, а потом смотрит, какая строка получилась “меньше”. Если две строки отличаются по длине, но в остальном совпадают, меньшей считается та, что короче. В следующих примерах иллюстрируется, как выполняется сравнение строк в VBA.
"abc" < "abc " 'Верно "abed" > "abc" 'Верно "ааа" < "aab" 'Верно "aab" < "abb" 'Верно "abb" < "abc" 'Верно
Бинарное н текстовое сравнение
До сих пор мы рассматривали сравнение строк, набранных только в нижнем регистре. Однако VBA предоставляет два разных способа сравнения символов в разных регистрах. Один из них, который используется по умолчанию, называется бинарным сравнением.
Для того чтобы понять, что такое бинарное сравнение, вы должны вспомнить, что все символы в компьютере хранятся в виде чисел. Для хранения текста в компьютере используется схема, в которой каждому символу соответствует некоторое число. Каждой букве от “а” до “z” (и русским буквам от “а” до “я”) соответствует порядковый номер. То же справедливо и для букв в верхнем регистре. Буквы в верхнем регистре имеют меньшие номера, чем буквы в нижнем регистре. Номер каждой буквы называется кодом символа. __ ______________________________________________________
СОВЕТ	Будьте внимательны при сравнении строк переменной и фиксированной длины. Помните, что строка фиксированной длины всегда имеет одно и то же количество символов, а строка переменной длины увеличивается или уменьшается в зависимости от количества записанных в ней символов. Посмотрите наследующий фрагмент программы. Dim FixLenAs Srting * 10, VarLen As String FixLen = "Тест" VarLen = "Тест" MsgBox (FixLen = VarLen)	'на экран выводится False Поскольку в строке FixLen всегда ровно 10 символов, она длиннее, чем строка VarLen, в которой только 4 символа. VBA считает, что длинная строка больше, чем короткая. Для разрешения этого противоречия можно воспользоваться функцией Trim, которая удаляет из строки обрамляющие ее пробелы, что и сделано в следующем примере. Dim FixLenAs Srting * 10, VarLen As String FixLen = "Тест" VarLen = "Тест" MsgBox (Trim(FixLen) = VarLen)	' выводится True
126
Неделя 1
Когда VBA сравнивает строки бинарным способом, он сравнивает коды символов Поскольку коды символов верхнего регистра меньше, чем коды верхнего регистра, строки, набранные в верхнем регистре, оказываются меньше, чем строки в нижнем регистре. Другими словами, строка “АДА” меньше, чем строка “ааа”.
Второй способ сравнения называется текстовым сравнением. При таком сравнении VBA игнорирует регистр символов и считает, что символы верхнего регистра равны символам нижнего. При текстовом сравнении строки “ААА” и “ааа” оказываются равными друг другу. В следующем примере поясняется разница между текстовым и бинарным сравнениями.
"Боб" = "боб"	'Неверно при бинарном сравнении
'Верно при текстовом сравнении
"Боб" < "боб"	'Верно при бинарном сравнении
'Неверно при текстовом сравнении
Выбор способа сравнения
Для выбора способа сравнения строк существует директива компилятора Option Compare.
Option Compare [ Text : Binary]
Для выбора бинарного сравнения предназначено ключевое слово Binary, а для текстового сравнения — Text. Директива Option Compare должна быть помещена на отдельной строке в начале модуля, в области его описаний.
Option Compare Text
Директива Option Compare используется только на уровне модуля и влияет на способ сравнения во всех процедурах этого модуля. Обычно ее помещают перед всеми объявлениями переменных и процедур. Если такая директива отсутствует, сравнение выполняется бинарным способом.
Оператор Like
Оператор Like служит для сравнения строк особым способом. Этот оператор применим только к строкам.
Оператор Like проверяет, соответствует ли строка указанному образцу. Таким способом можно искать в тексте все вхождения слов или даже фраз, соответствующих образцу; поиск такого типа иногда называют неопределенным поиском (fuzzy search).
Синтаксис оператора Like выглядит следующим образом.
StrExprl Like StrExpr2
StrExprl — любое строковое выражение. StrExpr2 — специально сконструированное строковое выражение, служащее образцом, с которым сравнивается выражение StrExprl.
Оператор Like имеет значение True, если первый операнд (StrExprl) совпадает с образцом, заданным вторым операндом. В противном случае значение будет False. Оба операнда должны быть правильными строковыми выражениями, иначе VBA выдаст сообщение об ошибке несоответствия типов.
Образец для сравнения задается с помощью различных специальных символов. Например, следующее выражение имеет значение True, если в строке AnyStr записан текст вроде “День”, “Дождь”, “Дурень”, т.е. любое слово, начинающееся на букву “Д” и заканчивающееся на букву “ь”.
День 4-й. Операторы и выражения
127
AnyStr Like "Д*ь"
Следующее выражение имеет значение True, если в строке AnyStr записан текст “кот” или “кит”, но не “компот”.
AnyStr Like “к?т"
Справа от оператора Like должна быть строка или строковое выражение с образцом для сравнения. В табл. 4.3 объясняются значения специальных символов, применяемых для конструирования образца.
Таблица 4.3. Специальные символы для образца в операторе Like
Символ подстановки	Что означает
#	Любая цифра
*	Любая строка
9	Любой одиночный символ
[список]	Любой символ из списка
[[список]	Любой символ, не входящий в список
Последние две строки в этой таблице показывают критерии совпадения или несовпадения с конкретными символами. Рассмотрим примеры использования списка символов в квадратных скобках.
Следующее выражение имеет значение True, если в строке AnyStr записано “абв” или “абг”:
AnyStr Like "аб[вг]"
Следующее выражение имеет значение True, если в строке AnyStr записано “мол” или “мел” или нечто похожее, но не “мал”:
AnyStr Like "м[!а]л"
В квадратных скобках можно также указывать диапазон символов, с которыми требуется совпадение или, наоборот, их отсутствие:
AnyStr Like "[a-ж]очка"
AnyStr Like "[!а-ж]очка"
Первое выражение имеет значение True, если в строке AnyStr записано “дочка” или “бочка”, но не “кочка”. Второе выражение, наоборот, имеет значение True, если в строке AnyStr записано “кочка” или “мочка”, но “дочка” и “бочка” дадут значение False.
Когда вы указываете диапазон символов, в квадратных скобках должны стоять сначала меньший символ, потом больший. Например, выражение [a-к] правильное, а [к-а] — нет. VBA игнорирует пустую пару квадратных скобок.
Поскольку символы ♦,[,?,* имеют специальное значение, их нужно заключать в квадратные скобки, если вы хотите сделать их частью строки образца. Например, для того чтобы проверить, заканчивается ли строка вопросительным знаком, вы должны составить выражение следующим образом.
AnyStr Like "*[?]"
Правая квадратная скобка и восклицательный знак тоже имеет специальное значение, но для того, чтобы вставить их в строку образца, вы должны поместить их вне квадратных скобок. Например, восклицательный знак можно найти по следующему образцу.
128
Неделя 1
AnyStr Like "*!"
Для поиска символа дефиса его следует поместить в начале или в конце строки образца, поскольку, будучи помещенным в середине, он будет означать диапазон символов.
Результат сравнения оператором Like зависит от установки директивы Option Compare. Если способ сравнения установлен как бинарный, оператор Like различает строчные и прописные буквы. При текстовом способе сравнения оператор Like не чувствителен к регистру символов.
^ПРИМЕЧАНИЕ
Если вам нужно просто проверить, является ли одна строка частью другой, не обязательно применять оператор Like. Для такой проверки существует функция Instr. Эта функция описана в 5-м уроке.
Оператор Is
И наконец, еще один оператор сравнения — оператор Is. Его можно применять только для сравнения выражений типа Object. Выражения с оператором Is всегда имеют тип Boolean.
Выражение с оператором Is имеют значение True, если оба операнда указывают на один и тот же объект, и значение False — в противном случае. Оператор Is необходим в языке Visual Basic, поскольку все остальные операторы сравнения в применении к объектам типа Object не имеют смысла. Объектные выражения имеют значения адресов, указывающих на конкретные объекты в памяти. На уроке 7-го дня объекты будут рассмотрены подробно.
Логические операторы
Обычно логические операторы применяются для того, чтобы, комбинируя условные выражения, создавать сложные условия для принятия решений в процедуре, или для формирования условий выполнения некоторой группы инструкций.
В качестве операнда в логическом операторе может использоваться любое значение типа Boolean или число, которое можно преобразовать к этому типу. VBA считает, что 0 соответствует значению False, а любое другое число — значению True.
Как правило, логический оператор имеет значение типа Boolean, хотя иногда он может приобретать значение Null. Это случается, если хотя бы один из его операндов имеет значение Null. Поскольку специальному значению Empty соответствует число О, VBA заменяет в логических выражениях значение Empty на False.
В табл. 4.4 приведены логические операторы языке VBA с описанием синтаксиса и объяснением работы.
Таблица 4.4. Логические операторы
Оператор	Синтаксис	Объяснение
And	El And E2	Конъюнкция. Выражение истинно, если истинны оба его операнда
Or	El Or E2	Дизъюнкция. Выражение истинно, если истинен хотя бы один его операнд
Not	Not El	Отрицание. Выражение истинно, если Е1 ложно, и наоборот
День 4-й. Операторы и выражения
129
Окончание табл. 4.4
Оператор	Синтаксис	Объяснение
Хог	El Хог Е2	Исключающая дизъюнкция. Выражение истинно, если истинен только один его операнд
Eqv	El Eqv Е2	Эквивалентность. Выражение истинно, если значения Е1 и Е2 совпадают
Imp	El Imp E2	Импликация. Выражение ложно, если Е1 — истинно, а Е2 — ложно
Логические операторы And, Not и Or вполне аналогичны одноименным функциям, встроенным в Excel. Однако в Excel нет функций, аналогичных VBA-операторам Eqv, Imp и Хог.
^ПРИМЕЧАНИЕ
Таблицы истинности
В следующих разделах мы рассмотрим каждый из логических операторов подробнее. Однако перед тем, как перейти к рассмотрению операторов, следует дать определение таблицы истинности. Таблицей истинности называется таблица, в которой приведены все возможные комбинации значений операндов, а также значения самого выражения.
Обычно таблица истинности состоит из трех столбцов — в первом приводится значение первого операнда, во втором — значение второго, а в третьем — значение самого выражения. Каждая строка таблицы содержит определенную комбинацию значений операндов. Посмотрите на эту строку из таблицы истинности оператора And.
False	True	False
В этой строке значение первого операнда — False, значение второго — True, и значение всего выражения при таких значениях операндов — False. Такая строка в таблице истинности говорит о том, что значение выражения False And True есть False.
Оператор And
Оператор And выполняет логическую конъюнкцию. Значение этого оператора будет True тогда и только тогда, когда оба его операнда имеют значение True.
Синтаксис оператора True выглядит следующим образом.
Операнд! And Операнд2
Операнд! и 0перанд2 — это любые логические выражения языка VBA. (Логическое выражение — это такое выражение, которое имеет тип Boolean, а значение — True или False.) В следующей таблице истинности приведены значения оператора And.
Операнд1	Операнд2	Значение выражения
True	True	True
True	False	False
False	True	False
False	False	False
/30
Неделя 1
Оператор And используется для того, чтобы проверить, являются ли оба условия истиной одновременно. Например, рассмотрим оператор And в следующем выражении.
(Gross_Sales < 50000) And (Net_Profit < 10000)
Это выражение имеет значение True, если значение переменной Gross_Sales меньше 50000, а значение переменной Net_Profit меньше 10000.
Обратите внимание на то, что оба операнда оператора And — это выражения сравнения, взятые в круглые скобки.
Скобки говорят о том, что выражения в них должны быть вычислены перед тем, как вычислять значение оператора And. Кроме того, скобки помогают человеку читать текст программы, группируя логически связанные величины. Скобки очень часто применяются в выражениях всех типов для того, чтобы группировать отдельные части в подвыражения.
Оператор Ог
Оператор Or выполняет операцию дизъюнкции, которую иногда называют включающее или. Значение этого оператора будет True тогда и только тогда, когда хотя бы один его операнд будет иметь значение True.
Синтаксис оператора Or имеет следующий вид.
Операнд! Or Операнд2
Операнд! и Операнд2 — это любые логические выражения языка VBA. В следующей таблице истинности приведены значения оператора Or.
Операнд1	Операнд2	Значение выражения
True	True	True
True	False	True
False	True	True
False	False	False
Оператор Or используется для того, чтобы проверить, является ли хотя бы одно из условий истиной. Например, рассмотрим оператор Or в следующем выражении.
(Gross_Sales < 50000) Or (Net_Profit < 10000)
Это выражение имеет значение True, если значение переменной Gross_Sales меньше 50000 или значение переменной Net_Profit меньше 10000.
Оператор Not
Оператор Not называется логическим отрицанием. Он инвертирует любое значение, к которому применяется. Его значением будет True, если значение операнда — False, и наоборот.
Синтаксис оператора Not имеет следующий вид.
Not Операнд!
Операнд! — это любое логическое выражение языка VBA. В следующей таблице истинности приведены значения оператора Not.
День 4-Й. Операторы и выражения
131
Операнд1	Значение выражения
True	False
False	True
Оператор Хог
Оператор Хог выполняет операцию, которую называют исключающее или. Значение этого оператора будет True тогда и только тогда, когда любой, но только один его операнд имеет значение True.
Синтаксис оператора Хог имеет следующий вид.
Операнд! Хог 0перанд2
Операнд! и 0перанд2 — это любые логические выражения языка VBA. В следующей таблице истинности приведены значения оператора Хог.
Операнд1	Операнд2	Значение выражения
True	True	False
True	False	True
False	True	True
False	False	False
Оператор Eqv
Оператор Eqv выполняет проверку эквивалентности, т.е. его значение будет True, если оба его операнда имеют одно и то же значение.
Синтаксис оператора Eqv имеет следующий вид.
Операнд! Eqv 0перанд2
Операнд! и 0перанд2 — это любые логические выражения языка VBA. В следующей таблице истинности приведены значения оператора Eqv.
Операнд1	Операнд2	Значение выражения
True	True	True
True	False	False
False	True	False
False	False	True
Если значение одного из операндов — Null, значение выражения также будет Null.
Оператор Imp
Оператор Imp выполняет операцию импликации.
Синтаксис оператора Imp имеет следующий вид.
Операнд! Imp 0перанд2
/32
Неделя 1
Операнд! и Операнд2 — это любые логические выражения языка VBA. В следующей таблице истинности приведены значения оператора Imp.
Операнд1	Операнд2	Значение выражения
True	True	True
True	False	False
False	True	True
False	False	True
Если Р и Q — переменные логического типа, следующие выражения эквивалентны.
Р Imp Q
Not (Р And (Not Q))
Эти выражения эквивалентны, потому что правило импликации говорит, что если выполняется первое условие, то должно выполняться и второе. Операция импликации истинна, если первое условие ложно, но она ложна, если первое условие истинно, а второе — ложно. Эта операция не так интуитивно понятна, как предыдущие операции, но, к счастью, ею не очень часто приходится пользоваться.
Операторы конкатенации строк
В языке VBA можно сливать строки, образуя строки большей длины. Операция слияния строк называется конкатенацией. Эта операция весьма полезна и, вообще-то, очень проста в использовании, но все же несколько слов о ней нужно сказать.
Использование конкатенации строк
Самое обычное использование операции конкатенации — это слияние строк, полученных из разных источников для формирования сообщения на экране. В листинге 4.1. вы видите процедуру, которая спрашивает у пользователя имя файла рабочей книги, открывает эту книгу и затем выбирает в ней определенный лист (в данном случае это лист под названием Отчет).
Лисшипа 4.1. Коикашенация строк
1:	Sub Open2DataEntry()
2:	' Пример конкатенации строк
3:
4:	Const BoxTitle = "Ввод данных"
5:	Const ShtName = "Sales Report"
6:
7:	Dim FName As String
8:
9:	'получаем от пользователя нмя книги
10:	FName = InputBox("Укажите имя кннгн:",
11:	BoxTitle)
12:
13:	'открываем книгу
14:	Workbooks.Open FileName:=Fkame
15:
16:	'выбираем лист
День 4-й. Операторы и выражения
133
17:	ActiveWorkbook.Sheets(ShtName).Select
18:
19:	'сообщаем результаты
20:	MsgBox "Книга 11 & FName & " открыта, " & ShtName S
21:	" выбран.", ,
22:	BoxTitle & 11 закончен"
23:	End Sub
В первой строке сделано объявление процедуры. В 4-й строке объявлена константа, которая будет использоваться для заголовка различных окон этой процедуры. В 5-й строке объявлена константа, содержащая имя листа, который должен быть выбран. В 7-й строке объявлена переменная, в которой будет храниться имя файла, введенное пользователем. Строки 10 и 11 — это одна инструкция, в которой вызывается функция InputBox для получения имени файла от пользователя процедуры. В 14-й строке открывается книга с именем, которое пользователь ввел в строке 10. В 17-й строке выбирается лист с именем Отчет.
(Инструкция в 14-й строке была скопирована из записанного макроса; непоименованная константа из записанного макроса была заменена переменной FName для того, чтобы можно было использовать другое имя в зависимости от ввода пользователя. Точно так же была создана инструкция в строке 17.)
Обратите внимание на то, как в строках 20-22 используется конкатенация строк для получения строки сообщения и нового заголовка окна. (Строки 20-22 представляют собой одну инструкцию — вы видите символ продолжения строки в конце строк 20-21 .)
Строковое выражение на месте первого аргумента функции MsgBox собрано из непоименованной константы, переменной Fname и константы ShtName, которые вместе образовали текст сообщения, передаваемого функции MsgBox. В строке 22 с помощью конкатенации образована строка, которая передается функции MsgBox в качестве второго аргумента, т.е. заголовка окна: константа BoxTitle соединяется с непоименованной текстовой константой.
Если вы запустите процедуру и в ответ на предложение программы введете имя файла VBA_Samples.xls, функция MsgBox в строках 20-22 выведет окно сообщения, которое вы видите на рис. 4.1. Предполагается, что у вас в текущем каталоге есть файл с таким именем, иначе вы получите сообщение об ошибке выполнения.
Ввод данных закончен
Книге VBA_Samplejds открыта. Sales Report выбран.
ОК
Рис. 4.1. Функция MsgBox в строках 20-22 выводит такое окно сообщения
Операторы конкатенации строк
В языке VBA есть два оператора для конкатенации строк. Сейчас мы рассмотрим их по отдельности и обсудим их достоинства и недостатки.
134
Неделя 1
Основной оператор конкатенации: “В”
Знак амперсанда имеет единственное применение в языке VBA — для слияния строк. Никаких других функций этот знак не выполняет. Во всех примерах этой книги мы используем именно этот знак; он считается основным, так как используется чаще в силу того, что не возникает сомнения в его назначении. Синтаксис оператора конкатенации имеет следующий вид.
Операнд! & 0перанд2
Операнд! и 0перанд2 — это любые выражения строкового или числового типа, VBA преобразует числа в строки перед тем, как выполнять слияние. Тип результата всегда String. Если один из операндов имеет значение Null или Empty, VBA трактует его как строку нулевой длины, т.е. строку, не содержащую символов. В листинге 4.1. вы могли видеть несколько примеров использования оператора конкатенации.
ПРИМЕЧАНИЕ
Если вы не отделите знак амперсанда от левого операнда хотя бы одним пробелом, VBA воспримет это как попытку описать переменную типом Long, а не как оператор конкатенации. Результаты такой ошибки могут быть разными - в одних случаях УВД попытается преобразовать переменную к типу Long, в других - выдаст сообщение об ошибке. Если вы явно описали все переменные, как и рекомендуется, вы получите сообщение об ошибке.
Дополнительный оператор конкатенации строк: “+”
Для конкатенации строк можно использовать также символ Синтаксис и действие этого знака почти ничем не отличаются от рассмотренного ранее знака амперсанда. Использование такого знака для конкатенации — дань традиции; в ранних версиях языка Visual Basic использовался только этот знак. Сейчас он оставлен для совместимости с предыдущими версиями. Однако главное назначение этого знака — это все же арифметическое сложение. Когда VBA встречает выражение, содержащее знак “+”, он сначала пытается выполнить сложение операндов. Конкатенация строк выполняется в том случае, если один из операндов — строка, которую нельзя преобразовать в число, или если оба операнда — текстовые строки.
Так надо
Всегда используйте для конкатенации строк оператор &. Таким образом вы избежите неоднозначности и облегчите чтение программ.
Так не надо
Не пользуйтесь для слияния строк оператором +. Этим вы можете ввести в заблуждение и VBA, и человека, читающего ваши программы.
Старшинство операторов и вычисление сложных выражений
Сложным мы называем выражение, составленное из нескольких выражений. Как в естественном человеческом языке сложные фразы составляются из простых, так и в языке VBA сложные выражения составляются из нескольких простых.
День 4-й. Операторы и выражения
135
Многие выражения, которые вы встречаете в программах, являются сложными, особенно если это выражения, служащие для принятия решений или для циклических вычислений; то же самое можно сказать о выражениях, входящих в математические формулы.
Для того чтобы понять, как VBA вычисляет сложные выражения, давайте рассмотрим представленную на языке VBA формулу, описывающую объем сферы (где Radius — это радиус сферы, a Pi — поименованная константа):
(4/3) * Pi * Radius Л 3
Здесь вы видите четыре оператора и пять числовых величин.
Поскольку каждый оператор должен иметь по два операнда, может показаться странным, как VBA вычисляет выражение, содержащее нечетное число операндов. Ответ прост: VBA группирует выражение по операторам, вычисляет значение каждого оператора и подставляет это значение в выражение. Вам наверняка знаком этот процесс из школьного курса математики — там это называлось упростить выражение.
Для того чтобы решить, какой из операторов должен быть вычислен раньше, существуют стандартные правила старшинства операторов, или старшинства операций. VBA всегда первым вычисляет выражение, взятое в скобки. В приведенном выше примере сначала вычисляется выражение 4/3, поскольку оно заключено в скобки. После выполнения деления VBA подставляет полученное значение в выражение. Полученное промежуточное выражение, приведенное ниже, имеет три оператора и четыре числовые величины.
1.333333 * Pi * Radius Л 3
Вслед за этим VBA выполняет возведение в степень — вычисляет Radius Л 3. Объясняется это тем, что операция возведения в степень имеет наивысший приоритет старшинства среди оставшихся операций. Значение, полученное после возведения величины Radius в третью степень, VBA подставляет в выражение. Если предположить, что значение Radius равно 3, то значение выражения Radius А 3 равно 8 (2 в третьей степени). Промежуточное выражение, приведенное ниже, имеет два оператора и три числовых значения:
1.333333 * Pi * 8
И наконец, VBA выполняет две операции умножения. Операторы умножения в этом выражении, как нетрудно догадаться, имеют одинаковый приоритет старшинства. Скобок в выражении больше нет, поэтому в такой ситуации VBA просто вычисляет выражение слева направо.
Первым выполняется умножение 1.333333 * Pi. Если значение Pi равно 3.14, результат этого выражения равен 4.186665. Промежуточное выражение, приведенное ниже, имеет один оператор и два операнда.
4.186665 * 8
Осталась только одна операция. VBA выполняет последнее умножение и получает результат — 33.493324.
Этот анализ способа вычисления выражений иллюстрирует правила, которыми руководствуется VBA при вычислении выражений, или, иначе говоря, порядок вычислений. Эти правила можно сформулировать следующим образом.
• Первыми вычисляются части выражений, взятые в скобки. Если выражение в скобках, в свою очередь, является сложным, к нему применяются те же самые правила.
• Вычисления выполняются в порядке старшинства операций. Старшинство операций приведено в табл. 4.5.
• Операции с одинаковым старшинством выполняются слева направо.
/36
Неделя 1
Так надо
Для изменения стандартного порядка вычисления применяются скобки - выражение в скобках вычисляется
Используйте скобки, когда вы не уверены в том, в каком порядке VBA будет вычислять выражение или если порядок вычисления не очевиден.
Помните, что применение скобок в сложных выражениях делает их понятнее для человека.
В общем, порядок, в котором VBA вычисляет выражения, таков.
1.	Арифметические операции.
2.	Конкатенации строк.
3.	Операторы сравнения.
4.	Логические операторы.
В табл. 4.5 приведен полный список операций в порядке старшинства от большего к меньшему. Операции, расположенные на одной строке, имеют равное старшинство. Операции с равным старшинством выполняются слева на право в том порядке, в каком они следуют в выражении.
Таблица 4.5. Список операций
Операция	Пояснения
*	Возведение в степень
-	Унарный минус
*,/ \ Mod	Умножение и деление имеют равное старшинство. Вычисляются слева направо
+, -	Сложение и вычитание имеют равное старшинство. Вычисляются слева на право
&	Конкатенация строк выполняется после всех арифметических операций и перед логическими операциями
<, <=, >, >=, =, <>, Is, Like	Все операции сравнения имеют равное старшинство. Вычисляются слева направо, для группирования применяются скобки
Not And Or Хог Eqv Imp
День 4-й. Операторы и выражения
137
Резюме
На этом уроке вы узнали, что такое выражения, и познакомились с операторами — арифметическими, строковыми, операторами сравнения и логическими операторами.
Кроме того, вы получили объяснения, почему так важно знать тип выражения и как VBA определяет тип результата выражения. Мы рассмотрели понятие совместимости типов данных, выяснили, как совместимость может повлиять на результат выражения и как VBA может иногда автоматически преобразовывать данные из одного типа к другому.
Наконец, вы узнали, как VBA вычисляет сложные выражения и в каком порядке выполняются операции в сложном выражении.
Вопросы о ответы
Нужно ли мне помнить все типы данных и правила их преобразования, которые VBA использует при вычислении выражений?
Нет, запоминать их дословно нет необходимости, но вы должны понимать главные концепции. Это поможет вам писать программы без досадных ошибок. Кроме того, понимая, как VBA определяет тип результата выражения, вы можете контролировать точность арифметических вычислений. Вам придется время от времени обращаться к материалу этого урока при исправлении ошибок, сообщения о которых вы иногда будете получать
У меня нет ясного понимания, как использовать логические операторы и операторы сравнения.
Логические операторы и операторы сравнения используются в выражениях не так, как арифметические операторы. Арифметические операторы вы обычно применяете для того, чтобы присвоить значение переменной или в качестве аргумента функции. Логические же операторы и операторы сравнения используются чаше для проверки истинности того или иного условия при принятии решения или при выполнении циклических вычислений. Принятие решений и циклические вычисления описываются в главах 8 и 9.
Каким из описанных операторов конкатенации я должен пользоваться?
Вы всегда должны пользоваться основным оператором конкатенации (&). В языке VBA есть дополнительный оператор конкатенации +, который сохранен для совместимости со старыми версиями языка Basic
Коллоквиум
В этом разделе мы предлагаем вам контрольные вопросы викторины и упражнения, которые помогут вам закрепить новые знания. Ответы на вопросы вы найдете в приложении.
Тест
1.	Что такое выражение? Что такое результат выражения?
2.	Сколько разных величин может встретиться в одном выражении? Как эти величины связаны друг с другом?
138
Неделя 1
3.	Можно ли отдельно взятую величину, такую как поименованная константа или переменная, считать выражением?
4.	Как можно воспользоваться результатом выражения? Должны ли вы использовать результат выражения?
5.	Для каких целей используется знак равенства как оператор?
6.	В чем состоит разница между операторами \ и /?
7.	Если переменные StrVal и NumVal имеют тип Variant и в переменной NumVal записано значение 17, а в переменной StrVal записано "23", укажите результат каждого из следующих выражений.
NumVal < 5
NumVal + StrVal
NumVal & StrVal
NumVal - (5 + (6 * 2))
StrVal & "банана"
Упражнения
Расставьте скобки в приведенных ниже выражениях так, чтобы их результат соответствовал указанному.
3*5-7	-6
4.	7 + 26 / 10	3.07
312 / 47 + 16 - 2	5.114754
17-5-44/2 Л	2	100
Напишите собственноручно процедуру с названием ThreeWords, в которой будут использованы три разные инструкции InputBox, каждая для ввода пользователем одного слова. Слова должны быть соединены в одну строку (с пробелами между словами), и эта строка выведена на экран функцией MsgBox. В этой процедуре объявите поименованную константу "Введите: ". Эта константа должна быть использована для того, чтобы заголовок окна менялся при вводе каждого слова, а именно при вводе первого слова "Введите: Первое слово", при вводе второго — "Введите: Второе слово" и т.д.
День 4-й. Операторы и выражения
139

Функции Visual Basic и Excel
Вам уже приходилось пользоваться одной из встроенных функций VBA — функцией InputBox. Теперь, когда вы достаточно хорошо знакомы с выражениями, вы научитесь включать в них функции. Сегодня мы рассмотрим следующие вопросы.
•	Что такое функция и как использовать функции в выражениях.
•	Как с помощью окна Object Browser (Просмотр объектов) определить список доступных функций.
•	Как с помощью окна Object Browser вставить функцию со списком аргументов в текст программы и сэкономить время, потраченное на ввод.
•	Как использовать функции VBA для преобразования данных из одного вида в другой или для управления строками.
Понятие о функциях
Функция — это встроенная формула, которая оперирует выражениями и формирует значения. Функция всегда возвращает значение, которое VBA вставляет в ту точку программы, где встречается имя функции. В зависимости от типа операции или вычисления, которые выполняют функции, их можно разделить на несколько групп. Функции служат для вычисления значений выражения и, в частности, для выполнения следующих задач.
•	Преобразование текстовых строк в данные другого типа.
•	Получение информации о текстовых строках.
•	Преобразование других типов данных в текстовые строки.
•	Форматирование чисел или других типов данных для вывода на экран.
•	Выполнение тригонометрических, логарифмических, статистических, финансовых и других вычислений.
•	Получение информации о файлах, дисководах, или о среде, в которой в данный момент выполняется VBA.
В этой главе рассматривается различные категории функций, а также подробно описываются наиболее важные функции VBA и их использование. На следующих за-
/40
Неделя 1
нятиях мы поговорим и о других функциях, о которых упоминается в этой главе. Также, по мере освоения следующих уроков, вы научитесь создавать свои собственные функции и использовать их в процедурах VBA.
В ходе предыдущих уроков использовались примеры как процедур, так и функций. Не путайте между собой термины функция и процедура. В общем случае процедура выполняет определенную задачу (или группу задач) так же, как выполняет определенную задачу конкретная команда в меню Excel. Функция же обычно обрабатывает одну или несколько величин и в результате возвращает определенное значение так же, как и формула в ячейке рабочего листа Excel.
Использование функций в выражениях
Для использования функции нужно просто ввести ее имя в инструкции VBA (вместе с необходимыми аргументами функции) в той точке, где вы хотите использовать результат функции. (Ввод имени функции в инструкции УВАдля ее дальнейшего использования называется вызовом функции.) В следующих строках показаны примеры типичного использования функций:
Tomorrow=Now+l
Anystr=CStr(AnyValue)
В первом примере функция Now извлекает значение даты и времени из системных часов компьютера и возвращает эту информацию как значение типа Date. При выполнении этой инструкции VBA вставляет значение даты, возвращенное функцией Now, в ту точку процедуры, где находится ключевое слово этой функции. Затем VBA вычисляет это выражение, прибавляя единицу к значению даты, возвращенной функцией Now, и присваивая этот результат переменной Tomorrow. Если сегодня 8 августа 1999 г., то после выполнения этой инструкции значение даты, сохраняемое в переменной Tomorrow, станет 9 августа 1999 г.
Вторая инструкция использует функцию CStr, которая преобразует аргумент в тип данных String и затем возвращает эту строку. Когда VBA выполняет эту инструкцию, он передает функции CStr значение, сохраняемое в переменной AnyValue. Функция CStr преобразует это значение в строку и возвращает эту строку в качестве результата функции. Например, если переменная AnyValue содержит число 12, функция CStr вернет строку ”12". Затем VBA вставляет в выражение строку, возвращенную функцией CStr, в ту точку, где находится ключевое слово CStr. В данном примере результат функции — это единственное значение в выражении, и VBA просто присваивает строковое значение, возвращенное CStr, переменной Anystr.
Следующая инструкция представляет собой другой пример типичного использования функций:
MsgBox TypeName(AnyVar)
Эта инструкция использует функцию TypeName, которая возвращает строку, содержащую название типа данных ее аргумента. При выполнении этой инструкции VBA сначала вызывает функцию TypeName, передавая ей некоторое значение, хранимое в переменной AnyVar. Функция TypeName определяет, какой тип данных был ей передан, и возвращает строку, содержащую название типа данных. Затем VBA вставляет в инструкцию строку, возвращенную TypeName, в ту точку, где находится имя функции TypeName. Поскольку название функции TypeName находится в инструкции как аргумент процедуры MsgBox, VBA передает результат функции TypeName как аргумент процедуре MsgBox. Если AnyVar является переменной типа Variant, функция TypeName возвратит строку "Variant", и процедура MsgBox выведет на экран слово Variant.
День 5-Й. Функции Visual Basic и Excel
141
Рассмотренные три примера иллюстрируют следующие важные свойства функций.
•	Результат функции можно использовать в выражении.
•	Результат функции можно присвоить переменной.
•	Результат функции можно использовать в качестве аргумента другой процедуры или функции.
•	Список аргументов функций заключается в круглые скобки.
Функцию можно использовать для получения значения в любой инструкции VBA, где допускается использование значений констант или переменных. В листинге 5.1 показана завершенная процедура, демонстрирующая использование функций в выражениях.
Листинг 5.1. Исиользовамие функций
1:	Sub FuncDemo()
2:	Dim vDate
3:	Dim tDate As Date
4:	vDate = CStr(Now)
5:	tDate = Time
6:	MsgBox "Дата: " & vDate
7:	MsgBox "Время: " & tDate
8:	MsgBox "Переменная vDate имеет тип: " & TypeName(vDate)
9:	MsgBox "Переменная tDate имеет тип: " & TypeName(tDate)
10:	End Sub
Строка 1 содержит объявление процедуры FuncDemo. Строки 2 и 3 объявляют переменные, используемые в FuncDemo. Строка 2 объявляет переменную vDate; эта переменная имеет тип Variant, потому что при объявлении переменной тип данных не указан. Строка 3 объявляет переменную tDate; эта переменная имеет тип Date.
Строка 4 — первая инструкция, с которой начинает выполняться процедура FuncDemo. При выполнении строки 4 сначала вызывается функция Now, которая возвращает текущее значение даты и времени системных часов компьютера как значение типа Date. Затем результат функции Now используется в инструкции. Ввиду того что Now является в инструкции аргументом функции CStr, VBA передает функции CStr результат функции Now. CStr возвращает строку, соответствующую переданному ей аргументу. В данном примере, аргументом является значение типа Date (результат функции Now), и функция CStr возвращает строку, содержащую текущую дату и время. Если сегодня 6 февраля 1999 г. и время 1:05 р.т. и 5 секунд, функция CStr в строке 4 вернет строку 2/6/99 1 :05: 05 РМ. И в завершение выполнения инструкции строки 4 результат функции CStr присваивается переменной vDate.
При выполнении инструкции в строке 5 сначала вызывается функция Time, которая возвращает значение времени из системных часов вашего компьютера как значение типа Date. Если текущее время 3:54 р.т., функция Time в строке 5 вернет значение 3:54:00 РМ как тип данных Date. Затем значение Date, возвращенное функцией Time, присваивается переменной tDate.
В строке 6 с помощью функции MsgBox на экран выводится непоименованная строковая константа и присоединенное к ней значение, которое хранится в переменной vDate. Обратите внимание на использование знака (&) оператора конкатенации. При выполнении этой инструкции прежде всего непоименованная текстовая константа объединяется со значением, содержащимся в переменной vDate. (Помните, в строке
142
Неделя 1
4 текущее значение даты и времени хранилось в переменной vDate. Это значение было присвоено vDate в результате преобразования в строку функцией CStr данных, который были возвращены функцией Now.) Затем строка, полученная в результате объединения, передается как аргумент процедуре MsgBox. Если сегодня 6 февраля 1999 г. и время 1:05 р.т. и 5 секунд, оператор MsgBox в строке 6 выведет на экран сообщение, показанное на рис. 5.1.
В строке 7 также встречается функция MsgBox. Когда VBA выполняет эту инструкцию, сначала вычисляется выражение, которое потом передается в качестве аргумента в функцию MsgBox. В данном случае строковое выражение, состоящее из букв, объединяется со значением типа Date. Поскольку выражение содержит оператор конкатенации (&), VBA правильно предполагает, что цель этого выражения — вывести значение типа String. Следовательно, VBA автоматически преобразует значение переменной tDate в строку и затем выполняет операцию конкатенации. Далее VBA передает строку, полученную в результате слияния, как аргумент функции MsgBox. Если текущее время 3:55, процедура MsgBox в строке 7 выведет на экран окно сообщения, похожее на то, которое показано на рис. 5.1, но содержащее текст Время: 3:55:00.
В строках 8 и 9 также вызывается функция MsgBox. В данном случае каждая строка выводит сообщение, показывающее тип данных переменной. Когда VBA выполняет строку 8, сначала вызывается функция TypeName. Аргументом для функции TypeName служит переменная vDate. TypeName анализирует содержимое переменной vDate и возвращает строку, которая содержит название типа данных информации, хранящейся в vDate. Переменная vDate содержит строковые данные, и функция TypeName возвращает строку, содержащую слово String. Затем VBA вставляет строку, возвращенную функцией TypeName, в выражение, которое формирует аргумент для процедуры MsgBox. Теперь VBA завершает вычисление выражения, соединяя буквенную строку со строкой, возвращенной функцией TypeName. Затем процедура MsgBox в строке 8 выводит окно сообщения, показанное на рис. 5.2.
Рис. 5.2. Инструкция MsgBox в строке 8 листинга 5.1 выводит это сообщение, отображающее тип данных, хранящихся в переменной vDate
Рис. 5.1. Оператор MsgBox в строке 6 листинга 5.1 выводит это сообщение с текущим значением даты и времени (в соответствии с системными часами вашего компьютера)
Строка 9 выполняется подобным образом. В связи с тем что vDate — это переменная типа Date, функция TypeName возвращает строку, содержащую слово Date. Поэтому инструкция MsgBox в строке 9 выводит сообщение Переменная tDate имеет тип: Date.
Так надо
Запомните, что функция TypeName сообщает о типе данных, хранящихся в переменной, а не о типе самой переменной. Строка 8 листинга 5.1 формируется на основе справедливого предложения о том, что данные, хранящиеся в переменной, - строковые; функция TypeName не сможет определить, является ли тип переменной строковым (в данном случае - это тип Variant, содержащий строку).
День 5-й. Функции Visual Basic и Excel
143
Так не надо
Не забывайте заключать список аргументов функции в круглые скобки (см. раздел 'Игнорирование результата функции' далее в этой главе).
Понятие об аргументах функции и о результате функции
Как вы уже видели ранее, например, в листинге 5.1, для некоторых функций необходимы один или несколько аргументов, а для некоторых нет необходимости в аргументах. Функции, которые не требуют аргументов, обычно только получают значение, получить которое другим способом нельзя. В качестве примера можно привести функцию Time, которая возвращает текущее значение времени из часов вашего компьютера и не требует никакого аргумента. Для использования функции, которая не требует никаких аргументов, просто введите имя функции в текст программы, как показано в следующем примере.
TimeNow=Time
Для выполнения других функций необходимо задать одно или несколько значений. Например, функция Sqr возвращает квадратный корень числа; для того чтобы вычислить квадратный корень, вы должны задать число для функции Sqr, как показано в следующей инструкции.
Root=Sqr(AnyNum)
Вы можете задать эти значения через список аргументов функции. Поместите список аргументов в круглые скобки и, если количество аргументов более одного, отделите каждый аргумент в списке запятой, как рассказывалось на уроке 2-го дня обучения при рассмотрении процедуры MsgBox.
Тип данных значения, возвращенного функцией, зависит от особенностей функции. Большинство функций возвращает значения типа Variant, хотя другие функции возвращают определенные типы данных, такие как String, Double и Integer. VBA часто автоматически преобразует результат функции в тот тип данных, который совместим с другими значениями в выражении, содержащемся в функции. Таким же образом VBA преобразует типы данных в различных операциях присваивания и вычисляемых выражениях. Все правила совместимости типа данных, которые вы изучили на 4-м уроке и которые применяются для типа данных Variant, переменных и констант, также применимы для значений, возвращаемых функциями.
Игнприрпвание результата функции
Обычно вы должны каким-либо образом использовать значение, возвращаемое функцией, — либо включив результат функции в выражение, либо в качестве списка аргументов, либо присвоив результат переменной. Однако в некоторых случаях, возможно, вам понадобится проигнорировать результат, возвращенный функцией.
На уроке 2-го дня обучения отмечалось, что процедура VBA MsgBox имеет второй необязательный аргумент, который позволяет вам определить количество управляющих кнопок в диалоговом окне. Вы можете использовать аргумент, задающий кнопки в процедуре MsgBox, для вывода сообщения, которое задает пользователю вопрос и позволяет ему ответить на него посредством выбора управляющей кнопки в окне сообщения.
144
Неделя 1
Если для MsgBox используется этот необязательный аргумент управляющих кнопок, должна существовать возможность получения из MsgBox значения, которое укажет, какую кнопку выбрал пользователь. В действительности, процедура MsgBox является функцией; однако чаще всего вы используете функцию MsgBox, игнорируя ее результат.
До этого момента во всех примерах и листингах процедур, приведенных в этой книге, аргумент управляющих кнопок при вызове функции MsgBox был опущен. Когда вы опускаете аргумент управляющих кнопок, MsgBox выводит диалоговое окно, которое содержит только одну кнопку. Поскольку диалоговое окно функции MsgBox до сих пор содержало только одну кнопку, не имело значения, каким был результат функции MsgBox. Поэтому ранее, все инструкции, содержащие вызов MsgBox, пренебрегали результатом, вызывая функцию MsgBox приблизительно следующим образом (вызов состоял только из имени функции MsgBox и последующего списка аргументов):
MsgBox AnyText, ,AnyTitle
Для создания кнопок в диалоговом окне функции MsgBox в VBA предусматривается несколько встроенных констант (в уроке дня 8 эти константы описываются более детально). Одна из этих констант, VbYesNo, указывает MsgBox, что в выводимом функцией диалоговом окне должны присутствовать кнопки Да и Нет. Следующая инструкция предполагает вывод диалогового окна, показанного на рис. 5.3:
MsgBox "Видите две кнопки?", vbYesNo, "Демонстрация кнопок"
Демонстрация кнопок
Видите две кнопки?
|j Да  j| Нет|
Рис. 5.3. Диалоговое окно функции MsgBox при использовании в функции аргумента кнопок
При использовании этой инструкции возникает только одна проблема: результат выбора пользователя не возвращается никоим образом; эта инструкция все равно пренебрегает результатом функции MsgBox.
Для получения результата функции MsgBox нужно сделать два изменения в этой инструкции: заключить список аргументов в круглые скобки и видоизменить инструкцию таким образом, чтобы результат функции использовался каким-либо образом. Обычно результат функции MsgBox можно присвоить переменной и позднее проверить его в другой инструкции, чтобы определить, какую кнопку вашей процедуры действительно выбрал пользователь. Следующая строка показывает, что вызов функции MsgBox изменен таким образом, что функция возвращает значение (обратите внимание на символ продолжения строки):
Response = MsgBox ("Видите две кнопки?", vbYesNo, "Демонстрация кнопок ")
При выполнении этой инструкции выводится то же сообщение, что показано на рис. 5.3. Когда пользователь выбирает кнопку Да или Нет, VBA закрывает диалоговое окно и сохраняет число, соответствующее выбранной кнопке, в переменной Response.
Заметьте, что в первой инструкции MsgBox, в котором мы пренебрегаем результатом функции, список аргументов в круглые скобки не заключается в отличие от второй инструкции MsgBox, которая использует результат функции MsgBox.
День 5-й. Функции Visual Basic и Excel
145
Эти две инструкции MsgBox иллюстрируют важное свойство функции: опуская скобки вокруг списка аргументов, вы тем самым даете VBA команду проигнорировать результат функции. Если список аргументов не заключен в скобки, VBA трактует вызов функции как вызов процедуры и не возвращает результат функции.
Если вновь посмотреть на примеры функции, приведенные выше в этой главе, и на функции, используемые в листинге 5.1, можно заметить, что все инструкции, которые вызывают функцию VBA, заключают список аргументов функции в круглые скобки, даже в том случае, когда список аргументов содержит только один аргумент. При вызове функций, которые не имеют аргументов, круглые скобки не используются.
Результатом каждой функции пренебрегать нельзя, да в этом и нет необходимости. Игнорирование результата функции может быть полезным только при использовании тех функций, которые выполняют некоторую часть задачи, обрабатывающую возвращаемое значение (как, например, функция MsgBox). Вы игнорируете результат функции в том случае, когда хотите, чтобы функция просто выполнила свою задачу и когда вас не интересует результат этой задачи. Например, функция MsgBox выполняет задачу по выводу на экран сообщения в диалоговом окне, как часть задачи получения ответа от пользователя. Функцию MsgBox также полезно использовать, если нужно просто вывести сообщение; в этом случае используется функция для вывода сообщения, но игнорируется результат. (На 8-м занятии будет объяснено, как использовать функцию MsgBox, чтобы позволить пользователю вашей программы сделать выбор, который повлияет на ее выполнение).
Если вы пытаетесь проигнорировать результат функции, которая не имеет аргументов (как, например, функция Now), или любой другой функции, результат которой не может быть проигнорирован, то, в зависимости от особенностей функции, VBA выведет сообщение об одной из нескольких возможных ошибках во время выполнения программы. Чаще всего вы в этой ситуации на экране появляется предостережение об ошибке несоответствия типа или синтаксической ошибке. Как правило, VBA не позволит проигнорировать результат функции, если ее имя является ключевым словом VBA (как, например, CStr и другие функции преобразования) и функций, чья единственная задача — обеспечить некоторое возвращенное значение, как, например, в математических функциях.
Использование поименованных аргументов функции
На 2-м уроке вы узнали, что список аргументов процедуры нужно располагать в определенном порядке. Также в определенном порядке вы должны располагать список аргументов функции. В функциях, которые используют более чем один аргумент, от положения значений аргументов в списке зависит соответствие аргумента определенному значению. Например, вы уже знаете, что для функции MsgBox первым аргументом является сообщение для вывода, вторым — количество и тип кнопок для диалогового окна, а третьим — заголовок окна.
Несмотря на то что второй аргумент функции MsgBox, который описывает управляющие кнопки для диалогового окна, является необязательными до этого момента опускался почти во всех примерах и упражнениях, в списке для второго аргумента вы должны включать запятые. Как вы убедились на уроке 2-го дня обучения, отсутствие запятых в описании второго аргумента приводит к ошибке несоответствия типа.
Несмотря на помощь, которую предлагает свойство Auto Quick Info редактора VB, по невнимательности можно пропустить запятые или переставить значение аргумента в функциях, которые имеют необязательные аргументы или несколько аргументов. При отсутствии запятых или неверной расстановке аргументов в списке аргументов функции может появиться сообщение об ошибке несоответствия типа или, что еще хуже, вовсе не появиться сообщение об ошибке. Иногда в результате перестановки
146	Неделя 1
значений (как, например, координат строки и столбца) во время выполнения в списке аргументов функции не появится никакого сообщения об ошибке; ваша программа просто выдаст неправильный ответ и вы не сможете определить почему.
Для того чтобы предотвратить ошибки при программировании и облегчить использование функций, которые имеют необязательные аргументы, в VBA существует альтернативная возможность для указания значений в списке аргументов. Можно передать функции значения, используя поименованные аргументы. Ниже приведено два примера вызова функции MsgBox, которые возвращают одинаковый результат: первая инструкция использует стандартный метод перечня аргументов, а вторая — метод поименованных аргументов (обе инструкции игнорируют результат функции MsgBox).
MsgBox AnyText, , AnyTitle
MsgBox Prompt:=AnyMsg, Title:=AnyTitle
Вторая инструкция использует поименованные аргументы и для вывода на экран сообщения (или приглашения), которое выводит функция MsgBox, и для аргумента Title (название), который определяет заголовок диалогового окна, присваивая значения каждому поименованному аргументу. Имя аргумента для заголовка функции MsgBox — Title; а выражение Title:=AnyTitle присваивает содержимое переменной AnyTitle аргументу, который VBA передает функции MsgBox для использования в качестве названия диалогового окна.
Далее Prompt является названием аргумента, отвечающего за текст приглашения функции MsgBox, а выражение Prompt:=AnyMsg присваивает содержимое переменной AnyMsg аргументу, который VBA передает функции MsgBox для использования в качестве текста, выводимого на экран в диалоговом окне сообщения.
ПРИМЕЧАНИЕ
Символ, который присваивает значение поименованному аргументу (:=), не является эквивалентом обычного оператора присваивания (=). Если опустить двоеточие (:) при присваивании значения поименованному аргументу, VBA может не выдать на экран сообщения о синтаксической ошибке, но не сможет правильно интерпретировать инструкцию. При выполнении инструкции VBA выдаст сообщение об одной из нескольких возможных ошибок — как правило, об ошибке несоответствия типа.
При использовании поименованных аргументов не обязательно включать запятые для необязательных аргументов. Обратите внимание, что во втором примере запятые между аргументом приглашения и аргументом названия отсутствуют, в то время как в первом примере они имеются. В действительности, поименованные аргументы не обязательно должны использоваться в каком-то особом порядке. Во втором примере аргумент Title можно указать перед аргументам Prompt:
MsgBox:=AnyTitle, prompt=AnyMsg
Функция MsgBox все равно использует в качестве названия диалогового окна значение, присвоенное аргументу Title. При использовании поименованных аргументов VBA использует имя аргумента для определения значения, которое соответствует этому аргументу.
Функция InputBox, как и все функции VBA, также имеет поименованные аргументы. В следующей инструкции показан аргумент функции InputBox, который использует поименованные аргументы:
User_Input=InputBox(Prompt:=AnyText, Title:=AnyTitle)
Обратите внимание, что список аргументов этой инструкции заключен в круглые скобки. Независимо от того, применяются поименованные аргументы при вызове
День 5-й. Функции У/sua/ Basic и Excel
147
функции или нет, если вы используете результат функции, то список аргументов всегда должен заключаться в круглые скобки.
Так надо
Запомните, что VBA использует имя аргумента для определения значения, соответствующего этому аргументу.
Заключайте список аргументов функции в круглые скобки, независимо от того, используете ли вы результат функции или нет.
ПРИМЕЧАНИЕ
Не смешивайте поименованные аргументы и стандартный список аргументов в одном и том же вызове функции. Для каждого отдельного вызова функции нужно использовать либо поименованные аргументы, либо стандартный список аргументов, хотя не обязательно использовать один и тот же метод во всей программе.
Для того чтобы определить имена аргументов функции, можно воспользоваться средством Auto Quick Info, высвечиваемым в окне подсказки. Оно появляется при вводе имени функции. (Информация об Auto Quick Info приведена в материале 2-го урока.) Для того чтобы вставить имя функции и полный список аргументов в текст программы, можно использовать окно Object Browser, как описано в разделе “Использование окна Object Browser для вызова функции” в этой главе. Для получения более подробной информации о назначении любой функции и ее аргументов воспользуйтесь справочной системой VBA.
Использование функций VBA
Встроенные функции VBA по назначению можно разделить на несколько категорий (например, математические функции, функции преобразования данных, функции взаимодействия, строковые функции и функции, предоставляющие информацию о диске). В следующих разделах будет рассмотрена каждая категория функций и приведены таблицы с перечнем функций и их назначением.
К сожалению, детально рассмотреть каждую функцию VBA в этой книге невозможно. Однако, к счастью, большинство функций — например, математические функции — говорят сами за себя и не требуют детального рассмотрения. Другие функции, например, некоторые функции преобразования типа данных и обработки строк, будут рассмотрены более подробно. Функции по обработке строк очень важны, и их использованию будет посвящен отдельный раздел.
Для получения более подробной информации об отдельных функциях введите имя функции, подсветите его и затем нажмите F1. VBA выведет тематический раздел помощи по этой функции.
Математические функции
VBA содержит стандартный набор математических функций. Ранее уже описывались примеры использования этих функций в выражениях и в качестве аргументов других функций. В табл. 5.1 приведен перечень математических функций, которые используются в VBA. число N означает любое допустимое числовое выражение; а все аргументы функции являются обязательными, если не указано другое.
148
Неделя 1
Таблица 5.1. Математические функции VBA
Функция (аргументы)	Возвращает/выполняет
Abs(N) Atn(N) Cos(N) Exp(N)	Возвращает абсолютное значение N Возвращает арктангенс N как угол, измеренный в радианах Косинус угла N, где N — угол, измеренный в радианах Возвращает число е, возведенное в степень N (е — это основание натурального логарифма, приблизительно равное 2.718282)
Fix(N)	Возвращает целую часть числа N. Функция Fix не округляет число, она отсекает любую дробную часть. Если число N отрицательное, функция Fix возвращает ближайшее отрицательное целое число, большее или равное N
Int(N)	Возвращает целую часть числа N. Функция Int не округляет число, она отсекает любую дробную часть. Если число N отрицательное, функция Int возвращает ближайшее отрицательное целое число, меньшее или равное N
Log(N) Rnd(N)	Возвращает натуральный логарифм N Возвращает случайное число; аргумент является необязательным. Функция Rnd используется для получения случайного фактора в программах, которые воспроизводят некоторые реально происходящие события, как, например, моделирование фондовой биржи. Используйте функцию Rnd только после инициализации генератора случайных чисел VBA с помощью инструкции Randomize
Round(N,P)	Возвращает результат округления числа N, до числа десятичных знаков, определяемых аргументом Р. Если вы опускаете аргумент Р, функция Round возвращает результат округления числа N до целого числа
Sgn(N)	Возвращает знак числа: -1, если число N отрицательное, 1, если N — положительное, 0, если N равно 0
Sin(N) Sqr(N)	Возвращает синус угла; N — угол, измеренный в радианах Возвращает квадратный корень числа N. Если число N — отрицательное, VBA выведет сообщение об ошибке во время выполнения. (По математическому определению, отрицательные числа не имеют квадратного корня).
Tan(N)	Возвращает тангенс угла; N — угол, измеренный в радианах
Так надо
Не забывайте, что функции Fix и Int возвращают целую часть, они отбрасывают дробную часть числа без округления. Единственное различие между этими функциями состоит в способе обработки отрицательных чисел. Если хотите округлить число до ближайшего целого, используйте функцию CInt (она описывается в следующем разделе этого урока).
Для округления числа, содержащего определенное количество знаков после запятой, используйте функцию Round, например для округления числа 2.534 до 2.53.
День 5-й. Функции Visual Basic и Excel
149
Не забывайте, что при необходимости вы можете получить дополнительные тригонометрические функции из основных математических функций VBA. Например, если вам необходимо вычислить котангенс угла, вы можете использовать формулу 1/Гап(х). Для получения перечня производных стандартных математических функций и их формул воспользуйтесь справочной системой. Как вы узнаете на последующих уроках (в частности, на 6-м уроке), вы сможете написать свои собственные функции.
Функции иреибразивания уанных
Visual Basic содержит несколько функций для преобразования данных одного типа в другой. Эти функции можно использовать для того, чтобы избежать ошибок несоответствия типа и для обеспечения явного контроля над типами данных в выражениях.
Например, если в отдельном выражении вы получили сообщение об ошибке несоответствия типа, то с помощью одной из функций преобразования можно преобразовать ошибочные значения в значения тех типов данных, которые соответствуют друг другу. Другой пример: вы хотите сохранить результат выражения как числовой тип Single (результат чаще всего сохраняется как тип Double); в этом случае следует использовать функцию CSng, как показано в следующем примере:
AnySingle=Csng(412/14)
В табл. 5.2 приведены функции преобразования данных. Число N — любое числовое выражение, S — любое строковое выражение, и Е — выражение любого типа. Каждый аргумент функции является обязательным, если не указано другое.
Функция (аргументы)	Возвращает/выполняет
Asc(S)	Возвращает значение, соответствующее коду первого символа строки S. Например, букве А соответствует код 65
Chr(N)	Возвращает строку, содержащую символ с кодом N. N должно быть числом от 0 до 255 включительно. Например, коду 65 соответствует буква А
Format(E. S)	Возвращает строку, содержащую значение, представленное аргументом Е, отформатированную согласно инструкциям, содержащимся в аргументе S. Вы можете использовать функцию Format для преобразования чисел, например 1000 в строку $1, 000.00
Hex(N)	Возвращает строку, содержащую шестнадцатеричное представление аргумента N
Oct(N)	Возвращает строку, содержащую восьмеричное представление аргумента N.
RGB(N,N,N)	Возвращает целое типа Long, представляющее значение цвета RGB. Значение N в каждом аргументе должно быть целым числом между 0 и 255 включительно. Слева направо аргументы соответствуют значениям красного, зеленого
150
Неделя 1
Функция (аргументы)	Возвращает/выполняет
Val(S)	Возвращает числовое значение, соответствующее числу, представленному строкой S. Аргумент S должен содержать только цифры и десятичную точку, иначе VBA не сможет преобразовать число. Если VBA не может преобразовать число, функция Vai возвращает 0
CBool(N) CByte(E)	Возвращает булев эквивалент числового выражения N Возвращает числовое значение типа Byte (от 0 до 255); аргумент Е может быть любым допустимым числовым или строковым выражением, которое может быть преобразовано в число
CCur (E)	Возвращает числовое значение типа Currency; аргумент Е может быть любым допустимым числовым или строковым выражением, которое может быть преобразовано в число
CDate(E)	Возвращает значение типа Date. Аргумент Е может быть любым допустимым выражением (строка или число), которое представляет дату в пределах от 1/1/100 до 12/31/9999 включительно
CDbl(E)	Возвращает числовое значение типа Double; аргумент Е может быть любым допустимым числовым или строковым выражением, которое может быть преобразовано в число
Clnt(E)	Возвращает числовое значение типа Integer; аргумент Е может быть любым допустимым числовым или строковым выражением, которое может быть преобразовано в число
CLng(E)	Возвращает числовое значение типа Long; аргумент Е может быть любым допустимым числовым или строковым выражением, которое может быть преобразовано в число
CSng(E)	Возвращает числовое значение типа Single; аргумент Е может быть любым допустимым числовым или строковым выражением, которое может быть преобразовано в число
CStr(E)	Возвращает числовое значение типа String; аргумент Е может быть любым допустимым числовым или строковым выражением.
CVar(E)	Возвращает числовое значение типа Variant; аргумент Е может быть любым допустимым числовым или строковым выражением.
Наиболее часто используемые функции преобразования данных (сгруппированы в конце табл. 5.2) — это те, которые начинаются с буквы С, что означает преобразование (convert — преобразование), за ней следует аббревиатура имени типа: CStr, CSng, CDbl и т.д.
^ПРИМЕЧАНИЕ
Использование переменных явного типа и функций преобразования типов требует от вас как программиста, быть внимательным при преобразовании данных из одного типа в другой. Как вы уже знаете из урока 4-го дня нашего курса, нельзя полностью полагаться на правила автоматического преобразования типов, потому что некоторые операторы, -например, знак (+) в зависимости от типа данных в выражении, ведут себя по-разному. Использование переменных с указанным типом и функций преобразования типа данных поможет вам избежать или заметить коварные ошибки, возникающие в результате преобразования типа данных, которые вы не сможете предвидеть.
День 5-Й. Функции Visual Basic и Excel
151
Функции уашы и времени
Для получения текущего значения даты или времени, разделения значения даты на составляющие части, или для преобразования строк или чисел в значения типа Date можно использовать функции даты и времени.
В табл. 5.3 приведены функции даты и времени и описаны их действия. N — любое допустимое числовое выражение, и D — любое допустимое выражение даты (включая значения типа Date, числа или строки, которые VBA может преобразовать в строки). Все аргументы функции являются обязательными, если не указано другое.
Функция (аргументы)	Возвращает/выполняет
Date	Возвращает текущее значение даты из системных часов вашего компьютера. (Вы также можете использовать эту функцию в качестве процедуры для установки значения системных часов своего компьютера. За подробностями обращайтесь к справочной системе VBA)
Time	Возвращает текущее значение времени из системных часов вашего компьютера как значение типа Date. (Вы также можете использовать эту функцию в качестве процедуры для установки значения системных часов компьютера. За подробностями обращайтесь к диалоговой помощи VBA)
Now	Возвращает текущее значение даты и времени из системных часов вашего компьютера
Year(D)	Возвращает целое число, содержащее значение года. Аргумент — выражение типа Date. Год представлен числом между 100 и 9999
Month(D)	Возвращает целое число, содержащее значение месяца. Аргумент — выражение типа Date. Месяц представлен числом между 1 и 12 включительно
Day(D)	Возвращает целое число, содержащее значение дня. Аргумент — выражение типа Date. День представлен числом между 1 и 31 включительно
Weekday (D)	Возвращает целое число, содержащее значение дня недели. День недели представлен числом между 1 и 7 включительно; 1 — это воскресенье, 2 — это понедельник и т.д.
Hour(D)	Возвращает целое число, содержащее значение часа. Аргумент — выражение типа Date. Час представлен числом между 0 и 23 включительно. Если выражение типа Date не содержит значение времени, функция Hour возвращает 0
Minute(D)	Возвращает целое число, содержащее значение минут. Аргумент — выражение типа Date. Минуты представлены числом между 0 и 59 включительно. Если аргумент не содержит значения времени, функция Minute возвращает 0
Second (D)	Возвращает целое число, содержащее значение секунд. Секунды возвращаются как число между 0 и 59 включительно. Если аргумент не содержит значение времени, функция Second возвращает 0
152
Неделя 1
Функция (аргументы)	Возвращает/выполняет
DateSerial (N,N,N)	Возвращает значение абсолютной даты. Слева направо аргументы соответствуют году, месяцу, числу. Значение года должно быть целым числом между 100 и 9999, месяц должен быть между 1 и 12, и день должен быть между 1 и 31 (все значения включительно)
TimeSerial (N,N,N)	Возвращает значение абсолютного времени. Слева направо аргументы соответствуют часам, минутам и секундам. Аргумент часа должен быть целым числом между 0 и 23, минуты и секунды должны быть между 0 и 59 (все значения включительно)
DateValue(E)	Возвращает значение типа Date, соответствующее дате, определенной аргументом Е, который должен быть любой строкой, числом или константой, представляющей дату
TimeValue(E)	Возвращает значение типа Date, соответствующее времени, определенному аргументом Е, который должен быть любой строкой, числом или константой, представляющей время
Timer	Возвращает число, равное числу секунд после полуночи, в соответствии с системными часами вашего компьютера
Функции взаимодействия с иользователем
Вы уже знаете, как пользоваться функциями для обмена данными с пользователем процедур: InputBox и MsgBox. Это единственные функции взаимодействия с пользователем в VBA, хотя класс Interaction в окне Object Browser, в дополнение к функциям InputBox и MsgBox, содержит перечень нескольких функций и процедур. Другие функции взаимодействия с пользователем позволяют взаимодействовать с операционной системой Windows или с другими приложениями. Например, можно использовать другие функции и процедуры для получения информации или дополнения реестра Windows, для пересылки последовательности нажатия клавиш в другое приложение, или для использования автоматических настроек для управления объектами другого приложения. Все это — довольно непростые задачи программирования. Подробнее об управлении другими приложениями с применением функций взаимодействия, вы узнаете в главе 20. Этот урок посвящен только функциям взаимодействия с пользователем — InputBox и MsgBox.
Функция InputBox
И функция InputBox, и функция MsgBox имеют несколько необязательных аргументов, которые еще не описывались. В общем случае синтаксис для функции InputBox выглядит следующим образом.
InputBox(Prompt[,Title, Default, XPos, YPos, HelpFile, Context])
где Prompt — любое строковое выражение. Аргумент Prompt является единственным обязательным аргументом функции InputBox, все остальные аргументы являются необязательными. Именно квадратные скобки в списке аргументов синтаксического примера показывают, что остальные аргументы являются необязательными.
Вы уже знакомы с аргументами Prompt и Title; первый является строкой, используемой для того, чтобы показать пользователю, какую именно информацию вы хотите
День 5-й. Функции Visual Basic и Excel
153
от него получить; второй аргумент — строка, используемая как заголовок диалогового окна для ввода информации.
Аргумент Default — также строковое выражение; он используется для того, чтобы предложить пользователю значение по умолчанию. Например, следующая инструкция, приглашает пользователя ввести имя файла и предлагает по умолчанию имя NEWFILE. Диалоговое окно, иллюстрирующее эту инструкцию и предлагающее значение по умолчанию, показано на рис. 5.4.
User_Input=InputBox("Укажите имя файла:
"Создание файла", "NEWFILE")
Рис. 5.4. Необязательный аргумент функции InputBox предлагает пользователю значение по умолчанию
Как вы уже, наверное, заметили, диалоговое окно InputBox появляется в центре экрана. Возможно, вам захочется, чтобы оно появлялось в другом месте экрана, особенно, если у вас открыты другие окна. Последовательное расположение диалоговых окон помогает пользователю программы понять, где в данное время выполняется процедура.
Аргументы XPos и YPos могут быть любыми числовыми выражениями. Они позволяют определить, где в активном окне появится окно функции InputBox. XPos и YPos показывают координаты верхнего левого угла диалогового окна. XPos — горизонтальное расстояние от левого края окна; YPos — вертикальное расстояние от вершины окна. Оба расстояния измеряются в твипах: один твип равен 1/20 точки (точка — единица измерения типа печати). Поскольку точка составляет 1/72 дюйма, твип приблизительно равен 0.0007 дюйма.
__________ Будьте осторожны при определении положения окна InputBox. Можно определить ZnDcnvnorwnniic такие положения для аргументов XPos и Ypos, что диалоговое окно вовсе не появит-
Ч* ** ся на экране, потому что его координаты будут находиться за правым или нижним краем окна. Хотя окна не видно, оно является активным и ни один из элементов управления не будет действовать, пока вы не ответите на “невидимый диалог”.
И наконец, два последних необязательных аргумента для функции InputBox — это аргументы HelpFile и Context. HelpFile — строковое выражение, которое содержит имя файла помощи в операционной системе Windows. Обычно это файл помощи, который вы создаете, используя компилятор Windows Help File. Аргумент Context — это числовое выражение, которое определяет тематический раздел в файле помощи. Число соответствует выводимому на экран диалоговому окну, например 0 — это, как правило, таблица содержания файла помощи.
Если вы определяете аргументы HelpFile или Context, вы должны определить оба аргумента. При определении файла помощи для диалогового окна ввода VBA автоматически добавляет в окно управляющую кнопку Help. (Компилятор Windows Help не входит в состав VBA; если вы хотите создать свой собственный файл помощи, приоб
154
Неделя 1
ретите компилятор Windows Help отдельно у компании Microsoft или используйте вспомогательный инструментарий независимого разработчика, например программное обеспечение RoboHelp от Blue Sky или Doc-to-Help от WexTech.)
Для использования поименованных аргументов в функции InputBox нужно просто включить имена аргументов из приведенного выше синтаксического примера: Prompt, Title, Default, XPos, YPos, HelpFile и Context. Следующая инструкция обеспечивает вывод на экран того же диалогового окна, что и инструкция на рис. 5.4, но использует для этого поименованные аргументы:
User_Input=InputBox(prompt:="yxajtHTe имя файла:
Title:="Создание файла", Default:="NEWFILE")
Функция MsgBox
Полный список аргументов функции MsgBox похож на список аргументов функции InputBox:
MsgBox(Prompt[, , Buttons, Title, HelpFile, Context])
Единственным обязательным аргументом для функции MsgBox является аргумент Prompt, который может быть любым строковым выражением; все остальные аргументы являются необязательными. Аргументы Title, HelpFile и Context в функции MsgBox соответствуют тем же назначениям и требованиям, что и их прототипы в функции InputBox. Обратите внимание, что для позиционирования диалогового окна в функции MsgBox аргументы не используются; ее диалоговое окно всегда появляется у центра окна. Заметьте также, что в функция MsgBox имеется аргумент Buttons вместо аргумента Default.
В функции MsgBox аргумент Buttons является числовым выражением, которое определяет, какое количество и какой тип кнопок появится в диалоговом окне MsgBox. Аргумент Buttons также определяет кнопку, активную по умолчанию, и наличие в окне стандартных значков вроде знака информации, восклицательного или вопросительного знаков (Critical, Information, Exclamation или Question) для предупреждений и обращений к пользователю. Следующая инструкция, например, выводит на экран диалоговое окно, показанное на рис. 5.5.
User_Input=MsgBox("Выберите кнопку", vbYesNo, "Проверка кнопок")
Эта инструкция использует одну из встроенных констант vbYesNo, которые предлагаются в VBA для использования с функцией MsgBox. Более подробно использование этих констант и значения, возвращаемого функцией MsgBox, описывается в ходе изложения 8-го урока.
I Проверка кнопок
Рис. 5.5. Аргументы функции MsgBox, определяющие количество и тип кнопок, которые появятся в диалоговом окне
День 5-Й. Функции Visual Basic и Excel
155
Так надо
Используйте поименованные аргументы для функций InputBox и MsgBox для тога, чтобы упростить их использование, а также облегчить чтение и понимание инструкций VBA.
Используйте символ продолжения строки, чтобы расположить каждый поименованный аргумент на отдельной строке, когда ваши инструкции становятся довольно длинными.
User_Input=MsgBox(PromptВыберите кнопку "
Buttons:=vbYesNo,_
Title:=”Проверка кнопок")
Эта инструкция упрощает чтение и понимание; и далее в книге многие примеры используют подобное форматирование, что делает их более понятными.
Строковые функции
Функции VBA можно использовать для нахождения строк в составе других строк, для сравнения строк, а также для копирования определенных частей строк. Функции для работы со строками используются очень часто, потому что строковые данные очень важны, их можно встретить в любом приложении VBA, будь то Excel или любая другая прикладная система. Управлять строковыми данными, полученными от пользователя, можно с помощью функции InputBox. Также данные этого типа могут появляться в исходном тексте программы VBA в качестве имен файлов рабочих листов Excel, документов Word, баз данных Access и некоторых других типов данных, которые хранятся на дисках.
Строковые данные также играют большую роль в Excel как названия рабочих листов, диапазон имен данных и т.д. В этом разделе рассматриваются строковые функции VBA; а в следующих разделах будет более детально рассмотрено, как использовать наиболее важные и полезные строковые функции.
В приведенной таблице аргумент N — любое числовое выражение, aS— любое строковое выражение. Каждый аргумент функции является обязательным, если не указано др \ гое.
Функция (аргументы) Возвращает/выполняет
Filter(Al, SI. Bl, N1) Возвращает массив строк, входящих в А1 (где А1 — строковый массив, к которому применяется фильтр), а В1 и N1 — необязательные аргументы. Если В1 равен True или опущен, возвращаемый массив содержит строки из А1, которые содержат в себе S1. Если В1 равен False, возвращаемый массив включает строки из А1, которые не содержат S1. Аргумент N1 определяет, как происходит поиск, с учетом регистра или без учета; если N1 опущен, функция Filter использует текущую установку Option Compare
lnStr(Nl, SI, S2, N2) Возвращает позицию S2 в SI. Аргумент N1 является начальной позицией для поиска; a N2 определяет выполнение операции поиска в зависимости от регистра. Аргументы N1 и N2 — необязательные. Если N2 опущен, поиск проводится по текущей установке Option Compare. Если аргумент N2 включен, необходимо также включить и аргумент N1
156
Неделя 1
Функция (аргументы)	Возвращает/выполняет
Join(Al, S1)	Возвращает строку, состоящую из объединенных строк массива А1. Аргумент S1 необязателен, он показывает разделительный символ, вставляемый между строками. Если аргумент S1 опущен, функция Join объединяет строки, используя символ пробела
LCase(S)	Возвращает строку, содержащую копию аргумента S, в которой все символы верхнего регистра преобразованы в символы нижнего регистра
Left(S, N)	Возвращает строку; копирует N символов из S, начиная с крайнего левого символа
Len(S)	Возвращает число, равное количеству символов строки S, включая символы пробела в начале и конце строки
LTrim(S)	Возвращает копию строки S после удаления всех символов пробела с левой стороны строки (с начала строки)
Mid(S, Nl, N2)	Возвращает строку; копирует N2 символов, начиная с позиции символа в строке S, который определен аргументом N1. Аргумент N2 — необязательный; если вы опустите N2, функция Mid возвратит все символы в строке S от позиции N1 до конца строки
Replace(Sl, S2, S3, Nl, N2, N3)	Возвращает копию строки S1, где каждый элемент S2 замещается S3. Аргументы Nl, N2 и N3 — все необязательные. N1 показывает, с какой точки в S1 должна начаться замена. N2 показывает, какое количество раз должна произойти замена. N3 определяет, производится ли операция поиска в зависимости от регистра; если аргумент N3 опушен, функция Replace использует текущую установку Option Compare
Right(N, S)	Возвращает строку; копирует N символов, начиная с крайнего правого, до символа, порядковый номер которого определен аргументом S. Например, Right)"программа", 6) возвращает строку "грамма"
RTrim(S)	Возвращает копию строки S после удаления всех символов пробела с правой стороны строки (символы в конце строки)
Space(N) Split(Sl, S2, Nl, N2)	Возвращает строку пробелов длиной в N символов Возвращает массив строк, созданный делением строки S1 на более мелкие строки. Необязательный аргумент S2 — одиночный символ, показывающий, где должна быть разделена строка S1; если аргумент S2 опущен, функция Split делит S1 по символу пробела. Аргументы N1 и N2 также являются необязательными. N1 определяет максимальное количество делений, a N2 определяет, зависит ли операция поиска от регистра
День 5-й. Функции Visual Basic и Excel
157
Функция (аргументы)	Возвращает/выполняет
StrComp(Sl, S2, N)	Сравнивает строки SI и S2 и возвращает число, показывающее результат сравнения; -1, если SKS2, 0, если S1=S2, и 1, если S1>S2. Аргумент N — необязательный; он показывает, зависит ли операция сравнения от регистра. Если аргумент N опущен, строки сравниваются с помощью текущей установки Option Compare
StrConv(S, N)	Возвращает строку, преобразованную в новый формат в зависимости от числового значения, определенного N. Для использования функции StrConv в VBA имеются встроенные константы; наиболее распространенными являются vbProperCase (преобразует строку таким образом, что каждая начальная буква слова становится прописной), vbLowerCase (преобразует строку в нижний регистр) и vbUpperCase (преобразует строку в верхний регистр)
String(N, S)	Возвращает строку длиной в N символов, совпадающих с первым символом в строке S. Например, String)3, "ёж") возвращает "ёёё"
StrReverse(Sl)	Возвращает строку, которая является копией строки S1 с обратным порядком символов. Например, StrReverse("Fred") возвращает "derf"
Trim(S)	Возвращает копию строки S после удаления символов пробела, как в начале, так и в конце строки
Ucase(S)	Возвращает строку S, где все символы нижнего регистра преобразуются в символы верхнего регистра
Некоторые из функций преобразования данных, перечисленные в табл. 5.2, также относятся и к функциям управления строками — в частности, функции Chr, Format и Cstr. Более подробная информация об использовании строковых данных в VBA приведена в разделах “Использование функций для управления строками” и “Форматирование значений данных” ниже в этой главе.
Функции для получения информации и диске, каталоге и другие
Время от времени для выполнения программ вам может понадобится информация о дисководе, месте нахождения определенного файла или может возникнуть необходимость создания списка файлов. Для получения информации о диске и каталоге, в VBA существует несколько различных функций. На уроке 12-го дня рассматриваются методы управления файлами и каталогами диска из программ VBA.
В VBA имеются и другие функции, о которых не упоминается в этой главе. Эти дополнительные функции позволяют общаться с другими приложениями, получать информацию об ошибках времени выполнения, о массивах, а также управлять различными объектами в Excel. Вы познакомитесь с этими функциями позже.
158
Неделя 1
Использование функций Excel
В дополнение к встроенным функциям Visual Basic for Applications, приложение Excel обладает широким разнообразием функций для выполнения математических, логических, финансовых и статистических операций над данными в рабочих листах. Многие из этих функций (но не все) доступны для использования в VBA.
НРИМЕЧАНИЕ
Другие приложения, поддерживающие VBA, как, например, Word 2000 или Access 2000, также позволяют использовать некоторые (или все) свои функции в VBA. В этой главе речь пойдет об использовании функций, касающихся всех VBA-приложений. (Запомните, VBA-приложение - это приложение, в котором вы выполняете свои процедуры VBA, например Excel 2000.)
Функции, которые Excel или любое другое приложение позволяет использовать в VBA, не являются частью VBA, они входят в состав этого приложения. Например, функции рабочих листов Excel являются не частью языка программирования VBA, а частью программы Excel. Не всякое приложение содержит функции, которые можно использовать в VBA. Функции, которые можно использовать в одном приложении, могут быть недоступными в другом. При написании процедур VBA для работы в любом приложении не следует использовать такие функции, потому что они могут быть недоступными в другом приложении. Например, при написании процедуры для работы в Word, Excel, Access или Microsoft Project, не пользуйтесь в программах VBA функциями системы Excel.
Для того чтобы использовать функции, которые принадлежат какому-либо приложению, нужно получить доступ к функции в среде VBA через объект Application. Объект Application представляет приложение и все его ресурсы. (Более подробно объекты будут рассматриваться на уроках 7-го дня; использование объектов объясняется на уроке 19-го дня.)
НРИМЕЧАНИЕ
Хотя многие математические функции Excel дублируют математические функции VBA, в Excel имеются и более специализированные функции для статистических и финансовых операций. При программировании операций в среде Excel вам, вероятно, придется использовать функции рабочих листов, которые являются частью системы Excel. На уроке 19-го дня обучения вы узнаете, что для применения функций рабочих листов Excel в приложениях VBA, которые отличаются от Excel, можно использовать свойство автоматизации (automation).
В качестве примера приводится инструкция, где используется функция Excel Мах. которая возвращает наибольшее число из списка аргументов.
MsgBox Application.Мах(4, 1, 3, 2) 'Выводит на экран 4
Обратите внимание, что в этой инструкции за словом Application следует точка (.). а затем, без пробелов, имя функции Мах. Точка показывает, что оператор относится к функции Мах, которая является частью объекта Application. При использовании функции какого-либо приложения в программе VBA необходимо включать ключевое слово Application и разделительную точку перед именем каждой такой функции. Например, при использовании любой функции рабочего листа Excel в инструкции VBA нужно включить ключевое слово Application и разделительную точку (.) перед именем каждой функции Excel.
День 5-й. Функции Visual Basic и Excel
159
ПРИМЕЧАНИЕ
Результат функции Excel игнорировать нельзя. При вызове функции Excel нужно обязательно использовать круглые скобки, а результат функции необходимо каким-либо образом использовать — либо в качестве значения другого выражения, аргумента другой функции или процедуры, либо в операторе присваивания.
Если вы давно работаете с рабочими листами в среде Excel, то, наверное, уже обратили внимание, что в Excel имеется множество функций, которые носят те же имена, что и некоторые функции, перечисленные в табл. 5.1-5.4.
Поскольку при использовании функции какого-либо приложения необходимо определить ключевое слово Application, ни у VBA, ни у пользователя не возникнет сомнений, к какой функции (VBA или Excel) относится оператор. В качестве примера приводится следующий фрагмент, где показаны две инструкции.
Rslt=Log(AnyNum)
Rslt=Application.Log(AnyNum)
Первая инструкция вызывает функцию VBA Log; вторая — функцию Excel Log. Для вызова функции VBA служит имя функции; для вызова функции главного приложения необходимо включить ключевое слово Application.
НРЕДУНРЕЖДЕНИЕ
Функции Excel (или функции других приложений), которые имеют те же имена, что и функции VBA, не обязательно выполняют те же задачи или возвращают те же результаты. Например, функция Excel LOG отличается от функции VBA Log, и они возвращают разные значения; функция Excel LN по своему действию схожа с функцией VBA Log. Перед тем как использовать функцию Excel вместо функции VBA, тщательно изучите действие и результат этой функции - в противном случае вы можете получить неверные результаты.
Не всякая функция Excel доступна к применению в среде VBA. Например, те функции Excel, которые дублируют функции VBA, являются недоступными, потому что в этом нет большого смысла. Так, функции Excel Date, Year, Month, Day, Hour, Minute и Second дублируют функции VBA с такими же именами, как в работе, так и в результатах. Ни одна из этих функций Excel не доступна в среде VBA. Случается, что некоторые из функций приложений недоступны в VBA, независимо от того, дублируют они функции VBA или нет.
Если вы не уверены, к какой из функций Excel вы можете обратиться в VBA, воспользуйтесь окном Object Browser (описывается в следующем разделе этой главы), чтобы просмотреть, включена ли функция в список компонентов, которые вам нужны. При этом нужно выбрать Application в списке Classes и Excel — в списке LI-brary/Project. Если функция, которая вам нужна, отсутствует в списке, значит, она недоступна в VBA.
Для того чтобы определить, какие из функций Excel (или другого приложения) доступны, как правильно их использовать и для чего они предназначены, воспользуйтесь справочной системой по ключевому слову function.
Использование окна Object Browser для вызова функции
Количество функций, которые могут быть доступны через VBA и Excel, весьма значительно: этот список насчитывает несколько сотен функций.
760
Неделя 1
Очевидно, трудно (если не сказать невозможно) запомнить назначение, правильный синтаксис и аргументы каждой функции. Большинство людей ограничиваются запоминанием имен и аргументов только тех функций, которые они наиболее часто используют, а затем полагаются на общее знание доступных функций, которые помогают определить нужную функцию для конкретной цели.
Для того чтобы найти доступные функции и определить список их аргументов, включая поименованные аргументы, можно воспользоваться окном Object Browser. Это окно является важным и полезным инструментом, который также позволяет вызвать диалоговую помощь по определенной функции и вставить “шаблон” для вызова функции в исходном тексте процедуры, включая все поименованные аргументы этой функции.
Просмотр о вставка функции Visual Basic
Для того чтобы включить вызов функции в текст программы, нужно раскрыть окно Object Browser, вы уже знаете, как это делается: выбрать из меню View-Object Browser или щелкнуть на кнопке Object Browser панели инструментов редактора VBA. Независимо от того, каким методом вы воспользуетесь для запуска окна Object Browser в VBA, вы увидите окно, показанное на рис. 5.6.
ПРИМЕЧАНИЕ
Окно Object Browser содержит информацию не только о функциях, но и обо всех процедурах, константах и доступных командах. Вы увидите не только функции, описанные в этой главе, но и множество дополнительной информации. В ходе следующих уроков вы познакомитесь с этим окном в деталях.
Members of 'Strings' w.
Space
Spate?	J
» Split	’ И
StrComp
Classes
Л Global
•St Information
•St Interaction KeyGodeConsta
•St Math (Strings.........r
«tt SystemCoiorCor VbAppWinStyle
,<&> VbCalendar VhCallTvne
* StrConv	js >
* String	' ; (
'* String?
Л StrReverse	Д
•*Tnm	i
о Trim?	ЛЗ
Function StrCompfS.'nngf, Stnng2, [Compere As
VbCompare Method - vtioinaryCarnpare])
Member of УВ4 SVlPSb
Puc. 5.6. Используйте окно Object Browser для того, чтобы определить, какие функции доступны, и для того, чтобы вставить вызов функции и все ее аргументы в текст программы
Вы уже знаете, как использовать окно Object Browser для того, чтобы просмотреть встроенные константы, которые предлагаются в VBA; таким же образом вы можете просмотреть доступные функции. Вот что для этого необходимо сделать.
1.	Выберите в верхнем поле списка Project/Library окна Object Browser VBA.
В списке Classes теперь показаны различные категории объектов, функций, процедур и констант, определенных VBA.
День 5-Й. Функции Visual Basic и Excel
161
2.	В списке Classes выберите категорию функции, которая вам нужна (Constants, Conversion, DateTime, FileSystem, Information, Math или Strings).
3.	Выберите определенную функцию, которую вы хотите использовать — или по которой хотите получить дополнительную информацию, — в списке компонентов. На рис. 5.6 показана функция StrComp.
Обратите внимание, что имя функции и список аргументов на рис. 5.6 появляются в нижней части окна Object Browser. Каждое имя, которое появляется в списке аргументов, используется в качестве поименованного аргумента функции. В окне Object Browser поле информации всегда используется для того, чтобы показать правильный синтаксис аргументов, в том числе и поименованных, для любой функции, которая находится в списке компонентов Members.
Также обратите внимание, что в поле информации в нижней части окна Object Browser отражен тип данных аргументов функции. Например, функция StrComp, показанная на рис. 5.6, имеет необязательный аргумент — Compare, — который является типом данных VbCompareMethod. (VbCompareMethod — стандартный тип данных; о стандартных типах данных вы узнаете в уроках дня 10). Если для аргумента функции не определен тип данных, значит, он относится к типу Variant.
Еще раз взгляните на рис. 5.6 и обратите внимание на кнопку со значком вопросительного знака в правом верхнем углу окна Object Browser. Если эта кнопка доступна, значит, по выбранной тематике в списке компонентов Members можно получить помощь в справочной системе. Щелкните на этой кнопке, чтобы посмотреть, какая помощь доступна по выбранной теме. В окне помощи по функциям VBA описываются любые ограничения по диапазону или типу данных аргументов функции, объясняется, какие аргументы являются необязательными, и рассказывается, как интерпретировать возвращаемые функцией значения.
СОВЕТ
Область в нижней части окна Object Browser - там, где появляется описание синтаксиса и список аргументов, - часто содержит “активные* слова, выделенные полужирным шрифтом, зеленым цветом или подчеркиваются (см. рис. 5.6 и 5.7). Щелчок на таких словах приводит к открытию в окне Object Browser соответствующей библиотеки или класса. Например, на рис. 5.6 тип данных (VbCompareMethod) выбранного необязательного аргумента Compare функции StrComp выделен полужирным шрифтом, зеленым цветом и подчеркнут. Щелчок на названии VbCompareMethod приведет к открытию в окне Object Browser встроенных констант класса VbCompareMethod; затем в списке компонентов VbCompareMethod будут показаны все встроенные константы, которые определены в модуле класса VbCompareMethod.
Для того чтобы поместить имя функции и полный список ее аргументов в текст программы, нужно сделать следующее.
1.	Установить курсор в модуле там, где вы хотите использовать результат функции.
2.	Открыть окно Object Browser и выбрать в списке <Members> функцию, которую вы хотите использовать в тексте исходной программы, как уже описывалось выше.
3.	Щелкнуть на кнопке Сору в окне Object Browser. Имя выбранной функции будет находиться в буфере обмена Windows.
4.	Закрыть окно Object Browser и нажать клавиши <Ctrl+V> для того, чтобы вставить имя функции в текст окна.
162
Неделя 1
Так надо
Помните, что вы можете вставить полный список аргументов функции в модуль. Для этого нужно выделить текст в нижней части окна Object Browser, затем щелкнуть на кнопке Сору на панели окна Object Browser (выбранный текст будет скопирован в буфер обмена Windows), а затем вставить его в свой модуль
Не забудьте добавить в функцию выражения присваивания, переменные или другие элементы для того, чтобы вставленная функция и список ее аргументов стал синтаксически правильной инструкцией.
Просмотр о использование функций Excel
Для того чтобы использовать окно Object Browser с целью просмотра функций, которые прикладная система VBA делает доступными, или для того, чтобы вставить функции прикладной системы в программу, нужно выполнить следующее.
1.	Открыть окно Object Browser.
2.	В списке Project/Library в верхней части окна Object Browser выбрать Excel.
В списке Classes теперь показаны различные категории объектов, функций, процедур, констант и других программных объектов, определенных в среде Excel, как показано на рис. 5.7.
3.	В списке Classes выбрать категорию функции, которая вам нужна. Например, для просмотра функций, доступных в рабочем листе, нужно выбрать компонент WorksheetFunction в списке Classes, как показано на рис. 5.7.
4.	Выбрать в списке Members of <Class> определенную функцию, которую вы хотите использовать или по которой хотите получить дополнительную информацию. На рис. 5.7 показана выбранная функция Sum.
ПРИМЕЧАНИЕ
Поскольку вы можете вызвать из VBA функции Excel только с помощью объекта Applicatoin - хранилища всех ресурсов Excel - для того, чтобы вывести на экран все функции Excel, вам следует выбрать библиотеку Excel в списке Project/Library (см. рис. 5.7). Тогда в окне Object Browser появятся все функции, команды и другие ресурсы Excel, которые могут быть доступны через объект Applicatoin.
В общем случае, объект Applicatoin - хранилище любых ресурсов прикладной системы VBA. Вы можете использовать окно Object Browser для того, чтобы просмотреть функции и объекты, доступные в любой прикладной системе VBA; для этого нужно лишь выбрать определенную библиотеку приложений в списке Project/Library окна Object Browser. Затем в окне появится список всех функций, команд и других ресурсов, которые становятся доступными в главном приложении посредством объекта Applicatoin.
День 5-й. Функции Visual Basic и Excel
163
Object Browser
"I Excel
Classes
® Walls i® WebOptions [Ш Window ® Windows itt Workbook ;® Workbooks :® Worksheet ’®iWorksheetFunction Д Worksheets
Members of WorksheetFunction'
» Substitute
* Subtotal	й
Sumlf
л SumProduct
* SumSq
SumX2MY2
« SumX2PY2
«3umXMY2
if1
 Function Sum(Argf, [ДгаЗ], [Arg3], [A/g-f], [Arg5], [Arg6],  [Arp?]. [Arg8], [Arg9], [Argf 0], [Argf f], [ArgfZJ, [Arpf3J, [Arg14j, [ArgfS], [Argf 6], [Anjf7], [Arpf3|. [ArgfF], [Arp20j.
Puc. 5.7. Вы также можете использовать окно Object Browser для того, чтобы просмотреть и вставить функции из Excel
Так надо
Запомните, что каждая библиотека главного приложения становится доступной только в том случае, если вы откроете окно редактора VBA из этого главного приложения. Например, вы можете пролистать библиотеку Excel в окне Object Browser только тогда, когда откроете окно редактора VBA из Excel. На 12-м занятии вы узнаете, как создавать дополнительные библиотеки, относящиеся к проектам VBA.
Запомните, что вы можете обратиться к функциям главного приложения только в том случае, когда перед именем функции будет находиться ключевое слово Applicatoin и разделительная точка (.).
Всякий раз, когда вы используете окно Object Browser для того, чтобы вставить функцию главного приложения в программу, не забывайте использовать ключевое слово Applicatoin и разделительную точку (.).
Использование функций для управления строками
Управление строковыми данными является важной составляющей многих программ, особенно тех, что взаимодействуют с пользователем. Для интерактивных программ управление строковыми данными имеет большое значение по двум причинам: во-первых, для формулирования сообщений, которые нужно вывести на экран пользователя, и, во-вторых, потому, что данные, вводимые пользователем (посредством функции InputBox) обрабатываются как строковые данные.
Чем эффективнее используется управление строковыми данными, тем более привлекательными, логически последовательными становятся сообщения, которые предназначены для пользователя и генерируются программой. К тому же правильное управление строковыми данными позволит правильно анализировать строки, которые вводит пользователь программы.
Для управления строковыми данными в VBA существует множество различных функций. Строковые функции были приведены выше в табл. 5.4 в этой главе. В этом и следующем разделах будет описано, как использовать наиболее важные строковые функции и некоторые функции преобразования данных для выполнения как простых, так и сложных операций над строковыми данными в программах.
164
Неделя 1
Удаление лишиих символов нробвлвв
Время от времени строковые данные в программе могут содержать лишние символы пробела в начале или в конце строки. Эти так называемые начальные и замыкающие символы пробела возникают по нескольким причинам.
Одна из наиболее общих причин наличия символов пробела — результат получения данных, введенных пользователем, при использовании функции InputBox. Эта функция возвращает весь текст, введенный пользователем, включая все символы пробелов. Если пользователь вводит дополнительные символы пробелов до или после основного значения, функция InputBox возвращает эти дополнительные символы как часть значения, возвращаемого строкой.
Другой наиболее распространенной причиной появления начальных и замыкающих символов пробела является ситуация, при которой используется содержимое переменной фиксированной длины. Строки фиксированной длины всегда имеют одну и ту же длину, и VBA, при необходимости, заполняет данные, присваиваемые строковой переменной (обычно замыкающими символами пробела) до объявленной длины. В результате каждый раз при использовании строки фиксированной длины есть вероятность, что она будет заполнена замыкающими символами пробела.
Дополнительные начальные или замыкающие пробелы в строке могут быть причиной различных затруднений, в одном случае небольших, в другом — более серьезных. При выводе строки на экран дополнительные символы пробела могут образовывать в тексте большие промежутки, текст становится непривлекательным и сложным для чтения. В других случаях дополнительные начальные или замыкающие символы пробела могут повлиять на операцию сравнения строк, объявляемую длину строки и некоторые другие факторы. Это может привести к тому, что в результате значение строки не будет использоваться в других выражениях, а процедура может вернуть ошибочный результат. В любом случае последствия могут быть непредсказуемыми.
Для операции удаления из строки нежелательных дополнительных — начальных или замыкающих — символов пробела (т.е. ее очистки) в VBA существует три функции. Первая, RTrim, удаляет символы пробела с правой стороны строки (замыкающие символы пробела). Вторая функция, LTrim, удаляет символы пробела с левой стороны строки (начальные символы пробела). И наконец, третья функция, Trim, удаляет символы пробела как с левой, так и с правой стороны строки.
Эти функции “очистки” в действительности не изменяют строку: строка передается функции в качестве аргумента, а функция возвращает копию строки с удаленными дополнительными пробелами. В листинге 5.2 демонстрируется использование функций очистки строки.
Листинг 5.2. Демонстрация функции UTrlm, LTrim о Trim
1:	Sub TrimDemo()
2:	Dim ExSpace As String
3:	ExSpace = " Бешеный конь "
4:	MsgBox "{" & ExSpace &
5:	MsgBox	"{”	&	RTrim(ExSpace) &
6:	MsgBox	"{"	&	LTrim(ExSpace) &
7:	MsgBox	"{"	&	Trim(ExSpace) &
8:	End Sub
День 5-й. Функции Visual Basic и Excel
165
В строке 1 объявляется процедура TrimDemo, строка 2 объявляет строковую переменную ExSpace, а строка 3 присваивает этой переменной значение, в котором содержатся начальные и замыкающие символы пробела (по четыре символа слева и справа). В строке 4 функция MsgBox используется для вывода неизмененной строки Exspace. В строковом выражении для аргумента функции MsgBox присутствует пара фигурных скобок, между которыми заключена строка ExSpace для того, чтобы было видно, содержатся ли в строке начальные или замыкающие символы пробела. При выполнении инструкции MsgBox на экран выводится окно, показанное на рис. 5.8.
В каждой из строк 5-7 листинга 5.2 используется одна из трех функций очистки строк; каждая строка содержит процедуру MsgBox для вывода на экран результата работы этих функции. Не забывайте, что содержимое строковой переменной ExSpace не изменяется: функция очистки строк возвращает копию строки, которая содержится в переменной, с удаленными дополнительными символами пробела.
Рис. 5.8. Процедура в листинге 5.2 выводит на жран окно, в котором видно, что строка в переменной ExSpace содержит начальные и замыкающие символы пробела
Так надо
Запомните, что когда вы сравниваете строки с помощью операторов сравнения, VBA полагает, что та строка, которая длиннее, больше другой строки, несмотря на то, что в остальном содержимое этих строк может быть
Перед тем как сравнивать строки, используйте функцию Trim для удаления символов пробелов в начале и конце строки. Тогда вы будете уверены, что при сравнении строк дополнительные символы пробелов не будут иметь большого значения. Эти символы не играют роли для пользователя, но имеют значения для компьютера.
Определение длины строки
Часто при форматировании сообщения для пользователя или при форматировании строковых данных, которые процедура затем вставляет в рабочий лист Excel, необходимо узнать длину строки (т.е. какое количество символов содержится в строке). Эту операцию позволяет выполнить функция Len. В общем случае синтаксис для функции Len выглядит следующим образом: Len (String)
String представляет любое допустимое строковое выражение. В следующем примере показано использование функции Len в выражении присваивания:
StrLen=Len("Alison")	'возвращает 6
Повторим, что строки фиксированной длины представляют собой особый случай. Поскольку строка фиксированной длины имеет одну и ту же длину, функция Len всегда возвращает объявленную длину строки. Например, если вы объявили FirstName как строковую переменную длиной 20 символов, функция Len будет возвращать 20 как значение длины строки переменной FirstName, даже, если в переменной хранится имя Сэм, которое фактически состоит только из 3 символов; остальные 17 являются символами пробела.
Как правило, функция Len используется, когда необходимо определить количество символов, содержащихся в строке, без учета дополнительных символов в начале и конце строки. В примере с переменной FirstName для определения действительной длины строки, хранящейся в переменной, следует использовать инструкцию:
NameLen=Len(Trim(FirstName))
766	Неделя 1
В этом примере функция Trim сначала удаляет любые символы пробелов в начале или конце строки, а затем функция Len возвращает длину отсеченной строки. Если в переменной FirstName содержится слово Сэм, то NameLen вернет значение 3.
Сравнение н нонск строк
На 4-м занятии мы сравнивали строки с помощью операторов сравнения и изучали действие установок Option Compare Binary и Option Compare Text.
Для сравнения строк в VBA предусмотрено несколько функций. Первая, StrComp, просто сравнивает две различные строки. В некоторых случаях можно использовать функцию StrComp вместо операторов сравнения (=, < или >) для сравнения строк, потому что она позволяет определить, какое выполняется сравнение: двоичное или текстовое, а также переопределить установку Option Compare на уровне модуля для этого частного сравнения.
Например, для того, чтобы провести сравнение символов строки без учета регистра (верхнего или нижнего), нужно в модуль добавить инструкцию Option Compare Text. Для проведения специального сравнения строк с учетом регистра следует использовать функцию StrComp и добавить в модуль инструкцию Option Compare Binary.
Использование функции StrComp
В общем случае синтаксис для функции StrComp выглядит следующим образом:
StrComp(Stringl, String2 [, Compare])
Stringl и String2 представляют собой два любых строковых выражения, которые нужно сравнить. Необязательный аргумент Compare может быть одной из встроенных констант.
•	VbBinaryCompare используется для сравнения двух строк с двоичным (с учетом регистра) сравнением.
•	VbTextCompare используется для сравнения двух строк с текстовым сравнением (без учета регистра).
•	VbDatabaseCompare имеет смысл только в Microsoft Access; используется для сравнения строк по методу, установленному для текущей базы данных. При использовании VbDatabaseCompare в VBA, а не в Access, можно получить сообщение об ошибке времени выполнения программы, утверждающее, что использовался недопустимый аргумент процедуры.
Если опустить аргумент Compare, функция StrComp будет использовать текущие установки Option Compare. При выполнении StrComp функция сравнивает две строки, используя определенный метод сравнения, и возвращает одно из следующих значений.
•	-1, если строка Stringl меньше, чем String2
•	0, если строки Stringl и String2 равны друг другу
•	1, если строка Stringl больше, чем String2
•	В Листинге 5.3 показано использование функции StrComp.
День 5-й. Функции Visual Basic и Excel
167
flucmuuz 5.3. Пример функции StrComp
1:	Sub Demo_StrComp()
2:	Const Dflt = "Пример"
3:	Dim UserStr As String
4:	UserStr = InputBox(Prompt:="BBenHTe	текст:",
5:	Title:="CpaBHeHHe строк",
6:	Default:=Dflt)
7:	MsgBox StrComp(UserStr,	Dflt,	vbTextCompare)
8:	End Sub
Строка 1 содержит объявление процедуры Demo_StrComp. Строка 2 объявляет переменную Dflt, которая в дальнейшем используется в инструкции InputBox в качестве значения по умолчанию для приглашения диалогового окна. Строка 3 объявляет строковую переменную UserStr, в которой будет храниться результат функции InputBox. В строках 4-6 содержится инструкция, которая вызывает функцию InputBox для того, чтобы получить данные от пользователя. Функция InputBox вызывает поименованные аргументы и определяет диалоговое окно приглашения, его название и предлагает значение по умолчанию для диалогового окна функции InputBox. Результат функции InputBox инструкция присваивает переменной UserStr.
Строка 7 содержит вызов функции StrComp; функция MsgBox используется только для вывода на экран возвращаемого значения функции StrComp. Функция StrComp в строке 7 использует все возможные аргументы. Переменная UserStr и константа Dflt передаются функции StrComp для сравнения. Строка 7 также определяет, что StrComp должна выполнить текстовое сравнение (определяет встроенная константа VbTextCompare). Эта процедура показывает, согласился ли пользователь с предложенным ему значением по умолчанию для функции InputBox или ввел строку, меньшую или большую, чем предложенное значение.
Использование фуикцои InStr
Другая функция сравнения, InStr, поможет вам определить, содержится ли в одной строке другая. Эта функция полезна в нескольких случаях. Например, ее можно использовать для того, чтобы проверить, содержит ли строка, введенная пользователем, определенное слово. Или, другой пример: с помощью функции InStr можно определить, содержит ли строка символы, которые могут помешать ее преобразованию в число.
В общем случае синтаксис для функции InStr выглядит следующим образом:
InStr([Start, ] Stringl, String2, [, Compare])
где Stringl и String2 — любые допустимые строковые выражения. Функция InStr проверяет, содержится ли строка String2 в строке Stringl. Необязательный аргумент Start является любым числовым выражением; если этот аргумент используется, он определяет, с какой позиции нужно начинать поиск. Необязательный аргумент Compare определяет, какой вид сравнения следует использовать при поиске строки String2 в строке Stringl. Допустимыми значениями для аргумента Compare функции InStr являются те же встроенные константы, что используются функцией StrComp: VbBinarуCompare, VbTextCompare, и VbDatabaseCompare.
Функция InStr возвращает число, показывающее позицию символа в строке Stringl, где была найдена строка String!; если функция не находит строку String! в Stringl, то она возвращает значение 0. Если значение строки Stringl или String2 равно Null, то функция InStr возвращает Null.
168	Неделя 1
В листинге 5.4 демонстрируется использование функции InStr.
Листинг 5.4. Исиользпваиие фцикции luStr
1:	Sub Demo_InStr()
2:	Const Dflt = "Пример"
3:	Dim UserStr As String
4:	UserStr = InputBox(Prompt:="BBeflHTe	текст:",
5:	Title:=“noHCK подстроки",
6:	Default:=Dflt)
7:	MsgBox InStr(l, UserStr,	Dflt,	vbTextCompare)
8:	End Sub
Строка 1 содержит объявление процедуры Demo_InStr. Строки 2 и 3 объявляют строковую константу и строковую переменную для использования с функцией InputBox. В строках 4-6 содержится простая инструкция, которая вызывает функцию InputBox для получения данных от пользователя и сохраняет эту строку в переменной UserStr.
Строка 7 содержит вызов функции InStr; возвращаемое значение функции просто выводится на экран. При вызове функции InStr в строке 7 используются все возможные аргументы для этой функции. В этой инструкции функция InStr ведет поиск строки в переменной UserStr, определенной константой Dflt и использует текстовое сравнение. Поскольку этот вызов функции InStr включает необязательный аргумент сравнения, инструкция также содержит начальную позицию для поиска. Функция InStr ведет поиск строки в переменной UserStr, начиная с первой позиции.
Если переменная UserStr содержит строку Хороший пример, то функция InStr возвращает значение 9. Если UserStr содержит строку Пример неудачный, InStr возвращает 1. Если переменная UserStr содержит строку Походящий текст, InStr возвращает значение 0. И наконец, результат функции InStr передается как аргумент функции MsgBox (также в строке 7), которая показывает результат вызова InStr.
Разбиение строки на части
Во многих процедурах может понадобиться разбить строку на составляющие части. Например, нужно проанализировать введенную пользователем строку, чтобы определить количество содержащихся в ней слов, и, если она содержит более одного слова, разделить ее на отдельные слова. В материале следующих уроков будут приведены примеры управления строками такого типа, а также примеры листингов.
Функция Left
VBA предоставляет три функции, чтобы помочь вам извлечь подстроки из более длинных строк. (Подстрока — это любая строка, которая является — или может являться — частью более длинной строки.) Одной из таких функций является функция Left, которая возвращает копию определенной части строки. Общий синтаксис для функции Left выглядит следующим образом.
Left(string, length)
где string представляет собой любое допустимое строковое выражение, a length — любое числовое выражение. Функция Left возвращает копию строки string, начиная с первого символа в строке и заканчивая количеством символов, определенным аргу
День 5-Й. Функции Visual Basic и Excel
169
ментом length. Если значение аргумента length больше, чем действительная длина строки string, то функция Left возвращает целую строку string.
В следующем примере функция Left копирует первые 17 символов строки OldStr и возвращает эти символы в виде строки; эта инструкция присваивает переменной NewStr результат функции Left. Если OldStr содержит строку The quick red fox jumps over the lazy brown dog, то после выполнения этой инструкции функция NewStr будет содержать строку The quick red fox.
NewStr=Left(OldStr, 17)
Функция Right
Следующая функция для работы с подстроками — функция Right. Общий синтаксис для функции Right имеет следующий вид.
Right (string, length)
где string представляет собой любое допустимое строковое выражение, a length — любое числовое выражение. Функция Right возвращает копию строки string, начиная с последнего символа строки и заканчивая (справа налево) количеством символов, определенным аргументом length. Если число, определенное в length, больше, чем действительная длина строки string, то функция Right вернет целую строку string. Функция Right всегда копирует символы с конца строки, действуя в направлении справа налево, не изменяя порядок символов.
В следующем примере функция Right возвращает последних четыре символа строки, которые хранятся в строке OldStr. Если OldStr содержит строку hairball, инструкция сохранит в переменной NewStr строку ball.
NewStr=Right(OldStr, 4)
Функция Mid
Иногда может понадобиться извлечь подстроку из середины строки, а не с левого или правого края строки, например при извлечении отдельных слов из текста. Для извлечения подстроки из середины другой строки служит функция Mid. В общем случае синтаксис функции Mid имеет следующий вид.
Mid (string, start [, length])
где string представляет собой любое строковое выражение, a start и length — любые числовые выражения. Функция Mid возвращает копию строки string, начиная с символа, определенного аргументом start. Необязательный аргумент length определяет, какое количество символов копирует функция Mid из строки string. Если опустить аргумент length (или значение length окажется больше, чем оставшаяся длина строки string), то функция Mid будет копировать все оставшиеся в строке string символы от позиции, определенной аргументом start, до конца строки string. Если значение аргумента start окажется больше, чем действительная длина строки string, то функция Mid вернет пустую строку string.
В следующем примере показано использование функции Mid.
NewStr=Mid(OldStr, 3, 4)
В предыдущем примере, если OldStr содержит строку unknowingly, оператор сохранит в переменной NewStr строку know.
170
Неделя 1
ЯРИМЕЧДННЕ
В VBA имеется еще одна функция, которую можно использовать для разделения строк на составляющие части: функция Split. Эта функция возвращает массив строк; она создает массив, разделяя исходную строку по определенному вами разделительному символу (обычно по символу пробела). В этой главе функция Split рассматриваться не будет, потому что еще не изучались массивы. Они освещены в главе 14.
Использоваппе символов, которые нельзя набрать оз клаонотуре
Иногда в строку необходимо включить символ, которого нет на клавиатуре, например символ греческой буквы, символ йены или символ авторского права.
Может понадобиться включить в строку символ, который уже имеет некоторое значение в VBA, например символ кавычек ("). Но его нельзя просто включить в строку, потому что VBA всегда будет предполагать, что этот символ используется для начала или завершения строки. Например, в следующем примере инструкция выполняться не будет, в то же время не появится и сообщения ни о синтаксической ошибке, ни об ошибке времени выполнения программы.
MsgBox "Эта "ерунда” не годится"
Эта инструкция предполагает вывод на экран строки Эта "ерунда" не годится, но она не будет выполняться, потому что символ кавычек (") определяет начало или конец буквенной строки. Аргумент функции анализируется таким образом: имеется три составляющих — строка (Эта), имя переменной (ерунда) и следующая строка (не годится).
Для того чтобы в список аргументов функции включить символы, которые нельзя набрать на клавиатуре или которые имеют определенное значение в строках VBA, можно использовать функцию Chr. Синтаксис этой функции имеет следующий вид.
Chr(charcode)
где charcode представляет любое числовое выражение, которое является допустимым кодом для символа в соответствии с кодовой таблицей, используемой на вашем компьютере. Аргумент charcode должен быть числовым значением в диапазоне от 0 до 255. Как вы помните, компьютер сохраняет буквы в виде числовых значений, при этом каждый символ имеет свое собственное уникальное значение. В качестве аргумента функция Chr получает код определенного символа и возвращает строку, содержащую символ, соответствующий переданному ей числовому коду. Для того чтобы просмотреть перечень кодов, которые распознает VBA, и соответствующих им символов, откройте справочную систему и обратите внимание на содержимое закладки Contents под заглавием Miscellaneous или проведите поиск по теме character sets. Просмотрев коды символов, вы выясните, что символу кавычек (") соответствует код 34. Если в качестве аргумента функции Chr передать числовое значение кода, соответствующего символу кавычек, то приведенная ниже инструкция выведет на экран сообщение: Это "пройдет".
MsgBox "Это" & Chr(34) & "пройдет" & Chr(34)
С помощью функции Chr в строку можно вставить нужный символ, а для того, чтобы слить этот символ со строкой, можно использовать оператор конкатенации (&). Например, следующая инструкция добавляет символ авторского права в начало строки и выводит результат на экран (см. рис. 5.9).
MsgBox Chr(169) & "1999, Корпорация Рога и Копыта"
День 5-й. Функции Visual Basic и Excel
171
Кроме того, добавляя специальные символы в строку, можно изменять и формат сообщений, которые выводятся на экран. Обратившись к функции Chr, можно изобразить символ возврата каретки (код символа 13). Это символ, который генерирует компьютер при нажатии на клавиатуре клавиши <Enter> и который используется в тексте для перехода на новую строку. На рис. 5.10 показано диалоговое окно, которое выводится на экран в результате выполнения следующей инструкции. Функция Chr применяется таким образом, что после добавления в связанную строку символа возврата каретки диалоговое окно содержит две строки текста (это одна инструкция, обратите внимание на символ продолжения строки в конце).
Mirrr-oft Г«гг1
MsgBox "Это первая строка" Chr(13) s_ "А это - вторая строка"
Рис. 5.9. Использование функции Chr для добавления символов, которые нельзя набрать на клавиатуре
Это первая строка А это-вторая строка
Г-аг—1
Рис. 5.10. Функция Chr служит для добавления в строку символов, которые влияют на способ форматирования текста при выводе на экран в процессе печати
Поскольку символы начала новой строки играют существенную роль при форматировании сообщений и других строковых данных, которыми управляют процедуры, в VBA имеется несколько встроенных констант, соответствующих таким символам, поэтому использовать функцию Chr не обязательно.
•	vbCr — символ возврата каретки (код символа 13). Эта константа соответствует выражению Chr (13). Включение символа vbCr в строку приводит к тому, что VBA — и большинство приложений Windows — при выводе строки на экран переходят на начало новой строки. В некоторых случаях, например при выводе строки на принтер, символ возврата каретки приводит к переводу текущей позиции курсора на начало текущей строки без перехода к следующей строке.
•	VbLf — символ перехода на новую строку (код символа 10). Эта константа соответствует выражению Chr(10). Включение символа VbLf в строку также приводит к тому, что VBA и большинство приложений Windows при выводе строки на экран переводит курсор на новую строку. В некоторых случаях, например при выводе строки на принтер, символ перехода на новую строку приводит просто к переводу текущей позиции курсора на следующую строку без возврата к левому краю области печати.
•	VbCrlf — комбинация символов возврата каретки и перехода на новую строку, которая соответствует выражению Chr (13) & Chr (10). Если этот символ используется в среде VBA, то при выводе строки на экран, он приводит к переходу на новую строку. В некоторых случаях, например при выводе строки на принтер или в текстовые файлы формата DOS, необходимо использовать символ VbCrlf и для перевода текущей позиции курсора на следующую строку, и для перевода текущей позиции курсора к левому краю области печати.
•	VbNewLine — любой символ, используемый для создания новой строки на той платформе программного обеспечения компьютера, на которой выполняется
172
Неделя 1
процедура VBA. В исполняемых процедурах в версиях MS Excel на платформе Windows или Macintosh следует использовать константу VbNewLine.
•	VbTab — символ табуляции (код символа 9). Это символ, соответствующий нажатию на клавиатуре клавиши <ТаЬ>. Константа VbTab соответствует выражению Chr( 9). Символы табуляции можно включать в строки для того, чтобы выстроить данные в колонки.
•	В результате выполнения следующего примера на экран выводится такое же диалоговое окно, как показанное на рис. 5.10, но для добавления в строку символа возврата каретки вместо функции Chr используется константа vbCr.
MsgBox "Это первая строка" & vbCr S_
"А это — вторая строка"
Так надо
Для тех кодов символов, которые вы используете часто, объявляйте константы на уровне модуля. Использование констант на уровне модуля сделает программы более удобными для чтения и легкими для обслуживания. Например, модуль, в котором часто фигурирует символ авторского права, может содержать следующее объявление на уровне модуля.
Const CRightSym As Integer=169 'символ авторского права
Использование этого символа авторского права будет выглядеть следующим образом (на экран будет выводиться такое же диалоговое окно, как показанное на рис. 5.9).
MsgBox Chr(CRightSym) & "1999, Корпорация Рога и Копыта"
Форматирование значений данных
С помощью функции MsgBox VBA может автоматически преобразовывать данные любого типа в строку для последующего вывода на экран или добавления в ячейку рабочего листа Excel. Но несмотря на это, стандартный набор функций форматирования данных не всегда сможет удовлетворить вас.
При преобразовании числового значения в строку VBA не добавляет в строку разделители тысячи, знаки доллара или другие числовые преобразования. К тому же, если числовое значение является очень маленьким или очень большим, то VBA может вывести на экран представление этого числа в экспоненциальном виде. Например, вам нужно преобразовать число 3145.25 в строку 3145.25. Если это число представляет собой сумму в долларах, вам, возможно, захочется представить его в виде строки, содержащей знак доллара и разделитель тысячи: $3,145.25.
Подобным образом при преобразовании значений даты и времени в строку VBA всегда использует короткий формат даты и времени, который принят в операционной системе компьютера, и всегда выводит на экран значение и даты, и времени. Возможно, вам захочется использовать другой формат даты или времени либо вывести значение только даты или только времени.
Для того чтобы получить практически любой формат данных, преобразовать числа или даты в строки или даже отформатировать строковые данные в соответствии с определенным образцом, можно использовать функцию Format. Иногда может возникнуть необходимость создания специального формата вывода данных.
СОВЕТ
Функция VBA Format похожа на функцию Excel Format, она использует те же самые символы формата, что и функция в Excel.
День 5-й. Функции Visual Basic и Excel
173
При использовании функции Format можно сформировать предопределенный формат (так называемый поименованный формат) или изображение особого формата с помощью комбинации специальных групп символов, называемых символами формата. Если ни один из имеющихся поименованных форматов не удовлетворяет вас, создайте нужный вам формат на основе символов формата. Две приведенные ниже инструкции MsgBox используют два различных формата функции Format и выводят на экран одинаковые диалоговые окна (см. рис. 5.11): первая инструкция использует поименованный формат, а вторая — изображение формата с помощью символов формата:
MsgBox Format (#2/27/76#, "Long Date")
MsgBox Format (#2/27/76#, "dddd, mmmm dd, yyyy")
Microsoft Excel
пятнииа. Февраль 27.1376
EZZSZU
Puc. 5.11. Изменение формата даты посредством функции Format
В табл. 5.5 представлены доступные поименованные форматы и объясняется принцип их действия.
Таблица 5.5. Поименованные форматы функции Format
Функция (аргументы)	Возвращает/выполняет
General	Форматирует значение даты и времени, используя установки краткого формата даты и времени компьютера. То же, что и VBA-преобразование по умолчанию даты в строки
Long Date	Форматирует только значение даты, используя установки длинного формата даты компьютера
Medium Date	Форматирует только значение даты, используя установки среднего формата даты компьютера
Short Date	Форматирует только значение даты, используя установки краткого формата даты компьютера
Long Time	Форматирует только значение времени, используя установки длинного формата времени компьютера
Medium Time	Форматирует только значение времени, используя установки среднего формата времени компьютера
Short Time	Форматирует только значение времени, используя установки краткого формата времени компьютера
General Number	Форматирует число в строку без добавления каких-либо определенных символов. То же, что и VBA-преобразование чисел в строки по умолчанию.
Currency	Форматирует число в формат с символом валюты (денежный формат), разделителем тысяч и только с двумя десятичными символами после запятой. Символ валюты и десятичный разделитель определяются локальными установками Windows.
/74
Неделя 1
Окончание табл. 5.5
Функция (аргументы)	Возвращает/выполняет
Fixed	Форматирует число таким образом, что имеется, по крайней мере, один символ перед десятичным разделителем и, по крайней мере, два символа после десятичного разделителя
Standard	Форматирует число с разделителем тысяч таким образом, что имеется, по крайней мере, один символ перед десятичным разделителем и, по крайней мере, два символа после десятичного разделителя
Percent	Форматирует число в процентный вид путем умножения на 100 и добавления символа процента. Например, при форматировании числа 0,21 возвращается значение 21%
Scientific	Форматирует число в стандартную экспоненциальную запись. (При изложении материала 3-го урока вы должны были прочесть раздел, поясняющий, что такое экспоненциальная запись)
Yes/No	Функция Format возвращает строку Yes, если число было отформатировано как ненулевое значение; и строку No для любых нулевых значений. Вы будете использовать этот поименованный формат наиболее часто с типом значений Boolean
True/False	Функция Format возвращает строку True, если значение было отформатировано как ненулевое значение; и строку False для любых нулевых значений. Этот поименованный формат наиболее часто используется с типом данных Boolean
On/Off	Функция Format возвращает строку On, если значение было отформатировано как не нулевое значение; и строку Off для любых нулевых значений. Наиболее часто используется для форматирования значений Boolean
Вы можете изменить длинный, средний и краткий форматы даты и времени на компьютере с помощью панели управления Windows. Также, используя панель управления Windows, можно изменить символы, применяемые в качестве разделителя тысяч и десятичного разделителя.
ИРИМЕЧДИЙЕ
Для создания нестандартного формата чисел, даты или времени можно скомпоновать строку с помощью символов формата, для того чтобы определить вид форматирования, который функция Format будет использовать для преобразования значения в строку. В табл. 5.6 перечислены символы формата, которые используются для создания стилей форматирования функции Format. Числовая величина, используемая в качестве примера в табл. 5.6, равна 1234.5. Полужирным шрифтом выделены символы формата, которые вы вводите. Результирующая строка дана обычным шрифтом.
День 5-й. Функции Visual Basic и Excel
175
Таблица 5.5. Символы формата для создания пользовательского формата
Функция (аргументы)	Возвращает/выполняет
0	Цифровой символ формата показывает цифру, если она присутствует в соответствующей позиции, или нуль в противном случае. Этот символ формата 0 можно использовать для отображения старших разрядов целого числа и младших в десятичной части. 00000.000 выведет на экран значение 01234.500
#	Цифровой символ формата показывает цифру, если она присутствует в соответствующей позиции; в противном случае не показывает ничего. Символ формата ♦ эквивалентен символу 0, не считая того, что не отображаются старшие и младшие разряды числа. #####.## выведет на экран значение 1234.5
$	Выводит на экран в соответствующей позиции символ $. $###,###.00 выведет на экран значение $1,234.50 Десятичный символ формата выводит десятичный разделитель в позиции, определенной строкой, содержащей символы формата 0 и #. ##.## выведет на экран значение 1234.5
%	Процентный символ формата умножает значение на 100 и добавляет символ процента в позиции, определенной строкой, содержащей символы формата 0 и |. #0.00% выведет на экран значение 0.12345 как 12.35% (12.345 округляется до 12.35)
, (запятая)	Тысячный разделитель добавляет запятые как тысячные разделители в строках, содержащих символы формата 0 и ♦. ###,###,###.00 выведет на экран значение 1,234.50
Е- е-	Выводит на экран значение в научном представлении с соответствующим знаком после экспоненты только для отрицательных величин. #.####Е-00 выведет на экран 1.2345Е03. Значение 0.12345 будет показано в виде 1,2345Е-01
Е+ е+	Выводит на экран значение в научном представлении с соответствующим знаком после экспоненты для положительных и отрицательных величин. #.####Е+00 выведет на экран значение 1.2345Е+03
/	Разделитель значений дня, месяца и года в значениях формата даты, mm/dd/yy выведет на экран значение 06/06/97. Можно переопределить разделитель значений, используя дефис для отображения в следующим виде 06-06-97
ГЛ	Определяет, каким образом вывести на экран значение месяца в формате даты, m показывает месяц как значение 2, mm — 02, mmm — Фев, a mmmm — Февраль
176
Неделя 1
Окончание табл. 5.6
Функция (аргументы)	Возвращает/выполняет
d	Определяет, каким образом вывести на экран значение дня в формате даты, d показывает день как значение 1, dd — 01, ddd выводит значение Пн, a dddd — Понедельник
У	Отображает день года как число от 1 до 366
УУ	Определяет, каким образом отобразить год в формате даты, уу показывает год как значение 97, уууу — 1997
q	Показывает квартал года как число от 1 до 4
W	Показывает день недели как число. (1 соответствует воскресению)
Ww : (двоеточие)	Показывает неделю года как число от 1 до 54 Разделяет часы, минуты и секунды для значений времени. hh:mm:ss покажет 02:02: 02
Н	Определяет, каким образом выводится значение часа для формата времени, h покажет 2, a hh — 02
N	Символ формата для значений времени, п покажет значение 1, а пп — 01. Hhnn “часов” покажет 1600 часов
S	Символ формата для значений времени, s покажет значение 1, a ss — 01
АМ/РМ	Показывает время в 12-часовом формате времени и добавляет AM или PM. h:nn АМ/РМ выведет на экран 4:00 РМ. Альтернативные форматы включают am/pm, А/P и а/р
@	Выводит пробел, если в форматируемой строке отсутствует соответствующий символ. @@@@ выведет на экран строку Да с двумя символами пробелами в начале строки. (По умолчанию порядок заполнения — справа налево)
& 1	Выводит либо соответствующий символ, либо ничего. &&&& выведет на экран строку Да без каких-либо дополнительных символов пробела Выводит все символы в верхнем регистре Выводит все символы в нижнем регистре Принуждает функцию Format вернуть строку, заполненную слева направо; в противном случае строка заполняется справа налево. !@@@@ выведет на экран строку Да с двумя символами пробела в конце строки
Использование функции Format для форматирования строк и чисел позволяет создать дополнительные разделы форматирования для различного отображения формата в соответствии с форматируемым значением. Разделы форматирования разделяются точкой с запятой (;). Например, в следующем примере форматирование содержит два раздела и форматирует отрицательные и положительные числа по-разному.
$###,###,##0.00;$(###,###, НО,00)
День 5-й. Функции Visual Basic и Excel
177
Приведенный выше пример отформатирует число 1234567.89 как $1,234,567.89, а число -1234567.89 как $(1,234,567.89).
Для форматирования строки можно включить более двух разделов. Если стиль форматирования содержит только один раздел, то он применяется ко всем форматируемым строкам. Если стиль форматирования содержит два раздела, то первая часть применяется к строковым данным, а вторая — к значениям типа Null и строкам нулевой длины (т.е. к строкам, которые не содержат никаких символов, представленных в виде " "). Например, следующая инструкция, которая выводит на экран результат функции Format, используется с двумя разделами стиля форматирования для строк.
MsgBox Format(strData,	Номер телефона неизвестен")
Если strData содержит числовое значение "5105551212”, то приведенная выше инструкция выведет в диалоговом окне (510)-555-1212, а если в strData содержится строка нулевой длины (""), то в окне появится сообщение Номер телефона неизвестен.
В стиль форматирования, используемый для преобразования числовых значений, можно включить до четырех различных разделов. Первый раздел служит для форматирования положительных чисел, второй — для отрицательных, третий — для нулевых значений, а четвертый раздел используется для значений типа Null. Следующий стиль форматирования содержит четыре раздела.
"$###,###,##0.00;$(###,###,##0.00);0.00;Значение отсутствует"
Приведенный выше стиль форматирования преобразует отрицательные числа, заключая их в круглые скобки, определяет, что нулевые значения всегда будут выводиться в виде 0.00, и возвращает текстовое сообщение, если форматируемое значение имеет тип Null. После форматирования число 1234567.89 предстанет в виде строки $1,234,567.89. Число -1234567.89 после преобразования примет вид $(1,234,567.89). Значение 0 будет выведено в виде 0.00, а значение типа Null будет преобразовано в строку Значение отсутствует.
Резюме
На этом уроке мы уяснили, что такое функция, и научились использовать функции в выражениях. Вы также узнали, что результат функции можно игнорировать и, что более важно, как использовать поименованные аргументы для того, чтобы упростить работу с необязательными аргументами. В ходе занятия мы рассмотрели различные категории функций, используемых в VBA.
Вы также узнали, как получить доступ к функциям Excel, которые доступны в VBA, как использовать окно Object Browser для определения доступных функций и как вставить имя функции и ее поименованные аргументы в текст программы. В этой главе были приведены примеры, иллюстрирующие эффективное управление строками. Вы изучили основную часть функций управления строками и научились форматировать значения данных в строки для последующего вывода на экран.
Вопросы о ответы
В таблице функций VBA имеется две различные функции, которые преобразуют числа в строки: Str и CStr. Какую из них использовать?
Обычно следует использовать функцию CStr. В функции CStr применяются международные установки компьютера (они устанавливаются в окне Региональные Стандарты (Regional Settings) панели управления Windows). В них определяется сим
178
Неделя 1
вол, который используется для показа десятичного разряда числа. При изменении региональных стандартов компьютера изменяется символ десятичного разряда, но функция CStr все равно корректно преобразует числа в строки. В то же время функция Str всегда использует точку (.) в качестве числового десятичного разделителя. Функция Str введена для обратной совместимости со старыми версиями языка BASIC
Я заметил, что в таблице имеется несколько функций для преобразования строк в числа: Vai, Cint, CLng, CSng, CDbl и Ccur. Какую из них использовать?
Ситуация с функцией Vai и другими функциями, которые преобразуют строки в числа, схожа с ситуацией с функциями Str и CStr. В функции Vai в качестве символа числового десятичного разделителя используется точка (.), а в других функциях — числовой десятичный разделитель, который определен международными установками компьютера. В общем случае вместо функции Vai следует использовать функции CInt, CLng, CSng, CDbl и Ccur. И функция Str, и функция Vai включены в VBA для поддержки совместимости с более ранними версиями языка программирования BASIC; функции CStr, Cint, CLng, CSng, CDbl и Ccur являются новыми и более эффективными дополнениями языка
Я не уверен, что понимаю, в чем состоит различие поименованных аргументов от стандартного списка аргументов.
Стандартный список аргументов просто содержит значения, передаваемые функции; они разделены запятой и расположены в определенном порядке. Значения каждого из аргументов определяются на основании его положения в списке аргументов. В случае поименованных аргументов для определения значения каждого аргумента применяется особое имя. При использовании поименованных аргументов их не нужно располагать в каком-либо определенном порядке
Я не уверен, что понимаю, каким образом используются аргументы HelpFile и Context в функциях InputBox и MsgBox.
Пока вы не приобретете Windows Help Compiler от Microsoft (или не будете использовать другой вспомогательный инструмент для создания файлов справки), маловероятно, что вы будете использовать эти аргументы. Аргумент HelpFile определяет имя файла, который нужно предварительно подготовить с помощью Windows Help Compiler. Аргумент Context определяет конкретный раздел помощи, который можно вывести на экран, когда пользователь щелкнет на кнопке Помощь (Help). Когда пользователь щелкнет на кнопке Справка (Help), VBA вызывает справочную систему Windows — ту же программу, которую использует каждое Windows-приложение для вывода файлов справки — и выводит раздел помощи, определенный контекстным индексом. Для того чтобы получить некоторое представление о том, как это происходит, введите следующую процедуру в модуль Excel и выполните ее.
Sub Demo_HelpButton()
Dim msglext As String
msglext = "Нажмите кнопку Справка для получения помощи"
MsgBox Prompt:=msglext,
Title:="Пример кнопки Справка",
Buttons:=vbMsgBoxHelpButton,
HelpFile:="VBAXL9.CHM", _
Context:=0
End Sub
День 5-й. Функции Visual Basic и Excel
179
Если щелкнуть на кнопке Справка (Help) в диалоговом окне, вызванном этой процедурой, в справочной системе Excel можно увидеть описание иерархий Microsoft Excel Objects.
Обилие информации о функции Format немного пугает. Действительно ли нужно все это запоминать?
Нет, не нужно запоминать все аргументы функции Format. Главное, чтобы вы знали о возможностях функции Format и понимали принцип использования функции для настройки преобразования чисел и дат в строки.
Коллоквиум
Ответы приведены в приложении.
Тест
1.	Приведите три примера задач, которые, по вашему мнению, требуют использования функций.
2.	Где в инструкции VBA можно использовать значение, возвращаемое функцией?
3.	Каким образом можно указать VBA, что вы хотите проигнорировать результат функции? Можно ли игнорировать результат любой функции VBA?
4.	В чем различие между функциями, выполняющимися в VBA, и функциями, выполняющимися в Excel?
5.	Какие ключевые слова существуют в VBA, которые нужно использовать при получении доступа к функциям Excel?
6.	Можно ли проигнорировать результат функции Excel?
7.	Для чего можно использовать окно Object Browser?
8.	Почему так важно уметь управлять строковыми данными?
Упражнения
1.	Добавьте к следующим инструкциям функции преобразования данных таким образом. чтобы не возникло ошибки несоответствия типа и выражение имело требуемое значение (Подсказка. Напишите процедуру, которая использует инструкцию MsgBox для вывода на экран результата каждого выражения и помните, что данные типа String обозначаются с помощью символа $, тип Integer — с помощью %, тип Currency — с помощью символа @ и тип Single — с помощью !):
Sum$ =12+15
Num% = "47" + "52"
Num? = 12.98 * "16"
Root! = Sqr(Usr_Input$)
' Значением Sum$ должно быть "27"
' Значением Num% должно быть 99
' Значением Num? должно быть 207.68
' Если значение Usr_Input$ равно "4", 'Root! Должно быть равно 2
2.	Напишите процедуру, которая получает от пользователя три числа и затем выводит на экран наименьшее из введенных чисел. Для определения наименьшего значения используйте функцию Excel MIN. В процедуре используйте поименованные аргументы для инструкции InputBox и MsgBox.
180
Неделя 1
3.	Напишите процедуру, которая получает от пользователя слово и выводит три первых символа слова, введенного пользователем, четыре последних символа слова и четыре символа, следующих после двух первых символов слова, введенного пользователем.
4.	Напишите процедуру, которая получает от пользователя строку и затем выводит на экран результат поиска буквы L во введенной строке. (В этом упражнении нужно просто вывести числовой результат функции InStr.)
День 5-й. Функции Visual Basic и Excel
181

<День Цй
Процедуры-функции и нестандартные функции
На предыдущем занятии вы узнали, что такое функция и как пользоваться функциями, встроенными как в Excel (или в другое приложение), так и в VBA. Этот урок посвящен тому, как создавать собственные функции и как использовать их в рабочих листах Excel. Сегодняшнее занятие посвящено следующим вопросам.
•	Как создавать процедуры-функции с поименованными аргументами, с необязательными аргументами; как указывать тип аргумента и как сделать так, чтобы ваши функции можно было использовать в таблицах Excel.
•	Как использовать ваши процедуры-функции в VBA-процедурах и в рабочих листах Excel.
•	Советы и требования по разработке общих и специальных функций для использования в рабочих листах Excel.
•	Что такое рекурсивная функция, какие преимущества и какие проблемы связаны с использованием рекурсивных функций.
Понятие о процедуре-функции и о нестандартной функции
Перед тем как вы приступите к написанию своих собственных функций, вам необходимо познакомиться с терминологией и основными понятиями, используемыми в VBA. Хотя существует только один способ создания собственных функций в VBA, вы можете — соблюдая некоторые основные принципы и ограничения — создать некоторое разнообразие собственных функций, пригодных для использования в Excel (или в другом VBA-приложении).
Процедура-функция — это особый вид процедуры VBA, которая возвращает результат. Ваши процедуры-функции, так же как и встроенные функции VBA, могут иметь необязательные аргументы или использовать поименованные аргументы. Процедуры-функции можно использовать для получения значений и дальнейшего их использова
/82	Неделя 1
ния в выражениях и операторах присваивания или в качестве аргументов для других функций и процедур. В написанных вами инструкциях указывается, какие аргументы использует функция, какие выполняет операции и какие возвращает значения.
Процедуры-функции, созданные для рабочих листов Excel, могут использоваться так же, как и встроенные функции рабочих листов. Для того чтобы использовать процедуры-функции в Excel, их текст должен соответствовать определенным принципам и ограничениям.
ПРИМЕЧАНИЕ
Функции VBA можно использовать в Excel для расширения набора встроенных функций. Другие приложения, поддерживающие VBA, например Access, также позволяют использовать функции VBA, созданные пользователем для расширения встроенного набора функций. Во всех приложениях, поддерживающих VBA, можно передать некоторое значение управляющего элемента в форме, например в текстовое поле. (О работе с формами вы узнаете в уроке 16-го дня.)
Для приложения Excel функции VBA представляют нестандартные функции. Хотя все нестандартные функции также являются процедурами-функциями, не все процедуры-функции удовлетворяют требованиям нестандартных функций.
Процедура-функция — наиболее общий термин для функции, которую создает пользователь; термин нестандартная функция подразумевает особый вид процедуры-функции, которую Excel может использовать в приложении. Вы не можете использовать процедуры-функции, которые не соответствуют требованиям нестандартной функции в формуле рабочего листа Excel; эти функции можно использовать в инструкциях ваших собственных процедур.
Так же как и встроенные функции VBA, процедуры-функции создаются для выполнения вычислений, получения информации или для форматирования данных. Например, у вас есть программа Excel, которая анализирует данные о потреблении энергии. В некоторых точках этой программы существует потребность перевести потребление природного газа, которое вычисляется в термах, в единицы измерения BTU (британская тепловая единица). Вместо того чтобы каждый раз печатать формулу для перевода единиц в BTU — что увеличивает вероятность опечатки, которая может привести к ошибочному вычислению, — можно написать процедуру-функцию, которая получает значение аргумента в термах и возвращает результат функции в единицах BTU.
Подобно формулам для перевода футов в сантиметры, фунтов в килограммы, градусов по Фаренгейту в градусы по Цельсию или формулам, которые вычисляют размер прибыли, комиссионные скидки, все эти формулы можно использовать в процедурах-функциях.
Длинные, сложные формулы диалоговых окон можно заменить простыми нестандартными функциями. Как правило, значительно легче запомнить и использовать название простой нестандартной функции, чем сложную формулу. Например, рабочий лист содержит данные по продажам (Sales) и данные по стоимости (Costs) на определенный квартал года. В рабочем листе можно ввести формулу, которая подсчитает процент прибыли, используя данные рабочего листа.
=((SUM(Sales) -SUM(Costs))/SUM(Costs))*100
Эта формула довольно длинная, но по сравнению с другими формулами, которые используют пользователи Excel, она сравнительно проста. Чем чаще вы используете эту формулу, тем больше вероятность, что вы сделаете опечатку при ее вводе. К тому же совсем не обязательно знать, что делает эта формула, когда вы впервые увидите ее. Вы можете устранить эти проблемы, создав нестандартную функцию для Excel. Например, вы можете создать функцию под названием Calc_PcntProfit, которая в каче
День 6-й. Процедуры-функции и нестандартные функции
183
стве аргументов получает область имен и возвращает результат вычисления функции. Подобные нестандартные функции запомнить и использовать проще, чем длинную формулу Excel.
Создание процедуры-функции
Процедуру-функцию нельзя записать с помощью средства записи макросов, хотя можно отредактировать записанный макрос так, чтобы он стал функцией. Однако, большая часть процедур-функций пишется непосредственно в модуле VBA.
Пишем процеддру-фднкцию
Процедуры-функции очень похожи на обычные процедуры, которые вы уже умеете писать. Основное отличие (помимо того, что процедура-функция возвращает значение, а простая процедура — нет) состоит в том, что тело процедуры-функции заключается между ключевыми словами Function и End Function, а не между знакомыми вам Sub и End Sub.
Общий синтаксис для процедуры-функции выглядит следующим образом.
Function имя [список аргументов]
'инструкции VBA
[имя = выражение]
End Function
Каждая процедура-функция начинается с ключевого слова Function, за которым следует название процедуры-функции, имя — это имя, которое вы дали процедуре-функции. Имена функций должны соответствовать тем же правилам, что и любое имя идентификатора в VBA. Они должны начинаться с буквы, не должны содержать символов пробела или любых символов арифметических, логических операторов или операторов отношения и не должны совпадать ни с одним из зарезервированных ключевых слов VBA.
После названия функции следует список аргументов функции, который заключается в круглые скобки. В синтаксическом примере список аргументов — это список аргументов вашей процедуры-функции. Список аргументов — необязательный параметр; вы не обязаны писать функцию, которая использует аргументы. Если вы используете список аргументов, следует разделить название каждого аргумента запятой (,).
Необязательный синтаксический элемент [имя = выражение] представляет собой присваивание результата функции, которое указывает VBA, какое значение функция должна возвратить. Хотя эта часть функции не является обязательной, следует всегда включать оператор присваивания в процедуру-функцию. Наконец описание функции заканчивается ключевыми словами End Function.
Даже если у функции нет аргументов - как у функций Now, Date и Time - вы все равно должны в описании функции ставить скобки для списка аргументов. Например, вы должны написать процедуру-функцию, которая возвращает имя файла, содержащего текущую рабочую книгу, в ячейку рабочего листа. Функция такого типа в аргументах не нуждается, потому что для ее выполнения не требуется внешней информации. Объявление такой функции будет иметь следующий вид: Function ThisBookName()
184
Неделя 1
Как правило, целью функции является выполнение некоторых вычислений или обработка определенных данных и возврат результата этой обработки. Как вы уже знаете, информация передается встроенной функции через список аргументов. При объявлении процедуры-функции вы перечисляете название каждого аргумента, который собираетесь передать функции, отделяя название каждого аргумента запятой. Названия аргументов должны соответствовать требованиям, предъявляемым к любому идентификатору VBA.
Названия, которые вы используете в списке аргументов, похожи на переменные: они ссылаются на любое значение, которое вы передаете функции во время ее вызова, или когда она вызывается в инструкции VBA, или в приложении Excel. Всякий раз, когда происходит обращение к названию аргумента в инструкции внутри функции, VBA ведет себя так, будто название аргумента — переменная, содержащая величину, приведенную в списке аргументов оператора, который вызвал вашу функцию.
ПРИМЕЧАНИЕ
Имена аргументов имеют такую же область определения, как и переменные, объявленные в функции локально, т.е. переменные аргументов недоступны за пределами списка аргументов процедуры-функции, в которой они объявлены.
Посмотрите еще раз на последний синтаксический пример, точнее на его строку с инструкцией [имя = выражение]. Эта конструкция называется присваивание результата функции. Имя — это имя функции, а выражение — это любое выражение, значение которого функция будет возвращать. Присваивание функции указывает VBA, какое значение функция должна вернуть. Обратите внимание, что имя процедуры-функции используется как переменная и ей присваивается значение. Процедуры-функции могут вообще не иметь ни одной, иметь одну или несколько различных инструкций присваивания результата функции.
Первый пример, который вы рассмотрите, обрабатывает строки. Как вы, наверное, помните из пятого урока, функция Len возвращает длину строки, включая обрамляющие символы пробела. Если вы хотите узнать длину строки без обрамляющих символов пробела, можно использовать вложенную функцию (когда одна функция вызывает другую):
StrLen = Len (Trim(AnyStr))
В этой инструкции функция Trim сначала отсекает все начальные и заключительные символы пробела строки, хранящейся в переменной AnyStr; результат функции Trim служит аргументом для функции Len и поэтому величина, возвращаемая функцией Len, равна длине строки без начальных и заключительных пробелов.
Этот тип операций является хорошим примером для процедуры-функции. Намного легче и проще использовать вызов процедуры-функции, чем постоянно писать вложенный вызов функции, показанный выше. В листинге 6.1 показана простая процедура-функция Slen, которая возвращает длину строки без обрамляющих символов пробела.
Листинг В.1. Пример простои нроцеддры-фднкции: SIeh
1:	Function SLen(tStr)
2:	SLen = Len(Trim(tStr))
3:	End Function
Строка 1 содержит объявление функции Slen и начинается с ключевого слова Function, за которым следует название функции. Следом за именем функции открывается скобка, которая указывают VBA, что это начало списка аргументов функции. Затем следует название аргумента tStr. Название аргумента сообщает VBA, что один аргумент
День 6-й. Процедуры-функции и нестандартные функции
185
должен быть передан процедуре-функции при вызове. Наконец, строка 1 заканчивается закрытием круглых скобок, которые завершают список аргументов функции.
ПРИМЕЧАНИЕ
При нажатии клавиши <Enter> после ввода ключевого слова Function, имени функции и списка аргументов функции редактор VB автоматически вводит ключевые слова End Function, так же как вы вводите ключевое слово Sub при создании процедуры.
Строка 2 функции SLen исполняет всю работу функции и производит присваивание значения функции. При вычислении выражения Len(Trim(tStr)) строка, полученная через аргумент tStr, передается функции Trim для удаления лишних символов пробела. В свою очередь, результат функции Trim передается в качестве аргумента функции Len. Затем функция SLen возвращает строку аргумента без начальных или завершающих символов пробела.
В строке 2 VBA вычисляет выражение Len(Trim(tStr)) и присваивает результат этого выражения функции SLen. Эта операция называется присваивание значения функции. Она указывает на то, что функция должна вернуть определенный результат.
И наконец, в строке 3 процедура-функция заканчивается ключевыми словами End Function. После выполнения этой команды происходит возврат к той инструкции, которая вызывала функцию SLen, и результат функции вставляется в ту точку, где используется имя этой функции.
Так надо
Убедитесь, что вы включили инструкцию присваивания значения функции в процедуру-функцию. Написание процедуры-функции, которая не возвращает результат, не имеет смысла.
Запомните, что VBA не выдаст никакого сообщения об ошибке, если вы забудете включить инструкцию присваивания значения функции. Вы должны удостовериться, что включили эту инструкцию в процедуру.
Примером функции SLen может служить приведенная ниже инструкция. Она выводит на экран результат выполнения функции SLen для строки " бешеный конь ":
MsgBox SLen " бешеный конь "
В этой инструкции строка аргумента содержит по четыре дополнительных символа пробела в начале и в конце строки, таким образом, общая длина строки (как указывает функция Len) составляет 15 символов. Функция SLen указывает длину строки без начальных и замыкающих символов пробела; в результате выполнения предыдущей инструкции на экране появится число 7.
Создание нестандартных фднкцнн для Excel
Нестандартными функциями (user defined functions, или сокращенно UDF) называются процедуры-функции, на действия которых наложены определенные ограничения. Это единственный тип функций, которые можно использовать в качестве формулы в ячейке рабочего листа Excel.
Все ограничения, касающиеся нестандартных функций, вытекают из одного условия: UDF никоим образом не должна изменять среду Excel. Это означает, что нестандартная функция не может выделять, вставлять, удалять или форматировать данные рабочего листа, диаграммы рабочего листа или любого другого листа. UDF также не может добавлять, удалять или переименовывать рабочий лист или рабочую книгу, а также изменять вид экрана и т.д. Например, если процедура-функция выделяет ячей
186
Неделя 1
ки или каким-либо образом изменяет текущий рабочий лист, то ее нельзя использовать в Excel в качестве нестандартной функции.
К тому же UDF не может устанавливать свойства объекта или использовать методы объекта, потому что в большинстве случаев установка свойств объекта или использование методов объекта приводит к изменениям среды Excel (объекты, методы и свойства будут рассмотрены на следующем уроке). Однако нестандартная функция может извлечь значение свойств объекта и выполнить любой метод объекта, который не изменяет среду Excel.
Как правило, нестандартная функция может только выполнять вычисления или производить операции с данными, полученными через список аргументов или извлеченными из Excel. Функцию SLen, приведенную в листинге 6.1, можно использовать в качестве нестандартной функции, так как она соответствует всем требованиям UDF.
ПРИМЕЧАНИЕ
При использовании процедуры-функции, которая в действительности не соответствует требованиям нестандартной функции, вы не получите сообщения об ошибке ни в VBA, ни в Excel. Однако такая функция не сможет вернуть результат. Например, при попытке вставить значение в ячейку рабочего листа Excel с помощью процедуры-функции, которая не соответствует требованиям, предъявляемым к нестандартным функциям, в ячейке появится сообщение об ошибке f VALUE. Это сообщение указывает на то, что функция или формула в этой ячейке не способна вернуть корректный результат.
Объявление шипа данных для результата фднкцин
Если тип данных для результата процедуры-функции не определен, то по умолчанию он будет иметь тип данных Variant. Как вы узнали из материала третьего дня обучения, значения, которые сохраняются или обрабатываются как тип данных Variant, занимают больше памяти, чем любой другой тип данных, и требуют больше времени для обработки.
Тип данных результата функции следует определять по тем же причинам, по которым необходимо устанавливать тип данных переменных и констант. Это нужно для того, чтобы ускорить выполнение программы, более эффективно использовать память, упростить текст программы и, наконец, для того, чтобы быстрее обнаружить ошибки программирования, когда VBA преобразует данные из одного типа в другой. При создании процедуры-функции обращайте внимание на объявление соответствующего типа данных для их результата.
Для определения типа данных результата, возвращаемого функцией, в конце строки объявления функции следует просто добавить ключевое слово As и название типа данных, который вы хотите использовать.
Обший синтаксис для объявления функции с определенным типом данных выглядит следующим образом.
Function имя([список аргументов]) [As тип данных]
Все остальные элементы синтаксиса при объявлении функции остаются прежними, как было показано в предыдущих примерах. Тип данных представляет название любого типа данных VBA. Элементы Имя функции и список аргументов соответствуют приведенным ранее примерам.
VBA не позволит присвоить результату функции тип данных, который несовместим с ранее объявленным типом. Например, если вы ошибочно напишете инструкцию присваивания таким образом, что она присвоит тип Integer результату функции, для которого уже объявлен тип String, то VBA выведет сообщение об ошибке несоответствия типа.
День 6-й. Процедуры-функции и нестандартные функции
187
Если присваиваемый тип данных отличается от типа данных, объявленного для процедуры-функции, но совместим с ним, то VBA преобразует значение к типу данных, объявленному для результата функции. Например, если вы присвоите тип данных Double функции, чей результат был объявлен как тип Long, VBA не выдаст сообщения об ошибке, просто при возврате результата функции он преобразует его из типа Double в тип Long.
Объявление типа данных для результата функции имеет еще одну особенность: если процедура-функция завершается без выполнения инструкции присваивания, то для функций, возвращающих результат типа String, VBA возвращает строку нулевой длины, а для процедуры-функции, которая возвращает числовой тип данных, — значение 0. (Нестандартная процедура-функция, которая завершается без выполнения инструкции присваивания, возвращает результат типа Variant, содержащий специальное значение Empty.)
Функция SLen, приведенная в листинге 6.2, всегда будет возвращать значение типа данных Long.
Листипа 6.2. Oupegeuune типа даииых рездльтата фдикции Slen
1:	Function SLen(tStr) As Long
2:	'Возвращает длину строки tStr без обрамляющих пробелов
3:	SLen = Len(Trim(tStr))
4:	End Function
Строка 1 содержит объявление функции SLen. Функция имеет такое же имя и такой же список аргументов, что и функция, приведенная в листинге 6.1, но в данном случае объявляется тип данных для результата функции. За списком аргументов функции, заключенным в круглые скобки, следует ключевое слово As и название типа данных Long. Это говорит о том, что в любом случае функция SLen должна вернуть результат типа данных Long.
В данном случае тип данных Long для результата функции SLen был выбран по двум причинам. Во-первых, потому что длина строки всегда является целым числом — вы никогда не получите дробную часть символа. Во-вторых, потому что тип данных Integer не имеет достаточного диапазона для хранения максимально возможной длины строки, — наибольшее положительное число типа Integer составляет 32767, а строка может иметь длину приблизительно 2 миллиарда символов. Тип данных Long является наименьшим типом данных, который, тем не менее, может вместить полный диапазон значений, которые может вернуть функция.
Строка 2 содержит комментарий, в котором описывается цель и назначение процедуры-функции. Строка 3 выполняется так же, как и строка 2, приведенная в листинге 6.1. В данном примере для результата функции SLen был определен тип данных. Если в строке 3 попытаться присвоить этой функции вместо числового значения строковое, то VBA выведет сообщение об ошибке несоответствия типа.
Если не указать тип результата функции и вместо числового значения результату функции присвоить значение типа String, то VBA выполнит присваивание функции SLen и SLen вернет значение этой строки как результат. Если затем использовать этот результат функции SLen в арифметическом выражении, это может привести к ошибочному результату или сообщению о какой-либо ошибке времени выполнения. Отследить ошибки, возникающие в результатах при несоответствии типа данных, довольно сложно. Дело в том, что ошибочный результат может использоваться несколькими функциями или процедурами, прежде чем ошибка станет явной. Объявление типа данных результата функции поможет предотвратить и выявить подобные ошибки.
188
Неделя 1
Так надо
Выбирайте для результата процедуры-функции тип данных, который требует меньших ресурсов памяти, но содержит полный диапазон возможных значений, которые может вернуть функция. Для объявления результата математических функций или функций, выполняющих общие числовые вычисления, наиболее типичным является использование типа данных Double.
Прежде чем выбрать тип данных для результата функции, определите, как вы собираетесь ее использовать; часто назначение процедуры-функции может повлиять на выбор типа данных. Например, если вы собираетесь использовать процедуру-функцию исключительно для выполнения некоторых типов финансовых вычислений, то для результата функции можно объявить тип данных Curruncy.
В каждую функцию или процедуру следует включить одну-две строки комментариев для краткого описания программы. Позднее, если понадобится изменить текст программы, зги комментарии могут сослужить хорошую службу. (Хотя назначение и порядок выполнения функции в момент написания может быть очевиден, нельзя сказать, что он останется прежним три месяца спустя.)
Объявление типа данных для аргдмвнтов функции
До тех пор пока тип данных не определен, все аргументы процедуры-функции передаются в качестве типа данных Variant. Так же как и для результата функции, можно определить и специальный тип данных для каждого аргумента этой функции.
Вы уже знакомы с причинами, по которым следует определять тип данных для переменных или результата функции; по этим же причинам следует объявлять тип данных для аргументов функции. Введение аргументов для процедуры-функции также помогает вам (или пользователю) при вызове функции вводить аргументы соответствующего типа и в определенном порядке.
Для того чтобы объявить определенный тип данных для аргументов процедуры-функции, следует просто добавить ключевое слово As и название типа данных, который вы хотите использовать, после имени аргумента в списке. В листинге 6.3 приведена функция SLen, аргумент tStr которой принимает только значение типа данных String.
Листинг 6.3. Опредедвние типа данных аргумента функции 51вп
1:	Function SLenftStr As String) As Long
2:	'Возвращает длину строки tStr без обрамляющих пробелов
3:	SLen = Len(Trim(tStr))
4:	End Function
За исключением объявления процедуры-функции в строке 1, листинг 6.3 полностью совпадает с листингом 6.2. После объявления типа данных аргумента при вызове функции SLen в качестве значения аргумента допускается только значение типа данных String. Вызов функции SLen с любым другим типом данных — даже с типом данных Variant — в качестве аргумента, приведет к выводу на экран сообщения об ошибке несоответствия типа (точнее, об ошибке несоответствия аргумента).
Создание необязательных аргдмвнтов
Как правило, все аргументы, которые перечисляются в списке аргументов процедуры-функции, должны использоваться при каждом вызове функции; такие аргументы являются обязательными. Вы уже познакомились с необязательными аргументами
День 6-Й. Процедуры-функции и нестандартные функции	189
при работе с функциями VBA InputBox и MsgBox. Таким же образом вы можете создать необязательные аргументы для собственных процедур-функций.
Необязательные аргументы можно использовать для контроля выполнения функцией определенной задачи. Например, один из необязательных аргументов функции MsgBox определяет количество управляющих кнопок в диалоговом окне. Функция FlipCase, приведенная в листинге 6.4, использует необязательный аргумент для того, чтобы определить, каким количеством символов оперирует функция.
При включении необязательного аргумента в список аргументов сначала необходимо перечислить все обязательные аргументы. После первого необязательного аргумента все последующие аргументы в списке должны быть необязательными. Для необязательных аргументов функции можно даже определить тип данных и задать значение по умолчанию.
Для того чтобы ввести необязательный аргумент, при объявлении процедуры-функции перед именем аргумента нужно ввести ключевое слово Optional, как показано в строке 1 листинга 6.4.
Лившица 6.4. Исиальзоваиие пввОязашельиоао аргдмвита в фдикции Flipcase:
1:	Function Flipcase(tStr As String, Optional nChar) As String
2:	'меняет регистр - верхний на нижний, нижний на верхний
3:	'первых nChar символов в строке tStr. Если nChar не задано,
4:	'меняет регистр всех символов
5:
6:	Dim k As Long 'счетчик цикла
7:	Dim	TestC	As	String	*	1
8:
9:	If IsMissing(nChar)	Then
10:	nChar = Len(tStr)
11:	End	If
12:
13:	For	k = 1	To	nChar
14:	TestC = Mid(tStr, k, 1)
15:	If (StrComp(TestC, "A", vbBinaryCompare) >= 0) And _
16:	(StrComp(TestC,	"Z",	vbBinaryCompare)	<=	0)	Xor _
17:	(StrComp(TestC,	"A",	vbBinaryCompare)	>=	0)	And _
18:	(StrComp(TestC,	vbBinaryCompare)	<=	0)	Then
19:	Mid(tStr, k, 1) = LCase(TestC)
20:	Elself (StrComp(TestC, "a", vbBinaryCompare) >= 0) And _
21:	(StrComp(TestC,	"z",	vbBinaryCompare)	<=	0)	Xor _
22:	(StrComp(TestC,	"a",	vbBinaryCompare)	>=	0)	And _
23:	(StrComp(TestC,	"я",	vbBinaryCompare)	<=	0)	Then
24:	Mid(tStr, k, 1) = DCase(TestC)
25:	End If
26:	Next k
27:	FlipCase = tStr
28:	End Function
Строка 1 содержит объявление функции FlipCase, которая имеет два аргумента. Для первого аргумента, tStr, объявлен тип данных String. Этот аргумент при вызове функции FlipCase является обязательным и должен содержать строковое значение. Два аргумента в списке отделены запятой; второму аргументу предшествует ключевое слово Optional, которое указывает, что при вызове функции FlipCase этот аргумент
190
Неделя 1
может быть опущен. (Если функция FlipCase содержит более двух аргументов, то любой другой дополнительный аргумент в списке после аргумента nChar также может быть необязательным.) Для необязательного аргумента nChar тип данных не объявляется, поэтому по умолчанию ему будет соответствовать тип данных Variant. (Запомните, любой переменной, функции или аргументу без объявленного типа данных будет соответствовать тип данных variant.)
И наконец, объявление функции FlipCase заканчивается определением того, что тип данных, возвращаемый функцией, всегда должен соответствовать типу данных String.
Пример с функцией FlipCase сложнее, чем любой из приведенных ранее примеров. Он содержит два блока, о которых раньше не упоминалось: один — для принятия решения, а другой — для повторения некоторых действий. Хотя каждый из блоков будет подробно описан в материале последующих уроках, ниже кратко изложена суть операций процедуры-функции FlipCase.
В строках 2-4 содержатся комментарии по описанию процедуры-функции. Строки 6 и 7 содержат объявление переменных, используемых функцией FlipCase.
В строках 9-11 приводится пример типичного использования особой встроенной функции VBA: IsMissing. Эта функция возвращает значение True только в том случае, если необязательный аргумент типа данных Variant не был включен в вызов процедуры-функции. (В следующем разделе этого урока вы узнаете, что происходит в случае, если необязательный аргумент с объявленным типом данных опускается при вызове процедуры.)
В строках 9-11 используется директива If...Then (более детально она будет рассмотрена в ходе восьмого урока). Она изменяет значение аргумента nChar в зависимости от того, включен ли аргумент в вызов функции. Если аргумент nChar опущен, ему присваивается значение, равное длине строки аргумента tStr. Таким образом проверяется, сколько символов находится в распоряжении функции FlipCase — несколько символов или вся строка.
В строках 13-26 выполняется вся работа функции FlipCase. Она состоит из двух блоков, при этом один из них помещен в другой. Первый, внешний, блок — цикл For...Next, который начинается в строке 13. (Цикл For...Next более детально будет описан при изложении материала девятого урока), при этом инструкции цикла повторяются установленное число раз. В данном случае, инструкции, заключенные в цикл For.. .Next, будут повторяться количество раз, которое определено значением аргумента nChar.
Функция Mid в первом операторе цикла For...Next (строка 14) копирует один символ из аргумента tStr и сохраняет его в переменной TestC. (В переменной к содержится число, соответствующее текущему количеству выполнения цикла For...Next. Так, при первом выполнении цикла копируется первый символ аргумента tStr, во второй раз — второй символ tStr и т.д.) Символ копируется в переменную TestC таким образом, что функция Mid вызывается один раз, следовательно, экономится время выполнения процедуры. Использование переменной TestC для сохранения проверяемого символа также помогает упростить текст программы для понимания.
После того как копия проверяемого символа была присвоена переменной, в строке 15 выполнение передается другой структуре — If...Then — для определения регистра символа в переменной TestC. Обратите внимание, что в конце строки используется символ продолжения строки. Условие логического выражение в строках 15-18 имеет значение True, если символ находится в диапазоне прописных букв в алфавитном порядке. Использование функции StrComp гарантирует, что независимо от установок Option Compare будет выполняться не текстовое, а бинарное сравнение.
День б-й. Процедуры-функции и нестандартные функции
191
Если символ, содержащийся в переменной TestC, является прописной буквой, выполнение передается в строку 19. В этой строке содержится процедура Mid, которая заменяет в строке один символ соответствующим ему символом нижнего регистра, полученным с помощью функции LCase.
ПРИМЕЧАНИЕ
В листинге 6.4 используется и функция Mid, и процедура Mid; не путайте их между собой. Функция Mid возвращает строку из середины другой строки; а процедура Mid используется для замены одного или более символов одной строки символами другой строки. Сначала функция FlipCase использует функцию Mid для возврата определенного символа строки, над которым производится действие. Далее в функции FlipCase используется процедура Mid для замены символа в строке эквивалентным ему символом верхнего или нижнего регистра. Редактор VB выделяет имя процедуры Mid синим цветом и не позволит вам создать процедуру с таким именем.
Далее выполнение программы передается строке 20, где символ снова сравнивается, но уже в диапазоне букв нижнего регистра. С помощью логического выражения проверяется, находится ли символ в переменной TestC в диапазоне букв нижнего регистра. И снова использование функции StrComp в выражении гарантирует, что независимо от установок Option Compare будет проводиться бинарное сравнение строк. (Если вместо бинарного использовать текстовое сравнение, функция FlipCase выполняться не будет, потому что не сможет различить буквы верхнего и нижнего регистров!).
Если символ в переменной TestC является буквой нижнего регистра, выполнение передается строке 24. В этой строке также используется процедура Mid для замены символа аргумента tStr (копия которого была передана переменной TestC) эквивалентным ему символом верхнего регистра, который извлекается посредством функции UCase.
Обратите внимание, что если символ в переменной TestC не является буквой ни верхнего, ни нижнего регистра, значение аргумента tStr не изменится, потому что, поскольку символ не является литерой ни верхнего, ни нижнего регистра, скорее всего, это цифра или знак препинания и преобразования не требуется (если оно вообще возможно).
В строке 26 цикл For...Next заканчивается; когда цикл выполнится определенное количество раз (это количество определяется аргументом nChar), выполнение передается строке 27, которая содержит инструкцию присваивания результата функции. На этом функция завершает свою работу и возвращает измененное значение аргумента tStr как результат функции.
Определения типа данных пеобязательпогп аредмента
В листинге 6.4 тип данных для необязательного аргумента nChar не был определен, поэтому по умолчанию ему был присвоен тип Variant. В некоторых случаях нужно, чтобы аргумент функции имел тип Variant. Однако чаще всего тип данных для необязательного аргумента следует объявлять. Тип данных необязательного аргумента рекомендуется объявлять по тем же причинам, по которым необходимо объявлять тип данных для обязательных аргументов; для того, чтобы помочь пользователю программы правильно использовать функцию и определить оператор, который передает функции некорректное значение. Тип данных необязательного аргумента объявляется так же, как тип данных обязательного аргумента — посредством ключевого слова As. В следующем примере для необязательного аргумента nChar функции FlipCase объявляется тип данных Long.
Function FlipCase (tStr As String, Optional nChar As Long) As String
192
Неделя 1
В листинге 6.6 в процедуре-функции FlipCase объявляется тип данных для необязательного аргумента.
Определение значения по умолчанию для необязательных аргдмвнтов
При объявлении необязательного аргумента функции VBA резервирует место под этот аргумент. Поскольку необязательные аргументы могут как присутствовать, так и отсутствовать при вызове функции, VBA можно дать указание присвоить необязательному аргументу значение по умолчанию. Это значение VBA использует при каждом вызове функции, даже если необязательный аргумент не был включен в список аргументов. Значение по умолчанию для необязательного аргумента задается так же, как объявляются поименованные константы: после названия аргумента ставится знак равенства (=) и присваивается определенное значение.
Function FlipCase (tstr As String, Optional nChar As Long=0) As String
В этом примере объявляется функция FlipCase. Объявление функции указывает на то, что обязательный аргумент tstr является типом данных String. Аргумент nChar является необязательным аргументом типа Long и, если он не включается в вызов функции, то по умолчанию имеет значение 0. В листинге 6.6 функция FlipCase использует необязательный аргумент с объявленным типом данных и присвоенным по умолча-
нию значением.	
^НРИМЕЧАНИЕ	Функция IsMissing предназначена только для проверки необязательных аргументов типа Variant. Для необязательных аргументов любого другого типа VBA выполняет инициализацию значений как для любой новой переменной (строки получают нулевую длину, а числовые типы - значение 0) и функция IsMissing всегда возвращает значение False. Для того чтобы определить, не пропущены ли необязательные аргументы определенного типа, следует провести проверку на нулевое значение или на нулевую длину строки, как показано в листинге 6.6.
Как VBA передает аргдмвнты
Вы уже знаете, как создавать процедуры-функции со стандартными и необязательными аргументами, и готовы ближе познакомиться с процессом передачи аргументов процедуре-функции. Вы узнаете, как управлять этим процессом и какой из процессов предпочтительнее использовать.
Передача аргументов по ссылке п по значению
VBA передает информацию в процедуру-функцию с помощью аргументов и делает это двумя способами: по ссылке и по значению. По умолчанию VBA передает функции все аргументы по ссылке. При передаче аргументов по ссылке VBA фактически передает адреса величин, указанных в списке аргументов при вызове функции. Это означает, что если функция в процессе работы изменит значение одного из своих аргументов, то и исходные данные, переданные в качестве этого аргумента, изменятся.
При передаче аргумента по значению, VBA делает копию исходных данных и передает функции эту копию для обработки. При изменении значения аргумента, переданного по значению, изменяется только переданная функции копия; исходные данные не изменяются. Передача данных по ссылке позволяет изменять исходные дан
День 6-Й. Процедуры-функции и нестандартные функции
193
ные, переданные функции через аргумент; а передача с помощью значения не позволяет изменять значение исходных данных.
Для того чтобы лучше понять разницу между передачей значений аргумента по ссылке и по значению, приведем такой пример: ваш коллега говорит вам, что в ящике его стола лежит документ, просит вас прочитать его и написать на полях ваши комментарии. Следуя его указаниям, вы берете документ из ящика стола, читаете и пишете на нем свои комментарии; после этого оставляете измененный документ в том же ящике, в котором нашли его. Таким образом, ваш сотрудник передал вам данные (документ) по ссылке. Вы получили ссылку на место нахождения данных и изменили эти данные.
Если, с другой стороны, ваш коллега вручит вам копию документа и попросит вас написать комментарии на копии исходного документа, он передаст вам данные по значению. Вы получите копию действительных данных (их значение), измените это значение и вернете измененную копию. Оригинал документа останется у вашего коллеги неизменным.
В связи с тем, что передача данных по ссылке позволяет изменять исходные значения аргументов, этот процесс может привести к нежелательному эффекту. В листинге 6.5 показана процедура, созданная для проверки функции FlipCase в листинге 6.4.
Листинг 6.5. Проверка процедуры н демонстрация ппПпчкых эффектов действия фдикции FlipCase
1:	Sub Test_FlipCase()
2:	Dim Userin As String, num As Long
3:	Userin = InputBox(prompt:“"Введите текст:",
4:	Title:“"Изменение регистра")
5:	num = 5
6:	MsgBox	FlipCase(Userin, num)
7:	MsgBox	Userin
8:	MsgBox	FlipCase(Userln)
9:	MsgBox	UserIn
10:	End Sub
В строке 2 объявляются переменные процедуры. Функция InputBox используется для того, чтобы предложить пользователю ввести некоторый текст. Строка 5 присваивает переменной Num число 5. Строка 6 вызывает функцию FlipCase с необязательным аргументом (со значением 5) и использует функцию MsgBox для вывода на экран результата функции FlipCase. Строка 7 показывает исходное значение — строку, хранящуюся в переменной Userin, — переданное функции FlipCase. Строка 8 вызывает функцию FlipCase без ее необязательного аргумента; а в строке 9 снова выводится на экран содержимое переменной Userin.
Если ввести текст листингов 6.4 и 6.5 и запустить процедуру Test_FlipCase, а затем ввести в качестве тестовой строки слово Изменение, можно получить интересный результат. В строке 6 листинга 6.5 VBA выполняет первый вызов функции FlipCase. Как и ожидалось, в результате выполнения функции на экран будет выведена строка иЗ-МЕНение: точнее, копия строки, переданной в аргументе Userin, в которой регистр первых пяти символов изменяется на противоположный (см. рис. 6.1). Однако в строке 7, на экран выводится исходная строка, введенная пользователем, и она тоже изменяется на иЗМЕНение (в строке 7 выводится окно, идентичное тому, что показано на рис. 6.1). Если снова посмотреть на листинг 6.4 (строки 19 и 24) можно увидеть, что функция FlipCase непосредственно изменила строку аргумента tStr. Поскольку
194
Неделя 1
аргумент tStr передается по ссылке (по умолчанию все аргументы передаются по ссылке), все изменения tStr являются фактическими изменениями исходных данных в переменной Userin процедуры Test_FlipCase.
Microsoft Excot
иЗМЕНенив m
Рис. 6.1. Строка 6 в листинге 6.5 выводит на экран это окно, показывающее, что функция FlipCase изменила переданную ей строку
При выполнении строк 8 и 9 из листинга 6.5 можно увидеть тот же процесс, но теперь уже меняется регистр всех символов строки: в результата выполнения функции возвращается строка ИзменЕНИЕ, однако и исходная строка, введенная пользователем, также изменилась на ИзменЕНИЕ.
Как указать споспб передачи аргументов
Очевидно, что подобные побочные эффекты, связанные с тем, что функция изменяет значение аргумента, чаще всего не нужны, а иногда и недопустимы. Побочные эффекты могут быть причиной серьезных проблем в программе. Часто довольно трудно отследить их причины, потому что не всегда бывает сразу ясна причина, по которой переменная изменяет значение. (Побочные эффекты — это общий термин; он относится к любому изменению в значении переменной или в среде выполнения программы, которое не планировалось заранее.)
Для того чтобы избежать подобных побочных эффектов, следует работать не с самим аргументом, а с его копией. Конечно, можно объявить в процедуре-функции локальную переменную и присвоить ей копию значения аргумента. Однако не стоит этим злоупотреблять, так как ввод еще одной переменной увеличивает объем резервируемой для функции памяти и объем работы, которую должен выполнить программист. Дополнительные переменные также усложняют программу, делая ее более трудной для понимания и исправления ошибок.
Для того чтобы быть уверенным, что функция работает только с копией аргумента, а не с ним самим, лучше передать аргумент по значению, а не по ссылке. (Запомните, передача по значению дает функции копию аргумента, т.е. его значение', а передача по ссылке — доступ к самому аргументу.)
Для того чтобы обозначить метод передачи аргумента — по значению или по ссылке, —перед этим аргументом следует вставить ключевое слово ByVai или ByRef. Как видно из их названий, ключевое слово ByVai определяет передачу аргумента по значению, a ByRef — по ссылке. (By value — по значению, by reference — по ссылке.)
Ниже приводится пример объявления функции FlipCase, в которой аргумент tStr передается по значению, а аргумент nChar — по ссылке.
Function FlipCase(ByVai tStr As String, Optional ByRef nChar) As String
Аргумент nChar можно без опасений передать по ссылке, потому что функция FlipCase не изменяет его, вернее, изменяет переменную nChar только тогда, когда этот необязательный аргумент опущен. Если в приведенном выше примере опустить ключевое слово ByRef перед аргументом nChar, то его значение все равно будет передано по ссылке, потому что по умолчанию все аргументы передаются по ссылке.
День 6-Й. Процедуры-функции и нестандартные функции
195
В листинге 6.6 значение необязательного аргумента tstr функции FlipCase передается по значению для того, чтобы избежать побочных эффектов.
ftucmuiia В.В. Исппльзоваиив непСязатедьных аргумеитоо и нередача аргдмеитпв но ссылке
1:	Function FlipCase(ByVai tstr As String,
2:	Optional ByRef nChar As Long = 0) As String
3:	'меняет регистр - верхний на нижний, нижний на верхний
4:	'первых nChar символов в строке tstr. Если nChar не задано,
5:	'меняет регистр всех символов
6:
7:	Dim k As Long 'счетчик цикла
8:	Dim TestC As String * 1
9:	If (nChar = 0) Then
10:	
И:	nChar = Len(tstr)
12:	End If
13:	For k = 1 To nChar
14:	
15:	TestC = MidftStr, k, 1)
16:	If (StrCompfTestC, "A", vbBinaryCompare) >= 0) And _
17:	(StrComp(TestC, “Z", vbBinaryCompare) <= 0) Xor
18:	(StrCompfTestC, "A", vbBinaryCompare) >= 0) And _
19:	(StrCompfTestC, "Я", vbBinaryCompare) <= 0) Then
20:	MidftStr, к, 1) = LCase(TestC)
21:	Elself (StrCompfTestC, "a”, vbBinaryCompare) >= 0)	And _
22:	(StrCompfTestC, "z", vbBinaryCompare) <= 0)	Xor _
23:	(StrCompfTestC, "a", vbBinaryCompare) >= 0)	And _
24:	(StrCompfTestC, "я", vbBinaryCompare) <= 0)	Then
25:	MidftStr, к, 1) = UCase(TestC)
26:	End If
27: Next к
28:	FlipCase = tStr
29: End Function
В листинге 6.6 приведен другой пример функции FlipCase, которая выполняется так же, как описывалось в листинге 6.4, за исключением строк 1 и 2 (объявление функции) и строки 10.
Из объявления функции FlipCase в строках 1 и 2 (обратите внимание на символ продолжения строки в конце строки 1) видно, что первый аргумент tstr — обязательный, передается по значению и должен иметь тип String. Второй аргумент является необязательным, передается по ссылке, должен иметь тип Long, и — если опускается при вызове функции — по умолчанию имеет значение 0. Как и в предыдущем примере, функция возвращает результат типа данных String.
Строку 10 необходимо изменить, потому что функция IsMissing работает только с необязательными аргументами типа Variant: IsMissing всегда возвращает значение False для необязательных аргументов с другим типом данных, отличным от Variant, независимо от того, был ли аргумент включен при вызове функции. Для того чтобы определить, указан ли аргумент nChar, необходимо проверить, присвоено ли ему значение по умолчанию, т.е. 0. (Это же значение VBA присвоит аргументу nChar, даже если оно не было определено по умолчанию.) Таким образом, в строке 10 листинга 6.6
196
Неделя 1
значение nChar сравнивается с 0; при положительном результате можно предположить, что аргумент nChar был при вызове опущен. Затем строка 11 присваивает аргументу nChar значение длины строки tStr, и далее функция выполняется, как описывается в листинге 6.4.
Если функцию FlipCase из листинга 6.4 изменить так, чтобы она соответствовала функции FlipCase из листинга 6.6, а затем снова выполнить процедуру TestJFlipCase, вы заметите, что функция FlipCase не изменяет исходную строку, введенную пользователем. Это объясняется тем, что аргумент tStr описывался с помощью ключевого слова ByVai.
Так надо
При написании процедуры-функции всегда внимательно проверяйте, не изменяет ли она значения аргументов. Добавляйте ключевое слово ByVai перед каждым аргументом, который процедура-функция изменяет в процессе выполнения.
Если вы сомневаетесь, как лучше передать аргумент: по ссылке или по значению, лучше передать его по значению; потому что при передаче по значению потери на объеме выделенной памяти и скорости не так существенны. К тому же передача аргумента по значению более эффективна (она упрощает программу), чем сохранение значения аргумента в локальной переменной.
Так не надо
Не объявляйте аргумент передаваемым по значению (с ключевым словом ByVai), если в процессе выполнения процедура-функция не изменяет этот аргумент. Передача аргументов по значению использует немного больше памяти и требует больше времени для выполнения, чем передача по ссылке, так как передача аргумента по значению указывает VBA, что нужно сделать копию данных аргумента.
Использование процедуры функции в Visual Basic for Application
Вы можете использовать свои собственные процедуры-функции в инструкциях VBA так же, как делаете в любой встроенной функции VBA; те же правила и преобразования, которые рассматривались на пятом занятии для применения встроенных функций, используются для применения ваших собственных процедур-функций. В строках 6 и 8 листинга 6.5 показан типичный вызов процедуры-функции.
Запомните, если вы не собираетесь игнорировать результат, то при вызове функции список аргументов следует заключать в круглые скобки. (Как и в случае встроенных функций, при необходимости вы также можете игнорировать результат написанных вами процедур-функций.)
Если при написании собственных процедур-функций вы хотите использовать поименованные аргументы, просто укажите в объявлении процедуры-функции имена из списка аргументов. Например, для использования поименованных аргументов при вызове функции SLen (листинг 6.3) следует использовать следующую инструкцию (AnyStr и MyString являются строковыми переменными):
AnyStr=SLen(tStr:=MyString)
День 6-й. Процедуры-функции и нестандартные функции
197
Для того чтобы использовать поименованные аргументы в вызове функции FlipCase в листинге 6.4 или листинге 6.6, следует использовать следующую инструкцию (AnyStr и User In являются строковыми переменными; AnyNum переменной целого типа).
AnyStr=FlipCase(tStr:=UserIn, nChar:=AnyNum)
Если вы не в состоянии запомнить все поименованные аргументы для своей функции, убедитесь, что вы включили свойство Auto Quick Info. Если оно включено, редактор VBA показывает всплывающую подсказку с перечнем аргументов вашей функции, так же как это было в случае встроенных функций. На рис. 6.2 показано окно Code Window, отражающее действия пользователя в процессе ввода функции FlipCase из листинга 6.6. Обратите внимание, что всплывающая подсказка отражает имя функции, список ее поименованных аргументов и тип результата функции. Вы можете включить или отключить свойство Auto Quick Info в диалоговом окне Options, выбрав из меню панели инструментов Tools-Options; щелкнуть на вкладке Editor и установить или убрать флажок на свойстве Auto Quick Info.
Рис. 6.2. Свойство Auto Quick Info показывает всплывающую подсказку, в которой список аргументов функций выводится как для встроенных функций VBA.(FC)
Исиользование окна Object Browser для поиска вашей ироцвддры функции
Если вы не запомнили, в каком модуле сохранили определенную функцию, можно использовать окно Object Browser для просмотра доступных в данный момент процедур-функций. В этом окне вы можете просмотреть исходный текст функции.
Для того чтобы просмотреть функции с помощью окна Object Browser, следует выбрать из меню панели инструментов View-Object Browser или щелкнуть на кнопке окна Object Browser на панели инструментов редактора VBA.
Вы уже знаете, как использовать окно Object Browser для просмотра доступных функций прикладных систем VBA или Excel. Наряду с элементом VBA, выпадающий список Project/Library показывает все текущие открытые файлы. Для того чтобы уви
198
Неделя 1
деть нестандартные функции (или другие процедуры) отдельного проекта VBA, выполните следующую последовательность действий.
1.	Выберите имя проекта в выпадающем списке Project/Library. (Обратите внимание, что на рис. 6.3 окно Project Explorer показывает, что проект Day06 хранится в файле рабочего листа Day06.xls; Project Explorer всегда показывает — после имени проекта в скобках — имя файла, в котором хранился проект.)
После того, как название проекта выбрано из списка Project/Library, список Classes содержит перечень всех модулей и других проектов, содержащихся в выбранном проекте.
Microsoft Visual Basic - ДеньОб.х!»
; Efe Edit Yw Insert Ffltmst Eebug Bun lootej&dd-lns ajindow Help
I ® 3-0 • * la	7 Vek’l ЫЙ?"'1 C?H

Exera$e04 Exercise05 ListingOl Listing02 Listmg03
Usting04
Listing06 Listjna07
|0ау06
Members ot ’ListingOS1
« Test_FlipCase
ш Object Browser
FlipCase
Ж
К Classes
I о <globals>
: В ExerciseM
I Exercise03
_J| «S Exercise04 < «8 ExerciseOS
I -f—'Г	ListingOl
—1—1 T-	Listing02
|П| x|| «« Listing03
Public Function FlipCa»e(8yya/fSfMs String, [nChar As Lotk?= U]) As String
Msznber of ПауГИ. i istirglii;
|listing05 Module Alphabetic | categoric
(Name)!
;ListingO5
«3 Listing04 «£ ListingOS «8 Listing06 ListingOT «St ListingOS «3 Sheet!
*3 Sheet2
jPuc. 6.3. Окно Object Browser служит для поиска нестандартных функций и процедур VBA и для просмотра исходного текста функции или процедуры
2.	Выберите модуль в списке Classes. На рис. 6.3 показан выбранный модуль с именем Listing06.
После того как выбран модуль из списка Classes, список Members of <class> содержит перечень всех процедур и функций, объявленных в выбранном модуле.
3.	Выберите функцию или процедуру, чей исходный текст вы хотите просмотреть в списке Members of <class>. При выборе одной из функций или процедур, станет доступна кнопка Show в окне Object Browser.
4.	Щелчок на кнопке Show (Показать) покажет исходный текст процедуры или функции. Редактор VB откроет соответствующий модуль в режиме редактирования с позицией курсора на первой строке после объявления процедуры или функции.
День 6-Й. Процедуры-функции и нестандартные функции
199
ПРИМЕЧАНИЕ
Как и в случае встроенных функций VBA, можно также копировать текст программы функций и процедур из окна Object Browser в буфер Windows. На уроке 5-го дня занятий рассказывалось, как копировать текст из окна Object Browser в буфер Windows.
Так надо
Запомните, что кнопка Show в окне Object Browser становится доступна, только если доступен исходный текст для выбранной функции или процедуры.
Запомните, что функцию можно выполнить, только вызвав ее в инструкции VBA, из главного приложения (как, например, использование функции в рабочем листе Excel) или в окне отладки редактора VB. (Использование окна отладки будет рассмотрено на уроке 15-го дня занятий.)
Так не надо
Не пытайтесь для выполнения процедуры-функции использовать диалоговое окно Макрос приложения Excd, оно открывается при выборе из меню панели инструментов Сервис-Макросы. Предполагается, что процедура-функция, как правило, возвращает определенный результат, а выполнение функции вне программы не имеет смысла. Поэтому в списке Имя Макроса диалогового окна Макрос процедуры-функции не приводятся.
Вставка описания процедуры-функции в окне Object Browser
Снова обратимся к рис. 6.3: в списке Members of <class> выбрана функция FlipCase из листинга 6.6. Обратите внимание, что (так же как и в случае встроенных функций VBA и функций прикладных систем) в нижней части окна Object Browser — под списками Classes и Members — показаны имя функции и все ее аргументы. Однако в отличие от встроенных функций VBA здесь отсутствует описание назначения функции. Для описания любой написанной вами функции или процедуры можно использовать окно Object Browser.
Описание функции или процедуры может оказать помощь пользователю программы. Например, описание функции, созданное с помощью окна Object Browser, появится не только в этом окне, но и в окне Мастер функций в Excel. Ознакомившись с описанием процедуры-функции, пользователь сможет воспользоваться этой функцией в формуле ячейки рабочего листа.
Для того чтобы добавить описание для любой написанной вами функции в процедуру-функцию, следует придерживаться приведенной ниже последовательности действий.
1.	Выбрать процедуру-функцию в окне Object Browser, как описывалось выше.
2.	Щелкнуть правой кнопкой мыши на функции, к которой хотите добавить описание, и выбрать Properties из выпадающего меню. Редактор VB покажет окно Member Options, изображенное на рис. 6.4.
3.	Ввести описание функции (или процедуры) в текстовом окне Description. На рис. 6.4 показано описание функции FlipCase, введенное в текстовое окно Description.
4.	Щелкнуть на кнопке ОК. Окно Object Browser закроет диалоговое окно Member Options, и вы вернетесь в окно Object Browser.
На рис. 6.5 показано, как выглядит окно Object Browser после добавления описания функции (показанное на рис. 6.4). Обратите внимание, что имя функции и список ее аргументов в нижней части окна Object Browser теперь включает введенное описание для функции FlipCase.
200
Неделя 1
МдтЬрг Option’;	I***
n»«« ;₽ijc2e * * ’*	r';	-	j	/ж |
zC’to "-par »	*	1
nChar символов e строке tStr	"vj
He|pF№> ___________ на|&	&	•
1	- .... j**J
Puc. 6.4. Введите дополнительное описание для созданных вами функций и процедур в диалоговом окне Member Options
'« Object Browser
ЯР1И1
|0ау06
Classes <
• о <globals>
•St ExerciseOl •St Exerci«e03 •St Exercl«e04 «St ExercIseOS
•St UstlngOI
•St Listlng02
•St Li«tlng03
•St Llstlna04
•St LlstlngOS •StfListingOe'” •St LictingOZ
•St ListingOS 83 Sheetl 83 Sheet?
83 Sheets
Membe's of 'usangOB'
FlipCase
♦ Test_FllpCas«
: Public Function FlipCas*(.9yVai tStr As String, [nChar As Long - 0]) As Stnng * Member of Ряйб I H(rr-i,(isi	—
Меняет регистр первых nChar симролсб а строке :Str	w
Рис. 6.5. Описание, которое вы добавили в диалоговое окно Member Options, появилось в окне Object Browser
Использование определенных пользователем функций в рабочих листах Excel
Для того чтобы использовать определенные пользователем функции (UDF, user defined functions, или, для краткости, нестандартные) в приложении Excel, следует ввести имя функции и список ее аргументов в ячейку' рабочего листа так же, как вводится любая встроенная функция. VBA даже позволяет использовать ваши нестандартные функции в Excel 2000. (Запомните, нестандартная функция — это та же процедура-функция, которая при использовании в главном приложении должна соответствовать определенным требованиям; в данном случае этим приложением является Excel.)
День 6-Й. Процедуры-функции и нестандартные функции
201
Если вы не помните имя нестандартной функции или ее аргументов, можно найти нестандартные функции в категории Определенные пользователем в диалоговом окне Excel Мастер функций. (Вывести диалоговое окно Мастер функций можно, если щелкнуть на кнопке Вставить функцию на панели инструментов, затем выбрать Вставить-Функцию из меню.)
На рис. 6.6 показано диалоговое окно Мастер функций. В списке Категория выделена группа Определенные пользователем, а в списке Функция выбрана функция Flip-Case. (Для получения более полной информации об использовании мастера функций обратитесь к справочной системе или документации по Excel.) На рис. 6.6 перед именем функции указано название модуля, потому что проект может содержать более одного модуля, в котором находится функция с этим же именем. Скорее всего, в вашем случае список Функция диалогового окна Мастер функций будет содержать только имя функции. Обратите внимание, что имя функции FlipCase, вместе с описанием, которое вы ввели для этой нестандартной функции (как описывалось в предыдущем разделе этой главы), отображается в нижней части окна Мастер функций.
Мастер функций - шаг 1 из 2
К?’вгимя-
В
9
Полный алфавитный перечень Финансовые
Дата и время Математические Статистические Ссылки и массивы Работа с базой данных Т екстовые Логические
Проверка свойств и значений
Inch2Cm
ListingOl.SLen
ListingO2.SLen
PcntProfit Power День0б.хЬ!51еп uEQV □IMP
Yds2Inch
Определенные попьзова! елем
iJstingOG Л pCase
Меняет регистр первых nCher симзолав 8 строке tStr>
j ок | отмен» {
Puc. 6.6. Вставка нестандартной функции в ячейку рабочего листа с помощью диалогового окна Мастер функций
Из диалогового окна Мастер функций нестандартную функцию можно вставить как обычную встроенную функцию Excel и при необходимости подставить значения для аргументов функции.
Так надо
Используйте окно Object Browser для того, чтобы описать нестандартную функцию, особенно при разработке в VBA функций специального назначения для пользователей прикладной системы Excel.
Если в ячейке рабочего листа при использовании нестандартных функций появляется ошибочное значение HVALUE!, следует тщательно проверить текст процедуры-функции, чтобы убедиться в том, что ни одно из правил использования нестандартных функций не нарушено. Ни Excel, ни VBA не запрещает использовать в ячейке рабочего листа процедуры-функции, которые нарушают эти правила. Но, если процедура-функция написана с нарушением этих правил, VBA не выполнит функцию, вместо этого он вернет ошибочное значение #VALUE! в ячейке Excel.
202
Неделя 1
Создание процедур фднкциО
При написании функции следует помнить, что ее основное назначение —вернуть некоторое значение, результат, поэтому процедуры-функции должны быть простыми и полностью соответствовать поставленной задаче. Как правило, тело функции должно содержать только текст, необходимый для выполнения вычислений или обработки данных; функция никогда не должна выполнять операции, не связанные непосредственно с получением результата.
Можно считать, что имеет смысл браться за написание функции, если вы воспользовались одной формулой более двух-трех раз. Как видно из примеров в этой главе, процедуры-функции также полезны для обработки нечисловых данных. Если одно и то же выражение или группа выражений используется более двух-трех раз, можно смело приступать к написанию функции.
Даже когда выражение для вычисления получается очень простым, все равно следует написать процедуру-функцию, если это выражение вы используете часто. В результате написания процедуры-функции снижается вероятность опечатки при вводе выражения и выражению присваивается имя. Например, достаточно часто приходится использовать выражение даты, которое вычисляет дату оплаты счета на основании даты выписки счета (скажем, Now+ЗО). Чтобы быть уверенным, что всякий раз при выписке счета будет происходить аналогичное вычисление и текст программы будет простым и понятным, можно написать функцию с именем InvoiceDue, которая в качестве результата будет возвращать искомую дату оплаты.
Несмотря на то что результатом процедуры-функции можно пренебречь, лучше написать функцию таким образом, чтобы избежать игнорирования результата. Это еще одна причина, по которой функции не следует выполнять дополнительных действий, кроме возврата необходимого результата.
Так надо
В качестве образца процедуры-функции можно привести пример таких функций VBA, как StrComp, Mid, функций преобразования данных CStr и CDbl и т.д. Каждая из этих функций выполняет простую задачу и возвращает простой результат, не изменяя исходные данные, хранящиеся в любом из передаваемых ей аргументов.
Так не над
В качестве образца при написании функций не следует пользоваться функцией MsgBox. Эта функция выполняет в VBA уникальные операции; и, следовательно, выполняет больший объем работы и имеет большее количество возможных вариантов, чем обычно выполняют и имеют функции.
Иногда при написании функции возникает соблазн сделать так, чтобы она как бы возвращала более одного значения, передавая ей аргументы по ссылке с тем, чтобы функция меняла их. Не делайте этого. Функция никогда не должна изменять переданные ей аргументы. Как будет рассмотрено на уроке 12-го дня обучения, существуют обстоятельства, при которых можно — и нужно — изменить значения в передаваемых по ссылке аргументах; в таких случаях следует использовать процедуру, а не функцию. Поэтому необходимо убедиться, что функции не изменяют аргументов, переданных по ссылке. При необходимости для передачи аргумента по значению следует использовать ключевое слово ByVai.
Иногда может оказаться, что написанные вами функции будут использоваться в программах или рабочих листах не совсем так, как было задумано при их написании.
День 6-й. Процедуры-функции и нестандартные функции
203
Например, функции Slen и FlipCase, о которых шла речь в этой главе, являются функциями общего назначения; их можно использовать в различных выражениях в разных процедурах.
Процедуры-функции, находящиеся в одном приложении, доступны для любого открытого документа этого приложения, поэтому можно собрать функции общего назначения в одном проекте и создать библиотеку функций. Например, в прикладной системе Excel можно собрать все процедуры-функции общего назначения в одном файле и назвать его MyFunctions.xls. Создание библиотек функций и процедур описано при изложении материала 12-го дня занятий.
Так надо
,Дважды проверьте каждую написанную функцию, чтобы убедиться, что она содержит присвоение результата функции. Таким образом, вы будете уверены, что функция не вернет по умолчанию пустое значение типа Variant, строку нулевой длины String или просто нулевое значение.	.
Избегайте побочных эффектов: каждый аргумент, значение которого изменяется в процессе выполнения про-> цедуры-функции, описывайте с помощью ключевого слова ByVai.
Если вы собираетесь использовать функции в ячейках рабочего листа Excel, убедитесь, что написанная функ-> ция соответствует всем правилам нестандартных функций, которые рассматривались в начале этой главы.
Разработка функции для Excel
При написании процедуры-функции для рабочих листов Excel, помимо общих требований к написанию нестандартных функций, следует обратить внимание на такие особенности.
•	Нестандартные функции для приложения Excel не должны иметь имена, напоминающие ссылки на ячейки, например А1 или R2C3.
•	Любые текстовые данные, возвращаемые из VBA в Excel, не должны превышать в длину 255 символов. Если нестандартная функция возвращает в ячейку строку длиной более 255 символов, то перед тем, как вставить строку в ячейку рабочего листа, Excel отсекает ее до максимальной длины в 255 символов.
•	При написании нестандартной функции для Excel, которая возвращает значение даты, убедитесь, что результату функции присваивается тип данных Date. Формат даты в ячейке рабочего листа Excel соотносится с результатом функции только в том случае, если он имеет тип данных Date (приложение Excel автоматически преобразует VBA-тип Date).
Есть еще одно обстоятельство, касающееся особенностей применения нестандартных функций в Excel. Оно относится к вызову нестандартной функции для вычисления или пересчета формулы в ячейке рабочего листа, в которую входит эта функция.
По умолчанию Excel вызывает нестандартную функцию для пересчета формулы в ячейке рабочего листа каждый раз при изменении аргумента функции; таким образом, Excel определяет, когда нужно пересчитать любую формулу рабочего листа. Однако можно задать нестандартную функцию таким образом, чтобы ее значение пересчитывалось каждый раз при пересчете любой ячейки рабочего листа.
Когда значение аргументов функции не связано со значениями других ячеек рабочего листа, можно задать нестандартную функцию как тип Volatile. Допустим, имеется нестандартная функция с именем PentProfit, которая вычисляет процент прибыли. Функция имеет два аргумента; один из них определяет ячейку рабочего листа, которая содержит значение общего дохода, а другой — ячейку, которая содержит общую стои-
204
Неделя 1
мосты Поскольку аргумент функции определяет координаты ячейки, изменение содержания ячейки не изменит значения аргумента функции PentProfit. Если в ячейке изменить значение общей стоимости или общего дохода, Excel не будет пересчитывать функцию PentProfit; и на экране будет показан ошибочный результат. Однако если задать эту нестандартную функцию как тип Volatile, она всегда будет показывать правильный результат, потому что Excel будет пересчитывать ее каждый раз при изменении любой ячейки рабочего листа.
Нестандартная функция, которая пересчитывается каждый раз при изменении любой ячейки рабочего листа, называются функцией типа Volatile. Для того чтобы задать нестандартную функцию такого типа, сразу после ее объявления нужно добавить следующую инструкцию.
Application.Volatile
Эта инструкция задает нестандартную функцию как тип Volatile и представляет собой тип процедуры (называемой методом), которая принадлежит Excel. Значит, при использовании метода Volatile объект Application нужно определить так же, как определяется объект Application при использовании функции Excel в инструкции VBA. В листинге 6.7 приведен пример функции типа Volatile с именем PentProfit, которая возвращает число, представляющее процент дохода, полученного при заданных значениях общих продаж и стоимости.
Лисшмна 6.7. Нестандартная функция тина Volatile в приложении Excel
1:	Function PentProfit(grSales As Currency,
2:	netCosts As Currency) As Currency
3:	Application.Volatile
4:	PentProfit = ((grSales - netCosts) / netCosts) * 100
5:	End Function
Строки 1 и 2 содержат объявление функции PentProfit. (Обратите внимание на символ продолжения строки в конце строки 1; объявление функции занимает две строки для наглядности.) Функция PentProfit имеет два обязательных аргумента, grSales и netCosts. Тип каждого аргумента объявлен как тип Currency. В связи с тем что метод передачи аргументов не определен, оба аргумента передаются по ссылке — этот метод используется по умолчанию. В объявлении функции PentProfit указывается, что результату функции присваивается тип данных Currency. Для аргументов функции и ее результата также выбран тип данных Currency, потому что функция создана специально для финансовых вычислений.
Строка 3 содержит необходимую инструкцию Application.Volatile; это первая инструкция после объявления функции. Инструкция Application.Volatile “регистрирует” нестандартную функцию в системе Excel. Теперь каждый раз при пересчете любой ячейки рабочего листа формула, содержащая эту функцию, будет пересчитываться.
Так надо
Метод Application .Volatile следует использовать только при необходимости. Если объявить все функции рабочего листа типа Volatile, это может привести к ненужному пересчету, что увеличит общее время, необходимое для пересчета рабочего листа.
День 6-Й. Процедуры-функции и нестандартные функции
205
Понятие о рекурсии
Перед тем как завершить этот урок, введем еще одно понятие, которое важно для понимания, хотя и не относится непосредственно к созданию процедур-функций VBA.
Существует понятие рекурсивной функции или процедуры, которая может вызывать саму себя. Практически во всех случаях рекурсия является ошибкой программирования, при которой программа зависает, или не выполняется, — наиболее частым результатом нежелательной рекурсии является отсутствие свободной памяти или отсутствие стекового пространства. (Стеком называется временная рабочая область памяти компьютера. VBA использует эту область памяти для хранения временных результатов выражений, копий аргументов функции, передаваемых по значению, результатов процедуры-функции, а также в любой другой промежуток времени, когда необходимо использовать временное рабочее пространство. Если процедуры требуют много памяти в процессе работы, они могут полностью израсходовать это пространство).
Анализ работы рекурсивной функции
В листинге 6.8 показана функция с именем Power, которая возвращает число, возведенное в степень, заданную вторым аргументом.
Амсшмиа 6.8, Шдикция Power: нрцмер рекцрсив
1:	Function Power(num As Double, pwr As Integer) As Double
2:	'с помощью рекурсии возводит число num в степень pwr
3:	If pwr = 0 Then
4:	Power = 1	'конец рекорсии
5:	Else
6:	Power = num * Power(num, pwr - 1) 'рекурсия
7:	End If
8:	End Function
Строка 1 содержит объявление функции Power. Она имеет два обязательных аргумента, num и pwr, и возвращает результат типа Double.
Внимательно посмотрите на строку 6 и обратите внимание, что в этой строке функция вызывает саму себя. Функция Power выполняется таким образом, что число, возводимое в степень п, имеет то же значение, что число, умноженное на себя в степени п-1. Например, 23— то же самое, что 2x2м или 2х22.
Для того чтобы понять, как выполняется функция Power, предположим, на экран выводится результат возведения числа 2 в степень 3, т.е. вызывается функция со следующими инструкциями.
MsgBox Power (2,3)
При первом вызове функции аргументу num соответствует значение 2, a pwr — значение 3. Строка 3 начинается с инструкции If...Then, которая проверяет, не пора ли прекратить рекурсию. Эта строка проверяет условие равенства аргумента pwr значению 0. В данном случае значение pwr равно 3, и управление передается в строку 6.
Строка 6 содержит присвоение функции. Выражение, присваиваемое результату функции Power, говорит о том, что результат функции равен значению аргумента num, умноженному на результат вызова функции Power снова, где второму аргументу передается значение pwr-1.
206
Неделя 1
Если вместо буквенных значений в выражение подставить числа, то вызов функции Power в строке 6 будет выглядеть следующим образом.
Power (2,3-1)
При втором вызове функции Power значение аргумента num остается прежним — 2, а значение аргумента pwr становится равным 2 (3-1=2), а не 3. И снова в строке 3 проверяется условие равенства аргумента pwr и 0; если он не равен 0, VBA снова выполняет присваивание функции в строке 6. Выражение в присваивании функции снова вызывает функцию Power, и второму аргументу вновь передается значение pwr-1.
Если в строке 6 вместо буквенных значений в выражение опять подставить числа, то вызов функции Power будет выглядеть следующим образом.
Power (2,2-1)
И опять-таки, при третьем вызове функции Power аргумент num равен 2, а аргумент pwr теперь равен 1 (2-1=1). Строка 3, как и прежде, проверяет значение аргумента pwr, он по-прежнему не равен 0, и функция присваивания в строке 6 выполняется в третий раз, вызывая в четвертый раз функцию Power.
В четвертом вызове функции Power аргумент pwr равен 0 (1-1=0), когда строка 3 проверяет значение аргумента pwr на равенство нулю, результат сравнения оказывается истинным, и VBA передает выполнение строке 4 функции Power, где рекурсия заканчивается. Строка 4 является другой инструкцией присвоения, результату функции присваивается значение 1. (Любое число, возведенное в степень 0, является 1.)
При первом, втором и третьем вызове функция Power не возвращает результат, потому что VBA не может вычислить выражение в инструкции присваивания функции (строка 6), пока серия рекурсивных вызовов функции не завершится на четвертом вызове функции Power. Теперь, когда функция Power возвращает результат, процесс рекурсии завершается и при каждом отдельном вызове функция Power возвращает соответствующий результат.
При четвертом вызове функция Power возвращает число 1. В строке 6 оно подставляется в выражение при третьем вызове функции. Подставляем вместо буквенного выражения числовые значения, и выражение принимает вид 2x1. Поэтому результатом третьего вызова функции Power становится значение 2.
В свою очередь, VBA подставляет значение 2 в выражение присваивания при втором вызове функции Power и (подставляем вместо буквенного выражения) это выражение становится равным 2x2. Таким образом, полученный при втором вызове функции Power результат 4, подставляется в выражение присваивания при первом вызове функции Power.
Снова подставляем этот результат вместо буквенного выражения, и выражение при первом вызове функции Power теперь равно 2x4. Наконец, исходный, первый вызов функции Power возвращает значение 8, которое является результатом возведения числа 2 в третью степень. Ниже приводится перечень операций, который подводит итог результатам функции, полученным при каждом отдельном вызове функции Power.
1	4-й	вызов
2x1=2	3-й	вызов
2x2=4	2-й	вызов
2x4=8	1-й	вызов
Вам нет необходимости писать функцию Power; оператор возведения в степень (л) выполняет те же действия. Функция Power была выбрана для того, чтобы наглядно продемонстрировать рекурсию и потому что она является одним из самых простых примеров выполнения рекурсии.
День 6-й. Процедуры-функции и нестандартные функции
207
ПРИМЕЧАНИЕ
Функция Power имеет один недостаток: она не будет выполняться, если попытаться возвести число в отрицательную степень. Если степень имеет отрицательное значение, то рекурсия будет происходить бесконечно, т.е. функция будет вызывать себя бесконечное число раз. (Значение аргумента pwd всегда будет меньше 0; вычитание единицы будет только увеличивать значение отрицательного числа, и оно никогда не будет равно 0.) Если вам нравится рисковать, можете вызвать функцию Power с отрицательным значением аргумента pwd и посмотреть на результаты бесконечной рекурсии.
Как избежать нежелательной рекурсии, а также других проблем
Теперь, когда вы немного разобрались в механизме происхождения рекурсии, вы сможете понять ее недостатки и то, как обычно проявляется рекурсия при ошибках программирования.
Одним из очевидных недостатков рекурсивных функций и процедур является то, что механизм их действия не так-то просто понять. Другой недостаток — то, что они могут использовать большой объем памяти: каждый раз, когда рекурсивная функция вызывает себя, VBA заново передает все ее аргументы и в памяти снова резервируется место для результата функции. Количество памяти, которое используется серией вызовов рекурсивной функции, может быть очень большим в зависимости от размера аргументов (в байтах) и размера возвращаемого результата (в байтах).
Практически любую задачу, которую можно выполнить посредством рекурсивной функции, с таким же успехом можно выполнить с помощью циклической структуры, не требующей дополнительного объема памяти. К тому же циклическая структура проще для понимания, чем рекурсивная функция. (Циклические структуры будут рассмотрены на девятом занятии.)
Непреднамеренная рекурсия — одна из наиболее распространенных ошибок программирования, особенно у начинающих программистов. При создании функции легко запутаться и написать инструкцию, которая вместо того, чтобы присвоить функции результат, приводит к ее рекурсивному вызову. Так же при указании результата функции можно легко забыть, что имя функции — это не переменная, и попытаться использовать имя функции как имя переменной. Такие попытки обычно и заканчиваются случайным вызовом рекурсии.
Если вы ненароком создали рекурсивную процедуру-функцию, маловероятно, что рекурсия закончится сама по себе, потому что рекурсивные функции должны быть тщательно отлажены таким образом, чтобы при выполнении определенных условиях вызов функции прерывался. Если условия прерывания рекурсии не проверены — как, например, в функции Power — вызов рекурсивной функции будет продолжаться до тех пор, пока хватит ресурсов памяти, а затем на экране появится предупреждение об ошибке времени выполнения.
Обычно при выполнении функции, которая содержит непреднамеренный рекурсивный вызов, компьютер на некоторое время как бы приостанавливает работу, а затем появляется сообщение о недостатке памяти.
Резюме
В сегодняшнем уроке вы узнали, как создавать процедуры-функции, и познакомились с определенными ограничениями, о которых нужно знать при создании и использовании процедур в Excel или в другой прикладной системе. Вы узнали, как объявить определенный тип данных для результата функции, как объявить тип данных
208
Неделя 1
для аргументов функций и как создать функции с необязательными аргументами. На этом занятии мы выяснили, чем отличаются методы передачи аргументов по ссылке и по значению и какой метод использовать для соответствующих аргументов.
Вы также научились использовать процедуры-функции в инструкциях VBA и рабочих листах Excel. Вы узнали, как использовать окно Object Browser для того, чтобы описать функцию и получить некоторые советы и рекомендации по созданию процедур-функций в прикладных системах VBA и Excel.
И наконец, теперь, ознакомившись с примером рекурсивной процедуры-функции, вы знаете, что представляет собой рекурсивная функция и как она работает. Вы узнали, что рекурсия является непростой техникой программирования, и получили советы, как избежать случайной рекурсии.
Вопросы о ответы
Я не могу найти свою процедуру-функцию в окне Object Browser.
Наверное, вы ищете ее не в том модуле. Используйте окно Object Browser (см. главу 2), чтобы найти имя определенной функции. Можно также посмотреть в другом модуле в списке Classes окна Object Browser. Если и после этого вы не можете найти свою функцию, вероятно, она находится в другом проекте. Выберите другой проект в выпадающем списке Projec/Library или выберите раздел All Libraries в выпадающем списке Projec/Library. Если вы не можете найти функцию, которую ищете, она может находиться в проекте, который в данный момент не открыт. Запомните, что окно Object Browser показывает только перечень модулей и функций в открытых проектах
В диалогом окне Excel Мастер функций отсутствует описание моей процедуры-функции.
Если в диалоговом окне Мастер функций отсутствует описание вашей процедуры-функции, это означает, что вы не ввели ее описание в окне Object Browser. Если вы хотите, чтобы описание вашей процедуры-функции появлялось в окне Мастер функций, следуйте инструкциям, которые даны в конце этой главы в разделе “Использование процедуры-функции в Visual Basic for Application”
Действительно ли я должен понимать, как работает рекурсия? Она кажется довольно загадочной.
Важно понимать, что такое рекурсия и как выглядит вызов рекурсивной функции для того, чтобы избежать рекурсии или определить случайный вызов рекурсивной функции. Однако не обязательно, чтобы вы сразу смогли написать рекурсивную функцию. Использование рекурсии — это утонченная техника программирования; тем не менее, существует несколько задач, которые не могут быть решены более очевидным или более простым способом
Я написал функцию, но не уверен, что она возвращает правильный результат. Как проверить правильность ее выполнения?
Перед тем как использовать функцию, обязательно проверьте правильность ее выполнения. Для проверки функции напишите процедуру для вызова функции и вывода ее результата на экран. Эта процедура должна вычислять значения функции для определенных значений аргументов, для которых вы вручную вычислили правильные ответы. Далее вычислите значения функции для значений аргументов в крайних точках действительного для функции диапазона. Например, если функция имеет аргумент типа данных Single, проверьте значения функции для наименьшего и наибольшего возможных чисел, которые поддерживает этот тип данных.
Если в обоих случаях вы получите правильные результаты и избежите сообщения об ошибке времени выполнения, вероятно, функция работает корректно.
В качестве образца выполнения процедуры-функции, которая выполняет проверку, можно привести пример процедуры, содержащейся в листинге 6.5. Обратите внима
День 6-й. Процедуры-функции и нестандартные функции	209
ние, что эта процедура выводит на экран не только значение результата функции, но и значения исходных аргументов после каждого вызова функции для того, чтобы проверить были ли аргументы функции случайно изменены. Обратите также внимание, что процедура проводит проверку функции как с необязательными аргументами, так и без них. Если у функции имеются необязательные аргументы, проверьте их поведение во всех возможных комбинациях (при их наличии или отсутствии).
Я понимаю, как выполняется функция FlipCase, ио я не уверен, что понимаю, как работают структуры For...Next и If...Then.
Как вы уже, наверное, поняли, мало проку от функции, которая не может проверить то или иное условие. Если вы понимаете основные действия функции и, в частности, инструкции присваивания, все идет по плану. На все вопросы, которые у вас возникли в связи со структурами For.. .Next и If.. .Then, вы получите ответы на уроках 8-го и 9-го дней занятий.
Коллоквиум
Ответы см. в приложении.
Тест
1.	Чем отличается процедура-функция от любой другой процедуры, которую вы создаете?
2.	Чем отличается процедура-функция от нестандартной функции?
3.	Каким требованиям должна удовлетворять процедура-функция?
4.	Что такое присваивание результата функции? Может ли процедура-функция содержать более одного присваивания?
5.	Что такое рекурсия?
6.	В каких случаях следует использовать рекурсию?
7.	В каких случаях следует использовать функцию isMissing?
8.	Может ли функция IsMissing работать с необязательными аргументами типа данных отличного от типа Variant?
9.	Почему в функции FlipCase используется функция StrComp?
10.	Когда (и почему) следует передавать аргументы функции по значению?
11.	Какие команды вы будете использовать, если хотите найти определенную процедуру-функцию: Сервис-Макрос в Excel или View-Object Browser в VBA? Почему?
Упражнения
1.	Как отмечалось на предыдущем уроке, в приложении Excel нет встроенных функций, соответствующих операторам VBA Xor, Eqv или Imp (однако имеются встроенные эквиваленты операторов And, Or и Not). Приведенный ниже листинг демонстрирует пример процедуры-функции, содержащей эквивалент оператора VBA Хог, и эта функция может использоваться в ячейке рабочего листа Excel.
Function uXOR (LI As Boolean, L2 As Boolean) As Boolean
uXOR=Ll Xor L2
210
Неделя 1
End Function
Функция называется uXOR (от “user Xor”). Запомните, функции нельзя давать имя, которое дублирует ключевые слова VBA, как, например, Хог.
Напишите две функции, одну с именем uEQV, вторую — с именем uIMP, соответствующие функциям VBA Eqv и Imp, которые можно использовать в Excel.
2.	С помощью окна Object Browser добавьте описание для процедуры-функции, написанной в первом примере.
3.	Напишите функцию с именем Yds2Inch для преобразования ярдов в дюймы и процедуру проверки этой функции. Yds2Inch должна иметь простой аргумент, значение которого будет измеряться в ярдах, и возвращать значение, измеряемое в дюймах. (Подсказка. Поскольку в ярде содержится 36 дюймов, умножьте количество ярдов на 36, и получите количество дюймов.)
4.	Напишите функцию с именем Inch2Cm для преобразования дюймов в сантиметры и процедуру проверки этой функции. Inch2Cm должна иметь простой аргумент, значение которого будет измеряться в дюймах, и возвращать значение, измеряемое в сантиметрах. (Подсказка. Для того чтобы получить сантиметры, умножьте количество дюймов на 2.54.)
5.	Напишите процедуру, которая использует функцию InputBox — чтобы пользователь ввел число, соответствующее количеству ярдов, — и выводит на экран значение, эквивалентное количеству содержащихся в нем сантиметров. Для преобразования ярдов в сантиметры используйте две функции Yds2Inch и Inch2Cm из упражнений 3 и 4.
День 6-й. Процедуры-функции и нестандартные функции
211
✓**! "jil
День / и
Объекты и коллекции
На уроке 1-го дня наших занятий мы рассказывали об истории возникновения языка Visual Basic for Applications и, в частности, о том, что он произошел от языков макросов для таких программ, как Microsoft Word и Excel. Язык VBA, унаследовав все свойства языка макросов, дает возможность управлять поведением приложения, для которого вы пишете свои процедуры. Это достигается через доступ к объектам приложения. Сегодня мы рассмотрим следующие вопросы.
•	Что такое объект, его методы и свойства.
•	Что такое тип данных Object и как использовать объектные переменные в выражениях.
•	Как пользоваться свойствами и методами объекта в VBA-программах.
•	Что такое коллекция объектов и как объект включает в себя другие объекты.
•	Как пользоваться коллекциями и контейнерами.
•	Как пользоваться окном Object Browser при работе с объектами, их свойствами и методами.
Понятие об объектах
В середине 80-х в программировании появилась новая концепция — объектно-ориентированное программирование (ООП). Со временем эта концепция стала необычайно популярной; например, всем известный рабочий стол Windows воплощает в себе многие принципы объектно-ориентированного подхода. Основная идея такого подхода состоит в том, что программное приложение, как и окружающий нас мир, должно состоять из отдельных объектов, каждый из которых обладает особыми свойствами и отличается своим поведением.
Объектно-ориентированное приложение объединяет исполняемый код программы и ее данные в объекты, что упрощает создание и преобразование сложных структур данных, а также действий, выполняемых над этими данными. Каждый объект приложения состоит из программы и связанных с ней данных. Многие приложения поддерживают различные типы объектов.
Язык VBA предоставляет доступ к объектам главного приложения и к объектам других приложений. (В ходе 20-го занятия вы узнаете, как использовать язык VBA для
212
Неделя 1
доступа и управления объектами других приложений.) С помощью языка VBA вы можете управлять приложением, влияя на объекты этого приложения из вашей программы. С помощью VBA вы даже можете создавать свои собственные объекты, используя модули класса. (О модулях класса вы узнаете на 11-м уроке.)
(ШИНШЕ
Информация, которую вы получите на этом уроке, пригодится вам не только при работе с приложением Excel. Объекты других приложений, поддерживающих язык VBA, таких как MS Word, MS Access и Microsoft Project, доступны для VBA, так же как и объекты приложения Excel. Объекты других приложений создаются для тех же целей, что и объекты Excel, однако некоторые объекты VBA-приложений имеют свои особенности. Например, все объекты приложения Access имеют непосредственное отношение к базам данных и управлению ими, в то время как объекты приложения Excel связаны с рабочими листами, рабочими книгами и т.д.
Свойства объекта
Как и объектам реального мира, VBA-объектам присуши некоторые качества или, как говорят программисты, свойства. Например, комнатный обогреватель имеет такие свойства, как потребляемая мощность, объем воздуха, проходящий через вентилятор за минуту, положения переключателей на его панели. Он обладает еще и такими характеристиками, как вес, цвет, размеры и т.д. Объекты в Excel также обладают свойствами, которые характеризуют их вид и поведение: например, рабочий лист Excel может быть невидимым, строка в рабочем листе или таблице характеризуется высотой, колонки имеют свою ширину и т.д.
Свойства определяют состояние и поведение объекта. Для изменения характеристик объекта вы изменяете его свойства. Например, для того чтобы изменить характеристики комнатного нагревателя, вы переключаете его регулятор. Чтобы изменить внешний вид, можно покрасить его в другой цвет, изменив таким образом его свойство, характеризующее цвет. В Excel можно изменить поведение рабочего листа, изменив его свойство — вычисление — с автоматического на ручное, а для изменения вида рабочего листа можно задать новые характеристики цвета для текста или графики на рабочем листе.
Для того чтобы определить текущее состояние или поведение объекта, следует обратиться к его свойствам. Например, чтобы определить выходную мощность нагревателя, вы прочтете его характеристики. Таким же образом вы определяете диск, папку и имя рабочей книги Excel, обратившись к ее свойству FullName.
Одни из свойств объектов можно изменять, другие нельзя. В примере с нагревателем можно переключить термостат — изменить свойство “термостат” нагревателя — для того, чтобы определить, когда нагреватель включится или выключится, однако нельзя изменить выходную мощность нагревателя, т.е. его свойство “выходная мощность”. Аналогичным образом одни свойства объектов VBA изменить можно, другие нельзя. Например, в рабочей книге Excel можно изменить имя автора рабочей книги, изменив свойство Author, но нельзя изменить свойство рабочей книги Name. (Свойство Name (Имя) рабочей книги содержит имя файла на диске, и для того, чтобы изменить имя файла, нужно создать новый файл или переименовать этот файл вне среды Excel.)
Свойства некоторых объектов имеют одинаковые или очень похожие имена: например, объекты Application, Workbook и Worksheet в Excel имеют свойство Name. Но имейте в виду, что свойства каждого объекта хранятся отдельно от свойств других объектов. На рис. 7.1 показано схематичное представление двух объектов Excel с их свойствами и методами. Обратите внимание, что каждый объект рабочего листа хранит данные о своих свойствах внутри рабочего листа, наряду с данными пользователя.
День 7. Объекты и коллекции
213
(Под данными пользователя подразумеваются любые сохраняемые в объекте данные, которые были непосредственно введены пользователем, например данные, содержащиеся в ячейках рабочего листа.) И хотя на рис. 7.1 показан пример рабочих листов Excel, все вышесказанное относится ко всем приложениям VBA; т.е. в каждом объекте сохраняются соответствующие данные.
Рабочий лист “Лист2"
Рабочий лист “Лист!"
Свойства Имя Видимый Активный Автор (и ДР-)		Данные (информация, которую пользователь ввел в ячейки листа)
		
Свойства Имя Видимый Активный Автор (и др.)
Данные (информация, которую пользователь ввел в ячейки листа)
Методы
(Совместно используются всеми рабочими листами)
Рис 7.1. Каждый объект имеет свои индивидуальные свойства, но объекты одного типа (как показанные здесь рабочие листы Excel) имеют общие методы
Методы объектов
Почти все объекты реального мира имеют некоторое присущее им поведение, или, иначе говоря, с каждым объектом связаны некоторые действия, которые он выполняет. Например, видеомагнитофон предназначен для записи телевизионных программ на магнитную ленту. Можно сказать, что видеомагнитофон обладает методом записи видео на ленту. Объекты VBA также обладают присушим им поведением, или возможностями, называемыми методами. Например, объект рабочей книги Excel имеет встроенную возможность добавлять новый рабочий лист. Таким образом, он обладает методом для добавления рабочих листов (называемым Add).
Методы изменяют значения свойств объектов, т.е. они изменяют данные, сохраненные в этих объектах. Методы очень похожи на процедуры, которые вам уже знакомы, но методы всегда привязаны к объекту; методы объекта доступны вам постольку, поскольку их предоставляет вам сам объект.
Одной из причин популярности объектно-ориентированного программирования является упрошенный подход к совместному использованию программы и, как следствие, создание более эффективных программ. Вместо того чтобы хранить отдельную копию программы для каждого метода каждого объекта, однотипные объекты VBA (как и объекты рабочего листа Excel, показанные на рис. 7.1) совместно используют методы.
Хотя однотипные объекты совместно используют исполняемый код программы для своих методов, метод считается частью объекта: когда вы обращаетесь к определенному методу конкретного объекта, этот метод воздействует только на тот объект, через который он получает доступ.
214
Неделя 1
Например, при настройке температуры обогревателя вам не обязательно знать, каким образом она регулируется, нужно только знать, как правильно изменить свойство термостата для установки нужной температуры. При записи телевизионной программы на видеомагнитофон не обязательно знать подробности процесса записи видеоизображения на ленту; это может быть тайной за семью печатями. Все, что нужно знать, чтобы записать телепрограмму на видеокассету, — как настроить видеомагнитофон, чтобы он начал запись. После того как вы воспользуетесь методом записи видеомагнитофона, его внутренний механизм возьмет управление на себя и запишет телепрограмму на видеоленту без какого-либо участия с вашей стороны.
Также нет особой нужды понимать, как выполняются методы объектов Excel или VBA; а также, как объект сохраняет или управляет данными, полученными от пользователя. Все, что вам необходимо знать, — как определить конкретный объект и как определить конкретный метод, которым вы хотите воспользоваться (или определенное свойство, которое вы хотите прочитать или изменить). Встроенная программа VBA выполнит за вас всю работу.
Классы объектов
Программные объекты сгруппированы в иерархические классы так же, как и объекты реального мира. Все объекты одного класса обладают одними (или схожими) свойствами и методами. Каждый класс объектов может содержать один или более подклассов. Объекты, которые принадлежат определенному классу объектов, называются членами этого класса.
Для того чтобы лучше понять иерархическую организацию объектов в классе, внимательно рассмотрите рис. 7.2, на котором показана диаграмма классификации различных типов обогревателей. Основной класс — обогреватели — находятся на верхней ступени иерархии. На рис. 7.2 показаны три подкласса: газовые, нефтяные и электрические обогреватели. Газовые обогреватели также делятся на два дополнительных подкласса: в одних используется натуральный газ, в других — жидкий пропан. По такому же принципу электрические обогреватели делятся на два дополнительных подкласса: одни работают на переменном токе, другие — на постоянном. В классе нефтяных обогревателей подклассов нет, потому что чаще всего в них используется керосин. Газовые, нефтяные и электрические обогреватели являются компонентами (членами) класса обогревателей. В свою очередь, обогреватели переменного и постоянного тока являются членами класса электрических обогревателей. (Теперь становится понятна терминология окна Object Browser для списков Classes и Members of <class>).
В самом верхнем квадрате на рис. 7.2 также приведен список гипотетических свойств и методов, которые являются общими для всего класса обогревателей. Свойство Комнатный показывает, для чего предназначен нагреватель, а свойство Термостат характеризует текущие установки термостата. Представьте, что у вас есть возможность управлять характеристиками обогревателя с помощью программы VBA, например можно прочитать свойство нагревателя Комнатный и определить, является ли он комнатным. Подобным образом можно извлечь свойство Термостат и определить текущие установки термостата. Гипотетический метод Включить класса обогревателей определяет порядок действий для включения нагревателя. Если с помощью VBA запрограммировать работу обогревателя, то для его включения нужно выполнить метод Включить. Метод УстановитьТермостат устанавливает режим работы термостата. Выполнение этого метода (т.е. изменение некоторых свойств термостата) приведет к изменению режима работы обогревателя.
День 1. Объекты и коллекции
215
Рис. 7.2. Гипотетическая иерархия классов объектов на примере обогревателей
Поскольку каждый из подклассов нагревателей, показанных на рис. 7.2, принадлежит к общему классу обогревателей, любой из подклассов в иерархии нагревателей обладает свойствами и методами всего класса. Например, электрический нагреватель постоянного тока обладает свойствами Комнатный, Термостат, и общими методами класса обогревателей Включить и УстановитьТермостат. Более того, каждый дополнительный подкласс может обладать своими собственными свойствами и методами, помимо основных свойств и методов класса. Например, электрические нагреватели могут обладать свойствами выходной мощности и силы тока, которые характеризуют потребление электричества.
Класс объекта определяет его свойства и методы. Объект, созданный пользователем или приложением, называется экземпляром. Например, если вы пойдете в магазин бытовой техники и купите комнатный электронагреватель, в вашем распоряжении будет находиться определенный экземпляр, принадлежащий классу электрических нагревателей.
Excel принадлежит к общему классу объектов, а именно к классу приложений, поддерживающих язык VBA. Когда вы запускаете Excel на своем компьютере, вы создаете экземпляр Excel. Если ваш компьютер обладает достаточными ресурсами памяти, вы можете запустить Excel несколько раз; при каждом запуске вы создаете новый экземпляр Excel на своем компьютере. Каждый раз при создании или открытии рабочей книги в Excel вы создаете экземпляр объекта Workbook.
VBA позволяет вам создавать собственные классы объекта. Для этого нужно создать модуль класса в вашем проекте. Ваш модуль класса содержит все определения свойств и методов вашего объекта; затем вы можете создать экземпляры своего объекта. Более подробно о создании классов вы узнаете на 11-м уроке.
276
Неделя 1
Использование объектов
Инструкции VBA, имеющие дело с объектами, могут выполнять с ними следующие действия.
•	Проверять состояние или статус объекта, обращаясь к значению, записанному в том или ином его свойстве.
•	Изменять состояние или статус объекта, изменяя значение его свойства.
•	Выполнять то или иное задание, присущее объекту, обращаясь его к методу.
Например, можно определить имя текущего активного рабочего листа Excel, если прочитать строку, записанную в свойстве рабочего листа Name. (Свойство рабочего листа Name содержит имя рабочего листа, выводимое в панели заголовка листа.) Для того чтобы добавить лист в рабочую книгу, следует использовать метод Add.
Для того чтобы воспользоваться свойствами или методами объекта, следует определить объект, чьи свойства или методы вы хотите использовать, а также конкретное свойство или метод объекта.
В языке VBA используется следующий синтаксис для указания свойства или метода объекта.
Object.Identifier
где Object — любая допустимая ссылка на объект. Ссылка на объект создается путем объявления переменной, указывающей на объект или с помощью методов, возвращающих ссылку на объект. Identifier — это любое допустимое свойство или имя метода. Если вы попытаетесь использовать свойства или методы, которые не принадлежат определенному объекту, VBA выведет на экран сообщение об ошибке в выполнении программы. Ниже приведено два примера. Первый является ссылкой на свойство рабочего листа Name; второй — ссылкой на метод рабочей книги Activate (для получения ссылки на объект в обоих случаях используется объектная переменная).
aSheet.Name
aBook.Activate
Обратите внимание, что во всех примерах объектная ссылка отделяется от имени свойства или метода разделительной точкой (.). В некотором смысле эта точка также связывает ссылку объекта со свойством или методом. Ввиду того что вы получаете доступ к свойству или методу через объект, вы должны указать ссылку на объект вместе с идентификатором свойства или метода. Разделительная точка указывает, где заканчивается объектная ссылка и начинается идентификатор свойства или метода. В то же время точка связывает ссылку на объект со свойством или методом, образуя идентификатор в инструкции VBA.
Не забывайте включать разделительную точку, иначе VBA не сможет корректно выполнить инструкции программы. В следующих двух примерах разделительная точка опущена.
aSheetName
aSheet Name
В первой строке ссылка на объект (переменная с именем aSheet) не отделена от свойства идентификатора (Name). VBA воспринимает это как простую переменную или идентификатор процедуры. Если у вас в программе есть переменная или процедура с именем aSheetName, VBA выведет на экран сообщение об ошибке выполнения про
День 1. Объекты и коллекции
217
граммы или создаст новую переменную, в зависимости от того, что определено в этом модуле в директиве Option Explicit.
Во втором примере объектная ссылка не связана со свойством идентификатора. В этом случае VBA воспримет инструкцию как вызов процедуры aSheet с одним параметром переменной Name и может вывести на экран сообщение о синтаксической ошибке или ошибке выполнения программы.
В табл. 7.1 перечислены некоторые из наиболее важных объектов приложения Excel (с точки зрения программирования в VBA). В таблице приведено имя объекта и его краткое описание. (Не забывайте, что Excel содержит значительно больше объектов, чем приведено в этой таблице.)
Таблица 7.1. Общеупотребительные объекты Excel
Объект	Описание
Application Chart Font	Приложение Excel Диаграмма рабочей книги Этот объект содержит характеристики шрифта и начертания текста рабочего листа
Name Range	Определенное имя для диапазона ячеек рабочего листа Диапазон ячеек (одна или несколько) или поименованный диапазон рабочего листа
Window	Любое окно в Excel; в окнах обычно отображаются рабочие листы, графики и т.д.
Workbook Worksheet	Любая открытая рабочая книга Любой рабочий лист в рабочей книге
Использование свойств объекта
Свойство объекта можно использовать только двумя способами: прочитать его значение или записать (установить) его значение. Как говорилось ранее на сегодняшнем уроке, не все свойства можно изменять. Свойства объекта, которые нельзя изменить, определяются как свойства только для чтения', свойства, которые можно установить, называются свойствами для чтения-записи.
Как правило, свойства содержат числовые, строковые значения или значения типа Boolean, хотя некоторые свойства могут иметь тип Object или другие типы данных.
В общем виде синтаксис для определения свойства выглядит следующим образом.
Object.property
где Object представляет собой любую допустимую ссылку на объект, a property — любое допустимое имя свойства для объекта, на который имеется ссылка.
Вы ссылаетесь на значения свойств объекта, используя свойства в выражении так же, как используете другие значения переменных или констант. Значение свойства можно присвоить переменной, можно использовать свойства объекта в выражениях в качестве параметров функций и процедур или в качестве параметров для методов объектов.
Для того чтобы присвоить значение свойства некоторой переменной, следует использовать такой синтаксис.
Variable = Object.Property
218
Неделя 1
где Variable — любая переменная, тип которой совместим с типом свойства объекта, Object — любая допустимая ссылка на объект, и Property — любое допустимое имя свойства объекта, на который ссылаются. В следующем примере переменной AnyStr присваивается строка, принадлежащая свойству Name, на которую ссылается переменная объекта aSheet рабочего листа Excel.
AnyStr = aSheet.Name
Свойство объекта можно использовать в выражении либо в качестве параметра функции или процедуры. В следующих строках показано допустимое использование свойства объекта (в каждой строке aSheet — это переменная типа Object, ссылающаяся на рабочий лист Excel).
MsgBox aSheet.Name
AnyStr="3TOT лист имеет имя: " & aSheet.Name
MsgBox Lease(aSheet.Name)
Почти каждый объект VBA обладает свойством, которое содержит его имя. В следующей инструкции функция MsgBox используется для вывода на экран свойства объекта рабочей книги Excel FullName. Это свойство содержит информацию о диске, путь к папке и имя файла рабочей книги.
MsgBox aBook.FullName
В этом примере переменная аВоок ссылается на объект открытой рабочей книги. Например, если аВоок ссылается на рабочую книгу с именем Продажи.xls в папке Мои документы, то в диалоговом окне появится
С:\Мои документы\ ПРОДАЖИ.XLS
Для того чтобы установить свойство объекта, нужно просто присвоить свойству новое значение, используя такой синтаксис.
Object.Property = Expression
где Object — любая допустимая ссылка на объект, Property — любое свойство объекта, на который ссылаются, и Expression — любое выражение VBA, которое имеет тип данных, совместимый с типом свойства объекта. Например, в следующей строке изменяется имя рабочего листа, ссылающегося на объект, с помощью переменной aSheet. Свойству Name присваивается значение
aSheet.Name = "Первый квартал"
В следующем примере изменяется текст, отображаемый в панели состояния в левом нижнем углу окна экрана. Свойству строки StatusBar объекта присваивается следующее выражение (в данном случае объект Application является объектом приложения Excel).
Application.StatusBar = "Создание отчета за третий квартал"
Так надо
: Для отображения сообщения о действиях, выполняемых процедурами, используйте свойство Application. StatusBar, особенно, если некоторые действия выполняется длительное время (например, сортировка большого списка, запрос к удаленной базе данных или обновление ссылок OLE). С помощью сообщения в строке состояитя вы инфорьмруете пользователя о том, что процадра в данный момент работает. Инструкция выгладит следующим образом.
Application.StatusBar = "Сообщение о текущих действиях"
День 7. Объекты и коллекции
219
Так не надо
При работе не забывайте возвращать управление строкой состояния приложению Excel. После выполнения процедуры вы должны установить значение False для свойства Application.StatusBar, в противном случае в окне состояния Excel будет выводиться установленное вами сообщение. Для того чтобы очистить окно  состояния и вернуть Excel управление им, используйте оператор типа
Application.StatusBar=False
В табл. 7.2 перечислены некоторые наиболее часто используемые свойства объектов Excel. Здесь указаны имя свойства, краткое описание типа данных свойства и значение, а также часто используемые объекты, которые обладают этим свойством.
Таблица 7.2. Общие и полезные свойства объектов
Свойство	Тип/значение	Используется в объектах
ActiveCell	Object: активная ячейка рабочего листа	Application, Window Workbook
ActiveChart	Object: активный график	Application, Window, Workbook
ActiveCheet	Object: активный рабочий лист	Application, Window, Workbook
Count	Integer: число объектов в коллекции	Все коллекции объектов
Formula	String: формула для ячейки рабочего листа	Range
Index	Integer: число объектов в коллекции	Worksheet
Name	String: имя объекта	Application, Workbook и другие
Path	String: имя диска и каталог, где хранится объект	Addin, Application, Workbook
Saved	Boolean: производилось ли сохранение рабочей книги со времени последнего изменения	Workbook
Selection	Object: текущее выделение	Application, Window
StatusBar	String: сообщение в окне состояния	Application
ThisWorkBook	Object: рабочая книга, из которой выполняется текущая процедура	Application
Type	Integer: число, соответствующее типу объекта	Window, Worksheet, Chart
Visible	Boolean: указывает является ли объект Excel видимым	Application, Worksheet, Range и другие
Value	Может быть разным: реальное значение, показываемое в ячейке	Range
220
Неделя 1
Как пользоваться методами объектов
Методы объекта используются в инструкциях VBA так же, как и в любой встроенной процедуре.
В общем виде синтаксис для метода объекта выглядит следующим образом.
Object.Method
Для методов с обязательными или необязательными аргументами используется следующий синтаксис:
Object.Method аргумент!, аргумент2, аргументЗ...
В обоих примерах Object — это любая допустимая ссылка объекта, a Method — имя любого метода, относящегося к объекту, на который ссылаются. Во втором примере синтаксиса строка аргумент!, аргументЗ и т.д. соответствует списку аргументов метода. Так же как и для любого вызова процедуры, аргументы метода перечисляются по порядку, каждый аргумент в списке отделяется запятой, в том числе запятыми отделяются и отсутствующие необязательные аргументы. В списке аргументов метода могут отсутствовать все аргументы, а может быть один или несколько аргументов; они могут быть обязательными или необязательными.
Например, рабочая книга Excel имеет метод Activate, который активизирует рабочую книгу и первый лист в этой книге. Если назначить переменную аВоок для ссылки на объект рабочей книги, то приведенная ниже инструкция активизирует этот рабочий лист (далее в ходе этого урока будет описано, как установить переменную со ссылкой на объект):
aBook.Activate
Хотя метод Activate не использует аргументов, многие методы объекта имеют один или более аргументов. Следующий пример иллюстрирует метод SaveAs объекта рабочей книги Excel; в примере используется один обязательный аргумент для метода SaveAs и один из нескольких необязательных аргументов для метода.
ActiveWorkbook.SaveAs Filename:="C:\VBA21\NEWFILE.xls", _
FileFormat:=x!Normal
Многие объекты имеют методы, которые возвращают значения, как это делает функция. Для того чтобы использовать возвращаемое методом значение, нужно заключить список аргументов функции в скобки, а вызов метода включить в инструкцию присваивания или другое выражение, как в случае с функциями. Так же как и при использовании функции, можно игнорировать результат, возвращаемый методом. Для того чтобы игнорировать результат метода (если он имеется), не нужно заключать в скобки список аргументов вызываемого метода, как это делается в том случае, когда метод не возвращает результат.
Так, например, метод Address (который принадлежит объекту Range) приложения Excel возвращает строку адреса некоторой области ячеек рабочего листа. В следующем примере приведена инструкция VBA, которая использует метод Address (myRange — переменная объекта, которая ссылается на некоторую область ячеек рабочего листа).
MsgBox myRange Address
Если переменная myRange ссылается на первую ячейку рабочего листа, инструкция MsgBox выводит строку $А$1.
Мы только что привели пример метода Address без использования аргументов, хотя этот метод имеет несколько необязательных аргументов. Эти аргументы определяют
День 7. Объекты и коллекции
221
стиль возвращаемых координат ячеек и указывают, являются ли эти координаты абсолютными или относительными. В следующем примере метод Address вызывается с третьим необязательным аргументом, который задает стиль возвращаемых координат ячеек:
MsgBox myRange.Address( , , xlRlcl)
Обратите внимание, что в приведенном примере необязательные аргументы отделены запятой, так же как в любой процедуре или функции. Поскольку аргумент, определяющий стиль ссылки на ячейки, является третьим аргументом, ему предшествуют две запятые. xlRlcl — это встроенная константа Excel, которая указывает, что координаты ячейки рабочего листа используют стиль R1C1. Если переменная объекта myRange ссылается на ячейку во второй строке третьей колонки, результатом предыдущего примера будет R2C3.
Так же как процедуры и функции VBA, методы имеют поименованные аргументы. Если переписать последний пример с использованием поименованных аргументов, он будет выглядеть следующим образом.
MsgBox myRange.Address)ReferenceStyle:=xlRlCl)
Использование поименованных аргументов упрощает программу. В двух приведенных ниже примерах для сохранения рабочей книги с новым именем используется метод SaveAs. Метод SaveAs принадлежит объекту рабочая книга и в данном случае не возвращает результат; объектная переменная aBook ссылается на рабочую книгу Excel.
aBook.SaveAs "NEWFILE.XLS", xlNormal, , , , True
aBook.SaveAs FileNname:="NEWNAME.XLS", _
FileFormat:= xlNormal,
CreateBackup:=True
В обоих примерах метод SaveAs использует только три из шести необязательных аргументов. Первая инструкция использует стандартный список аргументов, а вторая — поименованные аргументы. Вы сами видите, насколько первая инструкция нагляднее второй. (Обратите внимание, как символы продолжения и переноса строки упрощают инструкцию.)
ПРИМЕЧАНИЕ
Если вы не замечаете больших различий между методом и процедурой, не думайте, что вы что-то упустили. В действительности имеется только одно различие между методом объекта и процедурой (встроенной или созданной пользователем): метод принадлежит определенному объекту и его можно вызывать непосредственно через этот объект.
Вызов метода легко увидеть в программе благодаря тому факту, что метод присоединен к объектной ссылке точкой-разделителем, как это показано в примерах в этом разделе.
Как видно из приведенных в этой главе примеров, использование в программе методов объекта схоже с использованием процедуры или метода, кроме того, что вы можете определить объект, которому принадлежит метод. На сегодняшнем уроке вы узнаете общие правила использования объектов, методов и свойств. На 19-м занятии будут более детально описаны многие из приведенных на этом уроке методов и свойств.
В следующих разделах этого урока вы научитесь использовать окно Object Browser для получения подробной информации об объектах, методах и свойствах.
В табл. 7.3 приведены некоторые из наиболее часто используемых или полезных методов, принадлежащих объектам Excel. Здесь приведены имя, свойства, краткое описание цели метода и некоторые из наиболее часто используемых объектов, которым принадлежит этот метод.
222
Неделя 1
Таблица 7.3. Методы объектов Excel
Метод	Цель	Используется в объектах
Activate	Активизирует объект	Window, Workbook, Worksheet, Range и другие
Address	Возвращает координаты ячейки определенного объекта	Range
Calculate	Производит вычисления в открытой рабочей книге, рабочем листе или некотором диапазоне	Application, Range, Worksheet
Cells	Возвращает объект Range	Application, Range, Worksheet
Charts	Возвращает коллекцию диаграмм рабочего листа	Application, Workbook
Clear	Удаляет данные, содержащиеся в определенном объекте	Range
Close	Закрывает определенный объект	Window, Workbook, Workbooks
Justify	Выравнивает текст, содержащийся в определенном объекте	Range
Run	Выполняет определенную процедуру или функцию	Application, Range
Save	Сохраняет файл рабочего листа	Application, Workbook
SaveAs	Сохраняет определенный объект в другом файле.	Workbook, Worksheet
Select	Выделяет определенный объект	Range, Sheets, Worksheets
SendKeys	Посылает в активное диалоговое окно или приложение коды одной или нескольких комбинаций клавиш, имитируя нажатие клавиш пользователем	Application
Sheets	Возвращает коллекцию всех листов рабочей книги	Application, Workbook
Volatile	Регистрирует функцию как тип Volatile.	Application
Workbooks	Возвращает коллекцию рабочих книг	Application
Worksheets	Возвращает коллекцию рабочих листов	Application, Workbook
Объявление объектных переменных
Как вы помните из материала 3-го урока, в дополнение к типам данных Byte, Integer, Long, Single, Double и String, в VBA имеется тип данных Object. Переменные или выражения типа данных Object ссылаются на объект VBA или на объект, принадлежащий приложению, например на объекты приложения Excel Workbook, Worksheet и Range.
День 7. Объекты и коллекции
223
Переменные в модуле, процедуры и функции с типом данных Object объявляются так же, как и другие типы данных VBA.
Dim myObject As Object
Посредством инструкции Dim можно объявить переменную myObject, которая будет содержать ссылку на любой объект VBA или приложения. Для того чтобы использовать переменную типа Object для определенного типа объектов, можно объявить объектную переменную следующим образом.
Dim аВоок As Workbook
Воспользовавшись инструкцией Dim, можно объявить переменную аВоок, которая будет содержать ссылку только на объекты Workbook; при попытке установить ссылку переменной аВоок на объект Range или Worksheet VBA выведет на экран сообщение об ошибке несоответствия типа.
Можно использовать функцию IsObject для того, чтобы определить, насколько корректно переменная или выражение формируют ссылку на объект. Синтаксис для функции IsObject выглядит следующим образом.
IsObject(Object)
где Object — это переменная или выражение, которое следует проверить; если ссылка на объект корректна, функция IsObject возвращает значение True, в противном случае — False. Для того чтобы проверить тип объекта или переменной, можно воспользоваться функцией TypeName; применение функции TypeName будет рассмотрено на 10-м уроке.
Использование объектов в выражениях
Объектным выражением может быть любое выражение VBA, которое определяет конкретный объект. Результатом объектного выражения должна быть единственная объектная ссылка; все объектные выражения используются в программе с единственной целью — создание ссылок на конкретные объекты.
Объектное выражение может содержать объектные переменные, ссылки на объекты или метод, возвращающий объект. Вот примеры правильных объектных выражений (использующих объекты приложения Excel).
Application	Имя объекта, которое ссылается на объект приложения —
Excel
Application.ActiveSheet	Свойство объекта, значением которого является ссылка
на объект — активный лист
Application.Workbooks	Свойство, значением которого является ссылка на кол-
лекцию объектов — все открытые рабочие книги
Abook	Объектная переменная; инициализируется в инструкции
Set и ссылается на объект
Переменные или объектные выражения типа Object нельзя использовать в арифметических, логических выражения или операциях сравнения. Объектная ссылка — созданная объектным выражением или хранимая в объектной переменной — по сути, является только адресом, который указывает ячейку памяти, в которой хранится объект. Именно потому, что объектная ссылка является адресом, арифметические, логические выражения или операции сравнения не имеют смысла. Например, если сложить но
224
Неделя 1
мер вашего дома и номер соседнего дома, нет гарантии, что получившееся число совпадет с реальным номером другого дома на вашей улице, — то же самое происходит и с объектными ссылками.
Перед тем как передать объектной переменной ссылку на нужный объект, необходимо установить эту переменную. Для того чтобы присвоить объектной переменной ссылку на объект, следует ввести ключевое слово Set.
В общем виде синтаксис для ключевого слова Set выглядит следующим образом.
Set Var = Object
где Var — любая объектная переменная или переменная типа Variant. Object — любая допустимая объектная ссылка или объектное выражение. Если переменная Var объявляется с определенным типом объекта (например, Range или Workbook), ее тип должен быть совместим с типом объекта, на который ссылается Object.
В приведенном ниже фрагменте программы тип переменной полностью совпадает с типом объекта (используются объекты Excel).
Dim aSheet As Worksheet
Set aSheet = Application.ActiveSheet
В следующем примере свойство ActiveSheet возвращает не объект Workbook, а объект Worksheet, поэтому возникает ошибка несоответствия типа:
Dim aBook As Workbook
Set aBook = Application.ActiveSheet
Для того чтобы определить конкретный объект в выражении или чтобы объектная переменная сослалась на этот объект, следует использовать методы и свойства, которые возвращают объекты, например свойства ActiveWorkbook и ActiveSheet объекта Application или метод Cells объекта Worksheet.
Хотя стандартные операторы сравнения (<, <=, >, >=, <>, =) с объектами не используются, в VBA имеется один оператор сравнения, созданный исключительно для применения с объектными выражениями и переменными — это оператор Is.
В обшем виде синтаксис для оператора Is выглядит следующим образом.
Objectl Is Object2
где Objectl и Object2 — любые допустимые объектные ссылки. Оператор Is следует использовать, если две объектные ссылки относятся к одному объекту. Результатом сравнения оператора Is является значение True, если объектные ссылки одинаковы, в противном случае — False.
В листинге 7.1 приведена процедура, которая создает резервную копию активной рабочей книги. Такую процедуру можно использовать для сохранения копии активной рабочей книги пользователя под другим именем. При этом имя файла активной рабочей книги остается без изменения, как это делается при выборе команды File-SaveAs (и метода объекта SaveAs).
flucmuuz 7.1, Процеддра Backup ftctlVEBnnk
1:	Sub Backup_ActiveBook()
2:	'Создает резервную копию активной книги с новым именем
3:	'с помощью метода SaveCopyAs. К исходному имени добавляется "(резерв)"
4:
5:	Dim FName As String
6:	Dim OldComment As String
День 7. Объекты и коллекции
225
7:
8:	'сохраняем исходные заметки файла
9:	OldComment = Activeworkbook.Comments
10:
11:	'Заносим новые заметки в резервную копию
12:	ActiveWorkbook.Comments = "Резервная копия книги " &
13:	ActiveWorkbook.Name &
14:	", созданная процедурой Backup_ActiveBook()."
15:
16:	'Создаем новое имя файла на основе старого
17:	FName = Left(ActiveWorkbook.Name,
18:	InStr(ActiveWorkbook.Name, ".")) &
19:	"(резерв).xls"
20:
21:	ActiveWorkbook.SaveCopyAs FileName:=FName 'сохраняем копию с новым именем
22:	ActiveWorkbook.Comments = OldComment 'восстанавливаем старые заметки
23:	End Sub
Эта процедура использует различные объекты, свойства и методы Excel. Ссылка на объект ActiveWorkbook, используемая в процедуре Backup_ActiveBook (строки 9, 12, 13, 17, 18, 21 и 22), является свойством объекта Application, которое возвращает объектную ссылку на активную рабочую книгу. (Как вы узнаете в следующем разделе, ссылки объекта на свойства и методы объекта Application можно опустить.)
В строке 1 объявляется процедура Backup_ActiveBook. Строки 2 и 3 содержат комментарий, описывающий назначение и порядок выполнения этой процедуры.
В строках 5 и 6 объявляются строковые переменные FName и OldComment. В строке 9 строка свойства Comments объекта ActiveWorkbook копируется в переменную OldComment. В строках 12-14 (обратите внимание на символы продолжения строки в конце строк 12 и 13) свойству Comments объекта ActiveWorkbook присваивается новое значение. Это свойство принадлежит объекту рабочая книга и содержит текст комментария. Комментарий вводится в диалоговом окне Свойства, которое выводится при первой попытке сохранения рабочей книги или после выбора команды Файл-Свойства.
В строках 17-19 содержится обычная инструкция VBA; обратите внимание, что символ продолжения строки в конце строе 17 и 18 указывает, что все три строки являются единой инструкцией. Эта инструкция использует функции Left и InStr для создания нового имени файла резервной копии активной рабочей книги. Функция InStr возвращает позицию разделителя в имени файла свойства Name объекта ActiveWorkbook. (Свойство рабочей книги Name содержит имя этой книги.)
Результат функции InStr (строка 18) определяет, какое количество символов функция Left копировала из свойства Name объекта ActiveWorkbook. Поскольку функция InStr возвращает позицию разделителя имени файла (.), функция Left возвращает имя файла, включая разделитель, но без расширения XLS. А к имени файла добавляется строка (backup) .xls. Например, если свойство ActiveWorkbook содержит имя файла Day07.xls, результатом выполнения этого выражения будет строка Day07(backup) .xls. В строке 17 оператор присваивания сохраняет новое имя файла в переменной FName.
Строка 21 использует метод ActiveWorkbook. SaveCopyAs для того, чтобы сохранить файл активной рабочей книги с новым именем на текущем диске и в текущем каталоге; имя же файла активной рабочей книги остается прежним.
Наконец строка 22 заново сохраняет содержимое свойства ActiveWorkbook.Comments; файл активной рабочей книги теперь находится точно в таком же состоянии, как и
226
Неделя 1
перед началом процедуры. Новая копия рабочей книги на диске такая же, как и рабочая книга в памяти, исключая комментарий в листе Свойства, который указывает, что новый файл является резервной копией и имеет то же имя, что и оригинал.
ПРИМЕЧАНИЕ
Процедура Backup ActiveBook в листинге 7.1 сохраняет копию рабочей книги на текущем диске в текущей папке, имена которых могут отличаться от имен диска и папки, откуда был загружен файл. Свойство Name возвращает только имя рабочей книги, но информация о диске и папке отсутствует. Далее вы узнаете, как получить из рабочей книги полную информацию о диске и папке.
Доступ к объектам с помощью конструкции With... Eud With
Как вы видели в листинге 7.1, процедура может обращаться к одному и тому же объекту многократно, когда несколько инструкций используют один и тот же объект или методы одного объекта. Каждая инструкция листинга 7.1 в строках 9-22 использует свойство метода объекта, относящегося к рабочей книге Activeworkbook. Имеющиеся в VBA специальные структуры — With.. .End With — позволяют ссылаться на свойства или методы, которые принадлежат одному объекту, и не определять каждый раз ссылку на объект.
В общем виде синтаксис структуры With.. .End With выглядит следующим образом:
With Объект
' инструкции, которые используют свойства и методы объекта
End With
где Object — любая допустимая ссылка на объект. В листинге 7.2 снова приведена процедура Backup ActiveBook, только теперь в ней используется структура With.. .End With.
Aucmtiua 7.2. Сшрдктдра wlth...Enit With в ирпцеддре Backup flcflveBonl(
1:	Sub Backup_ActiveBook()
2:	'Создает резервную копию активной книги с новым именем
3:	'п помощью метода SaveCopyAs. К исходному имени добавляется ”(резерв)”
4:
5:	Dim FName As String
6:	Dim OldComment As String
7:
8:	With ActiveWorkbook
9:	'сохраняем исходные заметки файла
10:	OldComment = .Comments
11:
12:	'Заносим новые заметки	в резервную	копию
13:	.Comments = "Резервная	копия книги	" & .Name	& _
14:	", созданная процедурой Backup ActiveBook))."
15:
16:	'Создаем новое имя файла на основе старого
17:	FName = Leftf.Name, InStr(.Name, ”.")) & "(backup).xls"
18:
19:	.SaveCopyAs FileName:=FName
20:	.Comments = OldComment 'восстанавливаем старые заметки
21:	End With
22:	End Sub
День 1. Объекты и коллекции
227
Здесь процедура Backup_ActiveBook выполняется так же, как и в предыдущем примере. Но сейчас в ней используется структура With...End With. Эта инструкция начинается в строке 8 с ключевого слова With, за которым следует ссылка на объект ActiveWorkbook.
В строке 10 содержимое свойства ActiveWorkbook.Comments присваивается строковой переменной OldComment. Обратите внимание, что в этой строке перед свойством Comment используется только символ разделителя (.). Поскольку эта инструкция располагается внутри инструкции With рабочей книги ActiveWorkbook, предполагается, что объектная ссылка для свойства .Comments принадлежит именно ActiveWorkbook.
Строки 13 и 14 содержат простую инструкцию VBA, которая присваивает новое значение свойству .Comments. В строке 17, так же как и в листинге 7.1 новое имя файла сохраняется в переменной FName. Обратите внимание, что в этой строке инструкции свойство .Name используется так же, как свойство .Comments. В связи с тем что эта инструкция располагается внутри инструкции With...End With и включает символ разделителя перед именем свойства, предполагается, что свойство .Name принадлежит объекту ActiveWorkbook.
В строке 19 для сохранения резервной копии используется метод .SaveCopyAs, а в строке 20 в свойстве .Comments сохраняется исходный комментарий. В каждой строке инструкции With, которая определяет объект ActiveWorkbook, предполагается, что объектная ссылка для любого свойства или метода, которому предшествует символ разделителя, относится к объекту' ActiveWorkbook.
Наконец в строке 21 инструкция With завершается ключевыми словами End With.
Очевидно, что листинг 7.2 намного проще во всех отношениях. Если программа содержит несколько инструкций, которые используют свойства или методы ссылки на один объект, применение структуры with.. .End With упрощает программу.
Так надо
Запомните, что использование структуры With.. .End With ускорит выполнение процедуры: ссылка на объ-, ект определяется только один раз - в начале инструкции with; а следовательно, экономится время на определение ссылки для каждого свойства или метода в инструкции With.
Не забывайте сохранять любые данные даже при незначительном изменении. Например, процедура Backup_ActiveBook изменяет свойство Comments активной рабочей книги. Следовательно, процедура использует переменную для сохранения исходного комментария перед внесением изменений, а затем сохраняет исходное значение.
Так не надо
Не заставляйте пользователя вводить информацию без крайней необходимости. Процедуру Backup Acti veBook можно было бы написать с использованием инструкции InputBox, чтобы пользователь сам ввел имя файла вместо того, чтобы создавать его автоматически. В случае автоматической генерации имени файла можно быть уверенным, что резервные копии файла, созданные этой процедурой, будут иметь осмысленные имена и пользователь получит возможность экономить время на вводе имени файла. Генерирование имени файла в процедуре снижает вероятность ввода пользователем ошибочного имени.
228
Неделя 1
Коллекции объектов о контейнеры объектов
Коллекция — это группа связанных объектов, например несколько листов рабочей книги или несколько символов одного абзаца. Каждый объект такой коллекции называется ее элементом.
Коллекция сама по себе является объектом; она обладает собственными свойствами и методами. Например, каждая коллекция имеет свойство Count, которое возвращает количество элементов этой коллекции. Если активная рабочая книга содержит 16 рабочих листов, результатом приведенного ниже выражения будет число 16;
Application.ActiveWorkbook.Worksheets.Count
В приведенном выражении Worksheets является коллекцией всех рабочих листов рабочей книги, ActiveWorkbook — свойством объекта Excel Application, которое возвращает текущую рабочую книгу, а свойство Count коллекции Worksheets — общее количество рабочих листов в коллекции.
На этом примере видно, как одни объекты могут содержать другие объекты. Контейнером объекта может быть любой объект, содержащий один или более других объектов. В приведенном выше примере свойство Application содержит ссылку на объект ActiveWorkbook, который, в свою очередь, содержит коллекцию объекта Worksheets. Все объектные ссылки контейнера с помощью символа разделителя (.) составляют одно выражение.
Многие объекты могут содержать некоторые другие объекты: например, рабочая книга может содержать объекты Module, Chart и Worksheet; а объект Worksheet, в свою очередь, может содержать объекты Drawingobjects, Chartobjects или OLEObjects. На рис. 7.3 показаны некоторые объекты Excel, содержащие другие объекты. Такая иерархия объектов и контейнеров называется объектной моделью.
Рис. 1.3. Объект Application приложения Excel содержит коллекции объектов, например Workbooks, которые, в свою очередь, содержат другие объекты, например отдельные листы
День 7. Объекты и коллекции
229
Из рис. 7.3 видно, что объект Application является внешним контейнером; в нем содержатся все остальные объекты. Можно сказать, что объект Application содержит коллекцию, которая, в свою очередь, содержит другие коллекции. Объект Application включает в себя коллекцию Workbooks и Addins: Workbooks — это коллекция всех открытых рабочих книг, a Addins — коллекция всех установленных надстроек. Коллекция Workbooks, в свою очередь, содержит коллекции Worksheets и Sheets. В коллекцию Worksheets включены отдельные рабочие листы.
ПРИМЕЧАНИЕ
На рис. 7.3 показана неполная диаграмма, иллюстрирующая связи между различными объектами и их контейнерами. Excel содержит намного больше объектов и коллекций.
Рис. 7.3 дает представление о том, как правильно указать ссылку на объект. Для того чтобы было понятно, на какой объект вы хотите сослаться, нужно определить контейнер объекта. Например, для того чтобы сослаться на отдельный лист рабочей книги Excel, нужно указать ссылку на объект рабочей книги, которая содержит этот рабочий лист:
Workbooks(Sales.xls).Worksheets("Sales Report")
В этом выражении для ссылки на рабочую книгу Sales.xls используется коллекция Excel Workbooks; затем для ссылки на определенный лист используется коллекция Worksheets, содержащаяся в рабочей книге Sales.xls. (О том, как правильно указать обычный объект в коллекции, будет описано ниже в этом уроке.) В следующем примере показана более сложная ссылка объект:
Application.Workbooks("Sales.xls").Worksheets("Sales Report").Range("Al")
Объектное выражение ссылается на ячейку Al листа Sales Report рабочей книги Sales.xls. Как видно из этих двух примеров, указание полной ссылки объекта среди всех контейнеров объекта является крайне утомительным занятием. К счастью, VBA позволяет опустить ссылку на объект Application практически для всех объектов, которые он включает. В том случае, когда ссылка на Application опущена, подразумевается, что этот объект принадлежит приложению Excel. В следующем выражении используется ссылка на тот же объект, что и в предыдущем выражении (ячейка А1 в листе Sales Report рабочей книги Sales.xls) с учетом сказанного выше.
Workbooks("Sales.xls").Worksheets("Sales Report").Range("Al")
Объект Application следует указывать только в том случае, когда ссылка может быть неоднозначной или когда используются встроенные функции Excel, описанные в 5-м уроке.
Если при работе в Excel опускается ссылка на объект Workbooks, как правило (но не всегда), предполагается, что речь идет об активной рабочей книге. Приведенный ниже пример напоминает предыдущий, если предположить, что Sales.xls — активная рабочая книга.
Worksheets("Sales Report").Range("Al")
Если Sales.xls — активная рабочая книга, a Sales Report — активный рабочий лист, то приведенное ниже выражение совпадает с предыдущим.
.Range("Al")
230
Неделя 1
Однако во многих процедурах нельзя быть абсолютно уверенным, что отдельный рабочая книга или рабочий лист является активным во время выполнения процедуры, следовательно, нужно определить хотя бы некоторые из контейнеров объекта, на который вы ссылаетесь.
Не забывайте использовать методы и свойства, которые возвращают объект (например, Activeworkbook) для того, чтобы ссылка была не такой длинной. Использование в процедуре инструкции With.. .End With - лучший способ избежать многократного написания длинных ссылок на объект.
Иногда один объект может быть элементом нескольких коллекций. Например, объект Worksheet приложения Excel может быть элементом коллекций Worksheets и Sheets. В коллекции Worksheets содержатся все листы рабочей книги, включая рабочие листы или листы диаграмм. Ввиду того что и коллекция Sheets, и коллекция Worksheets содержат все листы рабочей книги, любой рабочий лист принадлежит обеим коллекциям. Объект Excel Range, который ссылается на область ячеек рабочего листа, может принадлежать объекту Worksheet, а также находиться в объектах Column или Row.
Вся эта информация о контейнерах и коллекциях может ввести в заблуждение, особенно в отношении тех объектов, которые могут содержаться в нескольких объектах или коллекциях. Может показаться, что объекты VBA могут одновременно находиться в различных местах, но это не так. Для того чтобы лучше понять контейнеры объектов и коллекций, нужно помнить, что объекты программы не являются физическими предметами, которые содержат другие объекты. Наоборот, запомните, что объект, который содержит другой объект, является просто адресом в памяти, который ссылается на другой объект. Контейнеры ссылаются на объекты, которые они “содержат”, посредством адреса в памяти. Например, многие люди могут знать ваш электронный адрес, при этом ваше присутствие в их доме необязательно. Так же разные объекты программы могут ссылаться на другие объекты через их адрес.
ПРИМЕЧАНИЕ	Если вам нужно сослаться на контейнер объекта, используйте свойство Parent. Все объекты имеют свойство Parent, которое возвращает ссылку на контейнер. Например, в следующем выражении показана ссылка на объект Excel Application, в котором содержится коллекция Workbooks: Workbooks.Parent
В табл. 7.4 приведены некоторые наиболее часто используемые коллекции объектов Excel. Здесь указано имя и дано краткое описание назначения коллекции.
Таблица 7.4. Некоторые коллекции Excel
Коллекции	Назначение
Charts	Коллекция всех листов диаграмм рабочей книги
ChartObjects	Коллекция всех объектов диаграмм рабочей книги
Windows	Коллекция всех окон в приложении, независимо от того, являются ли они активными
Workbooks	Коллекция всех открытых рабочих книг в приложении
Worksheets	Коллекция всех рабочих листов рабочей книги
День 7. Объекты и коллекции
231
Включение а коллекцию нового элемента
Обычно вы имеете дело с элементами, уже содержащимися в коллекции. Однако иногда вам может понадобиться добавить в коллекцию новый элемент. Например, в Excel нужно создать новую книгу или добавить новый рабочий лист в рабочую книгу; для этого нужно включить новый элемент в соответствующую коллекцию объектов.
Каждая коллекция обладает методом Add, который добавляет в нее новый элемент. Многие методы Add имеют один или более аргументов, которые позволяют определить различные исходные значения или условия свойств нового объекта. Следующая инструкция показывает, как используется метод Add коллекции Workbook для создания новой рабочей книги Excel:
Workbook.Add
Эта инструкция создает новую рабочую книгу и активизирует ее. Более подробно о добавлении новых элементов в коллекцию Excel будет рассказано на 19-м занятии.
Как абратоться к объекту в коллекции ила а кантебнере
Для того чтобы установить отдельный элемент в коллекции, следует использовать следующий синтаксис.
Collection(Index)
Здесь Collection — любое допустимое выражение объекта, которое обращается к коллекции. Index — строка или целое число, определяющее нужный элемент. Если аргумент Index является строкой, эта строка должна содержать текстовое имя объекта. Например, В Excel в качестве аргумента Index можно использовать имя рабочего листа, выводимое на панели Excel, имя именованной области рабочего листа, имя файла рабочей книги, имя кнопки панели инструментов или команды меню и т.д. Например, для того чтобы обратиться к рабочему листу активной рабочей книги с именем July Data, используется следующее выражение.
Worksheets("July Data")
Если аргумент Index — целое число, он должен соответствовать номеру элемента в коллекции. Каждый элемент при внесении в коллекцию получает порядковый номер. По этой причине определить номер для элемента коллекции бывает очень трудно.
Обычно для обращения к элементу коллекции используется текстовое имя; это не только указывает, к какому объект вы обращаетесь, но и упрощает чтение текста программы. Выражение Worksheets ("July Data") выглядит намного проще и понятнее, чем Worksheets(5). (Многие объекты обладают свойством Index, которое возвращает номер объекта в коллекции, но вы можете обратиться к элементу коллекции и без обращения к свойству Index.)
В случае необходимости обратиться ко всем элементам коллекции не следует использовать никакой индекс. Например, в следующей инструкции закрываются все видимые открытые книги Excel.
Workbooks.Close
Так надо
, Учтите, что методы и свойства объекта Application принадлежат Excel, а не VBA. При работе с различными : приложениями УВД вы заметите, что некоторые из этих методов и свойств могут различаться в отдельных приложениях: в Excel, Word, Access и та
232	Неделя 1
Так не надо
Не забывайте, что объект Application обладает многими полезными методами и свойствами, а кроме того,  может быть контейнером для объектов Excel (или другого приложения). Например, следует запомнить, что к функциям Excel - SUM, MIN, МАХ и тщ. - следует обращаться через объект Application, как описано в материале 5-го урока.
м	»А- л м. «.Г	м'	«SB» «'f<1 л	™«&> и« W* *
ПРИМЕЧАНИЕ
Объект Application может содержать методы с именами, которые дублируют имена процедур или функций VBA. При использовании в приложении таких методов следует указать объект Application, в противном случае VBA вызовет принадлежащую ему процедуру или функцию.
Например, объект Application содержит метод InputBox. Метод Excel InputBox не совпадает с функцией VBA с таким же именем: метод Excel Application. InputBox имеет на один аргумент больше; этот дополнительный аргумент позволяет ограничить вводимый пользователем тип данных (числовой, даты, текстовый). Для того чтобы использовать метод Excel InputBox, следует указать объект Application:
Application.InputBox
В противном случае VBA вызовет принадлежащую ему функцию InputBox.
Объекты, методы о свойства в окне Object Browser
Вы уже знаете, как использовать окно Object Browser для того, чтобы определить расположение и получить дополнительную информацию о функциях, процедурах и константах VBA и других приложений (см. 5-й урок). Так же можно использовать окно Object Browser для нахождения объектов, методов и свойств; и действительно, назначением этого окна, как видно из его названия, является просмотр всех объектов VBA и приложений, наряду с указанием информации о различных свойствах и методах.
Вы уже знаете, как запустить окно Object Browser: можно щелкнуть на кнопке Object Browser на панели инструментов Visual Basic или выбрать View-Object Browser на панели инструментов. На экране появится окно, показанное на рис. 7.4.
Вы также можете запустить окно Object Browser, нажав функциональную клавишу F2 в окне редактора Visual Basic.
Для того чтобы увидеть список доступных объектов Excel и список свойств и методов определенного объекта, сделайте следующее.
1.	Откройте окно Object Browser.
2.	В выпадающем списке Project/Library в верхней части окна Object Browser выберите библиотеку Excel, как показано на рис. 7.4.
3.	После этого в списке Classes будут содержаться все доступные для VBA объекты Excel.
4.	В списке Classes выберите объект, методы и свойства которого вас заинтересовали. На рис. 7.4 показана коллекция объекта Workbooks.
День 7. Объекты и коллекции
233
Obiect Browser
ГТ Г
{Excel
(Sasses S >?,'
i<£> validation VPageBreak
•@ VPageBreaks w
«3 Walls
«3 WebOptions
•3 Window
«3 Windows
Workbook ________|
•3 'Workbooks
«3 Worksheet iffl WorksheetFunct «3 Worksheets
•if1 XIApplicationlnter лР XIApplyNamesOi tad® XIArrangeStyle
XIArrowHeadLen^j
"IJdMSlli
Members of Wwteooks'
й? Application
* Close
Й? Count ЙР Creator Й? Item i® Open
OpenText
Й? Parent
* Function AddfrramoMeD As Woritbooit Member ot I	W<><УшдМ
Puc. 7.4. Окно Object Browser поможет определить, какие объекты в данное время доступны и какие свойства и методы принадлежат определенному объекту
5. Для того чтобы получить более подробную информацию об объекте, выбранном в списке Classes, следует щелкнуть на кнопке вопросительного знака (?) в верхнем правом углу окна Object Browser. Вы получите доступ к диалоговой системе помощи VBA.
(ШМЕЧМ1Е
Вы можете получить информацию по объекту, выбранному в списке Classes, только в том случае, если объект не выделен в списке Members of <class>. (Щелчок на объекте в списке Classes снимает любое выделение в списке Members of <class>.)
6. После того, как объект в списке Classes выделен, в списке Members of <class> в окне Object Browser отображаются все методы и свойства выбранного объекта.
В списке Members of <class> выберите определенный метод или свойства, которые вас интересуют. В нижней части окна Object Browser показаны имя метода или свойства; если вы выберите метод, который имеет аргументы, то в окне Object Browser будет показаны все его аргументы. На рис. 7.4 показан метод Add коллекции Workbooks.
ПРИМЕЧАНИЕ
Методы и свойства списка Members of <class> в окне Object Browser отображаются по-разному, при этом слева от каждого элемента появляется специальный значок. Перед свойством появляется рисунок руки, указывающий на карточку, а методы обозначаются рисунком, похожим на летящий зеленый кирпич (см. рис. 7.4). Небольшой голубой круг (на рисунке его нет) показывает, что свойство или метод доступны глобально.
Щелкните на знаке вопроса (?) в окне Object Browser для того, чтобы получить более подробную информацию по определенному методу или свойству в списке Members of <class> в диалоговой помощи VBA.
234
Неделя 1
Так надо
Не забудьте в программе включить ссылку на объект и разделитель (.) для каждого метода или свойства.
Если метод не возвращает результат или вы хотите проигнорировать результат метода, не заключайте список аргументов в скобки.
Для того чтобы получить информацию о различных доступных объектах, методах и свойствах и ускорить написание программы, воспользуйтесь окном Object Browser.
Так не надо
Не используйте все аргументы метода, если они вам не нужны. Просто игнорируйте их.
Резюме
На сегодняшнем уроке вы узнали, что объект — это совокупность данных и исполняемого кода, связанных вместе в одно целое. Вы узнали, что свойства объекта сохраняют данные о состоянии и статусе объекта, а также о том, что методы объекта — это специальные процедуры и функции, которые обеспечивают его работоспособность и выполнение. Вы также выяснили, как объекты группируются в иерархии классов и подклассов.
Вы научились использовать объекты программы VBA и познакомились с основным синтаксисом указания метода или свойства. Теперь вы сможете объявить переменные типа данных Object. Помните, что для присваивания объектной ссылке объектной переменной следует использовать команду VBA Set. Вы также узнали, как упростить программу с помощью структуры With...End With, когда несколько инструкций ссылаются на один и тот же объект.
Мы выяснили, что такое коллекция объектов и как объекты могут содержать другие объекты, а также, как описать отдельный объект через сложные объектные ссылки, включающие все контейнеры объекта, и каким образом можно описать отдельный объект в коллекции.
И, наконец, вы научились использовать окно Object Browser для просмотра доступных объектов, методов и свойств. Вы также знаете, как получить более подробную информацию об объектах, методах и свойствах через диалоговую помощь VBA.
День 7. Объекты и коллекции
235
Вопросы о ответы
Я пытаюсь написать процедуру VBA, которая изменяет вид активного рабочего листа, но не знаю, как правильно выбрать объект для ссылки, а если бы и знал, на какой объект сослаться, не уверен в том, какое свойство нужно изменить. Существует ли для этого какой-либо метод?
Если вы не уверены, какие объекты или методы использовать для выполнения конкретного задания, воспользуйтесь средством записи макросов, создав макрос, выполняющий похожую упрощенную задачу, и затем исследуйте записанную программу, чтобы проанализировать используемые ею объекты, свойства и методы. После этого вы можете отредактировать или сделать копию записанных инструкций для применения в своей процедуре. Вы также можете исследовать объекты, свойства и методы, которые используются в записанной программе, с помощью справочной системы или окна Object Browser.
Записать программу с помощью команды Запись Макроса, а затем отредактировать ее для упрощения и дальнейшего использования — это и есть хорошая техника программирования не только для среды VBA, но и для Excel.
Почему вместо более обобщенного типа данных Object нужно объявлять специальный тип данных Range или Workbook?
Объявление специального типа данных помогает отследить ошибки программирования. Если вы сделали ошибку и неправильно установили значение объектной переменной, специальный тип данных поможет определить ошибку, потому что в этом случае VBA выдаст сообщение об ошибке несоответствия типа. В противном случае у вас будут проблемы с отслеживанием подобных ошибок.
Правда ли, что VBA создает новый объект, когда я объявляю объектную переменную, а затем использует команду Set для присваивания объекта этой переменной?
Когда вы объявляете объектную переменную, а затем устанавливаете ее на конкретный объект, вы не создаете новый объект; вы создаете новую ссылку на объект. При желании вы можете объявить несколько объектных переменных со ссылкой на один и тот же объект.
Вы используете объектные переменные для того, чтобы дать им значимые имена для объектов, которые использует ваша программа. Например, программа становится намного проще, если она содержит ссылку с именем Datasheet, а не выражение.
Workbooks ("Ch07. xls"). Worksheets ("Datasheet")
Если VBA неявно понимает ссылку на объект Application, почему я должен явно указывать этот объект?
Хотя ссылка на объект Application во многих случаях является необязательной, в других случаях она обязательна. Как правило, вы должны обозначать объект Application каждый раз, если возникает неопределенность, на какой объект имеется ссылка. Например, для того чтобы использовать функции рабочего листа Excel, вы должны обозначить объект Application, как рассказывалось на 5-м уроке и как показано в приведенных ниже примерах.
AnyVar = Application.Sum(Range("Al:А12"))
AnyVar = Application.Max(Range("Al:А12"))
AnyVar = Application.Min(Range("Al:A12"))
Каждая строка использует встроенную функцию рабочего листа Excel (SUM, MAX, MIN) и присваивает результат выполнения этой функции переменной AnyVar. Для того чтобы избежать путаницы между встроенными функциями Excel, функциями VBA и функциями пользователя с одинаковыми именами, следует обозначать объект Application.
236
Неделя 1
Правда лн, что индексная строка, обозначающая отдельный элемент в коллекции, всегда должна быть текстовой строкой в кавычках?
Нет. Индексная строка может быть строковой переменной или любым строковым выражением, результатом которого является допустимое имя для элемента коллекции.
Как я мог}’ создать объектную ссылку на объект, который я хочу использовать?
Начинайте описание объектной ссылки от внешнего к внутреннему контейнеру объектов, а в конце завершите определенным методом или свойством, выбранным по своему усмотрению.
Арр 1 i cation. Container 1. Contained. ThingYouWant
Объект Application и другие объекты всегда составляют внешний контейнер. Запомните, что объект Application и другие объекты имеют свойства и методы, которые возвращают ссылки объекта; например, для ссылки на активную рабочую книгу, листы или окно используйте свойства ActiveWorkbook объекта Application, а также ActiveSheet и ActiveWindow.
Кроме того, для определения контейнера объекта можно использовать свойство объекта Parent. Например, вы хотите определить, содержит ли контейнер текущего объекта Window — рабочего листа с несколькими диаграммами — данные, отображающиеся в другом окне. Для того чтобы определить, какая рабочая книга содержит текущее активное окно, используйте свойство ActiveWindow объекта Application для ссылки на текущее активное окно, а затем свойство Parent активного окна для ссылки на рабочую книгу, содержащее это окно. Например, в приведенном ниже примере используется инструкция MsgBox для вывода на экран имени рабочей книги, которая содержит активное окно:
MsgBox ActiveWindow.Parent.Name
В следующем примере снова используется инструкция MsgBox, на этот раз для вывода на экран количества окон, которое содержит исходная рабочая книга активного окна.
MsgBox ActiveWindow.Parent.Windows.Count
В общем случае вы должны создавать объектные ссылки, переходя постепенно от знакомых ссылок к незнакомым. Оба приведенных примера начинаются со ссылки объекта Application (объект Application всегда доступен, и поэтому является “известным”). Далее каждая инструкция использует свойство объекта Window (Parent) для ссылки на контейнер объекта.
Мастерская
Ответы см. в приложении.
Контрольные вопросы
1.	В чем суть идеи объектно-ориентированного программирования?
2.	Что является объектом в объектно-ориентированном приложении?
3.	Что такое свойство? Для чего предназначены свойства объекта?
4.	Все ли свойства объекта можно изменить?
5.	Если изменить свойство Name некоторого объекта, изменится ли это свойство у других объектов?
6.	Что такое метод? Для чего предназначены методы объектов?
7.	Назовите три действия, которые обычно выполняет программа VBA при использовании объектов, методов и свойств?
День 7. Объекты и коллекции
237
8.	Что представляет собой основной синтаксис, необходимый для использования свойства или метода объекта?
9.	Для чего предназначен разделитель (.) в объектной ссылке?
10.	Что представляет собой объектное выражение и для чего оно используется?
11.	Как создать объектную ссылку?
12.	Что представляет собой коллекция объектов? Что такое элемент коллекции?
Экражпения
1.	Найдите ошибку: в следующей процедуре используется метод Add коллекции Workbook приложения Excel для создания новой рабочей книги, а затем заполняются данные о ее свойствах с помощью информации, полученной от пользователя посредством инструкций InputBox и информации, полученной из свойства OrganizationName объекта Application. (Информацию о свойствах вы можете заполнить вручную, выбрав из меню программы Excel Файл-Свойства. Свойство OrganizationName объекта Application содержит имя организации, вводимое в диалоговом окне при установке приложения Excel.)
Однако в этой процедуре содержится ошибка, и она выполняется неправильно. Если в модуле, содержащем эту процедуру, имеется директива Option Explicit, VBA выводит на экран сообщение об ошибке выполнения программы, в котором сообщается, что в программе не определены переменные. Если свойство директива Option Explicit не установлено, сообщение об ошибке не появляется, но информация о свойствах новой рабочей книги не изменяется.
Перепишите эту процедуру так, чтобы она выполнялась правильно.
Sub NewBook()
'Создает новую книгу и заносит данные в ее свойства, 'частично получая их от пользователя,
'а частично - из объекта Application.
Const nbTitle = "Создаем новую книгу"
Workbooks.Add	'добавляем книгу к коллекции Workbooks
With ActiveWorkbook
Title = InputBox(Prompt:= _
"Введите название новой книги:", _
Title:=nbTitle)
Subject = InputBox(Prompt:= _
"Опишите тему новой книги:",
Title:=nbTitle)
Author = Application.UserName
Keywords = ""
Comments = InputBox(Prompt:=
"Введите заметки к этой книге:", Title:=nbTitle)
End With
End Sub
2.	Перепишите приведенную ниже процедуру (которая выводит на экран информацию об операционной среде и пользователе объекта Excel Application) с использование инструкции With.. .End With.
238
Неделя 1
Sub Show_SystemInfo()
'Обращаясь к различным свойствам главного приложения,
'отображает информацию о вашем компьютере
МвдВох "Главное приложение: " & vbCr S
Application.Name S " v" & Application.Version &
”, Build " S Application.Build & vbCr & vbCr & _ "Папка библиотеки: 11 & Application.LibraryPath & vbCr & vbCr i "Пользователь: " & Application.UserName & vbCr & _ "	" & Application.OrganizationName
MsgBox "Операционная система:" &
Application.OperatingSystem & vbCr & vbCr S
"Мышь подключена: " &
Application.MouseAvailable S vbCr & vbCr &
"Всего памяти: ” & Application.MemoryTotal &
vbCr & "Используется памяти: " & Application.MemoryUsed & _ vbCr & "Свободно памяти: " & Application.MemoryFree
End Sub
3.	Найдите ошибку: функция Excel InputBox отличается от функции VBA с таким же именем. У функции Excel на один аргумент больше: аргумент Туре позволяет определить тип данных, которые пользователь может ввести в окне InputBox. Значение 1 аргумента Туре ограничивает ввод пользователя только числами; Excel выведет сообщение об ошибке, если пользователь введет текст, содержащий символы, отличные от цифр и десятичной точки.
В приведенном ниже листинге функция GetNumber должна вернуть число, полученное от пользователя. В ней используется функция InputBox с дополнительным аргументом Туре, который ограничивает ввод пользователя числом. Процедура Test_GetNumber просто использует инструкцию VBA MsgBox для вывода на экран результата функции GetNumber так, чтобы вы могли проверить выполнение функции.
Однако в функции GetNumber содержится ошибка. Если вы попытаетесь ввести текст этой функции, VBA выведет на экран сообщение об ошибке, в котором сообщается, что поименованный аргумент не найден. Измените функцию GetNumber так, чтобы она выполнялось правильно.
Function GetNumber()
'Использует функцию InputBox главного приложения.
'Возвращает число, введенное пользователем.
GetNumber = InputBox(Prompt:="BBeflHTe число:",
Туре:=1)
End Function
Sub Test_GetNumber()
MsgBox GetNumber
End Sub 4 *
4. Если для того, чтобы вставить рабочий лист, используется команда Вставка-
Лист, Excel предлагает имена ЛистЗ или Лист8, в зависимости от того, сколько
День 7. Объекты и коллекции
239
листов вы уже вставили в рабочую книгу. Если вы хотите, чтобы у рабочего листа было другое имя, вы может вручную переименовать его, щелкнув два раза на его корешке или выбрав в меню команду Формат-Лист-Переименовать.
Вам, может быть, удобнее просто вставить рабочий лист и дать ему нужное имя. В этом упражнении вам нужно написать общую процедуру, которая вставляет рабочий лист в активную рабочую книгу и одновременно присваивает новое имя. Вы должны написать эту процедуру “с нуля” в модуле рабочей книги Personal.xls. Назовите процедуру Sheetinsert и запрограммируйте в ней следующие действия.
а)	Создание в начале процедуры переменной сохранения в ней объектной ссылки на активный лист.
б)	Вызов функции InputBox для получения от пользователя имени нового листа и сохранение его в строковой переменной.
в)	Использование метода Add коллекции Worksheets для добавления рабочего листа в активную рабочую книгу.
г)	Присваивание рабочему листу имени, полученного от пользователя, путем обращения к свойству Name нового рабочего листа.
д)	Возврат к активному рабочему листу с помощью метода Select и объектной ссылки, которую вы сохранили в начале процедуры.
Подсказка. Запомните, что свойство ActiveSheet объекта Application возвращает объектную ссылку на активный рабочий лист; кроме того, когда вы используете метод Add для вставки рабочего листа, Excel сделает вставленный новый лист активным. Попробуйте записать макрос, который выполняет те же операции, и изучите используемые методы и объекты.
Итоги первой недели занятий: простая утилита, создающая рабочую книгу
После первой недели знакомства с основами программирования в VBA вы уже наверняка умеете пользоваться программой для записи макросов. Вы должны знать, как запустить Редактор Visual Basic и как использовать окно Object Browser для того, чтобы найти записанный вами макрос. Вы должны свободно пользоваться редактором Visual Basic для редактирования записанных макросов и процедур, а также для написания собственных процедур и функций.
В приведенном ниже листинге содержится полный модуль VBA, в котором используется многое из уроков прошедшей недели.
Листинг Я 1.1. Нтилита дня спздаипя ипвпй рпйпчвй книги
1:	Option Explicit
2:
3:	Sub NewBook()
4:	'Создает новую рабочую книгу, сохраняет ее с именем, введенным
5:	'пользователем. Эта процедура была создана путем редактирования
6:	'записанного макроса.
7:	Const nfTitle = "Создание файла рабочей книги"
240
Неделя 1
8:	Const nfPromptl = "Введите "	'1-я часть приглашения
9:	'2-я часть приглашения
10:	Const nfPrompt2 « " новой книги." & vbCr &
11:	"(Выберите Esc или нажмите Cancel для отмены.)"
12:
13:	Dim FName As String	'для имени файла
14:
15:	'сообщение в строке состояния Excel
16:	Application.StatusBar = "Создание рабочей книги: " &
17:	"процедура NewBook"
18:
19:	Workbooks.Add 'Метод Add создает новую книгу
20:
21:	With ActiveWorkbook	'собираем сведения
22:
23:	'спрашиваем название книги
24:	.Title = InputBox(prompt:=nfPromptl & "название" &
25:	nfPrompt2, Title:=nfTitle)
26:
27:	'спрашиваем тему книги
28:	.Subject = InputBox(prompt:=nfPromptl & “тему” & _
29:	nfPrompt2, Title:=nfTitlej
30:
31:	'спрашиваем автора книги, предлагаем имя
32:	'пользователя (из свойства UserName объекта Application )
33:	'в качестве значения по умолчанию
34:	.Author = InputBox(prompt:=nfPromptl & "автора" & _
35:	nfPrompt2, Title:=nfTitle,
36:	Default:=Application.UserName)
37:
38:	'спрашиваем список ключевых слов
39:	.Keywords = InputBox(prompt:=nfPromptl & "ключевые слова для" &
40:	nfPrompt2, Title:=nfTitle)
41:
42:	'заметки
43:	.Comments = InputBox(prompt:=nfPromptl & "заметки для" &
44:	nfPrompt2, Title:=nfTitle)
45:	End With
46:
47:	'Спрашиваем у пользователя имя файла новой книги. Для этого
48:	'вызываем функцию GetBookName. Функция GetBookName не использует
49:	'приглашение nfPrompt2, поскольку нельзя отказаться от ввода
50:	'имени файла. Вместо этого мы предлагаем пользователю
51:	'сохранить новую книгу в папке Мои документы с именем файла
52:	'Новая_книга.
53:	FName = GetBookName(lPrompt:=nfPromptl & "имя файла " &
54:	"для этой книги." S vbCr &
55:	"Вы ДОЛЖНЫ ввести имя файла." & vbCr &
56:	"Укажите диск и имя файла, если вы хотите " &
57:	"сохранить эту книгу в месте, отличном от" &
58:	"текущего диска и папки.",
59:	lTitle:=nfTitle,
60:	lDflt:="C:\Мои документы\Новая_книга.х1з")
День 7. Объекты и коллекции
241
61:
62:	'Сохраняем новый файл:
63:	ActiveWorkbook.SaveAs FileName:“FName,
64:	FileFormat:=xlNormal,
65:	ReadOnlyRecommended:“False,
66:	CreateBackup:“False
67:	'возвращаем управление строкой состояния в Excel
68:	Application.StatusBar = False
69:	End Sub 'NewBook
70:
71:
72:	Function	GetBookName(iDfIt As	String,
73:	Optional	IPrompt,
74:	Optional	ITitle) As	String
75:	'Спрашивает у пользователя имя книги и возвращает его в виде
76:	'текстовой строки. У этой функции один обязательный аргумент
77:	'для имени книги по умолчанию, и два необязательных аргумента.
78:
79:
80:
81:
82:	'проверяем, указано ли значение аргумента IPrompt
83:	If IsMissing(IPrompt) Then
84:	'если не указано, определяем его.
85:	IPrompt = "Введите имя файла:"
86:	End If
87:
88:	'спрашиваем имя файла.
89:	GetBookName = InputBox(prompt:=lPrompt,
90:	Title:“ITitle, ~
91:	Default:=lDflt)
92:	End Function 'GetBookName
93:
94:
95:	Sub Test_GetBookName()
96:	'Для проверки функции GetBookName
97:	MsgBox GetBookName)"Стандартное")
98:	MsgBox GetBookName(IPrompt:“"приглашение”,
99:	IDf ^“"стандартное")
100:	MsgBox GetBookName)IDfIt:“"стандартное",
101:	ITitle:="заголовок")
102:	End Sub
После того как вы выполните упражнения и ответите на вопросы в конце материала 1-го и2-го уроков, вы можете записать макрос, на котором основана процедура NewBook, а затем добавить к нему интерактивные свойства, которых не было в исходном макросе. Вы можете включить в процедуру NewBook функцию GetBookName и процедуру Test_GetBookName.
В приведенном ниже листинге процедуры и функции содержат большое количество комментариев, многие из которых вы уже встречали раньше. Это обычная техника программирования в VBA; в этих комментариях объясняется назначение и порядок выполнения процедуры NewBook и функции GetBookName. В частности, обратите внима
242
Неделя 1
ние на комментарии в начале объявления каждой процедуры и функции. Они описывают назначение процедуры или функции; а комментарии функции GetBookName также содержат описание аргументов функции и возвращаемого результата.
В строке 1 содержится директива Option Explicit, которая указывает, что все переменные, используемые в этом модуле, должны быть объявлены явно с помощью инструкции Dim.
В строке 3 содержится объявление процедуры NewBook. Эта процедура создана на основе макроса, записанного в упражнении 1 1-го урока, но в нее добавлены некоторые фрагменты, которые не могут быть получены простой записью макроса. В строках 4, 5 и 6 содержится комментарий о том, что представляет собой процедура NewBook и как она была написана. (Исходный макрос создавал новую рабочую книгу и сохранял под именем NewFile.xls.) Процедура NewBook создает новую рабочую книгу, запрашивает у пользователя имя файла, а затем сохраняет файл рабочей книги, при этом созданный файл остается активным.
В строках 7-11 объявляются константы, которые используются в процедуре NewFile. Строковая константа nfTitle служит в качестве заголовка для всех диалоговых окон процедуры NewBook. Строковая константа nfPromptl используется для первой части текста каждого диалогового окна InputBox процедуры NewBook. Строковая константа nfPrompt2 применяется во второй части текста практически каждого диалогового окна InputBox процедуры NewBook. Константа nfPrompt2 содержит символ возврата каретки для новой строки текста диалогового окна. Обратите внимание, как эта константа объединяется с двумя строками и с внутренней константой vbCr. Самый простой способ включить символ возврата каретки в строку — это использовать одну из встроенных констант (vbCr, vbCrLf, vbNewLine) или функцию Chr с кодом символа возврата каретки. Использование таких функций, как Chr не разрешается в объявлениях констант, но вы может объединить строки или добавить числа, используя буквенные или другие значения констант при их объявлении.
В строке 13 объявляются только переменные, которые используются в процедуре NewBook. В переменной Fname хранится имя файла, которое ввел пользователь.
В строке 16 начинается выполнение процедуры NewBook. В этой строке, как описывается в комментарии к строке 15, выводится сообщение в строке состояния Excel, в которой появляется надпись Создание рабочей книги: процедура NewBook. Сообщение выводится в строке состояния, чтобы пользователь имел представление, какое действие выполняется в данный момент.
В строке 19 для создания новой рабочей книги используется метод Add коллекции Workbooks. Эта строка взята их исходного макроса, к нему добавлен комментарий. Запомните, одним из методов использования объектов и методов может служить выполнение и запись макроса для конкретной задачи, а затем его редактирование или копирование записанного макроса в программу. При выполнении строки 19 Excel создает новую активную рабочую книгу.
Строка 21 начинается с инструкции With... End With, в которой пользователь вводит информацию о новой рабочей книге. Инструкция With... End With была добавлена в исходный макрос. Не забывайте, что ActiveWorkbook является свойством объекта Application, которое возвращает текущую рабочую книгу; в данном случае это новая рабочая книга, которая была создана в строке 19.
Информация о свойствах (Title, Subject, Author, Keywords и Comments) также добавлена в исходный макрос. Информация о каждом из свойств этой рабочей книги заполняется пользователем с помощью вызова функции InputBox. (Речь идет об информации, которая находится на вкладке Общие диалогового окна Свойства, которое можно открыть, выбрав команду Файл-Свойства.)
День 7. Объекты и коллекции
243
Обратите внимание, что когда, одно из свойств рабочей книги — Title, Subject, Author, Keywords или Comments — находится внутри в инструкции, ей предшествует разделитель (.). Этот разделитель необходим для того, чтобы отделить определенное свойство и объект, которому оно принадлежит (об этом рассказывалось на 7-м уроке). Если вы не будете использовать разделитель, процедура NewBook может выполняться некорректно.
В строке 24 пользователь с помощью функции InputBox вводит строку, которая присваивается свойству Title активной рабочей книги. Обратите внимание, что в вызове этой функции используются поименованные аргументы. Аргумент диалогового окна prompt является строкой, связанной с константой nfPromptl, буквенной строкой и строковой переменной nfPrompt2. Заголовок диалогового окна, выводимого функцией InputBox, определяет строковая константа nfTitle.
В строке 28 пользователю предлагается ввести строку для свойства Subject. Обратите внимание, что в вызове функции InputBox используются поименованные аргументы. При использовании комбинации констант, буквенных строк и строковых переменных программист имеет возможность создавать определенные сообщения для каждого диалогового окна инструкции InputBox. Это экономит время на вводе строки при каждом использовании инструкции.
В строках 31-33 содержится комментарий об операциях, выполняемых в строке 34. В этой строке для получения от пользователя некоторой строки также используется инструкция InputBox; однако в этом случае в нее включен аргумент, который предлагает некоторое значение по умолчанию. Значение этого аргумента извлекается из свойства UserName объекта Application, которое содержит имя пользователя операционной системы Windows. Если при установке Windows вы не вводили имя пользователя, то свойство UserName будет содержать имя пользователя, введенного при установке приложения Excel.
В строках 39 и 43 вводятся ключевые слова и тема, при этом используется та же техника, что и в строках 24 и 28. В строке 45 завершается инструкция With... End With.
В строках 47-52 содержится комментарий, в котором рассматривается дальнейшее выполнение процедуры и объясняется, почему для получения от пользователя информации не используется переменная nfPrompt2.
В строке 53 вызывается функция GetBookName, а ее результат присваивается переменной FName. Функция GetBookName написана специально для получения от пользователя имени файла для рабочей книги. Ее можно использовать в любой процедуре или макросе при открытии, закрытии или создании новой рабочей книги. Выполнение функции GetBookName будет более детально рассмотрено на следующих уроках. Функция вызывается в строке 53 и передает аргументы для диалогового окна InputBox, заголовка сообщения и предлагаемого по умолчанию имени файла рабочей книги.
Для сохранения рабочей книги в строке 63 используется метод SaveAs объекта ActiveWorkbook. Эта команда также используется в исходном макросе, причем строка, определяющая имя копии файла, была буквенной. А в процедуре NewBook инструкция SaveAs изменена таким образом, что поименованный аргумент Filenames в качестве имени файла использует любую строку, содержащуюся в переменной FName. Кроме того, инструкция была упрощена, а несколько поименованных аргументов из исходного макроса были удалены (это необязательные аргументы, и им были присвоены строки нулевой длины). При записи макросов необязательные аргументы используется часто. Поэтому, если вы видите, что поименованные аргументы передают пустые строки, можете смело удалять их.
И наконец, в строке 68 контроль над строкой состояния передается приложению Excel. Эта строка очень важна; если ее не будет, то сообщение, определенное в строке
244
Неделя 1
16, будет выводиться в строке состояния до тех пор, пока вы не закроете Excel или пока другая процедура не перехватит над ним контроль.
Процедура NewBook заканчивается в строке 69 ключевыми словами End Sub. В оставшейся части листинга описываются другие процедуры и функции модуля. Вы, наверное, помните, что исходные макросы закрывали рабочую книгу после ее создания и сохранения. Но обычно пользователь сразу начинает работать с созданной книгой, поэтому из этого варианта процедуры NewBook закрывающие книгу инструкции были удалены.
Строка 72 начинается с объявления функции GetBookName. Эта функция имеет три аргумента и возвращает строковое значение. Первый аргумент, IDfIt, — обязательный; два других, IPrompt и ITitle, — необязательные. Обратите внимание, что объявление функции занимает несколько строк и они объединены символом продолжения строки. Это было сделано для наглядности. В строках 75-80 комментируется назначение и объясняется порядок выполнения функции GetBookName.
В строке 83 начинается выполнение функции GetBookName. В этой строке используется структура If... Then (о ней очень кратко рассказывалось в этой главе) и функция IsMissing. Здесь определяется, был ли включен аргумент prompt при вызове функции GetBookName. Если он не был включен, строка 85 присваивает аргументу prompt значение.
Строка 89 является наиболее значимой: она содержит инструкцию присваивания функции, в которой определяется результат функции и одновременно используется функция InputBox для получения этого значения. Обратите внимание, что аргументы функции GetBookName просто передаются в качестве аргументов функции InputBox.
Функция GetBookName была специально написана для процедуры NewBook по нескольким причинам. Во-первых, запрос имени файла рабочей книги встречается во многих процедурах. Во-вторых, использование функции для получения имени файла дает возможность сохранить эту функцию в определенном модуле и не вводить ее каждый раз в процедуре, где существует такая необходимость. Если вы хотите видоизменить такую процедуру — например, изменяя каким-либо образом имя файла, — вам следует просто внести некоторые изменения в функцию GetBookName.
Строка 92 содержит ключевые слова End Function, которые завершают определение функции GetBookName.
И наконец, в строках 95-102 определяется процедура с именем Test_GetBookName, которая проверяет выполнение функции GetBookName на все возможные комбинации с обязательными и необязательными аргументами и выводит результаты этой проверки на экран. Если бы функция GetBookName была сложнее, в процедуре Test_GetBookName использовались бы переменные. На экран выводились бы исходные значения аргументов, чтобы убедиться, что функция GetBookName не изменяет их. В данном случае функция довольно проста и сразу видно, что ее аргументы не изменяются — кроме аргумента IPrompt, который изменяется только в том случае, если он не стоит на первом месте.
День 7. Объекты и коллекции
245
246
Неделя 1
<sAms
Освой самостоятельно
Неделя 2
Краткий обзор
Закончилась первая неделя изучения программирования на Visual Basic for Applications. К настоящему моменту вы уже должны уметь вводить, редактировать и использовать процедуры и функции в модулях, создавать и редактировать входящие в них макросы; самостоятельно писать процедуры и функции.
Что впереди
Вторая неделя, так же как и первая, предполагает изучение большого количества материала. Вы познакомитесь со множеством ключевых свойств Visual Basic for Applications.
День 8-й, “Программные решения на Visual Basic”. Вы познакомитесь с использованием структур принятия решений If.. .then и Select Case.
День 9-й, “Циклы”, будет посвящен повторяющимся действиям в процедурах и использованию циклов.
День 10-й, “Прогрессивная техника использования типов данных и переменных”. Мы рассмотрим основные вопросы сферы действия и то, каким образом увеличивается или ограничивается область влияния переменных, процедур и функций.
День 11-й, “Создание собственных типов данных и объектов классов”. Тема занятия — создание обычных типов данных и с ключевым словом Туре. Также рассматривается использование модулей класса для создания собственных объектов классов; возможность создания обычных объектов классов для разработки новых объектов для Excel или для улучшения или изменения характеристик встроенных в Excel объектов.
День 12-й, “Создание библиотек и программ: техника модульного программирования”. Вы научитесь создавать библиотеки процедур и функций VBA. На этом уроке мы приступим также к изучению программ, состоящих из нескольких процедур и функций, и узнаем, как добавлять список аргументов в создаваемые процедуры.
День 13-й, “Управление файлами в VBA”. Это занятие посвящено вопросам применения функций и инструкций управления файлами для получения информации о файлах, создания и удаления папок на дисках, копирования и уничтожения файлов. Также показано использование обычных файлов данных с операторами VBA Open, Write и Read.
День 14-й, “Массивы”, посвящен созданию и сортировке массивов.
Материал второй недели основывается и подается с учетом информации, предложенной вашему вниманию на занятиях первой недели учебного курса. Надеемся, вы отвечаете на все контрольные вопросы в конце каждого урока и полностью выполняе
День 8-й. Принятие решения в Visual Basic for Applications
247
те все упражнения. Лучший путь превратить новый изучаемый материал в реальные практические знания — использование его в работе. Иными словами, для изучения программирования необходимо писать программы.
В заключение первой недели вы научитесь писать простые процедуры и функции для выполнения отдельных задач. К концу второй недели вы должны быть готовы писать сложные программы, выполняющие серьезную работу. Как и первая неделя, вторая неделя учебного курса насыщена большим количеством информации. Однако, изучая по одной главе в день, вы не будете иметь проблем.
248
Неделя 2
Принятие решения в Visual Basic fur Applications
Сегодня мы научимся создавать процедуры и функции, работа которых зависит от различных условий. На этом занятии мы рассмотрим следующие вопросы.
•	Какие команды принятия решений и перехода существуют и как они работают.
•	Как создавать логические инструкции, с помощью которых в процедуре производится выбор одной из нескольких ветвей программы.
•	Как принимать простые решения в функции или процедуре с помощью структуры принятия решений If.. .Then.
•	Как принимать более сложные программные решения с использованием вложенных структур If.. .Then
•	Как принимать решения при одновременном существовании множества вариантов с помощью структуры Select.. .Case.
•	Как изменить последовательность работы программы с помощью инструкции GoTo.
•	В каких случаях и каким образом можно закончить процедуру или функцию преждевременно.
•	Как использовать аргумент Buttons в функции MsgBox, чтобы позволить пользователю процедуры сделать простой выбор.
Изучение команд принятия решений в VBA
До настоящего момента речь шла о процедурах и функциях, которые выполняются в VBA совершенно прямолинейным способом, в значительной степени похожим на тот, которым выполняется записанный макрос: начиная с первого оператора после строки описания процедуры или функции. Затем выполняются все последующие инструкции строка за строкой до тех пор, пока не будет достигнута инструкция End Sub или End Function, обозначающая окончание выполнения этой процедуры или функции, или до тех пор, пока не возникнет ошибка времени выполнения. Выполнение инструкций происходит от начала до конца, последовательно и без изменений.
День 8-й. Принятие решения в Visual Basic for Applications
249
Процедуры и функции, подобные упомянутым выше, хотя и могут выполнять достаточно сложные задачи, не в состоянии принимать решения, влияющие на работу программы при разнообразных условиях. Существует множество ситуаций, при которых возникает потребность выполнения процедурой или функцией различных действий при различных условиях. Например, в создаваемой процедуре, которая получает имя рабочей книги от пользователя и затем открывает ее, желательно, чтобы поступало предложение создать такую рабочую книгу, если она до того момента не существовала. В подобной ситуации могут быть использованы инструкции принятия решений VBA, предоставляющие пользователю возможность создания рабочей книги. Упомянутая процедура применяла бы тогда инструкции принятия решений каждый раз, чтобы выполнять выбор пользователя и либо создавать рабочую книгу, либо завершать выполнение процедуры.
Инструкции принятия решений часто служат для обработки специальных видов данных и последующего выбора действий в зависимости от конкретных данных. Например, может быть написана процедура, которая проверяет столбец на листе в рабочей книге для того, чтобы убедиться, что значения всех введенных чисел в этом столбце находятся между 1 и 10. Затем процедура может проверить каждое число в столбце по отдельности и выполнять некоторые специальные действия всякий раз, когда встретится число, значение которого не попадает в заданный интервал.
Программные решения не всегда имеют дело с задачами. Например, для того, чтобы пользователь процедуры сделал какой-нибудь выбор дальнейших действий, может быть использована функция InputBox для ввода текста пользователем или функция MsgBox, предоставляющая возможность пользователю сделать выбор с помощью щелчков на командных кнопках в интерактивном режиме (более детальное описание применения функции MsgBox приведено в конце главы). После получения выбора пользователя процедура должна быть способна осуществить действия, соответствующие этому выбору.
Процедуры, конечно, не могут на самом деле “принимать решения” подобно человеку. Однако они могут выбирать предопределенные действия, основанные на простых решениях и принимать относительно сложные решения, совмещая более простые.
При использовании инструкций принятия решений определяется условие или набор условий, при которых в процедуре исполняется та или иная ветвь. Поскольку инструкции принятия решений оказывают влияние на очередность выполнения программы, они часто называются инструкциями управления потоком (flow control) или инструкциями управления программой, но более известны как инструкции условного и безусловного перехода.
Инструкция условного перехода — это структура принятия решения, которая выбирает ту или иную ветвь выполнения процедуры на основании некоторого предопределенного условия или ряда условий. Инструкция безусловного перехода — это инструкция, которая просто изменяет очередность выполнения инструкций программы независимо от какого-либо специального условия. Условный переход используется гораздо чаще, чем безусловный.
Инструкция условного перехода — такая как If.. .Then — как правило, состоит из следующих элементов.
•	Логическое выражение, которое устанавливает условия, при которых конкретная последовательность инструкций VBA должна быть выполнена.
•	Ключевые слова VBA, которые обозначают начало и конец инструкций, подлежащих условному выполнению.
Когда при выполнении процедуры VBA встречается инструкция условного перехода типа If.. .Then, сначала вычисляется логическое выражение, описывающее условия, при которых должны быть выполнены конкретные действия. Если логическое выра
250
Неделя 2
жение принимает значение True, выполняются соответствующие инструкции. Когда встречается инструкция безусловного перехода GoTo, немедленно начинает выполняться часть программы, определенная в инструкции перехода.
Конкретный критерий, в соответствии с которым в VBA осуществляется ветвление операций, строится на основе логического выражения, тем самым задавая выбор последовательности операций. Для построения логических выражений в операциях перехода в VBA используются инструкции сравнения и логические инструкции (описанные в уроке 4-го дня).
Осуществление простого выбора
Наиболее простые конструкции осуществления выбора — это If... Then и If...Then...Else. Выражение If...Then предоставляет возможность реализации простого ветвления исполняемой программы с одним разветвлением. Тесно связанная с ним конструкция If.. .Then.. .Else предоставляет возможность разветвления исполняемой программы на две ветви, в зависимости от того, является ли указанное условие истинным.
Выбор с помощью оператора lf...Then
Уже были рассмотрены примеры инструкций If...Then, проверяющие аргумент функции при ее вызове. Инструкция If...Then выбирает одну из альтернативных ветвей программы или функции.
Инструкция If.. .Then имеет две различные синтаксические формы. Более простую форму имеет инструкция If.. .Then:
If condition Then statements
где condition — некоторое логическое выражение, a statements — одна или несколько инструкций, причем инструкции должны помещаться в одной строке. При выполнении инструкции такого типа прежде всего исследуется логическое выражение, представленное в condition. Если оно принимает значение True, то выполняется инструкция или инструкции, расположенные после ключевого слова Then и до конца строки. После этого продолжается выполнение следующей инструкции, находящейся на следующей строке.
Если логическое выражение, представленное в condition, принимает значение False, переход к выполнению следующей инструкции происходит сразу без выполнения альтернативной ветви. Следующий фрагмент процедуры показывает типичную инструкцию If.. .Then.
If temperature>100 then MsgBox "Слишком жарко!"
Размещение нсшьпх iicmpimnB i i|iol строке
Можно размещать несколько инструкций в оной строке, разделяя их двоеточием (:), как показано в следующем
statement! : statement2 : statements
В этой строке statement!, statement2, statements представляют некоторые простые инструкции VBA. В одной строке можно разместить произвольное количество инструкций, которое ограничивается только длиной строки.
Однако строки с большим количеством инструкций менее удобны для чтения и восприятия, и поэтому чаще применяется размещение по одной инструкции в каждой строке.
День 8-й. Принятие решения в Visual Basic for Applications
251
В листинге 8.1 показана незначительно измененная версия функции GetBookName, рассмотренная ранее в листингах раздела “Итоги первой недели занятий: простая утилита, создающая рабочую книгу”.
Листмня	8.1.	Шцнкция	6вТ8ввкНатв
1:	Function	GetBookName(iDfIt As	String,
2:	Optional	Prmpt,
3:	Optional	iTitle) As	String
4:
5:	'Проверяем, есть ли в списке аргументов строка Prmpt
6:	If IsMissing(Prmpt) Then Prmpt = "Введите имя книги:"
7:
8:	'С помощью функции InputBox получаем имя книги.
9:	GetBookName = InputBox(prompt:=Prmpt,
10:	Title:=lTitle,
11:	Default:=lDfIt)
12:	End Function 'GetBookName
Работа этой функции описана в упомянутом выше разделе предыдущей главы. В первой строке объявляется функция с одним обязательным и двумя необязательными аргументами и возвращаемым значением типа String (строковая или переменная символьного типа). Рассмотрим строку 6, в которой находится инструкция If.. .Then. При выполнении этой строки сначала вычисляется условное выражение IsMissing(Prmpt). Функция IsMissing возвращает True, если аргумент Prmt отсутствует в списке аргументов во время вызова функции GetBookName. Если Ismissing возвращает True, выполняется инструкция Prmpt = "Введите имя книги:", которая записывает строку в переменную Prmpt. Если Ismissing возвращает False, инструкция после ключевого слова Then не выполняется и значение переменной Prmpt не изменяется.
Таким образом, функция GetBookName использует значение Prmpt по умолчанию, когда необязательный аргумент Prmpt отсутствует. Присваивание значения переменной Prmpt происходит только в том случае, когда функция IsMissing возвращает значение True.
Наконец в строке 9 вызывается функция InputBox для получения возвращаемого ею значения.
Вторая форма инструкции If...Then называется блоком If. В блоке If...Then условия и инструкции размещаются в отдельных строках, как показано в следующем примере:
If condition Then statements End If
Здесь condition, как и в однострочной инструкции If.. .Then, представляет некоторое логическое выражение, определяющее условие, при котором выполняются альтернативные инструкции, a statements — одну или несколько инструкций; инструкции могут быть на одной или на нескольких строках. Также возможен случай, когда инструкции отсутствуют. Наконец, ключевые слова End If являются признаком окончания блока инструкций. На одной строке с ними может находиться только комментарий.
Так же как и в случае однострочной инструкции, сначала вычисляется логическое выражение, представленное в condition. Если результатом является True, последовательно выполняются инструкции, с первой строки после строки с ключевыми словами
252
Неделя 2
If...Then и до тех пор, пока не будут достигнуты ключевые слова End If. Затем выполняется инструкция, следующая за ключевыми словами End If.
Если результатом вычисления логического выражения является False, выполняется первый оператор после End If.
Некоторые примеры блоковых инструкций If...Then были рассмотрены на предыдущих уроках. В следующем примере показана типичная блоковая инструкция If.. .Then:
If temperature > 100 Then
MsgBox "Слишком жарко!"
End If
В рассмотренном примере, если значение переменной temperature оказывается больше 100, выполняется функция MsgBox, выводящая сообщение Слишком жарко! Если значение переменной temperature оказывается равным или меньше 100, выполняется первая инструкция, расположенная после ключевых слов End If. Напомним, что в одной строке с ключевыми словами End If не должно быть больше ничего, кроме комментария.
Так надо _____________________________________________________________________________________
Используйте однострочную инструкцию If.. .Then, когда логическое выражение для условия не слишком длинное и альтернативная ветвь программы содержит одну или две инструкции.
Используйте блоковую инструкцию If...Then, когда логическое выражение длинное или альтернативная ветвь программы содержит много инструкций. Блоковая форма более удобна для чтения и понимания.
Не забывайте, что ключевые слова End.. .If, завершающие блок If, должны быть единственными в строке.
Выбор между ветвями с помощью IL.Tbeo...Else
Инструкция If...Then позволяет выбрать одну альтернативную ветвь инструкции в процедуре. Чаще, однако, бывает более удобно выбирать одну из двух различных альтернативных ветвей инструкции в зависимости от соответствующих условий. Именно для этих целей в VBA предусмотрены инструкции If.. .Then.. .Else и If.. .Then.. .Elself.
Аналогично If...Then инструкция If...Then...Else имеет две формы: однострочную и блоковую. Общий синтаксис для однострочной инструкции If.. .Then.. .Else имеет вид
If Condition Then Statements Else Elsestatements
где Condition представляет некоторое логическое выражение. Statements и Elsestatements представляют одну или несколько инструкций VBA.
Аналогично однострочной If...Then все инструкции и ключевые слова однострочной инструкции If.. .Then.. .Else должны располагаться в одной строке.
При выполнении однострочной инструкции If.. .Then.. .Else сначала вычисляется логическое выражение, представленное в Condition. Если результат — True, выполняются инструкции, представленные в Statements, а затем выполняются инструкции, следующие за данной.
День 8-й. Принятие решения в Visual Basic for Applications
253
Если же результатом вычисления логического выражения оказывается False, перед переходом к следующей инструкции программы выполняются инструкции, представленные в ElseStatements. Типичная инструкция If.. .Then.. .Else имеет вид:
If temperature>100 Then MsgBox "Жарко!” Else MsgBox "Умеренно жарко!"
В этом примере, если значение переменной temperature больше 100, вычисление условного выражения дает в результате True и вызывается функция MsgBox, выводящая на экран сообщение. В противном случае, когда проверяемое значение temperature оказывается равным или меньшим 100, условное выражение дает False и на экран выводится альтернативное сообщение.
Как видно из представленного примера, однострочная форма инструкции If.. .Then.. .Else не совсем удобна для чтения и понимания. Также, поскольку все элементы такой инструкции должны располагаться в одной строке, размер и количество инструкций в альтернативно выполняемых ветвях ограничены длинной строки.
Блоковая инструкция If.. .Then.. .Else более удобна для чтения и понимания и, поскольку ее инструкции могут располагаться в разных строках, ограничения на их количество и размер здесь исчезают. Общий синтаксис блоковой инструкции If.. .Then имеет следующий вид.
If Condition Then
Statements
Else
ElseStatements
Endlf
Составные инструкции и принцип их взаимодействия здесь те же, что и в аналогичной однострочной инструкции. Основное отличие заключается в появившихся в блоковом варианте ключевых словах End If, роль которых в однострочном варианте выполняет символ конца строки. Здесь, так же, как и в предыдущих случаях, в строке с ключевыми словами End If не должно находиться больше ничего, за исключением комментария.
Инструкция If.. .Then.. .Else выбирает одну из двух ветвей, но никогда — обе одновременно. В следующем примере показана типичная блоковая инструкция If...Then...Else:
If temperature >100 Then
MsgBox "Жарко!"
Else
MsgBox "Умеренно жарко!"
End If
Этот пример содержит те же самые инструкции и работает точно так же, как и рассмотренная выше однострочная форма этого примера. Отличия последнего примера состоят в более удобном для чтения виде и в возможности вводить дополнительные инструкции.
На листинге 8.2 показана очередная версия функции GetBookName с блоковой инструкцией If.. .Then.. .Else.
Листинг 8.2. Блоковая инструкция if.. .Then.. .Else
1:	Function GetBookName(ByVai IDflt As String,
2:	Optional Prmpt,
3:	Optional iTitle) As String
254
Неделя 2
4:
5:	'Есть ли Prmpt в списке аргументов?
6:	If IsMissing(Prmpt) Then Prmpt = "Введите имя книги:"
7:
8:	'если имя по умолчанию	не	пусто,	преобразовать его
9:	'в верхний регистр,	иначе	-	создать	его.
10:	If IDflt о "" Then
11:	IDflt = UCase(lDflt)
12:	Else
13:	IDflt = "Новая_книга.ХЬЗ"
14:	End If
15:
16:	'С помощью InputBox получить имя книги.
17:	GetBookName = InputBox(prompt:=Prmpt,
18:	Title:=lTitle,
19:	Default:=lDflt)
20:	End Function
21:
22:
23:	Sub Test_GetBookName()
24:	'Процедура для проверки функции GetBookName
25:	МздВок	GetBookName("")
26:	MsgBox	GetBookName("Новая книга")
27:	MsgBox	GetBookName(Prmpt:="npHrnameHHe",	lDflt:="KHHra")
28:	MsgBox	GetBookName(lDflt:="KHHra",	lTitle:="3aronoBOK")
29:	End Sub
Эта версия процедуры GetBookName работает точно так же, как и предыдущая версия, показанная в листинге 8.1, за исключением добавленной в строках с 10 по 14 инструкции If.. .Then.. .Else.
Первая выполняемая строка этой функции — это строка 6, на которой расположена однострочная инструкция If...Then, проверяющая наличие аргумента Prmt при вызове функции. Если аргумент отсутствует, выполняется инструкция присваивания, расположенная в конце шестой строки и засылающая в переменную предусмотренное значение.
Следующая выполняемая строка, десятая, является началом блоковой инструкции If.. .Then.. .Else. При выполнении этой строки вычисляется логическое выражение IDflt <> 11". Если переменная IDflt — не пустая строка (не нулевой длины), то результатом вычисления логического выражения будет True и при вызове функции GetBookName в качестве аргумента IDflt будет использована пустая строка.
Если аргумент IDflt не содержит пустой строки, выполняется инструкция lDflt=DCase(IDflt), использующая функцию UCase для преобразования строки аргумента IDflt в верхний регистр. Затем выполнение инструкций продолжается со стро-ки17, первой строки после ключевых слов End If инструкции If.. .Then.. .Else.
Если аргумент IDflt содержит пустую строку, выполняется ветвь Else инструкции If.. .Then.. .Else и, следовательно, инструкция lDflt="NEWFILE.XLS", засылающая новую строку в переменную IDflt. Затем выполнение инструкций продолжается с 17 строки, первой строки после ключевых слов End If рассмотренной инструкции If...Then...Else.
День 8-й. Принятие решения в Visual Basic for Applications
255
В этой строке определяется результат функции GetBookName, как результат вычисле
ния функции GetBookName).
InputBox (значение функции InputBox заносится в результат функции
ПРИМЕЧАНИЕ
Заметим, что в объявлении этой версии функции GetBookName (строка 1) аргумент IDflt объявлен как передаваемый по значению, так как инструкции If.. .Then.. .Else в строкахс 10 по 14 изменяют значение этой переменной. Ключевые слова ByVai добавлены для того, чтобы переменная, передаваемая в качестве аргумента IDflt, не меняла своего значения вне функции GetBookName. (Напомним, что передавая функции аргумент по значению, вы передаете копию переменной, как показано в уроке 6-го дня).
Наконец строки с 23-й по 29-ю содержат процедуру, используемую для проверки работы функции GetBookName. Теперь, каждый раз при вызове функции будет выводиться предлагаемое имя рабочей книги прописными буквами. Если же для имени рабочей книги в функцию была записана пустая строка, функция выводит собственное имя файла рабочей книги: NEWFILE.XLS.
В последующих разделах данной книги приводится множество примеров использования инструкций If...Then и If.. .Then.. .Else.
Осуществление сложного выбора
До сих пор было рассмотрено программирование инструкций перехода с выбором некоторой альтернативной ветви выполняемой процедуры или с выбором одной из двух альтернативных ее ветвей. Однако чаще в процедурах возникает необходимость в более сложных видах выбора между тремя, четырьмя или более ветвями.
Вложенные инструкции If ...Then
Для более сложных программных решений некоторые инструкции If...Then или If.. .Then.. .Else можно размещать внутри другой инструкции аналогичного типа, которая называется вложенной инструкцией. (Вложение означает размещение инструкции управления программой одного типа внутри другой). Несмотря на то что запрета на вложение однострочных форм инструкций If...Then и If...Then...Else не существует, такое вложение представляет значительную трудность для прочтения и понимания. Поэтому при вложении инструкций указанного типа, как правило, используется их блоковая форма.
В листинге 8.3 показана простая процедура, иллюстрирующая взаимодействие вложенных инструкций If.. .Then.. .Else. В процедуре EvalTemperature обрабатывается некоторое числовое значение, вводимое пользователем.
ПРИМЕЧАНИЕ
В примере в листинге 8.3 для простоты показано вложение только одной инструкции If.. .Then.. .Else в другую. При желании такое вложение может быть выполнено сколько угодно раз.
256
Неделя 2
flucmuua 8.3. Вложенные инструкции if.. .Then.. .Else
1:	Sub EvalTemperaturef)
2:
3:	Dim temperature
4:
5:	temperature = Application.InputBox(
6:	prompt:="Сколько градусов на	приборе?",
7:	ТШе:="Процедура	EvalTemp”,
8:	Туре:=1)
9:
10:	If temperature > 100 Then
11:	MsgBox "Очень горячо!"
12:	Else
13:	If temperature	> 50 Then
14:	MsgBox "Нормально!"
15:	Else
16:	MsgBox "Слишком холодно!"
17:	End If
18:	End If
19:	End Sub
В третьей строке процедуры объявляется переменная temperature, которая затем используется для хранения вводимого пользователем значения. В строке 5 используется функция InputBox объекта Application, необязательный аргумент Туре:=1 говорит о том, что вводить можно только числовые значения. При попытке ввода пользователем нечислового значения на экран выводится сообщение об ошибке. Аргумент Туре:= в VBA-функции InputBox отсутствует.
Рассмотрим в листинге строки с 10-й по 18-ю. При выполнении инструкции в строке 10 вычисляется логическое выражение temperature>100. Если значение, хранимое в переменной temperature, оказывается больше 100, выполняется строка 11 и на экран выводится сообщение Очень жарко! Затем выполняется инструкция, следующая за ключевыми словами End If, — в данном случае строка 18. Если же значение температурной переменной оказывается равным или меньшим 100, условие для инструкции If, начинающейся в JO-й строке, не выполняется и, следовательно, выполняется ветвь данной инструкции, расположенная после ключевого слова Else, начиная со строки 13.
Здесь начинает выполняться вложенная, или внутренняя, инструкция If.. .Then.. .Else, полностью находящаяся внутри внешней инструкции If Then...Else, берущей начало в строке 10.
Если логическое выражение temperature>50 имеет значение True, выполняется инструкция строки 14 и на экран выводится сообщение Нормально!
Затем выполняется первая после ключевых слов End If инструкция — в данном случае это конец внешней инструкции End.. .If.. .Else.
Если условие внешней инструкции в строке 13 не выполняется (False), начинает выполняться ветвь Else внутренней инструкции If.. .Then.. .Else в строке 16 и на экран выводится сообщение Слишком холодно!
Затем выполняется первая инструкция после ключевых слов End If в строке 17.
Ключевые слова If.. .Then в строке 10, Else — в строке 12 и End If — в строке 18 являются составными частями внешней инструкции If.. .Then.. .Else. Ключевые слова
День 8-й. Принятие решения в Visual Basic for Applications
257
If...Then в строке 13, Else — в строке 15 и End If — в строке 17 являются составными частями внутренней инструкции If.. .Then.. .Else. Для ясного понимания структуры программы и для того чтобы без большого труда различать составные части внешних и внутренних инструкций, последние можно располагать с некоторым отступом от начала строки, как показано в листинге 8.3.
Так надо
Используйте варьирование отступа от начала строки в ваших программах, чтобы сделать их более удобными для чтения и понимания. Это особенно важно при использовании вложенных инструкций If... Then... Else
Так не надо
.г .	'A-
Не пишите программ в таком стиле:
ЙЙЙЙЙИ^^
, 50 Then ®Й|ЙЙЖ
MsgBox "Нормально!"
Ййй|8|йУ^^^^^^
MsgBox "Слишком холодно!" End If
ВЕОЭД|||й®11в
Хотя эта программа будет выполняться так же, как и инструкции в строках 10-18 в листинге 8.3, здесь гораз-до труднее разобраться во вложенности инструкций.
Использование lf...Then...Elself
В УВД предусмотрена также упрощенная версия инструкции If.. .Then.. .Else, которая может также быть использована как упрощенный эквивалент вложенных инструкций If.. .Then.. .Else, представленных в листинге 8.3. Такой сокращенной версией является инструкция If.. .Then... Elself.
Общий синтаксис этой инструкции имеет вид:
If Condition! Then
Statements?
Elself Condition?
ElselfStatements
[Else
Elsestatements]
End If
Здесь Condition! и Condition? представляют некоторые логические выражения; а Statements, ElselfStatements и Elsestatements — несколько инструкций.
При выполнении инструкции If.. .Then.. .Elself сначала вычисляется логическое выражение, представленное в Condition!. Если результат вычисления — True, выполняются все инструкции, представленные в Statements. Затем выполняется первая инструкция после ключевых слов Enf If.
258
Неделя 2
Если результатом вычисления логического выражения Condition 1 оказывается False, вычисляется логическое выражение, представленное в Condition2. Если оно вычисляется как True, выполняются инструкции, представленные в ElselfStatements, после чего начинается выполнение следующей после ключевых слов End If инструкции.
Наконец, если условие Condition2, так же, как и условие Conditionl, не выполняется, переход к следующей за ключевыми словами Endlf инструкции происходит сразу. Однако, если в инструкции If.. .Then.. .Elself имела место необязательная вставка (строки Else и ElseStatements в квадратных скобках), то в случае невыполнения обоих условий Conditionl и Condition2 перед переходом к выполнению следующей инструкции выполняются инструкции, обозначенные как ElseStatements.
Следующий пример иллюстрирует строки 10-18 листинга 8.3, переработанные на случай использования в нем инструкции If. .Then. .Elself:
If temperature>100 Then
MsgBox "Слишком жарко!"
Elself temperature >50 Then
MsgBox "Нормально!11
Else
MsgBox "Очень холодно!"
End If
Представленная инструкция If.. .Then.. .Elself работает точно так же и с тем же результатом. что и вложенные инструкции If.. .Then.. .Else в строках 10-18 листинга 8.3. Однако инструкция, представленная в последнем примере, более компактна.
При желании можно делать несколько вставок Elself в инструкцию If.. .Then.. .Elself, располагая их перед ключевым словом Else. Тогда при выполнении добавленных условий будут выполняться соответствующие им инструкции ElselfStatements.
Использование инструкций If.. .Then.. .Elself —- это дело личного вкуса. Большинство программистов отдают предпочтение вложенным инструкциям If.. .Then.. .Else как более наглядным и понятным. Инструкция If.. .Then.. .Elself была включена в BASIC для выполнения тех же задач, что и инструкция Select.. .Case, рассматриваемая в следующем разделе. Она оставлена в языке VBA для возможности трансляции написанных ранее на языке BASIC программ. Для случаев, когда необходимо сделать очень сложный выбор, инструкция Select Case подходит намного лучше, чем инструкции, рассмотренные ранее.
Применение инструкции Select Case
Как было показано в примерах вложенных инструкций If.. .Then.. .Else в листинге 8.3, а также с помощью инструкции If.. .Then.. .Elself достаточно легко производился выбор из трех возможных ветвей выполнения процедуры. Однако какие инструкции и каким образом лучше применять при необходимости выбора в процедуре из 5, 8 или 10 ветвей?
Для выбора среди нескольких возможных ветвей исполняемой процедуры можно использовать многократно вложенные инструкции If Then...Else, однако в этом случае также многократно возрастают трудности, связанные с ее пониманием и отладкой.
Также возможно использование инструкции If.. .Then.. .Elself со многими вставками Elself. Но и здесь возникают трудности чтения и восприятия.
День 8-й. Принятие решения в Visual Basic for Applications
259
Так надо
Используйте инструкцию Select Case при необходимости выбора среди более чем трех или четырех ветвей выполнения процедуры.
Так не надо
Не используйте слишком многих вложений инструкций типа If...Then...Else одна в одну. Работа с такими инструкциями слишком утомительна для человеческого понимания.
К счастью, в VBA предусмотрена условная инструкция перехода для выбора из большого количества ветвей процедуры — инструкция Select Case. По принципу работы Select Case аналогична группе независимых инструкций If, однако несколько удобнее. В ней используются ключевые слова Select Case со многими инструкциями Case, каждая из которых выполняется при наличии соответствующих ей условий. Однако при каждом выполнении инструкции Select Case может быть выполнена только одна из инструкций Case. Любая ветвь Case может содержать несколько инструкций.
Инструкция Select Case имеет следующий общий синтаксис.
Select Case TestExpression
Case ExpressionListl
statements1
Case ExpressionList2
statements 2
Case ExpressionListN
statementsN
[Case Else
ElseStatements]
End Select
Здесь TestExpression — некоторое числовое или символьное выражение, обычно переменная; ExpressionListl, ExpressionList2 и ExpressionListN представляют список логических выражений, каждое выражение в списке должно быть отделено запятыми; statementsl. statements2, statementsN и ElseStatements представляют несколько инструкций VBA. При желании в инструкцию Select Case можно сделать сколько угодно вставок Case Expressionlist.
При выполнении инструкции Select Case сначала вычисляется TestExpression и затем результаты этого вычисления сравниваются с каждым из выражений, хранящихся в каждом из ExpressionList. Если значения TestExpression соответствуют выражениям в ExpressionList для одного из разделов Case, инструкции этого раздела выполняются. Если значения TestExpression соответствуют более, чем одному разделу Case, выполняются инструкции только первого из них. TestExpression — это чаще всего имя простой переменной или математическое выражение, в более редких случаях — логическое выражение. Выражения в ExpressionList — типичные логические выражения.
После выполнения инструкций выбранного раздела Case выполняется следующая за ключевыми словами End Select инструкция.
Если значения TestExpression не соответствуют ни одному из разделов Case, выполняются инструкции, представленные в ElseStatements.
260	Неделя 2
В отдельных разделах Case ExpressionList может состоять из одного или более выражений, разделенных запятыми. ExpressionList имеет следующий общий синтаксис:
expression!, expression, expressionN
Выражения в ExpressionList могут быть некоторыми числовыми, строковыми или логическими выражениями, они также могут быть представлены рядом значений с использованием оператора То:
expression! То expression
Например, для обозначения ряда чисел от 1 до 10 в ExpressionList для некоторого раздела Case может быть использовано выражение:
Case 1 То 10
При выборе ветвей, когда в TestExpression применяются сравнения какого-либо вида (>, <, = или другие), используется следующий общий синтаксис:
Is Comparisonoperator expression
Здесь Comparisonoperator — какой-либо оператор сравнения VBA, за исключением операторов Is и Like; expression, — некоторое выражение VBA. Для того чтобы выполнялась инструкция ветви Case, когда TestExpression больше 10, например, можно применить следующее выражение:
Case Is>10
ПРИМЕЧАНИЕ
Ключевое слово Is, используемое в инструкции Select Case, отличается от оператора сравнения Is; применение операторов Is и Like в инструкции Select Case не допускается.
В листинге 8.4 показан пример инструкции Select Case.
Aucmuue 8,4. Использование инструкции Select Case
1:	Sub EvalTemperaturef)
2:
3:	Dim temperature
4:
5:	temperature = Application.InputBox(
6:	prompt:“"Сколько градусов на	приборе?",
7:	Title:“"Процедура EvalTemp",
8:	Туре:=1)
9:
10:	Select Case temperature
11:	Case Is > 100
12:	MsgBox	"Очень горячо!"
13:	Case	75	To	100
14:	MsgBox	"Нормально!"
15:	Case	50	To	74
16:	MsgBox	"Самый раз."
17:	Case	Is	> 32
18:	MsgBox	"Маловато."
19:	Case	Else
День 8-й. Принятие решения в Visual Basic for Applications
261
20:	MsgBox "Немедленно подогреть!"
21:	End Select
22:	End Sub
Процедура EvalTemperature, представленная в этом листинге, работает в основном так же, как процедура в листинге 8.3. В строках с 5 по 8, как и ранее, размещена функция InputBox, используемая для ввода числа пользователем.
Строки с 10 по 21 содержат инструкцию Select Case. TestExpression в строке 10 состоит из простой переменной temperature. Значение этой переменной сравнивается (проверяется на соответствие) с условиями каждой ветви Case инструкции Select Case.
Сначала проверяется соответствие значения переменной temperature условию первого раздела Case (строка 11). Это условие содержит выражение Is>100. Если значение temperature оказывается больше 100, выполняется инструкция MsgBox в строке 12 по выводу на экран сообщения. Условие этой ветви эквивалентно следующей инструкции If:
If temperature>100 then MsgBox "Слишком жарко!"
После окончания выполнения инструкции в строке 12 выполняется инструкция, следующая после ключевых слов End Select.
Если значение переменной temperature оказывается равным или меньшим 100, происходит переход к выполнению следующего условия Case в строке 13. В этом условии используется оператор то для обозначения ряда значений. Если значение temperature — некоторое число от 75 до 100, выполняется инструкция MsgBox в строке 14 и затем следующая за End Select в строке 21.
В противном случае, когда значение temperature не попадает в интервал значений 75—100, происходит переход к условию следующего раздела Case в строке 15, где также проверяется вхождение хранимого в переменной temperature значения в соответствующий интервал, на этот раз от 50 до 74. Если выражение (temperature) соответствует условию, выполняется строка 16 и затем — первая после End Select инструкция, иначе, как и в предыдущих случаях, происходит переход к выполнению условия следующего раздела Case. Описанные действия повторяются для оставшегося условия раздела Case в строке 17.
Наконец, если значение переменной temperature не удовлетворяет ни одному из условий разделов Case, выполняется ветвь Case Else.
Если при вызове функции Application.InputBox в строке 5 пользователем введено число 8, инструкции Select Case в строках с 10 по 21 будут выполняться для значения temperature, равного 8. Не трудно убедиться в том, что такое значение temperature не будет удовлетворять ни одному из условий в инструкциях Select Case. В этом случае выполняется ветвь Case Else (строка 19), в результате чего на экран выводится сообщение, показанное на рис. 8.1. На этом выполнение инструкции Select Case заканчивается и начинается выполнение следующей (со строки 21).
Microsoft Excel
:.: Нене дденно подогреть! jiligliiif
Рис. 8.1. После ввода числа 8 (листинг 8.3) на экран выводится это сообщение
262
Неделя 2
ПРИМЕЧАНИЕ
Если пользователь введет число больше 74, но меньше 75, будет выполняться четвертая ветвь Case. Число, например такое, как 74.5 не удовлетворяет ни одному из условий первых трех ветвей Case инструкции Select Case в листинге 8.4. После ввода значения 74.5 для temperature на экран выводится сообщение Маловато, которое является явно не корректным. Если бы интервал значений temperature в строке 15 был задан от 50 до 75, то это устранило бы пробел в интервалах для значений temperature второй и третьей ветвей. Поскольку выполняется только первый совпадающий случай условий, при значении temperature, равном 75, выполнялась бы первая ветвь Саве в строке 13. Значение temperature, равное 74.5, было бы корректно обработано в строке 15.
Так надо
Используйте инструкцию Select Case при необходимости выбора из более чем трех или четырех различных способов действия.
Используйте инструкцию Select Case в случае необходимости выбора при одном и том же способе действия и выполнении нескольких различных условий. Следующий раздел Case, например, может заменить группу аналогичных разделов для случаев, когда TestExpression имеет значение 2,4,6 или 8:
Case 2, 4, 6, 8
Так не надо
Не забывайте тщательно назначать интервалы значений переменных в условиях для ветвей Case во избежание нежелательных пробелов, подобных показанному в листинге 8.4.
Безусловное ветвление
Инструкции безусловного перехода всегда изменяют порядок выполнения в процедурах или функциях VBA. При их выполнении не проверяются никакие условия (поэтому их и называют безусловными) — просто выполнение переходит в указанное место.
Имеется только одна инструкция безусловного перехода: GoTo. Есть много причин и возможностей для использования инструкции GoTo. Однако процедуры, имеющие большое количество инструкций GoTo, трудны для понимания. В большинстве случаев инструкцию GoTo можно заменить инструкцией If, инструкцией Select Case или циклом, которые будут рассмотрены на следующем уроке.
Инструкция GoTo была включена в ранние версии языка программирования BASIC, которые не имели инструкции принятия решения Select Case, рассмотренной в этой главе, или циклов, которые будут рассмотрены в следующей главе. Программисты в ранних версиях BASIC использовали инструкцию GoTo в программах вместо современных инструкций VBA. Желательно как можно меньше использовать инструкции GoTo в VBA.
Однако мы все же рассмотрим основные принципы работы инструкции безусловного перехода GoTo, хотя бы для лучшего понимания ее применения при обработке ошибок, а также программ, в которых эта инструкция по тем или иным причинам применяется.
День 8-Й. Принятие решения в Visual Basic for Applications
263
Так надо
, Старайтесь заменять инструкцию GoTo какими-нибудь другими структурами, такими как инструкция Select Case или одна из циклических структур, которые будут рассмотрены в следующей главе.
Так не надо
[>- ' 	' 	’’' v~'"	. - -t,	.................... 1
. Не применяйте инструкцию GoTo, за исключением тех случаев, когда она используется в качестве составной
' части инструкции On Error GoTo (описанной в уроке 18-го дня, “Обработка ошибок’’).
Инструкция GoTo имеет следующий общий синтаксис:
GoTo line
Здесь line представляет некоторую метку или номер строки в той же процедуре, в которой находится инструкция GoTo. При выполнении инструкции GoTo выполнение процедуры непосредственно переключается на строку, содержащую метку line. Метка line — специальный тип идентификатора, который определяет заданную строку по имени. Метка line имеет следующий синтаксис:
name:
Здесь name — это идентификатор языка VBA. Метка может начинаться в любой позиции строки, при условии что это первый символ, отличный от пробела. Использование номеров строк в процедурах VBA является исключительно редким, вместо этого следует использовать метки.
В листинге 8.5 представлена процедура с примером использования инструкции GoTo для перехода к концу процедуры, когда пользователь выбирает кнопку Отмена в интерактивном режиме ввода.
Листинг 8.5. Ииструкция SoTo
1:	Sub MakeSalesRpt_Chart()
2:	'Эта процедура спрашивает у пользователя имя листа с
3:	'данными, потом спрашивает диапазон ячеек с данными для
4:	'диаграммы. Затем процедура спрашивает имя листа, на котором
5:	'будет создана диаграмма. Потом с помощью метода
6:	'Chartwizard создается круговая диаграмма.
7:
8:	Const sTitle = "Создание	диаграммы продаж"
9:
10:	Dim	SrcShtName As String
11:	Dim	SourceRng As String
12:	Dim	DestShtName As String
13:
14:	'получаем имя листа с данными
15:	SrcShtName = InputBox(prompt:=
16:	"Введите имя листа с данными, " &
17:	"по которым строится диаграмма:",
18:	Title:=sTitle)
19:	'проверяем, ввел ли пользователь имя листа или выбрал Cancel
20:	If Len(Trim(SrcShtName)) = 0 Then
21:	MsgBox "Имя листа не указано - процедура прервана"
264
Неделя 2
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
GoTo GiveUp
End If
'выбираем лист с указанным именем
Sheets(SrcShtName).Select
'получаем диапазон ячеек
SourceRng = InputBox(prompt:=
"Введите диапазон ячеек с данными " & "в стиле А1:“,
Title:=sTitle)
'проверяем, ввел ли пользователь диапазон или выбрал Cancel
If Len(Trim(SourceRng)) = 0 Then
MsgBox "Диапазон не указан - процедура прервана"
GoTo GiveUp
End If
'получаем имя листа с диаграммой
DestShtName = InputBox)prompt:="Введите имя листа, иа " & "котором будет построена диаграмма:", Title:=sTitle)
'проверяем, ну выбрал ли пользователь Cancel
If Len(Trim(DestShtName)) = 0 Then
MsgBox "Имя листа не указано 11 & " - процедура прервана"
GoTo GiveUp
End If
'выбираем лист и строим диаграмму
Sheets(DestShtName).Select
ActiveSheet.Chartobjects.Add(96, 37.5, 234, 111).Select
'с помощью метода Chartwizard строим диаграмму.
With Sheets(SrcShtName)
ActiveChart.Chartwizard Source:=.Range(SourceRng), Gallery:=xlPie, Format:=7, PlotBy:=xlColumns, CategoryLabels:=l, SeriesLabels:=l, HasLegend:=l, Title:="Отчет о продажах"
End With
GiveUp:
'конец процедуры - при отказе пользователя от ввода переходим сюда End Sub
^ПРИМЕЧАНИЕ
Если вы захотите ввести текст этого листинга в программу и поэкспериментировать с ним, вы должны указывать диапазон ячеек в стиле А1, например В4:О8. К тому же убедитесь, что информация, которую вы выбираете, пригодна для графического использования с круговой диаграммой.
День 8-й. Принятие решения в Visual Basic for Applications
265
Процедура MakeSeleRpt_Chart представляет собой отредактированную версию записанного макроса. Сначала макрос для создания круговой диаграммы был записан, а затем скопирован в другой модуль и отредактирован. Наименование этого макроса было изменено на MakeSeleRpt_Chart, и в него были добавлены различные объявления констант и переменных. Затем в процедуру были включены инструкции VBA для получения и обработки ввода от пользователя.
В строках 10—12 объявляются строковые переменные. SrcShtName содержит имя рабочего листа, хранящего данные для графика, SourceRng содержит диапазон данных для графика, a DestShtName — наименование рабочего листа, на который процедура поместит результирующую диаграмму.
Фактическая работа процедуры MakeSeleRpt_Chart начинается со строки 15. В этой строке используется функция InputBox для получения имени рабочего листа, содержащего исходные данные. Результат функции InputBox записывается в переменную SrcShtName.
В строке 20 начинается блоковая инструкция if...Then. В логическом выражении функция Trim в первую очередь удаляет все лидирующие и завершающие пробелы из строки SrcShtName, а функция Len — возвращает длину исправленной строки. Если длина исправленной строки равна нулю, то пользователь либо не ввел имя в диалоговом окне ввода, либо щелкнул на кнопке Отмена — функция InputBox возвращает пустую строку, если пользователь нажимает клавишу <Esc> или щелкает на кнопке Отмена.
В этом случае нет необходимости продолжать процедуру. Инструкция MsgBox в строке 21 выводит на экран сообщение, объясняющее, что процедура сейчас закончится. Строка 22 содержит инструкцию GoTo. После выполнения этой строки выполнение процедуры немедленно переходит в строку с меткой GiveUp: все инструкции между строкой, которая содержит GoTo, и строкой, которая содержит указанную метку, пропускаются. Затем выполняется инструкция в строке, следующей за помеченной. В этом случае метка GiveUp: находится в строке 64, перед инструкцией End Sub.
Строка 25 использует метод Select для выбора рабочего листа с именем SrcShtName, чтобы пользователь мог видеть лист источника данных во время ввода координат области данных для графика.
Затем в строке 28 вызывается функция InputBox, чтобы получить от пользователя координаты области данных для графика, которая направляет ввод пользователя в переменную SourceRng. Так же как и в случае с именем исходного рабочего листа, в строке 33 производится проверка, содержит ли строка с диапазоном области данных что-либо, кроме пробелов, или пустую строку, возвращенную, когда пользователь выбирает кнопку Cancel в диалоговом окне ввода.
Потом, если переменная SourceRng не содержит никаких данных, нет никакого смысла продолжать процедуру. Когда выражение, сравнивающее длины строк в строке 33, имеет значение True, выполняются строки 34 и 35. Строка 34 выводит на экран сообщение о том, что процедура сейчас закончится, и строка 35 переводит выполнение процедуры на строку с меткой GiveUp:.
В строке 39 имя листа, который будет содержать законченную секторную диаграмму, записывается в переменную DestShtName. Опять же, в строке 43 инструкция If...Then проверяет эту переменную, чтобы узнать, ввел ли пользователь строку или выбрал Cancel. Как и прежде, если строка, введенная пользователем, пуста, продолжать процедуру нет необходимости; выполняется альтернативная ветвь инструкций If.. .Then, и выполнение процедуры переходит на метку' GiveUp:.
Строка 50 будет выполнена только в том случае, если пользователь процедуры фактически вводит все три запрошенных значения. В строке 50 используется метод Select для выбора рабочего листа, который будет содержать законченную диаграмму.
266
Неделя 2
Строка 51 использует метод Add для того, чтобы создать и выбрать новый объект диаграммы. Строки 50, 51 были получены из записанного макроса.
Строка 55 использует метод Chartwizard, чтобы установить спецификации для диаграммного объекта, созданного в строке 51. Хотя строка записанного макроса была переформатирована и добавлена инструкция With...End With (строка 54), чтобы сделать этот метод выбора более понятным, существенных изменений по сравнению с исходным макросом не произошло. Добавлены только переменные ScrShtName и SourceRng и удалены некоторые аргументы.
Как правило, удаление аргументов, с которыми связаны пустые строки, безопасно, поскольку пустая строка сама собой представляет стандартное значение по умолчанию для большинства необязательных аргументов.
И наконец, строка 64 содержит метку GiveUp:. Эта строка является строкой, на которую все инструкции GoTo этой процедуры передают выполнение. Поскольку строка 64 является последней инструкцией в процедуре, перед окончанием процедуры в строке 66, все инструкции GoTo этой процедуры используются для раннего завершения процедуры. Как будет показано в следующей секции, для решения этой задачи существует более простой способ.
Раннее завершение процедур, функций и программ
Как видно из процедуры в листинге 8.5, существуют обстоятельства — такие как отмена пользователем ввода в диалоговом окне или отсутствие каких-либо необходимых данных, — при которых нет необходимости продолжать выполнение процедуры или функции. Такие обстоятельства, как отсутствие требуемого рабочего листа, могут возникать также и при выполнении больших модульных программ (как это описано в уроке 12-го дня).
Для таких случаев в VBA предусмотрены инструкции Exit и End, дающие пользователю возможность либо прекратить выполнение отдельной программы или функции, либо остановить большую модульную программу.
Использование инструкции Exit
Для того чтобы прекратить выполнение какой-либо отдельной программы или функции, используется одна из двух имеющихся в наличии форм инструкции Exit, в зависимости от того, что необходимо остановить — функцию или программу.
Инструкция Exit имеет две синтаксические формы:
Exit Sub
Exit Function
Exit Sub и Exit Function — обе работают в точности одним и тем же образом с одинаковым эффектом; Exit Sub используется для окончания программы, a Exit Function — для окончания функции.
В листинге 8.6 показана процедура MakeSelesRpt Chart из листинга 8.5, где теперь для досрочного окончания процедуры используется инструкция Exit Sub.
День 8-й. Принятие решения в Visual Basic for Applications
267
Листинг 8.В. Окпнчпние нрвцеддры дпсрвчнп с ивмвщью Exit Suh
1:	Sub MakeSalesRpt_Chart()
2:	'Эта процедура спрашивает у пользователя имя листа с
3:	'данными, потом спрашивает диапазон ячеек с данными для
4:	'диаграммы. Затем процедура спрашивает имя листа, на котором
5:	'будет создана диаграмма. Потом с помощью метода
6:	'Chartwizard создается круговая диаграмма.
7:
8:	Const sTitle = "Создание диаграммы продаж"
9:
10:	Dim	SrcShtName As String
11:	Dim	SourceRng As String
12:	Dim	DestShtName As String
13:
14:	'получаем имя листа с данными
15:	SrcShtName = InputBox)prompt:=
16:	"Введите имя листа с данными, " &
17:	"по которым строится диаграмма:",
18:	Title:=sTitle)
19:	'проверяем, ввел ли пользователь имя листа или выбрал Cancel
20:	If Len(Trim)SrcShtName)) = 0 Then
21:	MsgBox "Имя листа не указано - процедура прервана"
22:	Exit Sub
23:	End If
24:	'выбираем лист с указанным именем
25:	Sheets(SrcShtName).Select
26:
27:	'получаем диапазон ячеек
28:	SourceRng = InputBox)prompt:= _
29:	"Введите диапазон ячеек с данными " &
30:	"в стиле А1:“,
31:	Title:=sTitle)
32:	'проверяем, ввел ли пользователь диапазон или выбрал Cancel
33:	If Len(Trim)SourceRng)) = 0 Then
34:	MsgBox "Диапазон не указан - процедура прервана"
35:	Exit Sub
36:	End If
37:
38:	'получаем имя листа с диаграммой
39:	DestShtName = InputBox(prompt:="BBeflHTe имя листа, на " S _
40:	"котором будет построена диаграмма:",
41:	Title:=sTitle)
42:	'проверяем, ну выбрал ли пользователь Cancel
43:	If Len(Trim)DestShtName)) = 0 Then
44:	MsgBox "Имя листа не указано " &
45:	" - процедура прервана"
46:	Exit Sub
47:	End If
48:
49:	'выбираем лист и строим диаграмму
50:	Sheets(DestShtName).Select
51:	ActiveSheet.Chartobjects.Add)96, 37.5, 234, 111).Select
268
Неделя 2
52:
53:	'с помощью метода Chartwizard строим диаграмму.
54:	With Sheets(SrcShtName)
55:	ActiveChart.Chartwizard Source:=.Range(SourceRng),
56:	Gallery:=xlPie,
57:	Format:=7,
58:	PlotBy:=xlColumns,
59:	CategoryLabels:=l,
60:	SeriesLabels:=l,
61:	HasLegend:=l,
62:	Title:="OT4eT о продажах"
63:	End With
64:	End Sub
Эта версия программы MakeSelesRpt_Chart работает так же, как и программа, показанная в листинге 8.5, за исключением того, что вместо инструкции GoTo для досрочного окончания программы эта версия использует инструкцию Exit Sub.
Рассмотрим строки 22, 35 и 46. Каждая из этих строк содержит инструкцию Exit Sub. Так же как и в процедуре, показанной в листинге 8.5, выполнение инструкций If.. .Then приводит к выполнению инструкций в строках 22, 35 и 46 всякий раз, когда пользователь вводит пустую строку, выбирает кнопку Отмена в любом из трех диалоговых окон ввода или нажимает <Esc>.
После выполнения инструкции Exit Sub выполнение текущей процедуры прекращается и возвращается в программный модуль, вызывавший остановленную процедуру, содержащую инструкцию Exit Sub.
ПРИМЕЧАНИЕ
Если вы используете инструкцию Exit Sub или Exit Function в процедуре или функции, вызванной другой процедурой или функцией VBA, эта вызывавшая процедура или функция продолжает выполнение. Инструкции Exit Sub и Exit Function останавливают выполнение только текущей программы или функции.
Так надо
Используйте инструкцию Exit Sub или Exit Function вместо GoTo для досрочного окончания программы или функции. Инструкция Exit Sub делает более очевидным, что ваша программа прекратит выполнение, -и, следовательно, облегчает написание, чтение и понимание процедуры.
Использование инструкцои End
Вы уже ознакомились с ключевыми словами End Sub и End Function, используемыми для того, чтобы сигнализировать о достижении окончания процедуры или функции. Эти ключевые слова служат признаком прекращения выполнения инструкций в текущей процедуре или функции и возобновления выполнения процедуры или функции, которая вызвала текущую процедуру или функцию. Если текущая процедура не была вызвана другой процедурой, любое выполнение инструкций прекращается.
Например, рассмотрим листинг R1.1, приведенный в конце предыдущей главы. Процедура NewBook получает имя рабочей книги от пользователя при вызове функции GetBookName. При выполнении процедуры вызывается функция GetBookName, ей передаются аргументы, и затем управление переходит к инструкциям функции GetBookName.
День 8-й. Принятие решения в Visual Basic for Applications
269
Инструкции в GetBookName выполняются до тех пор, пока не будет достигнута либо инструкция End Function в конце функции, либо инструкция Exit Function. В любом случае выполнение программы в функции GetBookName прекращается и ее результат возвращается в процедуру NewBook.
После возврата из функции GetBookName заканчиваются вычисления всех выражений, использующих результат этой функции, и продолжается выполнение инструкций в процедуре NewBook, начиная с инструкции, следующей за той, которая вызвала функцию GetBookName.
Таким образом, процедура NewBook продолжает выполнение, даже если пользователь прерывает ввод в диалоговом окне, вызванном функцией GetBookName. Очевидно, если пользователь прерывает ввод имени рабочей книги, то нет необходимости продолжать процедуру NewBook — без рабочей книги не существует никакой имеющей смысл работы, которую выполняла бы процедура NewBook.
В этой ситуации было бы замечательно, если бы вы смогли написать функцию GetBookName так, чтобы весь процесс создания новой рабочей книги прервался, если пользователь прерывает диалог ввода, представленный функцией GetBookName. Как показано в примерах в листинге 8.6, для прерывания операции ввода и для немедленного окончания функции можно использовать комбинацию инструкций If.. .Then и Exit Function.
При простом досрочном окончании функции GetBookName выполнение просто возвращается к процедуре NewBook, а это не то, что вам требуется. Вам необходимо полностью прекратить выполнение в программе. (Программа может быть единственной программой или сотней процедур и функцией, работающих совместно.)
Для того чтобы полностью закончить выполнение программы, используйте ключевое слово End в отдельной строке:
End
После выполнения предыдущей инструкции прекращается всякое выполнение инструкций, процедур и функций. Все существующие переменные прекращают существование. и их значение теряется.
В листинге 8.7 показана функция GetBookName, отредактированная таким образом, что она полностью заканчивает какую-либо выполняемую программу, если пользователь прерывает ввод.
Листинг 8.7. Окончаиив выиплнения программы с номощью End
1:	Function GetBookName(ByVai IDflt As String,
2:	Optional Prmpt,
3:	Optional iTitle) As String
4:
5:	'есть ли аргумент Prmpt в списке аргументов?
6:	If IsMissing(Prmpt) Then Prmpt = "Введите имя книги:"
7:
8:	'если имя книги не пусто, преобразуем его в
9:	'верхний	регистр,	иначе присваиваем ему значение.
10:	If IDflt	<>	""	Then
11:	IDflt = UCase(lDflt)
12:	Else
13:	IDflt = "NEWFILE.XLS"
14:	End If
15:
16:	'с помощью функции InputBox получаем имя файла.
17:	GetBookName = InputBox(prompt:=Prmpt,
270
Неделя 2
18:	Title:=llitle,
19:	Default:=lDflt)
20:
21:	'проверка: не выбрал ли пользователь Cancel?
22:	If Len(Trim(GetBookName)) = 0 Then
23:	MsgBox prompt:="Программа остановлена", Title:=lTitle
24:	End 'конец программы
25:	End If
26:	End Function
27:
28:
29:	Sub Test_GetBookName()
30:	'процедура для проверки функции GetBookName
31:
32:	MsgBox GetBookName(lDflt:="", 1ТШе:="Тест завершения”)
33:	End Sub
Эта версия функции GetBookName идентична функции, описанной в листинге 8.2, и работает таким же образом. Исключение составляют строки 21-25, добавленные к этой функции с тем, чтобы вся программа заканчивалась, как только пользователь прерывает ввод в строке 17.
Строка 17 использует функцию InputBoox, чтобы получить строку от пользователя; строка, возвращаемая InputBoox, назначается результатом функции GetBookName. Если пользователь нажимает клавишу <Esc> или выбирает кнопку Отмена в диалоговом окне ввода, InputBoox возвращает пустую (нулевой длины) строку.
Строка 22 является началом блока If...Then. Тестовое условие этой инструкции использует функции Trim и Len для того, чтобы длина строки, включая возможные пробелы, помещалась в переменную результата функции. Если длина строки, возвращаемой функцией InputBoox, — нулевая, то пользователь либо ввел пустую строку в качестве имени рабочей книги, либо выбрал кнопку Отмена в диалоговом окне ввода, либо нажал клавишу <Esc>.
Если InputBoox возвращает пустую строку, то выполняется строка 23, которая использует MsgBox для вывода на экран сообщения о том, что программа была остановлена. Затем выполняется инструкция End в строке 24. После выполнения этой инструкции прекращается всякое выполнение и возврата к процедуре, которая вызвала функцию GetBookName, не происходит.
Строки 29-33 являются простой процедурой для тестирования функции GetBookName. При выполнении тестовой процедуры в строке 32 вызывается и начинает выполняться функция GetBookName. Если пользователь отменяет ввод в этой функции, выполняется инструкция End и заканчивается всякое исполнение — не завершается даже выполнение инструкции MsgBox в строке 32 тестовой процедуры!
Так надо
Пишите любую из процедур или функций, в которых используется инструкция End, таким образом, чтобы перед возможным выполнением этой инструкции всегда выводилось сообщение о том, что должно произойти и . почему. Если вы этого не делаете, то пользователи такой процедуры могут оказаться совершенно сбитыми ст  толку, не понимая, почему процедура или программа, которую они использовали, внезапно прекратила работу.
Если работа программы заканчивается из-за какого-либо действия пользователя - например, из-за прерывания ввода в диалоговом окне - без объяснения, пользователь может никогда не догадаться, почему процедура была завершена; большинство пользователей склонны рассматривать подобные вещи как дефекты программы.
День 8-й. Принятие решения в Visual Basic for Applications
271
Так не надо
Не забывайте закрывать рабочие книга и выполнять другую “уборку* перед тем, как процедура выполняет инструкцию End, чтобы пользователи вашей программы после ее окончания не были вынуждены вычищать такие вещи, как незаконченные диаграммы или иметь дело с рабочими книгами, содержащими несохраненные данные.
Выбор с помощью функции MsgBox
К настоящему времени вы уже вполне должны освоиться с использованием функции MsgBox, чтобы выводить сообщения для пользователей в озаглавленных диалоговых окнах. Вы можете также использовать процедуру MsgBox как функцию для получения выбора пользователя в ответ на сообщения или вопросы, которые выводит процедура с помощью включения необязательных аргументов Buttons. Для большинства простых вариантов выбора использование функции MsgBox для получения ответа от пользователя гораздо удобнее, чем получение текстового ввода в функции InputBox с последующим анализом этого текста для определения выбора.
Если при вызове функции включить аргумент Buttons в простые скобки, инструкция MsgBox будет работать как функция и выводить диалоговое окно с сообщением, оснащенное несколькими командными кнопками. При щелчке на одной из таких кнопок MsgBox возвращает число, показывающее, какую из командных кнопок выбрал пользователь. Количество и тип командных кнопок, выводимых в диалоговом окне MsgBox, можно назначать через аргументы кнопок.
Листинг 8.8 показывает простую процедуру, которая демонстрирует использование кнопок.
Листинг 8.8. Использоваиив MsgBox и аргдмвита Buffons для полцчвиия ввода пользователя
1:	Sub Demo_MsgBoxFunction()
2:	'Использует возвращаемое значение функции MsgBox
3:
4:	Const mTitle = "Использование кнопок функции MsgBox "
5:	Dim Resp As Integer
6:
7:	Resp = МздВох(рготрЬ:="Выберите кнопку.",
8:	Title:=mTitle,
9:	Buttons:=vbYesNoCancel + vbQuestion)
10:	Select Case Resp
11:	Case Is = vbYes
12:	MsgBox prompt:="BH выбрали кнопку 'Да'.",
13:	Title:=mTitle,
14:	Buttons:=vblnformation
15:	Case Is = vbNo
16:	MsgBox prompt:=”Bbi выбрали кнопку 'Нет'.",
17:	Title:=mTitie,
18:	Buttons:=vblnformation
19:	Case Is = vbCancel
20:	MsgBox prompt:="Вы выбрали кнопку 'Отмена'.",
21:	Title:=mTitle,
22:	Buttons:=vbCritical
23:	End Select
24:	End Sub
272
Неделя 2
Процедура Demo_MsgBoxFunction демонстрирует использование инструкции MsgBox в качестве функции, влияние разнообразных аргументов кнопок и способы получения значений, возвращаемых функцией MsgBox.
Строка 1 содержит объявление процедуры, а строка 2 — комментарий, кратко описывающий ее цель. В строке 4 объявляется константа для применения в качестве заголовка диалогового окна MsgBox, а в строке 5 объявляется переменная Resp типа Integer для хранения результата, возвращаемого функцией MsgBox.
В строке 7 содержится вызов функции MsgBox и запись возвращаемого ею результата в переменную Resp. Заметим, что вызов функции MsgBox занимает три строки (7, 8 и 9) и при этом используется символ продолжения строки. Вызов соответствует всем требованиям к использованию функции: список аргументов заключен в круглые скобки, результат записывается в переменную.
Список аргументов для вызова функции MsgBox, начинающийся в строке 7, содержит знакомые аргументы: prompt:= — для хранения текста, выводимого в диалоговом окне с сообщением, и Title:3 — для хранения заголовка диалогового окна с сообщением. Вызов функции также содержит новый аргумент — buttons:3.
Аргумент Buttons: = выражается суммой двух внутренних констант VBA; несколько таких констант для использования с аргументом Buttons в MsgBox определены явно.
Аргумент Buttons для MsgBox позволяет точно обозначить количество и тип кнопок, наличие специальной пиктограммы, характеризующей тип диалогового окна (предупреждение, вопрос, информация или критическое предупреждение). Аргумент Buttons также можно использовать, чтобы точно обозначить, какая из изображенных кнопок (кнопка 1, 2, 3 или 4) является кнопкой по умолчанию в диалоговом окне с сообщением. Можно назначить только один тип кнопок, одну пиктограмму и один параметр кнопки по умолчанию за один раз.
В строке 9 (часть вызова функции MsgBox, начинающегося в строке 7) константа vbYesNoCancel определяет, что диалоговое окно MsgBox должно содержать три командных кнопки: кнопку Да, кнопку Нет и кнопку Отмена, а также пиктограмму, используемую с сообщениями-запросами Windows.
При выполнении строк 7-9 вызова функции MsgBox, на экран выводится диалоговое окно, показанное на рис. 8.2. Оно остается на экране до тех пор, пока пользователь не вьгберет одну из командных кнопок или не нажмет клавишу <Esc>. (Нажатие клавиши <Esc> в диалоговом окне MsgBox, как и в любом другом диалоговом окне Windows — это то же самое, что и выбор командной кнопки Отмена.)
Использование кнопок функции MsqBox
Выберите кнопку.
ja ~ j Цет | Отмена |
Рис. 8.2. Вызов функции MsgBox в строке 7 листинга 8.8 выводит на экран это диалоговое окно. Обратите внимание, что командные кнопки Да, Нет и Отмена и пиктограмма со знаком вопроса однозначно определяются аргументом Buttons
Как только пользователь выбирает командную кнопку в окне сообщения, выдается цифровое значение, соответствующее выбору пользователя. В строке 7 результат функции MsgBox записывается в переменную Resp. В VBA выбору различных командных кнопок соответствуют различные цифровые значения. Поскольку каждой кнопке
День 8-Й. Принятие решения в Visual Basic for Applications
273
соответствует ее собственное цифровое значение, в VBA предусмотрен набор внутренних констант, включающий все возможные значения, возвращаемые функцией MsgBox.
В строке 10 начинается инструкция Select Case, обрабатывающая цифровое значение, которое возвращается функцией MsgBox в строке 7 и хранится в переменной Resp. Тестовое выражение для инструкции Select Case — это сама переменная Resp. Таким образом, в этой инструкции значение переменной Resp проверяется на соответствие каждому из набора условий Case.
Строка 11 содержит первое условие Case. Здесь проверяется, равна ли переменная Resp константе, соответствующей нажатию кнопки Да. Если пользователь выбрал кнопку Да, первое условие Case будет вычислено как True, и тогда выполнятся инструкции этой ветви Case, начиная со строки 12. Строки 12-14 содержат инструкцию MsgBox. На этот раз инструкция MsgBox представлена в своей более знакомой форме, как вызов процедуры, однако использует необязательный аргумент Buttons (строка 14). При выполнении этой инструкции на экран выводится диалоговое окно, показанное на рис. 8.3.
Аргумент Buttons в строке 14 использует константу vblnformation для того, чтобы в диалоговом окне находилась соответствующая пиктограмма. Поскольку ни одного значения, определяющего количество кнопок, не было включено, диалоговое окно с сообщением содержит одну обычную командную кнопку ОК. Следует обратить внимание на то, что этот вызов MsgBox не заключен в круглые скобки; следовательно, результат функции игнорируется. (Об игнорировании результата функции речь шла на 5-м уроке.)
Если пользователь выбрал кнопку Нет, значением выражения в строке 15 — которое проверяет, равен ли результат в Resp внутренней константе vbNO — является True, и тогда выполняется инструкция MsgBox в строках 16-18.
Эта инструкция выводит на экран диалоговое окно с сообщением, которое отличается от показанного на рис. 8.3 лишь тем, что Да заменено на Нет.
Если пользователь выбрал кнопку Отмена, значением Case в строке 19 — которое проверяет, равен ли результат в Resp внутренней константе vbCancel — является True и выполняется соответствующая инструкция MsgBox в строках 20-22. При выполнении этой инструкции на экран выводится диалоговое окно с сообщением, показанное на рис 8.4.
^Использование кнопок функции MsgBox
Использование кнопок функции MstjBox
Вы выбрали кнопку 'Да'.
Вы выбрали кнопку'Отмена'.
ОК
Рис. 8.3. Это диалоговое окно выводится, если пользователь выбрал командную кнопку Yes. Обратите внимание на информационную пиктограмму Windows
Рис. 8.4. Это диалоговое окно выводится на экран, если пользователь выбрал командную кнопку Отмена. Обратите внимание на пиктограмму аварийных сообщений Windows
В аргументе Buttons в строке 22 использована константа vbCritrical для назначения типа пиктограммы — пиктограммы аварийных сообщений. Поскольку количество кнопок не назначается, диалоговое окно с сообщением содержит единственную командную кнопку ОК.
И наконец, инструкция Select Case заканчивается в строке 23, а строка 24 является концом процедуры Demo_MsgBoxFunction.
274
Неделя 2
Далее вы увидите большое количество примеров использования функции MsgBox для получения выбора от пользователя.
Существует еще один параметр, определяемый аргументом Buttons для MsgBox, который не был включен в листинг 8.8. С помощью этого параметра можно установить, какая из четырех командных кнопок будет являться основной (используемой по умолчанию) в диалоговом окне MsgBox.
Как можно было заметить, в диалоговых окнах MsgBox в качестве обычно основной (по умолчанию) назначается первая командная кнопка. На рис. 8.2 можно заметить, что кнопка Да выступает в роли основной командной кнопки. Влияние этого выбора можно легко увидеть на следующем простом примере. Если после вывода на экран диалогового окна с сообщением, показанного на рис. 8.2, пользователь просто нажимает клавишу <Enter>, дальнейший ход выполнения программы будет таким же, как если бы пользователь выбрал кнопку Да.
Так надо
Используйте внутренние константы VBA для аргумента Buttons в MsgBox и для возвращаемых MsgBox значений. Использование имен внутренних констант делает программу лептой для написания, чтения и понимания.
Помните, что при комбинировании и добавлении значений аргумента Buttons функции MsgBox необходимо использовать оператор сложения (+).
Учтите также, что вы можете выводить и рассматривать списки всех имеющихся в наличии внутренних констант, относящихся к MsgBox, в окне просмотра объектов. Чтобы посмотреть список внутренних констант для аргумента Buttons, выберите VBA в списке Project/Library в окне просмотра объектов и затем выберите vbMsgBoxStyle в списке Classes; в списке Members Of выводятся внутренние константы для значений аргумента Buttons. При выборе vbMsgBoxResult в списке Classes в списке Members Of выводятся внутренние константы для возвращаемого значения функции MsgBox.
Так не надо
Не пытайтесь назначить более одного набора кнопок, более одной пиктограммы и более одной кнопки по умолчанию за один раз. Например, использование выражения vbYesNoCancel + vbAbortRetrylgnore в качестве значения для аргумента Buttons не приведет к выводу диалогового окна с шестью командными кнопками. Результаты попыток назначить более одного набора кнопок, более одной пиктограммы и более одной кнопки по умолчанию за один раз могут быть непредсказуемыми и уж точно не возымеют желательного эффекта.
Не используйте непоименованных числовых значений как для аргумента Buttons в MsgBox, так и для возвращаемого значения MsgBox. Использование непоименованных числовых значений делает программу трудной для чтения и понимания.
Часто бывает нужно, чтобы кнопкой по умолчанию была кнопка, отличная от первой из перечисленных в окне сообщения. Например, при использовании MsgBox для того, чтобы попросить пользователя подтвердить уничтожение рабочего листа в рабочей книге, можно установить, чтобы командной кнопкой по умолчанию была кнопка Нет вместо кнопки Да. Поскольку уничтоженный рабочий лист не может быть восстановлен, имеет смысл помочь пользователю избежать непреднамеренного подтверждения уничтожения, если он чаще, чем необходимо, нажимает клавишу <Enter>. (Почти каждый, кто случайно делал ошибочный выбор, нажимал клавишу <Enter> по привычке.)
Чтобы изменить командную кнопку, назначенную по умолчанию, необходимо добавить одну из предусмотренных внутренних констант VBA — vbDefaultButtonl, vbDefaultButton2, vbDefaultButtonJ или vbDefaultButton4 — к аргументу Buttons. Приве
День 8-й. Принятие решения в Visual Basic for Applications
275
денный ниже фрагмент процедуры показывает инструкцию MsgBox со строками 7-9 в листинге 8.8, измененную таким образом, что командной кнопкой по умолчанию в диалоговом окне с сообщением становится кнопка Нет:
Resp=MsgBox(prompt:="Выберите кнопку."._
Title:=mTitle,_
Buttons:=vbYesNoCancel+vbQuestion+_
vbDefaultButton2)
При выполнении указанной выше инструкции на экран выводится окно сообщения, изображенное на рис. 8.5. Это окно идентично изображенному ранее на рис. 8.2, за исключением того, что вследствие добавления константы vbDefaultButton2 в аргументе Buttons командной кнопкой по умолчанию вместо кнопки Да становится кнопка Нет.
Использование кнопок функции MsgBox
Рис. 8.5. Сравните это диалоговое окно с сообщением с окном, изображенным на рис. 8.2. Обратите внимание, что кнопка Нет в результате добавления vbDefaultButton2 к аргументу Buttons стала командной кнопкой, назначенной по умолчанию
Резюме
На сегодняшнем уроке вы изучили, как работают команды принятия решения VBA и при каких обстоятельствах в процедурах применяются необходимые программные решения и выбор альтернативных последовательностей действий. Вы также изучили разницу между условными и безусловными инструкциями перехода.
Теперь вы знаете, как использовать инструкцию If...Then, чтобы сделать простой выбор, и как использовать инструкцию If.. .Then.. .Else, чтобы сделать выбор между двумя различными ветвями программы. Вы также научились располагать инструкции If.. .Then.. .Else внутри друг друга, чтобы принимать более сложные решения, включающие более чем две различные ветви, и использовать инструкцию If.. .Then.. .Elself, чтобы упростить некоторые вложенные инструкции If.. .Then.. .Else.
Вы познакомились с использованием инструкции Select Case с целью выбора одной из нескольких возможных ветвей выполнения; вы убедились, что инструкция Select Case является мощной альтернативой вложенным инструкциям If.. .Then.. .Else. Вы также узнали о безусловном ветвлении с инструкцией GoTo и о том, что эта инструкция обладает очень ограниченным спектром использования в современном программировании VBA.
Мы выяснили, когда и как использовать инструкции Exit Function и Exit Sub, чтобы закончить процедуру или выполнение функции досрочно, а также когда и как использовать инструкцию End, чтобы закончить выполнение программы. В этой главе даны объяснения специфических различий между использованием одной из инструкций Exit и использованием инструкции End.
276
Неделя 2
Наконец мы рассмотрели вопрос применения параметрического аргумента Buttons с MsgBox с целью определения типа и количества командных кнопок и вида пиктограммы в диалоговом окне с сообщением. Вы также узнали, как использовать MsgBox в качестве функции, чтобы возвращать выбор пользователя.
На следующем занятии вы узнаете, как использовать циклы для повторения действий в программе.
Вопросы о ответы
Я не уверен, что точно знаю, когда следует использовать инструкцию If.. .Then.. .Elself.
Инструкцию If.. .Then.. .Elself наиболее целесообразно использовать, когда вам необходимо выбирать между тремя или четырьмя различными ветвями выполнения процедуры в зависимости от различных обстоятельств. Инструкция If.. .Then.. .Elself является альтернативой инструкции Select Case , и обе инструкции имеют в значительной степени сходный эффект: они дают возможность выбора одной из нескольких различных ветвей. Если вам не очень удобно пользоваться инструкцией If.. .Then.. .Elself, просто используйте вместо нее инструкцию Select Case.
Я не уверен, что понимаю, как написать логические тестовые выражения для различных инструкций If...Then.
Как правило, вы определяете условие для инструкций перехода путем создания логического выражения, которое имеет значение True, когда желаемое условие для выполнения конкретной процедурной ветви выполняется, и имеет значение False, когда условие не выполняется.
Почти всегда вы можете решить, как написать правильное логическое выражение, на основе тщательной формулировки условия на человеческом языке с последующей заменой части предложения логическим оператором. Например, вам нужно, чтобы конкретная ветвь процедуры работала всякий раз, когда значение переменной Netlncome оказывается меньше 10000. Вы можете описать ситуацию по-русски, например: когда Netlncome меньше чем 10000. Из этой фразы видно, что значение Netlncome сравнивается с константой 10000 и что оператором сравнения является выражение: меньше, чем (<). Исходя из этого, можно создать следующее выражение:
Netlncome<10000
Это логическое выражение принимает значение True всякий раз, когда Netlncome становится меньше 10000. Аналогичным способом можно создать и более сложные логические инструкции. Предположим, вы хотите, чтобы конкретная ветвь процедуры работала всякий раз, когда значение Netlncome падает ниже 10000, или всякий раз, когда доход в процентах (PentProfit) падает ниже 15%. Тщательно опишите ситуацию по-русски, например: когда Netlncome меньше чем 10000, или когда PentProfit меньше чем 15. В этой фразе два более коротких выражения связаны вместе словом или. Первое выражение уже было сформулировано. Второе выражение сравнивает PentProfit и 15 и использует оператор меньше (<). Тогда вторая часть логического выражения будет иметь вид:
PcntProfit<15
Теперь для получения окончательного логического выражения осталось соединить эти два выражения логическим оператором, соответствующим русскому слову или (меньшие части выражения должны быть заключены в круглые скобки; иначе оно не будет работать правильно):
(Netlncome<l0000)or(PentProfit<l5)
День 8-й. Принятие решения в Visual Basic for Applications
277
В одном из примеров этой главы использовалась функция приложения Excel Application.InputBox вместо VBA функции InputBox, чтобы ограничить ввод от пользователя только численным значением. Как узнать подробнее об использовании этой функции?
Вы можете узнать больше о функции Application.InputBox или о любом другом объекте, свойстве или методе, которые вы увидите в приведенных макросах или примерах в этой книге, с помощью использования окна просмотра объектов, как это описано в уроке 5-го дня. Чтобы получить больше информации, касающейся функции Excel Application. InputBox, откройте окно просмотра объектов и выберите Excel в списке Project/Library, затем выберите Application в списке Classes, затем InputBox в списке Members of. Наконец щелкните на кнопке ? справа вверху в окне Object Browser.
Я понимаю, как работает инструкция GoTo, но не уверен, когда и почему нужно использовать инструкцию GoTo.
Вы никогда не должны применять инструкцию GoTo, исключая специальные операторы обработки ошибок GoTo, описанные при изложении материала урока 18-го дня обучения. То, что вы знаете об инструкции GoTo из этой главы, поможет быстрее понять инструкции GoTo по обработке ошибок, описанные в уроке 18-го дня, а также понимать программы других программистов, использующих по тем или иным причинам инструкцию GoTo. Вместо инструкции GoTo следует всегда применять условные команды перехода и команды цикла, чтобы структурировать программу. Если необходимо закончить процедуру или программу досрочно — предпочтительней использование инструкций Exit или End вместо GoTo.
Коллоквиум
Ответы в приложении.
Тест
1.	Что такое условный и безусловный переход?
2.	Как описать условие, которое используется при условном переходе?
3.	Какие вы знаете инструкции условного перехода?
4.	Какие вы знаете инструкции безусловного перехода?
5.	Каким термином пользуются при описании ситуации, когда одну инструкцию If.. .Then или If.. .Then.. .Else размещают внутри другой инструкции?
6.	Сколько предложений Elself может содержать инструкция If.. .Then.. .Else? Может ли инструкция If.. .Then.. .Elself включать в себя предложение Else? Если вы хотите включить предложение Else в инструкцию If.. .Then.. .Elself, куда бы вы его поместили и какое количество таких предложений можно включать?
7.	Сколько предложений Case можно включить в инструкцию Select Case? Как определить ветвь инструкций для выполнения, если в инструкции Select Case нет ни одного предложения Case? Куда в этом случае переходит выполнение внутри инструкции Select Case?
8.	Когда и почему можно использовать инструкцию Select Case вместо нескольких вложенных инструкций If.. .Then.. .Else?
9.	Для чего используется инструкция Exit Sub? С какой целью применяется Exit Function?
278
Неделя 2
10.	Какой эффект оказывает ключевое слово End, размещенное в отдельной строке ?
11.	Какой цели служит аргумент Buttons функции MsgBox? Какие средства помощи предлагаются в VBA для определения значения аргумента Buttons?
12.	Если MsgBox используется в качестве функции, что представляет собой ее результат? Какие средства предлагаются в VBA для интерпретации этого результата?
Упражнения
В этой главе предлагается только два упражнения. Хотя упражнение 8.1 достаточно простое, упражнение 8.2 — на первый взгляд сложное и по трудоемкости равно двум-трем обычным упражнениям. Но не впадайте в панику из-за кажущейся сложности этого упражнения. На самом деле оно довольно простое. Вместо того чтобы сразу пытаться написать всю процедуру для упражнения 8.2, сначала запишите процедуру, которая выполняет первую задачу в списке. Когда эта процедура будет работать нормально, приступайте к программе для второй задачи в списке, затем к третьей и так далее, пока не закончите всю задачу. В крайнем случае, если вы надолго застрянете на каком-либо этапе, посмотрите решение этой задачи в приложении.
1. Исправьте ошибку. Инструкция Select Case в следующей процедуре должна выводить окно сообщения, определяющего меньше ли число, хранящееся в Num, чем 0, или его значение находится в диапазоне 0-10, или 10-20, или его значение не соответствует ни одному из рассмотренных случаев. В действительности, однако, эта инструкция Select Case ошибочно сообщает, что такие числа, как 10,01 и 0,7 больше 20. Исправьте эту программу.
Sub Case_Demo()
Dim sNum As String
Dim Num As Double
sNum = InputBox)"Введите число:")
Num = CDbl(sNum)
Select Case Num
Case Is < 0
MsgBox "Число меньше нуля"
Case 0 To 10
MsgBox "Число в диапазоне от 0 до 10"
Case И То 20
MsgBox "Число в диапазоне от 10 до 20"
Case Else
MsgBox "Число больше чем 20"
End Select
End Sub
2. Каждой цифре на клавиатуре телефона соответствует группа букв английского алфавита. Напишите процедуру, которая запрашивает у пользователя букву и затем выводит на экран цифру, которой соответствует эта буква. Цифры и соответствующие им буквы следующие:
2=АВС
3=DEF
День 8-й. Принятие решения в Visual Basic for Applications
279
4=GHI 5=JKL 6=MN0 7=PRS 8=TUV 9=WXY
He имеется цифр, соответствующих Q и Z.
Ваша процедура должна делать следующее.
•	Используйте функцию InputBox для запроса у пользователя отдельной буквы алфавита.
•	Используйте функцию Len в логическом выражении, чтобы определить, не прервал ли пользователь ввод. Если это так, процедура должна выводить сообщение о том, что ее выполнение было отменено, и затем прекратить свое выполнение. Окно сообщения должно включать в себя предупреждающую пиктограмму сообщений Windows (восклицательный знак). (Подсказка. Для проверки этого условия используйте инструкцию If...Then и выберите альтернативную ветвь выполнения. Для вывода предупреждающей пиктограммы добавьте в аргумент Buttons константу vbExclamation.)
•	Используйте функцию Len в логическом выражении, чтобы определить, ввел ли пользователь один символ. Если длинна строки, которую ввел пользователь, больше чем 1, выведите на экран сообщение о том, что пользователь ввел слишком длинную строку, и затем прекратите выполнение процедуры. Окно этого сообщения также должно содержать предупреждающую пиктограмму сообщений Windows.
•	После обработки ввода пользователя выведите на экран цифру, соответствующую букве, введенной пользователем. Если пользователь ввел букву или другой символ, не имеющие цифрового эквивалента, процедура должна выводить сообщение, свидетельствующее об этом факте. (Совет. Чтобы упростить сравнение, которое необходимо выполнить, переведите введенные пользователем символы в верхний регистр с помощью функции UCase и только потом применяйте инструкцию Select Case.)
•	Когда в окне сообщения выводится цифра, соответствующая букве, введенной пользователем, там также должна быть показана эта введенная пользователем буква и использована информационная пиктограмма сообщений Windows. (Совет. Добавьте константу vblnformation к инструкции MsgBox).
Большая часть работы, выполняемой данной процедурой, сводится к проверке ввода пользователя. Как вы узнаете на уроке 10-го дня, при проверке информации, введенной пользователем, можно избежать многих ошибок и проблем.
280
Неделя 2
Циклы
Вам уже известно, как выбирать различные варианты действий в зависимости от некоторых условий. Теперь настало время узнать, как заставить ваши процедуры многократно повторять некоторые действия либо заданное число раз, либо до выполнения определенного условия. На этом занятии мы рассмотрим следующие вопросы.
•	Что делают циклические команды и в чем разница между определенными и неопределенными циклами.
•	Когда и как использовать циклические структуры For для повторения операций фиксированное число раз.
•	Когда и как использовать циклическую инструкцию Do для повторения операций неопределенное число раз.
•	Как использовать четыре различные конфигурации инструкции Do.
•	Как и при каких обстоятельствах завершать цикл до выполнения всех его инструкций.
•	Как использовать вложенные циклы.
Что такое циклическое действия
Одним из недостатков записанных макросов является их неспособность повторять операции, если вы их не повторили вручную при записи самого макроса. В VBA имеется несколько мощных и гибких средств, позволяющих легко повторять операции и управлять их повторением.
Программные структуры, дающие возможность повторяться одной или нескольким инструкциям, называются циклами. Каждый раз, когда VBA завершает один полный цикл выполнения всех инструкций, находящихся внутри циклической структуры, называется итерацией цикла.
Некоторые циклические структуры созданы так, чтобы они выполнялись фиксированное число раз. Они называются определенными циклами. Другие выполняются неопределенное число раз, зависящее от некоторого условия. Ввиду того что это число повторений не определено, они называются неопределенными циклами.
И в тех и других структурах имеются выражения, показывающие, до каких пор следует повторять цикл. Они называются определителями цикла. В фиксированной структуре такой определитель почти всегда является числовым выражением. В неопределенной же структуре определитель обычно представляет собой логическое выра
День 9-Й. Циклы
281
жение, которое описывает условие, при котором выполнение цикла продолжается или останавливается. Логические выражения для циклических структур создаются и используются точно так же, как и в случае условных инструкций ветвления (принятия решений) языка VBA.
Принято два основных способа построения неопределенного цикла. В первом случае VBA будет проверять условие в определителе до выполнения цикла. Если условие выполнения имеет значение False, то инструкции внутри цикла просто будут пропущены. Во втором случае можно создать такой цикл, чтобы условие в определителе проверялось после выполнения этих инструкций.
На рис. 9.1 показано выполнение цикла, когда проверка происходит перед выполнением инструкций, находящихся внутри его структуры. В начале цикла VBA проверяет условие определителя (представленное в виде ромба). Если оно соблюдается, то выполняются инструкции, находящиеся внутри цикла. После выполнения последней из них происходит возврат в начало цикла и снова проверяется условие определителя. Если оно и в этот раз соблюдается, то VBA повторяет выполнение внутренних инструкций и снова выполняет переход в начало цикла, чтобы проверить определитель. Как только условия выполнения цикла перестают соблюдаться, то его выполнение прекращается и начинается выполнение находящихся за ним инструкций.
Обратите внимание, что если условия не соблюдаются, когда они проверяются первый раз, то инструкции внутри цикла не выполняются совсем; вместо этого тело цикла будет пропущено. (Телом цикла называются инструкции VBA, находящиеся между началом и концом циклической структуры.)
Конец
Рис. 9.1. Цикл, в котором условие определителя проверяется перед тем, как выполнять инструкции внутри цикла
282
Неделя 2
На рис. 9.2 показано выполнение цикла, когда проверка происходит после выполнения инструкций, находящихся в теле цикла. Вначале выполняются сами эти инструкции. Когда выполнится последняя из них, то проверяются условия определителя (представленного в виде ромба). Если условия соблюдаются, то происходит переход в начало и снова выполняются инструкции тела цикла. В конце цикла вновь проверяются условия определителя. Как только условия выполнения цикла перестают соблюдаться, его выполнение прекращается и начинается выполнение находящихся за ним инструкций.
Обратите внимание, что в этом случае VBA всегда выполняет инструкции из цикла, как минимум, один раз.
Рис. 9.2. Цикл, в котором условие определителя проверяется после того, как выполнены инструкции внутри цикла
Можно также создавать неопределенный цикл, у которого в определителе нет никакого условия вовсе. Такого рода циклы выполняются вечно и называются бесконечными. Они никогда не заканчиваются; большинство таких циклов является результатом ошибки программиста, хотя имеются специалисты, которые их используют.
В следующем разделе этого урока рассказывается об особенностях создания фиксированных и неопределенных циклов, а также о том, как создавать циклы, выполняющие проверку условия определителя перед или после выполнения их тела. Вы узнаете также, в каких условиях можно применять каждый из типов циклов.
День 9-Й. Циклы
283
Повторение фиксированное число раз: циклы Fur
Простейшим из циклов является фиксированный. В VBA имеется две различные структуры таких циклов: For...Next и For Each...Next. Обе называются циклами For, потому что всегда выполняются определенное число раз.
Применение цикла Fnr...Next
Первым из циклов For будет For...Next. Его следует использовать, когда требуется повторить действие или серию действий указанное число раз.
Цикл For.. .Next имеет такой синтаксис.
For counter = start То end [Step StepSize]
statements
Next [counter]
где counter представляет любую числовую переменную, соответствующую правилам VBA, обычно с типом данных Integer или Long. Выражение start является любым числовым выражением и определяет начальное значение переменной счетчика counter. Выражение end — также числовое выражение и определяет конечное значение той же переменной.
По умолчанию VBA каждый раз после завершения выполнения инструкций из цикла увеличивает значение переменной counter на 1 (прибавляет 1). Другое значение увеличения можно задать с помощью необязательного ключевого слова Step. После него необходимо указать величину (шаг), на которую надо увеличивать значение counter. В нашем синтаксисе StepSize представляет любое числовое выражение и как раз указывает эту самую величину.
statements представляет одну или несколько инструкций VBA (или вообще ни одной из них). Они составляют тело цикла, и каждая из них выполняется всякий раз, когда выполняется сам цикл.
Ключевое слово Next сигнализирует VBA, что достигнут конец цикла; необязательная переменная counter после Next должна быть той же переменной, которая находится после ключевого слова For в начале циклической структуры. Ее надо писать после Next, чтобы было легче читать исходный текст программы (особенно с вложенными циклами For.. .Next).
При выполнении цикла For...Next вначале переменной counter присваивается значение, представленное переменной start. Затем VBA выполняет все инструкции, представленные в statements, пока не будет достигнуто ключевое слово Next. Ключевое слово Next сигнализирует VBA, что достигнут конец тела цикла. Затем значение переменной counter увеличивается на величину переменной StepSize (шаг), если есть необязательное ключевое слово Step. Если этого слова нет, то значение counter увеличивается на 1. Происходит переход в начало цикла, а затем текущее значение counter сравнивается со значением, представленным end. Если counter будет меньше или равен end, то цикл выполняется снова. Если counter больше, чем end, то выполнение продолжается с первой инструкции, которая находится после ключевого слова Next.
284
Неделя 2
Использование For.„Next с возрастающим счетчпком
В листинге 9.1 показан простой пример цикла For... Next. Эта процедура получает от пользователя два числа, в заданном ими диапазоне суммирует все целые числа, а затем выводит на экран полученное значение. Если, например, вы запустили эту процедуру и ввели значения 4 и 8, то будут просуммированы числа 4, 5, 6, 7 и 8, а на экране будет отображено число 30.
Листинг 8.1. Пример цикла For-Neut с возрастающим счетчикам
1:	Sub Demo_ForNext()
2:
3:	Dim	k As Integer
4:	Dim	uStart As String
5:	Dim	uEnd As String
6:	Dim	uSum As Long
7:
8:	uStart = InputBox("Введите натуральное число:")
9:	uEnd = InputBox)"Введите второе натуральное число:")
10:
И:	uSum = 0
12:	For k = Cint(uStart)	То	Cint(uEnd)
13:	uSum = uSum + к
14:	Next к
15:
16:	MsgBox "Сумма натурального ряда от " & uStart &
17:	" до " & uEnd & " равна: " & uSum
18:	End Sub
В строках 3-6 описано несколько переменных, используемых этой процедурой. Переменная к служит в качестве счетчика цикла For...Next, а другие используются для хранения начального и конечного значений, полученных от пользователя, а также для хранения вычисленной суммы.
В строке 8 вызывается функция InputBox для получения от пользователя числа и присвоения его переменной uStart. В строке 9 опять используется InputBox для получения от пользователя другого числа и присвоения его на этот раз переменной uEnd. (Предполагается, что первое из этих чисел меньше второго.)
В строке 11 переменной uSum присваивается значение 0; введенные пользователем значения еще не обрабатывались, поэтому сумма должна быть нулевой.
Наконец в строке 12 начинается цикл For...Next. Когда VBA выполняет эту строку, то вначале выполняет вызов функции Cint, затем преобразует введенные пользователем строки в числа типа Integer, а потом вставляет их в инструкцию.
VBA присваивает переменной к (счетчику цикла) целочисленное значение, полученное из строки, хранящейся в uStart. Если в первом окне ввода было введено 4, то к в качестве начального значения присваивается 4.
Затем в той же строке 12 VBA сравнивает значение к с целочисленным эквивалентом строки, хранящейся в uEnd. Если к меньше или равно этому значению, то выполняются инструкции, находящиеся в теле цикла. Если во втором окне ввода было задано число 8, то будут сравниваться 4 и 8; так как 4 в самом деле меньше 8, то будет выполняться инструкция, находящаяся в строке 13.
День 9-й. Циклы
285
В теле цикла имеется только одна инструкция, где выполняются сложение и присвоение. При этом складываются текущие значения к и uSum, а результат сложения присваивается uSum. При первом выполнении цикла к равно 4 и uSum содержит 0; после выполнения этой инструкции в uSum хранится результат сложения 0+4, т.е. число 4.
Теперь в строке 14 мы видим инструкцию Next. Это сигнал о том, что тело цикла закончилось; VBA увеличит значение переменной счетчика и выполнит переход в начало цикла. В этой строке значение переменной счетчика равно 4. В цикле For.. .Next нет Step, так что в качестве шага возрастания используется значение по умолчанию, и к содержимому к будет прибавлена единица. Переменная счетчика будет содержать 5.
Выполняется возврат в строку 12, где находится начало цикла, и сравнивается значение переменной к с числом, представляющим содержимое переменной uEnd. В данный момент к содержит 5, которое меньше 8, поэтому VBA снова выполняет инструкцию из строки 13. После этого в переменной uSum будет находиться значение 9. Это результат сложения 4 (предыдущего значения uSum) и 5 (текущего значения счетчика).
Снова в строке 14 встретилась инструкция Next, значение к возросло, происходит переход в начало цикла, чтобы можно было сравнить значение счетчика с конечным значением. Вот таблица со значениями переменной счетчика к и переменной uSum , взятыми для каждого повторения цикла:
Цикл
Значение к
Значение uSum
0	0	0 (цикл не начался)
1	4	4
2	5	9
3	6	15
4	7	22
5	8	30
Когда цикл выполняется в пятый раз, переменная к будет содержать значение 8. После того как VBA увеличит в строке 14 значение счетчика, оно будет равняться 9. Когда будет выполнен переход в начало цикла, то выяснится, что переменная счетчика не удовлетворяет условию, при котором она должна быть меньше или равной указанному конечному значению 8. Переменная счетчика наконец-то превысила это значение, и выполнение цикла прекращается.
После завершения цикла VBA продолжит работу уже с инструкцией MsgBox из строки 16, которая выведет на экран два заданных вами числа и сумму всех целых значений из образованного этими числами диапазона.
В этой книге вам встретятся многие другие примеры циклов For... Next. Довольно часто они будут использоваться с массивами (об этом говорится в уроке 14-го дня,
ПРИМЕЧАНИЕ
Если первое введенное пользователем значение (находящееся в uStart) больше второго (из uEnd), то цикл For.. .Next совсем не будет работать: начальное значение счетчика превышает его конечное значение уже до начала выполнения цикла. В таком случае цикл вообще не начнет работу. Например, это случится, если вначале было введено 8, а затем 4; VBA пропустит инструкции цикла и продолжит работу с 16-й строки.
286
Неделя 2
Так надо
В качестве счетчиков циклов For.. .Next применяйте переменные с типами данных Integer или Long. ' В этом случае экономится память и ускоряется выполнение цикла.
После инструкции Next указывайте переменную счетчика цикла. Хотя это и не обязательно, но значительно облегчает чтение исходного текста программы. Кроме того, VBA не надо тратить время на то, чтобы опреде-  лить, какая переменная относится к данной инструкции Next, таким образом цикл будет работать быстрее.
Пользуйтесь тем, что при желании вы можете начинать счет от произвольного числа до любого другого.
Использование For...Next с убывающим счетчиком
Вам не всегда нужны циклы For.. .Next с возрастающим счетчиком. Иногда легче и полезнее написать цикл For, значение счетчика которого убывает. Для этого используйте ключевое слово Step и отрицательное число в качестве значения шага.
В листинге 9.2 показана процедура из листинга 9.1, с той лишь разницей, что цикл For...Next считает от большего значения к меньшему. Аналогично процедуре из листинга 9.1 здесь от пользователя принимается два числа, в заданном ими диапазоне суммируются все целые числа, а затем полученное значение выводится на экран. Если, например, вы запустите процедуру из листинга 9.2 и введете значения 8 и 4, то будут просуммированы числа 8, 7, 6, 5 и 4, а на экране появится число 30.
Листинг 9.2. Пример цикла For.. .Next с убывающим счетчикам
1:	Sub Demo_ForNextDown ()
2:
3:	Dim	k As	Integer
4:	Dim	uStart As Integer
5:	Dim	uEnd	As Integer
6:	Dim	uSum	As Long
7:
8:	uStart = CInt(InputBox(“Введите натуральное число:"))
9:	uEnd = CInt(InputBox)“Введите второе натуральное число:"))
10:
11:	uSum = О
12:	For k = uStart To uEnd Step -1 'цикл с убывающим счетчиком
13:	uSum = uSum + k
14:	Next k
15:
16:	MsgBox “Сумма натурального ряда от " & uStart &
17:	“ до “ & uEnd & “ равна: “ & uSum
18:	End Sub
Строка 8 отличается от соответствующей строки листинга 9.1. При выполнении этой инструкции вначале вызывается окно ввода InputBox для получения от пользователя числа. После того как число введено, VBA вызывает функцию CInt и преобразует String-результат функции InputBox в значение типа Integer. Это значение (соответствующее введенному пользователем числу) присваивается переменной uStart.
Аналогичным образом в строке 9 используется функция InputBox, чтобы получить значение от пользователя, вызывается функция CInt, чтобы преобразовать введенную пользователем строку в целое число, затем это целое значение присваивается пере-
чень 9-й. Циклы
287
менной uEnd. В этой процедуре вводимые пользователем значения не проверяются: предполагается, что первое из них больше второго.
С целью ускорить выполнение цикла функция Cint используется прямо в инструкциях, принимающих значения от пользователя. В процедуре же листинга 9.1 функция Cint выполнялась каждый раз, когда оценивалось условие определителя (строка 12 листинга 9.1), что замедляло цикл. В листинге 9.2 строки, введенные пользователем в обоих инструкциях InputBox, уже преобразованы в числа; поэтому цикл, который начинается в строке 12 этого листинга, работает намного быстрее.
Итак, в строке 12 листинга 9.2 начинается цикл For...Next. Когда VBA выполняет эту строку, он присваивает значение из uStart переменной счетчика, к. Если в первом окне ввода было задано число 8, то оно будет принято в качестве начального значения этого счетчика.
В строке 12 находится ключевое слово Step, так что VBA проверяет значение шага — величину, на которую будет увеличиваться значение счетчика. В этом цикле такой величиной является отрицательное число -1. Поэтому значение счетчика цикла, вместо того чтобы возрастать, убывает (от его значения производится вычитание).
Поскольку VBA уменьшает значение счетчика, то он проверяет, является ли эта величина большей или равной определенному конечному значению, вместо того, чтобы проверять, является ли она меньшей или равной этому значению.
Точно так же VBA в строке 12 сравнивает значение, хранящееся в к, с числом из uEnd. Если значение к меньше или равно uEnd, то выполняется тело цикла. Если вы во втором окне ввода задали число 4, то VBA будет сравнивать 8 и 4; 8 больше 4, поэтому будет выполняться инструкция в строке 13.
В этой строке складываются текущие значения к и uSum, а результат затем присваивается переменной uSum. Во время первого выполнения цикла к равно 8, а значение uSum равно О, поэтому после выполнения этой инструкции значение uSum будет равно 8, т.е. 0+8.
В строке 14 VBA встретилась инструкция Next. В цикле For...Next имеется Step, так что VBA использует в качестве шагового указанное после Step значение и отнимет от содержимого к единицу. Переменная счетчика будет содержать 7.
Затем выполняется возврат в строку 12, где находится начало цикла, и сравнивается значение переменной к с числом, представляющим содержимое переменной uEnd. В данный момент к содержит 7, которое больше 4, поэтому снова выполняется инструкция из строки 13. После этого в переменной uSum будет находиться значение 15, т.е. 8+7.
И вновь в строке 14 мы видим инструкцию Next, значение к уменьшилось, и происходит переход в начало цикла. Вот таблица со значениями переменной счетчика к и переменной uSum , взятыми для каждого повторения цикла:
Цикл	Значение к	Значение и Sum
0	0	0 (цикл не начался)
1	8	8
2	7	15
3	6	21
4	5	26
S	4	30
Когда цикл выполняется в пятый раз, переменная к будет содержать значение 8. После того как VBA уменьшит в строке 14 значение счетчика, оно будет равняться 3. Будет выполнен переход в начало цикла и сделано сравнение значений к и uEnd. Переменная счетчика не удовлетворяет условию, при котором она должна быть больше или равной указанному конечному значению, и выполнение цикла прекращается.
288	Неделя 2
После завершения цикла VBA продолжит работу уже с инструкцией MsgBox из строки 16, которая выведет на экран два заданных вами числа и сумму всех целых значений из образованного этими числами диапазона.
ПРИМЕЧАНИЕ
Если первое введенное пользователем значение меньше второго, то цикл For.. .Next вовсе не будет работать по той же причине, что и цикл из листинга 9.1, когда начальное значение счетчика превышает его конечное значение. Например, если вначале было введено 4, а затем 8, то VBA пропустит инструкции цикла и продолжит работу с 16-й строки.
Так надо
Чтобы в цикле For... Next счетчик работал в порядке убывания, используйте ключевое слово Step и отрицательное число.
Помните, что когда в цикле For... Next значение счетчика убывает, то цикл будет работать до тех пор, пока это значение остается большим или равным конечному значению счетчика; когда же значение счетчика возрастает, цикл будет работать до тех пор, пока это значение остается меньшим или равным конечному значению счетчика.
Помните, что в цикле For.. .Next после Step можно указывать любое числовое значение. В таких циклах счетчик может увеличиваться или уменьшаться на 0.1,2,5.1 или на любое другое десятичное число.
Как работает For...Next
Примеры процедур в листингах 9.1 и 9.2 показывают работу цикла For...Next в разных режимах, но ничего особо полезного не делают. Обычно такие циклы применяются, когда требуется выполнить работу определенное число раз или выполнить операции с определенным числом предметов.
Цикл For...Next можно использовать для проверки всех символов в строке, задания одного и того же форматирования в первых трех столбцах листа или для выполнения аналогичной задачи с определенным числом предметов.
В листинге 9.3 показан цикл For...Next, который повторяет определенные действия над каждым символом в строке. Функция PCase возвращает копию своего строкового аргумента, каждое слово в которой начинается с прописной буквы, а остальные буквы каждого слова — строчные. Например, если этой функции переданы для обработки строки VISUAL BASIC или visual basic, то будет возвращена строка Visual Basic. Кроме того, в листинге 9.3 имеется еще и процедура тестирования этой функции.
Лвствнв 9.3. Использование цикла For.. .Next ря аыпашш уебыша с каждым снмволвм в строке
1:	Function PCase(iStr As String) As String
2:	'Возвращает копию аргумента iStr, в которой первые символы
3:	'всех слов переведены в верхний регистр - Proper Case
4:
5;	Dim	oStr As String
6:	Dim	k As Long
7:	'записываем первый символ результирующей строки
8:	oStr	= UCase(Left(iStr, 1))
9:	'если пред, символ был пробелом, то текущий - первый в слове
10:	For	к = 2 То Len(iStr)
И:	If Mid(iStr, к - 1, 1) = " " Then
День 9-й. Циклы
289
12:	oStr = oStr & UCase(Mid(iStr, к, 1))
13:	Else	'если нет, переводим его в нижний регистр
14:	oStr = oStr & LCase(Mid(iStr, к, 1))
15:	End If
16:	Next к
17:
18:	PCase = oStr 'присваиваем результат функции
19:	End Function
20:
21:
22:	Sub test_PCase()
23:	'процедура для проверки функции PCase
24:
25:	Dim uStr As String
26:
27:	uStr = InputBox(“Ведите несколько слов, " &
28:	"разделенных пробелами:“)
29:	MsgBox "Вы ввели: " & uStr & Chr(13) &
30:	"Результат работы PCase : " & PCase(uStr)
31:	End Sub
В строке 1 находится описание функции PCase, у которой один обязательный аргумент (передаваемый по ссылке), а ее результат имеет тип String. В строке 5 описана строчная переменная, oStr, в которой должна будет находиться во время своего формирования изменяемая копия аргумента istr. В строке 6 описана переменная типа Long, к, которая будет использоваться в качестве переменной счетчика цикла. (В VBA строки могут иметь длину до двух миллиардов символов — предельного целого числа типа Long — так что счетчик цикла такого типа подходит для работы со строками максимально возможного размера.)
В строке 8 функция Left копирует первый символ аргумента istr, а функция UCase преобразует его в верхний регистр (если нужно); потом результат присваивается переменной oStr. Поскольку первый символ аргумента istr также является началом слова, то в строке 8 его только преобразуют в верхний регистр и сохраняют в oStr.
В строке 10 начинается цикл For...Next. При первом выполнении этого цикла его счетчику к присваивается значение 2. Работа начинается со второго символа из istr, так как первый уже был преобразован в строке 8. Конечное значение счетчика цикла (длину строки, хранящейся в istr) получают с помощью функции Len. Этот цикл For.. .Next будет выполняться для значений счетчика, начиная с 2 и заканчивая значением, равным числу символов из istr. При этом тело цикла будет выполняться один раз для каждого из символов в istr, оставшихся после обработки первого из них.
В строке 11 начинается тело цикла For...Next. Оно содержит единственную инструкцию If.. .Then.. .Else, которая также начинается в строке 11.
Функция PCase применяется для того, чтобы сделать прописной первую букву слова, поэтому ей необходимо знать, когда в строке начинается новое слово. Обычное определение слова, используемое в большинстве компьютерных программ, заключается в том, что это произвольная группа символов отделятся от других символов строки пробелами. Для того чтобы установить, является ли буква первой в слове, функция PCase проверяет, стоит ли перед ней пробел.
В строке 11 для возврата одного символа из istr применяется функция Mid. Начальная позиция, с которой надо копировать символы, определяется как к-1, а количество
290
Неделя 2
таких символов — как 1. При первом выполнении цикла к равна 2, что указывает на второй символ из iStr, так что функция Mid из строки 11 возвращает первый символ строки, находящейся в iStr (2-1). Логическое выражение инструкции If.. .Then.. .Else проверяет, не является ли символ, возвращенный функцией Mid, пробелом.
Если символ, предшествующий текущему из iStr (указанному к), является пробелом, то это означает, что текущий символ должен быть началом слова. Когда логическое выражение в строке 11 имеет значение True (истина), то VBA выполняет инструкцию из строки 12, в которой снова используется функция Mid, чтобы скопировать символ из iStr. На этот раз копирование происходит с позиции к, а результат этой операции передается функции UCase, которая, в свою очередь, переводит символ в верхний регистр. Результат добавляется в конец строки, уже находящейся в oStr, и измененная строка становится значением этой переменной. По мере выполнения цикла oStr становится все длиннее из-за того, что в нее после обработки копируется все новый и новый символ из iStr. После прекращения цикла у oStr будет та же длина, что и у iStr.
Если символ, предшествующий в iStr текущему (определяемому к), не является пробелом, то текущий символ должен находиться где-то в середине слова. Когда логическое выражение в строке 11 имеет значение False (ложь), то VBA выполняет инструкцию из строки 14, в которой вновь, в который раз, для копирования символа из iStr используется функция Mid. Символ копируется с позиции к и передается функции LCase, которая, в свою очередь переводит его в нижний регистр (символы в середине слова не должны быть в верхнем регистре). Результат добавляется в конец строки, уже находящейся в oStr, и измененная строка становится значением этой переменной.
В какой бы строке ни выполнялась программа, в 12-й или 14-й, следующей будет строка 16. Там находится инструкция Next. При ее выполнении VBA увеличивает счетчик цикла к на единицу (не было указано значения, определяемого с помощью Step) и выполняет переход в начало цикла (строка 10). Если к остается меньше или равным длине строки из iStr, то снова выполняется тело цикла.
Когда выполнение цикла закончится, то все символы из строки iStr будут обработаны, a VBA в строке 18 присвоит значение, которое будет возвращено функцией PCase. Этим значением является строка, хранящаяся в oStr. Затем в строке 19 функция завершается, возвращая результат.
В строках 22-31 находится простая процедура, предназначенная для тестирования функции PCase. В строке 27 для получения от пользователя набора символов используется функция InputBox. В строке 29 используется функция MsgBox, чтобы вывести на экран полученную от пользователя строку и выполнить функцию PCase с целью показать на экране ее результат. Если была введена строка visual basic for applications, то инструкция MsgBox из строк 29 и 30 выведет на экран диалоговое окно, показанное на рис. 9.3.
Такого рода действия, как обработка каждого символа строки, каждого столбца, каждой строки листа Excel, — являются идеальным применением цикла For.. .Next.
Microsoft Excel
Вы ввели: visual basic for applications
Результат работы PCase: Visual Basic For Applications
Рис. 9.3. Если вы введете строку visual basic for applications, то процедура test_PCase выведет такое сообщение
День 9-Й. Циклы
291
ПРИМЕЧАНИЕ
На самом деле вам не нужно писать функцию PCase, чтобы использовать ее в процедурах VBA. В этой главе ее используют в качестве легкого примера обработки строк с помощью цикла For. В VBA имеется другая функция, StrConv, которая выполняет те же и многие другие задачи. Ее можно использовать для перевода всей строки в верхний и нижний регистры, а также для перевода первой буквы каждого слова в верхний регистр. Кроме того, она применяется для преобразования строки из или в Unicode (это схема кодирования символов Международной организации по стандартизации). Более подробная информация об этой функции содержится в в справочной системе VBA.
Так надо
Чтобы циклы For.. .Next были легкими для понимания и написания, применяйте в них в зависимости от ситуации либо возрастающие, либо убывающие счетчики.
Так не над

Не меняйте внутри тела цикла For... Next значение его счетчика. Хотя это и можно делать, в результате может получиться, что цикл выполнится слишком мало или слишком много раз, приводя тем самым к трудноуловимым ошибкам внутри процедуры.
Применение цикла For Each-Next
Второй разновидностью цикла For является цикл For Each...Next. В отличие от For...Next он не использует счетчика цикла. Вместо этого он выполняется столько раз, сколько имеется элементов в определенной группе, такой как коллекция объектов или массив. (Коллекции объектов описаны при изложении материала урока 7-го дня, а массивы — урока 14-го дня.) Другими словами, цикл For Each...Next выполняется один раз для каждого элемента группы.
Цикл For Each.. .Next имеет следующий общий синтаксис.
For Each element In group
statements
Next [element]
Переменная element используется для перебора всех элементов указанной группы, group — это коллекция объектов или массив. Если group является коллекцией объектов, то element должен быть переменной типа Variant, Object или определенным типом объекта, таким как Range, Worksheet, Cell, Chart и т.д. Если group является массивом, то element должен быть переменной типа Variant, statements представляет одну или несколько инструкций VBA, которые составляют тело цикла.
Цикл For Each...Next имеет меньше возможностей, чем For...Next. Для него не подходит счетчик с возрастанием или убыванием по той причине, что он всегда выполняется столько раз, сколько элементов в указанной группе.
В листинге 9.4 показана функция SheetExists, которая использует цикл For Each.. .Next, чтобы определить, существует ли указанный лист в книге Excel.
292
Неделя 2
Листинг 8.4. Использование цикла For Each...Next
1:	Function SheetExists(sName As String) As Boolean
2:	'Возвращает True, если лист с именем sName существует в тех. книге
3:
4:	Dim aSheet As Object
5:
6:	SheetExists = False 'на случай, если листа не окажется
7:
8:	'перебираем все листы, сравнивая имя каждого с
9:	'аргументом sName, сравнение текстовое.
10:	For Each aSheet In ActiveWorkbook.Sheets
11:	If (StrComp(aSheet.Name, sName, 1) = 0) Then
12:	SheetExists = True 'лист найден, возвращаем true
13:	End If
14:	Next aSheet
15:	End Function
16:
17:
18:	Sub Test_SheetExists()
19:	'Для проверки функции SheetExists
20:
21:	Dim uStr As String
22:
23:	uStr = InputBox("Какой лист вы хотите найти " &
24:	"в текущей книге:")
25:	If SheetExists(uStr) Then
26:	MsgBox "Лист '" & uStr & _
27:	"' найден в текущей книге."
28:	Else
29:	MsgBox "В текущей книге листа '" & uStr &
30:	”' нет."
31:	End If
32:	End Sub
У этой функции есть один обязательный аргумент sName, который содержит строку, представляющую имя разыскиваемого листа. SheetExists возвращает значение типа Boolean: True (истина), если лист с именем, хранящемся в sName, находится в текущей книге, и False (ложь), если нет. Такую функцию вы можете использовать в своих процедурах, чтобы перед тем, как пытаться выбрать лист или диаграмму, проверять, существуют ли они на самом деле. Ведь если процедура пытается выбрать несуществующий лист, то VBA выдает сообщение об ошибке выполнения и программа останавливается. Функция, подобная SheetExists, даст возможность писать процедуры, обнаруживающие и обходящие возможные ошибки, как, например, те, когда пользователь вводит имя несуществующего листа.
В строке 4 описана используемая в цикле For Each...Next объектная переменная aSheet. В строке 6 присваивается значение False результату функции SheetExists. Эта инструкция предполагает, что лист с именем, хранящемся в sName, не будет найден. Такое предположение значительно облегчит задачу принятия решения в оставшейся части функции. Теперь остается только обнаружить, существует ли лист, и если да, то присвоить результату функции значение True.
День 9-й. Циклы
293
В строке 10 начинается цикл For Each...Next, использующий в качестве переменной элемента объектную переменную aSheet. В цикле группой является коллекция ActiveWorkbook.Sheets. ActiveWorkbook представляет свойство объекта Application и возвращает объектную ссылку на текущую активную книгу. Sheets служит методом книги, который возвращает коллекцию всех ее листов, включая и листы диаграмм.
В строке 10 для VBA дается указание выполнять инструкции в теле цикла столько раз, сколько листов в активной книге, т.е. повторять операции по одному разу для каждого объекта коллекции Sheets. В начале выполнения цикла For Each...Next переменной aSheet присваивается ссылка на первый объект из этой коллекции, каким бы он ни был.
В строке И начинается тело цикла, которое состоит из одной инструкции If...Then. Когда выполняется строка И, вначале вызывается StrComp, чтобы сравнить строку в sName со строкой, хранящейся в свойстве Name того листа, ссылка на который находится в aSheet. Одновременно указывается, что сравнение должно быть текстовым, т.е. проводящимся над этими строками без учета регистра. Здесь для сравнения строк вместо простого оператора сравнения используется StrComp, чтобы сравнение строк всегда было текстовым, независимо от значений, заданных Option Compare.
Если строки из sName и свойства Name равны друг другу, то выполняется строка 12, где результату выполнения функции присваивается значение True.
Какой бы ни был результат сравнения строк, выполняется строка 14, где находится ключевое слово Next, завершающее структуру цикла For Each...Next. После этого происходит переход в строку 10, где находится начало цикла, а затем проверяется, просмотрены ли все элементы группы. Если нет, то объектной переменной aSheet присваивается следующий элемент коллекции Sheets и цикл выполняется снова. Если, например, в активной книге имеется 10 листов, то цикл For Each...Next выполняется 10 раз; если имеется четыре листа, то цикл выполняется четыре раза.
Когда цикл выполнен для всех элементов коллекции ActiveWorkbook.Sheets, происходит переход в 15 строку, где заканчивается функция SheetExists и возвращается ее результат. Если во время выполнения цикла For Each...Next строки из sName и свойства Name когда-либо равнялись друг другу, то результатом функции будет True, а это означает, что лист, указанный в sName, находится в активной книге; в противном случае результатом функции будет False, т.е. листа не существует.
В строках 18-29 описана процедура Test_SheetExists, предназначенная для тестирования функции SheetExists. В строке 23 для получения от пользователя имени листа используется функция InputBox, а в строках с 25 по 31 находится инструкция If.. .Then.. .Else, которая вызывает функцию SheetExists, проверяет ее результат и выводит сообщение, имеется или нет в текущей книге названный пользователем лист.
Когда выполняется строка 25, то вызывается функция SheetExists, которой в качестве аргумента передается введенная пользователем строка (uStr). Если пользователь ввел правильное имя, то функция возвращает True и выполняется строка 26, в которой выводится соответствующее сообщение. В противном случае функция возвращает False и будет выполнена строка 29, выводящая сообщение, что листа не существует.
В этой книге вы еще много раз встретите примеры цикла For Each...Next, особенно в уроке 14-го дня, где этот цикл применяется для работы с массивами.
Так надо
Проверяйте, чтобы в цикле For Each.. .Next переменная элемента (element) была по типу совместима с указанной группой (group). Если указана коллекция листов, то переменная элемента должна иметь тип ' Variant, тип Object или тип Worksheet.
294
Неделя 2
Применяйте прием с предварительным указанием одного из результатов теста, чтобы облегчить для него процесс принятия решения, как это было сделано в строке 6 для функции SheetExists.
Для сравнения строк используйте функцию StrComp, когда требуется либо двоичное, либо текстовое сравнение, которое не должно зависеть от значений Option Compare.
Повторение неопределенное число раз: циклы По
В VBA имеется очень мощная инструкция для создания в процедурах и функциях циклических структур, повторяющихся неопределенное число раз. Все такие циклы создаются с помощью единственной инструкции — инструкции Do. Она обладает такими возможностями и такой гибкостью, что у ней фактически четыре конструкции, относящиеся к двум основным категориям.
Эти категории состоят, во-первых, из циклов Do, которые проверяют условие определителя перед выполнением тела цикла, и, во-вторых, из тех циклов Do, которые проверяют это условие после выполнения тела цикла. В последующих разделах этой главы описан синтаксис этих категорий, а также когда и как их использовать.
Независимо от того, проверяется ли определитель до или после выполнения тела цикла, существует два способа контролировать, сколько раз выполнялся неопределенный цикл.
•	Циклы, контролируемые счетчиками. В этом случае тело цикла выполняется, пока значение некоторого счетчика находится ниже или выше указанной границы; в этом есть определенное сходство с циклом For...Next, за исключением того, что на программисте лежит ответственность за инициализацию этого счетчика, а также за увеличение или уменьшение его значения. Такие циклы Do имеет смысл писать, когда шаг счетчика не является регулярным, а также когда нельзя определить конечную границу, пока не началось выполнение самого цикла. Например, требуется пройти первые 16 строк листа, иногда увеличивая за один раз значение строки на 1, а иногда за один раз — на 2. В связи с тем что увеличение номера строки (то есть шаг счетчика) меняется, цикл For.. .Next использовать нельзя; вместо него надо использовать цикл Do.
•	Циклы, контролируемые событиями. В этом случае значение определителя имеет значение True или False, в зависимости от некоторого события или действия, имевшего место во время выполнения цикла. Например, вы написали цикл, выполняемый неопределенное число раз, пока пользователь не введет известное число в диалоговом окне. Ввод такого числа как раз и является тем событием, которое завершит цикл. А вот другой пример: можно выполнять операции над ячейками листа, пока не будет достигнута пустая ячейка. Переход в пустую ячейку и будет событием, завершающим цикл.
Как VBA проверяет определитель цикла
Условие определителя в неопределенном цикле указывается в виде логического выражения точно так же, как и условия в инструкциях If.. .Then.
В VBA имеется два разных способа проверки условия определителя. Можно создать цикл, который выполняется, пока условие его определителя будет иметь значение True, и завершается, как только это условие изменит значение на False. Можно также создать цикл, который выполняется при значении False и завершается при значении True.
День 9-Й. Циклы
295
Предположим, что требуется процедура, которая поможет пользователю вводить данные в лист. В этом случае, возможно, потребуется, чтобы повторялись определенные инструкции, пока происходит ввод данных, и чтобы инструкции больше не выполнялись, если данные введены полностью. Поэтому можно написать цикл Do, который каждый раз принимает от пользователя данные и продолжает работу, если пользователь вводит непустую строку, и завершает работу, если введена пустая строка. Для этого цикла можно написать условие определителя, у которого логическое выражение будет True — и таким образом цикл будет продолжать выполняться — пока пользователь вводит непустые строки. В случае, если введенная пользователем строка находится в переменной uStr, логическим выражением для определителя цикла может служить одно из двух приведенных ниже выражений.
uStr <>
LenfTrim(uStr)) о О
Оба выражения имеют значение True, когда строка в uStr имеет ненулевую длину (или не состоит полностью из пробелов, как во втором примере).
Чтобы цикл выполнялся, пока условие его определителя имеет значение False, в инструкции Do следует использовать необязательное ключевое слово Until. Тогда VBA будет выполнять цикл, пока не (until) принято для условия значение True, т.е. цикл продолжает выполняться, пока условие имеет значение False, и прекращается, когда значением условия определителя становится True.
Например, можно написать процедуру, которая улучшает столбец за столбцом, применяя к их заголовкам определенный текстовый формат до тех пор, пока не встретится пустой столбец. Можно для этого написать цикл Do, работающий, пока не встретится первая пустая ячейка. В этом случае условие определителя можно сделать равным False, когда текущая ячейка непустая, или True в противном случае. Когда для получения текущей активной ячейки используется свойство ActiveCell объекта Application, а для получения ее содержимого применяется свойство Value, логическим выражением для определителя цикла может служить одно из двух следующих выражений.
ActiveCell.Value = ""
Len(Trim(ActiveCell.Value)) = 0
Оба выражения получают значение True, когда текущая ячейка пустая.
Какие использовать ключевые слова, While или Until, — в основном зависит от того, как вы себе представляете условия, при которых должен выполняться цикл, и от того, как будет легче построить условие определителя цикла.
•	Если требуется, чтобы цикл выполнялся, пока определенное условие равно True, используйте While.
•	Если требуется, чтобы цикл выполнялся, пока определенное условие равно False, используйте Until.
Всегда можно написать циклическую структуру любым из этих двух способов, в зависимости от того, как сформулировано логическое выражение; в этом случае выбор одного из них — это в основном дело вкуса.
В последующих разделах описано, как использовать отдельные возможности инструкции Do.
296
Неделя 2
ПРИМЕЧАНИЕ
При создании циклов Do будьте осторожны, когда выбираете условия определителя, которые в нужный момент действительно должны остановить цикл. Наиболее распространенной ошибкой среди программистов, создающих неопределенные циклы, является неправильная формулировка условия определителя, которая приводит к тому, что цикл никогда не сможет остановиться из-за того, что значение этого условия никогда не станет False (или True в цикле Until). Циклы, которые никогда не смогут остановиться, называют бесконечными, потому что выполняются они бесконечное число раз.
Если в одной из своих процедур вы столкнетесь с бесконечным циклом, то эта процедура никогда не закончится и компьютерная система может прийти к крушению или может быть заблокированной. Если крушение системы является результатом бесконечного цикла, то процедуру можно прервать, как описано во врезке к этому разделу. Если есть подозрение, что компьютер заблокирован из-за бесконечного цикла в одной из процедур, то ее можно прервать, нажав клавишу Esc (смотрите врезку). Перезагружайте компьютер только в последнюю очередь; перезагрузка может вызвать потерю данных в программах, которые выполнялись в этот момент.
Км прервать вышшае программы
Ввиду того что одной из наиболее распространенных проблем при работе с неопределенными циклами является создание по небрежности какого-нибудь бесконечного цикла, то важно знать, как прервать VBA при выполнении одной из ваших процедур. Те же самые приемы используются при прерывании и записанного системой макроса, и написанной от руки процедуры или функции.
Чтобы прервать выполнение VBA, нажмите клавишу Esc или комбинацию клавиш Ctrl+Break. VBA завершит выполнение текущей инструкции, приостановит работу следующих за ней и выведет на экран диалоговое окно с сообщением об ошибке выполнения, как показано на рис. 9.4. Командные кнопки в этом окне те же самые, что и в других аналогичных окнах (они были описаны в уроке 2-го дня), и имеют тот же эффект:
	Continue (продолжить). Возобновляет выполнение программных инструкций. Обычно выбирать эту кнопку при бесконечном цикле не стоит, так как он начнет выполняться снова.
•	End (закончить). Завершает программу. Теряются все переменные и их содержимое. При бесконечном цикле рекомендуется выбирать эту кнопку.
•	Debug (отладить). Активизирует режим прерывания VBA, чтобы можно было проверить содержимое переменных и пройти по циклу с той целью, чтобы выяснить, почему он не останавливается: Режим прерывания и отладка программ описаны в уроке 15-го дня, «Отладка и тестирование программ VBA».
•	Help (справка). Активизирует компьютерную справочную систему VBA, чтобы вывести информацию о том, почему прервано выполнение. Эта кнопка не очень полезна, если выполнение прервано с помощью Esc или Ctrl+Break: в этом случае будет сообщено, что выполнение прервано с помощью этих кнопок, и будут даны инструкции, как ввести режим прерывания или завершить программу.
Нельзя прервать VBA, когда выведено диалоговое окно; перед прерыванием процедуры надо закрыть все диалоговые окна - к счастью, клавиша Esc закрывает и открытые диалоговые окна.
В зависимости от цикла и находящихся в нем инструкций, VBA может не прореагировать на единичное нажатие Esc или Ctrl+Break. Фактически обычно требуется нажать Esc и держать ее в таком положении, чтобы выполнение процедуры было прервано. Но в этой ситуации можно нечаянно закрыть диалоговое окно об ошибке выполнения (см. рис. 9.4).
В результате это окно может промелькнуть на экране слишком быстро, чтобы можно было в нем что-либо прочитать. Если это случилось, то не переживайте; у вас, возможно, на основе того, насколько выполнилась программа, перед тем как произошло “зацикливание”, есть идея, с каким именно циклом такое могло случиться, да и обычной реакцией на появление такого окна будет выбор кнопки End - эквивалента клавиши Esc.
День 9-й. Циклы
297
Рис. 9.4. VBA выведет это окно, когда были нажаты клавиши Esc или Ctrl+Break с целью прервать выполнение процедуры или макроса
Циклы с проверкой условии перед выполнением
Чтобы условия определителя проверялись перед выполнением тела цикла, надо логическое выражение определителя просто поместить в начале инструкций, составляющих тело цикла. В последующих разделах описано, как реализовать эту возможность с помощью ключевых слов while и Until.
Создание циклов е помощью Во While
Первой циклической структурой, где условие определителя проверяется перед выполнением цикла, является Do While.
Do while имеет следующий общий синтаксис.
Do While condition
statements
Loop
где condition представляет логическое выражение для определителя цикла, statements — одну или большое количество инструкций VBA (или вообще ни одной из них), которые составляют тело цикла; все они выполняются каждый раз, когда выполняется сам цикл. Ключевое слово Loop, находящееся после statements, сигнализирует, что достигнут конец цикла, и указывает точку, с которой выполняется переход в начало цикла, чтобы проверить условие определителя.
В инструкции Do While выражение condition находится в начале цикла, поэтому условие определителя проверяется перед выполнением самого цикла. Поскольку в этом случае используется ключевое слово While, цикл будет выполняться до тех пор, пока логическое выражение, представленное condition, будет иметь значение True.
Перед выполнением цикла Do while вначале проверяется логическое выражение, представленное condition; если у него значение True, то выполняются инструкции, представленные statements. Когда достигнуто ключевое слово Loop, происходит переход в начало цикла и снова проверяется, сохранилось ли у condition значение True. Если да, то цикл выполняется снова; если у condition значение изменилось на False, то выполнение продолжается с инструкций, находящихся за ключевым словом Loop.
Обратите внимание, что если логическое выражение, представленное condition, будет при первом выполнении инструкции Do While иметь значение False, то цикл будет просто пропущен без всякого выполнения.
298
Неделя 2
В листинге 9.5 показан простой пример цикла Do While, в котором периодически принимается от пользователя какое-нибудь число и остановка происходит только после того, как получено 10 нечетных чисел.
Листпиа 8.5. Пример цикла Do while
1:	Sub Count OddNums()
2:	'Считает введенные пользователем нечетные числа, прекращает
3:	'работу, когда число нечетных чисел достигает 10
4:
5:	Const ocTitle = "Подсчет нечетных чисел"
6:
7:	Dim	OddCount As Integer	'счетчик нечетных чисел
8:	Dim	OddStr As String	'строка для отображения нечетных чисел
9:	Dim	Num	'переменная типа Variant для ввода
10:
11:	OddStr = ””
12:	OddCount = 0	'инициализация счетчика цикла
13:	Do While OddCount < 10
14:	Num = InputBox)"Введите число:", ocTitle)
15:	If (Num Mod 2) <> 0 Then
16:	OddCount = OddCount + 1
17:	OddStr = OddStr & Num & " "
18:	End If
19:	Loop
20:
21:	MsgBox prompt:="Вы ввели такие нечетные " &
22:	"числа: " & Chr(13) & OddStr,
23:	Title:=ocTitle
24:	End Sub
Count_OddNums просто показывает, как создавать цикл Do While. Цикл внутри процедуры выполняется только тогда, когда пользователь ввел меньше десяти нечетных чисел (нечетными называются числа, которые не делятся нацело на 2). Вначале может показаться, что эту задачу можно выполнить с помощью цикла For.. .Next, но в действительности это невозможно. Хотя в нашей процедуре цикл контролируется счетчиком, этот счетчик не обязательно возрастает при каждом выполнении цикла. Если пользователь ввел четное число, то значение счетчика нечетных чисел увеличено не будет. Единственным способом создать цикл со счетчиком, значение которого возрастает не всегда, будет использование одного из вариантов инструкции Do.
В строке 7 описана переменная OddCount, имеющая тип Integer, в которой будет храниться счетчик нечетных чисел. В строке 8 описана переменная OddStr, имеющая тип String, в которой будет храниться строка, сформированная из введенных пользователем нечетных чисел. В строке 9 описана переменная Num, имеющая тип Variant, в которой будет храниться введенное пользователем значение. Переменная такого типа используется для того, чтобы отдельно не преобразовывать в число строку, полученную от функции ввода InputBox.
В строке 11 инициализируется переменная OddStr путем присвоения ей пустой строки. В строке 12 находится очень важная инструкция присвоения, относящаяся к выполнению цикла. Здесь переменной OddCount присваивается значение 0, благодаря чему будет выполняться цикл, начинающийся в строке 13. (Не следует считать, что переменная содержит некоторое значение, пока оно ей не присвоено в одной из инструкций.)
День 9-Й. Циклы
299
В строке 13 начинается цикл Do. В этой инструкции в начале цикла находятся ключевое слово While и условие определителя, поэтому этот цикл будет выполняться, пока значением условия будет True, и условие определителя будет проверяться перед выполнением цикла. При первой проверке этой инструкции значением переменной OddCount является 0, поэтому выражение OddCount <10 является истинным, т.е. имеет значение True, и со строки 14 начинается выполнение цикла.
Строка 14 — это первая инструкция тела цикла; в ней для получения от пользователя числа применяется InputBox и введенное значение сохраняется в переменной Num.
В строке 15 находится инструкция If...Then, в которой проверяется введенное пользователем число. При проверке выражения (Num Mod 2) <> 0 вначале в скобках выполняется деление по модулю. (Помните, что в VBA сначала проверяется часть выражения, находящаяся в скобках.) Оператор деления Mod, как было установлено в ходе урока 4-го дня, возвращает остаток от деления. После деления Mod выполняется указанная операция отношения. Если результат этого деления не равен 0, то выражение имеет значение True; если результат равен 0, то у выражения значение False. Нечетным является число, которое не делится нацело на 2; поэтому если после деления Num на 2 получается остаток, то в Num должно находиться нечетное число.
Если Num является нечетным числом, то должны выполняться инструкции из строк 16 и 17. В строке 16 к текущему значению переменной счетчика цикла OddCount прибавляется единица, а получившийся результат присваивается этой же переменной OddCount. В строке 17 инструкция выполняет конкатенацию: добавляет строчный эквивалент нечетного числа в конец строки из переменной OddStr. (Эта строка формируется для того, чтобы можно было показать на экране введенные пользователем нечетные числа, когда цикл закончит работу.)
При выполнении строки 19 ключевое слово Loop сигнализирует о том, что достигнут конец цикла и надо перейти в его начало. Выполняется переход в строку 13, и снова проверяется логическое выражение цикла. Если OddCount все еще меньше 10, то снова выполняется тело цикла, принимается от пользователя новое число, проверяется, является ли оно нечетным, и, если нужно, увеличивается значение переменной OddCount.
Однако как только пользователь введет 10 нечетных чисел, значением OddCount также будет 10 и логическое выражение цикла уже не будет верным (не будет иметь значение True). Тогда тело цикла будет пропущено, и выполнение начнется с первой инструкции, находящейся после строки с ключевым словом Loop, в данном случае — со строки 21.
Инструкция в этой строке выводит сообщение с переменной OddStr, где находится перечень всех указанных пользователем нечетных чисел.
При изучении Do While обратите внимание, что в действительности цикл может выполняться намного больше десяти раз. Например, если при выполнении этой процедуры вы ввели все целые числа от 1 до 20, то цикл будет выполняться 20 раз, хотя было введено только 10 нечетных чисел.
Создание циклов с помощью Do Until
В инструкции Do вариант Do While — это только один из тех способов ее создания, в которых условие определителя проверяется перед выполнением тела цикла. Вторым из этих способов является вариант Do Until.
Do Until имеет такой общий синтаксис.
Do Until condition
statements
Loop
300
Неделя 2
где condition представляет логическое выражение для определителя цикла, а statements — инструкции VBA, которые составляют тело цикла. Ключевое слово Loop, находящееся после statements, сигнализирует, что достигнут конец цикла, и указывает точку, с которой выполняется переход в начало цикла, чтобы проверить условие определителя.
В инструкции Do Until выражение condition находится в начале цикла, поэтому условие определителя проверяется перед выполнением самого цикла. В связи с тем что в этом случае используется ключевое слово Until (что означает пока нет чего-либо), цикл будет выполняться до тех пор, пока логическое выражение, представленное condition, будет иметь значение False.
Перед выполнением цикла Do Until вначале проверяется логическое выражение, представленное condition; если оно имеет значение False, то выполняются инструкции, представленные statements. Когда достигнуто ключевое слово Loop, выполняется переход в начало цикла и снова проверяется, сохранилось ли у condition значение False. Если да, то цикл выполняется снова; если у condition значение изменилось на True, то выполнение продолжается с инструкций, находящихся за ключевым словом Loop.
Если логическое выражение, представленное condition, будет при первом выполнении инструкции Do Until иметь значение True, то цикл будет просто пропущен без всякого выполнения.
В листинге 9.6 показан простой пример цикла Do Until, в котором время от времени от пользователя принимается какое-нибудь число и остановка происходит только после того, как пользователь введет четное число, большее 10.
Листинг 8.8. Пример цикла Do Until
1:	Sub Stop_AtEvenNums()
2:	'Принимает числа от пользователя до тех пор, пока он не введет
3:	'четное число, большее 10, на этом работа заканчивается.
4:
5:	Const evTitle = "В ожидании четного числа..."
6:
7:	Dim EvenFlag As Boolean
8:	Dim Num	'Variant-переменная для ввода
9:
10:	EvenFlag = False 'инициализация условия цикла
11:	Do Until EvenFlag = True
12:	Num = InputBox("Введите число:", evTitle)
13:	If ((Num Mod 2) = 0) Then
14:	If Num > 10 Then
15:	MsgBox prompt:="BH ввели четное число, " &
16:	"большее чем 10 - цикл заканчивается.",
17:	Title:=evTitle
18:	EvenFlag = True
19:	Else
20:	MsgBox prompt:="BM ввели четное число, " 4
21:	"меньшее чем 10 - цикл" &
22:	"продолжается ",
23:	Title:=evTitle
24:	End If
25:	Else
26:	MsgBox prompt:="BH ввели нечетное число.",
27:	Title:=evTitle
День 9-й. Циклы
301
28:	End If
29:	Loop
30:
31:	MsgBox prompt:="Цикл закончен.", Title:=evTitle
32:	End Sub
Stop_AtEvenNums показывает, как создавать цикл Do Until. Цикл внутри процедуры выполняется до тех пор, пока пользователь не введет четное число, большее 10. Он является примером цикла, контролируемого событием. В данном случае событием, завершающим его работу, является ввод четного числа.
В строке 7 описана переменная EvenFlag, имеющая тип Boolean. Эта переменная используется в качестве флага, чтобы просигналить, должен ли цикл выполняться. Циклы, контролируемые флагом, являются особой разновидностью циклов, контролируемых событием. В этом цикле из-за того, что используется ключевое слово Until, поднятый флаг (со значением True) сигнализирует, что цикл должен завершиться. Цикл, использующий ключевое слово While, обычно организован так, чтобы он продолжал выполняться, если значение флага True.
В строке 8 описана переменная Num, имеющая тип Variant, в которой будет храниться введенное пользователем значение. Переменная такого типа используется для того, чтобы автоматически преобразовать строку в число.
В строке 10 переменная EvenFlag инициализируется путем присвоения ей значения False. Эта инструкция присвоения очень важна для цикла, который начинается в строке 11. Благодаря этому присвоению цикл действительно начнет выполняться.
Во время первой проверки инструкции из строки 11 переменная EvenFlag имеет значение False. Таким образом выражение определителя, где находится только переменная EvenFlag, тоже имеет значение False, поэтому со строки 12 начинается выполнение тела цикла.
В строке 12 для получения от пользователя числа применяется InputBox и введенное значение сохраняется в переменной Num. Затем в строке 15 инструкция If.. .Then.. .Else проверяет введенное пользователем число. Вначале проверяется выражение (Num Mod 2) = 0 (обратите внимание, что это не то же выражение, которое используется в листинге 9.5 для проверки числа на нечетность). На этот раз результат деления Mod проверяется на равенство 0. Если после деления Num на 2 нет остатка, то в Num должно находиться четное число.
Если Num является четным числом, то выполнение продолжается со строки 14, где начинается вложенная инструкция If.. .Then.. .Else. Эта инструкция проверяет, не является ли значение из Num большим 10. Если является, то выполняется строка 15, где используется инструкция MsgBox, чтобы вывести сообщение о том, что полученное от пользователя число четное и больше 10.
Строка 18 также очень важна в этом цикле. В этой строке переменной EvenFlag присваивается значение True. После выполнения этой строки условие определителя получает значение True и цикл прекращает выполняться после ближайшей проверки выражения определителя. Обратите внимание, что это выражение в строке 18 выполняется лишь тогда, когда значение переменной Num одновременно четное и больше 10.
Если значение Num не больше 10, то выполняется компонент Else, который начинается в строке 19 и содержит одну инструкцию MsgBox, находящуюся в строке 20. Эта инструкция выводит сообщение о том, что полученное от пользователя число четное, но меньше 10. Переменная EvenFlag не меняется, так что цикл продолжает выполняться.
302
Неделя 2
Если значение Num не является четным числом, то выполняется компонент Else из строки 25, в котором содержится одна инструкция MsgBox, находящаяся в строке 26. Эта инструкция выводит сообщение о том, что полученное от пользователя число нечетное.
Какие бы ответвления вложенной инструкции If...Then.. .Else ни выполнились, после них следует инструкция Loop из строки 29. Ключевое слово Loop сигнализирует о том, что достигнут конец цикла, и выполняется переход в его начало, на строку 11.
Снова проверяется выражение определителя цикла. Если переменная EvenFlag все еще False, то цикл выполняется снова. Если пользователь ввел четное число, большее 10, что привело к выполнению инструкции из строки 18, то EvenFlag будет иметь значение True, цикл выполняться не будет, а произойдет переход на строку 31. Там находится инструкция MsgBox, которая выводит сообщение о том, что цикл завершился.
Циклы с проверкой услпвии после выполпепия
Чтобы условия определителя проверялись после выполнения тела цикла, логическое выражение определителя надо просто поместить в конце инструкций, составляющих тело цикла, а именно после ключевого слова Loop, которое сигнализирует о конце цикла. В следующих разделах описано, как реализовать эту возможность с помощью ключевых слов While и Until.
Создаоие цоклоо с помощью Bo...Loop While
Первой циклической структурой, где условие определителя проверяется после выполнения цикла, является Do.. .Loop While.
Do.. .Loop While имеет такой общий синтаксис.
Do
statements
Loop While condition
где statements представляет одну или большее количество инструкций VBA (или вообще ни одной из них), которые составляют тело цикла. Ключевое слово Loop, находящееся после statements, сигнализирует, что достигнут конец цикла, и также указывает точку, с которой выполняется переход в начало цикла, condition представляет логическое выражение для определителя цикла.
В инструкции Do. ..Loop While выражение condition находится в конце цикла, поэтому условие определителя проверяется после выполнения самого цикла. Поскольку в этом случае используется ключевое слово While, то цикл будет выполняться до тех пор, пока логическое выражение, представленное condition, будет иметь значение True.
При выполнении цикла Do...Loop While вначале выполняются инструкции, представленные statements. Когда достигнуто ключевое слово Loop While, проверяется логическое выражение, представленное condition; если оно имеет значение True, то происходит переход в начало структуры и снова выполняется тело цикла. Когда в конце цикла вновь достигнуто ключевое слово Loop While, проверяется, сохранилось ли у condition значение True. Если да, то цикл выполняется снова; если значение condition изменилось на False, то выполнение продолжается с инструкций, находящихся после строки с ключевым словом Loop.
Обратите внимание, что каким бы ни было значение логического выражения, представленного condition, этот цикл выполнится хотя бы один раз.
В листинге 9.7 показана типичная процедура ввода данных, которая использует циклическую структуру Do...Loop While , чтобы принимать от пользователя какие-
День 9-й. Циклы
303
нибудь данные, пока не будет введено слово exit (выход), которое говорит о том, что ввод завершен.
Листинг 9.7. Типичный цикА Do...Loop While
1:	Sub Getlnput()
2:	'Пример цикла Do...Loop While. Цикл повторяется бесконечно,
3:	'пока пользователь не введет слово "конец"
4:
5:	Const iTitle = "Конец - делу венец"
6:
7:	Dim uStr As String
8:
9:	Do
10:	uStr = InputBox(prompt:="BBeflHTe строку " &
И:	"('конец'	- завершение работы):",
12:	Title:=iTitle, _
13:	Default:=“KOHeu“)
14:	'здесь	вы можете написать	текст для	обработки
15:	'ввода	пользователя.
16:	MsgBox	prompt:="Ba ввели:	" & uStr,	Title:=iTitle
17:	Loop While uStr о "конец"
18:
19:	MsgBox prompt:=”Ввод закончен.",
20:	Title:=iTitle, Buttons:=vbExclamation
21:	End Sub
В процедуре Getlnput имеется неопределенный цикл, выполняющийся до тех пор, пока пользователь не введет слово exit. Процедуру, подобную этой, можно применять, чтобы помочь пользователю вводить данные в один из ваших листов Excel. В процедуре Getlnput используется разновидность цикла, контролируемого событием, и обычно называемая циклом, контролируемым часовым (sentinel controlled). Такое название объясняется тем, что эти циклы ожидают появления определенного значения, которое так и называется — значение часового. В этой процедуре таким значением является строка, содержащая одно слово: exit.
В строке 9 начинается инструкция цикла Do для этой процедуры. Поскольку в строке находится только ключевое слово Do, этим лишь указывается начало тела цикла. В этой строке нет дополнительного ключевого слова (While или Until), как и нет логического выражения определителя цикла, поэтому VBA просто продолжает выполнять инструкции тела цикла, начиная со строки 10.
В строке 10 для получения от пользователя данных применяется функция InputBox. С ее помощью на экран выводится сообщение с предложением ввести строку. Обратите внимание, что в этой подсказке говорится о том, как завершить процесс ввода данных. В качестве строки, вводимой по умолчанию, в функции InputBox задано слово exit.
Комментарии в строках 14 и 15 содержат предложения по дальнейшей обработке введенных данных: их сохранение в ячейке листа или отправка по электронной почте, включение в массив, проверка, являются ли данные числовыми, нечисловыми и так далее и тому подобное. Если хотите это реализовать, то вам остается только вписать сюда соответствующие инструкции... В строке 16 находится инструкция MsgBox, которая просто показывает на экране то, что ввел пользователь.
304
Неделя 2
В строке 17 находится ключевое слово Loop, которое является указателем конца тела цикла. В этой строке также находятся ключевое слово While и логическое выражение условия определителя. При выполнении этой инструкции выражение проверяется. Ввиду того что в этом цикле используется ключевое слово While, то он будет выполняться до тех пор, пока логическое выражение будет истинным, т.е. его значением будет True. Если введенные пользователем данные не являются единственным словом exit, то выражение uStr <> "exit” является истинным и происходит переход в начало цикла, чтобы повторить выполнение его инструкций.
Если пользователь ввел слово exit (выбрав значение по умолчанию, предлагаемое функцией InputBox, или непосредственно набрав само слово), то выражение uStr <> "exit" будет ложным, т.е. его значением будет False. В этом случае цикл завершится и выполнение процедуры продолжится со строки 19.
В строках 19 и 20 находится инструкция MsgBox, которая посылает на экран сообщение о том, что ввод данных завершен. Обратите внимание, что в этой инструкции применяется аргумент Buttons (кнопки), чтобы вывести в сообщении стандартный значок системы Windows в виде восклицательного знака. В строке 21 находится инструкция End Sub, которая завершает процедуру.
Так надо
Проверьте, чтобы в подсказке InputBox было достаточно информации о том, что и как требуется вводить, а также о том, как завершить работу процедуры (или отменить, если есть такая возможность).
Создание циклов с помощью Do...Loop Until
Другой циклической структурой Do, где условие определителя проверяется после выполнения цикла, является Do.. .Loop Until.
Do.. .Loop Until имеет такой общий синтаксис.
Do
statements
Loop Until condition
где statements представляет инструкции VBA, которые составляют тело цикла, а condition — логическое выражение для определителя цикла.
В инструкции Do...Loop Until выражение condition находится в конце цикла, поэтому условие определителя проверяется после выполнения самого цикла. Поскольку в этом случае используется ключевое слово Until, то цикл будет выполняться до тех пор, пока логическое выражение, представленное condition, будет иметь значение False.
При выполнении цикла Do...Loop Until вначале выполняются инструкции, представленные в statements. Когда достигнуто ключевое слово Loop, проверяется логическое выражение, представленное condition; если его значение False, то происходит переход в начало структуры и снова выполняется тело цикла. Когда в конце цикла вновь достигнуто ключевое слово Loop, проверяется, сохранилось ли у condition значение False. Если да, то цикл выполняется снова; если у condition значение изменилось на True, то выполнение продолжается с инструкций, находящихся после строки с ключевым словом Loop.
Обратите внимание, что каким бы ни было значение логического выражения, представленного condition, этот цикл выполнится хотя бы один раз.
В листинге 9.8 показана другая версия процедуры ввода данных из листинга 9.7. На этот раз процедура Getlnput использует циклическую структуру Do...Loop Until, чтобы
День 9-й. Циклы
305
принимать от пользователя какие-нибудь данные, пока не будет отменено окна ввода или не будет введена пустая строка. Кроме того что используется другая конструкция цикла, процедура в листинге 9.8 более сложная, чем ее версия из листинга 9.7. На этот раз пользователь должен подтвердить, что он действительно заканчивает ввод данных.
Лнстннв В.а. Бодее сложная версия Getlnput, где применяется Do...Loop until
1: Sub Getlnput()
2: 'Пример цикла Do...Loop Until . Цикл повторяется бесконечно,
3s 'пока пользователь не введет пустую строку
4:
5:	Const iTitle	= "Ввод данных"
6:
7:	Dim	uStr As String
8:	Dim	Check As	Integer
9:
10:	Do
11:	uStr = InputBox(prompt:="BBeflHTe строку " &
12:	"(Cancel - конец работы):",
13:	Title:=iTitle)
14:	If uStr = "" Then 'подтверждение окончания
15:	Check = MsgBox(prompt:="Закончить ввод данных?",
16:	Title:=iTitle,
17:	Buttons:=vbYesNo + vbQuestion + _
18:	vbDefaultButton2)
19:	If Check = vbNo Then uStr = "x"
20:	Else
21:	'здесь	вы можете написать	текст для	обработки
22:	'ввода	пользователя.
23:	MsgBox	prompt:="Bn ввели:	" & uStr,	Title:=iTitle
24:	End If
25:	Loop Until uStr = ""
26:
27:	MsgBox prompt:="ввод данных завершен.11,
28:	Title:=iTitle, Buttons:=vbExclamation
29:	End Sub
В строке 10 начинается инструкция цикла Do для этой процедуры. Поскольку в строке находится только ключевое слово Do, то этим лишь указывается начало тела цикла. В этой строке нет дополнительного ключевого слова (While или Until), как и нет логического выражения определителя цикла, поэтому VBA просто продолжает выполнять инструкции тела цикла, начиная со строки 11, где для получения от пользователя данных применяется функция InputBox.
В строке 14 начинается инструкция If...Then.. .Else, которая проверяет введенные пользователем данные. Если введенная пользователем строка (хранящаяся в uStr) является пустой (""), то либо отображение окна ввода было отменено, либо была введена пустая строка. Эта инструкция проверяет, действительно ли пользователь намерен завершить процесс ввода данных.
Если введенная строка пустая, то выполняются инструкции из строк 15-19. В строке 15 начинается вызов функции MsgBox, выводящей сообщение с кнопками Yes/No (Да/Нет), в котором спрашивается, действительно ли надо завершить ввод. Обратите внимание, что эти кнопки задаются в функции таким аргументом, как Buttons
306
Неделя 2
(кнопки), в котором, кроме того, указано, что кнопкой по умолчанию является No (Нет). Еще обратите внимание, что в этом аргументе Buttons есть константа vbQuestion, чтобы в сообщении можно было вывести стандартный значок системы Windows в виде вопросительного знака.
В строке 19 находится инструкция If...Then, которая анализирует выбор, сделанный пользователем после вызова функции MsgBox из строки 15. Если пользователь выбрал в окне сообщения кнопку No, что означает продолжение ввода данных, то в инструкции из строки 19 переменной uStr присваивается единственная буква х. Эта инструкция необходима, чтобы цикл продолжал выполняться, потому что он прекратит работу, если в переменной uStr будет находиться пустая строка; поэтому задача инструкции состоит в том, чтобы в случае продолжения работы переменная uStr не содержала пустой строки.
Если во время сравнения при выполнении инструкции If.. .Then.. .Else, которая начинается в строке 14, переменная uStr не содержала пустой строки, то выполняется компонент Else, находящийся в строках 20-23. Как и в процедуре листинга 9.7, здесь показано только выполнение цикла при вводе пользователем данных, которые в действительности никак не обрабатываются.
В строке 25 находится ключевое слово Loop, которое является указателем конца тела цикла. В этой строке также находятся ключевое слово Until и логическое выражение условия определителя. При выполнении этой инструкции осуществляется проверка выражения. Ввиду того что в этом цикле используется ключевое слово Until, то он будет выполняться до тех пор, пока логическое выражение будет ложным, т.е. его значением будет False. Если введенные пользователем данные не являются пустой строкой, то выражение uStr = является ложным и происходит переход в начало цикла, чтобы повторить выполнение его инструкций.
Если пользователь ввел пустую строку или отменил окно ввода, а также подтвердил окончание ввода данных, то выражение uStr = будет истинным, т.е. его значением будет True. В этом случае цикл завершится и выполнение процедуры продолжится со строки 27. Обратите внимание, что если пользователь не подтвердил завершения ввода данных, то в uStr будет находиться единственный символ х и выполнение цикла будет продолжено, так как uStr не пустая.
Так надо
Применяйте циклические конструкции Do.. .Loop While и Do.. .Loop Until, если требуется, чтобы инструкции в теле цикла выполнялись хотя бы один раз при любых условиях.
В интерактивных циклических процессах пользуйтесь инструкциями, с помощью которых подтверждается отмена (как в листинге 9.8).
Немедленное завершение циклов
Многие причины, по которым требуется немедленно завершить выполнение цикла, аналогичны тем, которые относятся к процедурам и целым программам: пользователь отменил окно ввода, нужное значение не появилось и т.д. Однако есть еще и особые причины немедленно завершить цикл, не считая обнаружения ошибок. В некоторых случаях требуется немедленно завершить цикл, особенно For...Next и For Each.. .Next, потому что задача уже выполнена и дальнейшая работа цикла — простая трата времени.
День 9-й. Циклы
307
В VBA для немедленного завершения работы цикла имеется две инструкции. Какую из них использовать, зависит от типа цикла, к которому их надо применить.
•	Для немедленного завершения цикла For.. .Next или For Each.. .Next используйте инструкцию Exit For. Обычно циклы For немедленно завершают потому, что определенная цель работы цикла достигнута и на него больше не нужно тратить время.
•	Для немедленного завершения цикла Do используйте инструкцию Exit Do. Обычно циклы Do немедленно завершают потому, что возникла определенная ситуация, которая не только делает излишней дальнейшую работу, но и может изменить способ проверки логического выражения определителя цикла.
Если не считать, что Exit For применяется к циклам For, a Exit Do — к циклам Do, то обе инструкции представляют собой, в сущности, одно и то же.
В листинге 9.9 отображена функция SheetExists из листинга 9.4, но с инструкцией Exit For.
Листинг 9.9. Псиользованце Exit For для немвдлвнного завгртвння цикла
1:	Function SheetExists(sName As String) As Boolean
2:	'Возвращает True, если лист с именем sName существует в тек. книге
3:	Dim aSheet As Object
4:
5:	SheetExists = False 'на случай, если листа не окажется
6:
7:	'перебираем все листы, сравнивая имя каждого с
8:	'аргументом sName, сравнение текстовое.
9:	For Each aSheet In ActiveWorkbook.Sheets
10:	If (StrComp(aSheet.Name, sName, 1) = 0) Then
11:	SheetExists = True 'лист найден, возвращаем true
12:	Exit For	'незачем продолжать цикл
13:	End If
14:	Next aSheet
15:	End Function
16:
17:
18:	Sub Test_SheetExists()
19:	'Для проверки функции SheetExists
20:
21:	Dim uStr As String
22:
23:	uStr = InputBox("Какой лист вы хотите найти " &
24:	"в текущей книге:")
25:	If SheetExists(uStr) Then
26:	MsgBox "Лист '" & uStr &
27:	"' найден в текущей книге."
28:	Else
29:	MsgBox "В текущей книге листа '" & uStr &
30:	"' нет."
31:	End If
32:	End Sub
308
Неделя 2
Эта версия SheetExists работает точно так же, как и функция из листинга 9.4, за исключением того, что здесь для немедленного завершения цикла For Each.. .Next используется инструкция Exit For.
В листинге 9.9 обратите внимание на строку 13. Эта строка, где находится инструкция Exit For, является единственным отличием этой версии SheetExists от предыдущей.
В строке 10 начинается цикл For Each...Next, использующий в качестве переменной элемента объектную переменную aSheet. В цикле группой является коллекция ActiveWorkbook.Sheets.
В строке 10 для VBA дается указание выполнять инструкции в теле цикла столько раз, сколько листов в активной книге, т.е. повторять операции по одному разу для каждого объекта коллекции Sheets.
Если строки из sName и свойства Name листа равны друг другу, то выполняется строка 12, где результату выполнения функции присваивается значение True.
Поскольку SheetExists просто предназначена для того, чтобы вернуть True, после того как в активной книге будет найден лист с указанным именем, продолжать цикл For Each...Next не имеет смысла, особенно если учесть, что в Excel каждый лист книги имеет уникальное имя. Как только найдено соответствие между строкой из sName и свойством Name, то продолжение выполнения инструкций цикла является излишней тратой машинного времени. В подобных случаях немедленный выход из цикла ускоряет выполнение программ. Хотя в функции, такой как SheetExists, сэкономленное время составит считанные миллисекунды, в сложной программе общая экономия времени может быть значительной.
Строка 15, в которой находится ключевое слово Next, завершающее цикл For Each.. .Next, выполняется только тогда, когда строка из sName и свойство Name не равны друг другу. После этого происходит переход в строку 10, где находится начало цикла, а затем проверяется, просмотрены ли все элементы группы. Если нет, то объектной переменной aSheet присваивается следующий элемент коллекции Sheets и цикл выполняется снова.
В функции SheetExists из листинга 9.4 цикл For Each...Next всегда выполняется столько раз, сколько листов в активной книге. Если, например, в активной книге имеется 10 листов, то цикл For Each... Next выполняется 10 раз; если имеется четыре листа, то цикл выполняется четыре раза.
Однако в этой версии функции SheetExists количество выполнений цикла For Each.. .Next может быть меньше числа листов в активной книге, хотя и никогда не будет больше. Например, если в активной книге имеется 10 листов, а строка из sName равна свойству Name второго листа, то цикл For Each...Next будет выполнен лишь два раза. Если строка из sName равна свойству Name восьмого листа, то цикл For Each.. .Next будет выполнен восемь раз. Цикл будет выполнен 10 раз, если нужного листа вообще нет в активной книге или он является десятым по счету. Значит, использование для завершения цикла инструкции Exit For сэкономит время, нужное, как минимум, для выполнения цикла 1 раз, а как максимум — 9 раз.
Многие из создаваемых вами циклов выполняются сотни раз, поэтому в стратегическом плане немедленное завершение цикла может значительно сэкономить машинные ресурсы.
В строках с 18 по 29 листинга 9.9 описана процедура Test_SheetExists, предназначенная для тестирования функции SheetExists. Эта процедура идентична той, что используется в листинге 9.4.
Так надо
Если оставшиеся итерации циклов For Each.. .Next и For.. .Next становятся излишними, то для их немедленного завершения используйте инструкцию Exit For.
День 9-й. Циклы	309
Так не надо
Не используйте инструкцию GoTo для выхода из выполняющегося цикла. Это усложнит чтение и понимание исходного текста программы.
Без крайней необходимости не прибегайте к инструкции Exit Do для завершения цикла Do, т.е. применяйте ее только тогда, когда нет других способов выхода из цикла.
Вложенные циклы
Циклы можно размещать внутри других циклов точно так же, как это делается с инструкциями If...Then. Размещенные таким образом циклы обычно называются вложенными. Вложенные циклические структуры могут быть любого типа (смешанные циклы For и Do) и могут располагаться на любом уровне.
Однако при создании вложенных циклов необходимо помнить о следующем.
•	У каждого из таких циклов For... Next должна быть уникальная переменная счетчика.
•	У каждого из таких циклов For Each...Next должна быть уникальная переменная элементов.
•	Если во вложенном цикле используется инструкция Exit For или Exit Do, то завершается только текущий цикл; цикл более высокого уровня будет продолжать выполняться.
Вложенные циклы For
В листинге 9.10 показана простая процедура с использованием вложенных циклов For.. .Next. Процедура FormatHeading предназначена для использования в Excel, где она установит на активном листе в ячейках, одновременно находящихся в первых трех строках и первых шести столбцах, шрифт Arial с полужирным начертанием и размером в 12 пунктов. Внешний цикл считает по строкам, а внутренний — по столбцам.
Листинг 8.18. Вложенные циклы For...Next
1:	Sub FormatHeading()
2:	'Процедура применяет шрифт Arial, полужирный, 12 пунктов
3:	'к первым 3 строкам и первым 6 столбцам активной книги
4:
5:	Dim Rnum As Integer, Cnum As Integer
6:
7:	For Rnum = 1 To 3
8:	For Cnum = 1 To 6
9:	Cells(Rnum, Cnum).Select
10:	With Selection.Font
11:	.Name = "Arial"
12:	.Fontstyle = "Bold"
13:	.Size = 12
14:	.Strikethrough = False
15:	.Superscript = False
16:	.Subscript = False
17:	.OutlineFont = False
3/0
Неделя 2
18:	.Shadow = False
19:	.Underline = xlNone
20:	.Colorindex = xlAutomatic
21:	End With
22:	Next Cnum
23:	Next Rnum
24:	End Sub
Такие процедуры, как FormatHeading, можно использовать в заголовках листа и столбцов для установки в ячейках общего шрифта, его начертания и размера. В этой процедуре для задания шрифта Arial с полужирным начертанием и размером в 12 пунктов используется два цикла For.. .Next, причем один из них вложен в другой.
Внешний цикл For...Next считает от 1 до 3, и его переменная счетчика используется для задания координаты строки. Поэтому этот цикл считает от первой строки листа до третьей.
Внутренний цикл For... Next считает от 1 до 6, и его переменная счетчика используется для задания координаты столбца. Поэтому этот цикл считает от первого столбца листа до шестого.
При работе FormatHeading выполнение начинается с первой строки и с первого столбца. По мере увеличения счетчика внутреннего цикла выполняется форматирование на первой строке во всех столбцах по направлению слева направо. По завершении работы внутреннего цикла в первой строке будет отформатировано шесть столбцов. Затем внешний цикл For...Next увеличивает значение своего счетчика, переходя на следующий ряд, и операция повторяется. Поскольку снова выполняется внутренний цикл For...Next (как часть тела внешнего цикла), форматируется первых шесть столбцов уже во второй строке. То же самое происходит и в третьей строке. Внешний цикл For... Next выполняется только три раза, поэтому после третьего выполнения этого цикла процедура FormatHeading завершается.
Внешний цикл For...Next начинается в строке 7. Переменной цикла для него является Rnum. Этот цикл прекращает выполняться, как только Rnum становится больше 3, т.е. как только цикл выполнится три раза.
Внутренний цикл For...Next начинается в строке 8. Переменной цикла для него является Спит. Этот цикл прекращает работу, как только выполнится шесть раз.
В строке 9 используется метод Cell объекта Application приложения Excel, чтобы вернуть объект Range, указанный с помощью Rnum и Спит в качестве координат строки и столбца. Здесь также используется метод Select объекта Range, чтобы выбрать в активном листе указанную ячейку. Инструкция Cell была скопирована из макроса, при записи которого выполнялись только два действия: выбор ячейки и задание нужного форматирования. Инструкция выбора ячейки была скопирована в эту процедуру, и конкретные значения из этой инструкции были заменены переменными Rnum и Спит.
В строке 10 начинается инструкция with. Selection — это свойство объекта Application, которое возвращает текущую выборку. В данном случае ею является ячейка, выбранная инструкцией, находяшейся в строке 9. В объекте Selection.Font находятся все установки шрифта для текущей выборки. Внутри инструкции with все инструкции, находящиеся в строках 11-20, меняют свойства объекта Font, чтобы в выборке был шрифт Arial с полужирным начертанием и размером в 12 пунктов. Эти инструкции, как и та, что находится в строке 9, были скопированы из макроса.
В строке 22 находится инструкция Next, которая обозначает конец тела внутреннего цикла. Обратите внимание, как с помощью переменной счетчика, стоящей после этой инструкции, и с помощью соответствующего отступа можно отличить друг от
День 9-й. Циклы
311
друга внутренний и внешний циклы. Когда выполняется инструкция, находящаяся в строке 22, возрастает значение счетчика цикла Спит, и выполняется переход в начало внутреннего цикла, в строку 8.
Когда закончится выполнение внутреннего цикла For...Next (строки с 8 по 22), то VBA выполняет строку 23. В ней находится инструкция Next, которая обозначает конец тела внешнего цикла. Когда выполняется инструкция, находящаяся в строке 23, возрастает значение счетчика цикла Rnum и выполняется переход в начало внешнего цикла, в строку 7. Там новое значение счетчика сравнивается с его конечным значением, и, если нужно, повторяется выполнение цикла.
Когда закончится выполнение внешнего цикла For...Next (строки 7-23), VBA выполнит строку 24, завершив этим процедуру.
ПРИМЕЧАНИЕ
Циклы For Each... Next можно делать вложенными точно так же, как и For... Next; используйте те же приемы из листинга 9.10.
Вложенные циклы Во
В листинге 9.11 показана процедура с использованием вложенных циклов Do. Эта процедура Makeinvoices помогает пользователю выполнить ввод данных, необходимых для формирования счета-фактуры. Внешний цикл процедуры получает от пользователя номера счетов-фактур до тех пор, пока все они не будут введены или пока пользователь не отменит отображение окна ввода. Внутренний же цикл получает номера пунктов счета-фактуры до тех пор, пока все они не будут введены или пока пользователь не отменит отображение окна ввода. При изучении этой процедуры помните, что она представляет собой всего лишь заготовку. В действительности требуется сбор намного большего количества данных, чем это делается в процедуре. Она была упрощена насколько это возможно, чтобы было легче понять, как создаются вложенные циклы и как они между собой взаимодействуют.
Лмсшмиг 8.11. Вложенные циклы Do
1:	Sub Makeinvoices()
2:	'Собирает данные для нескольких счетов.
3:	'Внешний цикл собирает данные о конкретном счете,
4:	'внутренний цикл собирает строки для этого счета.
5:
6:	Const miTitle = "Выписываем счет "
7:
8:	Dim	InvcNum As String *	5	'номер счета
9:	Dim	ItemNum As String *	16	'строка с данными
10:	Dim	ItemDone As Boolean	'ввод строк закончен
11:	Dim	InvcDone As Boolean	'создание счетов закончено
12:
13:	InvcDone = False 'инициализация условия цикла
14:	Do Until InvcDone 'цикл формирования счета
15:
16:	'спрашиваем номер счета
17:	InvcNum = InputBox)prompt:="Введите номер счета:",
18:	Title:=miTitle)
19:	If Trim(InvcNum) = "" Then 'номер не введен
312
Неделя 2
20:	MsgBox prompt:="Создание счета прервано.",
21:	Title:=miTitle, Buttons:=vbExclamation
22:	InvcDone	=	True	'счет закончен
23:	ItemDone	=	True	'строки больше	не нужны
24:	Else
25:	ItemDone	=	False	'инициализация	условия цикла
26:	End If
27:
28:
29:	Do Until ItemDone 'в цикле вводим строки
30:	'спрашиваем строку
31:	ItemNum = InputBox(prompt:=,,Введите позицию :",
32:	Title:=miTitle i позиции счета " i InvcNum)
33:	If Trim(ItemNum) = "" Then 'ввод позиции отменен
34:	MsgBox prompt:="Ввод позиций для счета " &
35:	InvcNum & " закончен.",
36:	Title:=miTitle
37:	ItemDone = True 'ввод позиций закончен
38:	Else
39:	'здесь	вы можете написать	фрагмент	для обработки
40:	'ввода	пользователя.
41:	MsgBox	prompt:="Вы ввели	'" &
42:	ItemNum & "' в счет " i InvcNum,
43:	Title:=miTitle & позиции счета " i InvcNum
44:	End If
45:	Loop 'конец цикла ввода позиций
46:	Loop 'конец цикла создания счета
47:
48:	MsgBox рготр!:="Создаиие счета закончено", Title:=miTitle
49:	End Sub
В процедуре Makeinvoices внешний цикл Do используется для получения номера счета-фактуры. Когда этот номер введен, начинает выполняться внутренний цикл Do, который периодически принимает от пользователя номера пунктов. Если таких номеров больше нет, то пользователь вводит пустую строку или отменяет окно ввода. (Чтобы процедура была достаточно простой, в ней нет средств подтверждения ни одной из отмен.)
Когда пользователь укажет, что все номера пунктов введены, внутренний цикл Do прекращает выполнение, а внешний — продолжает. Он повторяет свое выполнение, предлагая ввести номер следующего счета-фактуры (предполагается, что требуется ввести несколько таких счетов).
В строках 8-11 описано несколько переменных. Номер счета фактуры будет храниться в InvcNum, а номер пункта — в ItemNum. Обратите внимание, что это строки фиксированной длины. ItemDone и InvcDone — это переменные типа Boolean, предназначенные для того, чтобы контролировать выполнение внутреннего и внешнего цикла Do соответственно.
В строке 13 переменная InvcDone инициализируется путем присвоения ей значения False. Благодаря этому присвоению цикл действительно начнет выполняться. В строке 14 начинает выполняться внешний цикл Do. Логическим выражением его определителя является единственная переменная InvcDone. В этой инструкции Do используется ключевое слово Until, поэтому условие проверяется перед выполнением тела цикла, и цикл будет продолжать выполняться до тех пор, пока значением InvcDone будет False.
День 9-й. Циклы	313
В строке 17 для получения от пользователя номера счета-фактуры применяется InputBox и введенное значение сохраняется в переменной InvcNum. Затем оно проверяется в инструкции If.. .Then.. .Else, расположенной в строках 19-26.
Сама проверка выполняется в строке 19. Если при этом выяснится, что InvcNum содержит пустую строку, то будут выполнены инструкции, находящиеся в строках листинга 20-23. Это означает, что на экран будет выведено сообщение об окончании ввода счетов-фактур и значением двух контролирующих циклы флагов станет True, что означает окончание ввода одновременно пунктов и счетов-фактур. Если InvcNum не содержит пустую строку, то выполняется инструкция, находящаяся в строке 25. В этом случае значением флага ItemDone станет False, а это означает выполнение ввода пунктов.
Какие бы ответвления инструкции If.. .Then.. .Else (начинающейся в строке 19) ни выполнились, за ними в строке 29 следует внутренний цикл Do. Он во многом аналогичен внешнему циклу Do, только переменная флага у него другая. У внутреннего цикла Do условие определителя проверяется перед выполнением его инструкций, и он будет продолжать выполняться до тех пор, пока значением ItemDone будет False.
Если ввод счетов-фактур был завершен (выполнялись строки с 20 по 23), то значением ItemDone будет True и внутренний цикл выполняться не будет. Вместо этого тело цикла будет пропущено и выполнение начнется со строки 46, где заканчивается структура внешнего цикла. После этого происходит переход в начало внешнего цикла (строка 14) и проверяется условие определителя. Значением ItemDone также будет True, таким образом, завершит выполнение и внешний цикл.
Если в результате выполнения инструкции If.. .Then.. .Else, которая начинается в строке 19, была выполнена инструкция из строки 25, то значением ItemDone является False и будет выполняться внутренний цикл, начинающийся в строке 29.
В строке 31 для получения от пользователя номера пункта применяется InputBox и введенное значение сохраняется в переменной ItemNum. Затем оно проверяется в инструкции If.. .Then.. .Else, расположенной в строках 33-44.
Сама проверка выполняется в строке 33. Если при этом выяснится, что ItemNum содержит пустую строку, то выполняются инструкции, находящиеся в строках листинга с 34 по 37. Это означает, что на экран будет выведено сообщение об окончании ввода пунктов счета-фактуры и значением флага ItemDone станет True, что, в свою очередь, указывает на окончание ввода пунктов. Если ItemNum не содержит пустую строку, то выполняются инструкции, находящиеся в строках 39-43. Они просто выведут на экран сообщение со строкой, полученной от пользователя. В случае, если эта процедура будет действительно использоваться для ввода данных, в этом месте можно поместить инструкции их сохранения или дополнительной проверки.
Какие бы ответвления инструкции If.. .Then.. .Else (начинающейся в строке 33) ни выполнились, после них процедура продолжает выполняться со строки 45, где заканчивается структура внутреннего цикла Do. После этого происходит переход в строку 29, где находится начало этого цикла и вновь проверяется условие его определителя. Если ItemDone сохранило значение False, то выполнение цикла повторяется снова.
Если у ItemDone значение True, то процедура продолжает выполняться со строки 46, где заканчивается структура внешнего цикла Do. После этого происходит переход в строку 14, где находится начало этого цикла, и снова проверяется условие его определителя. Если InvcDone по-прежнему имеет значение False, то выполнение внешнего цикла повторяется снова.
Если у InvcDone значение True, то процедура продолжает выполняться со строки 48, где выводится сообщение об окончании процесса ввода. В строке 49 процедура Makeinvoices завершается.
3/4
Неделя 2
Резюме
На этом уроке вы познакомились с терминами, которые описывают разные виды циклов и их составных частей. Вы узнали, что такое определитель цикла и в чем разница между фиксированным и неопределенным циклом; чем отличаются циклы, в которых проверка определителя проводится перед выполнением тела цикла, от тех, в которых определитель проверяется уже после выполнения.
Прежде всего, вы научились создавать фиксированные циклы с помощью инструкций For...Next и For Each...Next. Вы познакомились также с разновидностями цикла For...Next, а также с тем, как его создавать с возрастающим или убывающим счетчиком. Теперь вам известно, как использовать цикл For Each...Next для выполнения операций над всеми элементами определенной группы.
Вы получили представление о том, как создавать неопределенные циклы с помощью инструкции Do. В этой главе было показано, как в цикле Do проверяется условие определителя, а также как используется инструкция Do для создания циклов, где условие проверяется перед выполнением цикла, и циклов, где условие проверяется после выполнения. Теперь вам знакомы четыре разных способа создания циклов Do: Do While, Do Until, Do...Loop While и Do...Loop Until.
В этой главе также описаны некоторые причины, почему иногда требуется немедленное завершение цикла, и показано использование для этого в циклах For и Do инструкций Exit For и Exit Do. Наконец, в этой главе было показано, как создавать вложенные циклические структуры, и были даны примеры использования вложенных циклов For.. .Next и Do.
Вопросы п отпеты
Я не знаю, что мне использовать: цикл For или Do
Это очень легко решить. Если цикл требуется выполнить определенное число раз и можно легко использовать константы и математические выражения для указания верхней и нижней границы счетчика цикла, используйте For. А если вы не знаете, сколько раз будет выполняться цикл и трудно использовать для задания границ счетчика константы и математические выражения, лучше использовать цикл Do
Думаю, мне понятно, как работает фиксированный цикл, но я не знаю, что мне использовать: цикл For...Next или For Each...Next
Первый цикл используйте, когда его можно контролировать, считая число повторений цикла. Второй цикл используйте для работы со всеми элементами в коллекции, такими как листы в книге Excel
Мне ясно, что д ля некоторой задачи надо использовать цикл Do, ио где гарантия, что я выбрал нужный вариант из четырех?
Выбор “нужной” циклической структуры — это в основном выбор того цикла, который вам легче всего создать и у которого самое простое логическое выражение для условий определителя. Напишите нужные условия на русском языке, и это вам подскажет, какой вариант цикла Do следует использовать.
Попробуйте описать свои требования к работе цикла аналогично одному из приведенных ниже предложений, и цикл будет практически готов.
•	Начиная с первого (второго или третьего и т.д.) до л-го предмета, выполняйте...
(цикл For.. .Next)
День 9-й. Циклы
315
•	Начиная с я-го предмета до первого (второго или третьего и т.д.), выполняйте... (цикл For... Next с отрицательным шагом)
•	Выполняйте столько раз, сколько определено... (другой цикл For.. .Next)
•	Выполняйте для каждого предмета в... (цикл For Each.. .Next)
•	Делайте, пока выполняется некоторое условие... (цикл Do While)
•	Выполните это задание хотя бы один раз и затем снова выполняйте его, пока соблюдаются некоторые условия... (цикл Do.. .Loop While)
•	Выполняйте, пока не соблюдаются эти условия... (цикл Do Until)
•	Выполните это задание хотя бы один раз и затем снова выполняйте его, пока не соблюдаются эти условия... (цикл Do.. .Loop Until)
При написании аналогичных предложений избегайте таких фраз, как «В то время как еще действует режим ввода, предложите пользователю ввести еще что-нибудь». Они могут только вас запутать. В этом примере задачу требовалось сформулировать так: «Пока действует режим ввода, предложите пользователю ввести еще что-нибудь». В этом случае вы быстро поймете, что для реализации этой идеи надо лишь написать инструкцию Do While.
Ту же мысль можно выразить и таким способом: «Пока не отключен режим ввода, предложите пользователю ввести еще что-нибудь». В этом случае необходима инструкция Do...Loop Until. Оба подхода одинаково хороши, и оба выполняют одну и ту же задачу. Единственное различие между ними — это какой из них вам легче выразить и понять.
Коллоквиум
Ответы в приложении.
Тест
1.	Какая разница между фиксированными и неопределенными циклами?
2.	Можно ли изменить количество итераций цикла For.. .Next?
3.	Как в цикле Do задать проверку условия определителя до выполнения тела цикла, а как — после выполнения?
4.	Как в цикле Do задать продолжение выполнения, пока условие определителя истинно, а как — пока условие определителя не стало истинным?
5.	Что такое цикл Do, контролируемый счетчиком? Когда его использовать?
6.	Что такое цикл Do, контролируемый событием?
7.	Что такое цикл Do, контролируемый флагом?
Упражнения
1.	ОТЛАДКА. В процедуре предполагается принимать от пользователя числа, пока он не введет слово exit (выход) Для приема данных служит функция приложения Excel Application.InputBox. Она применяется вместо функции VBA InputBox, потому что ее необязательный аргумент Туре дает возможность указывать — вводимые пользователем значения должны быть числовыми.
316
Неделя 2
После того как цикл этой процедуры был создан, выяснилось, что у него имеется серьезный недостаток: он никогда не остановится. Найдите ошибку и исправьте. (Подсказка. Ошибка в способе определения условия цикла.)
ПРИМЕЧАНИЕ	Если вы набрали процедуру и запустили ее до исправления ошибки, то будьте готовы использовать клавишу Esc, чтобы прервать работу процедуры (как это сделать - описано во врезке к этой главе).
Sub GetNumber_B()
'В бесконечном цикле просит пользователя ввести число
Dim iNum
Do
iNum = Application.InputBox(prompt:="Введите число:", Type:=l)
Loop Until iNum = "конец"
MsgBox "Ввод чисел закончен."
End Sub
2.	Напишите процедуру, которая принимает от пользователя три слова, складывает их в одну строку и выводит ее на экран. В процедуре должно быть следующее.
•	Для повторения запроса на ввод слова используется цикл For.. .Next.
•	Инструкции для показа на экране того, что ввел пользователь, и для остановки цикла, если пользователь ввел пустую строку или отменил отображение окна ввода.
•	Для получения и сложения трех слов используется не более двух строковых переменных.
3.	Измените процедуру из предыдущего упражнения, чтобы в ней был неопределенный цикл, который принимает слова от пользователя до тех пор, пока тот не введет точку (.). (Подсказка. Замените цикл For циклом Do.)
4.	Напишите функцию с именем IsBookOpen, которая возвращает True, если указанная книга открыта, и False, если нет. Пусть заготовкой будет известная вам функция SheetExists. В своей функции используйте коллекцию Workbooks — в ней находятся все открытые книги Excel. Имя книги хранится в ее свойстве Name, как и имя листа. Однако в отличие от листов, строка, хранящаяся в этом свойстве, является именем файла данной книги. Например, в свойстве Name персональной книги макросов находится строка PERSONAL.XLS.
Как часть упражнения, напишите процедуру тестирования функции IsBookOpen.
5.	Напишите функцию с именем SVal, которая принимает аргумент типа String и возвращает результат типа Double, который содержит числовой эквивалент значения аргумента. Ваша функция должна будет удалять из аргумента все нечисловые символы (за исключением десятичной точки) и затем использовать для преобразования строки в число функцию VBA Vai.
Например, если в аргументе находится строка 1000 000 долларов, то функция сначала должна преобразовать строку к виду 1000 000, чтобы из нее можно было получить число, а затем для преобразования в число передать новую строку функции Vai.
Как часть упражнения, напишите процедуру тестирования функции SVal.
День 9-й. Циклы
317

ДоньЩй
Типы данных и переменных
В этой главе более подробно рассказывается о видимости и сохранении в памяти переменных, о том, как определить тип хранящихся в них данных с помощью функций VBA. В ходе этого урока мы рассмотрим следующие вопросы.
•	Как получить информацию о типе данных, хранящихся в переменных Variant или переменных другого типа, а также о типе данных результата выражения.
•	Как определить, присвоено ли какое-либо значение переменной типа Variant или Object.
•	Как использовать функции получения типа данных вместе с циклическими структурами и инструкциями If, чтобы проверять данные, полученные от пользователя или из других источников.
•	Как менять сохранение переменных, чтобы их значения не терялись в промежутках между вызовами процедур или функций.
Получение информации о переменных и выражениях
Есть много случаев, когда необходимо получить больше информации о переменной или выражении в программе на VBA. В начале этого раздела рассказывается о некоторых из этих случаев, а затем речь пойдет о функциях, помогающих в таких ситуациях.
Одна из ситуаций, когда необходимо получить о переменной больше информации, — когда у процедур или функций имеются аргументы (обычно необязательные) с типом данных Variant. Как известно, Variant может содержать любой тип данных. Таким образом, функции требуется числовое значение некоторого аргумента, а поскольку этот аргумент имеет тип данных Variant, при любом вызове этой функции ей можно передавать в качестве значений такого аргумента данные типа string, Date и т.д. Чтобы в таком случае избежать ошибок выполнения, можно использовать функции определения типа данных VBA, с помощью которых перед выполнением нашей функции значение аргумента можно было преобразовать в нужный тип или вывести соответствующее сообщение.
(wme
Одной из причин использования в функции или процедуре необязательного аргумента типа Variant является применение функции IsMissing: эта функция работает только с аргументами данного типа. Если необязательный аргумент имеет тип Variant, то необходимо проверять, чтобы у его значений был правильный тип данных.
318
Неделя 2
Информационные функции VBA можно использовать для получения более подробной информации о типе данных переменной, чтобы точно знать, на какую объектную переменную или объектное выражение она ссылается. Например, если требуется проверить, ссылается ли в Excel данная объектная переменная на объект Workbook или Worksheet, для этого можно использовать функцию TypeName. Эта функция вернет строку, в которой содержится определенное имя объекта: Workbook (книга) для объекта Workbook, Range (диапазон) для объекта Range и т.д.
Другим случаем, когда будут полезны информационные функции, является их использование в процедуре окна InputBox для получения данных от пользователя. Эти функции позволяют проверить, те ли данные введены, что требуются. Более подробный рассказ о проверке введенных данных содержится в одном из последующих разделов этой главы, “Проверка данных пользователя”.
В табл. 10.1 перечислены функции VBA получения информации о типе данных. Некоторые из них о типе данных выражения дают более общую, а другие — более детальную информацию. Более подробные сведения об этих функциях приведены в последующих разделах этой главы.
В табл. 10.1 V представляет единичное имя переменной, а Е — любое выражение, допустимое в VBA. Все аргументы функций из этой таблицы являются обязательными, если не указано обратное.
Таблица 10.1. Функции получения информации о типе данных
Функция	Применение
IsArray(V)	Возвращает True, если V является переменной массива; False — в противном случае. (В уроке 14-го дня рассказывается о переменных типа Array (массив))
IsDate(E)	Возвращает True, если Е является допустимым выражением типа Date (дата); False — в противном случае. Е может быть значением типа Date или строковым выражением, представляющим дату
IsEmpty(E)	Возвращает True, если выражение Е пустое; False — в противном случае. Е может быть числовым или строковым выражением, но обычно это переменная типа Variant (без значения). Всегда возвращает False, если в Е содержится более одной переменной
IsError(E)	Возвращает True, если Е является Variant-переменной, в которой находится числовое выражение, представляющее одно из значений ошибок VBA (или определенных пользователем); в противном случае возвращает False
IsMissing(V)	Возвращает True, если необязательный аргумент Е является Variant-переменной и не был включен в список аргументов функции или процедуры; в противном случае возвращает False. (В уроке 6-го дня рассказывается, как использовать эту функцию)
IsNull(E)	Возвращает True, если в выражении Е содержится специальное значение Null (что означает отсутствие в переменной допустимых данных); False — в противном случае. Е может быть любым числовым или строковым выражением; если в выражении какая-либо переменная является Null, то значение всего выражения тоже Null
День 10-й. Типы данных и переменных
319
Окончание табл. 10.1
Функция	Применение
IsNumeric(E)	E может быть любым числовым или строковым выражением. Возвращает True, если Е имеет числовой тип данных (Byte, Integer, Long, Single, Double или Currency) или строку, которую в VBA можно преобразовать в число; False — в противном случае. IsNumeric всегда возвращает False, если Е является выражением типа Date. Если Е имеет значение Empty, то IsNumeric возвращает True
IsObject(E)	Возвращает True, если выражение Е является ссылкой на допустимый Automation-объект; False — в противном случае. Некоторые, но не все такие объекты являются объектами VBA или главного приложения; в уроке 20-го дня рассказывается об объектах Automation и OLE
TypeName(E)	Возвращает строку, содержащую имя типа данных выражения Е. Е может быть выражением или переменной любого типа, кроме определенного пользователем
VarType(V)	Возвращает число, указывающее тип данных переменной V. V может быть любой переменной любого типа, кроме определенного пользователем
Применение информационных функции Visual Basic for Applications
В VBA имеется несколько функций, предоставляющих возможность проверить, имеет ли переменная или выражение определенный тип данных, и дающих общее представление о статусе переменной. Каждая из этих функций имеет единственный аргумент и возвращает значение типа Boolean (True или False).
Эти функции можно использовать для получения информации о переменных или выражениях, включая переменные аргументов. Такими функциями получения общей информации являются IsArray, IsDate, IsError, IsMissing, IsNumeric, IsObject, IsEmpty и IsNull. Обратите внимание, что имя каждой из этих функций начинается со слова 1s (Является ли...), а это означает, что проверяется тип и статус переменной или выражения. Краткие сведения о назначении и аргументах каждой функции приведены в табл. 10.1.
Использовать эти функции очень просто. Поскольку они возвращают значение типа Boolean, они будут часто применяться в инструкциях If...Then, а также в логических выражениях условий циклов Do.
Эти функции имеют такой общий синтаксис.
IsArray(varname)
IsDate(expression)
IsError(expression)
IsMissing(argname)
IsNumeric(expression)
IsObject(expression)
varname представляет любое имя переменной, expression — любое числовое (типа даты) или строковое выражение VBA, a argname — любое имя аргумента ваших функциональных процедур.
320
Неделя 2
Вы уже знаете, как использовать функцию IsMissing (урок 6-го дня), поэтому о ней в этой главе не рассказывается. Не найдете вы здесь и подробностей о функции IsArray, хотя бы потому, что вы еще не знакомы с типом данных Array. Эта функция используется для того, чтобы определить, не является ли указанная переменная массивом. (Массив похож на коллекцию объектов; в уроке 14-го дня, “Массивы”, будет рассказано более подробно о том, что такое тип данных Array и как его использовать.)
Поскольку функции IsEmpty и IsNull тесно связаны со специальными значениями VBA Empty и Null, эти функции будут описаны ниже в этой главе, в том разделе с описанием применения указанных значений.
Оставшиеся три функции — IsDate, IsNumeric, IsObject — используются во многом одинаково. В листинге 10.1 показана функция FlipCase из урока 6-го дня обучения. В нынешнем ее варианте используется не только функция IsMissing, чтобы определить, имеется ли необязательный аргумент, но и функция IsNumeric, чтобы узнать, содержатся ли в аргументе данные допустимого типа.
Листинг 10.1. Проверка типа даииых иЕоРязатвльиого аргумента
1:	Function FlipCase(ByVai tstr As String,
2:	Optional ByVai nChar) As String
3:	'Меняет регистр первых nChar символов в строке tstr.
4:	'Если nChar ие задано, меняет регистр всех символов
5:	Dim k As Long	'счетчик цикла
6:	Dim TestC As String * 1
7:
8:	'если nChar ие задано или задано неправильно
9:	If IsMissingfnChar) Or (Not IsNumeric(nChar)) Then
10:	nChar = Len(tStr) 'записываем в nChar длину всей строки
11:	End If
12:
13:	For k = 1 To nChar
14:	TestC = MidftStr, k, 1)
15:	If (StrCompfTestC, "A", 0) >= 0) And _
16:	(StrCompfTestC,	"Z",	0)	<=	0)	Xor
17:	(StrCompfTestC,	"A",	0)	>=	0)	And _
18:	(StrComp(TestC,	"Я",	0)	<=	0)	Then
19:	Mid(tStr, k, 1) = LCase(TestC)
20:	Elself (StrCompfTestC, "a”, 0) >= 0) And
21:	(StrCompfTestC,	"z",	0)	<=	0)	Xor
22:	(StrCompfTestC,	"a",	0)	>=	0)	And _
23:	(StrCompfTestC,	"я",	0)	<=	0)	Then
24:	MidftStr, k, 1) = UCase(TestC)
25:	End If
26:	Next k
27:	FlipCase = tstr
28:	End Function
Функция FlipCase из этого листинга в основном аналогична варианту из урока 6-го дня. Основным отличием является наличие в этом листинге функции IsNumeric, добавленной в инструкцию If...Then (строка 9). Эта инструкция проверяет не только наличие при вызове функции аргумента nChar, но и является ли его значение числовым. В последующих абзацах дается построчное объяснение функции FlipCase. Неко-
День 10-й. Типы данных и переменных
321
торые из подробностей работы этой функции, о которых рассказывалось в уроке 6-го дня, здесь повторяться не будут.
Как и раньше, у FlipCase два аргумента. Первый, tStr, является обязательным и передается по значению. Второй, nChar, необязательный, имеет тип данных Variant (заданный по умолчанию) и тоже передается по значению. В этом варианте FlipCase к аргументу nChar было добавлено ключевое слово ByVai, так как здесь возможна модификация nChar. Передача по значению защитит источник данных аргумента от изменения, когда значение аргумента будет модифицировано внутри функции.
В строках 9-11 находится инструкция If.. .Then, в которой, если нужно, меняется значение nChar. У первой части логического выражения этой инструкции, IsMissing(nChar), будет значение True, если пропущен аргумент nChar. Вторая часть логического выражения, (Not IsNumeric(nChar)), будет иметь значение True, если значение nChar нечисловое или не может быть преобразовано в число. (Помните, что логический оператор Not заменяет на противоположное значение логического выражения; если IsNumeric возвращает False, что означает нечисловое значение переменной nChar, то выражение Not IsNumeric(nChar) имеет значение True.)
В связи с тем что логические выражения в строке 9 связаны с помощью логического оператора Or, все выражение имеет значение True, так как у каждого из подвыражений имеется значение True. В результате, если nChar пропущен или содержит нечисловые данные, то выполняется инструкция из строки 10. В ней переменной nChar просто присваивается значение длины строки, содержащейся в tStr.
Этот прием присвоения nChar длины строки из tStr позволяет избежать сообщения об ошибке, когда выполняется инструкция цикла For...Next из строки 13, где nChar задает конечное значение счетчика. Например, если инструкция, которая вызывает FlipCase, передает без дополнительного тестирования в строке 9 в качестве значения nChar строку five (пять) (которую VBA не может преобразовать в число), то в строке 13 возникнет ошибка несоответствия данных. Это случится в тот момент, когда VBA попытается преобразовать строку five в число, используемое в качестве конечного значения счетчика цикла. Во избежание этого и применяется проверка с помощью IsNumeric. а также повторное присвоение числового значения переменной nChar. Проверка на наличие пропущенных или нечисловых данных (выполняется в строках с 9 по 11) позволяет сделать так, чтобы у nChar всегда было числовое значение к моменту ее использования в строке 13.
Цикл в строках 13-22 работает, как было рассказано в уроке 6-го дня: каждый символ строки, хранящейся в tStr, будет отдельно проверяться и по мере необходимости преобразовываться в верхний или нижний регистр. Наконец, в строке 23 присваивается результат функции.
Так надо
Чтобы необязательные аргументы типа Variant содержали данные нужных типов, используйте информационные функции, а затем соответствующим образом преобразуйте или измените данные аргумента, если не хотите ошибок выполнения при работе ваших процедур или функций.
Так не надо
Не надейтесь, что инструкция, вызывающая вашу функцию, будет для ее аргументов типа Variant всегда передавать данные нужного типа, особенно если вы написали функцию, которую будут применять другие пользователи.
322
Неделя 2
Краткая процедура в листинге 10.2 показывает другой типичный пример использования информационных функций VBA. В этом листинге процедура InvoiceDate использует функцию IsDate для контроля структуры Do...Loop Until, которая повторяет свое выполнение до тех пор, пока пользователь не введет в окне ввода выражение, допустимое для даты.
Листинг 10.2. Проверка шипа даииых, виодвниых пользоватилем
1:	Sub InvoiceDate))
2:	'повторяется, пока пользователь не введет правильную дату.
3:
4:	Dim uDate As Variant
5:
6:	Do
7:	uDate = InputBox(prompt:="Введите дату счета: ",
8:	ТШе:="Ввод даты")
9:	Loop Until IsDate(uDate)
10:	MsgBox "Вы ввели дату: " &
11:	Format(uDate, "long date")
12:	End Sub
В практических применениях процедура InvoiceDate может быть частью большей процедуры или ее можно написать в виде функции, получающей от пользователя дату и возвращающей ее в виде результата.
В строке 4 описана переменная uDate, в которой будут храниться введенные пользователем данные. Прежде всего обратите внимание на то, что тип этой переменной Variant, чтобы облегчить последующие преобразования данных.
В строке 6 начинается инструкция Do...Loop Until. Поскольку условие определителя находится в конце этой структуры, то оно проверяется после выполнения тела цикла. После прочтения инструкции Do, с которой начинается цикл, VBA немедленно приступает к выполнению в строке 7 инструкции, которая является первой (и единственной) инструкцией тела этого цикла.
В строках 7 и 8 инструкция InputBox предлагает пользователю ввести дату (дату гипотетического счета-фактуры) и присваивает полученную строку переменной uDate. Для простоты в этой процедуре нет никаких инструкций, нужных в ситуации, когда пользователь отменит окно ввода.
В строке 9 находится конец цикла вместе с условием определителя. В этой строке после выполнения тела цикла проверяется логическое выражение. Ввиду того что в цикле используется ключевое слово Until, цикл будет выполняться до тех пор, пока значением логического выражения не станет True. В этом выражении находится единственный вызов функции IsDate. Если введенная пользователем строка является допустимой формой даты, т.е. эту строку можно преобразовать в значение типа Date, то IsDate возвращает True и выполнение цикла прекращается. В противном случае IsDate возвращает False и цикл продолжает выполняться.
Например, если введена строка 12 декабря 1939, то функция IsDate возвращает True, так как VBA может преобразовать эту строку в значение типа Date. Аналогично, если будут введены строки, такие как 4/1/95 или 5-12-98, то эта функция также вернет True, завершая этим цикл. Однако если вы введете строку, подобную строке Двенадцатое декабря 1939 года, то IsDate вернет False и цикл продолжит выполняться. False будет и в случае таких недопустимых дат, как 30 февраля 1998 или 0 декабря 1999.
День 10-й. Типы данных и переменных
323
Наконец в строке 10 для вывода на экран даты пользователя используется инструкция MsgBox.
Так надо
; Чтобы проверить, ввел ли пользователь данные нужного типа, обратитесь к информационным функциям VBA. Информационные функции используйте аналогично тому, как это было сделано в листингах 10.1 и 10.2 для IsNumericиIsDate.
Так не надо
Если нужна определенная информация о типе данных переменной, то не используйте функции, начинающиеся словом Is, а вместо них применяйте TypeName и VarType (о них речь пойдет в следующем разделе).
Получение информации и тине данных о неременнон нлн о выражения
Для определения того, какой именно тип данных у выражения или у содержимого переменной, в VBA используются две функции: TypeName и VarType. В отличие от других информационных функций они дают более подробные сведения.
В общем функции TypeName и VarType используются для того же, что и более общие информационные функции (IsNumeric, IsDate и им подобные), о которых говорилось в предыдущем разделе. Каждую из этих двух функций можно использовать, если требуется точно узнать, какой у выражения или переменной тип данных, Integer или Double, а не только выяснить, числовая это переменная или нет. Эти функции также пригодятся, если требуется узнать определенный тип объекта, на который ссылается объектная переменная, например Range, Workbook, Worksheet и т.д.
Функцию TypeName можно использовать и в случае, если неизвестно, какой будет результат некоторого выражения. Например, можно задать выражение, которое требуется использовать в процедуре, и применить инструкцию MsgBox и функцию TypeName, чтобы вывести на экран имя типа данных его результата. Другими словами, если вы не знаете, будет ли тип данных результата tNum + tstr числовым или строковым, когда в одной переменной содержится строка, а в другой — число, то для вывода на экран типа данных результата можно использовать тестовую процедуру, аналогичную той, что имеется в листинге 10.3.
Листинг 10.3. Использоваиив тестовой ироцвддры для вывода типа даиных результата выражения
1:	Sub Find_Type()
2:	Dim tNum As	Variant
3:	Dim tstr As	Variant
4:
5:	tNum = 5
6:	tStr = "2“
7:	MsgBox TypeName(tNum + tstr)
8:	End Sub
324
Неделя 2
Листинг 10.3 очень прост. В строках 2 и 3 описаны две переменные, а в строках 5 и 6 присваивается числовое значение переменной tNum и строковое значение, представляющее число, — переменной tStr. (Если вы попробуете непосредственно сложить числовую константу и строковую, то VBA выведет сообщение о несовместимости типов.) В строке 7 находится инструкция MsgBox, которая выводит на экран результат функции TypeName. Здесь этот результат является строкой, содержащей имя типа данных результата выражения tNum + tStr.
Если вы введете и запустите эту процедуру, то на экране появится сообщение со словом Double, а это означает, что у результата выражения тип данных Double. Это более полезная информация, чем та, которую можно получить, используя функцию IsNumeric: с помощью TypeName можно узнать не только то, что значение числовое, но также — какой именно у него числовой тип.
В следующих двух разделах подробно описаны как функции TypeName и VarType, так и возвращаемые ими значения.
Результаты функции TypeName
Функция TypeName (имя типа), как подсказывает само ее имя, возвращает строку, содержащую имя типа данных переменной или выражения, передаваемого функции в качестве ее аргумента.
У функции TypeName такой общий синтаксис.
TypeName(varname)
varname представляет любое имя переменной или выражение, соответствующие правилам VBA. В табл. 10.2 показаны строки, которые возвращает TypeName в зависимости от типа данных varname.
Таблица 10.2. Значения, возвращаемые функцией TypeName
Возвращаемая Назначение строка
Objecttype Automaton-объект, типом которого является objecttype. Например, TypeName для объекта Workbook возвращает Workbook, а для Worksheet — Worksheet. (Некоторые, но не все объекты VBA и основных приложений, такие как Worksheet, Workbook и Range, являются Automaton-объектами)
Boolean	Тип Boolean
Byte	Тип Byte
Currency	Тип Currency
Date	Тип Date
Double	Тип с плавающей точкой Double
Empty	Неинициализированный тип Variant
Error	Variant, где находится числовое выражение,	представляющее одно
из значений ошибок VBA (или определенных пользователем)
Integer	Тип Integer
Long	Целый тип Long
День 10-Й. Типы данных и переменных
325
Окончание табл. 10.2
Возвращаемая строка	Назначение
Nothing	Неинициализированная переменная типа Object, т.е. переменная типа Object, которая не ссылается на объект
Null	Variant, где находится специальное значение Null, указывающее, что переменная или выражение не содержит допустимого значения
Object Single String Unknown	Объект, не поддерживающий Automation Тип с плавающей точкой Single String Automation-объект неизвестного типа, обычно тот, который принадлежит приложению, не являющемуся главным для VBA. (В уроке 20-го дня рассказывается, как в VBA использовать Automation и OLE для работы с объектами, принадлежащими другим приложениям)
При использовании функции TypeName возвращается одна из строк, указанных в табл. 10.2. Некоторые из них требуют дополнительных пояснений.
Во-первых, когда при использовании функции TypeName возвращается тип объектной переменной или объектного выражения, то если это Automation-объект, выводится строка с определенным именем объекта. (Automation был ранее известен, как OLE Automation-, с помощью программ VBA можно также управлять многими объектами OLE.) Если же объектная переменная или выражение ссылается на объект, который не поддерживает Automation, то выводится строка с общим названием Object (объект). Иногда функция TypeName не может точно определить тип Automation-объекта; в этом случае выводится строка Unknown (неизвестный). Этот последний случай касается объектов, не являющихся частью VBA, а созданных с помощью приемов, которые описаны в уроке 20-го дня.
Во-вторых, если тестируемая с помощью TypeName переменная является массивом, то в конце возвращаемой строки будут находиться две пустые круглые скобки. Например, в случае массива целых чисел типа Long будет получена строка Long(). Скобки () в конце имени типа данных и являются признаком того, что тестируемая переменная является массивом.
Обычно, как и в процедуре листинга 10.4, в качестве аргумента TypeName используется отдельная переменная, а не сложное выражение.
Листииг 10.4. Демонстрация резудьтатог, возвращаемых и разных случаях фднхцивО TypeName
1:	Sub Demo_TypeName()
2:	Dim	aVariant As Variant
3:	Dim	anObject As Object
4:	Dim	anArrayf) As Integer
5:
6:	MsgBox TypeName(aVariant) 'переменная типа variant
7:	MsgBox TypeName(anObject)	'объектная переменная
8:	MsgBox TypeName(anArray)	'массив целых
9:
326
Неделя 2
10:	Set anObject = ActiveWorkbook
11:	MsgBox	TypeName(anObject)	'объект: Workbook
12:	Set anObject = ActiveSheet
13:	MsgBox	TypeName(anObject)	'объект: зависит от
14:	'активного листа: "Chart",
15:	'"Worksheet", и т.п.
16:	End Sub
В процедуре Demo_TypeName несколько раз вызывается функция TypeName. В строке 1 находится описание процедуры, а в строках 2-4 — описание нескольких переменных. Обратите внимание, что переменная aVariant имеет тип Variant, a anObject является переменной общего типа Object. Переменная апАггау(), описанная в строке 4, имеет тип Array. Скобки после имени переменной говорят о том, что это массив. Элементы этого массива имеют тип Integer. (Массивы содержат несколько элементов, наподобие коллекции объектов; хотя до урока 14-го дня вам мало что будет известно о массивах, тем не менее полезно познакомиться с тем, как они обрабатываются функцией TypeName.)
В строке 6 для вывода на экран результата функции TypeName используется MsgBox. Аргументом этой функции является переменная типа Variant, которая называется aVariant. Этой переменной не было присвоено ни одного значения, поэтому TypeName сообщит, что переменная aVariant пуста и на экране отобразится слово Empty (пустой).
В строке 7 для вывода на экран результата функции TypeName также используется MsgBox. На этот раз аргументом является переменная типа Object с именем anObject. Этой переменной с помощью инструкции Set еще не была присвоена ссылка на определенный объект, поэтому anObject ни на какой объект не ссылается. Этот факт и отображается функцией TypeName, результатом которой для этой переменной является строка «Nothing» (ничего). Таким образом, в строке 7 на экран выводится слово Nothing.
В строке 8, как и в предыдущих двух, выводится результат функции TypeName. В этом случае аргументом является переменная массива апАггау. На экране же с помощью MsgBox будет выведена строка Integer(). Функция TypeName сообщает, что основным типом данных этой переменной является Integer, а также, что апАггау является массивом целых элементов. Скобки в конце имени типа данных говорят о том, что переменная является массивом.
В строке 10 находится инструкция Set, которая присваивает переменной anObject ссылку на текущую активную книгу. В строке 11 находится другая инструкция, которая с помощью MsgBox выводит на экран результат функции TypeName. В этом случае аргументом этой функции является переменная типа Object, которой присвоена ссылка на определенный объект. В результате TypeName возвращает строку с конкретным именем типа объекта, на который ссылается переменная. В данном случае переменной anObject присвоена ссылка на объект Workbook, поэтому будет возвращена строка Workbook. (Большинство доступных в VBA объектов являются Automation-объектами.)
В строке 12 мы вновь видим инструкцию Set, которая на этот раз присваивает переменной anObject ссылку на текущий лист активной книги. В строке 13 на экран снова выводится результат функции TypeName. Строка, выводимая в этой инструкции, зависит от типа листа, активного в момент запуска процедуры. Если в этот момент активным был обычный лист Excel, то TypeName вернет строку Worksheet (лист).
Функция TypeName имеет много практических применений. В листинге 10.5 имеется процедура FormatArialBoldl2, которая была записана в Excel в уроке 1-го дня, а теперь изменена для выполнения более сложных задач. В этом варианте у процедуры имеет
День 10-Й. Типы данных и переменных
327
ся один необязательный аргумент, который указывает диапазон форматируемых ячеек листа Excel. (О процедурах с аргументами вы подробно узнаете на уроке 12-го дня, «Модульное программирование».) Если аргумент пропущен, то форматируется текущая выборка в активном окне.
Процедура FormatArialBoldl2 была изменена, чтобы с помощью функции TypeName ее можно было просто завершить, если переданный объект не является диапазоном ячеек. Таким образом предотвращается ошибка выполнения в случае, когда процедура будет случайно вызвана с неправильной объектной ссылкой.
Aucmuua 10.5. Использооаиие фдихции ТдреНаше дня предотвращЕиия оттии вышчвиия
1:	Sub FormatArialBoldl2(Optional ByVai objRange)
2:	'Применяет шрифт Arial полужирный, 12 пунктов к объекту objRange,
3:	'при условии, что объект objRange является диапазоном ячеек.
4:	'Если необязательный аргумент objRange ие передай, процедура
5:	'формаирует ячейки, выделенные в активном окне.
6:	'Если необязательный аргумент objRange передан, но не является
7:	'объектом типа Range, эта процедура
8:	'заканчивает работу, ие делая ничего.
9:
10:	If IsMissing(objRange) Then
11:	Set objRange = Activewindow.Rangeselection
12:	Else
13:	If TypeName(objRange) <> "Range" Then
14:	Exit Sub
15:	End If
16:	End If
17:
18:	With objRange.Font
19:	.Name = "Arial"
20:	.Fontstyle = "Bold"
21:	.Size = 12
22:	.Strikethrough = False
23:	.Superscript	= False
24:	.Subscript =	False
25:	.OutlineFont	= False
26:	.Shadow = False
27:	.Underline =	xlNone
28:	.Colorindex = xlAutomatic
29:	End With
30:	End Sub
31:
32:	Sub Test_FormatArialBoldl2()
33:	FormatArialBoldl2
34:	FormatArialBoldl2 objRange:=ActiveSheet.Cells(2, 2)
35:	FormatArialBoldl2 objRange:=ActiveSheet
36:	End Sub
В строке 1 находится описание процедуры; как и в случае функций, процедуры можно описывать с обязательными и необязательными аргументами. (Об описании и использовании аргументов процедуры более подробно рассказывается в уроке 12-го дня.) В этом варианте у процедуры FormatArialBoldl имеется один необязательный
328
Неделя 2
аргумент, objRange, который передается по значению. Строки 2-8 представляют собой комментарий с описанием работы процедуры FormatArialBoldl2.
Для данного варианта процедуры очень важными являются строки 10-16. В строке 10 начинается инструкция If.. .Then, которая с помощью функции isMissing проверяет, имеется ли необязательный аргумент objRange. Если этот аргумент пропущен, то выполняется строка 11. В ней используется ключевое слово Set для присвоения переменной аргумента objRange ссылки на объект. ActiveWindow является свойством объекта Application приложения Excel и возвращает ссылку на активное окно. Rangeselection является свойством объекта window и возвращает ссылку на активную выборку листа, находящегося в этом окне. Поэтому выражение ActiveWindow.Rangeselection возвращает объектную ссылку на текущую выборку текущего активного окна в Excel.
Если при вызове процедуры FormatArialBoldl2 аргумент objRange не был пропущен, то выполняется ветвь Else (строки с 12 по 16), в которой находится вложенная инструкция If...Then. Внутренняя инструкция If (которая начинается в строке 13) использует функцию TypeName, чтобы проверить, является ли указанная в аргументе objRange ссылка объектом Range приложения Excel. (Такие объекты Range представляют собой одну или более ячеек листа.) Если в аргументе objRange содержится ссылка на объект, который не является объектом Range приложения Excel, то значением выражения TypeName(objRange) о "Range" является True и VBA выполняет строку 14, которая завершает процедуру. Благодаря этому завершению предотвращается ошибка выполнения, которая могла бы случиться в строке 18, где используется переменная objRange.
Однако если в аргументе objRange содержится ссылка на объект, который все же является объектом Range приложения Excel, то функция TypeName возвращает строку Range, значением выражения в строке 13 является False, a VBA продолжает выполнять процедуру со строки 18.
В строке 18 начинается инструкция with. Font — это свойство объекта Range, которое возвращает установки шрифта в ячейках диапазона. В строках с 19 по 28 для этого диапазона задаются различные свойства шрифта. Процедура FormatArialBoldl2 завершается в строке 30.
В строках с 32 по 36 находится процедура, с помощью которой тестируется FormatArialBoldl2. В строке 33 вызывается FormatArialBoldl2 без аргументов для форматирования всех ячеек, выбранных в этот момент на' листе из активного окна. В строке 34 снова вызывается процедура FormatArialBoldl2, на этот раз передавая определенный диапазон ячеек: Cells — это свойство объекта Worksheet в приложении Excel, и оно возвращает либо все ячейки листа, либо определенную ячейку, указанную своими координатами строки и столбца. (О свойстве Cells более подробно рассказывается в уроке 19-го дня, «Управление Excel с помощью VBA».) В результате вызова в строке 34 процедуры FormatArialBoldl2 будет отформатирована ячейка на пересечении второго столбца и второй строки активного листа. Наконец, в строке 35 выполняется вызов FormatArialBoldl2 с умышленной передачей ей неправильной объектной ссылки (ActiveSheet является свойством объекта Application приложения Excel и возвращает ссылку на текущий активный лист).
Так надо
При написании программ используйте функцию TypeName, а также другие информационные функции VBA, чтобы предотвратить возможные ошибки выполнения, тем более, если вы собираетесь передавать свои процедуры другим пользователям.
День 10-й. Типы данных и переменных
329
Результаты функции VarType
Функция VarType во многом похожа на TypeName; обе функции используются для одних и тех же целей и в основном одним и тем же способом. Однако VarType для указания типа данных своего аргумента возвращает числовой код, в отличие от TypeName, возвращающей строку.
Функция VarType имеет следующий общий синтаксис.
VarType(varname)
где varname представляет любое имя переменной, соответствующее правилам VBA. Обратите внимание, что в отличие от TypeName varname должно всегда быть именем одной переменной. В табл. 10.3 показаны числовые коды, которые возвращает VarType в зависимости от типа данных varname.
Таблица 10.3. Значения, возвращаемые функцией VarType
Константа VBA	Число	Назначение
vbEmpty	0	Неинициализированный тип Variant
vbNull	I	Variant, который не содержит допустимого значения
vblnteger	2	Тип Integer
vbLong	3	Целый тип Long
vbSingle	4	Тип с плавающей точкой Single
vbDouble	5	Тип с плавающей точкой Double
vbCurrency	6	Тип Currency
vbDate	7	Тип Date
vbString	8	Тип String
vbObject	9	Тип Object, обычно объект Automation. (Чтобы узнать конкретный тип объекта, используйте функцию TypeName)
vbError	10	Variant, где находится числовое выражение, представляющее одно из значений ошибок VBA (или определенных пользователем); значения ошибок VBA описаны в уроке 18-го дня
vbBoolean	11	Тип Boolean
vbVariant	12	Тип Variant. VarType только возвращает это значение вместе с vbArray, чтобы указать массив элементов типа Variant
vbDataObject	13	Объект доступа к данным; обычно OLE-объект. (Об OLE-объектах рассказывается в уроке 20-го дня)
vbDecimal	14	Тип с плавающей точкой Decimal (десятичный). Числа такого типа могут храниться только в переменных Variant. Эти числа могут использоваться только VBA; переменные типа Decimal описывать нельзя
vbByte	17	Тип Byte
330
Неделя 2
Окончание табл. 10.3
Константа VBA Число Назначение
vbArray	8192 Тип Array. VarType всегда комбинирует это значение
(путем арифметического сложения) с одним из других кодов, перечисленных в этой таблице, чтобы указать определенный тип элементов массива. VarType никогда не возвращает только код vbArray
При использовании функции VarType возвращается одно из чисел, указанных в табл. 10.3. Некоторые из них требуют дополнительных пояснений.
Во-первых, когда при использовании функции VarType возвращается тип объектной переменной, обратите внимание, что эта функция только сообщает, имеет ли переменная тип Object, который поддерживает Automation или не поддерживает. Далее, если функция VarType применяется к неинициализированной переменной типа Object, то VBA выводит сообщение об ошибке выполнения, где говорится, что объектная переменная не установлена. Если необходимо найти конкретный тип объекта или определить, была ли присвоена переменной ссылка на какой-нибудь объект, то вместо VarType используйте функцию TypeName.
Во-вторых, если тестируемая с помощью VarType переменная является массивом, то функция арифметически складывает код типа Array с кодом типа данных элементов массива. Например, для массива целых элементов типа Long VarType возвращает 8195, что равняется 8192 (код типа Array) плюс 3 (код типа Long). Поэтому любой код, больший 8192, говорит о том, что проверяемая VarType переменная является массивом. (В одном из разделов урока 13-го дня, “Управление файлами с помощью Visual Basic”, рассказывается, как с помощью логических операторов выполнять побитовое сравнение, когда надо узнать атрибуты файла; аналогичные приемы можно использовать для распознавания значений, хранящихся в числе, полученном от VarType.)
Так надо
При работе с кодами, возвращаемыми VarType, используйте имена констант VBA, которые указаны в табл. : 10.3. Это сделает ваши программы более легкими для чтения и отладки.
Специальные значения Emptg и Null
В VBA для того, чтобы представлять неинициализированные, отсутствующие или недопустимые данные, имеется два специальных значения, указываемых ключевыми словами Empty и Null. У них сходное значение, но разное применение.
Значение Emptg
В VBA значение Empty используется для указания Variant-переменной, которой не присваивалось никакого значения. Когда в VBA создается новая переменная, то ей автоматически присваивается значение Empty. Это значение сохраняется в Variant-переменной, пока ей не будет присвоено какое-либо другое значение.
Когда значение Empty встречается в выражениях, то оно не вызывает ошибок выполнения или несоответствия данных. Если в числовом выражении или в каком-либо другом (в качестве числа) встречается Variant-переменная, содержащая значение Empty, то VBA рассматривает его в качестве нуля (0). Если же Variant-переменная со
День 10-Й. Типы данных и переменных
331
значением Empty встречается в строковом выражении, то VBA рассматривает его в качестве строки нулевой длины ('"').
Значение Empty можно непосредственно присвоить Variant-переменной, если требуется показать, что в переменной не содержится никаких данных. Для выполнения этой операции используйте такую инструкцию (где V означает любую переменную типа Variant):
V=Empty
Ввиду того что VBA в зависимости от контекста выражения рассматривает Empty в качестве 0 или в качестве строки нулевой длины с помощью операторов сравнения невозможно распознать, содержит ли Variant-переменная значение Empty или действительно равна 0 или пустой строке. Именно этим объясняется наличие в VBA функции IsEmpty.
Эта функция имеет следующий общий синтаксис.
IsEmpty(expression)
где expression представляет любую допустимую в VBA переменную или выражение типа Variant. IsEmpty возвращает True, если expression — это единственная переменная типа Variant, в которой содержится специальное значение Empty; False — в противном случае. Обычно эта функция применяется с именем переменной в качестве аргумента. Хотя в качестве аргумента expression можно передать и сложное выражение, IsEmpty всегда возвратит False, если expression будет содержать более одной переменной.
Для распознания значения Empty в переменной типа Variant можно также использовать функции TypeName или VarType.
Значение Null
В VBA значение Null используется для указания Variant-переменной, которая не содержит никакого допустимого значения. В VBA значение Null, в отличие от Empty, переменным не присваивается. Единственным способом назначить переменной значение Null является прямое присвоение переменной этого значения или результата выражения, содержащего значение Null.
Когда Null встречается в выражениях, то оно не вызывает ошибок выполнения или несоответствия данных. Если в выражении встречается Variant-переменная, содержащая значение Null, то результатом всего выражения будет Null.
Значение Null входит в состав VBA, чтобы облегчить работу с информацией баз данных и работающими с ними приложениями. Многие такие приложения, например Microsoft Access, используют Null, чтобы указать в базе данных пропущенные или недопустимые значения. Можно написать процедуру, которая помогает выполнять ввод в базу данных на листах Excel. В этой программе можно, в частности, предусмотреть ввод значения Null для еще не введенных или неправильных номеров счетов-фактур.
Можно также использовать Null, чтобы указать, является ли допустимым значение, возвращенное функцией. Например, вы пишете функцию, в которой для получения от пользователя значения используется окно InputBox, отображение которого пользователь может отменить. Тогда для указания того, что отображение окна ввода отменено или нет никаких допустимых данных, можно сделать, чтобы функция в таких случаях возвращала Null.
Для того чтобы присвоить значение Null переменной, используйте такую инструкцию (где V означает любую переменную типа Variant):
V=Null
332
Неделя 2
В VBA имеется функция IsNull, с помошью которой можно узнать, содержит ли Variant-переменная значение Null. С помощью операторов сравнения VBA сделать это невозможно.
Эта функция имеет следующий синтаксис.
IsNull(expression)
где expression представляет любую допустимую в VBA переменную или выражение типа Variant. IsNull возвращает True, если expression — это единственная переменная типа Variant, в которой содержится специальное значение Null; False — в противном случае. Если expression будет содержать более одной переменной или какая-либо из переменных содержит Null, то значением всего выражения из expression будет Null, а IsNull вернет True.
Не путайте Empty и Null. В VBA Empty присваивается, чтобы показать, что значение Variant-переменной еще не присвоено (переменная еще не инициализирована). Присвоение же некоторой переменной значения Null требуется для того, чтобы указать на наличие в этой переменной недопустимых данных.
Так надо
Используйте функции IsEmpty и IsNull, чтобы проверить, нет ли в переменной значений Empty и Null.
Стандартные операторы сравнения не распознают ни одно из этих значений.
Защитное программирование: предотвращение ошибок
Защитное программирование — это только одно из многих названий разных приемов программирования, предназначенных для сокращения количества ошибок в программах. Целью защитного программирования в процедурах является заблаговременное предотвращение ошибок выполнения и других сбоев.
Вы уже знакомы с несколькими примерами такого программирования из каждого урока второй недели изучения VBA. Хорошими средствами создания “пуленепробиваемых” процедур и программ являются структуры принятия решений, циклы и информационные функции. Они будут надежной защитой от нежелательных ошибок выполнения, вызванных недопустимыми, пропущенными или просто неправильными данными.
Эти приемы защитного программирования особенно важны, если требуется распространять свои процедуры или функции среди других пользователей. Например, вы отвечаете за настройку Microsoft Excel на компьютерах менее опытных пользователей. В таком случае вы, возможно, разработали или записали несколько макросов, чтобы помочь своим менее опытным коллегам выполнять некоторые рутинные задачи, такие как ввод данных в листы Excel, генерация отчетов, создание некоторых деловых документов и т.п.
Когда дело дошло до этого, то плоды ваших трудов получат наивысшую оценку, если будут работать без ошибок выполнения. Если процедура «сбойнет» посреди сложного задания, то такой оборот дела не очень понравится вашему сотруднику, который не будет знать, что же делать дальше.
Как минимум, процедура, предназначенная для применения менее опытными пользователями, должна предвидеть ошибку выполнения и завершать свою работу вы
День 10-Й. Типы данных и переменных
333
дачей определенной информации, а не ограничиваться простым сообщением VBA об ошибке. Например, будет намного лучше, если процедура выведет на экран четкое и ясное сообщение, почему не может выполнять задание, и лишь затем закончит работу. Это будет намного лучше, чем обрекать пользователя не всегда понятные сообщения VBA об ошибке выполнения.
Даже если вы и не собираетесь ни с кем делиться своими процедурами, не пренебрегайте защитным программированием. Пусть ваша программа выполняет достаточно сложную задачу, например ввод данных в лист или создание отчета с выводом на печать. И если произойдет сбой из-за ошибки выполнения, то, мне кажется, вам будет трудно исправить последствия частичного выполнения задачи.
Проверка аргументов о других внутренних значений
Одни приемы защитного программирования состоят в том, чтобы проверять значения, поступающие в процедуру или функцию извне, такие как аргументы функции. Другие должны обеспечить все необходимое, чтобы среда, в которой работает процедура, была для нее благоприятной.
В этой главе уже были изучены листинги 10.1 и 10.5. Они показывают примеры такого рода программирования в действии. И функция Flipcase и процедура FormatArialBoldl2 обе содержат инструкции, которые проверяются различные условия среды и не дают случиться некоторым предвидимым ошибкам.
Например, у функции FlipCase есть инструкции, задача которых состоит в том, чтобы необязательный аргумент nChar получил значение нужного числового типа. Те же инструкции выполняют другую задачу: если аргумент nChar пропущен, то функция FlipCase дает свое значение. Все эти меры предосторожности помогают избежать ошибки выполнения, когда в оставшейся части FlipCase действительно может понадобиться нужное значение nChar.
А вот другой пример. У процедуры FormatArialBoldl есть инструкции, задача которых состоит в том, чтобы необязательный аргумент objRange действительно имел ссылку на объект, который можно форматировать. В таком случае можно будет избежать ошибки выполнения, когда процедура попытается изменить свойство объекта Font.
Аналогичные инструкции проверки значений и среды вы можете добавить в свои процедуры и функции, чтобы ваши программы работали гладко и не страдали от ошибок выполнения.
Проверка данных пользователя
Когда одна из ваших процедур работает как надо, то на компьютер и VBA можно рассчитывать — при любом запуске процедуры ее инструкции будут работать одинаково. Однако если речь идет о пользователе, то это не тот случай.
Если ваша процедура или функция получает данные от пользователя — не важно, вы это или кто-то другой, — то нельзя рассчитывать, что пользователь всегда будет работать одинаково и безошибочно вводить данные. Даже если вы написали абсолютно безошибочный исходный текст, то стоит пользователю ввести неверное значение, и в результате вы получите ошибку выполнения из-за несоответствия данных или из-за чего-то там еще.
Допустим, например, вы написали процедуру, в которой от пользователя требуется ввести дату в виде строки. Предположим также, что вы решили уберечь пользователя от лишнего ввода данных, и поэтому написали процедуру, где предполагается, что будут введены лишь месяц и число. После ввода месяца и числа к ним в самый конец добавляется текущий год. Если пользователь не знает или игнорирует тот факт, что
334
Неделя 2
процедура добавляет текущий год, и вводит полную дату 29/10/99, то текущий год (например, /99} все же будет вставлен в конец строки, в результате чего получится новая строка 29/10/99/99, которая не является допустимым выражением для даты. Если процедура попытается преобразовать такое значение в дату, то вмешается VBA, который выведет сообщение об ошибке выполнения и остановит процедуру.
А вот другой пример. Если вы напишете процедуру, которой необходимо получить от пользователя число, а пользователь введет нечисловую строку, то дело закончится ошибкой выполнения из-за несоответствия данных. И произойдет это в той точке процедуры, где одна из ее инструкций попытается использовать в качестве числа значение, введенное пользователем.
В процедурах для собственного пользования с целью преодоления проблем подобного рода вы можете предусмотреть наличие в окне ввода четкой подсказки о том, какого типа данные надо вводить. В процедурах же, которыми будут пользоваться другие люди, в дополнение к четкой и ясной подсказке необходимо добавить инструкции, анализирующие вводимые данные, чтобы они соответствовали тому, что требуется процедуре. Что касается примера с вводом даты, то в процедуру можно включить инструкции, которые будут проверять полученную от пользователя строку и добавлять в нее год лишь в том случае, если пользователь его пропустит.
Необходимо также обеспечить обработку тривиальных случаев, таких как отмена пользователем отображения окна ввода. Во многих листингах из уроков 8-го и 9-го дней имеются циклические структуры и структуры If...Then, которые проверяют полученные от пользователя данные, а также соответствующим образом реагируют на отмену ввода или ввод пустой строки. В листинге 10.2 показан элементарный цикл ввода данных, который делает так, чтобы вводимое значение фактически было нужного типа Date или строкой, которую можно преобразовать в Date.
Так надо
Проверяйте вводимые пользователем данные на соответствие тем, что требуются процедуре. Необходимо создан, цикл, который будет запрашивать данные от пользователя до тех пор, пока наконец-то не будет введено значение нужного типа или из нужного диапазона.
Так не надо
Не сбивайте с толку пользователя процедуры: если отказываетесь ввести его данные, то выведите сообщение, где объясняется, в чем он не прав.
В листинге 10.6 имеется функция, Getlnteger, которая получает от пользователя значение типа Integer. В этой функции имеется четкая и ясная подсказка, какого типа информацию следует вводить, а затем тщательно проверяется набранная пользователем строка — будет ли она иметь тип Integer. Если пользователь ввел нечисловые данные, число с плавающей точкой или число, выходящее за допустимый для типа Integer диапазон, то выводится сообщение с объяснением проблемы, а затем предлагается ввести другое число.
Листинг 10.6. Проверка вводимых пользователем данных с целью полдчить допустимые звачеввя
1:	Function Getlnteger() As Variant
2:	'Просит пользователя ввести целое число и проверяет,
3:	'действительно ли это целое число.
День 10-й. Типы данных и переменных
335
4:
5:	Const	iTitle	=	"Целое	число"
6:	Const	IntMax	=	32767	'максимальное положительное целое
7:	Const	IntMin	=	-32768	'минимальное отрицательное целое
8:
9:	Dim uNum As Variant
10:	Dim Okint As Boolean
11:
12:	Okint = False 'инициализация условия цикла
13:	Do Until Okint
14:	uNum = InputBox(prompt:=“Введите целое число: ",
15:	Title:=iTitle)
16:	If IsNumeric(uNum) Then 'числовое значение
17:	uNum = CDbl(uNum)	'преобразуем в Double
18:	If ((uNum >= IntMin) And (uNum <= IntMax)) Then
19:	If (uNum - Int(uNum)) = 0 Then 'нет дробной части
20:	uNum = Clnt(uNum)	'преобразуем
21:	Okint = True	'действительно, целое
22:	Else	'введена десятичн. точка
23:	MsgBox prompt:="Введите число без десятичной " &
24:	"точки1",
25:	Title:=iTitle, Buttons:=vbExclamation
26:	End If
27:	Else 'введенное чело выходит за диапазон
28:	MsgBox prompt:="Число должно быть " &
29:	"от " & IntMin & " до " & IntMax &
30:	Title:=iTitle, Buttons:=vbExclamation
31:	End If
32:	Else	'введено нечисловое значение
33:	If uNum = "" Then 'ничего не введено
34:	uNum = MsgBox(prompt:="Прервать ввод чисел?",
35:	Title:=iTitle,
36:	Buttons:=vbYesNo + vbQuestion)
37:	If uNum = vbYes Then 'отказ пользователя
38:	Getlnteger = Null 'присваиваем результату Null
39:	Exit Function	'конец работы функции
40:	End If
41:	Else
42:	MsgBox prompt:="Введите число!",
43:	Title:=iTitle,
44:	Buttons:=vbExclamation
45:	End If
46:	End If
47:	Loop
48:	Getlnteger = uNum
49:	End Function
50:
51:
52:	Sub Test_GetInteger()
53:	Dim iNum As Variant
54:
55:	iNum = Getlnteger
56:	If IsNull(iNum) Then
336
Неделя 2
57:	MsgBox "Ввод отменен."
58:	Else
59:	MsgBox "Вы ввели: " & iNum
60:	End If
61:	End Sub
Эта функция достаточно проста, хотя и не кажется такой на первый взгляд. В ней принимаются от пользователя данные, а затем над ними выполняется последовательность проверок.
•	Являются ли введенные данные числовыми?
•	Если числовые, то попадает ли их значение в допустимый для чисел типа Integer диапазон?
•	Если это число и оно находится в диапазоне Integer, то является ли оно целым (без дробной части)?
Только тогда, когда введенное пользователем значение пройдет все проверки, функция наконец выдаст само это число. Если значение не пройдет хотя бы одной из проверок, то появится сообщение, с объяснением, в чем именно не прав пользователь, а затем повторяется цикл, в котором от пользователя принимаются новые данные. Getlnteger также устанавливает, отменил ли пользователь отображение окна ввода, подтвердил ли отмену, и если действительно отменил, то возвращает Null.
В строке 1 находится описание функции Getlnteger. Обратите внимание, что функция возвращает результат типа Variant, а не Integer. Делается это для того, чтобы можно было вернуть значение Null, если пользователь отменит окно ввода.
В строках 5-7 описано несколько констант. iTitle дает заголовок для всех окон, выводимых функцией. IntMax представляет собой максимальное, a IntMin — минимальное значение, которые могут быть у чисел типа Integer.
В строке 9 описана переменная uNum, которая используется для хранения введенных пользователем данных. Обратите внимание, что тип этой переменной Variant, что несколько облегчает проверку и обработку ее значения. В строке 10 описана переменная типа Boolean, OKint. Она используется как флаг для контроля над циклом, который выводит на экран окно ввода и проверяет значение, полученное от пользователя.
В строке 12 переменной OKint присваивается значение False; таким образом начнет выполняться цикл из строки 13.
В строке 13 находится начало цикла Do Until. Тело этого цикла содержит все инструкции из строк 14-46. Этот цикл выполняется до тех пор, пока значением OKint не станет True.
Первая инструкция тела этого цикла находится в строке 14; в этой инструкции функция InputBox предлагает пользователю ввести целое число и присваивает полученное значение переменной uNum.
В строке 16 инструкция If.. .Then.. .Else проверяет, является ли введенное пользователем значение числом. Если строка в uNum не числовая, то для этой инструкции выполняется компонент Else, который находится в строках 32-46. В этом компоненте в строке 33 находится вложенная инструкция If.. .Then.. .Else, которая проверяет, не является ли пустой строка из переменной uNum. В случае положительного результата выводится инструкция MsgBox из строки 34. Эта инструкция запрашивает пользователя, не отменить ли ввод данных. Если получен ответ «Yes» (да), то в строке 38 результату Getlnteger присваивается значение Null и в строке 39 эта функция заканчивается. Таким способом можно проверить результат функции и определить, был ли отменен
День 10-й. Типы данных и переменных
337
ввод данных; если результатом Getlnteger является Null, то пользователь операцию ввода отменил.
Если uNum не пустая, то для вложенной инструкции If выполняется компонент Else, который находится в строках с 41 по 45. В этих строках используется MsgBox для вывода сообщения о том, что пользователь должен ввести числовое значение. Затем выполняется переход в строку 47, которая является концом тела цикла. (Помните, что эта ветвь текста программы выполняется только тогда, когда пользователь введет нечисловое значение.)
Если в строке 16 значение uNum будет числовым, то выполнение продолжается со строки 17. Здесь введенная пользователем строка переводится в число типа Double, а результат снова присваивается переменной uNum. (uNum имеет тип Variant, поэтому может содержать значение любого типа.) Это преобразование несколько облегчает дальнейшую проверку; теперь можно вместо сложного анализа строки выполнять простое арифметическое сравнение.
В строке 18 начинается вложенная инструкция If.. .Then.. .Else. Эта инструкция проверяет, не попадает ли значение uNum в допустимый для чисел типа Integer диапазон. В ней находится выражение ((uNum >= IntMin) And(uNum <= IntMax)), значением которого будет True , если uNum находится в диапазоне между -32768 и 32767.
Если значение в uNum не попадает в допустимый для чисел типа Integer диапазон, то для этой инструкции выполняется компонент Else, который находится в строках 28-31. В этих строках для вывода сообщения о том, что пользователь должен ввести число из нужного диапазона, используется MsgBox. Затем выполняется переход в строку 47, которая является концом тела цикла.
Если в строке 18 значение uNum будет находиться в допустимом для чисел типа Integer диапазоне, то выполнение продолжается со строки 19, где находится следующая вложенная инструкция If.. .Then.. .Else.
В строке 16 эта инструкция с помощью выражения (uNum - Int(uNum) )=0 определяет, какую выполнять ветвь программного текста. В этом выражении от значения из uNum отнимается его целый эквивалент (полученный с помощью имеющейся в VBA функции Int). Если результатом вычитания будет 0, то число в uNum не имеет дробной части. (В этом выражении функция Int используется вместо CInt, так как перед переводом чисел в целые CInt их округляет, в то время как Int просто отсекает дробную часть.)
Если у выражения в строке 19 получается значение False, т.е. пользователь ввел число с плавающей точкой, то выполняется вложенный компонент Else, который находится в строках 22-26. Эти строки просто выводят сообщение о том, что пользователь должен набрать число без десятичной точки. Затем выполняется переход в строку 47, которая является концом тела цикла.
Если у выражения в строке 19 значение True, значит, пользователь действительно ввел целое число. Выполнение продолжается со строки 20, где для преобразования uNum в Integer используется CInt. Эта операция необходима для того, чтобы значение в Variant-переменной имело тип Integer; в противном случае у нее будет тип Double.
В строке 21 переменной OKint присваивается значение True. Это является признаком того, что пользователь успешно ввел целое значение, завершив таким образом цикл. Данная строка выполняется только тогда, когда введенное значение успешно пройдет все три испытания. Если введенное пользователем значение не пройдет одно из них, то Getlnteger выводит соответствующее сообщение и повторяет цикл ввода.
Когда пользователь введет нужное значение, цикл прекратит работу и произойдет переход в строку 48, где функции Getlnteger будет присвоен полученный результат, после чего выполнение функции прекратится.
338
Неделя 2
В строках 52-61 находится процедура, с помощью которой тестируется функция Getlnteger. В строке 55 вызывается Getlnteger и передается возвращаемое ею значение переменной iNum, описанной в строке 53. Обратите внимание на инструкцию If.. .Then.. .Else в строках 56-60. В ней для того, чтобы определить, содержится ли в iNum значение Null, используется функция IsNull. Если содержится, то это значит, что пользователь в Getlnteger отменил отображение окна ввода, и результат этой функции не содержит никаких допустимых данных.
Так надо
Чтобы облегчить программирование задачи, надо написать список условий, которые необходимо проверить (как список в начале раздела анализа листинга 10.6). Если перед началом программирования вы создадите такой список, то в процессе работы избежите фальстартов, потраченных впустую усилий и, вполне возможно, некоторого разочарования.
Для расширения возможностей стандартных информационных функций VBA пишите собственные функции проверки типа данных. Это особенно валено в тех случаях, когда в своих программах вы проверяете, имеет ли число тип Integer, Double и т.д. Например, если вы пишете большое количество процедур или функций, проверяющих, является ли значение числом, скажем, типа Integer, то было бы полезным просто написать функцию Islnteger с результатом типа Boolean, а не повторять много раз одни и те же инструкции проверки.
Проверка данных, полученных не от пользователя
Ваши процедуры могут получать данные и из других источников, а не только от пользователя через окно ввода. Этими источниками могут быть ячейки листа Excel, а также файлы внешних данных.
Обычно вам надо применять некоторые или все проверки одного вида к данным, полученным процедурой из любого источника. Например, при получении данных из ячейки необходимо использовать те же инструкции проверки, которые применяются к данным, полученным с помощью окна ввода непосредственно от пользователя. Для проверки данных, полученных не от пользователя, используйте те же приемы защитного программирования, которые используются при непосредственном вводе данных.
Сохранение переменных в промежутках между вызовами процедур и функций
В уроке 3-го дня вы изучали живучесть (persistence) переменной, т.е. как долго она сохраняет присвоенное ей значение. Вы узнали, что переменные уровня процедуры сохраняют свои значения лишь тогда, когда выполняется процедура, в которой они были описаны.
Однако в некоторых обстоятельствах требуется сохранять значение переменной достаточно долго. Например, вам нужно, чтобы процедура, которая создает диаграммы, нумеровала их в порядке создания, даже если при каждом ее запуске создается только одна диаграмма. Таким образом, требуется, чтобы внутри процедуры сохранялись значения счетчика диаграмм. А вот другой пример. Возможно, вам понадобится написать процедуру, которая получает от пользователя имя листа и сохраняет последнее из введенных имен, чтобы предложить его как лист по умолчанию во время следующего запуска процедуры.
День 10-Й. Типы данных и переменных
339
Вы можете переназначить обычные правила сохранения VBA, добавив в описании переменной или процедуры (функции) ключевое слово Static. В таком случае VBA не будет стирать содержимое переменной, а будет сохранять его как угодно долго. Статические переменные сохраняются с момента своего создания до того времени, как будет закрыт проект, где они были созданы (книга Excel), или закончится текущий сеанс работы главного приложения.
При создании с помощью Static отдельной статической переменной используется такой синтаксис.
Static varname [As vartype]
где varname представляет любое имя переменной, соответствующее правилам VBA, a vartype — любой из стандартных типов данных: Byte, Integer, Long, Single, Double, Currency, String или Date. В сущности, синтаксис создания статической переменной, включая необязательное ключевое слово As, тот же, что и при описании переменной с помощью Dim. Если вместо Dim будет использовано ключевое слово Static, то VBA создаст переменную, сохраняющую свое значение неопределенно долгий период. Отдельную статическую переменную можно описывать только на уровне процедуры.
В процедуре или функции статическими можно сделать сразу все переменные. Для этого надо поместить ключевое слово Static в начале описания процедуры или функции, что видно на примерах общего синтаксиса.
Static Function name ([arglist]) [As vartype]
Static Sub name()
Здесь varname представляет любое имя процедуры или функции, соответствующее правилам VBA, arglist — любое допустимое определение списка аргументов функции, a vartype — тип данных результата функции.
Если функция (или процедура) была описана с помощью ключевого слова Static, то все переменные, определенные внутри нее, сохраняют свои значения в промежутках между вызовами этой функции (или процедуры).
В листинге 10.7 находится процедура MakeSalesRpt_Chart из урока 8-го дня, в которой теперь используются статические переменные. В этом варианте процедуры при ее первом вызове сохраняются вводимые пользователем значения исходного листа и диапазона ячеек с данными, для которого надо построить диаграмму. При следующем вызове процедуры они будут предлагаться как значения по умолчанию. MakeSalesRpt_chart также сохраняет обновляющееся значение счетчика созданных диаграмм и добавляет это число в название каждой новой диаграммы.
Листинг 10.7. Использование статических переменных
1:	Sub MakeSalesRpt_Chart()
2:	'Эта процедура спрашивает у пользователя имя листа с
3:	'данными, потом спрашивает диапазон ячеек с данными для
4:	'диаграммы. После этого процедура спрашивает имя листа, на котором
5:	'будет создана диаграмма. Потом с помощью метода
6:	'Chartwizard создается круговая диаграмма.
7:	'В процедуре используются переменные типа Static, поэтому при
8:	'повторном вызове процедура сохраняет введенные ранее значения.
9:
10:	Const sTitle = "Создание диаграммы продаж"
11:
12:	Static SrcShtName As String
13:	Static SourceRng As String
340
Неделя 2
14:	Static Chartcount As Variant
15:	Dim DestShtName As String
16:
17:	'получаем имя листа с данными
18:	SrcShtName = InputBox(prompt:="Введите имя листа с данными, " & _
19:	"по которым строится диаграмма:",
20:	Title:=slitle,
21:	Default:“SrcShtName)
22:
23:	'проверяем, ввел ли пользователь имя листа илн выбрал Cancel
24:	If Len(Trim(SrcShtName)) = 0 Then
25:	MsgBox "Имя листа не указано - процедура прервана"
26:	Exit Sub
27:	End If
28:	'выбираем лист с указанным именем
29:	Sheets(SrcShtName).Select
30:
31:	'получаем диапазон ячеек
32:	SourceRng = InputBox(prompt:“"Введите диапазон ячеек с данными " &
33:	"в стиле А1:",
34:	Title:=sTitle,
35:	Default:“SourceRng)
36:
37:	'проверяем, ввел ли пользователь диапазон или выбрал Cancel
38:	If Len(Trim(SourceRng)) = 0 Then
39:	MsgBox "Диапазон не указан - процедура прервана"""
40:	Exit Sub
41:	End If
42:
43:	'получаем имя листа с диаграммой
44:	DestShtName = InputBoK(prompt:="BBeflHTe имя листа, на 11 &
45:	"котором будет построена диаграмма:”,
46:	Title:=sTitle)
47:
48:	'проверяем, не выбрал ли пользователь Cancel
49:	If Len(Trim(DestShtName)) = 0 Then
50:	MsgBox "Имя листа не указано - процедура прервана"
51:	Exit Sub
52:	End If
53:
54:
55:	'выбираем лист и строим диаграмму
56:	Sheets(DestShtName).Select
57:	ActiveSheet.Chartobjects.Add(96, 37.5, 234, 111).Select
58:
59:	'set up the chart count
60:	If IsEmpty(ChartCount) Then
61:	ChartCount = 1
62:	Else
63:	ChartCount = Chartcount + 1
64:	End If
65:
66:	'с помощью метода Chartwizard строим диаграмму.
День 10-й. Типы данных и переменных
341
67:	With Sheets(SrcShtName)
68:	ActiveChart.Chartwizard Sour ce: = . Range) Sour ceRng),
69:	Gallery:=xlPie,
70:	Format:=7,
71:	PlotBy:=xlColumns,
72:	CategoryLabels:=l,
73:	SeriesLabels:=l,	_
74:	HasLegend:=l,
75:	Title:="Oi4eT о продажах" & Chartcount
76:	End With
77:	End Sub
В строках 12-15 описаны переменные, которые используются в этой процедуре. SrcShtName, SourceRng и Chartcount описаны как статические переменные, поэтому они будут сохранять свои значения между вызовами MakeSalesRpt Chart. Переменная DestShtName не описана как статическая, следовательно, ее (значение будет потеряно, как только закончится выполнение MakeSalesRpt_Chart. Новой для процедуры является переменная Chartcount, которая будет использоваться в качестве обновляющегося счетчика диаграмм, созданных процедурой в текущем сеансе работы Excel. Эта переменная описана как имеющая тип Static Variant, что дает возможность использовать функцию IsEmpty, когда надо определить, выполнялась ли ранее процедура в текущем сеансе Excel.
В строках 18-21 от пользователя принимается имя листа с данными, для которых надо построить диаграмму. Обратите внимание, что находящаяся в этих строках функция InputBox теперь использует аргумент Default (по умолчанию) и передает переменную SrcShtName как значение по умолчанию. В результате выполнения этих инструкций при первом запуске процедуры MakeSalesRpt_Chart в текущем сеансе Excel на экран выводится окно, изображенное на рис. 10.1. Обратите внимание, что его текстовое поле пустое.
Рис. 10.1. Это окно выводится на экран для получения от пользователя имени исходного листа при первом запуске MakeSalesRpt_Chart в текущем сеансе Excel
В строках 24-27 проверяются введенные пользователем данные, с целью выяснить, не было ли отменено от отображение окна ввода. Если его отображение было отменено, то выполняются инструкции из строк 25 и 26, где выводится сообщение, что ввод был отменен, и процедура завершается.
В строке 29 выбирается лист, который был назван пользователем, так чтобы его можно было видеть. Это пригодится пользователю, когда потребуется задать диапазон ячеек с данными для диаграммы.
В строках 32-35 задается нужный диапазон ячеек. Обратите внимание, что находящаяся в этих строках функция InputBox использует теперь аргумент Default (по умолчанию) и передает переменную SourceRng как значение по умолчанию. В результате
342
Неделя 2
выполнения этих инструкций при первом запуске процедуры в текущем сеансе Excel на экран выводится окно, изображенное на рис. 10.2. Обратите внимание, что его текстовое поле также пустое.
Рис. 10.2. Это окно выводится на экран для получения от пользователя диапазона ячеек с данными для диаграммы при первом запуске MakeSalesRpt_Chart в текущем сеансе Excel
В строках 37-41 проверяются введенные пользователем данные и выясняется, не было ли отменено отображение окна ввода. Если было его отображение было отменено, то выводится сообщение, и процедура завершается.
В строках 44-46 от пользователя принимается имя листа, на который надо будет поместить готовую диаграмму. Обратите внимание, что находящаяся в этих строках функция InputBox теперь не предлагает значения по умолчанию и DestShtName представляет собой единственную в процедуре переменную, которая не является статической. Она не описана как статическая, потому что не похоже, чтобы вы хотели новой диаграммой накрыть предыдущую. В результате выполнения этих инструкций на экран выводится окно, изображенное на рис. 10.3. Обратите внимание, что и его текстовое поле пустое.
Рис. 10.3. Это окно выводится на экран для получения от пользователя имени листа, в который надо поместить готовую диаграмму
В строках 49-52 снова проверяется, не отменил ли пользователь операцию. И вот в строках 56 и 57 выбирается нужный лист и на него помещается новый объект диаграммы.
В строках 60-64 тоже находится нечто новое по сравнению с предыдущей версией MakeSalesRpt_Chart. Это инструкция If.. .Then.. .Else, в которой проверяется, не имеет ли переменная Chartcount значения Empty; помните, что переменная типа Variant, которой еще не присваивалось значение, имеет специальное значение Empty. Если Chartcount пустая (т.е. имеет значение Empty), то это первый случай запуска процедуры в текущем сеансе Excel. В подобной ситуации в строке 61 переменной Chartcount присваивается значение 1. Если Chartcount не Empty, то в текущем сеансе Excel процедура выполнялась уже, как минимум, один раз. В этом случае в строке 63 значение переменной ChartCount увеличивается на 1. Эта инструкция If.. .Then.. .Else работает только потому, что ChartCount является статической переменной и сохраняет свое зна
День 10-Й. Типы данных и переменных
343
чение между вызовами MakeSalesRpt_Chart. Если бы она не была статической, ее значением всегда было бы Empty при любом запуске процедуры. Это происходило бы из-за того, что VBA при каждом запуске процедуры создавал бы ChartCount заново и уничтожал бы ее при каждом завершении MakeSalesRpt_Chart. Однако благодаря своей статичности ChartCount эффективно сохраняет по нарастанию то количество диаграмм, которое создано процедурой в текущем сеансе Excel.
Наконец, в строках 67-75 для создания диаграммы с помощью указанного диапазона ячеек с данными используется метод Chartwizard . На рис. 10.4 показан пример диаграммы, построенной с помощью процедуры MakeSalesRpt_Chart. Обратите внимание на заголовок этой диаграммы SalesReportl. Он заканчивается числом 1. Это результат конкатенации (в строке 75) имени диаграммы со значением ChartCount.
£3 Microsoft Excel - ДвньЮ xls
«Дайл Правка Биа Вставка Формат Сервис Данные Qkho Справка .	_	- |gj х|
>о*в ви? * чар • «». ' s, о о ?|	" ’ / ‘. к а ?
"" Диагр i jt] ___________________->
А В С D Е . F Q..H I Т 1	~'
2
 А’ ~5
6 7
8 9 10 11 12 13 14 15
'is 17 18
ИО Н \ Sheetl \sheet2 ( Грсдажи /	| _« I	| ИГ”
Готово	NUM
о,.
о-
Отчет о продажах1
Запад Север 19%	18%
Восто 24%
Юг
39%
В Север  Юг □ Восток □ Запад
-о

Рис. 10.4. Пример круговой диаграммы, созданной процедурой MakeSalesRpt_Chart. Обратите внимание на число 1 в конце заголовка диаграммы
Теперь если вы запустите MakeSalesRpt_Chart в том же сеансе Excel во второй раз, то процедура поведет себя точно так, как было описано, но содержимое некоторых выводимых ею окон будет другим.
Когда MakeSalesRpt_Chart (в строках с 18 по 21) предлагает ввести имя листа с исходными данными, на экране появляется окно, показанное на рис. 10.5 (предполагается, что прошлый раз это был лист с именем Sales Report (отчет о продажах)). Поскольку переменная SrcShtName является статической, то ее значение сохраняется в промежутке между первым и вторым запусками процедуры MakeSalesRpt_Chart. Поскольку инструкция InputBox (строки 18-21) для хранения значения по умолчанию использует SrcShtName, то на этот раз процедура в качестве такого значения может предложить Sales Report.
344
Неделя 2
Аналогично этому, когда MakeSalesRpt_Chart (в строках 32-35) предлагает ввести диапазон ячеек с исходными данными, на экране появляется окно, показанное на рис. 10.6 (предполагается, что прошлый раз это был диапазон B4:D8).
Когда MakeSalesRpt Chart (в строках 44-46) предлагает ввести лист, куда требуется поместить диаграмму, на экране появляется то же окно, которое уже было показано на рис. 10.3.
Наконец, когда создание второй диаграммы закончено (предполагается, на основе тех же данных, что и в прошлый раз), она появляется на экране, как показано на рис. 10.7. Обратите внимание, что на этот раз заголовок диаграммы, Sales Report2, заканчивается числом 2. Это результат конкатенации (в строке 75) имени диаграммы со значением Chartcount (оно увеличивается на 1 каждый раз при новом запуске MakeSalesRpt_Chart).
Другие примеры статических переменных будут приведены в листингах следующих глав этой книги.
Рис. 10.5. При каждом новом выводе этого окна на экран предлагается лист, выбранный при прошлом выводе в текущем сеансе Excel
Рис. 10.6. При каждом новом выводе этого окна на экран предлагается диапазон, выбранный при прошлом выводе в текущем сеансе Excel
День 10-Й. Типы данных и переменных
345
£*} Microsoft Excel - День! O.xls
ИЕЗВ
Правка Вид Вставка Формат Cgpenc Данные £>кно Справка	_|g| х|
]b 'а? о а вFi Mjb ej* -- • ?. ? г- h q 0	« ч »
Диагр 2 jj *_________________________________________________________________________
"A,,________B_______C j JD _ _E„_.L. f._________________G........H_______ 1 ______Z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 NO H\Sheetl ,Sheet2/ Продажи /	|«|	|	>||
o-
6-
-9
Отчет о продажах?
Запад Север 19о/о	18%
Восто 24%
Юг 39%
0 Север  Юг □ Восток □ Запад
a
□
Готово	NUM
Рис. 10.7. Пример круговой диаграммы, созданной во второй раз процедурой MakeSalesRpt_Chart. Обратите внимание на число 2 в конце заголовка диаграммы
Резюме
Сегодня вы узнали, как использовать информационные функции VBA, предназначенные для изучения типа данных, хранящихся в Variant-переменных. В частности, вы научились интерпретировать результаты функций TypeName и VarType и выяснили различия между ними. Вам, кроме того, стали известны специальные значения Empty и Null, имеющиеся между различия и где их используют. В частности, Empty автоматически присваивается переменной типа Variant, чтобы указать, что переменной еще не присваивалось никакое значение.
Кроме того, вы узнали, как комбинировать информационные функции этого типа с инструкциями принятия решений и циклическими структурами, чтобы избежать ошибок выполнения. Это делается таким образом, чтобы у величин, используемых внутри процедур (или функций) или полученных извне (от пользователя или из других источников), были правильные или прогнозируемые значения.
В этой главе также рассказывалось, зачем и как с помощью ключевого слова Static описывать переменные, которые сохраняют свои значения в промежутках между вызовами процедуры или функции.
346
Неделя 2
Вопросы о ответы
Почему так важно проверять внутренние значения данных? Я пишу программу и знаю, какие значения передаются при вызове функции
Главная причина проверки внутренних значений данных в том, что это защита от сделанной человеком ошибки. Нельзя всего запомнить о своих процедурах или функциях, тем более если они были написаны несколько дней (недель, месяцев) тому назад. Применение инструкций проверки этих значений спасет вас потом от многих неприятностей, особенно, если ваша процедура или функция будет специально оповещать, что не так со значениями данных.
Зачастую ваши сообщения об ошибках намного более понятны, чем те, что выдает VBA. Кроме того, когда выходит сообщение VBA об ошибке выполнения из-за несоответствия переданного функции необязательного аргумента, то точка, в которой обнаружена ошибка, может находиться очень далеко от действительного источника ошибки.
И наконец, будет лучше, если при обнаружении потенциальной ошибки процедура завершится в установленном порядке, чем наводить порядок после аварийного завершения процедуры в результате ошибки выполнения
Зачем программе тратить силы на «просвечивание» данных, переданных пользователем через окно ввода?
Количество этих самых сил в целом зависит от того, кто будет основным пользователем созданных вами процедур или функций. Если вы хотите распространять свои программы среди других пользователей, то на проверку их данных потребуются дополнительные усилия. Вы только огорчите пользователя, если окна ввода будут без пояснений требовать повторить операцию или если процедура аварийно завершится в результате ошибки выполнения.
Даже если единственным человеком, использующим ваши процедуры будете вы сами, то в них должна быть предусмотрена хотя бы элементарная проверка данных, чтобы потом не приводить данные в порядок, когда процедура аварийно завершится при выполнении своей задачи
Коллоквиум
Ответы в приложении.
Тест
1.	Для чего нужны информационные функции VBA, начинающиеся со слова Is?
2.	Какие три информационные функции, работающие с типами данных, возможно будут использоваться чаще всего?
3.	Какие информационные функции, работающие с типами данных, дают наиболее специальную информацию о проверяемой переменной?
4.	Какую информационную функцию, работающую с типами данных, надо использовать для нахождения специального типа объектной ссылки?
5.	Что сообщает функция IsMissing?
6.	Что означает, когда функция TypeName возвращает строки Nothing или Unknown?
7.	В чем смысл значения Empty? Присваивает ли VBA такое значение переменным?
8.	В чем смысл значения Null? Присваивает ли VBA такое значение переменным?
День 10-й. Типы данных и переменных
347
9.	Что такое защитное программирование?
10.	Для чего нужно ключевое слово Static и как оно действует?
11.	Как используется ключевое слово Static?
Упражнения
1. Напишите функцию, называемую IsMasterCard, которая возвращает True, если строка в аргументе представляет допустимый номер кредитной карточки MasterCard, или False в противном случае. В вашей функции должен быть единственный аргумент; он является обязательным и имеет строковый тип.
Допустимый номер кредитной карточки MasterCard всегда начинается с цифры 5 и состоит из четырех групп по четыре цифры, причем группы отделены друг от друга дефисом: 5234-5678-9012-3456. В этом упражнении разделяющие черточки являются обязательной частью номера. (Подсказка. Чтобы определить, является ли аргумент функции допустимым номером или нет, используйте следующее логическое выражение с оператором Like (о нем говорится в уроке 4-го дня): StrVal Like 5Ш-ИИ-ИИ-Ш1, где StrVal — это строка с номером MasterCard. Значением этого выражения будет True, если StrVal соответствует шаблону, который находится после оператора Like.)
2. Напишите процедуру, которая принимает от пользователя номер MasterCard. Она должна повторяться в виде цикла неопределенно долго, пока пользователь не введет допустимый номер. Для проверки полученного значения используйте функцию IsMasterCard, которую вы написали при выполнении упражнения 1. Процедура должна обеспечить отмену пользователем операции ввода, а также подтверждение отмены. Кроме того, необходимо, чтобы при повторении цикла отображалось сообщение, где объясняется, почему операцию следует повторить.
348
Неделя 2
^ГДвньЦй
Типы данных и классы объектов, определяемые пользователем
В этой главе рассказывается, как создавать в специальных случаях собственные типы данных и как их использовать в программах на VBA. Мы поговорим также о возможности создания собственных объектных классов. Создание таких классов является основным способом расширения возможностей встроенных объектов Excel; это ключ к пониманию того, каким образом пользователь может создавать собственные диалоговые окна (об этом рассказывается на уроке 16-го дня). Сегодня мы рассмотрим следующие вопросы.
•	Как создавать новые типы данных.
•	Как применять созданные типы данных для выполнения специальных задач или для облегчения обработки групп данных, представленных как одно целое.
•	Как использовать модули классов для создания собственных программных объектов.
•	Как использовать собственные классы объектов в VBA-программе.
Создание новых типов данных
В VBA имеется возможность создавать дополнительные типы данных, так называемые определяемые пользователем типы, которые вы сможете использовать в своих процедурах и функциях. Определяемый пользователем тип дает возможность представить несколько связанных между собой элементов данных как одно целое. Эту единицу данных можно затем передавать в качестве аргумента функции или получать в виде ее результата.
Определяемые пользователем типы данных создаются и используются, чтобы упростить некоторые задачи программирования и уменьшить общее число переменных в программе. Они облегчают хранение и обработку сложных групп данных.
Например, если требуется написать процедуру (или несколько процедур и функций) для создания счета и ввода в него информации в виде перечня по пунктам, то можно создать несколько переменных, в которых будут храниться все нужные процедуре данные. Может потребоваться одна переменная для номера счета, другая — для имени заказчика, третья — для адреса платежа, четвертая — для адреса отгрузки, пятая — для итоговой суммы, следующая — для даты выписки счета и тому подобное, а
День 11-Й. Типы данных и классы объектов...
349
этот список даже не включает в себя те переменные, что будут использованы для хранения данных из пунктов счета.
Как видите, можно быстро создать большое количество переменных. Отслеживать столько переменных, по меньшей мере, неудобно, а иногда с ними можно просто запутаться. Все эти различные элементы можно собрать в виде одного типа данных, создав новый тип. Затем можно описать и использовать одну переменную этого типа, чтобы хранить в ней всю информацию по счету и обращаться только к нужным элементам переменной.
В сущности, определяемый пользователем тип можно представить в виде чемодана, в который можно упаковать различные вещи (в нашем случае типы данных). С ним можно обращаться как с одним предметом, передавая его туда и сюда. О содержимом чемодана можно вспомнить, когда требуется из него взять или, наоборот, положить туда какую-нибудь отдельную вещь.
Определение нового шопа
Перед тем как описать и применять переменные нового типа, вначале необходимо этот тип определить. Его можно сконструировать из стандартных типов данных VBA, которые были описаны в материале урока 3-го дня обучения: Byte, Integer, Long, Single, Double, Currency, Date, Boolean и Variant. В создаваемые типы также можно включить массивы и уже определенные ранее типы.
Чтобы определить новый тип, в области описаний модуля вставляйте инструкцию Туре. Она имеет такой общий синтаксис:
Type VarName
ElementName As type
[ElementName As type]
[ElementName As type]
End Type
VarName и ElementName представляют любой идентификатор, соответствующий правилам VBA. type представляет любое имя типа данных VBA (или определенного пользователем ранее). Более точно, VarName — это имя создаваемого типа данных. ElementName — это имя элемента данных определяемого пользователем типа. Создаваемый тип может включать один или более элементов. Для каждого такого элемента надо указывать тип данных. Это делается с помощью стоящего после ElementName ключевого слова As, за которым следует имя типа данных. Типом элемента может быть любой из стандартных типов данных VBA, массив или созданный ранее тип.
Инструкция Туре может употребляться только на уровне модуля. Все определения типов надо помещать в области описаний в начале модуля перед описаниями всех процедур и функций, а также перед описаниями всех переменных, которые используют эти определенные пользователем типы.
ПРИМЕЧАНИЕ
Можно быстро перейти в область описаний модуля, который открыт в редакторе Visual Basic, выбрав в списке процедур окна модуля пункт Declarations.
Например, можно определить новый тип для почтового адреса:
Type MailingData
Street As String
350
Неделя 2
City As String
State As String *2
Zip As String * 5
End Type
Это определение создает тип, называемый MailingData, в котором находится четыре элемента: Street (название и номер улицы), City (название города), State (двухбуквенная аббревиатура штата) и Zip (почтовый индекс из пяти цифр).
А вот другой пример. Чтобы для хранения информации по счет-фактуре описать определяемый пользователем тип, надо использовать такое определение:
Type InvoiceHeading
Number As Integer
Datelssued As Date
Customer As String
ShipAddress As MailingData
BillAddress As MailingData
Total As Currency
End Type
Это определение создает новый тип, называемый InvoiceHeading, в котором находится шесть элементов: Number (номер счет-фактуры), Datelssued (дата выписки счет-фактуры), Customer (имя заказчика), ShipAddress (адрес поставки), BillAddress (адрес платежа) и Total (итоговая сумма счет-фактуры).
Обратите внимание, что элементы ShipAddress и BillAddress имеют определенный ранее тип MailingData. Поэтому эти два элемента содержат те же данные, что и тип MailingData. В сущности, InvoiceHeading является чемоданом, в котором находятся другие чемоданы: элементы BillAddress и ShipAddress.
Для каждого типа данных требуется столько памяти, сколько занимают все его элементы. Например, тип, у которого есть четыре элемента типа Integer, занимает всего 8 байт памяти. Каждый элемент типа Integer требует для хранения 2 байт (размер Integer), всего элементов четыре, таким образом, общая память составляет 8 байт.
TypeName, VarType и другие имеющиеся в VBA функции, возвращающие тип данных, нельзя использовать с переменными созданного пользователем типа, хотя и можно использовать с отдельными элементами этих типов.
Описание переменных определенного пользователем шина
Вы описываете переменную, имеющую нестандартный тип, таким же образом, как и любую другую переменную в процедуре или функции (если только описываете явно). Очевидно, переменную нестандартного типа нельзя описать неявно, так как VBA не может определить, какой должен быть у нее тип.
Для описания переменной с нестандартным типом применяйте ключевые слова Dim и Static. Эти переменные можно описывать на уровне процедуры или модуля. Перед вами две инструкции, описывающие переменные нестандартного типа. Первая инструкция описывает переменную типа InvoiceHeading, а вторая — переменную типа MailingData.
Dim Invoice As InvoiceHeading
Dim Address As MailingData
День 11-й. Типы данных и классы объектов...
351
Использование переменных определенного пользнвателем типа
Переменные определенного пользователем типа используются в основном так же, как и переменные других типов. Одним из немногих отличий является ссылка на отдельный элемент переменной нестандартного типа.
Вы уже знакомы с точечным разделителем (.), применяемым в объектных ссылках. Вы знаете, что точечный разделитель и соединяет, и разделяет два разных идентификатора, чтобы было известно, когда нужно обратиться к объекту, свойству или методу, принадлежащему другому объекту.
VBA также использует точечный разделитель для создания ссылок на элементы переменной нестандартного типа. Например, если вы опишете переменную Address с нестандартным типом MailingData, как определено в предшествующих разделах этого урока, то к элементу City из этой переменной надо будет обратиться следующим образом.
Address.City
Аналогично, если имеются переменные MyAddress и YourAddress, и обе описаны с нестандартным типом MailingData, то к элементам Zip надо будет обратиться таким образом.
MyAddress.zip
YourAddress.zip
Во всех трех предыдущих примерах точка (.) между двумя именами означает, что эти два имени вместе составляют один идентификатор. Поэтому VBA вначале обращается к памяти по тому адресу, который обозначен именем переменной, а затем использует имя элемента, чтобы обратиться к информации, хранящейся в этом элементе.
Перед вами снова определение нестандартного типа из примера заголовка счет-фактуры.
Type InvoiceHeading
Number As Integer
Datelssued As Date
Customer As String
ShipAddress As MailingData
BillAddress As MailingData
Total As Currency
End Type
Снова обратите внимание, что у элементов ShipAddress и BillAddress имеется ранее определенный нестандартный тип MailingData. Если имеется переменная, Invoice, у которой нестандартный тип InvoiceHeading, а требуется обратиться к элементу City адреса поставки, то необходимо использовать приведенное ниже выражение.
Invoice.ShipAddress.City
При обработке этого выражения VBA вначале обратится к данным, хранящимся в переменной Invoice. Первый точечный разделитель говорит о том, что надо обратиться к элементу ShipAddress переменной Invoice; второй, в свою очередь, — о том, что надо обратиться к элементу City элемента ShipAddress. Как и в случае с объектными ссылками, точечный разделитель (.) соединяет отдельные имена в один идентификатор, одновременно знакомя вас (и VBA) с каждой ссылкой.
Если имеется две переменные одного нестандартного типа, то можно прямо присвоить одну переменную другой, как, например, в следующей инструкции.
352
Неделя 2
MyAddress = YourAddress
Учитывая, что у обеих переменных тип данных MailingData, VBA копирует всю информацию в каждом из элементов YourAddress и сохраняет ее в соответствующих элементах MyAddress.
Нельзя однако присвоить одну нестандартную переменную другой нестандартной переменной, если у них разные типы. Например, нельзя присвоить содержимое переменной типа MailingData переменной типа InvoiceHeading.
Чтобы присвоить значение отдельному элементу нестандартного типа, необходимо явно указать этот элемент. Например, чтобы присвоить значение элементу City переменной MyAddress (предполагается, что у MyAddress нестандартный тип MailingData), используется следующая инструкция:
МуAddress.City = "Oaktown"
В этой инструкции строка Oaktown присваивается элементу City переменной MyAddress. Данные, присваиваемые элементу переменной нестандартного типа, должны быть совместимы с типом данных элемента.
Чтобы получить информацию из отдельного элемента нестандартной переменной и присвоить ее другой переменной, используется следующая инструкция.
AnyStr = MyAddress.City
В этой инструкции VBA принимает строку, хранящуюся в элементе City переменной MyAddress, и присваивает ее переменной AnyStr.
Элементы переменной нестандартного типа можно использовать в любом выражении, а также в качестве аргументов процедур и функций, где тип данных указанного элемента совместим с типом выражения или аргумента. Можно даже описать функции, которые возвращают результат нестандартного типа. (Пример такой функции показан в листинге 11.1.)
Так не над
Не пытайтесь присвоить переменную нестандартного типа переменной типа Variant, иначе VBA выведет сообщение о несовместимости данных.
На уроке 7-го дня нашего учебного курса вы изучали, как использовать в объектных ссылках инструкцию With, чтобы не набирать лишних символов и облегчить чтение исходного текста. Вы узнали, что эта инструкция используется, когда одновременно нужны ссылки на несколько свойств или методов одного и того же объекта. Инструкцию With еще можно использовать для упрощения ссылок на элементы переменной нестандартного типа. Фрагмент исходного текста показывает, как это делается.
With MyAddress
.Street = "123 American Lane, Suite 24“
.City = "Oaktown"
.State = "CA"
Zip = “94609"
End With
Когда выполняются эти инструкции, первым делом обрабатывается ключевое слово With, за которым следует имя переменной. Теперь VBA известно, что нужно обращаться к ссылке, хранящейся в переменной MyAddress. Обратите внимание, что перед каждым из элементов переменной, находящихся в инструкциях присвоения, стоит точечный разделитель (.). При выполнении каждой из этих инструкций вместо пропу
День 11-й. Типы данных и классы объектов...	353
щенной ссылки (указанной точечным разделителем) автоматически подставляются данные из переменной MyAddress. Как в случае с объектными ссылками и инструкцией with, необходимо ставить точечный разделитель. Ключевые слова End with завершают инструкцию with.
Теперь вы можете изучить созданный тип на практике. В листинге 11.1 представлен модуль VBA. В этом модуле находится определение нового типа, а также описания процедуры и функции. Определенный пользователем тип UtilityBill создан для хранения всей информации, относящейся к ежемесячному счету за коммунальные услуги. Его элементы (иногда называемые полями) хранят дату счета, итоговую сумму, а также количество потребленных за месяц кубометров газа и киловатт-часов электроэнергии — каждое число хранится в отдельном поле.
Процедура EnterJJtilityCosts управляет вводом данных и хранением счетов за коммунальные услуги. Функция GetJJtilityltem предназначена для получения от пользователя отдельных значений для одного счета. Весь же модуль является одной простой программой ввода данных для такого рода счетов. Аналогичную программу вы можете использовать для ввода своих данных (или данных своей компании).
Лившица 11.1. Оиредедвиив, создаиие и применение ивсшандартиых тииов
1:	Option Explicit
2:
3:	Const BillTitle = "Учет потребления энергии"
4:
5:	Type UtilityBill
6:	BillDate As Variant
7:	Cost As Currency
8:	Cbm As Integer
9:	Kwh As Integer
10:	End Type
11:
12:
13:	Sub EnterJJtilityCosts ()
14:	'Эта процедура служит для ввода данных о потребленной энергии.
15:	'Она вызывает функцию GetJJtilityltem для ввода одной записи,
16:	'и затем переносит данные в рабочий лист, выполняя цикл,
17:	'пока пользователь не закончит ввод данных.
18:	Dim Bill As UtilityBill
19:	Dim Done As Boolean
20:	Dim RNum As Integer
21:
22:	Worksheets("Потребление энергии").Select
23:
24:	RNum = 0
25:	Done = False
26:	Do Until Done
27:	Bill = GetJJtilityltem
28:	If IsNull(Bill.BillDate)	Then
29:	Done	= True
30:	Else
31:	RNum	= RNum	+ 1
32:	With	Bill
33:	Cells(RNum, 1).Value = .BillDate
34:	Cells(RNum, 2).Value = .Cost
354
Неделя 2
35:	Cells(RNum, 3).Value = .Cbm
36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87:	Cells(RNum, 4).Value = .Kwh End With End If Loop MsgBox prompt:="Ввод данных закончен. Создано ” & RNum & " записей.", Title:=BillTitle End Sub Function Get_UtilityItem() As UtilityBill 'Получает от пользователя все данные для одной записи 'и возвращает значение типа UtilityBill. Dim Item As UtilityBill Dim Tmp As Variant With Item Do	'ввод даты Tmp = InputBox(prompt:="Введите дату " & "учета:", Title:=BillTitle7 If Tmp = "" Then	'если ввод отменен Tmp = MsgBox(prompt:="Закончить ввод данных?", Title:=BillTitle, Buttons:=vbQuestion + vbYesNo) If Tmp = vbYes Then	'возвращаем значение Null .BillDate = Null Get_UtilityItem = Item Exit Function End If End If Loop Until IsDate(Tmp) .BillDate = CDate(Tmp) 'запоминаем дату в поле даты Do	'ввод стоимости расхода Tmp = InputBox(prompt:=”BBeflHTe общую стоимость " & "израсходованной энергии:", Title:=BillTitle) If Tmp = "" Then	'нельзя прерывать MsgBox prompt:="BH должны ввести стоимость!", Title:=BillTitle, Buttons:=vbExclamation End If Loop Until IsNumeric(Tmp) .Cost = CCur(Tmp)	'запоминаем стоимость Do	'ввод расхода газа Tmp = InputBox(prompt:="BBeflHTe расход газа " S "в кубометрах:", Title:=BillTitle) If Tmp = "" Then MsgBox prompt:="Вы должны указать расход газа!", Title:=BillTitle, Buttons:=vbExclamation End If Loop Until IsNumeric(Tmp) .Cbm = Cint(Tmp)	'запоминаем расход
День 11-й. Типы данных и классы объектов...
355
88:
89:	Do	'ввод киловатт-часов
90:	Tmp = InputBox(prompt:="BBeflHTe расход " &
91:	“электроэнергии:", Title:=BillTitle)
92:	If Tmp = "" Then
93:	MsgBox prompt:=,'Bn должны указать расход электроэнергии!’1,
94:	Title:=BillTitle, Buttons:=vbExclamation
95:	End If
96:	Loop Until IsNumeric(Tmp)
97:	.Kwh = Clnt(Tmp)	'запоминаем КВЧ
98:	End With
99:	GetJJtilityltem = Item 'возвращаем запись
100:	End Function
В строках листинга 1-12 находится область описаний модуля. В строке 1 — директива Option Explicit, которая означает, что все переменные модуля должны быть описаны явно. В строке 3 описана строковая константа уровня модуля, в которой будет находиться текст заголовка всех окон, выводимых процедурой или функцией.
В строках 5-10 находится определение нестандартного типа. Именем этого типа является UtilityBill, а содержатся в нем такие элементы: BillDate (типа Variant), Cost (типа Currency), Cbm (типа Integer) и Kwh (типа Integer). Типом элемента BillDate был назначен Variant, а не Date, чтобы ему можно было присвоить значение Null, если в переменной типа UtilityBill в этом элементе не будет данных. Ключевые слова End With в строке 10 завершают определение типа UtilityBill.
В строках 13-42 находится процедура Enter_UtilityCosts. Ее описание дано в строке 13, а комментарии, объясняющие работу процедуры, — в строках 14-17.
В сроках 18-20 описаны переменные, используемые этой процедурой. Переменная Bill имеет определенный пользователем тип UtilityBill. Done — это переменная типа Boolean, которая используется для контроля выполнения цикла, a RNum — переменная типа Integer, в которой сохраняется номер строки листа Excel, в которой нужно поместить информацию счета.
В строке 22 используется метод Select коллекции Worksheets, чтобы выбрать лист с именем Потребление энергии. В этом листе будет храниться составленный процедурой счет.
ПРИМЕЧАНИЕ
Чтобы не было проблем с работой программы из листинга 11.1, в вашей книге должен находиться лист с именем Потребление энергии.
Настоящая работа процедуры Enter_UtilityCosts выполняется в строках с 24 по 39. В строке 24 переменной RNum присваивается значение 0, так как данные еще не вводились. В строке 25 переменной Done присваивается значение False, чтобы начал выполняться цикл, находящийся в строках с 26 по 39-
В строке 26 начинается цикл Do Until, который выполняется до тех пор, пока значением переменной Done не станет True. (В процедуре Enter_UtilityCosts используется цикл, потому что пользователю может понадобиться ввести несколько счетов; используя цикл Do, контролируемый флагом, эта процедура может собирать данные по любому количеству таких счетов.)
Обратите особое внимание на строку 27. В ней вы видите выражение, где переменной Bill прямо присваивается результат функции GetJJtilityltem. В строке 45 ре
356
Неделя 2
зультат этой функции описан как имеющий нестандартный тип UtilityBill, который также определен для переменной Bill. (Более подробно о работе функции GetJJtilityltem мы поговорим чуть позже.)
В строках 28-30 находится инструкция If.. .Then...Else. В ней в зависимости от того, равен ли у Bill элемент BillDate значению Null, выбирается, какую ветвь с инструкциями надо выполнять. Если равен, то выполняется строка 29, где переменной Done просто присваивается значение True, что является сигналом к завершению цикла.
Если у Bill элемент BillDate не равен значению Null, то выполняются инструкции в строках 31-37. Инструкция в строке 31 увеличивает значение RNum на 1, чтобы был выполнен переход на следующую строку листа Excel. В строке 32 начинается инструкция with, относящаяся к переменной Bill. Эта инструкция была введена, чтобы облегчить чтение и запись нескольких следующих за ней строк.
В каждой из строк 33-36 один из элементов переменной Bill сохраняется в определенной ячейке листа. Для возврата ссылки на ячейку активного листа здесь используется метод Cells объекта Application. Аргументами этого метода являются, во-первых, координата строки, а, во-вторых, координата столбца. В строке 33 содержимое элемента BillDate сохраняется в первом столбце строки листа, имеющей номер RNum. В строке 34 содержимое элемента Cost сохраняется во втором столбце той же строки листа. Аналогично, в строке 35 содержимое элемента Cbm сохраняется в третьем столбце, а в строке 36 содержимое элемента Kwh сохраняется в четвертом столбце листа. Ключевые слова End With в строке 37 завершают инструкцию With.
В строке 39 находится ключевое слово Loop, которое обозначает конец цикла. Когда VBA выполняет эту строку, происходит переход в начало цикла, а затем, перед тем как повторять выполнение инструкций тела цикла, выполняется проверка условия определителя. Когда выполнение цикла завершается, выполняется переход в строку 40, где с помощью MsgBox выводится сообщение о том, что операция ввода данных завершена, и о том, сколько введено записей (на основе значения счетчика строк из RNum).
В строке 42 находятся ключевые слова End Sub, которые завершают процедуру Enter_UtilityCosts.
Теперь обратимся к работе функции Get_UtilityItem. Ее определение находится в строках 45-100.
В строке 45 находится описание функции. У нее нет аргументов, но обратите внимание на тип возвращаемого ею значения: это определяемый пользователем тип UtilityBill.
Хотя эта функция может показаться длинной, работает она достаточно просто. В строках 49 и 50 описаны используемые в ней переменные. У переменной Item нестандартный тип UtilityBill, и она используется для хранения данных, получаемых от пользователя, пока функция не будет готова вернуть свой результат. У переменной Ттр тип Variant, и она служит для временного хранения данных, полученных от пользователя с помощью функции InputBox.
В строке 52 начинается инструкция with, в которой находится почти все тело GetJJtilityltem, так как многие инструкции этой функции обращаются к элементам переменной Item.
В строках с 53 по 66 выполняется цикл Do...Loop Until, который повторяет свое выполнение до тех пор, пока пользователь не введет правильную дату. В строке 54 результат вызова функции InputBox присваивается переменной Ттр. В строках с 56 по 65 находится два вложенных цикла, которые проверяют введенное пользователем значение из строки 54.
Если пользователь введет вместо даты пустую строку или отменит ввод, то выполняются строки с 57 по 64. Строка 57 хранит маленький секрет. Чтобы не описывать
День 11-й. Типы данных и классы объектов...
357
переменных больше, чем абсолютно необходимо, в этой инструкции переменная Tmp работает “по совместительству”. К моменту выполнения этой инструкции значение, хранившееся в Tmp, уже было использовано (строка 56) и больше не нужно. Поэтому вполне безопасно снова использовать эту переменную для хранения значения, на этот раз возвращаемого в строке 57 функцией MsgBox. Эта функция спрашивает пользователя, надо ли закончить ввод данных, и использует аргумент Buttons (кнопки) для задания кнопок Yes/No (Да/Нет) и стандартного значка системы Windows в виде вопросительного знака.
В строке 60 находится другая инструкция If...Then, в которой проверяется ответ пользователя на сообщение, выведенное при выполнении строки 57. Если пользователь ответил yes (да), т.е. подтвердил, что надо закончить ввод данных, то выполняются строки 61-63. В строке 61 элементу BillDate переменной Item присваивается значение Null (помните, что это часть функции, находящаяся внутри инструкции with). С помощью этого присвоения можно будет определить, содержит ли результат функции допустимые данные. В строке 62 затем выполняется присвоение результата функции, а в строке 63 происходит выход из функции.
Если пользователь не ввел пустую строку или не отменил окно ввода, или на предложение завершить ввод данных ответил по (нет), то выполнение продолжается со строки 66, где находится конец цикла Do вместе с его условием определителя. Если введенное пользователем значение, находящееся в Tmp, не является допустимым или его нельзя преобразовать в дату, цикл выполняется снова.
Чтобы этот пример функции был более коротким и менее сложным, в нем отсутствуют инструкции, которые сообщают пользователю, почему снова выполняется цикл. Если вы действительно хотите написать похожую программу и предоставить ее в пользование других, в ней такие инструкции необходимы (см. предыдущие примеры). Пользователи должны знать, почему цикл повторяется (не введена правильная дата).
Как только правильная дата введена, выполняется строка 67, где хранящаяся в Tmp строка (полученная от пользователя) преобразуется в Date и присваивается элементу BillDate переменной Item. (Вспомните еще раз, что это часть функции, находящаяся внутри инструкции With.)
В строках 69-76 находится инструкция Do.. .Loop Until, которая повторяет свое выполнение до тех пор, пока пользователь не введет числовое значение для итоговой суммы счета. Обратите внимание, что в строках 72-75 при отмене пользователем окна ввода или вводе пустой строки функция отказывается отменить ввод данных и просто выводит сообщение, где настаивает, чтобы пользователь все же ввел сумму.
Как только пользователь введет числовое значение, выполняется строка 77, где хранящаяся в Tmp строка (полученная от пользователя) преобразуется в Currency и присваивается элементу Cost переменной Item.
Теперь в строках 79-86 принимается количество потребленного газа. И снова этот цикл отказывается разрешить пользователю отменить в этом месте операцию ввода и настаивает, чтобы пользователь указал какое-либо количество. Как только числовое значение введено, цикл завершается и выполняется строка 87, где полученное от пользователя значение (типа String) преобразуется в Integer и присваивается элементу Cbm переменной Item.
Наконец, в строках 89-96 принимается значение потребленных киловатт-часов, а в строке 97 полученное от пользователя значение (типа string) преобразуется в Integer и присваивается элементу Kwh переменной Item.
Теперь, когда функция получила от пользователя данные для всех элементов переменной Item, в строке 98 ключевые слова End With завершают инструкцию With.
В строке 99 переменная Item присваивается результату функции GetJJtilityltem, которая завершается, возвращая результат.
358
Неделя 2
Если вы попробуете собрать все эти данные, не используя определенного пользователем типа, то потребуется описывать и отслеживать четыре отдельные переменные. Вам, возможно, потребовалось бы включить все инструкции для получения от пользователя данных в процедуру Enter_UtilityCosts, которая стала бы очень длинной и скорее всего очень трудной для понимания из-за того, что в ней содержалось бы пять вложенных циклических структур. Другим способом было бы написание четырех отдельных функций, чтобы получить каждое отдельное значение. Объединив все четыре значения в одном нестандартном типе, для работы со всеми данными можно использовать одну функцию, что значительно упрощает процедуру EnterJJtilityCosts.
Функция GetJJtilityltem также упрощена, потому что ее инструкции заняты только получением данных и не выполняют их сохранения, а также не занимаются полным прекращением ввода; ей необходимо проверять, чтобы соответствующие элементы нестандартного типа получили корректные значения.
Так надо
Чтобы упростить получение, хранение и обработку связанных данных, используйте определяемые пользовате-Мем:^пыЖ1йДКДД||Д||И[||||1Д
Помните, что все определения нестандартных типов должны быть заданы на уровне модуля в области его опи-
Помните, что переменные и константы, заданные на уровне модуля, имеют соответствующую видимость и доступны всем процедурам и функциям этого модуля.
Помните, что для преобразования значения в VBA-тип Date можно всегда использовать функцию CDate.
Не забывайте при использовании переменной нестандартного типа ставить точечный разделитель (.) перед именами ее элементов.
Не забывайте, что Excel может неправильно сохранить значения даты из ваших переменных, у которых не будет явно задан тип Date.
Создание новых классов объектов
Как говорилось ранее в этой книге, вы можете создавать в VBA свои собственные классы объектов. Для этого используются три основных приема.
•	Добавление программного текста в модуль, относящийся к такому объекту приложения Excel, как книга или лист. Используйте этот прием, если требуется создать шаблон, расширяющий возможности первоначального объекта. Например, требуется создать лист, который в случае своей активизации запускает программу ввода данных. Для этого в модуль листа следует добавить необходимые инструкции, создав таким образом из листа новый объект с модифицированными свойствами и методами. Более подробно создание класса такого типа описано при изложении материала 19-го урока.
•	Добавление исходного текста в модуль, относящийся к объекту VBA Form. Как рассказывалось в главе 16, формы VBA могут в ваших программах использоваться для создания диалоговых окон. Добавив в объект формы текст программы, можно создать объект с нужными свойствами и методами. Целое приложение VBA можно представить как один или несколько таких объектов.
День 11-Й. Типы данных и классы объектов...
359
•	Добавление в проект модуля класса с последующим определением методов и свойств. Создавайте с помощью этого модуля новые классы объектов, если вам нужен специальный объект, не являющийся существующим объектом приложения Excel, таким, например, как книга.
Создание нестандартных классов на основе объектов из книг и листов описано в уроке 19-го дня, а применение форм VBA — в уроке 16-го дня. В ходе этого урока рассказывается, как создавать классы объектов, добавляя в проект модуль класса. В последующих разделах, во-первых, дается представление о том, что такое модуль класса, затем описываются основные принципы проектирования класса и, наконец, показывается, как добавить модуль класса, а также приводится пример определенного пользователем класса.
ПРИМЕЧАНИЕ
Создание новых классов - это сложный вопрос программирования. Поскольку вам необходимо понимать хотя бы элементы программирования модулей классов, то в этой книге и говорится об основах создания нового класса с помощью его модуля. Полностью все принципы проектирования и способы программирования новых классов в этой книге, к сожалению, не рассматриваются.
Что такое модули классов
Модуль класса — это особый вид стандартного модуля, применявшегося в этой книге до сих пор. В проекте каждый модуль класса представляет отдельный класс объектов. В таком модуле содержатся переменные и инструкции VBA, которые описывают класс. Класс можно определить только в его модуле; каждый модуль может определять только один класс.
Чтобы создать новый класс, вначале следует добавить в проект его модуль, а затем написать инструкции VBA, которые определяют свойства и методы класса (о чем рассказывается в этом уроке чуть позднее). В сущности, модуль класса выполняет ту же роль, что и ключевое слово Туре в определяемом пользователем типе: он создает в VBA новое определение объекта.
В модуле класса инструкции выполняются не так, как в стандартном модуле. На этот раз вы описываете переменную объекта, тип данных которой соответствует вашему нестандартному классу, а затем используете в инструкциях стандартного модуля методы и свойства этого объекта — точно так, как и объекта, являющегося частью Excel (или другого главного приложения VBA). Применение нестандартного класса и нестандартного типа данных в чем-то схоже: описывается переменная соответствующего типа, которая затем используется в инструкциях VBA.
В каждом модуле класса могут находиться переменные для хранения внутренних данных объекта, специальные процедуры для получения или задания значений объектных свойств, а также процедуры и функции, входящие в состав методов объекта. В следующем разделе рассказывается об основных принципах проектирования объектного класса.
Проектирование класса объектов
Перед тем как добавлять в проект модуль класса, необходимо тщательно проработать структуру объекта. При проектировании класса необходимо принять во внимание следующие главные требования.
•	Вся нужная объекту информация должна храниться внутри самого объекта.
Обычно для хранения информации, представляющей объект, в модуле класса
360
Неделя 2
описывается несколько переменных уровня модуля. Кроме того, для хранения данных, помогающих описывать объект, в его методах можно использовать статические переменные.
•	Все инструкции, нужные для обработки данных объекта или влияющие на его поведение, должны быть частью самого объекта. Это значит, что для модуля класса надо писать процедуры, которые станут методами объекта.
•	Все инструкции, нужные для вычисления значений на основе данных объекта, должны быть частью самого объекта. Это значит, что для модуля класса надо писать функции, которые также станут методами объекта.
Все эти требования к структуре объекта исходят из основной концепции объектно-ориентированного программирования: программный объект должен быть полным и самодостаточным, как объекты в реальном мире вокруг нас. Программный объект всегда должен обладать свойством инкапсуляции, т.е. данные и методы, определяющие его поведение, должны составлять единое целое.
Так надо
Внешние данные, необходимые для методов объекта, передавайте в виде аргументов метода.
Так не надо
В ваших методах не должно быть инструкций, которые обращаются к переменным, находящимся вне модуля класса. В таком случае данные объекта не будут должным образом инкапсулированы.
Не пытайтесь в стандартном модуле писать процедуры или функции, которые могут обрабатывать переменные внутренних данных объекта. Кроме того, что не будет доступа к этим переменным, значения внутренних данных объекта должны меняться только с помощью его методов и процедур свойств.
Класс объектов проектируйте в такой последовательности.
1.	Определите данные, требуемые для полного представления объекта. Это обычно легче сказать, чем сделать. На этом этапе необходимо попытаться определить ту информацию, которая полностью описывает объект. Например, такой основной информацией для круга является положение его центра и радиус. Вся остальные данные о круге: диаметр, окружность, его площадь и положение точки на окружности — можно получить из этой основной информации. Если ваш крут представляет собой выводимый на экран графический объект, то он, кроме того, может содержать информацию о толщине линии, с помощью которой он чертится, об ее цвете, о цвете, которым сам закрашен (если только закрашен): он может содержать и другие данные, нужные для его вывода.
2.	После того как определена основная информация, описывающая создаваемый объект, необходимо разделить эти данные на две категории. Во-первых, определите значения, которые может понадобиться читать или изменять; они будут свойствами объекта. Во-вторых, определите те значения, которые — при всем том, что они важны для описания объекта — не требуется читать или изменять программами, находящимися вне объекта. Хотя необходимо описывать переменные для всех основных данных объекта, процедуры свойств следует писать только для тех значений, которые надо читать или изменять извне объекта.
3.	Определите, какие из свойств могут быть для чтения/записи, а какие — только для чтения. У свойств первого вида значение можно менять. А свойства, значения которых можно только читать, а изменить нельзя, будут свойствами только
День 11-Й. Типы данных и классы объектов...
361
для чтения. Например, при создании абстрактного представления круга вам нужно будет как устанавливать, так и получать координаты центра и длину радиуса. Поэтому необходимо создать три свойства для чтения/записи: координаты центра X и Y, а также величину радиуса.
4.	Составьте список действий, которые ваш объект может выполнять со своими данными. На этом этапе вы определяете нужное поведение своего объекта. Каждое действие из этого списка будет методом объекта. Для каждого из этих методов, который выполняет определенное действие, вы напишете процедуру Sub. А для метода, который считывает и возвращает значение, следует написать процедуру Function.
Если вы знакомы с проектированием баз данных, то, возможно, заметили сходство первых этапов проектирования как объектного класса, так и записи базы данных: в обоих случаях целью является определение того минимума информации, который требуется для создания единой совокупности, соответствующей определенному объекту.
Чтобы объяснить, как объекты группируются в классы, во время 7-го занятия был приведен пример различных типов обогревателей. Сейчас мы разовьем эту тему дальше: абстрактное представление электрообогревателя послужит в качестве образца проектирования, создания и применения нестандартного объектного класса.
Для простоты предположим, что основная информация, необходимая для описания любого электрообогревателя, состоит в действительности из трех частей: включен ли наш обогреватель, какая у него мощность и в каком режиме он работает. Поэтому эти данные должны храниться внутри объекта. Если методы объекта выводят на экран диалоговые окна, то было бы полезным описать константы, которые хранят общий заголовок для этих окон. В табл. 11.1 перечислены все данные, необходимые, скажем, непосредственно для самого обогревателя. В каждой из переменных есть префикс eh, который указывает на ее внутренний характер по отношению к объекту (eh означает electric heater — электрообогреватель).
Таблица. 11.1. Внутренние данные объекта электрообогревателя
Имя	Тип	Описание
ehActive	Boolean-переменная	Указывает, включен или выключен обогреватель
ehThermostat	Single -переменная	Текущее значение потребляемой мощности, хранящееся как десятичная дробь, представляющая процент от максимальной мощности (1 — это 100 %)
ehTitle	String -константа	Константа, в которой хранится заголовок для всех окон ввода или сообщений, выводимых этим объектом
ehWattage	Integer -переменная	Максимальная мощность обогревателя
Чтобы у вас была информация о состоянии обогревателя, необходимо иметь возможность считывать значения, указывающие, включен ли он, его мощность и текущий режим. Поэтому все эти три показателя необходимо определить как свойства объекта обогревателя.
Кроме того, что надо считывать значение, указывающее, включен обогреватель или выключен, это значение необходимо еще и менять, если прибор требуется включить
362
Неделя 2
(или выключить). Поэтому свойство, указывающее, включен или выключен обогреватель, должно быть свойством для чтения/записи.
Аналогичным образом требуется как изменять, так и просто читать значение мощности обогревателя; таким образом, мощность тоже является свойством для чтения/записи. Было решено, что одной из особенностей обогревателя будет то, что новое значение режима задает пользователь; в результате текущее значение режима является свойством только для чтения. В табл. 11.2 перечислены свойства объекта электрообогревателя с указанием для каждого из них, является ли оно свойством для чтения/записи или только для чтения.
Таблица 11.2. Свойства объекта электрообогревателя
Свойство	Тип данных	Чтение/запись	Описание
Active	Boolean	Чтение/запись	Если его значение True, то обогреватель включен, а если False, то выключен. Для включения обогревателя присвойте этому свойству значение True, для выключения — False
Setting	Single	Только чтение	Это свойство возвращает текущее значение режима в виде процента (0-100) от максимума
Watts	Integer	Чтение/запись	Это свойство возвращает или устанавливает мощность обогревателя
И наконец, объект электрообогревателя должен выполнять несколько простых действий. Во-первых, ему надо получать от пользователя новое значение режима. Во-вторых, он должен возвращать число, указывающее количество потребляемых обогревателем киловатт. В-третьих, обогреватель должен выводить на экран окно сообщения, где говорится о его текущем состоянии. В табл. 11.3 перечислены методы объекта электрообогревателя с указанием для каждого из них, следует ли его создавать как процедуру Sub или Function.
Таблица 11.3. Методы объекта электрообогревателя
Метод	Реализовать в виде	Описание
SetThermostat	Sub	Задает значение режима, запрашивая его у пользователя как число от 1 до 100
EnergyUsed	Function	Возвращает число типа Single, указывающее количество потребляемого обогревателем электричества в киловаттах (вычисляется на основе текущих значений мощности и режима)
Show	Sub	Выводит информацию об обогревателе: мощность, включен или нет, значение режима, потребляемая энергия
День 11-й. Типы данных и классы объектов...
363
ПРИМЕЧАНИЕ
Пример с объектом электрического обогревателя носит не столь уж отвлеченный характер, как может показаться на первый взгляд. Многие используемые в бизнесе программы работают с аналогичными объектами и моделируют как потребности в электричестве, обогреве и вентиляции, так и другие показатели. Такого рода объекты часто применяются в различных имитационных программах. Объект электрообогревателя был умышленно сделан очень простым. В практическом приложении такой объект может содержать метод, который для данной цены киловатт-часа электроэнергии показывает, сколько стоит час работы обогревателя при текущем значении режима. А вот другой пример. Тщательно разработанный объект электрообогревателя может также содержать метод, показывающий значение в тепловых единицах, выдаваемое обогревателем при текущих показателях мощности и режима и т.д.
Теперь, когда определены некоторые критерии для структуры объекта электрообогревателя, в следующем разделе будет показано, как создавать настоящее определение объектного класса.
Создание определяемого пользователем класса
Класс объектов создавайте в такой последовательности.
1.	Добавьте в свой проект модуль класса и переименуйте его так, чтобы у него было подходящее имя. Например, модуль класса для объекта электрообогревателя будет называться ElectricHeater.
2.	В области описаний модуля класса опишите внутренние переменные объекта.
3.	Напишите процедуры свойств объекта. Для каждого свойства, значение которого требуется считывать, следует создавать процедуру Property Get. А для каждого свойства, значение которого требуется задавать, нужна процедура Property Let.
4.	Напишите определения методов объекта. Эти определения надо создавать в модуле класса в виде процедур Sub и Function.
В следующих двух разделах показано, как создать модуль класса и как добавить необходимые описания переменных, процедур и функций, нужных для определения свойств и методов объекта.
Вставка и переименование модуля класса
Чтобы вставить и переименовать модуль класса, выполните такие действия.
1.	В редакторе Visual Basic выберите Insert (Вставить), Class Module (Модуль класса). В проекте появляется новый модуль класса с именем по умолчанию Classi (если в проекте уже есть модуль с таким именем, то редактор использует имена, такие как Class2, Class3 и т.д.). Как и при добавлении стандартного модуля, редактор открывает новый модуль в окне программы, в котором можно набирать исходный текст VBA.
2.	Чтобы вывести на экран окно свойств (окно с заголовком Properties) (если его там нет), выберите команду View (Вид), Properties Window (Окно свойств) или щелкните на панели инструментов на значке Properties Window.
3.	В текстовом поле Name (Имя) окна Properties наберите новое имя объектного класса. На рис. 11.1 показано это окно для класса ElectricHeater из листинга 11.2 (см. следующий раздел этого урока).
364
Неделя 2
Ulf Properties ~ ElectrieH. ИЕВ
jEtectricHeater CiassModute At#abe«c [categorizes]
(Name)
[ElectricHeater
Instancing 1 - Private
Puc. 11.1. Переименование мо-дуля класса
ПРИМЕЧАНИЕ
Очень важно переименовать модуль класса и отнестись к этому серьезно. Данное вами имя будет именем нового класса и им придется пользоваться во всех инструкциях, предполагающих обращение к объекту этого класса. Имена для модулей классов должны соответствовать уже известным вам правилам, которыми пользуются при создании имен переменных, процедур и функций.
После того как вы создали модуль класса и дали ему имя, можно добавить переменные, константы, процедуры и функции, из которых будут состоять определения свойств и методов объекта.
Примечание
При переименовании модуля класса необходимо оставить у свойства Instancing (создание экземпляров) значение Private (личный) (см. рис. 11.1). Когда задано это значение, ваш нестандартный класс доступен только в книге, содержащей его модуль. Другим возможным значением свойства Instancing является PublicNoCreate (общий без создания экземпляров); при задании этого значения ваш нестандартный класс будет доступен в других книгах Excel, но новый экземпляр этого класса создать будет нельзя. Хотя при использовании значения Private видимость нестандартного класса будет ограничена, это дает возможность создавать столько экземпляров нестандартного объекта, сколько требуется. (О свойствах видимости public и private более подробно рассказывается на уроке 12-го дня, “Модульное программирование".)
Написание определений свойств и методов класса
Написание определений методов — это то же самое, что создание в VBA любой процедуры или функции; в обоих случаях используется одинаковый синтаксис, который вы уже знаете по работе с обычными модулями. Каждая процедура или функция, созданная в модуле класса, становится доступной как один из методов класса. (Как вы узнаете на 12-м уроке, для предотвращения доступа к такой процедуре или функции из модуля, который их не содержит, используется ключевое слово Private.)
Написание определений объектных свойств — это то же самое, что известное вам создание в VBA стандартной процедуры или функции; единственное различие в синтаксисе: процедуры Property Get дают возможность получать значение свойства, а Property Let — присваивать свойству значение.
Процедура Property Get имеет такой общий синтаксис.
Property Get name([arglist]) [As TypeName]
[statements]
День 11-й. Типы данных и классы объектов...
365
name = expression
(statements]
End Property
Ключевое слово Property (свойство) указывает, что это процедура объектного свойства, в то время как ключевое слово Get (получить) говорит о том, что эта процедура вернет значение свойства (это во многом похоже на стандартную функциональную процедуру VBA). В этом синтаксисе name — это имя процедуры свойства, а также имя самого свойства. Имена свойств должны соответствовать тем же правилам именования идентификаторов, которыми пользуются при создании имен переменных, а также обычных процедур или функций.
Необязательный arglist представляет список аргументов для процедуры свойства. У него должен быть тот же синтаксис, что и у аргументов функций, о которых говорилось на 6-м уроке. (Об аргументах процедур более подробно говорится на 12-м уроке.) TypeName представляет любой тип данных (VBA или определенный пользователем) и указывает тип данных, возвращаемых процедурой Property Get. Инструкции VBA (одна или несколько) обозначены statements.
Инструкция name = expression похожа на инструкцию присвоения функции: name — это имя процедуры Property Get и expression представляет значение, которое требуется вернуть в виде значения свойства. Ключевые слова End Property указывают на конец текста процедуры свойства. Несколько процедур Property Get находится в листинге 11.2.
Написание процедуры Property Get создает считываемое свойство объекта. Если для свойства вы напишете только процедуру Property Get, то, в сущности, будет создано свойство только для чтения. Обычно пишется пара процедур Property Get и Property Let или (если свойство является объектной ссылкой ) процедура Property Set. Создание процедуры Property Let или Property Set дает возможность задавать значение свойства.
У процедуры Property Let такой общий синтаксис.
Property Let name]{arglist,] value)
[statements]
End Property
Ключевое слово Property (свойство) также указывает, что это процедура объектного свойства, а ключевое слово Let (разрешить) говорит о том, что эта процедура присвоит свойству значение. В этом синтаксисе name — это имя процедуры свойства, а также имя самого свойства. Необязательный arglist представляет список аргументов для процедуры свойства.
Аргумент value является обязательной частью процедуры Property Let. Он представляет новое значение, которое присваивается свойству и может иметь любой тип данных VBA; это значение не может быть только объектной ссылкой, statements представляют инструкции VBA. Обычно эти инструкции проверяют аргумент value и затем сохраняют его в переменной уровня модуля в самом модуле класса. Несколько процедур Property Let представлено в листинге 11.2.
Если у вашего объекта есть свойства, которые обращаются к другим объектам, то потребуется присвоить какому-нибудь из этих свойств объектную ссылку. Для этого вместо одной инструкции, Property Let, необходимо использовать другую — Property Set.
Процедура Property Set имеет такой общий синтаксис.
Property Set name([arglist,] reference)
[statements]
End Property
366
Неделя 2
Синтаксис процедур Property Set и Property Let идентичный, за одним исключением: в Property Set вместо одного аргумента, value (значение), требуется другой — reference (ссылка), reference представляет допустимую ссылку на объект из Excel, из VBA, нестандартный объект или объект автоматизации из другого главного приложения.
ПРИМЕЧАНИЕ
Может потребоваться немедленно завершить выполнение процедуры свойства (в основном по тем же причинам, что и выполнение обычной процедуры или функции). С этой целью ключевое слово Exit можно использовать и в процедурах свойств. Его надо будет поместить в инструкции Exit Property, которая в исходном тексте займет отдельную строку.
В листинге 11.2 представлен целый модуль, в котором находятся все инструкции, необходимые для реализации объекта класса ElectricHeater со всеми свойствами, методами и внутренними данными, описанными в табл. 11.1-11.3.
Листинг 11.2. Оцрвдвлвнив нового класса в вго моддлв
1:	Option Explicit
2:
3:	Const ehTitle = "Электронагреватель"
4:
5:	Dim ehWattage As Integer	'Макс, мощность
6:	Dim ehThermostat As Single 'Режим регулятора мощности
7:	Dim ehActive As Boolean 'включен или выключен
8:
9:	Property Get Active() As Boolean
10:	Active = ehActive
11:	End Property
12:
13:	Property Let Active(Status As Boolean)
14:	ehActive = Status
15:	If Status Then Me.SetThermostat
16:	End Property
17:
18:	Property Get Setting!) As Single
19:	Setting = ehThermostat * 100
20:	End Property
21:
22:	Property Get Watts() As Integer
23:	Watts = ehWattage
24:	End Property
25:
26:	Property Let Watts(W As Integer)
27:	ehWattage = W
28:	End Property
29:
30:	Sub SetThermostat()
31:	'устанавливает режим регулятора мощности.
32:
33:	Const strErrMsg = "Число должно быть от 0 до 100"
34:
35:	Dim Temp As Variant
День 11-й. Типы данных и классы объектов...
367
36:	Dim fGoodValue As Boolean
37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87:	Do Temp = InputBox(prompt:="yKaMHie режим работы (проц, от макс.):" Title:=ehTitle, Default:=(ehThermostat * 100)) If Len(Trim(Temp)) = 0 Then Exit Sub If Not IsNumeric(Temp) Then MsgBox prompt:=strErrMsg, Title:=ehTitle, Buttons:=vbExclamation Else Temp = Int(Temp) If (Temp < 0) Or (Temp > 100) Then MsgBox prompt:=strErrMsg, Title:=ehTitle, Buttons:=vbExclamation Else Temp = CSng(Temp / 100) fGoodValue = True End If End If Loop Until fGoodValue ehThermostat = Temp End Sub Function EnergyUsed() As Single 'Возвращает потребляемую мощность в киловаттах в зависимости 'от максимальной мощности и режима работы If Not Me.Active Then EnergyUsed = 0 Else EnergyUsed = CSng((ehWattage * ehThermostat) / 1000) End If End Function Sub Show() 'Выводит информацию о данном экземпляре нагревателя: 'макс, мощность, включен или выключен, режим работы, и 'потребляемую энергию Dim strMsg As String With Me strMsg = "Нагреватель " If .Active Then strMsg = strMsg & "ВКЛЮЧЕН" Else strMsg = strMsg & "ВЫКЛЮЧЕН” End If
88:	strMsg = strMsg &	& vbCr & vbCr
368
Неделя 2
89:	strMsg	=	strMsg	&	“Макс, мощность: " & .Watts & "вт" & vbCr
90:	strMsg	=	strMsg	S	"Тек. режим: 11 & .Setting & "%" S vbCr
91:	strMsg	=	strMsg	&	"Потребляемая энергия: " & .EnergyUsed S "	Kbt"
92:
93:	MsgBox prompt:=strMsg, Title:=ehTitle, Buttons:=vblnformation
94:	End With
95:	End Sub
В строках листинга 1-7 находится область описаний модуля. В строке 1 находится директива Option Explicit, которая означает, что все переменные модуля должны быть описаны явно. В строке 3 описана строковая константа ehTitle, в которой будет находиться текст заголовка всех окон, выводимых методами этого объекта. В строках 5-7 описаны переменные, используемые для хранения внутренних данных объекта. Применение этих переменных описано в табл. 11.1.
{йдалвнЕ
По умолчанию любой нестандартный класс имеет видимость private по отношению к книге, в которой находится его модуль. Это значит, что для процедур и функций из других книг этот класс недоступен. Чтобы нестандартный класс можно было использовать в нескольких книгах, его модуль надо скопировать в каждую из них. (Видимость, как вы, возможно, помните из 3-го урока, относится к доступности переменной или объекта; о видимости public (общий) и private (личный) будет подробно рассказано на 12-м уроке.)
Поскольку константы и переменные, описанные в строках 3-7, находятся в области описаний модуля, т.е. описаны на уровне модуля, они будут доступны всем его процедурам и функциям. Хотя эти константы и переменные появятся в окне Object Browser, их видимость ограничена модулем класса, т.е. они недоступны процедурам и функциям из других модулей. (О видимости переменных более подробно рассказывается на 12-м занятии.)
Обратимся к табл. 11.2, где перечислены нужные свойства объекта ElectricHeater и указано, какое из них должно быть только для чтения, а какое — для чтения/записи. Первым значится Active, которое является свойством для чтения/записи. Чтобы создать это свойство, необходимо написать процедуру Property Get, чтобы можно было получать его значения, и процедуру Property Let, чтобы была возможность присваивать свойству значения.
В строках 9-16 находятся процедуры, необходимые для определения свойства Active. Сравните строки 9 и 13. В первой из них находится описание процедуры Property Get, в то время как во второй — описание процедуры Property Let. Обратите внимание, что имя у обеих процедур Active; это необходимо, чтобы Active было свойством и для чтения, и для записи. Ввиду того что Active является свойством типа Boolean (см. таблицу 11.2), то находящаяся в строке 9 процедура Property Get Active возвращает значение типа Boolean. В процедуре Property Let Active (строка 13) аргумент Status является обязательным; с его помощью процедуре передается новое значение свойства. Аргумент Status имеет тип Boolean, потому что должен соответствовать типу данных, возвращаемых процедурой Property Get Active.
Процедура Property Get Active просто возвращает текущее значение ehActive (строка 10), которое является внутренней переменной, где хранится текущее значение рабочего состояния обогревателя: True, если он включен, и False, если выключен. Вначале процедура Property Let Active присваивает в строке 14 переменной ehActive новое значение из аргумента Status. Затем в строке 15 проверяется, включен ли обог
День 11-й. Типы данных и классы объектов...
369
реватель (т.е. имеет ли Status значение True). Если включен, то в объекте вызывается метод SetThermostat. Это делается для того, чтобы получить значение режима, когда обогреватель включен.
Обратите внимание на ключевое слово Me (меня) в строке 15. Оно возвращает ссылку на текущий экземпляр объекта; используйте это ключевое слово, когда в исходном тексте объекта необходимо обратиться к его свойствам и методам, которые сами по себе являются его частью. При выполнении инструкции с ключевым словом Me вызывается свойство или метод текущего объекта.
Теперь посмотрите на строки 18-20, в которых дано описание процедуры Property Get Setting. Setting является свойством только для чтения, поэтому нужна лишь процедура Get. Свойство Setting должно возвращать текущий процент от максимальной мощности обогревателя. Этот процент представлен десятичным числом типа Single, которое хранится в переменной ehThermostat: 1 соответствует 100 %, 0.5 — 50 %, 0.1 — 10% и т.д. Поэтому в строке 19 ehThermostat умножается на 100 и результат возвращается в виде значения свойства Setting. Обратите внимание, что результат процедуры Property Get Setting описан как имеющий тип Single.
В строках 22-28 находятся процедуры Property Get Watts и Property Let Watts; эти две процедуры создают свойство Watts. Сравните их определения (соответственно строки 22 и 26) и обратите внимание, что у обеих процедур одно и то же имя, что процедура Get возвращает значение типа Integer, а аргумент процедуры Let также типа Integer. Процедуры соответствуют друг другу по имени и типу данных, что дает им возможность создать свойство для чтения/записи Watts со значением типа Integer. Процедура Property Get Watts просто возвращает значение, хранящееся в переменной ehWattage, а процедура Property Let Watts так же просто присваивает ehWattage новое значение мощности.
В строках 30-60 находится процедура SetThermostat; с ее помощью создается метод SetThermostat объекта ElectricHeater (см. табл. 11.3). Этот метод выполняется в виде цикла, пока пользователь не введет в качестве нового значения режима число в пределах от 0 до 100. Когда число из этого диапазона введено, то оно делится на 100, а затем полученное десятичное число типа Single присваивается переменной ehThermostat.
В строках 62-71 находится функция EnergyUsed; с ее помощью создается метод EnergyUsed объекта ElectricHeater. Этот метод должен возвращать число, показывающее количество киловатт, потребляемых обогревателем; поэтому для того чтобы можно было возвращать результат, метод и написан в виде функции. Вначале EnergyUsed проверяет, включен ли обогреватель. Обратите внимание на выражение Me.Active в строке 66, в котором для определения того, включен ли обогреватель, используется значение, возвращаемое Active, которое является собственным свойством объекта ElectricHeater. Если обогреватель не включен, то метод EnergyUsed просто возвращает 0 (строка 67). В противном случае в строке 67 вычисляются потребляемые прибором киловатты, а затем полученное значение возвращается в виде результата метода.
Наконец, в строках 73-95 находится метод Show. Этот метод выводит окно сообщения с информацией о текущем состоянии объекта: включен обогреватель или нет, какова его текущая мощность, какое значение режима и сколько потреблено энергии. Обратите внимание, что в строке 80 начинается инструкция With Me; также не упустите из виду, что вся информация, выведенная на экран методом Show, получена с помощью вызова собственных свойств и методов объекта ElectricHeater.
370
Неделя 2
Так надо
Убедитесь, что имена процедур Property Get и Property Let в точности совпадают; обе процедуры нужны для создания свойства для чтения/записи.
Проверяйте, чтобы у значения, возвращенного процедурой Property Get, был тот же тип данных, что и у значения, передаваемого процедуре Property Let; эти типы данных должны совпадать, иначе УВД выдаст сообщение об ошибке компиляции.
Если в исходном тексте метода объекта необходимо обращаться к методам того же объекта, пользуйтесь ключевым словом Me.
Не забывайте, что свойство становится доступным только для чтения тогда, когда написана только процедура Property Get. При желании можете создать свойство, доступное только для записи, — для этого нужно написать лишь процедуру Property Let.
После того как свойства и методы добавлены в класс объектов, они появляются в окне Object Browser. На рис. 11.2 показано окно Object Browser с классом ElectricHeater, выбранным в списке Classes, и методом Setting, выбранным в списке Members of 'ElectricHeater'.
Object Browser
ГоауЙ	3 «| | М3 -11
Members of 'ElectricHeater1
j Classes
® <globais>
ElectricHeater
Exerclse02
•St ListingOI Listing03
M MailAddress Modulel Sheet2
<3 Sheets d Thing S3 ThisWorkbook
UtilityBill
OS' Active
I’S? ehActive
Й1 ehThermostat ehTitle
Й? ehWattage EnergyUsed
i* SetThermostat
3S1 Setting
* Show
Й? Watts
j Private Cass ElectricHeater
Member of
Puc. 11.2. Модули ваших классов находятся в списке Classes окна Object Browser; методы и свойства вашего объекта находятся в списке Members of <имя класса>
День 11-Й. Типы данных и классы объектов...
371
Применение нестандартного класса
Объекты нестандартных классов используются точно так же, как и объекты Excel или любого другого главного приложения VBA, но с одним исключением. Перед тем как применять нестандартный класс объекта, необходимо с помощью ключевого слова New создать новый экземпляр этого объекта. {Экземпляр, как вы, возможно, помните из 7-го урока, — это конкретное воплощение объекта.)
Ключевое слово New можно использовать как часть инструкций Dim и Set:
Dim objvar As New ClassName
Set objvar = New ClassName
В синтаксисе первой инструкции objvar — это любой допустимый идентификатор VBA, a ClassName — это имя класса, из которого требуется создать новый экземпляр объекта. ClassName представляет имя нестандартного класса; оно точно такое же, как и у модуля этого класса. Вот пример инструкции, в которой создается новый экземпляр класса ElectricHeater.
Dim objHeater As New ElectricHeater
Когда выполняется эта инструкция, создается новый экземпляр объекта ElectricHeater и инициализируется переменная objHeater, которой присваивается ссылка на этот экземпляр.
В синтаксисе второй инструкции objvar представляет уже описанную объектную переменную, objvar может быть переменной типа Variant, общего типа Object или объектной переменной с типом, заданным некоторым классом. ClassName представляет имя нестандартного класса; оно точно такое же, как и у модуля этого класса. Вот пример, где в первой инструкции описана объектная переменная, а во второй — создается новый экземпляр класса ElectricHeater.
Dim objHeater As Object
Set objHeater = New ElectricHeater
Когда выполняется первая инструкция, создается переменная общего типа Object. При выполнении второй инструкции создается новый экземпляр объекта ElectricHeater и инициализируется переменная objHeater, которой присваивается ссылка на этот новый экземпляр.
В листинге представлена простая процедура, в которой показаны свойства и методы класса ElectricHeater. Процедура Test_Heater создает новый экземпляр этого объекта, присваивает значения его свойствам и применяет его методы.
Процедуру Testjieater необходимо ввести в стандартном модуле той же книги, где находится модуль класса ElectricHeater из листинга 11.2. Листинг 11.3 не будет работать, если вы введете его текст в тот же модуль, который определяет класс ElectricHeater.
Листинг 11.3. Использование объекта нестандартного класса
1:	Option Explicit
2:
3:	Sub Test_Heater()
4:
5:	Dim objHeater As ElectricHeater
6:
372
Неделя 2
7:	Set objHeater = New ElectricHeater
8:
9:	objHeater.Watts = InputBox(prompt:="Укажите макс, мощность (вт):")
10:	objHeater.Show
12:	With objHeater
13:	.Active = True 'включаем нагреватель
14:	MsgBox "Потребляемая мощность: " & .EnergyUsed & " Квт"
15:
16:	.Show
17:	.Active = False 'выключаем нагреватель
18:	.Show
19:
20:	.Active	= True 'включаем нагреватель
21:	.Show
22:
23:	.SetThermostat
24:	.Show
25:
26:	.SetThermostat
27:	.Show
28:
29:	.Active	=	False
30:	.Show
31:	End With
32:	End Sub
В строке 5 листинга 11.3 описана переменная objHeater с типом данных ElectricHeater. В строке 7 для создания нового экземпляра класса ElectricHeater используется инструкция Set с ключевым словом New; в этой же строке переменной objHeater присваивается ссылка на новый экземпляр объекта.
В строке 9 для задания мощности обогревателя применяется метод Watts объекта ElectricHeater; значение принимается от пользователя с помощью InputBox. В строке 10 в том же объекте вызывается метод Show, чтобы показать на экране текущее состояние прибора. В обеих строках (9 и 10) обратите внимание на использование с переменной objHeater точечного разделителя (.), когда выполняется обращение к методам и свойствам объекта. Этот прием аналогичен тому, который применяется при работе с методами и свойствами объектов, встроенных в Excel или другое главное приложение VBA.
В строке 12 начинается инструкция With. В строке 13 выполняется включение обогревателя. В строке 14 в инструкции MsgBox используется метод EnergyUsed, чтобы показать потребляемое обогревателем количество киловатт. В строке 16 в объекте ElectricHeater снова вызывается метод Show, а в строке 17 выполняется выключение обогревателя с помощью присвоения свойству Active значения False. В строке 23 для получения от пользователя нового значения режима применяется метод SetThermostat.
Так надо
Точечный разделитель (.) используйте с методами и свойствами нестандартного класса тем же способом, что и с любым встроенным в VBA объектом.
Для упрощения ссылок на свойства и методы нестандартного объекта используйте инструкцию With тем же способом, что и с любым встроенным в VBA объектом.
День 11-й. Типы данных и классы объектов...
373
Резюме
Сегодня вы изучили, как определять собственные нестандартные типы данных. Теперь вы знаете, как создавать определения этих типов, как описывать переменные нестандартного типа и как получить доступ к отдельным элементам таких переменных. Кроме того, вы увидели пример использования типов данных, определяемых пользователем.
Расширив понятие нестандартного типа данных на нестандартные объекты, мы показали, как определять собственные классы объектов. Вы узнали, как добавлять модуль класса в проект, изучили основные приемы проектирования таких классов, а также научились определять свойства и методы класса. И, наконец, вы узнали, как применять свой класс объектов в исходном тексте на VBA.
Вопросы о ответы
Почему нельзя сохранять переменную нестандартного типа в ячейке Excel или в другой переменной?
Присвоить содержимое переменной нестандартного типа другой переменной можно лишь в том случае, если у них один и тот же тип данных. Переменную нестандартного типа нельзя прямо присвоить ячейке Excel или другой переменной, потому что этот тип данных комбинирует в себе данные разных типов. Переменные нестандартного типа более удобны для хранения и передачи сразу нескольких данных, но в этих переменных необходимо обращаться к конкретным элементам, чтобы сохранять эти элементы в ячейках Excel или другой переменной
Думаю, мне понятно, как создавать новый класс объектов, но для чего это нужно?
Главным преимущество заключается в том, что данные и методы для их обработки будут инкапсулированы. Например, у вас есть ряд процедур для создания диаграммы продаж на основе данных из листа Excel. Если эти процедуры переписать, чтобы в результате можно было создавать объект диаграммы продаж, то для создания такой диаграммы будет легче использовать ваш исходный текст VBA. Нестандартные объекты полезны для любой группы данных, которые можно моделировать как объект со своими свойствами и методами
Если мой нестандартный класс доступен только в книге, в которой находится его модуль, то как этот класс использовать в нескольких книгах?
Чтобы использовать нестандартный класс в нескольких книгах, его модуль необходимо скопировать в каждую из этих книг. Самым легким способом представляется экспорт модуля класса в текстовый файл с помощью команды редактора Visual Basic File-Export File. Затем с помощью команды File-Import File того же редактора Visual Basic можно легко добавить модуль класса в другую книгу
374
Неделя 2
Коллоквиум
Ответы в приложении.
Тест
1.	Что такое определенный пользователем тип?
2.	С какого ключевого слова VBA начинается определение нестандартного типа?
3.	Где должно находиться определение нестандартного типа?
4.	Как создавать нестандартный класс?
5.	Как определять свойства класса?
6.	Что именно надо делать для создания в нестандартном объекте свойства для чтения/записи?
7.	Как определять методы класса?
8.	Почему любой новый модуль класса надо сразу переименовывать?
9.	Можно ли из другой книги получить доступ к объекту, определенному в модуле класса?
10.	Можно ли использовать окно Object Browser для просмотра доступных методов и свойств нестандартного объекта?
Упражнения
1. Опишите нестандартный тип данных для хранения информации, необходимой для списка адресов. В этом типе должны быть элементы для следующей информации: имени, фамилии, названия компании, улицы с номером дома, горо да, штата и ZIP-кода.
2. Напишите функцию, которая получает данные из списка адресов типа данных, созданного в упражнении 1, а также процедуру, которая вызывает эту функцию и сохраняет данные в листе Excel. Результат функции должен быть нестандартного типа. (Подсказка. Скопируйте из листинга 11.1 ту часть процедуры Enter_UtilityCosts, которая выбирает лист и сохраняет в нем данные.)
День 11-й. Типы данных и классы объектов...
375
Модульное программирование
На этом занятии речь пойдет о том, как наладить взаимодействие процедур, записанных в нескольких разных модулях. Кроме того, мы расскажем, как писать программы, состоящие из нескольких логически связанных процедур, совместно выполняющих сложную работу. В этой главе рассмотрены следующие вопросы.
•	Как с помощью модулей собрать часто используемые процедуры и функции в библиотеку процедур для того, чтобы они всегда были вам доступны.
•	Как с помощью ключевых слов Private и Public расширить или ограничить доступ к переменным, константам и процедурам в ваших модулях.
•	Как разрабатывать и воплощать широкомасштабные программы, состоящие из нескольких процедур и функций и расположенные в нескольких модулях.
•	Как с помощью списка аргументов ваши процедуры могут получать информацию и передавать ее в вызывающие процедуры.
Эффективное применение модулей
Вам уже известно, что исходный текст процедуры или функции хранится в модуле. На первом и втором уроках рассказывалось о том, как программа записи макросов в Excel выбирает модуль для хранения записанного макроса и как она добавляет новые модули в проекты VBA, хранящиеся в книгах Excel. В этой главе вы узнаете, как использовать модули для организации совместно используемых процедур и функций и о том, как сделать их доступными в каждом сеансе работы с программой Excel.
Модули можно использовать, чтобы организовывать и более эффективно управлять макросами и процедурами. Многие пользователи организуют процедуры разных категорий в зависимости от определенных задач, затем сохраняют каждую категорию в собственном модуле, а иногда и в собственной книге.
Например, вы хотите, чтобы некоторые или все книги имели стандартный вид, и поэтому вам требуется выбрать в качестве стандартов для титулов, заголовков строк, столбцов и тому подобного определенные шрифты, их стили и размеры в пунктах. Тогда можно было бы записать различные макросы или вручную написать процедуры, такие как макрос FormatArialBoldl2, созданный в ходе урока 1-го дня обучения, которые реализуют определенные стили шрифтов или другое форматирование в заданных вами стандартах.
376
Неделя 2
Если эти процедуры используются часто и их надо сделать доступными во всех книгах (или в большинстве), то нежелательно копировать модули с такими процедурами во все эти книги. Воздержитесь от того, чтобы просто помещать часто применяемые процедуры во всех использующих их книгах. Это может вызвать много путаницы и излишне увеличит размер книг — и в смысле занимаемого на диске места и в смысле требуемой памяти.
Кроме того, если такую процедуру, скопированную в несколько книг, потребуется изменить, вам придется редактировать и отлаживать каждую копию процедуры или, как минимум, копировать измененную процедуру во все книги, где она используется. Такое сложное редактирование и копирование только увеличат вероятность ошибки, и, скорее всего, результаты будут сведены на нет.
Задачи не решает и применение шаблона. При создании на его основе новой книги программа Excel скопирует в новый файл все модули из шаблона, что приведет вас к тем же самым проблемам.
Вместо этого чтобы сделать часто используемые процедуры и функции доступными, лучше всего создать их библиотеку. Библиотека процедур и функций VBA работает почти как обычная общественная библиотека, заполненная журналами и книгами: в любой момент, когда вам (или одной из ваших процедур) требуется использовать процедуру или функцию из библиотеки, VBA отыщет там нужный текст программы и выполнит его.
При описании личной книги макросов приложения Excel (хранящейся в файле Personal.xls) на уроке 1-го дня указывалось, что любую процедуру с помощью окна Макросы можно запускать, если только открыта книга, в которой эта процедура хранится. В этом можно убедиться, просмотрев в этом окне список процедур; обратите внимание, что в списке Имя макроса всегда находятся все процедуры из открытых в этот момент книг. (В окне Макросы перечисляются только процедуры без аргументов, но не функции). На рис. 12.1 наглядно показано, что все процедуры в модулях любой открытой книги доступны для всех других открытых книг.
Рис. 12.1. Процедуры из открытой книги доступны для всех других открытых книг в том же сеансе работы Excel
Когда VBA выполняет процедуру или функцию, он вначале ищет ее в текущем модуле. Если она там не найдена, он ищет в других модулях текущего проекта (если они есть). Если поиск и в этом случае не увенчался успехом, то VBA ищет по всем книгам, на которые имеются ссылки. (О создании такого рода ссылок речь пойдет чуть ниже в этом разделе.)
День /2-й. Модульное программирование
377
(ПРИММНЕ
Хотя окно Макросы делает процедуры из открытой книги доступными любой другой книге (т.е. процедуру можно выполнять как макрос), использовать процедуру или функцию в программе на VBA нельзя, если она хранится в книге, отличной от той, в которой записана ваша программа. Чтобы такие процедуры или функции были доступны, необходимо установить ссылку на внешний проект, о чем еще будет рассказано на этом уроке.
Например, хотя процедуру ExcelVBAHelp, хранящуюся в книге Personal.xls, с помощью окна Макросы можно запустить из любой открытой книги, ее нельзя вызвать из VBA-программы из книги, отличной от Personal.xls, если нет ссылки на книгу Personal.xls (как будет рассказано на этом уроке).
Этот скрытый процесс, выполняемый VBA, является ключом к работе библиотек процедур и функций. Чтобы сделать используемую всеми процедуру доступной любой книге Excel, все, что надо сделать, — позаботиться о том, чтобы процедура находилась в любой из книг, в которых VBA ведет поиск, или в открытой книге, или в книге, на которую имеется ссылка.
Так надо
Всегда устанавливайте ссылки на книги, если вы собираетесь использовать процедуры, записанные в этих книгах, в своих программах.
Помните, что в окне Макросы видны только процедуры без аргументов.
Так не надо
Не копируйте процедуры и функции в разные книги. Создайте вместо этого библиотечную книгу, где есть одна копия, используемая в остальных книгах.
Создание библиотечной книги
Для создания в Excel общей или специальной библиотеки функций и процедур просто создайте книгу, где имеются только модули VBA. Хотя не имеет значения, есть ли в такой книге рабочие листы, включать их туда, как и диаграммы, незачем. Поскольку диаграммы и листы в библиотечной книге не используются, то они будут занимать лишнее место на диске и в памяти компьютера. Одним из примеров библиотечной книги является файл Personal.xls, описанный на первом уроке.
Как пример создания библиотеки процедур и функций, предположим, что вы создали несколько макросов и процедур для создания и форматирования диаграмм Excel. Чтобы оформить их в виде библиотеки, надо создать книгу ChartTools.xls и перенести в ее модули все нужные процедуры. Библиотека процедур создания и форматирования диаграмм готова. Единственное, о чем вы должны побеспокоиться, — это чтобы она была доступна во время сеансов работы Excel.
Хотя VBA этого не требует, большинство пользователей предпочитают внутри библиотечной книги связанные процедуры держать вместе. Например, если в книге ChartTools.xls имеется несколько процедур для работы с круговыми диаграммами, то все их можно сгруппировать в одном модуле PieCharts (круговые диаграммы). Аналогично, если имеются процедуры для гистограмм, то их можно сгруппировать в другом модуле Barcharts (гистограммы) той же книги ChartTools.xls.
Группируя вместе в одной книге все процедуры, относящиеся к диаграммам, и затем разбивая их по категориям, вы не только создаете возможность полного доступа к
378
Неделя 2
библиотеке, но и облегчаете возможный поиск процедуры, если ее надо видоизменить, а также упрощаете совместное использование книги или ее частей вместе с другими пользователями.
Так надо
Всегда собирайте общие функции (такие как FlipCase и Slen. описанные в предыдущих главах) в общую библиотечную книгу, чтобы их можно было использовать во всех процедурах.
Всегда храните все общие процедуры и функции в книге Personal.xls.
Доступ к библиотеке процедур и функции
Чтобы сделать библиотеку процедур доступной, необходимо загрузить ее книгу, чтобы имеющиеся там модули были доступны для других книг. В следующих разделах объясняется, как это сделать.
Библиотечную книгу можно открыть с помощью команды Файл-Открыть; все процедуры открытой книги доступны любой другой открытой книге того же сеанса Excel. Этот прием имеет два недостатка. Во-первых, делать это надо вручную. Это не страшно, если процедуры из библиотеки используются не каждый день, но главная цель при создании библиотеки — избежать того, чтобы для доступа к ней выполнялись особые действия. В случае самых общедоступных библиотек открывать их каждый раз, по всей вероятности, будет неудобно.
Вторым недостатком является то, что такие действия делают доступными только процедуры, да к тому же те из них, что без аргументов. Хотя с помощью окна Макросы можно выполнить любую библиотечную процедуру, к функциям это не относится, за исключением формул, находящихся на листе Excel.
Средства для преодоления этих трудностей есть. Библиотечный файл можно автоматически загружать в Excel, если поместить его в каталог автозагрузки. Несмотря на то что это поможет получить доступ к процедурам библиотеки с помощью окна Макросы, пока что ее процедуры или функции в программе, не являющейся частью того же проекта с библиотекой, использовать нельзя. Чтобы решить эту проблему, в системе предусмотрена возможность делать ссылки на библиотеку. В следующих разделах описано, как автоматически загружать библиотеки и как делать на них ссылки.
Описанные выше приемы позволяют получить доступ к функциям и процедурам, которые могут и не быть организованы в библиотеки.
Загрузка библиотека с помощью каталога автозагрузки
Самый легкий способ сделать библиотеку процедур доступной при каждом запуске Excel — это поместить библиотечную книгу в каталог автозагрузки, основной или альтернативный. При любом запуске системы в этих каталогах открываются все файлы. Как говорилось ранее, когда открыта книга библиотеки, то все ее процедуры доступны в окне Макросы.
Чтобы переместить или скопировать библиотечный файл в основной или альтернативный каталог автозагрузки, применяются обычные средства работы с файлами системы Windows, а чтобы сохранить в одном из этих каталогов копию библиотеки, используйте в Excel команду Файл-Сохранить как.
День 12-й. Модульное программирование
379
Есть еще один способ сделать так, чтобы при запуске Excel автоматически загружалась книга - нужно поместить ее ярлык в каталог автозагрузки.
Каталог автозагрузки Excel называется Xlstart. Его местоположение зависит от конкретной версии Microsoft Office и от того, использовались ли при инсталляции каталоги, предлагаемые по умолчанию. Имя и расположение каталога автозагрузки устанавливается при инсталляции Excel и не может быть изменено.
Лучший способ определить точное имя каталога автозагрузки —обратиться к свойству StartupPath объекта Application приложения Excel. В этом свойстве находится полный путь такого каталога. Его можно легко вывести на экран с помощью Application. StartupPath. Наберите листинг процедуры и выполните ее; в результате на экран будет выведен полный путь (с буквой дисковода включительно) каталога автозагрузки Excel (см. листинг 12.1).
Листинг 12.1. Поиск каталога автозагрузки с номощью VBA
1:	Sub ShowStartupPath()
2:	MsgBox Application.StartupPath
3:	End Sub
Во 2-й строке листинга с помощью функции MsgBox на экран выводится значение свойства StartupPath объекта Application. Если при инсталляции Microsoft Office 2000 использовались предлагаемые по умолчанию значения, то в этом случае появится окно как на рис. 12.2.
Microsoft Excel
C\'A1NDOWS\ApplicetionC>etei'Micrasoft\ExcelV<LSTART
[OK
Рис. 12.2. Свойство StartupPath содержит полный путь к каталогу автозагрузки
Использовать альтернативный каталог автозагрузки не обязательно. Если вы все же решите его использовать, выберите в меню Сервис-Параметры и введите его имя и полный путь поиска. В отличие от основного каталога автозагрузки альтернативный каталог можно изменить в любой момент, но на приложение Excel это воздействует только при новом запуске.
Для того чтобы через меню Excel задать альтернативный каталог автозагрузки, сделайте следующее:
1.	Выполните команду Сервис-Параметры. Появится окно Параметры.
2.	Щелкните по вкладке Общие, чтобы активизировать соответствующее окно, как показано на рис. 12.3.
3.	В поле Каталог автозагрузки введите полный путь поиска, включая и букву дисковода. Если нужен доступ по сети, то можно использовать имена каталогов в формате UNC (Universal Naming Convention).
4.	Щелкните на ОК. Окно Параметры будет закрыто, альтернативный каталог — изменен.
5.	При новом запуске Excel введенное значение вступит в силу.
380	Неделя 2
Рис. 12.3. Использование вкладки Общие окна Параметры при вводе имени и пути поиска альтернативного каталога автозагрузки
Для того чтобы определить, имеется ли альтернативный каталог или его надо задать, используйте свойство AltStartPath объекта Application системы Excel. Введите в модуль Excel следующий текст процедуры и выполните ее; появится окно с сообщением, где будет указан полный путь (вместе с буквой дисковода) альтернативного каталога.
Листинг 1Z.Z. Вывод на экран алыпврнативногп каталага автозагрузки с помощью УВД
1:	Sub ShowAlternateStartPath()
2:	MsgBox Application.AltStartupPath
3:	End Sub
Для вывода на экран значения свойства AltStartPath объекта Application применяется процедура MsgBox во второй строке. Если было введено значение, показанное на рис. 12.3, то появится окно как на рис. 12.4.
Microsoft Excel
' САМОЙ ДОКУМЕНТЫ^. ALTERNATE
Рис. 12.4. В свойстве AltStartPath объекта Application находится полный путь поиска альтернативного каталога
Если появится строка нулевой длины, это значит, что в данный момент альтернативный каталог не используется. Хотя свойство StartPath имеет атрибут только для чтения (его значение можно прочитать, но не изменить), значение свойства AltStartPath можно и читать, и изменять.
День 12-Й. Модульное программирование
381
Чтобы в процедуре VBA изменить альтернативный каталог, надо просто присвоить строку с его значением свойству AltStartPath. Как и при вводе в окне Параметры, проверьте, имеется ли буква дисковода и полный путь поиска. В листинге 12.3 видно, как изменить каталог с помощью выражения VBA. (Как и в случае с окном Параметры, изменения вступят в силу только при новой загрузке Excel.)
Листинг 12.3. Изменение альтернативного каталога автозагрузки с помощью УВД
1:	Sub ChangeAltStartPath()
2:	Application.AltStartupPath = "С:\Мои документы\ХЬ Alternate"
3:	End Sub
В листинге 12.3 во второй строке вместо выражения в кавычках подставьте какую хотите строковую переменную или константу с именем и путем поиска каталога.
Так надо
Используйте команду Окно-Скрыть для того чтобы спрятать библиотечные книги с целью уменьшить бес- i порядок и не допустить случайных изменений в библиотеке из-за небрежности. Спрятанная книга все равно будет открываться в основном или альтернативном каталоге автозагрузки; (Более подробная информация о сокрытии и отображении книг содержится в документации по Excel.)
Используйте команду Сервис-Защита-Защитить книгу, чтобы уберечь библиотечную книгу от случай-. ных изменений. Если предполагается передавать книгу другим пользователям, то в команде можно пользоваться опцией Пароль, чтобы предохранить файл от внесения изменений/(Более подробная информация о защите книг содержится в документации по Excel.)
Помните, что при запуске Excel открываются все книги в обоих каталогах автозагрузки: и в основном, и в альтернативном.
Кроме того, что загрузка библиотеки при запуске Excel все же не дает доступа к ее процедурам и функциям из VBA-программы, этот способ имеет и другие недостатки. Ввиду того что библиотечный файл всегда открыт, используется часть компьютерной памяти. Время запуска Excel также возрастает пропорционально количеству библиотек в каталоге автозагрузки. Этого можно избежать, если вместо размещения в каталогах автозагрузки библиотечные книги сделать доступными с помощью ссылок (см. следующий раздел).
Доступ к библиотеке процедур и функций с помощью ссылок
Другим способом автоматического открытия библиотечной книги является создание в одной книге ссылки на другую. В этом есть ряд преимуществ. Во-первых, когда бы ни открывалась книга со ссылкой на другую, автоматически открывается и эта вторая книга, если она была закрыта. Во-вторых, в отличие от книг в каталоге автозагрузки, книги, на которые установлена ссылка, открываются только тогда, когда они нужны, экономя тем самым определенное время загрузки и компьютерную память.
Важнее всего, что при создании ссылки на другую книгу ее процедуры и функции становятся доступными для использования в VBA-программе, а не только в окне Макросы.
ПРИМЕЧАНИЕ
Можно создавать ссылки только на проекты, созданные тем же главным приложением, из которого создается ссылка. Например, в проекте на Excel можно создавать ссылки на другие проекты только приложения Excel.
382
Неделя 2
Для того чтобы создать ссылку в проекте VBA, сделайте следующее.
1.	Откройте книгу, в которую надо добавить новую ссылку, затем запустите Редактор Visual Basic.
2.	Проверьте, чтобы проект, в который надо добавить ссылку, был выбран в окне Проект, и выполните команду Сервис-Ссылки. На экране появится окно Ссылки (см. рис. 12.5).
В списке Доступные ссылки будут все открытые проекты VBA.
3.	Установите флажок с левой стороны от нужной книги, на которую надо создать ссылку.
4.	Если в списке нет нужного проекта, то щелкните на кнопке Обзор. На экране появится окно Добавление ссылки, внешне и в работе очень похожее на окно Открытие документа.
5.	Чтобы выбрать тип нужного проекта, используйте список Тип файлов. Если нужна книга, то выберите Microsoft Excel Files.
6.	Выберите нужный файл и щелкните Открыть; окно Добавление ссылки закроется, а выбранный проект появится в списке Доступные ссылки, слева от него будет установлен флажок.
References - Day12
Available References:
•/ Visual Basic For Applications
V Microsoft Excel 9.0 Object Library
У OLE Automation
i/ Microsoft Forms 2.0 Object Library
Microsoft Office 9.0 Object Library
1 Active Setup Control Library jActiveMovie control type library CCrsWpp 1.0 Type Library iCFtpWpp 1.0 Type Library J Core DRAW Type Library '. Direct 1.0 Type Library -DirectAnimation Library
’ DirectShowStream 1.0 Type Library .’Effect Library
Microsoft Office 9.0 Object Library-------
Location: C:\Pro^fc\0ffice\MSO9.Da Language: Standard
Puc. 12.5. Для установки ссылки из одного проекта VBA на другой пользуйтесь окном Редактор Visual Basic
7.	После того как созданы все требуемые для данного проекта ссылки, щелкните на кнопке ОК. Окно Ссылки закроется, а Редактор Visual Basic загрузит новую информацию в текущий проект; книга, на которую создана ссылка, откроется, если только не была уже открыта.
Теперь, когда бы ни открывалась книга со ссылкой, Excel будет гарантировать, что книга, на которую имеется эта ссылка, также будет открыта.
Кроме того, процедуры и функции проекта, на который установлена ссылка, будут доступны не только в окне Макросы, но и в исходном тексте VBA.
День 12-Й. Модульное программирование
383
Для удаления установленной ссылки необходимо выполнить те же действия, что и при ее установке, надо только сбросить флажок слева от нее, а не устанавливать его.
ПРЕДУПРЕЖДЕНИЕ
Не сбрасывайте флажки ссылок Visual Basic for Application, Office Object Library или Excel Object Library, иначе ваш проект не будет иметь доступа к таким ресурсам, как объекты Workbook, Worksheet и Application системы Excel, а также к встроенным функциям VBA, таким как Cint, Len, MsgBox и т.д.
После того как ссылка на проект установлена, при любом вызове его процедуры VBA прочитает нужную процедуру или функцию.
При создании ссылки на книгу она откроется для просмотра и редактирования в среде и Excel и редактора Visual Basic. Когда бы ни открывалась книга со ссылкой на другую книгу, модули и объекты второй книги появятся в окне Проект редактора Visual Basic, а ее листы будут видны в среде Excel. При желании можно выводить на экран или редактировать исходный текст модулей второй книги, если только они не защищены.
Как и в случае с библиотеками, загружаемыми из каталога автозапуска, вам возможно захочется скрыть или защитить книгу, чтобы случайно не внести в нее изменения или предотвратить беспорядок в рабочем пространстве. Чтобы другие пользователи не редактировали в библиотечной книге исходный текст VBA, необходимо защитить модули с помощью пароля (см. соответствующий раздел в уроке 21-го дня). Защищенные паролем процедуры и функции книги, на которую установлена ссылка, появятся в окне Просмотр объектов, но их модули нельзя будет вывести на экран или редактировать.
Использование ссылок для доступа к библиотечным книгам имеет существенный изъян: для каждого проекта VBA ссылку необходимо создавать отдельно. Например, если для пяти книг надо создать ссылку на одну библиотечную книгу, то необходимо пять раз повторить этот процесс.
ПРИМЕЧАНИЕ
Для любого открытого проекта со ссылками в окне Просмотр объектов видны процедуры и функции из проекта, на который есть ссылка. Окно Просмотр объектов можно использовать для их просмотра или для показа их исходного текста, как и для остальных процедур и функций открытого проекта, если только текст библиотеки не заблокирован для просмотра (т.е. для вывода на экран).
Обычно окно Макросы выводит на экран процедуры книги, на которую есть ссылка, так же как и процедуры любой другой открытой книги.
Можно создавать ссылку на библиотечную книгу в шаблоне Excel. В каждой созданной на его основе книге будет ссылка на эту библиотеку. (Более подробная информация о создании таких шаблонов содержится в справочной системе Excel.)
Так надо
Помните, что для доступа к библиотечной книге у каждой книги должна быть отдельная ссылка.
Скрывайте библиотечную книгу, к которой созданы ссылки, чтобы не загромождать рабочую площадь, и обеспечить защиту этой книги, если книгу планируется распространять среди других пользователей.
Всегда защищайте в библиотечной книге программный текст и блокируйте его от просмотра (как описано в уроке 21-го дня), если книга предназначения для распространения среди других пользователей.
384
Неделя 2
Расширенные правила видимости для многомодульных программ
Вы уже знаете, что сделать модули доступными для других проектов можно, создав библиотеку программ. Теперь вам нужно познакомиться с тем, как VBA применяет правила видимости для многомодульных программ. (Написание программ, у которых исходный текст распределен среди нескольких модулей, часто называют многомодульным программированием). Знание правил видимости для нескольких модулей поможет в понимании и использовании приемов структурного программирования, о которых рассказывается в следующем разделе этой главы.
Правила видимости для программ, использующих процедуры из нескольких модулей, ненамного сложнее, чем те, о которых говорилось на 3-м уроке. Кроме того, в VBA есть несколько способов расширения или ограничения видимости в разных модулях для констант, переменных, процедур, функций и определенных пользователем типов.
Области видимости Private и Public
Правила видимости для единичных модулей, описанные на 3-м уроке, применимы и в ситуациях нескольких модулей. Переменные и константы локальной видимости, т.е. описанные на уровне процедуры или функции, доступны лишь в этой программной единице. Переменные и константы, описанные на уровне модуля, доступны всем его процедурам и функциям.
Хотя переменные и константы, описанные на уровне модуля, доступны всем его процедурам и функциям, они недоступны процедурам и функциям другого модуля. Их область видимости ограничена модулем, в котором они были определены.
В VBA о переменных и константах, чья область видимости ограничена одним модулем, говорят, что они имеют область видимости private, так как они используются только внутри модуля с инструкциями, в которых они были описаны. Все переменные и константы, определенные на уровне модуля в разделе описаний (с помощью инструкции Dim), имеют область видимости private, а это означает, что они ограничены этим модулем.
В VBA то, что доступно всем модулям всех проектов, имеет область видимости public (общий), потому что общедоступно всем этим модулям. Все находящиеся в модуле процедуры, функции или определения пользовательских типов имеют такую область видимости и доступны всем модулям во всех проектах.
Иногда переменные, которые имеют область видимости public, называют глобальными переменными, так как они доступны глобально, т.е. по всей программе. Можно услышать от программистов, работающих не на VBA, что переменные и константы с областью видимости на уровне модуля являются глобальными, так как глобально доступны внутри отдельного модуля. Чтобы не было путаницы, в VBA (и в этой книге) термин private используется для предметов с областью видимости на уровне модуля, а public — на уровне нескольких модулей.
В списке перечислены три разные уровня видимости.
•	Область видимости local. Переменные и константы описываются внутри процедуры или функции. Они доступны только внутри той процедуры или функции, где были описаны.
•	Область видимости private. Переменные и константы описываются на уровне модуля. Они доступны любой процедуре или функции в пределах того модуля, где были описаны, но не вне его.
День 12-Й. Модульное программирование
385
• Область видимости public. Процедуры, функции и определяемые пользователем типы. Доступны в модуле, где были описаны, а также любому другому модулю того же проекта или другим проектам, при условии, что на проект с модулем, где они описаны, имеется ссылка.
Так надо
Помните, что когда переменные, константы, функции или процедуры имеют одинаковые имена, но разные области видимости, VBA использует ту из них, у которой область меньше.
Учтите, что у переменных, констант, функций или процедур с одинаковыми областями видимости не может быть одинаковых имен. Например, не должно быть переменной уровня модуля с тем же именем, что и у функции, процедуры или определяемого пользователем типа из того же модуля.
Для всех переменных, констант, функций, процедур и определяемых пользователем типов используйте уникальные • имена, чтобы не было путаницы и двусмысленностей, ведущих к трудно уловимым дефектам в программах.
Так не надо
Не забывайте, что прежде чем использовать в исходном тексте VBA процедуры и функции из библиотечного проекта, на этот проект следует создать ссылку. Только после этого применимы изложенные здесь правила для областей видимости public и private. В противном случае ни одна из процедур, функций, переменных или констант из библиотечного проекта в текущем проекте VBA видна не будет.	I
Переопределение правил видимости языка Visual Basic
Иногда необходимо ограничить иди расширить область видимости отдельной переменной, константы, процедуры, функции или определенного пользователем типа, а то и целого модуля. Для этого в VBA имеется два ключевых слова — Public и Private — и директива компилятора на уровне модуля.
Помните, что проект - собирательное имя для всех модулей и объектов VBA, хранящихся в одной книге.
Ках сделать модуль доступным только оуному проекту
Иногда необходимо, чтобы процедуры, функции и определенные пользователем типы из отдельного модуля не были доступны ни одному проекту, за исключением того, в рамках которого они описаны.
Например, имеются две разные книги. В одной — данные по оптовым продажам, а в другой — по бюджету отдела. В каждой книге может находиться группа процедур по составлению квартального отчета на основе ее данных. У процедур могут быть имена, такие как FirstQuarterReport (отчет по первому кварталу), SecondQuarterReport (отчет по второму кварталу) и т.д. Если открыты обе книги и требуется составить отчет по первому кварталу, для этого можно использовать первую процедуру. Однако если по ошибке вместо FirstQuarterReport из книги по продажам будет выполнена аналогичная процедура из книги по бюджету, произойдет ошибка выполнения или вы получите отчет с неожиданными результатами.
386
Неделя 2
Чтобы избежать такого рода случайностей, все содержимое некоторого модуля можно сделать с областью видимости private, чтобы его процедуры не использовались никакими проектами, кроме того, где находится сам модуль.
Предположим, в качестве другого примера, что вами написаны процедуры или функции, которые надо вызывать только из других процедур или функций, а не выполнять самому. Сделав модуль личным, можно свести на нет вероятность случайного выполнения одной из них. Эта операция над модулем фактически является обратной созданию библиотеки: вместо того, чтобы обеспечить общую доступность процедурам и функциям, получится, что они будут доступны только из определенного проекта.
Чтобы сделать модуль личным, используйте в нем инструкцию Option Private Module с синтаксисом
Option Private Module
Эта инструкция ограничивает доступность всех процедур, функций, определенных пользователем типов и любых переменных и констант уровнем модуля, в котором они описаны. Ее надо поместить в отдельной строке в области описаний модуля перед определениями переменных, констант, процедур, функций или определенных пользователем типов.
Чтобы разобраться с действием инструкции Option Private Module, обратите внимание на рис. 12.6, на котором вы видите окно Макросы приложения Excel. Там в списке Имя макроса находится несколько процедур (Procl, Ргос2, РгосЗ и Ргос4) из книги День12a.xls. Поскольку в списке перед именем каждой из них стоит имя книги, то можно сказать, что День12a.xls текущей книгой не является. Модуль из этой книги, в котором находятся описания названных процедур, не содержит инструкцию Option Private Module, так что процедуры доступны другим книгам.
Макрос	И
Има «*роса:
рау12а.х1з!Ргос1
iBalanceCheckBook IchangeAltStartPalh |MakeSalesRpt_Chart
Dayl2a.xls!Proc2
payl2a.xls!Proc3
|Dayl2a.xls!Proc4 Isho wA ItemateStartPath
IShowStartupPatfi
IT es t_F ir stNonB lank
TestFubar
Находится e: |Bce открытые книги
Описание
Рис. 12.6. Все процедуры в модуле из открытой книги Excel обычно доступны любой другой книге в окне Макросы
А теперь обратите внимание на рис. 12.7, на котором также показано окно Макросы приложения Excel. В модуль из книги День12а.хк, в котором находятся описания Procl, Ргос2, РгосЗ и Ргос4, была вставлена инструкция Option Private Module. Поскольку модуль стал личным, то его процедур в списке нет.
День 12-й. Модульное программирование
387
Макрос
ИВ
Над макроса
Balar iceChecI Bock
ChangeAltS tartPath MakeSatesRpt_Chart Show A Items teStartPath ShowStar tupPath T es t_F irstNonB lank TestFubar
^Все открытые ичиги
Puc. 12.7. При добавлении в модуль инструкции Option Private Module доступность его процедур и функций для содержащего его проекта была ограничена
ПРИМЕЧАНИЕ
Использование в модуле инструкции Option Private Module ограничит доступность процедур и функций только проектом, в котором содержится модуль с их описаниями. Ввиду того что эти процедуры и функции по умолчанию имеют видимость public, они остаются доступными для других модулей того же проекта.
Ограничеаие видимости одним миддлем с помощью ключевого слова Private
Ключевое слово Private ограничивает область видимости переменной, константы, функции, процедуры и определенного пользователем типа. Его надо использовать, когда в модуле есть отдельные функции, процедуры и определенные пользователем типы, которые не должны быть доступны другим модулям.
Ключевое слово Private назначает программному компоненту VBA видимость private, отменяя любую другую. Используйте это слово для уменьшения уровня видимости таких компонентов, как программы и функции, у которых иначе был бы уровень public.
Например, имеется функция, получающая от пользователя некоторое значение, такая как функции ввода данных, описанные на предыдущем уроке. Если такая функция используется только для отдельного проекта или задачи, например функция GetJJtilityltem из листинга 11.1, то нежелательно, чтобы она была общедоступной. Область ее видимости можно изменить, добавив к ее описанию ключевое слово Private и сделав таким образом функцию доступной только внутри модуля, в котором она описана. Следующие абзацы показывают общий синтаксис этого ключевого слова в описаниях переменных, констант, процедур, функций и определенных пользователем типов.
Чтобы описать private-переменную, используйте следующий общий синтаксис.
Private VarName [As TypeName]
VarName представляет любое имя переменной, удовлетворяющее стандартным правилам VBA, TypeName — имя любого стандартного для VBA или определенного пользователем типа данных. Обратите внимание, этот синтаксис точно такой, как у инструкции Dim, только вместо Dim стоит Private. Вводить описание переменной Private можно только на уровне модуля.
388
Неделя 2
Чтобы описать private-константу, используйте следующий общий синтаксис.
Private Const ConstName [As TypeName]=expression
ConstName представляет любое имя константы, удовлетворяющее стандартным правилам VBA, TypeName — имя любого стандартного для VBA или определенного пользователем типа данных, expression — любое выражение VBA. Этот синтаксис точно такой, как у инструкции описания Const. Вводить описание константы Private можно только на уровне модуля.
Чтобы назначить функции или процедуре видимость private, в модуле, где она описана, надо только к ее описанию добавить ключевое слово Private.
Private Function name([arglist]) [As TypeName]
Private Sub name ([arglist])
name представляет любое имя функции, удовлетворяющее стандартным правилам VBA, TypeName — имя любого стандартного для VBA или определенного пользователем типа данных, и arglist — список аргументов функции или процедуры. Обратите внимание, этот синтаксис точно такой, как у любого описания процедуры или функции. (О процедурах, использующих аргументы, рассказывается в следующем разделе этой главы.)
Доступность определенного пользователем типа можно ограничить и одним модулем, добавив в первую строку его определения ключевое слово Private.
Private Type VarName
VarName представляет собой любой идентификатор, удовлетворяющий стандартным правилам VBA и выбранный в качестве имени типа.
Так надо
Помните, что ключевое слово Private можно использовать только на уровне модуля; его нельзя использовать для изменения видимости локальных переменных или констант.
Расширение видимости на осе модули с помощью ключевого слова Public
Ключевое слово Public расширяет видимость отдельной переменной, константы, функции, процедуры или определенного пользователем типа, делая их доступными для всех модулей во всех проектах. Его надо использовать, когда имеются переменные или константы, которые требуется сделать доступными для других модулей в том же самом или другом проекте.
Ключевое слово Public назначает программному компоненту VBA видимость на уровне проекта, отменяя любую другую. Используйте это слово для расширения уровня видимости таких компонентов, как переменные и константы, у которых иначе был бы уровень private.
Например, как указывалось на 5-м уроке, для функции Chr в качестве констант можно использовать коды некоторых символов. На том занятии приводились примеры, такие как константа символа охраны авторских прав CRightSym (код 169), и предлагалось, чтобы такие константы описывались на уровне модуля с целью обеспечить доступность для всех его процедур и функций.
Однако константы такого рода полезны во всех модулях. Фактически, описания таких констант, как CRightSym, можно было бы вставить в проект библиотеки и сделать общедоступными для всех процедур и функций. Однако у констант, описанных на уровне модуля, имеется видимость private: они доступны в том модуле, где имеются их описания, но не доступны во всех остальных. Если к описанию константы доба
День 12-й. Модульное программирование	389
вить ключевое слово Public, то ее видимость изменится и константа будет доступна за пределами своего модуля. Следующие абзацы показывают общий синтаксис этого ключевого слова в описаниях переменных, констант, процедур, функций и определенных пользователем типов.
ПРИМЕЧАНИЕ
В модуле класса можно описывать общие переменные и константы, за исключением общих строковых переменных.
Чтобы описать public-переменную, используйте следующий общий синтаксис.
Public VarName [As TypeName]
VarName представляет любой удовлетворяющий стандартным правилам идентификатор, TypeName — имя любого стандартного типа данных. Этот синтаксис точно такой, как у инструкции Dim, только вместо Dim здесь фигурирует Public. Вводить описание переменной Public можно только на уровне модуля.
Чтобы описать public-константу, используйте следующий общий синтаксис.
Public Const ConstName [As TypeName]=expression
ConstName представляет любой удовлетворяющий стандартным правилам идентификатор, TypeName — имя любого стандартного типа данных, expression — выражение. Этот синтаксис точно такой, как у инструкции описания Const, только добавлено ключевое слово Public. Вводить описание константы Public можно только на уровне модуля.
Чтобы сделать функцию или процедуру доступной всему модулю, достаточно к ее описанию добавить ключевое слово Public.
Public Function name)[arglist]) [As TypeName]
Public Sub name ([arglist])
Здесь name представляет любой удовлетворяющий стандартным правилам идентификатор, TypeName — любой стандартный тип данных, и arglist — список аргументов функции или процедуры. Этот синтаксис точно такой, как у любого описания процедуры или функции, необходимо добавить лишь ключевое слово Public.
Аналогичным образом в первую строку определения пользовательского типа можно включить ключевое слово Public (VarName представляет любой удовлетворяющий стандартным правилам идентификатор).
Public Type VarName
Так надо
Используйте ключевое слово Public только на уровне модуля; его нельзя использовать для изменения видимости локальных переменных или констант.
МИЯИИИ1
Не забывайте, что даже если в модуль будет добавлена инструкция Option Private Module, в нем все, что имеет видимость public, останется доступным для других модулей того же проекта, но только не для модулей из других проектов.
390
Неделя 2
Даже если в модуль будет включена инструкция Option Private Module и он станет для своего проекта private, в модуле можно делать переменные и константы доступными для других модулей того же проекта. Для этого их надо описать с помощью ключевого слова Public, как было описано выше.
Эта инструкция ограничивает доступность элементов модуля, видимость которых определена как public, только проектом, в котором находится модуль. Ключевое слово Public делает переменную или константу доступной вне своего модуля, а в соединении с инструкцией Option Private Module — доступной всем модулям только в пределах проекта.
Как избежать замкидтых ссылок
Замкнутая ссылка возникает тогда, когда один элемент, например константа, определяется через другой элемент, другую константу, которая, в свою очередь,, определена через первую. Чтобы лучше понять это, давайте рассмотрим такие фразы.
Грач — птица величиной с ворону.
Ворона — птица величиной с грача.
В первой фразе размеры грача определены через размеры вороны, а во второй — размеры вороны через размеры грача. Оба определения ссылаются друг на друга, и ни одно из них не содержит информации о размерах птицы. Такая ситуация называется замкнутой ссылкой или порочным кругом.
В VBA можно столкнуться с этой ситуацией, если в модуле будут описания констант, аналогичные этим:
Const А=В
Const В=А
В отдельном модуле, скорее всего, такой ошибки не будет; неправильность инструкций очевидна. Если же это случится, то система может сообщить, что для константы значение не определено, и выведет на экран сообщение об ошибке, где говорится, что требуется выражение для описания первой константы.
Однако если описаны Public-константы, то велика вероятность создания замкнутой ссылки между ними в разных модулях. Если в одном модуле имеется такое описание:
Public Const А=В, а в другом — такое: Public Const В = А,
то VBA выведет сообщение об ошибке, где говорится, что есть замкнутые ссылки между модулями.
Так надо
Размещайте все описания public-констант в одном модуле; их проверка сразу станет намного легче.
Так не надо
Не создавайте замкнутые ссылки. Чтобы этого избежать, проверяйте описания констант.
День 12-й. Модульное программирование
391
Префикс модуля
Иногда нужен доступ к переменной, константе, процедуре иди функции с видимостью private, которые находятся в другом модуле, и в то же время существуют причины не присваивать им видимость public. Для этого требуется указать тот модуль. Делается это с помощью добавления его имени, называемого префикс модуля, к имени нужной переменной, константы, процедуры или функции.
Другой причиной использования префикса модуля является желание избежать путаницы при использовании переменных, констант, процедур или функций с видимостью public, чьи имена совпадают с именами элементов с той же видимостью из других модулей. Как известно, идентификаторы должны быть уникальны в пределах уровня видимости, так что VBA не позволит создать в одном модуле переменные, константы, функции, процедуры или определенные пользователем типы, чьи имена дублируют друг друга. Однако при работе одновременно с несколькими модулями возможно, что в двух из них public-идентификаторы будут совпадать. В такой ситуации путаницы можно избежать, точно указывая модуль с помощью его префикса.
ПРИМЕЧАНИЕ
Чтобы использовать префиксы модулей, необходимо (как было описано в этой главе) установить ссылку на проект с нужными процедурами, если их нет в проекте с вызывающей процедурой.
Чтобы добавить префикс модуля, используйте следующий общий синтаксис:
ModuleName.Identifier
ModuleName представляет имя модуля в таком виде, как в окне Проект. Обратите внимание на точку после имени модуля. Как в ссылках на объекты и элементы определенного пользователем типа, он соединяет два идентификатора в одну ссылку. Identifier — любая переменная, константа, процедура или функция, описанная в модуле ModuleName и имеющая видимость на уровне модуля.
В префикс модуля можно включить имя проекта, особенно при наличии двух проектов с модулями, имеющими одинаковые имена. Для этого в префикс к имени модуля следует добавить имя проекта с точечным разделителем (.).
Pro jectName. ModuleName. Identifier
Здесь ProjectName представляет имя проекта в таком виде, как в окне Проект.
Вспомним пример в начале главы с библиотечной книгой ChartTools.xls. Если в ней находятся модули PieCharts и Barcharts, в каждом из которых есть процедура с именем MkSalesChart, то для обращения к каждой такой процедуре необходимы следующие префиксы модуля.
PieCharts.MkSalesChart
Barcharts.MkSalesChart
ChartTools.PieCharts.MkSalesChart
ChartTools.Barcharts.MkSalesChart
В первой строке происходит обращение к процедуре из модуля PieCharts, во второй — к процедуре с тем же именем из модуля Bar Charts. В третьей и четвертой строках идет обращение к тем же процедурам из тех же модулей, но с использованием имени проекта.
392
Неделя 2
ПРИМЕЧАНИЕ
В системах Excel версий 5 и 7 имена для модулей можно задавать с пробелами. VBA 5.x, версия которого включена в состав Excel 97 или более поздней версии, не разрешает создавать для модулей или других объектов имена, нарушающие правила. При работе же с системами Excel версий 5 и 7 такие имена могут встречаться. В этом случае их надо заключить в квадратные скобки (чтобы они стали обязательной частью синтаксиса).
[ModuleName].Identifier
Обратите внимание наточенный разделитель (.) между префиксом модуля и идентификатором.
Структурное программирование
По мере прогресса в автоматизации работы с помощью VBA, в один прекрасный момент вы осознаете, что при выполнении некоторых заданий работает несколько макросов один за другим. Обычно это тот поворотный пункт, когда большинство людей решает организовать связанные друг с другом процедуры в одну программу. Этот раздел посвящен проектированию и созданию больших программ, состоящих из совместно работающих процедур, — либо с помощью написания всей программы от начала и до конца, либо путем реорганизации и соединения уже существующих процедур.
Вызов oguofi процедуры пз другой
Возможно, вы обратили внимание, что некоторые функции и процедуры, описанные на прошлом уроке, выглядели очень длинными; и при этом чем длиннее они становились, тем труднее было с ними работать. После этого легко вообразить (а ведь эти примеры выполняли довольно простую работу), как одна процедура для выполнения сложной задачи быстро становится крайне длинной и запутанной. Настолько длинной, что ее создание, понимание и поддержка становится почти невозможной.
Решение состоит в том, чтобы разделить выполняющую задание программу на несколько процедур, вызываемых другой процедурой. Дробя задание на меньшие шаги и создавая для каждого отдельную процедуру, мы облегчаем их написание, отладку, обновление. Для выполнения всех шагов создается другая процедура, которая запускает предыдущие в нужном порядке.
Например, требуется написать процедуру, использующую данные рабочей книги для подсчета прихода и расхода, для создания диаграмм продаж и расходов и для печати отчета. Можно написать одну процедуру, выполняющую все эти действия. В ней будет несколько сот строк текста, много вложенных инструкций If.. .Then и циклических структур, и она будет настолько сложной, что ее создание и поддержка будет сплошным кошмаром.
Если написать несколько работающих вместе процедур, программа станет проще в управлении. Разделив все задание на составные части и создав по каждой из них процедуру, мы сохраняем для процедур приемлемый уровень сложности. Такой прием часто называют программированием в стиле “разделяй и побеждай”.
Применяя этот прием к подсчету прихода и расхода, можно написать несколько отдельных процедур: одну процедуру, подсчитывающую валовой доход, вторую — для валового расхода, третью — для чистого дохода, также как и для создания нужных диаграмм и, наконец, четвертую — для печати отчета.
На рис. 12.8 видно соответствие отдельных процедур и разных частей общего задания. Обратите внимание, что каждая такая часть имеет соответствующую процедуру.
День 12-й. Модульное программирование
393
Функция или процедура 1 Вычисление валового дохода
^Создание квартального
Этап 2 Вычисление валового расхода
Этап 3 Вычисление чистого дохода
Функция или процедура 2 .' Вычисление валового расхода
Функция или процедура 3 Вычисление чистого дохода
Этап 4
Построение диаграмм
Функция или процедура 4 Построение диаграмм
Этап 5 Печать отчета
Функция или процедура 5 Печать отчета
Рис. 12.8. Упрощайте сложные задачи, создавая маленькие процедуры, выполняющие один этап большого задания
Вы, возможно, удивитесь — какой толк писать несколько процедур, ведь потом придется выполнять их каждую отдельно. Но можно собрать их в одну программу (правый прямоугольник на рис. 12.8), просто написав процедуру, вызывающую их в нужном порядке.
Вам известно, как из процедуры вызывать функцию; вызов процедуры во многом аналогичен. Если процедуры, написанные для шагов 1-5, называются соответственно CompGrossIncome, CompGrossExpenses, CompNetlncome, CreateCharts, PrintReport, то главная процедура, которая их выполняет, может выглядеть так.
Sub QuarterlyReport()
CompGrossIncome
CompGrossExpenses
CompNetlncome
CreateCharts
PrintReport
End Sub
Как видите, эта процедура не выполняет иной работы, кроме вызова в нужном порядке процедур, которые выполняют шаги, необходимые при создании квартального отчета. Процедура QuarterlyReport короткая и простая; ее можно легко прочитать и определить, какие, вообще говоря, задания она выполняет. Детали каждого задания находятся внутри соответствующей процедуры, вызываемой QuarterlyReport.
Другое преимущество разделения большого задания на малые части и написания отдельной процедуры для каждой из них заключается в том, что становится возможным повторно использовать некоторые из написанных программ. Например, если вы уже создавали годовые отчеты по доходам и расходам, то, вероятно, заметили, что
394
Неделя 2
можно использовать те же процедуры, написанные для вычисления валового дохода, валового расхода и чистого дохода по кварталу, для вычисления тех же данных за год, если просто заменить квартальные цифры годовыми.
Возможность повторного использования исходного текста — это одно из бесспорных преимуществ языков структурного программирования, таких как VBA. Если исходный текст отдельного задания был включен в одну процедуру, то надо снова писать текст для любого другого задания, которое могло бы использовать готовый или аналогичный исходный текст. Если выделить общие действия в отдельные процедуры, можно добиться, чтобы несколько разных программ использовали те же процедуры, сводя этим на нет необходимость программировать одно и то же задание более одного раза.
Далее в этой главе вы узнаете, как использовать аргументы процедур для обмена данными между ними, благодаря чему процедуры становятся более гибкими и мощными.
Программирование сверху вииз, иди Пошаговая детализация
Одна из распространенных стратегий при разработке программ называется программированием сверху вниз. Когда вы разрабатываете программу, используя такой подход, то начинаете с проектирования наиболее обшей части программируемого процесса, “вершины”, переходя к более и более специфичным его деталям, продвигаясь, так сказать, вниз.
Чтобы применить к программам проектирование сверху вниз, придерживайтесь такой общей последовательности.
•	Начните с общей схемы заданий, которые должна выполнять программа.
•	Для каждого шага в этой схеме подберите имя процедуры. Если имеется пять шагов, то надо выбрать пять таких имен.
•	Для каждого шага из общей схемы создайте более подробную схему всех действий, необходимых для его выполнения. После завершения этой схемы второго уровня дайте процедурное имя каждому ее шагу.
•	Для каждого шага из схемы второго уровня создайте более подробную схему всех действий, необходимых для его выполнения. Продолжайте последовательно создавать более подробные схемы для каждого подзадания, пока не будет достигнут уровень элементарности, при котором можно легко написать VBA-программу для реализации такого задания в виде одной короткой процедуры или функции.
Этот процесс последовательного проектирования также известен как пошаговая детализация, так как идет постепенное разбиение каждого шага на детали с последовательным разделением сложных действий на более простые составляющие.
Одно из преимуществ проектирования сверху вниз и пошаговой детализации состоит в том, что это позволяет сконцентрировать усилия для достижения большего эффекта. Если вы зашли в тупик при написании исходного текста для какого-нибудь подзадания, не переживайте. Дайте этому подзаданию процедурное имя, сделайте вид, что у вас есть друг, который и расскажет и покажет, как все это написать, и продолжайте проектирование.
Зачастую, когда вы кончаете проектировать другие задания, с этим заданием становится все ясно. А если и не ясно, то область неопределенности уже достаточно очерчена малым числом процедур, и известно, где сконцентрировать усилия экспериментатора. Это другой способ применения к программированию подхода “разделяй и побеждай”.
Работа методом проектирования сверху вниз также поможет определить, имеются ли все данные для выполнения определенного задания, как его решить, какие нужно
День 12-Й. Модульное программирование
395
будет описать переменные и константы и какие понадобятся определяемые пользователем типы.
Применяя концепцию программирования сверху вниз при создании квартального отчета, пример которого мы рассматривали в предыдущем разделе, вы могли бы составить такую схему.
1.	Вычисляем валовой доход.
2.	Вычисляем валовой расход.
3.	Вычисляем чистый доход.
4.	Составляем диаграммы.
5.	Печатаем отчет с диаграммами.
Здесь в общих чертах описана вся задача. Этой общей схеме будет соответствовать главная программа, которая будет состоять из нескольких инструкций, просто вызывающих соответствующие процедуры в нужном порядке.
Теперь для каждого задания из предыдущей схемы составим более подробную схему. Схема второго уровня для шага 1 (подсчета валового дохода) могла бы выглядеть таким образом.
Итоги по продажам в западном регионе.
Итоги по продажам в восточном регионе.
Итоги по продажам в южном регионе.
Складываем промежуточные итоги по западному, восточному и южному регионам.
Эта схема подсчета валового дохода по продажам подсказывает, что нужны, как минимум, три различные переменные, по одной на итоги каждого региона. Кроме того, видно, что первые три шага требуют дополнительной детализации, а четвертый можно выполнить простым арифметическим выражением после подсчета всех промежуточных итогов. Поэтому требуется написать процедуры для первых трех шагов, но не для четвертого, слишком простого, чтобы для него создавать специальные инструкции на VBA.
Теперь, работая над шагом 1.1 из схемы второго уровня, можно получить такую схему третьего уровня.
Выбираем лист с цифрами продаж по западному региону.
Складываем все числа из столбца В (здесь находятся цифры итогов продаж по неделям, предоставленные региональным менеджером).
Сохраняем полученный промежуточный итог в переменной для итога по западному региону.
Из этой схемы видно, что каждый из трех шагов достаточно прост, чтобы написать для него инструкции в исходном тексте VBA. Фактически каждому из них соответствует одна или две инструкции. На этом уровне дополнительной детализации уже не требуется; теперь есть шаги для отдельной процедуры.
Работу над шагом 1.2 можно закончить такой схемой третьего уровня.
Выбираем лист с цифрами продаж по восточному региону.
Складываем все числа из столбца В (здесь находятся цифры итогов продаж по неделям, предоставленные региональным менеджером).
Сохраняем полученный промежуточный итог в переменной для итога по восточному региону.
Обратите внимание, за исключением листа с исходными данными и переменной для промежуточного итога, эта схема идентична предыдущей, созданной для шага 1.1. Такая ситуация подсказывает, что для проведения обеих операций надо использовать одну процедуру, для которой лишь требуется указать, какой выбрать лист и в какой переменной сохранить промежуточный итог. Поскольку функция может возвращать
396
Неделя 2
результат, то можно сделать вывод, что для вычисления промежуточного итога больше подходит функция, и даже создать функцию, которая берет название листа с исходными данными и возвращает в результате итог по региону.
В этом примере рассмотрен процесс создания схем до третьего уровня включительно и полностью рассмотрена только пара шагов. Нет прямого указания, сколько уровней создавать, надо лишь продолжать процесс углубления детализации каждой схемы до тех пор, пока каждому шагу не будет четко соответствовать от одной до четырех инструкций VBA.
Так надо
Пишите короткие процедуры, выполняющие одно простое задание. Запомните, что в процедуре не должно быть больше 20 или 30 строк - примерно один или два заполненных экрана. Если процедура существенно длиннее, то, скорее всего, она перегружена работой.
Избегайте длинных процедур, выполняющих много разных заданий. Чем длиннее и сложнее процедуры, тем больше вероятность программных ошибок или трудностей при обновлении. И еще, если процедура очень длинная, то велика вероятность, что в ней дублируется намного больше исходного текста, чем нужно.
Так не надо
Не забывайте при написании процедур и функций о принципе ДЛЯ - 'Делай просто и ясно*.
Модульная организация программного проекта
Обычно весь исходный текст программы следует сохранять в одной книге, за исключением процедур или функций общего назначения, место которым — в библиотечной книге. В одном проекте используют разные модули, чтобы лучше организовать текст программы. Другой причиной, чтобы сохранять в одной книге текст одной программы, является то, что его легко преобразовать в программу-надстройку, о которой мы поговорим на 21-м занятии.
В больших проектах размешайте исходный текст в нескольких модулях, в каждом из которых должны выполняться определенные задания из большой программы. Например, программу на VBA, которая импортирует данные в лист книги, форматирует и выводит их в виде диаграммы, а затем печатает диаграмму и отчет, будет легче разработать и понять, если эта программа будет разделена на несколько модулей. Скажем, в одном модуле могли бы быть процедуры и функции, импортирующие данные, в другом — те процедуры и функции, которые форматируют и выводят диаграмму, и т.д.
Внутри отдельного модуля исходный текст организуется исходя из нескольких договоренностей, которые реализуются с помощью редактора Visual Basic. Во-первых, в самом начале модуля, в области описаний, перед другими инструкциями VBA располагайте инструкции Option. Это такие инструкции, как Option Explicit, Option Compare, Option Private Module и т.д. Во-вторых, после инструкций Option, но перед описаниями переменных, констант, процедур и функций располагайте описания определенных пользователем типов. Затем описывайте все переменные и константы уровня модуля или с видимостью public.
'Область описаний:
'Инструкции Option: Option Explicit, Option Compare и т.д.
'Определения нестандартных типов
'Описания переменных и констант уровня модуля
'Описания процедур и функций
День 12-й. Модульное программирование
397
Обмен данными между процедурами с ннмнщын аргументов
Точно так же как и функции, которые принимают данные из списка аргументов, можно создавать процедуры, которые принимают данные, передаваемые в виде аргумента. В сущности, создание списков аргументов для процедур и функций — это одно и то же. В следующих разделах рассказывается, когда и зачем надо создавать процедуры со списком аргументов, а также каким образом определять и использовать такой список.
Когда о с какой целью используются процедуры со сооском аргументов
Существует две основные причины использования в процедурах списка аргументов.
•	Список аргументов — это удобный, эффективный и надежный способ передачи информации процедуре для выполнения ее задачи. Его применение позволяет сделать процедуру более гибкой и мощной.
•	Список аргументов — это также удобный, эффективный и надежный способ вернуть информацию от процедуры. Для этого используйте аргумент, передаваемый по ссылке; как и в функциях, когда процедура меняет его значение, меняется и значение передаваемой переменной.
В разделе о программировании сверху вниз был приведен пример использования проектирования сверху вниз и пошаговой детализации для создания схемы программы. Вы помните, что во время этого процесса было обнаружено, что шаги вычисления промежуточных итогов по разным регионам были идентичны, за исключением имени листа с исходными данными и имени переменной для хранения итога. Поскольку тексты программ для такого вычисления очень схожи, то для получения всех промежуточных итогов надо использовать одну процедуру.
Чтобы использовать одну процедуру для вычисления итогов по каждому региону, вам необходимо указать ей, какой надо выбрать лист и в какой переменной сохранить итог. И то, и другое можно сделать, передав процедуре аргументы; один — для имени листа, а другой, передаваемый по ссылке, — для переменной, в которой будет храниться итог. Затем создается процедура, которая будет вычислять промежуточный итог для любого листа, какой ей назовут, и хранить итог в любой переданной ей переменной. С помощью добавленных к процедуре аргументов становится возможным использовать ее одну для вычисления промежуточных итогов по каждому из регионов, в зависимости от переданного ей имени листа. Фактически одну и ту же процедуру можно использовать для вычисления итога по любому листу, какой бы вы ни назвали.
Способ передачи процедуре информации с помощью списка аргументов также уменьшает количество описываемых переменных уровня модуля или видимости public. Это действительно полезно: сократится нужное программе количество памяти, и уж точно исчезнет потенциальный источник ошибок и путаницы. Чем больше в программе переменных уровня модуля или видимости public, тем больше шансов по невниманию использовать не ту переменную, что грозит побочными эффектами и трудноуловимыми ошибками. Для передачи процедуре информации используйте список аргументов, а не переменную уровня модуля.
398
Неделя 2
Как создать список аргументов процедуры
Список аргументов для процедуры создается точно так же, как и для функции. Известно, что в каждом описании процедуры после ее имени должны стоять круглые скобки; теперь вы, вероятно, поняли, что пустые скобки указывают VBA, что у процедуры нет аргументов.
Ниже приведен полный синтаксис описания процедуры.
Sub name([arglistJ) statements
End Sub
Здесь name представляет имя процедуры, arglist — необязательный список аргументов, statements — инструкции VBA, составляющие тело процедуры. Список аргументов может состоять из одного или нескольких аргументов, каждый из которых отделяется запятыми. У отдельного аргумента из списка такой общий синтаксис.
[Optional] [ByVali ByRef] varname [As typename]
Как и для функции, ключевое слово Optional указывает на то, что аргумент необязательный. Может присутствовать ключевое слово ByVai или ByRef , но не оба вместе. ByVai оповещает VBA, что аргумент надо передавать по значению, a ByRef — по ссылке. (Разница между аргументами, передаваемыми по значению и по ссылке, рассматривалась на уроке 6-го дня.) varname представляет имя аргумента, удовлетворяющее стандартным правилам VBA. В конце находится ключевое слово As, за которым следует typename, представляющий стандартный или определенный пользователем тип данных аргумента. Если это значение не будет указано, то VBA присвоит аргументу тип данных Variant.
Так надо
В списке аргументов вначале пишите обязательные, а затем необязательные аргументы (так требует VBA).
Для проверки, есть или нет внутри процедуры необязательные аргументы типа Variant, используйте функцию IsMissing - как и для функции с необязательными аргументами.
Так надо
Помните, что нельзя использовать функцию IsMissing для определения того, передан ли необязательный аргумент, если тип его данных отличен от Variant (эта функция работает только с необязательными аргументами, имеющими тип данных Variant).
Помните, что в описании процедуры значение по умолчанию для необязательного аргумента устанавливается точно так же, как и в описании функции.	•;
Помните, что VBA по умолчанию передает аргументы по ссылке. Если будет пропущено ключевое слово ByVai или ByRef, то аргумент будет передан по ссылке.
Помните, что имена аргументов имеют ту же видимость, что и переменные, описанные на уровне процедуры, т.е. переменные аргументов недоступны вне процедуры, в списке которой они были описаны.
Каждый раз при написании процедуры внимательно проверяйте, чтобы менялись лишь те аргументы, какие
День 12-й. Модульное программирование
399
Применение процедур с аргументами
Хотя создание списка аргументов для процедуры точно такое, как и для функции, вызов процедуры с аргументами несколько отличается. Когда аргументы передаются процедуре, их список не заключается в скобки, хотя это необходимо при описании процедуры.
Для вызова процедуры и передачи ей аргументов просто наберите ее имя, затем поставьте пробел и после этого перечислите значения всех аргументов из списка, отделяя каждое значение запятой. При определении значений аргументов из списка можно использовать и поименованные аргументы. Для этого просто используйте имя аргумента из списка, находящееся в описании процедуры.
Предположим, что есть процедура с именем CompGrossIncome, имеющая такое описание.
Sub CompGrossIncome(SheetName As String, sTotal As Currency)
Это описание сообщает VBA, что у CompGrossIncome два обязательных аргумента. Первый из них, SheetName, — это строка; у второго, sTotal, значение типа Currency. VBA передает оба аргумента по ссылке, так как нет ключевого слова ByVai.
Чтобы вызвать процедуру CompGrossIncome и указать значения ее аргументов, можно использовать одну из таких инструкций.
CompGrossIncome “Eastern",GrSales
CompGrossIncome SheetName:=”Eastern”,sTotal:=GrSales
В первой строке в нужном порядке лишь перечислены значения аргументов, которые отделены друг от друга запятыми. Во второй строке для указания значений использованы поименованные аргументы, которые также отделены друг от друга запятыми.
Если вы попробуете передать процедуре аргументы, которых нет в ее описании, то VBA выведет на экран сообщение об ошибке, в котором говорится, что у процедуры неверное количество аргументов. Если у процедуры есть обязательные аргументы (те, что описаны без ключевого слова Optional) и вы пропустили один или несколько из них, то VBA выведет то же самое сообщение о неверном количестве аргументов.
Окно Макросы не показывает процедуры с обязательными аргументами, так как нет /ПОИММНИИЕ способа передать их значения. Такого рода процедуры можно выполнить только путем f ПгИМЕтНПпЕ их вызова из программ VBA. Окно Макросы перечисляет только процедуры без аргументов, хотя в его списке может оказаться процедура, все аргументы которой необязательные.
В листинге 12.4 показаны две различные процедуры. В нем есть процедура MakeSalesRpt_Chart, похожая на одноименную процедуру, описанную в материале предыдущих уроков. Новым является то, что MakeSalesRpt_Chart для создания диаграммы вызывает процедуру Make LabeledPieChart, а не создает ее сама.
Листинг 12.4. Использпоаппе аргумептпп уля посыш дпфпрмацпп процедуре
1:	Sub Make_LabeledPieChart(srcSheet As String,
2:	sRng As String,
3:	destSheet As String,
4:	chTitle As String)
5:	'создает круговую диаграмму иа листе,
6:	'имя которого передано в destSheet; с помощью метода Chart-Wizard
400
Неделя 2
7:	'отображает данные из диапазона sRng на листе, имя которого передано в
8:	'srcSheet. Заголовок диаграммы передается в chTitle.
9:
10:	'выбирает лист и создает диаграмму
11:	Sheets(destSheet).Select
12:	ActiveSheet.Chartobjects.Add(96, 37.5, 234, 111).Select
13:	'создает диаграмму с помощью метода Chartwizard.
14:	With Sheets(srcSheet)
15:	ActiveChart.Chartwizard Source:=.Range(sRng),
16:	Gallery:=xlPie,
17:	Format:=7,
18:	PlotBy:=xlColumns,
19:	CategoryLabels:=l,
20:	SeriesLabels:=l,
21:	HasLegend:=l,
22:	Title:=chTitle
23:	End With
24:	End Sub
25:
26:	Sub MakeSalesRpt_Chart()
27:	'Запрашивает имя листа с исходными данными,
28:	'запрашивает диапазон ячеек с данными.
29:	'Спрашивает имя листа, на котором будет расположена
30:	'диаграмма. Потом эта процедура вызывает процедуру Make_LabeledPieChart,
31:	'которая	строит диаграмму.
32:	Const sTitle = "Создание диаграммы отчета"
33:
34:	Static	SrcShtName As String
35:	Static	SourceRng As String
36:	Dim DestShtName As String
37:
38:	'ввод имени листа с данными
39:	SrcShtName = InputBox(prompt:="Введите имя листа " &
40:	"с данными для диаграммы:",
41:	Title:=sTitle,
42:	Default:=SrcShtName)
43:
44:	'проверяем, ввел ли пользователь имя или выбрал Cancel
45:	If Len(Trim)SrcShtName)) = 0 Then
46:	MsgBox "Данные не введены - конец работы"
47:	Exit Sub
48:	End If
49:	'выбираем лист с данными
50:	Sheets(SrcShtName).Select
51:
52:	'Ввод диапазона ячеек
53:	SourceRng = InputBox(prompt:="Введите диапазон ячеек " &
54:	"с данными для диаграммы в стиле А1:", _
55:	Title:=sTitle,
56:	Default:=SourceRng)
57:
58:	'Проверяем, ввел ли пользователь диапазон или выбрал Cancel
59:	If Len(Trim(SourceRng)) = 0 Then
День 12-й. Модульное программирование	401
60:	MsgBox "Ячейки не указаны - конец работы"
61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80:	Exit Sub End If 'Ввод имени листа для диаграммы DestShtName = 1приГВох(ргогорГ:="Введите имя листа, " & “на котором будет построена диаграмма:", Title:=sTitle) 'ввел ли пользователь имя листа или выбрал Cancel? If Len(Trim(DestShtName)) = 0 Then MsgBox "Имя листа не ведено - конец работы" Exit Sub End If 'вызываем процедуру Make_LabeledPieChart для построения диаграммы. Make_LabeledPieChart srcSheet:=SrcShtName, sRng:“SourceRng, destSheet:“DestShtName, chTitle:=”OT4eT о продажах" End Sub
В строках 1-24 дано определение процедуры Make LabeledPieChart. Она создает круговую диаграмму с подписями, используя лист с исходными данными, их диапазон ячеек, лист, где должна быть диаграмма и название самой диаграммы, которые передаются ей в качестве аргументов. Поскольку эта процедура для создания диаграммы принимает всю информацию через список своих аргументов, то она может быть использована для создания круговой диаграммы на основе данных из любых диапазонов ячеек любого листа и размещения диаграммы опять же на любом листе.
Описание процедуры Make_LabeledPieChart и список ее аргументов находятся в строках 1-4. Обратите внимание на символ продолжения строки в конце строк 1, 2 и 3: в строках 1-4 находится одно описание процедуры. Список аргументов поделен на несколько строк, чтобы облегчить его чтение.
Процедура Make_LabeledPieChart включает четыре разных обязательных аргумента, и все являются строками. В аргументе srcSheet содержится имя листа с исходными данными; в sRng — диапазон ячеек с данными; в destSheet — имя листа, где будет помещена новая диаграмма; в chTitle — заголовок новой диаграммы.
В строке 11 выбирается лист, на котором будет построена диаграмма. Это делается с помощью коллекции Sheets и аргумента destSheet, в котором передается имя этого листа. В строке 12 создается объект диаграммы на нужном листе и заданном месте, а затем выбирается новый объект.
Строка 14 начинается с инструкции With для объекта, определенного с помощью srcSheet. Строки 15-22 представляют одну инструкцию VBA, где для создания нужной круговой диаграммы используется метод Chartwizard. В строке 15 обратите внимание на аргумент диапазона ячеек sRng, используемый для указания источника данных для диаграммы. Лист, где находится этот диапазон, указан в строке 14 с помощью инструкции With. В строке 22 обратите внимание на аргумент chTitle, используемый для указания заголовка диаграммы.
Вся информация, нужная Make_LabeledPieChart для выполнения ее работы, передается через список ее аргументов. Как только требуется создать круговую диаграмму нужного формата с подписями данных, можно воспользоваться этой процедурой, что
402
Неделя 2
бы взять данные с любого листа и вставить диаграмму опять же в любой лист (при условии, что эти листы находятся в одной книге). Это классический пример короткой, простой процедуры, хорошо выполняющей одно задание.
В строках 26-80 находится определение процедуры MakeSalesRpt_Chart. У нее нет аргументов. Эта процедура должна получить от пользователя имя листа с исходными данными, диапазон содержащих их ячеек и имя листа, где будет находиться диаграмма.
В строках 39-42 показано, как принимается от пользователя имя листа с исходными данными, а в строках 45-48 — как проверяется, не отменил ли пользователь диалог ввода. Если отменил, то процедура завершается; в противном случае она продолжается с 50-й строки, где выбирается лист с исходными данными.
В строках 53-56 от пользователя принимается диапазон ячеек с исходными данными, а в строках 58-62 — проверяется, не отменил ли пользователь диалог ввода. В строках 64—73 от пользователя принимается имя листа, где должна находиться диаграмма, и снова проверяется, не отменил ли пользователь операцию, введя пустую строку или отменив диалог ввода.
И наконец, в строках 76-79 показано, как процедура Make_LabeledPieChart действительно создает новую круговую диаграмму.
Преимущество создания процедуры Make LabeledPieChart в том, что вы получаете достаточно универсальное средство для создания круговой диаграммы определенного формата на любом листе и на основе данных опять же из любого листа. Процедура короткая, простая и легкая для понимания. Ее можно использовать для создания аналогичных диаграмм продаж, расходов, чистого дохода и т.д.
В примере в листинге 12.4 аргументы используются только для того, чтобы отправить процедуре информацию. В листинге же 12.5 показана процедура, которая использует свои аргументы для возвращения значений другой процедуре, которая ее вызвала. Здесь также представлены две различные процедуры. Первая, FirstNonBlankCell, просматривает первые 10 столбцов и первые 16 строк листа. Она возвращает в своих аргументах координаты строки и столбца первой найденной ею непустой ячейки. Вторая процедура просто помогает протестировать первую.
Листинг 12.5. Иснользоиание аргумеитоа уля возврота зночеииО из нрпцсууры
1:	Sub FirstNonBlankCell(nRow As Long, nCol As Long)
2:	'Просматривает в цикле первые 16 строк и 10 столбцов активного
3:	'листа в поисках непустой ячейки. Возвращает номера строки и столбца
4:	'в переменных nRow и nCol. Если не иайдеио ни одной непустой
5:	'ячейки в первык 16 строках и 10 столбцах,
6:	'возвращает 0 в обеих переменных.
7:	Dim Rcount	As	Integer
8:	Dim Ccount	As	Integer
9:
10:	nRow = 0	'начальные установки
11:	nCol = 0
12:
13:	'если активен не РАБОЧИЙ лист, прерываем выполнение
14:	If TypeName(ActiveSheet) о "Worksheet” Then Exit Sub
15:
16:	For Rcount = 1 To 16
17:	For Ccount = 1 To 10
18:	If Not IsEmpty(Cells(Rcount, Ccount).Value) Then
19:	nRow	= Rcount
20:	nCol	= Ccount
21:	Exit	Sub
День 12-й. Модульное программирование
403
22:	End If
23:	Next Ccount
24:	Next Rcount
25:	End Sub
26:
27:
28:	Sub Test FirstNonBlank()
29:
30:	Dim	Rows As Long
31:	Dim	Cols As Long
32:	Dim	OldSheet As Object
33:	Static sName As String
34:
35:	Set OldSheet = ActiveSheet
36:
37:	sName = InputBox("Введите имя рабочего листа:")
38:	Worksheets(sName).Select
39:	FirstNonBlankCell nRow:»Rows, nCol:=Cols
40:
41:	If Rows = 0 Then
42:	MsgBox "He найдено ни одной непустой ячейки."
43:	Else
44:	MsgBox "Первая непустая ячейка находится в строке " &
45:	Rows & ", столбец “ & Cols
46:	End If
47:	OldSheet.Select
48:	End Sub
В строках 1-25 находится определение процедуры FirstNonBlankCell. У нее два обязательных аргумента, оба являются числами типа Long и передаются по ссылке. Первый из них, nRow, представляет координату строки, а второй, nCol, — столбца. Поскольку VBA передает эти два аргумента по ссылке (способ передачи, принятый в VBA по умолчанию), то каждый раз, когда FirstNonBlankCell меняет их значения, значение первоначальных данных тоже меняется. Эта процедура использует переменные в качестве аргументов, чтобы передать их значения назад вызвавшей ее процедуре.
В связи с тем что 0 не является разрешенным в Excel значением для координат ряда или столбца, FirstNonBlankCell сообщит, что нельзя найти непустую ячейку, если nRow и nCol имеют значения 0.
Эта процедура должна вести в рядах и столбцах листа повторяющиеся операции (циклы), поэтому в качестве счетчиков цикла нужны две переменные, одна для строк, другая для столбцов. В строках 7 и 8 описаны такие счетчики: Rcount для строк и Ccount для столбцов.
В строках 10 и 11 значение 0 присваивается соответственно переменным nRow и nCol. Предположение, что процедура не сможет найти непустую ячейку, упрощает ее дальнейшую работу. Ей надо только определить, найдена ли такая ячейка, и больше ничего не делать.
В строке 14 используется функция TypeName в однострочной инструкции If .. .Then, чтобы проверить, является ли текущий лист рабочим листом Excel (т.е. не листом диаграмм и т.п.), и предотвратить в дальнейшем ошибки времени выполнения. Если лист таковым не является, тогда ячеек, которые надо проверять, просто нет, и процедура должна немедленно закончиться (Exit Sub). Поскольку значения, указывающие
404
Неделя 2
на неудачу при обнаружении непустой ячейки, были уже присвоены переменным аргументов в строках 10 и 11, то больше ничего делать не надо — процедура просто завершается.
В строках 16-24 находится цикл For.. .Next, предназначенный для продвижения по первым 16 рядам листа. Верхний предел 16 был выбран произвольно, так как 16 строк приблизительно соответствуют их максимальному числу, которое видно при отображении на полный экран (для большинства компьютерных систем).
В первой строке тела цикла For...Next, который считает по строкам, (строка 17 листинга), активизируется вложенный цикл For...Next (он занимает строки 17-23). Второй цикл считает по первым 10 столбцам листа. Верхний предел 10 был выбран произвольно, так как 10 столбцов приблизительно соответствуют их максимальному числу, которое видно при отображении листа на полном экране.
Телом вложенного цикла For...Next является единственная инструкция If...Then, в которой для проверки того, пуста ли ячейка в текущей строке и текущем столбце, используется функция IsEmpty. Если ячейка непустая, то в строках 19 и 20 текущие значения счетчиков строк и столбцов присваиваются соответственно переменным аргументов nRow и nCol. Затем в строке 21 процедура FirstNonBlankCell завершается. Теперь значения nRow и nCol отражают место расположения первой непустой ячейки и поступают в распоряжение той процедуры, которая вызвала FirstNonBlankCell.
В строках 28-48 находится простая процедура Test_FirstNonBlank, которую можно использовать для тестирования FirstNonBlankCell. В строке 28 содержится определение процедуры Test_FirstNonBlank, а в строках 30-33 — описания некоторых ее переменных. Первые из них, Rows и Cols, используются как аргументы FirstNonBlankCell. Объектная переменная OldSheet используется для удобства, чтобы сохранить и восстановить лист, активный в момент запуска Test_FirstNonBlank. Наконец, статическая переменная sName используется для хранения имени листа, который надо проверить с помощью FirstNonBlankCell. Эта переменная описана как статическая для удобства при неоднократном проведении тестирования.
В строке 35 сохраняется ссылка на лист, активный в момент запуска Test FirstNonBlank. В строке 37 применяется функция InputBox, чтобы получить имя листа, на котором надо протестировать FirstNonBlankCell, а в строке 38 этот лист выбирается.
В строке 39 вызывается процедура FirstNonBlankCell с передачей ей Rows и Cols в качестве аргументов. Обратите внимание, что процедура Test_FirstNonBlank не присваивает значений ни одному из них. Когда VBA выполняет эту инструкцию, он вызывает процедуру FirstNonBlankCell, которая работает так, как было описано в начале анализа этого фрагмента. Когда вызванная процедура закончит работу, VBA продолжит выполнение процедуры TestJFirstNonBlank со строки 41.
Строка 41 начинается с инструкции If.. .Then.. .Else, которая вычисляет значение переменной Rows. Если эта переменная равна 0, это значит, что процедура FirstNonBlankCell не нашла ни одной непустой ячейки и выполнение программы продолжается со строки 42, когда на экран выходит сообщение о том, что непустые ячейки не найдены.
Однако если переменная Rows не равна 0, это значит, что процедура FirstNonBlankCell нашла непустую ячейку, координаты строки и столбца которой хранятся в переменных Rows и Cols. Выполнение программы продолжается со строки 44, когда на экран выводится сообщение о номере строки и столбца найденной ячейки.
Снова обратите внимание, что процедура Test_FirstNonBlank не содержит никаких инструкций присвоения значений ни переменной Rows, ни Cols. Значения присваиваются им в процедуре FirstNonBlankCell; так как аргументы этой процедуры передаются
День 12-й. Модульное программирование
405
по ссылке, любые их изменения отображаются на первоначальном источнике значений аргументов, указанном при вызове процедуры. FirstNonBlankCell использует свои аргументы, чтобы вернуть их значения вызвавшей ее процедуре.
Преимущество использования в процедуре аргументов, передаваемых по ссылке, по сравнению с функцией в том, что с их помощью за один раз можно вернуть несколько значений.
Так надо
Используйте процедуру со способом передачи аргументов ByRef только тогда, когда за один раз нужно вернуть несколько значений. (Помните, если вы пропустите ключевые слова ByRef или ByVai, то VBA по умолчанию передаст аргументы по ссылке.)
Для возврата одного значения всегда используйте функцию.
Помните, что окно Просмотр объектов можно использовать для поиска и просмотра процедур, а также ввода комментариев к ним точно так же, как это делалось для функций. Об использовании окна Просмотр объектов рассказывается на 6-м уроке.
Так не надо
Не используйте в функциях передачу аргументов по ссылке для получения значений, не являющихся результатом функции. Функции всегда должны возвращать одно значение и никого не должны менять свои аргументы.
Резюме
На этом занятии вы узнали, как организовать часто используемые функции в библиотеку и сделать ее доступной для других проектов. Теперь вы можете организовать доступ к библиотечным проектам, разместив их в каталоге автозагрузки, чтобы они автоматически открывались каждый раз, когда запускается Excel. Кроме того, вы научились делать ссылки из одного проекта на другой.
Теперь вы знаете, как VBA определяет видимость переменных, констант, процедур, функций и определенных пользователем типов и какая разница между видимостью на уровне процедуры, private и public. Теперь вам известно, как расширять или ограничивать видимость переменных, констант, процедур, функций и определенных пользователем типов, добавив к их описаниям ключевые слова Public или Private. Мы описали использование префикса модуля для ссылок на элементы из модулей и проектов. Эти префиксы можно применять только в работе с элементами книг, на которые есть ссылки.
Далее, в этой главе было показано, как использовать наиболее эффективные приемы — проектирование сверху вниз вместе с пошаговой детализацией при разработке больших программ. Вы узнали, как одни процедуры могут вызывать другие и как использовать разные модули для организации процедур и функций, из которых состоит ваша программа.
Эта глава показала, как создавать и использовать для процедур списки аргументов, когда и почему это надо делать и как определять список аргументов. И наконец, вы ознакомились с примерами использования аргументов процедуры для отправки ей информации и возврата от нее значений.
406
Неделя 2
Вопросы о ответы
Обязательно ли создавать библиотечную книгу?
Нет, не обязательно. Однако это может сократить время, потраченное на переписывание по несколько раз одного и того же текста программы. Кроме того, если вы назначили кнопкам панелей инструментов и пунктам меню некоторые процедуры, о чем говорится в уроке 17-го дня, то где гарантия, что эти процедуры будут всегда доступны? Лучше всего будет поместить эти процедуры в библиотечный проект. Вообще говоря, выгоды от создания такого проекта намного окупают труды, затраченные на его создание.
Я немного путаюсь, когда речь идет об уровне видимости для определенных пользователем типов. Вы имеете в виду видимость переменных такого типа или его определения?
Видимость определенного пользователем типа относится к уровню видимости, при котором доступно определение такого типа. Переменные определенного пользователем типа можно описывать только тогда, когда определение этого типа доступно в том месте, где описываются переменные. Если определение пользовательского типа описано ключевым словом Private, то этот тип доступен только внутри модуля. Это значит, что переменные определенного пользователем типа можно описывать только в том модуле, в котором находится определение этого типа. Если требуется описать переменные этого типа в другом модуле, то необходимо определение пользовательского типа сделать public, чтобы оно было доступно за пределами своего модуля.
Я немного путаюсь. Если видимость переменной уровня модуля уже ограничена модулем, где она описана, какой тогда эффект от использования ключевого слова Private вместо Dim?
Вы правы: здесь легко запутаться. Абсолютно нет никакой разницы, использовать ли при описании переменной уровня модуля ключевые слова Private или Dim . В обоих случаях видимость переменной ограничена модулем, в котором она описана. Единственной причиной использовать в данной ситуации ключевое слово Private является то, что надо подчеркнуть разницу между переменными с видимостью private по отношению к модулю и переменными того же модуля, которые описаны с помощью ключевого слова Public.
Я также не понимаю эффект от добавления к процедуре, функции или определению пользовательского типа ключевого слова Public, поскольку они как бы по определению имеют видимость public. Зачем мне использовать Public для их описания?
Абсолютно нет разницы в видимости процедуры, функции или определенного пользователем типа, описанных с помощью или без помощи ключевого слова Public. Оно используется, чтобы в пределах модуля подчеркнуть разницу между процедурами и функциями с видимостью public и теми, что описаны с помощью ключевого слова Private.
Когда я использую аргументы процедуры, должен ли я посылать информацию или только принимать ее?
Хотя в этой главе два примера процедур со списками аргументов показывают, как аргументы одной процедуры используются только для посылки информации, а аргументы другой — только для возврата значений, нет нужды себя ограничивать, делая только одно или другое. Например, можно легко видоизменить процедуру FirstNonBlankCell из листинга 12.5, чтобы она принимала имя тестируемого листа как один из своих аргументов. Ее описание выглядело бы следующим образом.
FirstNonBlankCell(ByVai SheetName As String,
ByRef nRow As Long,
ByRef nCol As Long)
День 12-й. Модульное программирование
407
Аргумент SheetName предоставляет процедуре информацию (имя тестируемого листа), а два других аргумента, как и раньше, возвращают значения. Одна вещь, которую не стоит делать, — это использовать один и тот же аргумент и для отправки, и для возврата информации. Конечно, было бы неплохо организовать в этих целях принцип “Фигаро — тут, Фигаро —там”, но это может привести к путанице с программами.
Коллоквиум
Ответы вы найдете в приложении.
Тест
1.	Как VBA ведет поиск процедуры?
2.	Как пользуются окнами Object Browser и Макросы для просмотра списка доступных функций?
3.	Как создать библиотечный проект?
4.	Какие есть два способа сделать процедуры из библиотечного проекта доступными в окне Макросы?
5.	Как сделать процедуры и функции из библиотечного проекта доступными для использования в программе из другого проекта?
6.	Какое свойство объекта Application возвращает имя каталога автозагрузки? Можно ли поменять его имя с помощью этого свойства?
7.	Какое свойство объекта Application возвращает имя альтернативного каталога автозагрузки? Можно ли поменять его имя с помощью этого свойства?
8.	Как создать ссылку на другой проект?
9.	Что такое область видимость private? А что такое public?
10.	Какой эффект дает применение инструкции Option Private Module?
11.	Какой эффект дает использование ключевого слова Private? Ключевого слова Public?
12.	Можно ли использовать ключевые слова Public и Private для изменения видимости переменной уровня процедуры?
13.	Каковы две основные причины для создания списка аргументов процедуры?
14.	Показывает ли окно Макросы процедуры с аргументами? А окно Object Browser?
15.	Если для возврата значения требуется использовать аргумент процедуры, то как его передавать: по ссылке или по значению?
Упражнения
1.	Листинг перед вами представляет целый модуль. Добавьте в каждое описание в модуле ключевые слова Public или Private , чтобы каждый его элемент, у которого обычно видимость private, стал public и наоборот. Затем добавьте необходимые инструкции, чтобы ни один из этих элементов не использовался никаким проектом, за исключением того, где находится этот модуль. (Для простоты текст тела программ и функций пропущен.)
408
Неделя 2
Option Explicit
Private Type InvoiceHeader CustomerName As String CustomerNumber As Integer InvoiceNumber As Long InvoiceDate As Date
End Type
Public Spinning As Boolean
Public Currentinvoice As Long
Public InvoiceListf) As InvoiceHeader
Private Sub Fetchinvoice{)
' тело процедуры
End Sub
Private Sub ShutDownf) ' тело процедуры
End Sub
Private Sub LocatelnvoiceBodyf) ' тело процедуры
End Sub
Private Sub DisplayLineltemsf)
' тело процедуры
End Sub
Private Function InvoiceTotal() As Currency ' тело процедуры
End Function
2.	Спроектируйте сверху вниз баланс вашей чековой книжки. Схема должна включать следующее
•	Вначале программа должна спросить остаток на вашем счету.
•	Программа должна в циклическом режиме принимать данные об операциях до тех пор, пока вы не решите закончить ввод. Внесение денег на счет обозначается положительным числом, а снятие — отрицательным.
•	При вводе операции программа должна проверить, ввели ли вы правильное число, и в случае необходимости вывести на экран соответствующее сообщение об ошибке.
•	После ввода операции программа должна вывести на экран сумму операции, кредитовая она или дебетовая, а также новый баланс. Если баланс на счете становится ниже $100, то программа должна выводить предупреждение.
3.	Напишите программу на VBA по схеме, созданной в упражнении 2.
День 12-й. Модульное программирование
409
Управление файлами с помощью
Visual Basic
Рано или поздно вам понадобится, чтобы ваша процедура могла скопировать или удалить какой-нибудь файл, например книгу Excel. Может быть, вам потребуется переименовать файл или найти нужный файл на диске. В языке Visual Basic for Applications предоставлен широкий набор инструкций, функций и методов для самых общих задач управления файлами. В ходе этого занятия мы рассмотрим следующие вопросы.
•	Что такое атрибуты файла и как получить информацию об атрибутах или изменить один из атрибутов файла.
•	Как получить информацию о файле, такую как его размер или дата последнего изменения файла.
•	Как создавать на диске директории (папки).
•	Как копировать, перемещать, переименовывать или удалять файлы на диске.
•	Как узнать, какой диск и папка являются текушими и как изменить текущую папку или диск.
•	Как искать файлы по образцу имени, используя подстановочные знаки, такие как *.xls.
•	Как в ваших процедурах создавать такие же окна открытия или сохранения файла, как в приложении Excel.
Понятие об управлении файлами
В этом разделе мы прежде всего выясним, что такое управление файлами и каковы типичные действия для выполнения его задач. Затем вашему вниманию будет предложен обзор соответствующих функций, инструкций и методов Visual Basic for Application.
Что такое управление файламп
Когда говорят об управлении файлами, имеют в виду операции, выполняемые с файлами на диске вашего компьютера. Оно включает в себя такие действия, как их резервное копирование, удаление ненужных файлов с целью очистить место на диске,
410
Неделя 2
перенос с одного диска или каталога на другой и создание или удаление папок на диске. Есть также возможность просматривать в папке список файлов и определять их размер, а также дату и время последнего изменения.
ПРИМЕЧАНИЕ
На рабочем столе Windows и в ее справочной системе термином папка называется то, что опытные пользователи Windows и DOS знают как каталоги. В соответствии с современной терминологией в этой книге применяется термин папка, но в некоторых случаях используется и каталог.
Возможно, вы занимаетесь управлением файлами каждый день, хотя, может быть, свои действия так не называете. Каждый раз, когда вы используете рабочий стол, проводник или команды DOS, чтобы скопировать, удалить или переименовать файлы, просмотреть их список в папке, а также создать или удалить подкаталог, вы выполняете управление файлами. VBA предоставляет вам возможность выполнять все подобные операции с помощью процедуры или функции.
Средства Visual Basic для дправлвпия файлами
В табл. 13.1 перечислены функции, инструкции и методы VBA, предназначенные для управления файлами. В первом столбце находится ключевое слово VBA, во втором — к чему оно относится: к функции, к инструкции или к объектному методу. Наконец, в третьем столбце находится описание его применения.
Таблица 13.1. Функции, методы и инструкции для управления файлами
Имя	Категория	Применение
ChDir	Инструкция	Меняет текущий каталог (папку)
ChDrive	Инструкция	Меняет текущий дисковод
CurDir	Функция	Возвращает в виде строки текущий каталог (папку)
Dir	Функция	Возвращает имя каталога или файла, соответствующее образцу (включая подстановочные знаки), передаваемому как строковый аргумент. Используется для поиска на диске одного или более файлов
FileCopy	Инструкция	Копирует файл
FileDateTime	Функция	Возвращает значение типа Date, содержащее дату и время последнего изменения файла
FileLen	Функция	Возвращает длину файла в байтах
GetAttr	Функция	Возвращает число, представляющее комбинированные атрибуты файла или каталога, такие как системный, скрытый и т.д.
GetOpenFileName	Метод	Выводит на экран окно Открытие документа системы Excel и возвращает имя файла, выбранное пользователем. Недоступен в Word
GetSaveAsFileName	Метод	Выводит на экран окно Сохранение документа системы Excel и возвращает имя файла, выбранное пользователем. Недоступен в Word
День 13-й. Управление файлами с помощью Visual Basic
411
Окончание табл. 13.1
Имя	Категория	Применение
Kill	Инструкция	Удаляет файлы с дисковода
MkDir	Инструкция	Создает на диске каталог (папку)
Name	Инструкция	Переименовывает или перемещает файл
RmDir	Инструкция	Удаляет дисковый каталог (папку)
SetAttr	Инструкция	Задает атрибуты файла
Вы, возможно, обратили внимание, что в табл. 13.1 ни в одной строке нет аргументов. Некоторые же из этих функций, инструкций и методов имеют достаточно сложные наборы аргументов. Начиная со следующего раздела этой главы все, что перечислено в таблице, будет тщательно изучено во всех деталях.
Инструкции, функции и объектные методы управления файлами в соответствии с выполняемой работой можно условно разбить на шесть категорий.
•	Получение и изменение атрибутов файла.
•	Определение имени файла.
•	Получение или изменение дисковода и папки, а также создание или удаление папок.
•	Копирование и удаление файлов.
•	Переименование и перемещение файлов.
•	Получение информации о файлах, такой как их длина, а также дата и время последнего изменения.
В последующих разделах главы рассказывается о каждой из этих категорий и приводится описание всех относящихся к ней инструкций VBA.
Атрибуты файла
Перед тем как детально знакомиться с функциями, инструкциями и объектными методами, относящимися к управлению файлами, вам необходимо уяснить, что такое атрибуты файлов и что они означают. Данный раздел с этого и начинается, затем идет описание функции, с помощью которой получают информацию об атрибутах файла, и инструкции, с помощью которой эти атрибуты меняют.
Знакомство с атрибутами файлов
Каждый файл на диске имеет атрибуты, какого бы типа ни был дисковод (для жесткого или гибкого диска, RAM-, ZIP- и т.д.). Windows и DOS используют атрибуты файла, чтобы определить, какие действия по управлению файлами допустимы по отношению к нему. Например, эти системы запрещают вам — и любой прикладной программе — удалять, модифицировать или переименовывать файлы с атрибутом Только чтение.
Windows создает атрибуты, когда вами или работающей на компьютере прикладной программой создается файл. В сущности, атрибуты файла похожи на свойства объекта: они дают файлу некоторую характеристику. Атрибуты — это часть информации о файле, хранящейся на дисковом носителе. Windows хранит эту информацию вместе с именем файла, размером и данными о дате и времени.
472
Неделя 2
Windows автоматически поддерживает и обновляет атрибуты файла. В основном вам не надо о них беспокоиться, и обычно нет причины знать, что это такое — атрибуты файла. Но в некоторых случаях уметь пользоваться ими очень важно. Знакомство с ними и их значениями особенно пригодится при работе с функцией Dir системы VBA.
Для определения характеристик файла система Windows (и DOS) применяет всего семь атрибутов файлов. Каждый из них может быть скомбинирован с другими атрибутами, за исключением метки тома. Например, у файла одновременно могут быть атрибуты Скрытый, Системный, Каталог, Архивный и Только чтение. Ниже перечислены имена и значения всех таких атрибутов.
•	Архивный. Указывает, был ли файл изменен с того времени, как было проведено последнее резервное копирование такими программами, как Windows BACKUP, или средствами других производителей, например Fastback.', Backlt, Norton Backup и т.д. Если у файла установлен атрибут Архивный, то ему требуется резервное копирование. Если такого атрибута нет, то со времени последней такой операции файл изменен не был.
•	Каталог. Если у файла атрибут Каталог, то это означает, что он в действительности каталог или подкаталог (по терминологии Windows папка). Дисковый каталог в сущности файл, в котором хранится информация о других файлах; при создании каталога Windows создает соответствующий файл с атрибутом Каталог. Этот атрибут позволяет распознать файл с данными о других файлах и не дает его переименовать, скопировать или удалить как обычный файл данных.
•	Скрытый. Если у файла атрибут Скрытый, то Windows “скрывает” такой файл, не показывая его в большинстве случаев вывода каталогов на экран. Однако имеется режим просмотра, позволяющий показывать имена таких файлов.
•	Обычный. Если у файла атрибут Обычный, то это, в сущности, означает отсутствие у него каких-либо специальных атрибутов. Этот так называемый обычный атрибут файла (иногда называемый общим) только означает, что других атрибутов файла нет, кроме, возможно, архивного, если требуется резервное копирование.
•	Только чтение. Означает, что файла можно считывать, но не изменять. Windows запрещает изменять, удалять или переименовывать файлы с атрибутом Только чтение.
•	Системный. Указывает Windows, что файл является частью операционной системы компьютера. Это накладывает запрет на изменение файла, как и в случае с атрибутом Только чтение. Если с помощью DOS-команды Sys (или панели управления Windows) создается диск для загрузки, то на него переносятся все файлы с атрибутом Системный.
•	Метка тома. Указывает Windows, что файл является меткой тома на диске. (Метка — это имя, которое дается жесткому или гибкому диску при его форматировании; для этого используется команда DOS LABEL или меняется свойство Метка в окне свойств диска.) Метка тома, в сущности, — не полный файл, а только имя файла в корневом каталоге диска, имеющее атрибут файла Метка тома. На диске может быть только одна метка.
Так не надо
Не путайте открытие книги Excel в режиме только для чтения с атрибутом файла Только чтение. В первом случае речь идет о том, разрешает ли Excel делать изменения в файле. А во втором случае это касается уровня всей операционной системы. Файл с атрибутом Только чтение нельзя ни изменить, ни удалить ни в одном приложении. Если Excel открывает книгу с таким атрибутом, то там устанавливается режим только для чтения и никаких изменений вносить нельзя.
День 13-Й. Управление файлами с помощью Visual Basic
413
В Windows каждый отдельный атрибут фала представлен уникальным числовым кодом и сохраняется вместе с именем файла и данными о его размере. Если у файла несколько атрибутов, то их коды складываются, а сумма сохраняется. В следующем разделе рассказывается, как можно прочитать и интерпретировать такое число.
В табл. 13.2 перечислены все используемые коды атрибутов и соответствующие константы VBA. Как и в случае с другими числовыми кодами, для которых в VBA определены константы, вместо самого кода необходимо использовать его константу.
Таблица 13.2. Константы Visual Basic для атрибутов файлов
Константа VBA	Десятичное значение	Двоичное значение	Атрибут
vbNormal	0	00000000	Обычный
vbReadOnly	1	00000001	Только чтение
vbHidden	2	00000010	Скрытый
vbSystem	4	00000100	Системный
vbVolume	8	00001000	Метка тома
vbDirectory	16	00010000	Каталог
vbArchive	32	00100000	Архивный. Если установлен, то файл был изменен после последнего резервного копирования
Чтение атрибута файла
Функция GetAttr возвращает числовое значение, содержащее сумму всех кодов атрибутов файла.
Функция имеет следующий синтаксис.
GetAttr(pathname)
Здесь pathname —любое строковое выражение, представляющее имя файла, соответствующее стандартным правилам; может содержать букву дисковода и полный путь поиска по каталогам. Если буквы дисковода нет, то поиск будет выполняться на текущем диске; при отсутствии полного пути поиск файла будет проводиться в текущей папке.
GetAttr возвращает число, представляющее сумму всех числовых кодов атрибутов файла.
В листинге 13.1 показан пример использования функции GetAttr и интерпретация ее результата.
Листинг 13.1. Иснользоваиие фцикции беТЛИг и интернретация ее резужыпата
1:	Sub ShowFileAttr(fName As String)
2:	'Выводит сообщение с атрибутами файла,
3:	'имя которого передано в fName.
4:
5:	Dim fAttr As Integer
6:	Dim mStr As String
7:
8:	fAttr = GetAttr(fName)
9:	mStr = UCase(fName)
10:	mStr = mStr & " имеет следующие атрибуты: " & vbCr
11:	If (fAttr And vbReadOnly) Then
414
Неделя 2
12:	mStr = mStr & "Только чтение" & vbCr
13:	If (fAttr And vbHidden) Then
14:	mStr = mStr & "Скрытый" & vbCr
15:	If (fAttr And vbSystem) Then _
16:	mStr = mStr & "Системный" & vbCr
17:	If (fAttr And vbVolume) Then _
18:	mStr = mStr & "Volume" & vbCr
19:	If (fAttr And vbDirectory) Then _
20:	mStr = mStr & "Директорий" & vbCr
21:	If (fAttr And vbArchive) Then _
22:	mStr = mStr & "Архивный" & vbCr
23:	MsgBox mStr
24:	End Sub
25:
26:	Sub ListFileAttr()
27:
28:	Dim strPath As String
29:
30:	ShowFileAttr fName:="c:\io.sys"
31:	ShowFileAttr fName:="c:\msdos.sys“
32:
33:	strPath = Application.StartupPath
34:	ShowFileAttr fName:=strPath
35:
36:	strPath = strPath & "\Personal.xls"
37:	ShowFileAttr fName:=strPath
38:	End Sub
В листинге 13.1 представлены две различные процедуры. Первая, ShowFileAttr, находится в строках 1-24, а вторая, ListFileAttr, — в строках 26-38. Вторая процедура просто несколько раз вызывает первую, каждый раз передавая другое имя файла.
В строке 1 находится описание процедуры ShowFileAttr, у которой имеется один обязательный аргумент с именем fName и типом данных String. В строке 5 описана переменная fAttr с типом данных Integer, предназначенная для хранения значения, возвращаемого функцией GetAttr. В строке 6 описана переменная mStr с типом данных String, в которой будет храниться сообщение, создаваемое этой процедурой.
Когда VBA выполняет инструкцию из строки 8, то он вызывает GetAttr, передавая в качестве аргумента строку, хранящуюся в переменной fName; результат выполнения сохраняется в переменной fAttr. Если файл с указанным именем не найден на диске, то на экран выводится сообщение об ошибке времени выполнения с текстом: File not found.
В строках 9 и 10 находится первая часть создаваемого процедурой сообщения. В первой из них находится функция UCase, предназначенная для преобразования fName в верхний регистр.
В строках 11-22 находится несколько инструкций If...Then, оценивающих хранящееся в fAttr число, которое является суммой числовых кодов атрибутов файла. Обратите внимание, что эти инструкции не являются вложенными. Наоборот, значение fAttr тестируется шесть раз и при этом проверяется каждое возможное значение. Помните, что значение атрибутов, хранящееся вместе с файлом, является суммой кодов всех имеющихся в файле атрибутов. Если некоторая инструкция If.. .Then находит интересующий ее атрибут, то в строку из mStr добавляется его имя.
День 13-й. Управление файлами с помощью Visual Basic
415
Побитпвав срашне
Возможно, вам не совсем ясно, как работают логические выражения в строках 11-22 листинга 13.1. Это объясняется тем, что когда с числовыми значениями используются логические операторы (And, Or, Xor, Not и тд), VBA оперирует отдельными битами, из которых состоят числа.
Взгляните еще раз на табл. 13.2 и обратите внимание, что значения кедов атрибутов не являются последовательными числами. Присмотритесь к столбцу Двоичное значение и заметьте, что там значение каждого атрибута состоит из своей комбинации нулей и единиц. Кроме того, каждое значение атрибута представляет собой степень двойки, так что если написать его в двоичной форме (как в табл. 13.2), то он будет единицей или единицей с последующими нулями.
Когда VBA выполняет побитовое сравнение, в каждом числе сравниваются двоичные цифры. В зависимости от логического действия над каждой парой битов двух чисел выполняется оператор And, Or, Xor, Not или Imp. Если результат сравнения True (истина), то VBA присваивает значение 1 соответствующей цифре ответа; если результат False (ложь) - то 0. Поскольку результатом логической операции And будет True , если оба аргумента True, то результатом выражения 1 And 1 будет True.
В выражениях листинга 13.1 над значениями переменной f Attr и одной из констант атрибута файла выполняется оператор And, производя их побитовое сравнение. Например, если у файла имеются атрибуты Скрытый и Только чтение, то значением f Attr будет 3 (1+2; см. табл. 13.2). В двоичной системе 3 будет записано как 11. Двоичным значением Только чтение будет 01, а Скрытый - соответственно 10. Результатом выражения 11 And 01 будет True, что означает наличие атрибута Только чтение.
В строке 23 выводится сообщение, созданное предыдущими инструкциями, а в строке 24 процедура ShowFileAttr завершается.
В строке 26 дано описание процедуры ListFileAttr, которая просто несколько раз вызывает ShowFileAttr, передавая ей различные имена файлов. В строке 30 процедура ShowFileAttr выводит на экран атрибуты файла IO.SYS. Он является частью операционной системы Windows и всегда расположен в корневом каталоге диска начальной загрузки. Как часть операционной системы файл IO.SYS имеет атрибут Системный; у него еще есть атрибут Только чтение, который предотвращает удаление этого жизненно важного файла. Кроме того, он имеет атрибут Скрытый, так что его обычно не видно в окнах файлов Windows или в списках каталогов программы проводник. При выполнении строки 30 на экране появляется сообщение, показанное на рис. 13.1.
Microsoft Excel
CVOSYS имеет следующие атрибуты:
Только чтение
Скрытый
Системный
Рис. 13.1. Процедура ShowFileAttr выводит такое сообщение для файла IO.SYS, находящегося на диске начальной загрузки Windows
Аналогичное сообщение выводится процедурой ShowFileAttr для файла C:\MSDOS.SYS. Обратите внимание, что в строке 34 вместо имени файла задается имя папки (папка автозагрузки). После того как выполнится эта строка, на экране появится сообщение, в котором будет говориться об атрибуте Каталог. (Помните, пап
416
Неделя 2
ка и каталог — одно и то же.) Последнее сообщение, в зависимости от того, выполняли ли вы недавно резервное копирование Personal.exe или нет, выведет или не выведет атрибут Архивный.
ПРИМЕЧАНИЕ
В процедуре ShowFileAttr нет тестирования атрибута Обычный, так как он просто означает отсутствие всех остальных атрибутов файла.
Так надо
Все переменные, предназначенные для хранения значений кодов атрибутов, надо описывать как имеющие тип Integer. Эти числа никогда не превысят установленный для Integer предел.
Так не надо
Не забывайте, что коды атрибутов файла всегда обрабатываются как одно число, являющееся суммой кодов отдельных атрибутов.
Изменение ampufigma файла
Иногда вам может понадобиться изменить атрибуты файла. Например, вам требуется изменить атрибуты шаблона книги так, чтобы среди них появился атрибут Только чтение. Это нужно для того, чтобы шаблон не был случайно удален.
Для изменения атрибутов файлов используется инструкция SetAttr. Она выполняет те же действия, что и команда Файл, Свойства (доступна в любом окне Windows просмотра папок или по щелчку правой кнопки мыши на значке файла), а также команда DOS ATTRIB.
Инструкция имеет следующий синтаксис.
SetAttr pathname, attributes
Здесь pathname представляет любое строковое выражение, содержащее соответствующую правилам Windows спецификацию файла (имя и, возможно, полный путь поиска по каталогам, а также букву дисковода). Выражение attributes представляет числовое выражение, которое должно быть числом от 0 до 255 и является числом файлового атрибута или суммой таких чисел.
В листинге 13.2 показан пример использования инструкции SetAttr.
Листинг 13.2, Иснольздваине инструкции SetAttr удя изменения атрибутов файда
1:	Sub DelProtectFile(fName As String)
2:	'Устанавливает атрибут Read-only для файла fName
3:
4:	Dim fAttr As Integer
5:
6:	fAttr = GetAttr(fName)
7:
8:	'Если атрибут уже установлен, не устанавливаем повторно
9:	If Not CBoolf(fAttr And vbReadOnly)) Then
10:	SetAttr fName, fAttr + vbReadOnly
11:	End If
День 13-й. Управление файлами с помощью Visual Basic
417
12:
13:	MsgBox prompt:="Атрибут Только чтение для файла " &
14:	fName & " установлен.",
15:	Т1Л1е:="Защита от удаления"
16:	End Sub
17:
18:
19:	Sub Test_DelProtectFile()
20:	Dim iName As String
21:
22:	iName = Application.GetOpenFilename
23:	DelProtectFile iName
24:	ShowFileAttr iName
25:	End Sub
Процедура DelProtectFile защищает файлы от случайного удаления или изменения путем задания них атрибута Только чтение. Другие атрибуты при этом не изменяются. У этой процедуры имеется один обязательный аргумент с именем fName и типом String, который используется для передачи ей имени файла. В строке 4 описана переменная fAttr, используемая процедурой для хранения значения атрибутов файла.
В строке 6 для получения атрибутов у файла, указанного с помощью fName, используется GetAttr. В строках 9-11 находится инструкция If, которая выполняет SetAttr лишь тогда, когда у файла нет атрибута Только чтение. В строке 10 непосредственно вызывается SetAttr. Обратите внимание, что для получения нового значения атрибутов их старые значения арифметически складываются с константой vbReadOnly. Таким образом сохраняются эти значения.
Наконец, в строках 13-15 находится инструкция MsgBox, выводящая на экран сообщение, что у файла задан атрибут Только чтение.
Процедура Test_DelProtectFile непосредственно выполняет DelProtectFile. В строке 22 для получения от вас имени файла используется относящийся к Excel метод GetOpenFileName (о нем рассказывается в следующем разделе). Затем в строке 23 вызывается процедура DelProtectFile, чтобы добавить к остальным атрибут файла Только чтение. Наконец, в строке 24 вызывается процедура ShowFileAttr (см. листинг 13.1); она выводит на экран сообщение, позволяющее проверить новые атрибуты.
Так надо
Следует иметь в виду, что при задании вами атрибутов файла будут иметь только эти атрибуты. Чтобы добавить новые атрибуты, не изменив старые, надо вначале считать текущие атрибуты, прибавить к ним новые, а : затем задать новое значение атрибутов.
Так не надо
Не пытайтесь с помощью SetAttr задавать файлу атрибуты Каталог (vbDirectory) или Метка тома (vbVolume). иначе получите сообщение об ошибке времени выполнения.
Не добавляйте коды атрибутов существующего файла, не проверив отсутствия именно этих кодов. В противном случае вы можете получить совсем не те атрибуты, что вы ожидали.
478
Неделя 2
ПРИМЕЧАНИЕ
Чтобы добавить новый атрибут файла к существующему их набору, надо использовать оператор Or. Использование этой побитовой операции позволит избежать многих проблем, возникающих при арифметическом сложении кодов новых атрибутов со старыми. Результатом выражения 0 Or 1 будет 1, а результатом 1 Or 1 также будет 1. Благодаря этому можно комбинировать новый атрибут с уже имеющимся числовым значением, используя выражения, такие как OldAttr Or vbArchive. Если в OldAttr атрибут Архивный не задан, то это произойдет в результате операции Or. Если же задан, то останется без изменений.
Как получишь имя файла
В этом разделе мы расскажем, как с помощью VBA-функции Dir можно искать в каталоге файлы с именами, соответствующими указанному образцу. Потом вы научитесь применять в своих процедурах стандартные окна приложения Excel для открытия и сохранения файлов. Использование этих окон облегчит пользователю ввод в процедуру имени файла.
Поиск файлов с помощью функции Dir
Иногда вам может понадобиться посмотреть, нет ли в каталоге файлов, соответствующих образцу, такому как \Му Documents\*.xls. Для поиска на диске каталога используйте VBA-функцию Dir. Эта функция выполняет те же действия, что и списки файлов в окнах папок Windows или команда DOS DIR: она дает возможность видеть, какие файлы имеются в папке и нет ли в ней других папок.
Функция имеет следующий синтаксис.
Dir (pathname!, attributes])
Здесь pathname представляет выражение типа String, в котором находится имя файла, заданное в соответствие с правилами Windows. Имя файла может также содержать букву дисковода и полный путь по каталогам. Там могут также быть подстановочные знаки для поиска файла. Необязательный аргумент attributes — это число, представляющее атрибуты искомых файлов. Если этот аргумент указан, то Dir ищет соответствующие ему файлы. Если он пропущен, то поиск ведется среди обычных файлов, т.е. среди тех, которые не имеют атрибутов Скрытый, Метка тома, Каталог или Системный.
При вызове функции Dir с аргументом pathname возвращается строка с именем первого файла, соответствующего имени из аргумента.
Если в имени файла содержатся подстановочные знаки, такие как * или ?, то, чтобы найти в каталоге все соответствующие имени файлы, функцию Dir необходимо использовать в два этапа. Во-первых, надо вызвать ее с аргументом pathname, чтобы получить первый подходящий файл, а затем вызывать ее без аргументов, пока не будет получена пустая строка. Как только Dir вернет пустую строку, это будет означать, что подходящих файлов больше нет. Если после этого снова вызвать Dir без аргументов, то VBA выдаст сообщение об ошибке времени выполнения.
На листинге 13.3 показан пример использования функции Dir для обнаружения только одного файла.
День 13-Й. Управление файлами с помощью Visual Basic
419
Листинг 13.3. Обнаружение файла с помощью Dir
1:	Function IsDiskFile(fName As String) As Boolean
2:	'Возвращает True, если файл fName найден на диске, False - если нет
3:
4:	If (Dir(fName)	<> "") Then
5:	IsDiskFile = True
6:	Else
7:	IsDiskFile = False
8:	End If
9:	End Function
10:
11:
12:	Sub Test_IsDiskFile()
13:	Dim iName As String
14:
15:	iName = InputBox)prompt:="Введите нмя файла:11,
16:	ТШе:="Проверка функции IsDiskFile")
17:	MsgBox "Поиск файла" & iName & ”: " & IsDiskFile(iName)
18:	End Sub
В строках 1-9 находится функция IsDiskFile, у которой один обязательный аргумент fName типа String. Эта функция возвращает результат типа Boolean: True, если файл с именем из аргумента fName есть на диске, и False, если его нет.
Работа функции IsDiskFile очень проста. В строке 4 находится инструкция If.. .Then.. .Else, которая в своем логическом выражении вызывает функцию Dir, передавая ей для поиска содержимое fName в качестве имени файла. В этом вызове Dir нет никаких аргументов атрибутов, так что будет найден любой файл, за исключением тех, что имеют атрибуты Скрытый, Системный, Каталог или Метка тома.
Поскольку функция IsDiskFile непосредственно предназначена для того, чтобы определить, существует или не существует определенный файл, то результат выполнения функции Dir не используется ни для чего другого, кроме как для сравнения его с пустой строкой. Если этот результат не является пустой строкой, то файл найден и в строке 5 VBA выполняет присваивание функции IsDiskFile результата True. Если выдана пустая строка, то файл не найден и VBA в строке 7 выполняет присваивание той же функции результата False.
В строках 12-18 описана процедура тестирования функции IsDiskFile. Эта процедура для получения от пользователя имени файла использует функцию InputBox, а затем вызывает IsDiskFile как часть инструкции MsgBox, где говорится, существует ли файл с введенным именем.
Обратите внимание, что если вы вызываете IsDiskFile с именем файла, где имеются единичные (?) или множественные (*) подстановочные знаки, то при наличии хотя бы одного файла, удовлетворяющего этим критериям, функция возвратит True. (Полная информация по подстановочным знакам в именах файлов находится в справочной системе Windows.)
420
Неделя 2
При поиске файлов по именам можно использовать один из двух подстановочных символов: знак вопроса (?) или “звездочку” (*). Эти символы выполняют функцию, подобную функции символов поиска повторяющихся выражений в операторе Like (см. главу 4). В имени файла знак вопроса (?) означает любой единичный символ, а “звездочка” (*) - любое их количество. Рассмотрим два примера.
Book7.xls '
Book*.xls
В первом примере будет проводиться поиск любого файла с названием “Book”, за которым следует любой единичный символ, и с расширением .xls, таких как Bookl.xls, Book2.xls, Booky.xls и т.д. Во втором примере будет проводиться поиск любого файла с названием “Book”, за которым следует любое количество символов, и с расширением .xls, таких как BookEnd.xls, BookList.xls, Book Schedule.xls ит.д.
Можно также использовать функцию Dir для поиска в папке всех файлов, соответствующих определенному имени. Такое свойство этой функции наиболее полезно, когда в имени файла содержатся подстановочные символы. Функцию Dir можно использовать в случае, если в указанной папке необходимо найти все файлы шаблонов.
В листинге 13.4 показан пример использования функции Dir для обнаружения нескольких файлов.
Листинг 13.4. ЛОиаружвиие нескольких файлон с помощью Dir
1:	Sub ShowFilesf)
2:	'Выводит имена и атрибуты всех файлов в
3:	' указанном каталоге
4:
5:	Dim	sAttr	As	Integer
6:	Dim	fName	As	String
7:	Dim	pName	As	String
8:	Dim	fCount As Integer
9:
10:	pName = InputBox("Укажите каталог для поиска:")
11:	If Trim(pName) = "" Then Exit Sub
12:	If Right(pName, 1) <> "\" Then pName = pName & “\"
13:
14:	sAttr = vbDirectory + vbArchive + vbReadOnly + _
15:	vbHidden + vbSystem
16:
17:	'Ищем первый файл и считываем атрибуты
18:	fName = Dir(pName & “*.*", sAttr)
19:	If (fName <> "") And _
20:	((fName <> ".") And (fName <> "..")) Then
21:	ShowFileAttr pName & fName
22:	fCount = 1
23:	End If
24:
25:	Do While (fName <> "")
26:	fName = Dir()
27:	If (fName <> "") And _
День 13-й. Управление файлами с помощью Visual Basic
421
28:	((fName <> And (fName <> Then
29:	ShowFileAttr pName & fName
30:	fCount = fCount + 1
31:	End If
32:	Loop
33:
34:	MsgBox "Найдено " & fCount & " файлов."
35:	End Sub
Процедура ShowFiles предлагает пользователю ввести имя папки (каталога) и затем для поиска всех находящихся там файлов использует функцию Dir. В строках с 5 по 8 описано несколько переменных. Переменная sAttr используется для хранения числового значения атрибутов файла, fName — для временного хранения имен, возвращаемых Dir, и pName — для хранения пути к каталогу, в котором процедура ведет поиск. Переменная fCount предназначена для хранения числа всех найденных файлов.
В строке 10 вызывается функция InputBox, чтобы получить от пользователя имя каталога. В строке 11 проверяется, не отменил ли пользователь операцию ввода; если отменил, то процедура просто завершается. В строке 12 проверяется, что введенный пользователем путь по каталогам заканчивается символом разделения (\). Если указанного символа нет, то следует убедиться, что символ добавлен в конец пути по каталогам, который находится в pName.
В строке 14 задается значение переменной sAttr для хранения атрибутов разыскиваемых файлов. Обратите внимание, что эта переменная хранит значения всех атрибутов файлов, за исключением атрибута метка тома.
В строке 18 выполняется первый вызов функции Dir с использованием пути по каталогам из pName, к которому добавляется спецификация *.* (поиск всех файлов), а также передается sAttr, чтобы указать, по каким атрибутам выполнять поиск. Если указаны все атрибуты файлов (кроме метки тома), то Dir найдет любой файл из указанного каталога, включая любой его подкаталог. Результат выполнения этой функции будет присвоен переменной fName.
В строках 19—23 находится инструкция If...Then, которая оценивает строку, возвращенную функцией Dir и сохраняемую в fName. Обратите внимание, что логическое выражение этой инструкции в действительности разбито на две строки: 19 и 20. Первая его часть проверяет, не пустая ли fName. Вторая часть (строка 20) проверяет, нет ли в fName строки из одной точки (.) или из двух точек (..). Эти специальные строки используются файловой системой Windows для обозначения текущего каталога (.) и его родительского каталога (..). Хотя эти специальные имена технически являются каталогами и функция Dir сообщит об их существовании в соответствии с атрибутом и спецификацией подстановочных символов из этого примера, в действительности на диске их нет. Любая же попытка получить их атрибуты с помощью GetAttr закончится сообщением об ошибке времени выполнения.
Если fName непустая и не содержит специальных каталогов (. или ..), то VBA выполняет строку 21, где вызывается процедура ShowFileAttr из листинга 13.1, чтобы вывести на экран сообщение с именем файла и его настоящими атрибутами. Затем выполняется строка 22, где начинается счет найденных файлов; делается это путем присвоения переменной fCount значения 1.
Теперь, когда найден первый подходящий файл, для поиска оставшихся файлов каталога необходимо использовать формат функции Dir второго этапа. В строках 25-32 находится цикл Do, который выполняется до тех пор, пока в fName не появится пус
422
Неделя 2
тая строка. Если при вызове в строке 18 функции Dir был найден хотя бы один подходящий файл, то цикл будет выполняться не менее одного раза.
Обратите особое внимание на строку 26. Здесь снова вызывается функция Dir уже без всяких аргументов. При таком вызове этой функции VBA предполагает, что необходимо найти другие файлы, удовлетворяющие аргументам имени и атрибутов, которые использовались в предыдущем вызове Dir. Эта функция возвратит следующий файл из каталога, удовлетворяющий указанным критериям. Если таких файлов больше нет, то будет возвращена пустая строка.
В строках 27-31 расположена инструкция If...Then, аналогичная той, что находится в строках 19-23. Если строка в fName непустая и не содержит специальных каталогов (. или ..), то VBA выполняет строку 29, где вызывается процедура ShowFileAttr, и строку 30, чтобы увеличить на 1 значение счетчика найденных файлов.
Когда все подходящие файлы каталога будут найдены, функция Dir вернет пустую строку и цикл остановится. В строке 34 на экран выводится сообщение, где говорится, сколько найдено файлов.
Так надо
Надо помнить, что VBA в Excel (или в любом приложении, созданном для Windows 95 и выше) признает и может использовать длинные имена файлов.
Ввод имени файла с помощью стандартных окон Excel
У объекта Application системы Excel имеется два метода: GetOpenFilename и GetSaveAsFilename — которые можно использовать в программе на VBA для того, чтобы упростить задачу получения от пользователя имени файла.
Метод GetOpenFilename
Во многих предыдущих примерах этой книги для получения от пользователя имени файла применялась функция InputBox. Хотя в этих случаях к функции нет претензий, у нее нет преимуществ работающего с файлами диалогового окна Открытие документа, которое выводится на экран, когда в системе Excel выполняется команда Файл-Открыть. В этом окне легко увидеть, какие файлы имеются на диске, и просмотреть списки файлов на различных дисководах и каталогах.
В программе на VBA есть возможность использовать метод GetOpenFilename системы Excel, чтобы вывести на экран окно, которое внешне и по своим функциональным обязанностям не отличается от того, что появляется при выполнении команды Excel Файл-Открыть. Этот метод возвращает строку, в которой содержится имя выбранного пользователем файла, в том числе буква дисковода и полный путь по каталогам. Если пользователь отменил работу окна, то GetOpenFilename возвращает значение False (Ложь).
Метод имеет следующий синтаксис.
object.GetOpenFilename(fileFilter, filterindex, title, multiSelect)
Переменная object должна быть ссылкой на объект системы Excel. Ссылка на объект обязательна, но остальные аргументы метода такими не являются. Строка fileFilter представляет любое отвечающее правилам выражение типа String, выполненное в специальном формате, указывающее типы файлов из поля со списком Тип файлов в окне Открытие документа. Если этот аргумент будет пропущен, то тип файлов из этого поля будет АП Files (*.*) (Все файлы (*.*)).
День 13-й. Управление файлами с помощью Visual Basic
423
filterindex представляет определенное числовое выражение и указывает, какой тип файлов должен использоваться VBA в качестве установленного по умолчанию для поля со списком Тип файлов. Если этот аргумент пропущен или указано число, большее, чем имеется типов файлов, то первый из них, находящийся в списке, получает статус типа файлов по умолчанию, title представляет любое выражение типа String и является заголовком, выводимым VBA для этого окна. Если этот аргумент пропущен, то на экран будет выведено окно с обычным заголовком Открытие документа.
Наконец multiSelect представляет любое значение или выражение типа Boolean. Если его значение True, то пользователь может выбирать в окне Открытие документа несколько имен файлов. В этом случае GetOpenFilename возвращает массив с именами всех выбранных файлов; О том, что такое массивы, мы поговорим на следующем занятии.
Строку с типами файлов создавайте в таком формате:
"FilterNamel,filespecl,FilterName2,filespec2,FilterNameN,filespecN"
FilterNamel — это текст, который требуется показать в списке Тип файлов, filespecl представляет спецификацию типа файлов, которую Windows 95/98 использует для ограничения списка файлов, выводимых в окне Открытие документа. Можно задавать столько типов файлов, сколько захотите.
Ниже приведен пример строки с типами файлов.
"XL 9 Templates (*.xlt),*.xlt,Workbooks (*.xls),*.xls"
В результате в списке Тип файлов будут находиться XL 9 Templates (*.xlt) и Workbooks (*.xls). Когда будет выбран первый из них, в поле Имя файла появится спецификация *.xlt и в окне будут перечислены файлы только с таким расширением.
В листинге 13.5 показан пример использования метода GetOpenFilename.
Дистииа 13.5. Мсппльзпваиив GetOpenFilename дня иодучвния от поньзаватеня имени файла
1:	Sub Open2DataEntry()
2:	'Открывает указанную рабочую книгу и выбирает
3:	'лист Продажи.
4:
5:	Const iTitle = "Ввод данных"
6:	Const ShtName = "Продажи"
7:
8:	Const FilterList = _
9:	"Templates (*.xlt),*.xlt,Workbooks (*.xls),*.xls"
10:
11:	Dim fName As String
12:
13:	With Application
14:	fName = .GetOpenFilename)Title:=iTitle,
15:	FileFilter:=FilterList,
16:	filterindex:=2)
17:	End With
18:	If fName = "False" Then
19:	MsgBox prompt:=“Программа остановлена",
20:	Title:=iTitle
21:	Exit Sub
22:	End If
23:
24:	Workbooks.Open FileName:=fName
25:	ActiveWorkbook.Sheets(ShtName).Select
26:	MsgBox prompt:="Книга " & fName &
27:	" открыта, лист ” & ShtName &
424
Неделя 2
28:	" выбран.'1,
29:	Title:=iTitle & " Complete"
3 0: End Sub
В процедуре Open2DataEntry открывается книга, указанная пользователем, в ней — лист выбирается с именем Продажи. (Подразумевается, что в открываемой книге есть лист с таким именем.) В строках 5 и 6 описана пара констант. Первая, iTitle, используется для передачи заголовка окнам, выводимым на экран этой процедурой, а вторая, ShtName, передает имя листа.
Строки 8 и 9 требуют особого внимания. Обратите внимание на символ продолжения в конце строки 8. Эти две строки являются описанием одной константы, FilterList. Она передает строку с типами файлов в предстоящем вызове метода GetOpenFilename.
В строке 11 описана переменная fName для хранения имени файла, которое будет получено от пользователя. В строках 14-16 находится инструкция, которая вызывает метод GetOpenFilename и присваивает результат его выполнения переменной fName. Обратите внимание на точечный разделитель перед его именем и на то, что эта инструкция находится внутри другой инструкции With Application. Где бы ни использовался метод GetOpenFilename, необходимо указывать ссылку на объект Application.
При выполнении этой инструкции на экране появляется окно, показанное на рис. 13.2. Взглянув на поле со списком Тип файлов, можно наглядно убедиться в эффективности аргумента fileFilter. Обратите внимание, что диалоговое окно на рисунке идентично окну Открытие документа системы Excel, только отличаются их заголовки и содержимое списка Тип файлов. Поскольку значение аргумента filter Index равно 2 (строка 16), то типом файлов по умолчанию будет Workbooks (*.xis) , а не Templates (*.xlt).
Ввод данных





: goayiaxte
U SJjDayl4.xls
; ^T|Dayl5.xls syDayl6.xls
' syDayl7.xls
й SlDayl8.xls
' SjDayl9.xls
; «jDay20.xls
' KjDay21.xls
S) VBA.J5ample.xls
i &]ДеньО1.х1з ^Удень02,х)з
| ^День03,х15
Р &]ДеньО4.х18
• &]ДеньО5.х1з ^]ДеньО6.х1$ §1ДеньО7 xis
:  &)ДеньО8.х1з
м5деньО97х1§
День 10.xls 3£]день11.х1$ ^День12.х1§ М]День12а.х1з а^ИтотИ-Х-хЬ ^Книгах, xis ^Книга23.х1з ^]Книга3.х1з
,Имя файла:
^Workbooks (*,xls)
Рис. 13.2. Для вывода на экран этого окна процедура Open2DataEntry использует метод GetOpenFilename. Обратите внимание на список Тип файлов
Вы выбираете имя, точно так же как в окне Открытие документа системы Excel. Если выбрана кнопка Открыть, метод GetOpenFilename возвращает строку, где находятся имя файла, буква дисковода и полный путь (так как аргумент multiSelect не ис
День 13-й. Управление файлами с помощью Visual Basic
425
пользовался, то можно выбрать только один файл). Если выбрана кнопка Отмена, то метод вернет значение False.
Метод GetOpenFilename ограничивает список типов файлов лишь теми, которые
'	были указаны в аргументе fileFilter. Например, если был указан тип "Workbooks
С ПРИМЕЧАНИЕ	(*. xls)", то в диалоговом окне этого метода будут только книги Excel. В процедуре
0pen2DataEntry из листинга 13.5 можно выбрать только шаблоны и книги Excel; файлы других типов в ее диалоговом окне будут недоступны.
В строках 18-22 находится инструкция If, которая проверяет, отменено ли действие GetOpenFilename. Если выбрана отмена, то инструкция MsgBox, находящаяся в строках 19 и 20, выведет на экран сообщение, что операция отменена, и VBA завершит процедуру.
В строке 24 открывается выбранная пользователем книга, а в 25-й выбирается лист с указанным именем. При тестировании процедуры необходимо убедиться, что в открываемой книге есть лист с таким названием, или в строке 6 изменить описание константы, чтобы она соответствовала какому-либо реальному названию. Наконец, в строках 26-29 находится инструкция MsgBox, которая должна сообщать об успешном завершении процедуры.
Так надо
Следует помнить, что GetOpenFilename при выборе файла возвращает строку, а при отмене операции -значение False типа Boolean. Если результат выполнения присваивается переменной типа Variant, то сравнивать значение этой переменной нужно не со строкой “False", а с булевым значением.
; Помните, что GetOpenFilename возвращает массив, состоящий из строк (пусть даже выбран один файл), если необязательному параметру multiSelect присвоено значение True. Массивы мы рассмотрим в ходе следующего урока.
i Надо помнить, что если аргумент f ileFilter не будет задан, то GetOpenFilename выведет окно со спи-ском всех типов файлов.
Так не надо
Не удивляйтесь, если увидите в окне Просмотр объектов или в справке дополнительный аргумент buttonText метода GetOpenFilename. В системе Windows этот аргумент будет проигнорирован; он преду-
: смотрен для совместимости с Excel for the Macintosh.
He забывайте, что выбираемые типы файлов ограничены аргументом fileFilter. Чтобы пользователь мог выбирать любой тип файла, в этот аргумент необходимо включить тип файла *.*.
Метод GefSaveAsFilename
Для вывода на экран диалогового окна, которое похоже внешне и работает так же, как окно программы Excel при выполнении команды Файл-Сохранить как, можно использовать метод GetSaveAsFilename. Этот метод также возвращает строку, где находится имя выбранного пользователем файла, включающее в себя и букву дисковода и полный путь по каталогам. Если пользователь отменит операцию, то будет возвращено значение False типа Boolean.
Метод GetSaveAsFilename имеет следующий синтаксис.
object.GetSaveAsFilename(initialFilename, fileFilter, filterindex, title)
426
Неделя 2
Его синтаксис и аргументы почти такие же, как и у метода GetOpenFilename. Переменная object — это ссылка на объект Application, fileFilter — это выражение типа String, выполненное в специальном формате, указывающее типы файлов из поля со списком Тип файлов в окне Сохранение документа, filter Index — это числовое выражение, указывающее, какой тип файлов должен использоваться VBA в качестве установленного по умолчанию, и title — это любое выражение типа String для заголовка окна. Если title будет пропущен, то на экране появится окно с обычным заголовком Сохранение документа.
У метода GetSaveAsFilename есть еще один необязательный аргумент, initialFilename, который представляет любое соответствующее правилам имя файла. Если будет указан этот аргумент, то находящееся в нем имя при первом выводе окна Сохранение документа появится в текстовом поле Имя файла.
Для GetSaveAsFilename строки с типами файлов определяются точно так же, как и для GetOpenFilename.
В листинге 13.6 приведен пример использования метода GetSaveAsFilename.
Лпстииг 13.6. Ишльзоваввв GEtSaveAsFilename уля иолучепия от иользоватеяя имени фаПда
1:	Sub Convert2Template()
2:	'Сохраняет текущую рабочую книгу как шаблон.
3:
4:	Const FilterList = "Templates (*.xlt),*.xlt"
5:	Const iTitle = "Преобразование рабочей книги в шаблон"
6:	Static sName As String
7:	Static TCount As Variant
8:	Dim iName As String
9:
10:	If IsEmpty(TCount) Then
11:	TCount = 1
12:	Else
13:	TCount = TCount + 1
14:	End If
15:
16:	sName = "Шаблон" & CStr(TCount) & ".xlt"
17:	With Application
18:	iName = .GetSaveAsFilename) InitialFilename :=sName,
19:	FileFilter:=FilterList,
20:	Title:=iTitle)
21:	End With
22:
23:	If iName = "False" Then
24:	MsgBox prompt ^"Преобразование отменено.",
25:	Title:=iTitle
26:	Else
27:	ActiveWorkbook.SaveAs FileName:=iName,
28:	FileFormat:=xlTemplate
29:	End If
30:	End Sub
День 13-й. Управление файлами с помощью Visual Basic
427
В процедуре Convert2Template текущая книга сохраняется в виде шаблона Excel, и для получения имени этого нового файла используется метод GetSaveAsFilename. В строке 4 описана константа для типов файлов, в 5-й — для заголовков выводимых на экран диалоговых окон.
В строках 6 и 7 описаны статические переменные. Переменная sName используется для сохранения предложенного имени нового шаблона, a TCount — для присвоения именам шаблона номера по умолчанию. В строке 8 описана переменная iName типа String, чтобы сохранять выбранное вами имя файла.
В строках с 10 по 16 задается значение счетчика шаблонов и составляется предлагаемое для шаблона имя. В строках с 18 по 20 находится инструкция, которая вызывает метод GetSaveAsFilename и присваивает его результат переменной iName. Обратите внимание на точечный разделитель (.) перед именем метода и на то, что эта инструкция находится внутри другой инструкции With Application. При вызове метода GetSaveAsFilename необходимо указывать объект Application (или ссылаться на него).
Когда VBA выполняет инструкцию из строки 18, на экран выводится окно, изображенное на рис. 13.3. Обратите внимание, что оно точно такое, как и окно приложения Excel Сохранение документа, за исключением того, что отличаются их заголовки и содержимое списков Тип файла.
Преобразование рабочей книги в шаблон
IО Source
 Ш)шаблон1?х^' Г1 ®]шаблон2.х1а.хК
Я йчяфайла; р
Эп файла: ^Templates(* ,'xlt)
ШаблонЭ.х1д
Отмена
Рис. 13.3. Для вывода на экран этого окна процедура Convert2Template использует метод GetSaveAsFilename
ПРИМЕЧАНИЕ
Аргумент fileFilter метода GetSaveAsFilename не только ограничивает показываемые в окне файлы, но и гарантирует, что вводимое пользователем имя будет иметь трехбуквенное расширение, определяемое текущим выбором в поле со списком Тип файла.
Даже если пользователь введет правильное расширение как часть имени нового файла, метод GetSaveAsFilename всегда добавит текущее расширение из списка.
428
Неделя 2
ПРИМЕЧАНИЕ
Снова посмотрите на рис. 13.3 и обратите внимание на файл Шаблон2.xla.xlt. Он был сохранен ранее с помощью процедуры Convert2Template. Тогда пользователь ввел в текстовое поле Имя файла строку Template2.xla. Поскольку в длинных именах файлов системы Windows количество точек не ограничено одной и расширение *.xla не соответствует ни одному типу файлов из аргумента fileFilter, метод GetSaveAsFilename добавляет к имени файла расширение *.xlt (текущее значение в поле со списком Тип файла), перед тем как создать сам файл.
Чтобы пользователи могли ввести любое расширение, нужно чтобы в аргументе fileFilter находился тип файла *.*.
В строке 23 начинается инструкция If.. .Then.. .Else, которая проверяет, не отменил ли пользователь действие GetSaveAsFilename. Если была выбрана кнопка Отмена (или нажата клавиша Esc), то выполняется инструкция из строки 24, которая выводит на экран сообщение, что преобразование в шаблон отменено. Если действие GetSaveAsFilename не отменено, то инструкция, находящаяся в строке 27, вызывает метод SaveAs, сохраняя текущую книгу как файл шаблона Excel. (О методе SaveAs более подробно рассказывается при изложении материала урока 19.)
Так надо
Необходимо иметь в виду, что и GetOpenFilename, и GetSaveAsFilename могут менять текущие дисковод и папку. Вам, возможно, захочется воспользоваться функцией CurDir, чтобы получить текущие дисковод и папку и хранить их в переменной, а затем снова перейти на них с помощью ChDri ve и ChDir. (Об этих функциях и инструкциях рассказано далее в этой главе.)
Надо использовать аргумент fileFilter, чтобы пользователь процедуры мог вводить имена файлов только нужного типа, - способ, обеспечивающий процедуре создание нужных файлов. Например, можно написать программы на VBA, которые создают файлы одного типа, известного Excel.
Диски и папки
В этом разделе рассказывается о том, как использовать функции и инструкции VBA для получения текущих дисковода и папки, как их менять и как создавать и удалять подкаталоги. Помните, что текущим называется дисковод, который используется системой Excel, когда вы не укажете определенную букву дисковода. Аналогично текущей называется папка, которая используется в случае, когда для нее не указано определенного имени.
Определение пути к текущей папке и бдквы дисковода
Определить текущую букву дисковода и путь к текущей папке достаточно просто. И ту, и другую информацию можно получить с помощью функции CurDir. При этом возвращается строка, в которой содержится полный путь к текущей папке с буквой дисковода включительно. (Название CurDir — это сокращение словосочетания current directory (текущий каталог):, помните, что папка и каталог — это одно и то же.)
Функция CurDir имеет следующий синтаксис.
CurDir[(drive)]
День 13-й. Управление файлами с помощью Visual Basic
429
Аргумент drive представляет любое выражение типа String и сообщает функции, для какого дисковода требуются данные о текущей папке. Если этот аргумент будет пропущен, то функция выдаст текущую папку текущего дисковода. Обычно в аргументе drive содержится одна буква; если там находится строка из нескольких символов, то в качестве буквы дисковода будет использован первый из них.
В листинге 13.7 показан пример использования функции CurDir.
Анстниг 13.7. Исиояьзовапие функции CurOIr дня получвиня швкдщих кашаяога и дисковода
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: И: 12: 13: 14: 15:	Sub ShowCurDriveDir)) 'Выводит текущий дисковод и директорию Dim dirName As String Dim DirLetter As String dirName = CurDir)) DirLetter = LeftfdirName, 1) MsgBox "Текущий дисковод - " & DirLetter MsgBox "Текущая директория - " & dirName dirName = CurDir("A") MsgBox "Текущая директория на диске А - " & _ dirName End Sub
Эта процедура просто демонстрирует работу функции CurDir. В строках 4 и 5 описаны переменные этой процедуры. Переменная DirName предназначена для хранения результата выполнения CurDir, a DirLetter — для хранения буквы дисковода, извлеченной из DirName.
В строке 7 вызывается функция CurDir без аргументов, чтобы получить текущие папку и дисковод, результат же выполнения присваивается переменной DirName. В строке 8 для копирования первой буквы у CurDir и присвоения ее DirLetter используется функция Left. Поскольку CurDir возвращает полный путь, с буквой дисковода включительно, первый символ результата выполнения этой функции всегда является буквой дисковода. В каждой из строк 9 и 10 на экран выводятся сообщения; в первом из них говорится о букве текущего дисковода, а во втором — о текущей папке этого дисковода.
Затем в строке 12 снова вызывается функция CurDir, на этот раз с аргументом — буквой А. В данном случае требуется получить текущую папку на диске А:. (Если при выполнении процедуры в этом дисководе нет диска, то вы можете получить сообщение об ошибке времени выполнения.) Наконец, в строке 13 выводится сообщение о текущей папке на дисководе А:.
Изменение текущей паики
Если в процедуре на каком-либо дисководе требуется изменить текущую папку, следует использовать инструкцию ChDir. При наличии определенного опыта работы с DOS легко определить, что ChDir предназначена для того же, что и команды DOS CHDIR и CD. (Название ChDir — это сокращение словосочетания change directory (изменить каталог).)
430	Неделя 2
Инструкция ChDir имеет следующий синтаксис.
ChDir path
Аргумент path представляет выражение типа String, в котором в стандартном виде записан путь к папке; иногда там указана буква дисковода — в таком случае меняется текущая папка указанного дисковода, а он может и не быть текущим. (Для изменения текущего дисковода используйте инструкцию ChDrive, речь о которой пойдет дальше.)
В листинге 13.8 показан пример использования инструкции ChDir.
Листинг 13.8. Использоваиие ChDir
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:	Sub Demo_ChDir() 'Пример инструкции ChDir Dim oldDir As String MsgBox "Текущий каталог - " & CurDir() oldDir = CurDir)) ChDir "\мои документы" MsgBox "Теперь текущий каталог - " & CurDir ChDir oldDir MsgBox "Текущий каталог - " & CurDir)) End Sub
Эта процедура демонстрирует работу инструкции ChDir. В строке 4 описана переменная oldDir типа String, предназначенная для хранения текущей папки перед ее изменением. В строке 6 для вывода на экран сообщения о текущей папке используется MsgBox, а в строке 7 значение текущей папки (возвращаемое CurDir) присваивается переменной oldDir.
В строке 8 инструкция ChDir используется для того, чтобы сделать текущей папку \Му Documents. (Предполагается, что эта папка находится на том же дисководе, что и текущая, но таковой не является; возможно, что при наборе текста процедуры вам захочется в этой строке ввести имя какой-нибудь своей папки.) Далее, в строке 9 выводится сообщение о новой текущей папке. В строке 10 снова используется инструкция ChDir, на этот раз для восстановления первоначальной папки. И наконец, в строке 11 выводится сообщение с подтверждением того, что эта папка снова стала текущей.
Так надо
Следует сохранять в переменной имя текущей папки, чтобы потом ее вновь можно было сделать текущей.
Не забывайте, что методы GetOpenFilename и GetSaveAsFilename могут приводить к изменениям текущих дисководов и папок. Возможно, потребуется сохранить в строковой переменной имя текущей лапки, а затем с помощью ChDir и ChDrive восстановить первоначальные значения.
День 13-й. Управление файлами с помощью Visual Basic
431
Изменение дисковода
Чтобы изменить текущий дисковод, необходимо использовать инструкцию ChDrive. (ChDrive — это сокращение от change drive (изменить дисковод).)
Инструкция ChDrive имеет следующий синтаксис.
ChDrive drive
Аргумент drive — это любое выражение типа String, представляющее букву дисковода. Если в нем содержится больше одного символа, то используется только самый первый из них. Когда в качестве аргумента указана пустая строка, текущий дисковод не меняется. Если указан символ, не являющийся буквой от А до Z, то VBA выведет сообщение об ошибке времени выполнения. Также будет сообщено об ошибке, если указана буква дисковода, в действительности не существующего в компьютерной системе, хотя можно указывать буквы для дисков, соединенных с компьютером по сети.
В листинге 13.9 показан пример инструкции ChDrive.
Листинг 13.Я. ИсиользоааииЕ ChSrive для изменения текдщеао дисковода
1:	Sub Demo_ChDrive()
2:	Dim oldDir As String
3:
4:	oldDir = CurDir()
5:	MsgBox "Текущая директория - " & oldDir
6:	ChDrive "A"
7:	MsgBox "Теперь текущие дисковод и директория - " & CurDir()
8:	ChDrive oldDir
9:	ChDir oldDir
10:	MsgBox "Текущий директория - " & CurDir()
11:	End Sub
В строке 2 описана переменная oldDir типа String для хранения текущей папки. В строке 4 вызывается функция CurDir и результат ее выполнения присваивается oldDir. В строке 5 на экран выводится сообщение о текущих дисководе и папке, данные о которых получены с помощью CurDir (и хранятся в oldDir).
В строке 6 используется инструкция ChDrive, чтобы сделать текущим дисковод А:. (Перед тем как запускать процедуру, убедитесь, что в дисковод А: вставлен диск.) В строке 7 на экран выводится сообщение, подтверждаюшее, что новый текущий дисковод теперь А:.
Затем в строке 8 для восстановления первоначального дисковода в качестве текущего используется ChDrive. Поскольку буква дисковода всегда присутствует в строке, выводимой в результате выполнения функции CurDir, в качестве аргумента для инструкции ChDrive выступает oldDir. Инструкция берет из аргумента только первый символ, являющийся старым значением буквы текущего дисковода, а затем снова делает первоначальный дисковод текущим. Чтобы быть уверенными, что восстановлена и первоначальная папка, в строке 9 инструкция ChDir в качестве текущей восстанавливает старую папку. И наконец, в строке 10 выводится другое сообщение, подтверждаюшее, что восстановлены старые значения текущих дисковода и папки.
432
Неделя 2
Создание напки
Иногда для хранения файлов новых книг или по другим причинам требуется, чтобы одна из ваших процедур создала на диске новый подкаталог. Для создания каталога используется инструкция MkDir. Она предназначена для того же, что и команды DOS MKDIR или MD, а также команда Windows Файл-Создать-Папку. (MkDir — это сокращение от make directory (создать каталог).)
Инструкция MkDir имеет следующий синтаксис.
MkDir path
Аргумент path представляет выражение типа String, в котором находится путь по каталогам в стандартном виде; иногда там указана буква дисковода. Если ее там нет, то новая папка создается на текущем диске. Если в path указана уже существующая папка или есть недопустимые символы, то VBA выведет сообщение об ошибке времени выполнения. Сообщение об ошибке времени выполнения будет и в случае попытки создать подкаталог несуществующего каталога. MkDir не меняет текущие дисковод и папку.
В листинге 13.10 показан пример использования инструкции MkDir.
Листинг 13.10. Исипльзоваиие MkDir для создаиия нового каталога
1:	Sub Demo_MkDir()
2:	'Пример инструкции MkDir
3:
4:	Dim newDir As String
5:
6:	newDir = "A:\testl"
7:	MkDir newDir
8:	ChDir newDir
9:	MsgBox "Текущая директория на диске А: - " &
10:	CurDir("А")
11:	End Sub
В строке 4 описана переменная newDir типа String для хранения имени новой папки. В строке 6 этой переменной присваивается значение A:\testl, а в строке 7 для создания новой папки используется инструкция MkDir (перед тем как запускать процедуру, убедитесь, что в дисковод А: вставлен диск — иначе VBA выведет сообщение об ошибке времени выполнения). После того как создана новая папка, в строке 8 она назначается текущей на диске А:, а в строках 9 и 10 для вывода на экран сообщения о текущей папке на А: используется MsgBox (как подтверждение создания новой папки).
Удаление папки
Иногда требуется, чтобы наряду с созданием новых папок процедура удаляла существующие. Для удаления на диске папок используется инструкция RmDir. Она предназначена для того же, что и команды DOS RMDIR или RD, а также команда Windows Файл-Удалить, используемая для удаления и файлов и папок. (RmDir — это сокращение от remove directory (удалить каталог).)
Инструкции RmDir имеет следующий синтаксис.
RmDir path
День 13-й. Управление файлами с помощью Visual Basic
433
Аргумент path представляет выражение типа String, в котором находится путь по каталогам в стандартном виде; иногда там указана буква дисковода. Если ее там нет, то папка удаляется с текущего диска. Если в path указана несуществующая папка или есть недопустимые символы, то VBA выведет сообщение об ошибке времени выполнения.
В листинге 13.11 показан пример использования инструкции RmDir.
Листинг 13.11. Исппльзование RmOIr для удаления пайки
1:	Sub Demo_RmDir()
2:	'Пример инструкции RmDir
3:
4:	Dim delDir As String
5:
6:	delDir = "AATESTl"
7:	If CurDir("A") = delDir Then ChDir "A:\"
8:	RmDir delDir
9:	End Sub
В строке 4 описана переменная delDir типа String для хранения имени удаляемой папки. В строке 6 этой переменной присваивается значение A:\TEST1 (предполагается, что выполнялась процедура из листинга 13.10, во время которой на диске А: была создана папка Testi). В строке 7 для определения на диске А: текущей папки вызывается функция CurDir. Если удаляемая папка оказывается текущей, то в этой же строке 7 вызывается функция ChDir, чтобы на диске А: сделать текущим корневой каталог. Папку нельзя удалить, если она является текущей или непустой.
Далее, в строке 8 для удаления с диска А: подкаталога Testi используется инструкция RmDir.
Так надо
Перед использованием RmDir следует применять функцию Dir, чтобы определить, является ли папка пустой. Если она таковой не является, то для удаления из нее файлов надо использовать инструкцию Kill (о ней будет рассказано далее в этой главе), а для удаления всех подкаталогов используйте RmDir.
Перед удалением папки с помощью функции CurDir необходимо проверить, не является ли она текущей.
Так не надо
Не пытайтесь удалять палку, содержащую файлы или другие папки, иначе VBA выведет сообщение об ошибке времени выполнения.
Не пытайтесь удалять папку, которая является текущей, иначе VBA выведет сообщение об ошибке времени выполнения.
Копирование и удаление файлов
Копирование и удаление являются наиболее распространенными действиями по управлению файлами. В этом разделе рассказывается, как копировать и удалять файлы в процедурах VBA.
434
Неделя 2
Копирование файла
Для копирования файла в VBA используется инструкция FileCopy. Она эквивалентна команде DOS COPY или команде Windows Правка-Копировать.
Инструкция FileCopy имеет следующий синтаксис.
FileCopy source,destination
Аргументы source и destination являются выражениями типа String, которые представляют собой имена файлов в стандартной форме. Иногда там могут быть полный путь по каталогам и буква дисковода. При попытке копировать файл в себя VBA выдаст сообщение об ошибке времени выполнения. Сообщение об ошибке времени выполнения будет и в случае копирования файла туда, где на диске не хватает места.
Лисшмна 13.12. Процедура, использующая инструкцию FileCopg
1:	Sub CopyFiles()
2:	'Копирует выбранный пользователем файл на
3:	'другой диск и в другую директорию.
4:
5:	Dim	sName	As	String
6:	Dim	dName	As	String
7:
8:	Do
9:	With Application
10:	sName = .GetOpenFilename(Title:= _
11:	"Выбор копируемого файла")
12:	If sName = "False" Then Exit Sub
13:	dName = .GetSaveAsFilename)Title:=
14:	"Выбор места для копии")
15:	If dName = "False" Then Exit Sub
16:	End With
17:	FileCopy Source:=sName, Destination:=dName
18:	Loop
19:	End Sub
Эта процедура дает пользователю возможность выбрать для копирования файл, а затем указать диск и папку, куда копировать, и имя для копии (целевого файла). В строках 5 и 6 описаны две переменные типа String для хранения имен исходного и целевого файлов. В строке 8 начинается бесконечный цикл Do (цикл без определенных условий). Цикл из строк с 8 по 18 выполняется до тех пор, пока пользователь не отменит одну из двух файловых операций. В строке 9 начинается инструкция With Application. В строках 10 и 11 находится инструкция, которая вызывает метод GetOpenFilename для вывода на экран окна открытия файла и присваивает имя файла (результат выполнения метода) переменной sName.
В строке 12 проверяется, не отменил ли пользователь открытие файла. Если да, то процедура завершается. Если не отменил, то VBA продолжает работу и выполняет в строках 13 и 14 инструкцию, вызывающую метод GetSaveAsFilename, чтобы дать пользователю выбрать для целевого файла имя, дисковод и папку. В строке 15 проверяется, не отменил ли пользователь эту операцию. Если отменил, то процедура завершается. Если нет, то VBA продолжает работу и выполняет в строке 17 инструкцию FileCopy.
День 13-й. Управление файлами с помощью Visual Basic
435
При выполнении этой инструкции происходит копирование файла (чье имя хранится в sName) в другой файл, чье имя, дисковод и папка хранятся в dName. После копирования цикл Do повторяется.
Так надо
Для того чтобы определить, имеет ли открытый файл статус только для чтения, проверяйте значение свойства Readonly объекта Workbook. Это свойство может иметь значения True и False.
Так не надо
Не пытайтесь копировать открытые файлы, если только они не были открыты в Excel со статусом только для чтения. В противном случае VBA выдаст сообщение об ошибке времени выполнения.
Удаление файла
Для удаления файла используется инструкция с неприветливым названием Kill. Она предназначена для того же, что и команды DOS DEL, а также команда Windows Файл-Удалить.
Инструкция Kill имеет такой синтаксис.
Kill path
Аргумент path представляет выражение типа string, в котором находятся имена файлов в стандартном виде; иногда там указываются буква дисковода, полный путь по каталогам, а также подстановочные знаки (* или ?). Если имеются * или ?, то Kill удаляет все файлы, соответствующие спецификациям path.
В листинге 13.13 показан пример использования инструкции Kill на практике.
Листинг 13.13. Использование Kill для ддалеиия фаОлоо
1:	Sub DelFiles()
2:	'Удаляет файлы, пока пользователь не закроет окно.
3:
4:	Dim	fName As String
5:	Dim Ans As Integer
6:
7:	Do
8:	With Application
9:	fName = .GetOpenFilename(Title:=,,yflaneHHe файла")
10:	End With
11:	If fName = "False"	Then	Exit	Sub
12:	Ans = М5дВох(рготрЬ:="Удалить	" & fName &
13:	Т1Ь1е:="Удаление файла",
14:	Buttons:=vbQuestion + vbYesNo)
15:	If Ans = vbYes Then
16:	Kill fName
17:	End If
18:	Loop
19:	End Sub
436
Неделя 2
В этой процедуре вначале используется метод GetOpenFilename, чтобы пользователь выбрал имя файла, затем подтверждается удаление, удаляется файл и затем выполнение повторяется. В строке 4 описана переменная типа String для хранения имени файла, а в строке 5 переменная типа Integer для хранения ответа пользователя на сообщение с просьбой дать подтверждение.
В строке 7 начинается бесконечный цикл Do, который выполняется до тех пор, пока пользователь не отменит операцию с файлом. В строке 8 начинается инструкция With. В строке 9 вызывается метод GetOpenFilename и присваивается имя файла (результат выполнения метода) переменной fName. В строке 11 проверяется, не отменил ли пользователь открытие файла. Если да, то процедура завершается. Далее, в строках 12-14 находится инструкция MsgBox, которая просит пользователя подтвердить удаление выбранного файла. В случае подтверждения VBA в строке 16 выполняет инструкцию Kill, которая удаляет файл.
Так надо
Перед удалением файлов следует проверять их атрибуты. В случае попытки удалить файлы с атрибутами Скрытый, Системный или Только чтение VBA выдаст сообщение об ошибке времени выполнения. Если требуется удалить файлы с этими атрибутами, используйте такие функции как GetAttr, чтобы узнать атрибуты, и SetAttr, чтобы их изменить.
Так не надо
Не пытайтесь удалить открытую книгу, иначе VBA выдаст сообщение об ошибке времени выполнения.
Переименование или перемещение файлов
Иногда требуется изменить имя существующего файла или переместить его в другую папку того же диска. Для этого служит инструкция Name.
Инструкция Name имеет такой синтаксис.
Name oldpathname As newpathname
Аргументы oldpathname и newpathname являются выражениями типа String, которые представляют собой имена файлов в стандартной форме. Иногда в один из них (или в оба) могут быть включены полный путь по каталогам и буква дисковода. При наличии буквы дисковода она должна быть одинаковой в каждом из этих выражений, иначе VBA выдаст сообщение об ошибке времени выполнения. В newpathname не может быть имени существующего файла, иначе вы вновь получите то же сообщение об ошибке. Если в oldpathname и newpathname указаны разные папки, то VBA переместит файл в новый каталог и, если надо, изменит его имя.
На листинге 13.14 показана процедура, в которой для переименования файлов используется инструкция Name.
Лпсшина 13.14. Использование Name для переимеиоваиия или перемещении файлвв
1:	Sub SenameOrMoveFilef)
2:	'Переименование файла
3:
4:	Const iTitle = "Переименование - "
День 13-й. Управление файлами с помощью Visual Basic
437
5:	Dim oldName As String
6:	Dim newName As String
7:	Dim oldDir As String
8:
9:	oldDir = CurDir()
10:	With Application
11:	oldName = .GetOpenFilename(Title:=iTitle & "выбор файла")
12:	If oldName = "False" Then Exit Sub
13:
14:	newName = .GetSaveAsFilename(InitialFilename:=oldName,
15:	Title:=iTitie S "новое имя")
16:	If newName = "False" Then Exit Sub
17:	End With
18:
19:	If Left(oldName, 1) = Left(newName, 1) Then
20:	Name oldName As newName
21:	Else
22:	FileCopy oldName, newName
23:	Kill oldName
24:	End If
25:
26:	ChDrive oldDir
27:	ChDir oldDir
28:	End Sub
Эта процедура переименовывает или перемещает файл. Пользователь выбирает имя, папку и дисковод для первоначального файла и для его нового имени и/или места расположения. В строке 4 описана строковая константа, используемая в заголовках окон, выводимых в процедуре. В строках с 5 по 7 описаны переменные, используемые в процедуре. Переменная oldName предназначена для хранения старого имени файла и newName — для нового, a oldDir предназначена для хранения имени диска и папки, являвшихся текущими в момент начала выполнения процедуры, с тем чтобы восстановить их после ее завершения.
В строке 9 вызывается функция CurDir и присваивается ее результат переменной oldDir. В строке 10 начинается инструкция With Application. В строке 11 используется метод GetOpenFilename, чтобы пользователь выбрал файл, который надо переименовать или переместить. В строке 12 используется инструкция If для проверки, не отменил ли пользователь открытие файла. Если да, то процедура завершается.
Далее, в строке 14 вызывается метод GetSaveAsFilename, чтобы дать пользователю выбрать новые имя, дисковод и папку. Обратите внимание, что в этом методе для предлагаемого нового имени используется аргумент InitialFilename. В строке 16 проверяется, не отменил ли пользователь эту операцию. Если отменил, то процедура завершается.
В строке 19 начинается инструкция If.. .Then.. .Else, которая проверяет, выбрал ли пользователь для переноса файла другой диск. В ее логическом выражении для получения первой буквы из каждой строки oldName и newName используется функция Left. Если буквы одни и те же, то это значит, что пользователь переименовывает или переносит файл на том же диске. При этом в строке 20 выполняется инструкция Name, чтобы переименовать файл. Если пути по каталогам в oldName и newName различны, то Name переносит файл в новую папку.
438
Неделя 2
Если первые буквы в строках из oldName и newName различны, это значит, что пользователь выбрал перенос файла на другой диск. В этом случае нельзя использовать инструкцию Name, поэтому в процедуре вначале для копирования файла с одного диска на другой применяется FileCopy, а затем для удаления исходного файла — Kill.
И наконец, инструкции ChDrive и ChDir восстанавливают первоначальные дисковод и папку, которые были текущими в момент начала процедуры. (Помните, что методы GetOpenFilename и GetSaveAsFilename могут менять текущие дисковод и папку.)
Так надо
Перемещайте файл с одного диска на другой, копируя его с помощью FileCopy и затем удаляя исходный файл с помощью Kill, как это делается в листинге 13.4.
не надо
Не путайте инструкцию Name со свойством Name, которое имеется у многих объектов. Инструкция переименовывает файлы, а свойство хранит имя объекта.
Не пытайтесь с помощью инструкции Name переместить файл с одного диска на другой.
Не пытайтесь переименовать открытый файл. Если вы эго сделаете, VBA выдаст сообщение об ошибке времени выполнения. Файл надо вначале закрыть.
Получение информации о файле
Иногда важно знать размер файла, а также дату и время его последней модификации. Например, для процедуры резервного копирования книг Excel может понадобиться, чтобы она следила, не заменяется ли недавно скопированный файл более старой его версией.
Дата и время изменения
Каждый раз, когда вы (или прикладная программа, такая как Excel) меняете файл, рядом с именем файла система Windows записывает дату и время (по показаниям часов компьютера), так что можно сказать, когда файл был изменен последний раз. Для того чтобы эта информация стала известной процедурам VBA, необходимо использовать функцию FileDateTime. В результате ее работы выдается информация о дате и времени изменения файла в виде значения с типом данных Date.
Функция FileDateTime имеет следующий синтаксис.
FileDateTime(pathname)
Аргумент pathname является выражением типа String, которое представляет собой имя файла в стандартной форме. Иногда в нем может быть полный путь по каталогам и буква дисковода, но подстановочные знаки (* или ?) запрещены.
В следующем разделе на листинге 13.15 показан пример использования функции FileDateTime.
День 13-й. Управление файлами с помощью Visual Basic
439
Длина файла
Чтобы узнать величину файла, используют функцию FileLen. Она возвращает длину файла в байтах.
Функция FileLen имеет такой синтаксис.
FileLen(pathname)
Аргумент pathname является выражением типа String, которое представляет собой имя файла в стандартной форме. Иногда в нем может быть полный путь по каталогам и буква дисковода, но подстановочные знаки (* или ?) должны отсутствовать. Если в pathname указан открытый файл, то возвращается его размер, установленный в момент последнего сохранения файла на диск.
В листинге 13.15 показан пример использования функции FileLen.
Листинг 13.15. Использование фднкций FlleDateTime и FileLeH
1:	Sub ShowFileDateSize(fName As String)
2:	'Выводит размер и дату последнего изменения файла.
3:
4:	Dim	msgl	As	String
5:	Dim	msg2	As	String
6:	Dim	fDate As Date
7:	Dim	fLen	As	Long
8:
9:	fDate = FileDateTime(fName)
10:	fLen = FileLen(fName)
11:	msgl = "Размер: " & Format(fLen, ”###,###,###") & _
12:	" байт."
13:	msg2 = "Дата последнего изменения: " &
14:	Format(fDate, "long date")	&
15:	Format(fDate, "long time")
16:	MsgBox Title:="File Date and Size",
17:	prompt:=fName & Chr(13) &
18:	msgl & Chr(13) & msg2
19:	End Sub
20:
21:	Sub Test_ShowFileDateSize()
22:	Dim sName As String
23:
24:	Do
25:	With Application
26:	sName = .Get0penFilename(Title:="Pa3Mep и дата изменения файла")
27:	End With
28:	If sName <> "False"	Then ShowFileDateSize sName
29:	Loop Until sName = "False"
30:	End Sub
В процедуре ShowFileDateSize используются функции FileDateTime и FileLen для вывода на экран сообщения о текущем размере файла, а также о дате и времени его последнего изменения. В этой процедуре, чтобы было известно, о каком файле выводить информацию, используется единственный аргумент fName. Тип этого аргумента String.
440
Неделя 2
В строках с 4 по 7 описано несколько переменных. Первые две, msgl и msg2, используются при составлении текста, выводимого процедурой сообщения. Переменная fDate предназначена для хранения даты и времени, a fLen — для хранения длины файла. Обратите внимание, что у fLen тип Long, так как длина файла может достигать миллионов байтов.
В строке 9 вызывается функция FileDateTime, которая для указания имени файла использует аргумент fName, и в той же строке результат выполнения функции сохранятся в переменной fDate. Далее, в строке 10 вызывается функция FileLen, использующая тот же аргумент fName, и в той же строке 10 результат выполнения функции сохранятся в переменной fLen.
Далее, в строках 11 и 12 содержится инструкция, создающая первую часть выводимого процедурой сообщения и сохраняющая ее в msgl. В строках 13-15 содержится другая инструкция, создающая вторую часть сообщения и сохраняющая ее в msg2. И наконец, инструкция MsgBox, находящаяся в строках 16-18, выводит на экран имя файла, его размер, а также дату и время его последнего изменения.
В строках 21-30 содержится процедура, которая тестирует ShowFileDateSize. При этом вначале используется метод GetOpenFilename, чтобы обеспечить возможность выбрать файл, а затем вызывается процедура ShowFileDateSize. Она выводит на экран сообщение, аналогичное показанному на рис. 13.4.
Размер и дата изменения файла
Рис. 13.4. Для сбора информации о размере файла и времени последнего изменения процедура ShowFileDateSize использует функции FileLen и FileDateTime
Резюме
На этом уроке с целью управления файлами вы научились использовать функции и инструкции VBA. Вы также узнали, как использовать методы GetOpenFilename и GetSaveAsFilename для обеспечения вывода на экран из процедур таких окон Excel, как Открытие документа и Сохранение документа.
Теперь вы знаете, как вести поиск одного или нескольких файлов в папке; как определить текущие папку и дисковод, а также как их изменить. Эта глава познакомила вас с тем, как с помощью VBA создавать и удалять папки, а также копировать, переименовывать, перемещать или удалять файл. И наконец, было показано, как получить дату и время последней модификации файла и узнать его длину.
День 13-Й. Управление файлами с помощью Visual Basic
441
Вопросы о ответы
Как мне узнать, сколько на диске свободного места, перед тем как копировать туда файл, чтобы не получить сообщение об ошибке в случае, если диск заполнен?
К сожалению, VBA не позволяет узнать, сколько на диске свободного места. Чтобы при копировании файлов избежать такого рода сообщений, пользуйтесь приемами обработки ошибок (об этих приемах рассказывается на 18-м уроке).
В окне Object Browser и справочной системе я обнаружил, что у Excel есть еще один связанный с файлами метод — FindFile. Что он из себя представляет и как им пользоваться?
Этот метод открывает то же диалоговое окно, что и команда приложения Excel Файл-Открыть. Как только пользователь выберет имя файла, система откроет его. У метода FindFile нет аргументов; при успешном открытии файла метод возвращает True, а если пользователь отменил операцию, то False. Управлять диалоговым окном метода из процедуры нельзя; его работа аналогична команде Excel Файл, Открыть. Из-за этого польза от метода FindFile несколько ограничена.
В процедуре метод можно использовать для поиска, предварительного просмотра и открытия книг, но не для получения информации о файлах. Как только при работе метода FindFile выбрана отмена или книга открыта, процедура будет завершена. Метод FindFile в процедуре можно использовать, вызывая его таким образом (ссылка на объект Application обязательна):
Application.FindFile
Коллоквиум
Ответы в приложении.
Тест
1.	Назовите семь атрибутов файлов.
2.	Если у файла несколько атрибутов, то как их выразить одним числом?
3.	Какая функция применяется для чтения атрибутов файла? Что она возвращает?
4.	Можно ли использовать SetAttr для задания атрибутов Каталог или Метка тома?
5.	Что делают методы GetOpenFilename и GetSaveAsFilename?
6.	Как пользоваться функцией Dir для поиска в папке нескольких файлов?
7.	Как узнать текущие дисковод и папку?
8.	Какую инструкцию VBA надо использовать для копирования файла?
9.	Какую инструкцию VBA надо использовать для переноса файла с одной папки в другую в пределах одного дисковода? Можно эту же инструкцию использовать для переноса на другой дисковод?
442
Неделя 2
Упражнения
1.	НАЙДИТЕ ОШИБКУ. В этих двух фрагментах программы есть источник ошибки выполнения. В чем дело? (При выполнении упражнения исходите из предположения, что fName — переменная типа String.)
(А)
fName = GetOpenFilename
(Б)
With Application
fName = GetSaveAsFileName
End With
2.	НАЙДИТЕ ОШИБКУ. В инструкции есть источник ошибки времени выполнения. Где именно?
Name "C:\EXAMPLES\SALES.XLS" As "A:\SALES.XLS"
3.	Используя функцию IsDiskFile из листинга 13.3 в качестве образца, напишите функцию с именем IsDiskFolder, возвращающую значения True, если указанная папка существует, и False в противном случае.
4.	Напишите процедуру, которая с помощью функции InputBox получает вводимое пользователем имя папки и меняет текущую папку на указанную пользователем. Для проверки того, существует ли указанная папка, перед тем как делать ее текущей, используйте созданную в упражнении 3 функцию IsDiskFolder. Если папки не существует, то процедура должна предложить ее создать. Обеспечьте грамотную обработку ситуации, когда пользователь отменяет операцию ввода.
День 13-й. Управление файлами с помощью Visual Basic
443
бДеньЦй
Массивы
В этой главе рассказывается о массивах. Это удобный и широко распространенный способ хранения однотипных данных. Массивы полезны при создании сортированных и несортированных списков данных, при хранении табличной информации и для многого другого. В ходе этого занятия мы рассмотрим следующие вопросы.
•	Как описывать массивы с помощью инструкции Dim.
•	Как использовать массивы в процедурах VBA.
•	Как получать информацию о массивах с помощью функций IsArray, LBound и UBound.
•	Как выполнять операции с каждым элементом массива с помощью циклической структуры For... Each.
•	Как менять размеры массивов с помощью инструкции ReDim.
•	Как удалять и очищать массивы с помощью инструкции Erase.
•	Как передавать массивы процедурам и функциям в качестве аргументов.
•	Как сортировать массив в определенном порядке.
•	Как выполнять поиск в массивах.
Понятие о массивах
Массив — это набор переменных одного типа данных с общим именем. Как и определенные пользователями типы, о которых говорилось на уроке 11-го дня нашего учебного курса, массивы являются удобным способом хранения однородных данных в одном месте. Таким способом в программировании достигается значительная эффективность. Но в отличие от определенных пользователем типов, все элементы данных, хранящихся в массиве, должны быть одного типа; например, если создан массив для хранения данных Integer, то каждый элемент хранящихся в массиве данных должен быть числом типа Integer.
Массивами обычно пользуются, чтобы представлять списки или таблицы с информацией, где все элементы — данные одного типа, т.е. в списке находятся только числа Ir.teger, данные одного из типов String, Currency и т.д. Массив дает возможность хранить и обрабатывать все свои элементы, используя одну переменную. Кроме того, что сокращается общее количество имен переменных, другим существенным преимуществом является то, что для обработки элементов в массиве можно использовать циклические структуры (в основном такие, как For...Next или For...Each). С их помощью
444
Неделя 2
можно малым числом инструкций переработать горы данных. Если для тех же задач на каждый элемент данных задавать по переменной, то число инструкций возрастет до нескольких сотен.
Что такое одномерные массивы
Наименее сложный массив — это всего лишь список элементов данных; массив такого рода называется простым, или одномерным. Одномерные массивы называются так потому, что список данных — это как линия, проведенная на листе бумаги; у списка только одна размерность — длина, и поэтому он одномерный.
На рис. 14.1 показана схема одномерного массива. Каждый “кирпичик” данных из массива называется его элементом. В массиве на рис. 14.1 содержится 10 элементов; в каждом из них хранится число типа Double. Обратите внимание, что элементы массива, которых всего 10, пронумерованы от 0 до 9. (Этот способ нумерации распространен в программировании и известен как нумерация с нижней границей 0.)
Чтобы получить доступ к отдельному элементу массива, указывается имя массива, за которым следует номер того элемента, чье содержимое надо взять или изменить (этот номер называется индексом). Индекс надо всегда заключать в скобки. Например, если массив на рис. 14.1 называется NumArray, то в следующей инструкции переменной AnyNum присваивается число 55,4.
AnyNum= NumArray(7)
В этой инструкции число 7 — это индекс массива; обратите внимание, что он заключен в скобки и никакими пробелами не отделен от имени массива. Ввиду того что нумерация элементов начинается с 0, эта инструкция в действительности ссылается на восьмой элемент массива NumArray.
Снова посмотрите на рис. 14.1 и обратите внимание, что в элементе номер 7 находится значение 55,4. Когда VBA выполняет инструкцию, то берет значение 55,4 из указанного элемента массива и присваивает его переменной AnyNum точно так, как и при любом другом присвоении.
Индексы используются и при запоминании данных в отдельном элементе массива. В следующей инструкции число 12 запоминается в восьмом элементе массива, показанного на рис. 14.1:
NumArray(7) =12
Когда VBA выполняет эту инструкцию, то помещает значение 12 в указанный элемент массива, затирая при этом находившееся в нем значение — опять же, как и при любом другом присвоении. Элемент массива можно использовать в любом выражении VBA точно так же, как и значение любой константы или переменной.
В основном одномерные массивы служат для представления списков данных.
Что такое многомерные массивы
Одномерные массивы хорошо работают с простыми списками данных. Однако зачастую в программах приходится представлять информацию, которая находится в таблицах, имеющих строки и столбцы, — как если бы она находилась в ячейках Excel. Для этого необходимо использовать многомерный массив.
На рис. 14.2 показана схема наиболее распространенной формы многомерных массивов — двумерного массива. Многомерные массивы называются так из-за того, что у них более одной размерности: длина (число строк), ширина (число столбцов). Как будет показано далее в этом разделе, возможны и другие размерности.
День 14-Й. Массивы
445
Одномерный массив
Рис. 14.1. Одномерный числовой массив; одномерные массивы — это, в сущности, списки однотипных данных
На рис. 14.2 у двумерного массива два столбца (номер 0 и номер 1) и 10 строк (пронумерованные от 0 до 9), а элементов всего 20. Как и в случае с одномерными массивами, для доступа к элементам многомерных массивов используется их индексация, т.е. для указания отдельного элемента применяют номер его столбца и номер строки. Индексация двумерного массива во многом напоминает указание ячейки листа Excel; первая размерность массива соответствует столбцам листа, а вторая — строкам.
Если у массива на рис. 14.2 имя NumTable, то следующая инструкция присваивает значение 10,2 (из первого ряда второго столбца массива) переменной AnyNum:
AnyNum= NumTable(1,0)
Аналогичным образом следующая инструкция присваивает значение 2,5 элементу из второго ряда первого столбца массива:
NumTable)1,0)= 2,5
446
Неделя 2
Элемент (0,1)
Элемент (0,3)
Двумерный массив
Столбец 0	Столбец 1
12.4	10.2	Ж
* 2.5	1.2
16.7	9.3
А. 8.18	88.1	*
0.125	23.4
21.45	12.1
17.8	41.2
62.37	55.4
7818.01	86.1
37.2	99.02
Строка 0
Строка 1
Строка 3
Строка 4
Строка 5
Строка 6
Строка 7
Строка 8
Строка 9
Строка 2
/---------Элемент (1,3)
Элемент (1,0)
Рис. 14.2. Двумерный числовой массив. Такие массивы обычно используются для представления таблиц данных в виде строк и столбцов
Обратите внимание, что в обоих выражениях индексы массива заключены в круглые скобки, и координаты столбца и строки отделены запятыми.
В массивах может быть более двух размерностей. На рис. 14.3 показан трехмерный массив; у него есть длина, ширина и (скажем так) высота. Элементы трехмерного массива можно представить в виде ящиков, сложенных штабелем во столько-то ящиков длины, во столько-то ширины и во столько-то высоты. По-другому трехмерный массив можно представить в виде страниц книги, на каждой из которых находится таблица с одинаковым числом строк и одинаковым числом столбцов. На рис. 14.3 показан массив на трех “страницах” (пронумерованных от 0 до 2); на каждой из них находится таблица с двумя столбцами и 10 строками. В рамке каждого элемента показаны его индексы.
Можно создавать массивы более чем с тремя размерностями; фактически VBA дает возможность задавать вплоть до 60 размерностей. Работа с массивами четырех и более размерностей быстро становится утомительной; к счастью, велика вероятность, что вам этого не придется делать. В основном, в программировании требуется использовать одно- и двумерные массивы. Вам редко понадобятся массивы более сложные, чем список или таблица данных, поэтому лучше оставить их в покое. Хотя, возможно, вы с ними и столкнетесь, но потребность в такого рода массивах невелика.
День 14-Й. Массивы
447
Трехмерный массив
I О I
| 2 |
Столбец 0	Столбец 1
Эломииг (00.0)	Элемент (0.1,0)
Элемент (0,0,1)	Элемент (01,1)
Элемент (0,0,2)	Элемент (0,1 Д
Элемент (0,0.3)	Элемент (0,1,3)
Элемент (0,0,4)	Элемент (0,1,4)
Столбец 0	Столбец 1
Элемент (1,0,0)	Элемент (1,1,0)
Элемент (1,0,2)	Элемент (1,1,2)
Элемент (1,0,2)	Элемент (1,1,2)
Элемент (1,0,2)	Элемент (1,1,2)
Элемент (1.0,2)	Элемент (1,1,2)
Столбец 0	Столбец 1
Элемент (2,0,0)	Элемент (2,1,0)
Элемент (2,0,2)	Элемент (2,1,2)
Элемент (2,0,2)	Элемент (2,1,2)
Элемент (2,0,2)	Элемент (2,1,2)
Элемент (2,0,2)	Элемент (2,1,2)
Рис. 14.3. Трехмерный массив аналогичен таблице, расположенной на нескольких страницах
Так надо
Индекс массива следует заключать в круглые скобки.
Индексы многомерного массива необходимо отделять запятыми.
В качестве индексов массивов требуется использовать целые числа.
Так не надо
Не отделяйте пробелами имена массивов и индексы: в программе набирайте имя, скобки и индексы как одно аМаапи|миМИ|11Д18||1И1М^
Статические и динамические массивы
Обычно число элементов массива указывается при его описании; эта операция будет изучаться в следующем разделе урока. Описание сообщает VBA, какая величина у каждой из размерностей массива. После того как массив описан, VBA выделяет под все его элементы нужное количество памяти. Для массива на рис. 14.1 ее надо выделить столько, чтобы помещалось 10 чисел типа Integer; для массива на рис. 14.2 нужна память под 20 таких чисел и т.д.
(ПРИМЕЧАНИЕ
Переменные массивов подчиняются тем же самым правилам видимости и сохранения в памяти (scope and persistence rules), что и другие переменные. Об этих правилах рассказывалось в ходе 3-го урока “Что такое типы данных, переменные и константы”, а также на 11-м уроке.
448
Неделя 2
VBA резервирует память под элементы массива на все время существования его переменной. Такого рода массивы называются статическими, так как число их элементов не меняется.
Выбор размера массива может вызвать значительные трудности, когда неизвестно количество данных, которые будут в нем храниться, или когда это количество может сильно меняться. Если для этого иногда требуется 100 значений, а иногда — только 10, то место для 90 значений тратится впустую (разница между наибольшим и наименьшим числом элементов).
Для такого рода случаев в VBA предусмотрен особый тип массивов, называемых динамическими. Своему названию они обязаны тому, что в них можно менять число элементов по ходу выполнения программы. Динамический массив при грамотном программировании может увеличиваться или уменьшаться точно в соответствии с числом своих элементов, не тратя при этом впустую память. Для того чтобы изменить размер такого массива, используйте инструкцию ReDim. О ней еще будет рассказано на этом уроке.
Теперь, когда вы ознакомились с тем, каким образом данные хранятся в массивах, самое время узнать, как следует описывать и использовать массивы. Этому посвящена оставшаяся часть урока.
Инструкция Option Base
До сих пор вам попадались массивы с нумерацией, имеющей нижнюю границу 0. В этом случае индекс любой размерности первого элемента равен 0; поэтому у массива с 10 элементами будут индексы от 0 до 9. Такой метод, очевидно, может привести к путанице, так как индекс 0 в действительности указывает на первый элемент массива, индекс 5 — на шестой элемент и т.д.
Было бы намного удобнее, если бы элементы массива нумеровались начиная с 1, а не с 0. В таком случае индекс 1 означал бы первый элемент массива, индекс 5 — пятый и т.д.
К счастью, в VBA имеются две возможности, чтобы назначить первый номер элементов массива. Во-первых, можно указывать наименьший номер индексов при описании массива (об этом еще будет рассказано на уроке), а во-вторых, чтобы указать, начинается ли нумерация индексов с 0 или 1, можно использовать директиву компилятора Option Base.
Директива компилятора Option Base имеет следующий синтаксис.
Option Base Oil
Эта директива дает возможность по умолчанию назначить 0 или 1 в качестве нижней границы любого индекса. Если не используется Option Base, то по умолчанию нумерация массива начинается с 0. Эту инструкцию необходимо поместить в область описаний модуля перед всеми описаниями переменных, констант или процедур. Инструкцию Option Base нельзя размещать внутри процедуры.
Следующие инструкции служат примерами директивы компилятора Option Base.
Option Base 0 ' значение по умолчанию
Option Base 1 ' индексы массива начинаются с 1
ПРИМЕЧАНИЕ
В модуле необходимо иметь только одну инструкцию Option Base; ее действие имеет силу для всех массивов, описанных в модуле: безразлично, на уровне ли процедуры или на уровне модуля.
День 14-й. Массивы
449
Описание массивов
До этого времени вам была известна инструкция Dim, применяемая для описания переменных. Как вы могли ожидать, эта инструкция используется и при описании массивов. Фактически можно сказать, что ключевое слово Dim является сокращением от dimension (размерность). С самого начала в программном языке BASIC это ключевое слово использовалось исключительно для назначения размерностей массивов, отсюда и сокращение. Однако в современном Visual Basic for Applications использование ключевого слова Dim было расширено на все переменные. С помощью Dim можно описывать и одно- и многомерные массивы.
При описании массива с помощью инструкции Dim используется следующий синтаксис.
Dim VarName([Subscripts])[As Type]
VarName представляет любое имя массива, соответствующее стандартным правилам VBA. Subscripts представляет размерность (размерности) массива. Можно описывать массивы, имеющие вплоть до 60 размерностей. Для одномерного массива в Subscripts описывается одна размерность; для двумерного — две размерности (выражения для каждой из них отделены друг от друга запятой) и так далее, до того количества размерностей, которое нужно. Каждое выражение внутри Subscripts добавляет в массив новую размерность.
ПРИМЕЧАНИЕ
Можно описывать статические и динамические массивы с помощью ключевых слов Public, Private и Static точно так, как и другие переменные и с тем же эффектом по отношению к видимости и сохранению в памяти массива.
Выражение Subscripts имеет такой синтаксис.
[lower То] upper [,[lower То] upper]...
Переменная lower указывает на нижнюю границу индекса массива, a upper — на верхнюю. Обратите внимание, что в каждом из выражений верхняя граница upper обязательна в отличие от lower То. Если будет указана только верхняя граница, то VBA будет нумеровать элементы массива в зависимости от значения в Option Base. Если это значение 1, то элементы будут нумероваться от 1 до значения upper; в противном случае нумерация будет начинаться с 0.
Если в выражении Subscripts будут находиться lower То, то это будет значительно облегчать понимание программ и помогать находить в них ошибки, что сделает программы более надежными. Использование lower То также дает возможность указать начальное значение индекса, которое отличается и от 0, и от 1. Например, если в программе требуется создать массив с элементами, пронумерованными от 5 до 10 или от -5 до 0, то такая возможность существует.
Как и в стандартных описаниях переменных, тип данных можно описать и для массивов. Для этого в описание массива надо включить такое выражение, как As type. Тогда у каждого элемента массива будет указанный тип данных, type представляет любой тип данных, соответствующий правилам VBA: Currency, Double, String и т.д. Можно также описывать массивы с типом данных, определяемым пользователем. Если type будет пропущен, то у всех элементов массива будет тип Variant. При инициализации элементы числовых массивов получают значение 0, а строковых — значение пустой строки.
450
Неделя 2
Обратите внимание, что выражение Subscripts не является обязательным. Чтобы создать динамический массив, их нужно пропустить (скобки в описании массива должны остаться, независимо от того, будет или не будет пропущено выражение, обозначаемое Subscripts).
Все приведенные ниже примеры соответствуют правилам описания массивов.
Dim January)1 То 31) As String
Dim January(31) As String 'предполагается, что Option Base 1
Dim MailingListf) As MailData 'динамический массив определяемого пользователем типа
Dim Grab Bag() 'динамический массив типа Variant
Dim LookupTable(2, 10) 'предполагается, что Option Base 1
Dim HexMultiplicationfO To 15, 0 To 15) As String
Dim LookupBook(l To 3, 1 To 2, 1 To 10)
В этих примерах следует обратить внимание на то, что если задано Option Base 1, то первые две инструкции описывают идентичные массивы: оба одномерные и имеют по 31 элементу, проиндексированному от 1 до 31. В описании третьего массива (MailingList) в качестве типа данных элементов используется определяемый пользователем тип. (Определяемые пользователем типы данных были описаны на 11-м уроке.) И в третьем, и в четвертом описании пропущены индексы; это описания динамических массивов. В пятом и шестом примерах описаны двумерные массивы. В последнем примере представлен трехмерный массив.
Так надо
Помните, что если в описании массива есть выражения Subscripts, то будет создан статический массив с фиксированным числом элементов.
Учитите, что если в описании массива пропущены выражения Subscripts, то будет создан динамический массив. Имейте в виду, что значение Option Base может повлиять на общее число элементов в массиве. Допустим имеется такое описание.
Dim NumArray(lO)
Если Option Base 1, то элементы этого массива индексируются от 1 до 10, а получается их всего 10. Однако в случае отсутствия Option Base или наличия Option Base 0 элементы массива будут проиндексированы от 0 до 10, таким образом их будет уже 11.
Использование массивов
Как только массив описан, самое время применить его. Как говорилось в начале урока, для доступа к элементу массива надо использовать его имя с индексными значениями, заключенными в скобки.
Для доступа к элементу массива используется такой общий синтаксис.
arrayName(valid!ndexl, [validlndex2]...)
Здесь arrayName представляет имя массива. Выражение validlndexl — соответствующий правилам первый индекс, a validlndex2 — второй индекс, если он есть. При любом доступе к элементу массива необходимо указывать значение индекса для каждой размерности. Например, для двумерного массива необходимо указывать два значения. Индекс, соответствующий правилам, — это любая переменная VBA или выражение, равное целому числу из диапазона значений, определенных для данного индекса при описании массива. Например, для одномерного массива, при описании которого значения индек
День 14-й. Массивы
451
са были определены в диапазоне от 1 до 10, такое значение может быть значением из указанного диапазона для любого целочисленного выражения VBA. Если значение индекса какой-либо размерности выходит за пределы установленного для нее диапазона, VBA выводит на экран сообщение об ошибке выполнения.
Вот пример типичного описания и использования массива.
Dim Factorial(0 То 30) As Double
Dim I As Integer
Factorial(0)=l
For 1=1 To 30
Factorial)I)=I * Factorial(1-1)
Next I
В листинге 14.1 показана процедура DemoStaticArray, где числовой массив описывается и используется для приема и последовательной обработки группы чисел. При изучении этого листинга обратите внимание, что в нем находится целый модуль с директивами компилятора, описаниями констант уровня модуля и двумя полностью описанными процедурами.
При выполнении процедуры DemoStaticArray вначале предлагается указать, какое количество чисел следует обработать. (Будет предложено ввести значение от 3 до 15.) Затем надо будет ввести сами эти числа. Каждое из них будет храниться в элементе массива. После того как будет введено последнее из этих чисел, все они появятся на экране в листе Excel. (Вывод на экран осуществляется только ради наглядности; ввод и обработка данных будут проводиться лишь с содержимым массива.) Наконец, поступает запрос указать диапазон элементов массива (например, первые пятнадцать элементов или числа со второго по восьмое), для которых процедура вычислит сумму и среднее. Вначале надо будет указать нижний предел диапазона, а затем — верхний. После того как определен диапазон, числа из него суммируются и на экране появляются сумма и среднее.
Листинг 14.1. Оиисание и иснояьзовоиие статического чнсдового мгссогг о нроцеддре DemoStatlcftrrag
1:	Option Explicit
2:	Option Base 1
3:
4:	'Процедура DemoStaticArray объявляет и использует
5:	'статические числовые массивы.
6:
7:	'Макс, число элементов в массиве
8:	Const ARRAY_MAX As Integer = 15
9:
10:	'Мин. число элементов, которое нужно ввести
11:	Const ARRAY_MIN As Integer = 3
12:
13:	Sub DemoStaticArray))
14:	'Объявление одномерного массива
15:	Dim NumArray(ARRAY_MAX) As Double
16:	Dim	aSum As Double	'сумма чисел
17:	Dim	Count As Integer	'счетчик цикла
18:	Dim	NumCnt As Integer	'для подсчета чисел
19:	Dim	cLow As Integer	'нижиий предел суммы
20:	Dim	cHigh As Integer	'верхний предел суммы
452
Неделя 2
21:	Dim	mStr	As	String	'строка сообщения
22:	Dim	pStr	As	String	'строка приглашения
23:	Dim	tRsp	As	String	'возвращаемое при вводе значение
24:	Dim	oldSheet As String 'исходное имя листа
25:
26:	'запоминаем исходное имя листа
27:	oldSheet = ActiveWorkbook. ActiveSheet. Name
28:
29:	'выбираем новый лист
30:	ActiveWorkbook.Sheets("Лист1").Select
31:
32:	'очищаем ячейки для отображения
33:	'содержимого массива
34:	For Count = 1 То (ARRAY MAX + 2)
35:	Cells(Count, l).Value~= ""
36:	Cells(Count, 2).Value =
37:	Next Count
38:
39:	'Предлагаем указать число вводимых элементов.
40:	'Требуем ввести как минимум ARRAY_MIN чисел.
41:	Do
42:	pStr = "Сколько чисел вы введете ? (от" & _
43:	ARRAY_MIN & " До" &
44:	Str(ARRAY_MAX) & ") "
45:	tRsp = InputBox)prompt:=pStr,
46:	Title:=”BBOfl целого числа",
47:	Default:=ARRAY_MAX)
48:	If Len(Trim)tRsp)) = 0 Then
49:	CancelDemo сМзд:="Ввод отменен.",
50:	rSheet:“OldSheet
51:	Else
52:	NumCnt = Clnt(tRsp)
53:	End If
54:
55:	If (NumCnt	< ARRAY_MIN) Or (NumCnt > ARRAY_MAX) Then
56:	MsgBox "Введите число от " &
57:	ARRAY MIN & " До " & ARRAY MAX
58:	End If
59:	Loop Until (NumCnt >= ARRAY MIN) And (NumCnt <= ARRAY_MAX)
60:
61:	'Ввод чисел в массив
62:	For Count = 1 To NumCnt
63:	pStr = "Введите " & Count & "-e число"
64:
65:	'get a value
66:	tRsp = InputBox(prompt:=pStr,
67:	ТШе:="Ввод действительного числа",
68:	Default:=Count)
69:	'Состоялся ли ввод?
70:	If Len(Trim(tRsp)) =	0	Then
71:	CancelDemo сМзд:="Ввод данных отменен",
72:	rSheet:“oldSheet
73:	Else
День 14-й. Массивы
453
74:	'Сохраняем введенные числа в элементах массива NumArray
75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: ИЗ: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126:	NumArray)Count) = CDbl(tRsp) End If Next Count 'Выводим элементы массива NumArray на лист For Count = 1 To NumCnt Cells(Count, 1).Value = Count S ”-e число" Cells(Count, 2).Value = NumArray(Count) Next Count 'Выясняем последовательность элементов, для которых 'будем вычислять сумму и среднее Do 'Первый элемент последовательности Do tRsp = InputBox(prompt:="Введите номер первого " & "элемента последовательности:", Т1Х1е:="Ввод целого числа", Default:="l") If Len(Trim)tRsp)) = 0 Then cLow = 0 Else cLow = Cint(tRsp) End If If (cLow < 1) Or (cLow >= NumCnt) Then MsgBox prompt:="Вы должны ввести номер " & "от 1 до " & (NumCnt - 1) End If Loop Until (cLow >= 1) And (cLow < NumCnt) 'Последний элемент последовательности Do tRsp = InputBox)prompt:="Введите номер последнего " & "элемента последовательности:", Title:="BBOfl целого числа", Default:=NumCnt) If Len(Trim(tRsp)) = 0 Then cHigh = 0 Else cHigh = Clnt(tRsp) End If If (cHigh <= cLow) Or (cHigh > NumCnt) Then MsgBox prompt:="Вы должны ввести число " & "от " & (cLow + 1) & " до " & NumCnt End If Loop Until (cHigh > cLow) And (cHigh <= NumCnt) 'Складываем элементы массива NumArray, начиная с 'NumArray(cLow) и до NumArray(cHigh)
454
Неделя 2
127:
128:	'устанавливаем сумму в 0,	потом	в	цикле	перебираем
129:	'массив, вычисляя сумму последовательности
130:	aSum = 0
131:	For Count = cLow To cHigh
132:	aSum = aSum + NumArray(Count)
133:	Next Count
134:
135:	'выводим результат вычисления суммы и среднего
136:	mStr = "Сумма NumArray)" & cLow & ’) до NumArray(" S
137:	cHigh & ") = " & aSum S vbCr
138:	mStr = mStr & "Среднее NumArray)" & cLow &
139:	") до NumArray)" s cHigh &")="&_
140:	(aSum / (cHigh - cLow +1)) S
141:	vbCr & vbCr & "Хотите выбрать другую последовательность?"
142:	Count = MsgBox(prompt:=mStr,
143:	Buttons:=vblnformation + vbYesNo,
144:	ТШе:="Вывод")
145:	Loop Until Count = vbNo
146:
147:	'Возврат на исходный лист
148:	ActiveWorkbook.Sheets(oldSheet).Select
149:	End Sub
150:
151:	Sub CancelDemo(eMsg As String, rSheet As String)
152:	'Выводит сообщение eMsg, делает активным
153:	'лист rSheet, завершает программу.
154:	MsgBox prompt:=cMsg, тШе:="Демонстрация статического массива"
155:	ActiveWorkbook.Sheets(гSheet).Select
156:	End 'завершение программы
157:	End Sub
В процедуре DemoStaticArray продемонстрированы типичные приемы описания статического массива, заполнения его данными и их последующей обработки. Здесь также показано несколько приемов программирования, о которых рассказывалось на предыдущих уроках.
В листинге 14.1 имеется две директивы компилятора и два описания констант уровня модуля. В строке 2 находится директива Option Base 1, благодаря которой во всех массивах, описанных в модуле, начальным номером будет 1. В строке 8 описана константа, указывающая максимальный размер массива; в строке 11 описана другая константа, указывающая его минимальный размер. (Использование констант для задания таких величин может в дальнейшем облегчить изменение исходного текста, если потребуется изменить размер массива.)
В строке 13 находится описание процедуры DemoStaticArray. В строке 15 описывается одномерный статический массив с числом элементов, равным ARRAY МАХ, тип данных которых Double. Из-за директивы Option Base 1 элементы массива будут пронумерованы от 1 до ARRAY_MAX (какой бы ни была сама константа ARRAY_MAX). В строках 16-24 описаны другие переменные, используемые в процедуре: счетчиков циклов, верхнего и нижнего значения индекса, строк подсказок и сообщений и так далее.
День 14-й. Массивы
455
Поскольку в ходе выполнения процедуры активный лист меняется, в строке 27 имя листа, текущего в момент начала процедуры, будет сохранено в переменной oldSheet с тем, чтобы процедура могла снова восстановить его в качестве текущего.
В строке 30, для того чтобы сделать активным 1-й лист, применяется метод Select свойства Sheets. В строках 34-37 цикл For...Next очищает содержимое ячеек листа, в которых будут выведены элементы массива. Обратите внимание, что значение, при котором заканчивается цикл, равняется константе ARRAY_MAX плюс 2 (чтобы очистить две дополнительные строки листа). Все, что надо сделать для увеличения максимального размера массива с помощью этой константы, — это изменить описание самой константы ARRAY_MAX; в процедуре или программе не требуется жестко фиксировать ни один из циклов For... Next.
Теперь начинается реальная работа процедуры. В строках 41-59 находится цикл Do.. .Until, с помощью которого процедура узнает от пользователя, сколько элементов массива надо заполнить. В строках 42-44 создается строка подсказки, которая будет выведена в окне ввода, находящемся в строках 45-47. Обратите внимание, что в инструкции InputBox в качестве числа заполняемых элементов предлагается максимальный размер массива. В строках 48-53 проверяется введенное пользователем значение — не отменен ли процесс ввода. Если отменен, то будет вызвана процедура CancelDemo с соответствующими аргументами. (Эта процедура находится в листинге в строках 151-157.) Если ввод не отменен, то строка в tRsp преобразуется в число типа Integer (с помощью функции CInt) и сохраняется в переменной NumCnt.
Далее, в строках 55-58 проверяется, чтобы NumCnt была больше или равна минимальному размеру массива и не превышала его максимальный размер. Наконец, в строке 59 цикл завершается; это происходит только тогда, когда пользователь ввел значение в пределах между минимальным и максимальным числом элементов.
В строке 62 начинается цикл For.. .Next, где предлагается ввести число, которое затем сохраняется в элементе массива. Этот цикл выполняется то число раз, которое введено в NumCnt. В строке 63 создается строка подсказки, которая будет выведена в окне ввода, находящемся в строках 66-68. Обратите внимание, что в данном окне ввода значением по умолчанию является текущее значение счетчика цикла; при желании можно при каждом проходе цикла просто нажимать Enter или щелкать на кнопке ОК. Инструкция If, которая начинается в строке 70, проверяет, не отменил ли пользователь ввод данных. Если отменил, то снова должна быть вызвана процедура CancelDemo, которая завершит всю программу. В противном случае введенное пользователем значение преобразуется инструкцией, находящейся в строке 75, в число типа Double (с помощью функции CDbl) и сохраняется в элементе массива со значением индекса, равным текущему значению счетчика циклов. При первом выполнении этого цикла введенное значение сохраняется в элементе 1, при втором выполнении — в элементе 2 и т.д. Цикл For.. .Next завершается в строке 77.
ПРИМЕЧАНИЕ
Обратите внимание, что в процедуре DemoStaticArray все циклы For.. .Next используют одну и ту же переменную счетчика цикла Count. Допускает это потому, что ни един из этих циклов не является вложенным в другом таком же и каждый раз выполняется только един из них, поэтому они могут использовать одну и ту же переменную счетчика цикла, сокращая тем самым число используемых программой переменных.
В строках 80-83 находится другой цикл For...Next, который просто выводит элементы массива в выбранный ранее лист. Этот процесс нужен только для того, чтобы показать, что происходит в ходе выполнения процедуры; отсутствие этих инструкций на ее работу не повлияет.
456
Неделя 2
В строках 87-145 находится цикл Do...Until, который вначале выводит нижний и верхний пределы диапазона обрабатываемых элементов массива, а затем складывает все их значения и вычисляет среднее.
В строке 89 начинается вложенный цикл Do...Until, который спрашивает у пользователя нижний предел этого диапазона. Обратите внимание, что пользователь не сможет отменить эту операцию; ему все равно придется ввести номер элемента, лежащего между первым и предпоследним элементом с введенными данными. (Необходимо, чтобы в качестве нижнего предела не был выбран последний элемент массива, иначе его верхний предел будет за пределами самого массива.)
В строке 107 начинается другой вложенный цикл Do...Until, и на этот раз — для получения верхнего предела обрабатываемых элементов массива. Обратите внимание, что этот цикл также не дает отменить свое выполнение, а просто требует, чтобы было введено число, лежашее между уже выбранным нижним значением и максимальным номером заполненных элементов массива (представленным NumCnt). Цикл завершается в строке 123.
В строке 130 переменная aSum инициализируется в 0, а затем в строках 131-133 цикл For.. .Next вычисляет сумму, проходя по всем элементам массива (от верхнего до нижнего номера) и последовательно складывая их содержимое со значением переменной aSum.
В строках 136-141 создается текст сообщения, где будут представлены введенные пользователем нижний и верхний пределы, сумма всех элементов диапазона и их среднее. Обратите внимание, что последняя величина вычисляется на ходу в строке 140 во время создания текста сообщения. Функция MsgBox, вызываемая в строке 142, выводит на экран только что созданное сообщение и спрашивает пользователя, будет ли он работать с другим диапазоном элементов массива. С помощью аргумента Button, находящегося в строке 143, на экран в окне сообщения выводятся значок I (информация), а также кнопки Да и Нет. Значение MsgBox присваивается переменной Count. Несколько рискованный вариант использования переменной, но он позволяет сэкономить одну переменную в процедуре.
На рис. 14.4 показан пример сеанса работы процедуры DemoStaticArray, на котором видно сообщение, выдаваемое инструкцией MsgBox со строки 142.
И наконец, в строке 145 в качестве определяющего условия цикла проверяется значение Count: если пользователь в окне сообщения (строка 142) выбрал Да, то цикл выполняется снова (возвращается к строке 89); в противном случае он завершается. И в конце концов в строке 148 восстанавливается лист, который был активным в момент вызова процедуры DemoStaticArray, а сама процедура завершается в строке 149.
Теперь пару слов о процедуре CancelDemo (строки 151-157). Она была написана, чтобы не дать процедуре DemoStaticArray продолжаться слишком долго, и представляет собой оригинальный способ прекращения ее работы. Имеется два места, где процедура DemoStaticArray позволяет пользователю отменить ввод данных и таким образом прекратить работу всей программы. В обоих случаях пользователю необходимо сообщение о том, что работа прервана, и надо навести порядок, восстановив лист, активный в момент начала работы DemoStaticArray. Процедура CancelDemo была создана как раз для того, чтобы не дублировать исходный текст: она принимает аргумент для выводимого сообщения (чтобы каждый раз было особое сообщение) и аргумент для восстанавливаемого листа.
День 14-й. Массивы
457

’aJ Файл Правка Вид Вставка Формат Сервис Данные ^кно, Древка
jD с? И Л&	г Л il О <3 ^А"»1	’ 10 ««SliilT
——__,—дЕ__д_1_—„Z..Z—./ 7. А В C D Е F G Н IT 1 11-е число	1
2 '2-е число	2
3 |3-е число
4 4-е число: 5 ’5-е число
6 а 6-е число, _?_• 7-е число
8 с; 8-е число: 9 9-е число 10 10-е числ 11 ,11-е числ' 12'12-е числ-1? 13-е числ: 14 14-е числ:
15 :15-е числ
Вывод	|3
5
6	- i ) C^MMaNurnArray(1)noNumArrt^(15)-12(l
ХГ Среднее NumArrayO) до NumAna^S)* 8
8	Хотите выбрать другую последовательность?
9	................ ' -	
ю'	______*т_J..-'
-и--------------------------------------------------
12
13
14
15
16	—
17
18	v
И < ► Н\ЛИСТ1/Ъктг/Листз/	|«|	|	>|Г
Готово
Рис. 14.4. Пример сеанса работы процедуры DemoStaticArray
Так надо
Для указания минимальных и максимальных размеров массива используйте константы: это значительно облегчит процесс изменений в тексте программ, обрабатывающих массивы. Например, если в листинге 14.1 требуется сделать максимальный размер массива равным 20 элементам, для этого достаточно лишь изменить описание константы ARRAY МАХ. Поскольку везде в программе, где работают с массивом, используют эту константу, то не следует беспокоиться об изменениях в работающих с ним циклах For... Next.
Использование ReDim с динамическими массивами
На этом уроке уже говорилось, что возможны обстоятельства, когда заранее неизвестно, сколько элементов будет в массиве. В демонстрационной программе из листинга 14.1 совершенно произвольно предполагается, что обрабатываться будет не более 15 чисел.
Если в процедуре с этого листинга для обработки будет выбрано менее 15 чисел, то неиспользуемые элементы массива все равно будут занимать память (ввиду того что массив описан как статический с 15 элементами, столько их у него всегда и будет). Неиспользуемые элементы массива напрасно занимают память и переводят ресурсы компьютера, которые могут понадобиться другим процедурам или программам. Другая проблема в том, что в этой процедуре нельзя обрабатывать более 15 чисел.
Обе проблемы решаются путем замены статического массива динамическим. В этом случае можно создавать массив, который по мере надобности становится больше или меньше. Динамический массив создается с помощью инструкции Dim, а его размер будет задаваться по ходу программы с помощью другой инструкции ReDim.
458
Неделя 2
Для инструкции ReDim используется такой общий синтаксис.
ReDim (Preserve] varname(subscripts) [As type]
[varname(subscripts) [ As type]]...
Необязательное ключевое слово Preserve, судя по названию (сохранить), обеспечивает сохранение данных в существующем массиве при изменении его размера с помощью ReDim, varname представляет имя массива, a subscripts — размерности массива. (Синтаксис выражений varname в инструкциях ReDim и Dim аналогичный.) type представляет любой стандартный или определяемый пользователем тип данных. Отдельное выражение As type необходимо использовать при каждом определении массива.
Все указанные примеры соответствуют правилам описания динамических массивов и возможного использования с ними инструкций ReDim.
Dim aMonth() As String 'описывает динамический массив aMonth
ReDim aMonth(1 To 30) 'заново задает размер массива в 30 элементов
ReDim aMonth(1 То 31) 'заново задает размер массива в 31 элемент
ReDim Preserve aMonth(1 То 31) 'заново задает размер массива в 31 элемент с сохранением содержимого
Dim LookupTable() As Integer 'описывает динамический массив
ReDim LookupTable(3,15) 'заново задает размер и двумерную структуру массива
ReDim LookupTable(4,20) 'заново задает размер массива
ReDim Preserve LookupTable(4,25) 'можно заново задавать только последнюю размерность Dim Grab_Bag As Variant 'описывает переменную типа Variant
ReDim Grab_Bag(20) As Integer 'создает в Variant массив из 20 целых чисел
В этих примерах показаны некоторые важные моменты. Во-первых, при использовании в многомерном массиве ключевого слова Preserve можно изменить значение только последней размерности. Во-вторых, можно использовать ReDim для создания массива определенного типа внутри Variant-переменной. Поскольку в такой переменной могут храниться данные любого типа, то ее можно использовать и для хранения динамического массива! (Использование переменной типа Variant для хранения динамического массива дает возможность использовать по отношению к нему ReDim и менять тип его данных.)
Обычно инструкция ReDim служит для задания или изменения размера динамического массива, который уже был описан с помощью инструкций Dim, Private, Public или Static. Ее можно использовать сколько угодно раз для изменения в динамическом массиве количества элементов и размерностей. Однако с помощью инструкции ReDim нельзя изменить тип данных массива, если только он не содержится в переменной типа Variant или его элементы сами не имеют такого типа. Если динамический массив хранится в переменной типа Variant, то тип данных можно поменять в инструкции ReDim с помощью выражения As type.
ПРИМЕЧАНИЕ
Попытка использовать ReDim по отношению к статическому массиву (массиву, размерности которого точно указаны в описаниях Dim, Private, Public или Static) приводит к сообщению об ошибке выполнения.
День 14-Й. Массивы
459
ПРИМЕЧАНИЕ
В любом из главных приложений Microsoft Office 2000 (или более поздней версии) правилами VBA разрешается присваивать все содержимое массива динамическому массиву с помощью такого рода инструкций:
DynArray() = AnyArray))
После выполнения инструкции динамический массив слева от знака равенства (=) будет содержать копию массива, находящегося справа от него. При необходимости VBA изменит размер динамического массива, чтобы величины размерностей обоих массивов совладали. Если у динамического массива описан некоторый тип данных, то у второго массива должен быть совместимый тип.
В листинге 14.2 показано использование динамического массива. С помощью процедуры DemoDynamicArray из этого листинга выполняется то же задание, что и в листинге 14.1, но с применением динамического массива для ввода любого количества чисел без использования лишней памяти.
Листинг 14.2. Оннсанне и всвввьзвваввв дввамвчвсквгв массива в нрвцедурв 0ешв0диаш1сД ггау 
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:	Option Explicit Option Base 1 'минимальное число элементов массива Const ARRAY_MIN As Integer = 3 Sub DemoDynamicArray)) Dim NumArrayO As Double 'Объявление динамического массива Dim Array_Size As Long	'размер массива Dim aSum As Double	'сумма чисел Dim Count As Long	'счетчик цикла Dim cLow As Long	'нижний предел суммы Dim cHigh As Long	'верхний предел суммы Dim mStr As String	'строка сообщения Dim pStr As String	'строка приглашения Dim tRsp As String	'результат функции InputBox Dim oldSheet As String	'имя исходного листа 'запоминаем имя исходного листа oldSheet = ActiveWorkbook.ActiveSheet.Name 'выбираем новый лист ActiveWorkbook.Sheets("Лист1").Select 'Сколько чисел будет введено? 'Минимальное число - ARRAY_MIN. pStr = "Сколько чисел вы введете? (минимум " & ARRAY_MIN & Do tRsp = InputBox)prompt:=pStr,
• / 	ТШе:="Выбор размера массива", Default:=ARRAY_MIN)
- •	If Len(Trim)tRsp)) = 0 Then
460
Неделя 2
34:	CancelDemo cMsg:="Pa3Mep массива на указан.'1,
35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86:	rSheet:=oldSheet Else Array_Size = Cint(tRsp) End If If (Array_Size < ARRAY_MIN) Then MsgBox "Введите число больше " & (ARRAY_MIN - 1) & End If Loop Until (Array Size >= ARRAY_MIN) 'размер динамического массива ReDim NumArray(Array_Size) 'Очистка ячеек ClearDisplay Rows:=(Array_Size + 2), Cols:=2 'Ввод чисел в массив For Count = 1 To Array_Size pStr = "Введите " & Count 4 "-e число" 'Ввод числа tRsp = InputBox(prompt:=pStr, Title:="BBOfl числа", Default:=Count) 'Состоялся ли ввод? If Len(Trim(tRsp)) = 0 Then CancelDemo сМзд:="Ввод данных прерван", rSheet:=oldSheet Else 'Сохранение числа в элементе массива NumArray NumArray(Count) = CDbl(tRsp) End If Next Count 'Вывод массива в лист DisplayArray Rows:=Array_Size, ArrayVal:=NumArray 'Выясняем последовательность элементов, для которых 'будем вычислять сумму и среднее Do 'Первый элемент последовательности Do tRsp = InputBox(prompt:="Введите номер первого " 4 "элемента последовательности:", Title:="8BOfl целого числа", Default:=“1") If Len(Trim(tRsp)) = 0 Then cLow = 0 Else cLow = Clnt(tRsp) End If
День 14-й. Массивы
461
87:	If (cLow < 1) Or (cLow >= Array_Size) Then
88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132:	RangeErrorMsg mLow:=l, mHigh:“Array Size - 1 End If Loop Until (cLow >= 1) And (cLow < Array_Size) 'Последний элемент последовательности Do tRsp = InputBox(prompt:“"Введите номер последнего “ & "элемента последовательности:", Title:="BBOfl целого числа", Default:=Array_Size) If Len(Trim(tRsp)) = 0 Then cHigh = 0 Else cHigh = Clnt(tRsp) End If If (cHigh <= cLow) Or (cHigh > Array_Size) Then RangeErrorMsg mLow:=cLow + 1, mHigh:“Array Size End If Loop Until (cHigh >= cLow) And (cHigh <= Array Size) 'Складываем элементы массива NumArray, начиная с 'NumArray(cLow) и заканчивая NumArray(cHigh) 'устанавливаем сумму в 0, потом в цикле перебираем 'массив, вычисляя сумму последовательности aSum = 0 For Count = cLow To cHigh aSum = aSum + NumArray(Count) Next Count 'выводим результат вычисления суммы и среднего mStr = "Сумма NumArray)" & cLow & ") до NumArray)" & cHigh &")="& aSum & vbCr mStr = mStr & "Среднее NumArray)" & cLow & ") до NumArray)" & cHigh &")="&_ (aSum / (cHigh - cLow + 1)) & vbCr & vbCr & "Хотите выбрать другую последовательность?" Count = MsgBox)prompt:=mStr, Buttons:=vblnformation + vbYesNo, Title:="Output") Loop Until Count = vbNo 'Возврат на исходный лист ActiveWorkbook.Sheets(oldSheet).Select
133: End Sub
134:
135: Sub RangeErrorMsg(mLow As Long, mHigh As Long)
136: 137: 138: 139:	MsgBox prompt:“"Введите число " & "от " & mLow & " до " & mHigh, Title:“"Демонстрация динамического массива"
462
Неделя 2
140:	End Sub
141:
142:	Sub CancelDemo(eMsg As String, rSheet As String)
143:	'Выводит сообщение eMsg, делает активным
144:	'лист гSheet, завершает программу.
145:	MsgBox prompt:=cMsg, Title:“"Dynamic Array Demo"
146:	ActiveWorkbook.Sheets(rSheet).Select
147:	End 'Завершение программы
148:	End Sub
149:
150:	Sub ClearDisplayfRows As Long, Cols As Long)
151:	'Очистка ячеек
152:	Dim rCount As Long
153:	Dim cCount As Long
154:
155:	For rCount = 1 To Rows
156:	For cCount = 1 To Cols
157:	Cells(rCount, cCount).Value = ""
158:	Cells(rCount, cCount).Value = ""
159:	Next cCount
160:	Next rCount
161:	End Sub
162:
163:	Sub DisplayArray(Rows As Long, ArrayVal As Variant)
164:	'Выводит элементы массива, переданные в ArrayVal
165:	Dim rCount As Long
166:
167:	For rCount = 1 To Rows
168:	CellsfrCount, 1).Value = rCount & “-e число"
169:	CellsfrCount, 2).Value = ArrayVal(rCount)
170:	Next rCount
171:	End Sub
Процедуры DemoDynamicArray и DemoStaticArray (см. листинг 14.1) очень схожи, но есть некоторые важные отличия. Анализ листинга 14.2 и будет посвящен основным признакам их сходства и различия.
Прежде всего, в строке 2 находится директива Option Base 1, так что все массивы этого модуля будут начинать свою нумерацию с 1. Обратите внимание, что есть только описание константы для минимального размера массива (строка 5), а для максимального размера — нет. В процедуре DemoDynamicArray максимальный размер массива указывает пользователь.
В строке 8 описан динамический массив NumArray. VBA узнает о том, что массив динамический, по тому признаку, что скобки в его описании пусты. Выражение с ключевым словом As указывает на то, что элементами динамического массива являются числа типа Double. В строках 9-17 описаны переменные, которые процедура использует в работе. Обратите внимание, что в строке 9 описывается переменная Array_Size. В ней будет храниться задаваемый пользователем размер массива; переменная заменяет константу ARRAY_MAX из листинга 14.1. Также следует заметить, что переменная NumCnt больше не используется. Объясняется это тем, что в процедуре DemoDynamicArray просто создается массив нужного размера и нет необходимости от
День 14-й. Массивы
463
слеживать, сколько элементов массива из общего их числа хранят нужные данные. В массиве этой процедуры никогда нет лишних элементов.
В строках 20-23 выполняются те же функции по поддержанию порядка (сохранение текущего листа и выбор нового), что и в листинге 14.1.
В процедуре DemoDynamicArray нигде ничего не будет делаться, пока пользователь не задаст размер массива, поэтому в строках 29-44 находится цикл Do...Until, содержащий запрос к пользователю ввести нужное значение. Этот цикл будет повторяться до тех пор, пока пользователь не введет число, большее или равное минимальному размеру массива из трех элементов. Обратите внимание, что введенное пользователем число применяется для инициализации переменной Array_Size.
В строке 47 находится главная инструкция процедуры. Там с помощью ReDim задается размер массива NumArray. При этом создается одномерный массив с числом элементов, равным Array_Size. Если пользователь ввел 3, то будет три элемента; если ввел 20, то и элементов будет 20 и т.д.
В строке 50 вызывается процедура ClearDisplay (она находится в строках 150-161, и речь о ней пойдет чуть позже). Эта новая процедура очищает лист, перед тем как показывать на нем содержимое массива.
ПРИМЕЧАНИЕ
Процедуры ClearDisplay и DisplayArray были созданы, чтобы удалить из DemoDynamicArray исходный текст, не связанный непосредственно с обработкой массива. В DemoDynamicArray больше нет ни одной инструкции для вывода на экран содержимого массива или манипулирования областью вывода. Такие изменения в процедуре приведут к большему соответствию правилам проектирования программ, о которых рассказывалось на 12-м уроке, “Модульное программирование", а также позволят достичь две цели. Во-первых, немного уменьшится процедура обработки массива, что облегчит работу с ней. Во-вторых, от этой процедуры будут отделены инструкции, связанные с выводом на экран. С соответствующими изменениями в ClearDisplay и DisplayArray процедуру DemoDynamicArray можно использовать в любом VBA-проекте, находится ли он в Excel или нет. Те же приемы проектирования используйте для процедур, создаваемых в любой версии VBA.
В строках 53-67 для пользователя выводится подсказка задать элементы массива, как в соответствующем цикле ввода данных из листинга 14.1. Затем в строке 70 для вывода на лист содержимого массива вызывается процедура DisplayArray, которой в качестве аргументов передаются размер массива и сам массив. Для листинга это тоже новая процедура; она находится в строках 161-173, и о ней мы поговорим чуть позже.
Наконец, в строках 74-129 для пользователя выводится запрос назначить нижнюю и верхнюю границу диапазона элементов, для которых нужно вычислить сумму и среднее. Этот цикл и находящиеся в нем инструкции работают точно так же, как и в листинге 14.1. Однако обратите внимание, что вместо применяемой в том листинге константы ARRAY_MAX здесь фигурирует переменная Array Size. Далее, внутренние циклы Do для вывода сообщений об ошибках вызывают другую процедуру, RangeErrorMsg, вместо того чтобы самим выводить эти сообщения.
Процедура RangeErrorMsg находится в листинге 14.2 в строках 135-140. Она была создана, как ClearDisplay и DisplayArray, чтобы сократить процедуру DemoDynamicArray и сделать затраченные вами на программирование усилия более эффективными. Хотя выигрыш в этой небольшой программе и невелик, в более объемной программе вы получили бы существенное сокращение длины и сложности путем использования короткой, многоцелевой процедуры, выводящей на экран в чем-то похожие сообщения об ошибках.
464
Неделя 2
Процедура CancelDemo, находящаяся в строках 142-148, работает так же, как и аналогичная процедура в листинге 14.1.
В строке 150 описана процедура ClearDisplay. У нее два обязательных аргумента: число очищаемых строк и число очищаемых столбцов. ClearDisplay имеет две переменные, выполняющие роль счетчиков (строки 152 и 153), и два вложенных цикла For...Next, предназначенных для очистки всех ячеек, координаты которых соответствуют указанным строкам и столбцам.
В столбце 163 описана процедура DisplayArray. У нее два обязательных аргумента: число строк в массиве и аргумент типа Variant, в котором находится сам массив. Второй аргумент используется, чтобы массив можно было передать как аргумент процедуры; о массивах-аргументах функций и процедур еще будет рассказано в этом уроке. У DisplayArray имеется переменная, выполняющая роль счетчика. В этой процедуре в лист вводится содержимое массива, аналогично тому, что было описано в листинге 14.1.
На рис. 14.5 показан пример сеанса работы процедуры DemoDynamicArray. Обратите внимание, что там ячейки с данными выходят за нижний край видимой части листа — в данном случае пользователь выбрал массив с 20 элементами.
У 2айл Правка Вид Вставке Формат Сервис Данные Qkho Справка
।	-к -	> •, *9 :	•	>
_______А1_________т|_______ '-е число____________________
А В С ' D Ё F G Н Г
1 |1-е число 1
2 2 >э число 2
3 3-е число 4 4-е число' 5 _ 5-е число 6 '.>-е число 7 7-е число 8 е число 9 ;9-е число 10 10-е числ 11 а 11-е числ
12 12-е числ 13 • 13-е числ
3
4
5
6
7
8
9
10
11
12
13
Output
Ы Сумма NumArrey(l) до NumArrayfZOi - 210
Среднее NumArrayfl) ла НитАту(гв) -10.5
Нет
14 • 14-е числ 14
15 15-е числ	15
16'16-е числ	16
17я 17-е числ	17
18 18-е числ	18
М 1 >'М\Лист1/Лист2/Л«тЗ/
Готово	NUM
Рис. 14.5. Пример сеанса работы процедуры DemoDynamicArray
Так надо
Помните, что динамические массивы создаются в описаниях Dim. Private. Public или Static с помощью только пустых скобок.
Помните, что ReDim Preserve в случае многомерного массива используется для изменения величины только последней размерности. Однако в многомерном массиве все размерности можно изменить с помощью одного ключевого слова ReDim.
День 14-Й. Массивы
465
Исходный текст, специфичный для определенного приложения, по мере возможности собирайте в отдельных процедурах, чтобы общераспространенные процедуры можно было адаптировать к любому варианту VBA: Excel VBA, Word VBA, Access VBA и т.д.
Так не надо
Не забывайте, что с помощью ReDim нельзя изменить тип данных массива, если только он не содержится в переменной типа Variant или не является массивом Variant
Функции LBound и UBound
Чтобы отслеживать размеры статических и динамических массивов, вам, возможно, приходилось для хранения минимального и максимального значений индекса использовать две переменные, как это было в листингах 14.1 и 14.2. К сожалению, если в такой ситуации полагаться на собственные переменные, то придется взять на себя всю ответственность за их точность и своевременное обновление. Если вы не будете за это отвечать, то процедура не будет работать корректно. В такой ситуации будет обработано меньше элементов, чем в действительности содержится в массиве, или выйдет сообщение об ошибке выполнения при попытке доступа к элементу массива с индексами, лежащими за пределами действительных размеров массива. Такие программные ошибки трудно отследить.
К счастью, в VBA входят две функции, которые позволяют облегчить бремя работы вручную с минимальным и максимальным значениями индекса: LBound и UBound. Эти функции возвращают нижнее и верхнее значения индекса статического или динамического массива.
Для функций LBound и UBound используется такой общий синтаксис.
LBound(arrayName [(dimension])
UBound(arrayName [(dimension])
Функция LBound возвращает первое значение индекса массива, представленного arrayName. Функция UBound возвращает наибольшее значение индекса массива, представленного arrayName. Эти функции можно использовать либо со статическими, либо с динамическими массивами. Аргумент dimension представляет целое число, указывающее номер индекса, для которого надо получить верхний или нижний предел. Если аргумент dimension будет пропущен, то VBA вернет границу для первого индекса.
Вот примеры использования функций LBound и Ubound.
Dim S(3 То 9) As String
For I=LBbound(S) To UBound(S)
S(I)=String(10, 65+1)
Next I
Dim DatalOYearjl To 365, 1990 To 1999)
For DayNum=LBound(DatalOYear, 1) To Ubound(DatalOYear, 1)
For YearNum=LBound(DatalOYear, 2) To Ubound(DatalOYear, 2)
Matrix(DayNum, YearNum)=Rnd
Next YearNum
Next DayNum
466
Неделя 2
Обратите внимание, что во втором примере в функциях LBound и UBound используется необязательный аргумент dimension. Внешний цикл For...Next выполняется столько раз, сколько элементов находится в первом измерении массива DatalOYear, в то время как внутренний For...Next выполняется для того количества элементов, которое содержится во втором измерении этого массива.
Бывают случаи, когда информация, возвращаемая функцией UBound, не является слишком полезной. Иногда массив не полностью занят нужными данными (как, например, в листинге 14.1) или с массивом необходимо выполнить определенные операции, перед тем как он будет заполнен. Обычно заполнение начинается с первого элемента массива, затем постепенно заполняются элементы со все большими и большими индексами. Часто получается, что индекс последнего элемента с нужными данными меньше максимального значения, возвращаемого UBound. В таком случае необходимо иметь отдельную переменную, чтобы отслеживать максимальный действующий индекс массива. Это значение можно еще назвать текущим максимальным индексом.
Этот момент проиллюстрирован на рис. 14.6. На рисунке представлен массив с именем wordList, который показан в тот момент, когда в него вводятся данные. Нужные данные (список уже введенных слов) находятся в первых шести элементах, в то время как в последних четырех — пустые строки. Обратите внимание, что текущий, или максимальный, действующий индекс массива равен 6. Если со списком надо каким-то образом работать, не дожидаясь, пока закончится ввод данных, для отслеживания текущего максимального индекса нужна переменная (которая и представлена на рисунке).
Массив WordList
####	####	####	####	####	####	??	??	??	??
1 м 2	3	4	5	6 м 7	8	9	10
LBound(WordList)	Curindex	UBound(WordList)
Рис. 14.6. Частично заполненный массив. Обратите внимание, что в переменной Curindex находится текущий максимальный индекс массива WordList. Значимые данные находятся только в элементах между нижней границей и данным значением индекса
Использование Erase для чистки и удаления массива
В VBA имеется специальная инструкция Erase, дающая возможность выполнить одну из двух задач, в зависимости от того, работаете ли вы со статическим или с динамическим массивом. В статическом массиве инструкция Erase очищает все его элементы, приводя, по существу, массив в то состояние, которое было при его создании в памяти. В динамическом массиве Erase полностью удаляет из памяти массив вместе с его содержимым.
После того как элементы массива заполнены, данные в нем остаются (неважно, статический массив или динамический), пока его элементам не будут присвоены новые значения или пока VBA не уничтожит массив. (Для массивов действительны те же правила видимости и сохранения в памяти, что и для других переменных; об этих правилах говорилось на 3-м и 12-м уроках.)
День 14-Й. Массивы
467
В некоторых случаях требуется вычистить из массива все значения: обнулить числа, сделать пустыми строки и т.д. Обычно для того чтобы присвоить всем элементам определенное значение, создается цикл, такой как For...Next или For...Each. Вот примеры использования таких циклов для обнуления всех элементов числового массива.
For k=LBbound(NumArray) То UBoundjNumArray)
NumArray(k)=0
Next k
For Each Num In NumArray
Num=0
Next Num
В первом программном фрагменте для перебора всех элементов массива используются функции LBound и UBound и цикл For...Next; во втором фрагменте для выполнения этой задачи применяется цикл For.. .Each и, как и в первом случае, каждому элементу присваивается значение 0. Хотя эти циклы не такие уж и длинные, ту же задачу для статического массива можно выполнить более эффектно — с помощью одной-единственной инструкции Erase.
Erase NumArray
Массивы имеют тенденцию занимать относительно большие объемы памяти: массив из 20 элементов, например, занимает столько памяти, сколько 20 отдельных переменных того же типа данных. Поскольку для массивов нужно столько памяти, в случае динамических массивов, как только они перестают быть нужными, их следует очистить. (Статический же массив нельзя удалить из памяти, пока VBA его автоматически не сотрет.)
Как и другие переменные, описываемые на уровне процедуры, VBA удалит массивы, описываемые на том же уровне, как только завершится процедура. Массивы же, описанные на уровне модуля, существуют, пока выполняется хотя бы одна процедура из этого модуля. В случае большой программы имеет смысл отозвать память, занимаемую динамическими массивами такого уровня. Это и позволяет сделать инструкция Erase.
Инструкция Erase имеет такой общий синтаксис.
Erase array1 [,array2, ...]
Аргументы arrayl и аггау2 представляют любые имена массивов, соответствующие правилам VBA. В этой инструкции можно указывать сколько угодно массивов, отделяя их имена запятыми.
Инструкция Erase удаляет динамические массивы из памяти, освобождая занимаемое ими место. После этого надо опять создавать динамический массив с помощью инструкции ReDim, перед тем как использовать его снова. При попытке доступа к элементам динамического массива будет сообщение об ошибке выполнения, если после использования Erase к массиву не была заново применена инструкция ReDim.
В случае со статическими массивами поведение инструкции Erase будет немного сложнее и будет зависеть от типа данных элементов массива. Ниже в таблице показано действие инструкции Erase на статические массивы разных типов (о значениях типа Empty и Nothing рассказывалось на 11-м уроке).
468
Неделя 2
Тип статического массива	Действие инструкции Erase
Любой числовой	Обнуляет элементы массива
Любой строковый	Присваивает элементам массива значения пустой строки	в строках постоянной длины всем сим- волам присваивает значение пробела
Variant	Присваивает элементам массива значение Empty
Object	Присваивает элементам массива значение Nothing
Любой тип, определяемый пользователем	Каждой переменной определенного пользователем типа присваивает значение: числовому типу — значение 0, типу String — строку нулевой длины, типу Variant — значение Empty, объектному типу — значение Nothing
Инструкцию Erase можно поместить в любом месте программы. Вот примеры ее использования:
Dim NumArray(1 То 10) As Single 'описывает статический массив Dim k as Variant 'универсальный счетчик циклов 'заполнение массива квадратами индексов For k =LBbound(NumArray) То UBoundjNumArray) NumArray(k)= k*k Next к 'вывод на экран значений квадратов For k =LBbound(NumArray) То UBoundjNumArray) MsgBox "Квадрат " & к & " равен " & NumArray(к) Next к 'повторная инициализация массива Erase NumArray For к =LBbound(NumArray) To UBoundjNumArray) MsgBox NumArrayjk) 'вывод нулей Next к
Так надо_____________________________________________________________________________________I
Помните, что Erase не удаляет из памяти статические массивы, а только повторно инициализирует их элементы.
Помните, что Erase уделяет ж памяти динамический массив, уничтожая при этом его содержимое.
Так не надо
Не забывайте, что для обработки всех элементов массива можно использовать циклическую структуру i For...Each.
День 14-й. Массивы
469
Использование массивов в качестве аргументов процедур и функций
VBA дает возможность передавать массивы процедурам и функциям в качестве аргументов. Это может быть очень полезно; из-за того что не надо указывать размер передаваемого массива, для обработки массива можно писать универсальные процедуры или функции. Например, можно написать функцию, которая в качестве аргумента получает числовой массив и в качестве результата возвращает среднее всех находящихся в нем чисел. В качестве другого примера можно написать процедуру, которая по ссылке получает массив и сортирует его (о сортировке массивов рассказывается в следующих разделах). В обоих этих примерах можно написать процедуру или функцию, работающую с массивами любого размера: 5 элементов, 10, 100 и больше.
Общий синтаксис описания массивов как аргументов процедуры или функции тот же, что для любых аргументов, описанных при изложении материала 6-го и 11-го уроков.
[ByVai I ByRef] arrayname]) As type
Подобно уже изученным аргументам процедур и функций, ключевое слово ByVai означает, что аргумент будет передан по значению, a ByRef — по ссылке. Если пропущено и то, и другое слово, то аргумент будет передан по ссылке.
Здесь arrayname]) представляет имя массива; в качестве такого аргумента можно использовать любой идентификатор, соответствующий стандартам VBA. После arrayname обязательно должны стоять пустые скобки; они означают, что аргумент является массивом. Тип type представляет любой стандартный или определенный пользователем тип данных.
Вот примеры использования массивов в качестве аргументов процедур и функций.
Sub ShowText(Lines]), NumLines As Integer)
'передается по ссылке массив Lines типа Variant
End Sub
Sub SortList]ByRef List]) As String, NumLines As Integer)
'передается по ссылке массив типа String
End Sub
Function ComputeAve(Numbers() As Double) As Double
...'передается по ссылке массив типа Double
End Function
Так надо
Помните, что при передаче аргументов по значению (с помощью ключевого олова By Vai) VBA передает процедуре или функции копию данных.
Помните, что VBA в любом из приложений Microsoft Office 2000 (или более поздней версии) дает возможность создавать функции, возвращающие массивы.
Так не надо
Не передавайте без крайней необходимости массивы по значению; при таких ‘экспериментах* с большими массивами быстро истощаются ресурсы памяти и появляются сообщения об ошибке выполнения.
470
Неделя 2
Сортировка массивов
ПРИМЕЧАНИЕ
Сортировка массивов и поиск в них являются трудными темами. Не расстраивайтесь, если при первом прочтении этот и следующий разделы покажутся непонятными. Если вы хоть что-то поймете в этих процессах и сможете ответить на вопросы в конце главы, то все хорошо. К этим разделам можно будет обратиться снова, когда действительно надо будет реализовать в программах сортировку и поиск.
Среди других манипуляций с данными из массива как бы особняком стоят две операции, которые будут выполняться наиболее часто: сортировка массивов и поиск в них. Сортировка массива просто означает процесс выстраивания значений в его элементах в порядке возрастания или убывания. В массиве, отсортированном, например, в порядке возрастания, в первом элементе содержатся данные с наименьшим значением, во втором — со следующим более высоким значением и т.д.; в последнем элементе массива находятся данные с наибольшим значением. В массиве, отсортированном в порядке убывания, в первом элементе находится наибольшее значение, а в последнем — наименьшее.
Обычно массив сортируют, если его данные необходимо обработать в определенном порядке или вывести на экран или если в нем необходимо организовать поиск определенных данных. Поиск данных в массиве будет намного быстрее и эффективнее, если массив отсортирован.
За многие годы компьютерные “светила” потратили немало усилий на разработку технологий сортировки массивов, пытаясь увеличить скорость и эффективность этого процесса. Теперь таких технологий много; у каждой из них есть свои преимущества и недостатки. К сожалению, их слишком много, чтобы все охватить в этой книге; многие авторы написали целые тома на эту тему. К счастью, имеется пара технологий сортировки, легких и для понимания и (относительно) для программирования.
В листинге 14.3 показана процедура, где для сортировки массива используется метод пузырька. Этот метод — один из самых простых. Пузырьковая сортировка работает по принципу, при котором смежные элементы массива сравниваются и, если нужно, меняется местами. Свое название она получила оттого, что элементы массива должны медленно всплывать наверх (“пузырьками”) к своему законному месту в отсортированном массиве.
Перед вами схематические команды, или псевдокод, показывающие пузырьковую сортировку в порядке возрастания.
Имеется массив с именем Nums:
1: Проводить цикл, начиная с первого элемента Nums до последнего, используя счетчик I 1А: Проводить цикл, начиная с элемента Nums, следующего после того, который выбран внешним циклом, до последнего, используя счетчик J
1Б: Если Nums(I)> Nums(J), то поменять местами содержимое Nums(I) и Nums(J)
Этот псевдокод можно затем перевести в инструкции VBA:
' Имеется массив Nums со значениями индекса от 1 до N
For I =LBbound(Nums) То (UBound(Nums)-1)
For J = (1+1) To UBound(Nums)
If Nums(I) > Nums(J) Then
tmp = Nums(I)
Nums(I) = Nums(J)
Nums(J) = tmp
День /4-й. Массивы
471
End If Next J
Next I
Чтобы провести сортировку в порядке убывания, просто поменяйте оператор сравнения (>) на оператор (<). После этого пузырьковая сортировка заработает не в порядке возрастания, а убывания.
ПРИМЕЧАНИЕ
Схематические команды, подобные тем, которые вы увидели и которые были описаны на 12-м уроке, часто называют псевдокодом из-за того, что это фразы на естественном языке, и они лишь приблизительно напоминают инструкции языка программирования, которые еще надо будет написать. Псевдокод - хорошее средство для работы со сложным процессом; тогда будет легче определить, какие именно инструкции языка программирования надо написать, чтобы решить поставленную задачу.
В листинге 14.3 приведена процедура DemoBubbleSort, где используется лист Excel, чтобы наглядно показать процесс пузырьковой сортировки. Процедура не требует со стороны пользователя никакого ввода; массив вначале заполняется случайными числами, которые генерирует функция Rnd, а потом выполняется его сортировка. Во время выполнения процедуры на экране видны указатели на элементы массива, которые в этот момент сравниваются и меняются местами. При этом имейте в виду, что лист находится на экране только для эффекта; сама сортировка происходит в массиве.
Листинг 14.3. Наглядная демонстрация пузырькоааО сартнроакн массива а процедуре DemaDabbleSort
1:	Option Explicit
2:	Option Base 1
3:
4:	'Объявление констант
5:	Const ARRAY_MAX As Integer = 10
6:	Const DELAY_TIME As Single = 1
7:	Const FORMATSTR As String =
8:	Const	COMPSTR1	As	String	=	"<-—	сравниваем этот элемент"
9:	Const	COMPSTR2	As	String	=	"<-—	с этим элементом"
10:	Const	COMPSTR3	As	String	=	"<--- меняем местами этот элемент"
11:
12:	Sub DemoBubbleSort ()
13:
14:	Dim	IntArr(ARRAY_MAX) As Integer
15:	Dim	I As Integer
16:	Dim	J As Integer
17:	Dim	oldSheet
18:
19:	'Подготовка генератора случайных чисел
20:	Randomize Timer
21:
22:	'Запись случайных чисел в элементы массива IntArr
23:	For I = 1 То ARRAY_MAX
24:	IntArr(I) = IntjRnd * 1000)
25:	Next I
26:
472
Неделя 2
27:	'Запоминаем текущий лист, переходим на новый
28:	oldSheet = ActiveSheet.Name
29:	Sheets("Лист!").Select
30:
31:	'Выводим элементы массива
32:	DisplayArray ArrayVal:=IntArr
33:
34:	'Начинаем сортировку
35:	For I = 1 To ARRAY_MAX - 1
36:	For J = I + 1 To ARRAY_MAX
37:	'Выводим стрелочки, указывающие	на
38:	'сравниваемые элементы
39:	ShowCompare COMPSTR1, I,	COMPSTR2, J
40:
41:	If IntArr(I) > IntArr(J)	Then
42:	Beep 'Звуковой сигнал,
43:	'указывающий на	то, что сравниваемые элементы
44:	'будут меняться	местами
45:	ShowCompare COMPSTR3, I, COMPSTR2,	J
46:	'Меняем местами	элементы I	н	J
47:	Swap IntArr(I),	IntArr(J)
48:	'Выводим переставленные	элементы
49:	'в новые позиции
50:	DisplaySwapped Rowl:=I,	Row2:=J,	ArrayVal:=IntArr
51:	End If
52:	Next	J
53:	Next I
54:	MsgBox	prompt:“"Массив отсортирован!",
55:	Buttons:=vblnformation,
56:	ТШе:="Результаты сортировки"
57:	'Возвращаемся на исходный лист
58:	Sheets (oldSheet) .Select
59:	End Sub
60:
61:	Sub Swap(Il As Integer, 12 As Integer)
62:	Dim temp As Integer
63:	temp	= Il
64:	Il =	12
65:	12 =	temp
66:	End Sub
67:
68:	Sub WaitAWhile(ByVal Interval As Single)
69:	Dim TheTime As Date
70:	TheTime = Timer
71:	Do
72:	Loop Until (Timer - TheTime) >= Interval
73:	End Sub
74:
75:	Sub ShowCompare(SI As String, Il As Integer,
76:	S2 As String, 12 As Integer)
77:	Cellsfll, 2).Value = SI
78:	Cells(12, 2(.Value = S2
79:	WaitAWhile DELAY_TIME
День 14-й. Массивы
473
80:	Cells (II, 2 (.Value =
81:	Cells(I2, 2).Value = ""
82:	End Sub
83:
84:	Sub DisplayArray(ArrayVal() As Integer)
85:	Dim к As Integer
86:
87:	For к = 1 To UBoundj Array Vai)
88:	Cells(k, 1).Value = Format(ArrayVal(k), FORMATSTR)
89:	Next к
90:	End Sub
91:
92:	Sub DisplaySwapped(Rowl As Integer,
93:	Row2 As Integer,
94:	ArrayValj) As Integer)
95:
96:	CellsfRowl, 1).Value = ArrayVai(Rowl)
97:	Cells(Row2, 1).Value = ArrayVal(Row2)
98:	End Sub
На рис. 14.7 показан пример работы процедуры DemoBubbleSort с первым и десятым элементами массива (находящимися на листе в ячейках А1 й А10).
£3 Microsoft Excel - Day14.xls
Правка Виа Вставка Формат Сервис Данные Дкно Стрелка	- |g| х|
A1 л ™ "•	' ~~ " zz~;
А В C D Е F G Н IT
1	|	102 <-— сравниваем этот элемент
2	192.
3	'	303;
4	182
5	910
6	808
7	334	Г
8	534
9	|	290 <— с этим элементом	:
10	889
11
12
43;
14
15
16	—
17
18
МО Ц\ли<г1/Лист2/Т<т3/	|« |	I >|Г
ГОТОВО	NUM
Рис. 14.7. Пример работы процедуры DemoBubbleSort с листинга 14.3
474
Неделя 2
Обратите внимание, что в листинге 14.3 находится целый модуль, состоящий из шести различных процедур. Кроме того, в строке 2 имеется инструкция Option Base 1, так что VBA будет для всех массивов модуля начинать нумерацию индексов с 1.
В строках 5-10 находится несколько констант уровня модуля, которые применяются для задания максимального размера массива, определения продолжительности задержки и хранения нескольких строк, используемых при формировании сообщений, выводимых процедурой.
Сама процедура DemoBubbleSort занимает строки 12-59. Ее текст начинается с описания нескольких переменных (строки 14-17). В строке 14 описан статический массив, который будет отсортирован, затем идут описания переменных счетчиков цикла I и J, а также строковой переменной oldSheet, где будет находиться имя текущего листа.
В строке 20 для инициализации генератора случайных чисел используется инструкция Randomize, а в строках 23-25 находится цикл For, в котором массив заполняется этими числами с помощью функции Rnd. (Эта функция упоминалась на 5-м уроке. Результатом ее выполнения является случайное число; однако перед использованием Rnd с помощью инструкции Randomize необходимо инициализировать генератор случайных чисел.)
В строках 28 и 29 сохраняется имя текущего листа (для последующего восстановления) и выбирается имя листа, на котором будет показан процесс сортировки. В строке 32 вызывается процедура DisplayArray, чтобы вывести на выбранный лист содержимое массива до сортировки.
Реальная работа процедуры DemoBubbleSort начинается со строки 35. В результате работы вложенных циклов For (внешний начинается в строке 35, внутренний — в строке 36) каждый элемент массива сравнивается со всеми другими элементами.
В строке 39 вызывается процедура ShowCompare, которая выводит на лист маркеры, показывающие, какие элементы массива в данный момент сравниваются. В строке 41 проводится само сравнение этих элементов; если элемент, указанный счетчиком внешнего цикла, больше того, который указан внутренним циклом, то оба их значения надо переставить.
ПРИМЕЧАНИЕ
Чтобы в пузырьковой сортировке изменить порядок с возрастания на убывание, в строке 41 листинга 14.3 замените имеющийся оператор сравнения на оператор (<); теперь массив будет сортироваться в порядке убывания.
Если значения этих элементов массива не упорядочены, то инструкция Веер (строка 42) дает звуковой сигнал о том, что происходит перестановка, и снова происходит вызов процедуры ShowCompare (на этот раз показать, какие элементы переставляются). В результате выполнения инструкции Веер Windows будет играть заданную по умолчанию системную мелодию (если в компьютере есть звуковая плата) или будет сгенерирован гудок из динамика компьютера (если звуковой платы нет). В строке 47 вызывается процедура Swap, которая как раз и переставляет элементы местами. В строке 50 вызывается процедура DisplaySwapped, обновляющая на экране значения элементов массива. Структура внутреннего цикла For заканчивается в строке 52, а внешнего — в строке 53.
В строках 54-56 просто выводится сообщение о том, что операция сортировки завершена; в строке 58 восстанавливается лист, который был текущим в момент начала выполнения процедуры DemoBubbleSort.
В строках 61-66 находится процедура Swap. Обратите внимание, что ее аргументы передаются по ссылке (в VBA это способ передачи по умолчанию), поэтому все изменения этих аргументов, выполняемые в Swap, отображаются на их первоначальных пе
День 14-й. Массивы
475
ременных. В Swap лишь происходит обмен значениями аргументов; переменная Тетр используется для временного хранения одного из этих значений.
В строках 68—73 находится процедура WaitAWhile. Процедура ждет в течении заданного промежутка времени, записав показание системных часов в момент начала своей работы, а затем периодически повторяет эту операцию до тех пор, пока разница между текущим и первоначальным показаниями не превысит заданный промежуток. (Системные часы компьютера записывают количество секунд, прошедших с полуночи; в VBA это значение возвращает функция Timer.)
В строках 75-82 находится процедура ShowCompare. Она просто выводит свои строковые аргументы в виде сообщений в указанных строках листа, вызывает процедуру WaitAWhile, а затем удаляет с экрана свои сообщения.
В строках 84-90 находится процедура DisplayArray. Как и аналогичная процедура с листинга 14.2, она просто выводит содержимое массива на активный лист.
В строках 92-98 находится процедура DisplaySwapped. Она обновляет положение всех элементов, которые были переставлены во время сортировки.
Процедуру DemoBubbleSort необходимо ввести, а затем несколько раз запустить, чтобы почувствовать, как работает пузырьковая сортировка. Обратите внимание, что каждый элемент массива последовательно сравнивается с находящимися выше элементами, постепенно переводя более высокие значения в конец массива. Если экранные сообщения мелькают слишком быстро, то время отображения их на экране можно увеличить, соответственно изменив значение константы DELAY_TIME.
Теперь, когда вы хорошо узнали, как работает пузырьковая сортировка, посмотрите на листинг 14.4. Там делается в основном то же самое, что и на листинге 14.3, но была удалена вся “косметика”, а инструкции самой этой сортировки были перенесены в две общие процедуры с аргументами в виде массивов. Одна из них выполняет пузырьковую сортировку в порядке возрастания, другая — в порядке убывания. Процедура Sorter, находящаяся в листинге 14.4, является практической реализацией этой технологии и выполняет сортировку с максимальной скоростью.
«истина 14.4. Процедура Darter capmupgem массив с иамащьн пдзырькавога метада и двух оДщих процеддр сартиравки
1:	Option Explicit
2:	Option Base 1
3:
4s Private Const FORMATSTR As String = "ffH"
5:	Private Const ARRAY_MAX As Integer = 10
6:
7:	Sub Sorter))
8:	'Объявление констант
9:	Const iTitle As String = "Сортировка массива"
10:
11:	'Объявление переменных
12:	Dim IntArr(ARRAY_MAX) As	Integer
13:	Dim Count As Integer
14:	Dim oldSheet As String
15:
16:	'Подготовка генератора	случайных чисел
17:	Randomize Timer
18:
19:	'Запись случайных чисел в элементы массива IntArr
20:	For Count = 1 То ARRAY_MAX
21:	IntArr(Count) = IntfRnd * 1000)
22:	Next Count
476
Неделя 2
23:
24:	'Запоминаем текущий лист, переходим на новый
25:	oldSheet = ActiveSheet. Name
26:	Sheets("Лист1").Select
27:
28:	'Выводим элементы массива
29:	DisplayArray IntArr
30:
31:	MsgBox prompt:="Начинаем сортировку.",
32:	Buttons:=vblnformation,
33:	Title:=iTitle
34:
35:	'Сортировка массива по возрастанию
36:	BubbleSortAscending IntArr
37:
38:	'Вывод отсортированного массива
39:	DisplayArray IntArrf)
40:
41:	MsgBox prompt:="MaccHB отсортирован по возрастанию",
42:	Buttons:=vblnformation,
43:	Title:=iTitle
44:
45:	'Сортировка массива по убыванию
46:	BubbleSortDescending IntArr
47:
48:	'Вывод отсортированного массива
49:	DisplayArray IntArr
50:
51:	MsgBox prompt:="Массив отсортирован по убыванию",
52:	Buttons:=vblnformation,
53:	Title:=iTitle
54:
55:	'Возвращаемся на исходный лист
56:	Sheets(oldSheet).Select
57:	End Sub
58:
59:	Sub BubbleSortAscending(AnyArray() As Integer)
60:	Dim I As Integer, J As Integer
61:
62:	'Сортирует целый массив по возрастанию
63:	For I = LBound(AnyArray) To UBound(AnyArray) - 1
64:	For J = I + 1 To UBound(AnyArray)
65:	If AnyArray(I) > AnyArray(J) Then
66:	Swap AnyArray(I), AnyArray(J)
67:	End If
68:	Next J
69:	Next I
70:	End Sub
71:
72:	Sub BubbleSortDescending(AnyArray() As Integer)
73:	Dim I As Integer, J As Integer
74:
75:	'Сортирует целый массив по убыванию
76:	For I = LBoundjAnyArray) To UBound(AnyArray) - 1
День 14-й. Массивы
477
77:	For J = I + 1 To UBound(AnyArray)
78:	If AnyArray(I) < AnyArray(J) Then
79:	Swap AnyArray(I), AnyArray(J)
80:	End If
81:	Next J
82:	Next I
83:	End Sub
84:
85:	Sub Swapjll As Integer, 12 As Integer)
86:	Dim temp As Integer
87:	temp	= Il
88:	Il =	12
89:	12 =	temp
90:	End Sub
91:
92:	Sub DisplayArray(AnyArray() As Integer)
93:	Dim к As Integer
94:
95:	For к = LBound(AnyArray) To UBoundfAnyArray)
96:	Cellsjk, 1).Value = Format(AnyArrayjk), FORMATSTR)
97:	Next к
98:	End Sub
Процедура Sorter из листинга 14.4 является усовершенствованной версией процедуры, приведенной в листинге 14.3. В этой новой версии для сортировки массива в порядке возрастания или убывания используются процедуры BubbleSortAscending и BubbleSortDescending соответственно. У каждой из них один аргумент, AnyArray, который является массивом и передается по ссылке. Следовательно, когда они сортируют переданный им аргумент, то сортируют первоначальный массив.
ПРИМЕЧАНИЕ
Предполагается, что обе процедуры, BubbleSortAscending и BubbleSortDescending, обрабатывают массив, полностью заполненный нужными данными. Если следует отсортировать только часть массива, то процедуры сортировки недр модифицировать, чтобы у них был второй аргумент, указывающий максимальное число сортируемых элементов, а также чтобы их циклы использовали этот аргумент в качестве максимального значения счетчика
Обратите внимание, что в процедуре Sorter для вывода отсортированного массива на лист используется процедура DisplayArray (у которой также есть аргумент в виде массива).
Поиск в массивах
Часто в массиве необходимо искать определенное значение. Для поиска в массивах применяется два основных метода: последовательного и бинарного поиска.
При последовательном поиске проверка начинается с первого элемента массива, затем по очереди проверяются остальные, пока не будет найдено нужное значение. Есть несколько вариантов этого метода. Его надо применять в несортированном массиве для того, чтобы узнать, в каких элементах находится определенное значение.
Для сортированных массивов более быстрым и эффективным методом является бинарный поиск. В этом случае массив последовательно делится пополам (отсюда название
478
Неделя 2
“бинарный”), и поиск ведется только в той половине, где вероятнее всего находится нужное значение. Бинарный поиск работает только на сортированных массивах.
Более подробно об этих методах рассказывается в следующих двух разделах.
Последовательный поиск
Основной метод последовательного поиска очевиден. Допустим, имеется массив с 10 несортированными целыми числами и с индексами от 1 до 10. Поиск просто начинается с проверки элемента 1, нет ли в нем искомого значения. Если оно есть, то поиск прекращается; если нет, то так же проверяется второй элемент. Поиск продолжается до тех пор, пока нужное значение не будет найдено или не будут просмотрены все элементы. Для последовательного поиска может быть написан такой псевдокод.
1:	Присвойте признаку Не_найден значение Истина.
2:	Присвойте переменной I начальное значение индекса массива.
3:	Выполняйте следующий шаг, пока верны два условия:
I меньше или равно верхней границе массива
значением признака Не_найден остается Истина
ЗА: Если элемент с индексом I не равен искомому значению, то:
увеличьте на 1 значение I
если равен, то присвойте флажку Не_найден значение Ложь
Флажок булевого типа Не_найден показывает, был ли поиск успешным. При успешном поиске нужное значение находится в элементе с индексом, равным значению I.
В листинг 14.5 включена процедура LinearSearch, которая наглядно демонстрирует процесс последовательного поиска. В ней создается массив, заполняемый случайными числами, а затем в массиве проводится поиск числа, указанного пользователем.
Дастине 14.5. Процедура LlacarScarcb иагляуио двманстрирувт наслвдоаательнын ааиск в массиве
1:	Option Explicit
2:	Option Base 1
3:
4:	Private Const SHORT_DELAY = 1
5:
6:	Sub LinearSearch))
7:	'Объявление констант
8:	Const ARRAY_MAX As Integer = 10
9:	Const iTitle = "Последовательный поиск"
10:
11:	'Объявление переменных
12:	Dim IntArr(ARRAY_MAX) As Integer
13:	Dim	I As Integer
14:	Dim	NotFound As Boolean
15:	Dim	oldSheet As String
16:	Dim	SrchFor As Variant
17:	Dim	Rsp As Integer
18:	Dim	pStr As String
19:
20:	'Запоминаем текущий лист, переходим на новый
21:	oldSheet = ActiveSheet.Name
22:	Sheets)"Лист!”(.Select
День 14-й. Массивы
479
23:
24:	Randomize Timer 'Подготовка генератора случайных чисел
25:
26:	For I = 1 То ARRAY_MAX 'Запись случайных чисел в элементы массива
27:	IntArr(I) = IntfRnd * 1000)
28:	Next I
29:
30:	DisplayArray IntArr 'Выводим элементы массива
31:
32:	'Спрашиваем пользователя, какое число искать,
33:	'пока он не откажется
34:	Do
35:	SrchFor = Application.InputBox(
36:	prompt:="Введнте искомое число:",
37:	Title:=iTitle & " — Ввод",
38:	Туре:=1)
39:	SrchFor = CIntjSrchFor) 'преобразуем число в целое
40:
41:	'Начало поиска
42:	1=1
43:	NotFound = True
44:	Do
45:	'Выводим "?=?" возле проверяемого	элемента
46:	ShowSearch Row:=I, Num:=SrchFor
47:
48:	'не совпало?
49:	If IntArr(I) о SrchFor Then 'увеличиваем индекс
50:	1=1+1
51:	Else	'Устанавливаем признак неудачного поиска в FALSE
52:	NotFound = False
53:	End If
54:	Loop Until I > ARRAY_MAX Or NotFound = False
55:
56:	If NotFound Then 'сообщение о неудаче
57:	pStr = "He найдено число " & SrchFor
58:	Else 'сообщение об успехе
59:	pStr = "Число " & SrchFor & " найдено под номером " & I
60:	End If
61:
62:	pStr = pStr & Chr(13) & Chr(13) &
63:	"Искать другое число?"
64:	Rsp = MsgBox(prompt:=pStr,
65:	Title:=iTitle, Buttons:=vbYesNo)
66:	Loop Until Rsp = vbNo
67:
68:	Sheets(oldSheet).Select 'возврат на исходный лист
69:	End Sub
70:
71:	Sub WaitAWhilejInterval As Single)
72:	Dim TheTime As Date
73:	TheTime = Timer
74:	Do
75:	Loop Until (Timer - TheTime) >= Interval
480	Неделя 2
76:	End Sub
77:
78:	Sub DisplayArray(AnyArray() As Integer)
79:	Dim к As Integer
80:
81:	For к = LBound(AnyArray) To UBound(AnyArray)
82:	Cells(k, 1).Value = AnyArray(k)
83:	Next к
84:	End Sub
85:
86:	Sub ShowSearch(Row As Integer, Num As Variant)
87:	'отмечаем проверяемую строку
88:
89:	Cells(Row, 2).Value = ”?=? " & Num
90:	WaitAWhile SHORT DELAY
91:	Cells(Row, 2).Value = ""
92:	End Sub
На рис. 14.8 показан результат работы процедуры LinearSearch после успешного поиска значения 219.
Е
D
1
3
5 б
7 .
3
9
10
1V
47,
104
193
463’
734
617: 62'
879: 660'
562
Последовательный поиск
Числа 62 найдено под номером 7
Искать другое число’
Нет
13
14
15
16
17’
18
К	..	.............. _
Готово	i MUM1
Рис. 14.8. Результат успешного поиска с помощью процедуры LinearSearch
>1 \Ли<т1 /Листг/ЛлстЗ /
День 14-Й. Массивы
481
В строке 4 описана константа уровня модуля, которая указывает время задержки. Процедура LinearSearch начинается в строке 6. В строках 8 и 9 описаны две константы, которые она использует. Обратите внимание на константу ARRAY_MAX в строке 8. В строках 12-18 описаны переменные, используемые этой процедурой. Обратите внимание на описание IntArr в строке 12.
В строках 21-30 идет подготовка к выполнению основной задачи процедуры: сохраняется имя текущего листа, выбирается другой лист, массив заполняется случайными числами, и его содержимое выводится на выбранный лист (для этого вызывается процедура DisplayArray, которая находится в строках 78-84).
В строке 34 начинается цикл Do...Until; он выполняется до тех пор, пока пользователь не укажет, что прекращает поиск чисел в массиве. В строке 35 для получения числа от пользователя используется функция InputBox приложения Excel (она служит для того, чтобы пользователь ввел именно число — в строке 39 введенное значение преобразуется в число типа Integer.)
По-настоящему поиск начинается в строках 42 и 43, где задаются начальные условия цикла Do...Until, который, в свою очередь, начинается в строке 44. Этот цикл выполняется до тех пор, пока или число не будет найдено или счетчик цикла не превысит максимальный размер массива.
В строке 46 вызывается процедура Showsearch (строки 86-92), которая выводит на экран символ, показывающий, какой сейчас проверяется элемент массива.
В строках 49-53 выполняется сама проверка: с помощью инструкции If.. .Then.. .Else выясняется, нет ли в текущем элементе нужного значения. Если нет, то переменная индекса увеличивается на 1 и цикл повторяется; в противном случае флажку NotFound (Не найден} присваивается значение False (Ложь).
В строке 54 заканчивается структура цикла, при этом проверяется, имеет ли флажок NotFound значение False или превышен ли переменной индекса (I) максимальный размер массива. Если одно из условий истинно, то цикл завершается.
Инструкция If.. .Then.. .Else в строках 56-60 проверяет, найдено ли введенное пользователем значение, и начинает формировать соответствующее сообщение. И наконец, в строках 62-65 сообщение формируется полностью и выводится на экран с помощью окна MsgBox, которое к тому же спрашивает у пользователя, искать ли другое число. Если будет выбран ответ Yes (Да), то цикл, начинающийся в строке 34, выполняется снова. В противном случае в строке 68 восстанавливается первоначальный лист и процедура LinearSearch завершается.
В строках 71-76 находится процедура WaitAWhile, с которой вы уже знакомы по листингу 14.3. В строках 78-84 находится другая знакомая процедура DisplayArray, а с троках 86-92 — процедура ShowSearch.
Euuapubii поиск
Для сортированных массивов метод бинарного поиска считается большинством программистов лучшим из всех универсальных методов. Вместо того чтобы выполнять в массиве последовательный поиск, этот метод пользуется тем, что массив отсортирован. Когда это так, то естественно предположить, что если элемент в середине массива больше, чем нужное значение, то это значение можно будет найти в первой половине массива.
482
Неделя 2
В то же время, если элемент в середине массива больше нужного значения, то это значение можно будет найти во второй половине массива. После того как установлено, в какой половине массива оно находится, поиск продолжается в этой половине, которая при этом делится надвое. Процесс продолжается в образовавшихся половинах, пока поиск не увенчается успехом или не выйдет за пределы тех участков, в которых надо было его вести.
Чтобы лучше понять бинарный поиск, предположим, что вы ищете фамилию Харрис в 1000-страничной телефонной книге. Вооружившись методом бинарного поиска, вы открываете книгу на странице 500 (половина от 1000). Там в самом верху находится фамилия Мейнард. Поскольку, в соответствии с английским алфавитом, фамилия Харрис предшествует фамилии Мейнард, теперь вы знаете, что искать ее надо в первой половине книги, т.е. на первых 500 страницах. Вы открываете книгу на странице 250 (половина от 500) и находите наверху фамилию Фогарти. Харрис следует после Фогарти, т.е. находится во второй половине первых 500 страниц телефонной книги. Затем берется средняя точка на полпути от 250 до 500 страницы. Это страница 375. Процесс, при котором число интересующих вас страниц неуклонно тает, можно продолжать до тех пор, пока фамилия Харрис наконец-то не будет найдена или не станет ясно, что страницы с такой фамилией в книге нет.
Вот псевдокод бинарного поиска.
1:	Присвойте нижней границе интервала (называется Low) значение 1.
2:	Присвойте верхней границе интервала (называется High) значение верхней границы массива.
3:	Выполняйте следующие шаги, пока верны два условия:
элемент с индексом, лежащим посередине между Low и High, равен нужному значению
Low стала больше High
ЗА: Присвойте индексу средней точки (называется Median) значение посередине между Low и High.
ЗБ: Если нужное значение меньше того, что находится в Median, то присвойте High значение Median-1 в противном случае присвойте Low значение Median+1
Когда бинарный поиск будет завершен, то его успешность можно будет определить, сравнивая нужное значение со значением элемента с индексом Median. Если они равны, то поиск был успешный; если нет, то закончился неудачей. На рис. 14.9 показаны шаги бинарного поиска.
Процедура Binarysearch в листинге 14.6 наглядно демонстрирует работу бинарного поиска. В ней создается массив типа Integer, который заполняется случайными числами, затем сортируется, а потом его содержимое выводится на лист. Затем из процедуры поступает запрос ввести какое-нибудь число, которое надо будет найти в массиве.
День 14-й. Массивы
483
Шаг 1
Рис. 14.9. Пример бинарного поиска элементов со значением 389
ШагЗ
Листинг 14.6. Процедура Binarysearch паглядно демонстрирует бииариын поиск а массиве
1:	Option Explicit
2:	Option Base 1
3:
4:	Private Const ARRAY_MAX As Integer = 10
5:	Private Const SHORT_DELAY = 1
6:
7:	Sub BinarySearchj)
8:
9:	Const iTitle = "Бинарный	поиск"
10:
11:	'Объявление переменных
12:	Dim IntArr(ARRAY_MAX) As	Integer
13:	Dim I As Integer
484
Неделя 2
14:	Dim Hi As integer
15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66:	Dim Lo As Integer Dim Median As Integer Dim oldSheet As String Dim SrchFor As Variant Dim Rsp As Integer Dim pStr As String 'Запоминаем текущий лист, переходим на новый oldSheet = ActiveSheet.Name Sheets("Лнст1“).Select Randomize Timer 'Подготовка генератора случайных чисел For I = 1 То ARRAY_MAX 'Запись случайных чисел в элементы массива IntArr(l) = Int(Rnd * 1000) Next I BubbleSort IntArr 'Сортировка массива IntArr1 DisplayArray IntArr 'Выводим элементы массива 'Спрашиваем пользователя, какое число искать, 'пока он не откажется Do SrchFor = Application.InputBox( prompt:="Введите искомое число:", Title:=iTitle & " — Ввод", Type:=l) SrchFor = CInt(SrchFor) 'преобразуем число в целое 'Начало поиска Lo = LBound(IntArr) Hi = UBound(IntArr) Do Median = (Lo + Hi) \ 2 'целочисленное деление 'Выводим "?=?" возле проверяемого элемента ShowSearch Row:=Median, Num:=SrchFor If SrchFor < IntArr(Median) Then Hi = Median - 1 Else Lo » Median + 1 End If Loop Until (SrchFor = IntArr(Median)) Or (Lo > Hi) 'Совпало! If SrchFor = IntArr(Median) Then 'сообщение об успехе pStr » "Число " & SrchFor & " найдено под номером " & Median Else 'сообщение о неудаче pStr = "Не найдено число " & SrchFor End If
День	14-й. Массивы	485
67:
68:	pStr = pStr & Chr(13) & Chr(13) & _
69:	"Искать другое число?"
70:	Rsp = MsgBox(prompt:=pStr,
71:	Title:=iTitle, Buttons:=vbYesNo)
72:	Loop Until Rsp = vbNo
73:
74:	Sheets(oldSheet).Select 'возврат на исходный лист
75:	End Sub
76:
77:	Sub WaitAWhile(Interval As Single)
78:	Dim TheTime As Double
79:	TheTime = Timer
80:	Do
81:	Loop Until (Timer - TheTime) >= Interval
82:	End Sub
83:
84:	Sub BubbleSort(AnyArray() As Integer)
85:	'Сортирует массив по возрастанию
86:	Dim I As Integer, J As integer
87:	For I = LBound(AnyArray) To UBound(AnyArray) - 1
88:	For J = I + 1 To UBound(AnyArray)
89:	If AnyArray(I) > AnyArray(J) Then
90:	Swap AnyArray(I), AnyArray(J)
91:	End If
92:	Next J
93:	Next I
94:	End Sub
95:
96:	Sub Swap(Il As Integer, 12 As Integer)
97:	Dim temp As Integer
98:	temp	= Il
99:	Il =	12
100:	12 =	temp
101:	End Sub
102:
103:	Sub DisplayArray(AnyArray() As Integer)
104:	Dim k As Integer
105:
106:	For k = LBound(AnyArray) To UBound(AnyArray)
107:	Cells(k, 1).Value = AnyArray(k)
108:	Next k
109:	End Sub
110:
111:	Sub ShowSearch(Row As Integer, Num As Variant)
112:	'Отмечает проверяемую строку
113:
114:	Cells(Row, 2).Value = "?=? " & Num
115:	WaitAWhile SHORT DELAY
116:	Cells(Row, 2).Value = ""
117:	End Sub
486
Неделя 2
На рис. 14.10 показан результат работы процедуры Binarysearch после успешного поиска значения 583.

О	№ La > - >>L> "
2
3
5 6
7 8
9 10 11
12 13 14
15 16 17
18
153 204;
236 403 680;
707 826;
83 Г 833 948;
Бинарный поиск
Искать другое число?
Н \ Лис т 1/Лст2/ЛстЗ /
NUM.
иг
Рис. 14.10. Процедура BinarySearch после поиска значения 583
В строках 4 и 5 описаны Private-константы (конечно, уровня модуля), а в строках 9-20 — различные константы и переменные, используемые в процедуре BinarySearch. В строках 23-30, как и в предыдущих примерах, выполняется работа по поддержанию порядка.
После того как массив заполнен случайными числами, в строке 32 для его сортировки в порядке возрастания вызывается процедура Bubblesort (строки 84-94 листинга 14.6). (Та же знакомая вам пузырьковая сортировка, но лишь в порядке возрастания.) После того как сортировка проведена, в строке 34 для вывода на лист ее результатов вызывается процедура DisplayArray.
В строке 38 начинается цикл Do...Until, который, как и перед этим, выполняется до тех пор, пока пользователь не откажется от поиска числа в массиве. В строках 39-43 для получения от пользователя очередного числа и преобразования его в формат Integer используется функция InputBox приложения Excel.
По-настоящему процесс бинарного поиска начинается в строке 46. В строках 46 и 47 задаются начальные условия цикла Do...Until, который начинается в строке 48. Переменная Lo — это нижняя в пределах массива граница сегмента, в котором сейчас ведется поиск, а переменная Hi — верхняя граница этого сегмента. При первом проходе цикла их значения совпадают с физическими границами самого массива. Цикл Do, начинающийся в строке 48, выполняется до тех пор, пока не будет найден элемент с введенным числом или пока нижняя граница сегмента, где ведется поиск, не будет находиться выше его верхней границы.
День 14-Й. Массивы
487
В строке 49 переменной Median присваивается индекс элемента, который находится посередине между верхней и нижней границей сегмента, где сейчас ведется поиск. Обратите внимание, что в этой инструкции используется оператор целого деления (\).
В строке 51 вызывается процедура ShowSearch (строки 111-117), которая выводит на экран маркер, показывающий, какой сейчас проверяется элемент массива. В строках 53-57 выполняется сама проверка. Если нужное значение меньше того, что находится в элементе с индексом Median, то верхней границе (Hi) сегмента, где поиск будет продолжен, присваивается значение на единицу меньше, чем индекс Median; в противном случае нижней границе сегмента присваивается значение на единицу больше этого индекса.
В строках 61-66 проверяется, найдено ли введенное пользователем значение, и начинается формирование соответствующего сообщения. Обратите внимание, что определение того, найден ли элемент с таким значением, просто сводится к проверке, есть ли нужное значение (SrchFor) в элементе с индексом Median.
В строках 68-71 сообщение формируется полностью, а с помощью окна MsgBox пользователю выдается запрос, следует ли искать в массиве другое число.
В оставшейся части модуля содержится несколько процедур, знакомых вам по предыдущим листингам этого урока. В строках 77-82 находится процедура WaitAWhile; строках 84-94 — процедура Bubblesort (выполняет сортировку только в порядке возрастания), а в строках 96-101 — используемая ею процедура Swap. В строках 103-109 находится процедура DisplayArray, и в строках 111-117 — процедура ShowSearch.
Так надо
Если вы не уверены, отсортирован массив или нет, то для поиска в нем всегда применяйте последовательный
Так не надо
1 Не используйте бинарный поиск в несортированном массиве; там он не сработает, и его результаты будут, , ошибочными.
:• Не используйте последовательный поиск, когда известно, что массив отсортирован. Хотя результат при этом и будет верный, бинарные методы работают намного быстрее и являются более эффективными.
Резюме
В этой главе вы познакомились с массивами — простейшей и, возможно, наиболее полезной структурой многих языков программирования. Массив — это набор переменных, имеющих общее имя. Доступ к отдельным элементам массива осуществляется с помощью целого значения индекса.
В этой главе было показано, как использовать директиву Option Base, чтобы указать VBA, нумеровать ли элементы массива с 0 или 1. Вы узнали, как описывать статические и динамические переменные с помощью инструкции Dim, а также как менять размер и размерности динамических массивов с помощью инструкции ReDim. Мы показали, как использовать для повторной инициализации статических или стирания из памяти динамических массивов инструкцию Erase, а также, как применять функции LBound и UBound для определения верхней и нижней границ массива.
488
Неделя 2
Наконец, вы научились сортировать массив с помощью классического пузырькового метода, а затем — методам надежного поиска в сортированных и несортированных массивах.
Вопросы о ответы
Почему в VBA для массивов используется нижняя граница индексов, равная нулю? Такая система выглядит сложной и громоздкой.
VBA и большинство других языков программирования для индексирования используют нулевую нижнюю границу из-за способа, применяемого для хранения массивов в памяти. Когда в VBA создается массив, для него просто резервируется единый непрерывный блок памяти, достаточно большой, чтобы вместить все элементы массива. При создании индекса VBA вычисляет, насколько далеко от начала непрерывного блока памяти будет храниться данный элемент — значение индекса переводится в значение сдвига относительно начального адреса. Поэтому первый элемент массива имеет сдвиг 0, второй — сдвиг 1, третий — сдвиг 2 и т.д.
Какие надо использовать инструкцию Option Base?
Удобнее всего использовать все инструкции Option Base. Для большинства людей удобнее всего использовать массивы, чьи индексы начинаются с 1, так что в модулях, вероятно, должно быть место для Option Base 1. Если вам удобнее работать с нулевой нижней границей, то используйте Option Base 0 (или просто пропускайте директиву Option Base). Если даже вы используете нулевую нижнюю границу по умолчанию, то можете в свои модули ввести директиву Option Base 0, чтобы точно указать применяемую схема нумерации.
Будут ли побочные эффекты, если не использовать Option Base 1?
Только если вы предполагаете, что индексы в массивах начинаются с 1. Если Option Base 1 не используется, но в программах в качестве начального индекса указана единица (1), вам никогда не попадется элемент с индексом 0. Далее, если однажды в качестве начального индекса вы использовали 1, а потом для получения минимального значения индекса применили функцию LBound, не исключены трудноуловимые программные ошибки.
Можно ли изменить типы данных в элементах массива с помощью инструкции ReDim?
Изменить тип данных массива можно только в том случае, если массив создан в переменной типа Variant прежде всего с помощью инструкции ReDim; в противном случае тип данных элементов массива изменить нельзя. Вот фрагмент, показывающий, как создавать массив в Variant-переменной с помощью ReDim.
Dim aList As Variant
ReDim aList(10) As Integer ' массив целого типа внутри Variant
ReDim aList(5) ' все еще целого типа
ReDim aList(10) As String ' теперь в Variant массив типа String
Можно ли изменить тип данных динамического массива с помощью инструкции ReDim, после того как он был стерт инструкцией Erase?
Нельзя. При определении динамического массива VBA присваивает его элементам определенный тип данных (по умолчанию Variant, если для определения типа данных не использованы выражения с ключевым словом As). Хотя с помощью Erase забирается вся память, используемая под динамический массив, сам массив не разрушается. VBA сохраняет информацию об имени и типе данных массива во внутренней таблице переменных и соответствующих им адресов памяти, хотя память под массив не выде
День 14-й. Массивы
489
ляется. Следовательно, когда бы к массиву не применялась инструкция ReDim, VBA знает о его типе данных и не дает этот тип изменить. Это очень помогает в программировании, предупреждая использование одного и того же массива под хранение разных типов данных.
Коллоквиум
Ответы в приложении.
Тест
1.	Сколько элементов массивов создается в этих описаниях?
Option Base 1
Dim Lookup(10,3,5) As Integer
Dim Cube(3,3,3) As Double
2.	Посмотрите на следующие описания массивов.
Option Base 1
Dim A(1980 To 1990,10,5 To 9)
Какими в этом случае будут результаты выполнения следующих функций.
(A) LBOund(A,l)
(Б) LBound(A,2)
(В) LBound(A,3)
(Г) UBound(A,l)
(Д) UBound(A,2)
(Е) UBound(A,3)
3.	НАЙДИТЕ ОШИБКУ. Исправьте следующий фрагмент программы.
Option Base 1
Dim Х(10, 20) As Double
For 1=1 To 10
For J=1 To 20
X(I,J) = IntfRnd * 1000)
Next J
Next I
ReDim X(5,10) As Single
For 1=1 To 5
For J=1 To 10
X(I,J) = Int(Rnd * 1000) Next J
Next I
4.	Исправит ли положение в предшествующей программе вставка инструкции Erase X перед инструкцией ReDim (строка 10)?
5.	Можно ли использовать элемент массива в инструкции VBA там, где можно использовать простую переменную?
490
Неделя 2
6.	Будет ли последовательный поиск правильным на сортированном массиве? А на несортированном?
7.	Почему на сортированном массиве надо использовать бинарный поиск? Можно ли этот поиск использовать на несортированном массиве?
Упражнения
1.	Преобразуйте процедуру Sorter из листинга 14.4 в другую, называемую Sorter2. В новой процедуре надо описать двумерный массив, заполнить его случайными числами, а затем отсортировать. В этом массиве должно быть пять столбцов и 15 строк. Сортировка должна происходить на основе значений из второго столбца. Имейте в виду, что при сортировке надо менять местами целые строки, а не только элементы второго столбца.
2.	Какие значения хранятся в массиве А, если имеется такой программный текст?
Dim А(1 То 9) As Long
Dim I As Integer
A(LBound(A)) = 2
For I=LBound(A)+l To Ubound(A)
A( I)=2*A(1-1)-l
Next I
3.	НАЙДИТЕ ОШИБКУ. Исправьте следующий фрагмент программы.
Dim Projlncome (1995 То 1999) As Double
Dim I As Integer
Projlncome (1995) = 1000
For I = 1995 To 2000
Projlncome(I) = 1.1 * Projlncome (I - 1)
Next I
4.	НАЙДИТЕ ОШИБКУ. Исправьте следующий фрагмент программы.
Option Base 1
Dim Х(100) As Double
Dim I As Integer
For I = 1 To 100
X(I) =1*1
Next I
For I = 1 To 100
Cells (1,1).Value = X(I)
Next I
ReDim X(10) As Integer
For I = 1 To 10
X(I) =1*1
Next I
For I = 1 To 10
Cells (1,1).Value = X(I)
Next I
5.	НАЙДИТЕ ОШИБКУ. Исправьте следующий фрагмент программы:
Option Base 2
Dim А(9) As Long
Dim I As Integer
День 14-й. Массивы
491
A(LBound(A)) = 2
For I = LBound(A) + 1 To Ubound(A)
A(I) = 2 * A(I-l) - 1 Next I
6.	НАЙДИТЕ ОШИБКУ. Исправьте следующий фрагмент программы. Option Base 1
Dim А(9) As Long
Option Base 0
Dim B(9) As Long
Dim I As Integer
A(LBound(A)) = 2
For I = LBound(A) + 1 To Ubound(A)
A(I) = 2 * A(I-l) - 1
B(I) = 2 * A(I-l) Next I
7.	НАЙДИТЕ ОШИБКУ. Исправьте следующий фрагмент программы. Dim А(9) As Long Option Base О Dim B(9) As Long Dim I As Integer A(LBound(A)) = 2
For I = LBound(A) + 1 To Ubound(A)
A(I) = 2 * A(I-l) - 1
B(I) = 2 * A(I-l) Next I
492
Неделя 2
Итоги второй недели: усовершенствование программы создания новой книги
Завершилась вторая неделя обучения программированию на языке Visual Basic для приложений (VBA). Теперь при работе с VBA вы должны чувствовать себя достаточно уверенно. К настоящему моменту освещены все основные концепции этого средства разработки приложений.
В последующих листингах содержится несколько завершенных VBA-модулей для Excel, в которых собранны воедино многие элементы и концепции программирования VBA, описанные в течение предыдущей недели.
Приведенные в этом разделе модули вместе образуют единую VBA-программу для Excel, которая позволяет пользователю в одной операции создавать файл новой книги и присваивать ему имя, не дублируя названия любых уже открытых или существующих на диске в этой же папке книг. Программа также позволяет пользователю при желании указывать число и названия листов в книге.
Эта программа была разработана с использованием технологии пошаговой детализации сверху вниз, описанной на 12-м уроке. В приведенной ниже схеме перечислены основные действия, выполняемые этой программой при работе.
1.	Получите у пользователя имя файла книги.
2.	Проверьте, не существует ли уже такая книга.
А. Если книга уже открыта, предложите сделать ее активной.
Б. Если книга не открыта, но существует в текущей папке, предложите ее открыть.
3.	Создайте новую книгу, используя стандартные параметры книги Excel.
4.	Получите общую информацию для новой книги.
А.	Потребуйте, чтобы пользователь ввел информацию об авторе.
Б. Потребуйте, чтобы пользователь ввел заголовок книги.
В.	Любая другая общая информация является необязательной.
5.	Сформируйте запрос в адрес пользователя, хочет ли он задать число листов в книге.
А. Получите от пользователя число листов, потребовав наличия, по меньшей мере, одного листа и запретив пользователю отменить эту операцию
Б. Создайте или удалите листы в книге, чтобы их количество соответствовало указанному пользователем.
6.	Запросите пользователя, хочет ли он изменить стандартные названия листов книги.
А.	Получите у пользователя названия для всех листов в книге; убедитесь, что названия уникальны.
Б. Запросите пользователя, нужно ли упорядочить названия листов в алфавитном порядке. Если да, упорядочьте массив названий листов.
В.	Переименуйте все листы в книге, используя массив названий листов, полученный ранее от пользователя.
7.	Сохраните новую книгу, но оставьте ее открытой в качестве текущей с выбранным первым листом.
Изучая приведенные ниже листинги, обратите внимание, как каждая задача обрабатывается собственной процедурой или функцией и как различные процедуры вызы
День 14-й. Массивы
493
вают несколько различных функций или процедур для выполнения своих задач. Обратите внимание также, что исходный текст программы разделен на три отдельных модуля: модуль главной программы, модуль, содержащий вспомогательные процедуры и функции, характерные для главной задачи, и третий модуль, содержащий более общие функции, используемые программой.
Листинг Я2.1 ГлааиыО мадцль и ироцеддра нраграммы OpeuNewBaak
1: Option Explicit 2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45:	Type Smrylnfo 'Те же данные, что и в окне свойств Title As String Subject As String Author As String Keywords As String Comments As String End Type Sub OpenNewBook() 'Создает новую книгу, при этом проверяя, 'что бы такая книга не была уже открыта, 'не существовала в текущей папке. 'Позволяет задать число листов и модулей, 'и выбрать для иих имена. Const nfTitle = "Создание книги" Dim IName As String 'Имя файла, включая путь Dim BName As String 'Имя файла без пути Dim OKName As Boolean Dim Smry As Smrylnfo 'Данные о свойствах книги Dim Ans As Integer 'Ответ функции MsgBox Dim mPrompt As String 'приглашение функции MsgBox Dim nBook As Workbook 'объект новой книги 'Выводим сообщение в строку состояния Application.StatusBar = "Создание новой книги..." BName = "Новая книга" 'имя файла по умолчанию OKName = False Do fName = GetBookName(Dflt:=BName, Title:=nfTitle & “ - Выберите имя ") If fName = "False" Then EndNewBook 'отмена пользователя BName = FuilName2BookName(fName) If IsBookOpen(BName) Then mPrompt = BName & " уже открыта. " & "Хотите перейти в нее?" Ans = MsgBox(Prompt:=mPrompt, Title:=nfTitle, Buttons:=vbQuestion + vbYesNo) If Ans = vbYes Then Workbooks(BName).Activate
494	Неделя 2
46:	EndNewBook
47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92:	End If Elself IsDiskFileffName) Then mPrompt = fName & vbCr & " ухе существует. Хотите открыть ее?" Ans = MsgBox(Prompt:=mPrompt, Title:=nfTitle, Buttons:=vbQuestion + vbYesNo) If Ans = vbYes Then Workbooks.Open fName EndNewBook End If Else OKName = True 'Книга не существует End If Loop Until OKName 'Создаем новую книгу с одним листом Set nBook = Workbooks.Add(xlWorksheet) 'узнаем свойства GetSmrylnfo lnfo:=Smry, Title:=nfTitle With nBook 'запоминаем свойства .Title = Smry.Title .Subject 3 Smry.Subject .Author 3 Smry.Author .Keywords = Smry.Keywords .Comments 3 Smry.Comments End With mPrompt 3 "Изменить число листов в книге?" & vbCr S "Сейчас в книге ” 6 nBook.Worksheets.Count s " листов." Ans 3 MsgBox(Prompt:“mPrompt, Title:=nfTitle, Buttons:=vbQuestion + vbYesNo) If Ans 3 vbYes Then 'создаем новые листы NewBook_Worksheets nBook, nfTitle End If 'Сохраняем файл новой книги nBook.SaveAs fileName:=fName, FileFormat:=xlNormal, ReadOnlyReconiinended:“False, CreateBackup:=Fals e nBook.Activate nBook.Sheets(1).Activate EndNewBook 'все закрываем End Sub
Листинг R2.1 представляет первый модуль программы и содержит только одну процедуру OpenNewBook. Эта процедура — более полная версия процедуры NewBook,
День 14-й. Массивы
495
приведенной в обзоре предыдущей недели обучения и она использует почти все функции VBA, изученные в течение недели.
Обратите внимание на определенный пользователем тип данных (см. урок 11-го дня обучения), объявленный в строках 3-12. Процедура OpenNewBook использует этот нестандартный тип для передачи результирующей информации между этой процедурой и процедурой, которая действительно получает информацию от пользователя. Цикл Do (см. 9-й урок) начинается в строке 33 и повторяется до тех пор, пока выбранное пользователем имя файла не повторит название книги уже открытой или уже существующей в текущей папке на диске.
Процедура OpenNewBook вызывает много других процедур и функций, помогающих ей выполнять свою задачу. Эти процедуры предотвращают дублирование исходного текста программы и позволяют процедуре OpenNewBook действовать гибко и эффективно. Все вызываемые ею процедуры и функции находятся во втором и третьем модулях (см. листинги R2.2 и R2.3).
В строке 34 процедуры OpenNewBook осуществляется вызов функции GetBookName для получения имени файла книги от пользователя. В строке 38 выполняется вызов функции FullName2BookName для получения имени книги из имени файла, выбранного пользователем. (Возвращаемое функцией GetBookName имя файла содержит полный путь папки, включая букву дисковода.)
Строки 39-59 содержат несколько вложенных инструкций If.. .Then.. .Else, которые выполняют проверку для обеспечения невозможности дублирования выбранным пользователем именем файла имени уже открытой или хранящейся на диске книги. Во время процесса проверки процедура OpenNewBook вызывает функции IsBookOpen и IsDiskFile (обе эти функции находятся в третьем модуле).
В строке 63 метод Add коллекции Workbooks используется для создания новой рабочей книги, содержащей только один лист; инструкция в строке 63 также определяет переменную объекта (см. 7-й урок) для ссылки на только что созданную книгу. Исходная инструкция метода Add была взята из сохраненного макроса. Затем метод Add был исследован с помощью системы интерактивной справки, благодаря чему стало ясно, что этот метод возвращает объектную ссылку на объект только что созданной книги и имеет необязательный аргумент, указывающий, что новая книга должна содержать только один лист. Если объектный метод возвращает результат, этот метод можно использовать в тексте программы VBA подобно функции — нужно только не забыть вставить скобки и использовать результат в каком-либо выражении.
ПРИМЕЧАНИЕ
Некоторые из инструкций во всех трех приведенных здесь модулях были созданы посредством копирования инструкций из сохраненных макросов. Затем методы из инструкций сохраненных макросов были исследованы с помощью системы интерактивной справки для выяснения дополнительных параметров этих методов. Эту технологию можно использовать для разработки собственного текста VBA-программы.
В строке 66 выполняется вызов процедуры GetSmrylnfo, которая использует переданный ссылкой (см. 12-й урок) аргумент процедуры для возвращения общей информации о новой книге процедуре OpenNewBook.
Строки 75-82 служат для выполнения задачи запроса пользователя о необходимости настройки числа листов в книге. Если пользователь решает настроить число листов, инструкция в строке 81 вызывает процедуру NewBook_Worksheets. Эта процедура выполняет задачу получения от пользователя требуемого числа листов, позволяя переименовывать листы и упорядочивать их имена в алфавитном порядке.
И наконец, в строках 85-88 выполняется сохранение новой книги, строки 89-90 обеспечивают, чтобы она была активной с выбранным первым листом.
496
Неделя 2
В следующем листинге показан еще один завершенный модуль, содержащий все процедуры, вызываемые непосредственно процедурой OpenNewBook. (Чтобы процедура OpenNewBook работала правильно, все три модуля должны быть в одной книге.)
Листинг Й2.2. Моцуль всномогатвльных ироцеуцр ирограммы BpeuNewBoak
1:	Option Private Module
2:	Option Explicit
3:
4:	Public Const STREQUAL As Integer	=	0
5:	Public Const STRLESS As Integer = -1
6:	Public Const STRGREAT As Integer	=	1
7:
8:	Sub EndNewBookOf)
9:	'Завершает программу OpenNewBook
10:	Application.StatusBar = False
11:	End
12:	End Sub
13:
14:
15:	Sub GetSmrylnfofByRef Info As Smrylnfo,
16:	ByVai Title As String)
17:	'возвращает данные свойств
18:	Title = Title & " Свойства"
19:	With Info	'читаем свойства
20:	.Author = GetSmryItem(Item:="ABTop", Title:=Title,
21:	Dflt:=Application.UserName,
22:	Required:=True)
23:	.Title = GetSmryltem(Item:="Название", Title:=Title,
24:	Required:=True)
25:	.Subject = GetSmryItem(Item:="TeMa", Title:=Title)
26:	.Keywords = GetSmryltem(Item:="Knio4eBHe слова", Title:=Title)
27:	.Comments = GetSmryItem(Item:="3aMeTKH", Title:=Title)
28:	End With
29:	End Sub
30:
31:
32:	Function GetSmryltemjltem As String,
33:	Optional	ByVai Title,
34:	Optional	Dflt,
35:	Optional	Required) As	String
36:	'читаем свойства
37:
38:	Const Prmptl = "Введите "
39:	Dim Prmpt2 As String	'2-я часть пришлашения
40:	Dim tStr As String
41:
42:	If IsMissing(Title) Then Title = UCase(Item)
43:	If IsMissing(Required) Or _
44:	VarType(Required) <> vbBoolean Then
45:	Required = False
46:	Title = Title & " - Дополнительный ввод”
47:	Else
День 14-й. Массивы
497
48:	Title = Title & " - Обязательный ввод"
49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100:	End If Prmpt2 = " этой книги." If Not Required Then Prmpt2 = Prmpt2 & vbCr & "(Нажмите Esc или выберите Cancel для выхода.)" End If Do tStr = InputBox(Prompt:=Prmptl & Item & Prmpt2, Titie:=Title, Default:=Dflt) If Required And (Trim(tStr) = "") Then MsgBox Prompt:="BH должны ввести " & Item & Title:=Title & " - ERROR", _ Buttons:=vbExclamation End If Loop Until (Not Required) Or (Trim(tStr) <> "") GetSmryltem = tStr End Function Sub NewBook_Worksheets(ByRef Book As Workbook, ByVai Title As String) 'Устанавливаем число листов в этой книге Dim shtNames() As String Dim numSheets As Integer Dim Ans As Integer	'для ф-ции MsgBox answers Dim k As Integer	' счетчик цикла Dim Sht As Worksheet Dim Sht2 As Worksheet Title = Title & " - Выбор листов" Do numSheets = GetInteger(Prompt:="Укaжитe число " & "листов в этой книге:", Title:=Title, Default:=l) If numSheets < 1 Then MsgBox Prompt:="Вы должны создать хоть одни " & " лист в этой книге!", Title:“Title, Buttons:=vbExclamation End If Loop Until numSheets >= 1 Ans = MsgBox(Title:=Title, Buttons:=vbQuestion + vbYesNo, _ РготрЬ:="Изменить имена листов?") If Ans = vbNo Then 'только добавляем For k = (Book.Worksheets.Count + 1) To numSheets Set Sht = Book.Worksheets(Book.Worksheets.Count)
498
Неделя 2
101:	Set Sht2 = Book.Worksheets.Add
102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153:	Sht2.Move after:=Sht Next к Exit Sub 'no more work to do End If ReDim shtNames(l To numSheets) 'меняем размер массива For к = 1 To numSheets shtNames(к) = "Лист " s к 'создаем имена Next к Get_NameList Book:=Book, List:=shtNames, _ Prompt:="BBeflHie имя листа:”, Title:=Title Ans = MsgBox(Title:=Title, Buttons:=vbQuestion + vbYesNo, Prompt ^"Отсортировать имена листов?") If Ans = vbYes Then BubbleSortStrings shtNames 'create the worksheets with names With Book 'start by just renaming the first sheet .Worksheets ').Name = shtNames)1) For к = 2 To numSheets Set Sht = .Worksheets ' Worksheets. Count) Set Sht2 = .Worksheets.Add Sht2.Move after:=Sht Sht2.Name = shtNames(k) Next к End With End Sub Sub Get_NameList(Book As Workbook, List)) As String, Prompt As String, Title As String) 'получает список листов Dim к As Long, tstr As String, GoodName As Boolean For к = LBound(List) To UBound(List) Do 'спрашиваем уникальное имя tstr = InputBox)Prompt:=Prompt, Title:=Title, Default:=List(k)) If Trim(tStr) = Then Exit Sub Else If StrCompftStr, List(k), vbTextCoiTipare) = STREQUAL Then GoodName = True Else If InListSoFarfList, tstr, к - 1) Or _
День 14-й. Массивы
499
154:	SheetExists(Book, tStr) Then
155:	MsgBox Prompt:“"Выберите другое имя. " &
156:	"Это имя уже существует; "	&
157:	"имена листов должны быть уникальными.1',
158:	Title:="Sheet Name Error",
159:	Buttons:=vbExclamation
160:	GoodName = False
161:	Else
162:	GoodName = True
163:	List(k) = tStr
164:	End If
165:	End If
166:	End If
167:	Loop Until GoodName
168:	Next k
169:	End Sub
170:
171:	Sub BubbleSortStrings(Arr() As String)
172:	'сортирует по возрастанию
173:	Dim I As Integer, J As Integer
174:	For I = LBound(Arr) To UBound(Arr) - 1
175:	For J = I + 1 To UBound(Arr)
176:	If StrComp(Arr(I), Arr(J), vbBinaryCompare) = STRGREAT Then
177:	Swap Arr(l), Arr(J)
178:	End If
179:	Next J
180:	Next I
181:	End Sub
182:
183:
184:	Sub Swap(Tl As Variant, T2 As Variant)
185:	Dim Tmp As Variant
186:	Tmp = T: T1 = T2: T2 = Tmp
187:	End Sub
Все функции и процедуры во втором модуле должны вызываться другой процедурой или функцией: почти все из них предназначены для вызова процедурой OpenNewBook. Обратите внимание на директиву компилятора (см. 12-й урок) Option Private Module в строке 1. Она означает, что ни одна из процедур или функций этого модуля не доступна извне книги, содержашей этот модуль.
Модуль начинается с объявления нескольких констант на уровне модуля (строки 4-6). Эти константы должны быть доступны всем модулям программы, поэтому их следует объявить с ключевым словом Public (см. 12-й урок). Это ключевое слово делает константы доступными вне данного модуля, в то время как директива Option Private Module ограничивает сферу их применения только содержащей данный модуль книгой. Эти константы служат для представления результатов выполнения функции StrComp, упрощая тем самым понимание текста использующей ее программы.
Строки 8-12 содержат процедуру EndNewBook. Она всего лишь обеспечивает возврат управления программе Excel, а затем использует инструкцию End для прекращения выполнения программы. Подобная процедура удобна потому, что в OpenNewBook существует несколько мест, где она должна быть остановлена. Поскольку во время своего выполне
500
Неделя 2
ния процедура OpenNewBook использует свойство StatusBar приложения Excel для отображения сообщения о состоянии, две инструкции, предназначенные для восстановления управления Excel над панелью состояния и для выхода из подпрограммы, повторяются несколько раз. Благодаря использованию процедуры EndNewBook процедура OpenNewBook становится короче и понятней. Если в будущем процедура OpenNewBook будет изменена так, что она будет влиять на другие части среды Excel (например, на то, обновляет ли Excel экран), в процедуру EndNewBook можно добавить другие задачи “уборки”.
Строки 15-29 содержат процедуру GetSmrylnfo. Обратите внимание, что эта процедура не только имеет аргументы, но и использует аргумент Info для возврата данных вызвавшей ее процедуре (см. 12-й урок). Аргумент Info имеет нестандартный тип данных Smrylnfo; эта процедура заполняет все элементы в переменной аргумента Info. Обратите также внимание, что для получения значений конкретных элементов процедура GetSmrylnfo вызывает функцию GetSmryltem.
Строки 32-67 содержат функцию GetSmryltem, которая возвращает единственное строковое значение. Эта функция имеет несколько аргументов, большинство из которых необязательно. Аргумент Item — имя элемента общей информации, в отношении которого функция GetSmryltem направляет запрос пользователю. Необязательный аргумент Dflt представляет строку, предлагающую содержимое конкретного подлежащего получению элемента общей информации. Необязательный аргумент Requred позволяет определить, должна ли функция GetSmryltem требовать ввода пользователем запрошенной информации или можно обойтись без нее. Обратите внимание, как функция GetSmryltem проверяет отсутствие и наличие необязательных аргументов с правильным типом данных и устанавливает соответствующие значения по умолчанию для отсутствующих или неверных необязательных аргументов.
В строках 7-132 содержится процедура NewBook_Worksheets. Эта процедура имеет два аргумента: один для объектной ссылки на создаваемую книгу и второй для заголовка, подлежащего отображению диалоговыми окнами сообщений этой процедуры. Процедура объявляет несколько переменных; обратите внимание на динамический массив (см. 14-й урок), объявленный в строке 74 и на переменные объекта (см. 7-й урок), объявленные в строках 78 и 79.
Процедура NewBook_Worksheets начинается с цикла Do, который запрашивает пользователя о числе листов, подлежащих включению в книгу, до тех пор пока введенное число не меньше 1. Обратите внимание, что для получения целого значения эта процедура вызывает функцию общего назначения Getlnteger. (Функция Getlnteger находится в третьем модуле.) Инструкции в строках 95-98 проверяют, намерен ли пользователь изменить имена листов, определяемые по умолчанию. Если нет, цикл For в строках 99-103 создает требуемое число листов, перемещая каждый новый лист в конец книги. В строке 104 выполняется выход из процедуры NewBook_Worksheets, поскольку больше ничего не нужно делать.
Если пользователь хочет изменить имена листов, определяемые по умолчанию, в строках 107-110 размерность динамического массива shtNames устанавливается такой, чтобы он был достаточно большим для размещения в нем всех необходимых имен листов, а затем массив заполняется условными именами листов по умолчанию. Затем в строках 113-115 осуществляется вызов процедуры Get_NameList для заполнения массива shtNames именами листов, полученными от пользователя. Аргумент, переданный ссылкой, процедура Get_NameList использует для возврата значений в массив shtNames. Строки 117-119 служат для запроса пользователя о необходимости упорядочения имен листов в алфавитном порядке и вызова процедуры BubbleSortStrings для упорядочения массива shtNames.
И наконец, строки 122-131 создают и переименовывают новые листы в книге. Поскольку книга была создана с единственным листом, строка 124 просто переименовывает лист, а цикл For (строка 125) создает имена только для дополнительных листов.
День 14-й. Массивы
501
Строки 135-169 содержат процедуру Get NameList. Эта процедура в основном заполняет список (переданный ссылкой в качестве аргумента массива строк) именами листов, исключая дублирование существующих в книге или уже введенных в список имен листов. Для выяснения того, какую книгу следует проверять на предмет дублирования имен, процедура Get_NameList принимает в качестве обязательного аргумента объектную ссылку на книгу, а также аргументы, представляющие массив строк, подлежащую отображению подсказку и заголовок диалоговых окон сообщений.
Процедура Get_NameList использует функции LBound и UBound, чтобы она могла работать с любым массивом строк. Для того, чтобы строки всегда сравнивались в виде текста (т.е. без учета строчных и прописных букв), эта процедура обращается к процедуре StrComp. Чтобы выяснить, ввел ли пользователь уникальное имя листа, процедура Get_NameList вызывает функции InListSoFar и SheetExists. Эта процедура выполняет цикл до тех пор, пока не будут обработаны все элементы в массиве или пока пользователь не закроет одно из диалоговых окон ввода.
Строки 171-181 содержат процедуру BubbleSortStrings. Эта процедура полностью аналогична процедурам циклической сортировки, приведенным на 14-м уроке, но она была изменена для упорядочения в алфавитном порядке строк, а не чисел. Обратите внимание, что в строке 176 функция StrComp используется для сравнения строк. Тем самым обеспечивается сравнение строк без влияния на него настроек Option Compare — эта процедура всегда будет выполнять алфавитное сравнение строк с учетом регистра символов, что и предполагается делать в данном случае.
И, наконец, строки 184-187 содержат процедуру Swap, используемую процедурой BubbleSortStrings. Обратите внимание, что эта версия процедуры Swap была изменена для применения переменных типа Variant: она может менять местами значения любого типа данных. Преимущество такого изменения процедуры Swap в том, что теперь она может использоваться любой процедурой цикличного упорядочения, независимо от того, выполняется ли упорядочение строк или чисел.
Третий и последний модуль, образующий завершенную программу, представляет собой группу поддерживающих функций.
Aucmuuz Я2.3. Маддль поддерживпищпх фдикциО программы OpeuNewBoak
1:	Option Private Module
2:	Option Explicit
3:
4:	Function	GetBookName(DfIt	As	String,
5:	Optional	Title) As String
6:	'Спрашивает имя файла, возвращает "False" при отмене
7:	Dim Fltr	As String, tName	As	String
8:
9:	If IsMissing(Title) Then 'указано название в аргументах?
10:	Title = "Ввод названия книги"
11:	End If
12:
13:	Fltr = "Excel Workbooks,*.xis"	'только	книги
14:	With Application
15:	tName = .GetSaveAsFilename(lnitialFilename:=Dflt,
16:	FileFilter:=Fltr,
17:	Title:=Title)
18:	End With
19:	GetBookName = tName 'имя файла
20:	End Function
502
Неделя 2
21:
22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73:	Function FullName2BookName(fileName As String) As String 'возвращает полное имя файла Dim tName As String, к As Long tName = ReverseStr(fileName) к = InStr(tName, "\") If к = 0 Then FullName2BookNanie = fileName Else tName = Left(tName, к - 1) End If 1 FullName2BookName = ReverseStr(tName) End Function Function IsBookOpen(ByVal BName As String) As Boolean 'возвращает True если книга открыта Dim aBook As Object IsBookOpen = False 'assume failure BName = Trim(BName) For Each aBook In Workbooks If StrCor4p(aBook.Name, BName, vbTextCoinpare) = STREQUAL Then IsBookOpen = True Exit For End If Next aBook End Function Function IsDiskFileffName As String) As Boolean 'возвращает True если fName найден на диске, False если нет If (Dir(fName) <> "“) Then IsDiskFile = True Else IsDiskFiie = False End If End Function Function InListSoFar(List() As String, SrchFor As String, Limit As Long) As Boolean 'линейный поиск SrchFor в List Dim k As Long InListSoFar = False For k = LBound(List) To Limit
День	14-й. Массивы	503
74:	If StrComp(Listfk), SrchFor, vbTextCompare) = STREQUAL Then
75: 76: 77: 78: 79: 80:	InListSoFar = True Exit Function End If Next к End Function
81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95;	Function SheetExists(Book As Workbook, ShtName As String) As Boolean t Dim Sht As Object SheetExists = False For Each Sht In Book.Sheets If StrComp(Sht.Name, ShtName, vbTextCompare) = STREQUAL Then SheetExists = True Exit Function End If Next Sht End Function
96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: НО: 111: 112:	Function Getlnteger(Prompt As String, Optional Title, Optional Default) As Long 'возвращает целое Dim tRsp As Variant If IsMissing(Title) Or VarTypefTitle) <> vbString Then Title = "Ввод целого" End If If IsMissing(Default) Or (Not IsNumeric(Default)) Then Default = Else Default = CLng(Default) End If
113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124:	Do tRsp = InputBox(Prompt:“Prompt, Title:=Title, Default:=Default) If (Not IsNumeric(tRsp)) Or (Len(TrimftRsp)) = 0) Then MsgBox Prompt:="Введите целое число!", Title:“Title, Buttons:=vbExclamation Loop Until IsNumeric(tRsp) And (Len(Trim)tRsp)) > 0) Getlnteger = CLng(tRsp) End Function
125: 126: 127: 128: 129:	Function ReverseStr(Str As String) As String 'обращает порядок символов в аргументе Str Dim к As Long, tStr As String
504
Неделя 2
130:	tstr =
131:	For к = 1 To Len(Str)
132:	tstr = MidfStr,	k, 1) & tstr
133:	Next к
134:	ReverseStr =	tstr
135:	End Function
Этот модуль включает поддерживающие функции, используемые процедурами в обоих листингах, R2.1 и R2.2. Функции этого модуля имеют более общее назначение, чем процедуры и функции в листинге R2.2, и поэтому они были выделены в отдельный модуль для упрощения их копирования впоследствии в другую книгу, нуждающуюся в аналогичных поддерживающих функциях, или для включения их в библиотечную книгу.
Обратите внимание, что этот модуль, подобно листингу R2.2, содержит директиву компилятора Option Private Module, делающую все процедуры и функции в этом модуле приватным для содержащей их книги.
Строки 4-20 содержат функцию GetBookName. Эта версия функции аналогична приведенной в обзоре первой недели, но она значительно усовершенствована. Функция имеет другие аргументы по сравнению с предыдущей версией. Для получения имени файла от пользователя в строке 15 в этой версии вместо InputBox используется метод GetSaveAsFilename (см. 13-й урок). Поскольку указанная строка фильтра содержит только расширение .xis, метод GetSaveAsFilename не позволяет пользователю выбрать имя файла, отличающееся по типу от книги Excel. Обратите внимание, что функция GetBookName возвращает строку "False", если пользователь отменяет выбор файла.
В строках 23-38 содержится функция FullName2BookName. Она возвращает только часть имени файла строки, содержащей полную спецификацию пути файла (включающую в себя букву дисковода и полный путь к папке — подобно строкам, возвращаемым методом GetSaveAsFilename, описанным на 13-м уроке).
Обратите внимание, что FullName2BookName вызывает еще одну функцию, ReverseStr, помогающую ей выполнить свою задачу. В полном пути папки имя файла — часть, следующая за последней обратной косой чертой (\) в строке пути. Поскольку все функции поиска строк VBA выполняют поиск только слева направо, трудно найти последний случай конкретного символа, и поэтому функция FullName2BookName работает, изменяя порядок представления полного имени пути, а затем копируя все символы, предшествующие первой обратной косой черте. Например, если путь папки C:\Budget\June.xls, FullName2BookName изменяет его на slx.enuJ\tegduB\:C, копирует из него slx.enuJ, а затем снова изменяет порядок символов на противоположный для получения June.xls.
Строки 41-53 содержат функцию IsBookOpen, которая возвращает результат Boolean, показывающий, является ли указанная в аргументе книга открытой. Эта функция использует цикл For Each... Next (см. 9-й урок) для проверки всех открытых в текущий момент книг в коллекции Workbooks.
В строках 56-63 содержится функция IsDiskFile, которая возвращает значение Boolean, показывающее, существует ли на диске файл, соответствующий дисководу, папке и имени файла в аргументе fName. Обратите внимание на обращение к функции Dir в строке 58. Функция IsDiskFile идентична функции с таким же именем, описанной на 13-м уроке.
Строки 66-79 содержат функцию InListSoFar, которая возвращает результат Boolean, зависящий от того, найдено ли в массиве List вплоть до элемента, указанного аргументом Limit, значение, представленное в аргументе SrchFor. Функция InListSoFar
День /4-й. Массивы
505
выполняет в массиве простой линейный поиск (см. 14-й урок). (Эта функция использует линейный, а не более эффективный двоичный поиск, поскольку на данный момент массив не упорядочен, а двоичный поиск работает только с упорядоченными массивами.)
Строки 82-94 содержат функцию SheetExists, которая также возвращает значение Boolean, на этот раз зависящее от того, существует ли определенный массивом ShtName лист в книге, указанной массивом Book.
Строки 97-123 содержат функцию Getlnteger, которая возвращает целое число Long. Эта функция требует аргумента для строки приглашения, подлежащей отображению диалоговым окном InputBox и двух необязательных аргументов для заголовка диалогового окна и вводимого значения по умолчанию. Обратите внимание на строки 103-111, в которых проверяется наличие необязательных аргументов и присваиваются разумные значения, если необязательные аргументы отсутствуют или имеют неверный тип. В строке 113 начинается цикл Do, повторяющийся до тех пор, пока пользователь не введет численное значение; пользователю не разрешается закрыть диалоговое окно ввода. Как только пользователь вводит численное значение, оно преобразуется в целочисленное значение Long (любые десятичные знаки отбрасываются) и возвращается в качестве результата функции Getlnteger.
И наконец, строки 126-135 содержат функцию ReverseStr. Эта функция просто возвращает копию строки, переданной в качестве ее аргумента, изменив порядок следования символов на обратный. Обратите внимание, что в строке 128 в одной инструкции Dim объявляются две переменные. В строке 131 начинается цикл For... Next (см. 9-й урок), повторяющийся столько раз, сколько имеется символов в строке аргумента Str, представляя их в обратном порядке.
В этой программе собрано почти все, что изучено вами в течение первых двух недель обучения программированию на языке VBA. Как видите, многое из того, что вы освоили в течение второй недели, упрощает (и делает возможным) создание мощных и гибких программ посредством использования нескольких процедур и функций, работающих вместе. На протяжении следующей недели читатели продолжат знакомство с VBA на основе уже изученного материала.
506
Неделя 2
<sAms
Освой самостоятельно
Неделя 3
Обзор
Завершилась вторая неделя изучения программирования на Visual Basic для приложений. Были рассмотрены все основные функции этого языка, и читатели должны чувствовать себя достаточно уверенно при его использовании. Теперь можно приступать к изучению некоторых более сложных функций VBA.
Что дальше
Получив твердое представление об основных функциях языка программирования VBA, читатели располагают всем необходимым для овладения его более сложными функциями. В ходе третьей недели они узнают, как заставить VBA-программы выполняться так, как если бы они были составной частью Escel, и как придать им совершенный, профессиональный вид.
На уроке 15-го дня учебного будет показано, как использовать средства отладки редактора Visual Basic для исследования работы программы и для отыскания и исправления ошибок в программах. Средства отладки редактора Visual Basic позволяют подробно исследовать выполнение процедур и программ и обнаруживать или отслеживать различные проблемы, обнаружить которые иначе было бы почти невозможно.
На 16-м уроке читатели узнают, как создавать и использовать настраиваемые диалоговые окна для своих программ. На 17-м уроке речь пойдет об использовании инструкций меню и панелей инструментов VBA для создания и управления меню панелями и меню Excel с помощью своих процедур.
Урок 18-го дня обучения посвящен способам обработки ошибок в VBA, с помощью которых программы и процедуры пользователя смогут изящно и профессионально справляться с ошибками времени выполнения. На 19-м уроке вы получите дополнительную информацию по управлению объектами Excel с помощью процедур и программ VBA. На 20-м уроке читатели научаться использовать программные объекты из других Windows-приложений, как, например, управление программой Word из Excel и наоборот. На этом же занятии также показано, как использовать функции и процедуры в файлах DLL и как управлять другими приложениями, пересылая кода нажатия клавиш.
И наконец, в ходе 21-го урока мы выясним, как использовать процедуры, выполняющиеся автоматически при открытии или закрытии книги или документа, и как указать конкретную процедуру, подлежащую выполнению в случае конкретного события. Кроме того, на этом же занятии вы узнаете о способах преобразования процедур в программы-надстройки Excel.
Приложение содержит ответы и решения для большинства упражнений, приведенных в главах.
День 15-й. Отладка VBA-программы
507
Отладка VBA-программы
Отладкой называется процесс проверки программы и устранения найденных ошибок, которые искажают результат или делают выполнение программы невозможным. На сегодняшнем уроке мы рассмотрим специальные средства VBA, предназначенные для отладки программ. В редакторе Visual Basic есть несколько мощных инструментов для поиска и исправления ошибок в процедурах и функциях. В ходе этого занятия мы рассмотрим следующие вопросы.
•	Какие типичные ошибки встречаются при разработке процедур и функций.
•	Что такое режим прерывания и что такое точки останова; как приостановить выполнение программы для проверки правильности ее работы.
•	Как пользоваться контрольными выражениями для отслеживания значений, полученных в процессе работы программы.
•	Как пользоваться командами Step Into (Шаг с заходом) и Step Over (Шаг с обходом) для наблюдения за работой программы и каждой ее процедуры.
•	Как проверить порядок вызова процедур.
•	Для чего предназначено окно отладки и как им пользоваться для проверки значений выражений.
Сегодняшний урок начинается с обсуждения основных технологий отладки (т.е. обнаружения и устранения ошибок), а затем в нем представлены примеры использования этих технологий.
Распространенные топы ошибок
Закон Мерфи (“Если неприятность может случиться, она обязательно случится”) особенно актуален в компьютерном программировании. Почти любая часть программы может оказаться неверной. Каждый программист, независимо от квалификации или используемого языка программирования, вынужден затрачивать определенное время на отыскание ошибок и недостатков в своих программах. Как правило, эти недостатки и ошибки приводят к неожиданной остановке выполнения программ (их разрушению) или дают ошибочные результаты. Любые дефекты или ошибки в компьютерной программе называются программными ошибками.
508
Неделя 3
При программировании на VBA читатели столкнуться с четырьмя основными типами ошибок:
•	Синтаксические ошибки являются результатом опечаток, таких как неправильный ввод ключевого слова VBA, переменной или имени процедуры. Обычно синтаксические ошибки случаются при создании текста программы. Как было показано на 12-м уроке дня, редактор Visual Basic может обнаруживать большинство синтаксических ошибок во время редактирования или ввода каждой строки программы. (VBA проверяет синтаксис при перемещении точки ввода из только что измененной или введенной строки.) Иногда некоторые синтаксические ошибки проявляются в виде ошибок компиляции.
•	Ошибки компиляции проявляются при обработке инструкций, которые VBA не в состоянии правильно скомпилировать. Они могут иметь место при попытке запуска процедуры VBA. Если во время компиляции VBA обнаруживает любые проблемы, связанные с текстом программы, он отображает сообщение об ошибке и не выполняет никакую часть программы. Например, при наличии в модуле объявления Option Explicit и использовании переменной без предварительного объявления ее в инструкции Dim VBA перемещает точку ввода к месту обнаружения необъявленной переменной и отображает сообщение об ошибке компиляции.
•	Ошибки времени выполнения проявляются при обработке выражений или инструкций, которые VBA не может вычислить или выполнить, таких как неверные операции, неверные аргументы процедур и функций или недопустимые математические операции. Ошибки времени выполнения происходят во время выполнения программы. Типичными примерами ошибок времени выполнения являются выражения, которые приводят к потере или переполнению порядка, несоответствию типов в выражениях присваивания или аргументах процедур при попытке открытия несуществующих файлов или попытке деления на нуль.
•	Логические ошибки являются результатом ошибки в рассуждениях программиста. Типичными признаками логических ошибок в программе служат ошибочные результаты: процедуры или функции могут создавать неверные численные результаты, задача может выполняться неправильно или не полностью или же процедура может выполнять не ту задачу. В зависимости от конкретных результатов логической последовательности выполнения программы, некоторые, но не все, логические ошибки могут вызывать ошибки времени выполнения.
КАМШЯПвр УВД
Когда VBA выполняет любую программу в только что созданном или измененном модуле, он компилирует исходный текст этого модуля. В ходе компиляции VBA считывает весь исходный текст программы модуля. При этом он создает свои внутренние таблицы переменных и констант и убеждается в правильности образования всех структур ветвления и организации циклов, а также объявлений процедур и функций. VBA также проверяет все вызовы процедур и функций, чтобы убедиться в наличии требуемого числа аргументов соответствующего типа. Этот процесс компиляции - важный шаг подготовки УВД к выполнению программы. Именно во время него VBA обрабатывает директивы компилятора (такие как Option Explicit. Option Compare и Option Base) и убеждается, что весь текст программы модуля соответствует ограничениям установленным директивами компилятора.
Как правило, VBA компилирует текст программы только один раз, при первом выполнении процедуры после ее создания или редактирования. При сохранении проекта скомпилированный код сохраняется вместе с исходным текстом. При каждом последующем выполнении программы для экономии времени VBA использует уже скомпилированный код программы. При каждом добавлении или изменении существующего текста модуля VBA снова выполняет компиляцию исходного текста.
День 15-й. Отладка VBA-программы
509
ПРИМЕЧАНИЕ
Можно заставить VBA выполнить компиляцию текста программы в любое время, выбрав команду Debug-Compile <Project> (Отладка-Компилировать). При этом VBA выполняет компиляцию всего текста программы проекта. Можно сэкономить некоторое время, затрачиваемое на отладку, выполнив компиляцию текста программы и устранив все ошибки компилятора прежде, чем предпринимать попытку запустить программу.
Режим прерывания
В ходе отладки текста VBA-программы часто требуется точно знать, как VBA выполняет инструкции программы и какие значения хранятся в различных переменных в конкретные моменты выполнения программы. Например, ошибка может присутствовать в процедуре, выполняющей сортировку массива, приводя к тому, что массив остается несортированным. Гораздо проще найти ошибку в процедуре сортировки, если известно, какие ветви инструкций If...Then выполняются, и если удается убедиться в правильности выполнения циклов Do или проверить значения сохраненные в переменных во время выполнения процедур сортировки.
Режим прерывания редактора Visual Basic предоставляет возможность взаимодействовать с текстом программы более непосредственно, чем при простом выполнении одной из программ. При выполнении процедуры программа выполняется от начала до конца с максимальной скоростью, доступной для VBA; нельзя точно сказать, ни какие части программы выполняются в каждый конкретный момент времени, ни какое значение содержит та или иная переменная. Однако в режиме прерывания появляется возможность выполнять программу (или ее отдельные части) построчно или по отдельным процедурам —выполняя программы на “малой скорости”. Выполнение программ по одной инструкции за раз называется пошаговым выполнением.
VBA обеспечивает пять различных способов активизирования режима прерывания редактора Visual Basic.
•	Щелкнуть на кнопке Debug в окне сообщения об ошибке.
•	Установить одну или несколько точек останова
•	Применить в программе инструкцию Stop.
•	Воспользоваться командой Debug-Step Into (или нажать <F8>).
•	Нажать клавишу <Esc> или <Ctrl+Break>, чтобы прервать выполнение программы.
Все эти способы запуска режима прерывания редактора Visual Basic описаны в последующих разделах этого урока.
Кроме команд отладки, доступных через меню Debug, редактор Visual Basic имеет панель инструментов Debug, содержащую кнопки для наиболее часто используемых команд отладки. Плавающее окно панели инструментов Debug редактора Visual Basic показано на рис. 15.1. Этот рисунок поможет идентифицировать кнопки команд, описываемых в остальной части урока.
ПРММЕЧАИ1Е
Редактор Visual Basic не отображает панель инструментов Debug автоматически. Для отображения этой панели инструментов выберите команду меню View-Toolbars-Debug. (Если эта панель уже отображена, слева от пункта меню Debug появляется символ птички (флажок).) Панель инструментов Debug может быть прижата к краю экрана или настроена, как и остальные панели инструментов в Excel.
510
Неделя 3
Локальные переменные
Шаг с выходом
-Окно отладки
Прервать Шаг с заходом Конструктор
Окно контрольных значений
Продолжить
Стек вызовов
Сброс | Шаг с обходом 1—Контрольное значение Точка останова
Рис. 15.1. Панель инструментов Debug
Переход в режим прерывания оз окна сообщения об ошибке
Во всех случаях ошибки времени выполнения во время выполнения одной из процедур или функций VBA отображает диалоговое окно, подобное показанному на рис. 15.2 (конкретное сообщение об ошибке будет зависеть от действительной ошибки времени выполнения). Это диалоговое окно отображает номер ошибки времени выполнения, краткое пояснение природы ошибки и несколько кнопок. Например, на рис. 15.2 ошибка времени выполнения имеет номер 11 и кратко описана как Division by zero (Деление на нуль).
Рис. 15.2. Типичное окно сообщения об ошибке времени выполнения
Диалоговое окно сообщения об ошибке времени выполнения содержит следующие командные кнопки.
•	Continue. Продолжает выполнение прерванной VBA-программы. Как правило, в случае ошибки времени выполнения команда Continue отключена, как показано на рис. 15.2.
•	End. Завершает выполнение VBA-программы и выходит из режима прерывания.
•	Debug. Отображает инструкцию VBA, вызвавшую ошибку времени выполнения, открывая при необходимости соответствующий проект и модуль. VBA остается в режиме прерывания, готовый для проверки значений переменных, последовательности процедур, приведшей к текущей процедуре, и другим связанным с отладкой действиям. Командная кнопка Debug служит для быстрого перехода к инструкции, вызывающей проблему.
•	Help. Отображает интерактивную справку по конкретной ошибке времени выполнения.
День 15-Й. Отладка VBA-программы
51'1
Для перехода в режим прерывания из диалогового окна сообщения об ошибке времени выполнения достаточно щелкнуть на кнопке Debug. Редактор Visual Basic откроет окно Code, в котором будет выделена инструкция, вызвавшая ошибку времени выполнения, как показано на рис. 15.3. Обратите внимание, что выполняемая в текущий момент инструкция указывается желтым выделением и желтой стрелкой на левом поле окна. (На рис. 15.3 окна Project и Properties закрыты, чтобы развернутое окно Code заполняло все доступное пространство экрана; это позволяет увеличить объем текста программы, одновременно видимый на экране. На рисунке показана также панель инструментов Debug.)
£gjj Microsoft Visual Basic - Day15.xls [break] - [ListingOZ (Code)]

EdS.yibw Insert Fgrmei debug Bun loots Add-Ins- y/indow Help
| (General)
-rj |DebugDemo2
Median = (Lo + Hi) \ 2 ’целочисленное деление
'выводим значения переменных в окно отладг
Debug.Print IntArr(Median), SrchFor, Lo, Hi, Median
If SrchFor < IntArr(Median) Then Hi = Median - 1 Else Lo = Median + 1 End If
Loop Until (SrchFor = IntArr(Median)) Or (Lo > Hi)
Debug

Stop 'переход в режим останова
If SrchFor = IntArr (Median) Then 'совпадение
pStr = "Число " & SrchFor & " найдено под номером " & Median
Else 'сообщение о несовпадении
pStr = "Не найдено " & SrchFor
End If
Рис. 15.3. Так выглядит окно Code в режиме прерывания
Точки останова
Для того чтобы определить причину ошибки, будь то логическая ошибка или ошибка выполнения, удобнее выполнить программу в пошаговом режиме. Пошаговое выполнение инструкций программы позволяет либо точно выяснить, какая часть программы выполнялась до и во время ошибки времени выполнения, либо наблюдать действие группы инструкций, работа которых кажется неправильной.
Пошаговое выполнение всех инструкций в процедуре или программы может оказаться достаточно долгой и трудоемкой задачей. Пошаговое выполнение всех инструкций в программе требуется редко. К счастью, редактор Visual Basic позволяет выполнять большую часть программы с максимальной скоростью, переходя в режим прерывания только во время выполнения определенных, специфичных инструкций. (Помните, что для пошагового выполнения программы вначале необходимо перейти в режим прерывания.)
5/2
Неделя 3
Точка останова ~ это специально помеченная строка в тексте программы; встречая точку прерывания, VBA переключается из обычного режима выполнения программы в режим прерывания. Используя точки останова, большую часть программы можно выполнять с максимальной скоростью, переходя в режим прерывания только тогда, когда VBA достигает определенных, вызывающих особый интерес инструкций. После установки точка останова продолжает действовать до тех пор, пока она не будет удалена или пока не будет закрыт проект, содержащий модуль с установленной точкой останова.
Точка останова может быть установлена в любой строке исходного текста программы, содержащей выполняемую инструкцию. Обычно точки останова устанавливаются несколько ранее инструкций, которые вызывают сомнения. Для того чтобы установить точку останова, сделайте следующее.
1.	Откройте модуль и процедуру, инструкции которых вы хотите выполнить в пошаговом режиме.
2.	В окне Code поместите курсор в строку, в которой VBA должен выполнить переход из обычного режима выполнения в режим прерывания. Эта строка станет новой точкой останова и должна содержать выполняемую VBA-инструкцию.
3.	Выберите команду Debug-Toggle Breakpoint, чтобы вставить точку останова. VBA окрасит строку в окне Code, показывая, что она выбрана в качестве точки останова, как показано на рис. 15.4: строка точки останова выделяется темно-красным цветом и на поле окна слева от строки появляется темно-красная точка.
•^j Microsoft Visual Basic - Day15.xls [break] - [ListingO! (Code)]
HBB
File Edit View Insert F^rmet Qebug Bun loots ^dcHns Vjtindow Help
'ВД'И'-
|(General)	~v] |0ebugDemo1
'пока он не откажется
Do
SrchFor = Application.InputBox ( _
prompt:="Введите искомое число:", Title:=iTitle s. " Ввод", _
Type:=1)
SrchFor = Cint(SrchFor) 'преобразуем в целое
Lo Ч LBound(IntArr) ’начало поиск*
Hi = UBound(IntArr)
Do
Median = (Lo + Hi) \ 2 'целочисленное деление
If SrchFor < IntArr(Median) Then
Hi = Median - 1
Else
Lo = Median + 1
End If
Loop Until (SrchFor = IntArr(Median)) or (Lo > Hi)
If SrchFor = IntArr(Median) Then 'совпадение
pStr = "Число " & SrchFor & " найдено под номером " &
Рис. 15.4. Точка останова в модуле VBA
День 15-Й. Отладка VBA-программы
513
ПРИМЕЧАНИЕ
Установить точку останова можно также, нажав клавишу <F9> или используя кнопку Тoggle Breakpoint панели инструментов Debug редактора Visual Basic. Точку останова можно также установить, щелкнув в соответствующей строке напевом поле окна Code.
Для того чтобы удалить точку останова, необходимо выполнить те же действия, что и для ее добавления: если использовать команду Toggle Breakpoint применительно к строке, которая уже содержит установленную точку останова, VBA удаляет точку останова. Для удаления всех точек останова в модуле выберите команду Debug-Clear All Breakpoints или нажмите комбинацию клавиш <Ctrl+Shift+F9>.
Инструкция Stop
Точки останова, установленные командой Toggle Breakpoint, действуют только в текущем сеансе работы. Иногда, особенно в сложных программах, отладка может продолжаться в течение нескольких рабочих сеансов. Поэтому может потребоваться установка более или менее постоянных' точек останова. Именно для этой цели в VBA предусмотрено ключевое слово Stop.
Во всех случаях, встречая инструкцию Stop, VBA прекращает выполнение программы, отображает модуль и процедуру, содержащие выполняемую в текущий момент инструкцию, и переходит в режим прерывания. При использовании инструкции Stop создается жесткая точка останова, т.е. точка останова становится неотъемлемой частью текста программы. Единственный способ удаления точки останова, созданной инструкцией Stop, — удаление этой инструкции из исходного текста программы.
Как правило, инструкцию Stop вставляют но всех случаях, когда требуется постоянная точка останова; завершив отладку программы, отредактируйте исходный код и удалите из него инструкцию Stop.
Так надо	|
...п	.————----------------------------.---------.-----------------------I
Имейте в виду, что точки останова, установленные командой Toggle Breakpoint, действуют только в теку- ; ;. щем сеансе.
^Используйте инструкцию Stop для создания точки останова, надолго остающейся в тексте программы.
Не забудьте удалить инструкции Stop, завершив разработку процедур и функций.
Прерывание программы командой Step Into
Читатели уже знают, что процедуру можно выполнить непосредственно из ее исходного текста, воспользовавшись командой Run-Run Sub или щелкнув на кнопке Run Sub в панели инструментов редактора Visual Basic.
Аналогичным образом можно запустить выполнение любой процедуры в режиме прерывания. Просто поместите курсор в процедуру, которую нужно выполнить в режиме прерывания и выберите команду Debug-Step Into. (Можно также нажать клавишу <F8> или щелкнуть на кнопке Step Into в панели инструментов Debug.) VBA перейдет в режим прерывания и сделает объявление процедуры текущей выполняемой инструкцией. Как уже отмечалось выше, в режиме прерывания текущая выполняемая ин-
574
Неделя 3
сгрукция выделяется в окне Code желтым цветом, а на поле рядом с ней отображается стрелка, как показано на рис. 15.3.
Прерывание программы нажатием клавиши
Как вы уже узнали, VBA позволяет прервать выполнение программы нажатием клавиши <Esc,> (или нажатием комбинации клавиш <Ctrl+Break>). При прерывании выполнения программы VBA отображает диалоговое окно сообщения об ошибке; это диалоговое окно показывает, что выполнение программы было прервано, и предоставляет те же возможности, что и диалоговое окно сообщения об ошибке времени выполнения- Continue. End, Debug и Help. Чтобы продолжить работу в режиме прерывания, выберите кнопку команды Debug; VBA перейдет в режим прерывания и отобрази! инструкцию, которая выполнялась в момент прерывания процедуры.
Выход из режима прерывания
Часто нужно выполнить одну часть программы в пошаговом режиме, а остальную — в обычном. В других случаях, вероятна;, потребуется завершить и режим прерывания, и выполнение программы, чтобы можно было внести изменения в исходный текст и исправить ошибки, обнаруженные во время режима прерывания.
Для выхода пт режима прерывания и продолжения выполнения программы в обычном режиме выберите команду Run-Continue. VBA выйдет из режима прерывания и продолжит выполнение программы в обычном режиме вплоть до конца программы. Если до достижения конца текста программы VBA встретит точку останова или инструкцию Stop, он снова перейдет в режим прерывания. (Выйти из режима прерывания ; продолжить выполнение программы в обычном режиме можно также, нажав клавишу <F5 > или ни ткнув на кнопке Continue в панели инструментов.)
Для выхода из режима прерывания и полного прекращения выполнения программы выберите команду Run-Reset. VBA выйдет из режима прерывания и полностью остановит выполнение программы; все переменные утратят свои значения, а все процедуры будут удалены из памяти. (Прервать режим прерывания и остановить выполнение программы можно также, щелкнув на кнопке Reset в панели инструментов VBA.)
Так надо
Помните, что в режиме прерывания команда Continue замещает команду Run Sub в меню Run. Кроме того, в режиме прерывания кнопка Continue панели инструментов замещает кнопку Run..
Помните, что не всегда можно продолжить выполнение программы - пошаговое или обычное - при переходе в режим прерывания из диалогового окна сообщения об ошибке времени выполнения. Обычнодакая ошибка бывает достаточно серьезна и препятствует выполнению остальных инструкций программы. :
Команда Step Into
Теперь, зная, как перейти в режим прерывания, можно приступить к изучению использования команды лля пошагового выполнения программы в режиме прерывания. Команда Step Into позволяет внимательно исследовать выполнение процедуры, функции или всей программы для ее отладки.
При использовании команды Step Into для пошагового выполнения программы VBA входит внутрь каждого встреченного им вызова процедуры или функции, входя в каждую выполняемую инструкцию. Пошаговый процесс, выполняемый командой
День 15-й. Отладка VBA-программы
515
Step Into, позволяет отслеживать каждую инструкцию в каждой функции или процедуре, вызываемой прямо или косвенно главной программой.
Для того чтобы использовать команду Step Into, необходимо располагать какой-либо программой для пошагового выполнения. Листинг 15.1 содержит слегка сокращенную версию процедуры BinarySearch (и поддерживающие ее процедуры) из 14-го урока. (Эта версия процедуры BinarySearch не иллюстрирует анимацией процесс поиска; она только отображает содержимое массива, чтобы пользователь мог видеть, какие значения должны быть найдены в массиве.) Процедуры, представленные в листинге 15.1, служат для демонстрации во время изучения команд отладки.
Листов 15.1. Фрагмеиш ирвграммы для деминстрации отладки
1: Option Explicit
2: Option Base 1
3:
4: Private Const ARRAY MAX As Integer = 10
5:
6: Sub DebugDemol()
7:	Const iTitle = "Бинарный поиск"
8:
9:	Dim IntArr(ARRAY_MAX) As Integer
10:	Dim	I As Integer
11:	Dim	Hi As Integer
12:	Dim	Lo As Integer
13:	Dim	Median As Integer
14:	Dim	oldSheet As String
15:	Dim	SrchFor As Variant
16:	Dim	Rsp As Integer
17:	Dim	pStr As String
18:
19: oldSheet = ActiveSheet.Name 'запоминаем текущий лист
20:	Sheets("ЛнстГ1 11).Select 'переходим на новый лист
21:
22:	Randomize Timer 'Подготовка генератора случайных чисел
23:	For I = 1 То ARRAY_MAX 'Запись случайных чисел в массив
24:	IntArr(I) = Int(Rnd * 1000)
25: Next I
26:
27:	BubbleSort IntArr 'Сортировка массива IntArrl
28:
29:	DisplayArray IntArr 'Выводим элементы массива
30:
31:	'Спрашиваем пользователя, какое число искать,
32:	'пока он не откажется
33: Do
34:	SrchFor = Application.InputBox(
35:	prompt:="Введите искомое	число:",
36:	Title:=iTitle & " Ввод",
37:	Type:=l)
38:	SrchFor = Clnt(SrchFor) 'преобразуем в целое
39:
40:	Lo = LBound(IntArr)	'начало поиска
41:	Hi = UBound(IntArr)
516
Неделя 3
42:	Do
43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88:	Median = (Lo + Hi) \ 2 'целочисленное деление If SrchFor < IntArr(Median) Then Hi = Median - 1 Else Lo = Median + 1 End If Loop Until (SrchFor = IntArr(Median)) Or (Lo > Hi) If SrchFor = IntArr(Median) Then 'совпадение pStr = "Число " & SrchFor & " найдено под номером " & Median Else 'сообщение о несовпадении pStr = "Не найдено " & SrchFor End If pStr = pStr & Chr(13) S Chr(13) & "Искать другое число?" Rsp = MsgBox(prompt:=pStr, Title:=iTitle, Buttons:=vbYesNo) Loop Until Rsp = vbNo Sheets(oldSheet).Select 'возврат на исходный лист End Sub Sub BubbleSort(xArray() As Integer) 'Сортирует массив по возрастанию Dim I As Integer, J As Integer For I = LBound(xArray) To UBound(xArray) - 1 For J = I + 1 To UBound(xArray) If xArray(I) > xArray(J) Then Swap xArray(I), xArray(J) Next J Next I End Sub Sub Swap(Il As Integer, 12 As Integer) Dim temp As Integer temp =11: Il = 12: 12 = temp End Sub Sub DisplayArray(AnyArray() As Integer) Dim k As Integer For k = LBound(AnyArray) To UBound(AnyArray) Cellsjk, 1).Value = AnyArray(k) Next k End Sub
Представленный в листинге 15.1 модуль, по существу, не отличается от представленного листинге 14.6 — некоторые инструкции и комментарии были изменены, чтобы занимать меньше места, а анимация поиска была удалена, но функционирование процедур в модуле осталось неизменным. Процедура DebugDemol работает точно так же
День 15-й. Отладка VBA-программы
517
как процедура BinarySerch из листинга 14.6: она создает массив, заполняет его случайными числами, упорядочивает его, а затем ищет в нем значения, введенные пользователем. Процедуры DisplayArray, BubbleSort и Swap работают так же, как описанные для листинга 14.6.
Единственное различие между листингами 15.1 и 14.6 в том, как сформатированы некоторые инструкции. Обратите внимание на символы двоеточия (:) в строке 79. Они разделяют инструкции VBA в одной строке — что противоположно действию символа продолжения строки. Символы двоеточия (:) указывают VBA, что в одной физической строке присутствует более одной логической инструкции. Обратите также внимание на символ продолжения строки в строке 71; использование этого символа превращает инструкцию If... Then в единую строку If... Then, исключая потребность в инструкции End If.
После ввода программы, представленной в листинге 15.1, можно приступать к использованию команды Step Into. Чтобы потренироваться в ее использовании (и установке точки останова) выполните следующие действия.
1.	Переместите курсор в строку 27 (к инструкции, вызывающей процедуру BubbleSort) и выберите команду Debug-Toggle Breakpoint, чтобы установить точку останова (или щелкните на кнопке Toggle Breakpoint в панели инструментов Debug). VBA выделит строку текста программы, указывая наличие в ней установленной точки останова. (Установка точек останова была описана в предыдущем разделе этого урока.)
2.	Запустите процедуру DebugDemol. VBA будет выполнять процедуру до тех пор, пока не достигнет инструкции с установленной точкой останова, после чего перейдет в режим прерывания. Отобразится окно Code, аналогичное показанному на рис. 15.5. К этому моменту VBA готов к выполнению вызова процедуры BubbleSort.
3.	Выберите команду Debug-Step Into (или нажмите клавишу ^F8>, или щелкните на кнопке Step Into панели инструментов Debug). VBA выполнит инструкцию, вызывающую процедуру BubbleSort, войдя в программу этой процедуры.
После выбора команды Step Into в окне Code отобрази гея текст процедуры BubbleSort; теперь VBA готов приступить к выполнению инструкций этой процедуры. (В окне Code инструкцию, которую он будет выполнять следующей, VBA выделяет желтым цветом и помечает желтой стрелкой на левом поле, как показано на рис. 15.6.)
4.	Продолжайте использовать команду Debug-Step Into. При каждом выполнении команды Step Into VBA выполняет одну инструкцию. Выполняя пошаговое выполнение программы листинга 15.1 с помощью команды Step Into, обратите внимание, что по достижении процедуры Swap в строке 72 команда также по одной выполняет инструкции этой процедуры.
Простейший и наиболее практичный способ пошагового выполнения программы -нажатие клавиши <F8> для вызова команды Step Into или щелчок на кнопке Step Into в панели инструментов Debug редактора Visual Basic.
518
Неделя 3
| Microsoft Visual Basic - Day15.xls [break] - [LictingQl (Code)}
£<^ Insert Fgmial Qebug Bun Tools £dd-lns Window Help	- llffl xj
ха-a f- « •  >  »fe’	o Lnacon
J(General)	y‘] JpebugDemol
Dim Rsp Аз Integ-zf
Dim pStr Аз S1. ring
oldSheet - ActiveSheet..Name
Sheets ("ЛИСТ1”) .Select
Randomize Timer
For I = 1 To APRAYJ4AX
IntArr(I) - Int(Rnd * 1G00)
Next i
+ Debug	___________Ej
-  Й ► <  a 'S Cl e> □ p & 5ь
ДвиЬЫ eSort IntArr ’Сортировка массива IntArrl
DisplayArray IntArr 5Выь.лдим опеменп: массива
De
и SrchFor = Application.InputBox( _
prompt’'Введите искомое число:", Title’-iTxtle & " Eton",
3,|a~j : 'Д	<	> s * " " f
Puc. 15.5. Редактор Visual Basic отображает окно Code, остановив выполнение программы в точке останова, установленной в строке 27листинга 15.1
Microsoft Visual Basic - Dayl 5.xls [break] - [ListingOI (Code)]
EcM View lnse4 T^rmai• ••Qebug -Bun Tools &dd+ns ^ntfow:JdelpM<.	-lelxf
Sa-H <«	- »	«SW-' 3 tn67.coii
'(GGnerrtl)	.j ^BubbteSorr
MsgBox (prompt :=pStr, ~	> ,, , £ SJ Q» е, ОР^Л*^,
Title : =iTitle, Butte'ii'3",.,,--vurea-ivo-f ""1..........................
Sub Bubblesort (xArrayp As Integer)
Pit. I Integer, J Ac Integer
For I = ТВ-оспа )x.Array) To UD^und (xAr ray) - 1
for J = 1 + 1 To L'Dciind I'xArray)
If >:Al ra.y (I) > xArray*.!' Then
Swap xArrayH), xArray(J)
J
Puc. 15.6. При пошаговом выполнении в режиме прерывания VBA указывает текущую инструкцию в окне Code, помечая ее стрелкой и выделяя текст цветом
День 15-Й. Отладка VBA-программы
519
Если продолжать использование команды Step Into для пошагового выполнения всех инструкций в процедурах BubbleSort и Swap, выполнение программы будет длиться до тех пор, пока со временем не произойдет возврат в главную процедуру. После пошагового выполнения всех инструкций в процедуре BubbleSort выполнение VBA возвращается к строке 29. Если снова вызвать команду Step Into, можно перейти к пошаговому выполнению процедуры DisplayArray, вызываемой в строке 29.
Устав от пошагового выполнения программы, представленной в листинге 15.1, можно прекратить выполнение программы, выбрав команду Run-Reset или щелкнув на кнопке команды Reset в панели инструментов редактора Visual Basic.
Если вы хотите завершить выполнение программы листинга 15.1 в обычном режиме, выберите команду Run-Continue или щелкните на кнопке команды Continue в панели инструментов редактора Visual Basic.
Команда Step Over
Как видно из предыдущего упражнения, пошаговое выполнение каждой процедуры программы может быть достаточно трудоемким процессом — и это не всегда имеет смысл. Например, если вы входите в процедуру BubbleSort, поскольку подозреваете в ней наличие ошибки, но уверены, что процедура Swap работает успешно, пошаговое выполнение всех инструкций в процедуре Swap при каждом пошаговом выполнении процедуры BubbleSort не желательно (да и не требуется).
К счастью, отладчик VBA имеет команду Step Over, дополняющую действие команды Step Into. При использовании команды отладки Step Over в инструкции, вызывающей процедуру или определенную пользователем функцию, VBA выполняет эту процедуру не в пошаговом, а в обычном режиме и возобновляет пошаговое выполнение с первой инструкции, следующей за вызовом этой процедуры. Обход вызовов процедур помогает сосредоточиться на отладке инструкций в текущей процедуре, не тратя время на пошаговое выполнение инструкций в подчиненных процедурах, которые наверняка работают правильно.
Для того чтобы лучше ознакомиться со способом использования команды Step Over, выполните следующие действия.
1.	Если вы еще не установили точку останова в строке 27 листинга 15.1, сделайте это сейчас.
2.	Выполните процедуру DebugDemol. Как и в предыдущем упражнении, VBA выполняет ее до тех пор, пока не достигнет инструкции, содержащей точку останова, после чего переходит в режим прерывания и отображает окно Code, аналогичное уже показанному на рис. 15.5.
3.	Выберите команду Debug-Step Over (можно также нажать комбинацию клавиш <Shift+F8> или использовать кнопку команды Step Over в панели инструментов Debug).
Как только команда Step Over выбрана, VBA выполняет инструкцию, которая вызывает процедуру BubbleSort, выполняя ее полностью (включая процедуры, вызываемые процедурой BubbleSort) в нормальном режиме без пошагового выполнения каких-либо содержащихся в ней инструкций. VBA возобновляет пошаговое выполнение с первой инструкции в DebugDemol, следующей за той, которая вызвала процедуру BubbleSort: со строки 29 листинга 15.1.
4.	Продолжайте использовать команду Debug-Step Over для выполнения инструкций. VBA выполняет одну инструкцию при каждом вызове команды Step Over, за исключением случая, когда инструкция вызывает одну из процедур или
520
Неделя 3
функций. Продолжая пошаговое выполнение листинга 15.1 посредством команды Step Into, обратите внимание, что по достижении вызова процедуры DisplayArray в строке 29 команда Step Over приводит к тому, что VBA выполняет процедуру DisplayArray без пошагового выполнения каких-либо ее инструкций; команда “перешагивает” через инструкции в вызванной процедуре.
Простейший и наиболее практичный способ использования команды Step Over -нажатие клавиши <Shift+F8> для вызова команды Step Over или щелчок на кнопке Step Over в панели инструментов Debug.
Устав от пошагового выполнения программы листинга 15.1 посредством команды Step Over, можно возобновить выполнение программы в обычном режиме, выбрав команду Run-Continue или щелкнув на кнопке команды Continue в панели инструментов редактора Visual Basic. Для прекращения выполнения всей программы выберите команду Run-Reset или щелкните на кнопке команды Reset в панели инструментов редактора Visual Basic.
Команды Step Out u Run To Cursor
Иногда может оказаться, что пошаговое выполнение процедуры было запущено по ошибке. Это может быть следствием случайного использования команды Step Into вместо команды Step Over. Или же оказывается, что процедура, выполняемая в данный момент в пошаговом режиме, работает правильно и нужно быстро вернуться к пошаговому выполнению процедуры, вызвавшей текущую процедуру. Редактор Visual Basic имеет команду Step Out, которая помогает избежать потери времени на ненужное пошаговое выполнение процедур.
При использовании команды Debug-Step Out (или одного из ее эквивалентов: <Ctrl+Shift+F8> или кнопки Step Out в панели инструментов Debug) VBA выполняет оставшуюся часть текущей процедуры или функции в обычном режиме и возвращается к режиму прерывания с первой инструкции, следующей за той, которая вызвала выполняемую в текущий момент процедуру. Например, если вы выполняете пошаговое выполнение процедуры Bubblesort в листинге 15.1 и используете команду Step Out, VBA выполняет оставшуюся часть процедуры BubbleSort (и любых вызванных ею процедур) в обычном режиме, возвращаясь в режим прерывания в строке 29: первой инструкции, следующей за той, что вызвала процедуру BubbleSort.
Иногда требуется пошаговое выполнение процедуры, но при этом нежелательно пошаговое выполнение цикла — особенно если цикл выполняется сотни раз. На этот случай редактор Visual Basic предлагает команду Run То Cursor, помогающую пропустить большие циклы или другие блоки инструкций, которые наверняка работают правильно. Это позволяет не тратить лишнее время на пошаговое выполнение программы.
Чтобы использовать команду Run То Cursor, поместите курсор в любую инструкцию, которая будет выполняться после текущей. Затем выберите команду Run То Cursor (или нажмите комбинацию клавиш <Ctrl+F8>. VBA выполнит в обычном режиме все инструкции от текущей инструкции (указанной стрелкой на левом поле окна Code) до текущей позиции курсора, а затем снова перейдет в режим прерывания.
День 15-й. Отладка VBA-программы
521
Как пользоваться контрольными значениями переменных
Само по себе пошаговое выполнение программы ле обеспечивает всех средств, необходимых для выявления и исправления ошибок в программах. Используя пошаговое выполнение, можно, например, выяснить, что VBA выполняет не ту ветвь инструкции If... Then... Else. В этой ситуации ясно, что логическое выражение в инструкции If дает неверное значение. Но как выяснить, связана ли проблема с самим выражением или с неверным значением в одной из переменных, используемых в выражении?
Контрольные значения переменных позволяют проверять содержимое переменных во время выполнения программы. Используя контрольные, значения переменных, окно Watch Window и режим прерывания, можно отслеживать значения, генерируемые программой во время ее выполнения. Например, пытаясь выяснить, что именно неверно в логическом выражении инструкции If... Then... Else, можно проследить значения любых переменных в логическом выражении, а также значение самого логического выражения. Отслеживая эти переменные и результат выражения, можно будет установить, почему инструкция If... Then... Else не дает ожидаемых результатов.
Как правило, выполняется контроль переменных или выражений, значения которых связаны с логическими ошибками или ошибками времени выполнения. Вероятно, потребуется отслеживание всех переменных в любом выражении, дающем ошибку времени выполнения — например, деление на нуль. — чтобы выяснить, какая переменная (переменные) ответственна за ошибку времени выполнения и в какой момент программа получает неверное значение. Подобная информация исключительно важна при выявлении и исправлении ошибок в программах. Для отслеживания содержимого переменных или создаваемых выражением значений редактор Visua! Basic использует окно Watch Window.
Редактор Visual Basic не отображает окно Watch Window аатоматически. Его необходимо отобразить вручную, выбрав команду View VW;J' Window или щелкнув на кнопке Watch Window в панели инструментов Debug
На рис. 15.7 вы видите окно Watch Window с песготькими контрольными значениями переменных и одним контрольным выражением. Обратите внимание, что в окне Watch Window отображается имя выражения, его текущее значение, тип выражения и контекст выражения. Само выражение может быть именем отдельной переменной или любым выражением программы (По умолчанию окно Watch Window отображается прижатым к нижнему краю окна редактора Visual Basic.)
На рис. 15.7 первые четыре элемента, приведенные в окне Watch Window. — отдельные переменные; последний элемент — выражение. В случае отдельной переменной в столбце Value отображается значение, хранящееся в переменной в текущий момент; в случае выражений в столбце Value отображается результат выражения, вычисляемый при использовании текущих значений переменных. Если переменная в окне Watch Window в данный момент не находится в области видимости, в столбце 'ЛДие отображается текст <0ut of context*. Для элеменюв массивов с недопустимыми значениями индексов в столбце Value окна Watch Window отображается ickct <subscript out of range* (индекс вне допустимого диапазона;.
522
Неделя 3
ЙMicrosoft Visual Basic - Dayl 5.xls [break] - [ListingO! (Ccidu)]
Fde £drt ^?ew insert Format Qebug gun loots АсШ/rs ^ndctw tfeip
[(General)
SrchFor = Applitaticn.InputBox •( _
prompt: --"Введите искомое число:
Title:=iTitle & ” Ввод
Type:=1} SrchFor = CInt(Src
| *• Debug
LO = LLOUnd (IntArr) ‘HUtSjTO пояс Hi = PBound;IntArr!
Do
Median =• (Lo + Hi) \ 2 ’u.tct c-c.
If SrchFor < IntArr(Median) Then Hi - Median - 1
Eire
Lo - Median + 1
IWetches
j Expression
। Hi
tValugi
W
SE
IntArriMsd ।
SrchFor
SrchFor < 1ntAn(Median) True
Integer Integer Vanant/lrileger
jrj [DebugDernol
В ; "1 C> «Я □ P Й •. > %=
Variarrt/Boolean
ListtnqCH.DebugDemol
I Context	i
ListingO! DebugDemol bstingOl.DebugOemol ListingOI DebugDemol ListingO! DebugDemo!


Puc. 15.7 Окно Watch Window с несколькими переменными и выражениями в нем
Столбец Туре окна Watch Window сообщает тип данных контрольного выражения. Дая переменных Variant -- таких, как переменная SrchFor, отображенная на рис. 15.7, — столбец Туре сообщает, какой именно тин данных хранится в переменной Variant. На рис. 15.7 показано, что переменной SrchFor является Variant, которая в текущий момент хранит данные типа Integer.
Столбец Context окна Watch Window показывает, в каком модуле и процедуре находится контрольное значение переменной или выражение. Все контрольные значения переменных в окне Watch Window, показанном на рис. 15.7, находятся в одном и том же модуле и получены из одной и гой же процедуры.
/МЕШКЕ
Контрольные выражения действуют только в течение текущего сеанса работы.
Как добавить контрольное выражение
Для того чтобы получить возможность следить за контрольным значением переменной или контрольным выражением, оно должно быть добавлено в окно Watch Window. В список окна Watch Window можно добавить любую переменную или выражение и: исходного текста, выполнив следующие действия.
1,	В окне Code выберите текст, содержащий переменную или выражение, которое нужно добавить в окно Watch Window. Для добавления имени отдельной переменной достаточно поместить курсор где-либо внутри имени переменной.
День 15-й. Отладка VBA-программы
523
2.	Выберите команду Debug-Add Watch. VBA отобразит диалоговое окно Add Watch, показанное на рис. 15.8.
3.	Заполните поля диалогового окна (описанные далее) и щелкните на кнопке ОК. VBA закроет диалоговое окно Add Watch и добавит выбранное контрольное значение переменной или контрольное выражение в окно Watch Window.
Рис. 15.8. Окно Add Watch с переменной Median, добавляемой в список контрольных значений
Диалоговое окно Add Watch имеет следующие управляющие элементы для добавления контрольных значений.
•	Текстовое поле Expression позволяет вводить переменную или выражение, которое нужно отслеживать. В случае выделения текста в окне программы перед открытием диалогового окна Add Watch выделенный текст вносится в поле автоматически. Необходимо ввести допустимое имя переменной или выражение VBA.
•	Группа Context содержит раскрывающиеся списки Procedure и Module. По умолчанию VBA использует текущие модуль и процедуру. Можно указать контрольное значение или выражение из другого модуля или процедуры, выбрав требуемый модуль в раскрывающемся списке Module и требуемую процедуру — в раскрывающемся списке Procedure.
•	Группа Watch Туре (Тип контрольного значения) содержит три переключателя: Watch Expression, Break When Value is True (Останов, если значение выражения истинно), и Break When Value Changes (Останов при изменении значения выражения). Эти переключатели используются для выдачи редактору Visual Basic специальных инструкций о требуемом способе обработки контрольного значения переменной или выражения. При выборе переключателя Watch Expression VBA просто добавляет контрольное значение переменной или контрольное выражение в окно Watch Window. При выборе переключателя Break When Value is True VBA переходит в режим прерывания во всех случаях, когда контрольное значение переменной или выражения становится равным True. Переключатель Break When Value Changes указывает VBA перейти в режим прерывания во всех случаях изменения контрольного значения переменной или выражения.
Последние два переключателя в группе Watch Type (Break When Value is True и Break When Value Changes) используются для сокращения времени, затрачиваемого на пошаговое выполнение программы. Например, при попытке найти источник ошибки
524
Неделя 3
деления на нуль выясняется, что одна из переменных в выражении деления принимает нулевое значение, вызывая ошибку времени выполнения. Во избежание этой ошибки необходимо найти точку программы, в которой этой переменной присваивается нулевое значение, — или выяснить, присваивалось ли этой переменной какое-либо значение. Для ускорения этого процесса проследите подозрительную переменную в режиме Break When Value Changes. Затем можно было бы выполнить программу в обычном режиме, переходя в режим прерывания и просматривая окно Watch Window только при изменении значения подозрительной переменной.
Для приобретения некоторого навыка в установке и использовании контрольных значений переменных выполните следующие действия.
1.	Удалите все точки останова из листинга 15.1, выбрав команду Debug-Clear АН Breakpoints (Снять все точки останова).
2.	Установите точку останова в строке 40 листинга 15.1. (Установка точек останова была описана ранее в этом уроке.)
3.	Выполните процедуру DebugDemol. VBA будет выполнять процедуру, пока не достигнет точки останова. Затем он перейдет в режим прерывания и отобразит окно программы.
4.	Выберите команду View-Watch Window или щелкните на кнопке Watch Window в панели инструментов Debug для отображения окна Watch Window, если оно еще не видимо.
5.	Выберите команду Debug-Add Watch (Добавить контрольное значение). VBA отобразит диалоговое окно Add Watch, показанное на рис. 15.8.
6.	В текстовое поле Expression (Выражение) введите имя переменной Lo и щелкните на кнопке OK. VBA закроет диалоговое окно Add Watch и добавит переменную Lo в окно Watch Window.
7.	Повторите пп.5 и 6, чтобы добавить в окно Watch Window переменные Hi и Median и выражение IntArr(Median).
8.	Используйте команду Step Over (описанную в предыдущем разделе этого урока) для пошагового выполнения цикла Do в строках 42-49. Обратите внимание на изменения контрольных значений переменных в окне Watch Window.
На рис. 15.7 показано окно Watch Window, аналогичное отображаемому при выполнении приведенных выше инструкций. Если хотите, можете осуществить пошаговое выполнение последующих повторений цикла Do, обращая внимание на изменение значений переменных в окне Watch Window во время выполнения цикла.
Редактирование контрольного выражения
В некоторых случаях необходимо отредактировать контрольное выражение. Может потребоваться всего лишь изменение имени переменной на аналогичное во избежание ввода совершенно нового контрольного значения переменной или по какой-либо иной причине. VBA позволяет редактировать выражения в окне Watch Window.
Для изменения контрольного выражения выполните следующие действия.
1.	Выберите команду View-Watch Window, чтобы отобразить окно Watch Window, если оно еще не открыто.
2.	В окне Watch Window выберите контрольное выражение, которое нужно изменить.
3.	Выберите команду Debug-Edit Watch (Изменить контрольное значение). Эта команда вызывает диалоговое окно Edit Watch, которое по существу идентично диалоговому окну Add Watch, уже показанному на рис. 15.8.
День 15-й. Отладка VBA-программы
525
4.	Выполните все необходимые изменения в имени контрольного выражения, контексте или типе контрольного значения.
5.	Щелкните на кнопке ОК, чтобы закрыть диалоговое окно Edit Watch.
Удаление контрольного выражения
В ходе работы с контрольными значениями переменных и выражений список окна Watch Window разрастается. В определенный момент, вероятно, станет ясно, что в действительности требуются не все усыновленные контрольные выражения и некоторые из них нужно удалить из окна Watch Window. Это можно сделать одним из двух способов.
•	В окне Watch Window выберите контрольное выражение, которое нужно удалить, и нажмите клавишу <Del>. VBA бет подтверждения удалит выбранное контрольное выражение из окна Watch Window
•	Выберите контрольное выражение, которое хотите удали гь. а затем выберите команду Debug- Edit Watch. VBA отобразит диалоговое окно Edit Watch. Выберите в нем командную кнопку Delete. VBA удалит выбранное контрольное выражение из панели Watch Window.
Быстрая проверка контрольного значения
Как вы только что узнали, диалоговые окна Add Watch (Добавление контрольного значения) и Edit Watch (Изменение контрольного значения) позволяют определять для контрольных значений переменных специальные условия, такие как переходить ли в режим прерывания при изменении контрольного значения переменной. Однако в большинстве случаев, вероятно, будет требования просто добавление переменной в окно Watch Window без установки каких-либо специальных параметров. Поэтому редактор Visual Basic предоставляет возможность более быструю альтернативу использованию диалогового окна Add Watch, когда определение специальных контрольных параметров не требуется. (Параметры для контрольных значений переменных и выражений всегда можно установить или изменить, волю,плевавшись диалоговым окном Edit Watch, описанным ранее в этом уроке )
Чтобы использовать диалоговое окно Watch Window, выполните следующие действия.
1.	В окне Code поместите курсор на персменнею или выберите выражение, которое хотите проконтролировать.
2.	Выберите команду Debug- Watch Window (можно также нажать комбинацию клавиш <Shift+F9> или щелкнуть на кнопке команды Watch Window в панели инструментов Debug). VBA отобразит диалоговое окно Watch Window, пример которого приведен на рис. 15.9.
Quick Watch
UWily ggii;)'i--  э| ......гэй
.... Cancel
will то	< - ftp I
Puc. 15.9. Диалоговое окно Watch Window, отображающее текущий контекст и значение переменной Mcdicir
526
Неделя 3
3.	Если нужно добавить эту переменную или выражение в окно Watch Window, щелкните на кнопке Добавить в диалоговом окне Watch Window. VBA добавит переменную или выражение в окно Watch Window в качестве стандартного контрольного выражения, с контекстом, показанным в диалоговом окне Watch Window.
4.	Чтобы закрыть диалоговое окно Watch Window без добавления выбранной переменной или выражения в окно Watch Window, щелкните на кнопке Cancel.
Подсказки значении данных
Иногда может требоваться только взглянуть на значение переменной, не добавляя его в окно Watch Window. Например, во время пошагового выполнения программы может потребоваться взглянуть на текущее значение счетчика цикла, не создавая контрольного значения дтя него.
Бросить краткий взгляд на значения переменных можно, используя функцию Auto Data Tips (Подсказки значений данных). Перейдя в режиме прерывания, содержимое переменной или выражения можно просмотреть, задержав указатель мыши над элементом, значение которого вас интересует. Примерно через полсекунды VBA отобразит всплывающее окно, в котором отображается значение этой переменной. На рис. 15.10 показан листинг 15.1. выполняемый в режиме прерывания. Указатель мыши (в форме 1-курсора) был задержан нал выражением UBoundfIntArr); под выражением появилось всплывающее окно, отображающее значение выражения.
Microsoft Visual Basic - Dayl 5 xls [break] - (ListingOI(Code)]
-£dft View Insert; .Format. Qebug Bun lools Add-Ins ytfndow
* £3	' " A -' ‘ ►	 IS Ы d? W ’’' 0 Ln 45, Col 29
t
ItGeneral)"
[Ke bugDernо 1
- Application.InputEox( prompt:-"Введите искомое число:"
Ввод"(
1
iTitle
Debug
И

Hi - GBound(IntArr?
Do 1 Li В о u n dfiniArn
If SrchFor < IntArr(Median) Then
H: ~ Medisn - 1
Lo = Median + i
Expresston
W
H;
iCArtWeoani
[vaiue
I Context___________
ListingOI DebugDemol ListingOl DebugDemol LislingOI DebugDemol ListingO! DebugDemol LisnngOI DebugDemol
SrchFor
SrchFor < IntArr(Median)
34
Шж

№
Рис. 15.10. Использование функиии Auto Data Tips для просмотра значения выражения
День 15-й. Отладка VBA-программы
527
Функция подсказки значений по умолчанию включена. Если подсказка значений данных не появляется, в редакторе Visual Basic выберите команду Tools-Options (Сервис-Параметры) для отображения диалогового окна Options. Затем щелкните на вкладке Editor (Редактор), чтобы отобразилась страница параметров редактирования редактора Visual Basic. Убедитесь, что флажок Auto Data Tips установлен. (Функцию подсказки значений данных можно отключить, сбросив этот флажок.)
Окно локальных переменных
Окно Locals Window (Локальные переменные), хоть и не является частью окна Watch Window или системы установки контрольных значений переменных и выражений, выполняет связанную с ними функцию. Окно Locals Window отображает все переменные, являющиеся локальными (или включающими в себя активный диапазон значений) для выполняемой в текущий момент процедуры или функции. Например, на рис. 15.11 отображены все локальные переменные процедуры BubbleSort листинга 15.1.
Окно Locals Window содержит три столбца: Expression (имя переменной или объекта), Value (текущее значение переменной или свойства объекта) и Туре (тип данных или объекта выражения). Обратите внимание на прямоугольники слева от записей ListingOI и хАггау в окне Locals Window на рис. 15.11. Подобно прямоугольникам во всех древообразных списках Windows, они указывают на раскрытые (если прямоугольник содержит знак минуса) или раскрывающиеся (если прямоугольник содержит знак плюса) записи в окне Locals Window. Щелчок на знаке плюса раскрывает структуру дерева свойств и значений объекта; щелчок на знаке минуса закрывает и скрывает структуру дерева. Например, на рис. 15.11 запись ListingOI отображает список всех переменных и констант уровня модуля. Щелчок на символе слева от записи хАггау отображает список всех элементов массива и значение каждого элемента. Окно Locals Window используется для просмотра значений и структуры переменных и объектов в выполняющейся в текущий момент программы, функции или модуля.
Рис. 15.11. Окно Locals Window используется для просмотра значений выражений и объектов в выполняющейся в настоящий момент функции, процедуре или модуле
Последовательность вызова процеддр
Часто нужно точно выяснить цепочку или последовательность вызова процедур, ведущую к конкретной инструкции, которая генерирует ошибки. Последовательность вызова процедур важна потому, что часто она играет важную роль при определении источника ошибки. Например, можно найти инструкцию, приводящую к делению на нуль, но при этом неизвестно, почему одна из переменных в инструкции содержит нуль. Знание точной последовательности вызова процедур может помочь в определе
528
Неделя 3
нии, например того, что процедура была вызвана с отсутствующим необязательным аргументом или с неверным значением в обязательном аргументе.
VBA позволяет просмотреть цепочку вызова процедур посредством диалогового окна Call Stack (Стек вызовов). Для просмотра цепочки вызова процедур выберите команду View-Call Stack, щелкните на кнопке команды Call Stack в панели инструментов Debug или нажмите комбинацию клавиш <Ctrl+L>. Редактор Visual Basic отобразит диалоговое окно Call Stack, показанное на рис. 15.12 (конкретная последовательность вызова процедур будет зависеть от действительно выполняемой программы в момент открытия диалогового окна Call Stack). Как видно на рис. 15.12, в диалоговом окне Call Stack приводится последовательность вызова процедур и функций, ведущая к текущей процедуре или функции. Текущая процедура или функция отображается в верхней строке списка, а каждый последующий пункт списка представляет процедуру или функцию, вызвавшую процедуру или функцию, приведенную в списке над ним.
Рис. 15.12. Диалоговое окно Call Stack, отображающее последовательность вызова процедур, ведущих к процедуре Swap в листинге 15.1
Для открытия диалогового окна Call Stack, показанного на рис. 15.12, выполните следующие действия.
1.	Выберите команду Debug-Clear All Breakpoints, чтобы удалить из листинга 15.1 все точки останова.
2.	Установите точку останова в строке 79 (внутри процедуры Swap).
3.	Выполните процедуру DebugDemol. VBA будет выполнять процедуру до тех пор, пока не достигнет точки останова в строке 79, после чего перейдет в режим прерывания.
4.	Для отображения диалогового окна, показанного на рис. 15.12, выберите команду View-Call Stack, нажмите комбинацию клавиш <Ctrl+L> или щелкните на кнопке команды Call Stack в панели инструментов Debug. В списке этого диалогового окна показаны все вызовы процедур, ведущие к процедуре Swap: процедура Swap была вызвана процедурой Bubblesort, которая, в свою очередь, была вызвана процедурой DebugDemol.
Обратите внимание, что диалоговое окно Call Stack содержит две кнопки: Show (Показать) и Close (Закрыть). Кнопка Close просто закрывает диалоговое окно Call Stack. А вот кнопка Show выполняет очень полезную функцию.
Кнопка Show в диалоговом окне Call Stack используется для просмотра инструкции, содержащей вызов конкретной процедуры. Ее можно использовать для обратного отслеживания значений аргументов, переданных конкретной процедуре. Предположим, например, что мы пытаемся избавиться от ошибки деления на нуль и обнаруживаем, что неверное значение было передано в текущую процедуру в качестве одного из аргументов. Откройте диалоговое окно Call Stack, выберите имя процедуры, вызвавшей текущую процедуру, а затем щелкните на кнопке Show. VBA закроет диало
День 15-Й. Отладка VBA-программы
529
говое окно Call Stack в окне Code и отобразит инструкцию, вызвавшую текущую процедуру. Кнопку Show и диалоговое окно Call Stack можно использовать для просмотра всей цепочки вызова процедуры.
Как пользоваться окном отладке
Иногда даже совместного использования пошагового выполнения, контрольных выражений и трассировки вызова процедур окажется недостаточно для отслеживания ошибки в программе. Редактор Visual Basic предоставляет еще одно крайнее средство для обнаружения недостатков в тексте программы.
Окно Immediate Window (Проверка) редактора Visual Basic — это редактор, позволяющий просматривать значения переменных и выражений, выполнять вычисления “на лету”, изменять значения переменных и проверять результаты выполнения функций — все это делается в то время, пока выполнение VBA приостановлено в режиме прерывания. Для просмотра или использования окна Immediate Window выберите команду View-Immediate Window, нажмите комбинацию клавиш <Ctrl+G> или щелкните на кнопке Immediate Window в панели инструментов Debug; VBA отобразит окно immediate Window.
Команда Print используется для отображения информации в окне immediate Window. Команда Print используется в окне Immediate Window совместно со списком переменных или выражений. Сразу после ввода этой команды и нажатия клавиши <Enter> VBA вычисляет выражения, введенные с командой Print, и отображает их результаты под командой. На рис. 15.13 приведен пример сеанса использования окна Immediate Window.
Ж Immediate
□ f х
о
? temp
443
print temp
443
J
4
Puc. 15.13. Сеанс иегшыимич окна Immediate, в котором отображается вывод команд Print
Использование инструкции Debuy.Print
Обеспечиваемые редактором Visual Basic возможности отладки осуществляются с помощью объекта Debug. Объект Debug VBA имеет только один метод: Print. Метод Debug.Print используется для отображения информации в окне Immediate Window во время выполнения программы. Чтобы использовать этот метод, необходимо создать вызывающие его VBA-инструкции.
Обычно метод Debug.Print используется в комбинации с инструкцией Stop, описанной выше в этой главе. Комбинация этих двух элементов позволяет выполнять программы в обычном режиме, генерируя при этом отладочную информацию, аналогичную получаемой посредством контроля значений переменных или выражений. Как правило, в программу включают одну или несколько инструкцией, вызывающих метод Debug.Print и отображающих значения определенных переменных или выражений в окне Immediate Window. Хотя окна Immediate Window при обычном выполнении программы и не видно, VBA будет переходить в режим прерывания при каждой встрече с инструкцией Stop, де
530
Неделя 3
лая окно Immediate Window доступным. После этого в нем можно будет просмотреть любой вывод, генерируемый в программе инструкциями Debug.Print. Используя эту технологию, можно сэкономить значительное время отладки, избегнув пошагового выполнения программы. После просмотра информации в окне Immediate Window можно возобновить выполнение программы в обычном режиме.
Листинг 15.2 содержит процедуру и модуль DebugDemo2.
Листинг 15.2 Процедура OebugDemoZ, иддтстрирутщац исиользование метода Dehug.Prlut
1:	Option Explicit
2:	Option Base 1
4:	Private Const ARRAY_MAX As Integer =10
5:
6:	Sub DebugDemol()
Const iTitle = "Бинарный поиск"
8:
9:	Dire IntArr(ARRAY_MAX) As Integer
10:	Dire	I As Integer
11:	Dim	Hi As Integer
12:	Dim	Lo As Integer
13:	Dim	Median As Integer
14:	Dim	oldSheet As String
15:	Dim	SrchFor As Variant
16:	Dim	Rsp As Integer
17:	Dim	pStr As String
18:
19:	oldSheet = ActiveSheet.Name 'запоминаем текущий лист
20:	Sheets("Лист1").Select 'переходим на новый лист
22:	Randomize Timer 'Подготовка генератора случайных чисел
23:	For I = 1 То ARRAY__MAX 'Запись случайных чисел в массив
24:	IntArr(I) = Int(P.nd * 1000)
25:	Next I
27:	BubbleSort IntArr 'Сортировка массива IntArrl
18:
29:	DisplayArray IntArr 'Выводим элементы массива
38:
31:	'Спрашиваем пользователя, какое число искать,
32:	'пока он яе откажется
33:	Do
34:	SrchFor = Application.InputBox(
35:	prompt:^"Введите искомое	число:”,
36:	Title:=iTitle & " Ввод",
37:	Туре:=1)
38:	SrchFor ~ CInt(SrchFor) 'преобразуем в целое
39:
40:	I,o = LBoundfIntArr;	'качало поиска
41:	Hi = UBound(IntArr)
42:
43:	'выводим имена переменных в окно отладки
44:	Debug.Print "IntArr(Median) SrchFor", "Lo", "Hi",
45:	’'Median”
46:
День 15-й. Отладка VBA-программы
531
47:	Do
48:	Median = (Lo + Hi) \ 2 'целочисленное деление
49:
50:	’выводим значения переменных в окно отладки
51:	Debug.Print IntArr(Median), SrchFor, Lo, Hi, Median
52:
53:	If SrchFor < IntArr(Median) Then
54:	Hi = Median - 1
55:	Else
56:	Lo = Median + 1
57:	End If
58:	Loop Until (SrchFor = IntArr(Median)) Or (Lo > Hi)
59:
60:	Stop 'переход в режим останова
61:
62:	If SrchFor = IntArr(Median) Then 'совпадение
63:	pStr = "Число " S SrchFor & " найдено под номером " &
64:	Median
65:	Else 'сообщение о несовпадении
66:	pStr = "Не найдено " & SrchFor
67:	End If
6S:
69:	pStr = pStr & Chr(13) & Chr(13) &
70:	"Искать другое число?"
71:	Rsp = MsgBox)prompt:=pStr,
72:	Title:=iTitle, Buttons:=vbYesNo)
73:	Loop Until Rsp = vbNo
74:	Sheets(oldSheet).Select 'возврат на исходный лист
75:	End Sub
76:
77:	Sub BubbleSort(xArray)) As Integer)
78:	'Сортирует массив по возрастанию
79:	Dim I As Integer, J As Integer
80:	For I = LBound(xArray) To UBound(xArray) - 1
81:	For J = I + 1 To UBound(xArray)
82:	If xArray(I) > xArray(J) Then _
83:	Swap xArray(I), xArray(J)
84:	Next J
85:	Next I
86:	End Sub
87:
88:	Sub Swap(Il As Integer, 12 As Integer)
89:	Dim temp As Integer
90:	temp =11: Il = 12: 12 = temp
91:	End Sub
92:
93:	Sub DisplayArray(AnyArray)) As Integer)
94:	Dim k As Integer
95:
96:	For k = LBound(AnyArray) To UBound(AnyArray)
97:	Cellsfk, 1).Value = AnyArray(k)
98:	Next k
99:	End Sub
532
Неделя 3
Этот листинг аналогичен листингу 15.1, но в него включено несколько инструкций Debug.Print и Stop. Инструкция Debug.Print в строке 44 была добавлена с целью создания заголовка для последовательных значений, отображаемых в окне Immediate Window. Инструкция Debug.Print в строке 51 была добавлена для отображения значений переменных Median, Hi, Lo и SrchFor и элемента массива IntArr, индексированного переменной Median. И наконец, инструкция Stop в строке 60 включена, чтобы VBA перешел в режим прерывания и позволил увидеть значения, отображаемые инструкциями Debug.Print. На рис. 15.14 показано окно Immediate Window по завершении сеансом проверки процедуры DebugDemo2 одного поиска.
1 ОШ Immediate				
443				a]
IntArr(Median)	SrchFor	Lo	Hi	Median №
690	66	1	10	5
407	66	1	4	2
251 1L_J	66	1	1	1 Ж »1
Рис. 15.14. Окно Immediate, отображающее вывод инструкций Debug.print в листинге 15.2
При выполнении процедуры DebugDemo2 VBA выполняет процедуру в обычном режиме, пока не встретит инструкцию Stop в строке 60. После этого он переходит в режим прерывания. Для отображения окна Immediate Window может потребоваться нажатие комбинации клавиш <Ctrl+G>. Конкретный список отображаемых значений будет зависеть от действительного набора случайных чисел, генерируемых процедурой DebugDemo2. и от числа, вводимого пользователем для поиска.
После просмотра содержимого окна Immediate Window, продолжите выполнение процедуры DebugDemo2, нажав клавишу <F5> или щелкнув на команде Continue в панели инструментов редактора Visual Basic. При каждом поиске числа в списке, встретив инструкцию Stop, процедура DebugDemo2 будет переходить в режим прерывания и в окне Immediate Window будут отображаться новые значения вывода (эти значения генерируются циклом Do в строках 47-58).
Резюме
В этом уроке освещены различные функции отладки, предоставляемые VBA. Читатели узнали, что существует четыре основных типов ошибок: синтаксические, ошибки компиляции, ошибки времени выполнения и логические. Вы познакомились также с типичными причинами возникновения этих типов ошибок.
На этом уроке было рассказано, что собой представляет режим прерывания VBA и как его использовать в сочетании с командами Step Into, Step Over, Step Out и Run To Cursor для приостановки выполнения программы и последующего ее пошагового выполнения по одной инструкции или процедуре. Читатели познакомились с окнами Watch Window, Locals Window и Immediate Window.
В ходе урока было показано, как устанавливать и удалять точки останова и как их использовать. Вы узнали, как контролировать конкретные переменные или выражения в окне Watch Window и как добавлять, редактировать или удалять контрольные значения переменных. Кроме того, теперь вы знаете, как использовать инструкцию Stop в качестве жесткой точки останова.
День 15-Й. Отладка VBA-программы	533
С помощью диалогового окна Call Stack вы научились отслеживать цепочку вызова процедур. На уроке были рассмотрены также способы работы с окном Immediate Window. В ходе обучения было показано, как в VBA-программе использовать метод Debug.Print для отображения информации в окне Immediate Window во время выполнения программы.
Вопросы о ответы
Существует ли какой либо способ включения или отключения инструкций Debug.Print, чтобы не приходилось редактировать всю программу или процедуру, если окажется, что она работает правильно?
Нет. по крайней мере непосредственно. Однако существует непрямой способ включения и отключения инструкций Debug.Print: можно указать специальные директивы компилятора, которые будут выполнять так называемую условную компиляцию. Посредством условной компиляции можно управлять тем, какую часть программы действительно компилирует компилятор VBA. Условные директивы компилятора работают подобно объявлению констант и инструкций If VBA, с которыми вы уже знакомы, но им предшествует символ фунта (#), отличающий их от обычного текста VBA.
Используйте директиву компилятора #Const для объявления флага, указывающего подлежащий компиляции текст программы. Затем используйте директиву компилятора Then... #Else для управления тем, какая часть программы компилируется. Следующий фрагмент программы демонстрирует использование этой технологии.
flConst DEBUGGING = True
#If DEBUGGING Then
Debug.Print "Dbugging is on."
#End If
Если константа DEBUGGING, определенная в объявлении ifConst имеет значение False, инструкция Debug.Print не только не будет выполняться, но даже не будет компилироваться. Каким бы ни было значение константы DEBUGGING, оно занимает память только во время компиляции программы — оно не является частью VBA-программы и никогда не занимает ресурсы компьютера во время действительного выполнения программы.
Можно также установить набор констант компиляции, введя их в текстовое поле Conditional Compilation Arguments (Аргументы условной компиляции) вкладки General (Общие) диалогового окна Project Properties (Свойства проекта). Окно Project Properties можно отобразить, выбрав проект в проводнике проектов, а затем выбрав команду Tools-Project Properties. При вводе констант условной компиляции в окне Project Properties можно вводить только целые значения констант условной компиляции. Для установки значения константы DEBUGGING из предыдущего примера в текстовое поле Conditional Compilation Arguments нужно было бы ввести DEBUGGING = 1 для указания того, что константа DEBUGGING имеет значение True; для указания значения False используйте 0.
Существует ли инструкция, отменяющая действие инструкции Stop?
Нет. Для возобновления выполнения программы необходимо использовать команду Run-Continue. Однако можно попользовать условную компиляцию (как описано в предшествующем пункте) ,тля условной компиляции инструкции Stop в программе.
534
Неделя 3
Коллоквиум
Ответы приведены в приложении.
Тест
1.	Где помешают точку останова в процедуре или функции?
2.	Каким образом несколько точек останова могут помочь в отладке программы?
3.	Позволяет ли VBA контролировать значения всего массива?
4.	Как можно поэкспериментировать с вызовом функции, не создавая VBA-процедуры для вызова этой функции?
5.	Какая команда служит для сброса пошагового выполнения процедуры с целью ее повторного запуска?
Упражнения
1.	Измените строку 58 листинга 15.2, которая выглядит так:
58:	Loop Until (SrchFor = IntArr(Median)) or (Lo > Hi)
на следующую (оператор > заменен оператором >=):
58:	Loop Until (SrchFor - IntArr(Median)) or (Lo >= Hi)
Запустите модифицированную процедуру и исследуйте значения, генерируемые инструкцией Debug.Print. Какое действие на работу процедуры оказывает замена оператора > оператором >=?
2.	Измените следующую инструкцию If в листинге 15.2
53:	If SrchFor < IntArr(Median; then
54:	Hi - Median - 1
55:	Else
56:	Lo = Median + 1
57:	End if
на следующую:
53:	It SrchFor < IntArr(Median) Then
54:	Hi = Median
55:	Else
56:	Lo = Median
57:	End if
Запустите модифицированную процедуру и исследуйте значения, генерируемые инструкцией Debug.Print. Какое действие оказывает это изменение? Продолжает ли процедура работать правильно?
3.	НАЙДИТЕ ОШИБКУ. В чем заключается ошибка в этом цикле?
1:	Dim I Аз Integer
2:	Din. S As String
3:	Dim FindChar As String * 1
4:	Dim RsplChar As String * 1
5:
День 15-й. Отладка VBA-программы
535
6:	S = "Hello World!! How are you?"
7:	FindChar = "!"
8:	ReplChar = " "
9:	I = 1
10:
11:	Do While I < Len(S)
12:	If Mid(S, I, 1) = FindChar Then
13:	Mid(S, I, 1) = ReplChar
14:	End If
15:	Loop
4.	НАЙДИТЕ ОШИБКУ. В чем заключается ошибка в следующих инструкциях? К какой категории ошибок она относится?
1:	I = 55
2:
3:	If I < 10 And I > 100 Then
4:	Debug.Print "I = ”, I
5:	End If
6:	Debug.Print "Hello!"
5.	НАЙДИТЕ ОШИБКУ. В чем заключается ошибка в следующих инструкциях? Как ее можно исправить?
1:	Dim S As String
2:	Dim SubStr As String
3:	Dim I As Integer
4:
5:	S = "The rain in Spain stays mainly in plain"
6:	SubStr = "ain"
7:	I = InStrfS, Substr)
8:	Do While I > 0
9:	Debug.Print "Match at I
10:	I = InStr(I, S, Substr)
11:	Loop
536
Неделя 3

Деньги
Создание настраиваемых диалоговых окон
До сих пор вы учились использовать диалоговые окна, заранее определенные в VBA: процедуру и функцию MsgBox и функцию InputBox. Хотя функции MsgBox и InputBox обеспечивают программам пользователя гибкость, присущую только интерактивным программам, их возможности достаточно ограничены. При разработке более сложных программ потребуется отображение диалоговых окон, которые позволяют пользователю программы одновременно выбирать более одной опции, выбирать элементы из списка или одновременно вводить несколько значений — подобно тому, как это делается в диалоговых окнах Excel и других Windows-приложений. Часто будет требоваться использовать диалоговые окна, настроенные специально для конкретной программы, а не встроенные диалоговые окна Excel.
К счастью, VBA позволяет создавать и использовать в программах и процедурах нестандартные (настраиваемые) диалоговые окна, добавляя объект UserForm в проект. Используя разрабатываемые пользователем формы VBA, можно создавать нестандартные диалоговые окна для отображения данных или получения значений от пользователя программы в том виде, который наиболее соответствует потребностям программы. Например, можно отобразить диалоговое окно для отображения списка различных форматов даты и предоставить пользователю возможность выбрать один из них.
Нестандартные диалоговые окна позволяют программе взаимодействовать с ее пользователем самым сложным образом и обеспечивают разнообразную форму ввода и вывода данных. Этот урок поможет приступить к созданию форм VBA, применяемых в качестве нестандартных диалоговых окон. В ходе этого занятия вы узнаете, как решать следующие задачи.
•	Вставить новый объект UserForm в проект.
•	Добавлять элементы управления к объекту UserForm.
•	Устанавливать последовательность перехода по элементам управления в объекте UserForm при нажатии клавиши <ТаЬ>.
•	Связывать программу с элементами управления объекта UserForm.
•	Устанавливать свойства элемента управления объекта UserForm.
•	Использовать элементы управления в объекте UserForm.
День 16-й. Создание настраиваемых диалоговых окон	537
Знакомство с формами
Нестандартное диалоговое окно создается в VBA посредством добавления объекта UserForm в проект. Этот объект представляет собой „устое диалоговое окно; оно имеет строку заголовка и кнопку закрытия, но в нем отсутствуют какие-либо другие элементы управления. Нестандартное диалоговое окно создается путем добавления элементов управления в объект UserForm (обычно называемый просто формой). Каждый объект UserForm имеет свойства, методы и события, наследуемые им от класса объектов UserForm. Каждый объект UserForm также содержит' модуль класса, в который пользователь добавляет собственные методы и свойства или вписывает процедуры обработки событий для данной формы.
СаОытия и процедуры обработки событий
Мы говорим, что произошло событие, когда что-либо изменилось в окне или кто-то воздействовал на его эле-мент управления. Типичным примером события является щелчок на командной кнопке, переключателе или 1 флажке. Другими примерами событий являются изменение содержимого текстового поля или выбор в списке. Щелчки мыши, нажатия клавиш и внутренние действия компьютера - все эти действия вызывают события. (Иногда говорят создают события.)
Такие объекты, как формы или их элементы управления, вызывают различные события. Можно создать собст-у венные процедуры, реагирующие на эти события; такие процедуры называются процедурами обработки событий. В некоторых случаях, например при щелчке на кнопке, процедура обработки события выполнит единст-’ венное действие в ответ на данное событие. В других случаях, например при закрытии диалогового окна щелч-I ком на кнопке в его заголовке, процедура события выполняется в добавление к действиям, обычно выполняемым при этом событии.
j Процедуры обработки событий должны быть написаны в модуле класса, являющемся частью объекта UserForm, на события которого нужно отвечать, хотя события процедур могут вызывать другие процедуры или функции в стандарт-
; ном модуле или ссылочной библиотеке. Имена процедр событий всегда представляются в форме ; ObjectNameJSventName, где ObjectName - имя формы или элемента управления, a EventName - имя события, которое нужно обрабатывать. Используя такой формат имени, УВД может сопоставить соответствующую процедуру с конкретным событием. В остальной части урока читатели встретят несколько примеров процедур событий.
При создании нового объекта UserForm в проекте создается новый подкласс объекта UserForm. Любые процедуры или функции, записываемые в разделе General модуля класса формы, становятся дополнительными методами подкласса данного конкретного объекта. Новые свойства для формы могут быть также созданы посредством добавления процедур Property Get и Propery Let в ее модуль класса. Процедуры добавляются в наследуемый модуль класса формы посредством использования тех же технологий программирования модулей класса, о которых было рассказано на уроке 11-го дня нашего учебного курса. Новые экземпляры подкласса UserForm создаются с помощью инструкции Dim и ключевого слова New, как это было описано для других нестандартных классов на том же уроке.
Однако в большинстве случаев управление формой будет осуществляться посредством использования стандартных методов и свойств класса UserForm и создания процедур обработки событий для конкретной формы и ее элементов управления.
В табл. 16.1 приведены наиболее часто используемые методы UserForm и кратко описано их назначение. Эти методы будут доступны для каждого объекта UserForm. добавляемого в проект. Примеры использования большинства из них будут приведены далее в ходе изложения материма этого урока.
538
Неделя 3
Таблица 16.1. Обычно используемые методы объектов UserForm
Метод	Назначение
Сору Cut	Копирует выбранный в элементе управления текст в буфер обмена Windows Вырезает выбранный текст элемента управления и помещает его в буфер обмена Windows
Hide	Скрывает объект UserForm, не выгружая его из памяти и сохраняя значения элементов управления и любых переменных, объявленных в модуле класса формы
Paste	Вставляет содержимое буфера обмена Windows в текущий элемент управления
PrintForm	Выводит изображение формы, включая любые данные, введенные в элементы управления формы, на принтер по умолчанию
Repaint	Вызывает перерисовку изображения формы на экране. Этот метод следует использовать, если нужно перерисовать форму, не дожидаясь истечения обычного периода обновления
Show	Делает форму видимой на экране. Если форма еще не загружена в память. этот метод вначале загружает ее
Свойства объекта формы можно определить либо программными средствами, либо в окне свойств редактора Visual Basic. Не все свойства формы доступны для изменения в обоих этих случаях. Некоторые из них могул быть установлены только в окне Properties (Свойства). Установка свойств форм и элементов управления с помощью окна Properties подробнее описана ниже. В тексте VBA-программы свойства формы определяются так же, как свойства любого другого объекта: присвоением свойству нового значения. В табл. 16.2 приведены свойства объектов UserForm, использование или изменение которых наиболее вероятно.
Таблица 16.2. Наиболее часто используемые свойства объектов UserForm
Свойство	Описание
ActiveConlrol	Возвращав! ссылку на элемент управления в форме, который в текущий момент находится в фокусе. Имеет атрибут только для чтения
BackColor	Целое значение типа Long, представляющее конкретный цвет фона формы. Простейший способ задания этого свойства — использование окна Properties для выбора требуемого цвета; при необходимости номер цвета можно скопировать из окна Properties в текст программы
Caption	Текст, отображаемый в качестве заголовка формы. Имеет атрибут чтения/записи
Controls	Возвращает коллекцию всех элементов управления формы. Имеет атрибут только для чтения
Cycle	Определяет, вызывает ли нажатие клавиши <ТаЬ> циклический переход фокуса по всем элементам управления во всех групповых рамках и во всех страницах многостраничных элементов управления или только в текущей рамке или странице. Может содержать одну из двух встроенных констант: fmCycleAllForms или fmCycleCurrentForm. Имеет атрибут чтения/записи
День 16-й. Создание настраиваемых диалоговых окон
539
Окончание табл. 16.2
Свойство	Описание
Enabled	Содержит значение типа Boolean, указывающее, активизирована ли форма. Если значение равно False, ни один из элементов управления формы не доступен. Имеет атрибут чтения/записи
Font	Возвращает ссылку на объект Font, посредством которого можно выбрать характеристики шрифта формы или элемента управления
ForeColor	Аналогично свойству BackColor, но устанавливает цвет изображения — обычно текста — объекта формы
Большая часть текста программы в модуле класса формы фактически является процедурами обработки событий. В табл. 16.3 приведены события, для которых чаще всего создаются процедуры обработки событий. Несколько примеров процедур обработки событий приведено в примерах программ ниже в этой главе.	
Таблица 16.3.	Обычно используемые события объектов UserForm
Событие	Описание
Activate	Вызывается во всех случаях, когда форма становится активным окном. Это событие используется для обновления содержимого элементов управления диалогового окна, чтобы они отражали любые изменения, произошедшие, пока окно формы было неактивным
Click	Вызывается при щелчке кнопкой мыши на форме (любой ее части, не занятой элементом управления)
DblClick	Вызывается при двойном щелчке кнопкой мыши на форме (любой ее части, не занятой элементом управления)
Deactivate	Вызывается, когда форма перестает быть активным окном
Initialize	Вызывается, когда форма загружается в память в результате выполнения инструкции Load или метода Show. Это событие служит для инициализации элементов управления или представления формы
Terminate	Вызывается, когда форма выгружается из памяти. Это событие служит для выполнения любых специальных задач “уборки”, которые могут потребоваться прежде, чем переменные формы будут отбро-
	шены
Кроме методов, свойств и событий, которые объект UserForm предоставляет изначально, VBA предоставляет две инструкции, которые особенно полезны при работе с объектами форм: Load и Unload. Эти инструкции можно использовать для загрузки формы в память или для удаления ее из памяти.
Общий синтаксис инструкций Load и Unload следующий.
Load Object
Unload Object
В этих синтаксических конструкциях Object представляет любую допустимую ссылку на объект UserForm.
Инструкция Load загружает объект UserForm в память и запускает метод Initialize формы, но не делает форму видимой на экране. После того как форма загружена, для
540
Неделя 3
манипулирования объектом формы можно использовать процедуры и функции VBA. Инструкция Unload удаляет объект UserForm из памяти, отменяя любые переменные в форме. После этого форма больше не доступна для манипулирования посредством процедур VBA.
Теперь, после обзора основных компонентов нестандартного диалогового окна, можно подробнее познакомиться с элементами управления, которые превращают форму в действующее диалоговое окно.
Элементы управления
Объект UserForm может содержать элементы управления подобные тем, что находятся в других диалоговых окнах, отображаемых Excel и другими Windows-приложениями. Элементы управления — это элементы диалогового окна, которые позволяют пользователю взаимодействовать с программой. К этим элементам относятся кнопки переключателей, текстовые поля, линейки прокрутки, командные кнопки и т.п. В этом разделе читатели познакомятся со стандартными элементами управления, включенными в VBA, которые можно добавлять в формы.
ПРИМЕЧАНИЕ
Многие независимые поставщики программного обеспечения создают пакеты более сложных элементов управления. Нестандартные элементы управления (называемые объектами ActiveX и объектами автоматизации) можно добавить в панель инструментов Toolbox (Панель элементов), вначале создав ссылку из проекта на библиотеку, содержащую нестандартные элементы, а затем добавив их в панель инструментов Toolbox.
Каждый элемент управления — объект со специальными свойствами, методами и событиями. Подобно содержащим их формам, свойства элементов управления можно определять программным путем или с помощью окна Properties редактора Visual Basic. Значения свойства элемента управления присваиваются или получаются в VBA-программе, так же, как и для любого другого объекта. Использование окна Properties для определения свойств элемента управления подробнее описано далее в ходе изложения материала этого урока.
В табл. 16.4 приведены стандартные элементы управления, поставляемые с VBA, и кратко описано назначение каждого из них. Как видно из таблицы, в число стандартных элементов управления Excel VBA входят почти все элементы управления, которые можно встретить в других Windows-приложениях. (Некоторые содержащие VBA приложения предоставляют дополнительные элементы управления; эти дополнительные элементы управления доступны посредством библиотек элементов управления, поставляемых с приложениями.)
Таблица 16.4. Стандартные элементы управления, поставляемые с Excel VBA
Элемент управления	Назначение
Label (Надпись)	Обеспечивает надписи для элементов управления, которые сами не имеют собственных надписей. Этот элемент управления используется для помещения статического текста в создаваемую пользователем форму
TextBox (Поле)	Текстовое поле произвольной формы, предназначенное для ввода данных; может состоять из одной или нескольких строк
День /6-й. Создание настраиваемых диалоговых окон
541
Продолжение табл. 16.4
Элемент управления Назначение
ComboBox (Поле со списком)	Этот элемент управления объединяет в себе поле и список. Поле со списком используется для предложения пользователю значений, из которых тот может выбирать. Можно разрешить пользователю вводить значение, отсутствующее в списке, или ограничить его выбор только теми, что появляются в поле со списком
ListBox (Список)	Отображает список значений, из которых пользователь осуществляет выбор. Можно разрешить пользователю выбирать только одно значение или несколько значений
CheckBox (Флажок)	Стандартный флажок (квадратное окошко; когда otto выбрано, то содержит метку выбора). Флажки используются для выбора параметров, принимающих значения включе-но/выключено, истинно/ложно и т.н. Эти элементы управления следует применять jlih выбора параметров, которые не являются взаимоисключающими
OptionButton (Переключатель)	Стандартный переключатель (круглая рамка; при выборе в ней отображается черная точка). Переключатели используются для выбора параметров, которые не только принимают значения включено/выключено или истинно/ложно, но и являются взаимоисключающими
Toggle Button (Выключатель)	Выключатели используются для указания настроек, принимающих значения включено/выключено или истин-но/.тожно. Они служат той же цели, что и флажки. но отображают настройку в виде кнопки в нажатом или освобожденном положении
Frame (Рамка)	Зрительно и логически i руппирх et другие элементы управления (особенно флажки, переключатели и выключатели). Рамка используется длч витального группирования связанных дрхг с друюм э.темшнов управления
CommandButton (Кнопка)	Командная кнопка. Кнопки используются для таких действий, как Cancel <Отмене). Save (Сохранить), ОК и т.п. Когда пользователь щелкает на кнопке, выполняется процедура, связанная с элементом управления
TabStrip (Набор вкладок)	Этот элемент управления состоит из клиентской области, в которой помешаются другие элементы управления (такие как поля, флажки и т.п.) и кнопок вкладок. Он используется для создания диалоговых окон с вкладками, отображающих одинаковые данные в различных категориях
Multi Page (Набор страниц)	Этот элемент управления состоит из нескольких страниц, которые можно выбирать щелчком на странице. Он используется для диалоговых окон с вкладками, аналогичных отображаемым в окне при выборе команды Tools-Options (Сервис-Параметры). Его следует использовать, когда различные элементы управления служат для отображения различных данных
542
Неделя 3
Окончание табл. 16.4
Элемент управления	Назначение
RefEdit	Специальный элемент управления в виде поля, используемый для получения диапазонов рабочих листов. Щелчок на встроенной кнопке элемента управления RefEdit приводит к сворачиванию формы до размеров этого элемента управления и позволяет пользователю выбирать листы или диапазоны листов. Вторичный щелчок на кнопке этого элемента управления восстанавливает форму
ScrollBar (Полоса прокрутки) SpinButton (Счетчик)	Полоса прокрутки позволяет выбирать линейное значение, аналогично счетчику Счетчики — это специальная разновидность полей. Как правило, они используются для ввода численных данных, даты или других последовательных значений, относящихся к конкретному диапазону. Щелчок на стрелке вверх счетчика увеличивает значение в поле, а щелчок на стрелке вниз — уменьшает его
image (Изображение)	Этот элемент управления позволяет отображать в создаваемой пользователем форме графическое изображение. Он применяется для отображения графических изображений в любом из следующих форматов: *.bmp, *.cur, *.gif, *.ico, *,jpg или *.wmf. Графическое изображение можно обрезать, изменять его размеры или масштабировать, чтобы оно соответствовало размерам элемента управления, но элемент управления изображения нельзя использовать для редактирования фафического изображения. Однако можно установить. чтобы в результате щелчка на этом элементе управления выполнялась конкретная VBA-процедура
В основном манипулирование элементами управления осуществляется через их свойства и с помощью процедур обработки событий, создаваемых для каждого элемента управления. В табл. 16.5 приведена большая часть свойств элементов управления, которые могут потребоваться при работе с BVA-программами: свойств, которые позволяют изменять надпись элемента управления, получать информацию о состоянии элемента управления (т.е. выяснять, какой выбор был осуществлен пользователем) и т.п.
/примечание
К, сожалению, перечисление всех свойств, методов и событий для форм и всех стандартных элементов управления выходит за рамки этой книги - помимо объектов формы и всех стандартных элементов управления, существуют буквально сотни свойств, методов и событий. В этой главе описаны наиболее часто используемые методы, события и свойства. Для получения дополнительной информации о конкретных элементах управления обратитесь к интерактивной справке VBA. В частности, для отображения справки по объектам в библиотеке MSForms (в этой библиотеке содержатся объекты UserForm и объекты управления) используйте окно Object Browser. Прочтя эту книгу, читатели, возможно, пожелают также прочесть книгу Database Developer’s Guide with Visual Basic 6, изданную издательством Sams.
День 16-Й. Создание настраиваемых диалоговых окон
543
Таблица 16.5. Наиболее часто используемые свойства стандартных элементов управления
Свойство	Элементы управления, к которым относится свойство	Описание
Accelerator	CheckBox, Tab, Command-Button, Label, Page, Option-Button, ToggleButton	Содержит символ, используемый в качестве клавиши быстрого доступа к элементу управления. Нажатие комбинации клавиш <Alt + клавиша быстрого доступа> вызывает выбор элемента управления
BackColor	Все элементы управления	Число, представляющее цвет фона элемента управления
Caption	CheckBox, CommandButton, Frame, Label, OptionButton, Page, Tab, ToggleButton, Us- ’ erForm	Для надписи это текст, отображаемый элементом управления; для остальных элементов управления это надпись, появляющаяся на поверхности кнопки или вкладки, или отображаемая рядом с рамкой, флажком или переключателем
Cancel	CommandButton	Определяет кнопку, эквивалентную кнопке Отмена; щелчок на этой кнопке или нажатие клавиши <Esc> отменяет диалог. Только одна кнопка в форме может иметь это свойство
ControlTipText	Все элементы управления	Определяет текст, который отображается в качестве подсказки элемента управления (называемой, также, подсказкой инструмента), когда указатель мыши задерживается над элементом управления
Default	CommandButton	Определяет кнопку, выбираемую по умолчанию. Когда пользователь нажимает клавишу <Enter> в диалоговом окне, кнопка ведет себя так, как если бы на ней был выполнен щелчок мышью
Enabled	Все элементы управления	Хранит значение Boolean, определяющее то, активизирован ли элемент управления. Если значением свойства Enabled является False, элемент управления отображается в диалоговом окне, но не может быть выбран
ForeColor	Все элементы управления	Аналогично свойству BackColor, но определяет цвет изображения элемента управления — обычно его текста
544
Неделя 3
Продолжение табл. 16.4
Свойство Элементы управления, Описание к которым относится свойство
List	ComboBox, ListBox
Мах	ScrollBar, SpinButton
Min	ScrollBar, SpinButton
Name	Все элементы управления
RowSource	ComboBox, ListBox
Selected	ListBox
Массив типа Variant (одномерный или многомерный), который представляет список, содержащийся в элементе управления. Значение индекса в переменной Value используется в качестве индекса набора List для получения текста выбранного элемента списка. Методы Addltem и Removeltem элемента управления служат для добавления или удаления элементов списка
Значение типа Long, определяющее максимальное значение счетчика или значение в крайней нижней (для вертикальной полосы прокрутки) или крайней правой (для горизонтальной полосы прокрутки) позиции полосы прокрутки
Значение типа Long, определяющее минимальное значение счетчика или значение в крайней верхней (для вертикальной полосы прокрутки) или крайней левой (для горизонтальной полосы прокрутки) позиции полосы прокрутки
Содержит имя элемента управления. Это свойство можно устанавливать только в диалоговом окне Properties
Указывает источник списка объекта; в VBA Excel значением свойства RowSource обычно является диапазон рабочего листа.
Возвращает массив значений типа Boolean для списков, которые допускают выбор нескольких значений; каждый элемент массива содержит один элемент, который соответствует каждому элементу в списке. Если элемент в массиве Selected имеет значение True, соответствующий элемент списка выбран
День 16-Й. Создание настраиваемых диалоговых окон
545
Окончание табл. 16.4
Свойство	Элементы управления, к которым относится свойство	Описание
Tabindex	Все элементы управления	Число, указывающее позицию элемента управления при переходе к нему при нажатии клавиши <ТаЬ> (может принимать значение от 0 до числа, равного количеству элементов управления в форме)
TabStop	Все элементы управления	Значение типа Boolean, показывающее, может ли элемент управления быть выбран нажатием клавиши <ТаЬ>; если значение свойства Tabstop равно False, элемент управления все же может быть выбран щелчком мыши на нем
Value	Все элементы управления	Значение текущей настройки элемента управления: текст в текстовом поле, выбран ли флажок или переключатель, индекс выбранного элемента в списке или число, указывающее текущую позицию полосы прокрутки или счетчика
Visible	Все элементы управления	Значение типа Boolean, показывающее, выбран ли элемент управления
В табл. 16.6 приведены события элементов управления, для которых может потребоваться создание собственных процедур обработки событий. Каждый добавляемый в форму элемент управления будет иметь эти события. В частности, событие Click будет использоваться с кнопками, а события AfterUpdate или Change будут применяться для проверки данных или обновления других элементов управления.
Таблица 16.6.	Обычно используемые события объектов элементов управления
Событие	Описание
AfterUpdate	Вызывается после обновления значения данного элемента управления
BeforeUpdate	Вызывается после изменения значения данного элемента управления, но перед тем, как элемент управления будет обновлен
Change	Вызывается при всех изменениях значения элемента управления
Click	Вызывается при каждом щелчке кнопкой мыши на элементе управления. Это событие используется для связывания кнопки с каким-либо действием
DblClick	Вызывается при каждом двойном щелчке кнопкой мыши на элементе управления. Это событие используется для отображения дополнительных форм — например, текстового поля больших размеров
Enter	Вызывается всегда, когда элемент управления получает фокус
Exit	Вызывается всегда, когда элемент управления утрачивает фокус
546
Неделя 3
Теперь, когда читатели ознакомились с основными действиями по созданию нестандартного диалогового окна, содержащего форму, в последующих разделах этого урока будут приведены и пояснены примеры большинства элементов управления, перечисленных в табл. 16.4, а также нескольких свойств и событий, приведенных в табл. 16.5 и 16.6. В листингах примеров этого урока продемонстрировано, как заставить некоторые элементы управления изменять свое поведение или как-либо иначе отвечать на конкретные события.
Использование форм для создания диалоговых оной
Создание нестандартного диалогового окна — простой процесс. Для создания самого диалогового окна не нужно создавать никакой VBA-программы. Вместо этого редактор Visual Basic используется для графического создания диалогового окна в объекте UserForm. По существу, нестандартное диалоговое окно просто рисуется в форме. В каждой форме можно создать только одно диалоговое окно.
Для создания нестандартного диалогового окна VBA необходимо выполнить следующие основные действия.
1.	Добавьте объект UserForm в проект нового диалогового окна.
2.	Используйте панель инструментов Toolbox для помещения элементов управления в форму.
3.	Используйте окно Properties для определения свойств формы и ее элементов управления.
4.	Определите последовательность перехода по элементам управления формы при нажатии клавиши <ТаЬ>.
5.	Добавьте в модуль класса формы текст VBA-процедур для обработки событий формы и элементов управления, чтобы элементы управления выполняли требуемые действия.
6.	В стандартном модуле создайте VBA-процедуры для отображения диалогового окна UserForm и получения значений от элементов управления диалогового окна.
В нескольких следующих разделах показано, как в интерактивном режиме создать форму, поместить в нее элементы управления, установить последовательность перехода по элементам управления и определить их свойства. В последних разделах этого урока показано, как создавать процедуры обработки событий и как использовать нестандартные диалоговые окна в программе.
Добавление нового объекта формы
Первый шаг по созданию нестандартного диалогового окна заключается в добавлении в проект нового объекта UserForm. Объект UserForm содержит создаваемое диалоговое окно, а также обеспечивает рабочее пространство для его рисования. Поскольку объекты UserForm хранятся в коллекции объектов UserForms проекта, все они являются частью проекта.
Чтобы добавить новый объект UserForm, используйте команду Insert-UserForm редактора Visual Basic. Редактор Visual Basic добавит новый объект UserForm в текущий проект и присвоит ему имя по умолчанию — UserFormN, — используя ту же систему нумерации, что и для модулей. Новый объект UserForm отображается в режиме конструктора, как показано на рис. 16.1. (В режиме конструктора можно добавлять или удалять элементы управления в форме, определять свойства формы или элемента управления и вообще интерактивно манипулировать внешним видом формы. Когда
День 16-Й. Создание настраиваемых диалоговых окон
547
форма отображается и используется как часть выполняемой VBA-программы, она находится в режиме выполнения.)
На рис. 16.1 показано только что созданная нестандартная форма. Обратите внимание на жирную рамку вокруг формы, показывающую, что форма выбрана. Обратите также внимание на сетку из точек, отображаемую на поверхности формы, и на панель инструментов Toolbox (Элементы управления) справа внизу от пустой формы. Сетка из точек помогает выравнивать и определять размеры элементов управления в форме; она отображается только тогда, когда форма отображается в режиме конструктора. Панель инструментов Toolbox — это окно, в котором выбираются элементы управления, добавляемые в форму; обычно оно отображается, только если форма или один из элементов управления в ней выбраны.
Так надо
ПОМНИТЕ, что объект UserForm можно переименовать так же, как стандартный модуль или модуль класса: откройте диалоговое окно Properties объекта, выбрав объект в окне Project Explorer, а затем щелкнув на кнопке Properties в панели инструментов редактора Visual Basic. Затем отредактируйте свойство Name объ-: екта в окне Properties, чтобы изменить имя объекта.
ПРИСВАИВАЙТЕ формам описательные имена, чтобы можно было легко определить, для чего предназначена форма.
Так не надо
: НЕ ОСТАВЛЯЙТЕ новую форму с именем, присвоенным по умолчанию, таким как UserFonnl или , UserForm2. Переименовывайте новые формы немедленно после их создания.
Рис. 16.1. Новая форма UserForm в режиме конструктора; обратите внимание на сетку из точек в форме и на присутствие панели инструментов Toolbox
548
Неделя 3
Использование панели инструментов Toolbox
Когда редактор Visual Basic отображает форму в режиме конструктора, он отображает также панель инструментов Toolbox. На рис. 16.2 панель инструментов Toolbox показана в виде плавающего окна. Кнопки этой панели активизируют различные инструменты, которые позволяют вставлять элементы управления в форму.
Выключатель
а га
Поле
Поле со списком
Список Флажок Набор страниц Набор вкладок
Надпись Выбор объектов Переключатель Полоса прокрутки —~
Счетчик
Рисунок Рамка
Кнопка
RefEdit
Рис. 16.2. Панель Элементы управления позволяет включить элементы управления в форму
ПРИМЕЧАНИЕ
Если отображение панели инструментов Toolbox отключено с помощью команды View-Toolbox (Вид-Панели инструментов) (или щелчком на кнопке Toolbox в панели инструментов редактора Visual Basic), редактор Visual Basic может и не отображать Toolbox при отображении формы в режиме конструктора. Если панель Toolbox не появляется, когда форма находится в режиме конструктора и выбрана, используйте для ее отображения команду View-Toolbox или кнопку Toolbox в панели инструментов редактора Visual Basic.
Панель Toolbox, подобно всем панелям инструментов, является настраиваемой. Если были установлены дополнительные элементы управления от независимых поставщиков или панель инструментов Toolbox была настроена как-либо иначе, ее вид может отличаться от показанного на рис. 16.2. На этом рисунке показаны только стандартные элементы управления, поставляемые с Excel VBA.
Чтобы использовать кнопки панели Toolbox для вставки элементов управления в форму, выполните следующие действия.
1.	Щелкните на командной кнопке в панели Toolbox, соответствующей элементу управления, который нужно добавить в форму. При помещении над поверхностью формы указатель мыши изменится на перекрестье.
2.	Поместите перекрестье над тем местом формы, где должен располагаться левый верхний угол нового элемента управления.
3.	Нажмите и удерживайте нажатой левую кнопку мыши.
4.	Выполните перетаскивание вниз и влево, пока размер элемента управления не станет равным требуемому, а затем отпустите кнопку мыши. Редактор Visual Basic вставит элемент управления в форму, а указатель мыши снова приобретет форму стрелки.
День 16-Й. Создание настраиваемых диалоговых окон
549
Можно изменить размеры самой формы, щелкнув на ее заголовке, чтобы выбрать ее, а затем перетащив один из маркеров изменения размеров. Маркеры изменения размеров - это небольшие квадраты, отображаемые в углах и по сторонам графического объекта (вместе толстой серой рамкой) при его выборе.
Ниже кратко описано общее назначение каждого типа элементов управления, которые могут быть добавлены из панели инструментов Toolbox (см. рис. 16.2); этот перечень может служить небольшим справочником при выборе элемента управления, соответствующего конкретным потребностям.
•	Label (Надпись). Создает элемент управления Label. Надписи используются для вставки текста в форму, например заголовков элементов управления, не имеющих собственных инструкций, подсказок по использованию формы и т.п. Надписи применяются также для отображения такой информации, как текущее значение счетчика или полосы прокрутки. Текст в элементе управления Label можно изменить с помощью исходного текста VBA, но пользователь формы не может редактировать текст надписи. Эти элементы управления следует использовать для отображения текста, который не будет меняться или изменение которого пользователем нежелательно.
•	TextBox (Поле). Создает элемент управления TextBox. Поля используются для получения или отображения данных, которые могут быть представлены в виде текста, таких как имена или адреса, денежные суммы, даты и т.п. Поля служат также для отображения такой информации, как текущее значение счетчика или полосы прокрутки, и для обеспечения альтернативных способов установки значения счетчика или полосы прокрутки. В полях могут отображаться одна или несколько строк текста. При необходимости поля будут снабжаться вертикальной полосой прокрутки для прокрутки нескольких строк текста.
•	Frame (Рамка). Создает элемент управления Frame. В действительности эти элементы управления сами ничего не делают, но они содержат другие элементы управления, обычно переключатели, флажки или выключатели. Рамка используется для указания пользователю, какие элементы управления в диалоговом окне связаны друг с другом, или для отделения особой группы элементов управления от остальных элементов управления диалогового окна. Можно помешать пользователю входить или выходить из группы элементов управления, помешенных в рамку, с помощью нажатия клавиши <ТаЬ>, установив значение свойства Cycle равным fmCycleCurrentForm. Для помещения других элементов управления в рамку вначале необходимо поместить в форму элемент управления Frame, а затем поместить в нее необходимые элементы управления.
•	CommandButton (Кнопка). Создает элемент управления CommandButton. Командные кнопки используются для выполнения таких действий, как связанные со щелчком на кнопке. Действиями могут быть закрытие или сокрытие формы, обновление данных, подтверждение данных в элементах управления формы и т.п.
•	CheckBox (Флажок). Создает элемент управления checkbox. Флажки в качестве составной части элемента управления содержат надпись. Флажки используются для выбора значений типа истинно/ложно, включено/выключено, да/нет, которые не являются взаимоисключающими; например, если требуется выполнить поиск текста с учетом регистра и только целых слов, для выбора этих параметров поиска нужно было бы использовать соответствующие флажки.
•	OptionButton (Переключатель). Создает элемент управления OptionButton. Переключатели используются для выбора взаимоисключающих значений типа истин
550
Неделя 3
но/ложно, включено/выключено, да/нет; например, одновременно нельзя выполнять поиск и в прямом, и в обратном направлении, поэтому для выбора направления поиска нужно было бы использовать переключатель. Как правило, и флажки, и переключатели помещаются внутрь рамки.
•	ToggleButton (Выключатель). Создает элемент управления ToggleButton. Выключатели используются для выбора значений типа истинно/ложно, включено/выключено, да/нет. Они служат той же цели, что и флажки, но отображают настройку в виде кнопки в “отжатой” или “нажатой” позиции. Их следует использовать для выбора значений, которые не являются взаимоисключающими.
•	ListBox (Список). Создает элемент управления ListBox. Списки используются при необходимости отобразить список элементов и предоставить пользователю возможность выбора в нем одного ли нескольких элементов, без возможности добавлять в список новые элементы. Для определения того, может ли пользователь выбирать в списке более одного элемента, следует использовать свойство списка MultiSelect. Списки используются для отображения таких элементов, как имена рабочих книг и листов, диапазоны имен, месяца года и т.п.
•	ComboBox (Поле со списком). Создает элемент управления ComboBox, который объединяет в себе свойства поля и списка. Поле со списком используется для создания раскрывающихся списков и в тех случаях, когда нужно предложить пользователю значения, из которых он может выбирать. Поля со списком могут быть установлены для предоставления пользователю возможности вводить значения, отличающиеся от представленных в списке, или для ограничения его только этими значениями; это выполняется установкой свойства Style. Поля со списком могут использоваться для выбора только одного элемента одновременно.
•	Yah Strip (Набор вкладок). Создает элемент управления Tabstrip. Набор вкладок имеет одну клиентскую область, в которой можно помещать другие элементы управления, подобно элементу управления Рамка. При выборе новой вкладки клиентская область не изменяется. Вместо этого событие Change используется для обновления элементов управления в клиентской области набора вкладок в соответствии с конкретной программой. Наборы вкладок применяются для диалоговых окон с вкладками, в которых информация на каждой странице диалогового окна отображается в одних и тех же элементах управления, но в различных категориях. Например, если нужно создать форму для отображения итоговой информации по квартальным продажам, можно было бы использовать набор из пяти вкладок: по одной для каждого квартала и одна для годового итога. Каждая вкладка отображала бы одну и ту же информацию: рост продаж, прибыль и т.п. для различных кварталов.
•	MultiPage (Набор страниц). Создает элемент управления MultiPage. Этот элемент управления имеет несколько клиентских областей, в которые можно включать другие элементы управления. При выборе новой вкладки вся клиентская область изменяется для отображения различных групп элементов управления. Элементы управления MultiPage используются для создания диалоговых окон с вкладками, в которых информация на каждой странице отображается в других элементах управления. Диалоговое окно Options (Параметры) приложения Excel является примером элемента управления MultiPage.
•	ScrollBar (Полоса прокрутки). Создает элемент управления ScrollBar. Полосы прокрутки используются для создания вручную прокручиваемых элементов управления в форме или для ввода последовательных численных значений.
День /6-й. Создание настраиваемых диалоговых окон
551
•	SpinButton (Счетчик). Создает элемент управления SpinButton. Счетчики используются для ввода в диалоговом окне последовательных численных значений. Обычно счетчики объединяются с надписями для отображения значения счетчика. Счетчик можно также объединить с полем для обеспечения альтернативного метода ввода значения. При объединении счетчика с надписью или полем необходимо создать процедуру, синхронизирующую оба элемента управления. Примеры, приведенные ниже в этой главе, показывают, как синхронизировать элемент управления надписи или поля со счетчиком.
•	Image (Изображение). Создает элемент управления Image. Эти элементы управления используются для отображения в формах таких графических объектов, как логотипы, фотографии и т.п. Элементы управления изображений представляют события аналогично другим элементам управления, поэтому изображения можно использовать для ответа на щелчки мышью. Элементы управления изображений не могут редактироваться, поэтому они не могут получать фокус.
•	RefEdit. Создает элемент управления RefEdit. Этот элемент управления отображает поле с присоединенной к нему справа кнопкой. Щелчок на присоединенной кнопке элемента управления RefEdit приводит к сворачиванию всей формы до объекта, не превышающего по размерам поле RefEdit. При этом пользователь может выбирать листы в рабочей книге и диапазоны в листах. Повторный щелчок на кнопке элемента управления RefEdit восстанавливает форму в ее первоначальных размерах. Элементы управления RefEdit используются, когда нужно предоставить пользователю возможность открывать максимальную поверхность экрана, обычно скрываемую формой, и упростить пользователю ввод запрашиваемых диапазонов рабочих листов.
ПРИМЕЧАНИЕ	Когда форма отображается на экране в режиме конструктора, ее внешний вид и поведение можно проверить, воспользовавшись командой Run-Run Sub/UserForm (Запуск-Запуск подпрограммы/UserForm). Редактор Visual Basic отобразит форму в режиме выполнения с активными всеми ее элементами управления. Однако следует иметь в виду, что любые используемые формой процедуры, которые не сохранены в модуле класса формы, могут быть не инициализированы. Запуск формы инициализирует только ту часть программы, которая хранится в модуле класса формы; переменные в стандартных модулях не обязательно будут инициализированы. В результате, часть связанной с формой программы может не выполниться из-за различных ошибок времени выполнения, если только форма не является полностью самостоятельной.
Добавление элементов управления в форму
При создании новой формы редактор Visual Basic отображает совершенно пустую форму (рис. 16.1). Элементы управления добавляются в форму посредством отображения формы и последующего графического рисования в ней элементов управления с помощью различных инструментов из панели Toolbox.
Как правило, каждая форма должна содержать кнопки ОК и Cancel (Отмена) или их функциональные эквиваленты. Кнопка ОК - это кнопка, которая принимает настройки диалогового окна и инициализирует его основное действие, а кнопка Cancel позволяет пользователю закрыть диалоговое окно, не выполняя каких-либо действий. Обычно два первых элемента управления, добавляемых в форму, должны служить в качестве кнопок ОК и Cancel.
552
Неделя 3
СОВЕТ
Кнопки имеют специальное свойство - Cancel - указывающее кнопку Cancel (независимо от того, помечена ли кнопка надписью ‘Cancel’). Только одна кнопка в форме может иметь свойство Cancel, установленное в True; установка этого свойства для одной кнопки устанавливает свойство Cancel для всех остальных кнопок формы равным False. Если форма содержит кнопку с установленным свойством Cancel, нажатие клавиши <Esc> оказывает такое же воздействие, как щелчок на этой кнопке.
Для добавления элемента управления в форму следует использовать панель Toolbox, как описано в предыдущем разделе этого урока. В качестве примера добавления конкретного элемента управления в форму давайте добавим в новую форму кнопку, выполнив следующие действия.
1.	Выберите команду Insert-UserForm (Вставка-UserForm). Редактор Visual Basic добавит новую форму, отобразив ее в режиме конструктора, и отобразит панель Toolbox, как показано на рис. 16.1.
2.	Щелкните на инструменте CommandButton в панели Toolbox. Кнопка панели Toolbox изменит свой вид на “вдавленный”, указывая выбранный инструмент.
3.	Переместите указатель мыши на форму; он изменится на перекрестье с присоединенным к нему символом выбранного инструмента.
4.	Поместите перекрестье там, где должен располагаться верхний левый угол кнопки.
5.	Нажмите кнопку мыши и перетащите курсор вниз и вправо, чтобы нарисовать элемент управления кнопки. Во время перетаскивания курсора мыши редактор Visual Basic отобразит прямоугольный контур, показывающий размер кнопки.
6.	Придав кнопке нужные размеры, отпустите кнопку мыши. Редактор Visual Basic создаст элемент управления кнопки и вставит его в форму. На рис. 16.3 показана новая форма непосредственно после рисования в ней кнопки.
ПРИМЕЧАНИЕ
Во время рисования кнопки в этом упражнении читатели могли заметить, что верхний левый угол кнопки - как и контур самой кнопки - скачкообразно выравнивался по сетке из точек в форме. Эго действие - называемое привязкой к сетке - помогает выравнивать элементы управления и текст в форме. Можно включать и отключать привязку к сетке, настраивать размер сетки или скрывать сетку, изменяя настройки в диалоговом окне Options (Параметры) редактора Visual Basic. Выберите команду Tools-Options (Сервис-Параметры) и щелкните на вкладке General (Общие), чтобы отобразились Form Grid Settings (Параметры сетки в форме). Выберите или удалите нужные опции. Увеличение ширины или высоты ячеек сетки раздвигает точки сетки друг от друга, а уменьшение ширины или высоты помещает их ближе одну к другой.
Все элементы управления формы должны иметь уникальные имена; эти имена используются для ссылки на элемент управления в VBA-программе, как описано далее в этом уроке. При каждом добавлении элемента управления в форму VBA присваивает новому элементу управления имя по умолчанию, состоящее из имени типа элемента управления, за которым следует число. Уникальность имен BVA обеспечивает, включая в имя элемента номер, превышающий номер любого другого элемента управления этого же типа. Например, если добавленная в форму кнопка является первой добавленной кнопкой, она получает имя CoirimandButtonl. Следующая добавленная кнопка получит имя CommandButton2 и т.д. Если затем будет добавлено поле, оно будет названо TextBoxl.
День 16-й. Создание настраиваемых диалоговых окон
553
Vjjj Microsoft Visual Basic- Day16.xls
^£9» fi<M Xiew jnsert Ffirmat Qebug Bin 1осй ДСЙ-Ы'yySndow 'flelp '	•	- «-/	’
1®:ЗлИН •*-- > It *[««•«;*43!’’ " *' ‘J/
Puc. 16.3. Форма UserForm непосредственно после добавления в нее элемента управления CommandButton
Элементам управления можно — и нужно — присваивать более описательные имена, редактируя свойство Name в диалоговом окне Properties, как описано далее в этом уроке.
Так надо
. ПРИСВАИВАЙТЕ своим элементам управления описательные имена, помогающие запомнить, для чего они предназначены. Такое имя, как txtFirstName, гораздо больше говорит о выполняемых элементом управления действиях, чем TextBoxlO.
ПОМНИТЕ, что имена элементов управления должны соответствовать стандартным правилам для имен идентификаторов VBA: они должны начинаться с буквы и не могут содержать пробелов или знаков пунктуации.
ИСПОЛЬЗУЙТЕ префикс в именах элементов управления, такой как txt для полей, opt для переключателей btn для кнопок и т.п. Это поможет идентифицировать тип элемента управления при его использовании в VBA-программе.
ПОМНИТЕ, что каждый элемент управления в форме должен иметь уникальное имя.
ПРИМЕЧАНИЕ
К сожалению, освещение всех подробностей добавления и редактирования всех возможных типов элементов управления выходит за рамки этой книги. Познакомившись с основными техническими приемами добавления и редактирования элементов управления в форме, можно прибегнуть к интерактивной справочной системе для получения дополнительной информации по конкретным элементам управления и их свойствам.
554
Неделя 3
Редактирование элементов управления
Нарисовав элемент управления в форме, можно изменять его размеры, перемещать его, копировать, удалять, изменять его форматирование (размер шрифта, стиль, гарнитуру) или изменять его свойства (такие как имя элемента управления, его надпись и т.п.). Редактировать или перемещать элементы управления формы потребуется при совершенствовании конструкции и внешнего вида диалогового окна; например, может оказаться, что поле слишком мало или слишком велико для вводимых в нем значений, и поэтому нужно изменить его размеры. Или другой пример: наверняка потребуется изменять надписи любых кнопок, переключателей или флажков, помещаемых в форму, так как редактор Visual Basic присваивает новым кнопкам надписи, совпадающие с именем элемента управления по умолчанию — вероятно, нежелательно иметь кнопку, подписанную “CommandButton?”.
Существующая форма открывается двойным щелчком на ней в окне Project Explorer. Или же в окне Project Explorer можно выбрать форму, которую нужно отредактировать, а затем щелкнуть на кнопке Object.
Выбор элементом управления
Для редактирования элемента управления вначале его нужно выбрать, сделав на нем щелчок. Выбранные элементы управления имеют серую рамку с маркерами изменения размеров, как это показано для кнопки, отображенной на рис. 16.3. Для одновременного выбора нескольких элементов управления во время щелчка на выбираемых элементах удерживайте нажатой клавишу <Shift>. Одновременный выбор нескольких элементов управления удобен, если нужно выполнить изменение в форматировании, влияющее на все элементы управления в форме, как, например, изменение размера шрифта, стиля или гарнитуры шрифта. Выбор нескольких элементов управления удобен также, если нужно одновременно переместить несколько элементов управления.
Можно временно сгруппировать несколько элементов управления, используя команду Format-Group (Формат-Группировать). Когда элементы управления сгруппированы, они ведут себя как один объект при выборе, перемещении, копировании или каком-либо ином изменении. Элементы управления остаются сгруппироваными до тех пор, пока к выбранной группе не будет применена команда Format-Ungroup (Формат-Разгруппировать).
Перемещение элемента управления
Для перемещения элемента управления в новое положение в форме вначале его нужно выбрать. Затем поместите указатель мыши над любым участком серого контура вокруг выбранного элемента управления, но не на маркере изменения размера. Затем перетащите элемент управления в новую позицию.
Изменение размера элемента унравлення
Для изменения размера элемента управления вначале выберите его. Затем поместите указатель мыши на один из маркеров изменения размеров (квадраты, отображающиеся в углах и по центру сторон серой рамки вокруг выбранного объекта). Когда указатель находится над маркером изменения размеров, он изменяется на двунаправ
День 16-й. Создание настраиваемых диалоговых окон
555
ленную стрелку, указывающую направление, в котором можно изменять размеры выбранного элемента управления. Щелкните и перетащите маркер изменения размеров; редактор Visual Basic отобразит контур, показывающий новый размер элемента управления. Придав элементу управления требуемый размер, отпустите кнопку мыши; редактор Visual Basic перерисует элемент управления с новыми размерами.
Копирование, вставка в удаление элементов днравлення
Один из простейших способов создания нескольких одинаковых элементов управления — например, нескольких флажков или кнопок — создание одного элемента управления с последующим его копированием и вставкой в форму нужное количество раз. Для копирования элемента управления формы просто выберите его, а затем используйте команду Edit-Copy (Правка-Копировать) (или нажмите комбинацию клавиш <Ctrl+C>).
Скопировав элемент управления, его можно вставить в эту же или другую форму, используя команду Edit-Paste (Правка-Вставить) (или нажав комбинацию клавиш <Ctrl+V>). Вставив элемент управления, переместите его в нужное место.
Иногда может потребоваться удаление элемента управления из формы. Возможно, в форму был помещен элемент управления, который впоследствии оказался ненужным, или случайно было создано слишком много копий элемента управления; либо удаление требуется по каким-либо иным причинам. Для удаления элемента управления выберите его, а затем нажмите клавишу <Delete>. Редактор Visual Basic удалит выбранные элементы управления.
Редактирование иди форматирование заголонка элемента упрандения
Чтобы отредактировать текст заголовка такого элемента управления, как кнопка, переключатель или флажок, достаточно щелкнуть над текстом. Редактор Visual Basic поместит курсор вставки в текст заголовка. Теперь можно отредактировать текст, используя любые стандартные команды редактирования текста Windows, с которыми читатели уже знакомы. Для изменения гарнитуры шрифта, стиля или размера шрифта текста элемента управления необходимо отобразить окно Properties элемента управления и изменить свойство Font. (Использование окна Properties применительно к элементам управления описано ниже в этой главе.) Для отображения всего вводимого заголовка может потребоваться изменение размеров надписи, флажка, переключателя, кнопки или других объектов.
Для редактирования текста в заголовке формы отредактируйте ее свойство Caption в диалоговом окне Options (Параметры). Для форматирования текста заголовка формы следует использовать свойство Font.

Некоторые элементы управления, такие как поля, списки и поля со списком, не имеют собственных заголовков (или надписей). Используйте инструмент Label (Надпись) для помещения элементов управления Label в форму рядом с элементами управления, которые не имеют собственных надписей.
У правление последовательностью перехода
Когда форма активна, она ведет себя подобно другим диалоговым окнам Windows. Пользователь диалогового окна может осуществлять переход от одного элемента управления к другому с помощью клавиши <ТаЬ>; для перехода между элементами управления в обратном направлении можно использовать комбинацию клавиш
556	Неделя 3
<Shift+Tab>. Порядок, в котором элементы управления становятся активными в при нажатии пользователем клавиши <ТаЬ> или комбинации клавиш <Shift+Tab>, называется последовательностью перехода элементов управления. Обычно требуется, чтобы переход по элементам управления выполнялся слева направо и сверху вниз; в общем случае следует убедиться, что последовательность перехода обеспечивает логически обоснованное перемещение по элементам управления в диалоговом окне.
Редактор Visual Basic назначает последовательность перехода по умолчанию всем элементам управления по мере их добавления в форму. Следовательно, последовательность перехода по умолчанию соответствует порядку помещения элементов управления в форму. Однако часто к моменту помещения в форму всех элементов управления и перемещения их с целью придания им нужного вида последовательность перехода по умолчанию больше не обеспечивает логическую последовательность перемещения по элементам управления диалогового окна.
Для изменения порядка перехода элементов управления в форме выполните следующие действия.
1.	Выберите команду View-Tab Order (Вид-Последовательность перехода); редактор Visual Basic отобразит диалоговое окно Tab Order, показанное на рис. 16.4. В списке Tab Order отображаются все элементы управления формы в том порядке, который действует в текущий момент. (На рис. 16.4 показано диалоговое окно Tab Order для диалогового окна Make Regional Sales Chart, использованного в первом примере программы, приведенном ниже в этой главе.)
2.	В списке Tab Order выберите элемент управления, позицию которого нужно изменить.
ПРИМЕЧАНИЕ
Хотя надписи отображаются в диалоговом окне Tab Order и их позицию в списке можно изменить, в действительности VBA не делает надпись активным элементом управления; эти элементы управления предназначены только для отображения текста.
3.	Используйте кнопки Up (Вверх) или Down (Вниз) для изменения позиции выбранного элемента управления в списке последовательности перехода. Щелкните на кнопке Up для перемещения элемента управления вверх в списке (т.е. чтобы он значился выше в списке последовательности перехода) или на кнопке Down, чтобы переместить его вниз (т.е. сделать следующим в последовательности перехода позднее).
Рис. 16.4. Используйте диалоговое окно Tab Order для изменения порядка перехода по элементам управления формы
День 16-й. Создание настраиваемых диалоговых окон
557
4.	Повторите пп. 2 и 3 для каждого элемента управления, пока не получите требуемую последовательность перехода по элементам управления в форме.
5.	Щелкните на кнопке ОК в диалоговом окне Tab Order, чтобы подтвердить изменения и закрыть диалоговое окно.
Так надо
НЕ ЗАБУДЬТЕ проверить последовательность перехода элементов управления формы, чтобы убедиться, что она обеспечивает логичную последовательность перемещения по элементам управления.
Определение свойств формы о элемента управления в режомв конструктора
Каждая форма и каждый элемент управления обладает собственным набором свойств, подобно любому другому объекту VBA или основного приложения. Существует несколько свойств форм и элементов управления, которые и проще, и рациональнее устанавливать, когда форма отображается в режиме конструктора, чем делать это в тексте VBA-программы. Свойства, устанавливаемые для формы или элемента управления в режиме конструктора, становятся новыми свойствами по умолчанию для этой формы или элемента управления.
Например, хотя шрифт, цвет фона и цвет изображения элемента управления можно определить в тексте VBA-программы, изменять эти свойства во время выполнения программы обычно не требуется. Гораздо проще определить их нужным образом в режиме конструктора. Также можно присвоить элементам управления в форме значения по умолчанию, установив свойство Value элемента управления. Например, чтобы создать в форме флажок, который устанавливается по умолчанию, нужно было бы в режиме конструктора установить значение свойства Value флажка равным True. Этот флажок будет устанавливаться при каждой загрузке формы в память во время выполнения.
Аналогично этому во время конструирования можно заполнить списки, определив их свойство RowSource. В Excel список можно связать с рядом ячеек рабочего листа с помощью свойства RowSource, а поле — с ячейкой листа, установив свойство Controlsource. Поскольку требуется, чтобы свойства форматирования и значения по умолчанию элемента управления были более или менее постоянными, наиболее рационально определять эти (и другие) свойства в режиме конструктора.
Значение по умолчанию для поля можно также создать, введя значение непосредственно в элемент управления поля в форме. Затем, когда бы ни отображалась форма, в поле будет отображаться введенный текст по умолчанию.
В режиме конструктора свойства формы или элемента управления устанавливаются с помощью диалогового окна Properties. В этом диалоговом окне приводятся все свойства объекта, которые можно определять в режиме конструктора. Конкретные свойства, отображаемые в окне Properties, зависят от конкретного выбранного элемента управления. На рис. 16.5 показано диалоговое окно Properties кнопки btnCreate формы frmSalesChart, используемой в примерах ниже в этой главе.
558
Неделя 3
Microsoft Visual Basic - Day16.xls
Ffcrmet Qdbug Ban loots AdcHns Jflfihdow Hslp“
ЯI /	v "и м	gf"
ГГГ
Properties htrit
- Daylfi (DayJ6 xK)
_ Microsoft Excel Ots
] Sheetl (Sample
Sheet2 (Sales
О Sheets (Bar Ch
) Sheet4 (Line C: ThisWorkbook ; Й 'Э Forms
ESI frmCItyLlst I
El frrnCItyListCorr i
j 9 frmCOCA f El frmColumnSor
{btnCreate CommandButton dtihabetc ] categorized]
(Нэте)
btnCreate
® ©Modules
Accelerator AutoSize ? BackColor | Backstyie sCancel I Caption
Control?ipT ext Default Enabled
j Font =oreColor Height HelpContextiD Left Locked Mouseicon
; Mousepointer i Picture
.False
□ &H0OOOOOOF
1 - tmBackStyle False
Create
True True Tahoma  &H8000001Z Д8 0 216 False (None)
•0 - fmMousePoi (None)
xi!
• C Line Chart

Рис. 16.5. Диалоговое окно Properties, в котором приведен список свойств в режиме Alphabetic
Диалоговое окно Properties имеет две страницы. Первая страница, показанная на рис. 16.5, представляет собой алфавитный список всех свойств объекта. Вторая страница, показанная на рис. 16.6, — список свойств объекта по категориям. На рис. 16.6 показано то же диалоговое окно Properties, что и на рис. 16.5, с отображаемым списком свойств Categorized (По категориям). Обратите внимание на различные категории, выделенные жирным шрифтом: Appearance, Behavior, Font и т.д. Список свойств в конкретной категории можно развернуть или свернуть, щелкнув на прямоугольнике слева от заголовка каждой категории.
Для установки свойства формы или элемента управления щелкните на поле, соответствующем свойству, которое нужно изменить. Для большинства свойств отображается кнопка раскрывающегося списка. Щелкните на кнопке списка и выберите из него нужное значение свойства. Некоторые свойства (такие как Name и Caption) позволяют вводить любые текстовые значения. При выборе некоторых свойств отображается кнопка с многоточием (...). Щелчок на этой кнопке открывает дополнительные диалоговые окна, помогающие установить данное свойство.
Для изменения свойства формы или элемента управления выполните следующие действия.
1.	Выберите элемент управления, свойства которого нужно изменить. Чтобы выбрать форму, щелкните в любом участке ее поверхности, не занятом каким-либо элементом управления.
2.	Выберите команду View-Properties Window (Вид-Окно свойств), чтобы отобразилось диалоговое окно Properties. (Или же щелкните на кнопке Properties в панели инструментов редактора Visual Basic, или нажмите клавишу <F4>.)
3.	Заполните требуемые значения свойств для выбранного элемента управления. Перечень часто используемых свойств и их назначений приведен в табл. 16.5.
День 16-й. Создание настраиваемых диалоговых окон
559
Microsoft Visual Basic- Dny16.xls
*fde yiew Insert Format Qebug Bun Jools Add-Ins tttfndow Help
|8a-a	< > и .к «й-15Л 0
** I mpi'rlli tlhll ^ввв
al
iSl
IhtnCreate^^mandButt^
- DayJfi (Daylfi xls) s EL Microsoft Excel Ot*
Sheetl (Sampr
. к <] Sheet2 (Sales
<] Sheets (Bar Ch < в Sheet4 (Line C ~ S Th is Workbook
E Appearance
ES frmCItytist i
3 frmCItyLfctConn i 3 frmCOCA
'	3 frmColumnSor'i
3ff^atw*ert
щ I2J Modules
BackColor	О &H800000
BackStyle	1-fmBackSty
Caption	Create
Control!ipTexr ForeColor	”
Visible
□ Behavior AutoSize Cancel Default Enabled Locked TakeFocusOnC I True Wordwrap False
S Font Font
S Wise
 &Н8000СЮ True
Г LihChi
Cares' |
False False True True False
Tahoma
Puc. 16.6. Диалоговое окно Properties, отображающее список свойств в режиме Categorized
4.	Щелкните на кнопке Close (Закрыть) в верхнем правом углу диалогового окна Properties, чтобы закрыть его.
Окно Properties может быть расположено у границы окна редактора Visual Basic, подобно панели инструментов, или же может использоваться в виде плавающего окна, как показано на рис. 16.5 и 16.6. Режим отображения устанавливается так же, как и для любой панели инструментов.
Так надо
УЕЗДИТЕСЬ, что форма содержит, по меньшей мере, одну кнопку со свойством Cancel, установленным в True; это позволит диалоговому окну отвечать на нажатие клавиши <Esc>.
Отображение форм с помощью VBA
VBA использует созданный вами графический дизайн формы — с настройками свойств формы и элементов управления — для получения всей информации, необходимой для отображения диалогового окна: размеров диалогового окна, элементов управления в нем и т.п. В результате VBA позволяет отобразить форму диалогового окна с помощью единственной инструкции.
Для отображения нестандартного диалогового окна используется метод Show объекта UserForm.
FormName.Show
560
Неделя 3
В этом примере синтаксиса FormName представляет любой объект нестандартной формы в текущем проекте. FormName — это имя формы, отображаемое в окне Project Explorer. Например, при наличии формы, названной frmSalesChart, ее можно, было бы отобразить на экране с помощью следующей инструкции.
frmSalesChart.Show
Если в настоящий момент форма не загружена в память, метод Show загружает форму и отображает ее. Если форма уже загружена, метод Show просто отображает ее. В любом случае этот метод отображает форму, а затем присваивает ей фокус. Форма остается на экране до тех пор, либо пока не будет выполнен метод Hide объекта UserForm, либо пока форма не будет выгружена с помощью инструкции Unload. Форма остается загруженной в память до тех пор, пока экземпляр формы не выйдет за пределы видимости, — т.е. процедура, создавшая этот экземпляр объекта формы, перестанет выполняться — либо пока форма не будет выгружена с помощью инструкции Unload.
Хотя программа в модуле класса формы будет выполняться в результате события в диалоговом окне, общее выполнение программы приостанавливается для тех пор, пока форма диалогового окна не будет закрыта или скрыта. По умолчанию все формы VBA являются модальными по отношению к приложению, т.е. в приложении нельзя выполнять никаких других действий до тех пор, пока форма диалогового окна не закрыта или не скрыта.
ПРИМЕЧАНИЕ
Когда VBA выполняет метод Show для отображения формы диалогового окна, выполнение процедуры, содержащей вызов метода Show, приостанавливается др закрытия отображенной формы пользователем. Однако VBA будет выполнять программы любых процедур событий в модуле класса формы. (Создание процедур событий описано в следующем разделе.)
Использование VBA с элементами управления а форме
Отображения одного диалогового окна для выполнения задачи обычно недостаточно. Почти всегда требуется определить состояние элементов управления диалогового окна с целью выяснить, какие данные или опции выбрал пользователь. Например, если диалоговое окно используется для получения от пользователя информации о том, по каким столбцам и строкам должно выполняться упорядочение рабочего листа, необходимо иметь возможность выяснить, какие значения пользователь ввел после закрытия диалогового окна и до действительного начала операции упорядочивания.
В других случаях может потребоваться динамическое изменение заголовков кнопок (или других элементов управления) диалогового окна, динамическое обновление надписи или поля, связанного со счетчиком, или динамическое подтверждение введенных в диалоговое окно данных. Например, предположим, что имеется диалоговое окно, используемое для ввода данных в разрабатываемой программе учета. Может потребоваться подтверждение учетного кода или названия подразделения, введенного в поле, немедленно после ввода данных пользователем.
Для выполнения первой задачи необходимо иметь доступ и возможность манипулировать элементами управления диалогового окна после его закрытия (но не выгрузки из памяти). Для выполнения второй задачи необходимо иметь возможность создавать процедуры, отвечающие на события в элементах управления диалогового окна, чтобы процедура выполнялась автоматически самим элементом управления. Создание процедур событий — еще один способ заставить кнопку выполнять какое-либо действие.
В следующих разделах показано, как создавать процедуры событий и как получать доступ к элементам управления в форме диалогового окна.
День 16-й. Создание настраиваемых диалоговых окон	561
Создание процедур событии н других программ формы
Работая с формами диалоговых окон и их элементами управления, читатели узнают, что существует много случаев, когда щелчок на элементе управления или изменение его данных требует немедленного ответа элемента управления (или какой-либо другой части программы). VBA позволяет определять процедуры событий для ответа на конкретные события. В случае создания процедуры события для конкретного события элемента управления эта процедура выполняется при каждом наступлении данного события.
Каждый объект UserForm, добавляемый в проект, становится в проекте новым классом объекта. Каждый объект UserForm имеет наследуемый модуль класса, в котором нужно создавать процедуры событий и другие фрагменты программы, связанные с формой диалогового окна. Однако прежде чем приступать к написанию текста программы в форме, следует познакомиться с тем, как действует модуль класса формы.
Как уже известно читателям, первой частью любого модуля является область General. Она содержит область объявлений. В начале области объявлений помещаются все директивы компилятора, за которыми следуют все объявления на уровне модуля для переменных, констант и определяемых пользователем типов. За областью объявлений в области General модуля класса следуют процедуры Property Get или Property Let и любые дополнительные объявления процедур или функций. Как видите, для формы можно создавать нестандартные свойства и в нее можно добавлять специальные методы с объявлениями дополнительных процедур и функций, как это делается с любым другим модулем класса.
До сих пор освещались элементы модуля класса, с которыми читатели уже знакомы. Однако остальная часть модуля класса формы содержит раздел для каждого объекта в форме, включая саму форму. Именно в этой области создаются процедуры событий для формы и элементов управления.
На рис. 16.7 показано окно Code (Программа), в котором отображен модуль класса для формы frmSalesShart. (Эта форма используется в первом примере этого урока). На рис. 16.7 список Object (Объект) окна Code развернут. Обратите внимание, что в нем содержится список всех объектов элементов управления, перечисленных по именам. (Имя элемента управления — это содержимое его свойства Name.) Выбор объекта, для которого нужно написать процедуру события, осуществляется в списке Object.
На рис. 16.8 показано это же окно программы, но на нем развернут список Procedure (Процедура). Обратите внимание, что элемент управления btnCreate выбран в списке Object; в списке Procedure показаны все события, поддерживаемые этим объектом элемента управления. Для отображения VBA-программы для конкретного события выберите событие в списке Procedure. В окне Code отобразится процедура события (если она уже написана) для этого события. Если процедура события еще не написана, в окне Code отобразится объявление процедуры с соответствующим именем для данного объекта и события. На рис. 16.7 и 16.8 процедура btnOK Click видна в окне Code; эта процедура обрабатывает событие щелчка для объекта элемента управления btnCreate, являющегося кнопкой.
ПРЕДУПРЕЖДЕНИЕ
VBA полагается на процедуры событий, имеющие специальные имена, что позволяет сопоставлять процедуру с конкретным объектом или событием. Если изменить имя процедуры события так, что оно не будет более соответствовать форме ИмяОбъек-та_ИмяСобытия, VBA утратит ассоциативную связь процедуры с конкретным объектом и событием. Процедура остается в модуле, но становится невидимой в части General модуля формы.
562
Неделя 3
Microsoft Visual Basic - Daylbxls
ГГГ
J Bls EA View Insert fiarmat Qsbug Bun loots Д<Й4м tfmtow Help ,.'t> 4'f	, , . t- i
S.D-H x M 	► II HiffW 4 9 LnBSCon
Ж Dayl 6 xis - frmSalesChart (Code)
........ fWtf
s; Dayl6 (Dayi6.xls)
F it* Microsoft Excel Objects
® Sheetl (Sample Data)
! Ю Sheet2 (Safes Report)
:	SheeG (Bar Chart)
r 8ij Sheet* (Line Chart) ThisWorkbook
F Forms
3
| btnCreate "IbtnCancel
IcmbDstSheet fraChartType Label2 Label3 Label4 optBarChart optLineChart IrefSrcRange txtChartTitle jUserForm
I Click
ick ()
BEE
i data source, destinations Value) = "" Or __
j.Value) - "" Or _	t
;le.Value) « "" Then
jlittiCfftate
MsgBox prompt: = "You must enter a source ran' "a destination sheet, and а цй
Buttons:=vblnformation, _	I
Title: =scBoxTitle	-iU
Exit Sub	’
End If
'гпака sure the selected destination exists If Not SheetExists(ActiveWorkbook, .cmbDstShe,
Puc. 16.7. Выберите объект формы, для которого нужно написать процедуру события, в списке Object
££ Microsoft Visual Basic - Dayl 6 xis
I E'le Edit yiew Insert Ffiimat Qsbug Bun Tools Add-Ins ^ndo* Help	<
ai-O - -ем ‘ к II att	- F 
F DaylG (Dayi6.xls)
Ж Dayl 6.xls - frmSalesChart (Code)
fbtntipatp
BEEjl
F Microsoft Excel Objects
Sheetl (Sample Data) ® Sheet2 (Sales Report) : Bj SheeG (Bar Chart) г О Sheet* (Line Chart)
Q ThisWorkbook
- Forms
3 frmCityList
•• 3 frmCityListCombo
! 3 frmCOCA
3 frmColumnSort
3 frmSatesChart
F ft* Modules ' *«£ Exercised 1
Exercised?
.	ListingOS
.	Listinq04
Listing06
Private Sub btnCreate Cl.
Dim Ans As Integer
With Me
'make sure thereTs a If Trim(.cmbDstSheet
Trim(.refSrcRange Trim(.txtChartTit.
flirk
BeforeDropOrPaste
I Click
DbICIick Enter Error Exit KeyDown KeyPress KeyUp MouseDown MouseMove MouseUp
 MsgBox prompt:="You must enter a source ran: "a destination sheet, and a
Buttons: =vblnformation, __ Title:=scBoxTitle
Exit Sub
End If
’make sure the seiectea destination exists
If Not SheetExists(ActiveWorkbook, .cmbDstshes
Puc. 16.8. Чтобы создать или отредактировать процедуру обработки события, выберите событие объекта в списке Procedure
День 16-й. Создание настраиваемых диалоговых окон
563
Чтобы написать процедуру события (или другую часть программы) для формы или элемента управления, выполните следующие действия.
1.	В окне Project Explorer выберите объект UserForm, содержащий элемент управления, для которого нужно создать процедуру события.
2.	Щелкните на кнопке Code (Программа) или выберите команду View-Code (Вид-Программа), чтобы в окне Code отобразился модуль класса формы.
3.	В списке Object окна Code выберите элемент управления, для которого хотите создать процедуру события (см. рис. 16.7).
4.	В списке Procedure окна Code выберите событие, которое нужно обрабатывать (см. рис. 16.8).
Как только событие, для которого нужно создать процедуру, будет выбрано, редактор Visual Basic сделает одно из двух: если выбранный элемент управления уже имеет процедуру для данного события, редактор Visual Basic отобразит ее в окне Code. Если элемент управления не имеет процедуры для этого события, редактор Visual Basic создаст объявление новой процедуры события и поместит курсор вставки в тело процедуры, готовый для начала создания нового программного текста. Если процедура события вам не нужна, просто удалите ее из модуля формы.
Так надо
ПЕРЕИМЕНОВЫВАЙТЕ элементы управления перед созданием процедуры события для элемента управления. VBA не будет переименовывать процедуры событий при изменении имени элемента управления - вам придется вручную переименовать каждую процедуру события, созданную для элемента управления, имя которого было изменено.
ПОМНИТЕ, что для получения доступа к процедурам событий формы необходимо выбрать объект UserForm в списке Object окна Code.
СТАРАЙТЕСЬ делать объекты формы максимально самостоятельными. При создании объектов формы применяйте все правила программирования и рекоменд ации по дизайну модуля класса, о которых узнали на 11 -м уроке.
ПОМНИТЕ, что программа в модуле класса формы может вызывать процедуры или функции, хранящиеся в стандартных модулях.
Так надо
ПОМНИТЕ, что сокрытие формы с помощью ее метода Hide оставляет форму со всеми ее переменными в памяти. Выгрузка формы прерывает выполнение данного экземпляра формы, и все ее внутренние значения теряются.
УБЕДИТЕСЬ, что скрываете форму, а не выгружаете ее, если нужно обращаться к значениям элементов управления диалогового окна из программы в стандартном модуле.
ИМЕЙТЕ В ВИДУ, что все процедуры событий должны быть объявлены как Private для модуля класса формы.
Создаем диалоговое окно
В этом первом примере форма служит для создания диалогового окна, которое использует поле, надписи, рамку, переключатели, элемент управления RefEdit и несколько кнопок. На рис. 16.9 показано диалоговое окно в режиме конструктора, которое предстоит создать для использования с программой, приведенной в листингах 16.1 и 16.2. На рис. 16.10 показана форма этого же диалогового окна во время выполнения.
564
Неделя 3
•^1 Microsoft Visual Flnsic - Day! 6 xlt
41 ExerciseOl
: 41 Exercse02 41 ListhgO2 4t ListingCH
-41 Llsting06
Puc. 16.9. Форма диалогового окна Make Sales Chart в режиме конструктора
txtCharTitle
optBarChart
optLineChart
refSrcRange
cmbDstSheet
btnCreate
btnCancel
Puc. 16.10. Диалоговое окно Make Sales Chart во время выполнения
Многие пользователи Excel повседневно работают с аналогичными наборами данных, часто создавая диаграммы или схемы данных. При просмотре конкретных наборов данных, таких как месячные или квартальные итоги продаж, большинство пользователей Excel предпочитают один-два формата диаграмм всем другим. Например, может оказаться, что для вас (или вашей организации) конкретный стиль линейчатой диаграммы является наиболее наглядным способом просмотра данных из квартального отчета о продажах.
В подобном случае, хотя каждый раз создается диаграмма другого набора данных, может оказаться, что постоянно приходится вызывать мастер Chart Wizard и выбирать в нем одни и те же опции при каждом создании новой диаграммы. Многократный выбор всех опций и параметров в мастере Chart Wizard для повторного создания по
День 16-Й. Создание настраиваемых диалоговых окон
565
существу одной и той же диаграммы является неоправданно трудоемким и утомительным процессом. Работу можно ускорить, создав VBA-процедуру для создания требуемых диаграмм. Однако, как вы уже, вероятно, убедились, ввод диапазонов данных в диалоговом окне InputBox — трудный и чреватый ошибками процесс. При этом нельзя видеть рабочий лист, содержащий исходные данные диаграммы, что увеличивает вероятность ввода вами (или пользователем вашей процедуры) неверного или неполного диапазона и необходимости выполнять задачу снова.
Процесс создания предопределенных диаграмм можно упростить и ускорить, создав диалоговое окно, в котором можно вводить исходный диапазон диаграммы и в котором должна быть создана диаграмма, а затем использовать процедуры событий формы для автоматической вставки правильно отформатированной диаграммы в рабочую книгу. Если использовать элемент управления RefEdit для ввода исходных данных диаграммы пользователем, ему предоставляется возможность просматривать рабочие листы и выбирать диапазон данных для новой диаграммы, когда диалоговое окно находится на экране. Элемент управления будет автоматически заполнять ссылку на диапазон, когда пользователь выбирает диапазон из рабочего листа.
Нестандартное диалоговое окно, показанное на рис. 16.10, вместе с программой, приведенной в листинге 16.1, создает нестандартное диалоговое окно, позволяющее пользователю выбирать диапазон данных, которые должны быть представлены в виде диаграммы, место назначения результирующей диаграммы, заголовок диаграммы и одни из предопределенных форматов диаграммы. Щелчок на кнопке Create в диалоговом окне выполняет действительную вставку новой диаграммы.
Прежде чем можно будет приступить к работе с текстом программы, представленным в листинге 16.1, необходимо создать форму и диалоговое окно, в модуле класса которого будет введен исходный текст, представленный в листинге 16.1. Чтобы приступить к созданию этого демонстрационного диалогового окна, выполните следующие действия.
1.	Создайте новую рабочую книгу в Excel.
2.	Откройте редактор Visual Basic и используйте команду insert-UserForm, чтобы добавить форму в проект.
3.	Используйте окно Properties, чтобы изменить свойство Name формы на frmSalesChart, а ее свойство Caption — на Make Regional Sales Chart.
4.	Используя технические приемы, описанные ранее в этом уроке, добавьте в форму элементы управления и отредактируйте их заголовки так, чтобы форма выглядела подобно показанной на рис. 16.9. Помните, что надписи для полей должны быть добавлены отдельно.
ПРИМЕЧАНИЕ
При добавлении элементов управления в форму frmSalesChart, показанную на рис. 16.9 и 16.10, включите элемент управления Frame до добавления переключателей внутри рамки; в противном случае элементы управления не будут располагаться внутри рамки.
5.	Теперь установите свойство Name для элементов управления в форме, чтобы они имели имена, показанные на рис. 16.10. При выполнении этого пункта будьте внимательны: убедитесь в точном соответствии имен элементов управления с приведенными на рис. 16.10; в противном случае программа, приведенная в листинге 16.1, будет работать неправильно.
6.	Используйте окно Properties для установки следующих свойств: установите равным True свойства Value элемента управления optBarChart (это приведет к выбору опции optBarChart при первоначальном открытии диалогового окна) и Cancel
566
Неделя 3
элемента управления btnCancel (это приведет к тому, что нажатие клавиши <Esc> при открытом диалоговом окне будет оказывать такой же эффект, как и щелчок на кнопке btnCancel).
7.	Выберите форму frmSalesChart в окне Project Explorer и щелкните на кнопке Code, чтобы в окне Code отобразился модуль класса формы.
8.	В модуль класса формы frmSalesChart введите текст программы из листинга 16.1.
Диалоговое окно frmSalesChart является полностью самостоятельным объектом. Когда это диалоговое окно отображается на экране, в первом поле (элементе управления Ref Edit) нужно ввести диапазон для исходных данных диаграммы (используя запись R1C1), в поле со списком выбрать рабочий лист, в котором нужно создать диаграмму, в третьем поле ввести заголовок диаграммы и, наконец, в рамке выбрать столбчатую (Bar Chart) или линейную (Line Chart) диаграмму.
Для ввода диапазона в элемент управления refSrcRange пользователь может либо ввести диапазон непосредственно в поле элемента управления, либо выбрать диапазон из рабочего листа. Для выбора диапазона непосредственно из рабочего листа нужно щелкнуть на кнопке у правого края элемента управления refScrRange. В результате диалоговое окно будет свернуто до размера элемента управления refScrRange, как показано на рис. 16.11. При этом пользователь имеет возможность выбирать листы в текущей книге и диапазоны в листе. Когда пользователь выбирает диапазон — с помощью перетаскивания мыши — координаты выбранного диапазона (в записи R1C1) автоматически вводятся в элемент управления refScrRange. На рис. 16.11 показан результат выбора диапазона в рабочем листе с отображением выбранного диапазона в элементе управления refScrRange.

'Е) файл Древка Дид Вставка Формат Сервис Денные Дкно Справка	• — jiSl xj
B4	w-	- -	"	--- -..........
1 A | В \ C ... I .	E_ i Г G
1 2
3
5 6
7 8
Make Regional Sales Chad
BLUE SKY AIRLINES Продажи
^Филиал	Январь	Февраль
]Север	10111.	13400]
;Юг	22100	24050;
росток	13270	156701
13апад___	10800	„„21500]
('Sales Report'!$B$<:$D$8|
12
13
14
15
16
17
18
К < ► M;\ Sample Data
Укажите
NUM
Puc. 16.11. Элемент управления Ref Edit позволяет выбирать диапазоны данных в рабочем листе при активном диалоговом окне
День 16-Й. Создание настраиваемых диалоговых окон
567
Обратите внимание, что на рис. 16.11 кнопка в элементе управления refScrRange изменилась, отображая рамку со стрелкой. Повторный щелчок на этой кнопке приводит к разворачиванию диалогового окна до первоначального вида, как показано на рис. 16.12 (где отображено диалоговое окно со всеми заполненными элементами управления).
FH Mirmsnfl Fxcel Fi x|‘.
Файл Правка Биа Вставка Форцат Сервис Данные Дкно Оправка
-г й е, “п-*. 	. л (э ? ’
1 BLUE SKY AIRLINES Продажи
Make Regional Sales Chart
Ян1
Запад
Select Chart Type  ---y—f
S Bar Chart Г I
.JsJx.
Create |
Cancel |
М < > м\Sample Data \ Sales Report/ваг Chart / Lne Chart/ |<|	_	|	>j[-’
Готово	’	NUM
Рис. 16.12. Диалоговое окно frmSalesChart со всеми заполненными параметрами
Для выбора рабочего листа в качестве места назначения новой диаграммы используйте поле со списком cmbDstSheet. Как поясняется в анализе листинга 16.11, список cmbDstSheet заполняется — т.е. список заполняется записями — при каждой активизации формы. Список этого элемента управления заполняется списком всех рабочих листов, существующих в книге. Элемент управления поля со списком cmbDstSheet сконфигурирован со своими свойствами по умолчанию и поэтому действует, как истинное поле со списком: пользователь может выбрать элемент из списка или ввести текст в поле элемента управления.
Пользователь выбирает рабочий лист, который должен содержать новую диаграмму, выбирая существующий лист из раскрывающегося списка элемента управления cmbDstSheet или вводя имя нового рабочего листа. Как поясняется в анализе листинга, если пользователь вводит имя листа, который отсутствует в списке, диалоговое окно frmSalesChart предложит создать новый лист.
Элемент управления txtChartTitle в диалоговом окне frmSalesChart — стандартное поле; в нем пользователь может ввести любой текст в качестве заголовка новой диаграммы.
Для выбора одного из двух предопределенных стилей диаграммы нужно щелкнуть на одном из переключателей: optBarChart или optLineChart. Переключатели используются для этого выбора потому, что он является взаимоисключающим: нельзя создать
568
Неделя 3
столбчатую и линейную диаграмму одновременно, поэтому пользователь должен выбрать только одну из них. Поскольку оба переключателя находятся внутри элемента управления Frame, элемент управления optBarChart автоматически очищается при щелчке на optLineChart и наоборот.
И наконец, для создания новой диаграммы пользователь должен щелкнуть на кнопке btnCreate. Процедура события btnCreate_Click для элемента управления btnCreate выполняет задачу подтверждения записей, введенных пользователем в диалоговое окно, а затем создает указанную диаграмму. Для отмены процесса создания диаграммы пользователь может щелкнуть на кнопке btnCancel, нажать клавишу <Esc> или щелкнуть на кнопке Close диалогового окна frmSalesChart. Отмена диалогового окна не оказывает никакого действия, за исключением исчезновения окна из вида.
Листинг IB.1. Модуль класса формы frmSalesChart
1:	Option Explicit
2:
3:	Const scBoxTitle = "Диаграмма продаж"
4:
5:	Private Function SheetExists(Book As Workbook,
6:	ShtName As String) As Boolean
7:	'Возвращает true, если лист существует в указанной книге.
8:	Dim Sht As Object
9:
10:	SheetExists = False
11:	For Each Sht In Book.Sheets
12:	If StrComp(Sht.Name, ShtName, vbTextCompare) = 0 Then
13:	SheetExists = True
14:	Exit Function
15:	End If
16:	Next Sht
17:	End Function
18:
19:	Private Sub MakeLineChart()
20:	'Создает линейную диаграмму в фиксированной позиции, на листе,
21:	'выбранном в списке cmbDestSheet.
22:	'Диапазон данных указан в упр. эл-те refSrcRange.
23:	'
24:
25:	Dim iChart As Object
26:
27:	'Создание диаграммы на указанном листе
28:	With Sheets(Trim(Me.cmbDstSheet.Value)).Chartobjects
29:	Set IChart = .Add(96, 37.5, 234, 111)
30:	End With
31:
32:	With IChart.Chart
33:	.ChartType = xlLine
34:	.SetSourceData Source:=Range(Me.refSrcRange.Value),
35:	PlotBy:=xlColumns
36:	.HasTitle = True
37:	.ChartTitle.Characters.Text = Me.txtChartTitle
38:	.ApplyDataLabels Type:=xlDataLabelsShowNone, LegendKey:=False
39:	.HasDataTable = False
День 16-й. Создание настраиваемых диалоговых окон	569
40:	.Axes(xlCategory, xlPrimary).HasTitle = False
41:	.Axes(xlValue, xlPrimary).HasTitle = False
42:	End With
43:
44:	With IChart.Chart.Axes(xlCategory)
45:	.HasMajorGridlines = False
46:	.HasMinorGridlines = False
47:	End With
48:
49:	With IChart.Chart.Axes(xlValue)
50:	.HasMajorGridlines = True
51:	.HasMinorGridlines = False
52:	End With
53:	End Sub
54:
55:	Private Sub MakeBarChart()
56:	'Создает столбчатую диаграмму в фиксированной позиции, на листе,
57:	'выбранном в списке cmbDestSheet.
58:	'Диапазон данных указан в упр. эл-те refSrcRange.
59:	'
60:
61:	Dim bChart As Object
62:
63:	'Создание диаграммы на указанном листе
64:	With Sheets(Trim(Me.cmbDstSheet.Value)).Chartobjects
65:	Set bChart = .Add(96, 37.5, 234, 111)
66:	End With
67:
68:	With bChart.Chart
69:	.ChartType = xlBarClustered
70:	.SetSourceData Source:=Range(Me.refSrcRange),
71:	PlotBy:=xlColumns
72:	.HasTitle = True
73:	.ChartTitle.Characters.Text = Me.txtChartTitle
74:	.AxesjxlCategory, xlPrimary).HasTitle = False
75:	.Axes(xlValue, xlPrimary).HasTitle = False
76:	.ApplyDataLabels Type:=xlDataLabelsShowNone,
77:	LegendKey:=False
78:	End With
79:	End Sub
80:
81:	Private Sub btnCancel_Click()
82:	Me.Hide
83:	End Sub
84:
85:	Private Sub btnCreate_Click()
86:
87:	Dim Ans As Integer
88:
89:	With Me
90:	'Проверяем, указаны ли данные
91:	If Trim(.cmbDstSheet.Value) = "" Or _
92:	Trim(.refSrcRange.Value) = "" Or _
570
Неделя 3
93:	Trim{ .txtChartTitle.Value) = Then
94:
95:	MsgBox prompt:="Вы должны указать диапазон, " S
96:	"лист и заголовок.”,
97:	Buttons:=vblnformation,
98:	Title:=scBoxTitle
99:	Exit Sub
100:	End If
101:
102:	'Существует ли указанный диапазон?
103:	If Not SheetExists(ActiveWorkbook,	.cmbDstSheet.Value) Then
104:	'предлагаем создать
105:	Ans = MsgBox{prompt:=Trim{.cmbDstSheet.Value) & _
106:	"не существует." & vbCr &
107:	"Создать его?",
108:	Buttons:=vbYesNo + vbQuestion,
109:	Title:=scBoxTitle)
110:	If Ans	= vbYes Then
111:	Worksheets.Add.Name = .cmbDstSheet.Value
112:	Else
113:	Exit Sub
114:	End If
115:	End If
116:
117:	'Создаем	диаграмму
118:	If .optBarChart	Then
119:	MakeBarChart
120:	Else
121:	MakeLineChart
122:	End If
123:
124:	'Скрываем форму
125:	.Hide
126:
127:	'Выводим лист с	диаграммой
128:	Sheetsf.cmbDstSheet. Value).Select
129:	End With
130:	End Sub
131:
132:	Private Sub UserForm_Activate{)
133:	'Заполнение комбинированного списка cmbDstSheet
134:
135:	Dim aSheet As Worksheet
136:
137:	With Me.cmbDstSheet
138:	.Clear
139:	For Each	aSheet	In ActiveWorkbook.Worksheets
140:	.Additem aSheet.Name
141:	Next aSheet
142:	.Value =	.List(O)
143:	End With
144:	End Sub
День 16-й. Создание настраиваемых диалоговых окон
571
Строки 1—79 представляют область General модуля класса формы frmSalesChart. Строка 1 — обычная директива компилятора Option Explicit, а строка 3 содержит константу заголовка, используемую для двух диалоговых окон MsgBox, которые frmSalesChart может отображать.
Также относящиеся к области General модуля класса формы frmSalesChart, строки 10—17 содержат функцию SheetExists, строки 19—53 содержат процедуру MakeLineChart, а строки 55—79 — процедуру MakeBarChart. Функция SheetExists проверяет наличие рабочего листа в указанной книге (работа этой функции уже была пояснена в обзоре 2-й недели).
Процедура MakeLineChart создает предопределенную линейную диаграмму, а процедура MakeBarChart — предопределенную столбчатую диаграмму. Обе процедуры вызываются процедурами событий формы и при выполнении своих задач используют значения из элементов управления формы frmSalesChart. Обратите внимание, что SheetExists, MakeLineChart и MakeBarChart в своих объявлениях применяют ключевое слово Private; эти процедуры и функции доступны только внутри модуля класса формы frmSalesChart.
Строки с 81 до конца листинга 16.1 содержат процедуры событий для элементов управления формы и процедуры событий для самой формы. Строки 81—83 содержат процедуру btnCancel_Click. Эта процедура вызывается, когда пользователь щелкает на кнопке btnCancel в форме. Процедура btnCancel содержит только одну инструкцию и вызывает собственный метод Hide формы, служащий для удаления формы с экрана. Скрытие формы оставляет ее в памяти и, следовательно, сохраняет значения элементов управления формы.
Строки 85—128 содержат процедуру события btnCreate_Click. Эта процедура выполняется при каждом щелчке на кнопке btnCreate в форме. (Хотя свойство Caption кнопки, отображаемое на поверхности кнопки также имеет значение “Create” именно свойство Name кнопки нужно использовать для ссылки на элемент управления в VBA-программе; свойства Name и Caption не обязательно должны быть одинаковыми.)
В процедуре btnCreate_Click строка 87 объявляет переменную типа Integer, содержащую ответ на вызов функции MsgBox, который может происходить далее в этой процедуре. Строка 89 начинается с инструкции With Me, немного упрощающей ссылку на элементы управления формы в последующей части программы.
Форма frmSalesChart не может выполнять свою задачу, если не имеет всей запрошенной информации: источника данных, подлежащих представлению в диаграмме, места назначения для помещения новой диаграммы, заголовка диаграммы и требуемого типа диаграммы.
Поэтому строка 91 процедуры btnCreate_Click начинает инструкцию If, логическое выражение которой проверяет три различных условия; если любое из этих трех условий истинно, диаграмма не может быть завершена. Если свойство Value элемента управления cmbDstSheet содержит пустую строку, никакой рабочий лист назначения не выбран. Аналогично, если свойство Value любого элемента управления refSrcRange или txtCharTitle является пустой строкой, эти опции также не установлены. Обратите внимание, что все эти выражения объединяются оператором 0г, и поэтому, если любое из них истинно, все выражение также истинно.
В случае отсутствия любой из обязательных записей строки 95—98 используют MsgBox для отображения сообщения для пользователя, объясняющего, почему диаграмма не может быть выполнена; строка 99 осуществляет выход из процедуры btnCreate Click. Диалоговое окно остается на экране, готовое для ввода пользователем недостающих записей.
572
Неделя 3
Если вся требуемая информация введена, строки 103—113 содержат инструкцию If, которая использует функцию SheetExists для определения того, совпадает ли значение, введенное в поле со списком cmbDstSheeet, с именем существующего листа в книге. Помните, что раскрывающийся список поля со списком просто содержит предлагаемые значения; пользователь может ввести в поле со списком любой текст. Если рабочего листа, указанного свойством cmbDstSeet.Value, еще не существует, строки 105—109 используют MsgBox для уведомления пользователя об этом и предлагают создать рабочий лист. Строки 109—115 обрабатывают ответ пользователя; если пользователь отвечает Yes (Да), стока 111 одновременно добавляет и именует новый рабочий лист, в противном случае строка 113 осуществляет выход из процедуры btnCreate_Click.
Далее, строки 118—122 вычисляют состояние элемента управления переключателя optBarChart. Поскольку существует только две возможные диаграммы, можно спокойно предположить, что переключатель optLineChart выбран всегда, когда optBarChart не выбран. Если переключатель optBarChart имеет значение True (т.е. выбран), строка 119 вызывает процедуру MakeBarChart; в противном случае строка 121 выполняется для вызова процедуры MakeLineChart.
Когда элементы управления OptionButton помещаются внутрь элемента управления Frame, элемент управления Frame обеспечивает одновременный выбор только одно-f ИгНМпЯПИс го переключателя. В форме frmSalesChart выбор переключателя optLineChart приводит к отмене выбора переключателя optBarChart (и наоборот), поскольку оба переключателя заключены в объект Frame.
И наконец, строка 125 вызывает метод Hide формы для удаления формы с экрана (без удаления ее из памяти); строка 128 выбирает рабочий лист назначения, делая его активным, чтобы пользователь мог видеть только что созданную диаграмму.
Строки 132—144 содержат процедуру UserForm_Activate. Эта процедура выполняется при каждом наступлении события Activate: всегда, когда форма становится активной. Процедура UserForm_Activate вначале вызывает метод Clear для элемента управления поля со списком cmbDstSheet (строка 138), чтобы последующие добавления были единственными элементами, появляющимися в списке. Строки 139-141 используют цикл For Each для добавления имени каждого существующего в книге листа в список элемента управления cmbDstSheet с помощью вызова метода Additem элемента управления в строке 140. И, наконец, строка 142 устанавливает значение по умолчанию для выбора в списке элемента управления cmbDstSheet, присваивая первый элемент списка (поля со списком и другие элементы управления списков являются массивами, начинающимися с нуля) свойству Value элемента управления.
В листинге 16.2 показана процедура, отображающая форму диалогового окна frmSalesChart. Введите текст программы из листинга 16.2 в стандартный модуль той же рабочей книги, в которой было создано диалоговое окно frmSalesChart.
Листинг 16.2. Процеддра уля отображения диалогового окна frmSalesChart
1:	Sub InsertSalesChart()
2:	frmSalesChart.Show
3:	End Sub
Листинг 16.2 содержит только одну процедуру с одной единственной инструкцией. Эта инструкция вызывает метод Show формы frmSalesChart для отображения диалогового окна на экране. Если процедура InsertSalesChart вызывается впервые в течение
День 16-й. Создание настраиваемых диалоговых окон
573
текущего сеанса работы, VBA загружает диалоговое окно frmSalesChart в память в качестве части задачи отображения диалогового окна на экране. Когда диалоговое окно появляется впервые, оно выглядит подобно показанному на рис. 16.10.
При каждом последующем выполнении процедуры InsertSalesChart значения, выбранные для источника данных, назначения, заголовка и типа диаграммы, сохраняются. Поскольку форма диалогового окна скрыта, но не выгружена, она сохраняет последние записи, сделанные в полях и других элементах управления.
Если вы закрываете проект, содержащий frmlnsertFigure, используете команду Reset или выполняете какое либо другое действие для выгрузки формы, элементы управления формы возвращаются в состояние по умолчанию и диалоговое окно снова приобретает вид, показанный на рис. 16.10.
В следующем разделе приведен пример формы диалогового окна, использующего внешнюю программу для модуля класса формы и другой набор элементов управления.
Так надо
 ИСПОЛЬЗУЙТЕ ключевое слово Me внутри модуля класса для ссылки на свойства и методы одного и того же экземпляра формы.
ИМЕЙТЕ В ВИДУ, что эта же технология программирования форм применима ко всем VBA-лриложениям, а не только к Excel.
Диалоговое окно о его программа в стандартном модуле
В предыдущем примере показана самостоятельная форма. Формы могут также использовать стандартные модули, как вы увидите в следующем примере. В этом примере форма диалогового окна используется для упорядочения столбцов рабочего листа.
Форма frmColumnSort обеспечивает простое диалоговое окно для получения информации о том, по каким строкам и столбцам следует выполнять упорядочение и какие столбцы следует использовать в качестве основы для упорядочения строк. Форма диалогового окна frmColumnSort использует поля, кнопки, счетчик и флажок.
Прежде чем можно будет использовать диалоговое окно frmColumnSort, необходимо создать форму и рабочий лист с некоторыми данными в нем. Чтобы приступить к созданию этой второй демонстрационной процедуры, выполните следующие действия.
1.	Откройте или создайте рабочую книгу Excel и переименуйте рабочий лист в книге, чтобы его именем было Sample Data.
2.	Введите следующие данные в первые три столбца и первые пять строк рабочего листа Sample Data.
1	222	1
3	123	3
4	34	5
5	114
22	2	2
3.	Запустите редактор Visual Basic и добавьте объект UserForm.
4.	Измените свойство Name объекта UserForm на frmColumnSort, а его свойство Caption — на Column Sorting.
5.	Используя технические приемы, описанные ранее в этом уроке, добавьте элементы управления в форму*и отредактируйте их заголовки, чтобы форма выгля
574
Неделя 3
дела аналогично показанной на рис. 16.13. Помните, что надписи для полей должны быть добавлены отдельно.
Рис. 16.13. Элементы управления диалогового окна Column Sorting и их имена
6.	Установите свойство Name для элементов управления формы в соответствии с рис. 16.13. Выполняя этот пункт, будьте внимательны: убедитесь, что имена элементов управления в точности соответствуют показанным на рис. 16.13, в противном случае программа, приведенная в листинге 16.3, будет работать неправильно.
7.	Откройте модуль класса формы и введите в него текст программы, приведенный в листинге 16.3.
Диалоговое окно frmColumnSort получает от пользователя сразу пять чисел. Первые четыре числа получаются с помощью элементов управления полей и определяют диапазон столбцов и строк. Пятое число получается с помощью комбинации счетчика и динамически обновляемого поля и указывает столбец, по которому выполняется операция упорядочения. И наконец, флажок Descending указывает, должна ли операция упорядочения выполняться в порядке возрастания или убывания.
После щелчка на кнопке Sort диалоговое окно frmColumnSort упорядочивает блок строк в выбранном порядке, используя указанный столбец для определения порядка строк. Для выполнения процесса упорядочения диалоговое окно frmColumnSort вызывает процедуру сортировки, хранящуюся в стандартном модуле. Процедура сортировки представлена в листинге 16.4 ниже в этом разделе.
Диалоговое окно frmColumnSort имеет одно важное отличие от диалогового окна frmSalesChart, приведенного в предыдущем примере. Диалоговое окно frmColumnSort остается на экране даже после выполнения своей главной задачи; оно является ‘плавающим” диалоговым окном. Для закрытия этого диалогового окна пользователь должен щелкнуть на кнопке Close или нажать клавишу <Esc>.
Листинг 1В.З. Мвдджь класса фврмы frmColumnSort
1:	Option Explicit
2:
3:	Private Function ValidateSelf() As Boolean
4:	'Проверяет содержимое управляющих элементов
6:	With Me
'только числа могут быть введены
3:	If (Not IsNumeric(.txtFirstCol)) Or _
День 16-й. Создание настраиваемых диалоговых окон
575
9i	(Not IsNumeric).txtFirstRow)) Or _
10:	(Not IsNumeric).txtLastCol)) Or _
11:	(Not IsNumeric).txtFirstCol)) Then
12:	MsgBox Title: = .Caption,
13:	prompt:=“Во всех полях должны быть только числа.",
14:	Buttons:=vbExclamation
15:	ValidateSelf = False
16:	Exit Function
17:	End If
18:
19:	'начальные должны быть меньше конечных
20:	If (.txtFirstRow > .txtLastRow) Or _
21:	(.txtFirstCol > .txtLastCol) Then
22:	MsgBox Title:=.Caption,
23:	prompt:="Начальные строка и столбец должны " &
24:	"быть меньше, чем последние.",
25:	Buttons:=vbExclamation
26:	ValidateSelf = False
27:	Exit Function
28:	End If
29:
30:	'Сортируемая колонка в диапазоне?
31:	If (.spnSortCol.Value < .txtFirstCol)	Or _
32:	(.spnSortCol.Value > .txtLastCol) Then
33:	MsgBox Title:=.Caption,
34:	prompt:="Сортируемая колонка вне диапазона!",
35:	Buttons:=vbCritical
36:	ValidateSelf = False
37:	Exit Function
38:	End If
39:
40:	ValidateSelf = True 'Данные в порядке
41:	End With
42:	End Function
43:
44:	Private Sub btnClose_Click()
45:	Me.Hide
46:	End Sub
47:
48:	Private Sub btnSort_Click()
49:	'Выполняем сортировку
50:
51:	'Проверяем данные
52:	If Not ValidateSelf Then Exit Sub
53:
54:	With Me
55:	If .chkDescending Then
56:	'Сортируем по убыванию
57:	SortRowsDown FirstRow:=CInt(.txtFirstRow),
58:	LastRow:=CInt(.txtLastRow),
59:	FirstCol:=CInt(.txtFirstCol),
60:	Lasted :=CInt( .txtLastCol),
61:	SCol:=.spnSortCol.Value
576
Неделя 3
62:
63:
64:
65:
66:
67:
68:
Else
'Сортируем по возрастанию
SortRowsUp FirstRow:=CInt(.txtFirstRow),
LastRow:=CInt(.txtLastRow), FirstCol:=CInt( .txtFirstCol), Lasted: =CInt (. txtLastCol), SCol:=.spnSortCol.Value
69:	End If
70:	End With
71:	End Sub
72:
73:	Private Sub spnSortCol_Change()
74:	'Обновление текстового поля
75:	With Me
76:	.txtSortCol = .spnSortCol.Value
77:	End With
78:	End Sub
79:
80:	Private Sub txtSortCol_Change()
81:	'Обновление значения прокрутки
82:
83:	With Me
84:
85:	If IsNumeric(.txtSortCol) Then
86:	If (CLng(.txtSortCol) < 1) Then
87:	.spnSortCol.Value = 1
88:	.txtSortCol = 1
89:	Else
90:	If (CLng(.txtSortCol)	> 99) Then
91:	.spnSortCol.Value	=99
92:	.txtSortCol =99
93:	Else
94:	.spnSortCol.Value	= CLng(.txtSortCol)
95:	End If
96:	End If
97:	End If
98:	End With
99:	End Sub
100:
101:	Private Sub UserForm_Initialize()
102:	'Установка начальных значений
103:	With Me
104:	.spnSortCol	=	1
105:	.txtSortCol	=	1
106:	.txtFirstRow =	1
107:	.txtFirstCol =	1
108:	.txtLastRow	=	1
109:	.txtLastCol	=	1
110:	.chkDescending	=	False
111:	End With
112:	End Sub
День 16-й. Создание настраиваемых диалоговых окон
577
Листинг 16.3 содержит весь модуль класса для формы frmColumnSort. Строки 1—42 представляют общий раздел модуля. Строка 1 содержит директиву компилятора Option Explicit.
Строки 3—42 содержат функцию ValidateSelf. Эта функция возвращает результат типа Boolean, показывающий, являются ли значения, введенные в элементы управления диалогового окна, допустимыми. Строки 8—17 обеспечивают, чтобы все значения, введенные в полях, являлись численными. Если они не являются численными, с помощью MsgBox отображается сообщение об ошибке, результат функции принимает значение False и выполняется выход из функции. Строки 20—28 обеспечивают, чтобы начальное значение диапазона строк и столбцов было действительно меньше конечных значений диапазона. Если это не так, отображается сообщение об ошибке, результат функции принимает значение False и выполняется выход из функции. Строка 40 выполняется только в том случае, если все проверки успешно завершаются; эта строка устанавливает результат функции равным True, и в строке 42 функция ValidateSelf завершается.
Остальная часть листинга 16.3 состоит из процедур для элементов управления в форме frmColumnSort. Строки 44—46 содержат процедуру btnClose_Click. Эта процедура выполняется при каждом щелчке на кнопке btnClose. Она содержит только одну инструкцию, которая вызывает метод Hide формы для закрытия диалогового окна.
Строки 48—71 содержат процедуру события btnSort_Click. Эта процедура выполняется при каждом щелчке на кнопке btnSort. Строка 52 вызывает функцию ValidateSelf, чтобы убедиться, что элементы управления диалогового окна содержат допустимые значения, прежде чем пытаться выполнить операцию упорядочения. Если нет, процедура просто завершается. Если значения всех элементов управления в порядке, строка 55 проверяет значение флажка chkDescending. Если этот элемент управления выбран, его свойство Value имеет значение True, показывая, что сортировка должна выполняться в порядке убывания. Если флажок chkDescending установлен, выполняются строки 57—61; эти строки содержат единственную инструкцию, вызывающую процедуру SortRowsDown. Данная процедура не содержится в модуле класса формы. Она хранится в стандартном модуле, как показано в листинге 16.4. Если флажок chkDescending не установлен, выполняются строки 64—68; эта единственная инструкция вызывает процедуру SortRowsUp, которая также хранится в стандартном модуле (и показана в листинге 16.4).
Обратите внимание, что процедура btnSort_Click не содержит никаких инструкций для закрытия диалогового окна: нет никакого вызова метода Hide формы. В результате, по завершении выполнения процедуры btnSort_Click диалоговое окно остается на экране с рабочим листом, видимым на заднем плане, как показано на рис. 16.14. Для закрытия диалогового окна пользователь должен нажать клавишу <Esc> или щелкнуть на кнопке btnClose.
Строки 73—78 содержат процедуру события spnSortCol_Change. Эта процедура выполняется при каждом изменении значения счетчика spnSortCol. Данная процедура также содержит только одну инструкцию. Строка 76 просто изменяет свойство Value поля txtSortCol для отражения текущего значения счетчика. Процедура события spnSortCol_Change является половиной программы, которая поддерживает синхронизацию этого счетчика со связанным с ним полем. При каждом щелчке на элементе управления счетчика его новое значение отражается в поле.
578
Неделя 3
с D
. Е
2
4, 5’
3
1-
Column Sorting
I
Hret Row:
I
Hrst Column:
SortDolunn:
1
' : А I В .
1	'	5	' ' 2..
2	3	11
3	Г	34
4	’	22	123
5	4	222
। s I------1
7
8	:  
S 10 11 12~	;
13;	‘
14 15; 16: 17 1Я NO H\sampleData / Sales Report / Ba-Chart / Lne Chart / |<|	J >|(~"
Puc. 16.14. Форма frmColumnSort остается на экране, если только она не будет закрыта явно щелчком на кнопке Close
Строки 80—99 содержат процедуру события txtSortCol_Change. Эта процедура выполняется при каждом изменении значения поля txtSortCol. Она является второй половиной программы, поддерживающей синхронизацию между значениями в элементах управления txtSortCol и spnSortCol. Поскольку любой текст — цифры или буквы — может быть введен в поле txtSortCol, значение в этом поле должно быть подтверждено прежде, чем элемент управления spnSortCol изменит свое значение.
Вначале строка 85 выполняет проверку, чтобы убедиться, что новое значение действительно является числом. В случае положительного результата проверки строка 86 устанавливает, не является ли новое значение txtSortCol меньше 1 (в рабочем листе столбцы, номера которых меньше 1, отсутствуют). Обратите внимание, что функция Clng служит для того, чтобы значение txtSortCol было преобразовано в длинное целое значение в случае, если пользователь ввел число с десятичной дробью. Если значение txtSortCol меньше минимального, значения счетчика и поля устанавливаются равными минимуму. Строка 90 начинает проверку для определения того, не превышает ли значение txtSortCol максимальное значение 99; при положительном результате проверки значения счетчика и поля устанавливаются равными максимуму. И наконец, если значение txtSortCol не меньше минимального и не больше максимального, строка 94 устанавливает значение элемента управления счетчика spnSortCol равным целочисленному эквиваленту значения в поле txtSortCol.
Строки 101—112 содержат процедуру события UserForm_lnitialaize. Эта процедура события предназначена для самой формы, а не для элемента управления в ней, и выполняется при каждой загрузке формы в память (т.е. при ее инициализации). Процедура события инициализации требуется для этой формы потому, что элемент управления счетчика должен немедленно синхронизироваться со значением связанного с
День 16-й. Создание настраиваемых диалоговых окон	579
ним поля, до отображения диалогового окна. Для полноты картины эта процедура устанавливает также начальные значения для всех элементов управления формы.
В листинге 16.4 показана процедура, которая отображает форму диалогового окна frmColumnSort, а также содержит процедуры сортировки, используемые процедурами событий формы frmColumnSort. Процедуры сортировки хранятся в стандартном модуле, поскольку являются процедурами общего назначения и могут использоваться любой программой для упорядочения блоков строк и столбцов. Включение подпрограмм сортировки в исходный текст программы формы привел бы к неоправданному дублированию исходного текста программы. В реальных приложениях процедуры сортировки общего назначения, такие как SortRowsUp и SortRowsDown, должны были бы храниться в библиотечном проекте. Введите текст программы, приведенный в листинге 16.4, в стандартный модуль того же проекта, в котором было создано диалоговое окно frmColumnSort.
Листинг 16.4. СтпндгртныО моццаь, содержащиП нрвграммы, на которые ссылается прверамма формы IraiColaaiHSort
1:	Option Explicit
2:
3:	Sub DemoSortDialogf)
4:
5:	Const Datasheet = "Sample	Data”
6:
7:	Dim aDialog As Object
8:	Dim oldSheet As String
9:
10:	Worksheets(Datasheet).Activate 'Выбор листа с данными
И:
12:	Set aDialog = New frmColumnSort
13:	aDialog.Show
14:	Unload aDialog
15:	Set aDialog = Nothing
16:	End Sub
17:
18:	Sub SortRowsUp(ByVai FirstRow As Integer,
19:	ByVai	LastRow	As Integer,
20:	ByVai	FirstCol As Integer,
21:	ByVai	LastCol	As Integer,
22:	ByVai	SCol As	Integer)
23:	Dim	I	As	Integer
24:	Dim	J	As	Integer
25:	Dim	k	As	Integer
26:	Dim	Temp	As Variant
27:
28:	For I = FirstRow To LastRow - 1
29:	For J = I To LastRow
30:	If Cells(I, SCol).Value > Cells(J, SCol).Value Then
31:	' Меняем столбцы местами
32:	For k = FirstCol To Lasted
33:	Temp = Cells(I, k).Value
34:	Cells(I, k).Value = Cells)J, k).Value
35:	Cells(J, k).Value = Temp
580
Неделя 3
36:	Next к
37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65:	End If Next J Next I End Sub Sub SortRowsDown(ByVai FirstRow As Integer, ByVai LastRow As Integer, ByVai FirstCol As Integer, ByVai Lasted As Integer, ByVai SCol As Integer) Dim I As Integer Dim J As Integer Dim к As Integer Dim Temp As Variant ' Начинаем сортировку For I = FirstRow To LastRow - 1 For J = I To LastRow If Cells(I, SCol).Value < Cells(J, SCol).Value Then ‘ меняем местами For к = FirstCol To Lasted Temp = Cells(I, к).Value Cells(I, k).Value = CellsfJ, k).Value Cells(J, k).Value = Temp Next к End If Next J Next I End Sub
Листинг 16.4 начинается с процедуры DemoSortDialog, которая отображает форму frmColumnSort. Для создания экземпляра объекта UserForm процедура DemoSortDialog использует альтернативную технологию. Обратите внимание на объявление переменной Object в строке 7: переменная aDialog служит для хранения ссылки на экземпляр формы frmColumnSort.
Вначале в строке 10 процедуры DemoSortDialog активизируется рабочий лист, содержащий образец данных, которые будут упорядочиваться. (Вообще говоря, можно было бы использовать любой лист, содержащий строки и столбцы численных данных.) Затем в строке 12 создается новый экземпляр объекта frmColumnSort. Строка 13 вызывает метод Show формы для загрузки нового экземпляра формы в память и отображения его на экране. На то время, пока форма открыта на экране, выполнение процедуры frmColumnSort приостанавливается. Когда пользователь закрывает диалоговое окно, выполнение процедуры возобновляется со строки 14, которая выгружает форму из памяти. И наконец, строка 15 удаляет новый экземпляр формы frmColumnSort, устанавливая значение переменной aDialog равным Nothing.
Строки 18—40 содержат процедуру SortRowsUp. По существу, эта процедура работает аналогично процедурам сортировки массивов, представленным на 14-м уроке. Однако процедура SortRowsUp выполняет упорядочение ячеек рабочего листа непосредственно в нем, не используя массив. И наконец, строки 42—65 содержат процедуру SortRowsDown.
День 16-й. Создание настраиваемых диалоговых окон
581
Отметим еще раз, что эта процедура работает аналогично уже знакомым читателям процедурам сортировки, но упорядочивает ячейки листа в порядке убывания.
Создание диалоговых окон, представленных в примерах этого и предыдущего разделов, может показаться весьма трудоемким процессом, но действительно стоит затратить определенные усилия, чтобы создать описанные диалоговые окна и посмотреть, как работают процедуры событий.
Так надо
ИСПОЛЬЗУЙТЕ события Initialization или Activate формы для определения любых внутренних начальных условий формы.
ВЫЗЫВАЙТЕ процедуры, хранящиеся в стандартном модуле, из программы формы во избежание дублирования процедур общего назначения. Например, диалоговое окно frmSalesChart, представленное в листинге 16.1, могло бы быть улучшено удалением функции SheetExists в библиотечную рабочую книгу.
ПРИСВАИВАЙТЕ элементам управления надписей описательные имена, если на надпись нужно ссылаться в программе формы, - например, при необходимости синхронизировать надпись со счетчиком.
ПОМНИТЕ, что именно на вас лежит обязанность обновлять связанные поля или надписи элементов управления счетчиков и полос прокрутки, поскольку эти элементы управления не имеют собственных заголовков.
ИСПОЛЬЗУЙТЕ процедуры событий для событий Change, BeforeUpdate или AlterUpdate, чтобы подтвердить данные или синхронизировать элементы управления друг с другом.
ИСПОЛЬЗУЙТЕ технологию синхронизации элементов управления, показанную в листинге 16.3, с любыми комбинациями элементов управления. В ответ на изменение значения элемента управления с помощью VBA-программы можно динамически обновлять надписи, заголовки, поля, списки и другие элементы управления.
Так не надо
i НЕ ЗАБУДЬТЕ, что процедуру события можно также использовать для обновления собственного заголовка элемента управления.
Использование элемента списка
Заключительный пример этого урока в действительности не выполняет никакой конкретной задачи, а лишь демонстрирует, как можно использовать элемент списка и как можно заполнить список с помощью VBA-программы. В этом примере в диалоговом окне отображен элемент управления List, содержащий названия различных городов. Диалоговое окно содержит также кнопки ОК и Cancel и элемент управления надписи в нижней части окна, отражающий текущий выбор списка. При выборе элемента в списке надпись обновляется, отображая новый выбор; при закрытии диалогового окна с помощью щелчка на кнопке ОК процедура отображает выбранный элемент списка в диалоговом окне MsgBox.
Листинг 16.5 содержит модуль класса формы, а на рис. 16.15 показана форма диалогового окна, которую нужно создать для этого примера. Создав новую форму и переименовав ее элементы управления, введите текст программы, представленный в листинге 16.5, в модуль класса формы.
582
Неделя 3
btnOK
btnCancel
IstCity
IbICity
Puc. 16.15. В этом диалоговом окне выбор из списка отображается в надписи под списком
Aucmuua 1В.5. Моддль класса формы IrmCItgLIst
1:	Option Explicit
2:
3:	Dim fCancel As Boolean
4:
5:	Property Get Canceled() As Boolean
6:	Canceled = fCancel
7:	End Property
8:
9:	Private Sub btnCancel_Click()
10:	fCancel = True
11:	Me.Hide
12:	End Sub
13:
14:	Private Sub btnOK_Click()
15:	fCancel = False
16:	Me.Hide
17:	End Sub
18:
19:	Private Sub lstCity_Change()
20:	'update the label
21:	With Me
22:	.IbICity.Caption = .IstCity.Value
23:	End With
24:	End Sub
Листинг 16.5 содержит весь модуль класса формы frmCityList. Эта форма содержит мало элементов управления, поэтому ее модуль класса достаточно прост, но, тем не менее, демонстрирует некоторые дополнительные аспекты использования нестандартных форм в VBA-программе.
Строки 1-7 представляют раздел General модуля класса. Обратите внимание на объявление переменной в строке 3; переменная fCancel — переменная типа Boolean, используемая для указания того, может ли диалоговое окно быть закрыто щелчком на кнопке Cancel. Поскольку это — модуль класса, переменная fCancel недоступна никакой части программы, не являющейся частью модуля класса.
День 16-Й. Создание настраиваемых диалоговых окон
583
В строках 5—7 объявляется процедура Property Get, создающая свойство только для чтения — Canceled — для этого модуля класса. (Свойство имеет атрибут только для чтения, поскольку отсутствует процедура Let для установки его значения.) Свойство Canceled предназначено для определения того, было ли диалоговое окно закрыто посредством отмены. Свойство Canceled просто возвращает текущее значение переменной fCancel.
Строки 9—24 листинга 16.5 содержат процедуры событий для элементов управления формы. Строки 9—12 содержат процедуру btnCancel_Click, которая выполняется при каждом щелчке на кнопке btnCancel. В строке 10 переменная fCancel устанавливается в True для фиксации того факта, что диалоговое окно было закрыто щелчком на кнопке Cancel; в строке 11 метод Hide формы используется для закрытия диалогового окна без выгрузки его из памяти.
Строки 14—17 содержат процедуру btnOK_Click, выполняемую при каждом щелчке на кнопке btnOK. В строке 15 переменная fCancel устанавливается в False, указывая, что диалоговое окно не было закрыто преждевременно, а стока 16 закрывает диалоговое окно, вызывая метод Hide.
И наконец, строки 19—24 содержат процедуру lstCity_Change, выполняемую при каждом изменении значения списка IstCity, т.е. когда пользователь делает новый выбор в списке или когда свойству Listindex списка присваивается новое значение. Инструкция в строке 22 просто присваивает текущее значение списка элементу управления надписи IblCity, чтобы отразить текущий выбор в списке.
Чтобы увидеть форму frmCityList в работе, нужно ввести текст программы из листинга 16.6; этот листинг содержит процедуру DemoListBox, предназначенную для заполнения списка и запуска формы.
Aucmuua 1В.В. Стандартны!! модджь дт запдска формы frmCltgLlst
1:	Option Explicit
2:
3:	Sub DemoListBox()
4:	'Пример использования формы frmCityList
5:
6:	Dim aDlg As Object
7:
8:	Set aDlg = New frmCityList 'Создаем экземпляр
9:	Load aDlg	'Загружаем в память
10:
11:	'populate the list from outside the form
12:	With aDlg
13:	.IstCity.Clear	'Очищаем список
14:	.IstCity.Additem "Paris"	'Вводим данные
15:	.IstCity.Additem	"London"
16:	.IstCity.Additem	"Tokyo"
17:	.IstCity.Additem	"Rome"
18:	.IstCity.Additem	"Madrid"
19:	.IstCity.Additem	"Bern"
20:	.IstCity.Additem “Washington"
21:	.IstCity.Additem	"Cairo"
22:	.IstCity.Additem	"Mexico City"
23:	.IstCity.Additem	"Dublin"
24:	.IstCity.Additem	"Warsaw"
25:	.IstCity.Additem	“Vienna"
584
Неделя 3
26:	.IstCity.Listindex = 0 'Начальные условия
27:	End With
28:
29:	aDlg.Show
30:
31:	If aDlg.Canceled Then
32:	MsgBox prompt:="Город не выбран.”,
33:	Title:="List Box Demo",
34:	Buttons:=vbExclamation
35:	Else
36:	MsgBox prompt:="Bn выбрали: " & aDlg.IstCity.Value,
37:	Title:=”List Box Demo”,
38:	Buttons:=vblnformation
39:	End If
40:
41:	Unload aDlg
42:	Set aDlg = Nothing
43:	End Sub
Листинг 16.6 содержит единственную процедуру DemoListBox. В строке 6 объявляется переменная объекта, подлежащая использованию для экземпляра формы frmCityList, создаваемого и изменяемого этой процедурой. Строка 8 действительно создает новый экземпляр формы frmCityList, а в строке 9 инструкция Load используется для загрузки формы в память без ее отображения. Загруженная форма, хотя и не отображается на экране, теперь может изменяться VBA-программой.
Строки 12—27 содержат инструкцию With aDlg, предназначенную для манипулирования формой диалогового окна. Строка 13 вызывает метод Clear элемента управления списка для удаления любых существующих в списке записей. Полностью удаляя все элементы из списка, эта инструкция позволяет сделать известным содержимое списка, т.е. благодаря ей список не содержит никаких элементов, которые эта процедура туда не помещала.
Каждая из строк 14—25 вызывает метод Additem элемента управления списка для добавления в список нового элемента. Каждая из этих инструкций добавляет в список один элемент; элементы отображаются в списке том порядке, каком они были добавлены в него. И наконец, строка 26 устанавливает значение свойства Listindex элемента управления списка равным 0, что является первым элементом в списке (списки в элементах управления списков и полей со списком являются, по существу, массивами с нумерацией, начинающейся с 0). Присвоение значения свойству Listindex запускает событие Change списка; следовательно, при первом отображении на экране первый элемент списка выбирается по умолчанию.
Строка 29 действительно отображает форму на экране; форма выглядит подобно показанной на рис. 16.16. При выборе элемента в списке надпись в нижней части списка изменяется, отражая новый выбор. Обратите внимание, что элемент управления списка автоматически отображает собственную полосу прокрутки, если список оказывается длиннее, чем может поместиться в этом элементе управления формы.
День 16-й. Создание настраиваемых диалоговых окон
585
City List
X
\ Paris
London Tokyo Rome Madrid Bern Washington Caro Mexico City
Puc. 16.16. Процедура DemoListBox в листинге 16.16 заполняет список City List
Когда пользователь закрывает форму — щелчком на кнопке ОК или кнопке Cancel — выполнение возобновляется в DemoListBox со строки 31. Эта строка начинает инструкцию If, которая проверяет значение нестандартного свойства Canceled формы frmCityList (строки 5—7 листинга 16.5). Если значение этого свойства равно True, значит, диалоговое окно было закрыто с помощью щелчка на кнопке Cancel и строки 32—34 отображают соответствующее сообщение. Однако, если свойство Canceled имеет значение False, это означает, что форма frmCityList была закрыта с помощью щелчка на кнопке ОК, и поэтому строки 36—38 отображают сообщение, показывающее выбор, сделанный пользователем в списке.
И наконец, строка 41 выгружает форму из памяти, а строка 42 избавляется от экземпляра формы, устанавливая переменную объекта aDlg в значение Nothing.
Так надо
ИМЕЙТЕ В ВИДУ, что список можно заполнять, добавляя элементы из массива или связывая элемент управления списка с диапазоном ячеек в рабочем листе Excd - значение каждой ячейки становится элементом в списке.
ИМЕЙТЕ В ВИДУ, что свойство Value элемента управления списка содержит выбранный в данный момент элемент списка; свойство Listindex хранит индекс выбранного в настоящий момент элемента в массиве элементов списка, который, в свою очередь, хранится в коллекции List списка.
; ПОМНИТЕ, что управление полем со списком, по существу, не отличается от управления списком.
Резюме
В ходе этого урока читатели приобрели основные навыки, необходимые при работе с объектами UserForm в VBA-программе для создания нестандартных диалоговых окон для программ и процедур. Читатели узнали, как создавать, использовать и управлять формами и их элементами управления. Создание нестандартных диалоговых окон осуществляется путем визуального рисования в форме. На этом уроке было рассказано об основных функциях стандартных элементов управления форм VBA и способах манипулирования ими с помощью VBA-программы. В частности, было показано, как создавать процедуры событий для кнопок и других элементов управления формы в модуле класса формы и как использовать процедуры событий для обновления элементов управления диалогового окна, когда оно продолжает отображаться на экране.
586
Неделя 3
Вопросы о ответы
Как создать раскрывающийся список, который ограничивает выбор пользователя только теми элементами, которые представлены в списке?
Поместите элемент управления СошЬоВох в форму, где хотите создать раскрывающийся список. Используйте окно Properties для установки свойства Style элемента управления СошЬоВох в значение fmStyleDropDownList. Теперь элемент управления поля со списком будет вести себя подобно действительному раскрывающемуся списку: выбор пользователя будет ограничен только записями из списка. Для добавления или удаления элементов из списка элемента управления используйте методы Additem и Removeitem.
Как решить, какое событие — Initalization или Activate — использовать для установки начальных условий формы?
Событие Initialization следует использовать, если какое-либо начальное условие нужно установить только один раз. Например, используйте событие Initialization для установки значений по умолчанию в элементах управления диалогового окна или для обеспечения того, чтобы связанные элементы управления — такие, как поле и счетчик — были изначально синхронизированы. Событие Activate следует использовать, когда нужно подтверждать или обновлять элементы управления диалогового окна при каждой его активизации. Например, диалоговое окно frmSalesChart отображает список листов рабочей книги. Этот список должен обновляться при каждой активизации диалогового окна, поскольку новые листы могут быть созданы с момента загрузки диалогового окна в память.
Созданное мною диалоговое окно никогда не закрывается; что с ним не в порядке?
Чтобы закрывать диалоговое окно, любая кнопка диалогового окна, которая отвечает за это, должна содержать обращение к методу Hide формы. Если хотите, чтобы диалоговое окно закрывалось после нажатия пользователем клавиши <Esc>, убедитесь, что свойство Cancel одной (и только одной) кнопки диалогового окна установлено в True и что процедура события Click кнопки содержит обращение к методу Hide. В противном случае форма вообще не будет отвечать на нажатие клавиши <Esc>.
Как можно указать, где форма моего диалогового окна должна появляться на экране?
Позицией появления формы на экране можно управлять, устанавливая свойства Тор, Left и Startupposition формы. Свойство Startupposition управляет тем, как VBA отображает диалоговое окно при первом открытии: в качестве установок можно выбирать Manual, Centerowner, Centerscreen или Windows Default. Если свойство StartupPosition установлено в значение Manual, форма отображается с расположением верхнего левого угла, указанным свойствами Тор и Left. Установка Centerowner (установка по умолчанию) вызывает отображение формы в центре окна, которому оно принадлежит, — обычно окна вызывающего приложения. Установка Centerscreen приводит к отображению формы в центре всего экрана дисплея, а установка Windows Default — к ее отображению там, где операционная система Windows отобразила бы следующее открываемое ею окно.
Как можно отключить элемент управления в форме?
Элемент управления можно отключить, присвоив значение False свойству Enabled этого элемента управления. Чтобы снова активизировать элемент управления, присвойте свойству Enabled значение True.
Как можно скрыть элемент управления?
Элемент управления можно скрыть, присвоив значение False свойству Visible этого элемента управления. Чтобы показать элемент управления, присвойте свойству Visible значение True.
День 16-й. Создание настраиваемых диалоговых окон
587
Как можно заставить элемент управления поля отображать несколько строк?
Можно заставить поле отображать несколько строк, присвоив значение True его свойству Multiline или установив свойство Multiline в значение True в окне Properties в режиме конструктора. Поля, свойство Multiline которых установлено в True, автоматически отображают собственную полосу прокрутки, когда текст в поле превышает по размерам доступную область отображения текста. Как правило, при установке свойства Multiline потребуется также устанавливать в значение True и свойство Wordwrap: это приводит к тому, что текст в поле автоматически переносится на дополнительные строки, чтобы соответствовать текущей ширине элемента управления поля.
Как можно добавлять и удалять элементы из списков и полей со списком?
Элементы можно добавлять и удалять, используя методы Additem и Removeitem. Метод Additem имеет два аргумента: первый — строковое выражение, указывающее значение, подлежащее добавлению в список, а второй — число, указывающее, где именно в списке должен быть добавлен элемент. Если опустить второй аргумент, метод Additem добавит элемент к концу списка. Метод Removeitem принимает только один аргумент: численное выражение, указывающее, какая строка в списке подлежит удалению.
Как можно получить число элементов в списке или поле со списком?
Для выяснения количества имеющихся в списке элементов используйте свойство Listcount элемента управления списка или поля со списком.
Коллоквиум
Ответы приведены в приложении.
Тест
1.	Что понимается под режимом конструктора?
2.	Что делает метод Show?
3.	Что делает метод Hide?
4.	Можно ли получить доступ или манипулировать методами или свойствами формы, прежде чем она загружена в память?
5.	Какое действие оказывает инструкция Load? А инструкция Unload?
6.	Назовите два способа создания экземпляра конкретной формы.
7.	Что такое процедура события?
8.	Как создать процедуру события?
9.	Можно ли создать нестандартные свойства и методы для формы? Если да, то как это сделать?
10.	Что понимается под последовательностью перехода?
11.	Какую программу нужно создать, чтобы манипулировать элементами управления в форме до ее отображения на экране?
12.	Создается ли при разработке нового объекта UserForm новый класс объекта?
588
Неделя 3
Уиражнвиия
1. Модифицируйте процедуру DemoListBox, а также форму frmCityList для создания процедуры DemoComboBox и формы frmCityListCombo соответственно. Новые процедура и форма должны работать точно так же, как описано для листингов 16.5 и 16.6, но используя элемент управления ComboBox с раскрывающимся списком вместо элемента управления списка. Форма frmCityListCombo после завершения показана на рис. 16.17.
2. Создайте самостоятельную форму frmCOCA (СОСА означает Common Command-Oriented Calculator — калькулятор, ориентированный на обычные команды), которая имеет поля для получения от пользователя двух операндов и раскрывающийся список, из которого можно выбирать математический оператор. Включите в форму кнопку Calculate, выполняющую указанную операцию с двумя операндами и отображающую результат. Сделайте форму СОСА плавающим диалоговым окном, как продемонстрировано в листинге 16.3. (Подсказка. Весь текст программы для этого примера может содержаться в модуле класса формы.) На рис. 16.18 показано, как форма frmCOCA может выглядеть во время выполнения.
Калькулятор СОСА должен быть в состоянии выполнять следующие операции: + (сложение), - (вычитание), * (умножение), / (деление), Л (показательную) и Sqr (извлечение квадратного корня). Отобразите операнды в раскрывающемся списке, чтобы могли быть введены только эти операнды. (Подсказка. Для создания раскрывающегося списка установите свойство Style элемента управления ComboBox в значение fmStyleDropDownList.)
Рис. 16.17. Создайте эту форму для процедуры DemoComboBox
Operand 1
COCA Calculator
OperatorjFunetloh:
Oserand 2.
Puc. 16.18. Калькулятор COCA должен использовать плавающее диалоговое окно, подобное этому
День 16-Й. Создание настраиваемых диалоговых окон
589
Убедитесь, что предотвратили деление на нуль и попытки извлечения квадратного корня из отрицательного числа. (Совет. Для проверки вводимого пользователем оператора используйте инструкцию Select Case; включите выражение Case для каждого оператора, содержащего инструкции, которые выполняют запрошенную операцию над операндами.)
590
Неделя 3
Меню и панели инструментов
Научившись с помощью форм VBA создавать нестандартные диалоговые окна, можно приступать к изучению других средств VBA, которые позволяют разрабатывать завершенные профессиональные программы: меню и панелей инструментов. Visual Basic для приложений позволяет настраивать существующие меню и панели инструментов в Excel, добавляя новые команды и подменю, или создавать собственные завершенные меню и панели инструментов. Создавая собственные или настраивая уже существующие меню и панели инструментов, можно существенно усложнять стандартный интерфейс пользователя Excel (или иного основного приложения); можно также создавать завершенные системы меню и панелей инструментов для собственных приложений. На этом уроке мы рассмотрим следующие вопросы.
•	Что такое объект управляющей панели, элемент управления управляющей панели и какие свойства и методы эти объекты делают доступными.
•	Как добавлять команды или подменю в предопределенные управляющие панели Excel — как в меню, так и в панели инструментов.
•	Как использовать объекты управляющей панели и элементов управления управляющей панели для создания и управления нестандартными меню, подменю и командами меню.
•	Как использовать объекты управляющей панели и элементов управления управляющей панели для создания и управления нестандартными панелями инструментов и кнопками панелей инструментов и как вставлять в панели инструментов поля и списки.
Управляющие панели
Excel позволяет создавать новые меню и панели инструментов или добавлять новые команды в существующие меню и панели инструментов. Прежде чем приступить к изучению особенностей создания (или изменения) нового меню или панели инструментов, следует разобраться в основных объектах, используемых приложениями Microsoft Office (версии 97 и последующих) для реализации меню и панелей инструментов. Усвоение этих основ облегчит понимание подробных инструкций по созданию меню и панелей инструментов, приведенных далее в этом уроке.
День 17-й. Меню и панели инструментов
591
Пользователи Excel уже должны быть знакомы с встроенными меню и панелями инструментов, позволяющими вводить команды и инициировать действия в Excel. Меню и панели инструментов, отображаемые приложением (в сочетании с различными диалоговыми окнами), образуют интерфейс пользователя приложения, т.е. систему, посредством которой пользователь взаимодействует с приложением.
С точки зрения пользователя, существует определенное различие между интерфейсами, обеспечиваемыми меню и панелью инструментов; меню и панелям инструментов присущ особый вид и поведение. Меню — называемое также строкой меню — всегда отображается поперек верхней части окна приложения, под строкой заголовка окна приложения. Варианты выбора строки меню состоят из слов, представляющих категории команд; щелчок на одном из вариантов приводит к отображению списка пунктов меню и/или дополнительных меню (называемых подменю). Пункты меню представляют команды или опции; щелчок на пункте меню заставляет приложение выполнить соответствующее действие или выбрать соответствующий параметр.
Напротив, панель инструментов может отображаться в плавающем окне или быть прижатой к верхнему, нижнему, левому или правому краю окна приложения. Как правило, панели инструментов содержат кнопки; щелчок на кнопке панели инструментов заставляет приложение выполнить конкретную команду. Панели инструментов могут также содержать элементы управления интерфейса пользователя, отличающиеся от кнопок: списки, поля, всплывающие меню и другие.
На первый взгляд кажется, что строка меню и панель инструментов — совершенно различные объекты. Действительно, в Microsoft Excel 7 (и более ранних версиях) строки меню и панели инструментов были представлены двумя отдельными структурами объектов: одной группой объектов для строк меню, пунктов меню и подменю и другой группой объектов для панелей инструментов и кнопок панелей инструментов.
ПРИМЕЧАНИЕ
Объекты строки меню и панели инструментов версий VBA, предшествующих Microsoft Office 97, не освещаются в этой книге. Вместо этого основное внимание уделено объектам, используемым в настоящее время в Office 2000 и впервые представленным в Office 97. Тем не менее, VBA в Office 97 и Office 2000 поддерживает объекты панелей меню и инструментов предшествующих версий, обеспечивая обратную совместимость. Если вы располагаете - или получили - существующую VBA-программу, которая создает меню или панели инструментов, использующую более ранние объекты, эта программа должна правильно выполняться в приложениях Office 2000. Однако вновь создаваемые программы должны всегда использовать объекты управляющих панелей, описанные в этой главе.
При ближайшем рассмотрении, однако, оказывается, что строки меню и панели инструментов имеют много общего. Например, и строки меню, и панели инструментов представляют собой область (“панель”), содержащую такие элементы управления интерфейса, как пункты меню, кнопки и т.п. Или еще один пример — пункт меню выполняет такую же функцию, как кнопка панели инструментов: щелчок и на пункте меню, и на кнопке панели инструментов заставляет приложение выполнить какое-либо действие. Из-за их схожести строки меню и панели инструментов можно рассматривать как различные типы одного и того же объекта: объекта управляющей панели.
Все приложения Microsoft Office (в том числе Excel) реализуют строки меню и панели инструментов в виде различных подтипов одного класса объектов: объекта ComandBar. Главное преимущество такого объединения для программистов VBA заключается в следующем: оно значительно уменьшает количество объектов, свойств и методов, которые нужно изучать. Для создания меню и панелей инструментов в VBA в среде Excel 7 (или более ранних версиях) нужно было научиться использовать 10 различных объектов и коллекций свыше 23 различных свойств и Методов. Благодаря
592
Неделя 3
объединению функциональных особенностей строк меню и панелей инструментов в одном объекте ComandBar количество объектов, подлежащих изучению, уменьшается почти в два раза.
Тины управляющих панелей
Объект ConunandBar может быть одним из трех типов: строкой меню, всплывающим меню или панелью инструментов. Панели команд типа строки меню отображаются на экране в виде строки меню; они могут отображаться только поперек верхней части окна приложения, непосредственно под строкой заголовка окна приложения. Панели команд типа панели инструментов (называемые также обычными панелями команд) отображаются на экране в виде панели инструментов. Панели команд типа всплывающего меню используются для создания подменю в строке меню или всплывающих меню в панели инструментов.
Тип панели команд определяется значением, хранящимся в свойстве Туре панели команд; тип панели команд может быть определен только во время создания панели команд. Свойство Туре хранит численное значение, указывающее тип панели команд. В VBA-программе тип панели команд представляется посредством встроенных констант, приведенных в табл. 17.1. (Эти встроенные константы можно просмотреть в окне Object Browser, выбрав Office в раскрывающемся списке Project/Library (Проект/Библиотека), а затем выбрав MsoBarType в списке Classes (Классы).) Как и в случае других встроенных констант, не требуется беспокоиться о действительном численном значении, представляемом константой; достаточно разобраться в том, что она означает.
Таблица 17.1. Встроенные константы MsoBarType	
Имя константы	Тип панели команд
msoBarTypeMenuBar	Строка меню. Этот тип указывает, что панель команд функционирует как строка меню с присущими строке меню особенностями и ограничениями
MsoBarTypeNormal	Панель инструментов. Этот тип указывает, что панель команд функционирует как панель инструментов с присущими панели инструментов особенностями и ограничениями
msoBarTypePopup	Всплывающая панель команд. Этот тип указывает, что панель команд помешается в виде элемента управления в другую панель команд; в основном он используется для создания подменю в строке меню
Теперь, познакомившись с основными типами панелей команд, можно приступить к изучению компонентных частей панели команд, описанных в следующем разделе.
Части управляющей панели
Хотя читатели, вероятно, уже знакомы с различными частями меню и панелей инструментов (поскольку они отображаются на экране), необходимо разобраться, как управляющая панель и объекты элементов управления работают вместе, образуя меню и панели инструментов, отображаемые на экране. В последующих разделах вначале описаны компоненты управляющей панели типа строки меню, а затем основные компоненты управляющей панели типа панели инструментов.
День /7-й. Меню и панели инструментов
593
Упрпвляющая панель в впде строкп меню
На рис. 17.1 показано развернутое меню Insert (Вставка) приложения Excel. Когда свойство Туре объекта ConunandBar установлено в msoBarTypeMenuBar, объект управляющей панели отображается в виде строки меню. Эта строка размещается поперек верхнего края окна приложения (под строкой заголовка окна) и в ней приводятся пункты первого уровня меню, доступные с текущий момент. Каждое слово в строке меню представляет меню. При щелчке на названии меню в строке меню это меню отображается на экране.
Microsoft Lxcel Day 11 xls
файл Правка : Вставка Форма- Сервис Даинь-е Окно Оправка
Меню/Управпяющая панель
Столбцы
Диет И) Диепремма
Из файла
'3 Автосригуры
Пункт меню/Кнопка управляющей панели
Подменю/Управляющая панель т
Рис. 17.1. Развернутое меню Вставка приложения Excel, демонстрирующее основные элементы управляющей панели
Объекты ConunandBar являются контейнерами; они могут не содержать либо содержать один или несколько элементов управления управляющей панели. Элементы управления управляющей панели — это кнопки, подменю, списки и другие компоненты, появляющиеся в управляющей панели. Коллекция элементов управления может содержать другие объекты ConunandBar.
Присмотритесь к рис. 17.1 и обратите внимание, что каждый пункт первого уровня меню (такой как Insert) является объектом типа управляющей панели. Эти всплывающие управляющие панели, содержащиеся в строке меню, создают действительные меню. В меню приводится ряд команд или дополнительных меню, из числа которых пользователь может выбирать. Перечисленные в меню команды обычно называют пунктами меню. Показанные на рис. 17.1 Function (Функция) и Worksheet (Лист) являются пунктами меню Insert. Каждый пункт меню представляет собой элемент управления управляющей панели, а именно объект ConunandBarControl. (Различные типы элементов управления управляющих панелей подробнее описаны ниже в этой главе.)
С точки зрения конечного пользователя, дополнительные меню, содержащиеся в другом меню, — подобно тому, как на рис. 17.1 меню Insert содержит меню Name (Имя) и Picture (Рисунок) — называются подменю. Каждое подменю является еще одним объектом ConunandBar типа всплывающего меню (рис. 17.1).
ПРИМЕЧАНИЕ
Как уже известно читателям, пункты меню, ведущие к другим меню, обозначаются указывающей вправо стрелкой, расположенной справа от пункта меню. Пункты меню, ведущие к диалоговым окнам, обозначаются многоточиями (...) справа от пункта. Пункты, которые не имеют какого-либо символа, немедленно выполняют указанную команду. Например, показанные на рис. 17.1 пункты Name и Picture ведут к дополнительным меню, а пункт Function ведет к диалоговому окну. Пункт Worksheet -команда, которая будет выполняться немедленно после щелчка на ней.
594
Неделя 3
ПРИМЕЧАНИЕ
При создании и отображении нестандартных меню VBA будет автоматически добавлять стрелку, указывающую подменю; однако разработчик должен сам снабдить пункт многоточием для указания нестандартной команды, ведущей к диалоговому окну.
Управляющие панелп в виде панелей пнстрдмептов
На рис. 17.2 показана панель инструментов Formatting (Форматирование) в виде свободно размещаемого окна. Изучая рис. 17.2, сравните объекты управляющей панели и элементов управления с теми, которые показаны На рис. 17.1. Когда свойство Туре объекта CoiranandBar установлено в msoBarTypeNormal, объект управляющей панели появляется в виде панели инструментов. Панель инструментов может быть прижата к краю окна, расположена произвольно или настроена так же, как любая другая панель инструментов в Excel.
Форматирование

Arial
ю » ж к з
Список
Комбинированный список
Кнопка
Управляющая панель
Рис. 17.2. Панель инструментов Форматирование, показанная в плавающем окне
Подобно управляющим панелям типа строк меню, используемым в качестве панелей инструментов, объекты ComandBar могут не содержать либо содержать один или несколько элементов управления управляющей панели. В отличие от меню в панели инструментов более вероятно наличие таких элементов управления, как поля со списком, списки, поля и всплывающие меню. Аналогично управляющим панелям типа строки меню, коллекция элементов управления управляющей панели типа панели инструментов может содержать другие объекты CoiranandBar. Обратите внимание, что на рис. 17.2 панель инструментов Formatting содержит различные элементы управления управляющей панели, в том числе управляющую панель всплывающего типа (используемую для отображения палитры цветов шрифта).
Теперь, получив некоторое представление о структуре объектов управляющей панели, можно приступить к изучению некоторых конкретных типов доступных объектов CoiranandBarControl.
Типы элементов управлепия
Как было рассказано в предыдущих разделах этого урока, объекты CoiranandBar могут не содержать или содержать один либо несколько объектов CommandBarConrol. Каждый объект CommandBarConrol представляет всплывающее меню, кнопку, список или какой-либо иной элемент управления управляющей панели.
Тип объекта CommandBarConrol определяется значением, хранящимся в свойстве Туре элемента управления. Тип элемента управления управляющей панели можно определять только во время его создания. Свойство Туре хранит численное значение, указывающее конкретный тип объекта CommandBarConrol (кнопку, всплывающее меню, список и т.д.). В VBA-программе тип элемента управления управляющей панели представляется посредством использования встроенных констант, перечисленных в табл. 17.2. (Эти встроенные константы можно просмотреть в окне Object Browser, выбрав Office в раскрывающемся списке Project/Library (Проект/Бибпиотека), а затем выбрав MsoBarType в списке Classes (Классы).)
День /7-й. Меню и панели инструментов
595
Таблица 17.2. Часто используемые объекты CommandBarControl
Константа типа элемента управления	Объект элемента управления	Назначение
msoControl Button	CommandBarButton	В управляющей панели типа меню создает команду пункта меню. В управляющей панели типа панели инструментов создает кнопку
msoControlComboBox	CoinmandBarComboBox	В управляющей панели типа панели инструментов создает элемент управления поля со списком. В управляющих панелях типа меню не используется
msoControlDropdown	CommandBarComboBox	В управляющей панели типа панели инструментов создает элемент управления списка. В управляющих панелях типа меню не используется
msoControlEdit	CommandBarComboBox	В управляющей панели типа панели инструментов создает элемент управления поля. В управляющих панелях типа меню не используется
msoControlPopup	CommandBarPopup	В управляющей панели типа меню создает подменю. В управляющей панели типа панели инструментов создает раскрывающееся меню
В зависимости от конкретного типа элемента управления, выбранного во время создания элемента управления управляющей панели, действительный объект CommandBarControl окажется принадлежащим к одному из трех различных подклассов элементов управления: CommandBarPopup, CommandBarComboBox или CommandBarButton. В табл. 17.2 обратите внимание, что объект CommandBarComboBox реализует три типа элементов управления управляющей панели: поля со списком, списки и поля.
Элемент управления поля со списком позволяет пользователю либо ввести значение, либо выбрать его из списка. Основное различие между полем со списком и списком в том, что список ограничивает выбор пользователя только теми значениями, которые представлены в списке. Аналогично этому основное различие между полем со списком и полем состоит в том, что поле не отображает список, из которого можно было бы выбрать значение, хотя и позволяет пользователю ввести любое значение. По существу, поля и списки представляют отдельные поднаборы свойств, присущих полю со списком. Таким образом, используя объект CommandBarComboBox для реализации всех трех элементов управления, разработчики пакта Office уменьшили количество требуемых объектов элементов управления; конкретное поведение элемента управления CommandBarComboBox обусловливается содержимым его свойства Туре.
В последующих разделах этой главы читатели узнают об особенностях использования VBA-программы для создания и манипулирования элементами управления управляющей панели.
596
Неделя 3
ПРИМЕЧАНИЕ
Если просмотреть доступные константы MsoControlType в окне Object Browser, можно обнаружить гораздо больше констант типов элементов управления управляющих панелей, чем приведено в табл. 17.2. В этой таблице перечислены только те константы MsoControlType, использование которых наиболее вероятно. Многие приведенные в Object Browser константы MsoControlType представляют сложные формы элемента управления всплывающего меню, используемые для отображения цветовых палитр, графических изображений и т.п. Использование этих элементов управления будет требоваться достаточно редко. Если все же понадобится один из этих элементов управления, вполне вероятно, что существует встроенный элемент управления управляющей панели, который можно использовать. (Встроенные управляющие панели и элементы управляющей панели описаны ниже.)
Методы о свойства управляющей панели
VBA позволяет в Excel настраивать встроенные и создавать собственные управляющие панели. Прежде чем научиться создавать собственные объекты нестандартной управляющей панели или изменять встроенную управляющую панель, необходимо больше узнать о конкретных объектах, свойствах и методах, манипулирование которыми будет выполняться с помощью VBА-программы. В следующих разделах приведен обзор различных объектов, свойств и методов, с помощью которых создается управляющая панель.
Коллекция CommanilBars
Объект коллекции CommandBars используется для содержания ни одной, одной или нескольких управляющих панелей. (О коллекциях объектов рассказывалось на 7-м уроке.) В табл. 17.3 приведены свойства коллекции CommandBars, а в табл. 17.4 — ее методы.
Таблица 17.3. Свойства коллекции CommandBars
Свойство	Тип/описание
ActionControl	Только для чтения. Возвращает ссылку на объект CommandBarControl, свойство OnAction которого установлено для выполняющейся в данный момент процедуры. Возвращает значение Nothing, если выполняющаяся в данный момент процедура не была инициализирована элементом управления управляющей панели
ActiveMenuBar	Только для чтения. Возвращает ссылку на объект CommandBar, являющийся активной строкой меню в вызывающем VBA-приложении
Count	Только для чтения, Long. Возвращает число, указывающее количество элементов в коллекции CommandBars
DisplayKeysInTooltips	Чтение-запись, Boolean. Если имеет значение True, то указывает, что клавиши быстрого доступа должны отображаться в подсказках ScreenTips каждого элемента управления управляющей панели
День 17-й. Меню и панели инструментов
597
Окончание табл. 17.3
Свойство	Тип/описание
DisplayTooltips	Чтение-запись, Boolean. Если имеет значение True, то указывает, что подсказки ScreenTips элементов управления управляющей панели должны отображаться. Установка этого свойства будет оказывать немедленное воздействие на все управляющие панели во всех выполняющихся приложениях пакета Office и в любом приложении Office, открытом впоследствии, до тех пор, пока свойство не будет переустановлено
Large Buttons	Чтение-запись, Boolean. Если имеет значение True, то указывает, что должны отображаться большие кнопки панели инструментов
М е nuAnimationS tyle	Чтение-запись, Long. Возвращает или устанавливает способ анимации указанной управляющей панели. Может иметь один из следующих типов: msoMenuAnimationNone, msoMenuAnimationRandom,	msoMenuAnimationUnfold	или msoMenuAnimationSlide
В табл. 17.3 обратите внимание, что многие из свойств коллекции ComandBars соответствуют параметрам просмотра, устанавливаемым интерактивно в диалоговом окне Customize (Настройка), как описано далее в этом уроке. (Диалоговое окно используется для установки параметров управляющих панелей приложения или для интерактивного создания и настройки управляющих панелей. Доступ к диалоговому окну Customize можно получить, выбрав команду меню View (Bnfl)-Toolbars (Панели инструментов)-Сиз1от!ге (Настройка) в Excel.) Методы коллекции ComandBars приведены в табл. 17.4.
Таблица 17.4. Методы коллекции CommandBars	
Метод	Описание
Add	Добавляет новую управляющую панель в коллекцию ComandBars
FindControl	Возвращает ссылку на указанный объект ComandBarControl
ReleaseFocus	Освобождает фокус интерфейса пользователя от всех управляющих панелей. Для перемещения фокуса на отдельный элемент управления используйте метод SetFocus этого элемента управления
Каждый из приведенных в табл. 17.4 методов подробно описан далее в этом уроке.
Большинство VBA-приложений содержат много различных управляющих панелей. Отдельные управляющие панели приложения — будь то нестандартные управляющие панели, создаваемые пользователем, или панели, встроенные в приложение — содержатся в коллекции ComandBars объекта Application. Доступ к коллекции ComandBars приложения осуществляется посредством свойства ComandBars объекта Application.
Для возвращения ссылки на коллекцию ComandBars Excel используйте следующий синтаксис.
ObjectExpression. ComandBars
598
Неделя 3
В этом примере ObjectExpression представляет любое допустимое выражение, приводящее к ссылке на объект Application. Большинство приведенных в этом уроке листингов содержит примеры VBA-программ, ссылающихся на коллекцию CommandBars.
Объект CommandBar
Объект CommandBar представляет отдельную управляющую панель. Объектами CommandBar придется манипулировать не только в качестве строк меню и панелей инструментов, но и в качестве подменю и всплывающих меню. Свойства объекта CommandBar приведены в табл. 17.5, а его методы — в табл. 17.6.
Таблица 17.5. Свойства объекта CommandBar
Свойство	Описание
Builtln	Только для чтения, Boolean. Имеет значение True, если указанная управляющая панель встроена в вызывающее VBA-приложение
Controls	Только для чтения. Возвращает коллекцию CommandBarControls, представляющую все элементы управления в управляющей панели (или элементы управления в управляющей панели, являющейся частью элемента управления всплывающего меню)
Enabled	Чтение-запись, Boolean. Имеет значение True, если управляющая панель активизирована. Для управляющих панелей типа панели инструментов панель инструментов появляется в списке доступных панелей инструментов, если это свойство имеет значение True
Height	Чтение-запись, Long. Возвращает или устанавливает высоту управляющей панели в пикселях. Если управляющая панель прижата к краю окна или защищена от изменения размеров, изменение этого свойства приводит к ошибке
Index	Только для чтения, Long. Возвращает номер указателя управляющей панели в коллекции CommandBars
Left	Чтение-запись, Long. Возвращает или устанавливает расстояние в пикселях левого края управляющей панели от левого края экрана.
Name	Чтение-запись, String. Возвращает или устанавливает имя управляющей панели. Если управляющая панель является встроенной, свойство Name возвращает ее имя на английском языке США. Для возвращения имени управляющей панели с учетом локальной (т.е. языковой) версии необходимо использовать свойство NameLocal
Name Local	Чтение-запись, String. Возвращает имя встроенной управляющей панели, отображаемой в языковой версии вызывающего VBA-приложения. Изменение свойства LocalName для нестандартной управляющей панели изменяет свойство Name и другие связанные с ним свойства
Position	Чтение-запись, Long. Возвращает или устанавливает позицию указанной управляющей панели. Для указания значения Position используйте класс MsoBarPosition встроенных констант: msoBarLeft, msoBarTop, msoBarRight, msoBarBottom, msoBarFloating, msoBarPopup или msoBarMenu. (Константа позиции msoBarMenu указывает, что управляющая панель отображается как строка меню)
1ень 17-й. Меню и панели инструментов
599
Окончание табл. 17.5
Свойство	Описание
Protection	Чтение-запись, Long. Возвращает или устанавливает способ защиты управляющей панели от настройки пользователем. Может быть одной из — или суммой — встроенных констант MsoBarProtection: MsoBarNoProtection (по умолчанию), MsoBarNoCustomize, MsoBarNoResize, MsoBarNoMove,	MsoBarNoChangeVisible,	MsoBarNoChangeDock, MsoBarNoVerticalDock, MsoBarNoHorizontalDock
Rowindex	Чтение-запись, Long. Возвращает или устанавливает порядок размещения управляющей панели, прижатой к краю окна относительно других управляющих панелей. Может быть целым значением больше нуля или же любой из следующих констант MsoBarRow: msoBarRowFirst или msoBarRowLast. Управляющие панели с более низким значением Rowindex прижимаются к краю окна первыми. Если более одной управляющей панели имеют одинаковое значение Rowindex, последняя назначенная управляющая панель будет отображаться первой
Top	Чтение-запись, Long. Возвращает или устанавливает растояние в пикселях от верхнего края управляющей панели до верхнего края экрана.
Type	Только для чтения, Long. Возвращает тип управляющей панели; различные типы управляющих панелей приведены в табл. 17.1. Значение этого свойства может быть установлено только при создании объекта ConunandBar, как описано далее в этом уроке
Visible	Чтение-запись, Boolean. Имеет значение True, если управляющая панель видна. По умолчанию это свойство имеет значение False для только что созданных нестандартных управляющих панелей. Прежде чем свойство видимости может быть установлено в True, свойство Enabled управляющей панели должно быть установлено в True
Width	Чтение-запись. Возвращает или устанавливает ширину управляющей панели в пикселях
Для экономии места в таблицах этого урока не приводятся свойства, общие для подавляющего большинства других объектов в VBA - такие как Application (возвращающее ссылку на приложение, которому принадлежит объект) и Parent (возвращающее ссылку на контейнер объекта). Для просмотра всех свойств объекта следует воспользоваться окном Object Browser.
ПРИМЕЧАНИЕ
Некоторые из приведенных в табл. 17.5 свойств требуют дополнительного пояснения. Для встроенных управляющих панелей свойство Name содержит имя управляющей панели на английском языке США, а свойство NameLocal содержит имя управляющей панели, отображаемое на языке локальной языковой версии Microsoft Office. Для нестандартных управляющих панелей (т.е. управляющих панелей, создаваемых пользователем) свойства Name и NameLocal всегда содержат идентичные значения: строку, содержащую имя, присвоенное нестандартной управляющей панели.
Свойство Protection позволяет ограничить действия, которые пользователи могут выполнять по отношению к созданной управляющей панели. Пользователям можно запретить настраивать, изменять размеры или перемещать управляющую панель. Можно также ограничить способы размещения управляющей панели у границы окна:
600
Неделя 3
можно запретить пользователям изменять положение управляющей панели либо просто запретить ее размещение у края окна. В частности, можно запретить прижимать управляющую панель к левому или правому краю окна приложения, если она содержит список или всплывающие элементы управления, поскольку эти элементы управления не отображаются в управляющих панелях, прижатых вертикально. Для присвоения значений свойству Protection управляющей панели необходимо использовать константы, приведенные для этого свойства в табл. 17.5. Можно присвоить более одного типа защиты управляющей панели, объединив константы MsoBarProtection в нужной комбинации. Примеры использования свойства Protection показаны в листингах, приведенных далее в этом уроке.
В табл. 17.6 приведено краткое описание методов объекта ComandBar.
Таблица 17.6. Методы объекта CommandBar
Метод	Описание
Delete	Удаляет указанную управляющую панель из коллекции ComandBars
FindControl	Возвращает ссылку на указанный объект ComandBarConrtol, соответствующий указанному критерию. Метод FindControl следует использовать для обнаружения элемента управления на основе значений свойств Type, Id, Tag и Visible объекта CommandBarControl. Этот метод может также выполнять поиск всех подменю или всплывающих элементов управления в указанном элементе управления управляющей панели
Reset	Заставляет встроенную управляющую панель выполнить собственную переустановку в исходную конфигурацию, удаляя все нестандартные элементы управления и восстанавливая любые удаленные элементы управления встроенной управляющей панели
ShowPoup	Отображает управляющую панель в виде контекстного меню. По умолчанию контекстное меню отображается в текущей позиции указателя мыши, хотя при необходимости можно указать позицию отображения контекстного меню. Метод ShowPoup будет генерировать ошибку времени выполнения, если только свойство Position управляющей панели не установлено в msoBarPopup
Каждый из приведенных в табл. 17.6 методов подробнее описан далее в этом уроке. Прежде чем продолжить изучение управляющих объектов, которые можно добавлять в управляющую панель, необходимо несколько ближе познакомиться с особенностями встроенных управляющих панелей Excel и с тем, где Excel хранит нестандартные управляющие панели.
Встроенные н нестандартные управляющие панели
VBA позволяет создавать программы, которые будут отображать, модифицировать или как-либо иначе манипулировать управляющими панелями. Манипулирование будет осуществляться либо встроенными управляющими панелями Excel, либо управляющими панелями, которые созданы вами. В двух следующих разделах пояснены важные характеристики нестандартных и встроенных управляющих панелей. Этот раздел завершается листингом примера, демонстрирующим, как вывести список всех управляющих панелей, встроенных в Excel.
День 17-й. Меню и панели инструментов
601
ПРИМЕЧАНИЕ
Из-за ограничения на объем книги здесь опущено описание использования диалогового окна Customize (Настройка) (доступ к которому осуществляется с помощь команды View (BMfl)-Customize (Настройка)) с целью интерактивного создания управляющих панелей. Вместо этого основное внимание уделяется использованию VBA-программ для создания нестандартных управляющих панелей.
Нестандартные управляющее панелв
Управляющие панели, создаваемые пользователем — интерактивно или с помощью VBA-программы, — называются нестандартными управляющими панелями, и их можно создавать в среде VBA приложения Excel. Действительные компоненты объектов, необходимые для создания нестандартной управляющей панели и ее элементов управления, предоставляются библиотекой Office VBA. (Выберите Office в списке Proj-ects/Libraries (Проекты/Библиотеки) в окне Object Browser.)
Используя нестандартные управляющие панели, VBA-программы можно снабдить столь же профессиональным интерфейсом пользователя, какой вы видите в приложении Excel и в других приложениях Microsoft Office. Объекты CommandBar и CommandBarControl VBA позволяют создать собственную полностью настраиваемую систему меню и панелей инструментов. Создание нестандартной системы меню или панелей инструментов требует выполнения двух основных действий.
1. Создания объекта управляющей панели.
2. Добавления элементов управления в управляющую панель.
В разделах второй половины этого урока показано, как написать VBА-программу, необходимую для создания новых управляющих панелей и как добавить элементы управления в управляющую панель. Ниже поясняется, как Excel хранит создаваемые нестандартные управляющие панели.
По умолчанию в Excel нестандартные управляющие панели всегда сохраняются с рабочей областью Excel. Это означает, что нестандартные управляющие панели доступны всегда во всех рабочих книгах. Точнее говоря, управляющие панели, сохраненные в рабочей области Excel, хранятся в файле UserName9.xlb, где UserName представляет имя, вводимое пользователем во время регистрации в Windows. Если текущий пользователь не зарегистрирован в Windows, управляющие панели в рабочей области сохраняются в файле Excel.xlb. Эта система хранения имеет одно ограничение: управляющие панели, созданные и сохраненные в рабочей области Excel для одного пользователя, могут оказаться недоступны для других пользователей, если они входят в систему под другим именем пользователя.
Или же можно связать управляющую панель из рабочей области с конкретной книгой Excel. При присоединении управляющей панели к книге копия управляющей панели сохраняется в книге. При каждом открытии книги любые управляющие панели, сохраненные в ней, оказываются доступны. Поскольку книга содержит полные копии любых присоединенных управляющих панелей, можно спокойно удалить управляющую панель из рабочей области Excel. Присоединение нестандартной управляющей панели к книге Excel имеет еще одно преимущество: это упрощает поставку нестандартных управляющих панелей другим пользователям. Если нестандартная управляющая панель присоединена к книге, ее и связанную с ней VBA-программу можно распространять, просто передавая пользователям копию книги, содержащую управляющую панель и ее программные модули.
602
Неделя 3
Присоединение управляющей панели к книге выполняется с помощью кнопки Attach (Вложить) в диалоговом окне Customize. (Диалоговое окно Customize можно отобразить, выбрав команду View-Toolbars -Customize.)
Так надо
ПОМНИТЕ, что не существует способа с помощью VBA-программы присоединить нестандартную управляю- ; щую панель к книге Excel.
ИСПОЛЬЗУЙТЕ VBA-программу для создания временных управляющих панелей (как описано далее в этом уроке) во избежание загромождения файла . xlb пользователя нестандартными панелями инструментов.
Встроенные управляющие панелп
Управляющие панели, присущие конкретному приложению, называют встроенными управляющими панелями. Excel имеет несколько встроенных управляющих панелей. В их число входят все меню, панели инструментов и контекстные меню, присущие Excel. Любые встроенные управляющие панели Excel можно настраивать интерактивно (посредством диалогового окна Customize) или манипулируя управляющей панелью с помощь VBA-программы.
То, встроена ли конкретная управляющая панель в приложение, всегда можно выяснить, проверив свойство Builtln объекта ConunandBar; это свойство всегда возвращает значение True для встроенных управляющих панелей и значение False для управляющий панелей, созданных пользователем.
Общее число встроенных управляющих панелей в таком приложении, как Excel, может быть достаточно большим (Excel имеет более 80 встроенных управляющих панелей.) В частности, это связано с тем, что большинство приложений отображает различные строки меню в зависимости от текущего режима работы приложения. Например, Excel отображает различные строки меню в зависимости от типа данных, отображаемых в активном окне: одну строку меню при отображении листа, другую — для листов диаграмм, третью при отсутствии видимых открытых книг. Каждая из этих <онфигураций меню в действительности является отдельным объектом ConunandBar.
Оюображенпе списка встровпных управляющих панелвп
При работе с управляющими панелями для получения ссылки на конкретную • правляющую панель в коллекции CommandBars используется имя управляющей панели содержащееся в свойстве Name). (Способы доступа к конкретным управляющим панелям в коллекции CommandBars описаны далее в этом уроке.) Вы, вероятно, знаете имена лозданных вами управляющих панелей; однако правильное имя встроенной управляющей панели, которой нужно манипулировать, может быть неизвестно.
В связи со сравнительно большим количеством встроенных управляющих панелей з Excel, система интерактивной справки не приводит имена встроенных управляющих танелей. Один из способов выяснения доступных управляющих панелей (и их имен) — просмотр списка управляющих панелей в диалоговом окне Customize. (Диалоговое кно Customize можно отобразить, выбрав команду View-Toolbars -Customize.)
Для создания списка всех встроенных управляющих панелей Excel (или любого вызывающего VBA-приложения) можно также использовать VBA. Процедура ListBuiltlnCommandBars в листинге 17.1 выполняет задачу отображения списка всех встроенных управляющих панелей Excel и служит примером использования свойств объекта ConunandBar и коллекции CommandBars.
Лень 17-й. Меню и панели инструментов
603
Листинг 17.1. Процедура LIstBulltlnEommaadBors создает список встроопных Вправляющих нонвлой Excel
1:	Sub ListBuiltlnCommandBars()
2:	'Перечисляет иа листе все встроенные панели управления
3:
4:	Dim CmdBar As CommandBar
5:	Dim RCount As Long
6:
7:	'Вывод информации
8:	ActiveWorkbook. Sheets ("sheetl'1) .Activate
9:	ActiveSheet.Cells)1, 1).Value = "Управляющие панели"
10:
11:	RCount = 2
12:	For Each CmdBar In Application.CommandBars
13:	Application.StatusBar = "Вывод " & CmdBar.Name
14:	ActiveSheet.Cells(RCount, 1).Value = CmdBar.Name
15:	If CmdBar.Builtln Then
16:	ActiveSheet.Cells(RCount, 1).Value = CmdBar.Name
17:	RCount = RCount + 1
18:	End If
19:	Next CmdBar
20:
21:	Application.StatusBar = False
22:	End Sub
Строка 4 листинга 17.1 объявляет CmdBar в качестве объектной переменной со специальным типом объекта CommandBar. Строка 5 объявляет переменную RCount, предназначенную для отслеживания текущей строки в списке, генерируемом этой процедурой.
Строка 8 активизирует лист Sheetl в активной книге. Строка 9 вводит заголовок результирующего списка управляющих панелей.
Строка 11 инициализирует переменную RCount значением 2, указывая, что следующий элемент списка должен отображаться во второй строке листа. Строки 12—19 содержат цикл For... Each, проверяющий все управляющие панели в коллекции CommandBars приложения. Переменная объекта CmdBar служит в качестве переменной цикла.
Поскольку построение списка управляющих панелей может занять несколько секунд, строка 13 использует строку состояния Excel для отображения сообщения об обрабатываемой в текущий момент управляющей панели, чтобы пользователь знал, что процедура продолжает работать.
Строка 15 содержит инструкцию If... Then, которая проверяет, является ли проверяемая в текущий момент управляющая панель (на которую ссылается переменная цикла CmdBar) встроенной. Если свойство Builtln управляющей панели имеет значение True, панель является встроенной и в этом случае выполняются строки 16 и 17. Строка 16 отображает в листе имя управляющей панели, полученное из ее свойства Name; строка 17 увеличивает значение переменной RCount. И наконец, строка 21 очищает строку состояния Excel.
На рис. 17.3 показан лист Excel после выполнения процедуры ListBuiltlnCommandBars. Обратите внимание, что список управляющих панелей включает в себя как строки меню (такие как Worksheet Menu Ваг (Строка меню листа)), так и панели инструментов (такие как Standard (Стандартная) и Formatting (Форматирование)).
604
Неделя 3
kJ Microsoft Lxcel - Day17.xls

DSB

A
D
F
•r. 10
Управляющие панели
Управляющие панели Worksheet Menu Bar Chart Menu Bar Standard
Formatting
PivotTable
Chart
Reviewing
1
2 3
4 5
6 7
8
9 Forms
IO 11 12
Stop Recording External Data Auditing Full Screen Circular Reference Visual Basic Web i Control Toolbox
15 16
17 .............
И<> H\Sheetl/Sheet? / Sheets/ Готово
ИГ
Puc. 17.3. Лист Excel после выполнения процедуры ListBuiltlnCommandBars
Объекты, методы n свойства зкемеитов управления
Теперь, получив представление об объекте и коллекции управляющей панели, можно приступить к более подробному изучению объектов, используемых для помещения пунктов меню, кнопок и других элементов управления во встроенную или нестандартную управляющую панель. В следующих разделах приведен обзор различных объектов, свойств и методов, используемых для создания элементов управления в управляющей панели.
Коллекция CommandBarControls
Объект CommandBarControls — это коллекция всех элементов управления конкретной управляющей панели. Доступ к коллекции элементов управления управляющей панели осуществляется посредством свойства Contois объекта CommandBar. В листингах, приведенных далее в этом уроке, демонстрируется использование свойства Contois для возвращения объекта CommandBarControls.
Коллекция CommandBarControls очень проста; по существу, она представляет собой просто хранилище управляющих объектов управляющей панели. Единственно важное свойство коллекции CommandBarControls — свойство Count. Это свойство, имеющее атрибут “только для чтения”, возвращает значение типа Long, указывающее количество элементов управления в коллекции (и, следовательно, количество элементов управления в управляющей панели).
День 17-Й. Меню и панели инструментов
605
Эта коллекция имеет только один метод Add. Метод CommandBarControls.Add добавляет новый элемент управления в управляющую панель. Использование этого метода подробно описано далее в этом уроке.
Объект CommandBarControl
Объект CommandBarControl представляет отдельный элемент управления управляющей панели. Объектами CommandBarControl придется манипулировать в качестве пунктов меню и кнопок панелей инструментов. В табл. 17.7 приведены свойства объекта CommandBarControl; методы этого объекта перечислены в табл. 17.8.
Таблица 17.7. Свойства объекта CommandBarControl
Свойство	Описание
BeginGroup	Чтение-запись, Boolean. Если это свойство имеет значение True, элемент управления управляющей панели начинает группу элементов управления управляющей панели; разделительная линия отображается перед элементом управления
Builtln	Только для чтения, Boolean. Если это свойство имеет значение True, элемент управления является встроенным в Excel. Если элемент управления управляющей панели является нестандартным или встроенным элементом управления, свойство которого OnAction установлено, значение свойства Builtln —- False
Caption	Только для чтения. String. Содержит текст заголовка указанного элемента управления. Имя элемента управления, хранящееся в свойстве Caption, используется для ссылки на конкретный элемент управления в коллекции CommandBarControls. Заголовок элемента управления отображается в качестве его подсказки ScreenTip по умолчанию
DescriptionText	Чтение-запись, String. Содержит описание элемента управления управляющей панели. Описание всегда отображается в строке состояния Excel, когда пользователь помещает указатель мыши над элементом управления
Enabled	Чтение-запись, Boolean. Это свойство необходимо устанавливать равным True, чтобы активизировать элемент управления, и равным False — чтобы отключить его
Height	Чтение-запись, Long. Возвращает или устанавливает высоту элемента управления в пикселях
HelpContextID	Чтение-запись, Long. Содержит идентификационный номер темы справки, связанной с элементом управления. Для использования этого свойства должно быть установлено также свойство HelpFile
HelpFile	Чтение-запись, String. Содержит имя файла справки для темы Help (Справка), связанной с элементом управления справочной панели; это свойство должно использоваться в сочетании со свойством HelpContextID
606
Неделя 3
Продолжение табл. 17.7
Свойство	Описание
Id	Только для чтения, Long. Возвращает идентификационный номер элемента управления. Это свойство используется для ссылки на конкретные элементы управления управляющей панели; идентификационный номер элемента управления определяет встроенное действие для этого элемента управления. Если элемент управления является нестандартным, свойство Id всегда возвращает 1
Left	Только для чтения, Long. Возвращает расстояние в пикселях от левого края элемента управления до левого края экрана
OLEUsage	Чтение-запись, Long. Позволяет указывать то, как элемент управления управляющей панели представляется при слиянии одного приложения Office с другим. Если только оба приложения не поддерживают управляющие панели, используется обычное слияние меню OLE, которое управляется свойством OleMunuGroup. (Для получения более подробной информации об этом свойстве обратитесь к интерактивной справке VBA)
OnAction	Чтение-запись, String. Содержит имя VBA-процедуры, подлежащей выполнению при каждом щелчке на элементе управления или изменении его значения
Parameter	Чтение-запись, String. Некоторые встроенные элементы управления используют это свойство для изменения своего поведения по умолчанию, если вызывающее VBA-приложение способно использовать значение. Для нестандартных элементов управления это свойство можно использовать для передачи информации процедуре OnAction элемента управления или для хранения информации об элементе управления
Priority	Чтение-запись, Long. Возвращает или устанавливает приоритет элемента управления управляющей панели. Значение свойства Priority определяет, может ли элемент управления быть опушен в панели инструментов, прижатой к краю окна, если ее элементы управления не помещаются в одной строке. Допустимыми являются значения от 0 до 7. Значение Priority, равное 1 означает, что элемент управления никогда не будет опускаться; значение 0 позволяет VBA определять приоритет элемента управления автоматически. Что касается любых других значений, то элементы управления, имеющие большие значения этого свойства, опускаются первыми
Tag	Чтение-запись, String. Содержит такую информацию об элементе управления управляющей панели, как информацию о конкретном элементе управления или уникально идентифицирующую его
TooltipText	Чтение-запись, String. Содержит текст, отображаемый в подсказке элемента управления. Если свойство TooltipText содержит пустую строку, то в качестве подсказки используется значение свойства Caption
День 17-Й. Меню и панели инструментов
607
Окончание табл. 17.7
Свойство	Описание
Тор	Только для чтения, Long. Возвращает или устанавливает расстояние в пикселях от верхнего края элемента управления управляющей панели до верхнего края экрана
Туре	Только для чтения, Long. Возвращает тип элемента управления управляющей панели. Значение этого свойства может устанавливаться только во время создания элемента управления. Наиболее часто используемые константы типов элементов управления приведены в табл. 17.2
Visible	Чтение-запись, Boolean. Имеет значение True, если элемент управления управляющей панели видим
Width	Чтение-запись, Long. Возвращает или устанавливает ширину элемента управления управляющей панели в пикселях
Примеры почти всех свойств, перечисленных в табл. 17.7, используются в листингах, приведенных далее в этом уроке. В табл. 17.8 приведены методы, присущие общему объекту CommandBarControl.
Таблица 17.8. Методы объекта CommandBarControl
Метод	Описание
Сору	Копирует элемент управления управляющей панели в другую управляющую панель
Delete	Удаляет элемент управления управляющей панели
Execute	Вызывает выполнение команды, связанной с встроенным элементом управления. Для нестандартных элементов управления этот метод выполняет процедуру, указанную свойством OnAction элемента управления
Move	Перемещает элемент управления из одной управляющей панели в другую
Reset	Переустанавливает устроенный элемент управления; этот метод восстанавливает действия, первоначально выполнявшиеся встроенным элементом управления, и переустанавливает свойства элемента управления в их исходные значения
SetFocus	Перемещает фокус клавиатуры к элементу управления, чтобы он мог принимать ввод с клавиатуры. Конкретный вид воспринимаемой элементом управления поступающей с клавиатуры информации зависит от типа элемента управления. Если свойство Enable элемента управления имеет значение False, этот метод будет генерировать ошибку времени выполнения
Основной объект CommandBarControl имеет несколько подклассов, таких как CommandBarComboBox, CommandBarButton и CommandBarPopup. В действительности существует еще несколько подклассов элементов управления управляющей панели, но, к сожалению, ограниченность объема книги не позволяет описать все доступные элементы управления. Поэтому в этом уроке основное внимание уделяется тому, чтобы дать чи
608
Неделя 3
тателям твердое представление о наиболее часто используемых элементах управления управляющих панелей.
Ниже описаны свойства и методы трех наиболее часто используемых подклассов элементов управления. Внимание уделяется только тем свойствам и методам, которые характерны для подклассов элементов управления. Каждый из следующих подклассов имеет также все свойства и методы, приведенные для общего объекта CommandBarControl в табл. 17.8.
Элемент управления СоттапЛВагВнПнн
Объект управления CommandBarButton — вероятно, наиболее часто используемый элемент управления управляющей панели. Этот элемент управления обеспечивает кнопки в управляющих панелях типа панелей инструментов и пункты меню в управляющих панелях типа меню. Свойства и методы объекта CommandBarButton приведены в табл. 17.9.
Таблица 17.9. Свойства и методы объекта CommandBarButton
Свойство/метод	Описание
BuiltlnFace	Чтение-запись, Boolean. Это свойство имеет значение True, если лицевая поверхность элемента управления (т.е. графическое изображение, отображаемое элементом управления) является его встроенной лицевой поверхностью. Этому свойству можно присваивать только значение True, что переустанавливает лицевую поверхность во встроенную
CopyFace	Метод. Копирует лицевую поверхность кнопки в буфер обмена Windows
Faceld	Чтение-запись, Long. Используется для установки или получения идентификационного номера поверхности кнопки, в текущий момент присвоенной элементу управления кнопки. Свойство Faceld диктует внешний вид кнопки, но не выполняемое ею действие. Свойство Id объекта элемента управления определяет действие, выполняемое кнопкой. Для кнопки с нестандартной лицевой поверхностью значение Faceld равно 0
Paste Face	Метод. Вставляет содержимое буфера обмена Windows поверх объект элемента кнопки
ShortcutText	Чтение-запись, string. Содержит текст клавиши быстрого доступа, отображаемый рядом с кнопкой, когда кнопка отображается в меню, подменю или контекстном меню. Прежде чем можно будет установить свойство ShortcutText, свойство OnAction кнопки должно быть установлено
State	Чтение-запись, Long. Определяет внешний вид кнопки. Свойство State может содержать одну из следующих констант MsoButtonState: msoButtonUp, msoButtonDown, msoButtonMixed.
Style	Чтение-запись, Long. Определяет способ отображения элемента управления кнопки, style может содержать одну из следующих констант MsoButtonStyle: msoButtonAutomatic, msoButtonlcon, msoButtonCaption, msoButtonlconandCaption.
День 17-й. Меню и панели инструментов
609
Элемент управления CommandBarPopup
Объект управления CommandBarPopup — вероятно, второй по значимости элемент управления управляющей панели. Он обеспечивает меню и подменю в управляющих панелях типа меню и всплывающие меню в управляющих панелях типа панелей инструментов.
Элемент управления CommandBarPopup имеет только одно специальное свойство: CommandBar. Каждый элемент управления CommandBarPopup содержит объект управляющей панели, на который можно ссылаться посредством свойства CommandBar. Присоединенный к элементу управления CommandBarPopup объект управляющей панели обеспечивает хранилище для команд всплывающего меню и подменю.
Элемент унравления CommandBarComboBox
Третий наиболее часто используемый элемент управления управляющей панели — элемент управления CommandBarComboBox — в действительности обеспечивает три различных элемента управления: элемент управления поля со списком, элемент управления списка и элемент управления поля Конкретный подтип объекта CommandBarComboBox указывается во время создания элемента управления. Свойства и методы, характерные для элемента управления CommandBarComboBox. приведены в табл. 17.10.
Таблица 17.10. Свойства и методы объекта CommandBarComboBox
Свойство/Метод	Описание
Additem	Метод. Добавляет пункт в элемент управления поля со списком (или списка). Этот метод генерирует ошибку времени выполнения, если выполняется для встроенного элемента управления поля со списком или для элемента управления поля
Clear	Метод. Удаляет все пункты из элемента управления поля со списком или списка
DropDown Lines	Чтение-запись, Long. Возвращает или устанавливает количество строк в элементе управления поля со списком или списка. Если его значение установлено равным 0, количество строк в списке определяется числом элементов в списке. Элемент управления должен быть списком или полем со списком; это свойство генерирует ошибку времени выполнения, если выполняется для встроенного элемента управления или для элемента управления поля
DropDownWidth	Чтение-запись, Long. Возвращает или устанавливает ширину списка в пикселях. Если его значение установлено равным —1, ширина списка определяется длиной самой длинной записи в списке. Если оно установлено равным 0, ширина списка равна ширине элемента управления
List	Чтение-запись, String. Возвращает или устанавливает значение элемента списка в элементе управления поля со списком. Для встроенных элементов управления это свойство имеет атрибут “только дтя чтения”
ListCount	Только для чтения, Long. Возвращает число элементов списка в элементе управления поля со списком
610
Неделя 3
Окончание табл. 17.10
Свойство/Метод	Описание
ListHeaderCount	Чтение-запись, Long. Возвращает или устанавливает число элементов списка в элементе управления поля со списком, которые появляются над разделительной линией. Значение, равное —1, указывает, что в элементе управления поля со списком нет никакой разделительной линии
Listindex	Чтение-запись, Long. Возвращает или устанавливает номер индекса выбранного элемента в списке элемента управления поля со списком. Если в списке ничего не выбрано, это свойство возвращает нуль. Установка свойства Listindex заставляет элемент управления выбрать данный элемент и выполнить в приложении соответствующее действие
Removeitem	Метод. Удаляет элементы списка из элемента управления поля со списком или списка
Text	Чтение-запись, String. Возвращает или устанавливает текст в области отображения или редактирования элемента управления поля со списком или поля
В листингах, приведенных далее в этом уроке, показаны примеры использования основных свойств и методов всех объектов элементов управления управляющих панелей, описанных в табл 17.7 — 17.10. В следующем разделе этой главы освещены элементы управления управляющих панелей, встроенные в Excel.
Встроенные элементы управления
Кроме встроенных управляющих панелей, Excel содержит встроенные элементы управления для управляющих панелей. Эти встроенные элементы управления выполняют такие предопределенные задачи, как сохранение активной книги, отображение диалогового окна Save As (Сохранить как) и т.п. Excel включает несколько тысяч встроенных элементов управления для управляющих панелей.
Хотя можно создать нестандартные элементы управления и написать все программы для выполнения элементом управления требуемого действия, часто можно сэкономить массу времени, используя встроенные элементы управления в нестандартных панелях инструментов или меню. Нередко задача, которую нужно выполнить, оказывается уже запрограммированной и внедренной в один из встроенных элементов управления управляющей панели приложения, включая кнопки панели инструментов, всплывающие меню, списки выбора шрифтов и т.п.
Каждый встроенный элемент управления управляющей панели идентифицируется специальным численным кодом. Указание встроенного элемента управления осуществляется посредством присвоения свойству Id объекта CommandBarControl идентификационного номера, соответствующего этому встроенному элементу управления.
Выяснить, встроен ли конкретный элемент управления управляющей панели в приложение, можно, проверив свойство Builtln объекта CommandBarControl; это свойство всегда возвращает значение True для встроенных элементов управления и False для нестандартных элементов управления.
Общее количество встроенных элементов управления управляющих панелей в таких приложениях, как Excel, обычно достаточно велико — как правило, несколько более 3000. Из-за большого количества встроенных элементов управления в Excel сис
День 17-Й. Меню и панели инструментов
611
тема интерактивной справки не приводит идентификационные номера, соответствующие различным встроенным элементам управления. Для создания списка всех встроенных элементов управления управляющих панелей Excel можно использовать VBA-программу. Процедура ListAllCommandBarControls в листинге 17.2 выполняет задачу отображения списка всех встроенных элементов управления управляющих панелей Excel и служит примером использования свойств объекта CommandBarControl и коллекции CommandBarControls.
Листинг 17.2. Создание списка всшровипых злемептпв дправлеиия управляющих панелвО
1: 2: 3: 4:	Sub ListAllCommandBarControls;) 'Создает список всех встроенных управляющих 'панелей, их имен и идентификаторов t
5: 6:	Const Maxitems = 4000
8: 9: 10: 11! 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:	Dim CmdBar As CommandBar Dim aBtn As Object Dim IDCount As Long Set CmdBar = CommandBars.Add(Name:="Temporary", _ Position:=msoBarFloating, MenuBar:=False, temporary:=True) On Error Resume Next For IDCount = 1 To Maxitems Application.StatusBar = "Adding ID " & IDCount CmdBar.Controls.Add ID:=IDCount Next IDCount On Error GoTo 0 'Вывод информации на лист ActiveWorkbook.Sheets("sheet2").Activate With ActiveSheet .Cellsfl, 1).Value = "ID" .Cells(l, 2).Value = "Caption" End With
31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:	IDCount = 1 For Each aBtn In CmdBar.Controls Application.StatusBar = "Вывод ID " & IDCount With ActiveSheet .Cells(IDCount + 1, 1).Value = aBtn.ID .Cells(IDCount + 1, 2).Value = aBtn.Caption End With IDCount = IDCount + 1 Next aBtn CmdBar.Delete
43: 44:	Application.StatusBar = False End Sub
612
Неделя 3
По существу, процедура ListAllCommandBarControls работает, добавляя идентификаторы встроенных элементов управления в объект временной управляющей панели до достижения ею разумного предела, устанавливаемого константой Maxitems, объявленной в строке 11. Значение Maxitems установлено равным 4000, поскольку обычно Excel содержит от 3000 до 4000 встроенных элементов управления управляющих панелей. Если когда-либо при выполнении этой процедуры будет получена информация точно о 4000 элементов управления, следует увеличить значение константы Maxitems.
В строках 8-10 объявляется несколько переменных, используемых в процедуре ListAllCommandBarControls. CmdBar служит для хранения ссылки на объект временной управляющей панели, a aBtn — д ля хранения ссылок на различные объекты элементов управления управляющих панелей. IDCount применяется в качестве переменной счетчика цикла.
Строки 12-15 содержат одну инструкцию, которая вызывает метод Add коллекции CommandBars приложения. Эта инструкция создает новый объект управляющей панели, названный “Temporary”, размещаемый в качестве панели инструментов, прижатой к краю окна. Новая управляющая панель является временной, т.е. она не будет автоматически сохраняться программой Excel.
В строке 16 устанавливается ловушка обработки ошибок. В данном случае обработка ошибок заключается просто в переходе к инструкции, следующей за инструкцией, вызвавшей ошибку. Это как раз одни из немногих случаев, когда подходит этот тип обработки ошибок: он позволяет процедуре продолжать выполняться даже при генерации неверного идентификатора элемента управления.
ПРИМЕЧАНИЕ
В листингах 17.2 и 17.3 инструкция On Error VBA служит для установки ловушек обработки ошибок, которые заставляют процедуру продолжать выполняться даже в случае ошибки времени выполнения. Этот аспект языка VBA еще не освещался в этой книге. Однако процедуры обработки ошибок в листингах 17.2 и 17.3 весьма важны для их функционирования. На 18-м уроке особенности обработки ошибок будут описаны более подробно. Применительно к листингам 17.2 и 17.3 достаточно знать, что инструкции On Error вызывают продолжение выполнения с инструкции, следующей непосредственно за той, что привела к ошибке времени выполнения, без остановки или отображения диалогового окна сообщения об ошибке времени выполнения.
Строки 17—20 содержат цикл For... Next, который создает Maxitems новых элементов управления управляющих панелей. Каждому новому элементу управления присваивается значение идентификатора, соответствующее текущему значению счетчика цикла. Строка 18 обновляет строку состояния приложения, чтобы пользователь знал, что процедура продолжает выполняться, даже если построение управляющей панели занимает некоторое время. Строка 19 использует метод Add коллекции CommandBarControls, возвращаемый свойством Controls объекта управляющей панели, на который ссылается константа CmdBar, для добавления нового элемента управления в управляющую панель. Свойство Id нового элемента управления устанавливается равным текущему значению счетчика цикла посредством использования аргумента ID метода Add.
Строка 22 удаляет ловушку обработки ошибок, ранее установленную в строке 16.
Строки 25-29 выбирают лист в книге и вводят заголовок списка встроенных элементов управления управляющих панелей. Строки 32—39 содержат цикл For Each, который осуществляет последовательный просмотр всех элементов управления в управляющей панели, на которую ссылается константа CmdBar. Программа внутри цикла вставляет в список идентификационный номер и заголовок элемента управления.
И наконец, в строке 41 метод Delete управляющей панели, на которую ссылается CmdBar, используется для удаления управляющей панели из коллекции CommandBars приложения. На рис. 17.4 показан лист Excel после выполнения процедуры ListAllCommandBarControls.
День 17-й. Меню и панели инструментов
613
1Д Microsoft Excel - Dayl 7.xls
файл Ставка 8иа Вставка Формат Сервис Данные Дкно Оправка ’ ‘	Iffl X
Д2н£Я * Ч2>®<7 «’ <- ft г f- 11 Я О«;-'• ’ О.
____ »ю ~ е W Я ш ® % «8 Й Я5^<*]шу*• &<
_______А1_______2]	Ю_______________________________~
•!• |lD [Caption	.
2 «Орфография .
3 «Сохранить	 Cj
___	4	«Печать
______	18	Созд&ать .
d_____	19	«Копировать	_	"i
___21 «Вырезать
22 Вст&авить	_	: |ч,
___।	23 Открыть	j
37 Пов&торить очистку	, |"
42 «Microsoft Word	; J igs
47 Очистить	I
51 Другая
52 «Копилка
59 Другая	d
,	60 «Двойное подчеркивание	: гй
М <HH\Sheetl\sheet2/Sheet3/	|«|	|	->][”
Готово	NUM
Рис. 17.4. Excel отображает этот лист после выполнения процедуры ListAllCommandBarControls
Так надо
ПОМНИТЕ, что свойство Controls возвращает коллекцию элементов управления управляющей панели.
ИСПОЛЬЗУЙТЕ свойство StatusBar приложения для отображения сообщений о протекании процесса, которые позволяют пользователям процедуры определить, что процедура все еще выполняется - особенно когда она выполняет задачи, занимающие длительное время.
Лицевые поверхности элементов управления
Лицевая поверхность элемента управления — это графическое изображение на поверхности элемента управления, которое можно видеть, когда он появляется на экране. Свойство Faceld элемента управления содержит численное значение, указывающее, какую лицевую поверхность использует данный элемент управления. Лицевую поверхность элемента управления можно выбрать из предопределенной группы поверхностей элементов управления, определенных библиотекой Office VBA.
Чтобы присвоить поверхность элементу управления управляющей панели, необходимо знать, какой идентификационный номер соответствует нужной лицевой поверхности. Доступные идентификационные номера лицевых поверхностей и соответствующие им графические изображения в интерактивной справке VBA не приводятся. Вместо этого можно написать VBA-процедуру для создания панелей инструментов, содержащие кнопки для отображения всех доступных лицевых поверхностей. Листинг 17.3 содержит такую процедуру.
6/4
Неделя 3
Листппа 17.3, ОшоОражвиив осек двстдппых лицевых повврхппсшеО злемептов управления
1:	Option Explicit
2:
3:	Sub DisplayAllFacelDsf)
4:	'Создает несколько управляющих панелей для демонстрации вида кнопок.
5:	'В каждой панели может быть 250 различных видов кнопок.
6:
7:	Const MaxFaces = 250
8:	Dim	fDone As Boolean
9:	Dim	IDStart As Long
10:	Dim	IDEnd As Long
11:	Dim	CmdBar As CommandBar
12:	Dim	k As Long
13:	Dim aBtn As CommandBarButton
14:	Dim	tLeft As Long
15:	Dim	tTop As Long
16:
17:	On Error Resume Next
18:
19:	IDStart	= 0
20:	IDEnd	=	MaxFaces - 1
21:	tLeft	=	50
22:	tTop = 100
23:	fDone	=	False
24:	Do
25:	Set CmdBar = CommandBars.Add(Position:=msoBarFloating,
26:	MenuBar:=False,
27:	temporary:=False)
28:	With CmdBar
29:	.Left = tLeft
30:	.Top = tTop
31:	.Visible = True
32:	For k = IDStart To IDEnd
33:	Application.StatusBar = "Adding Face: " 4 k
34:	Set aBtn = .Controls.Add(Type:=msoControlButton,
35:	temporary:=False)
36:	aBtn.Faceld =	k
37:	If Err.Number	=	0	Then
38:	aBtn.TooltipText	= “Face ID: "4k
39:	Else
40:	fDone = True
41:	IDEnd = k - 1
42:	aBtn.Delete
43:	Exit For
44:	End If
45:	Next k
46:	End With
47:
48:	CmdBar.Name = "Faces " 4 IDStart 4 " to " 4 IDEnd
49:	CmdBar.Width = 600
50:
51:	If Not fDone Then
День 17-й. Меню и панели инструментов
615
52:	IDStart	= IDEnd	+ 1
53:	IDEnd =	IDStart	+ (MaxFaces	- 1)
54:	tLeft =	tLeft +	10
55:	tTop = tTop + 20
56:	End If
57:	Loop Until fDone
58:
59:	Application.StatusBar = False
60:	End Sub
В ходе выполнения процедуры Display Al LFacelds создается серия нестандартных управляющих панелей (каждая из которых содержит максимум 250 кнопок) и кнопкам присваиваются последовательные значения идентификаторов лицевой поверхности до тех пор, пока не произойдет ошибка. Ошибка в присвоении кнопке идентификационного значения лицевой поверхности означает отсутствие разрешенных значений идентификаторов лицевых поверхностей, в результате чего процедура завершается.
..........	Даже на быстром компьютере процедура DisplayAllFacelDs может требовать не-/nPMMFlIllllllF сколько минут для завершения своей задачи. Для проверки выполнения процедуры
DisplayAllFacelDs следите за строкой состояния Excel; строка состояния обновляется каждый раз, когда новый идентификатор лицевой поверхности добавляется в нестандартную управляющую панель.
В строке 7 объявляется константа MaxFaces, которая ограничивает максимальное количество лицевых поверхностей в управляющих панелях, создаваемых процедурой DisplayAllFacelDs. В строках 8—15 объявляются переменные, используемые процедурой DisplayAllFacelDs. fDone — переменная типа Boolean, применяемая в качестве флага для управления выполнением цикла Do в строках 24-57. IdStart и IDEnd служат для хранения начального и конечного значений идентификаторов каждой управляющей панели. CmdBar и aBtn — объектные переменные, используемые для временного хранения ссылок на новые управляющие панели и элементы управления управляющих панелей, создаваемые процедурой DisplayAllFacelDs. tLeft и tTop позволяют хранить значения местоположения верхнего левого угла каждой управляющей панели; к служит в качестве переменной счетчика цикла.
В строке 17 устанавливается ловушка обработки ошибок, заставляющая процедуру продолжать выполняться со следующей VBA-инструкции даже в случае ошибки времени выполнения. В строках 19-23 выполняется инициализация переменных процедуры DisplayAllFacelDs: IDStart устанавливается в 0 (для каждого первого идентификатора лицевой поверхности), a IDEnd устанавливается на единицу меньше, чем максимальное число лицевых поверхностей управляющей панели. (Идентификационные номера лицевых поверхностей начинаются с 0, поэтому диапазон 0—249 представляет идентификационные номера первых 250 лицевых поверхностей.) Переменные tLeft и tTop устанавливаются в 50 и 100 пикселей соответственно. Эти две переменные обеспечивают координаты верхнего левого угла каждой управляющей панели, отображаемой процедурой DisplayAllFacelDs; первая управляющая панель отображается с верхним левым углом, отстоящим на 50 пикселей от левого и на 100 пикселей от верхнего края экрана. Переменной флага fDone присваивается значение False, поскольку цикл не завершил создание всех управляющих панелей идентификаторов лицевых поверхностей.
В строке 24 начинается цикл Do, который создает управляющую панель, а затем заполняет ее 250 кнопками. Каждой кнопке в управляющей панели присваивается последовательный идентификатор лицевой поверхности, и поэтому конечным результа
616
Неделя 3
том этого цикла является ряд управляющих панелей, которые в любом случае содержат кнопки со всеми доступными идентификаторами лицевых поверхностей.
В строках 25-27 содержится единственная инструкция, которая использует метод Add коллекции CommandBars приложения для добавления новой управляющей панели. Новая управляющая панель не прижата к краю экрана панелью инструментов, не является строкой меню (т.е. она отображается как стандартная панель инструментов) и не является временной (т.е. созданные процедурой DisplayAllFacelDs панели инструментов будут автоматически сохраняться в рабочей области Excel). Метод Add возвращает объектную ссылку на только что созданную управляющую панель, которая присваивается переменной CmdBar.
В строке 28 начинается инструкция With, которая содержит инструкции для завершения инициализации новой управляющей панели. Строки 29 и 30 устанавливают координаты для верхнего левого угла управляющей панели, присваивая значения ее свойствам .Left, и Тор. Присваивая значение True свойству .Visible новой управляющей панели, строка 31 обеспечивает ее видимость.
Строки 32-45 содержат цикл For... Next, который заполняет управляющую панель кнопками и присваивает каждой кнопке идентификационный номер. Значения счетчика цикла For изменяются от текущего значения переменной IDStart до текущего значения переменной IDEnd. Когда этот цикл For выполняется в первый раз, значения его счетчика изменяются от 0 до 249; при втором выполнении цикла значения счетчика изменяются от 250 до 499 и т.д.
Строка 33 обновляет строку состояния Excel, чтобы пользователь знал, что процедура работает (даже на быстром компьютере процесс построения каждой управляющей панели занимает несколько секунд).
Строки 34 и 35 содержат одну инструкцию, которая использует метод Add коллекции CommandBarControls, возвращаемой свойством Controls объекта управляющей панели CmdBar, для создания нового элемента управления управляющей панели. Аргумент Type:=msoControlButton приводит к тому, что новый элемент управления управляющей панели создается в качестве элемента управления CommandBarButton. Аргумент Temporary метода Add устанавливается в значение False, чтобы по окончании сеанса работы этот элемент управления управляющей панели был сохранен в управляющей панели. Метод Add коллекции CommandBarControls возвращает объектную ссылку на только что созданный элемент управления управляющей панели; эта ссылка присваивается объектной переменной aBtn.
Строка 36 присваивает текущее значение счетчика цикла For (к) свойству .Faceld нового объекта кнопки, на который ссылается переменная aBtn. Таким образом, первая созданная этой процедурой управляющая панель будет содержать кнопки с идентификаторами лицевых поверхностей от 0 до 249; вторая управляющая панель будет содержать кнопки с идентификаторами лицевых поверхностей от 250 до 499 и т.д.
Любая попытка присвоить свойству .Faceld неверный идентификационный номер лицевой поверхности приводит к ошибке времени выполнения. Таким образом, когда все разрешенные идентификационные номера лицевых поверхностей использованы, в строке 36 будет происходить ошибка времени выполнения. Строка 37 использует объект Err (см. 18-й урок) для определения того, имела ли место ошибка времени выполнения. (Инструкция обработки ошибок в строке 17 предотвращает остановку процедуры при наличии ошибки времени выполнения.)
Если ошибки времени выполнения не происходит, выполняется инструкция в строке 38, которая присваивает значение свойству TooltipText кнопки, чтобы идентификационный номер лицевой поверхности отображался в экранной подсказке кнопки. (Экранная подсказка — это информация, отображаемая при помещении указателя мыши над элементом управления управляющей панели; для отображения подсказок
День 17-й. Меню и панели инструментов
617
флажок Display ScreenTips (Отображать подсказки для кнопок) в диалоговом окне Customize (Настройка) должен быть установлен.)
Если ошибка времени выполнения происходит, выполняются инструкции в строках 40—43. Вначале эти инструкции устанавливают переменную флага FDone в значение True, чтобы цикл Do завершился, настраивают значение переменной IDEnd, удаляют последнюю созданную кнопку управляющей панели, а затем осуществляют выход из цикла For.
В строке 48 свойство Name управляющей панели используется для присвоения управляющей панели имени, которое указывает, идентификаторы каких поверхностей кнопок находятся в этой управляющей панели. В стоке 49 свойство Width управляющей панели устанавливается равным 600 пикселям; это значение было определено методом проб и ошибок, обеспечивая размещение 10 строк по 25 кнопок в каждой, что в сумме дает 250 кнопок.
В строках 51—56 выполняется увеличение значений переменных IDStart, IDEnd, tLeft и tTop только в том случае, если значение переменной флага fDone — False. Значения переменных IDStart и IDEnd увеличиваются таким образом, чтобы при следующем выполнении цикла Do он заполнял новую управляющую панель следующим блоком из 250 кнопок. Значения переменных tLeft и tTop увеличиваются так, чтобы каждая последующая управляющая панель отображалась несколько ниже и правее предыдущей.
На рис. 17.5 показана книга Excel непосредственно после выполнения процедуры DisplayAllFacelDs. Каждая управляющая панель, за исключением последней (отображенной поверх остальных), созданная этой процедурой, содержит 250 кнопок. Инструкции обработки ошибок и тесты в процедуре DisplayAllFacelDs остановили заполнение последней управляющей панели, когда все разрешенные идентификационные номера лицевых поверхностей были использованы.
Microsoft Excel - Dayl 7.xls

|Щд>айл Правка Вид Вставка Формат Сервис Данные Дкно Справка
__ - l<g| Х|

Arial
iwirrsi
8В| Й
W..i '
4
_5” б".
7 '
$
9
10
11 
12
13"
14
15-
16.
17
M\s
Готово
Faces Oto 249
graces 250to499 i^rr-m-ii-miii i —
HHjF_acesV000to124^ "
^^^M^Fsces 1500 to 1749  ^^^MfFaces 1750to~1999~
^^^^MfacesZOOQ to 2249
МИИИиЬ-асег 2bU0to 2749 ИИИ1^^ИИ|расез275^о2999
Faces 5500 to 5684
<&
Face ID.560в|
Rl, —г
Rl ;
№|.
—Rl.
MRl
Rl Rl №
se « а ф



н
Рис. 17.5. Процедура DisplayAllFacelDs создает эти управляющие панели, отображая все доступные лицевые поверхности кнопок
618
Неделя 3
Как видно из рис. 17.5, многие идентификаторы лицевых поверхностей кнопок пусты; эти идентификаторы лицевых поверхностей соответствуют пунктам всплывающих меню, рядом с которыми не отображаются графические изображения. Можно также заметить, что многие идентификаторы лицевых поверхностей кнопок содержат идентичные графические изображения. Первые 1000 идентификационных номеров представляют наибольший интерес и являются наиболее полезными.
Чтобы выяснить идентификационный номер лицевой поверхности, соответствующий конкретному изображению на кнопке, задержите указа!ель мыши над этой кнопкой и прочтите идентификационный номер лицевой поверхности в подсказке кнопки. На рис. 17.5 показана видимая подсказка.
Так надо
ПОМНИТЕ, что текст, отображаемый в подсказке элемента управления управляющей панели, получается из свойства TooltipText элемента управления.
ПОМНИТЕ, что ад отображаются, только если флажок Show ScreenTips on Toolbars (Отображать подсказки для кнопок) во вкладке Options (Параметры) диалогового окна Customize (Настройка) установлен. (Для отображения диалогового окна Customize выберите команду View-Toolbars-Customize.)

НЕ ЗАБУДЬТЕ, что многие идентификационные номера лицевых поверхностей кнопок имеют идентичные графические изображения или пусты. В целом, наиболее полезны и интересны примерно первые 1000 идентификаторов лицевых поверхностей.
Управление нестандартными и встроенными
управляющими панелями
При создании самостоятельного приложения необходимо знать, как создавать собственные нестандартные управляющие панели, которые будут использоваться в качестве меню или панелей инструментов, и как отображать или удалять их. В этом разделе показано использование объектов и методов управляющей панели для создания, отображения и удаления нестандартной управляющей панели и для восстановления встроенной управляющей панели в ее первоначальном состоянии. Пример исходного текста программы, собирающего все эти элементы воедино, приведен в конце этого урока.
СОВЕТ
В общем случае следует создавать собственные нестандартные управляющие панели, а не модифицировать встроенные. Хотя всегда можно восстановить исходное состояние встроенной управляющей панели, она может оказаться уже настроенной, и параметры управляющей панели по умолчанию не приведут к действительному восстановлению панели инструментов к состоянию, привычному для конкретного пользователя.
Добавление новой управляющей панели
Первый шаг по созданию нестандартной системы меню иди панелей инструментов заключается в добавлении нового объекта упраатяюшей панели в коллекцию управляющих панелей приложения. Для этого используется метод Add коллекции CommandBars.
День 17-й. Меню и панели инструментов
619
Общий синтаксис для создания новой управляющей панели выглядит следующим образом:
CommandBars.Add([Name,] [Position,] [MenuBar,] [Temporary])
Все аргументы метода Add являются необязательными. Name представляет любое строковое выражение и указывает имя, которое нужно присвоить новой управляющей панели. Это имя используется для доступа к новой управляющей панели. Position представляет численное выражение, определяющее допустимый позиционный номер новой управляющей панели. Этим параметром может быть любая из констант, приведенных в табл. 17.11.
MenuBar — выражение типа Boolean, указывающее, должна ли новая управляющая панель использоваться в качестве строки меню. Если значение MenuBar — True, новая управляющая панель инициализируется так, чтобы отображаться на экране в качестве строки меню; если это значение — False, новая управляющая панель отображается как панель инструментов. Аргумент Temporary также является выражением типа Boolean, которое в данном случае указывает, является ли новая управляющая панель временной. Если значение аргумента Temporary равно True, управляющая панель не будет сохранена в конце текущего сеанса работы; в противном случае она автоматически сохраняется в рабочей области Excel.
Метод Add создает пустую управляющую панель и добавляет ее в коллекцию CommandBars; по умолчанию новая управляющая панель видима. Листинги 17.2 (строки 17-20) и 17.3 (строки 25-27) содержат примеры использования метода Add для создания новых управляющих панелей.
Так надо
УДАЛЯЙТЕ управляющие панели, как только завершите работу с ними.
ДОБАВЛЯЙТЕ элементы управления в нестандартную управляющую панель прежде, чем делать ее видимой.
: Отображение пустого меню или панели инструментов не слишком полезно.
Так не надо
НЕ ЗАБЫВАЙТЕ использовать свойство ActiveMenuBar коллекции CommandBars для выяснения того, какая строка меню активна в текущее время.
Сокрытие, отображение и позиционирование управляющей панели
VBA-программу можно использовать для отображения или сокрытия как встроенных, так и нестандартных управляющих панелей. Свойство Visible управляющей панели управляет отображением панели на экране. Можно также проверить текущее значение свойства Visible для определения того, отображается ли уже управляющая панель на экране.
Используя свойство Position и одно из предопределенных значений констант, можно указывать позицию управляющей панели: плавающую или одну из нескольких позиций у края окна. В табл. 17.11 приведены константы свойства Position и пояснено их влияние на позицию управляющей панели. (Эти константы можно отобразить в окне Object Browser, выбрав Office в списке Projects/Libraries, а затем выбрав MsoBar-Position в списке Classes (Классы).)
620
Неделя 3
Таблица 17.11. Константы MsoBarPosition для свойства Position управляющей панели
Константа	Действие
msoBarBottom	Прижимает управляющую панель горизонтально к нижнему краю окна приложения
msoBarFloating	Помещает управляющую панель в плавающее окно панели инструментов
msoBarLeft	Прижимает управляющую панель вертикально к левому краю окна приложения
msoBarMenu Bar	Отображает управляющую панель в качестве строки меню, проходящей поперек верхнего края окна приложения
msoBarPopup msoBarRight	Отображает управляющую панель в качестве всплывающего меню Прижимает управляющую панель вертикально к правому краю окна приложения
msoBarTop	Прижимает управляющую панель горизонтально к верхнему краю окна приложения
Только что созданная управляющая панель полностью пуста. Обычно следует добавить элементы управления в управляющую панель перед ее отображением. При отображении управляющей панели типа строки меню она становится единственной системой меню, доступной интерактивному пользователю - только меню созданной строки меню будут доступны. Если отобразить пустую строку меню, пользователь окажется не в состоянии выполнить какие-либо действия! Каждая создаваемая управляющая панель типа строки меню должна иметь команду, удаляющую нестандартное меню и восстанавливающую исходную строку меню приложения.
ПРИМЕЧАНИЕ
В листинге 17.4 демонстрируется использование свойств Visible и Position для сокрытия и отображения панели инструментов Visual Basic во всех ее крайних позициях и в окне плавающей панели инструментов.
Листинг 17.4. Изменение видимости и нозиции иоиели инструментов Visual Basic, оыцоиняемое нроцвдцроО OEmoTnoltiarPosItlOH
1:	Option Explicit
2:
3:	Private Const DELAY1 As Integer = 2
4:
5:	Sub DemoToolbarPosition()
6:
7:	Const dTitle = "Demo Toolbar Positioning"
8:
9:	Dim	oldvisible As Boolean
10:	Dim	oldPosition As Integer
11:	Dim	tBar As CommandBar
12:
13:	MsgBox prompt:="Измененне установок управляющей " &
14:	"панели Visual Basic",
15:	Buttons:=vblnformation, Title:=dTitle
16:
День 17-й. Меню и панели инструментов
621
17:	Set tBar = CommandBars)"Visual Basic")
18:	oldVisible = tBar.Visible 'Запоминаем текущие установки
19:	oldPosition = tBar.Position
20:
21:	If tBar.Visible Then
22:	MsgBox prompt:="Панель VB будет скрыта.", _
23:	Buttons:=vblnformation, Title:=dTitle
24:	Else
25:	MsgBox prompt:="Панепь VB будет отображена.",
26:	Buttons:=vblnformation, Title:=dTitle
27:	End If
28:
29:	tBar.Visible = Not tBar.Visible
30:
31:
32:	MsgBox prompt:="Панель Visual Basic будет " &
33:	"выведена плавающей, а потом прижата к " &
34:	"различным краям окна, с небольшой паузой " &
35:	"при изменении позиции.",
36:	Buttons:=vblnformation, Title:=dTitle
37:
38:	tBar.Position = msoBarFloating
39:	tBar.Visible = True
40:	Wait DELAY1
41:
42:	tBar.Position = msoBarTop
43:	Wait DELAY1
44:
45:	tBar.Position = msoBarBottom
46:	Wait DELAY1
47:
48:	tBar.Position = msoBarLeft
49:	Wait DELAY1
50:
51:	tBar.Position = msoBarRight
52:	Wait DELAY1
53:
54:	'Восстанавливаем старые установки.
55:	MsgBox prompt ^"Восстанавливаем старые установки.",
56:	Buttons:=vblnformation, Title:=dTitle
57:	tBar.Visible = oldVisible
58:	tBar.Position = oldPosition
59:	End Sub
60:
61:
62:	Sub WaitfDelay As Integer)
63:	Dim TheTime As Single
64:	TheTime = Timer
65:	Do
66:	DoEvents
67:	Loop Until (Timer - TheTime) >= Delay
68:	End Sub
622
Неделя 3
Процедура DemoToolbarPosition (строки 5—59 листинга 17.4) действительно достаточно проста: она всего лишь исследует использование свойств Visible и Position объекта CommandBar. Строки 13—15 отображают диалоговое окно сообщения, информирующего пользователя, что в настройках панели инструментов Visual Basic будут выполнены изменения.
Свойство CommandBars объекта Applications в строке 17 используется для установки объектной переменной tBar для ссылки на панель инструментов V isual Basic. (Объектная ссылка Application понятна изначально, поскольку CommandBars является глобальным свойством объекта Application.) Свойство CommandBars возвращает коллекцию управляющих панелей; подобно любой другой коллекции, отдельный элемент коллекции CommandBars указывается посредством указания имени конкретной управляющей панели, на которую нужно сослаться. Поэтому в строке 17 ссылка на панель инструментов Visual Basic присваивается переменной tBar. Объектная переменная используется для ссылки на панель инструментов, чтобы значительно уменьшить объем вводимой с клавиатуры информации и чтобы сделать VBA-программу короче и понятней.
В строках 18 и 19 выполняется сохранение текущих значений свойств Visible и Position панели инструментов Visual Basic, чтобы впоследствии их можно было восстановить.
Строки 21—27 используют инструкцию If... Then... Else для проверки значения свойства Visible управляющей панели. Если в текущий момент управляющая панель видна на экране, строки 22 и 23 используют диалоговое окно сообщения для уведомления пользователя о предстоящем сокрытии управляющей панели; в противном случае строки 25 и 26 отображают сообщение о том, что теперь управляющая панель будет отображаться.
Строка 29 просто использует логический оператор Not для инвертирования текущего значения свойства Visible и для присвоения его в качестве нового значения свойства Visible. Если управляющая панель отображается на экране, строка 29 вызывает ее сокрытие; если управляющая панель не отображается, строка 29 приводит к ее отображению на экране.
Строки 32—36 содержат одну инструкцию MsgBox, которая информирует пользователя о следующем действии процедуры DemoToolbarPosition
Строка 38 присваивает константу msoBarFloating свойству Position управляющей панели, приводя к тому, что управляющая панель будет отображаться в окне плавающей панели инструментов. Поскольку точно не известно, действительно ли панель инструментов отображается на этом этапе, строка 39 принудительно отображает панель инструментов на экране, присваивая значение True свойству Visible. Строка 40 вызывает процедуру Wait для обеспечения задержки на время, указанное константой DELAY1, — в данном случае около 2 секунд.
Затем строка 42 изменяет позицию управляющей панели, прижимая ее к верхнему краю окна приложения, и снова делает задержку приблизительно на 2 секунды. После этого строка 45 изменяет позицию управляющей панели у нижней границы окна приложения и снова делает задержку приблизительно на 2 секунды, чтобы изменение можно было наблюдать на экране. Аналогичным образом строки 48 и 51 прижимают управляющую панель к левому и правому краям окна приложения соответственно.
И наконец, строки 55—58 отображают диалоговое окно сообщения, уведомляющее пользователя о предстоящем восстановлении исходных настроек панели инструментов Visual Basic, а затем присваивают свойствам Visible и Position панели инструментов сохраненные значения.
Строки 62—68 содержат процедуру Wait. Эта процедура воспринимает значение в качестве времени ожидания в секундах. Она просто выполняет цикл до тех пор, пока
День 17-й. Меню и панели инструментов
623
текущее значение системного таймера не превысит значение таймера на момент вызова процедуры Wait на величину, указанную аргументом Delay.
Обратите внимание на инструкцию DoEvents в строке 66 процедуры Wait. Эта инструкция важна потому, что она обеспечивает возможность приложению Excel выполнять такие задачи, как обновление отображения на экране, во время выполнения цикла в процедуре Wait. Без инструкции DoEvents в процедуре Wait приложение Excel было бы не в состоянии отображать управляющую панель в ее новых позициях на экране.
Так надо
СОХРАНЯЙТЕ исходные свойства Position и Visible управляющей панели перед их изменением, чтобы впоследствии эти настройки можно было восстановить.
ПРОВЕРЯЙТЕ значение свойства Protection для того чтобы узнать, допустима ли конкретная позиция па-, нели инструментов непосредственно у границы окна.
Так не надо
НЕ СЧИТАЙТЕ, что все управляющие панели (встроенные или нестандартные) могут быть прижаты к любому краю окна. Некоторые управляющие панели слишком длинны, чтобы быть прижаты к левому или правому краю окна и их свойство Protection установлено для предотвращения этого. При попытке размещения защищен-; ной панели инструментов в любой из запрещенных позиций VBA генерирует ошибку времени выполнения.
Удаление управляющей панели
Управляющая панель и ее элементы управления занимают определенный объем памяти. Для восстановления ресурсов памяти, используемых нестандартными управляющим панелями, следует удалять любые панели, в которых программа больше не нуждается. Для удаления управляющей панели используйте метод Delete управляю-
щей панели.	
^ЛРНММИЕ	Нестандартная управляющая панель отличается от переменной VBA - она не исчезает по окончании выполнения VBA-процедуры. При создании новой управляющей панели она остается в коллекции CommandBars Excel до конца текущего сеанса работы, если только она не будет удалена. Фактически управляющая панель будет автоматически сохраняться в рабочей области Excel, если только она не создается в качестве временной управляющей панели (путем установки аргумента Temporary метода Add в значение True во время создания управляющей панели).
Общий синтаксис для удаления управляющей панели выглядит следующим образом.
CommandBarObject.Delete
CommandBarObject — любая допустимая объектная ссылка на объект CommandBar. Когда VBA выполняет метод Delete, управляющая панель удаляется и любая память, используемая этой управляющей панелью, возвращается в общий пул доступной памяти. Встроенные управляющие панели нельзя удалять: VBA игнорирует попытки удаления встроенной управляющей панели, не генерируя ошибки времени выполнения. Следующая процедура может служить примером использования метода Delete.
624
Неделя 3
Sub DeleteCustomCommandBars()
Dim tBar As CommandBar
ForEach tBar in CommandBars
If Not tBar.Builtln Then tBar.Delete
End If Next tBar End Sub
В предыдущем примере процедура удаляет все нестандартные управляющие панели в коллекции CommandBars приложения, оставляя встроенные управляющие панели без изменений.
Переустановка встроенное управляющей панели
Если было выполнено редактирование или модификация встроенной управляющей панели, на определенном этапе может потребоваться удалить из управляющей панели нестандартные команды или меню. Вместо написания всех VBA-инструкций для отмены любых выполненных изменений можно воспользоваться методом Reset объекта CommandBar для возвращения встроенной управляющей панели в ее исходное, устанавливаемое по умолчанию состояние.
Общий синтаксис восстановления встроенной управляющей панели следующий.
CommandBarObject.Reset
CommandBarObject представляет любую допустимую объектную ссылку на объект управляющей панели. При выполнении метода Reset управляющая панель, на которую осуществляется ссылка, восстанавливается в своей конфигурации по умолчанию — все нестандартные элементы управления удаляются из нее, независимо от времени или способа их создания. Следующий пример процедуры демонстрирует пример метода Reset.
Sub ResetWorkSheetMenuBarf)
’ reset the built-in worksheet menu
CommandBars)"Worksheet Menu Bar"(.Reset
' reactivate the worksheeet menu
CommandBars("Worksheet Menu Bar"(.Visible = True
End Sub
Коллекция CommandBars Excel используется для возвращения ссылки на Worksheet Menu Ваг (строка меню листа), которая переустанавливается, а затем делается видимой.
ПРИМЕЧАНИЕ
Если метод Reset используется применительно к управляющей панели, которая была настроена посредством диалогового окна Customize (Настройка) или какой-либо другой VBA-программы, можно удалить команды, которые не предполагалось удалять. При использовании метода Reset следует действовать осторожно.
День 17-й. Меню и панели инструментов
625
Работа с элементами управления
Чтобы сделать нестандартную управляющую панель полезной, необходимо создавать и управлять элементами управления, которые она содержит. В этом разделе показано, как создавать, отображать или удалять элементы управления в управляющей панели типа меню или панели инструментов, будь то нестандартная или встроенная управляющая панель. Если вы добавляете нестандартные элементы управления, необходимо также указать процедуры событий, которые должны выполняться при щелчке на элементе управления. В некоторых случаях может потребоваться добавление нестандартного элемента управления в одну из встроенных управляющих панелей приложения.
Для создания, манипулирования и управления элементами управления управляющих панелей необходимо выполнить некоторые или все из следующих операций.
•	Добавление элемента управления в управляющую панель.
•	Присвоение имени новому элементу управления.
•	Указание или изменение действия, подлежащего выполнению при щелчке на элементе управления.
•	Определение текста подсказки, отображаемой для элемента управления в строке состояния.
•	Включение и отключение элемента управления.
•	Удаление элемента управления.
•	Установка флажка элемента управления в управляющей панели типа меню. (Установить флажок — означает поместить метку слева от пункта меню или отобразить сопровождающую пункт меню кнопку в “нажатом” положении.)
В последующих разделах показано, как выполнять каждую из этих задач управления элементами управления управляющей панели.
Добавление элемента управления в унравляющуш панель
Для добавления элементов управления в нестандартную или встроенную управляющую панель применяется метод Add коллекции CommandBarControls. Свойство Controls управляющей панели возвращает коллекцию CommandBarControls, принадлежащую этой управляющей панели.
Для добавления элемента управления в управляющую панель используется следующий общий синтаксис.
CommandBar.Controls.Add([Туре,] [Id,] [Parameter,] [Before,] [Temporary])
CommandBar представляет любую допустимую объектную ссылку на объект CommandBar. Все аргументы метода Add являются необязательными. Аргумент Туре — численное выражение, являющееся типом подлежащего созданию элемента управления. Наиболее часто используемые значения аргумента Туре приведены в табл. 17.2.
Аргумент Id — численное выражение, представляющее идентификационный номер встроенного элемента управления. Для создания списка доступных встроенных элементов управления и их идентификационных номеров используйте процедуру, показанную в листинге 17.2. Если опустить аргумент Id метод Add заключает, что создается нестандартный элемент управления, и добавляет в управляющую панель пустой элемент управления.
626
Неделя 3
Аргумент Parameter представляет любое выражение, которое нужно использовать для инициализации свойства Parameter элемента управления.
Аргумент Before является любым численным выражением и указывает, перед каким элементом управления в управляющей панели должен быть вставлен новый элемент управления. Если опустить этот аргумент, метод Add вставит новый элемент управления в конец управляющей панели.
И наконец, аргумент Temporary — выражение типа Boolean, указывающее, является ли элемент управления временным добавлением в управляющей панели. Если значение аргумента Temporary — True, новый элемент управления не сохраняется с объектом управляющей панели; в противном случае в конце текущего сеанса работы элемент управления автоматически сохраняется с управляющей панелью (при условии, что сохраняется и сама управляющая панель). Листинги 17.2 и 17.3 содержат примеры использования метода Add для создания новых элементов управления управляющих панелей.
Так надо
СОЗДАВАЙТЕ подменю и в управляющих панелях типа меню, указывая msoControlPopup для аргумента Туре метода Add.
ПРИДЕРЖИВАЙТЕСЬ стандартов разработки интерфейса пользователя для Windows-программ, вставляя определенные стандартные меню в каждую создаваемую нестандартную строку меню. Например, следует всегда включать меню File и, как правило, меню Edit.
Так не надо
НЕ ЗАБЫВАЙТЕ немедленно устанавливать свойства Caption, Faceld и OnAction созданного элемента управления управляющей панели, чтобы он имел имя, графическое изображение на своей лицевой поверхности и процедуру события для выполнения действия при щелчке на элементе управления.
Именование или переименование элемента управления
Первоначально только что созданный новый элемент управления не имеет никакого имени. (Встроенные элементы управления управляющих панелей, добавляемые в нестандартную управляющую панель, уже имеют имена.) Имя элемента управления хранится в его свойстве Caption. Всегда следует немедленно присваивать имя новому элементу управления, присваивая значение его свойству Caption немедленно после создания элемента управления.
Иногда может потребоваться изменить имя элемента управления (обычно для элементов управления в управляющих панелях типа меню) после первоначального присвоения ему имени. Наиболее распространенным примером такого случая может служить использование управляющей панели для отображения списка последних открывавшихся файлов, например меню File Excel, которое обеспечивает команды меню для быстрого повторного открытия любой из нескольких последних открывавшихся книг. Для обеспечения работы этой части меню имена элементов управления меню должны периодически изменяться по мере открытия дополнительных файлов.
Присвоение или изменение имени элемента управления управляющей панели не федставляет сложности: достаточно присвоить новое значение свойству Caption элемента управления. Следующий фрагмент текста программы демонстрирует использование свойства Caption для переименования элемента управления (приведенные инст-''укции изменяют имя элемента управления с Initialize на Init):
CommandBars!"MyCustomMenu") .Controls!{.Initialize”) .Caption = "SInit"
День 17-й. Меню и панели инструментов
627
Чтобы создать горячую клавишу для элементов управления управляющей панели, отображаемых в виде пунктов меню, включите символ амперсанда (&) перед буквой, которую нужно определить в качестве горячей клавиши. Например, чтобы сделать букву F горячей клавишей в имени меню File, аргументу Caption элемента управления необходимо было бы присвоить следующую строку.
SFile
Теперь в меню этот элемент управления отображается как “File". Если этот элемент является пунктом меню первого уровня, для открытия меню File пользователь может нажать комбинацию клавиш <A)t+F>; или же для активизирования этого элемента управления пользователь может просто нажать клавишу F.
Так надо
ПОМНИТЕ, что в управляющих панелях типа меню свойство Caption элемента управления отображается в качестве имени пункта меню.
УБЕДИТЕСЬ, что все элементы управления меню или подменю имеют уникальные буквы горячих клавиш, если решаете присвоить горячие клавиши своим нестандартным элементам управления.
Указание процеууры события элемента управления
Выполнение конкретного действия элементами управления управляющих панелей достигается во многом аналогично тому, как это делается для кнопки нестандартного диалогового окна: путем присоединения VBA-процедуры к элементу управления в качестве процедуры события. При каждом щелчке на элементе управления или его обновлении VBA выполняет указанную процедуру события.
Процедура события для элемента управления управляющей панели указывается посредством присвоения строки свойству OnAction элемента управления. Строка, сохраненная в свойстве OnAction, называет конкретную VBA-процедуру, которую нужно выполнять в качестве процедуры события элемента управления. Значение свойству OnAction можно присвоить в любое время.
Для присвоения процедуры события элементу управления управляющей панели используется следующий общий синтаксис.
Object.OnAction = procName
Object представляет любую допустимую ссылку на объект CommandBarControl. procName — любое допустимое строковое выражение, которое указывает процедуру события, подлежащую выполнению при щелчке на указанном элементе управления.
ПРИМЕЧАНИЕ
В случае присвоения значения свойству OnAction встроенного элемента управления управляющей панели указываемая процедура события заменит встроенное действие элемента управления.
Удаление элементов днравлення
Удаление элементов управления из управляющей панели может требоваться по различным причинам. Например, может понадобиться удаление встроенного элемента управления Format из строки меню приложения, чтобы помешать пользователям переформатировать лист. Или может потребоваться удаление элемента управления из
628
Неделя 3
одной из нестандартных управляющих панелей. Независимо от причины элементы можно удалять из нестандартных и встроенных управляющих панелей. Когда элемент управления больше не нужен или недопустим, для удаления его из управляющей панели можно использовать метод Delete.
Для удаления элемента управления используется метод Delete объекта CommandBarControl, имеющий следующий синтаксис.
Controlobject.Delete
Controlobject — это любая допустимая ссылка на объект CommandBarControl. Когда VBA выполняет метод Delete, указанный элемент управления удаляется из управляющей панели. Следующий фрагмент программы, например, удаляет элемент управления со свойством Caption, имеющим значение Calculate, из управляющей панели MyCustomMenu.
CommandBars("MyCustomMenu") .Controls("Calculate") .Delete
Включение или отключение элемента управления
Во многих случаях конкретный элемент управления требуется сделать недоступным в управляющей панели. Например, может быть написана программа, которая добавляет в Excel диспетчер текущих заданий, имеющий команду Print (Печать), выполняющую печать списка заданий. Если список пуст, может требоваться отключение команды Print, поскольку печатать нечего. В этом случае можно было бы удалить элемент управления Print из управляющей панели (панели инструментов или меню), но это могло бы озадачить пользователей программы; они могли бы не понять, почему различные пункты меню или кнопки панели инструментов появляются и исчезают.
В подобном случае больше подходит отключение элемента управления. Если отключить элемент управления для команды Print, он будет продолжать отображаться в меню или панели инструментов, но будет затемняться, показывая, что в данный момент команда недоступна. Почти все пользователи Windows знакомы с видом отключенных пунктов меню или кнопок панелей инструментов и легко поймут, что пункт временно недоступен.
Для включения или отключения элементов управления управляющих панелей достаточно изменить свойство Enabled элемента управления. Это свойство хранит значение типа Boolean; если значением свойства Enabled является True, элемент управления включен. Если значение Enabled — False, элемент управления отключен. Свойство Enabled можно также использовать для выяснения текущего состояния включения конкретного встроенного элемента управления.
В следующем фрагменте программы приведен пример использования свойства Enabled.
CommandBars)"MyCustomMenu").Controls)"Calculate").Enabled = False
ПРИМЕЧАНИЕ
Свойство Enabled имеет значение True no умолчанию для любых новых создаваемых элементов управления управляющих панелей.
День 17-й. Меню и панели инструментов
629
Состояние элемента управления
Некоторые типы элементов управления всего лишь включают или отключают конкретную функцию программы, т.е. являются командами переключения. Например, команда Formula Ваг (Строка формул) в меню View (Вид) Excel включает и выключает отображение строки формул. Когда строка формул отображается, слева от команды Formula Ваг появляется метка галочки (флажок), показывающая, что данная функция включена. Когда строка формул не отображается, флажок рядом с командой Formula Ваг отсутствует.
Еще один пример переключателя в управляющей панели — кнопка Web Toolbar (Панель Web) в стандартной панели инструментов Excel, которая включает и отключает отображение панели Web. Когда панель Web отображается, кнопка Web Toolbar отображается в “нажатой” позиции, показывая, что в настоящий момент это свойство включено. Когда панель Web не видна, кнопка Web Toolbar отображается в “отжатой” позиции, показывая, что это свойство отключено.
На определенном этапе, вероятно, придется создать собственные команды переключения. Для отображения или удаления флажка рядом с элементом управления меню или для изменения способа отображения кнопки используется свойство State элемента управления. Каждый объект CommandBarButton имеет свойство State. Это свойство хранит численное значение, указывающее текущее состояние элемента управления. Свойство State может содержать любую из констант MsoButtonState: msoButtonUp, msoButtonDown или msoButtonMixed. По умолчанию при создании нового элемента управления свойство State содержит константу msoButtonUp. Это свойство может также использоваться для определения текущего состояния элемента управления.
Если элемент управления CommandBarButton появляется в управляющей панели типа меню и не имеет конкретного присвоенного ей идентификатора лицевой поверхности, изменение свойства State на msoButtonDown приводит к отображению флажка слева от элемента управления меню. Для элементов управления в управляющей панели типа панели инструментов или для элемента управления меню, имеющего присвоенный ему идентификатор лицевой поверхности, установка свойства State в msoButtonDown приводит к отображению графического изображения кнопки в “нажатом” состоянии.
ПРИМЕЧАНИЕ
Нельзя изменить свойство State встроенных элементов управления; встроенное поведение элемента управления определяет его свойство State. Однако можно отключить встроенные элементы управления, установив их свойство Enabled в False.
Отыскание конкретных элементов управления
Иногда требуется обнаружить нестандартный или встроенный элемент управления, имя которого точно не известно. Объект коллекции CommandBars обеспечивает метод — FindControl, — который позволяет обнаружить конкретный элемент управления, исходя из типа элемента управления, его идентификатора, видимости или значения свойства Тад. Используйте метод FindControl для обнаружения встроенных или нестандартных элементов управления.
Метод FindControl имеет следующий синтаксис.
CommandBarObject.FindControl([Type,] [Id,] [Tag,] [Visible,] [Recursive]
Здесь CommandBarObject представляет любую допустимую ссылку на отдельный объект CommandBar или на объект коллекции CommandBars. Все аргументы метода FindControl являются необязательными. Метод FindControl возвращает объектную ссылку на пер
630
Неделя 3
вый найденный элемент управления, соответствующий указанному критерию поиска. Критерий поиска указывается посредством аргументов метода FindControl. Если ни один соответствующий элемент управления не найден, метод возвращает специальное объектное значение Nothing. Если найдено более одного соответствующего критерию элемента управления, возвращается ссылка на первый соответствующий элемент управления.
Аргумент Туре — это численное выражение, представляющее тип искомого элемента управления. Этим аргументом может быть любая из констант MsoControlType. (Полный список констант MsoControlType можно просмотреть в Object Browser, выбрав Office в списке Projects/Libraries, а затем выбрав MsoControlType в списке Classes.)
Id — это численное выражение, представляющее идентификационный номер элемента управления. Этот аргумент наиболее полезен при поиске встроенных элементов управления управляющих панелей. Поскольку все нестандартные элементы управления имеют идентификационный номер, равный 1, использование аргумента Id при поиске нестандартных элементов управления обычно оказывается бесполезным.
Аргумент Тад представляет любое значение, по которому желательно выполнить поиск. Метод FindControl ведет поиск элементов управления, свойство Тад которых содержит то же значение, которое указано для аргумента Тад.
Аргумент Visible — выражение типа Boolean, показывающее нужно ли ограничивать поиск только теми элементами управления, которые видимы в текущий момент. И наконец, аргумент Recursive также является выражением типа Boolean; установка значения этого аргумента равным True указывает, что нужно искать любые управляющие панели, содержащиеся внутри искомой управляющей панели, т.е. меню и подменю, содержащиеся внутри управляющей панели.
Следующий фрагмент программы демонстрирует использование метода FindControl:
Set aCtrl = CommandBars("Standard") .FindControl(Type:=msoControlCustom,
Visible:=True, Recursive:=True)
В предыдущей инструкции метод FindControl используется для отыскания первого видимого нестандартного элемента управления в панели инструментов Standard приложения и для поиска всех подменю и всплывающих меню в панели инструментов Standard. Если элемент управления, соответствующий этому критерию, найден, объектной переменной aCtrl будет присвоена ссылка на этот элемент управления. Если в панели инструментов Standard не существует никаких видимых нестандартных элементов управления, объектной переменной aCtrl присваивается значение Nothing.
Применение на практике: управляющая панель типа мент
В этом уроке читатели узнали много нового об управляющих панелях типа меню и >б элементах управления в меню. Теперь пора применить эти знания на практике и создать работающую систему меню. Система меню, создаваемая программой, приве-1енной в листинге 17.5, не слишком связана с реальными задачами, но она демонстрирует использование всех важных объектов управляющих панелей и элементов правления в управляющей панели типа меню.
Создаваемая листингом 17.5 система меню также иллюстрирует важное требование ия любой системы меню: обеспечение наличия какой-либо команды выхода. Если не
День 17-й. Меню и панели инструментов
631
включить способ выхода из активизируемой системы меню, никогда не удастся вернуться к встроенным меню приложения в текущем сеансе работы — чтобы вернуть на экран стандартные меню, придется выходить из приложения и перезапускать его.
Нестандартная строка меню, созданная листингом 17.5, содержит лишь два пункта: File и Demo. Меню File имеет только три пункта меню: две имитирующие команды (Open (Открыть) и Save (Сохранить)) и команду Exit (Выход). Выбор команды File-Exit восстанавливает обычную строку меню, отображаемую приложением, а затем удаляет нестандартную строку меню.
Меню Demo имеет два пункта меню и одно подменю. Подменю имеет четыре пункта. Первый пункт меню демонстрирует, как выполнять установку и снятие флажка меню; второй пункт меню демонстрирует переключение пиктограммы кнопки в меню. Первые два пункта меню также демонстрируют изменение заголовка пункта меню. Все пункты меню Demo и его подменю вызывают одну и ту же процедуру шаблонной команды; в реальной системе меню нужно было бы иметь отдельные процедуры событий для каждой команды меню. На рис. 17.6 показана созданная листингом 17.5 нестандартная строка меню с раскрытым меню Demo и развернутым подменю. Как видно из рис. 17.6, флажок первого пункта подменю установлен.
Рис. 17.6. Строка меню, созданная процедурой DemoMenuSystem, отображающая меню с двумя уровнями подменю
Листинг 17.5. Процеддра DemoMeHuSgsfет создает минимажьвдм строка МЕВП1
1:	Option Explicit
2:
3:	Const	MenuName	= "Demo Menu"
4:	Const	Openicon	=23
5:	Const	Saveicon	= 3
6:	Const	Mugicon = 480
7:
8:
9:	Sub DemoMenuSystem ()
10:	'Демонстрация меню.
11:
12:	Dim myMenuBar As CommandBar
13:	Dim aMenu As Object
14:
15:	'Создание управляющей панели
16:	Set myMenuBar = CommandBars.Add(Name:="Demo Menu",
17:	MenuBar:=True,
18:	temporary:=True)
19:
20:
21:	With myMenuBar.Controls
632
Неделя 3
22:	'Создаем меню File
23:	Set aMenu = .Add(Type:=msoControlPopup,	temporary:=True)
24:	aMenu.Caption = "SFile"
25:
26:	'Создаем меню Demo
27:	Set aMenu = ,Add(Type:=msoControlPopup,	temporary:=True)
28:	aMenu.Caption = "SDemo"
29:	End With
30:
31:	'Заполняем меню File
32:	With myMenuBar.Controls!"File"(.Controls
33:	'Добавляем пункты меню File | Open
34:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
35:	aMenu.Caption = "SOpen"
36:	aMenu.Faceld = Openicon
37:	aMenu.OnAction = "Dummycommand"
38:	aMenu.Parameter = "File Open"
39:
40:	'Добавляем пункты меню File |	Save
41:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
42:	aMenu.Caption = "SSave"
43:	aMenu.Faceld = Saveicon
44:	aMenu.OnAction = "Dummycommand"
45:	aMenu.Parameter = "File Save"
46:
47:	'Добавляем пункты меню File |	Exit
48:	Set aMenu = . Add(Type:=msoControlButton, temporary:=True)
49:	aMenu.Caption = "ESxit"
50:	aMenu.OnAction = "Exitcommand"
51:	aMenu.Parameter = "File	Exit"
52:	aMenu.BeginGroup = True
53:	End With
54:
55:	'Заполняем пункты меню Demo
56:	With myMenuBar.Controls!"Demo").Controls
57:	'1-й пункт
58:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
59:	aMenu.Caption =	"SFirst	Command"
60:	aMenu.OnAction = "DummyCommand"
61:	aMenu.Parameter	= "Demo	First"
62:
63:	'2-н пункт
64:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
65:	aMenu.Caption =	"&Second Command"
66:	aMenu. OnAction = "Dummy Command"
67:	aMenu.Parameter	= "Demo Second"
68:
69:
70:	Set aMenu = .Add(Type:=msoControlPopup, temporary:=True)
71:	aMenu.Caption = “S&ubmenu"
’2:	aMenu.BeginGroup = True
’3: End With
’4:
День 17-й. Меню и панели инструментов
633
75:	'Добавляем пункты меню Demo | Submenu .
76: With myMenuBar.Controls("Demo").Controls("Submenu").Controls 77:
78:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
79:	aMenu.Caption = "Turn checkmark on"
80:	aMenu.OnAction = "CheckToggle"
81:
82:	'add a menu command to demonstrate a toggle with button
83:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
84:	aMenu.Caption = "Turn option on"
85:	aMenu.Faceld = Mugicon
86:	aMenu.OnAction = "ButtonToggle"
87:
88:	'add a menu command with a separator
89:	Set aMenu = .Add(Type:=msoControlButton, temporary:=True)
90:	aMenu.Caption = "Commandl"
91:	aMenu.OnAction = "DummyCommand"
92:	aMenu.Parameter = aMenu.Caption
93:	aMenu.BeginGroup = True
94:
95:	'add another dummy menu command
96:	Set aMenu >= .Add(Type:=msoControlButton, temporary:=True)
97:	aMenu.Caption = "Command2"
98:	aMenu.OnAction = "DummyCommand"
99:	aMenu.Parameter = aMenu.Caption
100:	End With
101:
102:	myMenuBar.Visible = True 'activate the menu
103:	End Sub
104:
105:
106:	Sub CheckToggle()
107:	'toggles the checkmark, and changes caption
108:
109:	With CommandBars("Demo Menu"(.Controls("Demo")
110:	With .Controls("Submenu").Controls(l)
111:	If .State = msoButtonUp Then
112:	.State =	msoButtonDown
113:	.Caption	= "Turn checkmark	off"
114:	Else
115:	.State =	msoButtonUp
116:	.Caption	= "Turn checkmark	on"
117:	End If
118:	End With
119:	End With
120:	End Sub
121:
122:
123:	Sub ButtonToggle()
124:	'toggles the button's state, and changes the menu caption
125:
126:	With CommandBars(MenuName).Controls("Demo")
127:	With .Controls("Submenu").Controls(2)
634
Неделя 3
128:	If .State	= msoButtonUp Then
129:	.State =	msoButtonDown
130:	.Caption	= "Turn option	off"
131:	Else
132:	.State =	msoButtonUp
133:	.Caption	= "Turn option	on"
134:	End If
135:	End With
136:	End With
137:	End Sub
138:
139:
140:	Sub Dummycommand()
141:	'Имитирует выполнение команды
142:
143:	Dim CmdCtrl As Object
144:
145:	Set CmdCtrl = CommandBars.Actioncontrol
146:
147:	If CmdCtrl Is Nothing Then Exit Sub
148:
149:	MsgBox prompt:="Simulation of the " &
150:	CmdCtrl.Parameter & " command.",
151:	Buttons:=vblnformation,
152:	Title:="Menu System Demonstration"
153:	End Sub
154:
155:
156:	Sub Exitcommand()
157:	'Удаляет созданное меню
158:	CommandBars(MenuName).Delete
159:	End Sub
Процедура DemoMenuSystem (строки 9—103 листинга 17.5) начинается с добавления нестандартной управляющей панели в коллекцию CommandBars посредством инструкций в строках 16—18. Для удобства результат метода Add (объектная ссылка на только что созданную управляющую панель) присваивается объектной переменной myMenuBar.
Обратите внимание, что аргументом MenuBar метода Add является True; в результате, новая управляющая панель создается в качестве управляющей панели типа меню. Кроме того, значением аргумента Temporary также является True, чтобы эта управляющая панель типа меню не сохранялась автоматически.
ПРИМЕЧАНИЕ
Прежде чем запускать процедуру DemoMenuSystem, введите весь текст программы, приведенный в листинге 17.5. После запуска процедуры DemoMenuSystem обратите внимание, что во время отображения нестандартной строки меню на экране сохраняется возможность редактировать рабочий лист, выбирать кнопки из экранных панелей инструментов и выполнять другие действия. Для восстановления доступа к встроенным строкам меню Excel необходимо выбрать команду File-Exit из нестандартной строки меню.
День 17-й. Меню и панели инструментов
635
Строки 21—29 добавляют меню File и Demo в строку меню. В строке 23 метод Add коллекции Controls служит для создания нового элемента управления для меню File. Обратите внимание, что аргумент Туре метода Add использует константу msoControlPopup для указания того, что этот элемент управления должен быть создан в качестве элемента управления всплывающего меню: именно так меню File создается в строке меню. Для удобства результат метода Add (объектная ссылка на новый элемент управления) присваивается объектной переменной aMenu. Строка 28 устанавливает заголовок элемента управления всплывающего меню, присваивая строку свойству Caption нового элемента управления. Обратите внимание, что амперсанд (&) используется для указания буквы “F” в слове “File” в качестве горячей клавиши меню. Аналогично строки 27 и 28 создают еще один элемент управления всплывающего меню и присваивают значение его заголовку. Описанные инструкции создают меню File и Demo, отображенные на рис. 17.6.
На этом этапе строка меню состоит из самой управляющей панели и двух пустых меню. Строки 32—53 заполняют меню File, добавляя элементы управления в управляющую панель, являющуюся частью элемента управления всплывающего меню. Обратите внимание, что строка 32 начинает инструкцию With, которая ссылается на коллекцию Controls, принадлежащую элементу управления “File” в коллекции Controls строки меню.
Строки 34—38 добавляют элемент управления Open в управляющую панель меню File. В строке 34 метод Add используется для создания элемента управления; обратите внимание, что в данном случае аргумент Туре указывает, что элемент управления должен быть создан в качестве типа элемента управления msoControlButton. Добавление кнопок в меню создает пункты в этом меню. Строка 35 устанавливает заголовок элемента управления меню; это текст, появляющийся в меню в качестве команды. Строка 36 присваивает значение значению FacID элемента управления кнопки; оно указывает графическое изображение, отображаемое слева от пункта меню. Строка 37 устанавливает свойство OnAction элемента управления кнопки для выполнения процедуры Dummy Command. (Процедура Dummycommand используется для имитации действительной команды, которая могла бы выполняться этим пунктом меню.) И наконец, строка 38 устанавливает свойство Parameter элемента управления кнопки. Процедура Dummycommand использует значение в свойстве Parameter элемента управления для сообщения о том, какая команда меню имитируется.
Строки 40—45 добавляют в меню команду Save. Как и ранее, кнопка добавляется в управляющую панель меню File. Затем свойству Caption дается имя команды присваивается, свойству Faceld присваивается значение, имя процедуры события сохраняется в свойстве OnAction и свойство Parameter устанавливается так, чтобы процедура DummyCommand могла сообщить об имитируемой ею команде.
И наконец, строки 47—52 добавляют команду Exit в меню File. И вновь для создания команды меню тип элемента управления указывается в качестве кнопки. На этот раз никакое свойство Faceld не устанавливается; в результате область слева от команды меню останется пустой. Обратите также внимание, что свойство OnAction кнопки Exit устанавливается на процедуру Exitcommand. Эта процедура удаляет эту демонстрационную строку меню.
При каждом выборе команды File-Exit VBA будет выполнять процедуру Exitcommand (в строках 156—159 листинга 17.5). Строка 52 устанавливает свойство BeginGroup кнопки Exit, чтобы перед командой Exit в меню отображалась разделительная линия.
Строки 56—73 добавляют команды в меню Demo, добавляя элементы управления в управляющую панель, присущую всплывающему элементу управления Demo. Строки 58—61 добавляют еще одну шаблонную команду меню, а строки 64—67 добавляют вторую шаблонную команду. Однако обратите внимание на строки 70—72: эти инструк
636
Неделя 3
ции создают подменю в меню Demo. Обратите также внимание, что аргумент Туре метода Add в строке 70 создает всплывающий элемент управления; этот всплывающий элемент управления становится подменю меню Demo. Кроме того, строка 72 устанавливает свойство BeginGroup этого элемента управления в значение True для отображения в меню перед ним разделительной линии. Инструкция в строке 72 отвечает за присутствие разделительной линии, видимой в меню Demo на рис. 17.6.
На этом этапе подменю в меню Demo не содержит никаких элементов управления, поэтому строки 76—100 добавляют элементы управления в элемент управления Demo, Submenu. Обратите внимание, что первые две кнопки в подменю имеют специальные процедуры событий, отличающиеся от процедуры DummyCommand, указанной для всех остальных команд меню.
И наконец, строка 102 обеспечивает отображение нестандартной строки меню на экране, устанавливая ее свойство Visible в значение True. Новая строка меню отображается на экране, замещая любую ранее отображавшуюся строку меню.
Строки 106—120 содержат процедуру CheckToggle. Эта процедура выполняется всегда при выборе команды Demo-Submenu-Tum Checkmark off (или Demo-Submenu-Turn Checkmark on). Процедура CheckToggle устанавливает или удаляет флажок рядом с первой командой в пункте меню Demo — Submenu и соответствующим образом изменяет заголовок команды. В момент создания флажок не установлен и заголовком элемента управления является Turn Checkmark on.
Процедура CheckToggle начинается с исследования текущего состояния кнопки. Если свойство State кнопки содержит константу msoButtonUp, состояние кнопки изменяется на msoButtonDown, а рядом с кнопкой появляется флажок, как показано на рис. 17.6. Если состоянием кнопки уже является msoButtonDown, ее состояние изменяется на msoButtonUp, а ее заголовок — на Turn Checkmark on.
Строки 123—137 содержат процедуру ButtonToggle. Эта процедура выполняется всегда при выборе команды Demo-Submenu-Turn option off (или Demo-Submenu-Turn option on). По существу, она работает аналогично только что описанной процедуре CheckToggle. Поскольку этот элемент управления имеет графическое изображение, никакой флажок не появляется, когда состоянием кнопки является “нажатое”; вместо этого изображение слева от команды меню приобретает вид “нажатой” кнопки.
Строки 140—153 содержат процедуру Dummycommand, которая существует исключительно для имитации процедур событий, которые могли бы вызываться различными командами меню. Эта процедура выполняется при выборе любого из пунктов меню File-Open; File-Save; Demo-First Command; Demo-Second Command; Demo-Submenu-Commandl или Demo-Submenu-Command2.
Обратите внимание на строку 145: эта строка использует свойство Actioncontrol коллекции CommandBars для возвращения объектной ссылки на объект элемента управления, являющийся результатом выполнения этой процедуры. Свойство Parameter элемента управления используется в диалоговом окне сообщения, отображаемом процедурой Dummycommand для идентификации элемента управления, вызвавшего эту процедуру. Этот программный текст включен в процедуру Dummycommand для демонстрации того, как можно выяснить, какой элемент управления привел к выполнению конкретной процедуры, и того, как свойство Parameter можно использовать для передачи данных процедуре события, подлежащей использованию.
И наконец, строки 156—159 содержат процедуру Exitcommand. Эта процедура выполняется всегда при выборе команды File-Exit. Строка 158 использует метод Delete для удаления нестандартной управляющей панели. Как только строка меню удалена, Excel снова отображает собственную строку меню.
День 17-й. Меню и панели инструментов
637
Так надо
ПОМНИТЕ, что можно создать разделитель меню, присвоив значение True свойству BeginGroup элемента управления, перед которым должен быть разделитель.
ПОМНИТЕ, что всегда нужно снабжать меню File командой Exit или Close, которая будет останавливать выполнение программы и удалять нестандартную строку меню.
Применение на практике: управляющая панель типа панели инструментов
Процедура DemoToolbarSystem в листинге 17.6 создает нестандартную панель инструментов, заполняет ее несколькими кнопками и другими элементами управления, а затем отображает ее в окне плавающей панели инструментов. Хотя нестандартная панель инструментов, созданная процедурой DemoToolbarSystem, не выполняет никакой реальной задачи, она демонстрирует несколько элементов управления управляющей панели, которые обычно используются в панелях инструментов. И, что самое главное, листинг 17.6 демонстрирует, как можно сконструировать собственные работающие панели инструментов.
На рис. 17.7 показано, как созданная процедурой DemoToolbarSystem панель инструментов выглядит при отображении на экране в окне плавающей панели инструментов. Эта панель инструментов защищена от сокрытия, размещения у краю окна, изменения размеров или настройки. Щелчок на кнопке белой лампочки сворачивает панель инструментов до единственной кнопки: кнопки желтой лампочки, которая разворачивает панель инструментов.
iDerno Toolbar
Q © Utilities» О Д ! Up	» ♦ Sample text	Q ] фэ
Puc. 17.7. Процедура DemoToolbarSystem, представленная в листинге 17.6, создает эту нестандартную панель инструментов
Кнопка с улыбающимся лицом демонстрирует работу переключателя. Щелчок на ней отключает все остальные элементы управления панели инструментов; кнопка с улыбающимся лицом остается в нажатой позиции, а ее изображение изменяется на грустное лицо. Щелчок на кнопке с грустным лицом включает все остальные элементы управления панели инструментов, восстанавливает кнопку в ее отжатой позиции и изменяет изображение на кнопку с улыбающимся лицом.
Кнопка Utilities демонстрирует использование всплывающего элемента управления в панели инструментов. Щелчок на этой кнопке отображает всплывающее меню, содержащее команды Disable Toolbar, Hide Toolbar и Delete Toolbar. (Каждая из этих команд выполняет то же действие, что и остальные кнопки панели инструментов; они присутствуют только в демонстрационных целях.)
Щелчок на кнопке панели инструментов, содержащей символ Info, отображает диалоговое окно сообщения с некоторой информацией о текущей операционной среде VBA. Щелчок на кнопке с символом колокольчика вызывает системный звуковой сигнал по умолчанию.
Следующий элемент управления демонстрирует список в панели инструментов. Поскольку этот элемент управления является списком, пользователь может только выбирать значения, которые появляются в списке. Выбор в этом списке определяет изображение кнопки справа от элемента управления списка; выбор Up (Вверх), Down
638	Неделя 3
(Вниз), Left (Влево) и Right (Вправо) вызывает изменение изображения кнопки справа от списка в соответствии с выбором. Щелчок на кнопке справа от списка повторяет значение, выбранное в списке. Эти элементы управления были включены, чтобы показать, как добавлять и заполнять элемент управления списка и как получать значение этого элемента управления.
Следующая пара элементов управления состоит из поля и кнопки, которая повторяет значение, введенное в поле. В поле можно ввести любой текст; щелчок на кнопке с отображением речи справа от поля отображает диалоговое окно сообщения, в котором отображается текущее значение поля. Эти элементы управления были включены, чтобы показать, как добавить элемент управления поля и как получить значение этого элемента управления.
Щелчок на кнопке в форме красного ромба приводит к удалению нестандартной панели инструментов с экрана и из коллекции CommandBars.
Выполнив процедуру DemoToolbarSystem для создания нестандартной панели инструментов и отображения ее на экране, поэкспериментируйте с перетаскиванием панели инструментов в различные позиции у края окна. Обратите внимание на то, что панель инструментов не удается прижать к краю окна и что нестандартная панель инструментов не отображается в списке панелей инструментов, которое появляется на экране по команде View (Вид)-Тоо1Ьаге (Панели инструментов). Объясняется это тем, что процедура DemoToolbarSystem устанавливает свойство Protection нестандартной панели инструментов для предотвращения ее расположения непосредственно у края окна, изменения размеров или сокрытия.
Обратите также внимание на экранные подсказки, которые появляются при помещении курсора мыши над элементами управления панели инструментов; текст экранных подсказок предоставляется свойством TooltipText. (Экранная подсказка — переключаемое свойство; если подсказки не появляются, выберите команду View-Toolbars-Customize и установите флажок Show ScreenTips on Toolbars (Отображать подсказки для кнопок) на вкладке Options (Параметры) диалогового окна Customize (Настройка).)
Дистивг 17.D. Создаиие нестандартиоО панели иистрдментов
1:	Option Explicit
2:
3:	Private Const Demo_tbName = "Demo Toolbar"
4:	Private Const HappyButton = 59
5:	Private Const SadButton = 276
6:	Private Const WhiteBulbButton = 342
7:	Private Const YellowBulbButton = 343
8:	Private Const InfoButton = 487
9:	Private Const BellButton = 273
10:	Private Const DmndButton = 482
11:	Private Const UpArrowIcon = 38
12:	Private Const RightArrowIcon = 39
13:	Private Const DownArrowIcon =40
14:	Private Const LeftArrowIcon =41
15:	Private Const Speechicon = 274
16:
17:
18:	Sub DemoToolbarSystem()
19:
20:	Dim myToolBar As CommandBar
21:	Dim aBtn As CommandBarControl
22:
День 17-й. Меню и панели инструментов
639
23:	'Создает управляющую панель
24:	Set myToolBar = CommandBars. Add (Name :=Demo tbName,
25:	Position:=msoBarFloating,
26:	MenuBar:=False,
27:	temporary:=True)
28:	'Заполняем панель
29:	With myToolBar.Controls
30:	'Создание кнопки сворачивания
31:	Ser aBtn = . Add (Type :=msoControlButton,	temporary:=True)
32:	aBtn.Faceld = WhiteBulbButton
33:	aBtn.OnAction = "ExpandButton_Click"
34:	aBtn.Caption = "ExpandButton"
35:	aBtn.TooltipText = "Collapse this toolbar"
36:	aBtn.Tag = True
37:
38:	'создание кнопки enable/disable
39:	Set aBtn = .Add(Type:=msoControlButton,	temporary:=True)
40:	aBtn.Faceld = HappyButton
41:	aBtn.State = msoButtonUp
42:	aBtn.OnAction = "EnableButton_Click"
43:	aBtn.Caption = "EnableButton"
44:	aBtn.TooltipText = "Disable toolbar buttons"
45:
46:	'добавление пустого пункта меню
47:	Set aBtn = .Add(Type:=msoControlPopup, temporary:=True)
48:	aBtn.Caption = "Utilities"
49:	aBtn.TooltipText = "Utility Commands"
50:
51:	' создание кнопки information
52:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
53:	aBtn.Faceld = InfoButton
54:	aBtn.OnAction = "InfoButton_Click"
55:	aBtn.Caption = "InfoButton"
56:	aBtn.TooltipText = "VBA Information"
57:	aBtn.BeginGroup = True
58:
59:	'создание кнопки звонка
60:	Set aBtn = .Add(Type:=msoCdntrolButton, temporary:=True)
61:	aBtn.Faceld = BellButton
62:	aBtn.OnAction = "BellButton_Click"
63:	aBtn.Caption = "BellButton"
64:	aBtn.TooltipText = "Beep"
65:
66:	'кнопка co стрелкой
67:	Set aBtn = .Add(Type:=msoControlDropdown, temporary:=True)
68:	aBtn.BeginGroup = True
69:	aBtn.Caption = "IconSelect"
70:	aBtn.Tag = aBtn.Caption
71:	aBtn.TooltipText = "Select the button icon"
72:	aBtn.OnAction = "IconSelect_Update"
73:	aBtn.Additem "Up"
74:	aBtn.Additem "Down"
75:	aBtn.Additem "Left"
640
Неделя 3
76:	aBtn.Additem “Right"
77:	aBtn.Listindex = 1
78:	aBtn.Width = 100
79:
80:	'create the icon-display button
81:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
82:	aBtn.Faceld = UpArrowIcon
83:	aBtn.OnAction = "IconButton_Click"
84:	aBtn.Caption = “IconButton"
85:	aBtn.TooltipText = "Display	list	selection"
86:
87:	'создание текстового поля
88:	Set aBtn = .Add(Type:=msoControlEdit, temporary:=True)
89:	aBtn.BeginGroup = True
90:	aBtn.Caption = "AnyText"
91:	aBtn.TooltipText = "Enter some	text"
92:	aBtn.Width = 150
93:	aBtn.Text = "Sample text"
94:
95:	'create a button to echo the entered text
96:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
97:	aBtn.Caption = "EchoButton"
98:	aBtn.TooltipText = "Echoes the text box entry"
99:	aBtn.OnAction = "EchoButton_Click"
100:	aBtn.Faceld = Speechicon
101:
102:	'кнопка закрытия панели
103:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
104:	aBtn.Faceld = DmndButton
105:	aBtn.OnAction = "RemoveDemoToolbar"
106:	aBtn.Caption = "Remove the demo toolbar"
107:	aBtn.BeginGroup = True
108:	End With
109:
110:	'populate the Utilities menu popup control
111:	With myToolBar.Controls! "Utilities").ConunandBar.Controls
112:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
113:	aBtn.Caption = "^Disable Toolbar"
114:	aBtn.OnAction = "EnableButton_Click"
115:	aBtn.Faceld = HappyButton
116:
117:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
118:	aBtn.Caption = "&Hide Toolbar"
119:	aBtn.OnAction = "ExpandButton_Click"
120:	aBtn.Faceld = WhiteBulbButton
121:
122:	Set aBtn = .Add(Type:=msoControlButton, temporary:=True)
123:	aBtn.Caption = "Delete Toolbar"
124:	aBtn.OnAction = "RemoveDemoToolbar"
125:	aBtn.Faceld = DmndButton
126:	End With
127:
128:	'final toolbar configuration
День 17-й. Меню и панели инструментов
641
129:	With myToolBar
130:	'установка защиты
131:	.Protection = msoBarNoCustomize	+ msoBarNoResize +
132:	msoBarNoChangeVisible + msoBarNoChangeDock
133:	.Left = 200
134:	.Top = 200	'position the toolbar
135:	.Position = msoBarFloating	'floating by default
136:	.Visible = True	'make it visible
137:	End With
138:	End Sub
139:
140:
141:	Sub ExpandButton_Click()
142:	'разворачивает или сворачивает панель с помощью свойства Visible
143:	'управляющих элементов
144:	Dim k As Integer
145:	Dim aBtn As CommandBarControl
146:	Dim aCmdBar As CommandBar
147:
148:	Set aCmdBar = CommandBars(Demo_tbName)
149:	Set aBtn = aCmdBar.Controls("ExpandButton")
150:
151:	With aBtn
152:	If .Tag = True Then
153:	.Tag = False
154:	.Faceld = YellowBulbButton
155:	.TooltipText	= "Expand this	toolbar"
156:	For к = 2 To	aCmdBar.Controls.Count
157:	aCmdBar.Controls(k).Visible = False
158:	Next к
159:	Else
160:	.Tag = True
161:	.Faceld = WhiteBulbButton
162:	.TooltipText	= "Collapse this	toolbar"
163:	For к = 2 To	aCmdBar.Controls.Count
164:	aCmdBar.Controls(k).Visible = True
165:	Next к
166:	End If
167:	End With
168:	End Sub
169:
170:
171:	Sub EnableButton_Click()
172:	'enables or disables the toolbar buttons
173:	Dim	aCmdBtn	As CommandBarControl
174:	Dim	aBtn As	CommandBarControl
175:	Dim	aCmdBar	As CommandBar
176:
177:	Set	aCmdBar	= CommandBars(Demo_tbName)
178:	Set aBtn = aCmdBar.Controls("EnableButton")
179:
180:	With aBtn
181:	If .State = msoButtonUp Then
642
Неделя 3
182:	.State = msoButtonDown
183:	.Faceld = SadButton
184:	.TooltipText = "Enable	toolbar	buttons"
185:	For Each aCmdBtn In aCmdBar.Controls
186:	If aCmdBtn.Caption <> "EnableButton" Then
187:	aCmdBtn.Enabled = False
188:	End If
189:	Next aCmdBtn
190:	Else
191:	.State = msoButtonUp
192:	.Faceld = HappyButton
193:	.TooltipText = "Disable	toolbar	buttons"
194:	For Each aCmdBtn In aCmdBar.Controls
195:	If aCmdBtn.Caption <> "EnableButton" Then
196:	aCmdBtn.Enabled = True
197:	End If
198:	Next aCmdBtn
199:	End If
200:	End With
201:	End Sub
202:
203:
204:	Sub InfoButton_Click()
205:	'displays info about VBA's environment
206:
207:	Const aTitle = "Info - Toolbar Demo"
208:
209:	MsgBox Buttons:=vblnformation,
210:	Title:="VBA " & aTitle,
211:	prompt:="VBA is running in:" & vbCr &
212:	Application.Name & vbCr &
213:	"Build: " & Application.Build
214:	End Sub
215:
216:
217:	Sub IconButton_Click()
218:	'displays the current drop-down list selection
219:
220:	Dim tmp As String
221:	Dim aBtn As CommandBarControl
222:
223:	Set aBtn = CommandBars.FindControl(Tag:="IconSelect")
224:
225:	MsgBox prompt:="The current list selection is: "
226:	& aBtn.Text,
227:	Buttons:=vblnformation,
228:	Title:="Icon Selection"
229:	End Sub
230:
231:
232:	Sub IconSelect_Update()
233:	'updates the IconButton's FacelD whenever the list
234:	'selection changes
День 17-й. Меню и панели инструментов
643
235:	
236:	Dim IconButton As CommandBarControl
237:	Dim IconSelect As CommandBarControl
238: 239:	With CommandBars(Demo_tbName)
240:	Set IconButton = .Controls("IconButton")
241:	Set IconSelect = .Controls!"IconSelect")
242:	End With
243: 244:	With IconButton
245:	Select Case Trim(IconSelect.Text)
246:	Case "Up"
247:	.Faceld = UpArrowIcon
248:	Case "Down"
249 :	.Faceld = DownArrowIcon
250:	Case "Left"
251:	.Faceld = LeftArrowIcon
252:	Case "Right"
253:	.Faceld = RightArrowIcon
254:	End Select
255:	End With
256:	End Sub
257: 258:	
259:	Sub EchoButton_Click()
260:	'echoes the text box entry
261:	With CommandBars(Demo_tbName).Controls!"AnyText")
262:	MsgBox prompt:="The text box contains: " & vbCr &
263:	& .Text &
264:	Buttons:=vblnformation,
265:	Title:="Text Box Selection"
266:	End With
267:	End Sub
268: 269: 270:	Sub BellButton_Click()
271:	'sound system Beep tone; exact sound produced depends
272:	'on your hardware and Windows configuration
273:	Beep
274:	End Sub
275: 276: 277:	Sub RemoveDemoToolbarf)
278:	’removes the demo toolbar
279:	With CommandBars(Demo_tbName)
280:	If .Visible Then .Visible = False
281:	.Delete
282:	End With
283: End Sub	
644
Неделя 3
В строках 3-51 объявляется несколько приватных констант, используемых для присвоения идентификационных номеров лицевых поверхностей для кнопок панели инструментов. Значения для этих констант были получены посредством выполнения процедуры DisplayAllFacelDs, приведенной в листинге 17.3, и последующего исследования результирующего набора панелей инструментов для отыскания нужных лицевых поверхностей.
Процедура DemoToolbarSystem (Строки 18—138 листинга 17.6) начинается с создания нового объекта панели инструментов в строке 24. Обратите внимание, что аргумент MenuBar имеет значение False, чтобы новая управляющая панель была панелью инструментов. Эта инструкция присваивает результат метода Add объектной переменной myToolBar. Объектная переменная позволяет несколько сократить объем вводимой с клавиатуры информации и придать программе более сжатый вид.
Строки 31-36 добавляют первый элемент управления в новую панель инструментов. Строка 31 использует метод Add коллекции Controls для добавления элемента управления msoControlButton в панель инструментов. Строка 32 присваивает кнопке лицевую поверхность, устанавливая свойство Faceld. Строка 33 устанавливает процедуру события кнопки, присваивая значение свойству OnAction; в данном случае щелчок на кнопке будет выполнять процедуру Expand Button_Click. Строка 34 присваивает имя кнопке, устанавливая ее свойство Caption, а строка 35 устанавливает текст подсказки кнопки, устанавливая ее свойство TooltipText.
Строки 39-44 добавляют вторую кнопку в панель инструментов. Этот элемент управления также является кнопкой, идентификатор лицевой поверхности, процедура события и текст подсказки которой инициализированы. Обратите внимание, что строка 41 инициализирует также свойство state этой кнопки. Поскольку этот элемент управления используется для реализации переключателя, важно инициализировать его состояние.
Затем строки 47—49 добавляют всплывающий элемент управления в панель инструментов: этот элемент управления создает кнопку Utilities, видимую на рис. 17.7. После всплывающего элемента управления строки 52—57 создают кнопку, используемую в качестве команды Information. Обратите внимание, что строка 57 устанавливает свойство BeginGroup кнопки Info в значение True, вызывая появление разделительной линии перед этим элементом управления. Строки 60—64 создают кнопку Bell (Звонок).
Строки 67—78 требуют особого внимания. Эти инструкции создают элемент управления списка и заполняют его пунктами. Строка 67 использует метод Add для создания нового элемента управления; аргумент Туре служит для указания того, что новый элемент управления должен быть списком. Этот элемент управления также начинает новую группу, поэтому строка 68 соответствующим образом устанавливает свойство BeginGroup. После установки свойств Caption, TooltipText, Tag и OnAction элемента управления списка строки 73-76 заполняют список; каждая инструкция использует метод Addltem элемента управления списка для добавления пункта в список. Строка 77 устанавливает текущую выборку в списке на первый пункт, присваивая значение 1 свойству Listindex, а ширина элемента управления списка устанавливается равной 100 пикселям. Строки 81—85 создают кнопку, связанную с только что созданным элементом управления списка.
Строки 88—93 также требуют особого внимания. Эти инструкции создают элемент управления поля. Обратите внимание на применение константы msoControlEdit с аргументом Туре метода Add. Обратите также внимание, что строка 93 устанавливает свойство Text элемента управления, присваивая тем самым полю значение по умолчанию.
Строки 96—100 создают кнопку, связанную с полем, а строки 103—107 создают — кнопку, которая служит для удаления этой панели инструментов.
День 17-й. Меню и панели инструментов
645
На этом этапе всплывающее меню Utilities все еще пусто, поэтому строки 111-126 заполняют его. И наконец, строки 129—137 завершают установку свойств панели инструментов. Строка 131 устанавливает свойство Protection панели инструментов, чтобы она была защищена от настройки, изменения размеров, скрытия и размещения у самого края окна. Обратите внимание, что константы защиты суммируются для объединения их значений и обеспечения множественных форм защиты панели инструментов. Строки 133—136 устанавливают позицию панели инструментов на экране и обеспечивают ее видимость.
Остальные процедуры в листинге 17.6 являются процедурами событий для различных кнопок панели инструментов.
Процедура ExpandButton_Click (строки 141—168) выполняется при каждом щелчке на кнопке лампочки в панели инструментов. Эта процедура исследует свойство Тад элемента управления ExpandButton (Кнопка развертывания); если это свойство имеет значение True, панель инструментов уже развернута и должна быть свернута. В этом случае выполняются инструкции, сформулированные в строках 153-158. Эти инструкции инвертируют значение свойства Тад, изменяют идентификатор лицевой поверхности кнопки на пиктограмму желтой лампочки, а затем последовательно просматривают все остальные элементы управления в коллекции Controls панели инструментов, изменяя их свойство Visible на False. В результате этого действия все остальные элементы управления исчезают из панели управления, по существу, сворачивая ее до единственной кнопки.
Если свойство Тад кнопки ExpandButton имеет значение False, панель инструментов уже свернута и должна быть развернута. В этом случае выполняются строки 160—166. Эти инструкции восстанавливают видимость остальных элементов управления — разворачивая панель инструментов — и изменяют лицевую поверхность кнопки на пиктограмму белой лампочки. Процедура ExpandButton_Click демонстрирует один из способов реализации переключателя.
Строки 171—201 содержат процедуру EnableButton_Click. Эта процедура выполняется при каждом щелчке на кнопке с улыбающимся (или грустным) лицом. Если кнопка уже отжата, ее состояние изменяется на msoButtonDown, лицевая поверхность замещается на пиктограмму с грустным лицом, а все элементы управления, кроме этого, отключаются. Если кнопка уже нажата, ее состояние изменяется на msoButtonUp, лицевая поверхность изменяется на пиктограмму улыбающегося лица, а все элементы управления панели инструментов включаются. Свойство TooltipText кнопки также изменяется соответствующим образом, отражая действие кнопки после щелчка.
Строки 204—229 содержат процедуру InfoButton Click, которая выполняется при каждом щелчке на кнопке InfoButton. Эта процедура просто отображает окно сообщения, которое содержит некоторую информацию о среде, в которой в текущий момент времени выполняется VBA.
Строки 217—229 содержат процедуру IconButton_Click. Эта процедура выполняется при каждом щелчке на элементе управления IconButton. Она просто отображает окно сообщения о текущем состоянии элемента управления списка IconSelect (Выбор пиктограммы). Обратите внимание на строку 223, которая присваивает ссылку на элемент управления IconSelect объектной переменной aBtn.
Процедура IconselectJJpdate (строки 232—256) выполняется всегда при осуществлении выбора в списке IconSelect. Эта процедура исследует выбор в списке (хранящемся в свойстве Text элемента управления списка) и выбирает соответствующую лицевую поверхность для элемента управления IconButton (Кнопка пиктограммы).
Строки 259—267 содержат процедуру EchoButton_Click, которая выполняется при каждом щелчке на элементе управления EchoButton (Кнопка повторения). Эта процедура просто отображает окно сообщения, повторяющего текст, в настоящее время введенный в элемент управления поля AnyText.
646
Неделя 3
Процедура BellButton_Click (строки 270-274) выполняются при каждом щелчке на элементе управления BellButton (Кнопка звонка). Она использует инструкцию Веер для вызова системного звукового сигнала Windows по умолчанию. Конкретный издаваемый звук зависит от аппаратной и программной конфигурации компьютера; в большинстве систем это звук, подобный удару колокольчика.
И наконец, строки 277—283 содержат процедуру RemoveDemoToolbar. Эта процедура использует метод Delete для удаления панели инструментов из коллекции ComandBars.
Резюме
В этой главе было рассказано, как создавать и манипулировать нестандартными управляющими панелями, используя объекты VBA и их свойства и методы. Читатели узнали о различных объектах VBA, которые можно использовать для создания управляющих панелей типа меню и панелей инструментов, а также для изменения встроенных меню и панелей инструментов.
В этом уроке было подробно описано создание, отображение и удаление управляющих панелей. Читатели научились создавать подменю, кнопки и другие элементы управления в управляющей панели. Они узнали, как изменять заголовки элементов управления и присваивать элементам управления процедуры событий. Изложение материала этого урока было завершено работающими примерами завершенной системы меню и завершенной панели инструментов.
Вопросы о ответы
Как можно установить, что управляющая панель является встроенной?
Используйте свойство Builtln; если его значение — True, значит, управляющая панель встроена.
Как можно передать информацию аргументам процедуры, если она является процедурой события для элемента управления управляющей панели?
Нельзя присвоить процедуры, имеющие аргументы, свойству OnAction пункта меню или кнопки панели инструментов. Вместо этого используйте процедуру без аргументов и сделайте информацию доступной процедуре события посредством приватных переменных уровня модуля, свойства Тад или свойства Parameter элемента управления.
Обязательно ли всегда использовать встроенные элементы управления Excel для выполнения их встроенных задач?
Нет. Можно добавлять встроенные элементы управления в собственные нестандартные управляющие панели. Если не указывать процедуру события OnAction, элемент управления использует свое встроенное поведение. Можно, однако, присвоить встроенному элементу управления новое действие, указав процедуру события. Убедитесь, что задачи, присваиваемые встроенным кнопкам панели инструментов, аналогичны их обычным задачам, чтобы не смутить пользователей своей управляющей панели.
Коллоквиум
Ответы на вопросы приведены в приложении.
День 17-й. Меню и панели инструментов
647
Teem
1.	Как добавить подменю в меню?
2.	Почему следует всегда включать File-Exit (Файл-Выход) или аналогичную команду в свои нестандартные строки меню?
3.	Нужно ли указывать процедуру события при создании элемента управления управляющей панели?
4.	Как указать тип управляющей панели при ее создании?
5.	Как указать тип элемента управления управляющей панели при его создании?
6.	Можно ли изменить свойство State встроенного элемента управления управляющей панели?
Упражнения
1.	Измените процедуру в листинге 17.1 для создания процедуры ListAllConimandBars, которая будет отображать список всех управляющих панелей в приложении, включая нестандартные управляющие панели. Добавьте в список дополнительный столбец для указания того, является ли управляющая панель нестандартной или встроенной.
2.	Модифицируйте процедуру, только что созданную в упражнении 1, для создания процедуры ListCommandBarTypes, добавляющей в список управляющих панелей третий столбец, показывающий тип управляющей панели (т.е. является ли она меню, панелью инструментов или всплывающей панелью). (Подсказка. Для определения типа управляющей панели используйте свойство Туре управляющей панели и сравните его содержимое с содержимым констант msoBarTypeMenuBar, msoBarTypeNormal и msoBarTypePopup.)
3.	Напишите процедуру AddMyMenu, добавляющую временное меню в любую активную в текущий момент строку меню в Excel. Нестандартное меню должно содержать один пункт: кнопку. Процедура события кнопки должна удалять временное меню из строки меню. (Подсказка. Для возвращения ссылки на активную строку меню используйте свойство ActiveMenuBar коллекции Application.CommandBars.)
4.	Создайте процедуру AddVBA_ToolbarExtras, которая создает и отображает нестандартную панель инструментов (названную “VBA Programmer”), содержащую три кнопки. Отделите кнопки одну от другой разделительными линиями. Первой кнопкой должен быть встроенный элемент управления New (Создать), создающий новую книгу. Второй кнопкой должна быть нестандартная кнопка, выполняющая процедуру ExcelVBAHelp. Третья кнопка также должна быть нестандартной; она должна выполнять процедуру, которая удаляет нестандартную панель инструментов. (Подсказка. Для первой кнопки используйте встроенный элемент управления New Excel, имеющий идентификатор 2520. Дня второй кнопки используйте идентификатор лицевой поверхности 49, а для третьей — 478.)
В качестве процедуры события для второй кнопки используйте следующую процедуру.
Sub ExcelVBAHelp()
Application.Help "vbaxl9chm“
End Sub
648
Неделя 3
Обработка ошибок
Ваши процедуры и программы на языке VBA, особенно на стадиях разработки, будут часто содержать логические ошибки или будут неспособны правильно обработать некоторые значения данных, полученных от пользователя или считанных с дискового файла. Эти логические ошибки и другие сбои приводят к ошибкам времени выполнения VBА-программ и к их неожиданному завершению. К настоящему времени вы, вероятно, уже имеете некоторый опыт обшения с диалоговыми окнами ошибок во время выполнения программ VBA; такой опыт имеется у всех программистов, потому что создание совершенных программ практически невозможно.
На сегодняшнем уроке мы изучим специальные механизмы обработки ошибок в VBA. Эти механизмы позволят вам писать коды так, чтобы либо обойти ошибку, либо, по крайней мере, завершить выполнение программы организованным способом. Обработка ошибок является важным свойством профессиональных программ. Сегодняшний урок охватывает следующие темы.
•	Краткий обзор стратегий обработки ошибок.
•	Как перехватывать ошибки с помощью оператора On Error GoTo.
•	Как обрабатывать ошибки, используя оператор Resume.
•	Как использовать функции Err и Erl, чтобы выяснить тип ошибки и определить в программе место, в котором она произошла.
•	Как создавать определяемое пользователем условие возникновения ошибки, используя оператор Error.
Стратегии обработки ошибок
С некоторой натяжкой можно сказать, что вы можете использовать две основные стратегии обработки ошибок: предупредительное программирование и перехват ошибок. Обычно при разработке программ применяется комбинация обеих этих стратегий.
Предупредительное программирование во многом подобно предусмотрительному вождению автомобиля. При таком управлении машиной вы стараетесь предупреждать опасные ситуации в пути и избегать их. Применяя предупредительную стратегию программирования, вы стараетесь предупреждать состояния, которые приведут к ошибками во время выполнения программы, и писать код так, чтобы при обнаружении таких состояний избежать сбоя программы во время ее выполнения.
День 78. Обработка ошибок
649
Предупредительное программирование может принимать множество форм. Вы уже видели один пример предупредительного программирования: отображение на экране вводимой пользователем информации с целью удостовериться, что введенные им значения являются именно теми значениями, которые ожидает ваша программа. Например, вы выводите на экран диалоговое окно InputBox и ожидаете получить целочисленное значение, а пользователь вместо этого вводит строку, которая не может быть преобразована в числовое значение; в этом случае, очевидно, программа закончится с ошибкой времени выполнения из-за несоответствия типов данных в некоторой точке вашей программы после оператора InputBox. При использовании предупредительной стратегии в такой ситуации к вашей процедуре ввода вы добавляете такой код, чтобы с его помощью проверить вводимое пользователем значение (используя, скажем, функцию IsNumeric) и повторять запрос на ввод до тех пор, пока не будет введено правильное значение. Предусмотрительно убеждаясь, что введенное пользователем значение именно то, которое ожидается, вы избегаете возможных ошибок во время выполнения вашей программы.
Другой пример предупредительного программирования показан в следующем фрагменте программы. Поскольку деление числа на нуль математически невозможно, при попытке деления некоторого числа на нуль VBA всегда будет генерировать ошибку времени выполнения программы. Следовательно, чтобы принять меры против деления на нуль, можно использовать условный оператор If:
X = Vai(InputBox("Введите первое число", "Ввод”, ”"))
Y = Vai(InputBox("Введите второе число”, “Ввод”, ””))
If Y о О Then
MsgBox Str(X) & "/" & Str(Y) & -=" & Str(X / Y)
Else
MsgBox “Деление на нуль запрещено!"
End If
Приведенные выше операторы предотвращают возникновение ошибки времени выполнения программы, поскольку сначала проверяются значения введенных величин, участвующих в операции деления, а само деление выполняется лишь тогда, когда делитель не равен нулю. Другой путь решения рассматриваемой проблемы состоит в использовании циклической структуры, выполняемой до тех пор, пока пользователем не будет введен ненулевой делитель.
На протяжении этой книги вы уже встречались со многими примерами предупредительного программирования. К настоящему времени вы должны довольно хорошо понимать смысл того, как использовать методы такого программирования. Однако не всегда можно даже представить себе все возможные ситуации, которые могли бы приводить к ошибкам времени выполнения ваших программ на языке VBA. Часто ошибки выполнения программы происходят как результат совершенно неожиданных и непредвиденных событий. Например, ваша программа для хранения данных могла бы использовать некоторую рабочую книгу. Если пользователь удаляет или перемещает такую книгу в другую папку на диске, ваша программа во время выполнения, вероятно, сгенерирует ошибку, когда программа попытается открыть отсутствующую рабочую книгу. Действительно, нет способа предупредить этот вид проблемы или написать соответствующий предупредительный код для ее устранения.
В стратегии перехвата ошибок используются специальные команды (инструкции) VBA для “захвата” ошибок, которые предотвращают генерирование VBA обычного сообщения об ошибках времени выполнения. Такие инструкции перехвата ошибок сообщают VBA, что вы намерены обрабатывать ошибку времени выполнения самостоятельно, и определяют, какая часть кода вашей программы будет выполняться при возникновении той или иной ошибки. Затем вы пишете код для обработки обнаруженных ошибок выполнения под управлением вашей программы.
650
Неделя 3
Чтобы обработать ситуацию наподобие отсутствия файла с рабочей книгой, например, вы могли бы использовать инструкции перехвата такой ошибки в открывающей файл процедуре. Вместо того чтобы получить ошибку времени выполнения и преждевременный останов программы из-за отсутствующего файла, вы могли бы использовать возможность перехвата этой ошибки для вызова специальной процедуры ее обработки, которая точно сообщит пользователю о том, какой файл отсутствует, даст совет по решению этой проблемы, изменит панели с меню и закроет вашу программу организованным способом — вместо того, чтобы предоставить пользователю вашей программы самому устранять беспорядок, возникший в результате непредвиденного завершения программы.
В некоторых случаях вы можете даже исправлять ошибки времени выполнения без прекращения выполнения программы. Использование стратегии перехвата ошибок может также иногда уменьшить количество кода предупредительного программирования, который вы должны были бы написать.
VBA использует взаимозависимую систему инструкций, операторов и функций для захвата и обработки ошибок. В следующих нескольких разделах этого урока сначала описываются операторы VBA захвата ошибок и их синтаксис, а затем представляется несколько примеров применения этих операторов и функций для практического захвата и обработки ошибок.
Инструкция On Error GoTo
Прежде чем вы сможете обработать возможную ошибку, необходимо перехватить ее. Система времени выполнения VBA обнаруживает ошибки времени выполнения программы всякий раз, когда они происходят. Захватывая ошибку, обнаруженную во время выполнения программы, вы предотвращаете выдачу системой времени выполнения VBA обычного сообщения об этой ошибке. Перехватывая ошибку, нужно также определить, какие операторы VBA должны выполниться для ее обработки. Инструкция On Error GoTo используется как для перехвата ошибок времени выполнения, так и для определения того, какая часть кода вашей собственной программы VBA должна выполняться для обработки этих ошибок. Инструкцию On Error GoTo можно использовать также для выключения перехвата ошибок.
Инструкция On Error GoTo имеет эти две синтаксические формы:
On Error GoTo Label
On Error GoTo 0
В первой форме синтаксиса Label представляет собой или метку строки, или ее номер. (Мы изучали метки и номера строк, а также безусловный оператор GoTo на 8-м уроке.) Когда VBA выполняет инструкцию On Error GoTo Label, устанавливается ловушка для захвата ошибки. Если ошибка времени выполнения происходит после того, как VBA выполнил инструкцию On Error GoTo Label, вместо генерирования сообщения об этой ошибке управление передается строке исходного текста, определенной меткой Label. Эта метка должна находиться в той же процедуре, что и относящаяся к ней инструкция On Error GoTo, иначе VBA выдаст сообщение об ошибке компилятора. По окончании процедуры, содержащей инструкцию On Error GoTo Label, VBA удаляет ловушку ошибки; такие ловушки ошибок, установленные инструкцией On Error GoTo Label, имеют те же характеристики, что и локальные переменные. Если ловушку ошибки устанавливает больше одной процедуры в цепочке ее вызовов, VBA использует ловушку с наиболее локальной областью видимости. Если процедура устанавливает более одной ловушки ошибок, VBA использует самую последнюю выполненную инструкцию On Error GoTo Label, чтобы определить, какую ловушку использовать.
День 18. Обработка ошибок
651
Использование On Error GoTo 0, как показано во второй синтаксической форме, заставляет VBA удалять любую ловушку ошибки, которую вы могли предварительно установить с помощью оператора On Error GoTo Label. Любая ошибка времени выполнения, возникшая после выполнения инструкции On Error GoTo 0, снова вызовет обычное сообщение об ошибках. Инструкция On Error GoTo 0 служит для очистки любых обработчиков ошибок, которые вы могли установить инструкцией On Error GoTo Label. Например, вы установили обработчик ошибки лишь для фрагмента внутри процедуры и хотите, чтобы для любых операторов, которые выполняются после этого фрагмента, VBA обеспечивал стандартную реакцию на ошибку времени выполнения программы. В этом случае вы должны установить обработчик ошибки инструкцией On Error GoTo Label, потом разместить инструкции, в которых вы ожидаете появления ошибки, и потом с помощью инструкции On Error GoTo 0 убрать ловушку для обработки этой ошибки.
В следующем фрагменте программы для перехвата ошибки деления на нуль используется инструкция On Error Goto, а затем, после выполнения критических операторов, обработчик ошибки очищается.
X = Vai(InputBox("Введите первое число ", "Ввод", ""))
Y = Vai(InputBox("Введите второе число ", "Ввод",
'Установка ловушки ошибки
On Error GoTo DivideByZero
MsgBox Str(X) & "/" & Str(Y) & ’=" & Str(X I Y)
GoTo Skipl 'Обход обработки ошибки
DivideByZero:
MsgBox "Деление на нуль запрещено!"
Skipl:
On Error GoTo 0 'Нормальный результат обработки ошибки
Инструкция Resume
Инструкция On Error GoTo Label направляет выполнение операторов VBA к специфической точке вашей процедуры, определенной номером строки или меткой, начиная с которой размещен код обработки ошибки. Если ошибка не является фатальной, вы можете обработать ее, а затем продолжить выполнение вашей процедуры с того места, где первоначально произошла ошибка времени выполнения, или с некоторого другого определенного вами места процедуры. (Напомним, что фатальной называется любая ошибка, с которой вы не можете справиться самостоятельно, например отсутствие файла данных или недостаток памяти; нефатальной является любая ошибка, от которой вы можете избавиться путем исправления данных, например неправильный ввод пользователя.)
Инструкция Resume служит в вашем коде обработки ошибок для указания VBA, когда и с какого места продолжить выполнение программы. Кроме того, всякий раз, когда VBA сталкивается с инструкцией Resume, он рассматривает ошибку времени выполнения как решенную (исправленную) и восстанавливает внутренние механизмы обработки ошибок времени выполнения для следующей ошибки.
Инструкция Resume имеет четыре различные синтаксические формы, перечисленные ниже.
Resume
Resume 0
Resume Next
Resume Label
652
Неделя 3
Две синтаксические формы (Resume и Resume 0) вызывают один и тот же эффект: они предписывают VBA продолжать выполнение с того же оператора, который первоначально вызвал ошибку времени выполнения. По существу, инструкции Resume и Resume 0 указывают VBA повторить оператор, который вызвал ошибку. Обычно формы Resume или Resume 0 используются для повторения выполнения оператора после попытки исправить условия, которые привели выполнение этого оператора к ошибке. Используйте одну из этих форм для исправления ошибок типа неудачной попытки открытия дискового файла: ваш код обработки ошибок, например, мог бы задать некое альтернативное расположение этого файла или даже создать отсутствующий файл, а затем снова попробовать повторить выполнение того же оператора открытия файла, используя инструкции Resume или Resume 0.
Следующая форма синтаксиса Resume Next заставляет VBA продолжить выполнение программы с первого оператора, следующего сразу после того оператора, который первоначально вызвал ошибку времени выполнения. Используйте эту форму инструкции Resume, если вызвавший ошибку оператор может быть безболезненно пропущен. Вообще говоря, редко можно встретить правильно написанную процедуру, на выполнении которой не будут сказываться пропуски операторов, способные вызвать ошибки времени выполнения, так что вы, вероятно, не будете использовать инструкцию Resume Next очень часто — если, конечно, вы не уверены, что ваш код обработки ошибок содержит операторы, которые выполняют ту же (или параллельную) задачу, что и оператор, вызвавший ошибку времени выполнения.
Последняя форма синтаксиса рассматриваемой инструкции, Resume Label, предписывает VBA продолжить выполнение программы с первого оператора после отметки, определенной Label, которая может быть любой меткой или номером строки. Используйте оператор Resume Label, если необходимо продолжить выполнение с точки в вашей программе перед оператором, который вызвал ошибку времени выполнения, либо если вы хотите продолжить выполнение с точки на несколько операторов после того, который вызвал ошибку времени выполнения.
Всякий раз, когда VBA встречает инструкцию Resume (в любой из ее синтаксических форм), он полагает, что ошибка времени выполнения устранена (или связанная с ней /ПРИМЕЧАНИЕ проблема так или иначе разрешена). До тех пор любые дополнительные возникающие при выполнении программы ошибки не будут перехвачены и будут вызывать сообщения об ошибках времени выполнения, прекращая выполнение вашей процедуры. Таким образом, чтобы восстановить внутренние механизмы обработки ошибок времени выполнения, VBA должен выполнить инструкцию Resume.
Ниже приведен ряд фрагментов кода, каждый из которых показывает пример использования одной из форм оператора Resume.
' Пример 1
X = Vai(InputBox("Введите первое число ”, "Ввод", ""))
Y = Vai(InputBox("Введите второе число ", "Ввод", ""))
On Error Goto DivideByZero
Z = X / Y
MsgBox Str(X) &	& Str(Y) & "=" & Str(Z)
GoTo Skipl 'Обход обработки ошибки
DivideByZero:
Do
Y = Vai(InputBox(“Введите ненулевое второе число "))
Loop Until Y <> 0
Resume 0	'Попытка снова выполнить оператор, вызвавший ошибку
День 18. Обработка ошибок	653
Skipl:
On Error GoTo 0 'Восстановление нормальной обработки ошибок
Код обработки ошибок в предыдущем примере выполняет цикл, чтобы получить от пользователя отличное от нуля число, и затем продолжает выполнение того же оператора, который первоначально вызвал ошибку времени выполнения: операцию деления. Инструкция On Error GoTo 0 включена после кода обработки ошибок для удаления ловушки ошибок. Если предположить, что на этом программа не заканчивается, вызванная последней инструкцией ситуация оказалась бы неблагоприятной для будущих ошибок времени выполнения, при появлении которых они обрабатывались бы кодом обработки ошибок после метки DivideByZero.
' Пример 2
X = Val(InputBox("BBenHTe первое число ", "Ввод", "")) Y = Val(InputBox("Введите второе число ", "Ввод", "")) On Error GoTo DivideByZero
Z = X / Y
MsgBox Str(X) &	& Str(Y) b "=" & Str(Z)
GoTo Skip2
DivideByZero:
Resume Next
Skip2:
On Error GoTo 0 'Восстановление нормальной обработки ошибок
В этом примере ошибка времени выполнения, по существу, просто игнорируется и выполнение продолжается с оператора, следующего за тем, который вызвал ошибку: с оператора MsgBox, который отображает результат деления. В этом примере инструкция On Error GoTo 0 также включена после кода обработки ошибки по тем же причинам, что и в предыдущем примере.
' Пример 3
X = Val(InputBox("Введите первое число ", "Ввод", ""))
Retry:
Y = Vai(InputBox("Введите второе число ", "Ввод", ))
On Error GoTo DivideByZero
Z = X / Y
MsgBox Str(X) & "/" & Str(Y) & "=" & str(Z)
GoTo Skip3
DivideByZero:
MsgBox "Деление иа нуль запрещено. Попытайтесь снова."
Resume Retry
Skip3:
On Error GoTo 0 'Восстановление нормальной обработки ошибок
В этом заключительном примере перед оператором, который получает от пользователя значение делителя, используется метка Retry:. Если происходит ошибка деления на нуль, пользователь видит сообщение, объясняющее проблему, и инструкция Resume направляет VBA на продолжение выполнения программы с метки Retry:. Затем пользователя снова просят ввести другое значение делителя и операция деления повторяется. Программа снова и снова повторяет эти действия до тех пор, пока пользователь не введет отличное от нуля значение делителя.
654
Неделя 3
Так надо
Перед возобновлением выполнения программы убедитесь, что вы так или иначе разрешили ситуацию, вызвавшую ошибку. В частности, если вы используете инструкции Resume или Resume Next для повторения сбойного оператора без устранения вызвавших ошибку условий, ваша процедура может войти в бесконечный цикл: ошибка время выполнения произойдет снова, ваша процедура выполнит код обработки ошибок и затем возвратится к вызвавшему ошибку оператору, который снова начнет тот же цикл.
Перед закрытием программы в результате фатальной ошибки не забывайте удалять любые меню, закрывать рабочие книги и документы и, если возможно, сохранять все необходимые данные.
Так не надо
.к....................	...'ШИЖвИвв
НЕ завершайте свою процедуру, если не уверены в том, что ошибка времени выполнения является фатальной, и в том, что нет никакого способа избавиться от нее. Профессиональные программы должны всегда обрабатывать ошибки во время выполнения программы настолько изящно, насколько это возможно, и завершать их выполнение только в тех случаях, когда не имеется никакого другого способа решить возникшую проблему.
Определение типа ошибки, ее местонахождения и текста сообщения
Часто для успешного решения проблем вам необходимо будет точно знать тип произошедшей ошибки времени выполнения. Это может оказаться полезным также для получения сообщения, которое соответствует возникшей во время выполнения программы ошибке. Реже вам необходимо будет знать точное место в программе, где произошла такая ошибка. VBA обеспечивает функцию Err, использование которой позволяет точно определить тип возникшей ошибки времени выполнения, функцию Error для определения того, какой текст сообщения соответствует конкретной ошибке, а также Erl для определения места в вашей программе, где произошла ошибка времени выполнения. Кроме того, VBA также обеспечивает вас объектом Err, из которого вы можете получить номер ошибки, текст сообщения об ошибках и конкретный объект или модуль, в котором произошла эта ошибка.
Чтобы понять, зачем вам могут понадобиться точный тип ошибки времени выполнения и точное местоположение сбойного оператора в вашей программе, рассмотрим следующий фрагмент кода.
X = CDbl(InputBox("Введите первое число ", "Ввод",
Y = CDbl(InputBox("Введите второе число ", "Ввод", ""))
MsgBox Str(X) & "/" & Str(Y) & "=" & Str(X I Y)
В этом фрагменте результаты функций InputBox в первых двух строках преобразуются в числа с двойной точностью функцией CDbl. Теперь пользователь может вводить не только нуль в ответ на второе приглашение к вводу (в результате чего во время выполнения программы возникает ошибка деления на нуль), но и ввести строку, которая вообще не может быть преобразована в число. В случае использования функции CDB1 при этом будет сгенерирована ошибка времени выполнения. (Функция Vai, которой мы пользовались раньше, возвращает 0, когда не может преобразовывать строку в число.)
Чтобы знать, как обработать ошибку времени выполнения в предыдущем фрагменте кода, необходимо установить, возникла ли ошибка деления на нуль или значение
День 18. Обработка ошибок
655
введенной строки просто не может быть преобразовано в число. Чтобы определить наиболее подходящую точку для продолжения выполнения программы, вы должны быть способны выяснить, какой оператор вызвал ошибку: один из двух вводных диалогов или операция деления.
В следующих разделах этого урока показано, как узнать тип ошибки, соответствующий ей текст сообщения и местоположение сбойного оператора в вашей программе.
Определение шипа ошпбкп: фдплция Err
Чтобы выяснять тип возникшей конкретной ошибки времени выполнения, используется функция Err, которая возвращает целое число, соответствующее этой конкретной ошибке.
Функция Err имеет следующий простой синтаксис.
Err
Функция Err не имеет никаких аргументов и просто возвращает целое число, которое указывает на специфическую ошибку времени выполнения (это число называется кодом ошибки). В табл. 18.1 перечислены коды ошибок времени выполнения VBA и кратко описаны их причины. (Более подробную информацию об этих ошибках можно найти в справочной системе в разделе “Перехватываемые ошибки” (Trappable Errors). ) Функция Err всегда будет возвращать один из этих кодов либо определяемый пользователем код ошибки (создание определяемых пользователем кодов ошибки описано ниже в этом уроке). Следующий фрагмент кода показывает пример использования функции Err для возврата кода ошибки.
On Error GoTo HaveError
X = 1 / О
GoTo Skipl
HaveError:
MsgBox "Номер ошибки " & Str(Err)
Skipl:
Таблица 18.1. Коды перехватываемых ошибок в VBA
Код ошибки Описание
3	Инструкция Return без GoSub
5	Недопустимый вызов процедуры
6	Переполнение
7	Не хватает памяти
9	Индекс выходит за	пределы	допустимого диапазона
10	Массив имеет фиксированную	длину	или временно блокирован
11	Деление на 0
13	Несоответствие типа
14	Не хватает памяти для строки
16	Слишком сложное выражение
17	Невозможно выполнение требуемой операции
18	Произошло прерывание, вызванное пользователем
20	Инструкция Resume вне программы обработки ошибок
656
Неделя 3
Продолжение табл. 18.1
Код ошибки	Описание
28 35 47 48 49 51 52 53 54 55 57 58 59 61 62 63 67 68 70 71 74 75 76 91 92 93 94 97	Не хватает стековой памяти Процедура Sub, Function или Property не определена Слишком много клиентов библиотеки приложения Ошибка при загрузке библиотеки Неверное соглашение о вызовах библиотеки Внутренняя ошибка Неверное имя или номер файла Файл не найден Неверный режим файла Файл уже открыт Ошибка устройства ввода-вывода Файл уже существует Неверная длина записи Переполнение диска Ввод за пределами конца файла Неверный номер записи Слишком много файлов Устройство недоступно Нет разрешения Диск не готов Невозможно переименование с другим именем диска Ошибка доступа к файлу/каталогу Путь не найден Не задана объектная переменная или переменная блока With Цикл For не инициализирован Недопустимая строка шаблона Недопустимое использование Null Невозможен вызов процедуры Friend для объекта, не являющегося экземпляром определяющего класса
298 320	Системная библиотека DLL не загружается Нельзя использовать имена символьных устройств в именах данных файлов
321 322 325 327 328 335	Недопустимый формат файла Невозможно создание требуемого временного файла Недопустимый формат файла ресурсов Не найдено значение данных по имени Недопустимый параметр. Невозможна запись массивов Невозможен доступ к системному реестру
День 18. Обработка ошибок
657
Продолжение табл. 18.1
Код ошибки	Описание
336 337 338 360 361 363 364 365 368	Компонент ActiveX зарегистрирован неправильно Компонент ActiveX не найден Компонент ActiveX выполняется неправильно Объект уже загружен Невозможно загрузить или выгрузить данный объект Указанный элемент ActiveX не найден Объект выгружен Невозможна выгрузка в данном контексте Истек срок действия данного файла. Программе требуется файл более новой версии
371	Данный объект не может использоваться в качестве формы-владельца метода Show
380 381 382 383	Недопустимое значение свойства Недопустимый индекс массива свойств Инструкция Property Set не поддерживается во время выполнения Инструкция Property Set не поддерживается (свойство доступно только для чтения)
385 387 393 394	Требуется индекс массива свойств Инструкция Property Set не разрешена Инструкция Property Get не поддерживается во время выполнения Инструкция Property Get не поддерживается (свойство доступно только для записи)
400 402 419 422 423 424 425 429	Форма уже отображается; модальное отображение невозможно Сначала необходимо закрыть самую верхнюю модальную форму Нет разрешения на использование объекта Свойство не найдено Не найдено свойство или метод Требуется объект Недопустимое использование объекта Невозможно создание объекта или возвращение ссылки на этот объект компонентом ActiveX
430 432 438 440 442	Класс не поддерживает программирование объектов Не найдено имя файла или класса при программировании объектов Объект не поддерживает это свойство или метод Ошибка программирования объектов Потеряна связь с библиотекой типов или библиотекой объектов для удаленного доступа
443 445	Программируемый объект не имеет значения по умолчанию Объект не поддерживает эту команду
658
Неделя 3
Окончание табл. 18.1
Код ошибки	Описание
446 447 448 449	Объект не поддерживает поименованные аргументы Объект не поддерживает текущую национальную настройку Поименованный аргумент не найден Аргумент является обязательным или неправильно присвоено значение свойства
450	Недопустимое число аргументов или неправильное присвоение значения свойства
451 452 453 454 455 457 458 459 460 461 480 481 482 483 484	Объект не является семейством Недопустимый порядковый номер Указанная функция библиотеки DLL не найдена Программный ресурс не найден Ошибка блокировки программного ресурса Запись уже связана с элементом данного семейства Переменная использует не поддерживаемый в Visual Basic тип Компонент не поддерживает события Недопустимый формат буфера обмена Указанный формат не соответствует формату данных Невозможно создание автоматически обновляемого изображения Недопустимый рисунок Ошибка принтера Драйвер принтера не поддерживает данное свойство Ошибка получения системной информации о принтере. Убедитесь, что принтер установлен правильно
485 486	Недопустимый тип рисунка Невозможно напечатать изображение формы на принтере данного типа
520 521 735 744 746 31001 31004 31018 31027 31032 31036 31037	Невозможна очистка буфера обмена Невозможно открыть буфер обмена Невозможно сохранить файл в каталоге TEMP Образец поиска не найден Слишком длинная замена Не хватает памяти Отсутствует объект Класс не определен Невозможно активизировать объект Невозможно создать внедренный объект Ошибка сохранения в файле Ошибка загрузки из файла
День 18. Обработка ошибок
659
Получение текста сообщения об пшнбке: функция Error
Для определения того, какой текст сообщения соответствует тому или иному коду ошибки, применяется функция Error. Эта функция возвращает строку с текстом сообщения об ошибке.
Функция Error имеет следующий синтаксис.
Error ([errorcode])
Необязательный аргумент errorcode может быть любым числовым выражением, которое определяет одну из кодов ошибок, перечисленных в табл. 18.1. Если вы опускаете аргумент errorcode, функция Error возвращает текст сообщения об ошибке, соответствующий последней возникшей и не устраненной ошибке времени выполнения. Если не устраненная ошибка времени выполнения отсутствует, функция Error возвращает пустую строку (""). В качестве примера рассмотрим следующий фрагмент кода, который отображает сообщение, содержащее номер ошибки и соответствующую этой ошибке строку:
On Error Goto ShutDown
X = 1 / 0 'Недопустимая операция
GoTo Skipl
ShutDown:
MsgBox Str(Err) & " означает" & Error
End ' Остановка выполнения всей программы
Skipl:
Этот фрагмент устанавливает ловушку ошибки с помощью инструкции On Error и затем выполняет недопустимую операцию деления на нуль. Оператор MsgBox отображает текстовую строку, связывая код ошибки с текстом соответствующего сообщения.
Определение места в программе, где произотла ошибка: функция Erl
Для выяснения того, в каком месте программы возникла ошибка, в VBA используется функция Erl. Эта функция возвращает номер самой близкой строки, предшествующей строке, в которой возникла ошибка времени выполнения.
Общий синтаксис функции Erl.
Erl
Функция Erl возвращает номер последней строки, предшествующей содержащей сбойный оператор строке. Erl возвращает не сами метки строк, а лишь их номера.
Для использования функции Erl необходимо добавить номера к некоторым строкам в
вашей процедуре, как это показано в следующем примере кода. Если процедура вообще не содержит номеров строк или не возникало никаких ошибок, функция Erl
возвращает 0.
ПРИМЕЧАНИЕ
Номера строк - еще одно историческое наследие в VBA. В старом языке программирования Basic номера строк были необходимы; каждая строка программы начиналась с числа, и выполнение программы проходило от строк с меньшими номерами к строкам с большими номерами. За исключением специальных случаев, таких как применение функции Erl, нумеровать строки в VBA необязательно.
При создании номеров строк в вашей программе VBA просто удостоверьтесь, что первым символом в строке является число и что операторы VBA в строке отделены от него, по крайней мере, одним пробелом; VBA предполагает, что число в начале строки является номером данной строки.
660
Неделя 3
В следующем фрагменте первые три строки имеют номера (от 1 до 3). Оператор MsgBox отображает сообщение Ошибка времени выполнения в строке 2 путем вызова функции Erl, которая выдает номер последней строки перед строкой, содержащей ошибку, — в данном случае той самой строки, которая содержит ошибку.
1	On Error GoTo ShutDown
2	X = 1 / 0 'Недопустимая операция
3	GoTo Skipl
ShutDown:
MsgBox "Ошибка времени выполнения в строке " & Str(Erl)
End 'Остановка выполнения всей программы
Skipl:
Так надо
Для того чтобы определить после получения результата функции Err, что же именно произошло и что делать дальше, применяйте инструкцию Select Case.
Всегда используйте функцию Error для того, чтобы получить текст сообщения, соответствующий возникшей
Так не надо
Не запутывайте пользователя ваших процедур мудреными сообщениями об ошибке. Делайте текст сообщения простым и по возможности предлагайте способы устранения возникшей ошибки.
Искусственное создание ошибки н создание собстовнных коуов ошибки: инструкция Error
Язык VBA позволяет вам принудительно вызывать сообщение об ошибке времени выполнения или создавать собственные коды ошибок с помощью инструкции Error. Это можно применить для того, чтобы обработать некоторую ситуацию так, как будто в ней произошла ошибка и выдать собственный код этой ошибки.
ПРИМЕЧАНИЕ
Не пугайте функцию Error с инструкцией Error. Функция Error возвращает текст сообщения, соответствующий некоторому коду ошибки. Инструкция Error позволяет создавать собственные коды ошибки.
Используйте инструкцию Error для принудительного создания ошибки времени выполнения с помощью одного из кодов, приведенных в табл. 18.1, в ситуациях, когда вы хотите обработать неподходящее условие или значение в вашей процедуре так, будто бы эта ошибка возникла во время выполнения программы. Предположим, например, что у вас есть процедура с необязательным аргументом, который всегда должен содержать целочисленное значение или другое значение, которое может быть преобразовано в целое число. Если ваш необязательный аргумент представляет собой тип данных Variant, вы не можете полагаться на VBA в деле искусственного создания ошибки несоответствия типов и выдачи вам сообщения о том, что вы сделали программную ошибку и передаете неправильный тип данных в качестве необязательного аргумента этой процедуры. Вместо этого вы могли бы проверять тип данных, передаваемых с необязательным аргументом, и затем использовать инструкцию Error, чтобы спровоцировать ошибку несоответствия типов во время выполнения программы.
День 18. Обработка ошибок
661
Используете ли вы оператор Error, чтобы вызвать ошибку времени выполнения с предопределенным кодом ошибки или с определяемым пользователем кодом ошибки, во время выполнения программы VBA ведет себя точно так же, как и в случае возникновения любой другой естественной ошибки. VBA прибегает к любой ловушке ошибки, которую вы устанавливаете с помощью инструкции On Error GoTo; если в программе нет ни одной вашей ловушки ошибок, VBA отображает сообщение об ошибке времени выполнения, как обычно.
Общий синтаксис инструкции Error выглядит так.
Error errorNumber
Здесь errorNumber представляет собой любое числовое выражение, которое дает число в диапазоне от 0 до 65535. Если аргумент errorNumber — одно из чисел кода ошибки, перечисленных в табл. 18.1, VBA поведет себя так, будто эта ошибка времени выполнения возникла в действительности; функции Err, Erl и Error будут возвращать результаты, соответствующие коду ошибки времени выполнения, которую вы определили в аргументе errorNumber.
Если вы используете число, не совпадающее ни с одним из кодов ошибки, перечисленных в табл. 18.1, VBA устанавливает функцию Err, которая возвратит это число, и функцию Erl, которая сообщит номер последней строки перед инструкцией Error. Функция Error возвратит строку Application-defined or object-defined error (определенная приложением или объектом ошибка).
В следующем фрагменте, например, устанавливается ловушка ошибки и затем используется инструкция Error для генерирования определяемой пользователем ошибки всякий раз, когда значение А является нулем.
' example 1
Dim A As Double
On Error GoTo BadValue
Retry:
A = Vai(InputBox("Введите ненулевое число "))
'Создание определенной пользователем ошибки для введенного нулевого числа
If А = 0 Then Error 65535
MsgBox "1 /" & А & "=" & (1/А)
GoTo Endit
BadValue:
MsgBox "Допустимо только ненулевое число! Попытайтесь снова."
GoTo Retry
Endit:
Следующий фрагмент демонстрирует установку ловушки ошибки и использования затем инструкции Error для генерирования ошибки времени выполнения под номером 11 (деление на нуль) всякий раз, когда введено нулевое значение А.
' example 2
Dim A As Double
On Error GoTo BadValue
Retry:
A = Val(InputBox("Введите ненулевое число "))
'Провоцирование ошибки деления на нуль ПЕРЕД тем, как это фактически произойдет
If А = 0 Then Error И
MsgBox "1 /” & А & ”=" & (1/А)
GoTo Endit
662
Неделя 3
BadValue:
MsgBox "Деление на нуль недопустимо "
GoTo Retry
Endit:
Чтобы избежать конфликта с предопределенными кодами ошибок времени выполнения, обычно достаточно начать вашу нумерацию кода ошибки с числа 65535 и потом понижать это значение.
Использование объекта Err
Очень важно понимать, как использовать функции Err, Erl и Error, а также инструкцию Error. Эти функции, используемые в текстах обработки ошибок, вы часто можете увидеть в проектах других программистов и в текстах программ, который вы могли бы получить из сети. Вам часто также будут встречаться функции для приема информации об ошибках, описанные ранее в этой главе, в программах VBA, разработанных для Excel 5, Excel 7, WordBASIC и других реализаций языка Basic. Функции получения информации об ошибках, которые мы обсудили ранее в этой главе, с самого начала были частью языка программирования BASIC.
Однако, начиная с VBA, входящего в комплект Qffice 97, предпочтительной методикой получения информации об ошибке или провоцирования ее возникновения становится использование объекта Err. Любой фрагмент обработки ошибок, который вы пишете для приложения Office 2000, должен использовать объект Err. Все приведенные ниже примеры обработки ошибок в этом и последующих уроках используют объект Err. Объект Err имеет несколько свойств, которые дают возможность получить ту же информацию, до которой вы можете добраться с использованием любой из ранее описанных функций информации об ошибках, включая дополнительную информацию, связанную с использованием внешних ресурсов таких, например, как DLL-подпрограммы (эту тему мы рассмотрим на 20-м уроке). В табл. 18.2 перечислены свойства объекта Err и дано краткое описание каждого свойства.
Таблица 18.2. Свойства объекта Err
Свойство	Tun	Описание
Description	Чтение-запись	Строка, содержащая описание ошибки. Свойство Description обеспечивает ту же информацию, что и функция Error
HelpContext	Чтение-запись	Строка, определяющая номер раздела в справочном файле Microsoft Windows
HelpFile	Чтение-запись	Строка, содержащая путь “диск - папка - имя файла” к справочному файлу Microsoft Windows
LastDLLError	Только для чтения	Код ошибки, который соответствует ошибке, возникшей при вызове процедуры в DLL
Number	Чтение-запись	Определяемый VBA или пользователем номер ошибки; лежащий в диапазоне 0 до 65535. Свойство Number обеспечивает ту же информацию, что и функция Err
Source	Чтение - запись	Строка, определяющая имя объекта или приложения, в котором первоначально возникла ошибка
День 18. Обработка ошибок
663
Свойства Helpcontext и HelpFile не очень пригодятся вам. Эти свойства предназначены для использования с файлом справки, который вы можете создать с помощью компилятора справки Microsoft Windows (или с помощью инструмента создания справки какого-либо независимого разработчика, например RoboHelp фирмы Blue Sky Software или Doc-To-Help фирмы Wextech). Транслятор справки Microsoft Windows не включен в комплект VBA (или Microsoft Office); однако вы можете получить его в Microsoft отдельно.
В дополнение к свойствам, перечисленным в табл. 18.2, объект Err имеет два метода, которые можно использовать либо для очистки внутреннего механизма VBA обработки ошибок, либо для принудительного вызова ошибки VBA или определяемой пользователем ошибки времени выполнения: Clear и Raise, соответственно.
Синтаксис метода Clear выглядит так.
Trr.Clear
Метод Clear не имеет никаких аргументов. Вызов метода Clear заставляет объект Err очищаться от любой информации (об ошибке), которую он мог хранить в настоящее время. Свойства объекта Err восстанавливаются автоматически всякий раз, когда система времени выполнения VBA встречается с разрешенной (т.е. так или иначе исправленной) ошибкой, но можно использовать метод Clear и для явной очистки от информации об ошибке.
Для принудительного генерирования ошибки или создания определяемой пользователем ошибки времени выполнения вы, скорее всего, будете использовать метод Raise. В частности, метод Raise используется тогда, когда вы хотите сгенерировать определяемую пользователем ошибку в одном из ваших модулей класса.
Синтаксис метода Raise следующий.
Err.Raise number [, source, description, helpfile, helpcontext]
В этом примере number — единственный требуемый аргумент; number представляет собой числовое выражение кода ошибки — это один из номеров ошибок VBA или определяемый пользователем код ошибки. (Определяемые пользователем коды ошибок были описаны в предыдущем разделе этой главы.)
Source — любое строковое выражение, которое представляет местоположение источника ошибки в исходном тексте. Если вы опускаете этот аргумент, когда используете метод Raise, значением по умолчанию объекта Err будет имя текущего проекта VBA. Если вы устанавливаете этот аргумент при вылавливании ошибки в модуле класса, определите исходное имя в форме project.class, где project — имя проекта VBA и class — имя модуля класса.
Description — строка, описывающая конкретную ошибку. Если вы опускаете этот аргумент и при поиске ошибки VBA, объект Err автоматически обеспечивает соответствующее описание текста для ошибки, которую вы вылавливаете. Если вы опускаете этот аргумент при поиске ошибки, определяемой пользователем, объект Err по умолчанию использует значение Application-defined or object-defined error (ошибка, определенная прикладная программой или объектом).
Helpfile представляет собой строку, дающую весь путь “диск - папка - имя файла’’ к справочному файлу Microsoft Windows; helpcontext является строкой, определяющей конкретный связанный с данной ошибкой раздел справки. Если вы опускаете эти аргументы при поиске кода ошибки VBA, они автоматически заполняются разделами справки, связанными с ошибкой VBA. Как упоминалось ранее в этом разделе, для создания справочных файлов необходимо использовать транслятор справки; в комплектах Microsoft Office и VBA такой транслятор справки отсутствует.
664
Неделя 3
Запомни
Если вы хотите создать определяемый пользователем код ошибки времени выполнения в одном из своих модулей класса, используйте метод Raise объекта Err.
Добавляйте встроенную константу vbOb jectError к любым определяемым пользователем кодам ошибки, которые вы генерируете внутри модуля класса. Например, если в модуле класса вы хотите описать определяемую пользователем ошибку 6010, используйте метод Raise с аргументом vbOb jectError + 6010. Это позволит системе времени выполнения VBA дать точную информацию об ошибке, связываемой с объектом.
Поувеуем итоги: примеры ибрабитки итибик
В оставшихся разделах этого урока мы рассмотрим примеры различных способов применения методов обработки ошибок, описанных выше. Во всех примерах, приведенных здесь и далее по тексту этой книги, объект Err используется как наиболее предпочтительная методика получения информации об ошибке и принудительного создания ошибок времени выполнения программы.
Первые четыре листинга похожи друг на друга и демонстрируют различные пути обработки одной и той же ошибки времени выполнения. Заключительный пример показывает, как использовать принудительно вызванные ошибки времени выполнения, чтобы помочь вам заставить ваши собственные процедуры вести себя так, будто это встроенные процедуры VBA.
Обработка неустранимых ошибок
Первым примером обработки ошибок является листинг 18.1, который содержит процедуру DemoFatalError. Основная выполняемая в этой программе (и в следующих трех программах) задача состоит в том, чтобы отобразить таблицу квадратных корней из чисел от 5 до -2 на бланке Excel; эти корни вычисляются с использованием функции Sqr. Функция Sqr, однако, требует, чтобы ее аргумент был неотрицательным числом, и генерирует ошибку времени выполнения всякий раз, когда вы передаете ей отрицательное число: код этой ошибки — 5, а описание — Недопустимый вызов процедуры. Листинги 18.1—18.4 демонстрируют различную технику обработки таких ошибок для решения связанных с ними проблем.
Функция Sqr с отрицательным аргументом генерирует ошибку времени выполнения, поскольку отрицательные числа действительно не имеют квадратного корня. (Всякий раз, когда вы умножаете два отрицательных числа друг на друга, в результате получа-ОРИМЕЧАИИЕ еТСЯ положительное числ0 ) Однако кому-то может показаться странным тот факт, что \	число 4 имеет квадратный корень, а число -4 его не имеет. Чтобы разрешить это оче-
видное противоречие, математики изобрели мнимое число, представляемое символом /и представляющее собой квадратный корень из -1. Используя мнимую единицу /, становится возможным представить квадратные корни отрицательных чисел. Например, хотя квадратный корень из 4 равен 2, квадратный корень из -4 равен 2/, т.е. 2, умноженное на квадратный корень из -1.
Процедура DemoFatalError в листинге 18.1 обрабатывает ошибку времени выполнения как неразрешимую, т.е. фатальную ошибку. Когда обращение к функции Sqr генерирует ошибку времени выполнения, код обработки ошибок отображает сообщение об ошибке и процедура оканчивается. DemoFatalError использует свойства объекта Err для идентификации типа ошибки и функцию Erl — для идентификации номера строки, которая содержит сбойный оператор.
День 18. Обработка ошибок	665
ПРИМЕЧАНИЕ
При вводе листинга 18.1 обратите особое внимание на строки 26-30. Дополнительные номера строк в операторах VBA действительно являются частью программы VBA, которую вы должны ввести. Эти номера строк включены в текст с тем, чтобы функция Erl могла сообщить вам, где произошла ошибка времени выполнения.
AucmiiHg 18.1. Процеддро nemoFatalError: цспользовацце оОьекша Err 11 функции Erl
1: 2: 3: 4: 5: 6: 7: 8:	Option Explicit Sub DemoFatalError() Const tblHead = "Таблица квадратных корней" Const FIRST As Integer = 5 Const LAST As Integer = -2 Const INCR As Integer = -1
10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47:	Dim Count As Integer Dim cRow As Integer Dim oldSheet As String oldSheet = ActiveSheet.Name 'Запоминаем лист Worksheets!“Sheetl").Select 'Переходим на новый On Error GoTo BadValue	'Устанавливаем ловушку ошибки Cellsfl, 2).Value = tblHead 'Заголовки таблицы Cells(2, 1).Value = " X" Cells(2, 2).Value = " Sqr(X)" Cells(3, 1).Value = String(35, Cells(3, 2).Value = String(35, cRow = 4 1:	For Count = FIRST To LAST Step INCR 2:	Cells(cRow, 1).Value = Count 3:	Cells(cRow, 2).Value = CStr(Sqr(Count)) 4:	cRow = cRow + 1 5:	Next Count MsgBox Buttons:=vblnformation, Title:=tblHead, prompt:="Таблица квадратных корней построена." GoTo Ending BadValue: MsgBox Buttons:=vbCritical, Title:=tblHead, prompt:="0fflH6Ka! " & Err.Number Err.Description & " в строке " & Erl & " в " & Err.Source & vbCr & _ "Неверное значение: " & Count & vbCr & "Обработка прекращена." Ending: Sheets(oldSheet).Select End Sub
666
Неделя 3
Если вы правильно введете листинг 18.1, то процедура DemoFatalError отобразит диалоговое окно, показанное на рис. 18.1 (вы можете переименовать свой проект в соответствии с 18-м днем вашего изучения Excel 2000 вместо следующего по умолчанию его названия VBAProject).
) Anal
j Г"
2
3
4
5
6
|И|
8
JJ
10
К
12:
13 •
14
15
15
17
4

0

* г Л S. !, «1 сс%
- ж к н  к 9 ,»• Ж % 1Л а .3 ;г ПС  • *• Д, -.
В С D Ё F G Н i т
Таблица квадратных корней
’ Sqr(X)	Й
W//W//
Таблица квадратных корней
и
|М < » н\яЬе<Ч1/Sheetz/She<?t3 /
l«l

1‘ ’8 .ч?-
В

Рис. 18.1. Процедура DemoFatalError из листинга 18.1 выводит такое сообщение с описанием возникшей ошибки
DemoFatalError — единственная процедура в листинге 18.1. В строках 5—8 объявляется несколько констант, используемых в этой процедуре: заголовок для таблицы величин квадратных корней и диалоговых окон, отображаемых этой процедурой, начальное и конечное значения для таблицы квадратных корней и значения приращения для цикла For. Строки 10-12 объявляют переменные, используемые программой DemoFatalError: переменную для подсчета корня, переменную для текущей строки рабочего листа и переменную для хранения имени листа, который был активен до начала этой процедуры. В строке 14 сохраняется имя текущего рабочий листа, а затем в строке 15 происходит переход на новый лист.
В строке 17 устанавливается ловушка ошибок с помощью инструкции On Error GoTo goto, а в строках 19—23 формируется таблица и заголовки столбцов на рабочем листе для таблицы квадратных корней.
Обратите особое внимание на строки 26-30. Инструкции These содержат цикл for... next, который строит таблицу квадратных корней. Каждая строка в цикле имеет в начале соответствующий номер: строки пронумерованы от 1 до 5. Эти номера позволяют функции Erl идентифицировать строку, которая генерирует ошибку времени выполнения. В противном случае Erl возвратила бы значение 0.
В строках 32 и 33 отображается диалоговое окно с сообщением о том, что таблица квадратных корней сформирована. Фактически эти строки в данной процедуре вы-
День 18. Обработка ошибок
667
подняться никогда не будут, поскольку она написана так, чтобы ошибка времени выполнения происходила всегда. Часть программы обработки ошибок в этой процедуре обрабатывает ошибку как фатальную, поэтому выполнение никогда не добирается до строки 32. (В порядке эксперимента измените константу LAST на 1 вместо -2, и затем выполните процедуру DemoFatalError.)
Строка 35 содержит инструкцию GoTo, которая направляет выполнение операций к строке, обозначенной меткой Ending, чтобы обойти текст обработки ошибок в строках 37—42. (Инструкции GoTo мы рассматривали на 8-м уроке.)
Фактический текст части программы для обработки ошибок находится в строках 37-43. Поскольку при установке ловушки ошибки инструкция On Error GoTo определяет метку строки BadValue, VBA выполняет инструкции в строке 37 всякий раз, когда ошибка времени выполнения происходит в процедуре DemoFatalError. Для этой процедуры текст обработки ошибок просто отображает диалоговое сообщение о том, каков номер ошибки, и соответствующее разъяснение: в какой строке она произошла и какое значение переменной привело к этой ошибке (см. рис. 18.1).
		Вы можете иногда использовать свою собственную программу обработки ошибок, /ррргт............чтобы выдать больше информации относительно ошибки времени выполнения, чем
1	это может сделать VBA. Обратите внимание, что диалоговое сообщение, приведенное
в листинге 18.1 и показанное на рис. 18.1, также указывает на то, что сбойное значение является таковым, с каким VBA не в состоянии справиться.
Поскольку программа обработки ошибок находится в конце процедуры и не содержит никаких инструкций Resume, VBA продолжает выполнение процедуры с первой инструкции после строки 43.
Обратите внимание на метку Ending в строке 45 и на оператор в строке 46, восстанавливающий рабочий лист, который был активен до начала этой процедуры. Оператор в строке 46 должен быть выполнен независимо от того, происходит или нет ошибка времени выполнения.
Когда вы устанавливаете ловушку обработки ошибок в процедуре, она (ловушка) дей-.	ствует только в этой конкретной процедуре. Как только эта процедура прекращает вы-
/пРИМЕЧАИИЕ	полнение, ее ловушка ошибки больше не действует. В каждой процедуре следует ус-
\	танавливать собственную ловушку ошибок и писать собственную программу обработки
ошибок. Ваша программа обработки ошибок может, однако, вызывать и другие процедуры и/или функции.
Так надо
Всегда удостоверьтесь, что ваша процедура независимо ни от чего выполняет все необходимые служебные действия - наподобие закрытия рабочих книг, удаления областей меню или сохранения данных, - прежде, чем она заканчивается из-за обнаруженной фатальной ошибки.
Так не надо
НЕ обязательно нумеровать все строки в вашей программе VBA. Даже одна неправильная нумерация строк сама по себе может вызвать проблемы.
668
Неделя 3
Разрешение ошибок без осшаноокн программы
Процедура DemoFatalError в листинге 18.1 просто прекращает работу сразу после представления номера ошибки, ее местонахождения в программе и соответствующего пояснения. Она не предпринимает ничего для разрешения этой ошибки и продолжения выполнения. Процедура DemoResolveError в листинге 18.2, однако, разрешает условие возникновения ошибки, вводя в таблицу квадратных корней мнимое значение корня (квадратный корень из отрицательного числа — мнимое число). DemoResolveError справляется с ошибкой времени выполнения самостоятельно и создает у пользователя впечатление, что все работает без проблем, разрешая условие, которое привело к этой ошибке, и используя инструкцию Resume Next для продолжения выполнения программы.
Листинг 18.2. Процедура ПвтиДввиквЕггог: нснользованцв инстрдкцин пвзнтв Next
1:	Option Explicit
2:
3:	Sub DemoResolveError!)
4:
5:	Const	tblHead	= "Таблица квадратных корней”
6:	Const	FIRST As Integer = 5
7:	Const	LAST As	Integer = -2
8:	Const	INCR As	Integer = -1
9:
10:	Dim	Count As Integer
11:	Dim	cRow As Integer
12:	Dim	oldSheet As String
14:	oldSheet = ActiveSheet.Name 'запоминаем лист
15:	Worksheets)"Sheetl").Select 'Переходим на новый
16:
17:	On Error GoTo BadValue 'Устанавливаем ловушку ошибки
IB:
19:	Cellsfl, 2).Value = tblHead 'Заголовки таблицы
20:	Cells(2, 1).Value = ’’ X”
21:	Cells(2, 2).Value = " Sqr(X)“
22:	Cells(3, 1).Value = String(35,
23:	Cells(3, 2).Value = String(35,
24:
25:	cRow = 4
26:	For Count = FIRST To LAST Step INCR
27:	Cells(cRow, 1).Value = Count
28:	CellsfcRow, 2).Value = Sqr(Count)
29:	cRow = cRow + 1
30:	Next Count
31:
32:	MsgBox Buttons:=vblnformation, Title:=tblHead, _
33:	рготрЬ:="Таблица квадратных корней построена."
34:
35:	Sheets(oldSheet).Select
36:	Exit Sub
37:
38:	BadValue:
39:	CellsfcRow, 2).Value = Sqr(Abs(Count)) & "i"
40:	Resume Next
41:
42:	End Sub
День 18. Обработка ошибок
669
Процедура DemoResolveError в листинге 18.2 отображает рабочий лист и диалоговое окно, показанные на рис. 18.2. Обратите внимание, что на сей раз процедура способна составлять таблицу квадратных корней даже для отрицательных чисел.
DemoResolveError работает точно так же, как и процедура из листинга 18.1, за исключением способа обработки ошибки времени выполнения и наличия вспомогательной части программы.
Г па Ди чпЫуЬ*	................ ...........................i.......
Дмвка йид Вставка Формат Сервис Данные £>кио Дправка	’ ’ _ |g| xt
Ё <?'	уг, о .	
>. (Anal	V 10 »|Х X 5**«1В
zi -_____________________________________________________________
, А В С D Е F G н 1 Z
;1 |	Таблица квадратных корней	4	(
X ; Sqr(X) '	'
з -----------------------------
4	5’2.236068
Таблица квадратных корней
6	3- 1,732051
7 „	2 1.414214 ij таблица квадратных корней построена I
»8Я	1, Г		**	, -1
9	°.',. '"prY"’’”
10	-111	•« lUr-n-i---n-Л
11 	-24,41421356237371------'-------------'------ ’
12
13
14
15
16	'	! -js
17	! 7.
М 4 > M\sheetl/sheet2Z5heet3/	|«|	1	>11
Готово	I " Т. T.fTTRjM! : ;
Рис. 18.2. Процедура DemoResolveError формирует таблицу квадратных корней даже для отрицательных значений
Как и ранее, в этой процедуре инструкция On Error GoTo в строке 17 устанавливает ловушку ошибок. Строки 19—23 создают заголовки столбцов таблицы, а строки 25— 30 заполняют таблицу. Обратите внимание, что на сей раз в операторах VBA нет никаких номеров строк: в этой процедуре функция Erl не используется, поэтому нет никаких причин для установки номеров строк.
Когда происходит неизбежная ошибка времени выполнения (передача функции Sqr отрицательного числа), ловушка ошибки инициирует переход к первому оператору после метки BadValue: в данном случае это строка 39. В строке 39 причина для возникновения ошибки времени выполнения обходится; здесь используется функция Abs для получения абсолютного значения переменной Count, а затем вычисляется ее квадратный корень. (Абсолютное значение числа — это значение числа независимо от его знака: например, абсолютное значение и 2, и -2 равно 2.) После вычисления квадратного корня абсолютного значения оператор в строке 39 конкатенирует (присоединяет) символ i к концу полученного числа для указания того, что это число является мнимым.
Важной является также строка 40. После вычисления мнимого квадратного корня из отрицательного числа в строке 39, инструкция Resume Next в строке 40 предписывает VBA продолжить выполнение инструкции, следующей сразу после оператора, кото
670
Неделя 3
рый вызвал ошибку времени выполнения: в данном случае это происходит в строке 29 (внутри цикла For). При этом возникает эффект разрешения процедуре продолжать выполняться так, будто ошибки времени выполнения никогда не происходило — и в некотором смысле это действительно так!
Поскольку эта процедура разрешает ошибку времени выполнения, вызванную передачей функции Sqr отрицательного числа в качестве аргумента, процедура фактически закончит выполнять цикл For в строках 26—30, а затем выполнит операторы в строках 32—36. В строке 32 выполняется оператор MsgBox, который позволяет сообщить пользователю о том, что формирование таблицы квадратных корней закончено, в строке 35 восстанавливается лист, который был активен перед началом выполнения этой процедуры, и в строке 36 даются инструкции по выходу из процедуры. Оставшиеся операторы в строках 38—40 процедуры — текст обработки ошибок; таким образом, процедура выполняет свою работу полностью и из нее можно корректно выйти.
Поошорное выполнение вызвавшего ошибку опврвшоро
Листинг 18.3 демонстрирует другой способ разрешить ошибку времени выполнения, которая происходит при формировании таблицы квадратных корней. При этом используется инструкция Resume 0 для повторения вызвавшего ошибку оператора после обработки сбойных значений переменной так, чтобы ошибки времени выполнения больше не происходило.
Листинг 18.3. Процедура 8втоНб1гуЕггвг: исиользооаиие инструкции Resume о удя повторения оператора, вызвавшего отиОку
1:	Option Explicit
2:
3:	Sub DemoRetryError()
4:
5:	Const tblHead = "Таблица квадратных корней"
6:	Const FIRST As Integer = 5
7:	Const LAST As Integer = -2
8:	Const INCR As Integer = -1
9:
10:	Dim	Count As Integer
11:	Dim	cRow As Integer
12:	Dim	oldSheet As String
13:	Dim	Temp As Integer
14:	Dim	tstr As String
15:
16:	oldSheet = ActiveSheet.Name 'Запоминаем лист
17:	Worksheets("Sheetl").Select 'Переходим на новый
18:
19:	On Error GoTo BadValue 'Устанавливаем ловушку ошибки
20:
21:	Cellsfl, 2).Value = tblHead 'Заголовки таблицы
22:	Cells(2, 1).Value = " X"
23:	Cells(2, 2).Value = " Sqr(X)”
24:	Cells(3, 1).Value = String(35, ”-")
25:	Cells(3, 2).Value = String(35, "-")
26:
27:	cRow = 4
28:	For Count = FIRST To LAST Step INCR
День 18. Обработка ошибок
671
29:	tStr =
30:	Temp - Count
31:	Cells(cRow,	1).Value	=	Temp
32:	Cells(cRow,	2).Value	=	CStr(Sqr(Temp)) & tStr
33:	cRow = cRow + 1
34:	Next Count
35:
36:	MsgBox Buttons:=vblnformation, Title:=tblHead,
37:	prompt:="Таблица квадратных корней построена."
38:
39:	Sheets(oldSheet).Select
40:	Exit Sub
41:
42:	BadValue:
43:	Temp =	-Temp
44:	tStr =	"i"
45:	Resume	0
46:	End Sub
Процедура DemoRetryError в листинге 18.3 формирует тот же рабочий лист и сообщение, что и ранее, как показано на рис. 18.2; однако для достижения тех же результатов применяется иная методика.
Листинг 18.3 так же, как и листинг 18.2, содержит несколько исключительных ситуаций. Сначала обратите внимание на добавление двух новых переменных, объявленных в строках 13 и 14. Переменная Тетр используется для временного хранения и работы со значением счетчика цикла. Переменная tStr, когда это необходимо, используется для добавления обозначения мнимых чисел /.
Как и прежде, имя текущего листа сохраняется прежним — Sheetl, ловушка ошибки установлена и заголовки столбцов внесены в рабочий лист.
Однако цикл For в строках 28—34 изменился. Обратите внимание, что в строке 29 переменная tStr устанавливается как пустая строка, а в строке 30 переменной Тетр назначается текущее значение Count. Строки 31 и 32, которые заполняют текущую строку таблицы квадратных корней, теперь, используют переменную Тетр вместо переменной счетчика цикла. Это сделано потому, что код обработки ошибок (строки 42 до 45) изменяет содержимое переменной, вызывающей ошибку времени выполнения. Если бы эта процедура фактически изменила значение самой переменной Count, цикл For не смог бы выполниться требуемое число раз.
Как только счетчик циклов становится отрицательным числом, происходит обычная ошибка времени выполнения. Как и прежде, VBA направляет выполнение программы к операторам после метки BadValue, поскольку в строке 19 установлена ловушка для перехвата ошибки. В строке 43 просто инвертируется (заменяется на обратный) знак числа, сохраняемого в переменной Тетр, и результат заносится обратно в переменную Тетр. В строке 44 переменной tStr присваивается значение i. В заключение, в строке 45 используется инструкция Resume 0, чтобы заставить VBA продолжать выполнение с того же оператора, который первоначально вызвал ошибку времени выполнения программы. Значение в Тетр теперь — положительное число (поскольку его знак был инвертирован в строке 43), так что функция Sqr в строке 32 не будет вызывать ошибки времени выполнения. Поскольку переменная tStr содержит теперь символ i, значение квадратного корня вводится в рабочий лист с соответствующим суффиксом, указывающим, что это — мнимое число. (В следующем цикле переменная tStr снова будет установлена как пустая строка.)
672
Неделя 3
Так не надо
НЕ изменяйте переменную счетчика цикла, если вы действительно не хотите изменить количество выполнений
Проуолжвпив выполнения с зауаппоа точки программы
Другой способ обработки ошибки времени выполнения состоит в том, чтобы игнорировать ее или пропускать операцию, которая приводит к ошибке. Эта методика может оказаться особенно полезной в циклических процедурах: вы можете просто игнорировать ошибку, возникшую во время выполнения программы, в предположении, что она не будет происходить на следующей итерации цикла. В примере, приведенном в листинге 18.4, используется именно эта методика для разрешения ошибки времени выполнения. Когда такая ошибка происходит из-за попытки найти квадратный корень из отрицательного числа, процедура DemoResumeError игнорирует эту ошибку и позволяет циклу продолжить выполнение.
Листинг 18.4. Процедура РетоИеаитеЕггог исвожьздет инструкции Resume Lohel
1:	Option Explicit
2:
3:	Sub DemoResumeError()
4:
5:	Const tblHead = "Таблица квадратных корней"
6:	Const FIRST As Integer = 5
7:	Const LAST As Integer = -2
8:	Const INCR As Integer = -1
9:	
10:	Dim Count As Integer
11:	Dim cRow As Integer
12:	Dim oldSheet As String
13:	Dim Temp As Double
14:	
15:	oldSheet = ActiveSheet.Name 'Запоминаем лист
16:	Worksheets("Sheetl”).Select 'Переходим на новый
17:	
18:	On Error GoTo BadValue	'Устанавливаем ловушку ошибки
19:	
20:	Cellsfl, 2).Value = tblHead 'Заголовки таблицы
21:	Cells(2, 1).Value = " X"
22:	Cells(2, 2).Value = " Sqr(X)"
23:	Cells(3, 1).Value = String(35, ”-")
24:	Cells(3, 2).Value = String(35, "-")
25:	
26:	cRow = 4
27:	For Count = FIRST To LAST Step INCR
28:	Temp = Sqr(Count)
29:	Cells(cRow, 1).Value = Count
30:	Cells(cRow, 2).Value = Temp
31:	cRow = cRow + 1
32:	ResumeLoop:
33:	Next Count
День 18. Обработка ошибок
673
34:
35:	MsgBox Buttons:=vblnformation, Title:=tblHead,
36:	prompt:="Таблица квадратных корней построена."
37:
38:	Sheets(oldSheet).Select
39:	Exit Sub
40:
41:	BadValue:
42:	Resume ResumeLoop
43:	End Sub
На рис. 18.3 представлен рабочий лист и диалоговое окно, отображаемые процедурой DemoResumeError из листинга 18.4. Обратите внимание, что таблица квадратных корней оканчивается на 0 и не содержит никаких отрицательных чисел.
Процедура DemoResumeError работает, по существу, так же, как и процедуры, с которыми вы уже познакомились на этом уроке, только методика обработки ошибок различна.
Обратите внимание, что в строке 13 переменная Тетр объявляется типа Double. Эта переменная используется для временного хранения вычисленного значения квадратного корня. Обратите также внимание, что результат функции Sqr в цикле For в строках 27— 33 назначается переменной Тетр. Если аргумент функции Sqr отрицателен, оператор в строке 28 вызывает ошибку времени выполнения. Обратите также внимание на добавление метки ResumeLoop в строке 32. Это отметка точки в программе, откуда выполнение цикла продолжается после возникновения ошибки времени выполнения.

|0ё*| Файл Правка Вид Вставка Формат Сервис Данные Йкно Справка
«и	< * г- й « ол ^,7.’
' Anal	» 10	, , Ж К 3 « 1 Я S ® X  ‘4 Д # _•

И
BCD
Таблица квадратных корней Sqr(X)
2
3
4
5
6
7
"з".
10
11
12
13
14
15 
16, 17
М. 4 >J Н\sheetl / Sheet2 /sheet3 /
5 2,236068
4	2
3, 1,732051
2 1,414214 1
О'
Таблица квадратных корней
Таблица квадратных корней построена/
ИГ
1 о
Е
G
Н
Рис. 18.3. Процедура DemoResumeError формирует таблицу квадратных корней, но игнорирует отрицательные числа, которые вызывают ошибки времени выполнения
674
Неделя 3
Когда происходит ошибка, VBA переводит выполнение программы к первому оператору после метки строки BadValue в соответствии с установленной в строке 18 ловушкой ошибки. Код обработки ошибок в этой процедуре состоит из единственной инструкции Resume ResumeLoop в строке 42. Когда VBA выполняет эту инструкцию, выполнение программы переходит к первому оператору после метки строки ResumeLoop: к оператору Next в строке 33. Цикл For... Next затем продолжает выполняться обычно, как будто никакой ошибки не произошло. Обработка ошибки таким образом приводит к простому игнорированию любых отрицательных чисел в таблице квадратных корней, разрешая процедуре завершить выполнение операций без прерывания.
Принудительное создаппе отпбкп
В этом заключительном примере используется метод Raice объекта Err для генерирования ошибки времени выполнения прежде, чем она фактически произойдет при обращении к функции Sqr. Подобную методику вы можете использовать также с определяемыми пользователем кодами ошибок.
Листинг 1 В.5. Процедура DemoForcedError: исвользоваиие методе Raice оОъвкта Err цля принудительного создания отиОки оремеии выполнения
1:	Option Explicit
2:	Option Base 1
3:
4:	Const rtBadProcedureCall As Integer = 5
5:	Const BINCOMP As Integer = 0
6:	Const StrCmpLess As Integer = -1
7:	Const StrCmpGreat As Integer = 1
8:
9:
10:	Sub DemoForcedError()
11:
12:	Const dTitle = "Forced Error"
13:
14:	Dim	oldSheet As	String
15:	Dim	strArray(6)	As String
16:	Dim	tmpArray As	Variant
17:	Dim	intArray(6)	As Integer
18:	Dim	Count As Integer
19:	Dim	Ans As Integer
20:	Dim	ErrLine As Long
21:
22:	On Error GoTo rtError	'Устанавливаем ловушку ошибки
23:
24:	oldSheet = ActiveSheet.Name 'Запоминаем лист
25:	Worksheets)"Sheetl").Select 'Переходим на новый
26:
27:	Cells)1, 2).Value = dTitle
28:
29:	strArray(l)	=	"a": strArray(2)	= "b": strArray(3)	=	"c"
30:	strArray(4)	=	"A": strArray(5)	» "B": strArray(6)	=	"C"
31:
32:	intArray(l)	=	2: intArray(2) =	6: intArray(3) = 4
33:	intArray(4)	=	5: intArray(5) =	1: intArray(6) = 3
День 18. Обработка ошибок
675
34:
35:	Testi:
36:	tmpArray = strArray
37:	1 SortStringList List:=tmpArray, Descending:“True
38:	For Count = 1 To UBoundftmpArray)
39:	Cells(Count + 1, 1).Value = tmpArray(Count)
40:	Next Count
41:
42:	Test2:
43:	tmpArray = strArray
44:	2 SortStringList List:“tmpArray
45:	For Count = 1 To UBoundftmpArray)
46:	Cells(Count + 1, 2).Value = tmpArray(Count)
47:	Next Count
48:
49:	Test3:
50:	Ans “ MsgBox) Buttons :=vbQuestion + vbYesNo,
51:	Title:“dTitle,
52:	prompt:“"Передать неверный аргумент направления?")
53:	If Ans = vbYes Then
54:	tmpArray “ strArray
55:	3 SortStringList List:=tmpArray, Descending:="True"
56:	For Count = 1 To UBound(tmpArray)
57:	Cells(Count + 1, 3).Value = tmpArray(Count)
58:	Next Count
59:	End If
60:
61:	Test4:
62:	Ans = MsgBox(Buttons:=vbQuestion + vbYesNo,
63:	Title:“dTitle,
64:	prompt:“"Передать неверный аргумент массива?")
65:	If Ans = vbYes Then
66:	tmpArray = intArray
67:	4 SortStringList List:“tmpArray
68:	For Count = 1 To UBound(tmpArray)
69:	Cells(Count + 1, 4).Value = tmpArray(Count)
70:	Next Count
71:	End If
72:
73:	AfterTests:
74:	MsgBox Buttons:=vblnformation, Title:“dTitle,
75:	prompt:“"Сортировка закончена."
76:	Sheets(oldSheet).Select
77:	Exit Sub 'конец работы
78:
79:	rtError: 'Обработка ошибок
80:	ErrLine = Erl
81:	MsgBox Buttons:=vbCritical, Title:“dTitle,
82:	prompt:“"Ошибка! " & Err.Number & vbCr &
83:	Err.Description & " в " &
84:	Err.Source
85:	Select Case ErrLine
86:	Case Is = 3
676
Неделя 3
87:	Resume	Test4
88:	Case Is = 4
89:	Resume	AfterTests
90:	Case Else
91:	MsgBox	Buttons:=vbCritical,	Title:=dTitle,
92:	prompt:«“Продолжение невозможно!”
93:	End Select
94:	End Sub
95:
96:
97:	Sub SortStringList(ByRef List As Variant,
98:	Optional Descending)
99:	'Сортирует по возрастанию или по убыванию,
100:	'в зависимости от значения аргумента Descending
101:
102:	Dim uCount As Integer, dCount As Integer
103:	Dim CmpResult As Integer
104:
105:	If IsMissing(Descending) Then Descending = False
106:	If TypeName(Descending) <> "Boolean" Then
107:	Err.Raise Number:=rtBadProcedureCall,
108:	Description:«“Неверный тип аргумента Descending",
109:	Source:="SortStringList"
110:	End If
111:
112:	If TypeName(List) о "StringO" Then
113:	Err.Raise Number:=rtBadProcedureCall,
114:	Description:="HeBepHbift тип аргумента List",
115:	Source:="SortStringList"
116:	End If
117:
118:	For uCount = LBound(List) To UBound(List) - 1
119:	For dCount = uCount + 1 To UBound(List)
120:	CmpResult = StrComp(List(uCount), List(dCount), BINCOMP)
121:	If Descending Then
122:	If	CmpResult	=	StrCmpLess Then
123:	Swap List(uCount), List(dCount)
124:	Else
125:	If	CmpResult	-	StrCmpGreat Then	_
126:	Swap List(uCount), List(dCount)
127:	End If
128:	Next dCount
129:	Next uCount
130:	End Sub
131:
132:	Sub Swap(Il As Variant, 12 As Variant)
133:	Dim Temp As Variant
134:	Temp = II: Il « 12: 12 = Temp
135:	End Sub
День 18. Обработка ошибок
677
Листинг 18.5 демонстрирует практический способ использования метода Raise объекта Err для того, чтобы сделать поведение ваших собственных процедур и функций похожим на поведение встроенных процедур и функций VBA, вынуждая появление ошибки времени выполнения. К настоящему времени вы уже должны знать, что встроенные процедуры и функции VBA генерируют ошибки времени выполнения, если вы передаете аргумент неправильного типа или с недопустимым значением. Эти ошибки могут помочь вам определить ваши собственные ошибки программирования. Навязывая подобное поведение вашим собственным процедурам — особенно если вы предполагаете распространять их среди других пользователей, — вы можете сделать их более простыми в использовании.
Листинг 18.5 содержит несколько процедур. Процедура DemoForcedError (строки 10—94) просто устанавливает переменные, необходимые для проверки процедуры SortStringList, и затем вызывает эту процедуру несколько раз с допустимыми и недопустимыми аргументами. Рис. 18.4 иллюстрирует рабочий лист и диалоговое окно, отображаемые процедурой DemoForcedError после выполнения третьего теста процедуры SortStringList.
> hi Г «г н!	ЬЬ.
Фейп Правка .Вид Вставка Формат Сервис Денные Дкно Справка
Ba a	ча е	2 f. л л о юо%
[Anal	.10 -XKSSJiffi®
А
D
Е

9
i_E
2 ,
4 а
5 :
6 =
7 А
9
11
12
13
/14_",
15
is;
17
К 4 ►
В С
_ Forced Error
“ А В С
.а 0 с
Г о!
Forced Error
гЯ
Ошибка! 5	‘	.
Неверный тип аргумента Descending е SortStringList
Н \sheetl /sheet2 /sheets /
I ИГ
NUM
F
G
Рис. 18.4. Процедура DemoForcedError отображает это сообщение, когда пробует проверить работу процедуры SortStringList с недопустимым аргументом
Процедура SortStringList (строки 97—130) является реализацией сортировки методом пузырька, разработанным для сортировки массивов строк в порядке возрастания или убывания (т.е. в алфавитном или обратном порядке).
Процедура SortStringList имеет два аргумента. Первый, обязательный аргумент имеет тип Variant; SortStringList ожидает, что он будет содержать массив строк. Второй, необязательный аргумент также имеет тип Variant; SortStringList ожидает, что этот аргумент будет значением типа Boolean, указывающим, должен ли список сорти
678
Неделя 3
роваться в порядке по возрастанию или по убыванию. Если какой-либо аргумент не имеет ожидаемого типа, процедура SortStringList использует метод Raise объекта Err, чтобы вынудить VBA генерировать ошибку времени выполнения. В следующих нескольких абзацах этого раздела мы подробнее рассмотрим, как работает программа в листинге 18.5.
Листинг 18.5 содержит полный программный модуль. Обратите внимание на директиву Option Base 1 в строке 2, которая присваивает всем массивам в этом модуле начальный нижний индекс, равный 1. Строки 4—7 объявляют некоторые константы уровня модуля. Строка 4 объявляет константу, которая будет содержать значение ошибки времени выполнения; в строке 5 объявляется константа для определения типа сравниваемых строк; строки 6 и 7 объявляют константы, представляющие некоторые из значений, возвращаемых функцией StrComp.
Процедура DemoForcedError начинается в строке 10. В строках 12—20 объявляются константы и переменные. Обратите внимание, что в строке 15 объявляется массив строк, в строке 16 объявляется переменная типа Variant, а в строке 17 объявляется целочисленный массив.
В строке 22 устанавливается ловушка для обработки ошибок в процедуре DemoForcedError, строки 24 и 25 сохраняют имя текущего листа и переключают этот активный лист на лист Sheetl, а строка 27 вводит заголовок в рабочий лист. Строки 29 и 30 заполняют массив строк, а строки 32 и 33 заполняют целочисленный массив некоторыми тестовыми значениями. Обратите внимание, что эти строки содержат многократные операторы, отделяемые друг от друга двоеточием (:).
Строки 35-40 формируют первый тест процедуры SortStringList. Сначала массив строк приводится к переменной tmpArray типа Variant. Поскольку переменные типа Variant могут хранить любой тип данных, в результате выполнения операции назначения в строке 36 в переменной tmpArray должна сохраниться копия массива строк.
В строке 37 происходит обращение к процедуре SortStringList путем передачи переменной tmpArray как массива, который нужно отсортировать, и определения порядка сортировки по убыванию. Обратите внимание, что эта строка также имеет свой собственный номер: это — строка номер 1 (поскольку это первая строка, в которой проверяется процедура SortStringList). После того как массив отсортирован, в строках 38-40 начинает выполняться цикл For, который отображает сортируемый массив в первом столбце рабочего листа. Поскольку оба аргумента имеют правильный тип, этот тест процедуры SortStringList не приводит ни к каким ошибкам времени выполнения.
Строки 42—47 содержат второй тест процедуры SortStringList. Этот тест работает точно так же, как первый тест, за исключением того, что вызов процедуры SortStringList осуществляется без второго необязательного аргумента. В процедуре SortStringList предполагается, что, если направление сортировки не определено, список должен сортироваться в порядке возрастания. Поскольку второй аргумент необязателен (и может быть опушен), а первый аргумент имеет корректный тип данных (массив строк), это обращение к SortStringList также не приводит ни к каким ошибкам времени выполнения. Обратите внимание, что строка 44, в которой вызывается процедура SortStringList, имеет номер строки: это — строка номер 2, поскольку это — второе обращение к SortStringList.
Строки 49—59 содержат третий тест процедуры SortStringList. Пользователя сначала спрашивают, хочет ли он выполнять этот тест; если пользователь щелкает на кнопке Yes (Да), тест процедуры SortStringList выполняется. В строке 55 происходит обращение к процедуре SortStringList с преднамеренной передачей строкового значения в качестве необязательного аргумента Descending. Поскольку этот аргумент должен быть значением типа Boolean, а не строковым значением, процедура SortStringList генерирует ошибку времени выполнения, что приводит к вызову моду
День 18. Обработка ошибок	679
ля обработки ошибок процедуры DemoForcedError. (Подробнее работа процедуры SortStringList описана ниже). Обратите внимание, что строка 55, в которой вызывается SortStringList, имеет номер строки 3, поскольку это — третья строка, из которой вызывает процедура SortStringList.
Строки 61—71 содержат четвертый и заключительный тест процедуры SortStringList. Пользователю вновь направляется запрос, выполнять ли тест. Если так, то оператором в строке 67 вызывается SortStringList. Обратите внимание, что оператор в строке 66 назначает целочисленный массив переменной tmpArray. Когда SortStringList вызывается в строке 67, аргумент List оказывается целочисленным массивом, а не массивом строк. Поскольку этот аргумент имеет неправильный тип данных, SortStringList естественным образом генерирует ошибку времени выполнения.
Строки 73—77 программы DemoForcedError отображают сообщение, информирующее пользователя о том, что тестирование завершено, и восстанавливают первоначальный лист. В строке 77 осуществляется выход из процедуры, поскольку больше нет работы, которую нужно было бы выполнять, а текст модуля обработки ошибок в конце процедуры должен быть пропущен; поэтому проще всего закончить процедуру именно в этой точке.
Строки 79—93 содержат операторы обработки ошибок процедуры DemoForcedError. Когда происходит ошибка времени выполнения, VBA передает управление первому оператору после метки rtError в соответствии с ловушкой ошибок, установленной инструкцией On Error GoTo в строке 22. Сначала в строке 80 в переменной ErrLine сохраняется результат вызова функции Erl. Затем в строках 81-84 оператор MsgBox информирует пользователя об ошибке времени выполнения, сообщая точный числовой код ошибки, ее описание и номер строки текста программы, в которой произошла ошибка. Строки 85-93 содержат оператор Select Case, который выбирает соответствующую точку для продолжения процедуры DemoForcedError в зависимости от того, в какой строке произошла ошибка времени выполнения. Если ошибка произошла в третьем тесте, процедура продолжается с начала четвертого теста; если же ошибка произошла в четвертом тесте, процедура продолжается с точки в конце тестов.
ПРИМЕЧАНИЕ
Попробуйте в порядке эксперимента заменить оператор Select Саве в строках 85-93 листинга 18.5 единственной инструкцией Resume Next и понаблюдайте, как изменится поведение процедуры DemoForcedError.
Строки 97-130 содержат процедуру SortStringList. В строках 102-103 объявляются переменные, используемые SortStringList. В строке 105 проверяется, был ли при обращении использован необязательный аргумент Descending; если тот аргумент отсутствует, по умолчанию устанавливается значение False, так что список будет сортироваться в порядке возрастания.
В строке 106 происходит проверка типа данных аргумента Descending с использованием функции TypeName. Если тип аргумента Descending не Boolean, в строке 10 используется метод Raise объекта Err для вызова ошибки времени выполнения: в этом случае ошибка определяется константой rtBadProcedureCall (которая хранит числовой код ошибки, называемой Недопустимый вызов процедуры). Чтобы сделать появляющееся в результате сообщение об ошибках настолько ясным, насколько это возможно, в методе Raise используется два необязательных аргумента Description и Source: аргумент Description передает строку, которая в точности описывает обнаруженную проблему, а аргумент Source передает строку с точным описанием процедуры, в которой произошла ошибка.
Из-за принудительной ошибки во время выполнения программы VBA немедленно останавливает выполнение процедуры SortStringList и возвращается к процедуре
680
Неделя 3
DemoForcedError. После этого VBA устанавливает внутренние механизмы ошибки для указания того факта, что был сделан недопустимый вызов процедуры, и прерывает выполнение процедуры DemoForcedError. Поскольку процедура DemoForcedError содержит установленную ловушку ошибки, она обрабатывает ошибку времени выполнения непосредственно.
ПРИМЕЧАНИЕ
В порядке эксперимента удалите инструкцию On Error GoTo из строки 22 в листинге 18.5 и посмотрите, что случится после вызова процедуры SortStringList с недопустимыми аргументами.
Если аргумент Descending имеет правильный тип, выполнение продолжается со строки 112, в которой также используется функция TypeName для проверки типа аргумента List. Если аргумент List — массив строк, функция TypeName в качестве результата возвращает строку String (). Если аргумент List не является массивом строк, выполняется вызываемый в строке 113 метод Err.Raise, вынуждая возникновение ошибки времени выполнения. Этот оператор производит тот же эффект, что и строка 107: SortStringList завершается и об ошибке времени выполнения сообщается в контексте процедуры, которая вызвала SortStringList (в данном случае процедуры DemoForcedError). Оставшаяся часть процедуры SortStringList, как мы уже говорили, выполняет пузырьковую сортировку.
Строки 132-135 содержат процедуру Swap, используемую SortStringList. Вы уже знаете, как работает эта процедура.
Резюме
На этом уроке мы рассмотрели механизмы, используемые для перехвата ошибок и их обработки в ваших процедурах VBA. Вы узнали, что существует две основные стратегии обработки ошибок: предупредительное программирование и перехват ошибок. Первая стратегия обнаруживает условия, которые вызывают ошибку времени выполнения, и предпринимает необходимые шаги во избежание этой ошибки. Перехват ошибки заключается в установке ловушки для ошибки (с использованием инструкции On Error GoTo), которая предписывает VBA автоматически выполнять переход к некоторой части вашей процедуры, которая обычно содержит модуль для обработки перехваченной ошибки без остановки выполнения процедуры.
На этом уроке мы также выяснили, как использовать инструкцию On Error GoTo для установки ловушек ошибок. Вы узнали, что в инструкции On Error GoTo используется метка строки для определения места в вашей процедуре, к которому VBA переключает выполнение программы, если происходит ошибка времени выполнения. Теперь вы знаете, как использовать инструкцию Resume для определения того, с какого места вашей процедуры необходимо продолжить выполнение после обработки перехваченной ошибки. Ваш модуль обработки ошибок будет заканчиваться, как правило, инструкцией Resume некоторого вида.
На этом уроке вы научились также использовать функции Err, Erl и Error для выяснения числового кода конкретной ошибки времени выполнения, номера строки оператора, который вызвал ошибку, и вызова соответствующего текста сообщения об этой ошибке. Вы также изучили, как использовать инструкцию Error, чтобы спровоцировать (вынудить) возникновение ошибки времени выполнения либо с предопределенным VBA кодом ошибки, либо с кодом ошибки, который вы определяете сами.
В заключение мы рассмотрели, как использовать свойства и методы объекта Err для получения информации об ошибках времени выполнения и для искусственного
День 18. Обработка ошибок
681
создания ошибок либо с предопределенным VBA кодом, либо с определенным пользователем кодом ошибки.
Этот урок мы завершим несколькими примерами написания операторов обработки ошибок и использования объекта Err.
Вопросы о ответы
В какой точке моей процедуры я должен установить ловушку ошибки с помощью инструкции On Error GoTo?
Ловушка ошибки обычно устанавливается в начале вашей процедуры, особенно если она предназначается для обработки любой ошибки, которая могла бы произойти во время выполнения процедуры. В то же время вы можете установить ловушку ошибки непосредственно перед любым склонным к ошибке оператором — ваша процедура может содержать и более одной инструкции On Error GoTo с метками, указывающими различные операторы обработки ошибок. Тем не менее, вообще говоря, рекомендуется использовать только одну инструкцию On Error GoTo.
Что случится, если в результате выполнения одного из операторов в коде обработки ошибок моей процедуры произойдет другая ошибка времени выполнения?
VBA остановит выполнение кода в вашей процедуре и отобразит сообщение о вновь возникшей ошибке, поскольку ваша процедура не предусматривает обработку такой ошибки до тех пор, пока вы не сбросите внутренние механизмы реакции VBA на ошибки времени выполнения с помощью инструкции Resume.
Могу ли я использовать инструкцию On Error GoTo в моем коде обработки ошибок, чтобы предусмотреть появление любых дополнительных ошибок во время обработки моим кодом первой ошибки?
Можете, но сначала вы должны использовать инструкцию Resume, чтобы очистить внутренние механизмы VBA обработки ошибок времени выполнения, либо использовать метод Clear объекта Err. Ниже приведена обычная методика программирования вложенного обработчика ошибки.
FirstError:
'Операторы записи номера ошибки и т.д.
Resume NextLinel
NextLinel:
'Установка второго обработчика ошибки
On Error GoTo SecondError
'Операторы, которые обрабатывают первую ошибку
GoTo ReturnLabel
SecondError:
'Операторы обработки второй ошибки
Resume ReturnLabel
Обратите внимание, что первая инструкция Resume предписывает продолжение выполнения просто с метки следующей строки. Этот прием программирования удовлетворяет требованиям VBA по обработке первой ошибки. Инструкция On Error GoTo затем устанавливает внутренний обработчик ошибки.
Что случится, если для обработки ошибки я использую только инструкцию Resume О?
Ваша процедура войдет в бесконечный цикл, поскольку ошибка фактически не разрешается и вызвавший ошибку оператор не пропускается. Когда в первый раз про
682
Неделя 3
изойдет ошибка времени выполнения, VBA выполнит инструкцию Resume 0 в вашем коде обработки ошибок, вернется к сбойному оператору, и та же ошибка времени выполнения произойдет снова. Только используйте инструкцию Resume 0 после того, как ваш код обработки ошибок устранил условия, вызвавшие ошибку времени выполнения (изменены значения переменных, рабочий лист или рабочая книга заменены другими и т.п.).
Коллоквиум
Ответы приведены в приложении.
Тест
1.	Какую инструкцию следует использовать, чтобы заставить вашу процедуру продолжать выполнение с оператора, следующего за тем, который вызвал ошибку времени выполнения?
2.	Что случится, если произойдет новая ошибка в то время, когда ваша процедура все еще обрабатывает предыдущую ошибку времени выполнения?
3.	Может ли процедура иметь множество наборов операторов обработки ошибок?
4.	Какую инструкцию можно использовать, чтобы заставить вашу процедуру продолжать выполнение с того же оператора, который вызвал ошибку времени выполнения?
5.	Как можно отключить вашу ловушку ошибок?
6.	Какой оператор (инструкция) очищает внутренние механизмы VBA обработки ошибок времени выполнения? Какой метод объекта Err очищает внутренние механизмы VBA обработки ошибок?
7.	Какая функция возвращает текст сообщения об ошибках в соответствии со специфическим кодом ошибки время выполнения? Какое свойство объекта Err возвращает текст сообщения об ошибках в соответствии с кодом ошибки?
8.	С помощью какого оператора (инструкции) можно искусственно вызвать ошибку времени выполнения? Какой метод объекта Err принудительно вызывает ошибку?
Упражнения
1. Напишите процедуру под названием MyErrorHandler, которая захватывает и обрабатывает ошибку время выполнения, происходящую при вызове функции Mid с недопустимыми индексами. (Функция Mid возвращает указанную часть строки (подстроку).) Ниже приведен псевдокод процедуры MyErrorHandler.
1.	Установить ловушку ошибки.
2.	Запросить пользователя ввести строку.
3.	Запросить пользователя ввести начальный индекс.
4.	Запросить пользователя ввести число символов, которые следует извлечь из строки.
5.	Вызвать функцию Mid и результат назначить переменной.
6.	Отобразить первоначальную строку и извлеченную подстроку.
7.	Закончить процедуру.
День 18. Обработка ошибок
683
Функция Mid приводит к ошибке времени выполнения программы всегда тогда, когда ее аргументы нулевые или отрицательные. Ваша процедура должна обработать эту ошибку в соответствии со следующим псевдокодом:
1,	Назначить пустую строку переменной для извлеченной подстроки.
2.	Отобразить сообщение об ошибке.
3.	Продолжить выполнение оператора, следующего сразу за тем, который вызвал ошибку времени выполнения.
2.	Что отображается в диалоговом окне после выполнения следующих операторов?
Dim S As String
Dim L As Integer
On Error GoTo RunTimeError
S = "Hello" L = 0
Mid(S, L, 1) = "J”
MsgBox S
GoTo Endit
RunTimeError:
Resume Next
Endit:
3.	Какое сообщение отображается в диалоговом окне после того, как выполнятся следующие операторы?
Dim S As String
Dim L As Integer
On Error GoTo RunTimeError
S = "Hello"
L = 0
Mid(S, L, 1) = "J"
MsgBox S
GoTo Endit
RunTimeError:
L = 1
Resume 0
Endit:
4.	Что отобразится в диалоговом окне после того, как выполнены следующие операторы? Что вам будет сообщено о количестве макровызовов операторов обработки ошибок?
Dim S As String
Dim L As Integer
Dim Count As Integer
Count = 0
On Error GoTo RunTimeError
S = "Hello"
L = -10
Mid(S, L, 1) = "J"
MsgBox S & vbCr & vbCr & Count
684
Неделя 3
GoTo Endit
RunTimeError:
Count = Count + 1
L = L + 1
Resume 0
Endit:
5.	Что будет отображено в диалоговом окне после выполнения следующих операторов?
Dim S As String
Dim L As Integer
On Error GoTo RunTimeError
S = "Hello"
L = 0
MidfS, L, 1) = "J"
MsgBox S
GoTo Endit
RunTimeError:
MsgBox "Номер ошибки времени выполнения " & Err
Endit:
День 18. Обработка ошибок
685
Управление приложением Excel с помощью VBA
Как вы уже знаете, язык программирования Visual Basic for Application разработан преимущественно для работы с приложениями и для расширения их возможностей. VBA доступно множество различных объектов Excel. Свойства и методы этих объектов позволяют управлять из программ VBA почти всеми аспектами функционирования Excel. На сегодняшнем уроке мы рассмотрим, как манипулировать наиболее важными объектами в Excel 97 и Excel 2000 (которые известны также как Excel 8 и Excel 9 соответственно).
•	Работа с объектами Workbook и Worksheet.
•	Использование объектных методов Excel, которые возвращают объекты Range, включая определение и выделение диапазона рабочего листа.
•	Ввод значений и формул в ячейки рабочего листа Excel.
•	Вставка, вырезание и копирование данных в рабочих листах Excel.
Работа с объектами Workbook
В Excel 97 представлен новый формат рабочей книги; этот новый формат является расширением формата рабочей книги, использовавшегося ранее в Excel 5 и Excel 7. Новый формат используется и в Excel 97, и в Excel 2000. Основные различия между форматом рабочей книги Excel 97/2000 и форматом его двух предыдущих версий состоит в способе сохранения ваших VBA-проектов в файле рабочей книги, а также в том, что события, связанные с рабочей книгой или листом, могут быть отображены в VBA; вы можете создавать процедуры, которые Excel будет выполнять автоматически всякий раз, когда происходит определенное событие, такое как открытие рабочей книги, ее активизация, закрытие и т.п. Более подробно процедуры обработки событий рассматриваются на 21-м уроке, а на этом уроке мы обсудим основные операции использования объектов Excel.
Формат рабочей книги Excel 97/2000 позволяет решать общие задачи крупноформатных таблиц значительно быстрее и проще. Excel располагает множеством доступных из VBA объектов, методов и свойств, которые позволяют вам выполнять основные рутинные задачи в ваших рабочих книгах программным путем. Следующие несколько разделов представляют некоторые наиболее общие объекты, методы и свойства.
686
Неделя 3
Обращение к объекту Workbook
В Excel каждая рабочая книга является объектом Workbook, a Workbooks является коллекцией всех открытых рабочих книг в текущем сеансе Excel. Чтобы обратиться к конкретной рабочей книге, следует воспользоваться методом Workbooks объекта Application, имеющим следующий синтаксис.
Workbooks (Index)
Здесь индекс Index представляет собой одно из двух выражений.
•	Числовое выражение, характеризующее рабочую книгу, с которой вы хотите работать. Значение 1 определяет первую открытую в данном сеансе рабочую книгу, 2 — вторую открытую в данном сеансе книгу и т.д.
•	Строковое выражение, содержащее имя открытой рабочей книги, которую вы хотите использовать.
Чаще всего — и это рекомендуется из соображений наглядности — в качестве Index используется текстовая строка. Листинг 19.1 демонстрирует соответствующий пример.
Листинг 1Я.1. Использование коллекции Workbooks для обращения к рабочей книге
1:	Sub SetWorkbookProtection()
2:	'Устанавливает защиту книги
3:
'4:	Workbooks) "День19.ХЬ5") .Protect Structure:=True, Windows:=True
5:	MsgBox "Установлена зашита книги fleHbl9.XLS.“
6: End Sub
Выражение в строке 4, Workbooks("День19.ХЬЗ"), возвращает ссылку на объект рабочей книги День19.х1з. Метод Protect устанавливает защиту структуры и окон рабочей книги. Инструкция MsgBox в строке 7 отображает сообщение о том, что защита этой рабочей книги была активизирована. (Это предполагает, что рабочая книга под названием День19.х1з в данный момент открыта; в противном случае вы получите ошибку времени выполнения.)
Так надо
Для ссылки на активную рабочую книгу используйте ActiveWorkbook.
. Для ссылки на рабочую книгу, которая содержит выполняемую в данный момент процедуру, используйте ThisWorkbook, особенно если вы предполагаете преобразовать свою программу в надстройку Excel.
Так не надо
Не используйте без особой необходимости числа в качестве аргумента Index метода Workbooks. Понятно, что такая инструкция как Workbooks!"ДЕНЫЭ.ХЬЗ"), оказывается более ясной, чем, скажем, Workbooks(2). Применение текстового имени для рабочей книги является также более аккуратным стилем программирования. Если рабочие книги в текущем сеансе Excel открываются не только из вашей программы, вы не можете быть уверены в том, что книга День 19.xls в действительности является второй рабочей книгой.
День 19-й. Управление приложением Excel с помощью VBA
687
Открытие рабочеО книги
Если нужная вам рабочая книга не открыта, для ее загрузки в память используется метод Open.
Workbooks.Open(Filename)
Здесь аргумент Filename представляет собой строку, описывающую полный путь к рабочей книге: устройство (жесткий диск), папка и имя файла. Если же вы не укажете имени диска или папки, Excel будет искать файл с вашей книгой в текущей папке на текущем диске. В листинге 19.2 показано использование метода Open.
Листинг 18.2. Использование метона Open для открытая раОочеП книги
1:	Sub OpenWorkbook()
2:	'Открывает книгу
3:
4:	Dim WorkbookName As String
5:
6:	WorkbookName =
7:	InputBox("Укажите полное имя книги:")
8:	If WorkbookName	о ""	Then
9:	Workbooks.Open Filename:=WorkbookName
10:	End If
11:	End Sub
В этой процедуре переменная с именем WorkbookName объявлена как String (в строке 4), а функция InputBox приглашает пользователя ввести имя файла открываемой рабочей книги (в строках 6-7). Инструкция If... Then в строке 8 проверяет, не отменил ли пользователь ввод имени. Если не отменил — другими словами, если переменная WorkbookName непустая, — метод Open использует значение WorkbookName для открытия файла (в строке 9).
Так надо
Если вы хотите, чтобы пользователь сам выбрал открываемый файл, используйте встроенное в Excel диалоговое окно Open. Эго окно можно открывать либо с помощью метода GetOpenFilename (который мы рассматривали на 12-м уроке), либо с помощью коллекции Dialog, как показано ниже (эта коллекция содержит все * диалоговые окна, встроенные в Excel).
: Application.Dialogs(xlDialogOpen).Show
Так не надо
Не теряйтесь, если вы собираетесь позволить пользователю открывать рабочую книгу только для чтения или хотите защитить ее паролем. Метод Open имеет до 13 аргументов, которые позволяют предусмотреть все возможные ситуации. Чтобы увидеть все аргументы метода Open и получить справку по их заданию, используйте окно Object Browser.
688
Неделя 3
Создание рабочей книги
Если в вашей процедуре предусматривается создание новой рабочей книги, используйте метод Add коллекции Workbooks, который выполнит за вас эту работу.
Workbooks.Add ([Template])
Необязательный аргумент Template определяет вид создаваемой Excel рабочей книги. Если вы опускаете этот аргумент, Excel по умолчанию создает рабочую книгу с некоторым количеством чистых рабочих листов; количество таких листов в новой рабочей книге определяется на вкладке Общие в диалоговом окне Параметры (которое появляется после выбора команд Сервис-Параметры). Количество листов во вновь создаваемой рабочей книге можно также задать, установив свойство Application.SheetsInNewWorkbook.
Для создания новой рабочей книги на основе существующего шаблона в качестве аргумента Template передается строковое выражение, определяющее имя такого шаблона. Если же шаблон рабочей книги, который вы намерены использовать, отсутствует в Excel или находится в другой директории, включите в аргумент Template полный путь к нему — имена диска, папки и файла с этим шаблоном.
Для создания рабочей книги, содержащей единственный рабочий лист, в качестве аргумента Template используется одна из следующих внутренних констант (эти константы определены в классе xlWBATemplate): xlWBATWorksheet, xlWBATChart, xlWBATExcel4MacroSheet или xlWBATExcel4IntlMacroSheet.
Листинг 19.3 представляет собой пример процедуры Add для создания новой рабочей книги.
Листинг 18.3. Использпоание метода add для создания идвпП раОочЕД книги
1:	Sub CreateMonthlyReport()
2:	'Создает новую книгу на основе шаблона Excel
3:
4:	Dim dPath As String
5:
6:	dPath = "C:\Program Files\Microsoft	Office"
7:	dPath = dPath & "\Templates\1033\"
8:	Workbooks.Add Template:=dPath & "Expense	Statement"
9:	With ActiveWorkbook
10:	.Title = "Expenses for Joseph P. Doakes"
11:	.Subject = "Expenses for January 1999"
12:	.Author = "Lisa Simpson"
13:	End With
14:	End Sub
В программе листинга 19.3 новая рабочая книга создается по шаблону Expense Statement, поставляемому с Excel и Microsoft Office 2000 (в строке 8). (Использованный в этом примере путь к шаблону рабочей книги в Microsoft Office 2000 предполагает стандартную установку — в действительности ваш файл шаблона может располагаться в другом месте.) Любая только что созданная рабочая книга автоматически становится активной книгой. В строках 9—13 используется объект ActiveWorkbook для помещения в файл новой рабочей книги некоторой информации о свойствах: название, предмет и автор.
День 19-й. Управление приложением Excel с помощью VBA
689
Активизация рабочей книги
Если ваша VBA-программа предусматривает одновременное открытие нескольких рабочих книг, вам может понадобиться переключаться от одной книги к другой, чтобы просмотреть, например, отчет или экран с выходными данными. Чтобы сделать рабочую книгу активной, используется метод Activate.
Object.Activate
В листинге 19.4 приведен пример процедуры, использующей метод Activate.
Листинг 13.4. Исидяьзооаиие метода Activate ря перехода к другой рдДочеЦ книге
1:	Sub ActivateTestf)
2:	'Делает активными книги, не меняя при этом экрана.
3:
4:	Dim SaveBook As String
5:
6:	SaveBook = ActiveWorkbook.Name
7:
8:	Application.Screenupdating = False
9:	Workbooks("DATA.XLS").Activate
10:
11:	'Здесь разместите инструкции, меняющие книгу DATA.XLS
12:
13:	Workbooks (SaveBook). Activate
14:	Application.Screenupdating = True
15:	End Sub
Этот пример процедуры демонстрирует два принципа хорошего программирования.
•	По мере возможности скрывайте от пользователя промежуточные операции, выполняемые вашей программой. Например, если ваша процедура содержит инструкции форматирования диапазонов и ввода данных, выполняйте эти задачи в фоновом режиме. Представляйте пользователю только окончательно сформированный экран. Присвойте для этого значение False свойству Screenupdating объекта Application.
•	Если вы активизируете рабочую книгу потому, что это предусмотрено вашей программой — но не для того, чтобы пользователь увидел другой файл, — ваша процедура должна всегда возвращать пользователя туда, откуда он стартовал. Нет ничего более обескураживающего, особенно для начинающих пользователей, чем запустить процедуру в одной рабочей книге и по непонятной причине закончить ее в другой рабочей книге.
Цель процедуры в листинге 19.4 состоит в переключении от текущей рабочей книги к другой книге (называемой Data.xls), чтобы внести в нее некоторые предусмотренные программой изменения, а затем вернуться к первоначальной рабочей книге. Сначала (в строке 4) сохраняется имя текущей активной рабочей книги в строковой переменной SaveBook. Далее, чтобы оградить пользователя от просмотра результатов промежуточных действий процедуры, в строке 8 свойству Screenupdating присваивается значение False. В строке 9 активизируется рабочая книга Data.xls. В последующих строках будут выполняться какие-то действия в этой книге — в данном примере фактический текст обрабатывающей программы опущен. Для возврата поль
690
Неделя 3
зователя к первоначальной рабочей книге в строке 13 она снова активизируется, а в строке 14 свойство Screenupdating снова получает значение True.
Так надо
Дайте пользователю знать, что происходит, когда вы переключаетесь от одной рабочей книги к другой. Простое использование функции MsgBox расскажет пользователю, где он находится в данный момент и чего ему следует ожидать.
Так не надо
Не активизируйте рабочую книгу без необходимости. Как правило, можно модифицировать или получить информацию о рабочей книге, просто обратившись к соответствующему объекту Workbook. Например, для выяснения формата книги Data.xls с помощью свойства FileFormat можно использовать следующий оператор без активизации этой рабочей книги.
FileFmt = Workbooksf“Data.xls").FileFormat
Сохранение рабочей книги
Если ваша программа сделала какие-либо изменения в рабочей книге, вы должны дать пользователю возможность сохранить эти изменения либо это должна делать непосредственно ваша программа. Такая задача облегчается путем включения в меню вашей программы встроенной в Excel команды Save (Сохранить) либо добавлением кнопки Save к панели инструментов, если ваша программа формирует меню или панели инструментов.
Однако иногда может потребоваться сохранить рабочую книгу под управлением процедуры. Например, вы можете создать свою собственную версию команды File-Save. Аналогично этому вы можете защитить начинающего пользователя от выполнения действий с непредвиденными последствиями, таких как закрытие рабочей книги либо выход из вашей программы или из Excel без сохранения его (пользователя) работы.
Для сохранения рабочей книги используется, как теперь нетрудно догадаться, метод Save.
Object.Save
В этом методе Object есть объектная ссылка на открытый объект Workbook, который вы хотите сохранить.
Несколько иной, более удобный метод сохранения называется SaveCopyAs. Этим методом копия указанной рабочей книги сохраняется на диске без воздействия на рабочую книгу в памяти компьютера. Этот метод полезен, например, если вы хотите создать команду Revert, которая будет возвращать рабочую книгу в первоначальное состояние. Вы используете метод SaveCopyAs в начале вашей программы для создания на диске копии файла рабочей книги до того, как в этой книге будут сделаны какие-либо изменения. Затем вы можете вернуться к этой копии, открыв ее и снова сохранив книгу под ее исходным именем. Ниже приведена основная форма метода SaveCopyAs.
Object.SaveCopyAs(Filename)
Здесь Object представляет собой объектную ссылку на объект Workbook, который должен быть сохранен, a Filename — имя, которое вы хотите использовать для сохраняемой копии рабочей книги.
В листинге 19.5 приведен пример процедуры, использующей методы Save и SaveCopyAs.
День 19-й. Управление приложением Excel с помощью VBA	691
flucmuue 18.5. Эспользовпние методов Save и SnvBCopg|fts
1:	Sub BackUpToFloppy()
2:	'Сохраняет н делает резервную копию книги на диске А:
3:
4:	Const FloppyDrv = "А:"
5:
6:	With ActiveWorkbook
7:	If Not .Saved Then .Save
8:	.SaveCopyAs Filename:=FloppyDrv & .Name
9:	End With
10:	End Sub
Эта процедура сохраняет активную рабочую книгу и делает резервную копию на дискете в устройстве А. Процедура начинается с объявления константы для буквы накопителя на гибком диске. Инструкция With инициирует несколько команд для объекта ActiveWorkbook.
В строке 7 инструкция If... Then проверяет свойство Saved рабочей книги. Если это свойство ложно (False), значит, книга была изменена и эти изменения все еще не сохранены, так что процедура вызывает метод Save.
Имя резервного файла формируется путем конкатенации значения константы FloppyDrv со свойством Name рабочей книги. Результат конкатенации служит в качестве аргумента Filename метода SaveCopyAs, который сохраняет копию рабочей книги.
Так надо
Используйте свойство Path объекта Workbook для проверки того, сохранялась ли когда-либо рабочая книга. Если Path возвращает пустую строку, значит, эта книга никогда ранее не сохранялась.
Так не надо
Не используйте метод Save для новой рабочей книги, которая никогда ранее не сохранялась. Если же вы сделаете это, Excel сохранит новую книгу под ее текущим именем, например Книга1 .xls. Для назначения имени впервые сохраняемой рабочей книге используйте метод SaveAs.
!Object.SaveAs(Filename)
Метод SaveAs включает несколько других аргументов, которые позволяют, кроме прочего, выбрать формат файла или назначить файлу пароль. Для просмотра, вставки или получения справки обо всех аргументах метода SaveAs используйте окно Object Browser.
Закрытие рабочей книги
После окончания работы с активной книгой вы должны закрыть ее так, чтобы она сохранилась в памяти компьютера и при этом не было беспорядка на экране. Закрывать рабочую книгу следует с использованием одной из следующих форм метода Close.
Workbooks.Close
Object.Close(SaveChanges)
В случае первой синтаксической формы просто закрывается каждая открытая рабочая книга. Перед ее закрытием Excel предложит вам сохранить изменения в книге, если это необходимо. Вторая форма метода Close предполагает закрытие конкретной
692
Неделя 3
рабочей книги, указанной как Object. Аргумент SaveChanges используется для определения порядка закрытия рабочей книги.
•	Если этот аргумент имеет значение True, Excel автоматически сохранит книгу перед ее закрытием.
•	Если SaveChanges равен False, Excel закроет рабочую книгу без сохранения любых изменений.
•	Если аргумент SaveChanges опущен, Excel предложит вам сохранить, если необходимо, внесенные в книгу изменения.
Листинг 19.6 демонстрирует использование метода close.
Листинг 18.6. Исиользоввиив метода Close
1:	Sub CloseAllf)
2:	'Закрывает все открытые книги и предлагает сохранить изменения
3:
4:	Const qButtons = vbYesNo + vbQuestion
5:
6:	Dim	Book As Workbook
7:	Dim	Ans As Integer
8:	Dim	MsgPrompt As String
9:
10:	For Each Book In Workbooks
11:	If Not (Book.Name = ThisWorkbook.Name) Then
12:	If Not Book.Saved Then
13:	MsgPrompt = "Сохранить изменения в " & Book.Name &
14:	Ans = MsgBox(Prompt:=MsgPrompt, Buttons:=qButtons)
15:	If Ans	=	vbYes Then
16:	Book.Close SaveChanges:=True
17:	Else
18:	Book.Close SaveChanges:=False
19:	End If
20:	Else
21:	Book.Close
22:	End If
23:	End If
24:	Next Book
25:	End Sub
В этой процедуре закрываются все открытые рабочие книги (за исключением книги, содержащей данную процедуру) с предложением пользователю сохранить изменения в каждой книге, в которой эти изменения еще не сохранялись. Если вы хотите предотвратить отмену пользователем сохранения книги перед ее закрытием, используйте подобную приведенной здесь процедуру вместо метода Workbooks. Close, поскольку встроенная в Excel функция Save имеет кнопку Cancel (отмена). После объявления константы и нескольких переменных (в строках 4—8) в процедуре открывается цикл For Each для обработки всех рабочих книг в коллекции Workbooks.
Поскольку закрытие рабочей книги, содержащей выполняемую в данный момент процедуру, приведет к прекращению выполнения этой процедуры, в строку 11 включена инструкция If... Then. Эта инструкция проверяет, не имеет ли обрабатываемая в данный момент рабочая книга то же имя, что и книга, содержащая процедуру
День 19-Й, Управление приложением Excel с помощью VBA
693
CloseAll. Если так, то команды закрытия книги пропускаются; этот фрагмент (в строках 12—22) выполняется, только если проверяемая рабочая книга не содержит процедуры CloseAll. (Ссылку на рабочую книгу, содержащую выполняемую в настоящий момент программу VBA, возвращает свойство ThisWorkbook.)
Если рабочая книга содержит изменения, которые не были сохранены (строка 12), функция MsgBox выдаст запрос о необходимости сохранения этих изменений. Если пользователь выбирает кнопку Yes (т.е. в переменную Ans возвращается значение vbYes), процедура выполняет метод Close и устанавливает для SaveChanges значение True (в строках 15-16). В противном случае процедура просто закрывает рабочую книгу с установкой для SaveChanges значения False (в строке 19). Если же рабочая книга не содержит несо-храненных изменений, процедура выполняет метод Close без аргументов (строка 21).
Так надо
Полный синтаксис метода Close можно исследовать с помощью окна Object Browser или справочной системы VBA. Этот метод включает два других аргумента, которые позволяют указать имя файла рабочей книги, которая никогда ранее не сохранялась, и назначать маршрут рабочей книги по компьютерной сети.
Используйте свойство Application.ThisWorkbook всегда, когда требуется сослаться на рабочую книгу, * содержащую выполняемую в данный момент процедуру.
Работа с объектами Worksheet
Объекты Worksheet содержат ряд свойств и методов, которые вы можете использовать в своих VBA-программах. С помощью этих свойств и методов можно активизировать и скрывать рабочие листы, добавлять новые листы в рабочую книгу, а также перемещать, копировать, переименовывать или удалять рабочие листы. В последующих разделах описана каждая из этих операций с рабочими листами.
Обращение к объекту Worksheet
Каждый рабочий лист является объектом Worksheet, a Worksheets представляет собой коллекцию всех листов в данной рабочей книге. Для ссылки на конкретный рабочий лист служит коллекция Worksheets объекта Workbook.
Object.Worksheets!Index)
Здесь Object представляет ссылку на объект Workbook, который содержит рабочий лист. Индекс Index может иметь следующий смысл.
•	Число, представляющее номер листа, с которым вы хотите работать. Например, 1 означает первый лист в рабочей книге, 2 — второй лист и т.д.
•	Имя (текстовая строка) рабочего листа, который вы намерены использовать. Это имя, которое появляется в заголовке рабочего листа.
В листинге 19.7 приведен пример наиболее общего способа применения метода Worksheets: для идентификации рабочего листа используется текстовая строка.
694
Неделя 3
Листана 13.7. Использование коллекции Worksheets для оВращения к рабочему листу
1:	Sub SetWorksheetProtection()
2:	'Устанавливает защиту листа
3:
4:	Workbooks("fleHbl9.XLS").Worksheets("Sheetl").Protect _
5:	Contents:=True, Scenarios:=True
6:	MsgBox "Защита на лист 'Sheet!' установлена."
7:	End Sub
Выражение Workbooks(“День19.ХЬЗ").Worksheets("Лист!") возвращает объектную ссылку на лист с именем Лист1 в рабочей книге День19.х1з. Метод Protect устанавливает защиту содержимого и сценариев этого рабочего листа. Функция MsgBox сообщает пользователю, что зашита листа с именем Sheet 1 активизирована.
Так надо
Если вам необходимо сослаться на активный рабочий лист, используйте свойство ActiveSheet рабочей книги. Когда вам требуется сослаться на каждый лист книги (или вы не уверены, какого рода лист вы выбираете), вместо коллекции Worksheets используйте коллекцию Sheets. Эта коллекция содержит не только рабочие листы, но и листы диаграмм.
Так не надо
Не следует указывать обьект Workbook, если процедура и рабочий лист находятся в одной и той же рабочей
Не используйте числа в качестве аргумента Index, если в этом нет особой необходимости. Как правило, такое обращение, как Worksheets! "Продажи за июнь") воспринимается легче,чем,скажем, Worksheets)7).
Активизация рабочего листа
Большинство рабочих книг содержит более одного листа, так что в программах VBA может потребоваться переключаться с одного рабочего листа к другому. Например, вам нужно отобразить один лист с исходными данными, а затем переключиться к другому листу для просмотра отчета или диаграммы. Для такого переключения между рабочими листами используется метод Activate.
Object. Activate
Здесь Object представляет ссылку на объект Worksheet, который вы собираетесь использовать. В листинге 19.8 представлен пример использования метода Activate.
Листинг 18.8. Использпвание метода Activate для переключения мвждд раОочими листами
1:	Sub DisplayReport()
2:	'Показывает лист "Sheet!", если пользователь хочет его посмотреть
3:
4:	Dim Ans As Integer, qBtns As Integer
5:	Dim mPrompt As String
6:
7:	mPrompt = "Вы хотите просмотреть лист ""Sheet!""?"
8:	qBtns = vbYesNo + vbQuestion + vbDefaultButton2
День 19-й. Управление приложением Excel с помощью VBA
695
9:	Ans = MsgBox(Prompt:=mPrompt, Buttons:=qBtns)
10:	If Ans = vbYes Then
11:	Workbooks (" День19. XLS"). Worksheets (" Sheet 1"). Activate
12:	End If
13:	End Sub
В этой процедуре пользователя спрашивают, хочет ли он видеть конкретный лист, в данном случае поименованный по умолчанию как Sheetl (строки 7-9). Если пользователь выбирает кнопку Yes, рабочий лист с именем Sheetl активизируется (строка 11).
Так надо
: Если вам нужно выбрать рабочий лист, используйте метод Select.
Object.Select
Этот метод оказывается полезным для создания трехмерных ссылок и определения листов, которые вы собираетесь распечатывать. (Трехмерные ссылки представляют собой ссылки на диапазон ячеек, расположенных более чем на одном рабочем листе.)
Так не надо
Не активизируйте рабочий лист, если в этом нет необходимости. Чтобы получить доступ к большинству свойств и методов объекта Worksheet, вы можете просто использовать метод Worksheets для ссылки на рабочий лист, который вас интересует. Следующее выражение, например, возвращает стандартную ширину рабочего
. листа Продажи за июнь без активизации этого листа.
, stwidth = Worksheets(“Продажи за июнь”).Standardwidth
Создание нового рабочего листа
Коллекция Worksheets имеет метод Add, который можно использовать для вставки новых листов в рабочую книгу. Синтаксис этого метода таков.
Object. Worksheets.Add([Before] [,After] [,Count] [,Type])
Здесь, как и ранее, Object представляет ссылку на объект, в который вы хотите добавить новый рабочий лист. Аргумент Before указывает на лист, перед которым вставляется новый лист; аргумент After указывает рабочий лист, после которого добавляется новый лист. (Как видно, оба этих аргумента можно не использовать одновременно в одном и той же инструкции.) Если вы опустите оба аргумента Before и After, VBA добавит новый рабочий лист перед активным листом.
Аргумент Count представляет собой число добавляемых новых рабочих листов. (Метод Add добавляет только один лист, если аргумент Count опущен.) Аргумент Туре представляет тип листа, который вы хотите вставить. Можно выбирать из трех значений аргумента Туре (определенных в классе xlSheetType): xlWorksheet (по умолчанию), xlExcel4MacroSheet или xlExcel4IntMacroSheet. Листинг 19.9 демонстрирует использование метода Worksheets.Add.
696
Неделя 3
Лпстпнг 13.9. Исиользоваиие мвтодя Add для спздаиип нового раПпчвго листа
1:	Sub CreateTempWorksheet()
2:	'Создает временный лист н скрывает его
3:
4:	Application.Screenupdating = False
5:	Worksheets.Add
6:	With ActiveSheet
7:	.Name = "Temporary"
8:	.Visible = False
9:	End With
10:	Application.Screenupdating = True
11:	End Sub
Эта процедура может оказаться полезной для создания нового рабочего листа, который будет содержать промежуточные результаты или другие данные, не предназначенные для просмотра пользователем. После отключения обновления экрана (строка 4) процедура добавляет новый рабочий лист (строка 5). В инструкции With (строки с 6 по 9) на этом новом активном листе выполняются две операции. Во-первых, в строке 7 используется свойство Name (рассматриваемое в следующем разделе) для изменения имени нового рабочего листа на Temporary. Во-вторых, в строке 8 новый рабочий лист скрывается путем задания свойству Visible значения False.
Переименование рабочего листа
Имя рабочего листа — это тот текст, который появляется в строке заголовка листа. Если вам нужно переименовать некоторый рабочий лист, измените свойство Name этого листа.
Object. Name
Здесь Object — это рабочий лист, который вы хотите переименовать. В только что рассмотренном листинге 19.9 приведен пример процедуры, которая изменяет свойство Name рабочего листа.
Коиирование и перемещении рабочего листа
Если вам необходимо перестроить листы в рабочей книге, используйте методы Сору и Move. Синтаксис обоих этих методов идентичен.
Object.Copy([Before] [,After])
Object.Move([Before] [,After])
Здесь Object — объектная ссылка на рабочий лист, который вы хотите переместить или скопировать. Аргумент Before указывает рабочий лист, перед которым находится лист, подлежащий копированию или перемещению; аргумент After указывает лист, после которого расположен подлежащий копированию или перемещению лист. (Нельзя использовать оба эти аргумента одновременно в одной и той же инструкции.) Если оба аргумента — и Before, и After — опущены, то VBA создаст новую рабочую книгу для скопированного или перемещенного листа. В листинге 19.10 представлена процедура, в которой используется метод Move.
День 19-й. Управление приложением Excel с помощью VBA
697
Листинг 19.19. Использование метода Мо»в длп пврвмвщеппя раПочвго лнстп
1:	Sub CreateWorksheetAtEnd()
2:	'Создает новый лист в конце книги
3:
4:	Dim NewSheet As String
5:
6:	Application.Screenupdating =	False
7:
8:	Worksheets.Add Before:=Worksheets(Worksheets.Count)
9:	NewSheet = ActiveSheet.Name
10:	With Worksheets(NewSheet)
11:	.Move After:=Worksheets(Worksheets.Count)
12:	.Activate
13:	End With
14:
15:	Application.Screenupdating =	True
16:	End Sub
После объявления переменной и запрета обновления экрана процедура обращается к методу Add для добавления рабочего листа (строка 8). В этом вызове метода Add используется необязательный аргумент для вставки нового рабочего листа непосредственно перед последним листом уже существующей рабочей книги. В строке 8 определяется номер последнего листа в рабочей книге с использованием свойства Count, которое просто подсчитывает количество рабочих листов в книге. Затем имя нового рабочего листа сохраняется в переменной NewSheet.
В стоке 11 для перемещения нового листа за последний лист рабочей книги используется метод Move. Здесь свойство Count также использовано для получения количества рабочих листов в книге. Затем новый лист активизируется (строка 12) и снова разрешается обновление экрана (строка 15).
Так надо
Помните, что листы можно вставлять также после указанного рабочего листа. Процедура в листинге 19.10 s вставляет лист перед последним листом в книге, а лишь затем перемещает его, в связи с тем, что Excel не позволяет вставлять лист в конец рабочей книги. Для вставки рабочего листа куда-либо, кроме конца книги, ис- ’ пользуйте метод Add с аргументом After.
Так не надо
Не забывайте снова разрешать изменение экрана, если вы его запрещали.
Удаление рабочего листа
Чтобы содержать рабочую книгу в порядке и при этом экономить дисковое пространство, необходимо удалять рабочие листы, потребность в которых уже отпала. Это особенно актуально в тех случаях, когда ваше приложение создает временные рабочие листы для хранения промежуточных результатов (как показано в листинге 19.9 выше в этом уроке). Для удаления рабочих листов используется метод Delete.
Object.Delete
698
Неделя 3
Здесь Object является ссылкой на объект Worksheet, который вы хотите удалить. В листинге 19.11 приведен пример использования метода Delete.
Листинг 19.11. Исподьзоппние метода Selete для цдалвпня раПочвго листа
1:	Sub DeleteTemporarySheetsf)
2:	'Удаляет все временные листы
3:
4:	Dim Sheet As Worksheet
5:
6:	Application.DisplayAlerts = False
7:
8:	For Each Sheet In Workbooks("День19.ХЬЗ").Worksheets
9:	If InStrjl, Sheet.Name, "Temporary") Then
10:	Sheet.Delete
11:	End If
12:	Next Sheet
13:
14:	Application.DisplayAlerts = True
15:	End Sub
Эта процедура в цикле “пробегает” по листам в рабочей книге и удаляет все ранее созданные временные листы. При этом предполагается, что временными являются листы с такими иенами, как Temporaryl, Temporary2 и т.д.
В процедуре объявляется переменная Sheet для объекта Worksheet и свойству DisplayAfter объекта Application присваивается значение False (строка 6). Это подавляет обычное диалоговое сообщение Excel, которое вы видите всякий раз, когда удаляете рабочий лист. В строке 8 начинается цикл For Each для просмотра всех листов рабочей книги День19.хк. Каждый лист тестируется функцией InStr. Цель теста — установить, не входит ли подстрока Temporary в имя данного рабочего листа. Если это так, то в строке 10 этот лист удаляется. Когда этот процесс заканчивается, свойству DisplayAfter снова присваивается значение True (строка 14).
Так надо
Используйте аргумент Compare в функции InStr, чтобы сделать сравнение имен листов текстовым. В процедуре листинга 19.11 предполагается, что текущей глобальной установкой сравнения является Option Compare Text.
Так не надо
Не забывайте снова включать предупреждающие сообщения Excel, если ваша процедура их отключила/
Методы для обращения к диапазона
Большинство операций, выполняемых с обычными рабочими листами, — в том числе ввод информации, вырезание или копирование данных, а также разные способы форматирования — оперируют с ячейками, диапазонами и именами диапазонов. И нет ничего удивительного в том, что многие методы объектов Excel в VBA тоже имеют дело с диапазонами.
День 19-й. Управление приложением Excel с помощью VBA
699
Точно так же, как при работе в Excel вам нужно выделить диапазон рабочего листа прежде чем что-либо с ним делать, в программе VBA вам необходимо сослаться на диапазон листа перед тем, как применить по отношению к нему какую-либо операцию. Для этого вам придется поработать с наиболее общим из всех объектов Excel: объектом Range. Объект Range может быть простой ячейкой, строкой или столбцом таблицы, выделенными ячейками или даже трехмерным диапазоном (т.е. диапазоном, который включает выделенные ячейки более чем на одном рабочем листе). В следующих разделах рассматриваются различные методы и свойства, которые возвращают объект Range.
Использование метода Range
Самый наглядный и простой путь идентификации ячейки или диапазона состоит в использовании метода Range. Этот метод имеет следующий синтаксис.
Object.Range(Name)
Здесь Object является ссылкой на объект Worksheet, который содержит рассматриваемый диапазон. Если вы опускаете Object, VBA предполагает, что метод применяется к активному объекту ActiveSheet. Аргумент Name представляет собой ссылку на диапазон или имя диапазона, введенные как текст. Метод Range работает также с поименованными диапазонами.
В листинге 19.12 содержится пример применения метода Range.
Листинг 18.12. Исиодьядиаиие метода Ванде для раПпты с диапазонами
1:	Sub FormatRangeFont()
2:	'Устанавливает шрифт в диапазоне ячеек с помощью метода Range
3:
4:	With Worksheets("Sheetl")
5:	.Range("Al:L1").Font.Size = 24
6:	.Range)"Al:L1").Font.Bold = True
7:	.Range("Al:LI").Font.Name = "Times New Roman"
8:	End With
9:	End Sub
В этом примере все три инструкции в строках 5-7 возвращают диапазон А1:Ы на рабочем листе с именем Sheetl. (Обратите внимание, что координаты диапазона необходимо указывать в кавычках.)
Процедура в листинге 19.12 устанавливает различные атрибуты шрифта для конкретного выделенного диапазона. Font является свойством объекта Range и возвращает ссылку на объект Font, ассоциируемый с выделенным диапазоном. Свойства объекта Font такие же, как и атрибуты шрифта, которые мы назначаем в диалоговом окне Шрифт: bold (полужирный), italic (курсив), underline (подчеркивание) и т.п. В следующем списке приведена выборка свойств объекта Font; во всех случаях Object является объектом Range. Для просмотра полного списка свойств объекта Font используйте окно Object Browser.
•	Object.Font.Bold. Включает (True) или отключает (False) полужирный стиль шрифта.
•	Object.Font.Italic. Включает (True) или отключает (False) курсивный стиль шрифта.
700
Неделя 3
•	Object.Font.Underline. Включает или отключает подчеркивание; значение свойства Underline может быть любым из этих зарезервированных констант (определенных в классе XlUnderlineStyle): xlUnderlineStyleNone, xlUnderlineStyleSingle, xlUnderlineStyleDouble, xlUnderlineStyleSingleAcCounting или xlUnderlineStyleDoubleAcCounting.
•	Object.Font.Name. Устанавливает имя типа шрифта. Можно указывать значение свойства Name как текстовую строку, например "Arial".
•	Object.Font.Size. Устанавливает размер шрифта в точках.
Многие объекты в Excel имеют свойство Font, которое возвращает объект Font и которое вы можете использовать для форматирования текста в таких объектах, как Range, Character и Style; само по себе приложение Excel содержит лишь несколько объектов, имеющих свойство Font. Для определения того, имеет ли конкретный объект свойство Font, используйте окно Object Browser.
Так надо
Пользуйтесь альтернативным синтаксисом метода Range, который требует двух аргументов.
Object.Range(Celll, Се112)
Как и ранее, Object является объектом Worksheet, который содержит выделяемый диапазон. Аргумент СеШ определяет ячейку верхнего левого угла, а Се112 - ячейку нижнего правого угла выделяемого диапазона. Каждый из этих аргументов может представлять адрес ячейки как текстовую строку, объект Range, содержащий единственную ячейку либо входящие в рассматриваемый диапазон столбец или строку ячеек.
Преимущество такого синтаксиса состоит в том, что в нем углы диапазонов определяются отдельными аргументами, что позволяет модифицировать каждый угол выделяемых диапазонов под управлением вашей процедуры. Например, вы могли бы присвоить переменным имена, скажем, UpperLeft и LowerRight, и возвращать объекты Range различных размеров:
Range(UpperLeft, LowerRight)
Не используйте в методе Range координаты для выделения тех диапазонов, которым можно присвоить имена. Использование имен диапазонов делает текст ваших процедур более наглядным с точки зрения его расшифровки и отладки.
Использование метода Cells
Хотя для возврата единственной ячейки вы можете использовать метод Range, метод Cells также позволяет сделать это и, кроме того, придает программе большую гибкость.
Object.Cells(Rowindex, Columnindex)
Здесь Object — ссылка на объект Range или Worksheet, содержащий ячейку, с которой вы хотите работать. Если ссылка опущена, метод применяется к активному объекту ActiveSheet.
Аргумент Rowindex представляет собой номер строки, в которой находится ячейка. Если Object является рабочим листом, то значение Rowindex = 1 ссылается на первую
День 19-й. Управление приложением Excel с помощью VBA
701
строку этого листа; если же Object — диапазон, то Rowindex = 1 ссылается на первую строку этого диапазона.
Аргумент Columnindex представляет столбец, в котором находится рассматриваемая ячейка. Его значением может быть либо буква (как литеральная константа или как строковая переменная), либо номер, указывающий на столбец. Если Object является рабочим листом, то значение Columnindex = "А" или 1 ссылается на столбец А этого листа; если же Object — диапазон, то Columnindex = "А" или 1 ссылается на первый столбец этого диапазона.
В листинге 19.13 приведен пример использования метода Cells.
Листинг 18.13. Иснользование метода Cells дня оОрпщення к ячеОке
1:	Sub WriteNewData()
2:	'Вносит данные из формы пользователя в лист
3:
4:	Dim	I As Integer
5:	Dim	DBNewRow As Integer
6:	Dim	NewRange As String
7:
8:	With Range("Database")
9:	'переход на новую строку
10:	DBNewRow = .Row + .Rows.Count
11:
12:	'Ввод данных из формы в ячейки новой строки
13:	For I = 1 То .Columns.Count
14:	Cells(DBNewRow, (I + .Column) - 1).Value = _
15:	Listingl3Form.Controls("txt" & CStr(I)).Text
16:	Next I
17:
18:	'Расширение диапазона	Database
19:	NewRange = “=" & .Parent.Name & "!"
20:	NewRange = NewRange S	Cells(.Row,	.Column).Address &
21:	NewRange = NewRange &
22:	Cells(DBNewRow, (.Column + .Columns.Count) - 1).Address
23:	Names("Database"J.RefersTo = NewRange
24:	End With
25:	End Sub
Эта процедура является частью приложения для ведения базы данных. Пользователь вводит данные в диалоговую форму-окно (подобную показанной на рис. 19.1). Процедура WriteNewData вносит новые данные в рабочий лист в конец диапазона данных и увеличивает диапазон данных на размер включенной строки с новыми данными.
В строках 4-6 объявляются переменные, используемые в этой процедуре. Строкой 8 начинается инструкция With, в которой указывается диапазон ячеек, на которые в данный момент ссылается имя базы данных. В строке 10 переменной DBNewRow присваивается значение, соответствующее строке, расположенной непосредственно ниже диапазона данных; в этой строке будут сохранены новые данные.
В цикле For...Next в строках 13—16 введенные пользователем в диалоговую форму значения переносятся на рабочий лист. Индекс I цикла просматривает значения от 1 до значения, соответствующего количеству столбцов в диапазоне, которое получается путем возврата свойства Count коллекции Columns объекта Range. (Коллекция Columns содержит все столбцы объекта Range, и его свойство Count возвращает количество столбцов.)
702
Неделя 3
Рис. 19.1. Процедура WriteNewData предполагает ввод данных с помощью диалогового окна, подобного показанному здесь
Внутри цикла метод Cells (в строке 14) возвращает каждую ячейку в новой строке, и свойство Value ячейки заносится в соответствующий текст управляющего элемента. В этой части программы предполагается, что каждый управляющий элемент в форме пользователя имеет имя txtN, где N — соответствующий номер столбца в диапазоне данных. В строке 15 программы, следовательно, формируется имя управляющего элемента, из которого переданы значения данных, путем конкатенации строкового эквивалента текущего значения индекса цикла с литеральной константой "txt". (В разделе “Ввод значений и формул” свойство Value рассматривается более подробно.)
Диапазон данных теперь нуждается в изменении с учетом добавленной новой строки. Ссылку на диапазон можно переопределить с помощью конкретного имени путем изменения свойства RefersTo этого имени. (Поименованные диапазоны на рабочем листе являются объектами Name и к ним можно обращаться через коллекцию Names рабочего листа.) Свойство RefersTo должно содержать строку в форме "=sheet!reference", где sheet — имя рабочего листа, содержащего поименованный диапазон, a reference — адрес диапазона в форме R1C1.
В строках 19—23 компонуется правильно сформатированная строка для имени нового диапазона данных: в строке 19 добавляется имя листа к адресу диапазона с использованием свойства Parent объекта Range для получения имени рабочего листа, содержащего объект Range. В следующей (20-й) строке программы добавляются координаты верхнего левого угла новой диапазона с использованием свойства Address объекта Cells для возврата текстовой строки, содержащей адрес ячейки, на которую требуется ссылка: в данном случае это те же строка и столбец, что и в исходном диапазоне данных. В строке 21 добавляются координаты нижнего правого угла нового диапазона — также с использованием свойства Address; теперь это ячейка, соответствующая новой строке и старом столбце (т.е. столбце исходной базы данных). Наконец, в строке 23 назначается новое определение свойству RefersTo диапазона данных — путем ссылки на коллекцию Names.
Использооанив метода Offset
Когда вы определяете свои Range-объекты, вы часто не можете знать используемый в таком определении адрес конкретного диапазона. Например, вам может понадобиться сослаться на ячейку, расположенную на две строки ниже и на один столбец правее активной ячейки. Вы, конечно, могли бы найти адрес активной ячейки и затем вычислить координаты требуемой ячейки, но VBA Excel предоставляет для этой цели более наглядный и гибкий путь: метод Offset. Этот метод возвращает объект Range, смешенный относительно конкретной ячейки на заданное количество строк и столбцов.
Object.Offset([RowOffset] [, Columnoffset])
День 19-й. Управление приложением Excel с помощью VBA
703
Здесь Object ссылается на исходный Range-объект. Аргумент RowOffset представляет собой количество строк смещения относительно Object. При этом можно использовать положительное число (смещение вниз), отрицательное число (смещение вверх) или нуль (смещение отсутствует, т.е. искомая ячейка располагается в той же строке). Если вы опускаете этот аргумент, то по умолчанию он принимает значение 0.
Аргумент Columnoffset — это количество столбцов смещения относительно объекта Object. Для его задания также можно использовать положительное число (смешение вправо), отрицательное число (смещение влево) или нуль (смещение отсутствует). Если значение этого аргумента не указано, по умолчанию оно принимает значение 0.
Листинг 19.14 демонстрирует процедуру, в которой используется метод Offset.
Листинг 18.14. Использование метода Offset дня оказания дианазона
1:	Sub SelectData()
2:	'Выделяет данные в диапазоне
3:
4:	Dim DBRows As Integer
5:
6:	Worksheets!"Sheetl").Select
7:	With Range("Database")
8:	DBRows = .Rows.Count
9:	.Offsetfl, 0).Resize(DBRows - 1, .Columns.Count).Select
10:	End With
11:	End Sub
Эта процедура выделяет некоторый диапазон данных, называемый Database, т.е. она выбирает диапазон, содержащий только данные и не содержащий заголовков столбцов. Для этого отбрасывается первая строка исходного диапазона. Это удобно, если вам необходимо выполнить некую глобальную операцию с данными, например сортировку или изменение общего формата.
В строке 6 программы выбирается рабочий лист Sheetl, а инструкция With в строке 7 выбирает на рабочем листе объект Range с именем Database. В строке 8 вычисляется количество строк в диапазоне с использованием свойства Rows.Count объекта Range и сохраняется в переменной DBRows.
Метод Off set (1,0) в строке 9 возвращает диапазон, который смещен относительно диапазона Database на одну строку вниз. Поскольку этот новый диапазон включает, возможно, пустую строку, расположенную ниже исходного диапазона, ее необходимо исключить. Чтобы сделать это, используется свойство Resize для изменения размеров диапазона.
Object.Resize([Rowsize] [, Columnsize])
Здесь Object — объектная ссылка на диапазон, который вы хотите изменить. Аргументы Rowsize и Columnsize — количество строк и столбцов измененного диапазона соответственно. В строке 9 выражение Resize)DBRows-1, .Columns.Count) удаляет ненужную строку из смещенного диапазона.
Метод Select в строке 9 выделяет новый диапазон. (Подробнее о методе Select мы поговорим в разделе “Выделение диапазона” ниже в этой главе.)
704
Неделя 3
Другие методы и свойства для указания диапазона
Методы Range, Cells и Offset являются наиболее общими, но не единственными методами для получения объектов Range. В листинге 19.14 мы видели, как использовался метод Resize для указания диапазона конкретного размера. Ниже приведено несколько других методов и свойств, которые возвращают объекты Range.
•	[cellRef]. Можно указать единственную ячейку путем заключения ссылки на эту ячейку в квадратные скобки. Например, выражение [А1].Font.Size = 16 устанавливает в ячейке А1 шрифт размера 16. Заметьте, что в данном случае ссылка на ячейку (cellRef) в кавычки не заключается.
•	Object.RowsfIndex). Этот метод возвращает строку на рабочем листе или в диапазоне, на который ссылается объект Object. Если такое указание отсутствует (т.е. Object опущен), VBA по умолчанию будет ссылаться на активный лист. Index представляет собой номер строки. Если объектом является рабочий лист, значение Index, равное 1, ссылается на первую строку этого листа; если же объектом является диапазон — то на первую строку диапазона.
•	Object.EntireRow. Это свойство возвращает всю строку или строки, содержащиеся в указанном объектом Object диапазоне.
•	Object. Columns (Index). Этот метод возвращает столбец на рабочем листе или в диапазоне, указанном объектом Object. Если object опущен, VBA по умолчанию использует активный лист. Index — номер столбца, который может быть числом или буквой. Если объектом является рабочий лист, значение Index, равное “А” или 1, ссылается на столбец А листа; если же объектом является диапазон — то на первый столбец этого диапазона.
•	Object.Entirecolumn. Это свойство возвращает весь столбец или столбцы, содержащиеся в указанном объектом Index диапазоне.
•	Object.CurrentRegion. Это свойство возвращает текущую область диапазона Object. Текущая область определяется как область, окружающая текущую ячейку или диапазон и ограниченная сверху и снизу пустыми строками, а справа и слева — пустыми столбцами.
Работа с ячейками и диапазонами
Теперь вы уже знаете, как возвращать объект Range и пользоваться многочисленными свойствами и методами объектов типа Range. В следующем разделе мы исследуем некоторые из этих свойств и методов. Чтобы ознакомиться со всеми свойствами и методами объекта Range, а также получить справку по этому объекту, используйте окно Object Browser.
Выбор ячейки иди диапазона
Для выбора ячейки или целого диапазона ячеек используется метод Select.
Object.Select
Здесь Object является ссылкой на объект Range, который вы выбираете. В листинге 19.15 приведен пример использования рассматриваемого метода.
День /9-й. Управление приложением Excel с помощью VBA
705
Листинг 18.15. Иснользпвание метода Select для выОора дианазона
1:	Sub CreateChart))
2:	'Создает диаграмму на основе данных в диапазоне Sales
3:
4:	With Workbooks)"День19.ХЬЗ").Worksheets)"Sheet2")
5:	.Activate
6:	.Range)"Sales").Select
7:	End With
8:
9:	Charts.Add
10:	End Sub
Эта процедура создает новый диаграммный лист из выбранного диапазона. В строке 5 активизируется рабочий лист Workbooks)"День19.ХЬ5").Worksheets)"Sheet2"). В строке 6 выбирается диапазон на рабочем листе, называемом Sales. В строке 9 выполняется метод Add объекта Charts для создания диаграммы.
Так надо
. Для возврата объекта Range, который выбран в данный момент, используйте свойство Selection.
Так не надо
I Не выбирайте диапазон, если в этом нет необходимости. Метод Select является одним из наиболее медленных VBA-методов, так что избегайте его применения везде, где это возможно, для повышения скорости работы ваших программ.
Работа со значениями и формулами
Большинство ваших VBA-программ для Excel будет в той или иной форме использовать данные рабочих листов. Например, процедура может читать содержимое ячейки для проверки его правильности. Либо ваша программа может собирать данные с помощью пользовательской диалоговой формы, и вам понадобится передавать данные, введенные в диалоговое окно, в соответствующие ячейки рабочего листа. (Как это делается, показано в листинге 19.13.)
Если вам необходимо получить содержимое ячейки или требуется ввести данные в диапазон, используйте два предоставляемых VBA свойства объекта Range: Value и Formula. Синтаксис этих свойств следующий.
Object.Value
Object.Formula
В обоих случаях Object представляет собой ссылку на объект Range, с которым вы хотите работать. Для получения содержимого ячейки следуйте приведенным ниже указаниям.
•	Если все, что вам требуется, — это значение ячейки, используйте свойство Value. Например, если в ячейке А1 содержится формула =2*2, то выражение Range) "Al") .Value вернет значение 4.
706
Неделя 3
•	Если вас интересует содержащаяся в ячейке формула, используйте свойство Formula. Например, если ячейка А1 содержит формулу =2*2, то выражение Range("Al") .Formula вернет текстовую строку “-2*2”.
Для ввода данных в ячейку или в диапазон свойства Value и Formula используются попеременно. В листинге 19.16 показаны соответствующие примеры.
ЛЦСПН1Н8 18.1В. Использоваппе методов Value п Formula дхя ввода данных
1:	Sub CreateLoanPmtCalculator()
2:	'Строит на листе Sheet? расчет арендной платы
3:
4:	Worksheets("Sheet3").Select
5:
6:	With Range("Al") 'Надписываем ячейки
7:	.Value = " Расчет выплат по ссуде "
8:	.Font.Bold = True
9:	.Font.Italic = True
10:	.Font.Size = 18
11:	.Offsetjl).Value	=	"Rate"
12:	.Offset)2).Value	=	"Period"
13:	.Offset(3).Value	=	"Amount"
14:	.Offset(5).Value	=	"Payment"
15:	End With
16:
17:	'Вводим формат ячеек и формулу
18:	With Range("Al")
19:	.Offsetjl,	1). Number Format	=	"0.00V
20:	.Offset(3,	1). Number Format	=	"$#,H0J; [Red] ($#,Ш)"
21:	,Offset(5,	1) .NumberFormat	=	"$#,H0.00J; [Red] ($#,00.00)"
22:	.Offset(5,	1).Formula = "=PMT($B$2/12, $B$3*12, $B$4)"
23:	End With
24:	End Sub
Эта процедура выполняет простой подсчет выплат по ссуде на листе Sheet3 активной рабочей книги. Первая инструкция With (в строке 6) в качестве начальной использует ячейку А1. Свойство Value этой ячейки в строке 7 устанавливается таким, чтобы она содержала текст “Расчет выплат по ссуде’’, а в строках 8—10 устанавливаются некоторые опции ее шрифта. В следующих 4-х строках (11 —14) используется свойство Value ячеек А2, АЗ, А4 и Аб для ввода меток. Обратите внимание на использование метода Offset для работы с этими ячейками.
Вторая инструкция With (в строке 18) также использует ячейку А1. Эта процедура разделена на две отдельные инструкции With исключительно для большей наглядности; нет никакой технической потребности в таком разделении. В следующих трех строках устанавливается числовой формат ячеек В2, В4 и В6. Это достигается использованием свойства NumberFormat рассматриваемого диапазона.
Object.NumberFormat = Formatstring
Здесь Object — объектная ссылка на ячейку или диапазон, подлежащие форматированию, a Formatstring — текстовая строка, которая специфицирует форматирование.
Наконец, в строке 22 используется свойство Formula для ввода в ячейку В6 формулы выплат.
День 19-й. Управление приложением Excel с помощью VBA
707
Создание имени дианазона
Имена диапазонов в VBA являются объектами типа Name. Для их создания используется метод Add коллекции Names, которая обычно является коллекцией определенных имен в рабочей книге. Для метода Add коллекции Names предназначен приведенный ниже сокращенный синтаксис. (Этот метод имеет всего девять аргументов, так что для их изучения, а также для получения диалоговой справки и более подробного ознакомления с методом Add используйте Object Browser.)
Names.Add(Name, RefersTo, RefersToRlCl)
Аргумент Name является неким строковым выражением, указывающим имя, которое вы хотите использовать для вновь созданного диапазона. Аргументы RefersTo и RefersToRlCl описывают диапазон, на который ссылается имя. Эти аргументы применяются следующим образом.
•	RefersTo. Этот аргумент служит для ввода описания диапазона в стиле А1, например "=Sales!$A$l:$C$6".
•	RefersToRlCl. Используйте этот аргумент, когда описание диапазона либо имеет вид R1C1, например "=Sales!RlCl:R6C3", либо определяется методом или свойством, возвращающим диапазон, например Range ("Al: С6") или Selection.
Листинг 19.17 демонстрирует пример использования метода Add коллекции Names.
Листинг 18.17. Именование дианазона с пспользпоанием метода fldil оОъектп Нате
1:	Sub CreateNamedRangej)
2:	'Создает поименованный диапазон, используя координаты и имя,
3:	'введенное пользователем
4:
5:	Const strTaskName = "Поименованный диапазон"
6:
7:	Dim strTopLeft As String
8:	Dim strBottomRight As String
9:	Dim RngName As String
10:
11:	Worksheets ("Sheetl"). Select
12:
13:	'Получаем координаты диапазона и его имя
14:	strTopLeft = InputBox(Prompt:="Укажите верхний левый угол " &
15:	"в формате R1C1:",
16:	Title:=strTaskName)
17:	strBottomRight = InputBox(Prompt:="Укажите нижний правый угол " &
18:	"в формате R1C1:",
19:	Title:=strTaskName)
20:	RngName = 1при1Вох(Рготр1:="Укажите имя нового " &
21:	"диапазона: ",
22:	Title:=strTaskName)
23:
24:	'сохраняем новое имя
25:	Names.Add Name:=RngName, RefersToRlCl:="=Sheetl!" &
26:	strTopLeft &   S _
27:	strBottomRight
708
Неделя 3
28:	'Выделяем новый диапазон
29:	Range(RngName).Select
30:	End Sub
Эта процедура не очень сложна; здесь используются несколько инструкций InputBox для получения координат диапазона (верхнего левого и правого нижнего углов) и ее имени. Затем процедура создает поименованный диапазон с использованием введенных координат и имени.
Строки 5-9 содержат объявления констант и переменных, используемых этой процедурой. В строке 11 выбирается рабочий лист, а строки 14-22 содержат инструкции InputBox для получения информации от пользователя.
Для рассматриваемой процедуры важной является строка 25; здесь используется метод Add коллекции Names для добавления вновь поименованного диапазона. Новое имя диапазона содержится в переменной RngName, а фактический диапазон ячеек в виде текстовой строки содержится в аргументе RefersToRlCl. Когда VBA выполняет эти инструкции, создается вновь поименованный диапазон определенных ячеек и добавляется к коллекции имен в рабочей книге. Для выделения вновь созданного диапазона в строке 29 используется метод Select.
Вырезание, коппропанив н очистка данных
Если ваши процедуры предусматривают выполнение неких основных функций по редактированию рабочих листов, вам очень пригодятся методы Cut, Сору и Clear. Методы Cut и Сору имеют идентичный синтаксис.
Object.Cut([Destination])
Object.Copy([Destination])
Здесь, как и прежде, Object — объектная ссылка на объект Range, который вы хотите вырезать или скопировать. Аргумент Destination (адресат) представляет ячейку или диапазон, куда вы хотите вырезать или скопировать вставляемый диапазон. Если этот аргумент опущен, данные вырезаются или копируются в буфер Windows.
Для удаления данных из диапазона можно применять метод Cut с или без адресного аргумента, либо использовать один из следующих методов.
Object.Clear
Object.ClearContents
Object.ClearFormats
Object.ClearNotes
Здесь везде Object — объектная ссылка на диапазон, который вы хотите очистить. Метод Clear удаляет из диапазона всю информацию: содержимое, форматирование и примечания. Метод ClearContents очищает содержимое всего объекта Object. Метод ClearFormats очищает только форматирование этого объекта. Как теперь нетрудно догадаться, метод ClearNotes удаляет все примечания из объекта Object.
Приведенный ниже листинг 19.18 демонстрирует использование методов Cut, Сору и Clear.
День 19-й. Управление приложением Excel с помощью VBA
709
Лостина 18.IS. Использование методов Cut, Сорд и Clear
1:	Sub CopyToTempSheet()
2:	'Копирует данные на временный лист
3:
4:	Dim Lastcell As Range
5:	Dim oldSheet As string
6:
7:	Application.Screenupdating = False
8:
9:	oldSheet = ActiveSheet.Name
10:	Worksheets. Add
11:	ActiveSheet.Name = "Temporary"
12:	Worksheets("Sheetl").Select
13:
14:	Set Lastcell = Range("Al").Specialcells(xlLastCell)
15:	With Worksheets("Temporary")
16:	.Cells.Clear
17:	Range("Al", LastCell).Copy Destination:=.Range("Al")
18:	End With
19:
20:	Sheets(oldSheet).Select 'возврат на исходный лист
21:	Application.Screenupdating = True
22:	End Sub
23:
24:
25:	Sub RestoreFromTempSheetf)
26:	'Читает данные с временного листа
27:
28:	Dim LastCell As Range
29:	Dim oldSheet As String
30:
31:	Application.Screenupdating = False
32:	oldSheet = ActiveSheet.Name
33:
34:	Worksheets("Sheetl").Select
35:	With Worksheets("Temporary")
36:	Set LastCell = .Range("Al").Specialcells(xlLastCell)
37:	.Range("Al", LastCell).Cut Destination:=Range("Al")
38:	End With
39:
40:	Sheets(oldSheet).Select
41:	Application.Screenupdating = True
42:	End Sub
Сначала в первой процедуре CopyToTempSheet добавляется рабочий лист, называемый Temporary (временный) и выбирается лист под именем Sheetl. Затем процедура копирует все данные с этого активного листа и сохраняет их на временном листе. При этом (в строке 14) используется метод Specialcells(xlLastCell) для возврата последней ячейки на активном листе и ссылка на этот результирующий Range-объект сохраняется в переменной LastCell. Далее инструкция With в два действия обрабатывает рабочий лист Temporary. В строке 16 для очистки исходного листа используется метод clear.
710
Неделя 3
(Метод Cells без аргументов возвращает каждую ячейку листа.) С целью копирования диапазона, определенного ячейкой А1 в своем верхнем левом углу и переменной LastCell — в нижнем правом углу, в строке 17 используется метод Cells. Определяющей при этом является ячейка А1 временного рабочего листа.
Вторая процедура, RestoreFromTempSheet, фактически, осуществляет обратный процесс. К этому моменту переменная LastCell уже содержит адрес последней ячейки временного рабочего листа Temporary (строка 36). Диапазон от ячейки А1 до значения переменной LastCell теперь вырезается и привязывается к ячейке А1 активного рабочего листа (строка 37). (Активным является лист Sheetl, выделенный в строке 34.)
Так надо
Для вставки данных из буфера в рабочий лист объектов Worksheet или Range используйте методы Paste или PasteSpecial. Синтаксис метода Paste следующий.
Object.Paste((Destination] , [Link])
Здесь Object имеет смысл объектной ссылки на рабочий лист. Необязательный аргумент Destination представляет собой некоторый объект диапазона. Данные вставляются начиная с верхнего левого угла диапазона. Если вы опустите аргумент Destination, данные будут вставлены в текущее выделение. Аргумент Destination можно указывать лишь в том случае, если содержимое буфера может быть вставлено в диапазон; применение этого аргумента эквивалентно применению команд Edit-Paste в Excel. Необязательный аргумент Link, если он имеет значение True, указывает на то, что связь должна быть установлена с источником данных; использование этого аргумента аналогично использованию команд Edit-Paste Special и контролируется флажком Link. Нельзя одновременно использовать аргументы Destination и Link в одной инструкции.
Помните, что различные методы Clear полностью удаляют (уничтожают) данные; они we резервируются в буфере. Для удаления данных с рабочего листа и размещения их в буфере для дальнейшего использования применяйте метод Cut с аргументами.
Резюме
На сегодняшнем уроке мы рассмотрели, как использовать Visual Basic for Application для управления объектами Excel. Вы изучили основные методы работы с объектами Workbook, Worksheet и Range в Excel. Вы научились также использовать метод Open для открытия рабочей книги, метод Workbooks.Add —для создания новой книги, метод Save — для сохранения и метод Close — для закрытия рабочей книги.
Для указания объекта Worksheet вы научились использовать коллекцию Worksheets с разными важными методами и свойствами Worksheet: Activate (для активизации рабочего листа), Workbooks.Add (для создания нового листа), Name (для возврата или установки имени листа).
Мы также выяснили, что Excel предоставляет ряд методов для возврата объекта Range рабочего листа. Наиболее популярным является метод Range, но вы можете использовать также методы Cells (для возврата единственной ячейки) и Offset (для указания диапазона, смещенного относительно некоторого другого диапазона). Объекты Range рабочего листа имеют множество свойств и методов, и вы теперь знаете, как их применять. Среди них — Select (для выделения диапазона), Value и Formula (для возврата содержимого ячейки и включения в нее значений и формул), Names.Add (определения имен диапазонов), а также методы Cut, Сору и Clear.
День 19-й. Управление приложением Excel с помощью VBA
711
Вопросы о ответы
Я хочу создать новую рабочую книгу Excel на основе шаблона Excel, поэтому в методе Workbooks.Add применяю аргумент Template. К сожалению, Excel выдала мне ошибку. Что я сделал неправильно?
Скорее всего, проблема состоит в том, что вашего шаблона нет в папке XLStart. Когда вы используете аргумент Template, Excel просматривает папку XLStart в поисках файла шаблона. Если Excel его там не находит, она начинает просматривать альтернативные папки загрузки (если таковые имеются); если же и после этого Excel не удается найти искомый файл с шаблоном, она выдает сообщение об ошибке. Эту проблему можно решить двумя способами: вы можете изменить текст своей VBA-программы, чтобы указать полный путь к шаблону, который вы хотите использовать, либо обозначить содержащую файл шаблона папку как альтернативную папку загрузки.
Для создания альтернативной папки загрузки можно использовать два способа.
•	Применить команды Сервис-Параметры, выделить вкладку Общие в диалоговом окне Параметры, а затем в поле Альтернативный каталог автозагрузки ввести имя своей папки.
•	Присвоить свойству AltStartupPath объекта Application значение, совпадающее с текстовой строкой, определяющей вашу папку.
При использовании любой из этих методик необходимо перезагрузить Excel для того, чтобы внесенные вами изменения были зафиксированы.
Свойство Name объекта Workbook возвращает имя в таком виде, в каком оно появляется в поле заголовка рабочей книги. Есть ли какой-либо способ получить полный путь к данной книге, включая устройство и папку, в которой она хранится?
Да. Вам нужно только воспользоваться свойством FullName.
Object.FullName
Здесь Object ссылается на рабочую книгу, к которой вы хотите получить полный путь. Если же вам необходимо знать только устройство и папку вашей книги, используйте другое свойство объекта Workbook — Path.
Как форматировать размер шрифта и шрифт в объекте Range?
Используйте для этого объекта свойство Font, возвращающее объект Font, с помощью которого вы можете установить характеристики шрифта диапазона. Свойства объекта Font те же, что и атрибуты шрифта, которые вы назначаете в диалоговом окне Font: полужирный, курсив, подчеркнутый и т.п. Для просмотра полного списка свойств Font используйте окно Object Browser.
Как поступить, если мне понадобится распечатать рабочую книгу Excel из программы VBA?
Нет проблем. Для этого следует использовать метод Printout, который имеет следующий синтаксис.
Object. Printout ([From] [, То ] [, Copies] [, Preview])
Здесь Object ссылается на некоторый распечатываемый объект, такой как рабочая книга, рабочий лист или диапазон. Аргумент From является необязательным и указывает на первую страницу, с которой начинается распечатка. Аргумент То также необязателен; он указывает на последний лист, подлежащий распечатке. Аргумент Copies определяет количество требуемых вам копий. Если этот последний аргумент вы опускаете, Excel напечатает одну копию. Аргумент Preview определяет, будет ли показан
712
Неделя 3
экран предварительного просмотра копии материала перед его распечаткой. Экран предварительного просмотра будет показан, если аргумент имеет значение True; если же этот аргумент будет иметь значение False или опушен совсем, экран предварительного просмотра отражен не будет. Метод Printout имеет также аргументы для указания активного принтера и печати в файл. Для получения подробной информации по методу Printout обратитесь к диалоговой справке VBA Excel.
Коллоквиум
Ответы приведены в приложении.
Тест
1.	В чем состоит различие между методами Save, SaveAs и SaveCopyAs объекта Workbook?
2.	Что случится, если вы примените метод Save к новому, ранее не сохранявшемуся объекту Workbook?
3.	Что произойдет, если при применении методов Сору или Move к рабочему листу вы опустите оба аргумента — Before и After?
4.	В чем различие между коллекциями Worksheets и Sheets?
5.	Назовите три метода для возврата указания диапазона рабочего листа. Чем они различаются?
6.	Две следующие процедуры решают схожие задачи. Можете ли вы объяснить, как они работают?
Sub FillAcross()
Dim SheetArray As Variant
SheetArray = Array)"Sheetl", "Sheet2”, "Sheet3")
Worksheets("Sheetl").Range)"Al”(.Value = "Sheet Title"
Worksheets(SheetArray).FillAcrossSheets
Range: = Worksheets("Sheetl").Range("Al“)
End Sub
Sub SpearTest()
Dim SheetArray As Variant
SheetArray = Array)"Sheetl", "Sheet2", "Sheet3")
Worksheets(SheetArray).Select
Worksheets)"Sheetl").Range)"Al").Active
Selection.Value = "Sheet Title"
End Sub
7.	В чем состоит различие между свойствами Value и Formula, применяемыми для возврата содержимого ячейки?
8.	В каких ситуациях при использовании метода Names.Add вместо аргумента RefersTo следует задавать аргумент RefersToRlCl?
9.	Назовите пять методов, которые можно применять для удаления данных из диапазона.
День 19-й. Управление приложением Excel с помощью VBA
713
Упражнения
1.	Напишите процедуру, которая будет сохранять каждую открытую рабочую книгу. Текст этой процедуры должен предусматривать проверку новых и непоименованных рабочих книг. Если книга не имеет названия, процедура должна спрашивать, нужно ли эту книгу сохранить. Если так, то процедура должна отображать диалоговое окно Save As (Сохранить как) для ввода пользователем имени соответствующего файла и пути к месту его сохранения.
2.	Excel не позволяет вырезать и копировать множественные выделения, т.е. множество выделенных и не связанных друг с другом диапазонов. Напишите процедуру, которая позволяла бы вырезать и копировать множество выделенных диапазонов в некоторые позиции другого рабочего листа. (Подсказка. Метод Area возвращает коллекцию, содержащую все диапазоны множественного выделения.)
3.	НАЙДИТЕ ОШИБКУ. Следующая процедура использует три метода Names.Add. Ни одна из инструкций не содержит ошибок, но каждая из них приводит к неожиданному результату. Можете ли вы ответить, почему?
Sub NameTests()
Names.Add Name:="Testl", RefersTo: = "Sheet4!$A$l"
Names.Add Name: = "Test2",
RefersTo: = Worksheets)"Sheet4").Range)"Al :E10")
Names.Add Name: = "Test3“, RefersTo: = 'Продажи за июнь'!А1:А10
End Sub
4.	В листинге 19.13 показано, как вводить данные пользователя из формы диалогового окна в базу данных на рабочем листе. Напишите процедуру, которая выполняла бы обратную операцию: загрузку содержащейся в базе данных записи — строку — в пользовательскую форму диалогового окна. Как и в листинге 19.13, будем предполагать, что имеет место прямое соответствие между полями базы данных и полями диалогового окна; вам не нужно создавать пользовательскую диалоговую форму.
714
Неделя 3
Работа с другими приложениями
Этот урок посвящен использованию Visual Basic for Application для работы с другими, отличными от Excel, приложениями и для управления ими. Например, VBA можно использовать для управления операциями в Word, Access, Outlook и в других компонентах Microsoft Office точно так же, как в приложениях, не входящих в состав Microsoft Office. С помощью VBA можно управлять любыми приложениями, поддерживающими OLE или Automation. На сегодняшнем уроке мы изучим следующие темы.
•	Что представляют собой OLE и Automation, для чего и как их можно использовать.
•	Как связывать и внедрять объекты в рабочий лист.
•	Как работать со связанными и внедренными объектами.
•	Как использовать Automation для управления другими приложениями.
•	Как запускать другие приложения из VBA-процедур.
•	Как передавать события нажатия клавиш в другие выполняемые приложения из Excel VBA.
•	Как объявлять и использовать функции библиотеки динамической компоновки (DLL).
Что такое Automation о OLE
Прежде чем начать изучение использования VBA для работы с OLE и объектами Automation, необходимо хорошо уяснить, что представляют собой Automation и OLE и для чего они предназначены.
Краткая история OLE
OLE — аббревиатура от Object linking and embedding (связывание и внедрение объектов) — позволяет пользователям создавать составной документ. Такой документ представляет собой некий файл, который содержит данные более чем из одного приложения. Например, вы можете создать рабочий лист в Excel, который в дополнение к его собственным числам и формулам будет содержать документ Word и/или рисунок, выполненный в графическом редакторе Paint; такой рабочий лист является составным документом. OLE делает взаимодействие между приложениями более наглядным и простым.
День 20-Й. Работа с другими приложениями
715
В ранних версиях Microsoft Windows вы могли обмениваться данными между приложениями только с использованием буфера обмена (Clipboard). Вы копировали данные из одной программы в буфер, а затем вставляли их из буфера в другое приложение. Если вам нужно было изменить эти данные, вы должны были открыть исходное приложение, открыть соответствующий файл, изменить данные, а затем снова повторить весь процесс копирования и вставки измененных данных.
Хотя использование буфера обмена для передачи данных между приложениями — лучше, чем ничего, это не самый эффективный метод работы. Microsoft представила DDE (аббревиатура от Dynamic Data Exchange — динамический обмен данными) как средство для уменьшения количества операций, выполняемых при передаче информации между двумя различными приложениями и при ее обновлении. С использованием DDE процесс обмена начинается с создания данных в одном приложении — называемом сервером, — и завершается копированием этих данных в буфер обмена. Приложение, поддерживающее DDE, — клиент — использует затем команду Paste Link для вставки данных из буфера в другое приложение. Команда Paste Link устанавливает коммуникационную связь между двумя приложениями. Такая связь обеспечивает впоследствии возможность более или менее автоматического обновления данных на DDE-клиенте после их изменения на DDE-сервере.
Так ие надо
Пусть вас не смущают термины клиентн сервер. Эти термины используются на протяжении всего этого урока, поэтому важно хорошо понимать, что они означают. Клиентом является приложение, которое принимает
\ (получает) данные; сервером является приложение, которое поставляет (передает) данные. Здесь можно про-' вести аналогию с взаимоотношениями заказчика и поставщика. Заказчик (т.е. клиент) запрашивает товары и Ь услуги (в нашем случае — данные) у поставщика этих товаров и услуг (т.е. у поставщика услуг - services рго- vider, или коротко server, т.е. сервера. - Прим, перев.).
Технология DDE значительно усовершенствовала первоначальную методику копирования-вставки, но все еще имела недостатки. Во-первых, вставка данных из буфера обмена, несмотря на использование DDE-связи, представляла собой простую вставку в приложение-клиент с небольшим форматированием или вовсе без такового. Во-вторых, чтобы сделать изменения в DDE-связанных данных, вам необходимо было переключиться на (или заново запустить) приложение-сервер, содержащее связанные данные. В лучшем случае такая процедура просто неудобна, в худшем создает вполне конкретные трудности, если вы не уверены в том, какое приложение является сервером для конкретных DDE-связанных данных, или если вы забыли, какой серверный документ содержит необходимые данные.
Для решения этой проблемы Microsoft избрала новый подход к совместному использованию данных: связывание и внедрение объектов (т.е. OLE). С помощью OLE данные, вставляемые из серверного приложения, появляются в клиентском документе как объект. Каждый объект может быть вставлен одним из двух способов: путем его связывания или внедрения. Оба типа OLE-объектов хранят имя сервера OLE и всю информацию, в которой нуждается сервер. Рассматриваемые типы OLE-объектов имеют следующие характеристики.
•	Связывание. Объект содержит изображение серверных данных и поддерживает связь между клиентом и сервером лучше, чем это делает DDE. Исходные данные остаются в своем файле под контролем серверного приложения. Если данные изменяются, такая связь гарантирует, что объект клиента будет обновлен автоматически. Например, если вы связали документ Word с рабочим листом, Excel будет предлагать обновить связь всякий раз, когда вы открываете содержащую эту связь рабочую книгу. Обычно OLE может поддерживать связь, даже
716
Неделя 3
если файл серверных данных (таких как документ Word) переименован или перемещен в папку на другом диске. Если файл серверных данных уничтожен, то при попытке редактирования связанного объекта Excel отобразит сообщение об ошибке, а в противном случае Excel отобразит последнее изображение серверных данных без выдачи сообщения об ошибке. Обычно вы должны вставлять объект как связанный, чтобы другие приложения могли запрашивать доступ к тому же файлу данных.
•	Внедрение. Объект содержит автономную версию серверных данных, по существу, копию этих данных. Между сервером и клиентом не устанавливается никакой связи, поскольку в этом нет необходимости. Объект клиента содержит не только данные, но и всю основную информацию, относящуюся к серверному приложению, такую как имя приложения, структура файла, коды форматирования. Обычно вы должны вставлять объект как внедряемый, если предполагаете работать с данными только в клиентском приложении.
ПРИМЕЧАНИЕ
Управлять связями OLE в рабочей книге можно вручную, используя команду Правка-Связи для отображения диалогового окна Связи. В этом окне можно указать, будет ли связь обновляться автоматически или вручную, изменить сервер для связи, обновить связи немедленно, а также выполнить другие действия, необходимые для управления связанными данными. Для более подробного ознакомления с диалоговым окном Link обратитесь к диалоговой справке Excel.
Другим преимуществом OLE является то, что во многих случаях вам не требуется сам серверный документ. Вы можете создать данные, запустив серверное приложение из приложения клиента, а затем вставить эти данные в документ клиента как внедренный объект. Это означает, например, что вы можете вставить пустой документ Word в рабочий лист Excel как внедренный OLE-объект, а затем напечатать письмо, резюме, заметку и прочее.
Связываете вы данные или вставляете их, OLE-объект сохраняет форматирование этих данных. Это означает, что объекты OLE появляются в клиентском документе точно такими же, какими они были созданы в серверном приложении. Объект OLE хранит имя его серверного приложения и, если объект связан, имя серверного файла, из которого извлекается объект. Таким образом, вам совершенно не нужно знать, какое приложение и какой файл являются источником внедренных данных. Вы просто дважды щелкаете на объекте: OLE загружает серверное приложение и, если необходимо, открывает соответствующий файл данных.
OLE также предоставляет вам следующие преимущества.
•	Перетаскивание данных между приложениями. Можно перемещать данные между двумя открытыми OLE-приложениями просто путем перетаскивания выделенных данных из одного приложения в другое. Если вы хотите лишь скопировать данные, в процессе перетаскивания удерживайте нажатой клавишу <Ctrl>.
•	Непосредственная вставка. Если вы вставляете OLE-объект из приложения-клиента, клиент активизирует режим непосредственной (in-place) вставки. При этом определенные свойства окна клиента — например, панель инструментов и команды меню — временно замешаются свойствами OLE-сервера; серверное приложение не отображается в отдельном окне. Другими словами, документ остается тем же самым и вид приложения изменяется.
•	Непосредственное редактирование. После двойного щелчка на объекте OLE с целью его редактирования включается режим редактирования на месте. При этом объект поддерживает изменения в документе и заменяет клиентское окно, как и в случае непосредственной вставки.
День 20-й. Работа с другими приложениями
717
•	Automation (автоматизация). Для VBA-программистов наиболее впечатляющей в OLE является автоматизация (технология Automation, которая впервые стала известной как OLE Automation). С помощью автоматизации объекты приложения (такие как Workbook, Worksheet, chart и т.п.) становятся видимыми для других приложений, поддерживающих автоматизацию. В частности, объекты приложения оказываются доступными для VBA. Это делает возможным писать в одном приложении VBA-процедуры, которые будут управлять объектами в другом приложении. Например, процедура VBA в Excel может использовать автоматизацию для управления объектами в другом приложении, таком как Word, Access или Project компании Microsoft. Любое приложение, которое поддерживает автоматизацию, экспортируют одинаковые или все его объекты независимо от того, поддерживает это приложение VBA или нет. Это означает, что вы можете использовать VBA-программы для управления объектами в приложениях, которые не имеют VBA, но могут функционировать как серверы OLE или Automation.
Как к этому приспособить VBA?
Microsoft разработала Visual Basic for Application с учетом OLE и автоматизации; фактически многие из объектов приложений, с которыми вы работаете в VBA, являются также объектами автоматизации (известными как компоненты автоматизации). В частности, главное приложение — такое как Excel — обычно предоставляет специальные объекты и коллекции объектов, которые открывают возможностям OLE доступ к другим приложениям. Такие объекты и соответствующие коллекции имеют множество свойств и методов, которые позволяют создавать и работать с любыми связанными или внедренными данными. На этом уроке мы изучим связанные с OLE объекты и коллекции в Excel.
Кроме того, VBA может использовать объекты в других приложениях, поддерживающих автоматизацию. Это означает, что VBA-процедуры Excel, например, могут вызывать и манипулировать объектами, созданными в другом приложении, так, будто эти процедуры работают в собственном приложении. Технология автоматизации VBA описана в следующих разделах этого урока.
Определение шипов объектных классов
На 7-м уроке мы говорили о том, что объекты в VBA главного приложения разделены на различные классы. Объекты, которые приложения экспортируют в качестве OLE-объектов и компонентов автоматизации, также разделены на различные классы.
Часто вам будет необходимо указать (специфицировать) класс OLE-объекта или компонента автоматизации, прежде чем вы сможете вызвать или использовать этот объект в своей VBA-программе. Для спецификации класса нужно дать VBA информацию, необходимую для поиска определения объекта и выполнения его методов. В порядке спецификации типа класса объекта — называемого также программным идентификатором (programmatic ID) — вам, в первую очередь, необходимо знать, что такое тип класса объекта.
ПРИМЕЧАНИЕ
Класс OLE-объекта или компонента автоматизации точно указывает, какого рода этот объект: рабочий лист Excel, документ Word, база данных Access и т.п. Каждый класс имеет уникальное имя и идентификационный код, которые обозначают его специфический класс OLE- или объекта автоматизации. Windows использует тип класса для определения того, какое приложение является сервером для конкретного связанного или внедренного OLE-объекта и где можно найти программные ресурсы для объектов автоматизации.
778
Неделя 3
Windows поддерживает специальную базу данных реестра Windows, которая содержит, кроме прочего, информацию о приложениях, инсталлированных в системе Windows, включая специфическую информацию об экспортируемых компонентах автоматизации и объектах OLE. Один из секретов работы OLE и автоматизации состоит в том, что база данных реестра содержит все классы объектов, экспонируемых различными приложениями или библиотеками, и прослеживает, какие выполняемые файлы поставляют ресурсы для этих объектных классов. Если ваше VBA-приложение нуждается в услугах компонента автоматизации, VBA запрашивает операционную систему Windows выполнить соответствующую программу для этого объекта; Windows делает это, основываясь на типе класса объекта.
Для выяснения программного 1D некоторого OLE-объекта или компонента автоматизации, доступного в вашей системе, можно найти тип класса этого объекта в базе данных реестра. Кроме того, можно воспользоваться стандартной программой Сведения о системе для получения информации об идентификаторах компонентов автоматизации и OLE-объектов, доступных в вашей компьютерной системе. В следующих нескольких разделах мы рассмотрим, как использовать каждую из этих методик поиска типа объектного класса.
Использование базы данных реестра
База данных реестра содержит сложную информацию о системной конфигурации ваших аппаратных средств и программном обеспечении. Эта база данных фактически состоит из нескольких файлов: System.dat, User.dat и Policy.pol. Файл Policy.pol — необязательный компонент, используемый в сетевых конфигурациях Windows. Но файлы System.dat и User.dat должна иметь каждая Windows-система. Редактор реестра одновременно считывает отдельные файлы данных и отображает их так, как если бы они были единым файлом базы данных.
ПРИМЕЧАНИЕ
База данных реестра Windows 95/98 заменила файлы WIN.INI, SYSTEM.INI и REG.DAT, используемые в предыдущих версиях Windows.
Запуск реуакшора реестра
Чтобы увидеть содержимое базы данных реестра, следует воспользоваться программой редактора реестра, поставляемой с Windows 95/98. (Редактор реестра в меню Пуск обычно не включен.) Для запуска программы редактора реестра выполните следующие действия.
1. Из меню Пуск системы Windows выберите команду Выполнить.
2. В поле Открыть наберите C:\Windows\Regedit.exe (я предполагаю, что ваша операционная система Windows инсталлирована в папке Windows на жестком диске С) и затем нажмите кнопку OK. Windows запустит редактор реестра, который отобразит окно, показанное на рис. 20.1. Конкретный вид древообразной диаграммы в вашей системе может несколько отличаться от показанной здесь в зависимости от конфигурации системы.
День 20-й. Работа с другими приложениями
719
Редактор реестра
ana
Ёеестр Правка Виа Справка
JМой компьютер
г+ ^МПИМШШИИЙМИ
fF '» HKEY_CURRENT_USER
Параметр	Значение
А*](По умолчанию)	(значение не присвоено)
+ 2j HKEY-LOCAL.MACHINE
:+ ГЧ HKEY.USERS
3 I HKEY_CURRENT_CONFIG
* 2J hkey.dyn.data
		 		
Рис. 20.1. Окно редактора реестра содержит древовидную диаграмму с данными об аппаратных средствах и программном обеспечении вашего компьютера
Редактор реестра в левой панели своего окна отображает древовидную диаграмму содержимого базы данных реестра, а в правой панели — конкретные данные, содержащиеся в выделенном входе базы данных. Обычно многие ветви этого дерева показывают строку value not set в столбце Данные на правой панели, как это делает ветвь HKEY_CLASSES_ROOT на рис. 20.1. Это, однако, не означает, что данная ветвь базы данных не содержит введенных данных; напротив, обычно это означает, что выделенная ветвь дерева базы данных реестра предшествует дополнительным ветвям.
Поиск объектных классов в базе данных реестра
Информацию о компонентах автоматизации и объектах OLE база данных реестра хранит в ветви HKEY_CLASSES_ROOT дерева базы данных (на рис. 20.1 эта ветвь выделена). Чтобы увидеть зарегистрированные в вашей системе объекты OLE и автоматизации, выполните следующие действия.
Щелкните на квадратике левее названия ветви HKEY_CLASSES_ROOT древовидной диаграммы для отображения других ветвей, примыкающих к данной ветви. Редактор реестра теперь отобразит список всех зарегистрированных типов файлов и OLE-объектов. (Этот список обычно очень длинный.)
Прокручивайте появившийся в левой панели окна список до тех пор, пока не найдете имен классов зарегистрированных OLE-объектов. Поскольку перед большинством имен класса OLE-объекта имеется много различных расширений файлов и они расположены в алфавитном порядке, вам потребуется некоторое время для прокрутки списка, прежде чем вы обнаружите имя класса своего OLE-объекта.)
Вы можете распознавать типы класса объекта OLE в реестре, поскольку они обычно начинаются с имени серверного приложения. На рис. 20.2 показан раскрытый
720
Неделя 3
список ветви HKEY_CLASSES_ROOT базы данных реестра, прокрученный до места, где появляется список OLE-объектов и компонентов автоматизации Excel. На рис. 20.2 показан выделенный объектный класс Excel.Application.9; этот объектный класс представляет непосредственно приложение Excel 9. (Как указывалось выше, Excel 2000 является Excel версии 9, a Excel 97 — это Excel версии 8.)
sg,‘ Редактор реестра
Еэестр Правка вид Справка
+ А I EftectBvr.EffectBvr	w] Параметр \	‘ J 1 Значение _	.'У	
EftectBvr.EftectBvr.l s i (По умолчанию)	"Приложение Microsoft Excel"
i+i I EventSystem.EventClass + ' 1 EventSystem.EvenlClass.1
EventSystem.Eventpublisher t	1 EventSystem.EventPubhsher
i+: _J EventSystem.EventSubscnpt i+. I EventSystem EventSubscnpt ' 41 / I EventSystem.EventSystem +	1 EventSystem EventSystem 1
4	1 E<cel Addin
it: I Excel.Application	—J
+ Excel.Apphcation.8 4	~	~
+ £j Excel Backup •+•	Excel.Chart
4 2J Excel Chart 5 +	1	Excel.Chart.8
+ ?.J Excel.CSV + I Excel.Dialog l+j 2J Excel.DIF '+i _ l Excel Macrosheet t+j '"l Excel Sheet Ж ::i.« Cvr.nl	C
<1	• I
Мойкомпыотб{йНКЕУ_С1А55Ё5_пЬоТ\Ехсе1Аррйса1>оп.9
ЯНН
Puc. 20.2. Ветвь HKEYCLASSESR.OOT базы данных реестра содержит зарегистрированные в вашей системе классы OLE-объектов
Заметьте, что данные, появившиеся в правой панели окна редактора реестра на рис. 20.2, дают имя выбранного объектного класса: Microsoft Excel Application. Обратите также внимание на появившиеся в левой панели окна другие OLE-объекты и компоненты автоматизации: Excel.Addin, Excel.Chart.5, Excel.Sheet.8 и т.п. Каждый из этих элементов является именем объекта OLE или автоматизации; это имя следует использовать для спецификации типа объектного класса.
После того как в базе данных реестра вы нашли нужный вам объектный класс, запишите его имя для будущего использования. В таблице 20.1 перечислены типы классов некоторых общих объектов.
Таблица 20.1. Типы классов общих объектов
Объект
Приложение Microsoft Access
Приложение Microsoft Excel
Диаграмма Excel
Рабочий лист Excel
Тип класса
Access. Application Excel.Application Excel.Chart Excel.Sheet
День 20-й. Работа с другими приложениями
721
Окончание табл. 20.1
Объект	Тип класса
Диаграмма Microsoft Graph Презентация MS PowerPoint Слайд MS PowerPoint Проект MS Project Приложение Microsoft Word Документ MS Word Рисунок MS Word Объект WordArt Объект Object Packager) Рисунок Paintbrush Рисунок MS Paint Звук	MSGraph.Chart PowerPoint.Show PowerPoint. Slide MS Project. Project Word. Application Word. Document Word. Picture MSWordArt Package Pbrush Paint. Picture SoundRec
Так надо
Для определения того, поддерживает ли конкретное приложение технологию OLE и является ли оно компонентом автоматизации, обращайтесь к документации по этому приложению либо в подразделение технической поддержки разработчика.
Так не надо
Не редактируйте, не переименовывайте и не делайте каких-либо изменений в содержимом базы данных реестра, если только вы не являетесь экспертом по Windews. Некорректное или ошибоиое вмешательство в эту базу данных может привести к тому, что ваш компьютер не буд ет загружаться или нормально функционировать.
Не следует думать, что каждый элемент, который вы видите в базе данных реестра, обладает всеми возможностями OLE и автоматизации. Некоторые из них являются только OLE-серверами, такими как Microsoft Draw, а некоторые - только OLE-клиентами. Другие же вообще не поддерживают OLE.
Использование утилиты Сведения и системе
Другой способ получения информации о компонентах автоматизации и объектах OLE, инсталлированных на вашей компьютерной системе, заключается в использовании утилиты Сведения о системе, которая включена в Microsoft Office. (Утилитой называют небольшое приложение, которое выполняет единственную задачу. Например, утилитой является программа Калькулятор.)
Получить доступ к утилите Сведения о системе можно двумя путями.
•	С помощью команд Справка - О программе, имеющихся в любом приложении Microsoft Office.
•	Путем запуска программы Msinfo32.exe, которая хранится в папке C\Program Files\Common Files\Microsoft Shared\MSInfo на вашем жестком диске С (или другом).
722
Неделя 3
Для запуска утилиты Сведения о системе с помощью команд Справка - О программе выполните следующие действия.
1. В любом приложении Microsoft Office (например, Excel) выберите команды Справка - О программе. Приложение отобразит диалоговое окно О программе, в котором будут представлены имя и версия приложения, а также относящаяся к приложению информация об авторских правах. Конкретный вид диалогового окна О программе варьируется от приложения к приложению, но каждое такое окно содержит кнопки ОК, О системе и Поддержка.
2. Щелкните на кнопке О системе в диалоговом окне О программе. Приложение запустит утилиту Сведения о системе, которая отобразит окно, похожее на показанное на рис. 20.3.
Команды Справка - О программе в редакторе VB Editor также отображают диалоговое окно О программе, которое содержит кнопку О системе для активизации утилиты Сведения о системе.
Утилита Сведения о системе в левой панели окна отображает некоторую информацию о вашей системной конфигурации в виде древовидной диаграммы. В правой панели окна отображается конкретная информация о выделенной в левой панели категории. Щелкая на квадратике левее входов в ветви диаграммы, можно раскрывать или сжимать соответствующую этим ветвям информацию.
[5J Сведения о системе	ГГ7Г|
. файл Древка Вид Сервис . Справка	
о| el t IM	
Сведения о системе	Объект •' Описание	Программе	f л.
it- Ресурсы аппаратуры	Package	Пакет	packager.exe
!+ Компоненты	midfile	Файл MIDI	C.\WINDOWS\mplayer.exe /mid
Программная среда	SounoRec	Звукозапись	C.\WINDOWS\sndrec32.exe
а Драйверы	avifile	Видеоклип	C-\WlNDOWS\mplayer exe /avi
Загруженные 16-разрядные moi	PBrush	Paintbrush Picture	C\PROGRA~1\ACCESS~1\MSPAINT.EXE
Загруженные 32-разрядные moi	Wordpad.Doc... Документ WordP...	C:\PROGRA~1\ACCESS~1\WORDPAO.EXE
Выполняемые задачи	Imaging Docu... Документ Image	C:\WINDOWS\Kodaklmg.Exe
Автоматически загружаемые т !-• Системные ловушки й Регистрация OLE INI-файл	Wanglmage.D.. Документ Image	C.\WlNDOWS\Kodaklmg.Exe
	RegWizCtrl Re.. RegWizCtrl	C\WINDOWS\SYSTEM\REGWIZC.DLL
	MSPhotoEd.3 Microsoft Photo Ed...	C:\Program Files\Common Files\Microsoft...
	MSphotoEdSc... Microsoft Photo Ed..	C\Program Files\Common Files\Microsoft ...
	Excel.Chart 8 Диаграмма Mier...	C:\PROGRA~l\MICROS~2\OFFICE\excel....
Реестр	Excel.Sheet.8	Лист Microsoft Ex... MSMap 8	Microsoft Map MS_ClipArt_G.. Microsoft Clip Gall.. Word.Docume... Документ Micros.. Word.Picture 8 Рисунок Microsoft... PAlNT.PaintCt... Paint Control Mathcad	Mathcad Document MathConnex.D... MathConnex Project MalhConnex.T.. MathConnexText .. CorelDraw.Gra.. CorelDRAW? 0 Gr.. CcrelDraw.CM. CorelDRAW?.!] Ex... Imaging Доку. Документ Image ActoExch.Doc... Adobe Acrobat Do...	C.\PROGRA~l\MICROS“'2\OFFICE\excel.... C\PROGRA~1\COMMON~1\MICROS~1\D... C.\PROGRA~1\COMMON~1\MICROS~1\A... C.\PROGRA~1\MICROS~2\OFFICE\WINW... C:\PROGRA~1\MICROS~2\OFFICE\WINW... C.\GAMES\MPATH\SYSTEM\PAINT.OCX C:\PROGRA~1\MATHSOFT\MATHCAD\M... C\Program Files\MathSoft\Mathcad\MATH... C:\Program Files\Ma(hSoft\Mathcad\TEXT... C\COREL\DRAW70\PROGRAMS\COREL __ C:\COREL\DRAW70\PROGRAMS\COREL... ?! 1 C \WINDOWS\KODAKIMG EXE C.\Acrobat3\ReBder\AcroRd32.exe
	<1		1	21
Д ля справки нажмите Ft	ftекущие сведения о системе
Рис. 20.3. Утилита Сведения о системе позволяет просматривать список инсталлированных OLE-объектов
День 20-Й. Работа с другими приложениями
723
На рис 20.3 показаны развернутая ветвь Регистрация OLE и выделенная подветвь Реестр. Правая панель отображает список всех OLE-объектов, инсталлированных в вашей системной базе данных. Заметьте, что идентификаторы типа класса для рабочих листов Excel (Excel.Sheet.8) и документов Word (Word.Document.8) представлены в списке Объект. Однако объекты приложений Excel и Word не перечислены.
Представляемая в окне Сведения о системе информация не настолько полна, как информация, которую вы могли бы получить непосредственно из базы данных реестра, но использование утилиты Сведения о системе более просто и более безопасно, чем использование редактора реестра. Для многих целей выбор типа класса объекта в окне Сведения о системе намного быстрее и нагляднее, чем при использовании базы данных реестра.
Добавление связанных и внедренных объектов
OLE значительно повышает возможности ваших приложений. Но за такую усовершенствованную технологию конечный пользователь платит дополнительной сложностью. Особенно это относится к начинающим пользователям, которые могут чувствовать себя некомфортно с множеством возможных вариантов выбора для обычной OLE-операции. Например, если вам нужно скопировать данные из серверного OLE-приложения, команда Paste Special дает вам множество выборов: вы можете вставлять информацию либо как объект, либо как рисунок, либо как текст (в зависимости от вида данных), вы можете вставлять связанные или несвязанные данные, вставлять данные как пиктограмму или как полное изображение.
Чтобы облегчить решение таких задач пользователям ваших программ, вы можете использовать манипулирующие OLE-объектами VBA-программы, поскольку VBA дает вам контроль над каждым из принимаемых решений на процедурном уровне. Например, вы можете предоставить вашим пользователям единственную команду или единственную кнопку на панели инструментов для создания конкретного OLE-объекта, и скрыть все детали и возможности выбора, которые используются при создании этого объекта. В следующих разделах мы рассмотрим, как создавать объекты OLE с помощью VBA.
Объекты Shape и OLEFormat
В Excel объекты Shape находятся в слое рисования рабочего листа. Слой рисования представляет собой участок рабочего листа, который включает линии, прямоугольники, растровые рисунки (bitmap), рамки и другие рисованные формы. Название слой здесь используется постольку, поскольку рассматриваемые рисованные формы существуют независимо от данных на рабочем листе, но отображаются поверх листа подобно тому, как архитекторы и инженеры используют чистые листы для создания перекрывающихся слоев на своих планах и схемах. Объекты в слое рисования могут быть закреплены или не закреплены в зависимости от конкретного местоположения на рабочем листе.
Объект Shape может быть OLE-объектом, каким-либо рисованным (чертежным) объектом, объектом Autoshape или объектом-рисунком. На этом уроке мы сосредоточим наше внимание только на объектах типа Shape, используемых для хранения объектов OLE.
Каждый Shape-объект имеет свойство OLEFormat, которое возвращает объект OLEFormat. Этот объект содержит информацию об OLE-характеристиках объекта Shape. Объект OLEFormat имеет свойство ProgID, которое хранит программный идентификатор этого конкретного OLE-объекта.
В следующих нескольких разделах этого урока я объясню, как добавлять OLE-объекты в коллекцию из разных источников.
724
Неделя 3
Использование метода AddOLEObject о коллекции Shapes
В Excel для добавления OLE-объектов в коллекцию Shapes используется метод AddOLEObject. Вы можете использовать этот метод для добавления любого связанного или внедренного OLE-объекта к коллекции Shapes. Ниже мы рассмотрим, как использовать метод AddOLEObject и чем этот метод различается в Word и Excel.
Метод AddOLEObject в Excel имеет следующий синтаксис.
Object. AddOLEOb j ect ([ Clas sType ], [ FileName ], [ Link ]
[DisplayAsIcon], [IconFileName], [Iconindex], [IconLabel], [Left], [Top], [Width], [Height]
Здесь Object — это выражение, содержащее ссылку на коллекцию Shapes. Для возврата ссылки на эту коллекцию используется свойство Worksheet.Shapes. Описание аргументов метода AddOLEObject приведено в следующем списке.
•	ClassType. Этот необязательный аргумент является строковым выражением, определяющим имя класса объекта, который вы хотите вставить. Используйте этот аргумент, когда вам нужно вставить новый (пустой или чистый) OLE-объект. Например, для вставки пустого рабочего листа Excel в качестве аргумента ClassType используется строка Word.Document. (В разделе “Определение типов объектных классов” выше в этой главе вы познакомились с тем, как определять типы классов различных OLE-объектов.)
•	FileName. Этот аргумент также необязателен и является строковым выражением, указывающим имя файла объекта, подлежащего вставке; имя файла должно иметь расширение, соответствующее зарегистрированному серверному OLE-приложению. Аргумент FileName используется в случае вставки существующего файла в качестве OLE-объекта. Например, если вы хотите вставить существующий файл документа Word в рабочий лист Excel, в качестве аргумента FileName используйте строку типа C:\Accounts\MyWords.doc (здесь предполагается, что вставляется файл с именем MyWords.doc, расположенный в папке Accounts на диске С). Если вы используете аргумент ClassType, то аргумент FileName игнорируется.
•	Link. Этот необязательный аргумент указывает, будет создаваемый объект связанным или внедренным. Если значение этого аргумента указано как True, то создаваемый объект будет связанным. Если же это значение False (это значение указанного аргумента предусмотрено по умолчанию), то OLE-объект под именем FileName будет внедренным. (Внедрение объекта предполагает создание его независимой копии.) При задании некоторого значения аргумента ClassType аргумент Link должен быть опущен или его значение должно быть False.
•	DisplayAsIcon. Данный аргумент определяет форму отображения объекта. Если он имеет значение True, то объект будет отображаться в виде пиктограммы. Если же аргумент DisplayAsIcon имеет значение False или опущен вообще, то объект будет отображаться в своей нормальной форме.
•	IconFileName. Этот аргумент имеет смысл имени файла пиктограммы и используется только тогда, когда аргумент DisplayAsIcon имеет значение True. Аргумент IconFileName представляет собой строку с именем файла пиктограммы, которую вы хотите отображать в качестве вставленного OLE-объекта. Если вы опустите этот аргумент или указанный файл не содержит какой-либо пиктограммы, то по умолчанию будет использоваться пиктограмма OLE-класса.
День 20-й. Работа с другими приложениями
725
•	Iconindex. Некоторые файлы содержат более чем одну пиктограмму; для выбора пиктограммы, которую вы намерены отображать, служит аргумент Iconindex. Пиктограммы нумеруются начиная с 0: первая пиктограмма в файле имеет номер 0, вторая, — 1 и т.д. Iconindex используется только тогда, когда аргумент DisplayAsIcon имеет значение True и в список аргументов метода включен аргумент IconFileName. Если вы указываете номер пиктограммы, которого не существует, будет использоваться пиктограмма с номером 1 (т.е. вторая пиктограмма в файле). По умолчанию значение аргумента Iconindex устанавливается равным 0.
•	IconLabel. Этот необязательный аргумент служит для назначения метки пиктограмме OLE-объекта (только если имеет значение True). Аргумент IconLabel должен представлять собой некоторое строковое выражение.
•	Left и Тор. Для установки позиции левого верхнего угла объекта относительно левого верхнего угла рабочего листа (не экрана!) применяются эти необязательные аргументы. Объект в коллекции Shapes позиционируется с использованием позиций, отсчитываемых от левого (Left) и верхнего (Тор) краев рабочего листа, так что объект при отображении и распечатывании листа будет находиться в одном и том же относительном положении. По умолчанию значения аргументов Left и Тор устанавливаются равными нулю, т.е. пиктограмма будет расположена впритык к верхнему и левому краям рабочего листа.
•	Width и Height. Эти необязательные аргументы устанавливают начальные ширину и высоту OLE-объекта, измеряемые в позициях рабочего листа, так что при отображении и печати объект будет иметь одни и те же относительные размеры. По умолчанию значения аргументов Width и Height выбираются в зависимости от самого вставляемого объекта и от того, будет ли он отображаться в виде пиктограммы.
Так надо
Помните, что в Excel только объекты Worksheet и Chart могут содержать коллекции Shapes.
Так ие надо
Не забывайте, что свойства Shapes объектов Worksheet и Chart служат для обращения к разным коллекциям Shapes, принадлежащим разным объектам.
Вставка нового внедренного объекта
Для вставки в рабочий лист нового внедренного объекта необходимо выполнить метод AddOLEObject коллекции Shapes с использованием аргумента ClassType. В листинге 20.1 приведен один пример вставки нового объекта — документа WordPad — в рабочий лист Excel.
Листинг 20.1. Исиользиваиие метода AddOLEObject для виедреиия нового объекта в рабочий лист Excel
1:	Sub InsertNewWordPadDocument))
2:	'Создает и вставляет документ Wordpad в активную ячейку
3:
4:	Dim Ans As Integer
726
Неделя 3
5:
6:	Ans = MsgBox(prompt:="BcTaBHTb новый докумнт Wordpad?'',
7:	Buttons:=vbOKCancel + vbQuestion,
8:	Title:="Вставка документа Wordpad'J
9:	If Ans = vbOK Then
10:	Application.StatusBar = "Вставка документа	Wordpad..."
11:	Worksheets("Sheetl"(.Shapes.AddOLEObject _
12:	ClassType:=''Wordpad. Document. 1"
13:	Application. StatusBar = False
14:	End If
15:	End Sub
Процедура InsertNewWordPadDocument, представленная в листинге 20.1, разработана специально для вставки документа Microsoft WordPad в качестве нового объекта в рабочий лист Excel. Процедура начинается с отображения диалогового окна для получения от пользователя конфигурации вставки объекта (в строках с 6 по 8). Инструкция If.. .Then в строке 9 анализирует ответ пользователя. Если пользователь выбрал кнопку ОК, оператор MsgBox возвращает значение vbOK и выполняются строки с 10 по 13. Строка 10 в панели состояний отображает для пользователя сообщение о том, что будет вставляться объект WordPad. Строки 11 и 12 содержат оператор вызова метода, вставляющего новый OLE-объект WordPad в рабочий лист с именем Sheetl. Наконец, в строке 13 управление панелью состояний передается Excel. На рис. 20.4 показан результат выполнения рассматриваемой процедуры.
Ig MicrosoH Excel-Д eHb20.xls
ifel £.айл (Правка Вид Вставка Формат Сервис Данные Оно Справка	. _ |g| х|
7о	* I. < 4. Ji ЙЛ ’0.
Anal	-rW т X X 3 Е g S Ш ® % М й
__________ВЗ______-j Ж'	_____________________________________________________________
-	..3 I......-	 g	р	-	j-j-	-	-=
.222
3 | I	|
ажа;	||
SBB
ЙЯ
8 --.................................................................	.ед
111
ИИ	'	||
иж
ЙЯ
ЙЯ	; 1М
14
15
ЙЯ	:	..й
ЙН	.	и
М	Sheetl/sheets/sheets/	|« |	|	»|Г
Готово	а11Н8|мйМ|[|ИД
Рис. 20.4. Процедура InsertNewWordPadDocument в листинге 20.1 вставляет этот пустой документ WordPad в рабочий лист
День 20-й. Работа с другими приложениями
727
Если же вы добавляете объект как пиктограмму, вид вставляемого OLE-объекта в значительной степени зависит от серверного приложения. В некоторых случаях серверное приложение загружает в память и представляет чистый или пустой объект, как это делает WordPad в примере листинга 20.1. Другие серверные приложения отображают некоторый определенный по умолчанию объект; например, когда вы вставляете новый объект, Microsoft Data Мар отображает карту мира. В большинстве случаев, однако, в качестве объекта вы получите лишь контейнер, т.е. чистую картинку, как показано на рис. 20.4.
В Excel при выполнении метода AddOLEObject новый объект вставляется в рабочий лист, но не активизируется.
Так надо
Помните, что если аргументы метода AddOLEObject вы заключите в круглые скобки, то этот метод будет действовать как функция, возвращая объектную ссылку на вновь вставленный Shape-объект. Вы можете одновременно создавать новый объект и назначать для него объектную переменную, т.е. переменную для ссылки на это объект: Set aShape = aSeet.Shapes.AddOLEObject(ClassType: - "MSMap.8'*)
Если вы хотите быть уверенным, что новый объект открыт и пользователь вашей программы может немедленно начать работать с этим новым объектом, используйте метод Activate. Метод Activate принадлежит объекту OLEForinat, который содержится в новом Shape-объекте. Ниже приведен пример использования метода Activate (aSheet является объектной переменной, ссылающейся на объект Worksheet):
aSheet.Shapes.AddOLEObject(ClassType = "MSMap.8).OLEForinat .Activate
Помните, что время, требуемое для вставки объекта, зависит от того, выполняется ли в этот момент серверное приложение. Если это так, то вставка может занять лишь секунду или две. В противном случае вам придется ждать, пока серверное приложение загрузится в память.
Так не надо
Не забывайте, что расположение нового объекта по умолчанию определяется текущей активной ячейкой; Excel использует активную ячейку для позиционирования верхнего левого угла объекта. Перед тем как вставлять новый объект, активизируйте ту ячейку, относительно которой вы хотите разместить ваш OLE-объект.
Вставка существующего файла как внеуренного объекта
Для внедрения OLE-объекта из существующего файла применяется метод AddOLEObject с аргументом FileName. При этом аргумент Link должен либо иметь значение False, либо быть опущен вообще. Листинг 20.2 демонстрирует, как это делается в Excel.
Листинг Z0.2. Мспользованив метода AddOLEOhlect для вставки сцщвствующвгп фаОла
1:	Sub EmbedPaintPicturef)
2:	'Вставляет существующий файл MSPaint
3:
4:	With Worksheets) "Sheetl") .Shapes,
5:	.AddOLEObject FileName:="C:\WIND0WS\ny3npbKH.BMP", _
6:	DisplayAsIcon:=True,
7:	IconFileName:="C:\Program	Files"	&
8:	"\Accessories\MSPaint.EXE",
9:	Iconindex:=0,
10:	IconLabel:="Пузырьки.bmp	-	Открывается двойным щелчком"
11:	End With
12:	End Sub
728
Неделя 3
Эта процедура внедряет растровый рисунок Microsoft Paint в рабочий лист Excel путем указания имени файла для метода AddOLEObject(cTpoKa 5) без аргумента Link. (Файл Пузырьки.Ьтр — это файл обоев, поставляемый с Windows; он находится в папке, в которой вы инсталлировали Windows — для работы в своей системе вам может понадобиться изменить путь к этой папке в строках 5—7 этой процедуры. Можете также использовать любой файл .bmp, который у вас есть.) Процедура также отображает объект как пиктограмму путем присвоения аргументу DisplayAsIcon значения True (строка 6) и указания файла пиктограммы (выполняемого MS Paint в строках 7 и 8), ее индекса (строка 9) и метки (названия), которая появляется внизу пиктограммы (строка 10). Рис. 20.5 иллюстрирует вставленную рассматриваемой процедурой пиктограмму с меткой.
Q Microsoft Excel - День20.х1н
бь) файл {Tpast-a Вид Встдвкв Формат Сервис Денные ркно Справка	_ |gl х|
оtg«diiani,00’-'•з J	”
~ ЗдПаГ-	у n ~ x к 3
A В | C ’ D Ё \ F G H ' I T
3 | 4 5 6 7 8 9 10 11 12, 13 14 15 16 17 M «W.l Готово
Puc. 20.5. Процедура EmbedPaintPicture в листинге 20.2 внедряет файл Пузырьки.Ьтр с растровым рисунком в рабочий лист как пиктограмму с подписью
Вставка существующегв файла как связанного объекта
Если вы предпочтете вставить существующий файл как связанный, а не как внедренный объект, то аргументу Link метода AddOLEObject следует присвоить значение True, как показано в листинге 20.3.
Лисшииг 20.3. Исиояьзование метода AddOLEOb|ecf для встааки сдщоствующего файла как соязанноао оОьекта
1: Sub LinkPaintPicture()
2: 'Вставляет ВИР-файл как связанный объект 3:
4:	Application.Screenupdating = False
День 20-й. Работа с другими приложениями
729
5:	With Worksheets("Sheetl")
6:	.Activate
7:	.Cells(2, 2).Select
8:	.Shapes.AddOLEObject	FileName:="C:\Windows\Jlec.bmp",
9:	Link:=True
10:	End With
11:	Application.ScreenUPdating = True
12:	End Sub
Процедура LinkPaintPicture вставляет файл с растровым рисунком в рабочий лист Excel как связанный объект. Несколько первых операторов отключают отображение экранных изменений (строка 4), активизируют рабочий лист (строка 6) и затем выделяют ячейку, в которой будет отображен верхний левый угол объекта (строка 7).
Далее, для вставки файла выполняется метод AddOLEObject (строки 8 и 9). Аргумент FileName указывает растровый файл, а аргументу Link присвоено значение True для установления связи с этим файлом. (Файл Лес.bmp является одним из поставляемых с Windows файлов с узорами и находится в той же папке, в которой инсталлирована Windows. Вам может понадобиться изменить имя папки и/или диска для работы с этой процедурой в вашей системе; вы можете также использовать любой .bmp-файл, который у вас имеется.) На рис. 20.6 показан связанный растровый объект, вставленный рассматриваемой процедурой.
ЕЗ Microsoft Excel - День20.х1з
НЕЗЕ!
И < ► М\Sheetl/Sheet;/Sheet4 /	|«|	I НГ~
Готово	ГОИ
Рис. 20.6. Процедура LinkPaintPicture в листинге 20.3 вставляет этот .bmp-файл в рабочий лист
730
Неделя 3
Так надо
Связывайте файлы всегда, если хотите, чтобы данные в связанном изображении можно было обновлять и если ; уверены, что связанный файл всегда будет размещаться в том месте, откуда он был вставлен.
Если же вы не уверены, что внешний файл всегда будет доступен или если у вас нет желания или нужды обновлять связанные данные, применяйте внедрение файлов.
Коллекция OLEOb jects
Кроме коллекции Shapes, Excel поддерживает также коллекцию OLEObjects. Это коллекция всех связанных или внедренных OLE-объектов на рабочем листе; каждый объект в коллекции OLEObjects является объектом OLEObject. В отличие от коллекции Shapes, которая содержит линии, прямоугольники и другие элементы чертежного слоя, коллекция содержит только связанные или внедренные объекты OLE. Для добавления объекта OLEObject в коллекцию OLEObjects служит метод Add, описываемый следующим синтаксисом.
Object.OLEObjects.Add([ClassType], [Filename], [Link], [DisplayAsIcon], [IconFilename], [Iconindex], [IconLabel], [Left], [Top], [Width], [Height])
В рассматриваемом методе Object представляет некоторую допустимую объектную ссылку на объект Worksheet. Аргументы метода OLEObjects.Add имеют тот же смысл, что и аргументы метода Shapes.AddOLEObject. Этот последний метод мы рассматривали раньше в ходе этого урока; описание его аргументов приведено в предыдущих разделах. В листинге 20.4 показана процедура из листинга 20.2, модифицированная для использования коллекции OLEObjects вместо коллекции Shapes.
Если вы пытаетесь программировать OLE-объекты в версии Excel, более ранней чем Excel 97, используйте коллекцию OLEObjects вместо коллекции Shapes.
Лисшииг 20.4. Кспользоввние метода OLEOb|Bcts.Add для вставки внедреииого растрового фаПла в раОочиП лист Excel
1:	Sub EmbedPaintPicturef)
2:	'Вставляет существующий файл MSPaint
3:
4:	With Worksheets("Sheetl")
5:	.OLEObjects.Add FileName:="C:\WIND0WS\ny3HpbKH.BMP",
6:	DisplayAsIcon:=True, _
7:	IconFileName:="C:\Program	Files"	&
8:	“\Accessories\MSPaint.EXE",
9:	lconlndex:=0,
10:	IconLabel.-^'Пузырьки.bmp	-	Открывается двойным щелчком"
11:	End With
12:	End Sub
День 20-й. Работа с другими приложениями
731
Эта процедура работает точно так же, как и процедура, представленная в листинге 20.2, за исключением того, что теперь используется коллекция OLEObjects и метод Add для вставки внедренного OLE-объекта (строка 5). Сравните эту процедуру с представленной в листинге 20.2 и заметьте, что в них используются одни и те же аргументы Filename, DisplayAsIcon, IconFilename, Iconindex и IconLabel; различаются только объектная коллекция и используемый метод.
Так надо
Помните, что коллекции Shapes и OLEObjects частично перекрываются (совпадают): каждый объект коллекции ! OLEObjects содержится также в коллекции Shapes, а каждый OLE-объект коллекции Shapes содержится и в ; коллекции OLEObjects. Используйте коллекцию OLEObjects при необходимости сослаться только на OLE-объекты, находящиеся на рабочем листе, без вывода на экран других объектов, входящих в коллекцию Shapes.	;
Так не надо
Не забывайте, что коллекция OLEObjects содержит объекты OLEObject. Такие объекты имеют несколько иные свойства, чем объекты Shape и OLEFormat.
Работа со связанными а внедренными объектами
После того как вы связали или внедрили OLE-объект в рабочий лист, вы можете — кроме прочего — изменять размер и форматирование этого объекта, обновлять данные объекта OLE, редактировать его или удалять с рабочего листа.
Управление связанными или внедренными OLE-объектами производится с использованием свойств и методов самого объекта Shape и объекта OLEFormat, содержащегося в Shape-объекте. При работе в Excel у вас также появляется возможность манипулировать связанными и внедренными объектами OLE с помощью свойств и методов объекта OLEObject.
Доступ к OLE-объектам
Перед тем как перейти к изучению техники манипулирования OLE-объектами, вам необходимо узнать, как ссылаться на эти объекты после их вставки в рабочий лист. Одним из способов сделать это является использование в качестве посредника коллекции Shapes.
Object.Shapes(Index)
Здесь Object представляет допустимую ссылку на объект Worksheet, содержащий интересующий вас OLE-объект. Аргумент Index может иметь следующий смысл.
•	Число, представляющее объект, который вы хотите использовать. Значение 1 означает первый Shape-объект, вставленный на рабочий лист; 2 — второй вставленный на рабочий лист Shape-объект и т.д.
•	Имя (в виде текста) объекта Shape, который вы хотите использовать. Каждый добавленный в коллекцию Shapes OLE-объект получает имя в форме Object п, где п — число, соответствующее порядку, в котором объект был вставлен в рабочий лист. Первый OLE-объект является объектом Object 1, второй — объектом Object 2 и т.д.
732
Неделя 3
Примеры организации доступа к объектам коллекции Shapes приведены ниже в этом уроке.
VBA Excel предоставляет также возможность доступа к отдельным OLE-объектам через коллекцию OLEObjects. С этой целью применяется тот же синтаксис, какой мы применяли при использовании в качестве посредника коллекции Shapes (в следующих примерах оба выражения ссылаются на один и тот же объект в рабочей книге).
ActiveWorkbook .Worksheets (" Sheetl"). OLEObjects (1)
ActiveWorkbook. Worksheets! "Sheetl") .OLEObjects! "Object 1")
Помните, что коллекция Shapes может содержать и объекты, отличные от объектов OLE. Если OLEFormat объекта Shape имеет значение Nothing, то данный Shape-объект не является объектом OLE.
Для определения того, является ли конкретный OLE-объект связанным или внедренным, проверьте свойство объекта Shape. Чтобы выяснить тип Shape-объектов, используйте зарезервированные константы класса MsoShapeType (находящиеся в библиотеке Office): для связанных OLE-объектов свойство Туре имеет значение msoLinkedOLEOb ject, а для внедренных - значение msoEmbeddedOLEOb ject.
Переименовывайте OLE-объекты после их вставки, давая им осмысленные имена. Ни числовая (например, Shapes (3)), ни строчная (Shapes (“Object 3")) индексация в коллекции Shapes (и в Excel, в коллекции OLEObjects) не несут никакой смысловой нагрузки и легко забываются. Чтобы избежать этого, либо сохраняйте ссылку на объект в переменной уровня модуля, либо изменяйте свойство Name объекта (подробнее об этом см. следующий раздел ‘Использование свойств OLE-объекта").
Использование свойств OLE-объекта
Как вы теперь знаете, коллекция Shapes содержит Shape-объекты. Подобно любым другим объектам в VBA, объекты Shape имеют набор свойств, которые вы можете использовать в своих VBA-программах для изменения вида и поведения.
Одним из свойств объекта Shape является свойство OLEFormat, которое возвращает OLEFormat-объект. Этот объект содержится в объекте Shape и хранит дополнительную информацию, требуемую для полного представления внедренного OLE-объекта. Другим свойством Shape-объекта является свойство LinkFormat, которое возвращает, как теперь нетрудно догадаться, LinkFormat-объект. Этот объект также содержится в объекте Shape и хранит некоторую дополнительную информацию, необходимую для представления связанного OLE-объекта.
В табл. 20.2 перечислено большинство свойств общего пользования, связанных с О LE-объектами.
День 20-й. Работа с другими приложениями
733
Таблица 20.2. Свойства, связанные с OLE-объектами
Свойство	Объект	Значение
AutoUpdate	LinkFormat	True, если OLE-объект автоматически обновляет данные после их изменения на сервере
BottomRIGHTCell	Shape	Возвращает объект Range, указывающий на ячейку рабочего листа, которая находится под правым нижним углом Shape-объекта
Height	Shape	Устанавливает или возвращает значение в пунктах положения верхнего края Shape -объекта
Left	Shape	Устанавливает или возвращает положение левого края объекта Shape относительно левого края рабочего листа
Line	Shape	Возвращает объект LineFormat. чьи свойства управляют видом рамки OLE-объекта
LinkFormat	Shape	Возвращает объект LineFormat, чьи свойства хранят информацию о связанном OLE-объекте
Name	Shape	Имя объекта. Используется для отыскания или переименования OLE-объекта
Object	OLEFormat	Возвращает объект автоматизации, связанный с объектом. Об использовании автоматизации см. выше в этом уроке
OLEFormat	Shape	Возвращает объект OLEFormat, чьи свойства хранят информацию о связанных и внедренных OLE-объектах
OnAction	Shape	Это свойство используется для установки или поиска имени процедуры события для данного Shape-объекта.
Progid	OLEFormat	Возвращает строку, содержащую тип класса OLE-объекта
Shadow	Shape	Возвращает ссылку на объект ShadowFormat, который хранит информацию о падающей от объекта тени: будет ли она видимой, будет ли цветной, отделенной от объекта и т.п.
Top	Shape	Устанавливает или возвращает положение верхнего края объекта в позициях относительно верхнего края рабочего листа
TopLeftCell	Shape	Возвращает объект Range, указывающий на ячейку под левым верхним углом объекта Shape. Свойства TopLeftCell и BottomRIGHTCell используются для определения ячеек рабочего листа, накрываемых объектом
Type	Shape	Возвращает числовое значение, указывающее тип объекта. Используйте зарезервированные константы MsoShapeType для определения типа Shape-объекта
Visible	Shape	Для сокрытия объекта этому свойству присваивается значение False; чтобы объект был видим, установите для этого свойства значение True
Width	Shape	Устанавливает или возвращает ширину объекта Shape в пунктах
734
Неделя 3
Листинг 20.5 демонстрирует использование некоторых из указанных свойств.
Дисшипа 20.5. Процвддра Excel, исподьздющая различные свойства ВЬЕ-оВьекша
1:	Sub OLEObjectPropertiesf)
2:	'Эта процедура вставляет новый объект и
3:	'устанавливает некоторые его свойства
4:
5:	Dim BMPObj As Shape
6:
7:	With Worksheets("Sheetl")
8:	.Activate
9:	.Cells(2,	2).Value =	"Щелкните	дважды для редактирования"
10:	.Cells(3,	2).Select
11:	Set BMPObj = .Shapes.AddOLEObjectf	_
12:	FileName:="C:\Windows\Кольчуга.bmp")
13:	End With
14:
15:	With BMPObj
16:	.Line.Weight = xlThin
17:	.Name = "Битовый рисунок Кольчуга"
18:	.Shadow.Visible	=	True
19:	.Shadow.OffsetX	=	8
20:	.Shadow.OffsetY	=	8
21:	End With
22:	End Sub
В этой процедуре внедренный растровый рисунок вставляется в рабочий лист Excel, а затем устанавливаются некоторые свойства для нового OLE-объекта. Процедура начинается с объявления переменной типа Shape-объекта, называемой BMPObject, для хранения ссылки на новый объект (строка 5). В строках 8—10 описывается рабочий лист.
Подобно методам Add большинства объектных коллекций, метод AddObject можно использовать как функцию, создающую новый объект и в то же время возвращающую ссылку на этот объект. Операторы в строках 11, 12 делают именно это: они вызывают метод AddObject для вставки файла Gold Weave.bmp как внедренного в рабочий лист OLE-объекта и заносят полученную в результате объектную ссылку в переменную BMPObject.
Строки 15-21 содержат инструкцию With, которая использует переменную BMPObject для ссылки на вновь вставленный OLE-объект. Строка 16 устанавливает в качестве толщины рамки Shape-объекта тонкую линию путем назначения константы xlThin свойству Weight объекта LineFormat, возвращенному свойством Line объекта Shape. В строке 17 изменяется имя вставленного объекта путем назначения строки Битовый рисунок Кольчуга свойству Name. В строках 18—20 устанавливаются параметры тени для Shape-объекта путем присвоения свойствам объекта ShadowFormat значений, возвращенных свойством Shadow объекта Shape. Строка 18 позволяет убедиться, что тень видна, а в строках 19 и 20 устанавливается, насколько интенсивной должна быть тень вокруг объекта. Рис. 20.7 иллюстрирует окончательный вид рабочего листа после выполнения процедуры OLEObjectProperties.
День 20-й. Работа с другими приложениями
735
| О Microsoft Excel - День20 х1з
Ш
ж

.fell файл Древка йид Вставка Формат Сервис Данные Qkho Справка
;□	х ... <г л’£йьйПй"1С
12
13
14 ’
15 ’
адя
Готово
'(...NUMj':
Рис. 20.7. Процедура OLEObjectProperties вставляет показанный здесь внедренный объект Кольчуга.bmp, устанавливает стиль рамки и добавляет тень к изображению
Так надо
Помните, что информация о связанном OLE-объекте содержится в объектах Shape, OLEFormat и LinkFormat; информация о внедренном OLE-объекте хранится только в двух объектах: Shape и OLEFormat. Для пересчета дюймов в пункты, используемые для определения положения объекта на рабочем листе свойствами Тор и Left, применяйте метод Application. InchesToPoints. Метод Application.CentimetersToPointB позволяет пересчитывать в пункты также сантиметры.
Если вам нужна более подробная информация о свойствах, которые вы можете установить с помощью объектов LineFormat и ShadowFormat, исследуйте эти объекты с помощью окна Object Browser.
Используйте окно Object Browser также для уточнения доступности и типа конкретных свойств объектов Shape и OLEObject.
Не забывайте, что для доступа к OLE-объектам на рабочем листе и установки свойств этих объектов в Excel вы можете использовать также коллекцию OLEObjects. Однако некоторые свойства объектов OLEObject отличаются от свойств объектов Shape; свойство OLEObject. Shadow, например, является значением Boolean, которое просто указывает, будет ли отображаться тень, в отличие от более сложного объекта ShadowFormat, возвращаемого свойством Shape. Shadow.
736
Неделя 3
Использование методов OLE-объекта
Shape-объекты оснащены также различными методами, которые можно использовать для манипулирования объектами после их вставки. Многие из этих методов, такие как Copy, Cut, Delete, Activate и Select, настолько же просты и действенны, что и одноименные методы объектов Worksheet в Excel, рассмотренные нами на 19-м уроке. Однако два метода — Update и Verb — имеют специальное значение для OLE-объектов; эти методы доступны из свойства Shape.OLEFormat. В следующих двух разделах указанные методы рассматриваются более подробно.
Метод Update
Если вы вставляете объект Shape, содержащий связанный OLE-объект, связь между объектом и серверным файлом является обычно автоматической. Это означает, что когда файл на сервере изменяется, клиентский объект автоматически обновляется. Однако имеется два обстоятельства, при которых такое обновление не осуществляется автоматически.
•	Если связь предусматривает ручное управление. (Это можно сделать, выбрав команду Правка-Связи, затем выделив исходный файл в диалоговом окне Связи и установив флажок По запросу.)
•	Если вы закрыли, а затем снова открыли клиентский документ и выбрали Нет в ответ на запрос Excel в отношении того, желаете ли вы обновить связи.
В этих случаях для обновления объекта вам следует использовать метод Update.
Object.LinkFormat.Update
Здесь Object является ссылкой на Shape-объект, который содержит связанный OLE-объект, требующий обновления. Если вы работаете с объектом OLEObject, вместо приведенного выше используйте следующий синтаксис рассматриваемого метода (где Object есть ссылкой на OLEObject-объект в Excel).
Object.Update
В листинге 20.6 приведен пример того, как использовать метод Update объектов Shape.
Aucmuue 20.6. Использование метода Opdate ддя обповлеиия OLE-объектов в рабочей «ноге Excel
1:	Sub UpdateAllObjectsf)
2:	'Эта процедура обновляет объекты OLE
3:
4:	Dim	aSheet As Worksheet
5:	Dim	Obj As Object
6:
7:	For	Each aSheet In ActiveWorkbook.Worksheets
8:	Application.StatusBar = "Обновление объектов в "
9:	& aSheet.Name
10:	For Each Obj In aSheet.Shapes
11:	If Obj.Type = msoLinkedOLEObject Then
12:	Obj.LinkFormat.Update
13:	End If
День 20-й. Работа с другими приложениями
737
14:	Next Obj
15:	Next aSheet
16:	Application.StatusBar = False
17:	MsgBox ргошрЬ:="Связи обновлены.",
18:	ТШе:="Обновление объектов"
19:	End Sub
Эта VBA-процедура Excel обновляет все связанные OLE-объекты на каждом рабочем листе активной рабочей книги. Первый цикл For Each (строка 7) использует переменную aSheet, которая просматривает все рабочие листы активной книги. Свойство StatusBar отображает имя каждого рабочего листа в строке состояний, так что пользователь может наблюдать за продвижением операции (строки 8 и 9). Вложенный цикл For Each, начинающийся в строке 10, использует переменную Obj, просматривающую каждый объект Shape на каждом рабочем листе. В строке 11 делается проверка, является ли Shape-объект связанным OLE-объектом; если это так, то свойству Туре присваивается значение msoLinkedOLEObject. Если текущий объект содержит связанный OLE-объект, то он обновляется с помощью метода Update объекта LinkFormat (строка 12). Когда оба цикла For Each заканчиваются, процедура восстанавливает строку состояний Excel (строка 16) и отображает сообщение об окончании обновления данных (строки 17 и 18).
Так надо
Для обновления связанных OLE-объектов, входящих в коллекцию OLEObject в Excel, используйте метод OLEObject.Update.
Метод Verb
Каждый OLE-объект имеет одно или несколько действий (по-английски — verb, глагол), которые можно применить к объекту. В отличие от методов, позволяющих вам делать с объектом нечто с точки зрения VBA, действия позволяют выполнять определенные операции с объектом с точки зрения сервера, другими словами, действия выполняет с OLE-объектом приложение, использованное для создания этого объекта. Например, типичным действием является Edit: посылка OLE-объекту действия Edit открывает серверное приложение, что позволяет вам редактировать объект.
Для посылки действия OLE-объекту используется метод Verb. Прежде чем рассмотреть конкретный синтаксис метода Verb, приведем несколько фактов, которые следует иметь в виду при работе с действиями OLE-объектов.
•	Все OLE-объекты имеют первичное действие, которое определяет, что произойдет при двойном щелчке на объекте.
•	Для большинства объектов первичное действие позволяет редактировать объект. Если вы хотите редактировать объект, но не уверены, что это делает первичное действие, используйте действие Open.
•	Если объект поддерживает вторичное действие, можно указать его с помощью цифры 2 как значения аргументов verb или Verbindex (описанных при рассмотрении синтаксиса метода Verb).
•	Для внедренных объектов, которые поддерживаются OLE 2.0 или более поздних версий, первичное действие позволяет редактировать объект на месте и вторичное действие позволяет открывать объект в отдельном серверном окне.
738
Неделя 3
Метод OLEFormat.Verb в Excel имеет следующий синтаксис.
Object.Verb([Verb])
В этом синтаксисе Object является ссылкой на некоторый OLEFormat-объект Excel. Необязательный аргумент Verb является числовым выражением, определяющим действие, которое вы хотите чтобы выполнял OLE-сервер. Для более наглядного определения значения аргумента Verb Excel предлагает класс XIOLEVerb зарезервированных констант. Для посылки действия Орел используется константа хЮреп, а для посылки первичного действия — константа xlPrimary. Если опустить аргумент Verb, то на OLE-сервер отправляется действие по умолчанию.
Листинг 20.7 демонстрирует пример использования метода Verb в VBA-процедуре Excel.
Листинг 20.7. Использование метода OLEFormat.Verh для редактирования ОкЕ-дОьекта
1:	Sub InsertAndEditWordDocf)
2:	'Процедура вставляет и редактирует объект OLE.
3:	'Если пользователь отказывается редактировать в месте вставки,
4:	'документ редактируется в отдельном окне.
5:	'Если пользователь отказывается от редактирования, документ уничтожается
6:
7:	Dim	WordDoc As Object
8:	Dim	Ans As	Integer
9:	Dim	aSheet	As Worksheet
10:
11:	Set	aSheet	= ActiveWorkbook.Worksheets("Sheetl")
12:	With aSheet
13:	.Activate
14:	.Cells(3, 1).Select
15:	Set WordDoc = .Shapes.AddOLEObject(ClassType:="Word.Document”)
16:	End With
17:
18:	Ans = MsgBoKfprompt:="Редактировать на месте?", _
19:	Buttons:=vbYesNoCancel + vbQuestion,
20:	Т111е:="Вставка и редактирование документа Word")
21:	If Ans = vbYes Then
22:	WordDoc.OLEFormat.Verb xlPrimary
23:	Else
24:	If Ans	= vbNo Then
25:	WordDoc.OLEFormat.Verb xlOpen
26:	Else
27:	WordDoc.Delete
28:	End If
29:	End If
30:	End Sub
Эта процедура внедряет документ MS Word в рабочий лист Excel. Затем процедура спрашивает, не хочет ли пользователь отредактировать документ на месте; если пользователь отвечает Нет, для редактирования документа предоставляется отдельное окно. Если же пользователь отменяет процедуру, то вновь внедренный объект удаляется.
В строках 7~9 объявляются используемые процедурой переменные. Переменная WordDoc объявляется как универсальная переменная Object, поскольку тип объекта
День 20-й. Работа с другими приложениями
739
Document в Excel неизвестен (если только вы не имеете ссылки на библиотеку типов Word, установленной для данной рабочей книги; см. 12-й урок).
В строке 11 устанавливается переменная aSheet для ссылки на рабочий лист под именем Sheetl; она позволяет облегчить последующее написание текста для объектных ссылок в этой процедуре. В строках 13 и 14 устанавливается рабочий лист: лист активизируется и выделяется ячейка на пересечении первой колонки и третьей строки — это будет местом расположения верхнего левого угла нового OLE-объекта.
В строке 15 вставляется внедренный документ; в операторе этой строки метод AddOLEObject используется в качестве функции, а полученная в результате объектная ссылка запоминается в переменной WordDoc. В строках 18—20 используется инструкция MsgBox для отображения запроса о том, намерен ли пользователь редактировать документ. Выбор пользователя сохраняется в переменной Ans. Если переменная Ans получает значение vbYes — т.е. пользователь собирается отредактировать документ на месте, — то в методе Verb в качестве аргумента применяется константа xlPrimary (строка 22). Если же значением Ans оказывается vbNo — т.е. пользователь отказывается от редактирования на месте, — то аргументом метода Verb будет константа хЮреп (строка 25). И, наконец, если переменной Ans будет присвоено значение xlCancel (т.е. пользователь выбрал отмену операции), то в строке 27 вызывается метод Delete объекта Shape для удаления неиспользованного OLE-объекта.
ПРИМЕЧАНИЕ
ПРИМЕЧАНИЕ
Если аргумент Verb или DoVerb вы заключите в скобки (как показано в примерах синтаксиса для Excel и Word), то методы Verb/Doverb возвратят значение True при условии, что метод завершился успешно, и False - в противном случае.
Вы можете определить доступные для конкретного объекта действия с помощью редактора реестра, который позволяет просмотреть имеющиеся действия в базе данных реестра. Для этого запустите редактор реестра, как описано выше в этом уроке для поиска типа класса объекта, а затем используйте команды редактора Правка-Найти для просмотра типов классов объекта. Нажимайте клавишу F3 (или выберите Правка-Найти Следующий), пока не найдете ветвь \HKEY_CLASSES_ROOT\ <class type> в базе реестра (здесь cclass type> - тип класса, который вы искали). Разверните этот вход и ищите ветвь \HKEY_CLASSES_ROOT\<class type>\protocol\StdFileEditing\Verb - в списке Verb перечислены действия, которые воспринимаются данным типом класса. Рис. 20.8 иллюстрирует окно редактора реестра после нахождения типа класса Excel.Sheet.8 и развертывания затем соответствующих ветвей базы данных реестра.
Для определения конкретного действия в качестве значения аргумента метода Verb используйте числовые значения (0, 1,2 и т.д.) ветвей Verb в базе реестра. В соответствии с информацией, показанной на рис. 20.8, для активизации режима редактирования рабочего листа Excel на месте вы должны были бы в качестве аргумента метода Verb использовать значение 0.)
740
Неделя 3
Использование автоматизации
Одним из преимушеств OLE (особенно версии OLE 2+) является тот факт, что вы получаете доступ к оригинальным инструментам OLE-объекта. Простым двойным щелчком или с помощью метода Verb вы можете редактировать OLE-объект с использованием всей мощи серверных меню и команд.
До недавнего времени не было никакой возможности управлять сервером программными средствами. Чтобы редактировать или создавать OLE-объекты, неважно — на месте или в отдельном окне, вы должны были, по крайней мере, быть знакомы с серверным приложением. Хотя вы могли бы добровольно затратить время и усилия на изучение новой программы, от пользователей ваших VBA-программ ждать этого не приходится.
Рис. 20.8. Используйте редактор реестра для поиска имеющихся действий OLE-объектов
Эта ситуация изменилась с вводом OLE Automation в Microsoft Office 95. После появления Microsoft Office 97 OLE Automation стала известной просто как Automation. Приложения Windows, которые поддерживают автоматизацию, экспортируют свои объекты VBA, так же как и некоторым другим приложениям и инструментальным средствам разработки, которые поддерживают стандарт автоматизации.
Точно так же, как VBA распознает и манипулирует объектами VBA-приложений, он может также распознавать и манипулировать объектами других приложений. Microsoft Access, Visio, Microsoft Graph, Microsoft Word и PowerPoint — это далеко не все приложения, которые показывают VBA свои объекты. Visio 2.0, например, показывают такие объекты, как документы, страницы, формы и окна; Microsoft Access показывает такие объекты, как формы, отчеты и модули. Каждый из этих объектов имеет свою
День 20-Й. Работа с другими приложениями
741
собственную коллекцию методов и свойств, которые могут быть прочитаны или изменены VBA-программой, так же как методы и свойства Excel.
Число приложений Windows, которые к настоящему времени поддерживают Automation, быстро растет, хотя их все еще относительно мало. Многие эксперты допускают, что в ближайшем будущем Automation станет стандартом для взаимодействия приложений. Конечно, некоторые другие приложения, которые используют Visual Basic for Application, будут также поддерживать автоматизацию. Действительно, Microsoft уже делает VBA частью всех своих приложений для Windows; текущий список приложений Microsoft, оснащенных VBA и автоматизацией, включает Excel, Word, Access, Outlook, Project, PowerPoint и Visual Basic 6.
Доступ к объектам автоматизации
Способ вызова объекта автоматизации зависит от того, какой библиотечный файл содержит описания экспортируемого объекта: библиотека объектов (.olb), библиотека типов (.tlb) или библиотека динамических связей (.dll). Microsoft Access, например, экспортирует свои объекты через файл Msacc9.olb; Office экспортирует объекты через файл Mso98.dll; сама автоматизация экспортирует объекты через файл Stdole.tlb.
Если приложение обеспечивает библиотеку объектов, вы можете ссылаться на объекты из этой библиотеки прямо из программы VBA точно так же, как вы ссылаетесь на объекты Excel. Чтобы сделать объекты, определенные в файле библиотеки автоматизации, доступными конкретному VBA-проекту, следует использовать команды Сервис-Ссылки редактора VB Editor и в диалоговом окне Ссылки установить флажок на библиотеке объекта.
Некоторые приложения не имеют объектной библиотеки, но допускают прямой доступ к некоторым своим объектам. Если приложение не обеспечивает библиотеку объектов, но вы хотите вызвать новый объект, используйте функцию CreateObject. Если же в этом случае вы хотите получить доступ к существующему объекту, используйте функцию GetObject.
В следующих нескольких разделах методика использования автоматизации обсуждается более подробно.
Прямой доступ к объектам
Прямой доступ к объектам является наиболее наглядным способом работы с автоматизацией. В этом разделе я объясню, как использовать VBA для непосредственного управления объектами, определенными в другом приложении.
Для доступа к объектам, определенным в другом приложении, служит следующий синтаксис.
Application.ObjectName
Здесь Application — имя приложения, которое содержит требуемый вам объект, а ObjectName — имя самого объекта. Если вы не уверены, что использовать в качестве имени приложения, обратитесь к свойству Object, которым обладает каждый OLEFormat-объект и которое поможет вам выяснить имя приложения или ссылку на него.
Object.Object.Application
Здесь Object является ссылкой на OLEFormat-объект (такие объекты содержатся в объекте Shape). Применяемый таким образом метод Application возвращает строку, содержащую имя приложения.
742
Неделя 3
Метод Application можно использовать также для возврата ссылки на первоначальное приложение объекта. Предположим, у вас есть внедренный на рабочий лист документ Word, и вы назначили объектную переменную WordDoc, которая хранит ссылку на Shape-объект который содержит внедренный Word-документ. Тогда для отображения имени свойства ActivePrinter приложения Word вы могли бы использовать следующую инструкцию.
MsgBox "Текущим является принтер : " &
WordDoc.OLEFormat.Object.Application.ActivePrinter
Пример доступа к объекту автоматизации непосредственно из VBA Excel показан в листинге 20.8.
Листинг 20.8. ВВращенив к оОъекшц автоматизации неносредственно из УВД Ексе!
1:	Sub AutomateWordObject()
2:	'Процедура вставляет новый документ Word и
3:	'позволяет работать с ним с помощью его методов
4:
5:	Dim	WdApp As Object
6:	Dim	WdDoc As Object
7:	Dim	aShape As Shape
8;
9:	Application.StatusBar = "Вставка и редактирование документа..."
10:	Application.Screenupdating = False
11:
12:	'Выделяем верхнюю левую ячейку
13:	With Worksheets!"Sheetl")
14:	.Activate
15:	.Cells(2,	1).Select
16:	'Вставляем	новый документ
17:	Set aShape	= .Shapes.AddOLEObject(ClassType:="Word.Document")
18:	End With
19:
20:	'Устанавливаем ссылки на приложение Word и на документ
21:	Set WdApp = aShape.OLEFormat.Object.Object.Application
22:	Set WdDoc = aShape.OLEFormat.Object.Object
23:
24:	WdDoc.Activate 'Активизируем вставленный документ
25:
26:	'Прямой доступ к объектам приложения Word
27:	With WdApp
28:	.Selection.TypeText Text:="3T0 объект автоматизации!"
29:	.Selection.Expand Unit:=4 'расширяем выделение до абзаца
30:	.Selection.Range.Bold = True
31:	End With
32:
33:	'Проверяем обновление
34:	aShape.OLEFormat.Verb Verb:=xlVerbPrimary
35:	Cells(l, 1).Select
36:
37:	Application.ScreenUPdating = True
38:	Application.StatusBar = False
39:	End Sub
День 20-й. Работа с другими приложениями
743
VBA-процедура Excel в представленном листинге вставляет новый документ Word в рабочий лист, а затем использует методы и свойства Word (и самого документа) для редактирования этого документа. Процедура начинается с объявления двух переменных типа Object: WdDoc будет хранить ссылку на внедренный объект-документ, и в WdApp будет храниться ссылка на объект Application приложения Word. Третья объектная переменная будет хранить ссылку на Shape-объект, созданный для размещения в нем внедренного документа.
Поскольку выполняемые процедурой AutomateWordObject задачи могут потребовать нескольких секунд времени, оператор строки 9 процедуры отображает сообщение в строке состояния Excel. В строке 10 все время обновляемый экран Excel отключается, так что пользователь не будет видеть постоянные изменения экрана до тех пор, пока процесс вставки и редактирования не будет завершен.
Внутри инструкции With в строках с 13 по 18 активизируется рабочий лист (строка 14), выбирается ячейка на пересечении первого столбца и второй строки (строка 15 процедуры), затем вставляется документ Word и одновременно переменной aShape присваивается значение ссылки на новый объект-документ (строка 17).
Далее, в строке 21 устанавливается значение переменной WdApp для ссылки на объект Application приложения Word. В строке 22 значение ссылки на объект Document, обрабатываемый приложением Word, присваивается переменной WdDoc. В строке 24 используется метод Activate объекта Document, так что внедренный документ становится активным в Word. (Когда нежно использовать объект автоматизации, Word загружается автоматически.)
ПРИМЕЧАНИЕ
Двойное появление свойства Object в строках 21 и 22 листинга 20.8 - это не опечатка и не типографская ошибка! В Excel для ссылки на объекты автоматизации вы должны использовать метод Object свойства OLEFormat дважды подряд перед именем связанного или внедренного OLE-объекта. Если вы используете функцию TypeName для проверки типа объекта, возвращаемого свойством Shape.OLEFormat,Object, вы увидите, что это свойство обычно возвращает тип OLEObject. (Shape - это некоторый объект формы.) Таким образом, выражение Shape.OLEFormat.Object.Object возвращает ссылку на текущий объект.
Инструкция With в строках 27—31 содержит операторы, которые редактируют внедренный объект-документ Word с использованием Selection-объекта Word. Само приложение Word вызывается через объект автоматизации, на который ссылается переменная WdApp. В строке 28 вставляется некоторый текст, в строке 29 выделение расширяется для включения в него текущего параграфа, а в строке 30 выделенный в документе текст делается полужирным. В строке 29 используется непоименованная числовая константа для указания значения, эквивалентного значению константы wdParagraph приложения Word.
СОВЕТ
Значение любой внутренней константы любой версии VBA вы можете найти с помощью окна Object Browser: только выделите требуемую константу в списке Members и читайте ее значение снизу в окне Object Browser.
В строке 34 для активизации Shape-объекта с целью редактирования на месте используется метод Verb. Без этого шага представление внедренного Word-документа не будет обновлено и документ — даже если он сейчас содержит некоторый текст — будет оставаться на экране как пустое поле. Путем открытия внедренного документа Ех-
744
Неделя 3
cel инициирует обновление представления объекта. Поскольку в данной процедуре нет необходимости оставлять внедренный документ в режиме редактирования, в строке выделяется первая ячейка, которая и закрывает внедренный документ. Рис. 20.9 иллюстрирует результат выполнения рассматриваемой процедуры.
|Щ Microsoft Excel - flenb20.xls
ISj Файл (Травка Вид Вставке Формат Сервис Данны® 2кно Справка' '<'
S Anal	vio Ж ! Ж К з 19 9 1 Ё
А1
G
С
F
Н
В
В
Е
2 Это объект Automatinn!___________
3 '
5 J
6
7 ,
9 1
itr
n'i
12
13
14
.1.5 J
15 _______________________________
И 4 ► M'\sheetl/sheets/Sheets/'
Готово
,»иМ^
Рис. 20.9. Процедура в листинге 20.8 вставляет этот документ в рабочий лист Excel как внедренный объект
Создание нового объекта автоматизации
Если приложение имеет объектную библиотеку, вы можете использовать объекты приложения непосредственно в своей программе путем создания в вашем VBA-проекте ссылки на библиотеку объектов автоматизации. Если же приложение не имеет библиотеки объектов или вы не устанавливаете ссылку на объектную библиотеку, то можете использовать функцию CreateObject VBA для создания нового объекта автоматизации. В этом разделе мы обсудим две методики создания новых образцов объектов автоматизации в вашей VBA-программе.
Использование функции CreateObject
Функция CreateObject является одной из функций класса Interaction и имеет следующий синтаксис.
CreateObject(Class)
Аргумент Class здесь — программный идентификатор (иными словами, тип класса), который специфицирует приложение и тип объекта, который вы хотите создать. Например, программным идентификатором для Word является Word.Application, для Excel
День 20-й. Работа с другими приложениями
745
— Excel.Application. Другие примеры: вы можете использовать Visio.Application для приложения Visio; для приложения Microsoft Access вы могли бы использовать Access.Application, и т.п. Функция CreateObject возвращает объектную ссылку на вновь созданный объект. Листинг 20.9 демонстрирует пример использования этой функции.
Листинг 20.8. Испошоваппв фдимции CrEatEObjEct дня свзцания пВъекто автоматизации
1:	Sub CreateWordObject))
2:	'Процедура запускает Word, создает новый документ и
3:	'вставляет его в рабочий лист
4:
5:	Dim	WordApp As Object
6:	Dim	WdDoc As Object
7:	Dim	fName As String	'имя	документа
8:	Dim	iconfName As String	'имя	значка	документа
9:
10:
11:	'Создает экземпляр приложения Word
12:	Set WordApp = CreateObject)"Word.Application.9")
13:
14:	'Создаем имя файла на основе папки активной книги
15:	fName = ActiveWorkbook.Path
16:	fName = fName & "\Документ_созданный Excel VBA.doc"
17:
18:	'Создаем документ и устанавливаем объектную ссылку на него
19:	Set WdDoc = WordApp.Documents.Add
20:
21:	'Добавляем в документ немного текста
22:	With WordApp
23:	.Selection.TypeText Text:=”3T0T документ создан с помощью" S
24:	"VBA-программы, выполненной в Excel."
25:	.Selection.TypeParagraph
26:	End With
27:
28:	With WdDoc
29:	'Устанавливаем свойства документа
30:	.BuiltinDocumentProperties)"Title")	=	“Тестовый	документ"
31:	.BuiltinDocumentProperties("Subject")	=	"Проверка	автоматизации"
32:
33:	'Сохраняем документ и закрываем его
34:	.SaveAs fName
35:	.Close
36:	End With
37:
38:	WordApp.Quit	'Выход из Word
39:
40:	'Вставляем документ в Excel
41:	iconfName = "C:\Program Files\Microsoft Office?1 &
42:	"Office\WinWord.exe"
43:
44:	With ActiveSheet
45:	.Cells(2, 2).Activate
46:	.Shapes.AddOLEObject FileName:=fName,
746
Неделя 3
47:
48:
49:
50:
51:
52:	End With
Link:=True, _
DisplayAsIcon:=True, IconFileName:=iconfName,
Iconindex:=1, 1сопЬаЬе1:="Тестовый документ Word"
53: End Sub
Чтобы запустить Word, эта процедура использует функцию CreateObject для создания объекта "Word.Application" (строка 12). Данный объект сохраняется в переменной WordApp. Через этот объект-приложение Word вы теперь имеете полный доступ ко всем объектам, методам и свойствам Word.
Например, вы можете создать новый документ Word с помощью метода Documents.Add, как это показано в строке 19. Далее, для вставки в новый документ некоторого текста в строках 22—26 используется объект приложения Word Selection. В строках 28—36 устанавливаются некоторые свойства документа (строки 30 и 31), документ сохраняется (строка 34), а затем закрывается (строка 35). Наконец, в строке 38 для завершения рабочего сеанса Word, который был запущен с помощью функции вызова CreateObject (см. строку 12) используется метод Quit.
В строках 41 и 42 в переменной iconfName сохраняется полный путь к программному файлу Word и его имя; этот файл будет использоваться как источник для пиктограммы связанного OLE-объекта. (Если ваша копия Word инсталлирована на другом устройстве или в другой папке, вам нужно будет изменить эту строку в соответствии с вашей системой.) В строке 45 выделяется вторая ячейка во второй строке активного рабочего листа (как вы помните, метод AddOLEObject вставляет OLE-объект в текущую активную ячейку рабочей таблицы). Строки 46—51 содержат единственную инструкцию с методом AddOLEObject, использующим несколько аргументов. Документ теперь вставлен как связанный объект из файла с соответствующим именем (с помощью переменной fName, инициализированной в начале процедуры в строках 15 и 16). Вставленный объект отображается в виде пиктограммы, в качестве которой использована вторая пиктограмма из программного файла Word, с собственным названием. На рис. 20.10 показан окончательный вид рабочего листа, сформированного рассматриваемой процедурой.
Использование библиотек объектов, на которые имеются ссылки
Если вы установили ссылку на объектную библиотеку для своего VBA-проекта, вы можете объявить переменные, которые имеют конкретный тип данных объектов из этой библиотеки и создать новые экземпляры объектов в библиотеке с использованием ключевого слова New (которое мы рассматривали на 12-м уроке). В листинге 20.10 приведен пример применения объектов из библиотеки, на которую установлена ссылка.
День 20-й. Работа с другими приложениями
747
| Microsoft Excel - Д eHb20.xls
Файл Правка Вид Вставка Формат Сервис Данные £>кно Правка	 |g| х|
Тестовый документ Word
! Anal	♦ IQ
В2 ................
—~	А В I С ~~
«1 2-| з__
~4~ 5 ' в 7
S 9 10 j Tv 12> 13 14 ! 15' I6 'iz:i ИО»\sheetl/sheet2 /Sheets / Готово
Рис. 20.10. Процедура CreateWordObject создает документ Word с помощью функции CreateObject и затем связывает его с рабочим листом
Листинг 20.10. Использование объектов автоматизации из объектноб библиотеки, на которцю установлена ссылка
1:	Sub CreateReferencedObject()
2:	'Процедура запускает Word, создает новый документ и
3:	'вставляет его в рабочий лист
4:	'ЗАМЕЧАНИЕ. Для правильной работы
5:	'в проекте VBA необходимо установить ссылку на объектную библиотеку Word.
6:
7:	Dim	appWord As Object
8:	Dim	WdDoc As Document
9:	Dim	fName As String	'имя	документа
10:	Dim	iconfName As String	'имя	значка	документа
11:
12:	'Создает экземпляр приложения Word
13:	Set appWord = New Word.Application
14:
15:	'Создаем имя файла на основе папки активной книги
16:	fName = ActiveWorkbook.Path
17:	fName = fName S "\Документ.Ьос”
18:
19:	'Создаем документ и устанавливаем объектную ссылку на него
20:	Set WdDoc = appWord.Documents.Add
21:
748
Неделя 3
22:	'Добавляем в документ немного текста
23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55:	With appWord.Selection .TypeText Text:="Этот документ создан с помощью " & "VBA-программы, выполненной в Excel," & " при установленной ссылке на объект Word." .TypeParagraph End With With WdDoc 'Устанавливаем свойства документа .BuiltinDocumentProperties("Title") = "Тестовый документ 2" .BuiltinDocumentProperties("Subject") = "Проверка автоматизации" 'Сохраняем документ и закрываем его .SaveAs fName .Close End With appWord.Quit	'Выход из Word Set appWord = Nothing 'освобождаем экземпляр приложения 'Вставляем документ в Excel iconfName = "C:\Program Files\Microsoft Office!" S "Office\WinWord.exe" With ActiveSheet .Cells(2, 2).Activate .Shapes.AddOLEObject FileName:=fName, Link:=True, DisplayAsIcon:=True, IconFileName:=iconfName, Iconlndex:=l, IconLabel:="Automation документ Word” End With End Sub
Процедура из приведенного листинга выполняет, по сути, те же функции, что и процедура в листинге 20.9, но получает доступ к объектам автоматизации Word путем ссылки на объектную библиотеку Word. В строках 7—10 объявляется несколько объектных переменных, используемых этой процедурой. Обратите внимание, что, поскольку в этом проекте делается ссылка на объектную библиотеку Word, в объявлении в строке 8 используется конкретный тип Word-объекта — объект Document — несмотря на то, что рассматриваемая процедура является VBA-процедурой Excel.
ПРИМЕЧАНИЕ
Для корректного выполнения процедуры в листинге 20.10 ваш VBA-проект должен иметь ссылку на объектную библиотеку Word 9.0. Для установки этой ссылки выполните команды Tools - References в редакторе VB Editor, а затем установите флажок слева от строки Microsoft Word 9.0 Object Library в списке Available References. Если в списке такого названия нет, значит, Word либо не инсталлирован в вашей системе, либо инсталлирован некорректно.
День 20-й. Работа с другими приложениями
749
Далее, в строках 32 и 33 устанавливаются некоторые свойства документа, затем документ сохраняется (строка 36) и закрывается (строка 37). В строке 40 используется метод Quit приложения Word для завершения рабочего сеанса Word, а затем в строке 41 объектной переменной appWord присваивается значение Nothing, что обеспечивает удаление экземпляра приложения Word.
Наконец, в строках 44—53 выбирается ячейка во втором столбце и втором ряду активного в настоящий момент рабочего листа, а затем документ связывается с рабочей книгой. Рис. 20.11 иллюстрирует полученный в результате выполнения рассматриваемой процедуры рабочий лист.
Microsoft Excel - День20.х1$
j[jS) Файл Дравка Вид Вставка Формат Сервис Данные Окно'Справка •	 |g| х|
< Е Л $1	~~...............
Рис. 20.11. Процедура CreateReferencedObject создает новый документ Word с использованием ссылки на объект, а затем связывает его с рабочим листом
Доступ к существующему объекту автоматизации
Кроме создания экземпляра нового объекта, вам может понадобиться работать с экземпляром существующего объекта. Если серверное приложение уже запущено, то для доступа к существующему объекту автоматизации служит функция GetOb ject VBA:
GetObject([pathname], [Class])
В этом синтаксисе необязательный аргумент pathname представляет собой текстовую строку, указывающую полный путь к папке и имя файла, содержащего объект, который вы хотите отыскать. Если вы опускаете этот аргумент, то аргумент Class становится обязательным.
Необязательный аргумент Class — это текстовая строка, указывающая программный идентификатор объекта, с которым вы хотите работать. (Здесь для этого аргумента следует использовать тот же синтаксис, какой вы использовали при обращении к
750
Неделя 3
функции CreateObject выше в этом уроке.) Если вы опустите аргумент Class, то аргумент pathname становится обязательным и класс объекта определяется файлом, который вы указали: трехбуквенное расширение файла определяет тип файла и VBA использует библиотеку реестра для определения того, какой сервер автоматизации соответствует этому типу файла.
Используйте функцию GetObject для создания экземпляра объекта, основываясь на конкретном файле. Следующий фрагмент программы иллюстрирует пример того, как можно это сделать:
Dim AppVisio As Object
Set AppVisio = GetObject(pathname: = "C:\Visio\Drawing.vsd",_ Class:= "Visio.Application")
В приведенном примере объектной переменной AppVisio назначается ссылка на экземпляр приложения Visio с загруженным файлом Drawing.vsd.
Если аргумент pathname представляет собой пустую строку (""), то функция GetObject создает новый экземпляр указанного объекта аналогично тому, как это делает функция CreateObject. Следующий фрагмент программы, например, создает новый экземпляр Word:
Dim AppWord As Object
Set AppWord = GetObject(pathname: =
Class: = "Word.Application")
Если вообще опустить аргумент pathname, то функция GetObject вернет некоторый активный в данный момент объект указанного типа; если существующий экземпляр указанного объекта отсутствует, VBA сгенерирует ошибку времени выполнения. Как пример, следующий фрагмент программы сохраняет ссылку на существующий экземпляр Word в объектной переменной AppWord.
Dim AppWord As Object
Set AppWord = GetObject(Class: = "Word.Application")
Если в приведенном примере отсутствует, по крайней мере, один экземпляр Word, запущенный к моменту выполнения этой инструкции, возникает ошибка времени выполнения.
Так надо
Независимо от того, имеете ли вы ссылку на библиотеку объектов автоматизации внедренного объекта или нет, наилучшей методикой манипулирования внедренными объектами является получение ссылки на объекты автоматизации с помощью свойства OLEFormat. Object внедренного объекта.
В качестве лучшей методикой манипулирования связанными объектами следует назвать использование ссылки на библиотеку объектов автоматизации и применение методов CreateObject и GetObject. Эти методы, однако, можно использовать и в тех VBA-проектах, которые не имеют ссылки на библиотеку объектов автоматизации.
Используйте метод CreateObject всякий раз, когда хотите создать совершенно новый экземпляр объекта автоматизации.
Используйте метод GetObject всякий раз, когда хотите создать экземпляр объекта автоматизации с уже открытого конкретного файла, либо в тех случаях, когда экземпляр объекта автоматизации уже выполняется и вы не хотите запускать другой.
Помните, что если сервер автоматизации зарегистрирован в библиотеке реестра как объест в единственном экземпляре (single-instance object] т.е. в каждый момент в вашей системе существует лишь один его экземпляр, то независимо от того, сколько раз вы вызывали функцию CreateObject, будет создан лишь един экземпляр объекта. Отсюда следует, что функция GetObject всегда возвращает один и тот же экземпляр для таких объектов.
День 20-й. Работа с другими приложениями
751
Доступ и DLL из Visual Basic far Application
Другой способ работы с ресурсами, обеспечиваемыми другими приложениями, состоит в использовании обрабатывающей программы прямо из библиотек динамической компоновки другого приложения. Библиотеки динамической компоновки (Dynamic-link library, или DLL) являются собраниями функций и процедур, которые имеются во всех приложениях Windows. Сама Windows поставляется с определенным количеством файлов DLL, которые обеспечивают разработчиков сотнями специализированных — и очень надежных — функций. Использование этих процедур — простой способ включить мощь Windows в ваши VBA-программы. Сегодня библиотеки DLL поставляются с Windows в форме, известной как Windows Application Programming Interface (API).
Хотя большинство функций Windows API достаточно сложны, вы можете воспользоваться преимуществами каждой из них в своих программах VBA. Следующие несколько разделов покажут вам, как использовать библиотеки DLL в ваших VBA-процедурах. Мы рассмотрим также несколько примеров.
Объявление DLL-процеддр
Перед тем как вы сможете использовать некоторую функцию из DLL, вы должны сообщить VBA, где искать DLL-файл и какие аргументы нужны для этой функции. Для этого необходимо ввести инструкцию Declare на уровне модуля, т.е. перед любыми объявлениями процедуры в модуле. В зависимости от того, является ваша процедура функцией Function или программой Sub, следует использовать одну из следующих форм инструкции Declare.
Declare Function Name Lib "Libname" [Alias AliasName] (Arguments) [As Type]
Declare Sub Name Lib “Libname" [Alias AliasName] (Arguments) [As Type]
Как и в случае переменных и констант VBA, необходимо объявить процедуры DLL как Public (чтобы процедура была доступна всем модулям во всех проектах) или как Private (чтобы процедура была доступна только модулю, в котором она объявлена).
Name представляет имя процедуры, a Libname — имя DLL-файла, например “USER32”. Если процедура имеет то же имя, что и ключевое слово VBA или переменная типа Public, вы не сможете использовать в своей программе собственное оригинальное имя этой процедуры. Вместо этого следует использовать аргумент AliasName и указать в нем другое имя для данной процедуры.
Arguments представляет собой список требуемых процедурой аргументов. Этот список использует следующий синтаксис.
[Optional] [ByVai|ByRef] [ParamArray] VarName [As Type]
Optional указывает, что аргументы не являются обязательными. ByVai означает, что аргумент передается значением, как это имеет место в случае большинства DLL-процедур. ByRef означает, что аргумент передается ссылкой. ParamArray использует массивы данных типа Variant. Этот аргумент должен быть последним в списке аргументов; вы не сможете использовать в комбинации с Optional, ByVai или ByRef. VarName — это имя аргумента, а Туре указывает тип данных аргумента.
Следующая инструкция, например, объявляет функцию с именем MessageBeep из файла User32.exe библиотеки DLL.
Declare Sub MessageBeep Lib "USER32" (ByVai BeepType As Integer)
752
Неделя 3
После того как вы объявили DLL-процедуру, вы можете использовать ее в своей VBA-программе точно так же, как и любую другую процедуру Sub или Function.
(ММИЕ
Когда имя файла DLL оканчивается цифрами 32, это означает, что DLL содержит 32-битовый код. Независимо от вашего желания в ваших VBA-программах вы должны использовать 32-битовые версии процедур библиотеки DLL
Некоторые примеры использования DLL
В этом разделе приведено несколько примеров того, как использовать DLL-процедуры в программах VBA. Для более полного изучения DLL и Windows API обратитесь к книге Джеймса МакКорда (James McCord) Win32 API Desktop Reference. В этой книге вы найдете полное изложение рассматриваемого предмета.
Озвучивание уивамика
VBA имеет простую инструкцию Веер, которую вы можете использовать для привлечения внимания пользователя. К сожалению, Веер продуцирует только единственный звук. В большинстве случаев это, конечно, не проблема, но есть много ситуаций, когда хотелось бы получить больше. Например, вы обычно используете сигнал динамика по окончании длительной операции, чтобы вернуть внимание пользователя к экрану. Но что, если во время операции произошла ошибка? Было бы хорошо иметь другой звук, который сообщит вам об ошибке.
Если вам требуются различные звуки, сообщаю, что DLL-процедура MessageBeep дает вам возможность использовать пять различных звуков. Но чтобы воспользоваться преимуществами MessageBeep, вам или пользователям ваших VBA-программ потребуется звуковая карта, поддерживаемая Windows.
ПРИМЕЧАНИЕ
Библиотека DLL которая содержит процедуру MessageBeep - User32, - хранится в дисковом файле с именем User32.dll в директории \Windows\System. Она поставляется с Windows. Помимо MessageBeep, файл Llser32.dll содержит и другие полезные процедуры и функции, которые являются частью стандартного 32-битового интерфейса Windows API. Для получения полной информации относительно функций и процедур в User32.dll обратитесь к документации по Win32 Software Developer's Kit, предоставляемой Microsoft.
Следующая инструкция Declare позволяет затем использовать MessageBeep.
Declare Sub MessageBeep Lib "DSER32" (ByVai BeepType As Long)
Аргумент может принимать одно из пяти значений, перечисленных в табл. 20.3.
Таблица 20.3. Значения аргумента BeepType
Значение BeepType	Производимый звук
0 16 32 48 64	Сигнал по умолчанию Критический останов Вопрос Восклицание Звездочка
День 20-й. Работа с другими приложениями
753
Все эти звуки определены в панели управления. Листинг 20.11 демонстрирует процедуру, которая проигрывает все пять звуков.
Листинг 20.11. Использование процедуры MessageBeep из ДиВдиотекп OLL
1:	Option Explicit
2:
3:	Declare Sub MessageBeep Lib "USER32" (ByVai BeepType As Long)
4:
5:	Sub BeepTest()
6:	'Проигрывает пять звуковых сигналов,
7:	'установленных в панели управления.
8:	Dim I As Integer
9:
10:	For I = 0 To 64 Step 16
11:	Select	Case	I
12:	Case	0
13:	Application.StatusBar = "Стандартный звук"
14:	Case	16
15:	Application.StatusBar	=	"Критическая ошибка"
16:	Case	32
17:	Application.StatusBar	=	"Вопрос"
18:	Case	48
19:	Application.StatusBar	=	"Восклицание"
20:	Case	64
21:	Application.StatusBar	=	"Звездочка"
22:	End Select
23:	MessageBeep I
24:	WaitDelay DelaySeconds:=2
25:	Next I
26:
27:	Application.StatusBar = False
28:	End Sub
29:
30:
31:	Sub WaitDelay(DelaySeconds As Integer)
32:	Dim TimeNow As Date
33:	TimeNow = Timer
34:	Do
35:	Loop Until ((Timer - TimeNow) > DelaySeconds)
36:	End Sub
Листинг 20.11 демонстрирует полный модуль; этот модуль содержит две процедуры. Процедура BeepTest (в строках 5—28) просто просматривает все значения, принимаемые MessageBeep, и проигрывает соответствующие звуки. А процедура WaitDelay делает всего лишь паузу между звуками на указанное количество секунд.
Обратите внимание на строку 3 этого листинга. Строка 3 содержит инструкцию Declare Sub MessageBeep; это позволяет работать с файлом User32.dll из рассматриваемого модуля VBA.
754
Неделя 3
ПРИМЕЧАНИЕ
Конкретные звуки, издаваемые процедурой BeepTest, зависят от конфигурации вашего компьютера. Если вы не инсталлировали все звуки, обеспечиваемые Windows, то вы можете услышать только один или два звука из всех, которые продуцирует BeepTest. Вам следует изменить звуки, которые Windows проигрывает для конкретных событий - вопрос, звездочка и другие - с помощью панели управления Windows.
Переменная I в циклической структуре For...Next (строки 10-25) перебирает все значения от 0 до 64 с шагом 16. Инструкция Select Case (строка 11) просматривает каждое значение индекса I и отображает название звука в строке состояний. MessageBeep проигрывает звук (строка 23), а затем процедура делает паузу в две секунды (путем вызова WaitDelay в строке 24) перед тем, как пойти на следующий цикл с новым значением индекса I.
Так надо
Если вы собираетесь использовать аргументы процедуры MessageBeep повсюду в модуле или процедуре, объявляйте их как константы. Например, следующая инструкция объявляет константу QuestionBeep как 32.
Const QuestionBeep = 32
Так не надо
Не расстраивайтесь, если в вашей компьютерной системе нет звуковой карты или соответствующего драйвера. Если система не в состоянии проигрывать звуки, процедура будет издавать обычный сигнал, определенный по умолчанию.
Поиск папки Windnws
DLL-функция GetWindowsDirectory определяет путь к папке Windows. Это удобно, если вам нужно отыскать местонахождение одного из доступных приложений Windows (например, программа Телефон, использованная в листинге 20.15). Функция GetWindowsDirectory объявляется следующим образом.
Declare Function GetWindowsDirectory Lib "kernel32“_
Alias "GetWindowsDirectoryA"_
(ByVai Buffer As String,_
ByVai Size As Long) As Long
Аргумент Buffer является строковой переменной, в которой размещает имя пути папки Windows. Необходимо убедиться, что эта строка достаточно длинна, чтобы поместить имя пути, поскольку библиотеки DLL сами не удлиняют передаваемые им строки. Если строка недостаточно длинна, чтобы вместить в себя имя пути, то строка, возвращающаяся с аргументом Buffer, может выйти за границы выделенной этого памяти (с соответствующими неприятными последствиями). В качестве аргумента Buffer идеально использовать строку фиксированной длины. Ниже в качестве примера приведено объявление соответствующей строки для использования в качестве аргумента Buffer.
Dim WinDir As String * 255
Далее в инструкции Declare идет аргумент Size, который представляет максимальный размер буфера. Вы можете использовать функцию Len для определения длины буферной переменной, а результат использовать затем в качестве аргумента Size.
День 20-й. Работа с другими приложениями
755
Функция GetWindowsDirectory возвращает длину строки имени пути, скопированного в Buffer. В листинге 20.12 приведен пример использования этой функции.
Листинг 20.12. Исиодьзаваиие фцикции EetWImlowsOlrectorij ДиДлиотеки DLL
1:	Option Explicit
2:
3:	Declare Function GetWindowsDirectory Lib "kernel32"
4:	Alias "GetWindowsDirectoryA” _
5:	(ByVai Buffer As String, _
6:	ByVai Size As Long) As Long
7:
8:	Sub LaunchWordPad()
9:	'Запускает программу WordPad
10:
11:	Dim WinDir As String * 255
12:	Dim DirLength As Long
13:	Dim FullName As String
14:
15:	DirLength = GetWindowsDirectory(Buffer:=WinDir,
16:	Size:=Len(WinDirf)
17:	If DirLength = 0 Then
18:	MsgBox "He удается определить папку Windows!"
19:	Else
20:	FullName = Left(WinDir, DirLength) & "\WRITE.EXE"
21:	Shell pathname:=FulIName, Windowstyle:=vbMaximizedFocus
22:	End If
23:	End Sub
Листинг 20.12 содержит полный модуль. Процедура LaunchWordPad использует функцию Shell для загрузки программы WordPad из Windows. (Функция Shell рассматривается в следующем разделе.)
Строки 3—6 листинга содержат инструкцию Declare для объявления функции GetWindowsDirectory. В данном случае часть Alias является обязательной. Действительным именем DLL-процедуры является GetWindowsDirectoryA; без Alias-информации VBA не смог бы найти функцию в DLL.
Процедура LaunchWordPad начинается с объявления переменной WinDir как строки фиксированной длины в 255 символов (строка 11 процедуры). Эта переменная используется как буфер для функции GetWindowsDirectory (строка 15). Размер строки, скопированной в WinDir, сохраняется в переменной DirLength. Если DirLength равна нулю, это означает, что функция отработала неудачно (строка 17). В этом случае появляется соответствующее сообщение (строка 18). В противном случае имя пути файла Write.exe (который запускает программу WordPad) создается путем конкатенации строки пути к папке Windows — полученной с помощью функции Left(WinDir, DirLength) — со строкой “\WRITE.EXE” (строка 20). Инструкция Shell запускает WordPad в максимизированном окне с фокусом ввода (строка 21).
756
Неделя 3
ПРИМЕЧАНИЕ
Поведение процедуры LaunchWordPad может показаться несколько странным: она загружает программу с именем Write.exe вместо WordPad.exe, но на экране появляется программа WordPad. В версиях MicrosoftWindows, предшествовавших Windows 95, встроенная программа текстового редактора называлась Write. Эта же программа вместе с WordPad была перенесена в Windows 95; файл WordPad.exe фактически хранится не в той же папке, что и Windows (WordPad обычно находится в папке \Files\Accessories). Для совместимости “сверху вниз”, сднако, Windows продолжает включать программу Write в папке Windows. Когда выполняется программа Windows Write.exe, она просто запускает WordPad.exe. Процедура LaunchWordPad пользуется этим, используя функцию GetWindowsDirectory для возврата папки Windows, а затем выполняет Wite.exe в папке Windows.
Работа с приложениями, которые не поддерживают OLE оао автоматизации
Не все приложения поддерживают автоматизацию или OLE. В некоторых случаях единственный способ работы с другим приложением состоит в том, чтобы просто запустить его и так или иначе дать пользователю вашей процедуры возможность работать непосредственно с другим приложением, либо (как это описано ниже в этом разделе) использовать метод SendKeys для отправки приложению кодов (нажатых) клавиш.
Запуск другого проложения
Для запуска другого приложения из процедуры VBA следует использовать функцию Shell.
Shell(PathName [, Windowstyle])
Здесь PathName — строковое выражение для имени файла, который запускает приложение. Для запуска Microsoft Access, например, вы бы использовали имя файла программы Access: Msaccess.exe. Необходимо также включить сюда имя устройства и папки, чтобы VBA уверенно смог найти нужный файл. В текстовую строку аргумента PathName можно также включить некоторые командно-строковые переключатели или аргументы для приложения, которое вы запускаете. Например, для запуска Microsoft Access и открытия базы данных MyData.mbd вы использовали бы для аргумента такую строку, как эта (предполагается, что Access инсталлирован на устройстве С в папке \Program Files\Microsoft Office\OfTrce).
C:\Program Files\Microsoft Office\Office\MsAccess.exe MyData.mdb
Далее, аргумент Windowstyle представляет собой число, которое указывает вид окна приложения. VBA определяет несколько зарезервированных констант, которые облегчают использование аргумента Windowstyle; эти константы и их значения перечислены в таблице 20.4. Если аргумент Windowstyle опустить, то приложение стартует в минимизированном окне с фокусом ввода.
День 20-й. Работа с другими приложениями
757
Таблица 20.4. Зарезервированные константы VBA для аргумента Windowstyle функции Shell
Аргумент Windowstyle	Вид окна
vbHide vbNormalFocus vbMinimizedFocus vbMaximizeFocus vbNormalNoFocus vbMinimizedNoFocus	Скрытое, с фокусом Нормальный размер, с фокусом Минимизированное, с фокусом Максимизированное, с фокусом Нормальный размер, без фокуса Минимизированное, без фокуса
Если функция Shell завершается успешно, то она возвращает числовое значение: идентификационный (ID) номер задачи для приложения, которое только что стартовало. (Windows внутренне идентифицирует каждое выполняемое в настояший момент приложение, присваивая ему уникальный ID-номер задачи; такой номер изменяется от сеанса к сеансу.) Если функция Shell завершается не успешно, она генерирует ошибку. В листинге 20.13 показан пример использования функции Shell.
Aucmuua 20.13. Исцояьзовциие фднкдии Shell для заидсза ирилижеиия
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:	Option Explicit Sub StartControlPanel(slcon As String) 'Запускает панепь управления, а именно тот ее раздел, 'который указан в аргументе slcon On Error GoTo BadStart Shell pathname:“"CONTROL.EXE MAIN.CPL " & slcon, _ Windowstyle:=vbNormalFocus Exit Sub BadStart: MsgBox prompt:=“He удается запустить панель управления!”, Buttons:=vbOKOnly + vbExclamation, Title:=”IIaHenb управления: “ & slcon End Sub Sub ChangePrinter() 'Вызывает процедуру StartControlPanel и открывает папку Принтеры StartControlPanel ("Принтеры") End Sub
Панель управления Windows позволяет управлять многими аспектами среды Windows, включая установки принтера, шрифтов и цветов. Процедура StartControlPanel имеет то преимущество, что вы можете запустить любой раздел панели управления, используя следующую командную строку:
CONTROL.EXE MAIN.CPL IconName
758
Неделя 3
Здесь MAIN.CPL представляет собой .cpl-файл (аббревиатура от Control Panel Library)', IconName — имя раздела панели управления, который вы хотите запустить, например PRINTERS ИЛИ FONTS.
Процедура StartControlPanel принимает аргумент slcon, указывающий на раздел панели управления, с которым вы хотите работать. Процедура устанавливает ловушку ошибок On Error (строка 6) на случай, если панель управления не стартует правильно. В строке 8 выполняется функция Shell для загрузки панели управления и запуска модуля, указанного slcon. Если все идет хорошо, процедура благополучно завершается по инструкции Exit Sub (строка 16). Если же произошла ошибка, выполнение процедуры переходит к метке BadStart (строка 12) и инструкция MsgBox отображает эту печальную новость (строки 13—15).
Процедура Changeprinter (строки 19—22) представляет собой пример того, как можно было бы вызывать процедуру StartControlPanel.
Так надо
Сохраняйте результат работы функции Shell *- идентификационный номер задачи приложения, которое вы запустили, - в переменной модульного уровня или в общедоступной переменной, если позже в вашей VBA-процедуре или программе вы предполагаете снова ссылаться на это приложение.
Если перед запуском программы вам необходимо изменить директорию приложения, используйте инструкцию ChDir VBA (см. урок 13-го дня обучения).
Так не надо
Не вводите в текст своей программы выполняемые инструкции сразу после обращения к функции Shell, если вы хотите, чтобы эти инструкции выполнялись лишь после окончания работы стартовавшего приложения. Ин-< струкция Shell запускает приложение асинхронно, а это означает, что VBA активизирует приложение, а затем немедленно продолжает выполнение последующих инструкций вашей процедуры.
Активизация запущенного приложения
После запуска нескольких приложений вашей программе может понадобиться переключаться между ними. Например, вы можете захотеть, чтобы пользователь мог переключаться между Excel и панелью управления для изменения различных установок. Для переключения к некоторому запущенному приложению используется инструкция AppActivate.
AppActivate(Title [, Wait])
Здесь Title может быть либо числовым выражением, значение которого равно идентификационному номеру задачи (например, возвращенному функцией Shell), либо строковым выражением, содержащим имя приложения, к которому вы хотите переключиться. В данном случае именем приложения является текст, который появляется в строке заголовка приложения. Строка заголовка некоторых приложений включает и имя приложения, и имя активного документа. Если значение Title не соответствует строке заголовка ни одного приложения в точности, VBA пытается найти строку заголовка, которая начинается со строки, переданной аргументом Title. Если же Title соответствует началу строки заголовка более чем одного запущенного приложения, активизируется лишь какое-либо одно из них.
День 20-й. Работа с другими приложениями
759
Необязательный аргумент Wait является значением типа Boolean и определяет, когда VBA переключается к приложению. Если этот аргумент имеет значение True, AppActivate перед переключением к некоторому приложению ожидает до тех пор, когда вызванное ранее приложение перестанет быть активным; в VBA это означает, что AppActivate перед переключением ждет, пока основное приложение VBA (такое как Excel) активно. Если же Wait равно False или опущено вообще, AppActivate переключается к другому приложению немедленно. Задавайте аргументу Wait значение True в случае, если вы хотите быть уверенным, что ваша VBA-программа активизирует другое приложение, только когда основное приложение VBA активно, но не тогда, когда оно выполняется в фоновом режиме. Листинг 20.14 иллюстрирует действие инструкции AppActivate:
Ahcuihuu 20.14. Использование инструкции AppActivate для переклтчеиия к заидщеиному придожвиию
1:	Option Explicit
2:
3:	Dim NPadID As Long 4:
5:	Sub LoadWinReadMe()
6:	'Открывает текстовый файл Read Me из папки Windows в программе Блокнот
7:
8:	Const iTitle = "Чтение файла Read Me "
9:	Const WinReadMe = "C:\Windows\ReadMe.txt"
10:
11:	If Dir(WinReadMe) <> "" Then
12:	NPadID = Shell(pathname:="C:\Windows\Notepad.exe " &
13:	WinReadMe, _
14:	Windowstyle:=vbNormalNoFocus)
15:	Application.OnKey Кеу:="я+Е",
16:	Procedure:="ActivateNotePad"
17:	 MsgBox prompt:="<I>aftn Read Me загружен!” & vbCr &
18:	"Нажмите Ctrl+Shift+E для открытия.",
19:	Buttons:=vblnformation, Title:=lTitle
20:	Else
21:	MsgBox prompt:="He удалось найти " & WinReadMe,
22:	Buttons:=vbExclamation, Title:=lTitle
23:	End If
24:	End Sub
25:
26:
27:	Sub ActivateNotePad()
28:	'Вызывает программу Блокнот при нажатин Ctrl+Shift+E
29:
30:	On Error GoTo NotRunning
31:
32:	AppActivate Title:=NPadID, Wait:=True
33:	Exit Sub
34:
35:	NotRunning:
36:	MsgBox prompt:="He удалось запустить Notepad!",
37:	Buttons:=vbExclamation,
38:	Title:="Быстрый вызов Notepad"
39:	End Sub
760
Неделя 3
Процедура LoadWinReadMe загружает файл ReadMe.txt из папки Windows в приложение Notepad и устанавливает быструю клавишу для активизации Notepad в Excel. (Файл ReadMe.txt в папке Windows поставляется вместе с Windows и загрузился на ваш жесткий диск в процессе инсталляции Windows на вашем компьютере; этот файл содержит последние новости и информацию об операционной системе Windows.)
Листинг 20.14 представляет собой полный модуль. Обратите внимание на его строку 3, в которой на уровне модуля объявляется переменная NPadID. Эта переменная используется для хранения идентификационного номера задачи, возвращаемого функцией Shell. Объявление ее как переменной уровня модуля гарантирует, что идентификационный номер задачи для образца NotePad, определенный процедурой LoadWinReadMe, будет доступен всем процедурам в этом модуле.
Процедура Loadwin95ReadMe начинается в строке 5 листинга. В строках 8 и 9 объявляются локальные константы процедуры: заголовок для окон сообщений, отображаемых процедурой и константа для имени и пути к папке с файлом Windows ReadMe.txt. (Этот файл инсталлирован вместе с Windows в инсталляционной папке Windows; вам может потребоваться изменить объявление этой константы, если Windows на вашем компьютере инсталлирована на другом устройстве или в другой папке.)
В строках 11—23 выполняется фактическая работа рассматриваемой процедуры. Сначала в строке 11 начинается блок инструкции If...Then, в котором вызывается функция Dir (рассмотренная нами 13-м уроке) для того, чтобы убедиться, что файл ReadMe.txt находится именно в указанной папке. Если это так, то выполняются строки 12—19; в противном случае в строках 21 и 22 отображается сообщение о том, что файл не может быть найден.
В строке 12 вызывается функция Shell для запуска программы NotePad. Заметьте, что аргумент PathName конкатенирует имя программы NotePad с константой WinReadMe; когда NotePad стартует, он будет автоматически загружен в указанный файл. Результат работы функции Shell — идентификационный номер задачи для этого экземпляра NotePad — назначается переменной NPadID для дальнейшего использования.
Далее, в строке 15 создается событие ОпКеу, которое предписывает процедуре ActivateNotePad вызываться, как только пользователь нажимает комбинацию клавиш <Ctrl+Shift+E>. (События ОпКеу в Excel более детально рассматриваются в уроке 21-го дня обучения.) Наконец, в строках 17—19 отображается диалоговое окно, сообщающее об успешной загрузке текстового файла Read Me в NotePad.
Процедура ActivateNotePad (строки 27—39) активизирует приложение NotePad. В строке 30 устанавливается обработчик ошибок On Error GoTo на тот случай, если NotePad, запущенный процедурой LoadWinReadMe, не запускается. Если все идет хорошо, инструкция AppActivate в строке 32 активизирует NotePad. Если же происходит ошибка, управление передается на метку NotRunning (строка 35) и отображается сообщение об ошибке (строки 36—38).
Отправка кодов клавиш другому приложению
После загрузки приложения с помощью функции Shell или активизации его по инструкции AppActivate пользователь может работать с приложением непосредственно. Часто, однако, такое решение оказывается неудовлетворительным, поскольку основным достоинством программ VBA является автоматическое выполнение задач так, чтобы пользователю не нужно было самому выполнять каких-либо действий.
Управлять другим приложением, даже если оно не поддерживает OLE или автоматизацию, можно путем посылки другому приложению кодов клавиш с помощью инструкции SendKeys VBA. (Эта инструкция — одна из интерактивных процедур VBA.) Можно посылать приложению код клавиши или комбинации клавиш — включая те,
День 20-й. Работа с другими приложениями
761
которые используют клавиши <Alt>, <Ctrl> и <Shift>. Результат получается точно таким же, как если бы вы нажимали эти клавиши непосредственно в приложении. Ниже приведен синтаксис инструкции SendKeys:
SendKeys(String [, Wait])
Здесь String — клавиша или комбинация клавиш, которые вы хотите отправить активному приложению. В случае букв, цифр или знаков пунктуации нужно просто заключить символ в кавычки, как, например, “а”. Для других клавиш и их комбинаций используются строки, перечисленные в табл. 20.5.
Таблица 20.5. Строки, используемые в качестве аргумента String метода SendKeys
Клавиша	Строка
Backspace Break Caps Lock Delete Down Arrow End Enter (keypad) Enter Esc Help Home Insert Left Arrow Num Lock Page Down Page UP Print Screen RIGHT Arrow Scroll Lock Tab UP Arrow от Fl до Fl2	{BACKSPACE} или {BS} или {BKSP} {BREAK} {CAPSLOCK} {DELETE} или {DEL} {DOWN} {END} {ENTER} ~ (тильда) или {ENTER} {ESCAPE}или{ESC} {HELP} {HOME} {INSERT} или {INS} {LEFT} {NUMLOCK} {PGDN} {PGUP} {PRTSC} {RIGHT} {SCROLLLOCK} {TAB} {UP} от {Fl} до {Fl2}
Необязательный аргумент Wait является значением типа Boolean, указывающим, ожидать ли VBA выполнение кодов нажатых клавиш путем отсылки их приложению перед продолжением программы VBA. Если Wait имеет значение True, то VBA ожидает, пока приложение не закончит обработку посланных вами кодов клавиш, и лишь затем передает управление следующей инструкции процедуры; в противном случае VBA немедленно продолжает выполнение вашей процедуры.
Комбинируя клавиши из таблицы 20.5 с клавишами <Alt>, <CtrI> и <Shift>, можно создавать любые комбинации клавиш. Для этого следует лишь предварить строку из табл. 20.5 одним или более кодами, перечисленными в табл. 20.6.
762
Неделя 3
Таблица 20.6. Коды для клавиш <Alt>, <Ctrl> и Shift
Клавиша	Используемый код
Alt	% (процент)
Ctrl	Л (каретка)
Shift	+ (плюс)
Все, что вам нужно сделать для использования SendKeys, — это активизировать программу с помощью Shell или AppActivate. Затем нужно отослать коды клавиш, которые вам требуются. Например, вы можете закрыть любое приложение Windows, передав код комбинации клавиш <Alt+F4>, как показано ниже.
SendKeys String: = "%{F4}"
В листинге 20.15 приведен пример отправки кодов клавиш, которые использует стандартное приложение Windows Телефон для набора номера телефона из текущей ячейки на рабочем листе.
Лисшииа 20.15. Мправлеиие прилажвпивм с испяльзааанивм инструкции sendKegs
1:	Sub XLDialIt()
2:	'С помощью стандартной программы Телефон набирает номер,
3:	'записанный в активной ячейке.
4:
5:	Dim PhoneNumber As	String
6:	Dim Ans As Integer
7:
8:	With ActiveCell
9:	Ans = MsgBox(prompt:="Набираем номер " & .Value &
10:	vbCr & "Убедитесь, что модем включен.",
11:	Buttons:=vbOKCancel + vbExclamation,
12:	Title:="Ha6op номера")
13:	If Ans = vbCancel Then Exit Sub
14:	.Copy
15:	End With
16:
17:	Shell pathname:="C:\Windows\Dialer.exe",
18:	Windowstyle:=vbNormalFocus 'Запуск программы Телефон
19:	SendKeys String:="'v", Wait:=True	'Вставка в поле номера
20:	SendKeys String:="fcd", Wait:=True	'Начало набора
21:
22:	Application.Wait Now + TimeValue("00:00:15")
23:
24:	SendKeys	String, Wait:=True
25:	SendKeys	String:="fch", Wait:=True
26:	SendKeys	String:="%{F4}", Wait:=True
27:
28:	Application.CutCopyMode = False
29:	End Sub
День 20-й. Работа с другими приложениями
763
Процедура XLDiallT использует стандартную программу Windows Телефон для набора номера, введенного в активную ячейку рабочего листа. Для выполнения этой процедуры вам необходимо сначала ввести номер телефона в любую ячейку рабочего листа, сделать эту ячейку активной, а затем выполнить процедуру XLDiallT с помощью команд Сервис-Макрос-Макросы.
Используя ActiveCell в инструкции With (строки 8-15), процедура сначала отображает диалоговое окно с номером телефона, который будет набран, и предупреждением пользователю о том, что его модем должен быть включен. Если пользователь выбирает Cancel (или нажимает <Esc>), процедура заканчивается (строка 13). В противном случае содержимое активной ячейки копируется в буфер обмена (т.е. в Clipboard; строка 14).
Далее, процедура запускает программу Телефон с помощью функции Shell (строка 17). Две инструкции SentKeys отправляют программе коды следующих клавиш (строки 19 и 20).
< Ctrl+V> — вставить номер телефона из буфера обмена.
<Alt+D> — набрать номер.
В это время Телефон отображает диалоговое окно Набор номера. Поднимите трубку, но не нажимайте <Enter>, чтобы убрать диалоговое окно. Процедура XLDiallT ожидает 15 секунд, чтобы дать вашему телефону время набрать номер, а затем другая группа инструкций SendKeys отправляет коды следующих клавиш:
<Enter> — удалить диалоговое окно.
<Alt+H> — заставить систему набора номера телефона повесить трубку.
<Alt+F4> — закрыть программу Телефон.
Наконец, свойство CutCopyMode Excel получает значение False (строка 28), чтобы вывести Excel из режима копирования.
Так надо
Помните, что инструкция SendKeys чувствительна к регистру. Например, строки " ’Р" и"Л+р" обе отправляют комбинацию клавиш <Ctrl+Shift+P>. Если вы хотите отправить только <Ctrl+P>, используйте строку "Лр". Если вы хотите отправить следующие символы в строке SendKeys, заключайте их в фигурные скобки {...}: ||||И
Например, отправлять знак процента следует так.
SendKeys "{%}"
Резюме
На этом уроке вы узнали, как работать со связанными и внедренными объектами (OLE) и объектами автоматизации. Урок начался с обзора истории OLE. Технология OLE работает путем вставки данных из серверного приложения в документ клиента. Вы можете либо связать объект (в этом случае данные остаются с серверным приложением), либо внедрить объект (в этом случае данные полностью сохраняются в документе клиента). OLE обеспечивает такие возможности, как редактирование на месте и автоматизация.
Вы научились использовать метод AddOLEObject объекта Shape для вставки внедренных и связанных OLE-объектов в рабочие страницы Excel. Для новых объектов мы указываем тип класса; для существующих объектов мы указываем имя файла. Вы научились также использовать свойства OLEFormat и LinkFormat объекта Shape для работы с OLE-объектами после их связывания или внедрения.
764
Неделя 3
Далее, в этом уроке мы рассмотрели возможности автоматизации. Автоматизация является стандартом программного обеспечения, который позволяет приложениям экспортировать свои объекты языкам программирования, таким как VBA. Ваша процедура может работать с этими объектами путем выполнения их методов и считывания или установки их свойств. Если приложение имеет объектную библиотеку, то на его объекты вы можете ссылаться непосредственно. В противном случае вы можете использовать функции CreateObject и GetObject.
Для расширения ваших знаний о работе с другими приложениями мы рассмотрели также методики работы с библиотеками динамической компоновки (DLL) в ваших VBA-программах. DLL предлагает сотни специализированных функций и процедур, позволяющих выполнять задачи, которые с использованием только VBA решаются с большим трудом, если решаются вообще.
Наконец, на этом уроке мы рассмотрели несколько методик работы с приложениями, которые не поддерживают автоматизации или OLE. Вы научились использовать простую функцию Shell для запуска другого приложения. Вы узнали также, что можно активизировать любые выполняемые приложения путем использования инструкции AppActivate; научились использовать возвращаемый функцией Shell идентификатор ID задачи для активизации приложения и указывать такое имя приложения, которое появляется в строке заголовка в процессе его активизации. Мы изучили также возможности управления приложениями, не поддерживающими OLE и автоматизации, путем отправки им кодов клавиш. Инструкция SendKeys может отправить коды любых клавиш и их комбинаций активному Windows-приложению. Результат при этом получается такой же, как если бы вы нажимали эти клавиши непосредственно в приложении.
Вопросы п ответы
Как ввести новые пункты в базу данных реестра ?
Обычно пункты в базе данных реестра формируются автоматически в процессе инсталляции нового программного обеспечения, так что никогда не следует об этом волноваться. Хотя редактор реестра позволяет вам добавлять, удалять или редактировать информацию в базе данных реестра, настоятельно рекомендуется избегать каких-либо изменений в базе данных, если только вы не проинструктированы инженером технической поддержки или кем-либо еще, кто хорошо осведомлен о работе базы реестра.
Когда я использую метод AddOLEObject для вставки нового внедренного объекта, я получаю сообщение об ошибке, указывающее мие, что Excel не может вставить объект. Что я делаю неправильно?
Вы все делаете правильно. Дело в том, что некоторые приложения не поддерживают внедрения объектов. Вместо этого вам следовало бы попытаться вставить эти объекты из существующих файлов и связать их, а не внедрять. Обратитесь к листингам в этом уроке и просмотрите примеры связывания OLE-объектов из существующих файлов.
Помимо базы данных реестра, есть ли другой способ отыскать действия, предусмотренные для того или иного объекта?
Да, есть. Раскройте меню Правка и выделите команду Объект. При этом отобразится меню, в верхней половине которого перечислены действия объектов.
Как узнать, имеет ли приложение объектную библиотеку?
В редакторе VBA выберите команды Tools-References. Диалоговое окно References покажет вам список всех объектных библиотек в вашей компьютерной системе. Если объектная библиотека имеется в списке и вы выберете ее для добавления этой ссылки к вашему VBA-проекту, то сможете затем в диалоговом окне Object Browser увидеть объекты автоматизации библиотеки.
День 20-й. Работа с другими приложениями
765
Когда я пытаюсь запустить процедуру StartControlPanel в листинге 20.13 с опциями Password, Time, Date, International, Desktop или аналогичными, я получаю не то, чего ожидал, либо вообще ничего не происходит. Не связано ли это с неправильной работой панели управления?
Нет, с панелью управления все в порядке. Проблема в том, что некоторые разделы панели управления фактически размешены в других .cpl-файлах. Например, установки Password находятся в файле PassWord.cpl, установки International — в файле Intl.cpl и т.д. Чтобы определить, какие .cpl-файлы могут быть доступны для вас, используйте команды Start-Find-Files и просмотрите файлы с расширением *.ср1.
Могу ли я использовать функцию Shell для запуска команд или программ DOS?
Конечно. Для внешних команд DOS — другими словами, таких команд как FORMAT, ATTRIB или XCOPY32, которые имеют свои собственные исполняемые файлы — выполняйте функцию Shell с именем, соответствующим .ехе- или .com-файлу, например, FORMAT.COM или ATTR1B.EXE. Для внутренних команд DOS, таких как DIR и COPY, можете использовать следующий синтаксис:
Shell "COMMAND.COM /С DOSCommand"
Эта инструкция создает окно DOS в Windows и дает команду командному интерпретатору DOS, COMMAND.COM. Параметр /С сообщает DOS, что эта копия COMMAND.COM в памяти — временная. DOSCommand — это строка, содержашая внутреннюю команду, которую вы хотите выполнить, а также дополнительные переключатели и параметры. Используйте точный синтаксис для команды, которую вы предполагаете использовать в командной строке DOS. Например, Следующая инструкция перенаправляет вывод команды DIR в файл под именем DIR.TXT:
Shell "COMMAND.COM /С DIR /-Р > DIR.TXT"
Однако вы должны помнить из 5-го и 13-го уроков, что VBA включает множество функций и инструкций, которые могут выполнять многие требуемые вам операции, аналогичные выполняемым командами DOS.
Коллоквиум
Ответы приведены в приложении.
Тест
1.	Что представляют собой сервер и клиент в терминологии OLE?
2.	В чем состоит различие между связанным и внедренным объектом?
3.	Назовите четыре основных свойства OLE и дайте короткое их объяснение.
4.	Каково назначение базы данных реестра?
5.	Какие аргументы вы использовали бы в методе AddOLEObject для выполнения следующих задач? Аргументы, связанные с пиктограммами, опустите.
А. Вставить связанный существующий документ, созданный текстовым процессором.
Б. Вставить новый презентационный слайд.
В. Вставить внедренное существующее растровое изображение.
6.	Что представляет собой действие OLE? Что обычно делает первичное действие?
766
Неделя 3
7.	Что такое автоматизация?
8.	Назовите три методики, которые можно использовать для доступа к объектам автоматизации.
9.	Что означает утверждение: функция Shell выполняет приложения асинхронно?
10.	Какую роль выполняет аргумент Wait в инструкции SendKeys?
И. Что такое библиотека динамической связи?
12. Что такое Windows API?
Упражнения
1. Напишите процедуру, которая в цикле будет просматривать все OLE-объекты на рабочем листе и выводить имя и тип (связанный или внедренный) каждого объекта. С целью тестирования этой процедуры используйте команды Insert Object для интерактивной вставки различных связанных и внедренных объектов на рабочий лист. (Подсказка. Попытка прочитать свойство ProglD связанного объекта OLEObject будет приводить к ошибке времени выполнения; перед проверкой свойства ProglD выясните, является объект внедренным или связанным, путем сравнения свойства Туре с константой msoEmbeddedOLEObject.
2. НАЙДИТЕ ОШИБКУ. Следующая процедура отыскивает файл с именем DirList.txt и удаляет его, если он существует. Первая функция Shell выполняет команду DIR и перенаправляет вывод в DirList.txt. Вторая инструкция Shell предназначена для открытия файла DirList.txt в NotePad, но — в большинстве случаев — вместо этого возникает ошибка. Знаете ли вы почему9
Sub GetDirListText()
If Dir("c:\Windows\dirlist.txt")<> "" Then _
Kill "c:\Windows\dirlist.txt"
Shell "command.com /с dir > c:\Windows\dirlist.txt
Shell "notepad.exe c:\Windows\dirlist.txt", vbNormalFocus
End Sub
День 20-й. Работа с другими приложениями
767
Процедуры обработки событий и надстройки
Как вы, вероятно, уже поняли из этой книги, в языке Visual Basic for Applications предусмотрено немало способов выполнения процедур. Их можно запускать с помощью диалогового окна Макрос, комбинаций клавиш ускоренного доступа, команд специально созданных меню или панелей инструментов.
Для всех этих способов общим является то, что для выполнения процедуры вы должны обязательно что-нибудь сделать: выбрать команду в меню, нажать комбинацию клавиш или щелкнуть на кнопке. Но в языке VBA предусмотрены средства, позволяющие запускать процедуры автоматически при наступлении некоторого события, например открытия книги или выбора листа. Наш завершающий урок как раз и посвящен такому автоматическому запуску процедур. На этом уроке вы познакомитесь с такими вопросами.
•	Как запускать процедуру при открытии, закрытии или сохранении книги.
•	Как запускать процедуру при выборе активного листа Excel или при раскрытии окна, отображающего такой лист.
•	Как создавать процедуры, которые запускаются в ответ на нажатие клавиш, комбинаций клавиш или на манипуляции с мышью.
•	Как запускать процедуру в заданное время.
•	Как делать процедуры, которые реагируют на ввод данных пользователем или на пересчет ячеек листа.
•	Как создавать и как пользоваться надстройками для приложения Excel.
Что такое события и процедуры обработки событий
Вы уже встречались в этой книге с такими понятиями как процедура обработки события и программа, управляемая событиями. В частности, на 16-м уроке мы говорили о том, как создавать процедуры обработки событий для управляющих элементов на форме пользователя. На этом уроке вы узнаете, как создавать процедуры для обработки конкретных событий, связанных с различными объектами в Excel. Но сначала сто
768
Неделя 3
ит поближе познакомиться с программами, управляемыми событиями, и процедурами обработки событий.
Программа, управляемая событиями, — это такая программа, которая крутится в бесконечном цикле, ожидая и реагируя на некоторые события, в отличие от программ, которые просто выполняют линейную последовательность команд. Событием может быть нажатие клавиши на клавиатуре или щелчок кнопки мыши, выбор активного листа, открытие или закрытие книги. Поскольку поведение программы определяется конкретными событиями, которые возникают во время работы программы, то о таких программах говорят, что они управляются этими событиями (отсюда и произошел термин программа, управляемая событиями). При наступлении события программа запускает одну или несколько процедур, связанных с этим событием. Такие процедуры называются обработчиками события, или процедурами обработки событий.
Вам уже известно, что объекты в приложении Excel обладают некоторыми свойствами и методами, доступными для программиста с помощью программ, написанных на языке VBA. Кроме того, некоторые объекты реагируют на такие события, как двойной щелчок кнопкой мыши или открытие/закрытие файла. Для любого подобного события программист может создать свою собственную процедуру, предназначенную для его обработки. Созданные программистом процедуры способны расширять, заменять или устранять (все зависит от конкретного события) встроенное поведение объекта при возникновении данного события. Например, написав свою собственную процедуру обработки события WindowResize, можно запретить пользователю изменять размеры окна рабочего листа.
Хотя в приложении Excel содержатся сотни объектов, лишь некоторые из них создают события, которые вы можете обрабатывать своими процедурами. Вот все такие объекты приложения Excel: Application, Workbook, Worksheet и Chart.
Помимо процедур, предназначенных для обработки событий, предоставляемых объектами, можно также создавать процедуры обработки событий, которые выполняются в ответ на нажатие некоторой комбинации клавиш, или событий, которые запускаются в определенное время суток. Чтобы определить, какая процедура Excel должна выполняться в ответ на нажатие конкретных клавиш, можно использовать метод ОпКеу. Например, метод Excel ОпКеу можно настроить таким образом, что процедура OpenComments будет выполняться при каждом нажатии комбинации клавиш <Ctrl+Alt+N>.
В Excel также предусмотрен метод OnTime, который позволяет создавать события, связанные с определенной датой и временем суток. Ниже в этом уроке вы узнаете, как создавать процедуры обработки событий, “настроенных” на конкретное время или конкретную комбинацию клавиш.
Так надо
Определить, какие события вызываются тем или иным объектом (если такие есть), можно с помощью окна Просмотр объектов. События перечислены в списке Компоненты и отмечены слева значком молнии.
Имейте в виду, что одно и то же событие может вызываться на разных уровнях иерархии объектов. Например, рабочий лист Excel вызывает событие Selectionchange при каждом изменении выделения данных. То же самое событие происходит на уровне объекта рабочей книги Excel и связано с возникновением события SheetSelectionChange, которое вызывается при изменении выделения данных на листе в любой рабочей
День 21-й. Процедуры обработки событий и надстройки
769
Гуе хранятся процедуры обработки событии
Процедуры обработки событий хранятся не в стандартном модуле, а в модуле класса объекта. Такие объекты приложения Excel, как Workbook, Worksheet и Chart, содержат модули класса, в которых можно хранить процедуры обработки событий соответствующего объекта.
На рис. 21.1 вы видите раскрытое окно Программа с модулем класса объекта рабочего листа Excel. Обратите внимание на то, что в списке Объект окна Программа выбран объект Worksheet. В списке Процедура перечислены все события, для которых можно создавать процедуры обработки событий. При выборе какого-либо события в списке Процедура редактор Visual Basic вставляет “пустое” объявление для процедуры обработки этого события, если таковая еще не существует. На рис. 21.1 в окне Программа показано объявление пустой процедуры, предназначенной для обработки события Selectionchange рабочего листа Excel.
Microsoft Visual Basic - День21 xis
. Elie Edit View Insert Ffiimat Debug gun Tools fidd-ins Vyindow Це!р
M День21 .xis - Sheetl (Code)
3__o____________________
$ Day21 (День21.х1&)	i
; Microsoft Excel Objects	g I
в) Listing03 (Ltsting03)
® ListngCM (Listing04)
Щ Listings (ListinqOS)
© Dsting06 (LlstmgOb)
Й] ListingD? (Usting07)	? ;
O Sheetl (Sheetl)
8 ThisWorkbook	jjlj i
3 S Forms
3 SimulatedForm
- ti* Modules
ExerciseOl
ExerciseOS
i...	^
Exercise04	gl;
 Exercise05
InitializeAppEvents
•• •*£ ListingOl
Usting02 ListingOS
•	*£ ListingOS
•	*£ LiStinglO
•	*£ Listing 11	p
[Worksheet
^.|:.:.|SelectionChange
LAct ivat e BeforeDoubleClick BeforeRightClick Calculate
Private Sub Worksheet Change
Deactivate [FollowHyperlink
End Sub
‘lectionChange
Puc. 21.1. Процедуры обработки событий хранятся в модуле класса
Исключением из правила, определяющего, где и как хранить процедуры обработки событий, является объект Application. Этот объект не имеет модуля класса, в котором бы хранились его процедуры обработки событий. Поэтому процедуры объекта Application хранятся в модуле класса, создаваемом пользователем. Методы связи ваших процедур обработки событий с событиями объекта Application описаны в разделе “Работа с событиями объекта Application” ниже в этой главе.
Для того чтобы отобразить или создать процедуру обработки события для объекта, отличного от Application, сделайте следующее.
770
Неделя 3
1.	В окне редактора Visual Basic отобразите окно Проект, если оно еще не отображено.
2.	В окне Проект щелкните на объекте, для которого вы собираетесь создать (или модифицировать) процедуру обработки события. На рис. 21.1 показан рабочий лист Лист1 выбранной рабочей книги Excel.
3.	В окне Проект щелкните на кнопке Программа, чтобы отобразить в окне Программа модуль класса выбранного объекта (см. рис. 21.1).
4.	В списке Объект окна Программа выберите сам объект, т.е. если вы выбрали объект Worksheet (Лист1) в окне Проект, то вы должны выбрать элемент Worksheet в списке Объект, как показано на рис. 21.1.
5.	Выберите в списке Процедуры процедуру обработки событий, которую хотите создать или модифицировать. Список Процедуры будет содержать только те элементы, которые связаны с событиями, вызываемыми объектом, модуль класса которого вы открыли.
Имя и описапие процедуры обработки события
Связь конкретной процедуры в модуле класса объекта с определенным событием устанавливается с помощью средств языка VBA на основе имени процедуры. Поэтому создаваемые пользователем процедуры обработки событий должны отвечать определенным соглашениям об именах, а также некоторым другим требованиям.
Как правило, процедуры обработки событий должны использовать следующий синтаксис объявления.
Private Sub Object_EventName(Arguments)
Процедуры обработки событий в модуле класса объекта должны быть объявлены закрытыми, т.е. с использованием ключевого слова Private. В этом синтаксисе элемент Object представляет имя типа объекта, которому принадлежит процедура обработки событий, например, Арр (для объекта Application) или Workbook. Элемент EventName представляет имя обрабатываемого события, например BeforeSave, BeforeDoubleClick и т.д. Элемент Arguments представляет список аргументов процедуры обработки события. Списки аргументов для процедур обработки событий рассматриваются в следующем разделе.
Например, в следующей строке объявляется процедура для объекта Worksheet, предназначенная для обработки события Activate.
Private Sub Worksheet Activated
Другие примеры объявлений процедур обработки событий вы увидите ниже в этом уроке.
Так надо
Пользуйтесь автоматической вставкой объявления процедуры обработки события, которую за вас выполняет редактор Visual Basic. В этом случае объявление будет иметь корректное имя события и автоматически включать список аргументов процедуры обработки этого события.
День 21-й. Процедуры обработки событий и надстройки
771
Аргументы процедуры обработки события
Одни процедуры обработки событий имеют один или несколько аргументов, другие — ни одного. Обычно аргументы процедур обработки событий служат для достижения одной из двух целей: для передачи информации в обработчик события и для обеспечения средств отмены встроенной реакции объекта на данное событие.
Рассмотрим, например, следующее объявление обработчика события.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Процедура, созданная с помощью этого объявления, будет вызываться при закрытии объекта Workbook. Событие BeforeClose происходит непосредственно перед закрытием рабочей книги Excel, и при этом не важно, как закрывается эта книга: программными средствами или действиями, выполняемыми интерактивно пользователем приложения Excel. Следовательно, процедура Workbook_BeforeClose будет выполняться прямо перед закрытием рабочей книги. Эта процедура может содержать программные строки, которые реализуют проверку новых элементов данных на допустимость или на факт сохранения данных в рабочей книге. Если данные оказались неверными или не были сохранены, то у вас будет возможность предотвратить сохранение рабочей книги. Именно это и позволяет сделать аргумент Cancel.
В тот момент времени, когда событие BeforeClose запускает на выполнение процедуру Workbook_BeforeClose, аргумент Cancel установлен равным значению False (Ложь). Если в некоторой точке процедуры Workbook_BeforeClose аргумент Cancel установить равным значению True (Истина), то закрытие рабочей книги будет предотвращено. Однако следует отметить, что не каждая процедура обработки событий включает аргумент Cancel.
Рассмотрим еще одно объявление процедуры обработки события.
Private Sub Workbook_SheetActivate (ByVai sh As Object)
Это объявление процедуры относится к событию sheetActivate объекта Workbook. Событие SheetActivate возникает при активизации любого листа в рабочей книге Excel. При этом процедуре с помощью аргумента sh передается ссылка на активизируемый лист (либо на рабочий лист, либо на диаграмму). А поскольку это событие происходит внутри рабочей книги для любого активизируемого листа, то ваша процедура обработки события должна суметь определить, какой именно лист был активизирован. Средства для этого и предоставляются аргументом Sh.
События объекта Application
В предыдущем разделе вы узнали, что процедуры обработки событий для отдельных объектов внутри приложения (Workbook, Worksheet и т.д.) хранятся в модуле класса, который принадлежит этим объектам. Напомню также, что объект Application не имеет модуля класса. Поэтому создавать процедуры обработки событий для объекта Application в приложении Excel несколько сложнее, чем для других объектов. Решению этой задачи и посвящен данный раздел.
Чтобы создать процедуру обработки событий для объекта Application, сделайте следующее.
1. Создайте новый модуль класса в проекте VBA. Этому модулю класса можно присвоить любое имя.
112
Неделя 3
2. В раздел General модуля класса добавьте без изменений следующее объявление.
Public WithEvents Арр As Application
После добавления этого объявления объект Арр появится в списке Объект модуля класса (в окне Программа).
ПРИМЕЧАНИЕ
В объявлении WithEvents вы обязаны использовать имя Арр. В противном случае ваши процедуры обработки событий уровня приложения работать не будут.
1. Напишите свои процедуры обработки событий для объекта Арр в модуле класса, выбрав нужное событие из списка Процедуры в окне Программа.
2. Установите связь процедур обработки событий в модуле класса с объектом Application. Для этого сначала объявите переменную уровня модуля для своего нового класса, а затем создайте процедуру, в которой эта переменная уровня модуля будет ссылаться на объект Application. Это объявление и процедуру можно создать в любом модуле.
Предположим, что ваш модуль класса называется ApplicationEvents, и тогда для связи процедур обработки событий с объектом Application в модуле класса ApplicationEvents следует использовать следующие объявление и процедуру.
Dim EventEnabledApp As New ApplicationEvents()
Sub InitializeAppEvents()
Set EventEnabledApp = Application
End Sub
На рис. 21.2 показано окно Программа модуля класса Excel ApplicationEvents. Обратите внимание на то, что в разделе General модуля класса видно объявление Public WithEvents Арр As Application (это объявление частично закрыто списком Процедура). Как только вы добавите объявление переменной объекта Арр в раздел General модуля класса, элемент Арр появится в списке Объект окна Программа. На рис. 21.2 видно, что объект Арр уже выбран в списке Объект, а список Процедура специально раскрыт, чтобы показать, что в модуле класса в данный момент автоматически перечисляются все события, доступные для объекта Application, что, впрочем, справедливо и для любого другого объекта. (Сравните рис. 21.2 с рис. 21.1.)
Так надо
Если вы хотите, чтобы процедуры обработки событий, относящиеся к вашему объекту Application, находились все время *в боевой готовности”, создайте модуль класса для событий объекта Application в рабочей книге Personal.xls.
Чтобы быть уверенным в том, что события уровня приложения инициализируются при каждой загрузке проекта VBA, позаботьтесь о создании кода инициализации обработки событий на уровне приложения как составной части программы обработки события Open любой рабочей книги Excel.
День 21-й. Процедуры обработки событий и надстройки
773
Microsoft Visual Basic - Пень21 xls
i Eile Edit yiew Insert Formal Debug Bun loots Add-Ins Window Help
;sa-H z-^-ftM'10^ » в »E St	W/Й*; (Э j Ln 8, ebl 1
M День21 xls - ApplicationEvents (Code)
№H
Cj
L BQ ListingOS (Listing03) © Listing04 (Listing04)
. © Listing05 (Listing05) . к © Listing06 (Lsting06)
' © Listing07 (ListingO7) © Sheetl (Sheetl) ® ThisWorkbook
1 vb* Forms
ЁИ SimulatedForm
-J Modules
Exercised Exercisers
4t Exercise04
I A Exercise05
i- «С InitlalizeAppEvents
INewWorkbook
NewVVorkbriok
-jption Explicit
PubJ^i^JSj/thEvejits_App_?
Private Sub App_NewBork
End Sub
SheetActivate
SheetBeforeDoubleClick SheetBeforeRightClick SheetCalculate SheetChange SheetDeactivate SheetFollowHyperlink SheetSelectionChange WindowActivate WindowDeactivate WindowResize
Puc. 21.2. Создание процедур обработки событий, относящихся к уровню приложения, начинается с создания специального модуля класса, предназначенного для их хранения
Не забывайте, что процедуры обработки событий вашего объекта Application будут в действии только в том случае, если рабочая книга, которая содержит модуль класса с процедурами обработки событий данного приложения, загружена в данный момент.
Не загружайте несколько рабочих книг, содержащих процедуры обработки событий уровня приложения. В противном случае вы создадите двусмысленную ситуацию в среде VBA, которая чревата непредсказуемыми результатами.
Не забывайте, что нужно обязательно выполнить процедуру инициализации, которая присваивает ссылку на объект Application экземпляру нового класса объектов, чтобы реализовать связь ваших процедур обработки событий, относящихся к уровню приложения, с объектом Application.
События объектов приложения Excel
Несмотря на то что количество объектов в приложении Excel очень невелико, пользователь может получить доступ к 46 уникальным событиям. В табл. 21.1 перечислены самые популярные события объектов Excel с указанием объектов, которым они принадлежат, а также с описанием этих событий. В последующих разделах приводится информация по использованию самых важных событий из тех, что перечислены в табл. 21.1.
774
Неделя 3
Таблица 21.1. Наиболее часто используемые события объектов Excel
Событие	Принадлежит объектам	Наступает, когда
Activate	Chart, Workbook, Worksheet	Объект активизируется с помощью программы или в результате действий пользователя
Addininstall	Workbook	Рабочая книга инсталлируется как надстройка
AddinUninstall	Workbook	Рабочая книга инсталлируется как надстройка
BeforeClose	Workbook	Перед закрытием рабочей книги и перед тем как пользователю будет предложено сохранить изменения
BeforeDoubleClick	Chart, Worksheet	При двойном щелчке на встроенной диаграмме или рабочем листе, но перед действием, выполняемым при двойном щелчке по умолчанию
Before Print	Workbook	Перед выводом на печать рабочей книги или любой части ее содержимого
Before RightClick	Chart, Worksheet	При щелчке правой кнопкой мыши на встроенной диаграмме или рабочем листе, но перед действием, выполняемым при щелчке правой кнопкой мыши по умолчанию
BeforeSave	Workbook	Перед сохранением рабочей книги
Calculate	Chart, Worksheet	После пересчета содержимого ячеек рабочего листа или после построения диаграммы по новым или измененным данным
Change	Worksheet	При изменении свойства Value любой ячейки (в результате работы программы или действий пользователя)
DeActivate	Chart, Workbook, Worksheet	При деактивизации объекта (в результате работы программы или действий пользователя)
NewSheet	Workbook	При создании в рабочей книге нового листа (диаграммы или рабочего листа)
NewWorkbook	Application	При создании новой рабочей книги
День 21-й. Процедуры обработки событий и надстройки
775
Продолжение табл. 21.1
Событие	Принадлежит объектам	Наступает, когда
Open	Workbook	При открытии рабочей книги
SelectionChange	Worksheet	При изменении выделения в рабочем листе
SheetActivate	Application, Workbook	При активизации любого листа
SheetBeforeDoubleClick	Application, Workbook	При двойном щелчке на рабочем листе, но перед действием, выполняемым при двойном щелчке по умолчанию
SheetBefore RightClick	Application, Workbook	При щелчке правой кнопкой мыши на рабочем листе, но перед действием, выполняемым при щелчке правой кнопкой мыши по умолчанию
SheetCalculate	Application, Workbook	При пересчете содержимого ячеек рабочего листа или при изменении данных диаграммы
SheetChange	Application, Workbook	При изменении содержимого ячейки рабочего листа пользователем или внешней связью
SheetDeActivate	Application, Workbook	При деактивизации листа (рабочего листа или диаграммы)
SheetSelectionChange	Application, Workbook	При изменении выделения рабочего листа
WindowActivate	Application, Workbook	При активизации окна любой рабочей книги
Window DeActivate	Application, Workbook	При деактивизации окна любой рабочей книги
WindowResize	Application, Workbook	При изменении размеров окна любой рабочей книги
WorkbookActivate	Application	При активизации любой рабочей книги
WorkbookAddinlnstall	Application	При инсталляции любой рабочей книги как надстройки
WorkbookAddinUninstall	Application	При удалении любой рабочей книги как надстройки
WorkbookBefore Close	Application	Непосредственно перед закрытием любой рабочей книги
WorkbookBefore Print	Application	Непосредственно перед выводом на печать любой рабочей книги (или ее части)
776
Неделя 3
Окончание табл. 21.1
Событие	Принадлежит	Наступает, когда объектам
WorkbookBeforeSave	Application	Непосредственно перед сохра- нением любой открытой рабочей книги
WorkbookDeActivate	Application	Непосредственно перед деак- тивизацией любой открытой рабочей книги
WorkbookNewSheet	Application	При создании нового листа (рабочего листа или диаграммы) в любой открытой рабочей книге
WorkbookOpen	Application	При открытии рабочей книги
Так надо
Имейте в виду, что в табл. 21.1 перечислено только немногим более 30 событий из доступных 46. Для просмотра всех доступных событий объектов приложения Excel используйте окно Просмотр объектов.
Событие Open
Большинство крупномасштабных программ Excel модифицирует некоторым образом среду приложения. Эти модификации могут включать добавление новых команд меню, отображение специальной панели инструментов или установку параметров для Excel. В большинстве случаев эти модификации должны быть внесены в среду Excel сразу после того, как пользователь запустит свою программу, чтобы команды меню и панели инструментов были доступны с самого начала работы.
Если вы хотите, чтобы ваша программа хорошо воспринималась, вы не должны перекладывать на пользователя работу, связанную с добавлением меню и панелей инструментов или с изменением значений параметров. Подобные настройки должны автоматически выполняться при запуске программы с помощью процедуры обработки события Open, принадлежащего объекту Workbook. Событие Open генерируется при открытии любой рабочей книги, следовательно, процедура обработки события Open будет выполняться каждый раз, когда рабочая книга открывается, предоставляя идеальную возможность для создания любых специальных условий, необходимых для данной рабочей книги.
В процедуру обработки события Open можно включить следующие элементы.
•	Программные инструкции, которые инсталлируют или настраивают командные строки с целью создания меню и панелей инструментов специально для данной программы.
•	Параметры, устанавливающие, например, режим вычислений, действующий по умолчанию путь файлов, стандартный шрифт и объекты, отображаемые на экране (включая строки состояний и формул).
•	Назначения процедур обработки событий ОпКеу и OnTime.
•	Специальное диалоговое окно значений параметров для вашей программы.
День 21-й. Процедуры обработки событий и надстройки
777
В процедуру обработки события Open вы можете включить и любые другие действия, которые, по вашему мнению, должны выполняться при каждом открытии рабочей книги.
В листинге 21.1 показан пример процедуры обработки события Open.
ПРИМЕЧАНИЕ
Для корректной работы процедуры Workbook_Open, представленной в листинге 21.1, необходимо ввести ее в модуль класса объекта ThisWorkbook. Чтобы увидеть элемент ThisWorkbook, откройте в окне Редактор Visual Basic окно Project Explorer и разверните список папки Microsoft Excel Objects (если это необходимо). Дважды щелкните на объекте ThisWorkbook, чтобы отобразить окно Code модуля класса. Затем выберите в списке Objedt окна Code элемент Workbook, а в списке Procedure - элемент Open. Редактор Visual Basic автоматически введет объявление процедуры обработки события Open. После этого вы сможете ввести текст программы из листинга 21.1.
Листинг 21.1. Иснользовзнне соВытия Орен объекта Workbook
1:	Private Sub Workbook Open()
2:	'Листинг 21-1
3:	'Эта процедура запускается при открытии книги
4:
5:	Application.StatusBar	= "Запуск приложения VBA..."
6:
7:	'Подготовка среды
8:	With Application
9:	.AlertBeforeOverwriting = True
10:	.Calculation = xlManual
11:	.Caption = "VBA Rules!”
12:	.DisplayRecentFiles = False
13:	.MoveAfterReturn = False
14:	.PromptForSummaryInfo = False
15:	.SheetsInNewWorkbook = 8
16:	.UserName = 1приЬВох(рготрЬ:="Введите свое имя:",
17:	Title:="Обработка события Event”,
18:	Default:=.UserName)
19:	End With
20:
21:	'Отображение панели инструментов VB
22:	With Application.CommandBars("Visual Basic")
23:	.Visible = True
24:	.Position =	msoBarFloating
25:	.Left = 400
26:	.Top = 200
27:	End With
28:	Application.StatusBar = False
29:	End Sub
Процедура Workbook_Open начинается с присваивания значения свойству StatusBar объекта Application для отображения сообщения, уведомляющего пользователя о том, что происходит в программе (строка 5). (Свойство StatusBar можно использовать в
778
Неделя 3
любой процедуре. Это простой способ держать пользователя в курсе всего происходящего в программе, причем в смысле использования системных ресурсов этот способ менее обременителен, чем функция MsgBox.)
Строки 8—19 содержат инструкцию with, которая устанавливает некоторые свойства объекта Application приложения Excel. Большинство из этих свойств доступно в диалоговом окне Параметры (для установки этих свойств вручную используйте команду Сервис-Параметры). Однако в этом диалоговом окне вы не найдете свойства Caption (строка 11). Это свойство определяет текст, который выводится в строке заголовка главного окна приложения. Например, в самом приложении Excel выводится текст Microsoft Excel, но при желании его можно заменить названием вашего приложения, названием компании или вообще чем угодно.
Следующая инструкция With (строки 21—27) предназначена для отображения в заданном месте экрана панели инструментов Visual Basic. Эта процедура завершается установкой свойства StatusBar равным значению False (строка 28), благодаря чему сообщение в строке состояния “стирается”, а управление строкой состояния возвращается Excel.
Так надо
Чтобы запретить выполнение процедуры Workbook_Open, предназначенной для обработки события Open, при открытии рабочей книги удерживайте клавишу <Shift>. В этом случае Excel откроет рабочую книгу, не генерируя событие Open, и сразу после открытия рабочей книги будет установлен режим проектирования.
Помните, что в приложении Excel всегда (за исключением случая, когда удерживается клавиша <Shift>) при открытии рабочей книги выполняется процедура Workbook_Open, причем не важно, каким способом открыва-
. лась эта книга: с помощью программных средств (путем использования метода Workbooks.Open) или команд меню Excel.
С помощью окна Просмотр объектов стоит исследовать событие WorkbookOpen объекта Application, поскольку процедуру обработки события WorkbookOpen можно использовать для выполнения действий, связанных с открытием в приложении Excel любой рабочей книги.
Событие BeforeClose
Вполне вероятно, что при закрытии рабочей книги вам понадобится восстановить среду приложения Excel, особенно в том случае, если с помощью процедуры Workbook_Open были добавлены команды меню или панели инструментов. Для упрощения этой задачи можно воспользоваться событием BeforeClose, написав для него процедуру Workbook_BeforeClose. Событие BeforeClose выполняется непосредственно перед закрытием рабочей книги. Пример процедуры Workbook_BeforeClose показан в листинге 21.2.
Листинг 21.2. Мспользпванив события BefureClosE объекта Workbook
1:	Private Sub Workbook_BeforeClose(Cancel As Boolean)
2:	'Листинг 21-2
3:	'Эта процедура запускается при закрытии книги
4:
5:	Application.StatusBar	= "Закрытие приложения VBA ..."
6:
7:	'Сброс среды
8:	With Application
9:	.Caption = Empty
10:	.DisplayRecentFiles = True
11:	.RecentFiles.Maximum = 5
День 21-й. Процедуры обработки событий и надстройки
779
12:	.Calculation = xlAutomatic
13:	.MoveAfterReturn = True
14:	.PromptForSummarylnfo = True
15:	.SheetsInNewWorkbook = 3
16:	.CommandBars("Visual Basic").Visible = False
17:	End With
18:
19:	'Сохранение новых установок
20:	ThisWorkbook.Save
21:	Application.StatusBar = False
22:	End Sub
Эта процедура восстанавливает многие параметры среды, которые были модифицированы в приведенной выше процедуре Workbook_Open (строки 8-17). Обратите внимание на то, что свойству Caption присваивается значение Empty (строка 9). После присваивания свойству Caption специального значения Empty в строке заголовка главного окна восстанавливается исходный заголовок приложения. Поскольку эта процедура изменяет параметры рабочей книги (параметры, установленные этой процедурой, сохраняются вместе с рабочей книгой), то в строке 20 выполняется сохранение рабочей книги перед ее закрытием. Если не позаботиться о сохранении рабочей книги, то Excel отобразит сообщение, предлагающее сохранить изменения, внесенные в рабочую книгу.
Так надо
Обратите внимание на то, что процедура Workbook_BeforeClose имеет аргумент Cancel. Эго значит, что вы можете отменить закрытие рабочей книги, установив аргумент Cancel равным значению True. Возможность отмены становится особенно полезной, если процедуру Workbook_BeforeClose использовать для проверки данных в рабочей книге. В этом случае при обнаружении неверных данных можно легко отменить закрытие рабочей книги.
Помните, что Excel sce/да выполняет процедуру Workbook_Bef oreClose при закрытии рабочей книги независимо от того, как выполняется это закрытие: программными средствами или с помощью команд меню Excel.
С помощью окна Просмотр объектов стоит исследовать событие WorkbookBeforeClose объекта Application, поскольку процедуру обработки события WorkbookBeforeClose можно использовать для выполнения действий, связанных с закрытием в приложении Excel любой рабочей книги.
Событие Activate
Процедура Workbook_Open, приведенная выше в этом уроке, может оказаться весьма полезной для настройки меню, панелей инструментов и глобальных параметров, используемых каждой рабочей книгой и рабочим листом в программе. Однако во многих программах, которые работают с несколькими рабочими книгами и несколькими рабочими листами в каждой рабочей книге, требуется обеспечить изменение значений параметров при смене пользователем листов или рабочих книг. Например, при выборе пользователем определенного рабочего листа вам может потребоваться вывод на экран специально созданного диалогового окна для ввода данных, а при переходе к другой рабочей книге может возникнуть необходимость в настройке меню или отображении специальной панели инструментов.
В таких случаях пользователь активизирует различные объекты: рабочие листы и рабочие книги. Активизация конкретного объекта представляет собой некоторое событие, и перехватывать эти события можно путем написания процедуры, предназна
780
Неделя 3
ченной для обработки события Activate. Событие Activate доступно при работе с объектами Workbook, Worksheet и Chart, причем происходит оно при активизации объекта любым способом: программным или в результате взаимодействия пользователя с приложением Excel. Отмечу, что объект считается активизированным, когда он отображается в активном окне.
Пример использования события Activate показан в листинге 21.3.
Листинг 21.3. Иснодьзоваиие события Actuate объекта Worksheet
1:	Option Explicit
2:
3:	Private Sub ProcessData()
4:	MsgBox prompt:="Процедура обработки данных.",
5:	Title:=''IIpHMep процедуры обработки события"
6:	End Sub
7:
8:
9:	Private Sub Worksheet_Activate()
10:	'Выполняется при активизации рабочего листа
11:
12:	Do While True
13:	SimulatedForm.Show
14:	If Not	SimulatedForm.Canceled Then
15:	ProcessData
16:	Else
17:	Exit Do
18:	End If
19:	Loop
20:	End Sub
Листинг 21.3 содержит полный модуль класса объекта Worksheet. Строки 1—6 представляют содержимое раздела General этого модуля класса. Строки 9—20 содержат процедуру Worksheet_Activate, которая предназначена для обработки события Activate и которая содержится в разделе Worksheet этого модуля класса. Поэтому каждый раз, когда активизируется рабочий лист, содержащий этот модуль класса, выполняется процедура Worksheet_Activate.
Процедура Worksheet Activate начинается с запуска цикла Do While, который отображает диалоговое окно формы пользователя, предназначенное для ввода данных в этот рабочий лист (строка 13). В этой процедуре предполагается, что модуль класса формы пользователя содержит переменную Canceled, которая равна значению True, если диалог ввода данных отменен, и значению False в противном случае. В строке 14 проверяется состояние переменной Canceled диалогового окна формы пользователя. Если пользователь не отменил диалог ввода данных, в строке 15 выполняется процедура ProcessData (строки 3—6 листинга 21.3). В противном случае с помощью инструкции Exit Do реализуется выход из цикла (строка 17). Процедура ProcessData, занимающая строки 3—6, — фиктивная процедура, цель которой — представить обработку, которая может потребоваться для данных, введенных в диалоговое окно формы пользователя.
День 21-й. Процедуры обработки событий и надстройки
781
Так надо
Рассмотрите варианты использования других доступных для вас событий Activate. Псилтмо обработчик» Worksheet_Activate, можно соадпъ такие процедуры обработки событий, как Workbook_Activate и Chart_Activate. .
Имейте в виду, что событие Activate вызывается на нескольких уровнях различных объектов. Вы можете использовать событие WorksheetActivate для обработки факта активизации любого рабочего листа или листа диаграммы на уровне объекта Workbook или на уровне объекта Application.
Используйте событие WorkbookActivate объекта Application для создания процедуры, которая выполняется при активизации любой рабочей книги.
Используйте событие WindowActivate объекта Workbook для создания процедуры, которая выполняется при активизации окна, отображающего любую порцию рабочей книги. Событие WindowActivate вызывается и на уровне объекта Application.
Событие DeActivate
Как упоминалось выше в этой книге, обработчик события BeforeClose полезен для восстановления среды приложения Excel после изменений, которым она была подвергнута “усилиями” обработчика события Open. Аналогичным образом можно использовать событие DeActivate, а именно для создания обработчика, который восстанавливает меню, проверяет данные или выполняет некоторые другие задачи при деактивизации таких объектов, как Worksheet, Chart или Workbook. Событие DeActivate наступает каждый раз, когда любой из перечисленных объектов перестает отображаться в активном окне.
Пример процедуры обработки события DeActivate показан в листинге 21.4.
Листинг 21.4. Использование события DeActivate объекта Worksheet
1:	Option Explicit
2:
3:	Private Sub Worksheet_Activate()
4:	'Подготовка листа
5:	With Application.CommandBars("PivotTable")
6:	.Visible = True
7:	.Left = 100
8:	.Top = 200
9:	End With
10:	End Sub
11:
12:
13:	Private Sub Worksheet_Deactivate()
14:	'Сокрытие панели инструментов
15:	With Application.CommandBars("PivotTable")
16:	.Visible = False
17:	End With
18:	End Sub
782
Неделя 3
Листинг 21.4 содержит законченный модуль класса объекта Worksheet. Процедура Worksheet_Activate (строки 3-10) является обработчиком события Activate для рабочего листа. Она имитирует создание специальных условий для рабочего листа путем отображения панели инструментов Сводные таблицы.
Строки 13—18 содержат процедуру Worksheet_DeActivate, которая предназначена для обработки события DeActivate, принадлежащего рабочему листу. Цель этой процедуры —-удалить любую специальную конфигурацию, внесенную в среду приложения Excel процедурой обработки события Activate. В данном случае процедура Worksheet_DeActivate просто удаляет с экрана панель инструментов Сводные таблицы.
Так надо
Рассмотрите варианты использования других доступных для вас событий DeActivate. Помимо обработчиков^ Worksheet_DeActivate, можно создать такие процедуры обработки событий, как Workbook_DeActivate и Chart_DeActivate.
Имейте в виду, что событие DeActivate вызывается на нескольких уровнях различных обьектов. Вы можете использовать событие WorksheetDeActivate для обработки факта деактивизации любого рабочего листа или листа диаграммы на уровне объекта Workbook или на уровне объекта Application.
Используйте событие WorkbookDeActivate объекта Application для создания процедуры, которая выполняется при деактивизации любой рабочей книги.
Используйте событие WindowDeActivate объекта Workbook для создания процедуры, которая выполняется при деакгивизации окна, отображающего любую порцию рабочей книги. Событие WindowDeActivate вызывается и на уровне объекта Application.
Так не надо
Не используйте внутри процедуры обработки события DeActivate такие объекты, как ActiveWorkbook или ActiveSheet. Дело в том, что Excel переключается на новую рабочую книгу или лист еще до выполнения процедуры обработки этого события. Следовательно, любые инструкции, которые ссылаются на активную рабочую книгу или активный лист, могут быть восприняты неправильно.
Событие BeforeDoubleClick
Двойной щелчок мышью в приложении Excel приводит к различным результатам, которые зависят от “затронутого” объекта. Например, при двойном щелчке на ячейке активизируется режим редактирования содержимого ячейки, в то время как при двойном щелчке на диаграмме отображается диалоговое окно Формат области диаграммы. Подобное встроенное поведение может быть опасным, поскольку оно дает возможность пользователю редактировать ячейки или другие объекты, которые, по вашему мнению, не должны редактироваться. Для перехвата двойных щелчков, относящихся к объектам Worksheet или Chart, можно использовать событие BeforeDoubleClick. Это событие наступает непосредственно перед тем, как объект должен воспроизвести свою встроенную реакцию на двойной щелчок.
День 21-й. Процедуры обработки событий и надстройки
783
Для события BeforeDoubleClick предусмотрено два аргумента: Target и Cancel. Используйте аргумент Target (он представляет собой ссылку на объект Range в приложении Excel) для определения того, на какой ячейке рабочего листа был выполнен двойной щелчок. Аргумент Cancel используйте для отмены встроенной реакции объекта на двойной щелчок. В листинге 21.5 показан пример процедуры обработки события BeforeDoubleClick для объекта Worksheet, в которой используется оба возможных аргумента.
Аветяна 21.5. Мсипльзовнние события ВвТогвВонМвСНск объекта Worksheet
1:	Private Sub Worksheet BeforeDoubleClick(ByVal Target As Excel.Range,
2:	Cancel As Boolean)
3:	'Запрещает редактирование ячеек в нечетных строках и столбцах
4:
5:	With Target
6:	If ((.Column Mod 2) <> 0) Or
7:	((.Row Mod 2) <> 0) Then
8:	MsgBox prompt:^'Редактирование ячеек в нечетных " &
9:	"строках и столбцах запрещено.'',
10:	Title:="Пример события BeforeDoubleClick"
11:	Cancel = True
12:	End If
13:	End With
14:	End Sub
Листинг 21.5 содержит только одну процедуру Worksheet_BeforeDoubleClick, которая предназначена для обработки события BeforeDoubleClick, принадлежащего объекту Worksheet. Эта процедура довольно проста и демонстрирует возможность построения такой процедуры и использования ее аргументов.
В строках 1 и 2 содержится объявление процедуры Worksheet BeforeDoubleClick (это объявление переформатировано с помощью символа продолжения строки, чтобы оно поместилось на странице книги). Обратите внимание на то, что процедура Worksheet_BeforeDoubleClick имеет два аргумента: Target и Cancel. Эти аргументы предоставляются автоматически объектом Worksheet при выполнении этой процедуры. Аргумент Target представляет собой ссылку на ячейку рабочего листа, на которой был выполнен двойной щелчок. Аргумент Cancel хранит значение типа Bolean, означающее, хотите ли вы, чтобы объект Worksheet отменил встроенную обработку двойного щелчка.
Строка 5 содержит начало инструкции With, которая использует переменную объекта Target. В строках 5 и 6 расположено условное выражение инструкции If, которое проверяет номер строки и столбца ячейки на нечетность. (Оператор Mod возвращает остаток, образующийся при выполнении операции целочисленного деления. Если остаток после деления числа на 2 не равен нулю, значит, это число нечетное.) Если хотя бы один из номеров (строки или столбца ячейки) окажется нечетным числом, будет выполнена инструкция MsgBox (строки 8—10), которая отобразит сообщение, объясняющее причину невозможности редактирования содержимого ячеек.
Обратите внимание на строку 11. Эта инструкция устанавливает аргумент Cancel события BeforeDoubleClick равным значению True. Тем самым инструкция, содержащаяся в строке И, отменяет встроенный обработчик двойного щелчка для объекта Worksheet, в результате чего запрещается редактирование содержимого ячеек с нечетным номером строки или столбца.
784
Неделя 3
Так надо
Имейте в виду, что событие BeforeDoubleClick для листа диаграммы (объекта Chart) предоставляет прекрасную возможность для создания диаграмм детализации.
Помните, что двойной щелчок также является событием, которое действует и на уровнях объектов Application и Workbook. В этом случае двойной щелчок проявляет себя как событие WorksheetBeforeDoubleClick. Эго событие ‘срабатывает* каждый раз, когда двойной щелчок выполняется на любом листе в некоторой рабочей книге (для объектов Workbook) или когда двойной щелчок выполняется на любом листе в любой рабочей книге (для объекта Application).
Не забывайте о событиях BeforeRightclick и SheetBeforeRightClick. Эти события можно использовать для отображения создаваемых вами самими контекстных меню для рабочих листов и диаграмм.
Событие Change
Чтобы быть уверенным в точности данных рабочего листа, хорошо бы иметь возможность проверки содержимого каждой ячейки. Например, вы могли бы убедиться в том, что элемент поля даты действительно является датой или что введенное число удовлетворяет определенному условию (например, является положительным). Это можно сделать, написав процедуру для обработки события Change. Для события Change предусмотрен один аргумент Target, который представляет собой ссылку на объект Range в приложении Excel.
Пример использования события Change для верификации данных показан в листинге 21.6.
Листинг 21.В. Использование события Change оДъекта Worksheet
1:	Option Explicit
2:
3:	Private Function VerifiedCellData(ByVal rngSource As Excel.Range)
4:	'Проверяет данные в указанных ячейках.
5:	'В 1-м столбце должна быть дата, во 2-м должно быть
6:	'положительное число
7:	Const strTitle = "Проверка данных"
8:
9:	VerifiedCellData = False
10:
11:	With rngSource
12:	Select	Case .Column
13:	Case	1 'Дата
14:	If	Not IsDate(.Value)	Then
15:	MsgBox prompt:=11 Неверные данные в столбце даты.",
16:	Title:=strTitle
17:	Exit Function
18:	Else
19:	.NumberFormat = "mmmm d, yyyy"
20:	End If
21:	Case 2	'Количество
День 21-й. Процедуры обработки событий и надстройки
785
22:	If IsNumeric(.Value) Then
23:	If .Value < 0 Then
24:	MsgBox prompt:="Количество должно быть неотрицательным.",
25:	Title:=strTitle
26:	Exit Function
27:	Else
28:	.NumberFormat = "$#,##0.00 );{$#,##0.00)"
29:	End If
30:	Else
31:	MsgBox prompt:="Введите числовую величину.", _
32:	Title:=strTitle
33:	Exit Function
34:	End If
35:	End Select
36:	End With
37:
38:	VerifiedCellData = True 'Правильные данные
39:	End Function
40:
41:
42:	Private Sub Worksheet_Change(ByVal Target As Excel.Range)
43:	'При событии Change проверяет данные в листе
44:
45:	Static InError As Boolean
46:
47:	If InError Then
48:	InError = False
49:	Else
50:	If Not VerifiedCellData(Target) Then
51:	InError = True
52:	Target.Select
53:	Target.ClearContents
54:	End If
55:	End If
5	6: End Sub
Листинг 21.6 содержит законченный модуль класса объекта Worksheet. В строках 1— 39 представлены инструкции, входящие в состав раздела General модуля класса, а строки 42—56 содержат процедуру Worksheet_Change из раздела Worksheet этого класса модуля. Сначала рассмотрим процедуру Worksheet_Change.
Процедура Worksheet_Change выполняется каждый раз, когда изменяется свойство Value любой ячейки в рабочем листе, причем не важно, каким способом: программным или в результате действий пользователя. Обратите внимание на аргумент Target в строке 42. Этот аргумент автоматически предоставляется объектом Worksheet при каждом выполнении процедуры Worksheet_Change. Аргумент Target содержит ссылку на ячейку, у которой изменилось свойство Value.
Обратите внимание на объявление переменной InError с использованием ключевого слова Static. Эта переменная типа Boolean используется для отслеживания признака обработки ошибки в ячейке.
Итак, процедура Worksheet_Change выполняется каждый раз, когда изменяется свойство Value любой ячейки в рабочем листе. В строке 47 проверяется значение перемен
786
Неделя 3
ной InError. Если оно равно False, то в строке 50 вызывается функция VerifiedCellData, которая возвращает значение True, если данные в ячейке, на которую указывает аргумент Target, допустимы, и — False в противном случае. Если функция VerifiedCellData вернет значение False, в строке 51 переменная InError будет установлена равной значению True, которое указывает на то, что в данный момент происходит обработка ошибки. Затем в строке 52 выделяется ячейка рабочего листа, на которую ссылается аргумент Target. Тем самым выделение возвращается той же ячейке, чтобы пользователь мог легко заменить неверное значение, которое удаляется из ячейки с помощью инструкции, расположенной на строке 53.
При выполнении строки 53 без промедления снова вызывается процедура Worksheet_Change, поскольку в ячейке рабочего листа изменилось свойство Value. Следовательно, процедура Worksheet_Change выполняется рекурсивно. Если бы не было способа прекратить рекурсию, процедура Worksheet Change продолжала бы выполняться бесконечно долго. Для определения факта рекурсивного обращения к процедуре Worksheet_Change служит статическая переменная InError. При первом обнаружении ошибки переменная InError устанавливается равной True. Когда процедура Worksheet_Change выполняется рекурсивно, переменная InError равна значению True, и после выполнения строки 48 переменная InError устанавливается равной значению False. В этом случае обработка рекурсивного вызова завершается, как завершается обработка и исходного обращения к процедуре Worksheet_Change.
Следовательно, результатом выполнения процедуры Worksheet_Change является тестирование допустимости значения ячейки. Если ячейка содержит неверные данные, выделение возвращается в эту ячейку и отображается сообщение, уведомляющее о некорректности ее содержимого, а затем “пострадавшая” ячейка очищается от “неугодного” значения.
Функция VerifiedCellData (строки 3-36) принимает один аргумент — rngSource — который является ссылкой на проверяемую ячейку. Когда процедура Worksheet_Change вызывает функцию VerifiedCellData, она передает диапазон Target в функцию VerifiedCellData через аргумент rngSource. Функция VerifiedCellData использует инструкцию Case (строки 12—35) для проверки данных ячейки в зависимости от того, в каком столбце находится ячейка. Ячейки первого столбца рабочего листа должны содержать значения типа Date, а ячейки второго столбца — положительные числовые значения. Если значение некорректно, функция VerifiedCellData отображает сообщение, описывающее выявленную некорректность, и возвращает значение False, в противном случае возвращаемое значение равно True.
Так надо
Используйте аргумент Target события Change для ссылки на ячейку, содержимое которой было изменено.
Предусмотрите выполнение процедуры обработки события Change после того, как пользователь воспользуется такими командами, как Вырезать, Вставить или Очистить содержимое (Удалить), изменяющими содержимое ячейки. Объект Worksheet генерирует событие Change при любом изменении свойства Value.
Так не надо
Не забывайте о том, что событие Change также генерируется на уровнях объектов Application и Workbook. В этом случае оно проявляет себя как событие Sheetchange. Событие Sheetchange происходит для объекта Workbook каждый раз, когда изменяется любая ячейка в любом рабочем листе данной рабочей книги. Событие Sheetchange происходит на уровне объекта Application каждый раз, когда изменяется любая ячейка в любом рабочем листе любой рабочей книги.
День 21-й. Процедуры обработки событий и надстройки	787
Не забывайте о том. что если процедура обработки события Change (или любая процедура, которую она вызывает) изменяет содержимое какой-нибудь ячейки, событие Change генерируется повторно, что приводит к рекурсивному выполнению вашего обработчика этого события.
Событие Calculate
В рабочем листе с большой и сложной моделью данных изменение одной переменной может повлиять на результаты вычислений десятков формул. Если вы не хотите сами отслеживать все эти формулы — постоянно проверять, например, что все граничные условия по-прежнему соблюдаются, — можно использовать событие Calculate и процедуру обработки этого события, которая все проверит за вас. Событие Calculate не имеет аргументов. Пример использования события Calculate показан в листинге 21.7.
Листинг 21.7. Использование соОытия Calculate оОъЕкта Worksheet
1:	Option Explicit
2:
3:	Private Sub Worksheet_Calculate()
4:	'Выполняется при каждом перечсете листа
5:	If Range("GrossMargin") <0.2 Then
6:	MsgBox prompt:=”Общий доход меньше 20V,
7:	Title:="Пример события Calculate"
8:	End If
9:	End Sub
Процедура Worksheet_Calculate, представленная в листинге 21.7, довольно проста и демонстрирует использование события Calculate. Процедура Worksheet_Calculate вызывается каждый раз, когда на рабочем листе выполняются вычисления, причем не важно, каким способом: вручную или автоматически. Работа процедуры Worksheet_Calculate основана на предположении, что в рабочем листе существует некоторый диапазон, именуемый GrossMargin, который отображает результат вычисления формулы валовой прибыли. При выполнении вычислений в данном рабочем листе вызывается процедура Worksheet_Calculate, которая проверяет значение диапазона GrossMargin. Если это значение меньше 0,2 (т.е. меньше 20%), отображается сообщение, уведомляющее о полученном результате.
Так надо
Имейте в виду, что событие Calculate также генерируется на уровнях объектов Application и Workbook. В этом случае оно проявляет себя как событие SheetCalculate. Событие SheetCalculate происходит для объекта Workbook каждый раз, когда выполняется вычисление в любом рабочем листе данной рабочей книги.
• Событие SheetCalculate происходит на уровне объекта Application каждый раз, когда выполняется вычисление в любом рабочем листе любой рабочей книги.
Так не надо
Не забывайте о том, что событие Calculate также генерируется для листов диаграмм (т.е. объектов Chart).
788
Неделя 3
Другие события
Как видно из табл. 21.1, помимо событий, которые рассмотрены в предыдущих разделах, существует много других. Причем не следует забывать, что в табл. 21.1 перечислена только часть из 46 уникальных событий, генерируемых объектами приложения Excel. В Excel предусмотрено слишком много событий, чтобы для всех из них в этой книге привести примеры использования. Тем не менее, о некоторых событиях вы должны знать обязательно, поскольку они чрезвычайно полезны. В следующем списке перечислены не описанные до сих пор события, которые сгруппированы по выполняемым ими функциям.
•	Печать. Событие BeforePrint принадлежит объекту Workbook, а событие WorkbookBeforePrint — объекту Application. Используйте событие BeforePrint для выполнения любых задач, которые вы бы хотели реализовать до начала процесса печати. Например, вы могли бы использовать процедуру обработки события BeforePrint объекта Workbook для выбора определенных представлений для печати либо для получения гарантии того, что во всех рабочих листах выполнены вычисления или сформатирован отчет. Процедуру обработки события BeforePrint можно использовать для автоматизации настройки сложных отчетов, т.е. после того как пользователь выберет команду Печать, с помощью обработчика события BeforePrint пользователю можно предложить выбрать для печати нужное представление или листы.
•	Сохранение рабочих книг. Событие BeforeSave принадлежит объекту Workbook, а событие WorkbookBeforeSave — объекту Application. Эти события возникают непосредственно перед закрытием активной рабочей книги (для объекта Workbook) или перед закрытием любой рабочей книги (для объекта Application). Используйте событие BeforeSave для выполнения таких задач, как контроль данных, удаление временных рабочих листов и пр. Событие BeforeSave предоставляет два аргумента. Если аргумент SaveAsUi (он имеет тип Boolean) равен значению True, Excel отобразит диалоговое окно Сохранить как. Второй аргумент Cancel также имеет тип Boolean. Если он равен значению True, после завершения процедуры обработки события BeforeSave операция сохранения отменяется.
•	Создание новых объектов. Событие NewSheet принадлежит объекту Workbook. Это событие наступает каждый раз, когда к рабочей книге добавляется новая диаграмма или рабочий лист. Объект Application генерирует два события того же плана: WorkbookNewSheet и NewWorkbook. Событие WorkbookNewSheet возникает каждый раз, когда к любой открытой рабочей книге добавляется новый лист, а событие NewWorkbook наступает при создании новой рабочей книги. Используйте события NewSheet и NewWorkbook для добавления в листы или рабочие книги в процессе их создания таких заранее сконфигурированных элементов, как заголовки столбцов, названия листов, именованные диапазоны.
•	Изменение областей выделения. Событие Selectionchange принадлежит объекту Worksheet. Это событие наступает при каждом изменении области выделения в рабочем листе. На уровне объектов Workbook и Application генерируется событие SheetSelectionChange. Используйте событие Selectionchange для предоставления пользователю выбора одной из фиксированных областей выделения на рабочем листе или для контроля содержимого ячейки, которая перестала быть выделенной.
День 21 -й. Процедуры обработки событий и надстройки
789
Свойства и методы, связанные с событиями
Помимо событий, генерируемых объектами, в приложении Excel также предусмотрена возможность работы с такими элементами, как свойства и методы. Эти свойства и методы можно использовать для создания процедур обработки событий, которые не генерируются непосредственно объектами приложения Excel. Например, метод ОпКеу можно использовать для инсталляции своих собственных команд, выполняемых с помощью клавиатуры. В этом случае ОпКеу-процедуры будут выполняться при каждом нажатии определенной клавиши на клавиатуре. Таким образом, метод ОпКеу предоставляет способ косвенной инсталляции процедур, которые обеспечивают реакцию на события, связанные с нажатием конкретных клавиш. В следующих разделах этого урока свойства и методы, связанные с подобными событиями, рассматриваются более детально.
Сввисшво OnWindow
Свойство OnWindow по своему назначению напоминает событие WindowActivate: оно задает процедуру обработки события, которая выполняется всякий раз, когда пользователь активизирует определенное окно. Используйте свойство OnWindow, если хотите выполнять конкретную процедуру каждый раз, когда любое окно на конкретном рабочем листе становится активным окном. Свойство OnWindow можно использовать и в случае, если нужно вычислить содержимое текущего окна и разрешить или запретить отображение в вашем приложении команд меню или панелей инструментов в зависимости от конкретного содержимого окна. (Не забывайте, что на одном и том же рабочем листе можно открыть сразу несколько окон.) Свойство OnWindow принадлежит объектам Application и Window. Синтаксис установки свойства OnWindow имеет следующий вид.
Object.OnWindow = "ИмяПроцедуры"
Элемент Object представляет собой ссылку либо на объект Window, либо на объект Application. Если элемент Object ссылается на объект Window, процедура обработки события выполняется каждый раз, когда пользователь переключается в заданное окно. Если же элемент Object ссылается на объект Application, процедура обработки события выполняется каждый раз, когда пользователь переключается в любое окно.
Чтобы удалить процедуру обработки события, связанную с окном через свойство OnWindow, присвойте этому свойству пустую строку (строку с нулевой длиной):
Object.OnWindow = ""
Пример использования свойства OnWindow показан в листинге 21.8.
Листинг 21.В. Иснодьзованне свойства BHWlHdow
1:	Option Explicit
2:
3:	Sub SetWindowHandler()
4:	'Устанавливает обработчик события с помощью свойства OnWindow
5:
6:	Windows ("День_21.ХЬЗ: 2"). OnWindow = "WindowHandler"
7:	End Sub
8:
9:
10:	Sub WindowHandler()
790
Неделя 3
11:	'Выполняется каждый раз при активизации окна fleHb_21.XLS:2 12:
13:	With Windows{"День_21.ХЬ5:2")
14:	.Splitvertical = 150
15:	.DisplayFormulas = True
16:	.DisplayGridlines = False
17:	.Displayzeros = False
18:	.FreezePanes = True
19:	.Windowstate = xlMaximized
20:	End With
21:	End Sub
22:	Sub ClearWindowHandlerj)
23:	'Удаляет обработчик события с помощью свойства OnWindow
24:
25:	Windows("День_21.XLS:2").OnWindow = ""
26:	End Sub
Листинг 21.8 содержит законченный стандартный модуль. В данном примере процедура SetWindowHandler (строки 3—7) устанавливает свойство OnWindow для окна Day21.xls: 2 (строка 6). Процедура обработки события WindowHandler с помощью инструкции With устанавливает несколько свойств окна (строки 13—20): окно делится по вертикали (строка 14), формулы отображаются (строка 15), линии сетки отключены (строка 16), нули не отображаются (строка 17), панели зафиксированы (строка 18) и окно развернуто (строка 19). (Процедура WindowHandler работает только в том случае, если текущий отображаемый в окне лист является рабочим листом. В противном случае процедура генерирует ошибки времени выполнения.)
Чтобы создать новое окно для рабочей книги, убедитесь сначала, что окно рабочей книги не развернуто. Затем щелкните правой кнопкой мыши на строке заголовка окна рабочей книги и выберите из контекстного меню команду Новое. Excel для просмотра этой рабочей книги откроет новое окно.
Для того чтобы процедура WindowHandler работала корректно, нужно сначала выполнить процедуру SetWindowHandler после открытия второго окна в рабочей книге Day21.xls. Для удаления процедуры обработки события WindowHandler выполните процедуру ClearWindowHandler.
Так надо
Имейте в виду, что процедуры обработки событий, которые вы создаете путем назначения их свойству OnWindow, могут храниться в любом модуле, в отличие от процедур, связанных с событиями, генерируемыми объектами, которые должны храниться в модуле класса.
Помните, что процедуры обработки событий, которые вы создаете с помощью свойства OnWindow, должны быть инсталлированы процедурой, выполняющей присваивание этому свойству OnWindow. В отличие от объектов, вызываемых событиями, не существует способа автоматически связать OnWindow-процедуру обработки события с конкретным окном.	
Если вы хотите, чтобы всякий раз, когда активизируется или деактивизируется какое-нибудь окно, отображающее определенный рабочий лист или рабочую книгу, выполнялась некоторая последовательность действий, используйте вместо свойства OnWindow события объектов WindowActivate и WindowDeActivate.
День 21-й. Процедуры обработки событий и надстройки
791
Метод OnKeg
Напомню, что у вас есть возможность интерактивно назначать комбинации клавиш ускоренного доступа своим процедурам Excel. Это делается путем использования команды Сервис-Макрос-Макросы, выбора в открывшемся диалоговом окне Макрос нужной процедуры и затем — щелчка на кнопке Параметры. Комбинации клавиш ускоренного доступа можно также инсталлировать в своей программе Excel, используя метод ОпКеу. Обычно комбинации клавиш ускоренного доступа имеют форму <Ctrl+клавиша>, где элемент клавиша означает букву алфавита. (Не забывайте, что в приложении Excel использование прописных и строчных букв отличается по результатам.) Несмотря на ограниченный выбор букв (особенно потому, что в Excel некоторые буквы используются для встроенных комбинаций клавиш, например для команды Правка-Копировать используется комбинация клавиш <Ctrl+C>), в большинстве приложений этот метод работает прекрасно.
Метод ОпКеу также оказывается полезным при запрете определенных комбинаций клавиш. Например, если вы хотите запретить пользователю что-либо удалять, можно программными средствами удалить из панели инструментов кнопку Вырезать и из меню Правка — команды Очистить и Вырезать. Но что же делать с клавишей <Delete>? В этом случае для инсталляции своего собственного обработчика событий, связанного с клавишей < Delete > или любыми другими клавишами, можно использовать метод ОпКеу.
Application.ОпКеу(Кеу, [Procedure])
Обязательный аргумент Key представляет собой строку, задающую клавишу или комбинацию клавиш, которую вы хотите перехватывать. Для указания букв, цифр или знаков препинания достаточно заключить их в кавычки (например "а"). Для использования других клавиш используйте информацию, приведенную в табл. 21.2. Можно также создавать комбинации клавиш, состоящие из букв или одной из клавиш, перечисленных в табл. 21.2, и таких специальных клавиш, как <Alt>, <Ctrl> и <Shift>, которые обозначаются символами, указанными в табл. 21.3. (Значения клавиш, приведенные в табл. 21.2 и 21.3, совпадают со значениями клавиш, которые используются в методе SendKeys, описанном в уроке 20.)
Необязательный аргумент Procedure является строкой, задающей имя процедуры, которая выполняется каждый раз, когда пользователь нажимает указанную клавишу. Если в качестве аргумента Procedure ввести строку нулевой длины, заданная клавиша будет запрещена. Если вообще опустить аргумент Procedure, Excel вернет клавишу в ее нормальное состояние.
Таблица 21.2. Текстовые строки для использования в качестве аргумента Key метода ОпКеу
Для клавиш	Используйте
<Backspace>	{BACKSPACE} или	{BS}
<Break>	{BREAK}
<Caps Lock>	{CAPSLOCK}
<Delete>	{DELETE}
<Down Arrow>	{DOWN}
<End>	{END}
<Enter> (дополнительная клавиатура)	{ENTER}
<Enter>	{'} (тильда)
<Esc>	{ESCAPE} или {ESC}
<Home>	{HOME}
792
Неделя 3
Окончание табл. 21.2
Для клавиш	Используйте
<Insert> <Left Arrow > <Num Lock> <Page Down> <Page Up> < Right Arrow > < Scroll Lock> <Tab> <Up Arrow> <F1>—<F12>	{INSERT} {LEFT} {NUMLOCK} {PGDN} {PGUP} {RIGHT} {SCROLLLOCK} {TAB} {UP} {F1}-{F12}
Tiiiiu 21.3. CiMiiaw |ii linn <*lt>, <Ctrl> i <SHft>
Для клавиш	Используйте символы
<Alt>	% (процент)
<Ctrl>	Л (знак вставки)
<Shift>	+ (плюс)
Пример использования метода ОпКеу показан в листинге 21.9.
Лнсшниа 21.3. Исиодьзоиаиив метода ОиКец
1:	Option Explicit
2:
3:	Sub SetShortcutKeys()
4:	'Устанавливает горячие клавиши Ctrl+Shift+O и Ctrl+Shift+G
5:	With Application
6:	.ОпКеу Кеу:="Л+0", Procedure:="DisplayGeneralOptions"
7:	.ОпКеу Key:="A+G", Procedure:="ToggleGridlines"
8:	End With
9:	End Sub
10:
11:
12:	Sub DisplayGeneralOptions()
13:	'Открывает вкладку Общие диалогового окна Параметры
14:	Application.Dialogs(xlDialogOptionsGeneral).Show
15:	End Sub
16:
17:	Sub ToggleGridlines()
18:	'Включает и выключает отображение сетки
19:	With ActiveWindow
20:	.DisplayGridlines = Not .DisplayGridlines
21:	End With
22:	End Sub
23:
24:	Sub ResetShortcutKeysf)
День 21-й. Процедуры обработки событий и надстройки
793
25:	'Отменяет горячие клавиши Ctrl+Shift+O и Ctrl+Shift+G
26:	With Application
27:	.ОпКеу Кеу:="Л+0"
28:	.ОпКеу Key:="A+G”
29:	End With
30:	End Sub
Листинг 21.9 содержит полный модуль с четырьмя различными процедурами. В первой процедуре — SetShortcutKeys — для определения двух клавиш ускоренного доступа используется два метода ОпКеу. С помощью первой инструкции ОпКеу (строка 6) назначается процедура DisplayGeneralOptions, которая должна выполняться при каждом нажатии комбинации клавиш <Ctrl+Shift+O>. Вторая инструкция ОпКеу (строка 7) назначает процедуру ToggledGridlines, которая будет выполняться при каждом нажатии комбинации клавиш <Ctrl+Shift+G>.
Процедура DisplayGeneralOptions (строки 12—15) вызывает метод Show (строка 14) для отображения вкладки General (Общие) диалогового окна Options (Параметры) с помощью задания константы xlDialogOptionsGeneral. Процедура ToggledGridlines (строки 17-22) просто переключает свойство DisplayGridlines для активного окна (объекта ActiveWindow) (строка 20).
Процедура ResetShortcutKeys (строки 24—30) восстанавливает две назначенных ранее комбинации клавиш ускоренного доступа (<Ctrl+Shift+O> и <Ctrl+Shift+G>), возвращая им первоначальные действия (для комбинации клавиш <Ctrl+Shift+O> исходным действием является отсутствие каких-либо действий). Возврат комбинациям клавиш встроенных функций осуществляется с помощью метода ОпКеу без использования необязательного аргумента Procedure (строки 27 и 28).
Метод OnTime
С помощью метода OnTime можно выполнить VBA-процедуру в заданное время и даже в заданный день. Метод OnTime полезно использовать для создания процедур обработки событий, связанных с временем и датой, с целью выполнения следующих задач.
•	Создание в плановом порядке резервных копий данных рабочих книг. В уроке 19 была представлена процедура, которая создает резервную копию рабочей книги на дискете. Вы могли бы установить метод OnTime таким образом, чтобы программа резервирования запускалась, скажем, ежедневно в 5 часов вечера, причем резервную копию можно хранить не только на дискете, но и на сервере или в любом другом указанном месте.
•	Планирование таких долговременных работ, как пересчет ячеек в больших рабочих листах или печать отчетов (если вы не собираетесь сами занимать какое-то время системные ресурсы своего компьютера).
•	Отображение в окне приложения Excel сообщений, напоминающих о деловых встречах, свиданиях, обеденных перерывах и пр.
•	Запуск в определенное время других программ для проверки электронной почты и некоторых других задач.
Общий синтаксис метода OnTime имеет следующий вид.
Application.OnTime (ErliestTime, Procedure, [LastTime], [Schedule])
794
Неделя 3
Обязательный аргумент ErliestTime представляет собой число, означающее время и дату (по желанию) выполнения указанной процедуры. Для аргумента ErliestTime используйте последовательные числа даты (они были рассмотрены в уроке 3). Обязательный аргумент Procedure — это строка, содержащая имя процедуры, которая должна выполняться при наступлении времени, указанного с помощью аргумента ErliestTime.
Если приложение Excel не готово выполнить заданную процедуру в назначенное время (аргументом ErliestTime), то попытки выполнить эту процедуру будут продолжаться до тех пор, пока не наступит время, указанное с помощью необязательного аргумента LastTime. Для аргумента LastTime также используются последовательные числа даты. Если аргумент LastTime опущен, Excel выполнит заданную процедуру при первой же возможности сразу после наступления времени ErliestTime.
Необязательный аргумент Schedule представляет собой значение типа Boolean, которое подтверждает или отменяет выполнение назначенной процедуры в назначенное время. Если аргумент Schedule равен значению True или опущен, Excel выполнит соответствующую процедуру в запланированное время. Если же аргумент Schedule равен значению False, Excel отменяет установку метода OnTime.
Пример использования метода OnTime показан в листинге 21.10.
Проще всего ввести последовательное число даты для задания времени или даты в аргументах метода OnTime с помощью функции TimeValue или DateValue. Используйте функцию TimeValue для создания последовательного числа даты, содержащего только значение времени.
TimeValue(Time)
В приведенном здесь синтаксическом выражении аргумент Time означает строку, представляющее желаемое время, например: “5:00 РМ" или “17:00”. Аналогично, если вы хотите получить последовательное число даты, содержащее как значение времени, так и значение даты, используйте функцию DateValue.
DateValue(Date)
Аргумент Date означает строку, представляющее желаемую дату и время, например: “3/15/94 5:00 РМ”.
Лившица 21,10. Использование мвшоуа ОиПшв
1:	Sub SetReminder()
2:	'Устанавливает обработчик события OnTime
3:	Application.OnTime EarliestTime:=TimeValue("ll:42 AM”),
4:	Procedure:="День_21.XLS!RemindMe"
5:	End Sub
6:
7:
8:	Sub RemindMe()
9:	'Выводит напоминающее сообщение
10:	Beep
11:	MsgBox prompt:="Ha часах " &
12:	Format(Time,	"h:mm AM/PM")	&	_
13:	vbCr i
14:	“He забудь о	встрече!"
15:	End Sub
День 21-й. Процедуры обработки событий и надстройки
795
Листинг 21.10 содержит две процедуры. В первой процедуре, SetReminder (строки 1—5) метод OnTime используется для инсталляции процедуры RemindMe, которая должна быть выполнена в 3:22 после полудня (р.т.) (строки 3 и 4). Процедура RemindMe (строки 8—15) обеспечивает звуковой сигнал (в строке 10) и отображает напоминающее сообщение (строки 11—14). Константа vbCr в строке 13 представляет в сообщении символ возврата каретки. Этот символ удобно использовать, когда в окне сообщения нужно начать новую строку.
Так надо
Не забывайте о том, что в приложении Excel процедура обработки события OnTime выполняется только один раз. Если некоторая процедура была (?????????или не была) выполнена сегодня в полдень, то имейте в виду, чю та же самая процедура не будет выполнена и завтра в полдень даже в том случае, если вы оставите свой компьютер включенным и с загруженным приложением Excel.
Не пытайтесь отменить метод OnTime, который уже истек, в противном случае приложение Excel сгенерирует сообщение об ошибке.
Надстройки
Если вам уже приходилось использовать какие-либо надстройки Excel, то вы могли убедиться в удобстве работы с ними. Они вносят в среду приложения Excel дополнительные функции и команды, которые выглядят так, как будто они встроены в Excel. Созданные вами рабочие книги можно преобразовать в надстройки и получить при этом следующие преимущества.
•	В приложении Excel ваши процедуры Sub не отображаются в диалоговом окне Макрос, а ваши объекты не “попадут” в окно Просмотр объектов. Это значит, что пользователи должны получать доступ к процедурам ваших надстроек с помощью клавиш ускоренного доступа, команд меню, кнопок панелей инструментов или других таких косвенных средств, как обработчики событий.
•	Надстройки работают несколько быстрее, чем обычные файлы.
•	Вы можете защитить исходный текст своей надстройки, чтобы другие пользователи не могли просматривать или модифицировать его.
Важно иметь в виду, что надстройки загружаются по требованию. Это значит, что при инсталляции надстройки ее подготовка к работе проходит в два этапа.
1. Становятся доступными клавиши ускоренного доступа надстройки, инсталлируются строки команд меню, и к мастеру функций Excel добавляются функции надстройки.
2. Остальная часть приложения загружается в память тогда, когда пользователь либо выбирает одну из функций надстройки, либо нажимает клавишу ускоренного доступа надстройки, либо выбирает элемент меню, либо щелкает на кнопке панели инструментов надстройки.
796
Неделя 3
Исключением из описанной выше последовательности загрузки будет такая надстройка, которая имеет процедуру обработки события Open. В этом случае надстройка полностью загружается с самого начала.
Создание падсшрпйки
Создание надстройки — это довольно простой процесс. Создаваемые вами надстройки хранятся как файлы надстроек Excel и имеют расширение .xla.
Вы могли бы заметить, что некоторые надстройки, поставляемые с приложением Excel или распространяемые сторонними производителями, имеют расширение .xll. Это значит, что данные надстройки не созданы с помощью языка VBA. Используя язык VBA, можно создавать программы надстроек только с расширением . xla.
После полной отладки и тестирования программы вы готовы передать ее своим пользователям. Для преобразования проекта VBA, сохраненного в виде рабочей книги, в формат надстройки, выполните следующее.
1.	Откройте рабочую книгу Excel, которая содержит приложение VBA, подлежащее преобразованию в надстройку.
2.	Скройте любые рабочие листы, которые не предназначены для глаз пользователя, а затем выберите команду Сервис-Защита-Защитить книгу, чтобы обеспечить защиту структуры рабочей книги с помощью пароля.
3.	Перейдите в режим редактора Visual Basic.
4.	В окне Visual Basic откройте окно Project и выберите проект, предназначенный для преобразования в надстройку.
5.	Выберите команду Debug-Compile, чтобы скомпилировать свой проект VBA. Компиляция проекта непосредственно перед преобразованием его в надстройку гарантирует, что весь текст программы надстройки находится в скомпилированном состоянии, и что ваша настройка будет выполняться на максимальной скорости.
6.	Выберите команду Tools-Properties <VBA project>, чтобы отобразить диалоговое окно Project Properties, и щелкните на вкладке Protection, если она еше не отображена. На рис. 21.3 показано диалоговое окно Project Properties проекта День21.
7.	Во вкладке Protection диалогового окна Project Properties установите флажок Lock Projecr for Viewing (см. рис. 21.3).
8.	Введите пароль для своей надстройки в текстовое поле Password. После сохранения надстройки исходный текст программы надстройки можно будет редактировать только в том случае, если пользователь правильно введет пароль.
9.	Подтвердите свой пароль, введя его во второй раз в текстовое поле Confirm Password. Если два введенных варианта паролей не совпадут, то после вашего щелчка на кнопке ОК редактор Visual Basic отобразит сообщение об ошибке.
10.	Щелкните на кнопке ОК. Теперь ваш проект находится под защитой редактора Visual Basic. Компоненты проекта теперь нельзя просмотреть в окне Project, а исходный текст программы — в окне Code, если не ввести правильный пароль.
ПРЕДУПРЕЖДЕНИЕ
Не существует способа восстановить или узнать потерянный пароль для своей надстройки. Убедитесь, что вы хорошо помните пароли, используемые для защиты своих рабочих книг и VBA-проектов.
День 21-й. Процедуры обработки событий и надстройки
797
Day21 - Project Properties
Puc. 21.3. С помощью окна Project Properties можно защитить паролем исходный текст вашего VBA-проекта
11.	В окне Excel ыберите команду Файл, Сохранить как.
12.	Выберите в раскрывающемся списке Тип файла диалогового окна Сохранение документа элемент Надстройка Microsoft Excel.
13.	Заполните должным образом остальные опции диалогового окна Сохранение документа: введите для надстройки имя файла и выберите папку.
14.	Для сохранения рабочей книги щелкните на кнопке Сохранить. На этом создание вашей надстройки завершено.
Так надо
Имейте в виду, что, если не добавлять в проект защиту с помощью пароля, пользователь сможет редактировать исходный текст программы надстройки и объекты. Если вы же прибегнете к защите с помощью пароля, исходный текст вашей надстройки будет недоступен для других пользователей.
Используйте вкладку Generfl диалогового окна Project Properties, чтобы изменить имя проекта, ввести описание для надстройки, указать файл справки и файл контекстной справки для надстройки или задать аргументы условной компиляции.
Не забудьте удалить из своих процедур любые инструкции отладки. В противном случае при работе пользователя с надстройками любые оставшиеся в вашей программе инструкции Stop или Debug /Pr int вызовут проблемы,
Обязательно заполните поля Название и Заметки вкладки Документ диалогового окна Свойства рабочей книги до преобразования ее в надстройку. Текст поля Название используется в диалоговом окне Надстройки в качестве имени вашей надстройки, а текст поля Заметки служит ее описанием и приводится в нижней части диалогового окна Надстройки. Чтобы отобразить на экране это окно свойств, выберите в Excel команду Файл-Свойства, а затем щелкните на вкладке Документ.
Для ссылки на объекты внутри рабочей книги надстройки используйте объект ThisWorkbook.
798
Неделя 3
Управление надстройками из Visual Basic
В языке VBA предусмотрены методы и свойства, позволяющие управлять надстройками на процедурном уровне. С точки зрения языка VBA объект Addin представляет собой отдельное приложение надстройки, а объект Addins — коллекцию приложений надстройки, доступных для приложения, создаваемого для пользователя. Коллекцию Addins можно сравнить со списком надстроек, который отображается в диалоговом окне Надстройки при выборе команды Сервис-Надстройки.
Для обращения к объекту Addin используйте метод Addins.
Addins(Index)
Аргумент Index может принимать одно из следующих значений.
•	Число, представляющее надстройку, которую вы собираетесь использовать. Число 1 означает первую надстройку, которая появилась в диалоговом окне Надстройки, число 2 — вторую и т.д.
•	Имя надстройки (в виде текстовой строки), которую вы собираетесь использовать. Для тех надстроек, которые поставляются вместе с Excel, имя надстрой ки — это то имя, которое отображается в диалоговом окне Надстройки. Что касается ваших собственных надстроек, то в качестве имени может служить либо имя файла (без расширения), либо текст, который введен в поле Название вкладки Документ диалогового окна Свойства рабочей книги (которое открывается при выборе команды Файл-Свойства).
Например, в следующем выражении используется ссылка на надстройку Solver.
Addins("Solver Add-In")
Чтобы можно было работать с созданными вами же надстройками, необходимо добавить их в коллекцию Addins. Для этого используйте метод Add.
Синтаксис метода Add имеет следующий вид.
Addins.Add (FileName, [CopyFile])
Обязательный аргумент FileName представляет собой строку, содержащую полный путь файла надстройки. Необязательный аргумент CopyFile принимает значение типа Boolean, которое используется в том случае, когда файл надстройки сохраняется на дискете, компакт-диске или сетевом устройстве. Если аргумент CopyFile равен значению True, приложение Excel копирует файл надстройки на жесткий диск. Если аргумент CopyFile равен значению False, Excel оставляет файл на его прежнем месте. Если же вообще опустить аргумент CopyFile, Excel отобразит диалоговое окно, в котором будет сделан запрос о намерениях пользователя. Аргумент CopyFile игнорируется, если аргумент FileName ссылается на файл, находящийся на вашем жестком диске.
Благодаря использованию метода Add становится доступным новый объект Addin. Для настоящего использования надстройки, т.е. для того, чтобы сделать доступными для пользователя ее команды и функции, необходимо инсталлировать надстройку, установив ее свойство Installed равным значению True. Установка свойства Installed объекта Addin равным значению True эквивалентна установке (активизации) флажка соответствующей надстройки в диалоговом окне Надстройки. При этом выполняются следующие действия.
•	Реализуется первая часть “загрузочно-погрузочных” операций, т.е. становятся доступными функции, клавиши ускоренного доступа, меню и панели инструментов данной надстройки.
День 21-Й. Процедуры обработки событий и надстройки
799
• Генерируется событие WorkbookAddlnlnstall надстройки, и выполняется процедура обработки этого события, если таковая предусмотрена. Событие Addininstall аналогично событию Open, с которым вы познакомились выше в этом уроке, но отличается тем, что оно наступает только в том случае, когда рабочая книга инсталлируется как надстройка. Процедура обработки события Addininstall оказывается весьма полезным для выполнения таких операций, как инициализация надстройки и уведомление пользователя о том, что надстройка уже загружена.
Пример работы с надстройками показан в листинге 21.11.
Листинг 21.11. Работа с надстройками
1:	Sub InstallBudgetToolsf)
2:	'Устанавливает приложение-надстройку
3:	Dim aAddln As Addin
4:
5:	Set aAddln = Addins.Add(FileName:="C:\VBA21\Budget	Tools.XLA")
6:	With aAddln
7:	.Installed = True
8:	MsgBox "Надстройка " & .Title &
9:	" установлена.",
10:	vblnformation
11:	End With
12:	End Sub
13:
14:
15:	Sub RemoveBudgetTools()
16:	'Удаляет приложение-надстройку
17:	With AddIns("XL Add-In: Budget Tools")
18:	.Installed = False
19:	MsgBox "Надстройка " & .Title &
20:	" удалена.",
21:	vblnformation
22:	End With
23:	End Sub
Процедура InstallBudgetTools добавляет и инсталлирует надстройку, которая называется Budget Tools. В строке 5 используется метод Add, благодаря которому эта надстройка становится доступной для приложения Excel. В строке 5 устанавливается также объектная переменная aAddln, предназначенная для ссылки на вновь добавленный объект Addin. С помощью инструкции With (строка 6) выполняется инсталляция надстройки Budget Tools (строка 7) и отображается сообщение, уведомляющее пользователя о завершении инсталляции этой надстройки (строки 8—10).
Обратите внимание на использование свойства Title в строке 8. Многие свойства объектов Addin совпадают со свойствами объектов Workbook, например: Author, Comment, FullName, Name, Path, Subject и Title.
Если вы больше не собираетесь работать с какой-нибудь надстройкой, ее можно удалить, установив ее свойство Installed равным значению False, как показано в процедуре RemoveBudgetTools. Установка свойства Installed равным значению False также приводит к генерацию события AddlnOninstall.
800
Неделя 3
Так надо
Используйте события Addininstall и AddlnUninstall аналогично тому, как вы использовали события рабочей книги Open и BeforeClose соответственно.
Используйте события WorkbookAddlnlnstall и WorkbookAddln'Jninstall, генерируемые объектом Excel Application, для отслеживания моментов инсталляции и удаления любой надстройки.
Резюме
В приложении Excel предусмотрен ряд способов выполнения VBA-процедур, например с помощью диалогового окна Макрос, клавиш ускоренного доступа, команд меню и кнопок панелей инструментов. В каждом из перечисленных выше случаев вы или пользователь созданного вами приложения для выполнения процедуры должны что-нибудь сделать. Однако в Excel предусмотрены и автоматические способы выполнения процедур, которые и были рассмотрены в этой главе, а именно: с помощью событий объектов, а также с помощью методов и свойств, связанных с событиями.
Объекты генерируют определенные события, такие как закрытие или открытие рабочей книги. А вы имеете возможность писать процедуры, связанные с этими событиями. Написанные вами процедуры обработки событий будут выполняться каждый раз, когда происходит соответствующее событие. В этом уроке приведены примеры работы с некоторыми различными событиями. Здесь вы также узнали о том, как использовать метод ОпКеу для инсталляции процедур обработки событий, которые выполняются в ответ на нажатие определенных клавиш.
Вопросы о ответы
У меня есть рабочая книга, для которой я написал несколько процедур обработки событий. Можно ли создать новые рабочие книги, которые будут содержать те же самые процедуры обработки событий по умолчанию?
Да. Просто сохраните копию своей рабочей книги как файл шаблона Excel. И любые рабочие книги, которые вы будете создавать впоследствии из этого шаблона, будут содержать процедуры обработки событий и другие программные единицы подобно всем программам, сохраняемым в шаблоне рабочей книги.
У меня есть рабочий лист, который я создал для процедур обработки событий. Я хочу скопировать текст процедур обработки событий в другой рабочий лист, но без данных и формул исходного рабочего листа. Существует ли способ копирования текста процедур обработки событий, относящихся к рабочему листу, не копируя при этом весь рабочий лист?
Да, хотя и не совсем прямой. Можно скопировать процедуры обработки событий рабочего листа или рабочей книги в другой объект того же самого типа, используя команду редактора Visual Basic File-Export File, чтобы экспортировать модуль класса объекта. А затем можно импортировать этот модуль класса в другой проект.
Процедура обработки события DoubleClick очень удобна, но мне хотелось бы выполнять некоторую процедуру только в том случае, когда пользователь щелкает именно на изображении. Возможно ли это?
Да. Используйте свойство OnAction объекта Shape, чтобы назначить некоторую процедуру объекту фигуры. (Свойство OnAction будет хранить строку, содержашую имя процедуры, которую вы хотите выполнить.) После назначения процедуры свойству OnAction вы увидите, что после того как указатель мыши будет расположен над объек
День 21-й. Процедуры обработки событий и надстройки	801
том Shape, он примет форму руки с поднятым указательным пальцем. Для выполнения заданной процедуры вам останется лишь щелкнуть на объекте Shape.
У меня есть несколько процедур обработки событий, которые я использую каждый день — они связаны с нажатиями клавиш, OnTime-событиями и т.д. Существует ли способ автоматической инсталляции всех этих процедур обработки событий при каждом запуске приложения Excel?
Безусловно. Раскройте рабочую книгу Personal Macro Workbook (Personal.xls). В модуле класса этой рабочей книги создайте процедуру Open и включите в нее все инструкции, которые “отвечают” за процедуры обработки событий ОпКеу и OnTime. В каждую из этих инструкций не забудьте ввести строку, определяющую процедуру.
"Personal.XLS!ИмяПроцедуры"
В данном случае элемент ИмяПроцедуры означает имя процедуры обработки событий. Оно указывает Excel, где искать каждую процедуру. Скопируйте в модуль Personal.xls также и процедуры, связанные с событиями, возникающими при нажатии клавиш, или с наступлением заданного времени суток, чтобы они всегда были доступными. После сохранения изменений не забудьте снова скрыть рабочую книгу Personal.xls.
У меня создалось такое впечатление, что мои ОпКеу-процедуры ие работают, пока работает другая процедура. Может быть, я делаю что-нибудь неправильно?
Ничуть. Все дело в стандартном поведении свойства ОпКеу. Excel “перехватывает” клавишу или комбинацию клавиш только в том случае, когда не работают другие процедуры.
Коллоквиум
Ответы в приложении.
Твсш
1.	Что представляет собой процедура обработки событий?
2.	Можно ли хранить процедуру обработки событий в стандартном модуле?
3.	Что случится, если присвоить строку нулевой длины аргументу Procedure метода Excel ОпКеу? Что случится, если вообще опустить аргумент Procedure?
4.	В чем разница между аргументами EarliestTime и LastTime в методе Excel OnTime?
5.	Что подразумевают, когда говорят, что надстройка загружается по требованию?
6.	Выполняется ли процедура обработки события Change при вставке данных в рабочую книгу?
7.	Когда генерируется событие SheetActivate объекта Excel Application?
Упражнения
1. Хорошо спроектированная VBA-программа всегда “после себя убирает”, т.е. восстанавливает среду Excel после окончания работы. Создайте процедуру обработки события Open, которая сохраняет текущее состояние таких свойств объекта Application приложения Excel, как DefaultFilePath, PromptForSummarylnfo и SheetsInNewWorkbook. Затем напишите процедуру обработки события BeforeClose, которая вернет каждое свойство к своему исходному состоянию.
802
Неделя 3
2. Найдите ошибку. Приведенная ниже процедура Worksheet_DeActivate не работает должным образом. Почему?
Sub Worksheet_DeActivate()
With ActiveSheet
.UsedRange.Clear
.Cells(l, 1).Value = "Sample Data"
End With
End Sub
1.	Создайте процедуры для проверки того, какая процедура Excel запускается в работу первой: процедура Worksheet_Activate или процедура, установленная с помощью метода OnWindow.
2.	При нажатии клавиши <Delete> обычно стирается только содержимое ячейки. Напишите необходимые процедуры, которые устанавливают (а впоследствии удаляют) комбинацию клавиш < Ctrl+Delete> для удаления не только содержимого ячейки, но и всего остального: форматов и примечаний.
3.	В приложении Excel можно настроить метод OnTime таким образом, что некоторая процедура будет выполняться не в заданное время суток, а через регулярные промежутки времени — скажем, через каждые 60 минут. Например, если у вас есть процедура, которая создает резервную копию активной рабочей книги на севере сети, то эта процедура могла бы выполняться каждые 5 или 10 минут, чтобы вы не беспокоились насчет резервирования вашей работы вручную. Напишите процедуру, которая настраивает процедуру обработки события OnTime на запуск через регулярные интервалы, задаваемые пользователем.
Итоги З-й недели: модуль надстройки и специализированный класс книги
Наши поздравления! Завершилась третья и последняя неделя изучения основ программирования на языке VBA. На протяжении третьей недели были рассмотрены такие сложные темы, как построение диалоговых окон, обработка ошибок, создание специальных меню и панелей инструментов, а также управление другими приложениями Windows из своих VBA-программ. Последняя неделя нашего курса завершилась демонстрацией различных способов использования событий, генерируемых объектами в приложении Excel, использования свойств и методов, связанных с событиями, и создания надстроек. В листингах этой обзорной главы объединено несколько уже изученных вами концепций, а именно создание специальных строк меню и построение собственных диалоговых окон.
В приведенных ниже листингах представлены три полных VBA-модуля и модуль формы диалогового окна. Для того чтобы программы, содержащиеся в этих модулях, успешно работали, в той же самой рабочей книге должен находиться модуль из листинга R2.3 второй недели (все четыре модуля соответствуют друг другу).
Модуль, представленный в листинге R3.1, представляет собой коллекцию полезных процедур. По мере изучения этого листинга обратите внимание на то, что некоторые из этих процедур используют подпрограммы из библиотечного модуля, приведенного в листинге R2.3 (см. обзорную главу второй недели).
День 21-й. Процедуры обработки событий и надстройки
803
Листииг R3.1. Модджь Utilities - ВиВлиотвка мрицеддр-дтомт
1:	Option Explicit
2:
3:	'Программа MessageBeep из Windows DLL
4:	Declare Sub MessageBeep Lib "USER32" (ByVai BeepType As Long)
5:
6:	'Константы для MessageBeep
7:	Const	mBeepDefault = 0
8:	Const	mBeepCritical = 16
9:	Const	mBeepQuestion = 32
10:	Const	mBeepExclamation = 48
11:	Const	mBeepAsterisk = 64
12:
13:	Sub CopyFiles))
14:	'копирует выделенные пользователем файлы в новое место,
15:	'указанное пользователем.
16:
17:	Const msgTitle = "Копирование файла"
18:
19:	Dim sName As string 'имя источника
20:	Dim dName As String 'имя назначения
21:	Dim orgDrive As String 'исходный диск, папка
22:
23:	On Error GoTo BadNews 'обработчик ошибок
24:
25:	orgDrive =	CurDir()
26:
27:	With Application
28:	.StatusBar	= "Выделите	нужный	файл."
29:	sName = .GetOpenFilename)Title:=msgTitle & ” - Source")
30:	If sName =	"False" Then	GoTo	Canceljob
31:	.StatusBar = "Укажите место для копирования."
32:	dName = .GetSaveAsFilename)
33:	Title:=msgTitle & " - Destination",
34:	InitialFilename:=FullName2BookName( sName))
35:	If dName	=	"False" Then	GoTo Canceljob
36:	.StatusBar	= "Copying "	& sName & ” to	"	& dName
37:	FileCopy	Source:=sName,	Destination:=dName
38:	End With
39:
40:	CancelJob:	'конец работы
41:	ChDrive orgDrive 'возврат на исходный диск
42:	ChDir orgDrive 'возврат в исходную папку
43:	Application. StatusBar = False
44:	Exit Sub
45:
46:	BadNews:	'при ошибке выполнения
47:	MessageBeep mBeepCritical
48:	MsgBox Prompt:="He удается скопировать файл." &
49:	vbCr & Err.Description,
50:	Title:=msgTitle,
51:	Buttons:=vbCritical
52:	ChDrive orgDrive
804
Неделя 3
53:	ChDir orgDrive
54:	Application.StatusBar = False
55:	End Sub
56:
57:
58:	Sub Convert2Template()
59:	'Сохраняет текущую книгу в виде шаблона.
60:
61:	Const FilterList = "Templates (*.xlt),*.xlt"
62:	Const msgTitle = "Преобразование книги в шаблон”
63:
64:	Dim dName As String 'файл назначения
65:	Dim iName As String
66:	Dim orgDrive As String
67:
68:	On Error GoTo BadNews '
69:
70:
71:	With ActiveWorkbook
72:	If Not .Saved Then
73:	MessageBeep mBeepAsterisk
74:	MsgBox Prompt:=.Name & " не сохранен." &
75:	vbCr & "Сохраните " &
76:	"перед преобразованием.",
77:	Buttons:=vblnformation,
78:	Title:=msgTitle
79:	Exit Sub
80:	End If
81:	End With
32:
83:	orgDrive = CurDir() 'preserve original drive/folder
84:
85:	'set up default destination filename
86:	dName = Left(ActiveWorkbook.Name,
87:	Len(ActiveWorkbook.Name) - 4)
88:
89:	With Application
90:	'change drive/directory to Excel's templates path
91:	ChDrive .TemplatesPath
92:	ChDir .TemplatesPath
93:	.StatusBar = "Выбор имени для шаблона."
94:
95:	'имя файла от пользователя
96:	iName = .GetSaveAsFilename(Title:=msgTitle,
97:	InitialFilename:=dName,
98:	FileFilter:=FilterList)
99:	End With '
100:
101:	If iName <> "False" Then
102:	Application.StatusBar = "Создание	шаблона..."
103:	ActiveWorkbook.SaveAs fileName:=iName,
104:	FileFormat:=xlTemplate
105:	MessageBeep mBeepAsterisk
День 21-й. Процедуры обработки событий и надстройки
805
106:	MsgBox Prompt:="Шаблон создан успешно.",
107:	Buttons:=vblnformation,
108:	Title:=msgTitle
109:	End If
110:
111:
112:	ChDrive orgDrive
113:	ChDir orgDrive
114:
115:	Application.StatusBar = False
116:	Exit Sub 'no more	work to	do
117:
U8:BadNews:	'при ошибке выполнения
119:	MessageBeep mBeepCritical
120:	MsgBox Prompt:="He удается преобразовать книгу в шаблон" &
121:	vbCr & Err.Description,	_
122:	Title:=msgTitle,
123:	Buttons:=vbCritical
124:	Application.StatusBar = False
125:	ChDrive orgDrive
126:	ChDir orgDrive
127:	End Sub
128:
129:
130:	Sub Backup_ActiveBook()
131:	'Создает резервную копию с новым	именем
132:	'с помощью метода SaveCopyAs.
133:
134:	Dim fName As String
135:	Dim OldComment As String
136:
137:	With ActiveWorkbook
138:	'preserve original file comments
139:	OldComment = .Comments
140:
141:	'делаем заметки
142:	.Comments = "Резервная копия "	& .Name &
143:	", созданная процедурой." & OldComment
144:
145:
146:	fName = Left;.Name, InStr(.Name, ”.")) & " (backup) .xis"
147:
148:	.SaveCopyAs	fileName:=fName
149:	.Comments	=	OldComment
150:	End With
151:	End Sub
Листинг R3.1 — это законченный модуль. В строке 4 содержится объявление внешней процедуры DLL MessageBeep (День 20-й), а в строках 7—11 объявляются глобальные константы, используемые для выбора тона звучания, обеспечиваемого процедурой MessageBeep.
806
Неделя 3
В строках 13—38 содержится процедура CopyFiles. Она представляет собой версию процедуры, которая была представлена в уроке 13 (День 13-й). Обратите внимание на инструкцию On Error GoTo (День 18-й), вызываемую в строке 23. Если при выполнении этой процедуры возникают какие-либо ошибки (например, переполнение диска), то благодаря инструкциям обработки ошибок времени выполнения (динамических ошибок) (см. строки 46—54), на экране отображается сообщение об ошибке и выполняются некоторые “очистные работы”.
Строки 58—127 содержат процедуру Convert2Template, которая предназначена для преобразования активной рабочей книги в файл шаблона Excel. В этой процедуре также используется инструкция On Error GoTo (строка 68), предусмотренная для обеспечения процесса обработки динамических ошибок.
В строках 130—151 содержится процедура Backup_ActiveBook, которая создает резервную копию текущей рабочей книги и присваивает ей новое имя, в котором для обозначения резервного файла присутствует строка “(backup)”. Эта процедура аналогична процедуре, представленной в уроке 7, но является более усложненной версией.
В следующем листинге показан еще один законченный модуль, на этот раз модуль класса рабочей книги. Этот модуль класса содержит процедуры обработки событий Open и BeforeClose, которые настраивают меню Excel при каждом открытии рабочей книги, содержащей этот модуль. Этот модуль класса также содержит процедуру, используемую для поддержки действий, выполняемых процедурами обработки событий, генерируемых объектом рабочей книги.
^РШЛЯИЕ
Чтобы ввести текст программы в модуль класса рабочей книги, в окне редактора Visual Basic выберите команды Insert-Class Module. Visual Basic отобразит модуль класса в окне Project, после чего вы сможете ввести текст процедур и другие инструкции.
Аисшииг Я3.2. Модуль класса, содержащий процедуры обработки событий и поддерживающие ох процедуры
1:	Option Explicit
2:
3:	Private Sub Create_UtilityMenu(cmdBar As CommandBar)
4:	'Добавляет меню к панели инструментов
5:
6:	Dim ctrlMenu As CommandBarPopup
7:	Dim ctrlTemp As CommandBarButton
8:
9:	With cmdBar.Controls
10:	Set ctrlMenu = .Add(Type:=msoControlPopup,
11:	before:=.Count,
12:	temporary:=True)
13:	ctrlMenu.Caption	=	"SUtilities"
14:	End With
15:
16:	With ctrlMenu
17:	'Добавляет пункт Copy File
18:	Set ctrlTemp = .Controls.Add(Type:=msoControlButton)
19:	ctrlTemp.Caption = "&Copy File"
20:	ctrlTemp.OnAction = "CopyFiles"
21:	ctrlTemp.DescriptionText = "Copy any file"
22:
23:	'Добавляет Backup
24:	Set ctrlTemp = .Controls.Add(Type:=msoControlButton)
День 21-й. Процедуры обработки событий и надстройки
807
25:	ctrITemp.Caption = "SBackup Active Book"
26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: А £ .	ctrlTemp.OnAction = "Backup_ActiveBook" ctrlTemp.DescriptionText = "Back up the active workbook" ctrITemp.BeginGroup = True 'Добавляет Convert to Template Set ctrlTemp = .Controls.Add(Type:=msoControlButton) ctrlTemp.Caption = "Convert To STemplate" ctrITemp.OnAction = "Convert2Template" ctrlTemp.DescriptionText = "Convert active workbook " & "to an Excel template." ctrlTemp.BeginGroup = True End With End Sub Sub Workbook_Open() 'Создает меню Utilities Dim aMenu As CommandBar Dim ctrlMenu As CommandBarButton
47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:	For Each aMenu In Application.CommandBars If aMenu.Builtln Then 'встроенные не трогаем If aMenu.Type = msoBarTypeMenuBar Then Create_UtilityMenu cmdBar:=aMenu With aMenu.Controls("File").Controls Set ctrlMenu = .Add(Type:=msoControlButton, before:=2) ctrlMenu.Caption = "C&reate Named Workbook..." ctrlMenu .OnAction = "OpenNewBook" ctrlMenu.DescriptionText = "Создание новой книги с " & "заданным именем" End With End If End If Next aMenu End Sub
64: 65: 66:	Private Sub Workbook_BeforeClose(Cancel As Boolean)
67:	Dim aMenu As CommandBar
69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79:	For Each aMenu In Application.CommandBars With aMenu If .Builtln And (.Type = msoBarTypeMenuBar) Then aMenu.Controls("Utilities").Delete With aMenu.Controls("File") 'clean up File menu .Controls("Create Named Workbook...").Delete End With End If End With Next aMenu End Sub
808
Неделя 3
Прежде всего, обратите внимание на то, что модуль класса содержит три процедуры:	Create UtilityMenu (строки 3—38), Workbook_Open (строки 41—62) и
Workbook_BeforeClose (строки 65-79). И только две из этих трех процедур являются процедурами обработки событий для объекта рабочей книги. Процедура Create_UtilityMenu — это поддерживающая процедура, которая используется процедурой обработки события Workbook_Open и располагается в разделе General модуля класса. Остальные процедуры находятся в разделе Workbook модуля класса.
В листинге R3.2 также обратите внимание на то, что все процедуры объявляются с использованием ключевого слова Private. Редактор Visual Basic автоматически добавляет ключевое слово Private при вставке за вас объявления процедуры обработки события. Процедуры в модуле класса объекта объявляются закрытыми, чтобы к ним не было доступа снаружи модуля класса и они не могли отображаться в диалоговом окне Макрос. Процедура Create_UtilityMenu также объявлена с применением ключевого слова Private, поскольку она предназначена для использования только процедурой обработки события Workbook_Open. Объявление Private ограничивает доступность процедуры Create_UtilityMenu к модулю класса объекта Workbook.
Процедура обработки события Workbook_Open занимает строки 41—62. Эта процедура, по сути, состоит из одного цикла For Each (строка 47), в котором организуется добавление меню Utilitis в каждую встроенную строку меню. При этом нужно подчеркнуть, что изменениям подвергаются только встроенные строки меню типа msoBarTypeMenuBar, чтобы избежать нежелательного “проникновения” в другие созданные специально строки меню, которые были инсталлированы другими VBA-программами или надстройками.
Для добавления нового меню Utilitis процедура Workbook_Open вызывает процедуру Create_UtilityMenu (строка 50). В строках 51-58 процедуры Workbook_Cper. добавляется один элемент меню в меню Excel Файл, создавая тем самым новую команду File-Create Named Workbook (Файл-Создать именованную рабочую книгу). Обратите внимание на то, что эта новая команда выполняет процедуру OpenNewBook в листинге R3.3 (аргумент OnAction в строке 55). В строке 56 устанавливается свойство DescriptionText нового элемента меню, чтобы предоставить для пользователя подсказку, раскрывающую назначение этой команды.
В строках 65—79 содержится процедура обработки события Workbook_BeforeClose, которая перед закрытием рабочей книги удаляет все “невстроенные” меню и команды. В данном случае процедура Workbook_BeforeClose удаляет добавленные процедурой Create_UtilityMenu меню Utilitis и команду File-Create Named Workbook из каждой встроенной строки меню Excel.
Снова вернемся к разделу General модуля класса. Процедура Create_UtilityMenu, которая добавляет меню Utilitis в строку меню, заданную аргументом cmdBar, занимает строки 3—38. Обратите внимание на то, что эта процедура объявлена с помощью ключевого слова Private, чтобы она не была доступна вне этого конкретного модуля.
В строках 9—14 процедуры Create_UtilityMenu содержатся инструкции, которые добавляют сам элемент меню Utilitis. В строках 18—21 добавляется команда Utilitis-Copy File (Утилиты-Копировать файл), при выборе которой должна выполняться процедура CopyFiles, и устанавливается свойство DescriptionText этого нового элемента меню. В строках 24—28 добавляется команда Utilitis-Backup Active Book (Утилиты-Резервирование активной книги), при выборе которой должна выполняться процедура Backup_ActiveBook, и также устанавливается свойство DescriptionText этого нового элемента меню. Обратите внимание на то, что в строке 28 свойство BeginGroup устанавливается равным значению True, чтобы эта команда в меню отделялась от предыдущей команды линией. Аналогично в строках 31—35 добавляется команда Utilitis-Convert То
День 21-й. Процедуры обработки событий и надстройки
809
Template (Утилиты-Преобразовать в шаблон), при выборе которой должна выполняться процедура Convert2Template. Команда меню, добавленная последней, также отделяется линией от предыдущей команды меню.
Листинг R3.3 также содержит законченный модуль. Эта программа предназначена для выполнения ряда задач, необходимых для создания новой поименованной рабочей книги с заданным числом рабочих листов, поименованных по желанию и отсортированных во время создания рабочей книги. В листинге R3.3 содержится процедура OpenNewBook, которая вызывается при выборе команды File, Create Named Workbook. Остальные процедуры из листинга R3.3 используются процедурой OpenNewBook для выполнения поставленных перед ней задач.
Листинг R3.3. Процедура OpeiiNewBook со своей “свмтоО”
1:	Option Private Module
2:	Option Explicit
3:
4:	Public	Const	STREQUAL As Integer = 0	'StrComp:	равенство
5:	Public	Const	STRLESS As Integer = -1	'StrComp:	меньше чем
6:	Public	Const	STRGREAT As Integer = 1	'StrComp:	больше чем
7:
8:
9:	Sub OpenNewBook()
10:	'Создает новую книгу, проверяя при этом,
11:	'что бы такой книги не было в текущей	папке.
12:	'Дополнительно позволяет пользователю	указать
13:	'количество листов в книге
14:	'и дать им всем имена.
15:
16:	Const nfTitle = "Создание книги"
17:
18:	Dim fName As String 'полное имя файла
19:	Dim BName As String 'короткое имя файла
20:	Dim OKName As Boolean
21:	Dim Ans As Integer 'ответ функции MsgBox
22:	Dim mPrompt As String 'приглашение функции MsgBox
23:	Dim nBook As Workbook 'объект новой книги
24:
25:	'Сообщение в строку состояния
26:	Application.StatusBar = "Создаем новую книгу..."
27:
28:	BName	=	"Новая	книга"
29:	OKName = False
30:	Do
31:	fName	=	GetBookName (DfIt :=BName, Title:=nfTitle &
32:	" - Select Save As Name for New Workbook")
33:	If fName = "False" Then EndNewBook
34:
35:	BName = FullName2BookName(fName)
36:	If IsBookOpen(BName) Then
37:	mPrompt = BName & " is already open. " &
38:	"Хотите ли вы перейти на нее?"
39:	Ans = MsgBox)Prompt:=mPrompt, Title:=nfTitle,
40:	Buttons:=vbQuestion + vbYesNo)
810
Неделя 3
41:	If Ans = vbYes Then
42:	Workbooks(BName).Activate
43:	EndNewBook
44:	End If
45:	Elself IsDiskFile)fName)	Then
46:	mPrompt = fName & vbCr &
47:	" уже существует. Хотите	ее	открыть?"
48:	Ans = MsgBox(Prompt:=mPrompt, Title:=nfTitle,
49:	Buttons:=vbQuestion + vbYesNo)
50:	If Ans = vbYes	Then
51:	Workbooks.Open	fName
52:	EndNewBook
53:	End If
54:	Else
55:	OKName	=	True
56:	End If
57:	Loop Until	OKName
58:
59:	'load the form
60:	Load frmOpenNewBook
61:
62:	'set forin caption
63:	frmOpenNewBook.Caption = "Set Options for " & BName
64:
65:	'show the form
66:	frmOpenNewBook.Show
67:	If frmOpenNewBook.Canceled Then EndNewBook
68:
69:	'create new workbook with one worksheet
70:	Set nBook = Workbooks.Add(xlWorksheet)
71:
72:	'set summary information
73:	With frmOpenNewBook 'fill	in summary information
74:	nBook.Title = .txtTitle
75:	nBook.Subject = .txtSubject
76:	nBook.Author = .txtAuthor
77:	End With
78:
79:	If frmOpenNewBook.spnNumSheets.Value > 1 Then
80:	'adjust number of worksheets
81:	NewBook_Worksheets nBook, nfTitle
82:	End If
83:
84:	'Finally, save the new workbook file
85:	nBook.SaveAs fileName:=fName,
86:	FileFormat:=xlNormal,
87:	ReadOnlyRecommended:=False,
88:	CreateBackup:=False
89:	nBook.Activate
90:	nBook.Sheets)1).Activate
91:	EndNewBook 'do housekeeping
92:	End Sub
93:
День 21-й. Процедуры обработки событий и надстройки
811
94:	Sub EndNewBook()
95:	'ends OpenNewBook program, after cleaning up
96:	Application.StatusBar = False
97:	End
98:	End Sub
99:
100:	Sub NewBook_Worksheets(ByRef Book	As	Workbook,
101:	ByVai Title As String)
102:	'Get and set number of worksheets	for	the	new workbook
103:
104:	Dim shtNames() As String 'array for workbook names
105:	Dim numsheets As Integer 'number of worksheets from user
106:	Dim Ans As Integer 'MsgBox answers
107:	Dim к As Integer 'loop counter
108:	Dim Sht As Worksheet
109:	Dim Sht2 As Worksheet
110:
111:	Title = Title & " - Select Worksheets"
112:
113:	numsheets = frmOpenNewBook.spnNumSheets.Value
114:
115:	Ans = MsgBox(Title:=Title,
116:	Buttons:=vbQuestion + vbYesNo,
117:	Prompt:="Change default worksheet names?")
118:	If Ans = vbNo Then 'just add sheets
119:	For к = (Book,Worksheets.Count + 1) To numsheets
120:	Set Sht = Book.Worksheets(Book.Worksheets.Count)
121:	Set Sht2 = Book.Worksheets.Add
122:	Sht2.Move after:=Sht
123:	Next к
124:	Exit Sub	'no	more	work to do
125:	End If
126:
127:	ReDim shtNames(1 To numSheets) 'size the array of names
128:	For к = 1 To numSheets
129:	shtNames(k) = "Sheet" & к 'make default names
130:	Next к
131:
132:	'get names of worksheets	from user
133:	Get_NameList Book:=Book,	List:=shtNames,
134:	Prompt:="Enter a worksheet name:",
135:	Title:=Title
136:	Ans = MsgBox(Title:=Title,
137:	Buttons:=vbQuestion + vbYesNo,
138:	Prompt:="Sort worksheet names alphabetically?")
139:	If Ans = vbYes Then BubbleSortStrings	shtNames
140:
141:	'create the worksheets with	names
142:	With Book
143:	'start by just renaming the	first sheet
144:	.Worksheets(l).Name = shtNames(l)
145:	For к = 2 To numSheets
146:	Set Sht = .Worksheets(.Worksheets.Count)
812
Неделя 3
147:	Set Sht2 = .Worksheets.Add
148:	Sht2.Move	after:=Sht
149:	Sht2.Name	=	shtNames(k)
150:	Next к
151:	End With
152:	End Sub
153:
154:	Sub Get_NameList(Book As Workbook,
155:	List() As String,
156:	Prompt As String,
157:	Title As String)
158:	'gets a list of worksheet names as strings
159:	Dim к As Long, tStr As String, GoodName As Boolean
160:
161:	For к = LBound(List) To UBound(List)
162:	Do 'ask for a unique name
163:	tStr = InputBox)Prompt:=Prompt,
164:	Title:=Title,
165:	Default:=List(k))
166:	If Trim(tStr) = ""	Then
167:	Exit Sub 'user canceled, no more work
168:	Else
169:	If StrComp(tStr, List(k), vbTextCompare) = STREQUAL Then
170:	GoodName = True
171:	Else
172:	If InListSoFar(List, tStr, к	- 1) Or _
173:	SheetExists(Book, tStr) Then
174:	MsgBox Prompt:="Use a different name. " &
175:	“This name already exists; "	&
176:	"sheet names must be unique.",
177:	Title:="Sheet Name Error", _
178:	Buttons:=vbExclamation
179:	GoodName = False
180:	Else
181:	GoodName = True
182:	List(k) = tStr
183:	End If
184:	End If
185:	End If
186:	Loop Until GoodName
187:	Next к
188:	End Sub
189:
190:	Sub BubbleSortStrings(Arr() As String)
191:	'sorts any string array in ascending order
192:	Dim I As Integer, J As Integer
193:	For I = LBound(Arr) To UBound(Arr) - 1
194:	For J = 1 + 1 To UBound(Arr)
195:	If StrComp(Arr(I), Arr(J), vbBinaryCompare) = STRGREAT Then
196:	Swap	Arr(I), Arr(J)
197:	End If
198:	Next	J
199:	Next	I
День 21-й. Процедуры обработки событий и надстройки
813
200:	End Sub
201:
202:	Sub	Swap(Tl As	Variant,	T2	As Variant)
203:	Dim	tmp As Variant
204:	tmp	= Tl: T1 =	T2:	T2	=	tmp
205:	End	Sub
Обратите внимание на то, что сам этот модуль является закрытым (см. строку 1). В строках 4—6 объявляются константы, используемые при проверке результатов теста StrCompare. Строки 9—92 занимает процедура OpenNewBook.
Процедура OpenNewBook аналогична процедуре с таким же именем, которая представлена в обзорной главе по материалам второй недели (Неделя 2-я), но сейчас в ней используется специальное диалоговое окно, предназначенное для получения информации от пользователя, благодаря чему облегчается использование этой информации. Строка 26 обеспечивает отображение сообщения в строке состояния приложения Excel, чтобы держать пользователя в курсе всего происходящего. В строке 28 устанавливается стандартное имя для новой рабочей книги, а в строке 29 — управляющее значение переменной цикла Do, который начинается в строке 30 (а заканчивается в строке 57). В этом цикле для получения имени рабочей книги вызывается функция GetBookName (из листинга R2.3 обзорной главы по второй неделе курса).
Если пользователь не отменит взаимодействие с диалоговым окном, в котором запрашивается имя файла, в строках 36—56 будет проверена целесообразность создания новой рабочей книги с выбранным именем. Новая рабочая книга будет создана только в том случае, если ее имя не совпадает с именем уже открытой рабочей книги и не существует уже на диске в той же самой папке.
Если имя, выбранное пользователем, проходит все проверки, то в строке 60 обеспечивается загрузка в память формы пользователя frmOpenNewBook без ее отображения. Затем в строке 63 устанавливается свойство этой формы Caption, от которого зависит содержимое строки заголовка этой формы. В строке 66 диалоговое окно frmOpenNewBook становится видимым. (Текст модуля формы frmOpenNewBook содержится в листинге R3.4.) На рис. R3.1 представлена форма frmOpenNewBook в режиме выполнения с указанием имен управляющих элементов, расположенных на этой форме.
Создать
Отмена
txtTitle txtSubject txtAuthor
btnCreate
btn Cancel
spnNumSheets
IblNumSheets
Puc. R3.1. Управляющие элементы формы пользователя frm OpenNewBook
814
Неделя 3
Когда пользователь щелкнет либо на кнопке Create (Создать), либо на кнопке Cancel (Отмена), форма исчезает с экрана. В строке 67 процедура OpenNewBook проверяет, не был ли выбран вариант отмены. Если да, то процедура OpenNewBook завершается.
В строке 70 выполняется создание новой рабочей книги. В строках 73—77 обеспечивается передача в рабочую книгу общей информации о ней из управляющих элементов. Если пользователь решил создать рабочую книгу с несколькими рабочими листами, то в строке 81 будет вызвана процедура NewBook_Worksheets, предназначенная для добавления листов в рабочую книгу. После сохранения книги (в строке 85) новая рабочая книга активизируется с активным первым рабочим листом, после чего вызывается процедура EndNewBook для проведения заключительных операций.
Процедуры EndNewBook, Get_NameList, BubbleSortStrings и Swap работают аналогично одноименным процедурам, описанным в обзорной главе из второй недели (см. листинг R2.2). Процедура NewBook_Worksheets также работает подобно тому, как описано в предыдущей обзорной главе, но в данном варианте вместо использования инструкции InputBox она получает значение числа рабочих листов, добавляемых в рабочую книгу из управляющего элемента spnNumSheets формы frmOpenNewBook. Благодаря использованию в этой версии процедуры OpenNewBook формы пользователя стало возможным избавиться от пяти различных диалоговых окон ввода данных, которые присутствовали в предыдущем варианте программы.
Текст модуля формы frmOpenNewBook представлен в листинге R3.4.
Листинг R3.4. Моддль класса для фармы TrmOpeaNewBoak
1:	Option Explicit
2:
3:	Private fmCanceled As Boolean
4:
5:	Property Get Canceled))	As	Boolean
6:	Canceled = fmCanceled
7:	End Property
8:
9:	Private Sub btnCancel_Click()
10:	Me.Hide
11:	fmCanceled = True
12:	End Sub
13:
14:	Private Sub btnCreate_Click()
15:	'проверка данных и скрытие формы
16:	With Me
17:	If (Trim).txtAuthor) = "") Or _
18:	(Trim).txtSubject) = "") Or
19:	(Trim).txtlitie) = "") Then
20:	MsgBox Prompt:="BH должны ввести Заглавие, Тему," &
21:	" и Автора.",
22:	Buttons:=vbExclamation, _
23:	Title:=.Caption
24:	Else
25:	.Hide
26:	fmCanceled = False
27:	End If
28:	End With
29:	End Sub
День 21-й. Процедуры обработки событий и надстройки
815
30:
31:	Private Sub spnNumSheets_Change()
32:	'согласовываем надпись счетчика
33:	With Me
34:	.IblNumSheets.Caption = .spnNumSheets.Value
35:	End With
36:	End Sub
37:
38:	Private Sub UserForm_Activate()
39:	'reset the canceled flag
40:	fmCanceled = False
41:	End Sub
42:
43:	Private Sub UserForm_Initialize()
44:	'согласовываем счетчик и его надпись
45:	With Me
46:	.spnNumSheets.Value = 1
47:	.IblNumSheets.Caption = .spnNumSheets.Value
48:	fmCanceled = False
49:	End With
50:	End Sub
Для того чтобы программа работала, вы должны создать форму и вставить текст листинга R3.4 в ее модуль. Создание форм диалоговых окон описано на 16-м уроке.
В строке 3 объявляется переменная fmCanceled, которая используется для получения информации о том, было ли отменено диалоговое окно. Строки 5—7 занимает процедура Property Get Canceled, которая позволяет другим процедурам “узнавать” состояние переменной fmCanceled.
В строках 9—12 содержится процедура обработки события btnCancel_Click. Эта процедура выполняется каждый раз, когда пользователь щелкает на кнопке btnCancel. Функции процедуры btnCancel_Click очень просты: она скрывает форму (строка 10) и устанавливает переменную fmCanceled равной значению True, чтобы свойство формы Canceled (строки 5—7) корректно указывало на то, что диалоговое окно было отменено.
(miioBiE
Не забудьте установить свойство Cancel кнопки btnCancel равным значению True, чтобы нажатие клавиши <ESC> имело тот же эффект, что и щелчок на кнопке Cancel.
В строках 14—29 содержится процедура обработки события btnCreate_Click. Эта процедура выполняется каждый раз, когда щелкают на кнопке Create (Создать). Здесь прежде всего проверяется, что пользователем были введены такие данные, как тема, название и автор (строки 17—19) и отображается сообщение об ошибке, если хотя бы одно из данных отсутствует (строки 20—23). Но если пользователь предоставил всю необходимую информацию, в строке 25 обеспечивается сокрытие формы, а строка 26 “беспокоится” о том, чтобы свойство Canceled корректно отражало факт “не отмены” диалогового окна.
816
Неделя 3
Приложение
Ответы
День 1-1
Тест
1.	Простой макрос не отличается гибкостью. Он не может эффективно выполнять повторные действия или принимать решения в зависимости от условий, которые возникают при выполнении этого макроса.
2.	Ядра языков Visual Basic for Applications (VBA) и Visual Basic идентичны. Ho VBA имеет три серьезных отличия от Visual Basic.
•	Исходный текст программ VBA хранится в файлах приложений (например, в рабочей книге Excel), а не в отдельных текстовых файлах.
•	Программы, написанные на языке VBA, должны запускаться из приложения, в котором эти программы были написаны. Например, VBA-программа, написанная в приложении Excel, должна всегда выполняться из приложения Excel. (Однако у вас может быть другое приложение, которое, используя технологию автоматизации (Automation), способно “заставить" Excel выполнить конкретную VBA-макропрограмму.)
•	В языке VBA, помимо ядра Visual Basic, предусмотрено много расширений. Специфика этих расширений зависит от конечного приложения, в котором используется язык VBA. Например, в среде Excel 2000 в язык VBA добавлены средства, которые связаны с обслуживанием рабочих книг и рабочих листов, а также с вычислениями финансового и статистического характера.
3.	Три основных преимущества, достигаемые за счет добавления элементов VBA-программ в записываемый макрос.
•	Способность эффективно повторять действия.
•	Возможность создания таких макросов, которые способны выполнять различные инструкции в зависимости от условий, возникающих во время работы макроса, или в зависимости от выбора, который делает пользователь, запустивший этот макрос.
•	Возможность объединения нескольких мелких макросов в одну большую программу для выполнения более сложных задач.
4.	Нет, при первой записи макроса Excel его нельзя назначить кнопке панели инструментов, но можно назначить его клавише ускоренного доступа. Для назначения макроса Excel кнопке панели инструментов необходимо использовать команду Сервис-Параметры уже после завершения записи этого макроса.
5.	Excel сохраняет записываемые вами макросы в файле рабочей книги. Во время записи макроса можно выбрать место его сохранения: в текущей рабочей книге, в новой рабочей книге или в рабочей книге Personal.xls.
6.	Для выполнения макроса в приложении Excel выберите команду Сервис-Макрос-Макросы.
Приложение
817
7.	Чтобы запустить процесс записи макроса, выберите команду Сервис-Макрос-Начать запись.
8.	Чтобы сделать макрос Excel доступным для любой открытой рабочей книги во время любого сеанса работы, сохраните его в рабочей книге Personal.xls.
9.	В диалоговом окне Макрос приводится список всех макросов, записанных в рабочих книгах, открытых в данный момент независимо от того, скрыты эти книги или нет.
Упражнения
1.	Если вы записали макрос Excel в соответствии с инструкциями, приведенными в данном упражнении, то получите макрос, который создает новую рабочую книгу, а затем сохраняет и закрывает файл рабочей книги.
2.	При выполнении записанного вами макроса Excel создается и сохраняется еще одна рабочая книга, причем на том же диске и в папке с именем NewWorkbook.
3.	Поскольку макрос NewBook пытается сохранить на диске новый файл рабочей книги с именем, которое уже существует на этом диске, Excel отобразит диалоговое окно, в котором пользователю будет предложено заменить существующий файл. В этом диалоговом окне предусмотрено четыре кнопки: Да, Нет, Отмена и Справка. Если выбрать кнопку Да, Excel заменит существующий файл рабочей книги (а его прежнее содержимое будет безусловно потеряно), при этом записанный макрос нормально завершит свою работу.
Если выбрать кнопку Нет (или кнопку Отмена), операции по сохранению файла будут отменены. Но поскольку отмененная операция приводит к невыполнению инструкции в записанном макросе, макрос прерывается и его выполнение не продолжается.
Это упражнение иллюстрирует следующее. Если некоторая инструкция в записанном макросе нормально не выполняется, работа этот макроса не продолжается. Как вы узнаете в последующих уроках, добавление элементов VBA-программы помогает более тонко управлять поведением макроса. Например, было бы гораздо лучше, если бы вместо “выпадения в осадок” с выдачей сообщения об ошибке, макрос NewBook предложил бы пользователю ввести другое имя и снова попытался выполнить операцию сохранения.
День 2-й
Тест
1.	В модуле хранится исходный текст VBA-программы. Любой модуль может содержать ни одной, одну или несколько процедур. При этом максимальное число строк составляет приблизительно 4 000. Модули хранятся в файлах, в которых данное приложение обычно и хранит информацию — в рабочих книгах Excel, документах или шаблонах Word и т.д.
2.	Комментарии добавляются к записанному макросу, чтобы зафиксировать его назначение и описать специальные условия, которые должны быть соблюдены для его успешного выполнения. Лучше всего добавлять комментарии в макрос сразу же после его записи, пока вы еще хорошо помните, какие его части являются результатом специальных действий, выполненных вами, и это также облегчит возможные изменения этого макроса в будущем. Комментарии также следует добавлять и при каждом внесении изменений в макрос. Это поможет
818
Приложение
определить, какие части макроса вы отредактировали и на что повлияли эти изменения.
По тем же причинам следует также добавлять комментарии и в процедуры, написанные вами самими.
3.	Ключевое слово VBA — это любое слово или символ, который является частью языка программирования VBA, в отличие от слов (таких как имена макросов и процедур), созданных вами самими.
4.	Чтобы написать синтаксически корректную процедуру, необходимо определить макрос с помощью ключевого слова Sub, за которым следует имя процедуры и пара круглых скобок, расположенных на той же самой строке. Оканчивается этот макрос строкой, содержащей ключевые слова End Sub (редактор Visual Basic автоматически вставит эту инструкцию). Между строкой объявления процедуры и строкой ее завершения может находиться одна (ни одной) или несколько инструкций. Необходимые элементы процедуры показаны в следующем примере:
Sub AnyMacro()
' Ни одной, одна или любое количество инструкций программы
End Sub
5.	Объявление процедуры — это первая строка процедуры (содержащая ключевое слово Sub), которая указывает на имя процедуры.
6.	Тело процедуры — это часть процедуры, которая содержит команды и программные инструкции, выполняемые процедурой. Тело процедуры можно определить как часть процедуры между строкой, содержащей объявление процедуры, и строкой End Sub, которая завершает процедуру.
7.	С помощью окна Object Browser можно просмотреть список всех доступных в данный момент макросов и процедур. В окне Object Browser перечисляются все модули и процедуры в проекте, который выбран в данный момент в окне Project Explorer.
8.	При записи исходного текста VBA-программы используются соответствующие отступы, которые помогают сделать программу более читабельной. По тем же причинам следует делать отступы и при записи процедур: для улучшения читабельности и понимания.
9.	Процедура MsgBox предоставляет процедурам простой способ отображать сообщения для пользователей.
10.	Аргумент — это информация, которая передается VBA-процедуре для дальнейшего использования. Некоторые процедуры могут использовать несколько аргументов. В этом случае аргументы записываются в виде списка и отделяются друг от друга запятыми, образуя, таким образом, список аргументов.
11.	Символ продолжения строки состоит из символа пробела, за которым следует символ подчеркивания (_). Символ продолжения строки используется для индикации того, что для формирования единой VBA-инструкции следующая строка должна быть объединена с текущей. Используя символ продолжения строки, можно разделить VBA-инструкции на несколько строк в целях улучшения читабельности исходного текста процедуры. В следующем фрагменте программы показаны пять строк, содержащих символы продолжения строки. Результат использования этих символов — объединение всех шести физических строк в одну логическую строку, содержащую одну-единственную VBA-инструкцию.
Приложение
819
ActiveWorkbook.SaveAs Filename:="NEWFILE.XLS",
FileFormat:=xlNormal,
Passwords'"',
WriteResPassword:="",
ReadOnlyRecomEnded:= False, CreateBackup:= False
12.	Когда вы в окне программы переводите курсор из строки, которую вы только что отредактировали или ввели, VBA сначала анализирует эту строку на предмет правильности синтаксиса, а затем компилирует вашу программу в форму, которую можно использовать напрямую, уже без повторного анализа.
13.	Синтаксическая ошибка — это ошибка, которая образуется в результате неправильного построения программной инструкции VBA. Синтаксические ошибки обычно возникают из-за пропущенных или поставленных “не в том месте” запятых, круглых скобок, кавычек и пр. Кроме того, синтаксические ошибки часто появляются в результате пропуска таких ключевых слов, как Sub, или случайного удаления целых строк с ключевыми словами (например, в определении процедуры: End Sub).
VBA обычно уведомляет вас о наличии синтаксических ошибок по мере написания каждой строки программы. Если вы не исправите синтаксическую ошибку при ее первом обнаружении, VBA снова отобразит сообщение об ошибке при попытке выполнить процедуру, в которой она содержится.
14.	Ошибка времени выполнения (динамическая ошибка) — это ошибка, возникающая в процессе выполнения процедуры. У динамических ошибок много различных причин. Чаще всего они возникают в результате использования инструкций, которые с точки зрения синтаксиса корректны, но содержат данные “не того” типа или пытаются работать с файлами или частями файла (например, с конкретными рабочими листами), которые отсутствуют в момент выполнения процедуры, и т.п.
Упражнения
1.	Цель этого упражнения — приобретение некоторого опыта по вводу текста программы с помощью редактора Visual Basic. В этой процедуре используется команда (строка 5 данного листинга), которая вызывает справочную систему Excel (и, следовательно, Windows) и служит указанием к открытию справочного файла VBA Excel (справочника по Visual Basic). (Справочный файл для VBA Excel и основной справочный файл Excel — это разные файлы. В Excel 2000 файл справки VBA называется VBAXL9.CHM.)
Если эта процедура не выполняется или не отображается справочник по VBA, открытый на разделе “Модель объектов”, убедитесь в том, что вы точно скопировали этот листинг. Обязательно проверьте наличие таких ключевых слов, как Sub и End Sub, а также кавычек, в которые должно быть заключено имя файла справки в строке 5. Кроме того, убедитесь в том, что вы поставили точку (.)в строке 5 между словом Application и словом Help.
2.	Если во время написания строки, в которой используется процедура MsgBox, вы получите сообщения о синтаксических ошибках, вспомните о том, что для пропущенного второго аргумента необходимо включать “его” запятую.
Ваша процедура должна выглядеть подобно приведенному ниже листингу, хотя для процедуры вы могли выбрать произвольное имя.
Sub MyMessage()
820
Приложение
'Упражнение 2.3
'Этот Макрос отображает сообщение.
MsgBox "Это - сообщение Visual Basic", , "Сообщение VBA"
End Sub
3.	В строке 2 исходной процедуры текст сообщения не заключен в кавычки. Поэтому средствами контроля синтаксиса VBA была обнаружена синтаксическая ошибка. Если вы ввели эту процедуру “как есть” (чтобы узнать, какое будет выдано сообщение об ошибке), то должны были заметить, что это сообщение не связано напрямую с настоящей проблемой. Поскольку VBA интерпретирует текст без кавычек как имена переменных, то вы получите сообщения о том, что не хватает запятой или круглых скобок.
Это упражнение иллюстрирует тот факт, что VBA не всегда в точности может сообщать о причине синтаксических ошибок. Иногда, как в случае с пропущенными кавычками, синтаксическая ошибка, о которой было выдано сообщение, на самом деле была вызвана другой синтаксической ошибкой, которая была допущена где-то выше в исходном тексте программы (возможно, даже в той же строке или несколькими строками выше). Если вы получите сообщения об ошибках, связанные с пропущенными запятыми или круглыми скобками, но вы сомневаетесь в его правдоподобности, просмотрите свою программу (особенно “пострадавшую” строку и ее “верхние” окрестности) на предмет пропуска кавычек.
Исправленная процедура приведена ниже.
Sub Broken))
'Упражнение 2.4
'Этот Макрос содержал ошибку, но теперь он исправлен.
MsgBox "Еще одно сообщение"
End Sub
День 3-й
Тест
1.	В VBA существует шесть числовых типов данных: Byte, Integer, Long, Single, Double и Currency.
2.	Основная разница между типами данных Integer и Single состоит в том, что с помощью типа Integer можно хранить только целые числа (числа, нс имеющие десятичной точки), в то время как тип данных Single позволяет хранить числа с десятичной точкой. Кроме того, диапазон типа данных Single намного больше, чем у типа Integer.
Как тип Integer, так и тип Long, позволяют хранить только целые числа. Единственное различие между ними состоит в том, что диапазон значений, которые можно хранить с помощью типа данных Long гораздо больше, чем у типа Integer.
3.	Символ определения типа — это специальный символ, который, будучи добавленным в конец имени переменной или константы, задает тип данных этой переменной или константы.
4.	Переменная объявляется неявно просто уже фактом своего использования. Если некоторая переменная еще не использовалась выше, VBA “понимает”, что ее нужно создать, и, “не долго думая”, создает ее. Для явного объявления переменной используется инструкция Dim, которая специально указывает на необходимость создания переменной.
Приложение
821
5.	Названия идентификаторов должны начинаться с буквы, за которой может следовать любая комбинация букв или цифр. Хотя символ подчеркивания разрешен для использования в идентификаторе, никакие другие символы (пробелы, точки и пр.) использовать нельзя (за исключением символов определения типа). Для идентификаторов следует выбирать описательные имена, которые отражают их назначение, — будь то идентификатор имени переменной, константы или процедуры. Наконец, нелишне отметить, что прописные буквы и символы подчеркивания упрощают читабельность и понимание длинных идентификаторов.
6.	Перечислим преимущества явного объявления переменных.
•	Более быстрое выполнение VBA-программы.
•	Меньшая вероятность допущения ошибки из-за неправильного написания имен переменных.
•	Программа становится легче для чтения и понимания.
•	Стандартизация использования прописных букв в каждом вхождении имени переменной, которое должно совпадать с использованием прописных букв в объявлении переменной.
7.	Перечислим преимущества объявления типа переменных.
•	Более быстрое выполнение VBA-программы.
•	Память используется более эффективно.
•	Исходная программа становится легче для чтения и понимания.
•	Уменьшается вероятность появления ошибок в программе.
8.	Использование в программе поименованных констант облегчает их чтение и понимание, а также поддержку в дальнейшем. Используйте поименованные константы для значений, которые либо часто повторяются в вашей программе, либо трудно запоминаются, либо не сразу обнаруживают свой смысл.
9.	Назначение функции InputBox — получить данные от пользователя, которые будут использованы процедурой (например, имя файла, числа, участвующие в вычислениях, и пр.). Функция InputBox отображает диалоговое окно, содержащее задаваемый вами текст, с помощью которого пользователю предлагается ввести некоторое значение, и текстовое поле, в которое пользователь вводит запрашиваемое значение.
10.	Первый аргумент для функции InputBox (строка, содержащая текст, в котором выражается предложение ввести значение) является обязательным. Остальные аргументы необязательны.
11.	Функция InputBox всегда возвращает значение типа String.
Упражнеппя
1. Одним из ключевых моментов в принятии решения об объявлении некоторого элемента данных переменной или константой состоит в выяснении следующего: будет ли это значение вычисляться программой в процессе ее выполнения или оно известно заранее. Имена переменных и констант должны быть максимально описательны. При этом программисты стараются находить компромисс между достаточной описательностью имени и нежеланием вводить длинные слова. Несмотря на то, что вам придется выбирать различные имена для различных программных элементов, ваши объявления должны выглядеть следующим образом.
822
Приложение
(а)	Для вычисляемого количества столбцов в рабочем листе нужно использовать переменную, поскольку содержащееся в ней значение определяется после начала работы программы. Поскольку весьма маловероятно, что в рабочем листе будет более 32 767 столбцов, то был выбран тип данных Integer как наименьший и наиболее быстро обрабатываемый числовой тип, который способен хранить прогнозируемые данные. Во второй строке из приведенных ниже строк показано объявление, в котором используется символ объявления типа.
Dim NumCols As Integer
Dim NumColsi 'используется символ определения типа Integer
(б)	Поскольку это значение вычисляется программой, оно должно храниться в переменной. При этом используется тип данных Currency, так как это числовое значение представляет сумму в долларах, а тип данных Currency как раз и является самым быстро обрабатываемым и точным типом данных для хранения “денежных” (в данном случае долларовых) значений. Во второй строке из приведенных ниже строк показано объявление, в котором используется символ объявления типа.
Dim EastCoast_Sales As Currency
Dim EastCoast_Sales@ 'используется символ определения типа Currency
(в)	С этим элементом дело обстоит несколько сложнее. Если планируемое число опрашиваемых в плане изучения рынка вычисляется процедурой, то это должна быть переменная. Но обычно в качестве подобного элемента используется константа, представляющая известное заранее количество опрашиваемых людей. В приведенном ниже объявлении предполагается, что ответ в результате изучения рынка окажется около 2 процентов, и поэтому для него выбирается тип данных Double, чтобы с помощью этой константы получить в вычислениях максимальную точность. Во второй строке из приведенных ниже строк показано объявление, в котором используется символ объявления типа:
Const ProjectedResp As Double = 2
Const ProjectedResp! = 2 'используется символ определения типа Double
(г)	И опять-таки, поскольку это значение вычисляется процедурой, выбирается не константа, а переменная. И снова для объявления выбирается тип данных Double, чтобы при вычислении площади поверхности цилиндра иметь максимальный диапазон представления чисел и максимально возможную точность.
Dim CylinderArea As Double
Dim CylinderArea! 'используется символ определения типа Double
(д)	Коэффициент, используемый для преобразования дюймов в сантиметры, является константой. Этот вид данных идеален для выбора именованной константы. Обратите внимание на то, как цифра 2 (two — произносится “ту”) включена в имя константы в качестве звукового синонима для английской частицы “to” (тоже произносится “ту”), означающей в данном случае направление преобразования. Это обычная практика среди программистов делать идентификаторы выразительными, но не содержащими слишком много символов.
Const Inch2Cm As Double = 0.3937
Const Inch2Cm! = 0.3937 'используется символ определения типа Double
(е)	Снова объявляется переменная, поскольку значение прибыли в процентах вычисляется программой. Обратите внимание на то, как в имени переменной
Приложение
823
были использованы символ подчеркивания, цифра и прописная буква, чтобы сделать имя этой переменной выразительным и одновременно относительно коротким. Выбор пал на тип данных Single, поскольку в значениях, выраженных в процентах, обычно используется до двух десятичных знаков. Если же в вашей программе требуется большая точность, то вам стоит использовать тип Double. Если же вас больше интересует округление ошибок или, если вы предполагаете использовать это значение в других вычислениях, включающих денежные значения, то, возможно, вам стоит использовать тип данных Currency.
Dim PcntProfit_lstQ As Single
Dim PentProfit IstQ! 'используется символ определения типа Single
(ж)	Налоговая ставка самостоятельной занятости является, конечно же, константой. Это значение также идеально подходит для использования поименованной константы. Поскольку эта налоговая ставка может со временем измениться, благодаря поименованной константе такую программу будет легко поддерживать: достаточно один раз изменить объявление константы — и программа будет работать “как новая”. Для этого значения был выбран тип данных Currency, поскольку оно будет использоваться в вычислениях вместе с другими значениями, представляющими деньги.
Const SelfEmp_Tax As Currency =12
Const SelfEmp_Tax? 'используется символ определения типа Currency
2.	Ваша процедура должна выглядеть примерно так, как показано в следующем листинге. Обратите внимание на строки 7 и 8. В этих строках объявляется константа для строк заголовков диалоговых окон и все переменные, используемые в процедуре. В строке 9 для получения от пользователя строки используется функция InputBox, а в строке 10 для отображения строки, введенной пользователем, — процедура MsgBox.
Sub EchoThisf)
'Упражнение 3.2
'Процедура EchoThis
'принимает от пользователя строку и отображает ее с помощью MsgBox
Const BoxTitle = "Echo"
Dim User$
User$ = InputBox)"Введите любой текст:", BoxTitle)
MsgBox User$, , BoxTitle
End Sub
3.	В приведенном ниже листинге показано, как должен выглядеть записанный макрос Excel перед редактированием. (Имя открытого файла может, конечно, быть другим.) Поскольку открытие рабочей книги было первым предпринятым процедурой действием, и поскольку имя открываемой рабочей книги известно (VBA_Sample.xls в данном случае), нетрудно догадаться, что строка 6 содержит ту VBA-инструкцию, которая действительно открывает рабочую книгу. Следовательно, инструкцию, содержащую функцию InputBox для запроса у пользователя имени файла рабочей книги, нужно вставить непосредственно перед строкой 6.
Sub 0penSheet3()
I
' Макрос 0penSheet3 - ^отредактированная версия
824
Приложение
' Открывает ЛистЗ выбранной рабочей книги t
Workbooks.Open Filename:="C:\TVA-214WBA_Sample.xls"
Sheets("Sheet3").Select
End Sub
В следующем листинге показан тот же макрос после добавления функции InputBox. В строке 7 объявляется переменная, предназначенная для хранения имени файла. В строке 8 используется функция InputBox, чтобы предложить пользователю ввести имя файла, а результат этой функции присваивается переменной FName. Строка 10 представляет собой ту же VBA-инструкцию из строки 6 неотредактированного листинга. В этом варианте строка изменена таким образом, чтобы литеральная строка, заключенная в кавычки была заменена переменной FName. Теперь при выполнении этого макроса будет открываться файл, имя которого хранится в переменной FName.
Sub OpenSheet3()
I
' Макрос OpenSheet3
' Открывает рабочую книгу и выбирает лист с именем "ЛистЗ"
' Модифицирован добавлением запроса у пользователя имени открываемого файла I
Dim Fname As String
Fname = InputBox("Введите имя открываемого файла:",
"Open Sheet 3")
Workbooks.Open Filename:=Fname
Sheets("Sheet3").Select
End Sub
4.	В следующем листинге показано, как можно изменить макрос Excel из упражнения 1, приведенного в уроке 1, чтобы он предлагал пользователю ввести имя открываемого файла. Изменения, внесенные в этот макрос, подобны изменениям, продемонстрированным в предыдущем упражнении.
Sub NewBook() I
' Макрос NewBook
' Создает и сохраняет новую рабочую книгу.
' Модифицирован добавлением запроса у пользователя имени создаваемого файла I
Dim Fname As String
'В следующей строке создается рабочая книга
Workbooks.Add
'В следующей строке запрашивается имя нового файла
Fname = InputBox)"Введите имя для новой рабочей книги:",
"New Workbook")
'В следующей строке выполняется сохранение рабочей книги ActiveWorkbook.SaveAs Filename:=FName,
FileFormat:=xlNormal,
Password,
WriteResPassword:=”", _
Приложение
825
ReadOnlyRecommended: = False, CreateBackup:= False
'В следующей строке рабочая книга закрывается
ActiveWindow.Close
End Sub
День 4-fi
Teem
1.	Выражение — это значение или группа значений, представляющих единую величину. Результат выражения — это значение, полученное после выполнения всех операций, заданных в выражении, т.е. когда выражение сводится к единственной величине.
2.	Выражение может содержать одно или несколько различных значений. Значения в выражении связываются операторами.
3.	Да.
4.	Результаты выражений присваиваются переменным или используются в качестве аргументов функций или процедур. Результат выражения должен быть некоторым образом использован.
5.	Знак равенства (=) служит для обозначения как операции присваивания, так проверки на равенство.
6.	Символ / означает операцию деления с плавающей точкой. Результатом этого деления всегда является число с плавающей точкой, которое обычно имеет тип Double. Символ \ означает операцию деления нацело. Результатом целочисленного деления всегда является целое число, имеющее либо тип Integer, либо тип Long.
7.	Результатами выражений являются следующие значения.
а)	Значение типа Boolean False
b)	Число 40
с)	Строка 1723
d)	Число 0
е)	Строка 23Skidoo
Упражнения
1.	После добавления круглых скобок выражения имеют следующий вид.
* (5 - 7)
+ 26) / 10
/ ((47 + 16) - 2)
((17 - 5) - (44 / 2)) А 2
2.	Ваша процедура должна выглядеть следующим образом.
Sub ThreeWords()
'Упражнение 4.2
'Получает от пользователя три слова, объединяет их
'в одну строку и отображает эту строку на экране
Const Titlel = "Ввод: "
Const Title2 = " Слово"
826
Приложение
Const Prmpt = "Введите слово:" ' подсказка для функции InputBox
Dim strEcho As String 'строка, собранная для отображения
Dim strWord As String 'временное пристанище для слова пользователя
strWord = InputBox(Prmpt, Titlel S "Первое" S Title2)
strEcho = strWord
strWord = InputBox(Prmpt, Titlel S "Второе" S Title?)
strEcho = strEcho S " " S strWord
strWord = InputBox(Prmpt, Titlel & "Третье" & Title?)
strEcho = strEcho S " " S strWord
MsgBox strEcho, , "Три слова"
End Sub
В строках 6-8 объявляются константы, необходимые для работы этой процедуры. Обратите внимание, что здесь используются две отдельных поименованных константы для неизменяемой части заголовка, применяемого для диалоговых окон InputBox. В строках 10 и 11 объявляются все переменные этой процедуры. В строках 13—18 у пользователя запрашивается слово, и каждое слово по мере ввода добавляется к строке strEcho. В строке 20 выполняется отображение трех слов, объединенных в одну строку.
День 5-й
Тест
1.	Любые три из перечисленных ниже.
•	Преобразование текстовых строк в другие типы данных.
•	Преобразование других типов данных в текстовые строки и возвращение информации об этих текстовых строках.
•	Форматирование чисел или других типов данных для отображения.
•	Манипуляция или генерирование значений данных.
•	Выполнение тригонометрических, логарифмических, статистических, финансовых и других вычислений.
•	Получение информации о файлах, дисковых устройствах или о среде, в которой работает в данный момент VBA или ваше приложение.
2.	Функцию в качестве поставщика значения можно использовать в любой VBA-инструкции, где правомочно использовать значение константы или переменной (за исключением левой стороны операции присваивания).
3.	Опустите круглые скобки из списка аргументов функции, чтобы сообщить VBA о своем намерении игнорировать результат функции. Нельзя игнорировать результат каждой VBA-функции. Это касается только тех функций, которые наряду с возвратом некоторого значения выполняют определенную задачу.
4.	Функции VBA являются неотъемлемой частью VBA, и они доступны в любой версии этого языка независимо от используемого приложения (будь то Microsoft Excel, Word, Access, Project или какое-нибудь другое приложение). Функции же, предоставляемые приложением Excel (или каким-либо другим VBA-приложением), существуют только в данном конкретном приложении. И хотя некоторые (или все) функции такого приложения могут быть доступны для
Приложение
827
VBA, они не являются составной частью VBA и не могут быть доступны в каждом “офисном” приложении.
5.	Перед именем функции главного приложения, которую вы собираетесь использовать, необходимо включить ключевое слово Application и точку (.).
6.	Нет.
7.	В окне Object Browser можно определить, какие функции VBA и главного приложения доступны, и получить простой доступ к электронной справке для этих функций. Вы можете также скопировать имена функций и список аргументов из окна Просмотр объектов в буфер обмена Windows для последующей вставки в свой модуль.
8.	Возможность выполнения операций со строковыми данными очень важна, поскольку текстовые строки являются главнейшим средством обмена информацией с пользователем вашей программы (отображение сообщений или получение входных данных). Чем эффективнее вы сможете обрабатывать строковые данные, тем успешнее будет организовано взаимодействие с пользователем. Пользователю всегда приятнее работать с программой, которая выводит на экран привлекательные и понятные сообщения и при этом быстро и правильно анализирует данные, которые он вводит.
Упражнеиия
1.	После внесения исправлений выражения имеют следующий вид.
Sum$ = CStr(12 + 15)
Num* = Cint("47") + Cint("52")
Num? = 12.98 * Ccur("16")
Root! = Sqr(CSng(User_Input$))
2.	Ваша законченная Excel-процедура должна выглядеть примерно так.
Sub Demo_Min() 'Упражнение 5.2 Const InPrompt = "Введите число:" Const InTitle = "Число "
Dim Nl As String, N2 As String, N3 As String
Dim NMin As String
Nl = InputBox(Prompt:=InPrompt, Title:=InTitle & "1")
N2 = InputBox)Prompt:=InPrompt, Title:=InTitle & "2")
N3 = InputBox)Prompt:=InPrompt, Title:=InTitle S "3")
Nmin = Application.MinfNl, N2, N3)
MsgBox "Минимумом из " & Nl & ", " & N2 & ", и "
S N3 S " является: " & Nmin End Sub
В строках 7-9 для получения чисел от пользователя вызывается функция InputBox, возвращаемое значение которой присваивается соответствующим переменным. В строке 10 вызывается функция Excel MIN, а в строке 11 результат этой функции отображается на экране. Причем эти две строки можно было бы совместить, поместив вызов функции MIN прямо в инструкцию отображения результатов с помощью функции MsgBox, — это также был бы вполне приемлемый вариант.
Обратите внимание на то, что в данной процедуре реализована попытка построить наиболее полные, понятные и внешне привлекательные для пользователя сообщения. Вы должны стремиться все свои процедуры создавать в таком же стиле.
828
Приложение
3.	Ваша законченная процедура должна выглядеть примерно следующим образом.
Sub Demo Substrings()
'Упражнение 5.3
Const InTitle = "Выделение подстрок"
Dim Userin As String
Userin = InputBox(Prompt:="BBeflHTe некоторый текст:", Title:=InTitle, Default:=“Предложено”)
MsgBox Left(UserIn, 3)
MsgBox Right(UserIn, 4)
MsgBox Mid(UserIn, 3, 4)
End Sub
4.	Ваша законченная процедура должна выглядеть примерно следующим образом.
Sub SearchDemof)
'Упражнение 5.4
Const BoxTitle = "Демонстрация поиска подстрок"
Dim Userin As String, SrchFor As String
UserIn = InputBox(Prompt:="Введите некоторый текст:", _
Title:=BoxTitle, Default:="CTpoKa по умолчанию")
SrchFor = “Л"
MsgBox Prompt:=InStr(l, Userin, SrchFor, VbTextCompare),
Title:=BoxTitle & ": Сравнение текста"
MsgBox Prompt:=InStr(l, Userin, SrchFor, VbBinaryCompare),
Title:=BoxTitle & ": Сравнение двоичных данных"
MsgBox Prompt:=InStr(UserIn, SrchFor),
Title:=BoxTitle & ": Использование параметра Option Compare"
End Sub
В этой процедуре строки 8—13 реализуют отображение результатов функции поиска InStr, используя при этом все возможные варианты сравнения (если в вашей программе есть только один из них — тоже неплохо). Во всех трех инструкциях поиск начинается с первого символа строки Userin. В строке 8 выполняется текстовое сравнение, в строке 10 — двоичное, а в строке 12 используется текущий вариант сравнения.
Попробуйте изменить регистр буквы, которая определяет содержимое переменной SrchFor, т.е. замените “Л” на “л”, и посмотрите, какой результат вернет функция InStr. Попробуйте также изменить в модуле параметр Option Compare и посмотрите, как это повлияет на результат.
День 6-й
Тест
1.	Процедуры-функции обрамлены зарезервированными словами Function и End Function в отличие от простых процедур, начало и конец которых определяют ключевые слова Sub и End Sub. Функция возвращает некоторый результат, а процедура — нет.
2.	Нестандартная функция — это то же самое, что и процедура-функция, за исключением того, что термин нестандартная функция используется для любой процедуры-функции, которая соблюдает ограничения, связанные с запретом на изменение среды Excel, чтобы процедуру-функцию можно было использовать в ячейках рабочего листа. Создавая нестандартную функцию для использования в
Приложение
829
рабочих листах Excel, вы должны убедиться, то она ни коим образом не изменяет операционную среду Excel.
3.	Нестандартная функция не должна ни коим образом изменять среду главного приложения. Этого значит, что нестандартная функция не может менять значения переменных в приложении Excel.
4.	Присваивание результата функции — это инструкция в процедуре-функции, которая сообщает VBA, какое значение возвращает функция. Функция может иметь несколько инструкций присваивания. Однако функция возвратит то значение, которое было задано последним присваиванием результата, выполненным перед завершением работы функции.
5.	Рекурсия возникает в том случае, когда функция (или любая другая процедура) вызывает саму себя.
6.	Простите, это не такой простой вопрос. Обычно рекурсивные процедуры-функции использовать не рекомендуется. Рекурсивные процедуры, как правило, трудны для понимания и могут занимать большой объем памяти — иногда даже слишком большой, чтобы они могли успешно завершить вычисления или другие действия. Главная причина рассмотрения рекурсии в этом уроке состоит в том, чтобы дать вам представление о том, что может произойти, если вы случайно создадите рекурсивную функцию или процедуру.
7.	Используйте функцию IsMissing, чтобы узнать, присутствовал ли некий необязательный аргумент при вызове процедуры-функции (или другой процедуры).
8.	Функция IsMissing работает только с такими необязательными аргументами, которые имеют тип Variant. Если необязательный аргумент имеет любой другой тип данных, функция IsMissing всегда возвращает значение False.
9.	В функции FlipCase была использована функция StrComp, поскольку она сравнивает две строки и позволяет указать точный метод сравнения, который следует использовать для данной конкретной операции сравнения. Функция FlipCase в своем полном варианте “различает” регистр букв. Если же используется простой оператор отношений, то его действие зависит от параметра Option Compare. Если он установлен равным значению Text, то тест, проведенный с помощью простых операторов отношений, не “увидит” разницы между буквами “А” и “а”. При использовании функции StrComp в проверках гарантированно применяется двоичное сравнение, независимо от значения параметра Option Compare.
10.	Если ваша функция изменяет любое значение аргументов, то следует указывать, что аргумент функции передается по значению. При передаче аргументов по ссылке функция получает в аргументе копию исходного значения, чтобы она могла “без опаски” изменять это значение, не оказывая влияния на исходные данные.
11.	Вам следует использовать окно Object Browser. Процедуры-функции не приводятся в списке диалогового окна Макрос, которое открывается при выборе команды Сервис-Макрос-Макросы.
Упражнения
1.	Ваши эквивалентные функции должны быть аналогичны приведенным ниже двум листингам.
Нестандартная функция, эквивалентная оператору Eqv Visual Basic.
Function uEQV(Ll As Boolean, L2 As Boolean) As Boolean
uEQV = LI Eqv L2
End Function
830
Приложение
Нестандартная функция, эквивалентная оператору Imp Visual Basic.
Function uIMP(Ll As Boolean, L2 As Boolean) As Boolean
uIMP = Ll IMP L2
End Function
2.	При корректном выполнении действий, описанных в этом уроке, вы должны увидеть описание, введенное вами в окне Object Browser, в диалоговом окне Мастер функций приложения Excel.
3.	Ваша процедура-функция должна выглядеть примерно так, как показано ниже.
Function Yds2Inch(yds As Double) As Double
'возвращает размер в дюймах, эквивалентный числу
' ярдов в аргументе yds
Yds2Inch = yds * 36
End Function
В этой функции тип данных Double был выбран как для типа данных аргумента, так и для типа данных результата функции, чтобы обеспечить максимально возможный диапазон как для входных значений функции, так и для значения возврата. Такие типы данных, как Integer или Long, не подходят ни для аргумента функции, ни для значения возврата, поскольку у вас может быть дробное число ярдов, которое переводится тоже в дробное число дюймов. Однако для этой функции было бы вполне приемлемо использовать и такие типы данных, как Variant или Single.
Ваша тестовая процедура должна выглядеть аналогично следуюшему варианту:
Sub Test_Yds2Inch()
Dim Userin
Userin = InputBox(“Введите размер в ярдах:",
"Yds2Inch")
MsgBox Userin & " ярдов содержит" & Yds2Inch(CDbl(UserIn))
& ” дюймов"
MsgBox "Значение числа, введенного пользователем после " & "вызова Yds2Inch: ” 4 Userin
End Sub
В этой тестовой процедуре контрольные значения образуются в результате вызова функции InputBox. Это более простой путь по сравнению с использованием литеральных констант и многократным редактированием тестовых программ. Обратите внимание на то, что тестовая процедура не только отображает результат вызова функции, но также сопоставляет исходное значение, которое ввел пользователь, с аргументом функции, чтобы убедиться в том, что эта функция ни коим образом не изменяет свой аргумент. (В данном случае, т.е. для такой простой функции, этот “завершающий аккорд” не является необходимостью, но было бы совсем неплохо взять за правило всегда проверять, не изменяет ли ваша функция свои аргументы.)
4.	Ваша процедура-функция должна выглядеть примерно так, как показано ниже:
Function Inch2Cm(inches As Double) As Double
'возвращает размер в сантиметрах, эквивалентный числу
' дюймов в аргументе inches
Inch2Cm = inches * 2.54
End Function
Тип данных для аргументов функции и результата функции тот же, что и в функции из упражнения 6.3 и по тем же самым причинам.
Приложение
831
Ваша тестовая процедура должна выглядеть аналогично следующему варианту.
Sub Test_Inch2Cm()
Dim Userin
Userin = InputBox("Введите размер в дюймах:",
"Inch2Cm")
MsgBox Userin & " дюймов содержит” & Inch2Cm(CDbl(UserIn))
& " сантиметров"
MsgBox "Значение числа, введенного пользователем после " &
"вызова Inch2Cm: " & Userin
End Sub
Обратите внимание, что эта тестовая процедура также получает контрольное значение от пользователя, а затем проверяет его, чтобы убедиться в том, что эта функция не изменяет свой аргумент.
5.	Ваша процедура, предназначенная для отображения размеров в ярдах, переведенных в сантиметры, должна иметь примерно следующий вид.
Sub Yds2Cm()
Dim Userin As String
Userin = InputBox)Prompt:="Введите размер в " & _
"ярдах:",
Title:="HpflM в сантиметры")
MsgBox Userin & " ярдов содержит " &
Inch2Cm(Yds2Inch(CDbl(UserIn))) & " сантиметров."
End Sub
Обратите внимание на строку 7 предыдущего листинга. Здесь используется функция CDbl, которая в явном виде переводит строку, введенную пользователем, в число типа Double. Поскольку аргумент функции имеет тип данных Double, то VBA требует, чтобы и данные, передаваемые функции, тоже имели тип Double.
День 7-й
Тест
1.	Основная идея, лежащая в основе объектно-ориентированного программирования, состоит в том, что программное приложение, подобно окружающему нас миру, должно состоять из отдельных объектов, каждому из которых присущи свои собственные свойства и поведение.
2.	Объект программы состоит из текста программы и данных, связанных между собой так, что с ними можно обращаться как с единым целым.
3.	Свойство — это неотъемлемое качество объекта, как, например, его цвет, имя файла, факт видимости и т.п. Управляя свойствами объекта, мы управляем его внешним видом и поведением, т.е. мы используем свойства объекта, чтобы изменить его внешний вид и поведение, а также для того, чтобы узнать информацию о текущем внешнем виде и поведении объекта.
4.	Нет, одни свойства мы можем изменять, а другие — нет. Но независимо от того, можем мы или нет изменить свойство объекта, мы всегда можем прочитать его значение.
5.	Нет. Каждый объект хранит данные для своих собственных свойств отдельно. Несмотря на то что некоторые объекты имеют свойства с одинаковыми именами, это совсем не значит, что они совместно используют значения, сохраняемые в этих свойствах.
832
Приложение
6.	Метод — это поведение, присущее объекту программы, например, способность рабочей книги добавлять новый рабочий лист, способность рабочей книги сохранять свое содержимое в дисковом файле или способность приложения открывать имеющуюся рабочую книгу либо создавать новую рабочую книгу. Методы объекта используются для выполнения действий со свойствами и данными пользователя, сохраняемыми объектом.
(а)	Опросите текущее условие или состояние объекта, прочитав значение его свойства.
(Ь)	Измените текущее условие или состояние объекта, установив значение его свойства.
(с)	Используйте метод, чтобы заставить объект выполнить одно из его встроенных действий.
8.	Объект. Идентификатор, где элемент Объект представляет собой любую допустимую ссылку на объект, а элемент Идентификатор — это свойство или метод, определенный для объекта, на который дается ссылка. Разделительная точка (.), стоящая между ссылкой на объект и идентификатором свойства или метода, обязательна.
9.	Разделительная точка (.) как разделяет, так и объединяет идентификаторы, которые образуют ссылку на объект. Идентификаторы на объект нужно писать вместе, без пробелов, формируя, таким образом, единый идентификатор, используемый средствами VBA для определения того, на какой объект делается ссылка. Разделительная точка позволяет объединить идентификаторы вместе, но при этом и отделяет один идентификатор от другого, чтобы средства VBA могли распознать каждую составную часть ссылки на объект.
10.	Объектное выражение — это любое VBA-выражение, которое определяет объект. Вычисление объектного выражения должно сводиться к единственной ссылке на объект. Используйте объектные выражения в своих VBA-программах для создания ссылок на конкретные объекты.
11.	Используйте методы и свойства объекта (начиная со свойства Application, если это необходимо), которые возвращают ссылки на объекты. Для простоты ввода или для краткости можно также присваивать ссылки на объекты переменным с помощью команды Set.
12.	Коллекция объектов — это группа связанных объектов (например, все рабочие листы в рабочей книге или все графические объекты на рабочем листе). Элемент — это один объект в коллекции.
Упражнения
1.	Изъян в процедуре NewBook связан с инструкцией With... End With и ссылкой на объект внутри нее.
Проблема состоит в том, что была опущена разделительная точка (.) перед именами свойств для активной рабочей книги. В результате VBA интерпретирует свойства Title, Subject, Author, Keywords и Comments как имена переменных, а не ссылки на свойства объекта ActiveWorkbook. Вот почему VBA “выражает недовольство” существованием неопределенных переменных при включении инструкции Option Explicit.
Чтобы устранить проблему, достаточно добавить разделительную точку (.) перед каждым именем свойства, как показано в следующем листинге.
Sub NewBook))
'Создает новую рабочую книгу и вносит в нее итоговую
Приложение
833
'информацию, причем часть информации берется от 'пользователя, а часть - из объекта Application
Const nbTitle = "New Book"
Workbooks.Add ' добавляет рабочую книгу в коллекцию Workbooks
With ActiveWorkbook
•Title = InputBox)prompt:= _
"Введите название для этой рабочей книги:", Title:=nbTitle)
.Subject = InputBox)prompt:= _
"Введите тему этой рабочей книги:", _
Title:=nbTitle)
.Author = Application.OrganizationName
.Keywords = ""
.Comments = InputBox)prompt:= _
"Введите комментарии для этой рабочей книги:", Title:=nbTitle)
End With
End Sub
2.	После перезаписи процедуры Show_SystemInfo с целью использования инструкции With... End With она должна принять следующий вид.
Sub Show Systeminfo
'использует различные свойства приложения для отображения
'информации о вашей компьютерной системе
With Application
MsgBox "Главное приложение: " & vbCR & .Name & " v"
& .Version & ", Build " & .Build & vbCR & vbCR & “Библиотека: " & .Librarypath & vbCR & vbCR & "Пользователь: " & .UserName & vbCR & "	" &
.OrganizationName
MsgBox "Операционная система: " & .OperatingSystem &
vbCR & vbCR & "Мышь: " &
.MouseAvailable & vbCR & vbCR &
"Всего памяти: " & .MemoryTotal & vbCR & "Используется памяти: " & .MemoryUsed & vbCR & "Свободно памяти: " & .MemoryFree
End With
End Sub
Обратите внимание на то, что разделительная точка включена перед каждым именем свойства объекта Application внутри оператора with... End With. Заодно отметьте, как в этой процедуре используется внутренняя константа vbCR. Встроенная константа vbCR представляет символ возврата каретки — с ее помощью текст функции MsgBox делится для отображения на несколько строк.
3.	Проблема, связанная с функцией GetNumber, состоит в том, что в ней предполагалось использование функции Excel InputBox, а не функции VBA InputBox. Поскольку обе функции называются одинаково, то для использования Excel-версии нужно указывать объект Application. Поскольку в функции GetNumber опущена ссылка на объект Application, то было сделано предположение, что имелась в виду VBA-версия функции InputBox, которая не имеет аргумента Туре.
834
Приложение
Ниже приведен исправленный вариант функции GetNumber. Обратите внимание на изменения в строке 5.
Function GetNumber()
' использует функцию InputBox объекта Application
' для возврата значения, полученного от пользователя
GetNumber = Application.1приЬВох(РготрЬ:="Введите " & "число: ", Туре:=1)
End Function
Sub Test_GetNumber()
MsgBox GetNumber
End Sub
В строках 10—12 содержится та же самая процедура тестирования функции GetNumber.
Используйте в своих Excel-процедурах функцию, подобную GetNumber, чтобы гарантированно принимать от пользователя только числовые данные. В этом случае вы будете избавлены от необходимости сопровождать приглашением ввести число каждый вызов функции InputBox. Благодаря этому ваша программа будет более короткой и ясной.
4.	Ваша законченная процедура должна иметь примерно следующий вид.
Sub Sheetinsert))
'Добавляет новый рабочий лист в текущую рабочую книгу, затем
'переименовывает этот лист, используя имя, предложенное пользователем.
Dim sName As String
Dim oldSheet As Object
Set oldSheet = ActiveSheet
sName = InputBox(Prompt:="BBeflHTe имя для нового " &
"рабочего листа:", Title:="Добавление нового листа")
Worksheets.Add
ActiveSheet.Name = sName
oldSheet.Select
End Sub
В строке 8 используется инструкция Set для присваивания переменной oldSheet объектной ссылки на активный в данный момент рабочий лист. Затем в строке 9 вызывается функция InputBox, чтобы получить от пользователя имя для нового рабочего листа, сохранив его в переменной sName. В строке 11 используется метод Add коллекции Worksheets для добавления в активную рабочую книгу нового рабочего листа. Excel послушно добавляет новый рабочий лист и делает его активным. В строке 12 выполняется переименование нового рабочего листа путем присваивания строки, сохраненной в переменной sName, свойству Name активного листа. Наконец, в строке 13 снова выбирается лист, который был активным в тот момент, когда эта процедура только начала выполняться. Смена активных листов реализуется за счет вызова метода Select, который обеспечивает “самовыдвижение” объекта. При этом на нужный объект указывает переменная oldSheet, которая хранит ссылку на лист, который до начала работы этой процедуры был активным листом.
Приложение
835
День 8-и
Тесл
1.	Инструкция условного ветвления изменяет последовательность выполнения VBA-инструкций в процедуре в зависимости от результата проверки некоторого условия (истина или ложь). Инструкция безусловного перехода изменяет последовательность выполнения инструкций, не проверяя никаких условий.
2.	Используйте логические выражения для задания условий, при которых должно или не должно выполняться ветвление программы.
3.	Список VBA-инструкций условного ветвления программы.
If...Then
If...Then...Else
If...Then...Elself
Select Case
4.	В языке VBA предусмотрена только одна инструкция безусловного перехода: GoTo.
5.	Вложенность.
6.	Инструкция If.. .Then.. .Elself может содержать неограниченное число директив Elself. В инструкцию If.. .Then...Elself можно включить только одну директиву Else, которая должна стоять после всех директив Elself и перед ключевыми словами End If.
7.	В инструкцию Select Case можно включить любое количество директив Case. В инструкцию Select Case можно также включить директиву Case Else, которая определит направление выполнения программы в том случае, если ни одна из директив Case “не сработает”, т.е. не выполнится ни одно из условий, заявленных директивами Case. Директива Case Else должна стоять последней в списке директив инструкции Select Case перед ключевыми словами End Select.
8.	Используйте инструкцию Select Case в ситуациях, когда нужно рассмотреть более трех-четырех случаев. Это упростит написание, чтение и понимание вашей программы.
9.	Для преждевременного выхода из процедуры используйте инструкцию Exit Sub, а для преждевременного выхода из функции — инструкцию Exit Function.
10.	Ключевое слово End, стоящее на строке в одиночестве, завершает выполнение всех VBА-инструкций, т.е. останавливает выполнение целой программы. При этом значения всех переменных теряются.
И. Аргумент Buttons функции MsgBox используется в следующих целях.
а)	для задания количества и типа кнопок, отображаемых в окне вывода сообщений;
Ь)	для задания значков Windows (Информация, Запрос, Предупреждение и Опасность), отображаемых в окне вывода сообщений;
с)	для задания кнопок, выбранных по умолчанию в окне вывода сообщений.
В языке VBA предусмотрено несколько встроенных констант (они перечислены на обложке книги и в электронном справочнике по VBA), которые способны облегчить процесс задания значений для аргумента Buttons.
12. Значение, возвращаемое функцией MsgBox, означает, какую кнопку выбрал пользователь в данном диалоговом окне. В языке VBA предусмотрено несколь
836
Приложение
ко встроенных констант (они перечислены на обложке книги и в электронном справочнике по VBA), которые способны облегчить интерпретацию значений возврата функции MsgBox.
Упражнения
1. Ниже приведен исправленный вариант листинга. Проблема, связанная с инструкцией Select Case в этой процедуре, кроется в строках 12 и 14 и затрагивает директивы Case.
В первоначальной версии этой процедуры условие Case занимающее строку 12, проверяет, не попадает ли значение переменной Num в интервал 1 — 10. Такое условие позволяет числам между 0 и 1 не попадать в эту категорию обработки. А поскольку они не отвечают ни одному из имеющихся условий Case, то их обрабатывает директива Case Else. В исправленной версии строка 12 содержит другое условие проверки чисел, выбирая числа в интервале 0—10. И проблема исчезает.
Аналогичная ситуация сложилась в строке 14 исходного варианта процедуры. Здесь условие Case срабатывало только в том случае, если значение переменной Num лежало в пределах 11—20, не включая числа, лежащие между 10 и 11. В исправленном варианте строка 14 предлагает другой интервал проверки, выбирая числа в интервале 10—20.
Sub Case_Demo()
Dim sNum As String
Dim Num As Double
sNum = InputBox)"Введите число:”)
Num = CDbl(sNum)
Select Case Num
Case Is < 0
MsgBox "Num меньше 0"
Case 0 To 10
MsgBox "Число между 0 и 10"
Case 10 То 20
MsgBox " Число между 10 и 20"
Case Else
MsgBox " Число больше 20"
End Select
End Sub
2. В следующем листинге представлен один из возможных способов завершения этого упражнения.
Sub TeleDigits))
' отображает цифру телефонного номера, соответствующую
' букве алфавита, введенной пользователем.
Const tdTitle = "TeleDigits"
Dim Letter As String
Dim Msg As String
Letter = InputBox)prompt:="Введите одну букву " &
Приложение
837
"английского алфавита:", Title:=tdTiltle)
If Len(Trim)Letter)) = 0 Then
MsgBox prompt:="Операция ввода отменена.", Title:=tdTitle,
Buttons:=vbExclamation
Exit Sub
End If
If Len(Trim(Letter)) > 1 Then
MsgBox prompt:="Вы должны ввести один " & "символ - Отменено.", Title:=tdTitle, Buttons:=vbExclamation
Exit Sub
End If
Letter = UCase(Letter)
Msg = "Букве " & Letter & " соответствует цифра: "
Select Case Letter
Case "A" To "C"
MsgBox prompt:=Msg & "2", _ Title:=tdTitle, Buttons:=vblnformation
Case "D" To "F"
MsgBox prompt:=Msg & "3”, Title:=tdTitle, _ Buttons:=vblnformation
Case "G" To "I"
MsgBox prompt:=Msg & "4", Title:=tdTitle, Buttons:=vblnformation
Case "J" To "L"
MsgBox prompt:=Msg & "5", Title:=tdTitle, Buttons:=vblnformation
Case "M" To "0"
MsgBox prompt:=Msg & "6", Title:=tdTitle, Buttons:=vblnformation
Case "P", "R", "S"
MsgBox prompt:=Msg & "7",
Title:=tdTitle,
Buttons:=vblnformation
Case "T" To "V"
MsgBox prompt:=Msg & "8", Title:=tdTitle, Buttons:=vblnformation
Case "W" To "Y"
MsgBox prompt:=Msg & "9”, Title:=tdTitle, Buttons:=vblnformation
Case Else
838
Приложение
MsgBox prompt Никакая цифра не " &
" соответствует букве " & Letter, Title:=tdTitle, Buttons:=vbExclamation
End Select
End Sub
В строке 27 для упрощения последующих проверок выполняется преобразование в прописную букву символа, хранящегося в переменной Letter. В строке 29 готовится стандартная строка, используемая окнами сообщений ниже.
Наконец, строки 31-68 занимает инструкция Select Case, предназначенная для выяснения буквы, введенной пользователем. С помощью первых восьми проверок условий Case тестируется символ, содержащийся в переменной Letter, и определяется, в какой диапазон символов он попадает, а затем отображается сообщение, соответствующее этой цифре. Обратите внимание на то, что все условные выражения Case (кроме одного) используют для установки диапазона символов ключевое слово То и что в условных выражениях участвуют только прописные буквы. Это снимает с наших плеч необходимость беспокоиться насчет дополнительного сравнения строчных букв.
Особое внимание следует обратить на строку 52, которая содержит единственное в этой процедуре условное выражение Case, предлагающее вместо диапазона список букв для сравнения со значением переменной Letter. Поскольку для буквы “Q” не предусмотрено “телефонной” цифры, диапазон "Р-S". который включает букву “Q”, использовать нельзя, поэтому вместо диапазона нужно перечислить все возможные буквы в отдельности.
Если символ в переменной Letter не совпадет ни с одним из восьми выражений Case, VBA выполнит директиву Case Else (см. строки 64—67). С помощью этих инструкций просто отображается окно сообщений о том, что для данного символа нет соответствующей цифры.
День 9-н
Тест
1.	Определенный цикл всегда повторяется фиксированное число раз. Неопределенный цикл повторяется неопределенное число раз в зависимости от условий, возникающих во время выполнения цикла.
2.	Нет. Цикл For... Next является фиксированным циклом и всегда выполняется заданное число раз, хотя с помощью инструкции Exit For цикл For можно завершить “досрочно”.
3.	Если логическое выражение для определяющего условия значится в верхней части цикла, то VBA проверяет это условие перед выполнением цикла. Если же логическое выражение для определяющего условия находится в нижней части цикла, то VBA проверяет это условие после выполнения цикла.
4.	При использовании ключевого слова While VBA выполняет цикл до тех пор, пока определяющее условие равно значению True, и прекращает выполнение цикла, когда определяющее условие становится равным значению False. При использовании ключевого слова Until VBA выполняет цикл до тех пор, пока определяющее условие не станет равным значению True, т.е. VBA выполняет цикл до тех пор, пока определяющее условие равно значению False, и прекращает выполнение цикла, когда определяющее условие становится равным значению True.
Приложение
839
5.	Управляемый счетчиком цикл — это цикл Do, который выполняется до тех пор, пока значение счетчика остается выше или ниже заданного предела. По своей сути управляемый счетчиком цикл Do очень похож на цикл For... Next. Однако цикл Do можно использовать везде, где счетчик инкрементируется через нерегулярные интервалы.
6.	Управляемый событиями цикл Do представляет собой цикл, у которого определяющее условие становится равным True или False в зависимости от событий, наступающих при выполнении цикла, например, когда пользователь вводит определенное значение.
7.	Управляемый признаком цикл — это цикл Do, который в качестве определяющего условия использует переменную типа Boolean. Значение переменной признака можно изменять с помощью программных инструкций как до начала, так и внутри цикла. И именно от нового значения переменной признака будет зависеть продолжение выполнения этого цикла.
Упражнения
1.	Проблема здесь в том, что пользователь не может ввести значение, которое остановило бы цикл. Функция Excel Application.InputBox, используемая с необязательным аргументом Туре и с указанием, что пользователь должен вводить только числовые данные, запрещает пользователю ввести такую строку, как exit. Следовательно, определяющее условие для такого цикла никогда не перестанет быть равным True и такой цикл будет выполняться бесконечно.
Исправить цикл в этой процедуре можно двумя способами. В первом варианте приведенного ниже листинга определяющее условие (строка 9) было изменено таким образом, чтобы цикл можно было завершить, введя значение 0. Немного поэкспериментировав с функцией Application.InputBox, вы могли бы заметить, что она возвращает значение 0, если пользователь отменяет диалоговое окно.
Sub GetNumber))
'при получении от пользователя числовых значений цикл работает бесконечно
Dim Num
Do
Num = Application.1приЬВох(рготрЬ:="Введите число:”, Type:=l)
Loop Until Num = 0
MsgBox "Ввод чисел завершен."
End Sub
Второй вариант исправления изъяна в этом цикле состоит в использовании вместо функции Excel Application.InputBox функции VBA InputBox (строка 7). Однако это решение проблемы может подойти не во всех случаях (как первый вариант), поскольку оно означает, что вам придется отказаться от контроля ввода только числовых значений, обеспечиваемого аргументом Туре функции Excel Application.InputBox. (В следующем уроке описано, как можно написать собственную программу контроля вводимых данных.)
Sub GetNumber))
'при получении от пользователя числовых значений цикл работает бесконечно
840
Приложение
Dim Num
Do
Num = InputBox(prompt:="BBeflHTe число:")
Loop Until Num = “exit"
MsgBox "Ввод чисел завершен."
End Sub
2.	Для достижения желаемых результатов в этом упражнении можно использовать несколько различных решений. В следующем листинге показано одно из них.
Sub Three_Words()
'Получает от пользователя три слова и собирает их 'в одно предложение.
Const ITitle = "Три слова"
Dim uStr As String 'строка для данных, вводимых пользователем
Dim Sentence As String 'строка для выходного предложения
Dim k As Integer	'счетчик цикла
Sentence = ""
For k = 1 То 3
uStr = InputBox(prompt:="Введите слово:", title:=lTitle)
If Trim(uStr) = "" Then
MsgBox prompt := "Ввод слова отменен.", Title:=lTitle Exit For
Else
Sentence = Sentence & " " & Trim(uStr)
End If
Next k
'если переменная uStr пуста, то последний элемент ввода отменяется
If Trim(uStr) <> "“ Then
MsgBox prompt:="Вы ввели следующее предложение:"
& Chr(13) & Chr(13) & Sentence, Title:=lTitle
End If
End Sub
В строке 12 начинает работу цикл For... Next, настроенный на выполнение трех проходов. Инструкции, предназначенные для получения слова от пользователя, находятся в теле цикла For... Next, поэтому пользователю предлагается ввести слово три раза.
В строке 13 вызывается функция InputBox, чтобы присвоить переменной uStr слово, введенное пользователем. Строки 14—19 занимает инструкция If.. .Then.. .Else, выполняющая анализ того, что ввел пользователь. Если строка uStr пуста (или состоит только из символов пробелов), инструкции, расположенные в строках 15 и 16, отображают сообщение, констатирующее факт отмены составления предложения, и организуют выход из цикла For. Если переменная uStr содержит не пустую строку, а некоторое слово, то в строке 18 выполняется конкатенация слова, введенного пользователем, со строкой, хранимой в переменной Sentence, включая пробелы между словами. Собирая
Приложение
841
предложение с помощью цикла, можно обойтись использованием только одной строковой переменной. При этом функция Trim применяется несколько раз, чтобы избавиться от ненужных ведущих и хвостовых пробелов.
Строки 23—27 занимает инструкция If...Then, которая проверяет переменную uStr. Если переменная uStr не пуста, инструкция MsgBox в строках 24—26 отображает собранное предложение. Если же переменная uStr пуста, то это значит, что пользователь отменил последнюю операцию ввода и собранное предложение отображать не нужно.
3.	Вот как выглядит одно из возможных решений.
Sub GetSentence()
'В цикле получает от пользователя слова, собирает их в одно 'предложение. Сборка слов завершится, 'когда пользователь введет точку (.)
Const ITitle = "Построение предложения"
Dim uStr As String 'строка для слов, принимаемых от пользователя Dim Sentence As String 'строка для итогового предложения
Sentence = ""
Do
uStr = 1приЬВох(рготрЬ:="Введите слово ('.' для завершения):", Title:=lTitle)
If Trim(uStr) = "" Then
MsgBox prompt:="Ввод слов отменяется.", Title:=lTitle Exit Do
Else
If Trim(uStr) = "." Then
Sentence = Sentence & Trim(uStr)
Else
Sentence = Sentence & " " & Trim(uStr) End If End If
Loop Until Trim(uStr) = "."
'если uStr пуста, то ввод последнего элемента отменяет сборку
If Trim(uStr) <> "" Then
MsgBox prompt:="BH ввели следующее предложение:"
& Chr(13) & Chr(13) & Sentence, Title:=lTitle
End If
End Sub
Цикл Do начинается в строке 12. Поскольку определяющее условие стоит в конце цикла, то VBA немедленно приступает к выполнению тела цикла, начиная со строки 13.
В строке 13 вызывается функция InputBox, чтобы присвоить введенное пользователем слово переменной uStr. Строки 15—24 занимает пара вложенных инструкций If...Then. Внешняя инструкция If...Then, подобно такой же инструкции из упражнения 9.2, анализирует то, что ввел пользователь. Если строка uStr пуста (или состоит только из символов пробелов), инструкции, расположенные
842
Приложение
в строках 16 и 17, отображают сообщение, констатирующее факт отмены составления предложения, и организуют выход из цикла, на этот раз используя инструкцию Exit Do, поскольку в данном случае мы имеем дело с циклом Do.
Если переменная uStr не пуста, VBA выполняет директиву Else внешней инструкции If...Then (см. строки 19~23), которая содержит еще одну инструкцию If.. .Then. Эта внутренняя инструкция If.. .Then была включена в основном для косметических целей. Если пользователь ввел точку (.), то она “сцепляется” со строкой в переменной Sentence без пробела. Если это была не точка, содержимое переменной uStr присоединяется к содержимому переменной Sentence с пробелом между двумя словами.
Как только пользователь введет одну точку (.), условие цикла в строке 25 становится равным значению True, и VBA прекращает выполнение цикла.
4.	Ниже представлена законченная функция IsBookOpen и процедура тестирования этой функции.
Function IsBookOpen(bName As String) As Boolean
'Возвращает True, если рабочая книга с именем sName уже открыта
Dim aBook As Object
IsBookOpen = False 'предположим, что книга не найдена
'опрашиваем все книги, сравниваем имя каждой книги
' с содержимым переменной bName путем сравнения строк
For Each aBook In Workbooks
If (StrComp(aBook.Name, bName, 1) = 0) Then
IsBookOpen = True 'если имена книг совпадают, возвращаем True Exit For
End If
Next aBook
End Function
Sub TestIsBookOpen()
'тестирование функции IsBookOpen
Dim uStr As String
uStr = inputBoxf"Введите имя рабочей книги (включите " & "расширение .XLS):")
If IsBookOpen(uStr) Then
MsgBox "Книга '" & uStr & "' ОТКРЫТА."
Else
MsgBox "Книга '" & uStr & "' HE ОТКРЫТА."
End If
End Sub
Эта функция и тестирующая процедура работают точно так же, как и функция SheetExists, представленная в листинге 9.9, за исключением того, что здесь используется коллекция Workbooks, а не коллекция Sheets.
5.	Ниже представлена функция SVal и процедура ее тестирования как один из возможных вариантов решения данной задачи.
Приложение
843
Function SVal(ByVal iStr As String) As Double
'Удаляет нецифровые символы из аргумента iStr, а затем 'возвращает числовой эквивалент строки. Возвращает О, 'если введенная строка не содержит ни одной цифры или 'содержит более одного десятичного знака
Dim oStr As String 'рабочая строка для результата
Dim k As Long	'счетчик цикла
Dim PCount As Long 'счетчик числа точек (.)
iStr = Trim(iStr) 'удаляет пробелы из введенной строки oStr = "" 'рабочая строка должна быть пуста
PCount = 0 'счетчик числа точек начинает работать с О
For k = 1 То Len(iStr)
'к-ый символ является цифрой или десятичным знаком?
If (InStrfl, "0123456789.", MidfiStr,к,1),1) о 0) Then oStr = oStr & MidfiStr, к, 1) 'копируем символ
'отслеживаем количество десятичных точек (.)
If MidfiStr, k, 1) = "." Then PCount = PCount + 1
End If
Next k
If (PCount > 1) Or (oStr = "") Then
SVal = 0 'возвращаем нуль в качестве результата
Else
SVal = Val(oStr)
End If
End Function
Sub Test_Sval()
'Эта процедура тестирует функцию SVal
Dim uStr As String
Dim Num As Double
uStr = InputBox("Введите строку: ")
Num = SVal(uStr)
MsgBox "Вы ввели: " & uStr & " которая преобразована в: " & Num
End Sub
В строке 14 начинается цикл For...Next, который обрабатывает столько символов, сколько содержится в строке iStr.
После завершения цикла переменная oStr будет содержать копию содержимого переменной iStr за исключением любых нецифровых символов. С помощью инструкции If...Then в строках 23—28 проверяются значения переменных PCount и oStr. Если значение переменной PCount больше 1, значит, строка, введенная пользователем, содержит слишком много десятичных знаков. Если переменная oStr все еще пуста, значит, в строке, введенной пользователем, не содержится ни одного цифрового символа. Если хотя бы одно из этих условий равно True, входную строку нельзя преобразовать в число, и в этом случае в качестве результата функции возвращается нулевое значение.
844
Приложение
Если ни одно из этих условий не равно True, VBA в строке 26 выполняет директиву Else, которая в качестве результата функции Sval назначает результат преобразования строки oStr в число, реализуемого с помощью функции VBA Vai. Строки 31—40 занимает процедура, предназначенная для тестирования функции Sval.
День 10-0
Тест
1.	Все информационные функции VBA, которые начинаются со слова is, используются для определения того, имеет ли данная переменная конкретный тип данных.
2.	По всей вероятности, здесь нужно использовать такие функции, как IsNumeric, IsDate и IsObject.
3.	Функции TypeName и VarType предоставляют самую специальную информацию о данных, содержащихся в конкретной переменной.
4.	Используйте функцию TypeName для возврата строки, содержащей конкретное имя объекта, а функция VarType может лишь сообщить, поддерживает ли данный объект технологию автоматизации.
5.	Функция IsMissing сообщает, был ли включен в список аргументов определенный необязательный аргумент типа Variant при вызове некоторой функции. Функция IsMissing всегда возвращает значение False, если проверяемый необязательный аргумент имеет не тип Variant, а какой-либо другой тип.
6.	Значение Nothing говорит о том, что тестируемая объектная переменная еще не была установлена для ссылки на какой-либо объект. Значение Unknown говорит о том, что функции TypeName не удалось определить конкретный тип анализируемого объекта.
7.	Значение Empty говорит о том, что переменной типа Variant еще не было присвоено какое-либо значение. VBA присваивает это значение каждой переменной Variant в момент ее создания.
8.	Значение Null говорит о том, что переменная типа Variant не содержит действительных данных, причем VBA автоматически не присваивает переменным это значение. Только программист может присвоить переменной типа Variant значение Null.
9.	Предусмотрительное программирование — это всего лишь название, которое было дано технологии обеспечения контроля данных в программе, чтобы избежать возникновения динамических ошибок (ошибок времени выполнения) или обрабатывать динамические ошибки более элегантным способом, чем это возможно лишь встроенными средствами VBA.
10.	Назначение ключевого слова Static — сообщить VBA, значения каких переменных в функции или процедуре вы хотите сохранять между вызовами функций или процедур. В отличие от других переменных уровня процедуры, VBA не очищает содержимое статической переменной при завершении процедуры или функции, а сохраняет ее значение.
11.	Для объявления статической переменной вместо ключевого слова Dim используйте ключевое слово Static. Если вы хотите, чтобы все переменные в процедуре или функции были статическими, поместите ключевое слово Static в начале объявления этой функции или процедуры.
Приложение
845
Упражнвння
Начиная с этой главы, в раздел “Упражнения” больше не будет включаться анализ программных решений, за исключением редких случаев, когда нельзя будет обойти вниманием особо интересный или полезный момент в программе. Несмотря на то что в данном приложении вы по-прежнему будете находить программные решения задач и упражнений, предложенных в соответствующих главах книги, имейте в ввиду, что одна и та же задача часто может иметь множество равнозначных решений. Не следует огорчаться, если ваше решение отличается от варианта, предложенного здесь. Наши решения помогут вам в случаях, если вы споткнетесь о какую-нибудь задачку. Кроме того, всегда полезно узнать, помимо своих собственных, и другие возможные решения.
1. Ваша функция должна иметь примерно следующий вид.
Function IsMasterCard (CardNum As String) As Boolean
'анализирует строку в переменной CardNum, и возвращает True, если 'строка представляет действительный номер кредитной карточки 'Mastercard. Действительные номера Mastercard всегда начинаются 'с цифры 5 и состоят из четырех групп, состоящих, в свою очередь, из 'четырех цифр, а группы отделяются друг от друга дефисом.
IsMasterCard = (CardNum Like "5Ш(-]НН[-]НН[-]НН")
End Function
Sub Test_IsMasterCard()
'Эта процедура тестирует функцию IsMasterCard.
Dim uStr As String
Do
uStr = InputBox("Введите номер Mastercard " & _ "(включите дефисы):")
MsgBox isMasterCard(uStr)
Loop Until Len(Trim(uStr)) = 0
End Sub
Ваша процедура должна иметь примерно следующий вид.
Sub Get_MasterCardNum()
'Получает от пользователя номер кредитной карточки Mastercard и
'не выходит из цикла до тех пор, пока пользователь не введет 'действительный номер кредитной карточки MasterCard.
Dim uStr As String
Dim Ans As Integer
Do
uStr = InputBox("Введите номер Mastercard " &
"(включите дефисы):", "Ввод номера кредитной карточки")
If Len(Trim(uStr)) = 0 Then
Ans = MsgBox(prompt:="0тменить ввод номера кредитной карточки?", Title:="BBOfl карточки - Подтвердите отмену", Buttons.,=vbQuestion + vbYesNo)
If Ans = vbYes Then Exit Sub
846
Приложение
Elself Not IsMasterCard(uStr) Then
MsgBox prompt:="BH ввели неверный " &
"номер Mastercard." & Chr(13) &
"Попытайтесь еще раз.",
Т111е:="0шибка: Неверный номер карточки ", _ Buttons:=vbExclamation
End If
Loop Until IsMasterCard(uStr)
'здесь могут стоять инструкции, предназначенные для 'сохранения или использования номера Mastercard.
End Sub
День 11-0
Teem
1.	Нестандартный тип — это тип данных, который создается путем комбинации элементов, основанных на встроенных типах данных VBA. В нестандартном типе данных объединяются вместе несколько различных, но родственных типов данных.
2.	Определение нестандартного типа данных открывается ключевым словом Туре.
3.	Объявления нестандартных типов данных нужно размещать в области объявлений модуля на уровне модуля, т.е. перед объявлениями каких бы то ни было процедур или функций.
4.	Нестандартный класс создается путем добавления к вашему проекту модуля класса с последующим созданием процедур, определяющих свойства и методы вашего класса.
5.	Свойства класса определяются путем написания процедур Property Get и Property Let. Обычно процедуры определения свойств пишутся парами: процедура Get считывает значение свойства, а процедура Let присваивает значение свойству.
6.	Чтобы создать для нестандартного объекта свойство, которое можно как читать, так и устанавливать, необходимо написать пару Property-процедур: процедуру Property Get для считывания значения свойства и процедуру Property Let для присваивания значения свойству. Обе процедуры должны иметь одно и то же имя. Кроме того, тип данных значения возврата процедуры Property Get должен совпадать с типом данных аргумента процедуры Property Let.
7.	Методы класса определяются путем создания в модуле класса процедур Sub и Function. Каждая процедура Sub и Function, которую вы пишете в модуле класса, становится методом объекта.
8.	Модуль класса нужно сразу же переименовать, поскольку имя модуля класса является и именем самого класса.
9.	Только в том случае, если свойство Instancing модуля класса установлено равным значению PublicNoCreate. Однако при использовании этого значения свойства вам запрещается создавать новые экземпляры вашего нестандартного объекта. Обычно для свойства Instancing используется значение Private, которое разрешает построение желаемого числа экземпляров созданного вами объекта, но ограничивает доступность этого объекта рабочей книгой, содержащей модуль класса, определяющий ваш объект.
Приложение
847
10.	Да. После определения каждого свойства и метода для вашего объекта (путем написания соответствующих VBA-процедур и VBA-функций в модуле класса) все новые свойства и методы появятся в окне Object Browser.
Упражнения
1. Ваш новый нестандартный тип для рассылочной ведомости (списка адресов) должен выглядеть примерно так, как показано ниже (элементы State и Zip объявлены как строки фиксированной длины, чтобы они автоматически усекались до получения корректной длины).
Option Explicit
Type MailAddress
FName As String
LName As String
Company As String
Street As String
City As String
State As String * 2
Zip As String * 5
End Type
Const MailTitle = "Ввод данных в рассылочную ведомость"
Sub Enter_MailingData()
'Вводит информации для рассылочной ведомости. Она вызывает
'функцию Get_MailAddress для получения данных об одном почтовом
'адресе, а затем в цикле передает эти данные в рабочий лист до
'тех пор, пока не останется непереданных данных.
Dim Person As MailAddress
Dim Done As Boolean
Dim RNum As Integer
Worksheets("Лист1").Select ' помещаем данные в лнст2 (Sheet2)
RNum = О
Done = False
Do Until Done
Person = Get_MailAddress
If Len(Trim(Person.LName)) = 0 Then 'больше данных Нет
Done = True
Else	'сохраняем данные в рабочем листе
RNum = RNum + 1
With Person
Cells(RNum, 1).Value = .LName
Cells(RNum, 2).Value = .FName
Cells(RNum, 3).Value = .Company
Cells(RNum, 4).Value = .Street
Cells(RNum, 5(.Value = .City
Cells(RNum, 6).Value = .State
Cells(RNum, 7).Value = .Zip
End With
848
Приложение
End If
Loop
MsgBox prompt:="Ввод данных завершен. " & RNum &
"записей введено.", Title:=MailTitle End Sub
Function Get_MailAddress() As MailAddress 'получает все данные для одного элемента списка адресов 'и возвращает их в нестандартный тип MailAddress
Dim Item As MailAddress
Dim Tmp As Variant
With Item
Do	'получаем фамилию человека
.LName = InputBox(prompt:=“BBeflHTe ФАМИЛИЮ " & "человека:”, Title:=MailTitle)
If Len(Trim(.LName)) = 0 Then	'Отмена?
Tmp = МздВох(рготрЬ:="Завершить ввод данных в список адресов?", Title:=MailTitle, Buttons:=vbQuestion + vbYesNo)
If Tmp = vbYes Then .LName = "" Get_MailAddress = Item Exit Function
End If
End If
Loop While Len(Trim(.LName)) = 0
Do	'получаем имя человека
•FName = 1приЬВох(рготрЬ:="Введите ИМЯ," & "человека:", Title:=MailTitie)
Loop While Len(Trim(.FName)) = 0
Do	'получаем название компании
.Company = InputBox(prompt:="Введите название " & "компании:", Title:=MailTitle)
Loop While Len(Trim(.Company)) = 0
Do	'получаем номер улицы
•Street = InputBox)prompt:="Введите номер " & " и название улицы:", Title:=MailTitle)
Loop While Len(Trim(.Street)) = 0
Do	'получаем название города
.City = lnputBox(prompt:="BBeflHTe название города:", Title:=MailTitle)
Loop While Len(Trim(.City)) = 0
Do	'получаем название штата
.State = InputBox(prompt:="Введите 2 буквы названия" & "штата:", Title:=MailTitle)
Приложение
849
Loop While Len(Trim(.State)) = 0
Do	'получаем почтовый индекс
.Zip = 1приЬВох(рготрЬ:="Введите 5 цифр почтового индекса:", Title:=MailTitle)
Loop While Len(Trim(.Zip)) = 0
End With
Get_MailAddress = Item 'возвращаем заполненный MailAddress
End Function
День 12-0
Teem
1.	VBA сначала ищет процедуру в текущем модуле, а затем просматривает другие модули в текущем проекте. Затем VBA просматривает все проекты, указанные в проекте, вызывающем процедуру.
2.	Используйте окно Object Browser. В диалоговом окне Макрос перечисляются только процедуры.
3.	Любой проект может быть проектом библиотеки. Проект библиотеки создается путем размещения в проекте процедур и функций. Затем следует убедиться в доступности этого проекта.
4.	Обеспечить автоматическое открытие проекта можно либо разместив его файл в папке автозагрузки Excel, либо путем создания ссылки на проект библиотеки.
5.	Чтобы сделать процедуры и функции библиотечного проекта доступными для VBA-программы в текущем проекте, установите ссылку на этот проект библиотеки.
6.	Свойство StartupPath возвращает имя папки автозагрузки приложения Excel. Это свойство предназначено только для чтения, и его значение изменить нельзя.
7.	Свойство AltStartupPath возвращает имя альтернативной папки автозагрузки приложения Excel. Это свойство можно и считывать, и писать, т.е. его можно использовать для изменения альтернативной папки начального запуска приложения Excel.
8.	Для создания ссылки используйте команду редактора Visual Basic Tools-References.
9.	Область видимости Private означает, что доступность элемента ограничена модулем, в котором он объявлен. Область видимости Public означает, что элемент доступен для любого модуля.
10.	Объявление Option Private Module ограничивает доступность элементов в модуле, который содержит эту директиву, проектом, содержащим этот модуль.
11.	Ключевое слово Private приводит к тому, что область видимости таких элементов, как процедура или функция, которые обычно имеют открытую область видимости, становится закрытой, т.е. сужается до уровня модуля. Ключевое слово Public приводит к тому, что область видимости таких элементов уровня модуля, как переменная и константа, которые обычно имеют закрытую область видимости, становится открытой.
12.	Нет. Локальные переменные, объявленные внутри процедуры или функции, недоступны вне этой процедуры или функции. Если вам нужно сделать некоторое значение в одной процедуре доступным для другой процедуры, передайте его второй процедуре в качестве аргумента.
850
Приложение
13.	Для передачи информации процедуре или для возврата значения из процедуры.
14.	Процедуры, требующие передачи аргументов, не попадают в список диалогового окна Макрос. Если же все аргументы процедуры являются необязательными, то такая процедура попадет в список процедур диалогового окна Макрос. В окне Object Browser перечисляются все доступные процедуры независимо от того, какими являются их аргументы: обязательными или нет.
15.	По ссылке.
Упражнения
1.	После внесения изменений листинг приобрел следующий вид.
Option Explicit
Option Private Module
Private Type InvoiceHeader
CustomerName As String
CustomerNumber As Integer
InvoiceNumber As Long
InvoiceDate As Date
End Type
Public Spinning As Boolean
Public Currentinvoice As Long
Public InvoiceList() As InvoiceHeader
Private Sub Fetchinvoice()
'	тело процедуры
End Sub
Private Sub ShutDown() ' тело процедуры
End Sub
Private Sub LocatelnvoiceBodyj) ' тело процедуры
End Sub
Private Sub DisplayLineltemsj)
'	тело процедуры
End Sub
Private Function InvoiceTotal() As Currency
'	тело процедуры
End Function
Все элементы, которые обычно имеют закрытую область видимости, теперь стали открытыми, а все элементы, которые обычно имеют открытую область видимости, теперь стали закрытыми.
Приложение
851
2.	На это упражнение ответа нет. Изучите листинг упражнения 3 урока 12 в качестве примера VBA-программы, которая может служить результатом такой разработки.
3.	Ваша законченная программа может иметь следующий вид (этот листинг содержит один полный модуль).
Option Explicit
Sub BalanceCheckBook()
Dim Balance As Currency ' баланс чековой книжки
Dim Transaction As Currency ' сумма транзакции
Dim Done As Boolean	' признак окончания цикла транзакций
Get_StartBalance Amt:=Balance 'получаем остаток иа начало периода
MsgBox "Ваш остаток на начало периода составляет: $” & Balance
Do	' получаем транзакции
Get_Transaction Amt: transaction, Lastitem:=Done
If Not Done Then
Balance = Balance + Transaction
Display NewBal Bal:=Balance, Amt:transaction
End If
Loop Until Done
'Отображаем остаток на конец периода Display_NewBal Bal:=Balance End Sub
Sub Get_StartBalance(Amt As Currency)
'получает от пользователя остаток на начало периода и возвращает его в Amt
Dim istr As String
Amt = 0
Do
iStr = InputBox("Введите остаток на начало периода:")
If Len(Trim(istr)) = 0 Then 'ввод отменен?
If ConfirmCancel("Вы хотите завершить программу?") Then End	'останов всей программы
End If
Elself Not IsNumeric(iStr) Then
MsgBox prompt:="Пожалуйста, введите только число.",
Buttons:=vbExclamation
End If
Loop Until IsNumeric(istr)
Amt = CCur(iStr)
End Sub
Sub Get_Transaction(Amt As Currency, Lastitem As Boolean)
'получает от пользователя одну транзакцию и указывает, последняя ли
852
Приложение
'это транзакция, путем установки переменной Lastitem равной True.
Dim iStr As String
Amt = 0
Lastitem = False 'предполагается, что это не последний элемент Do
iStr = InputBox("Введите сумму транзакции: ")
If Len(Trim(iStr)) = 0 Then	'отменено?
If ConfirmCancelС Завершение ввода транзакций?") Then Lastitem = True
Exit Sub
End If
Elself Not IsNumeric(iStr) Then
MsgBox ргошрЬ:="Пожалуйста, вводите только числа. " &
"Вводите отрицательные числа для дебетов, " & "положительные числа - для кредитов.", _
Buttons:=vbExclamation
End If
Loop Until IsNumeric(iStr)
Amt = CCur(iStr)
End Sub
Sub Display NewBal(Bal As Currency, Optional Amt) 'Отображает баланс. Если включен необязательный аргумент Amt, 'то предполагается, что он содержит сумму транзакции
Dim Msgl As String 'сообщение для отображения, в двух частях
Dim Msg2 As String
Dim Btns As Integer 'кнопки для окна сообщений
Msgl = ""
Msg2 = "Ваше "
If IsMissing(Amt) Then 'присутствует ли аргумент Amt?
Msg2 = Msg2 & "Завершение "
Else	'транзакция является кредитом или дебетом?
If Amt < 0 Then Msgl = "Дебет” Else Msgl = "Кредит"
Msgl = Msgl & " сумма равна: $" & Abs(Amt) & vbCr i vbCr
End If
Msg2 = Msg2 & "Остаток равен: $" i Bal
'при необходимости добавляем предупреждение
If Bal <100 Then
Btns = vbExclamation
Msg2 = Msg2 & vbCr & vbCr &
"ВНИМАНИЕ, ваш остаток очень мал! "
Else
Btns = 0
End If
MsgBox prompt:=Msgl & Msg2, Buttons:=Btns
End Sub
Приложение
853
Function ConfirmCancel(pStr As String) As Boolean
'Возвращает TRUE или FALSE в зависимости от ответа
'пользователя на вопрос, содержащийся в аргументе pStr.
Dim Ans As Integer
Ans = MsgBox(prompt:=pStr, Buttons:=vbQuestion + vbYesNo)
If Ans = vbYes Then
ConfirmCancel = True
Else
ConfirmCancel = False
End If
End Function
День 13-й
Teem
1.	Архивный, папка, скрытый, обычный, только чтение, системный, метка тома.
2.	Путем их сложения. Байт атрибутов файла определяется как сумма всех его атрибутов.
3.	Для считывания атрибутов файла используйте функцию GetAttr. Эта функция возвращает одно число, содержащее сумму атрибутов файла.
4.	Нет. С помощью функции SetAttr присвоить файлу атрибуты папка или метка тома нельзя. Чтобы создать каталог (папку), используйте инструкцию MkDir. В VBA не предусмотрен способ создания или удаления дисковых меток тома.
5.	Функция GetOpenFilename отображает диалоговое окно, которое по своему внешнему виду и поведению аналогично диалоговому окну Excel Открытие документа. Функция GetSaveAsFilename отображает диалоговое окно, которое по своему внешнему виду и поведению аналогично диалоговому окну Excel Сохранение документа.
6.	Функция Dir используется в два этапа. Во-первых, она вызывается с аргументами, чтобы найти первый файл, совпадающий с указанным. Затем функция Dir вызывается без аргументов и при каждом очередном вызове предпринимается попытка найти очередной файл, совпадающий с указанным. Если таковые больше не обнаруживаются, функция Dir возвращает пустую строку.
7.	Используйте функцию CurDir, которая возвращает строку, содержащую полный путь текущей папки, включая букву дискового устройства.
8.	Инструкция FileCopy.
9.	Для перемещения файла из одной папки в другую на том же самом диске используйте инструкцию Name. Инструкцию Name нельзя использовать для перемещения файла на другой диск. Для этого воспользуйтесь инструкцией FileCopy, которая позволяет копировать файлы.
854
Приложение
Унражнвния
1.	В варианте (А) пропущена ссылка на объект Application. В варианте (В) пропущена разделительная точка перед именем метода GetSaveAsFilename. В исправленном виде эти программные фрагменты выглядят следующим образом.
(А)
fName = Application.GetOpenFilename
(В)
With Application fName = .GetSaveAsFilename
End with
2.	Здесь была предпринята попытка использовать инструкцию Name для перемещения файла на другой диск, что невозможно. Вместо этого для перемещения файла используйте следующие команды.
Filecopy "C:\EXAMPLES\SALES.XLS", "A:\SALES.XLS"
Kill "C:\EXAMPLES\SALES.XLS"
3.	Ваша функция IsDiskFolder может иметь следующий вид. Function IsDiskFolder(dirName As String) As Boolean 'возвращает True, если dirName находится на диске
If (Dir(dirName, vbDirectory) <> "") Then IsDiskFolder = True
Else IsDiskFolder = False End If End Function
4.	Ваша процедура может выглядеть следующим образом.
Sub SwitchDir() 'переводит в новую папку, 'создавая ее, если таковая еще не существует.
Const iTitle = "Смена каталога"
Dim iName As String
Dim Ans As Integer
iName = 1приЬВох(рготрЬ:="Введите имя каталога: ", Title:=iTitle)
If Trim(iName) = "" Then Exit Sub
If IsDiskFolder(iName) Then ChDrive iName ChDir iName
Else
Ans = MsgBox(prompt:=iName & " не существует. " & _ vbCr & vbCr & "Создать ее?",
Title:=iTitle, Buttons:=vbQuestion + vbYesNo) If Ans = vbYes Then
MkDir iName
Приложение
855
ChDrive iName
ChDir iName
End If
End If
End Sub
День 14-0
Teem
1.	Массив Lookup содержит 150 элементов (5 * 3 * 10), а массив Cube — 27 элементов (3 * 3 * 3). Количество элементов в массиве вычисляется путем перемножения числа элементов в каждом измерении.
2.	Значения, возвращаемые при различных вызовах функции.
(А)	1980
(В)	1
(С)	5
(D)	1990
(Е)	10
(F)	9
3.	Инструкция ReDim в строке 10 неверна. Нельзя использовать инструкцию ReDim для изменения типа данных элементов массива.
4.	Нет. даже использование инструкции Erase не позволяет изменить тип данных элементов массива.
5.	Да. VBA обращается с одним элементом массива подобно любой простой переменной.
6.	Да, линейный поиск будет работать корректно как на отсортированном, так и на неотсортированном массиве.
7.	На сортированном массиве вам следует использовать двоичный поиск, поскольку двоичный поиск использует преимущества упорядоченности элементов в массиве, в результате чего поиск выполняется быстрее и гораздо эффективнее. Двоичный поиск работает только на сортированных массивах. Использование двоичного поиска на неотсортированном массиве может привести к неверным результатам.
Упражнения
1.	Ваша процедура должна выглядеть примерно так, как показано ниже (обратите внимание на то, что процедура BubbleSort в этом модуле использует необязательный аргумент, с помощью которого задается восходящий или нисходящий способ сортировки).
Option Explicit
Option Base 1
Private Const FORMATSTR As String = "####"
Sub Sorter2()
Const NUM_ROWS As Integer =15
Const NUM_C0LS As Integer = 4
856
Приложение
Const iTitle As String = "Информация процедуры Sorter2"
Const Sorted As Integer = 2
Dim NumTable(NUM_R0WS, NUM_C0LS) As Integer
Dim I As Integer
Dim J As Integer
Dim oldSheet As String
oldSheet = ActiveSheet.Name 'сохраняем текущий лист
Sheets("Лист1").Select 'переключаемся на новый лист
Randomize Timer 'задаем начальное число для генератора случайных чисел For I = 1 То NUM_ROWS 'заполняем массив случайными числами
For J = 1 То NUM_COLS
NumTable(I, J) = Int(Rnd * 1000) Next J Next I
DisplayArray NumTable	'отображаем массив элементов
MsgBox prompt:="Bce готово к началу сортировки.", Buttons:=vblnformation, _ Title:=iTitle
'используем восходящий способ сортировки
BubbleSort xArray:=NumTable, sCol:=SortCol, AscEnd:=True
DisplayArray NumTable 'отображаем элементы отсортироваииого массива MsgBox prompt:="MaccHB сейчас отсортирован в восходящем порядке.", _ Buttons:=vblnformation, Title:=iTitle
'используем нисходящий способ сортировки
BubbleSort xArray:=NumTable, sCol:=SortCol, AscEnd:=False
DisplayArray NumTable ' отображаем элементы отсортированного массива MsgBox prompt:=”MaccHB сейчас отсортирован в нисходящем порядке.", Buttons:= vblnformation, Title:=iTitle
Sheets(oldSheet).Select	'восстанавливаем рабочий лист
End Sub
Sub BubbleSort(xArray() As Integer, sCol As Integer, Optional AscEnd) 'сортирует в восходящем или нисходящем порядке, 'в зависимости от значения аргумента AscEnd Dim I As Integer, J As Integer
If IsMissing(AscEnd) Or _
TypeName(AscEnd) <> "Boolean" Then AscEnd = True
For I = LBound(xArray, 1) To UBound(xArray, 1) - 1
For J = I + 1 To UBoundfxArray, 1) If AscEnd Then
If xArrayfl, sCol) > xArrayfJ, sCol) Then _ SwapRow xArray, I, J
Else
Приложение
857
If xArray(I, sCol) < xArray(J, sCol) Then _ SwapRow xArray, I, J
End If
Next J Next I End Sub
Sub SwapRow(xArray() As Integer,
sRowl As Integer, _
sRow2 As Integer) 'переставляем местами элементы на строках sRowl и sRow2 Dim k As Integer
For k = LBoundfxArray, 2) To UBoundfxArray, 2)
Swap xArray(sRowl, k), xArray(sRow2, k) Next k
End Sub
Sub Swap(Il As Integer, 12 As Integer)
Dim temp As Integer temp =11 Il =12 12 = temp End Sub
Sub DisplayArray(xArr() As Integer)
Dim kl As Integer, k2 As Integer
For kl = LBoundfxArr, 1) To UBound(xArr, 1)
For k2 = LBound(xArr, 2) To UBound(xArr, 2)
Cellsfkl, k2).Value = Format(xArr(kl, k2), FORMATSTR) Next k2
Next kl End Sub
2.	Элементы в массиве А имеют следующие значения.
А(1)	содержит 2
А(2)	содержит 3
А(3)	содержит 5
А(4)	содержит 9
А(5)	содержит 17
А(6)	содержит 33
А(7)	содержит 65
А(8)	содержит 129
А(9)	содержит 257
3.	В этом листинге на самом деле есть две проблемы. Во-первых, цикл For...Next в строке 4 вызывает ошибку времени выполнения, поскольку элемент Projlncome(I-l) оказывается недействительным, когда I равно 1995: 1995 минус 1 равно 1994. Число 1994 лежит ниже нижнего предела индексов массива. Во-вторых, верхний предел цикла For также генерирует ошибку, поскольку число 2000 не является допустимым индексом для массива Proj Income — на этот раз нарушается верхний предел индексов массива.
858
Приложение
4.	В строке 2 массив X объявлен как статический массив. Инструкция ReDim в строке 10 вызывает динамическую ошибку, поскольку нельзя изменить размерность статического массива.
5.	Инструкция Option Base в строке 1 является дефектной, поскольку с директивой компилятора Option Base можно использовать только 0 или 1.
6.	Инструкция Option Base в строке 3 является дефектной, поскольку модуль может содержать только одну директиву Option Base.
7.	Инструкция Option Base в строке 2 является дефектной, поскольку директива Option Base должна стоять в модуле до объявления любых массивов.
День 15-0
Тест
1.	Точка останова размещается перед инструкцией, которая вызывает ошибку времени выполнения, или перед той инструкцией, которая, по вашему подозрению, вызывает логическую ошибку.
2.	Разместите несколько точек останова перед теми инструкциями, которые вызывают ошибки времени выполнения (или, по крайней мере, подозреваются в этом). Когда выполнение процедуры останавливается на точке останова, исследуйте значения переменных, а затем возобновите выполнение программы с нормальной скоростью до тех пор, пока не достигнете следующей точки останова. Такая схема позволит быстро перемещаться между проблемными участками.
3.	Нет. Вы можете узнать значение только отдельных элементов массива.
4.	Используйте команду Print в окне Immediate Window. Команда Print позволяет поддерживать различные аргументы для функций с целью установления корректности их работы.
5.	Макрос можно сбросить с помощью команды Run-Reset.
Упражнения
1.	Замена выражения Lo > Hi выражением Lo >= Hi в строке 58 листинга 15.2 внесет небольшой дефект в работу двоичного поиска. После выполнения этой замены программа двоичного поиска не сможет отыскать некоторые элементы, о которых точно известно, что они существуют в списке. Чтобы убедиться в этом, выполните предложенную замену, а затем попытайтесь найти число, стоящее в списке первым. Вы увидите, что после выполнения двоичного поиска вы ошибочно будете уведомлены о том, что этот элемент не найден. Понаблюдайте за выполнением своей программы с помощью отладчика, чтобы увидеть своими глазами, почему так важно завершать цикл только в том случае, когда нижняя граница поиска больше верхней границы.
2.	Внесение этого изменения заставит программу двоичного поиска выполняться бесконечно при каждой попытке изменить границы поиска в верхней половине текущей области поиска. Если новое значение нижней границы поиска установлено некорректно, программа двоичного поиска не отыщет верный сегмент массива и застопорится, просматривая снова и снова ту же самую область массива.
Если вы сделаете эту замену и выполните процедуру DemoDebug2, вам придется нажать клавиши <Ctrl+Break>, чтобы прервать выполнение этой программы. Взгляните на список значений, сгенерированный в окне отладки Проверка ин
Приложение
859
струкциями Debug.Print, и вы увидите, что без конца просматривался один и тот же сегмент массива.
3.	В цикле Do не инкрементируется значение переменной I, которая позволяет последовательно просматривать символы строки S. Следовательно, этот цикл повторяется бесконечно. А вот как выглядит исправленная версия этого цикла.
Do While I < Len(S)
If Mid(S, I, 1) = FindChar Then
Mid(S, I, 1) = ReplChar
Else 1 = 1 + 1 End If Loop
4.	Условие инструкции If никогда не будет истинным, поскольку не существует такого числа, которое было бы меньше 10 и одновременно больше 100. Эта ошибка является логической.
5.	Первый аргумент в вызове функции InStr в строке 10 является логически некорректным. А вот правильный вариант.
Dim S As String Dim SubStr As String Dim I As Integer
S = "The rain is Spain stays mainly in the plain" SubStr = "ain"
I = InStrfS, SubStr)
Do While I > 0
Debug.Print "Match at I
I = InStrfl + 1, S, SubStr) Loop
День IG-fl
Teem
1.	Режим конструктора предполагает работу с формой в окне редактора Visual Basic. В режиме конструктора можно добавлять на форму или удалять с нее управляющие элементы, редактировать их или использовать окно Properties для установки свойств самой формы и ее управляющих элементов.
2.	Метод Show отображает форму на экране, загружая ее в память, если она еще не загружена.
3.	Метод Hide скрывает форму таким образом, что ее больше не видно, и поэтому она не может получить фокус. При использовании метода Hide форма остается загруженной в память.
4.	Нет. Форма должна быть загружена в память до того, как вы попытаетесь получить доступ к ее свойствам, методам и управляющим элементам.
5.	Инструкция Load загружает форму в память, но не отображает ее. Инструкция Unload удаляет форму из памяти. При этом все внутренние значения формы теряются.
6.	Экземпляр формы можно создать, используя прямую ссылку на нее. frmlnsertFigure.Show
860
Приложение
Экземпляр формы можно также создать, используя ключевые слова Set или Dim в сочетании с ключевым словом New.
Dim FigDialog As New frmlnsertFigure 'объявляет переменную
'и создает новый экземпляр
Dim FigDialog As Object
Set FigDialog = New frmlnsertFigure 'создает новый экземпляр
7.	Процедура обработки события — это процедура, которую вы пишете в модуле класса для обработки конкретного события (например, щелчка или двойного щелчка мышью, изменения значения управляющего элемента и т.п.).
8.	Процедуры обработки событий создаются путем написания текста процедур в модуле класса формы. Чтобы создать процедуру обработки конкретного события для конкретного объекта, выберите этот объект в списке Object окна Code, а затем выберите нужное событие в списке Procedure окна Code. Редактор Visual Basic автоматически создаст объявление д ля процедуры обработки этого события.
9.	Да, программист может создать свои свойства и методы для своей формы. Эти свойства создаются путем написания процедур Property Get и Property Let в разделе General модуля класса формы. Собственные методы создаются путем написания процедур и функций также в разделе General модуля класса формы.
10.	Порядок перехода — это термин, используемый для описания порядка, в котором управляющие элементы на форме получают фокус при нажатии клавиши <ТаЬ> (для перемещения вперед) или <Shift+Tab> (для перемещения назад).
11.	Если вы хотите выполнить некоторые манипуляции над управляющими элементами, расположенными на форме, до отображения формы на экране, вам следует сначала загрузить форму, используя инструкцию Load.
Load frmlnsertFigure
12.	Да. Каждая форма, добавляемая к проекту, создает новый класс объектов.
Упражнения
1. Для этого упражнения не предусмотрено никакого программного примера. Чтобы выполнить это задание, достаточно заменить управляющий элемент ListBox на форме frmCityList управляющим элементом СошЬоВох. Или же можете использовать программу, представленную в листинге 16.5, для модуля класса формы, и программу, представленную в листинге 16.6, для проверки функционирования формы. В этом случае вам останется изменить лишь имена формы или ее управляющих элементов.
2. Для данного упражнения приведена одна из возможных версий модуля класса формы frmCOCA и процедура, предназначенная для отображения формы frmCOCA и содержащаяся в стандартном модуле. Большая часть функциональной нагрузки перенесена на процедуру обработки события Click, возникающего при щелчке на кнопке btnCalculate. В задачу процедур обработки событий Change для текстовых полей операнда и оператора входит просто очистка текстового поля результата, чтобы никакой результат не отображался до тех пор, пока не будет выполнен щелчок на кнопке Calculate. Чтобы лучше представить себе управляющие элементы формы frmCOCA, обратитесь к рис. 16.18.
Вот как выглядит модуль класса формы frmCOCA.
Option Explicit
Приложение
861
Function ValidateSelf() As Boolean
ValidateSelf = False 'предполагаем недопустимые значения
With Me
If (Not IsNumeric(.txtOperandl)) Or _ (Not IsNumeric).txt0perand2)) Then MsgBox prompt:="OnepaHflbi должны быть числами.", Title:=.Caption, Buttons:=vbExclamation
Exit Function
End If
If Len(Trim(.cmbOperator.Value)) = 0 Then MsgBox prompt:="He задан оператор/функция", Buttons:=vbExclamation, Title:=.Caption Exit Function
End If
End With
ValidateSelf = True 'если мы дошли сюда, с данными - ОК
End Function
Private Sub btnCalculate_Click()
Dim Operand1 As Double
Dim 0perand2 As Double
Dim Result As Double
Dim OpStr As String
If Not ValidateSelf Then Exit Sub
With Me
'Получаем операнды и оператор/функцию. Используем функцию Vai, 'чтобы преобразовать строки в числа, при этом
'пустые строки будут преобразованы в нули
Operandl = Vai(.txtOperandl.Value)
OpStr = UCase(Trim(.cmbOperator.Value))
0perand2 = Vai(.txt0perand2.Value)
Select Case OpStr
Case Is = "+"
Result = Operandl + 0perand2 Case Is =
Result = Operandl - 0perand2 Case is =
Result = Operandl * 0perand2
Case Is = "A"
Result = Operandl ' 0perand2
Case Is = "I"
If 0perand2 <> 0 Then
Result = Operandl / 0perand2 Else
862
Приложение
MsgBox prompt:="Ошибка деления на нуль ", Buttons:=vbExclamation, Title:=.Caption Exit Sub
End If
Case Is = "SQR"
If Operandl > 0 Then
Result = Sqr(Operandl) Else
MsgBox prompt:="Ненадлежащий аргумент функции ", Buttons:=vbExclamation, Title:=.Caption
Exit Sub End If Case Else MsgBox prompt:="Неизвестный оператор", Buttons:=vbExclamation, Title:=.Caption Exit Sub End Select
'отображаем результат
.txtResult.Value = CStr(Result)
End With
End Sub
Private Sub btnClose_Click()
Me.Hide
End Sub
Private Sub txtOperandl_Change()
Me.txtResult = "" End Sub
Private Sub txt0perand2_Changej)
Me.txtResult = "" End Sub
Private Sub txtOperation_Change()
Me.txtResult = "" End Sub
Private Sub UserForm_Initialize() 'Заполняем список операторов With Me.cmbOperator .Additem "+" .Additem
.Additem "*"
.Additem "Л"
.Additem "/"
.AddItem "Sqr"
End With End Sub
Приложение
863
Процедура в стандартном модуле, предназначенная для отображения формы frmCOCA, имеет следующий вид.
Option Explicit
Sub DemoCOCAf)
'отображает форму frmCOCA frmCOCA.Show
End Sub
День 17-й
Тест
1.	С помощью метода Add коллекции CommandBarControls для меню, в которое вы хотите добавить подменю.
2.	Важно убедиться, что у пользователя есть возможность выйти из программы или из системы меню, которую вы инсталлировали. В противном случае пользователь может никогда не вернуться к исходным меню приложения.
3.	Нет, вы не должны указывать процедуру обработки события при создании элемента управления командной строкой. Но если для созданного вами управляющего элемента оставить свойство OnAction пустым, то при щелчке на нем никаких действий выполняться не будет. При установке свойства OnAction для встроенного элемента управления переопределяется присущее ему поведение.
4.	Тип командной строки определяется при ее создании путем использования соответствующей константы MsoBarType с помощью аргумента Туре метода Add.
5.	Тип управляющего элемента командной строки определяется при ее создании путем использования соответствующей константы MsoControlType с помощью аргумента Туре метода Add.
6.	Нет. Excel определяет свойство State встроенного управляющего элемента.
Упражнения
1.	Ваш вариант выполнения задания для этого упражнения должен выглядеть примерно следующим образом.
Sub ListAllCommandBars()
'создает рабочий лист Excel со всеми строками меню
Dim CmdBar As CommandBar
Dim RCount As Long
'отображаем информацию в рабочем листе Excel
ActiveWorkbook.Sheets("ЛистЗ").Activate
ActiveSheet.Cellsfl, 1).Value = "Тип”
ActiveSheet.Cells(l, 2).Value = "Имя командной строки"
RCount = 2
For Each CmdBar In Application.CommandBars
Application.StatusBar = "Перечень " & CmdBar.Name
If CmdBar.Builtln Then
ActiveSheet.Cells(RCount, 1).Value = "Встроенные"
864
Приложение
Else
ActiveSheet.CellsfRCount, 1).Value = "Настраиваемые" End If
ActiveSheet.CellsfRCount, 2).Value = CmdBar.Name
RCount = RCount + 1
Next CmdBar
Application.StatusBar = False
End Sub
2.	Ваш вариант решения этого упражнения должен иметь примерно следующий вид. Sub ListCommandBarTypesf)
'создает рабочий лист Excel, на котором перечисляются
'все встроенные командные строки, их имена и типы
Dim CmdBar As CommandBar
Dim RCount As Long
Dim strType As String
'отображаем информацию в рабочем листе Excel
ActiveWorkbook.Sheets("ЛистЗ”).Activate
ActiveSheet.Cells(l, 1).Value = "Встроенные"
ActiveSheet.Cells(l, 2).Value = "Тип строки"
ActiveSheet.Cells(l, 3).Value = "Имя командной строки"
RCount = 2
For Each CmdBar In Application.CommandBars
Application.StatusBar = "Перечень " & CmdBar.Name
If CmdBar.Builtln Then
ActiveSheet.Cells(RCount, 1).Value = "Встроенные”
Else
ActiveSheet.CellsfRCount, 1).Value = "Настраиваемые" End If
Select Case CmdBar.Type
Case Is = msoBarTypeMenuBar
strType = "Строка меню"
Case Is = msoBarTypeNormal
strType = "Панель инструментов"
Case Is = msoBarTypePopup
strType = "Меню"
Case Else
strType = "Неизвестный тип"
End Select
ActiveSheet.CellsfRCount, 2).Value = strType
ActiveSheet.CellsfRCount, 3).Value = CmdBar.Name
RCount = RCount + 1
Next CmdBar
Application.StatusBar = False
End Sub
Приложение
865
3.	Ваш вариант решения этого упражнения должен иметь примерно следующий вид.
Sub AddMyMenuQ
'Добавляет меню в активную в данный момент строку меню
Dim CmdBar As CommandBar
Dim aBtn As CommandBarControl
'получаем ссылку на активную строку меню
Set CmdBar = CommandBars.ActiveMenuBar
Set aBtn = CmdBar.Controls.Add(Type:=msoControlPopup, temporary:=True)
aBtn.Caption = "МоеМеню"
With CmdBar.Controls("МоеМеню")
Set aBtn = .Controls.Add (Type:=msoControlButton, temporary:=True)
End With
aBtn.Caption = "Удалите это меню"
aBtn.OnAction = "УдалитеМоеМеню"
End Sub
Sub RemoveMyMenu()
'удаляет специальное меню из строки меню
CommandBars.ActiveMenuBar.Controls("МоеМеню”).Delete End Sub
4.	Ваш вариант решения этого упражнения должен иметь примерно следующий вид.
Sub AddVBA_ToolbarExtras ()
Dim CmdBar As CommandBar
Dim aBtn As CommandBarControl
Set CmdBar = CommandBars.Add(Name:="Программист VBA", Position:=msoBarFloating, MenuBar:=False, temporary:=True)
With CmdBar
.Visible = True
'добавляем новый встроенный управляющий элемент
.Controls.Add Type:=msoControlButton, ID:=2520, _ temporary:=True
'добавляем кнопку Справка
Set aBtn = .Controls.Add(Type:=msoControlButton, temporary:=True)
aBtn.Caption = "Справка VBA"
aBtn.Faceld =49
aBtn.BeginGroup = True
aBtn.OnAction = "ExcelVBACnpaBKa"
866
Приложение
'добавляем кнопку Удалить
Set aBtn = .Controls.Add(Type:=msoControlButton, temporary:=True)
aBtn.Caption = "Удаляем панель инструментов"
aBtn.Faceld = 478
aBtn.BeginGroup = True
aBtn.OnAction = "УдалениеИзлишествУВА"
End With
End Sub
Sub ExcelVBAHelp()
Application.Help "vbaxl9.chm"
End Sub
Sub DeleteVBAExtras()
CommandBars)"Программист VBA").Delete
End Sub
День 18-0
Твен
1.	Инструкция On Error Resume Next.
2.	VBA останавливает выполнение программы и отображает диалоговое окно сообщения об ошибке времени выполнения.
3.	Конечно! Каждый набор инструкций обрабатывает ошибки определенного типа.
4.	Инструкция Resume 0, которая обычно располагается в конце инструкций обработки ошибок.
5.	Используйте инструкцию On Error Goto 0.
6.	Инструкция Resume очищает внутренние механизмы обработки ошибок времени выполнения, предусмотренные в VBA.
7.	Функция Error возвращает текст сообщения об ошибке, связанный с конкретными инструкциями, вызвавшими ошибку времени выполнения. Свойство Err. Description также возвращает текст сообщения о последней ошибке.
8.	Инструкция Error приводит к возникновению ошибки времени выполнения. (Не следует путать инструкцию Error, которая вызывает ошибку времени выполнения, с функцией Error, которая возвращает текст сообщения об ошибке.) Самым предпочтительным способом искусственного вызова ошибки является метод Err.Raise.
Упражнения
1.	Ваша процедура может выглядеть следующим образом.
Sub MyErrorHandler()
'объявляем константы
Const ERR_MSG = "Неверные индексы для функции Mid”
Const dTitle = "Мой обработчик ошибок"
Приложение
867
'объявляем переменные
Static istr As String
Dim eStr As String
Dim pStr As String
Dim Start As Integer
Dim Count As Integer
On Error GoTo Badlndex 'устанавливаем перехват ошибок
'получаем исходную строку, стартовый индекс и число символов
iStr = InputBox(prompt:="BBeflHTe строку",
Title:=dTitle, default:=iStr)
pStr = "Введите индекс для первого " &
"выделяемого символа."
Start = Val(InputBox(prompt:=pStr, Title:=dTitle))
pStr = "Введите количество выделяемых символов."
Count = Vai(InputBox(prompt:=pStr, Title:=dTitle))
eStr = Mid(istr, Start, Count)
MsgBox Buttons:=vblnformation, Title:=dTitle, prompt & istr & " ' " & vbCr & _ .......... & eStr & "'"
Exit Sub
Badlndex:
eStr = ""
MsgBox Buttons:=vbCritical, Title:=dTitle,
prompt:=ERR_MSG
Resume Next
End Sub
2.	Инструкции отображают строку Hello.
3.	Инструкции отображают строку Jello.
4.	Инструкции в окне сообщений отображают следующее:
Jello
11
Число 11 означает, что инструкция Mid вызвала ошибку времени выполнения 11 раз.
5.	Инструкции в окне сообщений отображают следующее.
Runtime error number 5
День 19-0
Teem
1.	Метод Save объекта Workbook сохраняет заданную рабочую книгу на диске с тем же самым именем, которое было у нее прежде. Метод SaveAs работает подобно команде Файл-Сохранить как, сохраняя рабочую книгу под новым именем и заменяя имя открытой рабочей книги новым именем. Однако метод SaveCopyAs сохраняет копию заданной рабочей книги в другом файле и оставляет исходный файл в его первозданном виде как на диске, так и в памяти.
868
Приложение
2.	Рабочая книга сохраняется под ее текущим именем. Если рабочая книга называется Bookl, Excel присваивает соответствующему файлу имя Bookl.xls.
Для присваивания имени новому файлу следует использовать метод SaveAs объекта Workbook. О том, сохранялся ли данный файл прежде, можно узнать, проверив свойство FullName, а именно, не возвращает ли это свойство строку с нулевой длиной
3.	Приложение Excel создает новую рабочую книгу для хранения скопированного или перемешенного рабочего листа.
4.	Коллекция Worksheets содержит только рабочие листы, принадлежащие текущей рабочей книге, в то время как коллекция Sheets содержит и рабочие листы, и листы с диаграммами.
5.	К трем основным методам, используемым для возврата диапазона рабочих листов, отоносятся следующие: Range, Cells и Offset. Метод Range для возврата ячейки или диапазона использует либо координаты, либо имя диапазона. Метод Cells для возврата одной ячейки использует номер строки и номер столбца. Метод Offset возвращает диапазон, который представляет собой смещение от другого диапазона на заданное число строк и столбцов.
6.	Обе процедуры создают трехмерные ссылки по нескольким листам Excel и вводят данные в ту же самую ячейку того же самого листа. В обеих процедурах для хранения имен листов (Sheetl, Sheet2 и Sheet3) используется массив SheetArray.
В первой процедуре (FillAcross) в ячейку А1 листа Sheetl вводится надпись и к объекту Worksheets применяется метод FillAcrossSheets. Этот метод, который является эквивалентом команды Excel Правка-Заполнить-По листам, имеет следующий синтаксис.
Объект.FillAcrossSheets(Диапазон [, Тип])
Элемент Объект представляет собой ссылку на коллекцию Worksheets. Элемент Диапазон — это диапазон для заполнения по рабочим листам. Он должен включать один из листов в объекте, заданном элементом Объект. Элемент Тип указывает, как копировать диапазон: xlAll, xlContents или xlFormulas.
Вторая процедура использует метод Select для группирования массива рабочих листов, активизирует ячейку А1 в листе Sheetl и вводит надпись в объект Selection. Эти действия эквивалентны выделению группы листов с последующим вводом некоторого значения и нажатием клавиш <Ctrl+Enter>.
Из этих двух процедур, вероятно, предпочтение следует отдать первой, поскольку заполняется по листам не просто одна ячейка, а диапазон. Кроме того, в этом случае не используются медленнодействующие методы Select и Activate.
7.	Метод Value всегда возвращает значение ячейки, т.е. данные, хранимые в ячейке, или результат вычисления формулы, записанной в ячейке. А метод Formula возвращает либо значение ячейки (если данная ячейка содержит константу), либо формулу ячейки в виде строки (если данная ячейка содержит формулу).
8.	Используйте метод RefersToRlCl, если описание вашего диапазона дано в стиле R1C1 или представляет собой метод или свойство, возвращающее диапазон.
9.	Методы Cut (без аргумента Destination), Clear, ClearContents, Cle'arFormats и ClearNotes.
Приложение
869
Упражнения
1.	Ваше решение должно выглядеть примерно следующим образом.
Option Explicit
Sub SaveAllWorkbooks()
'Сохраняет все рабочие книги
Dim wBook As Workbook
Dim Ans As Integer
Dim mPrompt As String
Dim qBtns As Integer
Dim mTitle As String
Dim NewName As Variant
For Each wBook In Workbooks
'В книге есть несохраненные изменения или путь нулевой длины?
If (Not wBook.Saved) Or (wBook.Path = "") Then 'Если да, проверяем новую рабочую книгу без имени If wBook.Path = "" Then
'Предлагаем пользователю сохранить ее
mPrompt = "Вы хотите сохранить " & wBook.Name & qBtns = vbYesNoCancel + vbQuestion mTitle = "Сохранение всех рабочих книг" Ans = MsgBox(Prompt:=mPrompt, Buttons:=qBtns, Title :=mTitle)
Select Case Ans
Case vbYes 'отображаем диалог GetSaveAsFilename NewName = Application.GetSaveAsFilename) InitialFilename: =wBook. Name, FileFilter:=”Excel Workbooks,*.xis", Title:=mTitle & " -Select Name")
If NewName <> False Then wBook.SaveAs Filename:=NewName End If
Case vbCancel
Exit Sub 'Отмена с "катапультированием" End Select
Else 'В противном случае просто сохраняем книгу wBook.Save
End If
End If
Next wBook
End Sub
2.	Ниже приводится процедура, предназначенная для выполнения операции копирования нескольких выделенных областей. Чтобы переделать эту процедуру для выполнения операции вырезки нескольких выделенных областей, достаточно заменить метод Сору методом Cut.
Option Explicit
Sub CopyMultipleSelection()
Dim R As Range
870
Приложение
Dim Sheetstr As String
Dim DestSheet As Worksheet
'Запрашиваем имя листа
Sheetstr = InputBoxf"Введите имя листа-приемника:", "Копирование нескольких выделенных областей")
'Проверяем, нажал ли пользователь кнопку Cancel (Отмена)
If Sheetstr <> Then
'Если Нет, преобразуем строку InputBox в объект Worksheet
Set DestSheet = Worksheets(Sheetstr)
'Проходим в цикле все выделенные области и копируем
'их в лист-приемник
For Each R In Selection.Areas
R.Copy Destination:=DestSheet.Cells(R.Row, R.Column) Next R
End If
End Sub
3.	Первый метод Names.Add определяет имя Testi для ссылки на строковую константу Sheetl !$А$1. Проблема состоит в некорректном описании диапазона в аргументе RefersTo.
Names.Add Name:="Testl", RefersTo:="Sheetl!$A$l"
Этот аргумент должен прочитать RefersTo : = "=Sheetl!$A$l“.
Второй метод Names.Add определяет имя Test2 для ссылки на содержимое ячейки Sheetl !А1, а не на саму ячейку. Проблема состоит в использовании вместо аргумента RefersToRlCl аргумента RefersTo.
Третий метод Names.Add определяет имя диапазона, но определение диапазона изменяется в зависимости от того, какой ячейкой является активная ячейка. Проблема состоит в том, что аргумент RefersTo использует относительный диапазон ('June Sales'!А1 :А10). Если вы не испытываете особой необходимости в использовании имени относительного диапазона, то всегда при определении имен диапазонов используйте абсолютные ссылки.
4.	Ваша процедура должна иметь примерно следующий вид.
Sub EditData()
Dim I As Integer
Dim CurrRow As Integer
Dim DBColumns As Integer
Dim DBColStart As Integer
'Получаем текущую строку
CurrRow = ActiveCell.Row
'Получаем количество столбцов в диапазоне Database
DBColumns = Rangef "Database") .Columns.Count
'Получаем самый левый столбец диапазона Database
DBColStart = Range("Database").Column
'Загружаем данные записи в форму диалогового окна
For I = 1 То DBColumns
Listingl3Form.Controls("txt" & CStr(I)).Text =
Приложение
871
Cells(CurrRow, (I + DBColStart) - 1).Value
Next I End Sub
День 20-fi
Teem
1.	Сервер — это приложение, которое поддерживает объект OLE и предоставляет сервис для этого объекта. Клиент — это приложение, содержащее объект OLE. Приложения-клиенты также иногда называются приложениями-контейнерами.
2.	Связанный объект содержит изображение данных сервера и поддерживает связь между клиентом и сервером. Встроенный объект содержит не только данные сервера, но и всю основополагающую информацию, связанную с приложением сервера, например, имя приложения, структуру файла и все коды форматирования.
3.	Первое преимущество, которое вы получаете благодаря OLE, состоит в возможности перетаскивать и опускать данные между двумя открытыми приложениями OLE. Второе преимущество состоит в возможности вставлять объекты по месту, т.е., оставаясь в том же самом документе, можно с помощью меню и панелей инструментов перейти к документу приложения сервера. Третье преимущество — в том, что можно редактировать объекты по месту, т.е., при двойном щелчке на объекте с целью редактирования этот объект остается в вашем документе, но вместо инструментов приложения-клиента на экране появляются инструментальные средства приложения-сервера. Четвертое преимущество связано с возможностью использования технологии автоматизации для управления открытыми объектами другого приложения OLE.
4.	База данных системного реестра содержит информацию о приложениях Windows, инсталлированных в вашем компьютере. В этой базе данных зарегистрированы данные, связанные с поддержкой OLE и технологии автоматизации каждого приложения, например тип класса для объектов приложения.
(А)	Установите свойство Filename равным имени документа и установите Link равным значению True.
(В)	Установите свойство ClassType равным типу класса презентационной графической программы.
(С)	Установите свойство Filename равным имени растрового файла и либо проигнорируйте Link, либо установите его значение равным False.
5.	Действие указывает, что можно сделать с объектом OLE с точки зрения “родного” приложения этого объекта.
Первичное действие — это стандартное действие, которое выполняется над объектом. Другими словами, это действие, которое происходит при выполнении двойного щелчка на объекте. Обычно первичное действие предусмотрено для открытия или редактирования объекта OLE.
6.	Автоматизация — это стандартная технология, посредством которой приложения открывают свои объекты для других приложений, работающих в системе. Такие языки, как Visual Basic for Applications, могут распознавать или манипулировать этими объектами путем выполнения методов и установки свойств.
7.	К объектам приложения-сервера можно обращаться напрямую. С помощью функции CreateObj можно создавать новые объекты, а с помощью функции GetObject можно получать доступ к существующим объектам.
872
Приложение
8.	Асинхронное выполнение приложения означает, что VBA, выполнив приложение, возвращается к процедуре, содержащей вызов функции Shell, и продолжает выполнение других инструкций в этой процедуре.
9.	Аргумент Wait определяет, ожидает ли ваша процедура перед продолжением работы, пока приложение обработает нажатия клавиш. Если установить аргумент Wait равным значению True, процедура согласна ждать. В протином случае (когда аргумент Wait установлен равным значению False) процедура продолжает выполняться, не ожидая обработки клавиш от приложения.
10.	Библиотеки динамической компоновки (DLL) представляют собой коллекции функций и процедур, которые доступны для всех приложений Windows. DLL-процедуры можно использовать в своих VBA-программах путем объявления этих процедур на уровне модуля.
11.	Аббревиатура API расшифровывается как Applications Programming Interface (интерфейс программного приложения). Это совокупность функций и процедур, имеющихся в библиотеках DLL, которые поставляются вместе с Windows.
Норажиеш
1. Ваша процедура должна выглядеть примерно следующим образом.
Sub EnumerateShapes()
Dim aShape As Shape
Dim aMsg As String
For Each aShape In ActiveSheet.Shapes
With aShape
aMsg = "Тип: “ & .Type & vbCr
aMsg = ''Имя: " & .Name & vbCr
If (.OLEFormat Is Nothing) Then
aMsg = aMsg & "He объект OLE'' & vbCr
Else
aMsg = aMsg s "Объект OLE "
If .Type = msoEmbeddedOLEObject Then
aMsg = aMsg & "встроенный." & vbCr
aMsg = aMsg & "ProglD: " & .OLEFormat.Progid s vbCr
Else
aMsg = aMsg s “связанный." & vbCr
End If
End If
End With
MsgBox prompt:=aMsg, Title:="IIepenHcb форм"
Next aShape
End Sub
2. Поскольку функция Shell работает асинхронно, вторая функция Shell (которая запускает редактор Notepad) может начать работать до того, как завершится первая функция Shell (которая создает текстовый файл DirList.txt). Следовательно, редактор Notepad может не найти файл DirList.txt и отобразит сообщение об ошибке. (Иногда редактор Notepad может отобразить файл DirList.txt пустым.) Решение проблемы состоит во вставке задержки между двумя операторами Shell. Например, следующая VBA-инструкция Excel создает трехсекундную задержку, которая во многих случаях оказывается достаточно длинной.
Application.Wait Now + TimeValue("00:00:03")
Приложение
873
День 21-0
Teem
1.	Процедура обработки события — это процедура Sub, связанная с конкретным событием (например, открытие или закрытие рабочей книги, выбор рабочего листа и т.п.), которое вызывается некоторым объектом в приложении Excel. Процедура обработки события выполняется автоматически при возникновении связанного с ней события.
2.	Процедуры, предназначенные для обработки событий, принадлежащих некоторому объекту, должны храниться модуле класса этого объекта. В стандартных модулях могут храниться только такие процедуры обработки событий, которые инсталлируются с помощью свойства или метода, связанного с событием.
3.	Если присвоить строку нулевой длины аргументу Procedure метода ОпКеу, Excel запретит выполнение каких-либо действий при нажатии этой клавиши. Если вообще опустить аргумент Procedure, восстанавливается нормальное поведение этой клавиши, которое может заключаться в отсутствии какого-либо обусловленного поведения.
4.	Аргумент EarliestTime содержит желаемое время запуска заданной процедуры обработки события. Аргумент LastTime определяет время, до наступления которого Excel будет ожидать, чтобы выполнить заданную процедуру, если программа была не готова к работе в момент времени, указанный с помощью аргумента EarliestTime.
5.	Загрузка по требованию означает, что надстройка загружается в память в два этапа. Сначала становятся доступными функции, меню и панели инструментов надстройки. Затем, когда пользователь выбирает или одну из функций, или какую-нибудь команду меню, или щелкает на какой-либо кнопке панели инструментов, надстройка загружается полностью.
6.	Да. Событие Change наступает при каждом изменении значения ячейки рабочего листа, и при этом причина этих изменений не важна.
7.	Событие SheetActivate объекта Excel Application генерируется в случае, когда активизируется любой лист в любой открытой рабочей книге.
Упражнепоя
1.	Как показано в приведенной ниже программе, сначала объявляем три переменных для хранения трех исходных параметров. Эти переменные объявляются на уровне модуля, т.е. вне какой-либо процедуры в модуле. В этом случае они будут доступны для всех процедур модуля. Процедура обработки события Open, принадлежащая объекту рабочей книги, сохраняет текущие параметры в соответствующей переменной и устанавливает новое значение свойства. В процедуре обработки события BeforeClose рабочей книги используются переменные, которые возвращают каждое свойство к своему исходному состоянию.
Dim OldDefaultFilePath As String
Dim OldPromptForSummarylnfo As Boolean
Dim OldSheetsInNewWorkbook As Integer
Private Sub Workbook_Open()
With Application
OldDefaultFilePath = .DefaultFilePath
874
Приложение
.DefaultFilePath = "C:\VBA21"
OldPromptForSummarylnfo = .PromptForSummarylnfo
.PromptForSummarylnfo = False
OldSheetsInNewWorkbook = .SheetsInNewWorkbook .SheetsInNewWorkbook = 8
End With
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
With Application
.DefaultFilePath = OldDefaultFilePath
.PromptForSummarylnfo = OldPromptForSummarylnfo
.SheetsInNewWorkbook = OldSheetsInNewWorkbook
End With
End Sub
2.	На первый взгляд кажется, что процедура обработки события WorkSheet_Deactivate пытается очистить текущий рабочий лист до перехода на следующий лист. Однако все обстоит по-другому. Дело в том, что процедура WorkSheet_Deactivate для выполнения своих задач использует объект ActiveSheet. И это неправильно, поскольку Excel активизирует лист, на который вы перешли, до генерации события Deactivate. Это значит, что каждая инструкция внутри блока With относится к новому листу, а не к только что оставленному листу.
Если вы хотите выполнить задачи на листе, с которого ушли, необходимо точно указать соответствующий объект Worksheet, например Worksheets) "Scratch Pad"). Если вы собираетесь использовать процедуру обработки события Deactivate для нескольких рабочих листов, то могли бы для сохранения имени текущего листа объявить открытую переменную (с использованием ключевого слова Public). Эту переменную можно было бы установить в процедуре обработки события WorkSheet_Activate для этого листа и использовать ее снова в процедуре WorkSheet_Deactivate.
3.	Используйте следующие процедуры для демонстрации того, что Excel запускает процедуру обработки события OnWindow после выполнения процедуры WorkSheet_Activate (первый листинг из приведенных ниже должен храниться в стандартном модуле, а второй — в модуле класса рабочей книги).
Sub SetHandlers))
Windows(ThisWorkbook.Name).OnWindow = "OnWindowEventHandler"
End Sub
Sub OnWindowEventHandler!)
MsgBox "Это процедура обработки события OnWindow."
End Sub
Поместите этот листинг в модуль класса рабочей книги.
Private Sub Workbook_Activate()
MsgBox "Это процедура обработки события Activate рабочей книги.”
End Sub
4.	Ваше решение может иметь следующий вид.
Sub Set_CtrlDeleteHandler()
Application.ОпКеу Key:="A{Del}",
Procedure:=“DeleteAll"
Приложение
875
End Sub
Sub DeleteAll))
Selection.Clear
End Sub
5.	Процедура Getlnterval использует функцию InputBox для получения количества минут между событиями. Это число хранится в глобальной переменной Interval. Затем процедура Getlnterval вызывает процедуру SetRegularlnterval для инициализации свойства OnTime. Процедура SetRegularlnterval преобразует число минут, содержащееся в переменной Interval, в часть суток — CInt)Interval) / (60 * 24) — и добавляет эту часть к значению функции Now. Результат суммирования запоминается в переменной nextTime. В методе OnTime аргумент EarliestTime устанавливается равным значению переменной nextTime, а аргумент Procedure — значению IntervalProc. Процедура IntervalProc имитирует любое действие, которое должно, по вашему мнению, выполняться через регулярные интервалы времени, и снова запускает процедуру SetRegularlnterval, чтобы создать следующее событие OnTime. Процедура SetRegularlnterval будет вызываться до тех пор, пока переменная Continue будет оставаться равной значению True. Процедура RemovelntervalProc останавливает возобновление события OnTime путем установки переменной Continue равной значению False.
Dim Interval As String
Dim Continue As Boolean
Sub Getlnterval))
Interval = InputBox)"Введите интервал, в минутах:”)
Continue = True
SetRegularlnterval
End Sub
Sub SetRegularlnterval))
Dim nextTime As Date
If Interval <> "" Then
nextTime = Now + CInt)Interval) I (60 * 24)
Application.OnTime EarliestTime:=nextTime,
Procedure:="IntervalProc"
End If
End Sub
Sub IntervalProc))
Beep
With Application
.StatusBar = "Сейчас выполняется процедура Interval Procedure."
.Wait Now + TimeValue)"07")
.StatusBar = False
End With
If Continue Then SetRegularlnterval
End Sub
Sub RemoveIntervalProc()
Continue = False
End Sub
876
Приложение
Предметный указатель
3
32-битовый код, 753
А
Application Programming
Interface, 752 automation, 159; 715; 742
в
BASIC, 24
Bitmap, 724
c
Clipboard, 716
Currency, 87
D
Date, 85
DDE. 716
Dynamic Data Exchange. 716
E
Empty, 114; 331
F
Flow control, 250
For...Next, 210
N
Null, 114; 331
О
Object linking and embedding, 715
OLE, 715
Option Base, 449
Option Compare, 127
Option Explicit, 98
p
Persistence, 97; 339
Private, 385
Public, 385
Q
QuickBASIC, 24
R
RefEdit, 552
Run time error, 78
V
Variant, 88
VBA-инструкции. 63
Visual Basic, 24
Visual Basic for Applications, 37
A
Абсолютная дата. 85
Автоматизации. 25
Автоматические отступы, 74
Автоматическое преобразование типов, 116
Автоматическая вставка объявления, 771
Автоматический запуск процедур, 768
Активизация приложения, 759
рабочего листа, 695
рабочей книги, 690
Активный рабочий лист, 695
Аргументы функции, 144
Арифметические операторы, 120
Асинхронное выполнение, 759
Атрибуты файла, 410
Б
База данных реестра, 719
Бесконечный цикл, 297
Библиотека динамических
связей, 742
процедур, 377
типов, 742
Бинарное сравнение, 126
Бинарный поиск, 478; 482
Буфера обмена, 716
В
Включающее или, 131
Вложенные инструкции, 256
циклы, 281; 310; 312
Внедрение, 717
Внешние данные, 361
Внутренние константы, 106
Возведение в степень, 124; 207
Возвращаемое значение, 110
Возрастающий счетчик, 287
Время жизни, 97
Время изменения файла, 439
Вставка файла, 728
Вторичное действие, 738
Вывод сообщений, 75
Вызов процедуры, 393
Выполнение макроса, 24
Выражение присваивания, 207
Выражения, 113
Вычисление сложных выражений, 135
г
Графический редактор Paint, 715
д
Дата, 85; 114
изменения файла, 439
Деление, 122
Деление по модулю, 123
Диапазон ячеек, 696; 700; 705
Дизайн формы, 560
Динамик, 753
Динамические массивы, 448
Динамическое изменение заголовков, 561
Директивы компилятора, 98; 449
Диски, 429
Длина файла, 440
Доступ к библиотеке, 379 к объектам, 227; 742
Дочерние окна, 41
Ж
Жесткая точка останова, 514
Живучесть переменной, 339
3
Загрузка библиотеки, 379
Закрытие рабочей книги, 692
Замкнутая ссылка, 391
Запись макроса, 24; 25; 27
Запуск макроса, 23
процедуры, 74
Защитное программирование, 333
Звук, 753
Значение Empty, 331
Null, 332
И
Игнорирование результата функции, 144
Идентификатор, 90
Идентификационный номер задачи, 758; 761
Изменение атрибута, 417
Изображение, 552
Имя диапазона, 708
Импликация, 132
Импорт модуля, 68
Инструкции принятия решений, 250
Интерактивные программы, 108
Интерфейс пользователя, 592
Исключающее или, 132
к
Классы объектов, 215; 359
Коды символа, 126
ошибок, 656
Коллекции, 212;597; 695
Комментарии, 63; 66
Компоненты автоматизации, 718
Конкатенация, 116; 133
Константы, 83; 102; 113; 385
Конструктор макросов, 24
Контрольное выражение, 525
Контрольные значения, 522
Копирование рабочего листа, 697
файла, 412; 435
элемента управления, 556
л
Личная книга макросов, 40; 377
Ловушки обработки ошибок, 613
Логические выражения, 115
значения, 87 инструкции, 249 константы, 105 операторы, 129 ошибки, 509
Локальная видимость, 385
Предметный указатель
877
Локальные переменные, 96; 528
м
Макрокоманда, 23
Максимальный действующий индекс, 467
Массивы, ИЗ; 444
Математические функции, 148
Меню, 591
Методы, 212; 214; 221; 605
Многомерные массивы, 445
Многомодульное программирование, 385
Модальные формы, 561
Модули, 39; 376
Модуль класса, 360; 538; 562
н
Набор вкладок, 551
страниц, 551
Надпись, 550
Надстройки, 768
Начальные условия, 27
Необязательные аргументы, 76;
190; 193; 221; 222
Неопределенный тип, 98
поиск, 127
цикл, 281
Непосредственная вставка, 717
Непосредственное редак-
тирование, 717
Несовпадение типов, 79
Несоответствие типов, 115
Нестандартная функция, 183
Неявное объявление переменных, 91
Номера строк, 660 О
Область видимости, 93; 103
Обмен данными, 398
Обновление данных, 738
Обработка ошибок, 652; 665
Обработчики события, 769
Образец для сравнения, 127
Обращение к рабочей книге, 687
Объект автоматизации, 742 Объектное выражение, 115; 224 Объектно-ориентированное программирование, 212
Объектная библиотека, 747
Объектные переменные, 224
Объекты,212; 605
Объявление объектных пере-
менных, 223
макроса, 63
констант, 103
Обязательный аргумент, 190; 293
Ограничение видимости, 388
Одномерные массивы, 445
Окно отладки, 508
редактора Visual Basic, 41
Округление числа, 149
Операнды, 114
Оператор And, 130
Eqv, 132
Imp, 132
Is, 129
Like, 127
Not, 131
Or, 131
Xor, 132
присваивания, 118
конкатенации, 134
сравнения, 124
Операция конкатенации, 103
Описание массивов, 450
переменных, 351
Определение нового типа, 350
типа ошибки, 655
Определенные циклы, 281
Определитель цикла, 281; 295
Определяемые пользователем типы, 349
Открытие рабочей книги, 688
Отладка программы, 508
Относительная ссылка, 32
Отправка кодов клавиш, 761
Отрицание, 131
Отступы, 74
Ошибка времени вы-
полнения, 78; 617
несоответствия типа, 189; 224
компиляции, 509
п
Панели инструментов, 591; 779
Панель управления, 758
Первичное действие, 738
Передача аргумента по зна-
чению, 193
по ссылке, 193
Переименование модуля, 70
файлов, 437
Переключатель, 550
Переменная, 88; 385
фиксированной длины, 102
Перемещение рабочего листа, 697
файлов, 437
элемента управления, 555
Перетаскивание данных, 717
Печать текста программы, 79
Плавающее окно, 41
Побочные эффекты, 195
Подменю, 592
Поименованные аргументы, 146; 222
константы, 102
Поиск макроса, 58
объектных классов, 720
строк, 167
Поле, 550
Поле со списком, 551
Полоса прокрутки, 551
Порядок вычислений, 136
Последовательный поиск, 478
Последовательность перехода,
537; 547; 557
Последовательность работы программы, 249
Пошаговая детализация, 395
Пошаговое выполнение, 521
Пошаговый режим, 512
Правила видимости, 385
Предопределенные константы, 106
Преобразование дат, 118
строк, 117
текстовых строк, 140
числовых типов, 117
числового значения, 173
булевых величин, 118
Префикс модуля, 392
Присваивание результата функции, 184
Проверка аргументов, 334
данных, 334; 339
значений выражений, 508
Программа, управляемая событиями, 768
Проект, 386
Проектирование класса, 360
Процедура, 70
Процедуры обработки событий, 538; 768
Процедуры-функции, 182
Р
Работа с другими приложениями, 715
Размерность, 450
Рамка, 550
Растровые рисунки, 724
Редактирование элементов
управления, 555
макросов, 39
Редактор реестра, 719
Режим прерывания, 508; 510
выполнения, 548
конструктора, 547
Резервная копия модуля, 67
Результат вычисления, 184
функции, ПО; 142; 226
Рекурсивная функция, 206; 208
Рекурсия, 207
С
Сведения о системе, 722
Свойства, 212; 605
Свойства объекта, 213
Связанные данные, 731
Связывание, 716
Сервер, 716
Серверное приложение, 728
Символы формата, 174
продолжения строки, 65
определения типа, 100
Синтаксические ошибки, 76; 509
Слияние строк, 133
Сложение, 116; 121
Слой рисования, 724
Событие, 1П\ 779; 780; 783; 787
Совместимость типов, 113
Содержимое ячейки, 706
Создание классов, 360
объекта автоматизации, 745
папки, 433
переменных, 91
рабочего листа, 696
рабочей книги, 689
события, 538
Сообщения о несоответствии ти-
пов, 117
для пользователя, 39
об ошибках, 76
Сортировка массива, 471
878
Предметный указатель
Составной документ, 715
Сохранение рабочей книги, 691
Список, 551
Список аргументов, 144; 398
Сравнение строк, 125
Ссылки на объекты, 224
на рабочую книгу, 687
Старшинство операторов, 135
Статические массивы, 448
Стек, 206
Строковые выражения, 115
функции, 156
Структурное программирование, 393
т
Таблицы истинности, 130
Текст макроса, 34; 44; 61
процедуры, 71
Текстовые поля, 541
строки, 87
Текстовое сравнение, 127
Текущая папка, 429
Те/о макроса, 63
никла, 294
Технология DDE, 716
Тип выражения, ИЗ константы, 106 ошибки, 655 результата, 121
Типичные ошибки, 508 Типы данных, 83; 318
Точка останова, 513
Трехмерные ссылки, 696
У
Удаление модулей, 69 папки, 433 рабочего листа, 698 управляющей панели, 624 файла. 436 файлов, 412 данных. 709
Управление файлами, 410
Уровни видимости, 96
ф
Фатальная ошибка. 652
Фоновый режим. 760
Формат данных, 173
рабочей книги, 686
Форматирование, 173
чисел, 140
Формы, 538
Функции, 113; 140
Функции времени, 152
преобразования данных, 150
ц
Циклы, 281
контролируемые событиями, 295
контролируемые счетчиками, 295
ч
Числа с плавающей запятой, 86
Числовое выражение, 114
э
Экспоненциальная запись, 84
Экспорт модуля, 67
Элементы массивов, 113
Элементы управления, 541
Предметный указатель
879
Учебное пособие
Мэтью Харрис
Освой самостоятельно программирование для Microsoft Excel 2000 за 21 день
Издательский дом “Вильямс” 101509, г. Москва, ул. Лесная, д. 43, стр. 1 Изд. лиц. ЛР № 090230 от 23.06.99 Госкомитета РФ по печати
Подписано в печать 22.12.99. Формат 70X100/16. Гарнитура Times. Печать офсетная. Усл. печ. л. 56,29. Уч.-изд. л. 54,75. Тираж 4000 экз. Заказ № 457.
Отпечатано с диапозитивов в ГПП «Печатный Двор» Министерства РФ по делам печати, телерадиовещания и средств массовых коммуникаций.
197110, Санкт-Петербург, Чкаловский пр., 15.
Освойте новые высоты программирования
Всего за 21 день вы узнаете все, что нужно для эффективной работы в Excel 2000. Настоящее руководство поможет вам овладеть основами и перейти к более серьезным вещам. В этой книге рассмотрены следующие вопросы:
SAMS Освой самостоятельво >р|гршш)нам1 *м Micrisift Excel 2000 за 21 ив
Расширьте возможности своих программ, разработанных для среды Ехсе'
знакомство с основными понятиями программирования для Excel 2000;
описание новых прогрессивных средств программирования, включенных в Excel 2000;
Познакомьтесь со встроенными функциями, массивами, процедурами, с обработкой ошибок, с различными операторами и выражениями, с переменными и константами
изучение новейших инструментов и функций на примерах, взятых из реальной жизни;
Узнайте, как из среды Excel с помощью Visual Basic управлять другими приложениями
получение советов признанных авторитетов в области программирования в интегрированной среде Excel 2000
Науиитесь экономить время, пользуясь программой просмотра Object Browser
Освойте модульную технику программирования, сделав большие проекты простыми в понима нии и легкими в применении.
Мэтью Харрис занимается разработкой и написанием программного обеспечения для микрокомпьютеров уже более 15 лет. Им создано немало коммерческих программных продуктов и множество домашних приложений для разных клиентов. Он преподавал программирование на языках BASIC1*. Pascal1^ Paradox®.Ero перу принадлежит книга Teach Yourself Visual Basic*for Applications in 21 Days, кроме того, он является соавтором книги Database Developer’s Guide with Visual Basic®4.
Категория -рсграммирова*- ие для Microsoft Office 2000
Предмет рассмотрения: Microsoft
Excel 2000
Уровень: для начинающих и средней квалификации ''льзователей
SAMS
wan samspublishing.com
www.williamspublishing.com