Текст
                    I
Уильям Орвис
VISUAL BASIC
for APPLICATIONS
*
Наиболее простой и быстрый способ
обучения программированию
/
И
Publishers
♦
Создайте Windows-приложения
с помощью этого мощного
инструмента
Усовершенствуйте ваши
программистские
и конструкторские навыки
с помощью совершенных
средств VBА
Простые и ясные примеры
и упражнения позволят достичь
успеха в программировании


Visual Basic for Applications на примерах
William J. Orvis Visual Basic for Applications by Example oue
Вильям Дж. Орвис Visual Basic for Applications на примерах БИНОМ Москва 1995 Торгово-издательское бюро BHV 1995
УДК 681.3.06 Издание выпущено при содействии Комитета РФ по печати Вильям Дж. Орвис Visual Basic for Applications на примерах: Пер. с англ. — М.: БИНОМ. — 512 с.: ил. ISBN 1-56529-553-6 ISBN 5-7503-0043-9 Книга является учебником по программированию на языке Visual Basic for Applications. В первую очередь она рассчитана на пользователей Excel, которые хотели бы восполь¬ зоваться Visual Basic for Applications для расширения возможностей Excel и для того, чтобы связать Excel с другими приложениями, такими как Word и Project. Совместное издание Фирмы БИНОМ и Торгово-издательского бюро BHV Все права защищены. Никакая часть этой книги нс может быть воспроизведена в любой форме или любыми средствами, электронными или механическими, включая фотографирова¬ ние, магнитную запись или иные средства копирования или сохранения информации без пись¬ менного разрешения издательства. ISBN 1-56529-553-6 ISBN 5-7503-0043-9 Autorizcd translation from the English language edition. © Original copyright. Que Corporation, 1994 © Перевод на русский язык, научное редактирование. Торгово-издательское бюро BHV, 1995 © Издание на русском языке. Издательство БИНОМ, 1995 © Художественное оформление. Н. Лозинская, 1995
Посвящается Барри, Киту, Эми, Джуди и Лоре с огромной благодарностью за их помощь и поддержку на протяжении многих лет. Об авторе Уильям Дж. Орвис —инженер-электроник из Национальной лаборатории Лоу¬ ренса Ливермора Калифорнийского университета. Он занимается крупномасштабным цифровым моделированием твердотельных приборов, разработкой микронных ва¬ куумных микроэлектронных приборов и исследованиями в области защиты данных в компьютерах (сам он именует себя разрушителем компьютерных вирусов). Орвис является членом CIAC, входит в группу департамента энергетики, которая занимается решением проблем, связанных с компьютерами. Получил звание бакалавра и магистра физики и астрономии в Университете Денвера, штат Колорадо. Автор книг "Excel 4 for Scientists and Engineers" (Sybex, 1993), "Do-It-Yourself Visual Basic for MS-DOS" (Sams Publishing, 1992),"Do-It-Yourself Visual Basic (for Windows)" (Sams Publi¬ shing, 1992, 2nd ed. 1993), "ABC’s of GW-BASIC" (Sybex, 1990), "Excel Instant Reference" (Subex,1989), "1-2-3 for Scientists and Engineers" (Subex, 1987, 2nd ed. 1991) и "Electrical Overstrees Protection for Electronic Devices" (Noues Data Corporation, 1986). Его книги переведены на японский, итальянский и греческий языки. Кроме того, Орвис печатался в журналах "Computer in Physics" и "IEEE Circuits and Devices". Благодарности Проект.такого масштаба не может быть плодом труда одного человека. Хотя мне хотелось бы думать, что создание этой книги —полностью моя заслуга, приходится признать, что я не смог бы свести мои результаты воедино без помощи всей организации Que. Подчас можно лишь удивляться, почему благодаря издателям вы на протяжении нескольких месяцев бессонными ночами и в выходные дни оказывае¬ тесь прикованными к компьютеру, но, увидев конечный продукт, понимаете, ради чего все затевалось. Таким образом, если люди приобретают плод вашего труда, вы ощущаете, что, может быть, действительно сотворили нечто стоящее. Список тех, кого я хотел бы поблагодарить, открывает Джо Викерт, который заставил меня приняться за этот проект (правда, ему не пришлось прилагать слишком много усилий) и подстегивал меня до тех пор, пока дело не было сделано. Следующие — Брайан Гамбрел, Лори Кейтс и Энди Сэфф, которые координировали проект и держали все под контролем. Я благодарен Дэнни и Тайен из Today Computers, Плезантон, штат Калифорния, которые поддерживали мою систему в рабочем состоянии и на помощь которых я мог рассчитывать в любое время. 5
В заключение я хочу поблагодарить фирмы B.J., Skye, Sierra и Shane за то, что они не разгадали раньше меня все загадки в MYST. Кроме того, я благодарю Джулию за то, что она присматривала за детьми, пока я работал над рукописью. Благодарности за использование торговых марок Все термины, упомянутые в этой книге как торговые или сервисные марки, надлежащем образом выделены. Использование их в этой книге не должно расцени- ватьря как покушение на подлинность любой торговой или сервисной марки. Visual Basic —зарегистрированная торговая марка корпорации Microsoft.
Обзор Введение Часть I. Введение в Visual Basic for Applications 1. Добро пожаловать в Visual Basic for Applications 2. Объекты, свойства и методы 3. Доступ к объектам Excel из Visual Basic Часть II. Основные элементы языка 4. Типы данных и переменные 5. Применение операторов присваивания и встроенных функций Часть III. Функции и процедуры 6. Создание и применение процедур 7. Создание и применение функций Часть IV. Управление выполнением программы 8. Принятие решений 9. Применение блочных структур If 10. Применение структур Select Case И. Не применяйте неструктурированные переходы Часть V. Циклы и повторяемые структуры 12. Применение вычисляемых циклов 13. Применение логически прерываемых циклов 14. Применение циклов объектного типа Часть VI. Ввод и вывод 15. Применение встроенных диалоговых окон 16. Применение рабочих таблиц
17. Создание диалоговь/х окон пользователя 18. Запись данных в дисковый файл последовательного доступа 19. Запись данных в дисковый файл произвольного доступа 20. Другие способы записи данных Часть VII. Отладка процедуры 21. Что делать, если ваша программа не работает 22. Применение контрольных точек и точек слежения 23. Применение прерываний по ошибке для реакции на непредвиденные события Часть VIII. Особенности расширения языка 24. Создание пользовательских меню и пиктографических меню 25. Создание пользовательских объектов 26. Межпрограммные коммуникации Приложения A. Ответы на вопросы B. Таблица кодов ANSI C. Коды ошибок Visual Basic Глоссарий Предметный указатель
Введение Visual Basic for Applications в примерах — одна из книг серии "В примерах” издательства Que. Общая философия этих книг заключается в том, что программиро¬ вание легче и лучше осваивается на примерах. Сложные описания синтаксиса команд и структур языка скорее всего нагонят тоску на программиста-новичка и не принесут особой пользы более квалифицированному специалисту. Поэтому книга предлагает короткие, несложные общие примеры, с помощью которых можно наглядно увидеть, как выполняются команды и функции. Для кого предназначена эта книга? Эта книга учит тому, как программировать на языке Visual Basic for Applications на трех различных уровнях — начальном, среднем и высоком. В начальных главах книги детально изложены все действия каждого примера. Последующие же главы описывают очевидные для начинающего программиста детали лишь в общих чертах. Таким образом, подробное обсуждение сведено к рассмотрению новых особенностей, которым посвящен текущий раздел. В первую очередь книга рассчитана на пользователей Excel, которые хотели бы воспользоваться Visual Basic for Applications для расширения возможностей Excel и для того, чтобы связать Excel с другими приложениям, такими как Word u Project. Читателю следует ознакомиться с приложениями Excel, а также с использованием таблиц для создания списка величин и их вычисления. Если вы не знаете, как подступиться к Excel, как присваивать значения ячейкам таблицы, вводить в эти ячейки формулы, форматировать таблицу, то было бы неплохо сначала всему этому научиться. Тем не менее знание макроязыка Excel 4 не является обязательным для работы с этой книгой. Если вы — опытный программист или хотя бы прилично программируете на BASIC, то вы немало почерпнете из этой книги, так как Visual Basic for Applications значительно отличается от большинства реализаций языка программирования BASIC. В то же время, обладая всеми чертами, присущими традиционным версиям языка BASIC, Visual Basic for Applications также имеет надстройки, которые дают возмож¬ ность управлять Excel и другими программами из комплекса Microsoft Office, используя объекты каждой из них. Это в корне отличается от традиционных методов дистанционного управления, когда одна программа управляет другой, эмулируя ввод команд с клавиатуры. Visual Basic for Applications позволяет осуществлять доступ непосредственно к командам. Опытным программистам, конечно же, захочется изу¬ чить эти особенности, поскольку тогда они смогут более эффективно использовать Visual Basic for Applications для расширения возможностей других программ. 9
Visual Basic for Applications в примерах Философия книги Задача этой книги — научить использовать практические достижения современного программирования с целью создания модульных, понимаемых программ. Смысл состоит в том, что определенная практика программирования придает программам большую очевидность и обеспечивает легкость их прочтения. Если вполне очевидно, для чего предназначена та или иная часть программы, то в следующий раз, когда вы захотите с ней работать, вам не придется потратить много времени на обдумывание ее смысла. Существуют веские причины для стремления писать легко понимаемые программы. В первую очередь это возможность понять то, что вы сделали. Наградой послужит то обстоятельство, что, создавая собственную программу, можно быть уверенным: вы делаете именно то, что хотите. Понимаемость программы принесет пользу и в дальнейшем, когда вы решите внести изменения или исправить ошибку. Отладка программы часто занимает больше времени, чем ее написание, поэтому потратьте немного дополнительного времени на вставку комментариев и приведение текста программы к более удобоваримому виду. Вдобавок написание понимаемых программ вознаградит любого, кому потребуется изменить или исправить вашу программу. Используйте интерактивную подсказку для ознакомления с полным синтаксисом команд В этой книге нет подробного описания каждой команды и вариантов синтаксиса языка Visual Basic for Applications, а приведены, скорее, современные конструкции программирования и наиболее общие опции и синтаксис. Применяя описанные здесь приемы программирования, вы не будете иметь проблем с созданием приложений практически любого типа. Используйте интерактивную подсказку (online help), чтобы посмотреть некоторые команды и их полный синтаксис. Но, не пытайтесь выучить каждую команду и оператор, приведенные в интерактивной подсказке Visual Basic for Applications: многие операторы были включены лишь в целях совместимости с более старыми версиями BASIC. Расширяйте примеры Примеры, приведенные в книге, не являются полностью законченными, —они могут служить рабочими блоками программы, демонстрирующей некие специфические задачи. Вам предлагается расширять и модифицировать эти примеры применительно к вашим потребностям. Пробуйте варьировать переменными, различными опциями и т.п. Вы не должны окончательно разрушить что-либо, экспериментируя с языком (ведь в любом 10
Введение случае вы будете сохранять свои файлы, не так ли?). Кроме того, вам предлагается создавать собственные примеры, чтобы поэкспериментировать с различными коман¬ дами и возможностями. Чем больше вы будете программировать самостоятельно, тем быстрее изучите язык. Немного о вводных примерах Некоторые примеры, приведенные в начальных главах, содержат ранее не описан¬ ные команды и операторы. Это сделано в целях повышения полезности примеров. При использовании более сложных команд их функции кратко описываются. Если вам потребуется дополнительная информация, просмотрите главу, в которой описана данная команда, или используйте интерактивную подсказку. Обзор книги Данная книга разделена на восемь частей. Часть I содержит краткое введение в Visual Basic for Applications и объектно-ориентированное программирование. После¬ дующие части посвящены различным элементам языка; таким образом, после глубо¬ кого изучения Visual Basic for Applications вы сможете использовать эту книгу в качестве справочного материала. Часть I: Введение в Visual Basic for Applications Часть I представляет собой введение в Visual Basic for Applications. Вводный пример дает возможность ’’почувствовать" язык и представить себе, как он взаимодей¬ ствует с Excel. Здесь также описаны различия между Visual Basic и более традицион¬ ными версиями языка BASIC, рассмотрена реализация объектно-ориентированного программирования в Visual Basic. Часть II: Основные элементы языка Часть II посвящена описанию основных элементов языка Visual Basic for Applications — типов переменных и данных. Эта часть книги ознакомит вас также с тем, как данные накапливаются в памяти и передаются встроенным функциям Visual Basic for Applications. Часть III: Функции и процедуры Часть III содержит описание использования Visual Basic for Applications для создания процедур и функций пользователя. В нее включены общие процедуры, командные процедуры, процедуры обработки событий, функции (в том числе пользо¬ вательские функции таблиц). 11
Visual Basic for Applications в примерах Часть IV: Управление выполнением программы В части IV рассмотрены управляющие структуры и способы их применения для преобразования программы из простого последовательного вычислителя в программу, управляемую логикой. Описаны логические переменные и выражения, операторы If, блочные структуры If и структуры Select Case. Все эти структуры используют логические условия для выбора программного блока, который должен выполняться. Часть V: Циклы и повторяемые структуры В части V рассматриваются повторяемые структуры, т.е. программные блоки, выполнение которых повторяется в зависимости от специальных условий или значения счетчика. Хотя для создания повторяемых структур можно использовать блочные операторы If, эти структуры используются настолько часто, что все языки програм¬ мирования обладают встроенными средствами для их реализации. Здесь описаны вычисляемые циклы For-Next, логически прерываемые циклы Do-Loop и логические структуры объектного типа For Each. Часть VI: Ввод и вывод В части VI поясняется, как вводить и выводить данные из ваших программ на Visual Basic for Applications. Доступу к таблице посвящена вся книга, а в этой части рассмат¬ ривается создание пользовательских входных форм и записи данных в файлы на дисках. Часть VII: Отладка процедуры В части VII обсуждаются искусство отладки процедур и применение отладочных средств Visual Basic for Applications. Время, которое вы тратите на отладку, зачастую значительно превосходит время, затраченное на процесс написания программы. Visual Basic for Applications обладает впечатляющим набором отладочных средств, позво¬ ляющих определить причину конкретной ошибки. Часть VIII: Особенности расширения языка К особенностям расширения языка отнесены те особенности, которые выходят за рамки традиционного языка программирования. Они включают в себя создание пользовательских и пиктографических меню, пользовательских объектов, а также управление другими программами с помощью DDE (Dynamic Data Exchenge) и OLE (Object Linking and Embedding). Приложения и глоссарий Приложения содержат различного рода дополнительную информацию: сюда во¬ шли таблица кодов символов ASCII/ANSI, перечень кодов ошибок Visual Basic for 12
Введение Applications, а также ответы на вопросы к каждой главе. За приложениями следуют подробный глоссарий и предметный указатель. Строки программы В этой книге используются следующие типографские соглашения: > Строки программы и синтаксиса набраны равноширинным шрифтом. > Метки-заполнители для переменных или литеральных значений в строках синтак¬ сиса и некоторых примерах даны в однопробельном виде курсивом. > Текст, предназначенный для ввода, выделен жирным шрифтом. > Имена файлов записаны прописными буквами. Например, MYFILE.TXT. > Новые термины, определяемые в тексте и глоссарии, выделены курсивом. Примечания, советы и предупреждения В книге приведена информация, которая развивает тему, не имеющую прямого отношения к текущей главе, или содержит специальные советы и предупреждения относительно применения некоторых команд либо методов, описываемых в этой главе. Примечание: Развивает различные темы, актуальные на данном этапе изложения материала, но не относящиеся непосредственно к основной теме, описываемой в данной главе. Совет: Предлагает быстрый и легкий путь осуществления некоторых действий, что может быть очевидно из текста. Предупреждение: Информирует о проблемах, с которыми вы можете столкнуться, используя про¬ грамму или методы, описываемые в данном разделе кн::ги. Принимайте во внимание предупреждения — они избавят вас от ненужной головной боли. Применение термина Visual Basic Термины Visual Basic и Visual Basic for Applications используются в книге как синонимы, хотя термин Visual Basic должен применяться к пакету Visual Basic для Windows. Я избрал эту более краткую, хотя и не совсем точную форму, так как не люблю прибегать к акронимам, а употреблять каждый раз Visual Basic for Applications очень утомительно. В тех случаях, когда различия между пакетом Visual Basic для Windows и Visual Basic for Applications имеют значение для изложения материала, указывается полное наименование. В остальных случаях термин Visual Basic приме¬ няется здесь для Visual Basic for Applications.
Часть I Введение в Visual Basic for Applications
Глава 1 Добро пожаловать в Visual Basic for Applications На протяжении многих лет глава корпорации Microsoft Билл Гейтс (Bill Gates) высказывал мнение о BASIC как о наиболее простом языке высокого уровня для всех типов прикладных программ. Эта точка зрения привела в конечном итоге к появлению Visual Basic for Applications. Впервые использовав Visual Basic for Applications в Excel и Project, специалисты корпорации Microsoft затем применили его во всех своих наиболее популярных пакетах, включая Word, Mail и PowerPoint. Первая часть этой книги знакомит с Visual Basic for Applications и его связями с Excel, последующие части —с особенностями языка и программированием на Visual Basic. Из этой главы вы узнаете: > Что такое Visual Basic > Как записать процедуру на Visual Basic > Как редактировать процедуру > Как подключить процедуру к кнопке Что такое Visual Basic for Applications? Visual Basic for Applications является-новейшим воплощением Microsoft Visual Basic 3.0 для Windows. Специалисты корпорации Microsoft перенесли Forms Designer из Visual Basic, связали Visual Basic c Excel, а затем добавили диалоговые таблицы взамен Forms Designer. Дополнительно они сделали все команды и объекты Excel (ячейки, таблицы и т.п.) доступными для Visual Basic, что позволило Visual Basic управлять всеми функциями Excel. Таким образом, в настоящее время Visual Basic является "супермакроязыком" для Excel, базирующимся на хорошо известных коман¬ дах и синтаксисе BASIC. Дополняя Excel, Visual Basic является также макроязыком для Project, а вскоре станет и макроязыком для Word, Mail и PowerPoint. Если вы намучились с различ¬ ными языками и операционными системами, то можете вздохнуть свободно. Большин¬ ство из выпущенных в последние годы основных прикладных пакетов были реализо¬ ваны со своими собственными макроязыками, и все эти макроязыки отличались друг от друга и от всех известных языков программирования. Создание корпорацией Microsoft универсального макроязыка в значительной степени избавляет от головной боли, связанной с разработкой пользовательских команд и процедур. Тот, кто хорошо знаком с более старыми версиями BASIC, такими как BASICA и GW-BASIC, может предположить, что Visual Basic —это примитивный язык програм¬ мирования, не имеющий современных конструкций и структур данных. Однако это 17
Visual Basic jot Applications в примерах не так. Хотя в Visual Basic соблюдаются синтаксис и правила программирования на BASIC и выполняются программы, написанные на BASICA и GW-BASIC, Visual Basic является современным языком программирования, полностью оснащенным всеми необходимыми структурными операторами и типами данных. Кроме того, в Visual Basic применяются объекты, что делает его использование при разработке приклад¬ ных программ более понятным и легким. И хотя уровень объектно-ориентированного программирования на Visual Basic не так высок, как на C++, Visual Basic управляет Excel, манипулируя Excel-объектами. Используя Visual Basic, нельзя создавать новые классы объектов, но можно вплотную приблизиться к решению этой задачи. Что можно сделать на Visual Basic for Applications? Visual Basic не только облегчит создание макропроцедур, но и совместно с OLE 2.0 позволит создавать "мегапрограммы", объединяющие и координирующие усилия многих мощных прикладных программ. Например, можно создать программу подго¬ товки документа, которая будет использовать Excel для доступа к базе данных, генерации графиков и таблиц, Word для включения этих графиков и таблиц в текст отчета, Mail для рассылки отчета, и все это —нажатием одной кнопки: Visual Basic будет "клеем", который все объединит и упорядочит. Так как указанные программы являются объектно-ориентированными, любая программа на Visual Basic будет иметь доступ ко всем командам и данным в них за счет доступа к объектам этих программ. Как записать процедуру па Visual Basic? Чтобы понять как работает процедура на Visual Basic, создайте первую процедуру с помощью программы Excel. Используя макрорекордер Excel, можно подробно записать все шаги по созданию таблицы, а затем, повторив процедуру, воссоздать таблицу. Первый пример — создание таблицы погашения ссуды. При выплате ссуды, размеры процента по ссуде, а также задолженности каждый месяц изменяются в зависимости от выплаченной суммы и дебетового сальдо счета. Обычно ежемесячно выплачивается некоторая сумма с фиксированной ставкой процента. Можно создать таблицу погашения ссуды для расчета ежемесячной задолженности, процента по ссуде и дебетового сальдо счета. Подготовка к записи процедуры В процессе создания таблицы вначале определите, где бы вы хотели начать запись, и подготовьте таблицу для этой цели. После включения рекордера все, что вы делаете, уже записывается. Для получения законченной таблицы лучше всего начать с чистого листа. Если требуется создать процедуру для специального форматирования некото¬ рых ячеек, следует перед началом процесса записи заполнить все ячейки данными. 18
Глава 1, Добро пожаловать в Visual Basic for Applications Установка типов ссылок Следующим шагом будет установка типов ссылок в записываемой процедуре. В Excel имеются два типа ссылок: относительные и абсолютные. Относительные ссылки определяют местоположение ячейки таблицы по отношению к текущей активной ячейке. Преимуществом относительных ссылок является возможность их использова¬ ния для форматирования различных областей таблицы относительно текущей выбран¬ ной ячейки. Абсолютные ссылки всегда указывают на конкретную ячейку. Когда запускается процедура, использующая абсолютные ссылки, всегда можно получить доступ к конкретной ячейке независимо от того, какая ячейка была выбрана в данный момент. По умолчанию используются абсолютные ссылки, но если вы хотите использо¬ вать в своей процедуре относительные ссылки, то перед тем как записать процеду¬ ру, выберите директиву Tools/Record Macro/Use Relative References. Для возвра¬ та к абсолютным ссылкам выберите директиву Tools/Record Macro/Use Absolute References. Запуск рекордера Существуют два способа запуска рекордера —в зависимости от того, где необхо¬ димо сохранить записанную процедуру. Для сохранения процедуры в новом модуле следует указать директиву Tools/Record Macro/Record New Macro. Если вы хотите начать запись в существующем модуле, поместите курсор в то место, куда необходимо вставить вновь записанный блок, и выполните директиву Tools/Record Macro/Mark Position for Recording, а затем начните запись директивой Tools/Record Macro/Record At Mark. Создание таблицы Для подготовки к записи таблицы погашения ссуды выполните следующие действия: 1. Запустите Microsoft Excel, если он еще не запущен. В противном случае создайте новую рабочую папку директивой File/New. 2. Выберите директиву Tools/Record Macro/Record New Macro. Появится диалого¬ вое окно Record New Macro. 3. В окне Macro Name запишите ”Таблица_погашения_ссуды". В окне Descrip¬ tion запишите "Создание таблицы погашения ссуды за 180 месяцев.”. Затем нажмите командную кнопку Options. Диалоговое окно приобретет вид, приведенный на рис. 1.1. 19
isual Basic for Applications в примерах Э?/.'- - ; Record Ntjw Macro , - л ; Macro Name: | Т аблица погашения ссудь4 I description: Создание таблицы погашения сс^ы за ♦ 180 месяцев. -Assign to Q Menu Item on Tools Menu: □ Shortcut Key: Ctrl* |e | -Store in О Personal Macro Workbook ® This Workbook О Nelv Workbook -Language ® Visual Basic О MS Excel 4.0 Macro Рис. 1.1. Диалоговое окно Record New Macro с установленными опциями В диалоговом окне Record New Macro можно установить несколько опций, которые будут включены в записываемую процедуру. Можно установить или отменить неко¬ торые опции и после того,, как процедура будет записана. Для этого нужно выбрать директивы Tools/Macro и Tools/Assign Macro. Поле Macro Name содержит имя, присваиваемое данной процедуре. Используйте короткие, но информативные имена. Имя должно состоять из одного слова или нескольких слов, объединенных символом подчеркивания (_). В поле Description можно описать, что будет делать данный макрос. Любой текст, помещенный в это окно, является комментарием к заголовку процедуры. В области Assign То предостав¬ ляется возможность подключить эту процедуру к элементу меню Tools или клавише быстрого доступа. Это можно сделать и позже директивой Tools/Assign Macro. Область Store In предоставляет три варианта опции выбора места хранения новой процедуры. Наиболее общий вариант — This Workbook. Он позволяет поместить процедуру в новый модуль, связывая ее с текущей рабочей папкой. Вариант Personal Macro Workbook помещает процедуру в специальную невидимую рабочую папку, которая всегда открывается после старта Excel. Таким образом, любая помещенная сюда процедура, становится глобально доступной всем открытым рабочим папкам. Чтобы увидеть персональную рабочую папку, выберите директиву Window/Unhide. Третий вариант —новая рабочая папка —размещает процедуру в новом модуле новой рабочей папки. 20
Глава 1. Добро пожаловать в Visual Basic for Applications Последнее поле в диалоговом окне —поле задания опции языка. Здесь в качестве языка для создаваемой процедуры можно выбрать Visual Basic или макроязык MS Excel 4.0. Пользуйтесь опцией Visual Basic, а опцию макроязыка MS Excel 4.0 выбирайте только тогда, когда создаете макрос для применений, ориентированных на MS Excel 4.0. Чтобы записать создание таблицы погашения ссуды, выполните следующие действия: 1. Нажмите ОК, чтобы закрыть диалоговое окно. В плавающем окне появится командная кнопка STOP. Эту кнопку вы нажмете в конце работы для остановки процесса записи процедуры. 2. Выберите ячейку А1 и напечатайте "Таблица погашения ссуды". 3. Выберите ячейку D1 и напечатайте "Процентная ставка". 4. Выберите ячейку D2 и напечатайте "Размер ссуды". 5. Выберите ячейку D3 и напечатайте "Сумма платежа". 6. Выберите ячейку Е1, напечатайте 0.0675 и нажмите клавишу ENTER. Это величина годовой процентной ставки в виде десятичной дроби. 7. Выберите директиву Format/Cel Is, выберите корешок Number, Percentage Cate¬ gory и код 0.00%. Нажмите командную кнопку ОК. Таким образом вы преобразуете ячейку в процентный формат вместо дроби. 8. Выберите ячейку Е2 и напечатайте 10000. Это размер ссуды. 9. Выберите директиву Format/Cells, затем выберите корешок Number, Currency Category и код в виде $#,##0.00_);($#,##0.00). Нажмите командную кнопку ОК. Таким образом число 10000 будет представлено как денежная единица. 10. Выберите ячейку ЕЗ и напечатайте =РМТ(Е1/12,180,Е2,0 ). Эта функция вычис¬ ляет размер ежемесячного платежа для погашения ссуды за 180 месяцев. Процен¬ тная ставка, записанная в ячейке Е1, делится на 12, что дает среднемесячную процентную ставку. Excel знает, что результат вычисления этой функции — денежная величина, и автоматически преобразовывает его в формат представления денежных единиц. Ячейка ЕЗ имеет отрицательное значение, так как погашение ссуды —это убыток. 11. Выберите ячейку В5 и напечатайте "Месяц". 12. Выберите ячейку С5 и напечатайте "Интерес". 13. Выберите ячейку D5 и напечатайте "Погашение ссуды". 21
Visual Basic for Applications в примерах 14. Выберите ячейку E5 и напечатайте ’’Дебетовое сальдо". 15. Подведите курсор мыши к разделителю между заголовками колонок D и Е, затем перетащите этот разделитель вправо для увеличения колонки D до размеров записи в D5. 16. Подведите курсор мыши к разделителю между заголовками колонок Е и F, затем перетащите этот разделитель вправо для увеличения колонки Е до размеров записи в Е5. 17. Выберите ячейку В6 и напечатайте "1". Для того, чтобы Excel знал, как выполнять выбор в случае применения манипулятора заполнения в шаге 19, потребуются два начальных числа. 18. Выберите ячейку В7 и напечатайте "2". 19. Выберите ячейки В6:В7, затем нажмите манипулятор заполнения в правом нижнем углу выбранной зоны и, удерживая кнопку мыши нажатой, перетащите его вниз к ячейке В185. Это создаст в колонке В последовательность целых чисел от 1 до 180. 20. Выберите ячейку С6 и напечатайте =ROUND(E1*E2/12,2). Путем умножения V12 части годовой процентной ставки на размер ссуды вычисляется месячный процент по ссуде (интерес банка). Результат округляется до ближайшего целого цента. 21. Выберите ячейку D6 и.напечатайте =$Е$3-С6. Размер суммы, направляемой в счет погашения ссуды, определяется как разность между размером платежа и процентом по ссуде. Встроенная в Excel функция РМТ вычисляет платеж как отрицательную величину (поскольку деньги идут в убыток), следовательно необходимо исполь¬ зовать знак минус. 22. Выберите ячейку Е6 и напечатайте =E2-D6. Дебетовое сальдо данного месяца вычисляется как разность между невыплаченным и выплаченным капиталами. 23. Выберите ячейку С7 и напечатайте =ROUND ($Е$1*Е6/12,2). 24. Выберите ячейку D6, нажмите манипулятор заполнения в правом нижнем углу выбранной зоны и, удерживая кнопку мыши нажатой, перетащите его вниз к ячейке D7. 25. Выберите ячейку D7 и напечатайте =Е6-Е7. 26. Выберите ячейку С7:Е7, нажмите манипулятор заполнения в правом нижнем углу выбранной зоны и, удерживая кнопку мыши нажатой, перетащите его вниз к ячейке Е185. 27. Пролистайте таблицу вверх. Убрав координатные линейки и сетку, можно придать таблице более привлекательный вид. 22
Глава 1. Добро пожаловать в Visual Basic for Applications 28. Выберите ячейки D1:E3, укажите директиву Format/Cells, выберите корешок Border, отметьте контрольные индикаторы Left, Right, Тор и Bottom, затем нажмите командную кнопку ОК. 29. Выберите ячейки В5:Е5, укажите директиву Format/Cells, выберите корешок Border, отметьте контрольный индикатор Outline, затем нажмите командную кнопку ОК. 30. Выберите ячейки В6:Е185, укажите директиву Format/Cells, выберите корешок Border, отметьте контрольный индикатор Outline, затем нажмите командную кнопку ОК. 31. Выберите ячейку А1. 32. Укажите директиву Tools/Options и выберите корешок View. Затем в окне Window Options отключите контрольные индикаторы Grid lines и Row & Column Headings, затем нажмите командную кнопку ОК. Таблица приобретет вид, приведенный на рис. 1.2. Microsoft Excel Bookl "|Eile Edlt^YiewlnsertTflrmat Tools Data Window Help |p|e?|iai |a|&|y| 0Э [gfA | № T4f||ioo* SMS I Arial Cyr i ■ S В А1 |*| | Таблица погашения ссуды Таблица погашения ссуды Процентная ставка • 6.75% Размер ссуды $10,000.00 [н] Сумма платежа ($88.49) Месяц Интерес Погашение ссуды Дебетовое сальдо 1 56.25 $32.24 $9,967.76 2 56.07 $32.42 $9,935.34 •з 55.89 $32.60 $9,902.74 4 55.7 $32.79 $9,869.95 5 55.52 $32.97 $9,836.98 6 55.33 $33.16 $9,803.81 7 55.15 $33.34 $9,770.47 8 54.96 $33.53 $9,736.94 9 54.77 $33.72 $9,703.22 10 54.58 $33.91 $9,669.31 11 54.39 $34.10 $9,635.21 12 54.2 $34.29 $9,600.92 13 54.01 $34.48 $9,566.44 14 53.81 ГЛ гл $34.68 $9,531.76 Sheet2 / Sheet3 / Sheet4 / Sheet5 / Sheet6| + Ready Recording Рис. 1.2. Готовая таблица погашения ссуды 23
Visual Basic for Applications в примерах Обратите внимание на командную кнопку Stop в плавающем окне, расположенном в верхнем углу таблицы. Нажмите эту командную кнопку для остановки рекордера. Сохраните рабочую папку под именем AMORT1.XLS. Среди корешков, расположен¬ ных внизу рабочей папки, найдите и выберите корешок Modulel, и вы увидите листинг вашей новой процедуры (см. рис. 1.3). f Q<;' \ > ,, . Microsoft ^xcel AMOH11.XLS |~р]ёЭД |a|Ei|y| RH E0 ЙШВ ®ajg [ [ IftlI Iftl|■ Ы|м||е|«|д|^|[sT%|, |^|^|[дЦ!№1 Г£1д1 l*l. „1 1 Таблица_погашения_ссуды Иасго ' Создание таблицы погашения ссуды за 180 месяцев. Sub Таблица_погашения_ссуды() Range("Al").Select ActiveCell.FormulaRlCl = "Таблица погашения ссуды1 Range("DI").Select ActiveCell.FormulaRlCl Range("D2").Select Act iveCe11.FormulaRlCl Range("D3").Select Act iveCe11.FormulaRlCl Range("El").Select Act iveCe11.FormulaRlCl Range("El").Select Select ion.NumberFormat Range("E2").Select Act iveCe11.FormulaRlCl "Процентная ставка1 "Сумка платежа" "0.06751 10000" 1<|<|НН|/ Sheetl4 / Sheet! 5 / Sheet16 \ИдФАв1/ Ready Рис. 1.3. Процедура, записанная в Modulel, позволяет воссоздать таблицу погашения ссуды 24
Глава I. Добро пожаловать в Visual Basic for Applications ' Таблица_погашения_ссуды Macro ' Создание таблицы погашения ссуды за 180 месяцев. 9 Sub Таблица_погашения_ссуды() Range (’’Al" ) . Select ActiveCell.FormulaRlCl = "Таблица погашения ссуды" Range("DI").Select ActiveCell.FormulaRlCl = "Процентная ставка" Range("D2").Select ActiveCell.FormulaRlCl = "Размер ссуды" Range("D3").Select ActiveCell.FormulaRlCl = "Сумма платежа" Range("El").Select ActiveCell.FormulaRlCl = "0.0675" Range("El").Select Selection.NumberFormat = "0.00%" Range("E2").Select ActiveCell.FormulaRlCl = "10000" Range("E2").Select Selection.NumberFormat = "$#,##0.00_);($#,##0.00)" Range("E3").Select ActiveCell.FormulaRlCl = "=PMT(R[-2]C/12,180,R[-1]C, 0) Range("B5").Select ActiveCell.FormulaRlCl = "Месяц" Range("C5").Select ActiveCell.FormulaRlCl = "Интерес" Range("D5") .Select ActiveCell.FormulaRlCl = "Погашение ссуды" Range("E5").Select ActiveCell.FormulaRlCl = "Дебетовое сальдо" Range("E6").Select Columns("D:D").Columnwidth = 18 Columns ( "E : E" ). ColumnWidth = 18 Range("B6").Select ActiveCell.FormulaRlCl = "1" Range("B7").Select ActiveCell.FormulaRlCl = "2" Range("B6:B7").Select 25
Visual Basic for Applications в примерах Selection.AutoFill Destination:=Range("B6:B185"), Type:= _ xlFillDefault Range("B6:B185").Select ActiveWindow.ScrollRow = 1 Range(”C6”).Select ActiveCell.FormulaRlCl = "=ROUND(R[-5]C[2]*R[-4]C[2]/12,2) " Range("D6").Select ActiveCell.FormulaRlCl = "=-R3C5-RC[-1]" Range("E6").Select ActiveCell.FormulaRlCl = "=R[-4]C-RC[-1]" Range("C7”).Select ActiveCell.FormulaRlCl = "=ROUND(R1C5*R[-1]C[2]/12,2)" Range("D6").Select Selection.AutoFill Destination:=Range("D6:D7”) , _ Type:=xlFillDefault Range("D6:D7”).Select Range("E7”).Select ActiveCell.FormulaRlCl = "=R[-1]C-RC[-1] ” Range("C7:E7").Select Selection.AutoFill Destination:=Range("C7:E185") , Type:= _ xlFillDefault Range(”C7:E185”).Select ActiveWindow.ScrollRow = 1 Range("DI:E3").Select With Selection.Borders(xlLeft) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlRight) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlTop) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlBottom) .Weight = xlThin .Colorindex = xlAutomatic End With 26
Глава 1. Добро пожаловать в Visual Basic for Applications • Selection.BorderAround Linestyle:=xlNone Range (”B5 : E5 ’’) . Select Selection.Borders(xlLeft).LineStyle = xlNone Selection.Borders(xlRight).LineStyle = xlNone Selection.Borders(xlTop).LineStyle = xlNone Selection.Borders(xlBottom).LineStyle = xlNone Selection.BorderAround Weight:=xlThin, ColorIndex:=xlAutomatic Range(”B6:E185").Select Selection.Borders(xlLeft).LineStyle = xlNone Selection.Borders(xlRight).LineStyle = xlNone Selection.Borders(xlTop).LineStyle = xlNone Selection.Borders(xlBottom).LineStyle = xlNone Selection.BorderAround Weight:=xlThin, Colorindex:=xlAutomatic With ActiveWindow .DisplayGridlines = False .DisplayHeadings = False End With End Sub Примечание: Обратите внимание на то, что пять строк листинга оканчиваются символом подчеркивания. В Excel это символ продолжения строки. Когда строка оканчи¬ вается данным символом, следующая строка рассматривается как ее продолжение, как будто она записана вместо символа подчеркивания. Выполнение процедуры Теперь, когда процедура создана, ее можно выполнить, чтобы воссоздать таблицу погашения ссуды. Для этого переключитесь на пустую таблицу, например Sheet2, в вашей рабочей папке и выберите директиву Tools/Macro. В диалоговом окне Macro выберите макрос Таблица_погашения_ссуды и нажмите командную кнопку Run. Будет автоматически воссоздана таблица погашения ссуды. Дополнение процедуры Убедившись, что таблица воссоздана, обратите внимание на маленькую проблему: вы разместили в колонке С формулу для вычисления ежемесячного ссудного процента, но не преобразовали эту колонку в формат представления денежных величин. Нужно ли переписывать весь макрос для устранения этого упущения? Нет, пусть это сделает Excel, внеся изменения в записанную процедуру. Необходимо лишь преобразовать 27
Visual Basic for Applications в примерам ячейки С6 и С7 в формат представления денежных единиц перед тем, как С7 будет скопирована в остальную часть таблицы (между шагами 25 и 26). Чтобы добавить в процедуру недостающую часть, выполните следующие действия: 1. Используйте корешок Modulel и найдите оператор, который выбирает ячейки С7:Е7 (второй оператор в следующем фрагменте листинга): ActiveCell.FormulaRlCl = "=R[-1]C-RC[-1]" Range("C7:E7").Select Selection.AutoFill Destination:=Range("C7:E185"), _ Type:=xlFillDefault 2. Поместите курсор в начало оператора Range("C7:E7").Select. 3. Выберите директиву Tools/Record Macro/Mark Position for Recording. Таким образом вы сообщите Excel, с какого места нужно начать запись. 4. Выберите корешок Sheet2. 5. Выберите директиву Tools/Record Macro/Record At Mark. Это запустит рекордер. 6. Выберите ячейки С6:С7, выберите директиву Format/Cel Is, укажите корешок Number, выберите Currency Category, затем используйте код $#,##0.00_); ($#,##0.00). Нажмите командную кнопку ОК. 7. Нажмите командную кнопку Stop и сохраните рабочую папку. Таблица еще не совсем корректна, так как исправлены лишь форматы ячеек С6 и С7. В тексте процедуры, содержащемся на странице Modulel, вы обнаружите изме¬ нения в том месте, куда в шаге 2 был помещен курсор. Ниже приведен текст исправленной процедуры; добавленные строки выделены жирным шрифтом: ' Таблица_погашения_ссуды Macro г Создание таблицы погашения ссуды за 180 месяцев. Sub Таблица_погашения_ссуды() Range("Al").Select ActiveCell.FormulaRlCl = "Таблица погашения ссуды" Range("Dl").Select ActiveCell.FormulaRlCl = "Процентная ставка" Range("D2").Select ActiveCell.FormulaRlCl = "Размер ссуды"
Глава 1. Добро пожаловать в Visual Basic for Applications Range("D3").Select ActiveCell.FormulaRlCl = "Сумма платежа" Range("El").Select ActiveCell.FormulaRlCl = "0.0675" Range("El").Select Selection.NumberFormat = "0.00%" Range("E2").Select ActiveCell.FormulaRlCl = "10000" Range("E2").Select Selection.NumberFormat Range("E3").Select ActiveCell.FormulaRlCl Range("B5").Select ActiveCell.FormulaRlCl Range("C5").Select ActiveCell.FormulaRlCl Range("D5").Select ActiveCell.FormulaRlCl Range("E5").Select ActiveCell.FormulaRlCl = "$#,##0.00_); ($#,##0.00)" = "=PMT(R[-2]C/12,180, R[-1]C, 0)" = "Месяц" = "Интерес" = "Погашение ссуды" = "Дебетовое сальдо" Range("Е6").Select Columns("D:D").Columnwidth = 18 Columns("E:E").Columnwidth = 18 Range("B6").Select ActiveCell.FormulaRlCl = "1" Range("B7").Select ActiveCell.FormulaRlCl = "2" Range("B6:B7").Select Selection.AutoFill Destination:=Range("B6:B185"), Type:= xlFillDefault' Range("B6:B185").Select ActiveWindow.ScrollRow = 1 Range("C6" ) .Select ActiveCell.FormulaRlCl = "=ROUND(R[-5]C[2]*R[-4]C[2]/12,2) Range("D6").Select ActiveCell.FormulaRlCl = "=-R3C5-RC[-1]" Range("E6") .Select ActiveCell.FormulaRlCl = "=R[-4]C-RC[-1]" Range("C7").Select ActiveCell. FormulaRlCl = "=ROUND (R1C5*R [-1 ] C [2 ] /12,2 ) " 29
Visual Basic for Applications в примерах Range("D6").Select Selection.AutoFill Destination:=Range(”D6:D7”) , _ Type:=xlFillDefault Range("D6:D7”).Select Range("E7").Select ActiveCell.FormulaRlCl = "=R[-1]C-RC[-1] " Range("C6:C7").Select Selection.NumberFormat = "$#,##0.00_) ; ($#,##0.00) Range("C7:E7").Select Selection.AutoFill Destination: =Range (”C7:E185’’) , Type:= _ xlFillDefault Range("C7:E185”).Select ActiveWindow.ScrollRow = 1 Range("DI:E3").Select With Selection.Borders(xlLeft) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlRight) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlTop) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlBottom) .Weight = xlThin .Colorindex = xlAutomatic End With Selection.BorderAround LineStyle:=xlNone Range("B5:E5").Select Selection.Borders(xlLeft).LineStyle = xlNone Selection.Borders(xlRight).LineStyle = xlNone Selection.Borders(xlTop).LineStyle = xlNone Selection.Borders(xlBottom).LineStyle = xlNone Selection.BorderAround Weight:=xlThin, Colorindex:=xlAutomatic Range("B6:E185").Select Selection.Borders(xlLeft).LineStyle = xlNone Selection.Borders(xlRight).LineStyle = xlNone 30
Глава L Добро пожаловать в Visual Basic for Applications Selection.Borders(xlTop).Linestyle = xlNone Selection.Borders(xlBottom).LineStyle = xlNone Selection.BorderAround Weight: =xlThinz, ColorIndex:=xlAutomatic With ActiveWindow .DisplayGridlines = False .DisplayHeadings = False End With End Sub Чтобы выполнить исправленную процедуру, переключитесь на чистую таблицу (например Sheet3), выберите директиву Tools/Macro, процедуру Таблица_погаше- ния_ссуды и затем нажмите командную кнопку Run. Получите таблицу с корректными форматами (см. рис.1.4). Обратите внимание на переформатированную колонку С (Интерес). В , . ь Microsoft Excel -AMORT1.XLS , ИН -1 Eile Edit View Insert Format Tools Data Window j Llelp | Arial Cyr ъ 0 [ip j ±j Г$Т%77 I +.0 I .00 I I .00 I ♦ .0 ! А1 Itl | Таблица погашения ссуды Таблица погашения ссуды Процентная ставка 6.75% Г± Размер ссуды $10,000.00 Сумма платежа ■ ($88.49) Месяц Интерес Погашение ссуды Дебетовое сальдо 1 $56.25 $32.24 $9,967.76 2 $56.07 $32.42 $9,935.34 3 $55.89 $32.60 $9,902.74 4 $55.70 $32.79 $9,869.95 .5 $55.52 $32.97 $9,836.98 б $55.33 $33.16 $9,803.81 7 $55.15 $33.34 $9,770.47 8 $54.96 $33.53 $9,736.94 9 $54.77 $33.72 $9,703.22 10 $54.58 $33.91 $9,669.31 11 $54.39 $34.10 $9,635.21 12 $54.20 $34.29 $9,600.92 13 $54.01 $34.48 $9,566.44 14 $53.81 $34.68 $9,531.76 |<|ф|н \ Sheetl / Sheet2 \Sheet3/ Sheet4 / Sheet5 / Shf >| SHHS S-r -! i S ** □ .Ъ J Ready Рис. 1.4. Таблица погашения ссуды, созданная скорректированной процедурой 31
Visual Basic for Applications в примерах Изучение и редактирование процедуры Рассмотрим процедуру по частям, чтобы понять, как она работает и как ее можно редактировать. Процесс редактирования записанной процедуры в общем виде вклю¬ чает в себя удаление ненужных операторов, включенных в нее рекордером. Например, когда закрывается диалоговое окно, в процедуру могут быть включены все опции диалогового окна, хотя изменилась лишь одна из них. Кроме того, когда вы записы¬ ваете что-либо в ячейку и нажимаете клавишу Enter, курсор, отмечающий следующую позицию ввода, смещается вниз на одну ячейку. Таким образом, потребуется повторно выбрать ячейку, чтобы ее отформатировать. Заполняя ячейку вводимым текстом, процедура не требует нажатия клавиши Enter. При нажатии клавиши Enter в процедуру включается оператор выбора следующей ячейки, хотя в этом нет необхо¬ димости. Совет: Если не нажимать клавишу Enter, а воспользоваться кнопками управления в строке формулы, то позиция ввода не переместится на одну ячейку вниз и дополнительные операторы в процедуре не появятся. Первые пять строк в процедуре —это строки комментария. В строке программы комментарий маркируется апострофом, и ту часть строки, которая находится справа от апострофа, Visual Basic игнорирует. Комментарии могут находиться в любом месте процедуры, в том числе и справа от исполняемого оператора. Обратите внимание на то, что в первые пять строк листинга включены наименование и описание процедуры, заданные в диалоговом окне Record New Macro. Следующие пять строк комментария —это заголовок процедуры, который как бы является оператором ее наименования и управления процессом записи: ' Таблица_погашения_ссуды Macro ' Создание таблицы погашения ссуды за 180 месяцев. г Sub Таблица_погашения_ссуды() Следующие два оператора выбирают ячейку А1 и помещают в нее текст "Таблица погашения ссуды" Range("Al").Select ActiveCell.FormulaRlCl = "Таблица погашения ссуды" 32 1 Вильям Дж. Орвис
Глава I. Добро пожаловать в Visual Basic for Applications Оператор RangeO.Select использует объект Range для адресации ячейки А1 в текущей таблице. Метод Select выбирает эту ячейку. Объекты и методы рассматри¬ ваются в следующих главах. Вторая строка использует свойство ActiveCell для ссылки на ячейку А1 (текущую активную ячейку) и свойство FormulaRlCl для ввода текста в ячейку. Свойства также будут рассмотрены в следующих главах. Следующие шесть операторов повторяют описанные действия для ячеек Di, D2 и D3: Range("DI").Select ActiveCell. FormulaRlCl = ’’Процентная ставка’’ Range(”D2").Select ActiveCell. FormulaRlCl = ’’Размер ссуды’’ Range (’’D3" ) . Select ActiveCell.FormulaRlCl = "Сумма платежа" Следующие два оператора помещают число 0.0675 в ячейку Е1 таким же путем, но два следующих оператора повторно выбирают ячейку Е1, чтобы затем переформа¬ тировать ее в процентное представление: Range("El").Select ActiveCell.FormulaRlCl = "0.0675" Range("El") .Select Selection.NumberFormat = "0.00%" Свойство Selection адресует текущий выбор, а свойство NumberFormat устанавли¬ вает формат выбранной ячейки. Эти операторы появились в результате того, что после записи в ячейку числа 0.0675 была нажата клавиша Enter, что переместило указатель активной ячейки на одну строку вниз. Ячейку Е1 теперь нужно выбрать повторно, чтобы изменить ее формат. Так как действия операторов Visual Basic не изменяют состояния активности ячейки, выделенный жирным шрифтом оператор можно удалить без ущерба для процедуры. Следующие четыре оператора помещают число 10000 в ячейку Е2 и преобразовы¬ вают его в формат представления денежных единиц: Range("Е2") .Select ActiveCell.FormulaRlCl = "10000" Range ("E2") .Select Selection.NumberFormat = ’’$#, ##0.00_) ; ($#, ##0.00) ’’ 2 Вильям Дж. Орвис 33
Visual Basic for Applications в примерах Очевидно, что и здесь оператор, выделенный жирным шрифтом, можно удалить Следующие две строки выбирают ячейку ЕЗ и помещают в нее формулу (такиь же образом, как текст или число) для вычисления размера ежемесячного платежа: Range("ЕЗ”).Select ActiveCell.FormulaRlCl = "=РМТ(R[-2]C/12,180,R[-1]С,0)" Следующие несколько строк помещают заголовки: Range("В5”).Select ActiveCell.FormulaRlCl = ’’Месяц” Range(”С5”).Select ActiveCell.FormulaRlCl = "Интерес” Range("D5").Select ActiveCell.FormulaRlCl = "Погашение ссуды” Range("E5").Select ActiveCell.FormulaRlCl = "Дебетовое сальдо" Range(”E6”),Select Последняя строка, выбирающая ячейку Е6, появилась вследствие нажатия клави¬ ши Enter, и ее можно удалить. Следующие два оператора изменяют ширину колонок D и Е: Columns("D:D").ColumnWidth = 18 Columns("Е:Е").Columnwidth = 18 Набор Columns выбирает колонку, а свойство ColumnWidth изменяет ее ширину Следующие четыре оператора помещают 1 в ячейку В6 и 2 в ячейку В7. Пяты! оператор выбирает группу ячеек В6:В7, а шестой оператор выполняет директиву Edi; Fill Down, создавая список месяцев и автоматически заполняя ячейки целыми числам] в интервале от 1 до 180: Range("В6”).Select ActiveCell.FormulaRlCl = ”1” Range(”В7").Select ActiveCell.FormulaRlCl = "2" Range("B6:B7").Select Selection.AutoFill Destination:=Range("B6:B185") , Type:- _ xlFillDefault Range(”B6:B185").Select 34
Глава I. Добро пожаловать в Visual Basic for Applications Следующие строки текста процедуры повторяют описанные действия для ячеек С6, D6, Е6, С7, D7 и Е7, а затем копируют эти формулы в тело таблицы. Строки, выделенные жирным шрифтом, можно удалить без ущерба для процедуры. ActiveWindow.ScrollRow = 1 Range("Сб").Select ActiveCell.FormulaRlCl = "=ROUND (R [-5] C [2] *R [-4] C [2]/12,2) " Range("06").Select ActiveCell.FormulaRlCl = "--R3C5-RC[-1]" Range("E6").Select ActiveCell.FormulaRlCl = "=R[-4]C-RC[-1]" Range("C7").Select ActiveCell.FormulaRlCl = "=ROUND(R1C5*R[-1]C[2]/12,2)" Range("D6").Select Selection.AutoFill Destination:=Range("D6:D7"), _ Type: =x.lFillDefault Range ("D6:D7").Select Range("E7").Select ActiveCell.FormulaRlCl = "=R[-1]C-RC[-1]" Range("C7:E7").Select Selection.AutoFill Destination:=Range("C7:E185") , Tvpe:= _ xlFillDefault Range ("C7 :E185") .Select Обратите внимание на то, что формулы в строках 3 и 5 записаны в формате R1C1, а не в формате Л1. Excei делает это автоматически, даже если в данной рабочей папке был использован формат А1. Следующие строки возвращают таблицу к началу. Затем идут несколько строк, содержащих оператор With: ActiveWindow. ScrollRow = 1 Range("DI:ЕЗ").Select With Selection.Borders(xlLeft) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlRight) .Weight = xlThin .Colorindex = xlAutomatic End With With Selection.Borders(xlTop) 35
Visual Basic for Applications в примерах .Weight = xlThin . Colorindex = xlAutomatic End With With Selection.Borders (xlBottom) .Weight = xlThin .Colorindex = xlAutomatic End With Оператор With облегчает ввод текста программы, улучшает ее читабельность, углубляет понимание. Действия операторов, расположенных между With и End With эквивалентны тому, как если бы все, что стоит справа от оператора With, было записано в этих операторах слева от десятичной точки. Выделенное жирным шрифтом можно удалить без ущерба для процедуры. Окончание процедуры размещает рамку вокруг ячеек, отключает координатную линейку и линии сетки, а также делает активной ячейку Л1. Все, что выделено жирным шрифтом, может быть удалено. Selection.BorderAround LineStyle:=xlNone Range("B5:E5”).Select Selection.Borders (xlLeft) .LineStyle - xlNone Selection.Borders (xlRight) .LineStyle = xlNone Selection.Borders (xlTop) .LineStyle = xlNone Selection. Borders (xlBottom) .LineStyle = xlNone Selection.BorderAround Weight:=xlThin, Colorindex:=xlAutomatic Range("B6:E185”).Select Selection. Borders (xlLeft) . LineStyle = xlNone Selection.Borders (xlRight) .LineStyle = xlNone Selection.Borders (xlTop) .LineStyle = xlNone Selection.Borders (xlBottom) . LineStyle = xlNone Selection.BorderAround Weight:=xlThin, Colorindex:=xlAutomatic With ActiveWindow .DisplayGridlines = False .DisplayHeadings = False End With End Sub 36
Глава L Добро пожаловать в Visual Basic for Applications Подключение процедуры к объектам таблицы Редко используемые процедуры можно запускать директивой Tools/Macro, но эта директива становится весьма неудобной в случае частого выполнения процедур. Для решения этой проблемы можно подключить процедуру к меню или другому объекту таблицы. Подключение процедуры к командной кнопке Процедуру можно подключить к любому из объектов таблицы. Чтобы подключить процедуру к командной кнопке, выполните следующие действия: 1. Выберите корешок Sheet4. 2. Выберите директиву View/Toolbars, отметьте контрольный индикатор Drawing Toolbar, затем нажмите командную кнопку ОК. 3. Нажмите командную кнопку Create Button и нарисуйте командную кнопку в правом верхнем углу таблицы, нажмите эту командную кнопку и, удерживая кнопку мыши нажатой, перетащите ее на удобное для вас место. Когда появится диалоговое окно Assign Macro, выберите Таблица_погашения_ссуды, затем нажми¬ те командную кнопку ОК. 4. Если новая командная кнопка не окажется сразу выбранной, то выберите ее, нажав командную кнопку Select пиктографического меню для включения средства выбо¬ ра, а затем выберите новую командную кнопку. Если вы вначале не включили средства выбора, то нажатие на новую кнопку сразу выполняет подключенную процедуру вместо того, чтобы выбрать командную кнопку. 5. Измените название командной кнопки на ’’Таблица ссуды", затем отмените выбор. 6. Сохраните рабочую папку. Теперь нажмите эту командную кнопку и процедура будет выполнена, создав таблицу погашения ссуды, изображенную на рис. 1.5. 37
Visual Basic for Applications в примеpax Рис. 1.5. Нажатием командной кнопки в таблице будет создана таблица погашения ссуды После того как все необходимые вам объекты размещены в таблице, можнс погасить пиктографическое меню Drawing. Подключение процедуры к командной кнопке пиктографического меню Процедуру можно подключить и к пиктографическому меню. Для этого нужж разместить в нем новую пиктограмму, а затем подключить к ней процедуру. Чтобь: подключить процедуру к пиктографическому меню, выполните следующие действия: 1. Выберите корешок Sheet5. 2. . Выберите директиву View/Toolbars, затем нажмите командную кнопку Customize Когда откроется диалоговое окно Customize, вначале выберите категорию Custom, а затем —пользовательскую пиктограмму и перетащите ее на свободное место в пиктографическом меню Drawing. Как только новая пиктограмма будет размещена в пиктографическом меню, автоматически появится диалоговое окно Assign Macro Выберите Таблица_погашения_ссуды и нажмите командную кнопку ОК. Когда вновь появится диалоговое окно Customize, нажмите командную кнопку Close. 38
Глава L Добро пожаловать в Visual Basic for Applications 3. Сохраните рабочую папку. Теперь пиктографическое меню Drawing содержит новую пиктограмму. При нажатии на эту пиктограмму в активной таблице будет воссоздана таблица погашения ссуды (рис. 1.6). На новой пиктограмме изображено улыбающееся лицо (в нижнем правом углу пиктографического меню). в.-> Microsoft Excel - AMORT1 .XLS : □□ -1 file Edit View insert Format Jools Data Window Help | D |в^| Е)1 HQd^lI & РЯЫ lz l^”l 1Щ1 Ё'И|100* 1Г±1 | Arial Cyr ||ij|io IliJlBI/I 7]адм4||адъ|4] А1 |<| | Таблица погашения ссуды погашения ссуды Процентная ставка 6.75% Размер ссуды $10,000.00 Сумма платежа ($88.49) Месяц Интерес Погашение ссуды Дебетовое сальдо 1 $56.25 $32.i 2 $56.07 $32> 3 $55.89 $32Т 4 $55.70 $32.7 5 $55.У2 $32 с 6 $55.33 $33.1 7 $55.15 $33.2 8 $54.96 $33.f У $54.77 $33./ 10 $54.58 $33.£ 11 $54.39 $34.1 12 $54.20 $34.: 13 $54 01 14 $53.81 №1 15№№| [□]№(©] И | <|►! MKSheeH^ Shed2 / Sheet3 / Sheet4 Shj + [ Ready Рис. 1.6. Таблица погашения ссуды создается нажатием на новую пиктограмму пиктографического меню Dra wing Подключение процедуры к меню Когда вы впервые создавали процедуру Таблица_погашения_ссуды, в диалоговом окне Record New Macro была опция, позволяющая подключить данную процедуру к меню. Чтобы сделать это сейчас, задайте директиву Tools/Macro, выберите макрос, затем нажмите командную кнопку Options. Появится диалоговое окно того же вида, что и на рис. 1.1. В окне Assign То отметьте контрольный индикатор Menu Item on Tools Menu, запишите "Amort” в текстовое окно рядом с контрольным индикатором, затем нажмите командную кнопку ОК. Когда появится диалоговое окно Macro, нажмите командную кнопку Close. В меню Tools добавится новый элемент Amort. Выбор данного элемента воссоздаст таблицу погашения ссуды. 39
Visual Basic for Applications в примерах Подключение процедуры к графическому объекту Процедуру можно подключить к любому графическому объекту таблицы —таким ж образом, как и к командной кнопке. Это относится ко всем линиям, окружностям и окнам которые можно создать с использованием пиктографического меню Drawing, а также: любому графическому образу. Нужно просто выбрать этот объект, затем выбрат директиву Tools/Assign Macro, выбрать процедуру и нажать командную кнопку ОК. После подключения процедуры к графическому объекту нет необходимости пере мещать этот объект в какое-то особое место на экране. В любом месте экрана можнс нажав на данный объект, выполнить подключенную к нему процедуру. Чтобы выбрат указанный объект и переместить, изменить или освободить его от макроса, необходим! включить средство выбора нажатием командной кнопки Select пиктографическоп меню Drawing. Теперь можно выбирать данный графический объект без выполнена подключенной к нему процедуры. Кроме того, если вы нажмете клавишу Ctrl и удерживая ее нажатой, нажмете на объект, то также выберете объект без выполнени! подключенной процедуры. Заключение Эта глава ознакомила вас с Visual Basic for Applications и его расширениями позволяющими подключатся к Excel и управлять им. Вы использовали макрорекорде; для записи пользовательской процедуры, редактировали эту процедуру, а зато подключали ее к различным объектам таблицы.В следующих главах описаны объек тная реализация Visual Basic фирмы Microsoft и операторы языка, использованные i записанной процедуре. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Какой символ используется в Visual Basic для продолжения оператора, занимаю щего более одной строки? 2. В каких трех местах может быть сохранен новый макрос? 3. Объясните разницу между абсолютными и относительными ссылками. Упражнения для закрепления материала 1. Включите макрорекордер и запишите процесс создания вашей любимой таблицы Excel Затем проверьте листинг процедуры и пошагово сравните его с тем, что вы делали. 2. Включите макрорекордер, введите в таблицу несколько чисел, а затем используйт их для создания графика. Проверьте текст программы, записанной макрорекорде ром в процессе создания и редактирования графика. 40
Глава 2 Объекты, свойства и методы Предыдущая глава позволила бегло ознакомиться с языком Visual Basic и возмож¬ ностями доступа к объектам Excel для управления таблицей, которыми он располагает. Но что такое объекты? Является ли это новым термином из программистского жаргона, используемым, чтобы произвести впечатление на ваших друзей, либо же это что- нибудь реальное? В этой главе описаны реализация объектно-ориентированного программирования (ООП —еще один новый жаргонный термин) в Visual Basic и его применение к Excel. Из этой главы вы узнаете следующее: > Что такое программные объекты > Как Visual Basic использует объекты > Как используются свойства объектов > Как используются методы объектов > Как находить используемые объекты Что такое объекты? Что представляют собой объекты в компьютерном коде? Известно, что такое физический объект. Ваш манипулятор мышь, клавиатура и компьютер — такие же физические объекты, как и ваша чашка кофе. Как же внутри вашего компьютера код превращается в объект? Объектно-ориентированное программирование — это наиболее современный стиль в разработке компьютерных программ. Этот стиль используется потому, что современное конструирование программ пытается добиться определенных специфи¬ ческих целей. Компьютерные программы должны быть: > проверяемыми > модернизируемыми > повторно используемыми > переносимыми Программы должны быть проверяемыми, чтобы вы могли убедиться, что они делают именно то, что должны делать. Программы должны быть модернизируемыми, чтобы вы или кто-либо другой могли понять программу достаточно хорошо, чтобы внести корректные изменения. Программы должны быть повторно используемыми, чтобы вы, лишь однажды написав процедуру, затем могли повторно использовать ее при любой необходимости. И, наконец, программы должны быть переносимыми, чтобы переносить их на разные платформы с минимальными переделками. 41
Visual Basic for Applications в примерах Современная практика программирования ищет достижения этих целей в модуль¬ ности компьютерных программ. Модульные компьютерные программы при разработке разбиваются на отдельные части, именуемые модулями. Каждый модуль выполняет специфические, строго определенные функции преобразования и имеет доступ только к тем данным, которые необходимы для данного преобразования. Код модуля, разработанный со строго определенным интерфейсом для других модулей программы, очень легок для отладки, сопровождения и понимания. Другим аспектом модульности является его замкнутость, придающая уверенности в том, что любые изменения, которые вы вносите в код модуля, окажут влияние только на функции этого модуля и ни на что другое. Таким образом, эти особенности облегчают отладку, сопровождение и понимание кода. Объектно-ориентированное программирование максимально использует принцип модульности. Программный объект в ООП называется "контейнером". Контейнер включает в себя данные и код, который знает, как манипулировать этими данными. Другими словами, программный объект — это просто блок, состоящий из кода и данных. Он может быть представлен как реальный физический объект, например в виде командной кнопки. Программный объект обладает специфической функцией преобразования и содержит весь необходимый код и все необходимые данные для реализации этой функции, но его внутренняя работа скрыта от постороннего глаза. В более традиционном программировании компьютерная программа разрабатыва¬ лась в виде функциональных наборов строк, в которых блоки данных передавались от модуля к модулю и каждый модуль модифицировал или использовал эти данные своим способом. Модули не содержали данных, а только коды для модификации любых переданных данных. Если программа случайно передаст такой процедуре неверные данные, процедура все равно обработает их, возвратив вам "мусор", или вообще вызовет аварию вашей системы. В объектно-ориентированном программировании данные и код, который манипу¬ лирует этими данными, объединены в структуре, называемой объект. Вместо того, чтобы передавать данные от модуля к модулю для выполнения различных вычислений, вы посылаете сообщение объекту, содержащему эти данные, и таким образом поручаете ему произвести вычисления. Передать объекту плохие данные невозможно, ибо данные, с которыми объект манипулирует, включены в этот объект. Более того, нельзя вызвать или выполнить объект — можно только послать ему сообщение с вежливой просьбой выполнить одну из решаемых им задач (конечно, если объект не изыщет возможности осуществить требуемую манипуляцию, то, скорее всего, ему придется вежливо сообщить вам, что он не может удовлетворить ваш запрос). Общие примеры объектов Visual Basic и Excel — это таблицы, области ячеек, командные кнопки, текстовые окна, рабочие папки, диаграммы и модули. 42
Глава 2. Объекты, свойства и методы Свойства и методы Программный объект обладает определенными свойствами и методами. Свойства — это видимые характеристики объекта, а методы — операции преобразования этих данных. Видимыми характеристиками называются данные, которые могут быть доступны вне объекта. Свойствами считаются данные, которыми объект манипулирует или которые позволяют контролировать, как объект выглядит или как он себя ведет. Например, свойство Value текстового окна —это текст, который вводится в это окно. Свойство шрифта Color управляет тем, как он выглядит, а свойство элемента меню Enabled управляет доступностью выбора этого элемента. Вызвать объект таким же образом, как и подпрограмму невозможно. Для измене¬ ния одного из свойств объекта или для выполнения одного из его методов необходимо послать объекту сообщение. Доступ к свойствам и методам, содержащимся в объекте, можно получить, указав имя этого свойства или метода после имени объекта. Имя объекта отделяется от имени свойства или метода точкой. Например, если Power является объектом, то доступ к его свойству Value обеспечит следующая конструкция: Power, Value Если продолжить эту конструкцию знаком равенства (=) и числом, то объект изменит значение указанного свойства. Чтобы получить значение этого свойства, поместите эту конструкцию справа от знака равенства. Когда выполняется метод, он может изменить значение лишь свойства данного объекта, но не других объектов. Метод может только запросить какой-нибудь объект об изменении некоторого его свойства. Рассмотрим в качестве примера объекта кнопку включения питания на мониторе' вашего компьютера (может быть у вас вместо кнопки выключатель, но в данном случае это не имеет значения и можно представить себе, что это кнопка). Когда вы нажимаете эту кнопку, внутренний механический соединитель производит включение, подсоеди¬ няя вход к выходу. Также абсолютно не важно, какой вид выключателя использован в вашем мониторе (известно, что существует большое количество различных видов выключателей). И то, как работает ваш выключатель, и его устройство вас не должны интересовать (пластиковая оболочка выключателя предохраняет его от вашего любо¬ пытства). Важно лишь то, соединены ли вход и выход переключателя в зависимости от состояния кнопки. Программная реализация такого переключателя в основном работает точно так же. Когда вы выбираете его с помощью мыши, он меняет свое состояние с "Включен” на "Выключен" или наоборот. Программный переключатель не обеспечивает механичес¬ кого соединения, но обладает свойством, именуемым Value, которое принимает значение "Включен" или "Выключен". Оба выключателя предназначены для одних и тех же целей. 43
Visual Basic for Applications в примерах Нажимая на кнопку с помощью указателя мыши, вы изменяете значение свойства Value с "Включен” на "Выключен” или наоборот. Кроме того изменяется внешний вид кнопки, имитируя таким образом нажатие. Само нажатие на кнопку ничего не включает (равно как и нажатие выключателя монитора). Выключатель на вашем мониторе соединен шнуром с источником питания. Механический переключатель просто соединяет вход и выход. Программная кнопка делает то же самое. Она не устанавливает ваш виртуальный монитор в состояние "Включен” или "Выключен", а лишь изменяет свойство Value на "Включен" или "Выключен". Другие объекты, например объект "Виртуальный экран", должны прове¬ рить состояние указанного свойства, чтобы определить, включаться или нет. Наследование Точно так же, как объект-выключатель является частью объекта-монитора, про¬ граммный объект может быть частью другого, большего программного объекта. Когда объекты объединены, проявляются специальные свойства, известные под названием "наследование". Когда один объект является частью другого объекта, он наследует все свойства и методы предыдущего, взамен разрешая доступ к своим свойствам и методам. Этот принцип присущ и механическому переключателю (являясь частью монитора, он посредством нажатия кнопки передает монитору по наследству свое свойство "Включен"/"Выключен" и метод изменения этого свойства). Для доступа к свойствам и методам объекта, являющегося составной частью других, более крупных объектов, нужно определить каждый из сборных объектов начиная с самого крайнего контейнера, а затем поставить точку и указать следующий (внутренний) составной объект. Затем опять точка —и следующий внутренний объект, и так до тех пор, пока не дойдете до объекта, к свойствам или методам которого вы пытаетесь добраться. Таким образом, получился список объектов, указанных слева направо в следующем порядке: от самого крайнего (главного) и вплоть до интересую¬ щего объекта. Завершают эту конструкцию точка и имя свойства или метода, к которому вы хотите получить доступ. Например, объект-монитор, именуемый MyMonitor, включает объект-кнопку Power, следовательно, MyMonitor наследует свойство Value объекта Power. Для доступа к свойству Value используется следующая конструкция: MyMonitor.Power.Value Если MyMonitor содержится в объекте MyHouse, ваш доступ может быть офор¬ млен следующей конструкцией: MyHouse.MyMonitor.Power.Value 44
Глава 2. Объекты, свойства и методы И так можно продолжать для каждого включенного объекта. Объект MyHouse теперь унаследовал возможности кнопки. Классы Каждый объект специфичен и уникален. Класс же описывает все объекты опреде¬ ленного типа. Таким образом классы являются более высоким уровнем определения объекта. Вы используете класс для создания объектов. Например, класс Button вы используете для создания специфических объектов Button. Каждый объект Button, создаваемый вами, отличается от всех других объектов Button. Кнопка Power 1 является объектом класса Button, так же, как и кнопка Power2. Хотя и Powerl, и Power2 принадлежат классу Button, это различные объекты. Поиск объектов Поиск объектов в Visual Basic —зачастую не простое занятие, так как программы на Visual Basic могут объединяться с несколькими прикладными программами, каждая из которых имеет собственную библиотеку объектов. Интерактивная подсказка Использование интерактивной подсказки — едва ли не самый легкий способ обнаружить объект. Окно Help не только содержит перечень объектов, но и расскажет вам, что они собой представляют и как их использовать. Интерактивная подсказка вызывается, как и любая прикладная программа под Windows, следовательно вы можете открыть несколько файлов Help одновременно. Дополнительно, когда отобра¬ жается подсказываемая тема, нажатием командной кнопки On Тор вы можете сохра¬ нить окно Help в верхней части вашей таблицы, чтобы затем читать его в процессе работы. Для доступа к интерактивной подсказке Excel задайте директиву Help/Contents. Появится окно Help, в котором Visual Basic отведен отдельный раздел ’’Programming with Visual Basic”. Чтобы ознакомиться с содержимым этого раздела (рис. 2.1), укажите "Programming with Visual Basic”. Перечень объектов Excel, доступных в Visual Basic, приведен в подразделе "Programming Language Summary”. Примечание: Чтобы использовать возможность Search интерактивной подсказки для поиска тем Visual Basic, вы должны находиться в разделе Visual Basic. Если вы находитесь в разделе Excel, вам будут доступны для поиска только темы Excell 45
Visual Basic for Applications в примерах д^И^МЯ^ИМ|ИИММЫД^^^^^Ддццд17ст№|1 Файл Редактирование Закладка Справка С^аржанне| Цомск j &азад | Хроно яогмя Visual Basic Reference То learn how to use Help, press Fl. An alphabetical listing of all programming language topics including functions, methods, objects, properties, and statements. Functions Methods Objects Properties Statements Keywords by Task An alphabetical listing of functions. An alphabetical listing of methods. An alphabetical listing of objects. An alphabetical listing of properties. An alphabetical listing of statements. A functional listing of keywords by programming task. Reference Information Reference topics including character set, data types, glossary, keywords, operators, screen regions, and trappable errors. Рис. 2.1. Раздел интерактивной подсказки Excel "Visual Basic" Object Browser Еще одним способом обнаружения объектов является использование Object Browser, позволяющего просматривать все доступные объекты Excel, Visual Basic, любых подключенных таблиц и любых других открытых приложений, объекты которых согласуются с операционной системой. Для запуска Object Browser в Excel сделайте активной модульную страницу и укажите директиву View/Object Browser или пиктограмму Object Browser на пиктог¬ рафическом меню Visual Basic. Появится диалоговое окно Object Browser, изображен¬ ное на рис. 2.2. 46
Глава 2. Объекты, свойства и методы JJbr aries/Work books: Objedt Brdvvsfe'r;.-.; ? ш □bjects/Modules: Methods/Properties: Pictures |*j AutoForm at !♦! PivotFieid ■ AutoOu time И PivotFields ■ BorderAround [Jj Pivotitem H Borders И Pivotitems И Calculate ДД PivotT able И PivotT able* H Characters В PlotArea И Checks ped mg H Point Щ1 Clear И Points Д ClearContents U ClearFormats M Rectangle M Clear Notes В Rectangles [*| ClearOutline 1*1 [~/j Cel*(Rowindex, Cokmnlndex) |Show| | Baste | | Close | | llphonv... | I tte,p I Рис. 2.2. Диалоговое окно Object Browser Visual Basic В верхней части диалогового окна Object Browser расположен выпадающий список всех открытых библиотек. Excel и Visual Basic рассматриваются как библиотеки объектов. На рис. 2.2. выбрано приложение Excel. В левой части диалогового окна Object Browser располагается список имен всех объектов выбранной библиотеки, справа —список свойств и методов выбранного объекта. На рис. 2.2 выбран объект Range, который используется для выбора области ячеек таблицы. Если вы выбрали метод, например, метод Cells, как изображено на рис. 2.2, его синтаксис будет изображен в нижней части окна. Если вы нажимаете командную кнопку интерак¬ тивная подсказка предоставит дополнительную информацию о выбранном методе. После того, как нужный объект найден, нажмите командную кнопку Paste. Это позволит включить в вашу программу синтаксическую строку, изображенную в нижней части окна (строка будет включена начиная с текущей позиции курсора). Макрорекордср Применение макрорекордера, описанное в главе 1, является самым простым способом научиться использовать объекты таблицы. Это особенно удобно, когда вы хотите присвоить какое-то значение или изменить формат ячеек. Поместите курсор в то место в вашем модуле, куда нужно вставить программный блок, затем выберите директиву Tools/Record Macro/Mark Position for Recording. Переключитесь в табли¬ цу, в которую вы хотите добавить данные, и задайте директиву Toois/Record Macro/'Record at Mark. Для окончания записи макроса нажмите командную кнопку Stop в плавающем окне. 47
Visual Basic for Applications в примерах Записанные действия будут вставлены в вашу процедуру в виде программного блока на Visual Basic, включающего все объекты, методы и процедуры, которые были использованы для данных действий. Скорректируйте программу и удалите ненужные операторы (см. гл. 1). Заключение В этой главе вы ознакомились с реализацией объектно-ориентированного програм¬ мирования в языке Visual Basic, узнали, как объекты используются для доступа к таблице Excel, что такое свойства и методы объектов и как их применять. В следующих главах вь$ ознакомитесь с Visual Basic более подробно. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Объекты можно рассматривать как "контейнеры". Какие две составляющие они объединяют? 2. Опишите программные объекта. 3. Опишите метод и объясните, как можно получить к нему доступ в программе. 4. Опишите свойство и объясните, как можно получить к нему доступ в программе. Упражнения для закрепления материала 1. Используйте Object Browser для поиска синтаксиса метода Cells объекта Range из Excel, как изображено на рис. 2.2. 2. Используйте Object Browser для поиска синтаксиса функции Cos модуля Math из библиотеки Visual Basic for Applications. 3. Используйте интерактивную подсказку для поиска той же функции, что и в примере 2. 4. Используйте Macro Recorder для записи процесса форматирования некоторых ячеек, затем сравните результат записи с теми действиями, которые вы записали. Попробуйте несколько раз — с различными типами форматирования и разным содержанием ячеек. 48
Глава 3 Доступ к объектам Excel из Visual Basic Excel фирмы Microsoft является библиотекой объектов, которые находятся внутри прикладных программ Excel. Как вы и предполагаете, объектами Excel являются таблицы, рабочие папки, диаграммы и области ячеек. Библиотеки функций таблиц Excel, меню и пиктографические меню также являются объектами. Visual Basic for Applications представляет собой метод, модифицирующий операции Excel. В этой главе рассмотрен интерфейс Visual Basic/Excel —с тем, чтобы узнать, как получить доступ и управлять различными объектами Excel. В этой главе вы изучите: > Как использовать наборы для доступа к объектам > Как использовать объекты Range для доступа к ячейкам таблиц > Как использовать специальные свойства, характеризующие активность, чтобы сделать процедуру более универсальной > Как изменять содержимое и преобразовывать ячейки > Как получить доступ к методам таблицы Использование наборов объектов Для доступа к большинству объектов в Visual Basic используются наборы. Набор — это контейнер для всех объектов определенного класса. Например, набор Workbooks объединяет все текущие открытые рабочие панки, а набор Worksheets — все таблицы в открытых рабочих папках. Поскольку Visual Basic не может получить доступ к рабочим папкам или таблицам по имени, необходимо использовать наборы для присоединения имени объекта. Ниже перечислены несколько наиболее часто используемых наборов: Sheets Workbooks Все страницы любых типов в рабочей папке Все текущие открытые рабочие папки Worksheets Все таблицы в рабочей папке Charts Все страницы диаграмм в рабочей книге Получить доступ к любому компоненту набора можно двумя путями: по номеру либо по имени объекта. Например, если у вас есть рабочая папка с именем Sales, доступ к таблицам этой рабочей папки обеспечивают следующие операторы: Workbooks("Sales") .Worksheets (2) Workbooks("Seles") .Worksheets("West") 49
Visual Basic for Applications в примерах Если West — вторая таблица в наборе Worksheets, то обе приведенные ссылки указывают на одну и ту же страницу. Если при доступе к набору использовать точное имя объекта, то будет обеспечен доступ именно к этому объекту при каждом обращении к набору. Таким образом, прежде, чем обратиться к набору, необходимо знать имя объекта. Используя порядковый номер, можно циклически опрашивать все компоненты набора путем простого изменения значения номера, и тогда нет необходимости знать, как называется объект, чтобы получить к нему доступ. И наконец, если не указывать конкретный объект в наборе, считается, что вам необходим доступ ко всему набору. В этом случае вы можете изменить некоторые глобальные свойства объектов набора, добавить или удалить некоторые объекты. Для доступа к компонентам набора очень удобно использовать оператор For Each языка Visual Basic (см. гл. 14 "Применение циклов объектного типа"). Оператор For Each повторяет блок операторов для каждого компонента набора. Предупреждение: Если к набору добавлен или удален из него один из компонентов, то нумерация других компонентов этого набора может измениться. При попытке обратиться к компонентам этого набора с теми порядковыми номерами, которые использовались до изменения, можно получить доступ не к тому компоненту, который вам нужен. Получение информации о наборе Информация о наборе описывается некоторыми свойствами. Большинство наборов имеют свойство Count, которое содержит количество компонентов в наборе. Чтобы получить имя компонента, выделите из набора единичный объект и проверьте свойство Name этого объекта. Другим удобным свойством является свойство Parent, которое содержит имя объекта, включающего этот набор. Чтобы увидеть, как работают эти свойства, выполните следующие действия: 1. Создайте новую рабочую папку директивой File/New. 2. Создайте модуль директивой Insert/Macro/Module. 3. Запишите в модуль следующую процедуру: Sub Getlnfo() 'Получение информации о наборе. Worksheets("Sheetl").Range("В2").Value = Worksheets.Count • Worksheets("Sheetl").Range("B3") .Value = Worksheets.Parent.Name Worksheets("Sheetl").Range("B4").Value = Worksheets(3).Name End Sub 50
Глава 3. Доступ к объектам Excel из Visual Basic 4. Выполните процедуру, для чего выберите директиву Tools/Macro, выберите Getinfo, нажмите Run; либо нажмите кнопку мыши в любом месте процедуры, а затем —пиктограмму Run на пиктографическом меню Visual Basic. Эта процедура получит значение свойства Count набора Worksheets и поместит его в ячейку В2 таблицы Sheetl. Затем она получит имя объекта Parent и поместит его в ячейку ВЗ, после чего получит имя третьей таблицы набора и поместит его в ячейку В4. Если вы переключитесь на Sheetl, то сможете увидеть значения, записанные в ячейки В2, ВЗ и В4 (рис. 3.1). В В2 содержится количество таблиц в этой рабочей папке (16), в ВЗ —имя рабочей папки (Воок2), в В4 —имя третьей таблицы (Sheet3). Добавление компонентов в набор Чтобы добавить в набор компоненты, используйте метод Add. Формат метода Add зависит от типа набора, с которым вы работаете. Например, когда используется метод Add для набора Workbooks, следует добавить аргумент, определяющий количество таблиц в новой рабочей папке. В большинстве случаев при использовании метода Add новая рабочая папка или таблица по умолчанию становится текущей. 51
Visual Basic for Applications в примерах В процессе работы с набором Workbooks вы можете также использовать метод Open для открытия существующей рабочей папки. Если вы открываете или добавляете другую рабочую папку иными средствами, такими как директива Fiie/Open, то она автоматически добавляется в набор. Удаление компонентов из набора Для удаления компонентов из набора Workbooks используются методы Delete или Close. Если вы применяете для набора метод Delete без указания компонента, то удаляются все компоненты набора. Метод Close закрывает рабочую папку, как и директива File/Close. Если вы закрываете рабочие лапки любым из этих способов, их таблицы автоматически удаляются из набора Worksheets. Чтобы увидеть, как работает механизм удаления и добавления компонентов набора, выполните следующие действия: 1. Выберите страницу Modulel и измените процедуру следующим образом (измене¬ ния предыдущей процедуры выделены жирным шрифтом): Sub Getlnfо() 'Получение информации о наборе. Worksheets (’’Sheet!” ). Range ( "B2" ) .Value = Worksheets . Count Worksheets(”Sheet1").Range(”B3").Value = Worksheets.Parent.Name Worksheets (’’Sheetl” ) . Range (”B4”) .Value = Worksheets (3) .Name ‘ Добавление и удаление компонентов набора Worksheets.Add 'Добавляется новая таблица Worksheets ("Sheetl") .Range ("Вб") .Value = Worksheets . Count Worksheets("Sheetl") . Range ("B7") .Value = Worksheets (3) .Name Worksheets(3).Delete 'Удаляется таблица 3 Worksheets ("Sheetl") .Range("B8") .Value = Worksheets (3) .Name Worksheets (" Sheetl") .Range ("B9") . Value = Worksheets . Count End Sub 2. Выполните процедуру, используя, как и ранее, директиву Tools/Macro или пиктограмму Run пиктографического меню Visual Basic. Переключитесь на страницу Sheetl, которая сейчас имеет вид, представленный на рис. 3.2. Обратите внимание на то, что метод Add добавил новую таблицу в начало списка страниц. Свойство Count вернуло значение 16, страница номер 3 изменилась с Sheet3 на Sheet2. Это произошло в результате того, что новая страница была вставлена в начало списка. Затем, использовав метод Delete для удаления страницы номер 3, мы уменьшили количество компонентов и вернули странице номер 3 имя Sheet3. 52
Глава 3. Доступ к объектам Excel из Visual Basic Обратите внимание на корешки внизу экрана. На них видна новая, добавленная в начало страница Sheet 17 и отсутствует страница Sheet2, которая удалена. Доступ к ячейкам как объектам Range Вы можете предположить, что отдельные ячейки таблицы тоже являются объек¬ тами. Но это не так. Отдельные ячейки в таблице могут быть доступны только как объекты Range. Объект Range — это одна ячейка или прямоугольная группа ячеек таблицы. Метод Union используется для объединения разрозненных групп ячеек в один объект Range. Три метода позволяют создавать объекты Range: Range, Cells и Offset. Кроме того, допускается применение квадратных скобок. Метод Range Метод Range, наиболее разносторонний метод, использует в качестве аргументов одну или две ссылки на ячейки и возвращает объект Range. Ссылки на ячейки должны быть строками в стиле А1 (известном как нотация "колонка-буква-строка-число”), 53
Visual Basic for Applications в примерах именующими область таблицы, иначе говоря — объект Range. Для отображения значений, возвращаемых свойствами Counte и Name, вы должны поместить эти значения в ячейки. В предыдущем примере вы сделали это, применив метод Range из ссылки на единичную ячейку. Ссылка на единичную ячейку, использованная в качестве аргумента, возвращает объект Range для единичной ячейки. Две ссылки на единичные ячейки возвращают объект Range для прямоугольной области, заключенной между этими двумя ячейками. Ссылка на прямоугольную область ячеек в нормальном формате Excel (например, А7:А9) также допустима в качестве имен поименованных областей таблицы. Совет: Поименованные области очень удобно использовать для выбора объектов Range, так как код в процедуре все равно применяется для корректной области, даже если вы переместили данную область в другое место таблицы. Если код для определения области использует абсолютные ссылки на ячейки, то перед перемещением области вам придется отредактировать процедуру. Рассмотрим дополнительные примеры применения метода Range, для чего откройте новую рабочую папку, создайте страницу модуля и запишите следующую процедуру: Sub theRanges() 'Доступ к ячейкам с помощью метода Range Worksheets("Sheetl").Range("Bl").Value = 1 'Единичная ячейка Worksheets("Sheetl").Range("АЗ", "C4").Value = 2 'Область ячеек Worksheets("Sheetl").Range("B7:C9").Value _ = 3 'Другая область ячеек 'Несмежная область ячеек Union(Worksheets("Sheetl") .Range("DI:F2") , _ Worksheets("Sheetl").Range("E7:G9")).Value = 4 'Другая несмежная область ячеек Worksheets("Sheetl").Range("Dll:F12,D15:E17").Value = 5 End Sub Выполните эту процедуру, затем переключитесь на таблицу Sheetl, чтобы увидеть результат. Sheetl имеет вид, изображенный на рис. 3.3. В первом примере метод Range обеспечивает доступ к единичной ячейке В1 и присвоение ей значения 1, во втором и третьем примерах —доступ к прямоугольной области ячеек и присвоение им значений 2 и 3. В четвертом примере используется метод Union для объединения двух объектов Range, описанных непересекающимися ссылками, в один и присвоения его ячейкам значения 4. В пятом примере две непересекающиеся ссылки создают один метод Range и присваивают его ячейкам значение 5. 54
Глава 3. Доступ к объектам Excel из Visual Basic Примечание: Если метод Range применяется к объекту Range, ссылка считается относительной, начиная от левого верхнего угла объекта Range (даже если ссылка указывает на абсолютное имя ячейки). Например, Range("B5").Range("B2”) указывает на ячей¬ ку С6, так как ячейка В2 имеет относительное смещение (одна строка вниз и один столбец вправо от А1), что применилось к объекту В5. Метод Cells Метод Cells, получая в качестве аргументов два целых числа, возвращает объект Range, содержащий единичную ячейку. Аргументы определяют номера строки и колонки выбранной ячейки. Кроме того, можно использовать строку, заключенную в кавычки, которая в качестве второго аргумента содержит букву колонки (но это не очень удобно). Метод Cells наиболее удобен для выбора групп ячеек по одной — просто изменяя значения аргументов. Этот метод можно применить также в качестве аргумента метода Range, что позволит вам использовать целые числа — аргументы для выбора областей, содержащих более одной ячейки. 55
Visual Basic for Applications в примерах Примечание: Метод Cells подобен методу Range в случае применения его для существующей области. Возвращенная область задается относительно верхнего левого угла сущес¬ твующей области. Таким образом, Range(”B7:D12").Cells(3,2) ссылается на ячей¬ ку С9. Применение метода Offset для таких случаев дает те же результаты, будучи при этом более очевидным, поскольку ячейка, выбираемая методом Offset, имеет смещение относительно существующего объекта Range. Рассмотрим примеры применения метода Cells. Для этого откройте новую рабочую папку, создайте новую страницу модуля и запишите следующую процедуру: Sub theCells() 'Доступ к ячейкам с помощью метода Cells Worksheets("Sheetl") .Cells (1, 2) .Value = 1 'Единичная ячейка Worksheets("Sheetl").Range(Worksheets("Sheetl").Cells(3, 1), _ Worksheets("Sheetl").Cells(4, 3)).Value - 2 'Область ячеек Worksheets("Sheetl").Cells (1, "D") .Value = 3 'Другая область ячеек For T = 1 To 10 Worksheets("Sheetl").Cells(I + 5, 2).Value = I Next I End Sub Выполните процедуру и переключитесь на Sheetl, чтобы увидеть результаты (см. рис. 3.4). Первый пример применения метода Cells вначале помещает число 1 в ячейку В1 (строка 1, столбец 2). Второй пример дважды применяет метод Cells в качестве аргументов метода Range для получения ссылки на область ячеек АЗ:С4. Использо¬ ванные в этих ссылках числа могут быть заменены переменными или формулами, как показано в последнем примере, где применен цикл For-Next, который более подробно рассматривается в гл. 12 ”Применение вычисляемых циклов”. Цикл повторяет опера¬ торы, заключенные между For и Next десять раз, наращивая переменную I на единицу при каждой итерации цикла. Метод Celis использует переменную I для вычисления номера строки и перемещения выбранной строки на одну позицию вниз при каждой итераций цикла. Затем значение переменной I помещается в выбранную ячейку. 56
Глава 3. Доступ к объектам Excel из Visual Basic Метод Offset Метод Offset работает аналогично методу Cells, с тем отличием, что возвращаемый объект Range всегда задается относительно верхнего левого угла текущего объекта Range. Метод Offset чаще всего применяется для создания объекта Range относительно текущей активной ячейки в рабочей таблице. В методе Offset используются два аргумента — индекс строки и индекс колонки, но эти индексы являются смещением относительно текущего объекта Range. Сравнив методы Cells и Offset можно увидеть, что в случае ссылки на одну и ту же ячейку индексы строки и колонки отличаются на 1. Например, два следующих оператора ссылаются на ячейку ЕЗ: Worksheets("Sheetl") .Range("Al") .Cells (3,5) Worksheets("Sheetl") .Range ("Al") .Offset(2,4) Для создания объектов Range относительно некоторых других объектов Range лучше использовать метод Offset (а не Range или Cells); это сделает вашу программу более однозначной. 57
Visual Basic for Applications в примерам Краткая форма метода Range Для упрощения использования метода Range можно в качестве аргумента указать ссылку на ячейку, заключив ее в квадратные скобки. Таким образом, два следующих оператора ссылаются на одну и ту же ячейку: Worksheets("Sheetl").Range("Е7") Worksheets("Sheetl”).[E7] Хотя сокращенная форма уменьшает время ввода текста, этот синтаксис менее читабелен, чем полный для метода Range, поэтому применения сокращенной формы рекомендуется избегать. Использование свойств, характеризующих активность Visual Basic имеет несколько специальных свойств объекта Application, значитель¬ но упрощающих программы на Visual Basic. Эти свойства всегда применяются к текущему активному объекту определенного класса. Активный объект —это верхняя страница рабочих папок и таблиц, активная ячейка объектов Range или выбранный объект графических объектов. Свойства, характеризующие активность, часто используются в методах Select и Activate. Метод Select выбирает область ячеек или страницу. Метод Activate делает активной отдельную ячейку в рамках текущего выбора. Будучи примененным к странице, Activate работает подобно Select. Свойство ActiveWorkbook применяется к рабочей папке, отображаемой в настоя¬ щий момент, а свойство ActiveSheet —к верхней странице активной рабочей папки. Верхняя страница может быть таблицей, страницей диаграммы, страницей макроса или страницей модуля — в зависимости от того, какой объект является текущим видимым или верхним. Свойство ActiveCell всегда применяется к одноячеечному объекту Range, содер¬ жащему активную ячейку в верхней таблице. В любой группе выбранных ячеек таблицы только одна ячейка является активной. Активная ячейка выделяется своим белым фоном, остальные выбранные ячейки имеют черный фон. Когда группа ячеек таблицы выбирается впервые, активной является ячейка, находящаяся в левом верхнем углу выбора. Каждый раз, когда вы нажимаете Enter для ввода значений в выбранную ячейку, активная ячейка перемещается вниз к следующей ячейке выбора. Когда активная ячейка достигнет нижней части выбора, она перемещается вправо на одну колонку выбора в его верхнюю ячейку. Когда активная ячейка достигнет нижнего правого угла выбора, она переместится назад в левый верхний угол. Свойство Selection тесно связано со свойством ActiveCell, но скорее относится ко всему выбору, чем к отдельной ячейке. Если выбрана только одна ячейка, Selection и 58
Excel из Visual Basic Глава ActiveCell одинаковы. Активная ячейка всегда располагается внутри активного выбо¬ ра, и обычно активной является ячейка в его левом верхнем углу. Рассмотрим дополнительные примеры применения этих свойств, для чего откройте новую рабочую папку, создайте новую страницу модуля и запишите следующую процедуру: Sub АСРгор () 'Применение свойств Active для форматирования ячейки. ActiveWorkbook.Worksheets("Sheetl").Select ActiveSheet.Range("B3").Select ActiveCell.Value = 23 ActiveCell.NumberFormat = "0.00” ActiveCell.HorizontalAlignment = xlCenter ActiveCell.BorderAround Weight:=xlThick, Colorindex:=4 ActiveCell.Interior.Colorindex = 6 'Теперь применяются те же свойства для форматирования выбора ActiveSheet.Range("С5:F7").Select Selection.Value = 23 Selection.NumberFormat = ”0.00" Selection.Horizontal Alignment = xlCenter Selection.BorderAround Weight:=xlThick, Colorindex:=4 Selection.Interior.Colorindex = 6 ActiveSheet.Range("Al").Select End Sub Выполните эту процедуру, и она переключится на рабочую таблицу Sheetl, запишет 23 в ячейку ВЗ, выделит это число зеленой рамкой и желтым фоном. Затем она выберет область C5:F7 и повторит то же самое для всех ячеек области. Таблица приобретет вид, изображенный на рис. 3.5. Первый блок операторов выбирает Sheetl, а затем —ячейку ВЗ в Sheetl, используя свойство ActiveSheet. Следующие несколько строк записывают число 23, преобразо¬ вывают его в числовой формат, центрируют число, рисуют вокруг него зеленую рамку и заполняют фон в ячейке желтым цветом. Каждый из этих операторов использует свойство ActiveCell для выбора ячейки ВЗ. Следующий блок операторов делает то же самое для области ячеек C5:F7. В этом случае объект Range является областью, а не одной ячейкой, и метод Select выбирает область целиком. Кроме того, свойство Selection используется для внесения изменений в весь выбор, а не в одну ячейку. 59
Visual Basic for Applications в примерах ш| File Edit Veew Insert Form st Tools lictgsoft E^l - Рис. 3.5. Процедура A CProp записывает числа в области ячеек и форматирует эти области Обратите внимание на то, что в тексте процедуры некоторые объекты повторяются. Вы можете несколько упростить процедуру, использовав оператор With. Чтобы сделать это, запишите после ключевого слова With список объектов, отделив их точкой. Предполагается, что все операторы, расположенные между With и End With используют этот список объектов, дописанный слева от точки. Например, вы можете переписать предыдущую процедуру так, как показано ниже, и она будет работать как предыдущая: Sub АСРгорО 'Применение свойств Active для форматирования ячейки. ActiveWorkbook.Worksheets("Sheetl").Select ActiveSheet.Range("B3").Select With ActiveCell .Value = 23 .NumberFormat = ”0.00” .HorizontalAlignment = xlCenter 60
Глава 3. Доступ к объектам Excel из Visual Basic .BorderAround Weight:=xlThick, Colorindex:=4 .Interior.Colorindex = 6 End With 'Теперь применяются те же свойства для форматирования выбора ActiveSheet.Range("С5: F7").Select With Selection .Value = 23 .NumberFormat = "0.00” .HorizontalAlignment = xlCenter .BorderAround Weight:=xlThick, Colorindex:=4 With .Interior .Colorindex = 6 End With End With ActiveSheet.Range("Al").Select End Sub Во втором блоке кода, относящемся к выбору, что второй оператор With располо¬ жен внутри первого. При построении множественных операторов With, как в этом примере, каждая переменная With пристраивается справа от предыдущей. Примечание: В большинстве случаев объекты, возвращаемые свойствами ActiveWorkbook, ActiveSheet или ActiveCell, совпадают с объектами но умолчанию, и, таким образом, их определение в программе не требуется. Тем не менее, если вы включите свойства Active, ваш код станет более читабельным, и если ситуация сложится таким образом, что эти объекты не будут равными объектам по умолчанию, вы гарантировано получите корректный объект. Использование свойств, характеризующих содержимое В большинстве ранее приведенных примеров для присвоения ячейке какого-либо значения использовалось свойство Value объекта Range. Объекты Range предостав¬ ляют две возможности для изменения содержимого ячейки: свойства Value и Formula. Свойство Value содержит текущее значение ячейки (числовое или символьное). Когда вы видите ячейку на экране, вы видите ее содержимое, преобразованное в текущий формат. Если вы ссылаетесь на ячейку в другой ячейке, используя ее в некоторой формуле, значение текущей ячейки будет вычислено с использованием величины, на которую вы сослались. Когда вы присваиваете ячейке некоторое значение, это значение присваивается свойству Value. 61
Visual Basic for Applications в примерах Свойство Formula характеризует содержимое, которое записано в ячейку. Если вы записали некоторое число, то формула и значение будут одинаковыми. Если вы записали формулу, то свойство Formula содержит формулу, а свойство Value — результат вычисления по этой формуле. Свойство Formula предлагает два варианта записи ссылок на ячейки в формуле. Если вы использовали свойство Formula, Excel предполагает, что строка, которая записана в ячейку —это формула со ссылками на ячейку в стиле А1 (известном как нотация "колонка-буква-строка-число"). Если вы используете свойство FormulaRlCl, Excel предполагает, что вы записали формулу, применив формат R1C1 для адресации других ячеек. Метод адресации ячеек R1C1 —это нотация "строка-колонка"; исполь¬ зуя этот метод, например R2C3, вы указываете на ячейку во второй строке и третьей колонке. Применение свойств форматирования Кроме установки содержимого ячейки можно установить шрифт, стиль, формат, цвет, фон и рамку ячейки. При установке шрифта или стиля ячейки вы можете дополнительно задать выделение текста жирным шрифтом или курсивом. Формати¬ рование производится по шаблону, описывающему, как число отображается в ячейке. Выберите директиву Format/Cells и корешок Number для получения списка доступ¬ ных числовых форматов. Кроме того, используя директиву Format/Cells, можно получить список цветов, фонов и рамок. Лучшим способом написания программ форматирования ячеек на Visual Basic является использование макрорекордера. Поскольку различные директивы формати¬ рования предоставляют возможность задавать большое количество опций и вариаций, представляется сложным корректно произвести форматирование, просто записывая форматы. Используя макрорекордер, вы можете по своему усмотрению изменить стиль и формат ячейки, отображенной на экране, а затем использовать записанный макрос в своей программе. Например, запустите макрорекордер и запишите какой-нибудь текст в одной ячейке и число в другой, затем поиграйте с форматом, цветом, размером, рамками и другими установками. Посмотрите на результаты в записанном макросе. Приведенный ниже макрос получен в результате создания таблицы, изображенной на рис. 3.6. Комментарии включены после того, как процедура была записана. Опции, помеченные комментариями "По умолчанию", в диалоговых окнах не изменялись, но все же были включены в записанную процедуру, так как они были заданы в одном окне наряду с другими опциями. ' StylePlay Macro ' Playing with styles Запуск рекордера Имя процедуры StylePlay 62
Глава 3. Доступ к объектам Excel из Visual Basic Sub StylePlayO Range("C4").Select ActiveCell.FormulaRlCl = "Sierra Range("C6").Select ActiveCell.FormulaRlCl = "1234" Range("C4").Select With Selection.Font .Name = "Times New Roman" .Fontstyle = "Bold Italic" .Size = 36 .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlNone .Colorindex = 7 End With Columns("С:C").Columnwidth = 20 Range("C4").Select Selection.Borders(xlLeft) LineStyle = xlNone Selection.Borders(xlRight)._ LineStyle = xlNone Selection.Borders(xlTop)._ LineStyle = xlNone Selection.Borders(xlBottom)._ LineStyle = xlNone Selection.BorderAround _ Weight:=xlMedium, _ Colorindex:=xlAutomatic Выбор ячейки С4 Ввод текста Sierra Выбор ячейки С6 Ввод числа 1234 Выбор ячейки С4 Выбор директивы Format/Cells и корешка Font Выбор шрифта Times New Roman Установка стиля Bold Italic Установка размера шрифта 36 точек По умолчанию По умолчанию По умолчанию По умолчанию По умолчанию По умолчанию Задание розового цвета Выбор командной кнопки ОК Расширение колонки С Выбор ячейки С4, выбор директивы Format/Cells и корешка Border По умолчанию По умолчанию По умолчанию По умолчанию Выбор Outline и средней линии 63
Visual Basic for Applications в примерам With Selection.Interior Выбор табулятора Pattern .Colorindex = 8 Задание голубого цвета .Pattern = xlSolid По умолчанию .PatternColorIndex = xlAutomatic End With Выбор командной кнопки OK Rows (" 6: 6" ) . P.owHeight = 50 Range(”C6").Select Selection.NumberFormat = _ "$#,##0.00_) ; ($#,##0.00)" With Selection .HorizontalAlignment = xlCenter .VerticalAlignment = xlCenter .WrapText = False .Orientation = xlHorizontal .Addindent = False End With With Selection.Font .Name = ’’Arial” .Fontstyle = "Bold" .Size = 24 .Strikethrough = False .Superscript = False .Subscript - False . OutlineFont. = False .Shadow = False .Underline = xlNone .Colorindex = xlAutomatic End With With Selection.Interior .ColorIndex = 4 • .Pattern = 17 .PatternColorIndex = xlAutomatic End With Columns("С:C").Columnwidth = 29.86 End Sub Расширение строки до 50 т. Выбор ячейки С6 Выбор директивы Format/ Cells и корешка Number Выбор формата Currency с двумя нулями после точки Выбор корешка Alignment Горизонтальное центрирование Вертикальное центрирование По умолчанию По умолчанию По умолчанию Выбор корешка Font По умолчанию Задание жирного шрифта Размер 24 точки По умолчанию По умолчанию По умолчанию По умолчанию По умолчанию По умолчанию По умолчанию Выбор корешка Patterns Задание зеленого цвета Выбор светло-серого фона По умолчанию Выбор ОК Расширение колонки С 64
Глава 3. Доступ к объектам Excel из Visual Basic И \ ч ..< •. ,..л . MiQ.tQBoft Excel - STVLEPL.XLfi - , л**- 4 ♦«т-'йЛу/ *| ЕНе Edit View Insert Format Tools Qata Y/lndow Help |d|i^|e| |<з|&|у| №1E0 ЖТ ЩЦл] lion* i[±l IWI ia>hj —ii±i |м~~1Г»1 tapiui ЕИМШ11ф1%1»№ (ЙЭ №I*i ®S Рис. 3.6. Результат выполнения процедуры Style Play Текст "Sierra” выведен ярко-розовыми буквами на голубом фоне с черной рамкой. Текст ”$1,234.00” — черный на зеленом. Просматривая эту процедуру, обратите внимание на то, что различные элемента форматирования применены к различным элементам ячейки. Форматирование шрифта применено к объекту Font, Borders — непосредственно к объекту Range, а цвета ячейки — к объекту Interior. Эта процедура задает некоторые свойства такими величинами, как xlAutomatic, xlNone или xlCenter. Все они —константы Excel. Их используют вместо числовых значений, так как при прочтении процедуры константы несут больше смысла, чем просто числа. Чтобы увидеть список констант, используйте Object Browser, выбрав библиотеку Excel и объекты Constants. Выполнение методов рабочих таблиц Выполнение методов рабочих таблиц подобно применению форматирования. Действительно, мы применяем некоторые форматы, используя скорее методы, чем свойства. 3 Вильям Дж. Орвис 65
Visual Basic for Applications в примерах В последнем примере для изменения свойств рамок выбора использован мето/] Border Around: Selection.BorderAround Weight:=xlMedium, Colorindex:=xlAutomatic Обратите внимание на разницу между выполнением метода BorderAround и присвоением значения свойству рамки LineStyle, а именно: Selection.Borders(xlRight).LineStyle == xlNone Изменяя, значение свойства, вы присваиваете ему новое значение (в данном случае, константу xlNone). Когда вы выполняете метод, то задаете новые величины в аргументах. Это осуществляется следующим образом: вы записываете имя аргумента, знак присваивания ”:=” (а не знак а затем новое значение переменной. В данном случае аргументу Weight присвоено значение константы xlMedium, а аргументу Colorindex —константы xlAutomatic. Удобны в применении и некоторые методы объекта Range, такие как Cut, Сору и Raste. Эти три метода не используют никаких аргументов — они просто передают содержимое объекта Range в буфер обмена или из него. Удобны также мел оды Clear и ClearContents, которые или полностью очищают ячейку или удалями только формулу и значение. Метод Printout удобно использовать для передачи объекта Range на принтер Вы можете, выбрать область, определить ее как область печати, а затем напечатать этот объект; также можно передать область методу Printout, который сделает все это для вас. Если вам требуется напечатать много маленьких секций таблицы, создайте в таблице кнопку и подключите к ней приведенную ниже короткую процедуру; таким образом все, что вам придется делать —это выбирать область и нажимать на кнопку, чтобы напечатать эту область: 'Печать выбранной области f Sub PrintltO Selection.Printout End Sub 66
Глава 3. Доступ к объектам Excel из Visual Basic Заключение В этой главе вы научились осуществлять доступ, изменять содержимое и форма¬ тировать ячейки таблицы. Кроме того, вы ознакомились с объектами и методами Excel и разобрались, как осуществлять к ним доступ и использовать их. Данная глава завершает часть I этой книги. В части II вы ознакомитесь с языком Visual Basic for Applications и тем, как он используется для различных вычислений. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое набор? 2. Назовите два способа доступа к содержимому набора. 3. Как определяется количество компонентов в наборе? 4. Каковы три основных метода выбора в таблице объекта Range? 5. Если В7 — текущая активная ячейка, и вам необходимо осуществить доступ к ячейке F12, что вы используете в качестве аргументов для ActiveCell.Cells (?,?) И для ActiveCell.Offset(?, ?) Упражнения для закрепления материала 1. Запустите макрорекордер и попробуйте все команды форматирования, чтобы увидеть, что будет записано в процедуре. Если вы не понимаете какую-либо команду или оператор, воспользуйтесь интерактивной подсказкой. 2. Создайте процедуру, которая изменяет цвет текущего выбора на красный и окантовывает его зеленой рамкой. 3. Создайте процедуру, которая добавляет новую рабочую папку с 16 таблицами (по умолчанию) к набору Workbooks, а затем удаляет таблицы с 4 по 16 из набора Worksheets этой рабочей папки.
Часть II Основные элементы языка
Глава 4 Типы данных и переменные Переменные и типы данных определяют, где и как данные хранятся в памяти компьютера. В языке со строгим контролем типов вы обязаны указывать тип каждой переменной перед ее использованием. Хотя Visual Basic не является языком со строгим контролем типов, он предоставляет возможность управлять использованием памяти вашего компьютера более эффективно, задавая в явном виде типы для всех ваших переменных. Из этой главы вы узнаете: > Все о типах данных > Как создавать новые типы данных > Как присваивать значения переменным > Как создавать и использовать массивы > Что такое область видимости переменных Что такое типы данных? Тип данных определяет, каким образом величина хранится в памяти компьютера. Задавая тип данных, мы также налагаем некоторые ограничения на сохраняемую величину, а именно: как велика она может быть, сколько разрядов может содержать и может ли она иметь дробную часть. Существует несколько способов размещения числа в фиксированном количестве байтов памяти. Например, вы можете выбрать тип данных, который использует все байты как простое целое число, что позволит получать числа с максимальным разрешением (т.е. с наибольшим количеством разрядов). Тем не менее, целые числа не могут иметь дробной части и не могут представлять собой очень большие величины. Чтобы устранить это ограничение, необходимо использовать некоторые байты для хранения информации о расположении десятичной точки, а остальные —для хранения значащих разрядов числа. Таким образом, ради хранения дробных чисел и больших величин приходится жертвовать некоторой разрешающей способностью. Можно было бы использовать больше байтов для хранения числа с тем же разрешением и показателем степени, но это потребует больше памяти. Определяя тип данных для величины, вы определяете также соотношение между разрядностью, размером и используемой памятью. 71
Visual Basic for Applications в примерах Встроенные тины данных В Visual Basic имеется одиннадцать встроенных типов данных, представленных в табл. 4.1. Таблица 4.1. Встроенные типы данных Visual Basic Тип данных Размер (байтов) Разрядность (цифр) Диапазон Boolean 2 1 True или False Integer 2 5 от -32768 до 32767 Long 4 10 от -2147483648 до 2147483647 Single 4 7 от -3.402823Е+38 до -1.401298Е-45 и от 1.401298Е-45 до 3.402823Е+38 Double 8 15 от -1.79769313486232Е+308 до -4.94065645841247Е-324 и от 4.94065645841247Е-324 до 1.79769313486232Е+308 Currency 8 19 от -922337203685477.5808 до 922337203685477.5807 Date 8 от 01.01.100 до 31.12.9999 String 1 + 1 на символ от 0 до 65535 символов Object 4 Любой определенный объект Array Определяется количеством и размером элементов Variant Определяется записанными Любой встроенный тип данных данными Тип данных Boolean использует два байта (16 бит) памяти. Этот тип данных имеет только два возможных значения: True (истина) или False (ложь), которые могли бы быть представлены одним битом, но в действительности тип Boolean хранится как Integer и значение 0 соответствует False, а -1 — True. Хотя при этом некоторое количество памяти используется впустую, корпорация Microsoft сделала именно так, чтобы обеспечить совместимость типа Boolean с другими типами данных при его использовании в формулах. 72
Глава 4. Типы данных и переменные Тип данных Integer использует два байта памяти для хранения смещенного целого числа. Целое число без знака может принимать только положительное значение, а смещенное целое может принимать как положительное так и отрицательное значение. Тип данных Integer использует один бит для хранения знака величины, а остальные 15 — для числа. Тип данных Long —это целое число двойной длины, которое использует четыре байта или 32 бита памяти. Этот тип данных также является смещенным целым, используя 31 бит для числа и один —для знака. Данные типа Long имеют двойное разрешение (по сравнению с данными типа Integer), и используют в два раза больше памяти. Тип данных Single представляет собой действительное число с плавающей точкой. Для хранения числа с плавающей точкой его необходимо разбить на две части: мантиссу и порядок. Мантисса —это цифровая часть числа, а порядок указывает на расположение десятичной точки. Используйте этот тип данных осторожно, особенно при вычислении денежных величин, так как он имеет только семь цифр разрешения. Если вы использовали два из этих разрядов для центов, оставляя таким образом только пять для долларов, то максимальным значением будет $34,028.23. При попытке записать любое число, которое больше чем это, произойдет ошибка переполнения. Тип данных Double — это Single двойного размера, с двойной разрешающей способностью и в восемь раз большим диапазоном чисел. Он также требует вдвое больше памяти. Тип данных Currency —это специальное восьмибайтное число, в котором десятич¬ ная точка всегда располагается между четвертой и пятой цифрами справа. Этот тип данных специально создан для минимальных ошибок округления, и, таким образом, он позволяет получить после вычисления корректное количество долларов и центов. При использовании типов данных с плавающей точкой ошибка округления может привести к получению величины, равной 4.999 999 999 вместо 5.0. И хотя в большинстве случаев эта разница ничтожно мала, эта мелочь для бухгалтерских расчетов существенна. Чтобы избежать возникновения таких проблем применяют тип данных Currency. Тип данных Date предназначен для хранения дат и времени в восьми байтах памяти. Даты записываются в виде целых чисел дней от 01.01.1900, а время —как дробная часть дня. Таким образом, значение 34398.75 в формате Date означает 5 марта 1994 года и время 18:00. Используя этот тип данных, имейте в виду, что хотя диапазон Visual Basic расширен от 01.01.100 до 31.12.9999, диапазон Excel —всего лишь от 01.01.1900 до 31.12.2078. Чтобы обеспечить совместимость с Lotus-1-2-3, Excel также содержит заложенную ошибку: несуществующую дату 29.02.1900 года. Из-за этой ошибки даты между 01.01.1900 и 29.02.1900 сдвинуты на один день. Но так как вы не астроном, вы, вероятно, не заметите эту неточность. 73
Visual Basic for Applications в примерах Тип данных String используется для хранения строк текста по одному байту памяти на каждый символ плюс один, чтобы отметить конец строки. Символы хранятся в памяти в виде кодов ANSI (см. приложение Б). Первые сто двадцать восемь кодов — это стандартные символы клавиатуры, знаки пунктуации, числа и управляющие символы. Вторые 128 кодов содержат псевдографические символы. Имейте в виду, что псевдографические символы ANSI, используемые приложениями Windows, и псевдографические символы, используемые приложениями ДОС, разнятся. Тип данных Object использует четыре байта памяти для хранения ссылки на любой объект Visual Basic. Тип данных Array в действительности не является отдельным типом данных. Он скорее определяет индексный список величин одного из других типов данных. Более подробно массивы рассмотрены далее в этой главе. Тип данных Variant используется в Visual Basic как тип данных по умолчанию. Как Variant вы можете хранить буквально все. Если тип данных для хранения величины не указан, то используется тип данных Variant. В таком случае производится проверка того, что сохраняется, и тип данных приводится в соответствие с этой величиной. Из-за этой проверки тип данных Variant имеет тенденцию впустую тратить время и память. Для простой процедуры такая потеря ничтожна, но в большой процедуре, которая производит много вычислений, вам необходимо поразмыслить над использованием этого типа данных. Примечание: Можно заметить, что в диапазонах типов данных, содержащихся в табл. 4. /. приведены несколько странные значения. Например, верхний предел значений типа Integer равен 32,767, а не какому-нибудь более приятному, круглому числу, вроде 30000. Дело в том, что эти числа и представляют собой приятные и круглые, но только двоичные числа. Числа в памяти хранятся в виде двоичных, а не десятичных чисел. Тип Integer использует один бит для знака, и 15 бит для числа. Таким образом наибольшее десятичное число, которое может быть представлено пятнадцатибитным двоичным числом — это 32767, так как 215 - 1 = 32767. Переменные Переменные —это поименованные области в памяти компьютера. После вычисле¬ ния какого-либо значения, вы должны записать его в память, чтобы затем можно было к нему обращаться. Вы можете использовать в вашей программе абсолютные области памяти, указывая, где хранить значения, но ссылка вроде 0380:0004 не очень понятна, зато переменная WestCoastSales несет определенный смысл. Использование перемен¬ ных дает Visual Basic возможность создавать прямое соогветствие между областью памяти и заданным именем. Затем вы сможете использовать это имя в своей программе и предоставить Visual Basic заботиться о хранении величин в надлежащих областях памяти. 74
Глава 4. Типы данных и переменные Имена переменных состоят из алфавитных символов, цифр и некоторых специаль¬ ных знаков. Имя переменной должно начинаться с алфавитного символа и не может содержать пробелов, точек и символов объявления типа (#, $, %, &, !). В именах переменных можно использовать заглавные буквы, но Visual Basic не делает различия между переменными, в зависимости от того, содержат или не содержат их имена заглавные буквы. Для задания имен переменных часто используются несколько слов, объединенных символом подчеркивания или выделенных с помощью заглавных букв. Ниже приведены примеры допустимых имен переменных: theDate aDate Sales_WesternRegion MyName GrossSales CapitalLoss TheAmountNeededToPayOffTheLoan Имя последней переменной несколько длинновато. И хотя можно объединить целое предложение для создания очень информативного имени переменной, стоит прики¬ нуть, сколько времени потребует безошибочный набор такого имени при создании программы. Действительно, такие длинные и заумные имена могут превратить про¬ граммирование в неоправданно трудоемкий и длительный процесс. Поэтому целесооб¬ разно использовать что-нибудь покороче — вроде PayOff. Старайтесь, чтобы имена переменных были короткими, но информативными. Объявление переменных Типы переменных —это то же, что и описанные ранее типы данных. Объявляя переменную определенного типа, вы указываете Visual Basic, какой тип данных должен быть использован при записи значения переменной в память. Если же вы не объявили тип переменной, по умолчанию используется тип данных Variant. Тип данных Variant имеет возможность хранить практически любую величину и бывает полезен для коротких, простых программ. Для более же длинных программ рекомендуется объявлять каждую переменную перед ее использованием. Приведем два аргумента в пользу объявления каждой переменной: > Позволяет выявить все опечатки в именах переменных. Неправильно напечатанное имя переменной в вашей программе приведет к появлению новой переменной, из-за чего программа будет работать некорректно. Следовательно, если вы должны объя¬ вить все переменные перед их использованием, необъявленная переменная повлечет за собой ошибку выполнения, что немедленно пометит эту переменную как проблему. > Экономит память и время. Перед тем, как действительно сохранить данные в переменной типа Variant, Visual Basic должен проверить, какой вид данных сохраняется в переменной, и после этого зарезервировать область памяти, доста¬ точную для размещения этих данных. Если вы объявили переменную заблаговре¬ менно, Visual Basic уже знает, данные какого типа будут сохраняться в переменной, и автоматически резервирует для них память. 75
Visual Basic for Applications в примерах Вынуждайте себя объявлять переменные Чтобы заставить себя объявлять каждую переменную перед ее использованием, поместите оператор Option Explicit в начало каждого модуля. Для каждого сущес¬ твующего модуля вы можете вручную записать в начало оператор Option Explicit. Для того чтобы Visual Basic автоматически помещал этот оператор в начало каждого модуля, выберите директиву Tools/Options, выберите корешок Module General и затем зафиксируйте независимую кнопку Require Variable Declaration. Объявление переменных с использованием оператора Dim Для объявления переменных включите оператор Dim в верхнюю часть процедуры, использующей эти переменные. Следующий синтаксис служит для объявления пере¬ менных с помощью оператора Dim: Dim variable As type, variable As type, . . . , где variable —имя переменной, type —тип данных. Ниже приведены примеры объявления переменных с помощью оператора Dim: Dim theName As String Dim Cost As Currency, I As Integer Dim ProductNumber As Long Dim Height As Single, Width As Single Dim aPicture Последний оператор объявляет переменную aPicture типа Variant. Любое объяв¬ ление переменных, не содержащее объявления типа в явном виде, объявляет перемен¬ ную типа Variant. Область видимости переменных Область видимости переменной состоит из тех модулей и процедур, в которых эта переменная может быть использована. Использование переменной в процедуре озна¬ чает, что можно получить или изменить ее значение в любом месте области видимости и эти изменения будут видны из любого места области видимости. На рис. 4.1 представлена схема, позволяющая определить область видимости переменной. Ниж¬ ний уровень определения —в процедуре. Переменные, объявленные оператором Dim в процедуре, могут быть использованы только в этой процедуре. Переменные varl (в Процедуре1), var2, var4 и var5 определены на процедурном уровне и, таким образом, могут использоваться только в соответствующих процедурах. Переменная с тем же именем, объявленная в другой процедуре, является особым, независимым объектом. 76
Глава 4. Типы данных и переменные Например, var2 определена в двух различных процедурах, но изменение значения var2 в ПроцедуреЗ не повлияет на значение var2 в Процедуре2. Модуль Public globalVar Модуль1 Dim varl Модуль2 Dim var3 Процедур!! Dim varl ПроцедураЗ Dim var2 ПроцедураД Dim var4 Процедура2 Dim var2 Процедураб Dim var5 Рис. 4.1. Область видимости переменных в соответствии с объектно-ориентированной моделью контейнеров. Переменные, объявленные во внешнем контейнере, доступны процедурам, входящим в этот контейнер Объявления на уровне модуля, подобно varl и var3, создают переменную, которая может использоваться в любой процедуре модуля. То есть var3 может использоваться и в Процедуре4, и в Процедуре5. Переменная varl может использоваться как в Процедуре2, так и в ПроцедуреЗ, но в Процедуре 1 объявлена другая переменная varl, таким образом, объявление на уровне модуля аннулируется. Переменная varl, опре¬ деленная в Процедуре 1, отличается от переменной varl, которая может использоваться в Процедуре2 и в ПроцедуреЗ. Объявление глобальных переменных Чтобы сделать переменную доступной всем процедурам во всех модулях, объявите ее на уровне модуля, использовав вместо Dim ключевое слово Public. Переменная, объявленная оператором Public, может использоваться в любой процедуре программы. На рис. 4.1 переменная globalVar определена как Public в Модуле1 и, таким образом, доступна во всех пяти процедурах. 77
Visual Basic for Applications в примерах Массивы Объявляя переменную, вы задаете единичную поименованную область памяти, а объявляя массив —создаете список смежных областей памяти под одним именем. Для осуществления доступа к отдельному элементу массива, имя массива снабжают номером элемента массива (индексом), заключенным в круглые скобки. Вы объявляете массив так же, как и любую другую переменную — с помощью операторов Dim или Public, но записываете после имени массива одно или несколько чисел в круглых скобках, определяя таким образом количество элементов, которое может содержать массив. Ниже приведены примеры объявления массивов: Dim Sales (5) As Currency Dim AcctNo(700 To 799) As Integer Dim theCoords(4,1 To 3) As Single Dim Birthdays(3 To 23,5 To 7) As Date Первый оператор создает массив с именем Sales, содержащий данные типа Currency. По умолчанию индекс начинается с "О", поэтому в этом массиве шесть элементов: Sales(O), Sales(l), Sales(2), Sales(3), Sales(4), Sales(5) Выбор элемента массива производится по указанному индексу. Во втором примере создается массив с именем AcctNo, содержащий элементы типа Integer. Этот оператор задает как нижний, так и верхний предел индекса. Таким образом, массив содержит 100 элементов: AcctNo(700), AcctNo(701), AcctNo(702), ..., AcctNo(799) В третьем примере создается двумерный массив чисел с плавающей точкой, названный theCoords. Поскольку это объявление содержит два индексных диапазона, массив является двумерным. Диапазон первого индекса от 0 до 4, второго —от 1 до 3. Задание элемента двумерного массива требует указания двух индексов: theCoords(0,1), theCoords(0, 2) theCoords(1,1), theCoords(1,2) theCoords(2,1), theCoords(2,2) theCoords(3,1), theCoords(3,2) theCoords(4,1), theCoords(4,2) , theCoords(0, 3) , theCoords(1, 3) , theCoords(2,3) , theCoords(3,3) , theCoords(4,3) 78
Глава 4. Типы данных и переменные Можно задавать массивы и большей размерности, хотя трудно вообразить, что будет представлять собой массив с размерностью больше 3. Последний пример создает двумерный массив переменных типа Date, где для обоих индексов заданы верхний и нижний пределы. Первый индекс изменяется от 3 до 23, второй — от 5 до 7. Применение констант для повышения читабельности программы Константа — это величина, которая не меняется в процессе выполнения програм¬ мы. Хотя вы можете просто использовать постоянную величину там, где это необхо¬ димо, но получающиеся в результате программы не очень легко читаются. Например, вы можете задать цвета красный, зеленый, синий и желтый с помощью констант 3,4,5 и 6. Следующая процедура изменяет цвет фона ячейки В5 активной таблицы: Sub Colors 1 () Range(”В5").Interior.Colorindex = 5 End Sub Сравните эту процедуру co следующей, которая делает то же самое: Sub Colors2() Const Blue = 5 Range(”B5").Interior.Colorindex = Blue End Sub Что касается второй процедуры, то гораздо легче понять, для чего она предназначена. Вторым, более важным направлением применения констант, является задание величин, которые могут изменяться от одной компиляции программы к другой, или задание значений, в которых вы не уверены. Когда вы используете такие значения во многих местах вашей программы и эти значения меняются, вам придется учитывать это в каждом случае. Зато используя константу, вы должны изменить только определение константы, чтобы изменить ее значение во всей программе. Создание и применение констант Чтобы задать константу, используйте ключевое слово Const, за которым следуют имя константы, знак равенства и значение константы. Следующие операторы опреде¬ ляют константы: Const Red = 3, Green = 4, Blue = 5, Yellow = 6 Const ArrayLimit = 100 Const maxAccounts = 1000 79
Visual Basic for Applications в примерах Прежде чем использовать константы, их необходимо определить. Область види¬ мости констант такая же, как и у переменных. Константы, определенные в процедуре, могут использоваться только в этой процедуре. Константы, определенные в модуле, могут использоваться во всех процедурах этого модуля. Константы, объявленные Public, могут использоваться в любой процедуре любого модуля программы. Обще¬ доступные константы определяют на уровне модуля, помещая ключевое слово Public перед ключевым словом Const: Public Const ArrayLimit = 100 Этот оператор определяет глобальную константу, именуемую ArrayLimit, со значением, равным 100. Константа ArrayLimit может быть использована в любом месте программы. Поиск встроенных констант Visual Basic и Excel используют длинный список встроенных констант, применение которых не требует объявления. Эти константы предназначены для задания значений различных свойств в Excel или Visual Basic, а также для задания величин при использовании диалоговых окон. Интерактивная подсказка описывает допустимые константы, а также свойства и функции, с которыми они используются. Для отображения полного списка констант можно использовать также Object Browser, выбрав библиотеку Excel и указав объект Constants. Object Browser отобра¬ зит полный список констант Excel. Все константы Excel начинаются с символов xl. Выбрав библиотеку Visual Basic for Applications и указав объект Constants, можно увидеть все константы Visual Basic. Они начинаются с vb. Создание пользовательских типов данных Встроенные типы данных охватывают большинство ситуаций, с которыми вы можете столкнуться. Тем не менее в некоторых случаях необходимо объединить несколько существующих типов данных в единую составную структуру. Например, если вы создаете базу данных клиентов, то скорее всего объедините имя, адрес и номер телефона. Вы могли бы хранить всю эту информацию в пяти массивах, но гораздо предпочтительнее объединить все это в единую структуру и затем создать единый массив таких структур. Чтобы создать структуру данных, используйте оператор Туре. Этот оператор состоит из ключевого слова Туре и имени нового типа. Следующие несколько строк описывают имена и типы элементов структуры данных и заканчиваются оператором End Туре. Например, следующий фрагмент программы создает структуру данных для базы данных клиентов: 80
Глава 4. Типы данных и переменные Type ClientType Name As String Address As String CityState As String Zipcode As Long Phone As String End Type Этот оператор только определяет структуру нового типа. Далее вы должны объявить переменную этого типа, прежде чем ее использовать: Dim MyClients(1000) As ClientType Этот оператор объявляет массив из 1000 структур данных типа ClientType. Для осуществления доступа к элементам структуры необходимо указать имя структуры, поставить точку и указать имя элемента. Таким образом, для присвоения значений этой структуре вы можете использовать, например, следующие операторы: MyClients (10) .Name = "Никифор Ляпис-Трубецкой’* MyClients(10).Address = "ул.Никакая, д.123" MyClients(10).CityState = "г.Одесса, Украина" MyClients(10).Zipcode = 12345 MyClients(10).Phone = 0482 123-456 Эти операторы осуществляют доступ к элементу массива MyClients с индексом 10 и присваивают значения пяти элементам структуры данных. Заключение В этой главе рассмотрены типы данных, переменные и структуры данных, которые определяют, как данные хранятся в памяти и формируют основные блоки построения программы. Из следующей главы вы узнаете, как объединять переменные для удобства вычислений. * Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Дайте определение типу данных и типу переменных. 2. Сколько байтов памяти использует тип данных Currency? 81
Visual Basic for Applications в примерам 3. Что подразумевается под ’’областью видимости переменных"? 4. Каким образом можно заставить себя объявлять все переменные в модуле? Упражнения для закрепления материала lt Создайте процедуры colors 1 и colors2, о которых говорилось в разд. "Применение констант для улучшения читабельности программы". Выберите таблицу и выпол¬ ните обе процедуры, чтобы убедиться, что они приводят к одинаковому результату. 2. Напишите оператор Туре, описывающий структуру данных для создания списка телефонов. Включите в эту структуру элементы для фамилии, имени, номера телефона, даты рождения и дата последнего контакта. 3. Изобразите схематически элементы трехмерного массива, созданного следующим оператором: Dim MyArray (3,3,5) 62
Глава 5 Операторы присваивания и встроенные функции Теперь, когда вы все знаете о переменных, самое время начать применять их для пользы дела. В этой главе изложено, как сочетать понятие объектов и переменных для организации пересылки данных и вычислений. В этой главе будут рассмотрены следующие вопросы: > Что представляют собой операторы присваивания > Как применять операторы комментариев > Как применять операции и функции > Как преобразовывать данные из одного типа в другой > Как создавать и использовать объектные переменные Что такое операторы присваивания? Операторы присваивания — это основа всех вычислений в Visual Basic, так как любая рассчитанная величина должна стать переменной, чтобы ее можно было хранить. Оператор присваивания состоит из переменной, располагаемой слева, знака равенства и формулы, располагаемой справа. В качестве формулы может выступать отдельная величина или выражение, состоящее из переменных, констант, операций и функций. Переменная, располагающаяся слева, может быть простой переменной, элементом массива или свойством объекта. В примерах, рассмотренных в этой книге, вы уже встречались с операторами присваивания, ибо без них невозможно создать работоспособную программу. Ниже приведены примеры операторов присваивания: theFil ename •= "c:\vba\examples\exampl.xls" Sales = Units * Price Profit = Sales - Cost Coords(3,2) = 19.37 Selection. Value = 25 Range ("B5").Formula = "=B4*B3-1" ActiveCell.FormulaRlCl = "Таблица погашения ссуды" ActiveCell.FormulaRlCl = "10000” ActiveCell.FormulaRlCl = "Дебетовое сальдо" ActiveCell.FormulaRlCl = "=ROUND(R[-5]C[2]*R[-4]C[2]/12,2" ActiveWindow. ScrollRow = 1 Selection. Borders (xlBottom) .Weight = xlThin ActiveWindow. DisplayGridlines = False 83
Visual Basic for Applications в примерах Первые три оператора присваивают значения переменным, четвертый присваивает значение элементу массива, а все остальные присваивают значения свойствам. Таким способом нельзя присвоить значения только объектам — они требуют специального оператора присваивания Использование операторов комментариев Во многих программах, которые используются в течение более длительного времени, чем два-три дня, комментарии играют не меньшую роль, чем операторы присваивания. Комментарий предназначен для различного рода пояснений: по струк¬ туре, операциям или программированию. Комментарий должен начинаться с апостро¬ фа С), и все, что вы запишете справа от этого знака будет полностью проигнорировано Visual Basic при выполнении вашей программы. Таким образом, можно добавить любые пояснения или текст, описывающий ваши действия. Комментарий не обязательно должен начинаться с начала строки — он может быть расположен в любом месте справа от оператора. Обязательно справа — потому, что если он будет располагаться слева, то этот оператор станет частью комментария и будет проигнорирован. В то же время полезно воспользоваться этой возможностью для исключения операторов из программы, хотя фактически они и не удаляются. Вам просто нужно поставить апостроф перед началом оператора, выполнение которого нежелательно —и этот оператор превратится в комментарий. В дальнейшем простым удалением апострофа вы сможете восстановить этот оператор в программе. Когда вы использовали в гл. 1 макрорекордер, Excel автоматически вставлял блок комментариев перед началом записанной процедуры. Эти комментарии поясняют назначение процедуры. В приведенных ниже операторах комментарии выделены жирным шрифтом, хотя у вас на экране они будут зеленого цвета: г ' Таблица_погашения_ссуды ' Создание таблицы погашения ссуды за 180 месяцев. г г Sub Таблица_погашения_ссуды() 'Старт процедуры создания 'таблицы погашения ссуды Range ("Al”) .Select 'Выбор ячейки Al в текущей таблице 'Запись заголовка в ячейку А1 ActiveCell.FormulaRlCl = "Таблица погашения ссуды" 'Это заголовок Range("DI").Select 'Заголовок строки запишется в ячейку D1 ActiveCell.FormulaRlCl = "Процентная ставка" 'Заголовок строки 84
Глава 5. Операторы присваивания и встроенные функции Несмотря на то, что комментарии не влияют на выполнение программы, от них в значительной степени зависит, сумеете ли вы или кто-нибудь другой разобраться в том, что программа делает и почему. Создавая программу, вы, вероятно, будете экономить время на комментировании, но тем не менее писать комментарии необхо¬ димо. То небольшое количество времени, которое вы затратите на запись нескольких строк комментариев, не сравнимо с тем, которое вы или кто-нибудь другой затратите, пытаясь понять, что же вы делали. Если вы не уверены в том, что уничтожите программу после одного или двух использований, убедитесь, что включили достаточное количество комментариев для того, чтобы каждый, кто будет читать эту программу, смог понять, что она делает. Однако помните, что программы, которым якобы уготовлена короткая жизнь, зачастую имеют обыкновение крутиться под руками долгие годы, особенно в тех случаях, когда они делают что-то полезное. Кому-нибудь непременно понадобится внести в них изменения или выловить ошибку, а для осуществления этих планов ему или ей будет необходимо прочитать и понять вашу программу. Даже если вы —единственный, кому это может понадобиться, наиболее вероятно, что вы забудете как и почему вписали тот или иной фрагмент в программу, если только комментарии не напомнят вам об этом. Объявление и присваивание объектных переменных Объектные переменные объявляются так же, как и любые другие: вы используете ключевое слово Dim, далее следует имя переменной, а затем As Object. Чтобы присвоить значение объектной переменной, используйте следующую конструкцию: ключевое слово Set, имя переменной, знак равенства и формула, определяющая значение объекта. Например, приведенная ниже процедура создает объектную пере¬ менную, присваивает ей объект Range, ссылающийся на ячейку В5, и‘затем использует эту объектную переменную для присваивания значения ячейке: Sub Ob j Var () Dim theRange As Object Set theRange = ActiveSheet.Range("B5”) theRange,Value = 10 End Sub Использование операций Visual Basic Основные математические действия производятся с помощью операций. Чаще всего применяются операции сложения, вычитания, умножения и деления. Поскольку эти операции обрабатывают два значения, они известны также как бинарные. Боль¬ шинство операций являются бинарными, хотя отрицание — унарная операция, поскольку использует одно значение. 85
Visual Basic for Applications в примерах В табл. 5.1. перечислены все математические и строковые операции, используемые Visual Basic. Таблица 5.1. Математические и строковые операции Visual Basic Операция Название - Возведение в степень - Отрицание * Умножение 1 / тт Равный приоритет Деление J \ Деление нацело Mod Вычисление остатка от деления + Сложение "1 „ - Вычитание -1 Равныи пРиоРитет & Конкатенация (строк) Приоритет операций В табл. 5.1. операции перечислены в порядке их приоритетов. Приоритет опреде¬ ляет, какая операция в формуле будет выполняться первой. Обратите внимание на то, что операции умножения и деления имеют одинаковый приоритет — так же, как сложения и вычитания. В любой сложной формуле лучше не полагаться на приоритет операции в Вычислениях, а использовать круглые скобки для обеспечения гарантированной корректности вычислений. Например, следующая формула вычисляет месячную плату за использование ссуды в размере principal долларов за п месяцев при известном месячном проценте (rate): Payment = principal*rate/(1 - (1/(1+rate)Лп)) Благодаря скобкам выражение 1+rate будет вычислено первым, а затем возведено в Степень п. Далее единица делится на результат возведения в степень и результат этого Действия вычитается из единицы. На полученный результат делится произведение principal и rate. Без скобок эта формула была бы некорректно вычислена всего в три этана: Payment = principal*rate/l - 1/1 + rateAn 86
Возведение в степень обладает высшим приоритетом, поэтому в первую очередь будет вычислено значение rate в степени п, а не выражение 1+rate в той же степени. Следующими согласно приоритету идут умножение и деление; следовательно, principal будет умножен на rate, а затем результат разделен на единицу (лишнее вычисление); затем единица разделится на единицу (еще одно лишнее вычисление). И наконец, идут сложение и вычитание, объединяющие все три результата. Итак, результат некорректен из-за отсутствия скобок. Эксперименты с операциями Сложение, вычитание, умножение, деление, отрицание и возведение в степень — широко известные операции. Деление нацело, вычисление остатка от деления (Mod) и строковые конкатенации (сцепление строк) известны не так хорошо. Для экспериментов с этими операциями воспользуйтесь панелью Immediate окна Debug. Чтобы открыть окно Debug, переключитесь в модуль (или создайте модуль) и задайте директиву View/Debug Window. Выберите корешок Immediate, чтобы открыть панель Immediate. Любая команда Visual Basic, которую вы запишете в одной строке панели Immediate, будет немедленно выполнена. Чтобы результат вычислений был напечатан, набирайте ? или слово print перед тем, как задавать условия для вычислений. Например, если вы запишете ? 5\4, Visual Basic произведет деление нацело и возвратит результат, равный единице, как изображено в верхней части панели Immediate на рис. 5. t. Если различные условия для вычислений разделить запятыми, результаты этих вычис¬ лений будут печататься с шагом 14 символов. Попробуйте несколько примеров деления нацело, приведенных на рис. 5.1, чтобы разобраться, как работает эта операция. ? 5\4 1 ?25\2,26\2 12 13 ?1293\236 5 1 13 2 Рис. 5.1. Воспользуйтесь панелью Immediate окна Debug для проверки операции деления нацело 87
Visual Basic for Applications в примерах Совет: Панель Immediate окна Debug очень удобно использовать для проверки большин¬ ства операций или функций Visual Basic. Если вы не уверены в том, как функции работает, панель Immediate предоставит вам самую удобную возможность прове рить это. Все, что вы запишете в строке панели Immediate, немедленно буде] выполнено. Некоторые многострочные выражения также могут быть вычислены, если вы разделите строки двоеточиями, например: For I = 1 to 10:print I:Next I Это выражение запускает трехстрочный цикл For-Next, который выводит в окне числа от 1 до 10. При делении нацело два числа преобразовываются в целые путем округления, затем выполняется операция деления и результат преобразовывается в целое путем усечения младших разрядов. На рис. 5.1 показано несколько примеров выполнения операций деления нацело. Операция вычисления остатка от деления (Mod) дополняет операцию деления нацело, возвращая остаток. Например, 5 Mod 3 = 2, так как 5 : 3 = 1 с остатком 2. Операция вычисления остатка чаще всего используется в случаях, когда нужно вычислить день недели в будущем, или час дня по прошествии нескольких часов. Например, 100 Mod 7 укажет, какой будет день недели через сто дней, считая от настоящего момента, при этом день сегодняшний выступает под номером 1. Рассмотрите еще некоторые примеры вычисления остатка на панели Immediate, как показано на рис. 5.2. Q Debug - BOOK1.XLS.Modulel ( Watch | iwnediale ] |<Ready> u ? 5 Mod 3 I* 2 ? 5 Mod 2, 5 Mod 1, 5 Mod 4 1 0 1 ? 9\5 Рис. 5.2. Использование панели Immediate окна Debug для изучения операции вычисления остатка 88
Глава 5. Операторы присваивания и встроенные функции Последняя из этой группы операций —операция конкатенации строк. Операция конкатенации просто объединяет две строки в одну. Используйте ее для построения сложных строк, объединяющих куски текста и результаты строковых функций. На рис. 5.3 показан результат использования операции конкатенации совместно с неко¬ торыми функциями, возвращающими строки. Debug - Bookl .Modulel Watch T l—ediate ] l<R»*fc>■ ...... L.-,;,! ? "Большой " £ "привет" Большой привет ? "Результат деления нацело 7\5 = "£Str(7\5)" Результат деления нацело 7\5 - 1 с остатком 2 А « "Visual" В - "Basic" ? А £ " " £ В Visual Basic Рис. 5.3. Использование панели Immediate окна Debug для изучения операции конкатенации Во втором примере на рис. 5.3 функция Str() использована для преобразования числа в строку. В третьем примере переменным А и В присвоены строковые значения, которые затем были объединены. Функции, в том числе строковые, рассматриваются далее в этой главе. Примечание: Если оператор Option Explicit размещен в верхней части модуля, расположенного за окном Debug, вы должны объявить переменные перед их использованием. Поскольку применять оператор Dim на панели Immediate нельзя, то нельзя здесь и создавать новые переменные. Однако вы можете использовать переменные, объявленные в указанном модуле. Если оператора Option Explicit в модуле нет, то все переменные, которые вы записываете на панели Immediate, создаются автома тически. В Visual Basic используются операции двух других типов — операции сравнения и логические операции. Они рассматриваются в гл. 8 "Принятие решений". 89
Visual Basic for Applications в примерах Использование функций Visual Basic Хотя рассмотренные выше операции дают возможность осуществлять основные математические действия, более сложные вычисления производятся с помощью встро¬ енных функций. Visual Basic оперирует этими функциями подобно Excel, с тем отличием, что они всегда возвращают единичное значение. Функции Excel могут быть применены к целым массивам чисел и возвращать массивы в качестве результатов. Функции Visual Basic обычно применяются для обработки отдельных значений и возвращают отдельные значения. Чтобы обработать массив значений с помощью функции Visual Basic, вы должны применять эту функцию к каждому элемент}' массива в отдельности. Математические функции В табл. 5.2 перечислены математические функции, используемые в Visual Basic. Таблица 5.2. Математические функции Visual Basic Функция Описание ■ Atn Возвращает арктангенс числа Sin Возвращает синус угла в радианах Cos Возвращает косинус угла в радианах Tan Возвращает тангенс угла в радианах Exp Возвращает ех Log Возвращает натуральный логарифм числа (основание е = 2.71828...) Sqr Возвращает квадратный корень числа Randomize Инициирует генератор случайных чисел Rnd Возвращает случайное число Abs Возвращает абсолютную величину числа Sgn Возвращает знак числа Fix Округляет число отсечением дробной части Int Округляет число до ближайшего меньшего целого 90
Глава 5. Операторы присваивания и встроенные функции Математические функции, приведенные в табл. 5.2, обычно требуют в качестве аргумента единичное значение, некоторым образом его трансформируют и возвращают также одно число. Тригонометрические функции Sin О, Cos(), Тап() применяются к углам в радианах и преобразовывают их в соответствующую тригонометрическую величину. Если вы хотите использовать вместо радиан градусы, предварительно умножьте величину угла в градусах на л/180. Функция Atn() берет в качестве аргумента число и возвращает величину угла в радианах, являющуюся арктангенсом этого числа. Чтобы получить величину угла в градусах, умножьте результат функции Atn() на 180/л. Например, используя панель Immediate окна Debug, вычислите синус 27 градусов и арктангенс 5: ? Sin(27*3.1415/180) 0.453978116452753 ? Atn(5) 1.37340076694502 Далее следуют две логарифмические функции: Log() и Ехр(). Обе эти функции — натуральные логарифмы с основанием е=2.71828. Затем функция Sqr(), которая вычисляет квадратный корень числа. Далее идут функции Randomize и Rnd(). Для применения функции Rnd() (вычисление случайных чисел) следует однократно вызвать функцию Randomize, которая инициирует генератор случайных чисел, что позволяет каждый раз получать разные результаты. Случайные числа фактически вычисляются по формуле, и поэтому если каждый раз использовать одно и tq же начальное число, то будем получать один и тот же набор чисел. Функция Randomize использует системные часы для вычисления случайного начального числа, благодаря чему вы всегда будете получать различные наборы случайных чисел. Функция Rnd() возвращает случайные числа. Если ее аргумент отрицателен, он используется в качестве начального значения. Если аргумент равен 0, функция возвращает предыду- щее случайное число; если аргумент положителен или отсутствует, функция возвра¬ щает следующее случайное число в последовательности. Проверьте функцию Rnd на панели Immediate (полученные вами числа будут отличаться от показанных в следующем примере): Randomi z е ? Rnd, Rnd, Rnd 0.4425265 0.6843034 0.5414312 ? Rnd, Rnd, Rnd 0.6601722 0.9210758 0.2244858 ? Rnd, Rnd, Rnd 91
Visual Basic for Applications в примерах 0.243394 0.8367548 0.7939159 ? Rnd, Rnd, Rnd 0.6482016 9.553748E-02 0.1604512 ? Rnd(-l), Rnd(- 1), Rnd(-l) 0.224007 0.224007 0.224007 ? Rnd i 3.584582E-02 ? Rnd(0), Rnd(0) , Rnd(0) 3.584582E-02 3.584582E-02 3.584582E-02 ? Rnd(l), Rnd(l) , Rnd(l) 8.635235E-02 0.1642639 0.1797358 Функция Abs() возвращает абсолютную величину аргумента, а функция Sgn() возвращает знак числа. Две последних функции Fix() и Int() применяются для преобразования числа с плавающей точкой в формат целых чисел. Для преобразования числа с плавающей точкой функция Fix() использует усечение до целой части аргумента, а функция Int() —округление до ближайшего целого,. меньшего чем аргумент. Будьте осторожны с этими двумя функциями в случае отрицательного аргумента: результаты вычислений будут не всегда совпадать с вашими ожиданиями. Проверьте эти функции на панели Immediate. Следующие примеры включают функцию преобразования типов CIntO (рассматривается далее в этой главе), исполь¬ зующую округление до ближайшего целого: ? Abs(-4), Abs(4), Sgn(-4), Sgn(4) 4 4-1 1 ? Fix(3.7), Fix(3.3), Fix(-3.7), Fix(-3.3) 33-3 -3 ? Int(3.7), Int(3.3), Int(-3.7), Int(-3.3) 33-4 -4 ?CInt(3.7), CInt(3.3), CInt(-3.7), CInt(-3.3) 43-4 -3 Обратите внимание на то, что список функций в табл. 5.2 несколько сокращен. Приведены только четыре встроенные тригонометрические функции, на самом же деле тригонометрических функций около 24, включая часто используемые гиперболические функции. В табл. 5.3 перечислены несколько стандартных формул, где использованы встроенные функции для вычисления недостающих. Эти формулы предназначены для положительных и отрицательных значений аргумента х. Некоторые из них, содержа¬ щие знак (+-), дают двойной результат. Используйте знак плюс (+) для вычисления первого значения и знак минус (-) для вычисления второго. 92
Глава 5. Операторы присваивания и встроенные функции Таблица 5.3. Формулы общих математических функции, не вошедших в Visual Basic Функция Формула Секанс Sec(x) = 1/Cos(x) Косеканс Csc(x) = 1/Sin(x) Котангенс Ctg(x) = 1/Tan(x) Арксинус Arcsin(x) = Atn(x/Sqr(l-xA2)) Арккосинус* Arccos(x) = 1.5708 - Atn(x/Sqr(l - xA2) ) Арксеканс* Arcsec(x) = Atn(Sqr(xA2 - 1) + (Sgn(x) - l)*1.5708 Арккосеканс* Arccosec(x) = Atn(1/Sqr(xA2 - 1)) 4- (Sgn(x) - l)*1.5708 Арккотангенс Arcctg(x) = 1.5708 = Atn(x) Гиперболический синус Sh(x) = (Exp(x) - Exp(-x))/2 Гиперболический косинус Ch(x) = (Exp(x) 4- Exp(-x))/2 Гиперболический тангенс Th(x) = (Exp(x) - Exp(-x))/ (Exp(x) + Exp(-x)) Гиперболический секанс Sch(x) =-2/(Exp(x) 4- Exp(-x)) Гиперболический косеканс Csch(x) = 2/(Exp(x) - Exp(-x)) Гиперболический котангенс Cth(x) = (Exp(x) 4- Exp(-x))/ (Exp(x) - Exp(-x)) Ареасинус Arsh(x) = Log(x 4- Sqr(xA2 4- 1)) Ареакосинус Arch(x) = Log(x ± Sqr(xA2 - 1)) Ареатангенс Arth(x) = Log((l 4- x)/(l - x) )/2 Ареасеканс Arsch(x) = Log((l ± Sqr(l - xA2))/x) Ареакосеканс Arcsch(x) = Log((l ± Sqr(l 4- хл2))/х) Ареакотангенс Arcth(x) = Log((x 4- l)/(x - l))/2 Логарифм по основанию п LogN(x) = Log(x)/Log(n) Число 1.5708 —это приближенное значение л/2. 93
'Visual Basic for Applications в примерах Чтобы посмотреть эти формулы в работе, воспользуйтесь панелью Immediate.: приведенном ниже примере вы вначале присваиваете значение переменной, а зате1 вычисляете формулу, использующую эту переменную: х = 5 ? Atn(Sqr(xA2 - 1)) + (Sgn(x) - 1)*1.5708 'Арксеканс 1.36943840600457 х = -5 ? Atn(Sqr(x^2 - 1)) + (Sgn(x) - 1)*1.5708 'Арксеканс -1.77216159399543 х = 7 ? (Ехр(х) - Ехр(-х))/(Ехр(х) + Ехр(-х)) 'Гиперболический тангенс 0.999998336943945 Строковые функции Visual Basic располагает большим набором строковых функций для поиска; обработки строк. Перечень строковых функций, применяемых в Visual Basic, приведи в табл. 5.4. Таблица 5.4. Строковые функции 1 ? Функция Описание Str Comp Сравнивает две строки LCase Преобразовывает строку в нижний регистр U Case Преобразовывает строку в верхний регистр Space Создает строку пробелов String Создает строку символов Len Определяет длину строки Instr Ищет подстроку Lset Выравнивает строку по левому краю Rset Выравнивает строку по правому краю Left Выделяет левую часть строки 94
Глава 5. Операторы присваивания и встроенные функции Функция Описание Right Выделяет правую часть строки Mid Выделяет или перемещает подстроку LTrim Удаляет ведущие пробелы RTrim Удаляет завершающие пробелы Trim Удаляет пробелы с двух сторон строки Asc Возвращает ASCII код символа Ch г Возвращает символ по ASCII коду Str Преобразовывает число в строку Format Преобразовывает число в строку по формату Vai Преобразовывает строку в число Hex Преобразовывает шестнадцатиричное число в строку Oct Преобразовывает восьмеричное число в строку Функция StrCompO сравнивает две строки; функции LCaseO и UCaseO преоб¬ разовывают строку в символы нижнего или верхнего регистра. Функция Space() создает строку, состоящую из пробелов; функция StringO создает строку, состоящую из многократно повторяющегося символа. Функция Len() вычисляет длину строки, а функция Instr() находит подстроку. Функции LSetO и RSet() обычно используются при обработке строк фиксированной длины для выравнивания подстроки по левому или правому краю строки. Чтобы проверить эти строковые функции на панели Immediate, вначале вам следует задать две строки, которые будут обрабатываться с помощью этих функций: А = ’’Маленький рыжий щенок убежал. " В = "ЯнвФевМарАпрМайИюнИюлАвгСенОктНояДек" ? LCase (В) янвфевмарапрмайиюниюлавгееноктноядек ? UCase(B) ЯНВФЕВМАРАПРМАЙИНИЛАВГСЕНОКТНОЯДЕК ? Len(B) 36 ? Instr (В, "Июл” ) 19 д5
Visual Basic for Applications в примерах Для изучения функции LSetO и RSetO прежде всего потребуется строка фикси рованной длины. Обычные строки имеют переменную длину; это означает, что ок достаточно длинные, чтобы вместить записываемый в них текст. Для объявлена строки фиксированной длины укажите нужную длину в операторе Dim, объявляюще! эту строку. Поскольку оператор Dim не выполняется в окне Debug, вы должна использовать его в процедуре для того, чтобы объявить переменные. Приведенное ниже является фиктивной процедурой, которая предназначена дл объявления строки. Закройте окно Debug и запишите следующую процедуру в модуль Dim D As String * 10 Sub StrTstO D = "123456789012345" Stop End Sub Оператор Stop вызывает остановку выполнения программы, но не прекращав программу, что позволяет проверять и использовать ее переменные. Щелкните кноп кой мыши в любом месте процедуры и выберите директиву Run/Start. Процедур будет выполняться до оператора Stop, а затем откроется окно Debug. В окне Debug проверьте переменную D. Заметьте, что она включает только 1! символов, а не 15, которые были ей присвоены. Используя функции LSetO и RSetO можно поместить заданную подстроку, выровняв ее по левому или правому Kpat строки фиксированной длины: ? D 1234567890 LSet D = "абв" ? D абв Rset D = "где" ? D где Функции Left(), RightO и Mid О служат для выделения части строки. Функцш LTrimO, RTrimO и TrimO удаляют пробелы слева или справа или с двух конца строки. Функции Asc() и Chr() работают в паре для преобразования символов в кодь ASCII/ANSI и наоборот. И наконец, функции Str(), Val(), Нех() и Oct() преобра зовывают числа в строку и наоборот. Попробуйте эти функции на панели Immediate, применяя те же строковые констан ты, что и раньше: 96
Глава 5. Операторы присваивания и встроенные функции А = "Маленький рыжий щенок убежал." В = "ЯнвФевМарАпрМайИюнИюлАвгСенОктНояДек" Функции Left() и Righ() используют два аргумента — строку и число. Число определяет количество символов для выделения и возврата, причем отсчет начинается соответственно с левого крайнего или правого крайнего символа строки. Функция Mid() использует три аргумента: строку, начальный символ и количество выделяемых символов. Например, функция Mid(B, 10,3) выделяет три символа из строки В начиная с десятого символа слева. Используя заданную выше строку В, эта функция возвра¬ щает текст "Апр". Вы можете также использовать функцию Mid() с левой стороны формулы. В этом случае она превращается в оператор Mid. Оператор Mid заменяет указанную подстроку вместо того, чтобы выделить ее. Таким образом, оператор Mid(B, 10,3) = "Отпуск” заменяет ”Апр” на "Отп". ? Left (А, 9) Маленький ? Right (А, 7) убежал. 7 Mid (В, 10,3) Апр ? В ЯнвФевМарАпрМайИюнИю лАв г С е нО ктНо яД е к Mid(B, 10,3) = "Отпуск" ? В ЯнвФевМарО тпМайИюнИюлАв г С енОктНояДе к Обратите внимание на то, что в строке заменено только указанное количество символов. Чтобы вставить более длинную или более короткую строку в подстроку, используйте следующую конструкцию: ? Left (В,9)&"Отпуск"&Right(B,Len(В)-12) ЯнвФевМарО тпу с кМайИюнИюлАв гС енОктНояДе к Чтобы изучить функции вырезки LTrim, RTrim и Trim, вначале создайте строку, содержащую много лишних пробелов слева и справа. На практике функции вырезки чаще всего используются для чисел, которые были преобразованы в строки, так как преобразовывающие функции нередко помещают пробелы до и после преобразован¬ ного числа. Таким образом, видя, где начинаются и заканчиваются пробелы, поставьте вертикальные штрихи в любом конце вырезанной строки. Функции вырезки удаляют пустые места (включая символы табуляции) с соответствующих сторон строк: 4 Вильям Дж. Орвис 97
Visual Basic for Applications в примерах С = ” большой привет ? ” | ”,&LTrim(C)&’’| ” I большой привет. I ? "|"&RTrim(C)&"|" | большой привет| ? "|"&TRim(C)&"|" I большой привет| Подпрограммы преобразования превращают числа в текст и символьное предста! ление чисел в числа. Функция Str О делает не очень многое —просто преобразовывав число, например, 27, в строку десятичных чисел. Функции Oct() и Нех() преобраа вывают числа в восьмеричные (основание 8) и шестнадцатиричные (основание К строки. Функция Val() делает обратное преобразование —строки в число. Функщ Val() игнорирует любое количество пробелов слева и преобразовывает строку в чиа до тех пор, пока не встретит символ, который не может быть частью числа. Функш игнорирует любой другой текст или цифры в строке. Обратите внимание на то, Ч1 функция Vai() игнорирует также любое количество пробелов между цифрам Восьмеричную или шестнадцатиричную строки можно задать, поместив перед число' &о или &h. Функция Val() корректно преобразовывает числа этого типа: ? Str(27), Oct(27), Нех(27) 27 33 ? Val(” 234abc567”) 234 ? Vai (’’ 12 3 abc 123 ? Val(" 1.2el0abc' 12000000000 ? Vai("abc") 0 ? Val(” &оЗЗ”) 27 ? Val(” &hlB”) 27 Функции даты и времени Функции даты и времени приведены в табл. 5.5. Они обеспечивают преобразован!! из текстового формата даты или времени в последовательный номер даты (ш последовательный номер времени, если этот номер включает только время). Послед! вательный номер даты сохраняет дату и время в одном числе с плавающей точки1: 98
Глава 5. Операторы присваивания и встроенные функции Дата хранится в виде номера дня, начиная от 01.01.1900, а время —в виде дробной части дня. Таким образом, 0.5 —это полдень, а 0.75 —18:00. Если вы вычитаете два последовательных номера даты, то получаете количество дней между этими двумя датами. Таблица 5.5. Функции даты и времени Функция Описание Date Устанавливает или возвращает текущую дату Time Устанавливает или возвращает текущее время Now Возвращает текущие дату и время DateSerial Преобразовывает в последовательную дату, три целых числа: год, месяц и год DateValue Преобразовывает в последовательную дату, символьное представление даты TimeSerial Преобразовывает в последовательную дату, три целых числа: часы, минуты и секунды Time Value Преобразовывает в последовательную дату, символьное представление времени Timer Возвращает временной интервал от полуночи Day Month Преобразовывает последовательную дату в день месяца Преобразовывает последовательную дату в месяц года Weekday Year Преобразовывает последовательную дату в день недели Преобразовывает последовательную дату в год Hour Преобразовывает последовательную дату в часы дня Minute Преобразовывает последовательную дату в минуты в часе Second Преобразовывает последовательную дату в секунды в минуте Первые две функции Date() и TimeO устанавливают или получают текущую системную дату. Если эти функции находятся в правой части оператора присваивания, они. позволяют получить значения даты или времени; если же находятся слева —то устанавливают. Функция Now() возвращает текущую дату и время в виде последова¬ тельного номера даты. 99
Visual Basic for Applications в примерах В панели Immediate воспользуйтесь функцией FormatO для отображения после¬ довательного номера даты в виде даты и времени или функцией CDblO — для преобразования их в число с плавающей точкой двойной точности: ? Format(Date,"mm/dd/yy hh:mm") 11/19/93 00:00 ? Format(Time," hh:mm") 00:25 ? Format(Now,"mm/dd/yy hh:mm") 11/19/93 00:25 ? CDbl(Now) 34292.0210416667 Функция DateSerialO использует три целых аргумента —год, месяц*и день (обратите внимание на порядок) —и преобразовывает их в последовательный номер даты. Функция DateValueO преобразовывает дату в символьном виде в последовательный номер дата. Функции TimeSerial() и TimeValueO выполняют те же действия для времени: ? Format(DateSerial(94,12,27),"mm/dd/уу/ hh:mm") 12/27/94 00:00 ? Format (DateValue ("12/.27/94") , "mm/dd/уу/ hh:mm") 12/27/94 00:00 ? Format (TimeSerial(12,18,27),"hh:mm:ss" ) 12:18:27 ? Format(TimeValue("13:44"),"hh:mm") 13:44 Функция Timer возвращает количество миллисекунд, отсчитанных начиная с полуночи предыдущего дня. Вызывая Timer до и после процедуры, можно определить время ее выполнения. Timer 2761.98 Функции Day(), MonthO, WeekdayO, Year(), Hour(), MinuteO и SecondO используют в качестве аргумента последовательный номер даты. Они возвращают день месяца, месяц года и т.д. в виде целого числа. ?Day(Now) 19 ? Month (Now) 100
Глава 5. Операторы присваивания и встроенные функции и ? Year (Now) 1993 ? Weekday (Now) 6 ? Hour (Now) 0 ? Minute (Now) 54 ? Second (Now) 8 Преобразование типов данных в Visual Basic Преобразование данных из одного типа в другой обычно производится в Visual Basic автоматически. Когда вычисляется формула, Visual Basic преобразовывает все числа в этой формуле в наиболее точный тип, вычисляет формулу, а затем преобра¬ зовывает результат в тип переменной слева. Функции преобразования типов, приве¬ денные в табл. 5.6, имеют два основных применения: описание преобразования типа и выполнение нетрадиционных преобразований. Таблица 5.4. Функции преобразования типов Функция Описание CBool Преобразовывает в тип Boolean CCur Преобразовывает в тип Currency CDate Преобразовывает в тип Date CDbl Преобразовывает в тип Double Clnt Преобразовывает в тип Integer CLng Преобразовывает в тип Long CSng Преобразовывает в тип Single CStr Преобразовывает в тип String CVar Преобразовывает в тип Variant CVErr Преобразовывает в номер ошибки 101
Visual Basic for Applications в примерах Если преобразование типа для вычислений не очевидно, вы можете использоват функции преобразования типов для выполнения преобразований вместо того, чтоб позволить Visual Basic сделать это за вас.- Хотя от вас не требуется производит преобразования таким образом, тем не менее делая так, вы поясняете, что во нт преобразовывается. Если вы хотите произвести нетрадиционные вычисления, то должны использоват эти функции для преобразования. Нетрадиционные вычисления необходимы дл решения таких задач, как визуализация даты в виде последовательного номера дата а не собственно даты, или преобразование числа в формате с плавающей точкой целое для усечения его величины. Ранее, в разд. "Функции даты и времени" это главы функция CDblO была использована для преобразования даты в последователь ный номер даты, и, таким образом, можно было увидеть, как дата и время храните в одном числе. Если вы попробуете вывести значение даты без преобразования ее символьное представление или без преобразования ее в число, то получите ошибку. Кроме этих функций, с целью преобразований различных типов данных исполь зуются математические функции Int() и Fix О, а также строковые функции Str( Val() и FormatO. Заключение В данной главе вы ознакомились с операторами присваивания, операторам комментариев и встроенными функциями Visual Basic. Основная работа в компьютер ной программе производится операторами присваивания. С их помощью вычисляют новые значения с использованием существующих величин и функций, а там организуется их хранение в памяти. Вы используете операторы комментариев дл: пояснения назначения операторов присваивания. Встроенные функции предоставляю возможность пользоваться основными числовыми и строковыми функциями, необхе димыми для производства большинства вычислений. Следующие две главы "Создани и применение процедур" и "Создание и применение функций" научат вас создават собственные функции. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Какие действия производит оператор присваивания? 2. Как Visual Basic производит преобразования типов данных при вычислена формулы? 3. Каким образом вы создаете и присваиваете значения объектной переменной? 4. Какие действия производят операции? 102
Глава 5. Операторы присваивания и встроенные функции 5. Что такое приоритет операции и как вы можете изменить порядок вычислений в формуле? 6. Какую функцию вы использовали бы, чтобы округлить -3.4 до -3? Упражнения для закрепления материала 1. Преобразуйте 1.34 радиана в градусы. 2. Вычислите значение 355/113 на панели Immediate. Это известное приближение к числу л и оно легко запоминается, так как использует каждое из первых трех нечетных целых чисел дважды. 3. Вычислите обратный гиперболический котангенс 25 на панели Immediate окна Debug. 4. Задайте три переменные на панели Immediate. Переменная А —это строка "Меня зовут", В —ваше имя, заключенное в кавычки, а С —ваша фамилия (также в кавычках). Запишите формулу, объединяющую эти три строки в предложение. 5. Напишите формулу, которая использует функцию Now() для получения даты и времени, выделяет дробную часть десятичного числа и умножает на количество минут в сутках. В результате должно получиться количество минут от полуночи до текущего времени. 6. Напишите формулу для определения дня недели 327 дней назад.
Часть III Функции и процедуры
Глава 6 Создание и применение процедур В этой книге вы уже встречались с понятием ’процедура", например, в гл. 1 вы поручали Visual Basic создать процедуру. Сейчас настало время рассмотрев проце¬ дуры более детально, чтобы узнать, как они работают и как их создавать. В данной главе описано: ► Что такое процедура ► Какие существуют типы процедур ► Что такое процедура обработки событий Что такое процедуры? Процедуры — это самостоятельно используемые небольшие программы, которые можно написать на Visual Basic. Один или более операторов Visual Basic объединяются в процедуре для решения определенной задачи. Прикладная программа в зависимости от сложности может объединять одну или более процедур. Обычно вы будете решать задачи, в которых процедуры осуществляют простые преобразования. Если задача слишком сложна, разбейте свою программу на несколько процедур, каждая из которых возьмет на себя решение небольшой части задачи. Каждая процедура начинается с оператора объявления процедуры и заканчивается оператором End Sub: Sub имя_процедуры (аргументы) тело процедуры End Sub Оператор объявления процедуры присваивает ей имя, отмечает ее начало и перечисляет аргументы, которые передаются процедуре при вызове из программы. Оператор End Sub отмечает конец процедуры. Все, что расположено между этими двумя операторами, называется телом процедуры и реализует возложенную на проце¬ дуру задачу. Список аргументов обеспечивает связь между вызывающей и вызываемой проце¬ дурами. Хотя те или иные переменные, объявленные глобальными, доступны обеим процедурам, некоторые специфические переменные должны передаваться и прини¬ маться в качестве списка аргументов. 107
Visual Basic for Applications в примерах Типы процедур Существует четыре типа процедур > Общие процедуры > Командные процедуры > Процедуры обработки событий > Функции Функции аналогичны встроенным функциям Visual Basic и более подроби рассматриваются в гл. 7. Отличительные особенности остальных трех типов процеду: в основном определяются решаемыми задачами. Общие процедуры Общие процедуры, известные как просто процедуры, —это стандартные прбце дуры Visual Basic, которые, в общем, ничего не изменяют вне своего тела. Он; вычисляют переменные, отображают документы, передают сообщения другим пре граммам и манипулируют дисковыми файлами. Командные процедуры Командные процедуры расширяют возможности прикладных программ на Visua Basic. В частности, они расширяют возможности Excel, касающиеся рабочих папо: или их содержимого. Следовательно, функционирование таких процедур аналогичн( директивам меню Excel. Процедуры, созданные макрорекордером, можно рассматри вать как примеры командных процедур, так как они представляют собой записанны; действия с таблицей. Командные процедуры обычно не получают никаких аргументов. Если им нужнь: какие-либо данные от пользователя, они, чтобы получить эти данные, выводя: диалоговое окно. Процедуры обработки событий Процедуры обработки событий связаны с конкретными событиями и выполняются когда эти события происходят. Событие —это нажатие командной кнопки, выполне ние директивы меню, открытие или закрытие таблицы Excel, изменение содержимое диалогового окна и т.п. Отметьте, что процедуры обработки событий могут быть такж и командными или общими процедурами. Чтобы сделать процедуру обрабатывающей событие, нужно просто подключить эту процедуру к событию. Например, в гл. 1 вы подключили процедуру к командной кнопке и, таким образом, эта процедура стала обрабатывающей событие, связанное с нажатием командной кнопки. 108
лава оздание и применение проие< Чтобы процедура обработки событий была активной всякий раз, когда пользова¬ тель открывает или закрывает рабочую папку, назовите ее Auto_Open или Auto Close. Чтобы процедура обработки событий запускалась на выполнение всякий раз, когда пользователь активизирует таблицу, приравняйте имя процедуры к свойству OnSheet- Activate конкретного объекта таблицы. Например, оператор Worksheets("Sheetl”).OnSheetActivate = "Initlt” запускает процедуру Initlt всякий раз, когда пользователь активизирует Sheetl. Если вы используете объект Application, процедура будет выполняться каждый раз, когда пользователь активизирует таблицу. Аналогично можно использовать свойство OnSheetDeactivate для выполнения процедуры каждый раз, когда пользователь деактивизирует таблицу (деактивизируют таблицу, закрывая ее или активизируя другую таблицу). Вызов процедуры До сих пор в этой книге вы исполняли процедуры, выбирая их в диалоговом окне Macro или подключая к командной кнопке. Однако процедура может быть выполнена и посредством вызова из другой процедуры; это называется "вызов процедуры". Чтобы вызвать процедуру, просто поместите ее имя в то место в другой процедуре, где хотите ее вызвать. Если вызываемая процедура нуждается в аргументах, поместите их после имени процедуры. Например, в следующем примере процедура Procl вызывает процедуру Ргос2: Sub Procl () 'Некоторые операторы. Ргос2 'Некоторые другие операторы. End Sub Sub Proc2 () ' Тело процедуры Proc2 End Sub Вызов процедуры из другого модуля Когда вы вызываете процедуру, Visual Basic вначале ищет ее в том же модуле, где находится вызывающая процедура, а затем —во всех других модулях, подключенных 109
Visual Basic for Applications в примерах к текущей рабочей папке. Таким образом, чтобы вызвать любую процедуру в текущей рабочей папке, как правило, не нужно делать ничего особенного. Тем не менее, если текущая рабочая папка содержит более одной процедуры с одинаковыми именами, то придется указать еще и имя модуля, в котором нужно искать эту процедуру. Чтобы подключить имя модуля к имени процедуры, нужно записать его в квадратных скобках, затем поставить точку и записать имя процедуры. Например, если Procl находится в Modulel, Ргос2 —в Module5, а другая Ргос2 —в Module2, то для вызова процедуры Ргос2 из Module5 нужно записать нечто, подобное такому примеру: Sub Procl() 'Некоторые операторы. [Module5].Ргос2 ' Некоторые другие операторы. End Sub Если же в рабочей папке всего одна процедура Ргос2, то ссылка на модуль не нужна. Однако, если в рабочей папке много модулей и процедур, то следует включать имя модуля, чтобы сделать код более читабельным и чтобы можно было легко найти вызываемую процедуру. Вызов процедуры из другой рабочей папки Если процедура, которую вы хотите вызвать, находится в другой рабочей папке, то нужно только подключить эту папку к текущей рабочей папке, чтобы сделать все ее модули и процедуры доступными. Для подключения другой рабочей папки к вашей текущей рабочей папке выпол¬ ните следующие действия: 1. Убедитесь, что другая рабочая папка сохранена. Нельзя подключить несуществую¬ щую рабочую папку. 2. Из активного модуля в текущей рабочей папке укажите директиву Tools/Re¬ ferences. 3. В диалоговом окне References отметьте контрольный индикатор перед именем нужной рабочей папки, как показано на рис. 6.1, затем нажмите командную кнопку ОК. Если рабочая папка не попала в список, воспользуйтесь командной кнопкой Browse для ее поиска. 110
Глава 6. Создание и применение процедур References Available References: Visual Basic For Applications » Microsoft Excel 5.0 Object Library UNSAVED: Bookl MISSING: ACTIVE.XLS STYLEPL.XLS L-°>O Cancelj | Browse... | I Help | jMYStUFRXLS к -Group Path: IAEXCEL\MYSTUFF.XLS Language: English/United States Рис. 6.1. Подключение рабочей папки при помощи диалогового окна References После того как подключена рабочая палка, Visual Basic будет искать модули в этой папке, если не сможет найти вызываемую процедуру в текущей рабочей папке. Если процедуры с заданным именем включает несколько рабочих папок, необхо¬ димо, так же как и в случае с несколькими модулями, подключить имя рабочей папки к имени процедуры (или к именам модуля и процедуры), чтобы конкретно указать пару "рабочая папка-модуль", содержащую требуемую процедуру. Например, если Procl вызывает Ргос2, а Ргос2 находится в Module5 рабочей папки MYSTUFF.XLS, то следует добавить имена модуля и рабочей папки к вызову Ргос2: Sub Procl () 'Некоторые операторы. [MYSTUFF.XLS].[Module5].Proc2 'Некоторые другие операторы. End Sub И опять, если во всех подключенных рабочих папках находится только одна Ргос2, то нет необходимости подключать имя рабочей папки. Тем не менее, сделав это, вы улучшите читабельность своих программ, а также будете уверены в том, что если две процедуры будут иметь одинаковое имя, то вы точно укажете, какую именно процедуру вызываете. Предотвращение доступа к модулям и процедурам Теперь, когда вы знаете как получить доступ к процедурам в других модулях и рабочих папках, возникает вопрос: как можно предотвратить доступ к данной проце¬ дуре из процедур, не включенных в данный модуль? Часто субпроцедуры создаются для других процедур модуля, но доступ к этим процедурам вне пределов данного 111
Visual Basic for Applications в примерах модуля нежелателен. Нередко наборы подобных, но различных процедур с одинаке выми именами имеются в двух или более модулях. Так, в программе анализа запасов сырья можно создать модуль для анализа каждог вида запасов и разработать процедуру анализа тенденций для конкретного вида сыры Вместо того чтобы беспокоиться о семействе процедур с именем Trend, вы можете сделат: каждую из этих процедур частной для модуля, в котором она расположена. Чтобы сделать процедуру частной для модуля, поместите ключевое слово Privat: в начало оператора объявления процедуры. Например, чтобы сделать процедуру Proc'.* доступной исключительно для процедур того же модуля, используйте заголово; процедуры: Private Sub Procl() End Sub Чтобы защитить весь модуль от доступа из процедур другой рабочей папки поместите в начало модуля следующий оператор: Option Private Module Передача величин процедуре Аргументы в заголовке процедуры дают возможность определить имена и типь: величин, которые будут переданы этой процедуре. За исключением глобальных перемен ных и переменных, определенных на уровне модуля, все переменные в процедуре являются частными для данной процедуры. Это означает, что переменные, определенные в процедуре, не могут быть прочитаны или изменены вне ее пределов, исключая аргументы, указанные в заголовке процедуры и передаваемые этой процедуре или из нее. Объявление переменных в заголовке процедуры Кроме объявления переменных, которые могут передаваться в процедуру или возвращаться вызывающей процедуре, вы объявляете также тип этих переменных. При этом используются те же типы, что и при определении типов переменных в операторе Dim. В качестве примера рассмотрим следующую простую процедуру, которая всего лишь получает два первых аргумента, перемножает их и возвращает результат в виде третьего аргумента: Sub MultiplyEm(Valuel As Single, Value2 As Single, _ Product As Single) 112
Глава 6. Создание и применение процедур Product = Valuel * Value2 End Sub Обратите внимание на то, что все три аргумента определены как тип Single. Так же как и в операторе Dim, если тип аргумента не определен, по умолчанию считается, что это аргумент типа Variant. Чтобы вызвать эту процедуру из другой процедуры, можно использовать нечто наподобие: Sub TestProc () Dim Result As Single MultiplyEm 5, 7, Result Debug.Print Result End Sub Эта процедура определяет переменную типа Single, передает числа 5 и 7 процедуре MultiplyEm и получает результат в переменной по имени Result. Метод Print объекта Debug печатает результат на панели Immediate окна Debug. Чтобы выполнить эту процедуру, откройте окно Debug и запишите на панели Immediate команду: Run("TestProc”) На рис. 6.2 приведено напечатанное значение переменной Result, полученное после выполнения команды Run. Debug Bookl _ f Watch T lm«»edie*e 'j |<Ready> LJ ” ■ — Run("TestProc") 35 Sub MultiplyEm(Valuel As Single, Value2 As Single, _ Product As Single) Product - Valuel 1 ► Value2 End Sub Sub TestProc() Dim Result As Single MultiplyEm 5, 7, Result Debug.Print Result End Sub Рис. 6.2. Запуск процедуры на панели Immediate окна Debug 113
Visual Basic for Applications в примерах Использование списка, разделенного запятыми Когда вы вызываете процедуру и помещаете аргументы в список, разделенный запятыми, они рассматриваются как последовательно передаваемые значения перемен¬ ных, указанных в заголовке процедуры. Например, в процедуре MultiplyEm перемен¬ ной Value! передается значение 5, переменной Value2 — значение 7, а результат, содержащийся в переменной Product, передается переменной Result. Если вы не можете использовать литеральные значения в качестве аргументов процедуры, то используйте переменные так, как это сделано в следующем примере: Sub TestProc() Dim Result As Single, ValA As Single, ValB As Single ValA = 5 ValB = 7 MultiplyEm ValA, ValB, Result Debug.Print Result End Sub Выполнение этой процедуры будет иметь тот же результат, что и предыдущая версия TestProc, в которой в вызове процедуры применялись литеральные значения. Использование списка поименованных аргументов Кроме списка, разделенного запятыми, для передачи аргументов можно применять поименованный список. В поименованном списке используется имя переменной из заголовка процедуры, затем знак присваивания (:=), затем значение или переменная, которые вы хотите передать. Если вы прибегаете к поименованному списку, то аргументы не должны строго соответствовать порядку, указанному в заголовке процедуры (как в случае списка, разделенного запятыми). Следующая версия TestProc получит тот же результат, что и две предыдущие: Sub TestProc() Dim Result As Single, ValA As Single, ValB As Single ValA = 5 ValB = 7 MultiplyEm Product:=Result, Valuel:=ValA, Value2:=ValB Debug.Print Result End Sub Поименованные аргументы целесообразно использовать для разъяснения того, что вы передаете процедуре. Это особенно удобно для процедур с большим количеством аргументов и таких, где не всегда очевидно, какое значение какой переменной передается. 114
Глава 6. Создание и применение процедур Передача величин адресом Когда вы вызываете процедуру и передаете ей переменную в виде аргумента, вы на самом деле передаете процедуре не значение, а адрес того места в памяти, в котором это значение записано. Если процедуре требуется данное конкретное значение аргу¬ мента, то для доступа к нему она использует адрес, который передан ей через заголовок. Когда процедуре нужно возвратить значение, она делает то же самое: использует адрес, передавая, как найти то место в памяти, в котором записано это значение. Передача значения адресом не требует какого-либо дополнительного программи¬ рования, — этот метод передачи аргументов применяется по умолчанию. Передача величин значением Когда аргумент передается процедуре адресом, процедура может изменить его значение. Иногда необходима уверенность в том, что вызываемая процедура не изменит значения аргумента, которое вы ей передали. В этом случае передавайте аргументы величиной, а не адресом. Передать аргументы величиной можно двумя способами — модифицировав заго¬ ловок процедуры или преобразовав аргумент в формулу. Любой из этих способов защищает значение аргумента от изменений. Модификация заголовка процедуры Чтобы модифицировать заголовок процедуры для передачи аргумента значением, нужно перед именем аргумента записать ключевое слово By Vai. Например, чтобы быть уверенным в неизменности двух входных аргументов, модифицируйте процедуру MultiplyEm следующим образом: Sub MultiplyEm(By Vai Valuel As Single, By Vai Value2 As Single, _ Product As .Single) Product = Valuel * Value2 Valuel = 99 End Sub Обратите внимание на то, что вставленный оператор используется для изменения значения аргумента Valuel. Убедитесь в том, что вы не поместили ключевое слово ByVai перед аргументом, который возвращает результат; в противном случае ничего не будет возвращено. Для печати значения переменной ValA, которая передается в виде аргумента Valuel, модифицируйте процедуру TestProc следующим образом: 115
Visual Basic for Applications в примерах Sub TestProc() Dim Result As Single, ValA As Single, ValB As Single ValA = 5 ValB = 7 MultiplyEm Product:=Result, Valuel:=ValA, Value2:=ValB Debug.Print Result Debug.Print ValA End Sub Если запустить эту процедуру из окна Debug прежде, чем будут записана ключевые слова By Vai, получится следующий результат: Run("TestProc") 35 99 После того как вы вставите ключевые слова By Vai, результат изменится: Run("TestProc") 35 5 Преобразование аргумента в формулу Вторым способом передачи аргумента величиной является преобразование его е формулу. Аргумент может быть не только формулой, но и формулой, котора* преобразовывается в величину корректного типа. Когда в качестве аргумента проце дуры используется формула, эта формула вычисляется, результат помещается к временное хранилище в памяти, а адрес этого временного хранилища затем передаете вызываемой процедуре. Если вызываемая процедура изменит значение аргумента, тс изменится лишь величина во временном хранилище, а не оригинальное значение. Чтобы преобразовать единичную величину в формулу, нужно просто заключить ее в круглые скобки. Для экспериментирования используйте предыдущий пример Удалите ключевые слова By Vai из заголовка процедуры и вызовите процедуру без ключевых слов. Затем попробуйте вызвать ее еще раз, предварительно заключив аргумент ValA в круглые скобки. Без круглых скобок процедура и результат буду иметь следующий вид: Sub TestProc() Dim Result As Single, ValA As Single, ValB As Single ValA = 5 ValB = 7 116
Глава 6. Создание и применение процедур MultiplyEm Product:=Result, Valuel:=ValA, Value2:=ValB Debug.Print Result Debug.Print ValA End Sub Sub MultiplyEm(Valuel As Single, Value2 TVs Single, _ Product As Single) Product = Valuel * Value2 Valuel = 99 End Sub e Run ("TestProc") 35 99 Если вставить круглые скобки, процедура и результат изменятся таким образом: Sub TestProc () Dim Result As Single, ValA As Single, ValB As Single ValA = 5 ValB = 7 MultiplyEm Product:=Result, Valuel:=(ValA) , Value2:=ValB Debug.Print Result Debug.Print ValA End Sub Sub MultiplyEm(Valuel As Single, Value2 As Single, _ Product As Single) Product = Valuel * Value2 Valuel = 99 End Sub Run("TestProc") 35 5 Примечание: Все структуры данных, включая массивы, должны передаваться адресом, и только единичные величины могут быть переданы значением. Чтобы передать структуру данных значением, ее предварительно придется полностью продуб¬ лировать. 117
Visual Basic for Applications в примерах Использование необязательных аргументов В некоторых случаях требуется разрешить пользователю не определять какой-либо аргумент, а использовать вместо него значение по умолчанию. Чтобы сделать это, запишите перед именем аргумента в заголовке процедуры ключевое слово Optional. В этом случае процедура должна проверить данный аргумент и, если он отсутствует, использовать вместо него значение по умолчанию. Следующая процедура объявляет второй входной аргумент необязательным и использует значение по умолчанию, если пользователь не задал этот аргумент: Sub MultiplyEm(Product As Single, Valuel As Single, Optional Value2) If IsMissing(Value2) Then Value2 = 10 Product = Valuel * Value2 End Sub Обратите внимание на то, что вы изменили аргументы следующим образом: сделали последний аргумент необязательным и удалили его тип, преобразовав его в тип Variant. Вы произвели эти изменения потому, что когда используется ключевое слово Optional, все аргументы справа от него будут необязательными и должны принадлежать к типу Variant. Вторая строка в процедуре использует оператор If, чтобы проверить отсутствие величины (оператор If подробно описывается в гл. 8). Если аргумент не был передан, функция IsMissingO возвращает True, в противном случае она возвратит False. Если аргумент отсутствует, оператор If присвоит перемен¬ ной Value2 значение ио умолчанию 10. Проверьте вызов процедуры, в котором отсутствует один аргумент: Sub TestProc() Dim Result As Single, ValA As Single, ValB As Single ValA = 5 ValB = 7 MultiplyEm Product:=Result, Valuel:=ValA Debug.Print Result Debug.Print ValA End Sub В окне Debug вы увидите следующий результат: Run (’’TestProc") 50 5 118
Глава 6. Создание и применение процедур Использование неопределенного количества аргументов Многие из встроенных функций таблиц Excel могут использовать один или более аргументов, причем конкретное количество аргументов не известно вплоть до выпол¬ нения. Можно создать процедуру с аналогичными возможностями, поместив ключевое слово РагатАггау перед последним аргументом в заголовке процедуры, и преобразовав тем самым этот аргумент в массив переменных типа Variant. В самой процедуре необходимо определить, сколько элементов может содержать этот массив, чтобы затем оперировать этими элементами. Используйте функцию UBoundO, чтобы найти вер¬ хний заданный элемент массива и цикл For Each (описанный в гл. 14 "Применение циклов объектного типа") для операций со всеми элементами массива. Следующая процедура MultiplyEm2 получает неопределенное количество аргумен¬ тов и возвращает произведение всех этих аргументов: Sub TestProc () Dim Result As Single, ValA As Single, ValB As Single Dim ValC As Single, ValD As Single, ValE As Single ValA = 5 ValB = 7 ValC = 3 ValD = 12 ValE = 4 MultiplyEm2 Result, ValA, ValB, ValC, ValD, ValE Debug.Print Result End Sub Sub Mui tipi у Em2 (Product As Single, ParamArray Values ()) Product = 1 For Each Value In Values Product = Product * Value Next Value End Sub Run ("TestProc") 5040 Процедура TestProc вызывает процедуру MultiplyEm2, которая присваивает 1 переменной Product, а затем оператором For Each последовательно умножает эту переменную на все элементы массива Values, которые она обнаружит. Возвращаемое значение является произведением пяти входных величин. 119
Visual Basic for Applications в примерах Примечание: Абзацные отступы, используемые в приведенных в данной книге процедурах, имя в основном косметическое назначение, но также и вспомогательное, — подчерки^ важность функции. Различные операторы, такие как операторы циклов и оператор If, разбивают процедуры на функциональные блоки. Абзацные отступы в начале это; блока и возврат к предыдущей позиции в его конце, позволяют сделать блок 6o.it читабельным, а также гарантируют, что вы не упустили ни один из ограничите^ блока. Если же вы дошли до конца процедуры и не вернулись к самой левой позищ. экрана — можете быть уверены; вы что-то где-то упустили. Обеспечение сохранности переменных в процедуре Когда процедура завершается и возвращает управление вызывающей процедур все переменные, объявленные в процедуре, разрушаются и не могут быть повтори использованы. Если процедура вызывается в следующий раз, новый набор переменны должен быть объявлен, использован, а затем разрушен после окончания процедура Естественно, это не касается переменных, объявляемых вне процедуры: они сох раня; ся до тех пор, пока объявившая их процедура не окончит свою работу. Переменные объявленные на уровне модуля, сохраняются до конца работы программы. Тем не менее иногда нужно сохранить переменные в процедуре до следующей вызова —например для сбора статистики о вызова х и р< • :рдуры или для использованп; результатов предыдущих вычислений в качестве начальной точки для следующих. 1 таких случаях следует объявить все переменные в процедуре на уровне модуля и.и же объявить их статическими. Используйте оператор Static для объявления перемен ных в процедуре таким же образом, как вы используете оператор Dim. Однако: отличие от переменных, объявленных Dim, переменные, объявленные Static, не уходя: после окончания процедуры. Например, вместо оператора Dim Result As Single используйте оператор Static, который позволит сохранить переменную Result ? процедуре и после того, как она передаст эту переменную вызвавшей процедуре: Static Result As Single Заключение В этой главе вы ознакомились с тремя из четырех типов процедур: общими процедурами, командными процедурами и процедурами обработки событий. Общш процедуры —это обычные процедуры, которые вычисляют или отображают что-либо командные процедуры манипулируют другими приложениями, такими как Excel иж 120
Глава 6. Создание и применение процедур Word. Процедуры обработки событий, которые также могут быть общими или командными, выполняются при наступлении определенного события, такого как открытие таблицы или нажатие командной кнопки. С помощью гл. 7 вы изучите функции —четвертый тип процедуры. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое процедура? 2. Дайте определение процедурам, описываемым в этой главе. Чем они отличаются? 3. Каким оператором начинается и каким оканчивается процедура? 4. Какой тип процедур создает макрорекордер? 5. Что такое вызов процедуры? Как можно вызвать процедуру? 6. Как сохранить значения переменных от одного вызова процедуры до другого? 7. Как сделать процедуру невидимой из других модулей? 8. Что подразумевается под передачей аргументов адресом? 9. Что подразумевается под передачей аргументов значением? 10. Как передать аргументы адресом? Как передать их значением? 11. Как вызвать процедуру из другой рабочей папки? Упражнения для закрепления материала 1. Напишите процедуру, именуемую Addlt, которая складывает значения двух аргументов и возвращает результат в третьем. 2. Напишите процедуру, именуемую Sumlt, которая складывает список аргументов — чисел с плавающей точкой —и возвращает сумму. 3. Во втором модуле создайте вторую процедуру Sumlt, складывающую список целых чисел. Затем в другой рабочей папке напишите процедуру, которая использует обе процедуры Sumlt, причем первую — для сложения списка вещественных чисел (2.7, 5.9, 23.85, 19.6, 8.4) и печати результата на панели Immediate окна Debug, а вторую —для сложения списка целых чисел (3, 45, 27, 15, 5, 14, 1) и печати результата таким же образом, как и в первом случае. Убедитесь в том, что вы выбрали правильную версию процедуры Sumlt. 4. Создайте стартовую процедуру, которая выполняется при старте Excel и печатает "Доброе утро!” в центре активной таблицы. 121
Глава 7 Создание и применение функций В гл. 6 были описаны три типа процедур, допустимых в Visual Basic: общ процедуры, командные процедуры и процедуры обработки событий. Настоящая гла знакомит вас с четвертым типом процедур Visual Basic —функциями. В Visual Bas имеется много встроенных функций; кроме того можно получить доступ к ес большему количеству функций, расположенных во внешних библиотеках. Visual Bas позволяет также создавать свои собственные пользовательские функции. Данная гла учит вас делать следующее: > Создавать и применять функции > Задавать тип данных для функции > Создавать пользовательские функции таблиц > Использовать функции таблиц > Использовать функции из внешних библиотек Что такое функции? Функции — это процедуры, возвращающие результирующее значение в свое имени. Другими словами, функции ведут себя как субпроцедуры. Главным преим ществом функций перед субпроцедурами является то, что их можно использова: непосредственно в формуле, подобно переменной. Чтобы использовать значенн возвращаемое субпроцедурой, следует присвоить это значение некоторой переменна а затем использовать эту переменную в формуле. Так как функции могут быть использованы в формуле, соглашения по их создам и применению несколько отличаются от того, что было описано в гл. 6. Но, основнь свойства и ограничения сохраняются. Создание функций Способ создания функций подобен способу создания субпроцедур, однако зд« есть и некоторые существенные различия. Заголовок и концовка функции подобк заголовку и концовке субпроцедуры, но взамен слова Sub используется слово Function Function имя_функции (аргументы) As тип Заголовок функции объявляет имя функции и некоторые аргументы. Кроме тог: так как функция используется в формуле подобно переменной, то она имеет и те данных подобно переменной. Тип данных определяют в заголовке функции, с 122
Глава 7. Создание и применение функций заголовком следует тело функции, позволяющее решить возложенную на функцию задачу. Особым условием функции является оператор присваивания, который при¬ сваивает полученное значение имени функции непосредственно перед окончанием ее работы. Оканчивается функция оператором End Function. Установка типа данных функции Заголовок функции содержит место для аргументов и объявлений типа данных функции. Список аргументов для функции объявляется так же, как и для процедуры. Каждая переменная в списке аргументов содержит предложение As тип, которое определяет тип аргументов функции. Аргументы предназначены для получения данных функцией либо для передачи данных из функции (либо и для того и для другого). Так же, как и в операторах Dim и Sub, если тип данных аргумента, не объявлен по умолчанию, принимается тип Variant. Тип данных значения, возвращаемого функцией, устанавливается предложением As тип в правой части заголовка функции. Типы данных аналогичны тем, которые использовались для списка аргументов или в операторе Dim. Например, можно легко переписать процедуры MultiplyEm, приведенные в приме¬ рах гл. 6, в виде функций, определив типы данных в заголовках: I 1 Функция умножения двух величин. Function MultiplyEm(Valuel As Single, Value2 As Single) As Single MultiplyEm = Valuel * Value2 End Function ' Функция умножения списка величин. Function MultiplyEm2 (ParamArray Values ()) As Single Dim Product As Single Product = 1 For Each Value In Values Product = Product * Value Next Value MultiplyEm2 = Product End Function 123
Visual Basic for Applications в примерах Вызов функции Вызов функции отличается от вызова процедуры, так как она вызывается как часть формулы, а не как отдельный оператор. Поскольку функция может выступать частью формулы, список ее аргументов нужно заключить в круглые скобки, чтобы связать аргументы с функцией. Без круглых скобок отличить формулу от функции будет крайне сложно или невозможно. Если не учитывать круглые скобки, то вызывают функцию тем же путем, что и процедуру: методы использования аргументов, передача величин адресом или значе¬ нием, использование значений по умолчанию для отсутствующих аргументов, приме¬ нение списка аргументов, — все то же самое. Например, для вызова предыдущей функции можно использовать процедуру: Sub TestProc() Dim Result As Single, ValA As Single, ValB As Single Dim ValC As Single, ValD As Single, ValE As Single ValA = 5 ValB = 7 ValC = 3 ValD = 12 ValE = 4 Debug.Print MultiplyEm(3, 5), MultiplyEm(ValA, ValB) Debug. Print Muitipl-yEm2 (ValA, ValB, ValC, ValD, ValE) Result = MultiplyEm(ValC, ValD) / 2 4- _ Mui tipi yEm2 (ValA, ValB, ValC) Debug.Print Result End Sub Первые две функции вызываются как аргументы метода Print объекта Debug. Каждый из этих двух вызовов вычисляет единичную величину и возвращает ее методу Print. Следующий оператор производит единичный вызов функции MultiplyEm2 также в качестве аргумента метода Print. Следующий — оператор присваивания - использует обе функции и возвращает значение переменной Result. Переменная Result печатается следующим оператором. Выполните эту процедуру на панели Immediate окна Debug, и вы получите результаты в виде трех строк, каждая из которых — продукт метода Print: Run (’’Test Proc") 15 5040 123 124
Создание пользовательских функций таблиц Функции Visual Basic можно использовать в таблицах —так же, как и в программах на Visual Basic. Однако, когда функции используются в таблицах, они должны возвра¬ щать только значение, т.е. они не могут выполнять директивы, как командные процедуры, ине должны модифицировать содержимое ячеек. Чтобы использовать пользовательскую функцию на Visual Basic в таблицах, запишите ее имя в формулу таблицы, как это делают для любой встроенной функции таблицы. Можно также выбрать функцию и список ее аргументов из Function Wizard в категории User Defined. Например, обе функции MultiplyEm, использованные в последнем примере, допустимы для применения в таблице. Если переключиться на чистую таблицу, записать в ячейку =MultiplyEm(5,7), а затем нажать Enter, то результат (35) появится в ячейке. На рис. 7.1 изображено несколько примеров использования обеих функций MultiplyEm. Обратите внимание на то, что функции работают со ссылками на ячейку так же хорошо, как и с литеральными значениями аргументов, но не работают со ссылкой на область. Ниже рассматривается как передать ссылки на область. □ FUNCEXMP.XLS ' . | a А в 1 С D 1 □ 2 Результат ‘Формула Данные 3 45!=MultiplyEm(5,9) 2 4 "1748!=MultiplyEm(E5,E6j 4 S 2520r=MultiplyEm2(3,4,5>,7) 23 6 ~69920!=MultiplyEm2(E3,E4,E5,E6,E7) 76 7 5 8 i j i Q □ Mil | ► | >|\ Modulel \ Sheet! / Sheet2 / Sheet3 / Shafl^L ■ I 3 Рис. 7.1. Применение пользовательских функций в таблицах Примечание: В каждой таблице, созданной в настоящей главе, колонка В содержит результат, а колонка С отображает функцию или формулу, которая порождает этот результат. В другом примере рассматривается равенство, вычисляющее размер обязательного платежа по ссуде с начальным балансом presval, процентной ставкой rate за прег периодов. Это равенство даст те же результаты, что и функция таблицы Pmt(), с тем отличием, что она возвращает положительное значение платежа и предполагает, что ссуда будет полностью выплачена за прег периодов: LPmt = presval * rate прег 1 1 4- rate 125
Visual Basic for Applications в примерах Встроенная функция Pmt() получает процентную ставку за период и количесл периодов, хотя для большинства ссуд чаще используются такие показатели, как годов; процентная ставка и количество лет, при условии, что платеж производится ежемесячн Следующая функция использует эту формулу, годовую процентную ставку (в ви; десятичной дроби) и количество лет, а возвращает сумму месячного платежа: ' Расчет платежа по ссуде на основании годовой процентной ставки, е количества лет и размера ссуды t у Function LPmt(presval As Currency, theRate As Single, _ theYears As Integer) As Currency Dim nper As Integer, rate As Single nper = theYears * 12 'Преобразование лет в месяцы rate = theRate / 12 'Преобразование годовой ставки в месячную LPmt = presval * rate / (1 — (1 / (1 + rate)) 74 nper) End Function В первую очередь нужно определить переменные nper и rate, так как в Excel имею? функции с этими именами и необходимо объявить переопределение имен этих фу incut Если вы не объявите переменные, Visual Basic попробует вставить эти функцк запутается и вернет сообщение об ошибке. После объявления переменных функш. преобразует годовую процентную ставку в месячную, а затем вычислит сумму платеж: Если записать эту функцию в модуль, а потом переключиться в таблицу и откры: Function Wizard, то новая функция появится в категории User Defined (см. рис. 7.2). I: рис. 7.3 показано применение этой функции для трех различных процентных ставок. Function Wizard- Step 1 of 2 Choose a function and press Next to fill in its arguments. Function £ategory: lUser Defined Al! Financial Date I Time Math I Trig Statistical Lookup & Reference Database Text Logical Information LPmt(pre«val, theRate, theYears J No help available. | Ijelp j I Cancel 11 < Hack £ Next > j Finish | Рис. 7.2. Пользовательская функция LPmtO отображается в окне Function Wizard вместе со своими аргументами 126
Глава 7. Создание и применение функций FUNCEXMP.XLS А В С 9 10 Платеж Формула 11 $1,097.01 =LPmt(130000,6%, 15) 12 $1,161.80 I =LPmt(130000,7%, 15) 13 $1,248.56 i =LPmi(1Зббб'б,8%,15) 14 i 15 1 . . | ► | НК Modutei~\ Sheet! / Sheet2 / Shce'lg » 4 ДД Рис. 7.3. Применение пользовательской функции LPmt() с целью вычисления суммы платежа по ссуде для трех различных процентных ставок Примечание: Когда вы записываете в формулу функции таблицы 6%, Excel заменяет эту величину десятичной дробью 0.06. Использование функции таблиц в процедуре Можно использовать не только функции Visual Basic в таблице, но и функции таблиц в процедуре. Все функции таблицы в процедуре Excel доступны и подключены к объекту Application. Используйте Object Browser для выбора библиотеки Excel и объекта Application. Это позволит не только отобразить полный список всех специаль¬ ных функций и вычислений, но и применить в Visual Basic каждую перечисленную функцию и вычисление. Однако будьте осторожны: некоторые функции таблиц имеют те же имена, что и функции Visual Basic, но используют различные аргументы или возвращают различные результаты. Например, функция Visual Basic zLogO возвра¬ щает натуральный логарифм (основание е = 2.71827...), а функция Excel Log О — десятичный логарифм (основание 10). Для доступа к функциям таблиц Excel запишите имя функции после наименования объекта Application, отделив его точкой. Если вы не указываете имя объекта Applica¬ tion, то получаете функцию Visual Basic. Например, чтобы использовать функцию Excel LogO в формуле, нужно записать примерно такой оператор: theLog = Application.Log (некоторая_величина) Этот оператор вычисляет десятичный логарифм некоторая—величина и присваи¬ вает его значение переменной theLog. Кроме обеспечения доступности множества функций Excel в программах на Visual Basic полезной является возможность создания функций-”наклеек”, модифицирующих функции Excel для использования в таблице. Функция-’наклейка” — это функция, которая перегруппировывает или модифицирует аргументы функции, чтобы упростить ее применение. 127
Visual Basic for Applications в примерах Все аннуитетные функции Excel получают процентную ставку за период ■ количество периодов, хотя большинство аннуитетов используют месячный плата годовую процентную ставку и количество лет выплаты. Следующий пример содерж очень удобный набор функций-'наклеек", которые преобразовывают четыре аннуи тетных функции Excel для получения годовых величин. Убедитесь в том, нт использование несколько измененного имени для функции не мешает встроенны функциям: ' Функция-наклейка для расчета процентной ставки. Function theRate (nyrs As Integer, pint As Currency, _ pv As Currency) As Single Dim nper As Integer, fv As Currency, guess As Single fv = 0 'Предположим, что ссуда выплачена. guess = 0.1 'Допустимая погрешность. nper = nyrs * 12 'Преобразование лет в месяцы ' Расчет месячной процентной ставки и преобразование ее 'в годовую процентную ставку. theRate = 12 * Application.rate(nper, -pmt, pv, fv, 0, guess) End Function ' Функция-наклейка для расчета суммы платежа. f Function thePmt(theRate As Single, nyrs As Integer, _ pv As Currency) As Currency Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. nper = nyrs * 12 'Преобразование лет в месяцы. rate = theRate / 12 'преобразование годовой ставки в месячную. 'Расчет платежа и преобразование его в положительное число. thePmt = -Application.pmt(rate, nper, pv, fv) End Function ' Функция-наклейка для расчета размера ссуды. Function thePV(theRate As Single, nyrs As Integer, _ thePmt As Currency) As Currency Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. 128
Глава 7. Создание и применение функций nper = nyrs * 12 'Преобразование лет в месяцы. rate = theRate / 12 'Преобразование годовой ставки в месячную. 'Расчет размера ссуды. thePV = Application.pv(rate, nper, -thePmt, fv) End Function ' Функция-наклейка для расчета срока выплаты. Function theNyrs(theRate As Single, thePmt As Currency, _ pv As Currency) As Integer Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. nper = nyrs * 12 'Преобразование лет в месяцы. rate = theRate / 12 'Преобразование годовой ставки в месячную. 'Расчет месяцев платежа и преобразование их в годы. theNyrs = Application.nper(rate, -thePmt, pv, fv) / 12 End Function В каждой функции предполагается, что окончательный размер заемного капитала -О, а результаты будут преобразованы в положительное число. На рис. 7.4 показано применение этих функций с некоторыми общими значениями. Q г ■ : ■ FUNCEXMP.XLS : . ai А В С DI 18 19 Значение Формула 20 7.4% =theRate(15,1200,130000) 21 $1,168 48 | i=thePmt(7%, 15,130000 22 201 =theNyrs(7%,1000,1 30000 23 $122,381.55 I ;=thePv(7%,15,1100 24 25 мГ« I Module! \ Sheetl/ Sheet2 / Sheet|*L □ Рис. 7.4. Использование аннуитетных функций-"наклеек” Передача массива пользовательской функции таблиц Хотя большинство функций имеют дело с единичными величинами, вам может потребоваться передать функции массив Excel или ссылку на область таблицы. Функции, использованные на рис. 7.1 могут получать в качестве аргументов только единичные величины, но не ссылки на область. Однако, как видно из рисунка, 5Вильям Дж. Орвис 129
Visual Basic for Applications в примерах процедура обрабатывает ссылки на область, содержащую единичную ячейку. Пр исходит это потому, что Excel перед передачей ссылок функции автоматичес заменяет их значениями. Ссылки на область большего размера требуют некотор дополнительной обработки для корректной передачи значений функций. Массив Excel заключается в фигурные скобки, и его колонки разделяю! запятыми, а строки — точками с запятой. Например, следующий массив являет двумерным массивом размера три на три: {1,2,3;4,5,6;7,8,9} = 1 23 4 5 6 7 8 9 Когда передается функции на Visual Basic массив Excel, он автоматичес- преобразовывается в массив Visual Basic. Однако, если передать ссылку на облас Excel, например С2:Е4, то она будет передана как объект Range. Если ваша функш рассчитывает получать в качестве аргумента массив, то нужно определить аргуме функции как массив Visual Basic. Вам же, скорее всего, потребуется передать функщ; таблицы ссылку на область. Чтобы передать функции на Visual Basic ссылку на область Excel, аргумс функции должен принадлежать к типу Variant. Значение, переданное функции, буд типа Variant и включит в себя объект Range. Если передать массив Excel функции аргументом типа Variant, то этот аргумент становится типа Variant и включает в се( массив. Обратите внимание на то, что в этом случае он отличается от маш переменных типа Variant. Если переданное значение —типа Variant, включающее себя массив, то можно осуществить доступ к этому массиву так же, как и к любо* другому (с помощью круглых скобок и индекса), ибо массивы Excel при передачек* в качестве аргумента точно переводятся в массивы Visual Basic. В случае, если переданное значение принадлежит к типу Variant и содержит обье» Range, можно выделить значение каждого элемента области, используя метод Cells свойство Value. Однако более простым способом доступа к этим величинам являет: применение цикла For Ea‘ch, который выполняется для каждого элемента массива аргумента. Если аргумент представляет собой область, то каждый элемент являет: объектом Range для единичной ячейки и автоматически преобразовывается в значеш ячейки. Второе преимущество применения этого цикла состоит в том, что если аргуMet является массивом, то цикл выполняется для каждого элемента массива и возврата: величину этого элемента. Таким образом, эта конструкция работает как с массива* Excel, так и с объектами Range. Например, следующая версия функции MultiplyEmO получает либо массив! Excel, либо ссылки на область и корректно перемножает значения: 130
Глава 7. Создание и применение функций ' Функция умножения массива величин. function Mui tipi уЕтЗ (theAr ray) As Single Dim Product As Single, Value Product = 1 For Each Value In theArray Product = Product * Value Next Value MultiplyEm3 = Product End Function На рис. 7.5 показаны результаты использования этой функции с различными аргументами. FUNCEXMP.XLS А s 1 с L.D iE Г 26 27 Результат формула Данные 28 45:-MuitipiyEm3((5,9) 2 23 699201=MultiplyEm3({2,4,23,76,5}) 4 30 69920i=MultiplyEm3({E28:E32)) 23 31 76 32 5 33 км ► | ►||\ Moduiel \ Sheetl / Sheet2 / Sheel3 / Sheet4 /| + Рис. 7.5. Использование функции MultiplyEm4 для передачи массивов Excel и ссылок на область Эта функция не может обрабатывать составные ссылки на области, такие, как В2:СЗ, D2:E3. Для обработки такой ссылки необходимо прибегнуть к методу, который применялся в гл. 6 для передачи множественных аргументов процедуре: используйте ключевое слово Param Array и преобразуйте аргумент типа Variant в массив. Чтобы выделить эти значения, потребуются два цикла For Each: первый —для выделения элемента из массива переменных типа Variant, а второй —для выделения элемента из массива или для выделения объекта, содержащегося в каждой переменной типа Variant. Так, следующая версия функции Multiply Em () получает в качестве аргумента любое количество массивов или ссылок на области и корректно перемножает все элементы: 131
Visual Basic for Applications в примерах ' Функция умножения массива, элементы которого — тоже массивы. Function MultiplyEm4 (ParamArray theArrayO) As Single Dim Product As Single, Part, Value Product = 1 For Each Part In theArray For Each Value In Part Product = Product * Value Next Value Next Part MultiplyEm4 = Product End Function В заголовке функции аргумент объявлен как массив параметров, состоящий ю переменных типа Variant. Оператор Dim объявляет локальные переменные, затек переменной Product присваивается значение 1. Первый цикл For Each выделяет элементе массива theArray, которые сами являются массивами или объектами Range, и записывай их в переменную Part. Второй цикл For Each выделяет из Part каждый из элементов являющихся отдельными величинами, и перемножает их. Два оператора Next завершай два цикла For Each, и в конечном итоге возвращается значение Product. На рис. 7.6 показан результат применения этой функции при нескольких различ ных аргументах. Рис. 7.6. Применение функции MultiplyEm4 для передачи множественных массивов Excel и ссылок на области FUNCEXMP.XLS А В | С D E 36 I 37 Результат формула Данные, 38 69920kMultiplyEm4(E38:E39,E40.E42) 2 39 69920!=Mu!tiplyEm4(E38,E39,E40/E41,E42) 4 40 69920 i=MultiplyEm4({2,4,23,76,5)) 23 41 #V ALUE! | =Mult i p ly E m4(2,4,23,76,5) 76 42 5 43 ЕЛЕ Modulel Sheet2 / Sheet3 / Sheet4 /■♦ Обратите внимание на то, что в последнем приведенном на рис. 7.6 примере возвращено ошибочное значение. Этот пример передает функции простой список чисел. Когда функция получает список, первый цикл For Each выделяет из него элемент, а второй цикл For Each пытается затем выделить элемент из простой величины, что приводит к ошибке. 132
Глава 7. Создание и применение функций Чтобы создать функцию, обрабатывающую единичные значения, списки значений, области ячеек, списки областей ячеек и массивы Excel, необходимо проверять значения, передаваемые функции, — с тем, чтобы определить, какого типа эти значения, а затем выбрать соответствующий метод для их выделения. Чтобы опреде¬ лить, какого типа переданное значение, используйте функцию TypeNameO, которая возвращает строку, содержащую название типа переменной, используемой в качестве аргумента. Если переменная — массив, то функция возвратит наименование типа, завершаемое парой пустых круглых скобок. Так, если аргумент — массив переменных типа Single, функция TypeNameO возвратит SingleO. После определения типа переменной используйте блочный оператор If для выбора корректного метода вычисления результата. Этот оператор подробно описан в гл. 9. Блочный оператор If проверяет, равно ли значение True. Если да, то блок кода между операторами If и Else выполняется. Если это значение равняется False, то выполняется блок кода между операторами Else и End If. Следующая функция получает список или массив любого типа и вычисляет результат. ' Функция умножения списка величин. Function Mui tipi yEm5 (Par amAr ray theArray() ) As Single Dim Product As Single, Part, Value Product = 1 For Each Part In theArray If TypeName (Part) = ’’Range” Or _ Right(TypeName(Part) , 2) = ’’ () ” Then For Each Value In Part Product = Product * Value Next Value Else ’ Product = Product * Part End If Next Part MultiplyEm5 = Product End Function Эта функция почти идентична функции MultiplyEm4(), за исключением того, что добавлен блочный оператор If. Оператор If использует функцию TypeNameO для проверки "объект Range или массив". Он проверяет непосредственно объект Range, но поскольку нет возможности использовать прямую проверку для массива, то выделяются два правых крайних символа и проверяется, являются ли они парой круглых скобок. Если да, то можно сделать вывод, что эти символы являются частью наименования типа массива. 133
Visual Basic for Applications в примерах ! i После того как блочный оператор If определит область или массив, для выделен частей области или массива выполняется блок кода, содержащий второй оператор ft Each. Если же If распознает область или массив, то делается вывод, что передо единичная величина. Тогда для умножения этого значения на Product используй оператор, располагающийся между операторами Else и End If. На рис 7.7 показа пример применения этой функции с различными аргументами. ; 49 50 51 52 53 54 55 56 Результат 6992CH=MultiplyEm5(E51 :E52,E53:E55) 69920i=MultiplyEm5(E51,E52,E53,E54,E55) 69920t=MultiplyEm5({2,4,23,76,5}) 69920bMultiplyEm5(2,4,23,76,5) ^Формула I fr I И|\ Modulel \SheeU/~Sheet2 / Sheet3 / Sheet4 /Д Е Данные А В Рис. 7.7. Функции MultiplyEniS передаются множественные массивы Excel, ссылки на области и единичные значения. Передача массива из функции таблице Обратная задача состоит в передаче массива Visual Basic назад таблице. Чтоб: осуществить это, функция должна принадлежать к типу Variant и массив должен храните как тип Variant. Чтобы использовать эту функцию, включите ее в область ячеек таблиц Приме чание: Для включения функции, возвращающей массив, в область ячеек рабочей таблиц выберите область в таблице, запишите функцию в первую ячейку и нажмк клавишу Enter, удерживая при этом нажатыми клавиши Ctrl-Shift. (или нажми: кнопку управления в строке формулы). Следующая функция возвращает названия всех месяцев года в виде 12-элементног горизонтального массива: ' Функция, возвращающая месяцы года. Function theMonths() theMonths = Array("Янв.” , "Фев.”, "Март", "Аир.”, "Май", "Июнь”, "Июль”, "Авг.", "Сен.", "Окт.”, "Ноя.”, "Дек.”) End Function 134
Глава 7. Создание и применение функций На рис. 7.8 показаны результаты применения функции к таблице различными способами. Во всех трех случаях вы выбираете 12 ячеек, записываете над ними формулу и нажимаете клавишу Enter, удерживая при этом нажатыми клавиши Ctrl Shift. ~| Elie Edit View insert Format Tools Qata window Help Гр1^1в1 lalaly] №1 №1 Ml №1*1 hop* IE IW1 lAiialCy ~|Г*1 |i0~|ft] | В | Z | Ц | |K|g|3B|S| |S|%| , |*ЛИ8| [ЁЩ] №] Г^]*] ЙЙ III I А В с I I » Е F I 6 н I 1 J К L М 1 I 1 2 {=theMonths( {-TRANSPOSE(theMonthsO)} 3 Янв. Янв. 4 Янв. Фев. Янв._ Янв. Март Апр. — 7 Янв. Май 8 Янв. Июнь 9 Янв. Июль 10 Янв. Авг. 11 Янв. Сен. 12 Янв. Окт. ! ] | | i 13 Янв. Ноя. I 1 tzzz 14 Янв. ДеГ"” i 15 1 16 {=theMonthsQ} ”i ! 17 Янв. ' Фев. 1Март Апр. Май Июнь Июль Авг. Сен. Окт. ! Ноя. Дек. 1R. г j —I I ! 14 Sheet3 / SheeU Moddel X Sh^H Ready /Ш. 7.8. Применение функции theMonthsO для передачи массива Visual Basic таблице Пример, расположенный в верхней левой части таблицы, записывает горизонталь¬ ный массив названий месяцев в вертикальный массив ячеек. В результате только первый элемент массива будет записан в каждую ячейку. Пример, расположенный в правой верхней части таблицы, использует табличную функцию TPANSPOSE0 для транспонирования горизонтального массива в вертикальный, чтобы он был корректно записан в ячейки. Пример, расположенный внизу, записывает этот массив в горизон¬ тальный массив ячеек, и поэтому транспонировать его не нужно. Использование функций в библиотеках DLL Использование динамически подключаемых библиотек (DLL) — краеугольный камень операционной системы Windows. Динамически подключаемая библиотека — 135
Visual Basic for Applications в примерах это библиотека специального типа, хранящаяся в файлах с расширением ".DLL"i присоединяемая к исполнительной программе в процессе ее выполнения, а не компи¬ ляции. Программы, которым нужны библиотечные функции, загружают их в памяп только тогда, когда они им нужны. Основным доводом в пользу использования DL1 является разделение кода библиотеки между многими приложениями. В этом случае потребуется только одна копия библиотеки на диске, что значительно уменьшает объеу используемой дисковой памяти. Кроме того, DLL существенно упрощает сопровожде ние программы: сопровождать нужно будет только одну копию библиотеки. Без DLL или подобных возможностей разделения кода каждая программа должна была 6к хранить в своем исполнительном файле используемые ею функции, и каждый раз изменив функцию, вы должны будете перекомпилировать каждое приложение, ис пользующее эту функцию. Почти все функции, которые вы хотите использовать, доступны в Visual Basic илв Excel, и, таким образом, использование DLL может не понадобиться. Более вероятно, что вы обратитесь к DLL для вызова специальной функции или процедуры, созданной с помощью полностью транслируемого языка, такого как С или FORTRAN. Такая возможность существует, так как большинство современных компиляторов позволяю! создавать DLL. Вам также может потребоваться вызвать системную функцию, которая недоступна Visual Basic, — например функцию, которая обращается к буферу клавиатуры или напрямую связывается с драйвером устройства. Предупреждение: Будьте осторожны, используя внешние библиотеки, так как некорректное обраще¬ ние к ним может привести к аварии системы. Прежде чем экспериментировать с DLL, убедитесь, что сохранили все свои программы. Наибольшая сложность в использовании динамически подключаемых библиотек состоит в получении информации об объявлениях. Если вы хотите осуществить доступ к системным файлам Windows, вам понадобится копия Windows Software Development Kit (SDK), где содержатся описания библиотечных функций и объявления функций. Чтобы использовать функцию или процедуру, находящуюся в DLL, нужно просто поместить оператор объявления этой функции в начало модуля. Оператор объявления содержит имя функции, имя библиотеки и типы аргументов. Он сообщает Visual Basic, где искать эту функцию и какие типы аргументов функция предполагает использовать. После объявления функцию можно использовать в таблице или любой программе Visual Basic. Далее приведены примеры объявления функции или процедуры: I Declare Sub MessageBeep Lib "User" (ByVai N As Integer) Declare Function GetCurrentTime Lib "User" () As Long Declare Sub GetKeyboardState Lib "User" (lpKeyState As Any) 136
Глава 7. Создание и применение функций В первом примере объявлена процедура MessageBeepO, находящаяся в библио¬ течном файле USER.DLL. Эта процедура использует единичный целый аргумент, который передается функции значением. Второй оператор объявляет функцию GetCurrentTimeO, также расположенную в USER.DLL. Эта функция не имеет аргументов и возвращает целое число типа Long. Последний оператор объявляет процедуру с именем GetKeyboardState из файла USER.DLL. Эта процедура исполь¬ зует один аргумент, который может быть любого типа. Примечания: 1. Файл USER.DLL является частью пакета Windows 3.1 и располагается в вашей директории WINDOWS/SYSTEM. 2. Тип данных Any — специальный тип, обычно используемый только для внешних процедур. Он отменяет контроль типов, и, таким образом, можно использовать переменные различных типов для создания типа, необходимого внешней функции или процедуре. Тем не менее будьте внимательны и убедитесь, что передаете процедуре корректные значения. После объявления библиотечной функции или процедуры их можно использовать подобно любым другим функциям или процедурам. Например, функция Get- CurrentTimeO возвращает количество миллисекунд, прошедших с момента запуска Windows. Вы могли бы использовать эту функцию в программе для регистрации времени, затраченного на выполнение программ в Windows. Чтобы вызвать эту функцию и отобразить результирующее значение на панели Immediate окна Debug, создайте процедуру, подобную следующей:. Declare Function GetCurrentTime Lib "USER" () As Long Sub TimeUsed() Debug. Print GetCurrentTime () End Sub Выполнив эту процедуру, вы получите следующий результат: Run("TimeUsed") 6211633 Этот результат показывает, что Windows отработала на протяжении 6211 с, или около 104 мин. Подобно любой другой функции GetCurrentTimeO может быть использована в формуле таблицы, как показано на рис.7.9. В этой формуле функция вызвана дважды: первый раз, чтобы определить количество миллисекунд, прошедших с момента запуска Windows, второй —как часть формулы, вычисляющей количество минут, прошедших с момента старта Windows. 137
Visual Basic for Applications в примерах Рис. 7.9. Применение функции GetCurrentTimeO из USER.DLL для получения количества миллисекунд, прошедших с момента запуска Windows Заключение В настоящей главе вы ознакомились с функциями — последним из четырех типов процедур, применяемых в Visual Basic. Функция —это процедура специального типа, возвращающая значение в имени функции. В Visual Basic можно создавать свои собственные функции для использования в своих программах на Visual Basic или создавать функции таблиц, которые можно использовать в таблицах. Кроме того, программа на Visual Basic может использовать все функции, доступные Excel. В случае необходимости вы можете, осуществлять доступ к большому количеству функций, расположенных в различных динамически подключаемых библиотеках, и использо вать их в своих программах. Данная глава завершает часть III этой книги. В первых трех частях были рассмотрены основная структура программ на Visual Basic и связь между Visual Basic и таблицей. Теперь, когда вы ознакомились с процедурами операторами и переменными, перейдем к управляющим структурам, которые позво¬ ляют принимать решения и управлять ходом выполнения процедуры. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1 Что такое функция и чем она отличается от других процедур? 2. Как задать тип данных функции? 3. Какой специальный оператор должен быть помещен перед концом функции? 4. Как выполнить процедуру в окне Debug? 5 Каковы ограничения, налагаемые на функции при использовании в таблице? 138
Глава 7. Создание и применение функций Упражнения для закрепления материала 1. Создайте пользовательскую функцию таблицы, имитирующую функцию Sum(). Сравните вашу функцию с функцией Sum(), чтобы убедиться, что они дают одни и те же результаты для различных типов аргументов. 2. Запишите процедуру, которая объявляет процедуру MessageBeep из библиотеки USER.DLL и выполняет ее и в таблице, и в процедуре. Используйте 0 в качестве аргумента типа Integer. 3. Напишите функцию-'наклейку”, вычисляющую будущую величину инвестиции, заданную настоящим значением, годовой процентной ставкой и количеством лет. Предположите, что процентная ставка вычисляется и кредитуется ежеквартально. Используйте встроенную функцию таблицы в качестве основы для вашей проце¬ дуры. 4. Напишите функцию, возвращающую в таблицу дни недели в виде массива. 5 Напишите функцию, обращающую матрицу два на два. Эта процедура должна получить матрицу два на два из таблицы, обратить ее и возвратить обратную матрицу в таблицу. Обратная матрица (А’1) — это матрица, которая, будучи умноженной на исходную (А), дает единичную матрицу (I). Сравните полученные результаты с результатами работы функции таблицы MINVERSEO.* а b , А-1 = d -b , A-1 A = I = 1 0 da - be da - be с d —c a 0 1 da - be da - be 6. Напишите процедуру обращения матрицы три на три. Используйте в качестве базовой функцию таблицы MINVERSEO. 139
Часть IV Управление выполнением программы
Глава 8 Принятие решений Принятие решений в компьютерных программах — это та характеристика, которая отличает компьютер от простого калькулятора. В то время как калькулятор выполняет только вычисления, заданные вами, компьютер может сравнить две величины и изменить последовательность выполнения программы в зависимости от результатов этого сравнения. Из данной главы вы узнаете: > Как компьютер принимает решение ► Как принимать решение в Visual Basic ► Как изменить последовательность выполнения программы на основании принятого решения > Как создавать логические выражения для принятия решений Что такое принятие решений Принятие решений в компьютерах основано на сравнении двух величин и измене¬ нии последовательности выполнения программы в соответствии с результатом этого сравнения. Результатом сравнения двух величин может быть: больше, равно или меньше. Комбинируя множественные сравнения, можно создавать сложные логичес¬ кие выражения. Затем можно использовать такие.выражения для проверки многоком¬ понентных данных, принять решение и на основании этого решения изменить после¬ довательность выполнения программы. Если в программе не используется принятие решений, выполнение начинается с первого оператора, затем последовательно выполняются следующие за ним и так до последнего оператора программы. Если программа использует принятие решений, вычисления осуществляются не последовательно: некоторые операторы пропускаются, а другие выполняются несколько раз. Когда принимается решение, установить точное время вычислений не просто, так как это определяется значениями исходных данных в ходе выполнения программы. Благодаря процессу принятия решений программа больше не является подобной мощному калькулятору, а становится машиной, наде¬ ленной сложной логикой. Примечание: Последовательность выполнения программы — это последовательность шагов, выполняемых программой от начала до конца. Последовательность выполнения можно сравнить с рисованием линии от оператора к оператору на распечатке программы для определения, какой оператор после какого выполняется. 143
Visual Basic for Applications в примерах Применение оператора If Простейшим оператором принятия решения является оператор If. Этот операт^ имеет следующий синтаксис: If выражение Then оператор где выражение — логическое выражение, обычно сравнение двух величин, ил формула с логическим результатом (True или False), или величина, принимают^ нулевое (False) или отличное от нуля (True) значение; оператор — любой операто] Visual Basic или составной оператор. Примечание: Составные операторы формируются путем размещения в одной строке несколько операторов, разделенных двоеточием. Например, следующие операторы aVal =25 MyName = ’’Вася" могут быть объединены в одной строке в виде составного оператора aVal=25 : МуЫате=’’Вася". Использования составных операторов следует по возможности избегать, так о они ухудшают понятность и читабельность программ. Оператор If проверяет значение выражение. Если это значение равно True, выполняется оператор; если же оно равно False, оператор не выполняется, а выпол¬ няется следующий оператор программы. Когда встречается оператор If, в последова¬ тельности выполнения п5роисходит небольшое отклонение, в связи с выполнением или невыполнением подключенного оператора. Заметьте, что этот оператор может быть именем процедуры, содержащей другой блок программы, который выполнится до того, как управление будет передано оператору, следующему за If. Оператор If широко применяется для проверки диапазона, в особенности когда диапазон определяет верхнюю или нижнюю границу. Например, если вы хотите убедиться, что значение индекса меньше заданного максимального значения, можно проверить его оператором If, и если индекс превосходит это значение, то приравнять его максимальному значению, например: If Index > MaxVal Then Index = MaxVal 144
Глава 8. Принятие решений В этом примере в логическом выражении производится сравнение величин Index и MaxVal. Если Index больше (>) чем MaxVal, логическое значение будет True и выполнится оператор Index = MaxVal. Если Index меньше или равен (<=) MaxVal, то остаток этого оператора пропускается. Логические величины Логические величины — это переменные типа Logical либо выражения, результа¬ том которых являются значения True или False. Большинство выражений, использую¬ щих логические величины, рассматривают значение 0 как False, а любое отличное от О —как True. Простейшими логическими величинами являются две предопределенные логи¬ ческие константы True и False. Наиболее общие логические выражения — это простые сравнения, использующие операторы логического сравнения, перечисленные в табл. 8.1. Другие виды логических выражений рассмотрены ниже в разд. ’’Логичес¬ кие выражения’’. Таблица 8.1. Операции логического сравнения в Visual Basic Оператор Описание = Равно о Не равно < Меньше > Больше <= Меньше или равно >= Больше или равно Is Идентично (только для объектов) Like Позволяет применять подстановочные символы Когда первые шесть операторов (=, <>, <, >, <=, >=) применяются к цифровым данным, они возвращают значение True, если условие выполняется, или False —если оно не выполняется. Тем не менее будьте внимательны, сравнивая две переменные различных числовых типов. Гораздо надежнее преобразовать одну из переменных в тип другой. Вообще преобразовывать следует в более точный тип; кроме того, учтите, что в случае с типом Currency числа при сравнении усекаются до четырех разрядов после точки. Чтобы быть уверенным в правильности сравнения двух переменных, преобразуйте их в один тип, используя функции преобразования типов, приведенные в табл. 5.6. 145
Visual Basic for Applications в примерах I Если любое из сравниваемых значений равно Null, результат сравнения равен Null Значение Null указывает на то,что переменная не имеет никакого значения. Сравнение строк —процесс менее однозначный. Применяются два метода сравне¬ ния строк —двоичный и символьный. По умолчанию выполняется двоичное сравнение кодов символов ANSI (см. приложение Б), где ”6” —больше, чем "а" и "А” —меньше чем, "а". Если разместить оператор ’’Option Compare Text” в начале модуля, метод сравнения будет изменен на символьный. При символьном сравнении не делается различия для строчных и заглавных букв: "а” равняется ”А" и "б” равняется "Б”, но ”6” все равно больше, чем "а”. { Операция Like — специальная операция, которая позволяет при сравнении строк использовать подстановочные символы. Производимое таким образом сравнение во многом напоминает выбор файлов в DOS. В табл. 8.2 перечислены подстановочные символы и указано, чему они соответствуют. Таблица 8.2. Подстановочные символы, применяемые в операции Like Подстановочный символ Объект сравнения * Любое количество символов ? Любой символ # Любая цифра (0-9) [ список символов] Любой символ из списка [ I список символов] Любой символ не из списка Первые два подстановочных символа (* и ?) вам хорошо известны из DOS. В Visual Basic они выполняют те же действия, что и в DOS. Чтобы использовать' подстановочные символы внутри сравниваемой строки, в качестве ее части, необходи-' мо заключить их в квадратные скобки ([]). Этот универсальный метод имеет одно исключение —правую квадратную скобку (]), которая всегда рассматривается как1 ограничитель списка символов и не может использоваться в качестве одного из символов строки; поэтому ее используют только вне списка символов. Кроме операций сравнения существует специальная функция сравнения строк. Функция StrCompO получает три аргумента —две строки и цифровой код. Синтаксис функции StrCompO имеет вид: Result = StrComp(строка!, строка2, код_сравнения) 146
Глава 8. Принятие решений Если строка1 > строка2, то функция возвратит +1; если строка1 = строкаЗ, то функция возвратит 0; если строка! < строка2, то функция возвратит -1. Цифровой код кодсравнения определяет двоичное (0) или текстовое (1) сравнение. Примечание: Когда сравниваются строки и числа, Visual Basic пытается преобразовать строку в число, чтобы затем произвести сравнение двух чисел. Однако слишком полагать ся на Visual Basic не следует, ибо из результирующего кода не очевидно, как осуществлялось сравнение. Чтобы корректно сравнить строку и число, преобра¬ зуйте одно значение в тип другого до сравнения, и будете точно знать, что с чем сравниваете. Приведем примеры логических величин, полученных в результате операций сравнения на панели Immediate окна Debug: 11. = 1, 1 > 2, 1 < 2, 2 <= 2 True False True True ? "Skye" < "Sierra", "Shane " > "B.J." False True ? "Вася" > "Вас" True ? "Вася" > "вася" False ? StrComp (’’Вася”, "вася", 1) 'Символьное сравнение О А = 25,7 В = 35,9 ? А >= В, А <= В, А = В, А < В, А > В False True False True False ? "Киев" Like "К*" True ? "Одесса” Like ”??e?* True ? "Киев” Like "К?[аеиоу]?", "Одесса" Like "Од[аеиоу]?*" True True ? "Киев" Like ”K?[!a]*", "Одесса" Like ”O????[!a]" True False ?"Mope[*]" Like "Mop?[[]??", ”Mope[*]" Like "?????[*]]" True True Для сравнения двух объектов используется операция Is, возвращающая True только в том случае, если обе сравниваемые переменные ссылаются на один и тот же 147
Visual Basic for Applications в примерах объект. Для проверки объектов различных типов используется функция TypeNameO с которой вы встречались в примере гл. 7. Для проверки различных типов величш применяется функция VarTypeO или логические проверки, описанные ниже. Полна описание функций TypeNameO и VarTypeO можно посмотреть в интерактивно; подсказке. Логические проверки Для проверки различных величин в Visual Basic имеются специальные функции Эти функции используются, чтобы проверить, является ли те или иные величина величинами нужного вам типа, перед тем как использовать их в вычислениях, а такж для проверки ошибочных значений или опущенных данных. Список функций логических проверок, доступных в Visual Basic, приведен в табл. 8.3 Таблица 8.3. Функции логических проверок Visual Basic Функция Is Array () IsDateO IsEmptyO IsErrorO IsMissingO IsNullO IsNumericO IsObjectO Описание Возвращает True, Возвращает True, Возвращает True, Возвращает True, Возвращает True, Возвращает True, Возвращает True, Возвращает True, если если если если если если если если аргумент аргумент аргумент аргумент — массив — дата — пустая строка — код ошибки аргумент опущен — Null аргумент аргумент аргумент — число — объект Если аргумент этих функций соответствует заданному типу, функции возвращают True; в противном случае они возвращают False. Исключение составляет функция IsMissingO, которая используется в процедуре с необязательными аргументами для проверки, переданы ли эти аргументы процедуре. Логические выражения Логические выражения —это простые расширения выражений сравнения, описан¬ ных выше. Чтобы создать логическое выражение, нужно объединить одну или более операций логического сравнения и знаки логических операций (булевых операций), приведенные в табл. 8.4. 148
Глава 8. Принятие решений Таблица 8.4. Булевы операции Visual Basic Функция Описание Not Инверсия или отрицание And Логическое "И" Or Логическое "Или" Xor Исключающее Или Imp Импликация Eqv Эквивалентность Булевы операции объединяют логические величины в соответствии с таблицами истинности. Таблица истинности содержит результаты различных логических выра- жеиий. Ниже приведена таблица истинности для всех булевых операций. Таблица 8.5. Таблица истинности для булевых операций (T=True, a F=False) А В Not А A And В А От В А Хот В A Imp В A Eqv В Т Т F т 1 Т F Т Т Т F F F т Т F F F Т Т F т Т Т F F F Т F F F Т Т Операции Not, And, Or, Xor и Eqv относительно просты и понятны. Операция Not обращает логическую величину; And возвращает True, если обе величины А и В равны True; Or возвращает True, если хотя бы одна из величин А и В равняется True; Xor возвращает True только в том случае, когда либо А, либо В равна True (но не обе); Eqv возвращает True, если А и В равны. Статистик мог бы объяснить, почему операция Imp называется "импликация", но вам достаточно понимать, что Imp используется для формирования множественных наборов операций, осуществляющих всевозможные комбинации из двух логических величин. Приведем логические выражения, вычис¬ ленные на панели Immediate: ? (1 = 1) And (2 > 1) True NumOpen = 2 Filename = "MYFILE.XLS" 149
Visual Basic for Applications в примерах ? (Filename = "MYFILE.XLS”) And (Numopen < 3) True numPeople - ” 127 ’’ ? 127 = numPeople 'Плохо, сравнение не очевидно True ? 127 = Vai(numPeople) 'Лучше делать так, сравнение очевидно True theLogicalValue = (Vai (numPeople) < 130) Or (Vai (numPeople > 100) ? theLogicalValue True При использовании булевых операций пользуйтесь для надежности круглыми' скобками, заключая в них обозначения простейших операций. В следующем примере' применение круглых скобок полностью изменило результат: 1 А = True В = False ? A Or В And В And А True \ ? (A Or В) And В And А False Заключение В этой главе вы познакомились с логическими величинами и формулами, а также с простейшими формами ветвления программ на Visual Basic, вы узнали как исполь¬ зовать операции сравнения и булевы операции для объединения этих сравнений в! множественные логические выражения. В последующих главах вы будете применять логические выражения для организации более сложного ветвления в программе. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Чем отличается простой калькулятор от компьютера? j 2. Что такое последовательность выполнения программы? : 3. Что собой представляет составной оператор Visual Basic? 4. Чем отличается двоичное сравнение от символьного? Какое из них применяется; по умолчанию? 5. Какие логические величины будут результатами следующих операций сравнения: 150
Глава 8. Принятие решений 5 >= 5 9 < 7 27.8 = 27.85 13.5 <> 11.3 "X" = ’’х' (двоичное сравнение) "Y” < "у" (символьное сравнение) > "а" (двоичное сравнение) "Pat" <= ’’Wilbur" (двоичное сравнение) Упражнения для закрепления материала 1. Напишите два оператора If, которые ограничивают величину диапазоном от 25 до 75. Если величина не попадает в диапазон, измените ее на величину ближайшего предела, т.е. если величина меньше 25, то на 25, а если больше 75 — то на 75 (значения между 25 и 75 не изменяются). 2. Напишите один оператор If, который ограничивает величину диапазоном от -50 до 50 и устанавливает в 0 все величины, не попадающие в диапазон. 3. Напишите оператор If, который проверяет значение строковой переменной FileNa- me и, если значение пустое, вызывает функцию GetFileNameO для получения имени файла и записи его в переменной FileName. 4 Напишите одну логическую формулу, использующую оператор Like для сопостав¬ ления имен Jenny, Michelle, Skye, Ashley и Kelly, но не Kristina, Crystal, Melissa, Elisabeth, Danielle или Casie. 5. Создайте таблицу истинности, показывающую, что следующие два логических выражения идентичны: Not (A And В) (Not A) Or (Not В) Сделайте то же для обратных выражений: Not (A Or В) (Not A) And (Not В) 151
Глава 9 Применение блочных структур If Блочные структуры If — наиболее эффективные из структур логического ветв.к ния. Применяя их, можно создавать на Visual Basic любые другие логически структуры, включая переходы и циклы. Из этой главы вы узнаете, как делать следующее: > Создавать одноблочные структуры > Создавать многоблочные структуры > Применять условие Else Выполнение одноблочных структур с оператором If-Then В предыдущей главе вы познакомились с оператором If-Then, позволяют выполнять единичный оператор при условии, что логическая величина имеет знамени True. Имеется возможность расширить этот единичный оператор до структура выполняющей блок кода при условии, что логическая величина имеет значение Тгш Блок кода или программный блок —это непрерывная группа операторов, спроектк рованная для совместного выполнения. Ниже приведена одноблочная структура If в общем виде: I f выражение Then блок операторов End If Первая строка блочной структуры If аналогична оператору lf-Then. Отличи} состоит в том, что после ключевого слова Then в ней ничего не записано. Когд| выражение имеет значение True, то выполняется блок операторов, заключенных межд;- операторами If и End If. Если выражение имеет значение False, то этот блок н* выполняется и управление переходит к оператору, следующему за оператором End 11} Например, в процедуре с необязательными аргументами необходимо проверятьэтй аргументы, чтобы узнать, переданы ли они процедуре. Если дополнительный аргумент не был передан процедуре, то взамен используется значение по умолчанию. Привс-1 денная ниже функция рассчитывает заработную плату служащего на основании! проработанного времени и тарифной ставки. Если значение тарифной ставки опущено то функция использует значение по умолчанию ($5.25 в час). Обратите внимание iq оператор Option Explicit в в.ерхней части текста функции. Этот оператор заставляв объявлять все переменные перед их использованием. ' 152
Глава. 9 Применение блочных структур If Option Explicit ' Функция расчета заработной платы. Function thePay(hours As Single, Optional PayRate) As Currency Const DefaultRate = 5.25 If IsMissing(PayRate) Then PayRate = DefaultRate End If thePay = hours * PayRate End Function Обратите внимание также на то, что в функции используется константа Default- Rate. Гораздо проще было бы вставить значение по умолчанию в оператор присваи¬ вания значения PayRate. Однако использование поименованной константы сделало код более читабельным и понятным. Другим преимуществом констант является использование одного значения во многих местах процедуры: потребуется всего одно изменение в операторе объявления константы, чтобы при необходимости изменить ее значение во всех местах процедуры сразу. Можно разместить все константы, определяющие тарифные ставки и премии, в верхней части модуля, использующего эти константы. Это позволит значительно облегчить их поиск и изменение. Создайте короткую процедуру, получающую неко¬ торые значения, а затем печатающую результаты на панели Immediate окна Debug: ' Процедура проверки функций. Sub TestProc () Dim theHours As Single, theRate As Currency Dim Payl As Currency, Pay2 As Currency theHours = 40 theRate = 8.75 Payl = thePay(theHours) 'Один аргумент. Pay2 = thePay(theHours, theRate) 'Два аргумента. Debug. Print Format(Payl, "$#,##0.00"), Format(Pay2, _ "$#,##0.00") End Sub Эта процедура вызывает функцию дважды: первый раз, не задав тарифной ставки, а второй — с тарифной ставкой. Функция FormatO используется здесь для форма¬ тирования строки аналогично тому, как вы форматировали числа в ячейках таблицы. 153
Visual Basic for Applications в примерах В этой процедуре при форматировании строки в начало числа помещается зн1 доллара, три десятичные цифры отделяются запятой и перед десятичной точи' обязательно указывается значащая цифра или 0. В каждой из двух позиций not десятичной точки также обязательно указывается значащая цифра или 0. Д получения дополнительной информации о кодах, применяемых для форматирован! строк, воспользуйтесь интерактивной подсказкой. Запустите программу на выполи ние, используя панель Immediate, и вы получите следующий результат: Run("TestProc") $210.00 $350.00 Совет: При использовании оператора Run для запуска новой процедуры с панели Inunedii окна Debug можно получить сообщение о том, что Visual Basic не может нак процедуру (обычно это происходит из-за наличия в процедурах ошибок). Чтй устранить это, переключитесь на модуль, содержащий процедуру, поместите к\р в любое место процедуры, которую хотите выполнить, а затем нажмите команду кнопку Run Macro на пиктографическом меню Visual Basic. Когда Visual Bai обнаружит ошибку, появится диалоговое окно с сообщением об ошибке. Нажж командную кнопку Go То, чтобы увидеть ошибочный оператор. Выявив и устраш ошибку в этом операторе, нажмите командную кнопку Run Macro, чтобы выполуА процедуру еще раз. Если процедура выполнится до конца без ошибок, то использовать оператор Run для запуска ее на выполнение с панели Immediate. Приведенная ниже процедура позволяет кроме заработной платы рассчитывать премию, если объем продаж передан и превышает заданное значение: f I I пункция расчета заработной платы и проверки премии. I i Function thePay2(hours As Single, Optional PayRate, _ ! Optional Sales) As Currency Dim Bonus As Currency Const BonusThreshold = 5000 Const DefaultRate = 5.25 Const BonusValue = 500 If IsMissing(PayRate) Then 'Если опущена, то значение по умолчанию. PayRate = DefaultRate End If Bonus = 0 If Not IsMissing(Sales) Then 154
Глава. 9 Применение блочных стриктур If If Sales > BonusThreshold Then 'Начисляется премия, если Bonus = BonusValue 'объем продаж > $5000. End If End If thePay2 = hours * PayRate + Bonus 'Сумма зарплаты. End Function Для расчета премии в эту функцию добавлены две вложенные блочные структуры If. Работают они следующим образом. Внутренняя структура If выполняется только тогда, когда аргумент внешней структуры принимает значение True. Предположим, что можно создать логическое выражение, которое объединяет оба логических сравне¬ ния, используемых во вложенных блочных структурах If, и вычислить премию при помощи одноблочной структуры If: If (Not IsMissing(Sales)) And (Sales > BonusThreshold) Then Bonus = BonusValue End If Однако, если значение Sales опущено, то в логическом выражении будет предпри¬ нята попытка сравнить отсутствующее значение с числом, что приведет к ошибочной ситуации. Использовав две вложенные блочные структуры If, вы всегда сможете обойти это сравнение, если значение Sales будет опущено. Создайте и выполните процедуру, которая проверит эту функцию на панели Immediate. Проще всего создать эту процедуру, скопировав предыдущую процедуру, азатем модифицировав ее следующим образом: ' Процедура проверки функций. Sub TestProc2 () Dim theHours As Single, theRate As Currency, theSales As Currency Dim Payl As Currency, Pay2 As Currency, Pay3 As Currency theHours = 40 theRate = 8.75 theSales = 8500 Payl = thePay2(theHours) Pay2 = thePay2(theHours, Pay3 = thePay2(theHours, Debug.Print Format(Payl, Format(Pay3, End Sub theRate) theRate, theSales) ”$#,##0.00”), Format(Pay2, ”$#,##0.00”) "$#,##0.00”), 155
Visual Basic for Applications в примерах Одноблочные структуры If удобно использовать для отладки. Например, чтоб^ определить блоки программы, работающие корректно, можно вставить в нее блокх кода для проверки или отображения различных величин. После окончания отлад^ программ эти отладочные блоки кода можно просто удалить из программы и.:| блокировать. Наиболее простым способом блокировки кода является помещение cj внутрь одноблочной структуры If, использующей в качестве логического аргумент некоторую глобальную переменную. Установка глобальной переменной в True разр шит выполнение отладочного кода, а установка ее в False блокирует код. ; Например, если при разработке функции thePay2() вы захотели выдать на панез Immediate сообщение о расчете премии, то для печати сообщения можно использова? одноблочную структуру If, а в качестве логического условия использовать констан: DoDebugging. После отладки процедуры можно просто изменить значение DoDebup ging на False, чтобы прекратить печать сообщения. Ниже приведены функцр thePay3() и процедура TestProc3, демонстрирующие эти возможности: Option Explicit Const DoDebugging = True 'Вкл./выкл. отладочные коды. г ' Процедура проверки функций. Sub TestProc3() Dim theHours As Single, theRate As Currency, theSales As Currency Dim Payl As Currency, Pay2 As Currency, Pay3 As Currency theHours =40 theRate = 8.75 theSales = 8500 Payl = thePay3(theHours) Pay2 = thePay3(theHours, theRate) Pay3 = thePay3(theHours, theRate, theSales) Debug.Print Format(Payl, ”$#,##0.00”), Format(Pay2, ”$#,##0.00”), Format(Pay3, "$#,##0.00”) End Sub r f Функция расчета заработной платы и проверки премии. г / Function thePay3(hours As Single, Optional PayRate, _ Optional Sales) As Currency Dim Bonus As Currency Const BonusThreshold = 5000 Const DefaultRate = 5.25 156
Глава. 9 Применение блочных структур If Const BonusValue =500 If IsMissing(PayRate) Then 'Если опущена, то значение по умолчанию. PayRate = DefaultRate End If Bonus = 0 If Not IsMissing(Sales) Then If Sales > BonusThreshold Then 'Начисляется премия, если Bonus = BonusValue ' 'объем продаж > $5000 If DoDebugging Then ' 'Это отладочный код Debug.Print "***Премия начислена** *" End If End If End If thePay3 = hours * PayRate + Bonus 'Сумма зарплаты. End Function Обратите внимание на то, что объявление константы DoDebugging помещено в верхнюю часть модуля, что делает константу доступной для всех процедур модуля. Результат выполнения этой процедуры на панели Immediate: Run("Tes иРгосЗ") ***Премия начислена*** $210.00 $350.00 $850.00 Заметьте, что хотя функция thePay3() вызывалась из процедуры трижды, премия рассчитывалась всего один раз — когда объем продаж был передан и его величина превысила $5,000.00. Попробуйте различные значения величины theSales в процедуре, чтобы проверить, как выполняется условие расчета премии. Выполнение многоблочных структур с опера тором If-Th en -Elself Одноблочные структуры If можно расширить до многоблочных, добавив условие Elself. Ниже приведен синтаксис многоблочной структуры If: If выражение 1 Then блок опера торов 1 Elself выражение2 Then блок опера торов 2 Elself выражение3 Then 157
Visual Basic for Applications в примерах блок операторов 3 (другие блоки) End If Когда встречается многоблочная структура If, Visual Basic определяет значев выражение!, и если оно равно True, то выполняется блок операторов 1. Если значев выражение2 — False, то Visual Basic пропускает весь блок операторов 1, определ значение выражение2, и если оно равно True, то выполняется блок операторов 2. Ed значение выражение2 — False, то Visual Basic пропускает весь блок операторов! определяет значение выражениеЗ. Так продолжается для всех условий Elself, вши до оператора End If. Другими словами, выполняется блок операторов, следующий условием, результат которого равен True. Все остальные блоки многоблочной струю ры игнорируются, а управление передается оператору, следующему за End If. Многоблочные структуры If очень удобно применять для анализа выполнеа одного из нескольких условий. Например, в функции thePay2() использовались,! вложенные структуры If, чтобы определить, была ли величина Sales передана фу1 ции, а затем для сравнения Sales с заданной величиной при расчете премии. 3 вложенные структуры можно переписать в виде многоблочной структуры If: ' Функция расчета заработной платы и проверки премии. Function thePay4(hours As Single, Optional PayRate, _ Optional Sales) As Currency Dim Bonus As Currency Const BonusThreshold = 5000 Const DefaultRate = 5.25 Const BonusValue = 500 If IsMissing(PayRate) Then 'Если опущена, то значение по умолчанию. PayRate = DefaultRate End If Bonus = 0 If IsMissing(Sales) Then 'Если объем продаж опущен, то ничего не делается. • Elself Sales > BonusThreshold Then 'Начисляется премия, если Bonus = BonusValue 'объем продаж > $5000. End If thePay4 = hours * PayRate + Bonus 'Сумма зарплаты. End Function 158
Глава. 9 Применение блочных структур If Первый оператор многоблочной структуры If проверяет, была ли передана вели¬ чина Sales. Если нет, то выполняется первый блок структуры. В данном случае этот блок состоит только из оператора комментария. Если величина Sales была передана, то логическое выражение в первом операторе структуры If принимает значение False и управление переходит к оператору Elself, где проверяется значение логического выражения данного оператора. В данном случае, если величина Sales больше заданного значения BonusThreshold, то выполнится блок кода, следующий за оператором Elself и служащему будет начислена премия. Результаты выполнения этой функции иден¬ тичны результатам выполнения функции thePay2(). Применение условия Else Существует еще одно условие, которое можно добавить в оператор If — условие Else. За условием Else следует последний блок логической структуры If, который выполняется, если не выполнился ни один из других блоков. Приведем полный синтаксис блочной структуры If, включающей в себя условие Else: If выражение 1 Then блок опера торов 1 Elself выражение 2 Then блок опера торов 2 Elself выражение3 Then блок опера торов 3 (другие блоки) Else блок операторов условия Else End If В данном случае в операторах If и Elself последовательно проверяются логические величины, и если ни одна из этих величин не примет значение True, то будет выполнен блок операторов условия Else. Условие Else применяется в тех случаях, когда необходимо, чтобы программа что-нибудь сделала, если не было выполнено ни одно из предыдущих условий, например, — для реакции на непредвиденную ситуацию, принятия решения по умолчанию, выдачи сообщения об ошибке и т.п. Можно использовать условие Else, чтобы написать функцию thePay5(), включив все операторы, относящиеся к расчету премии, в одну блочную структуру If: 159
Visual Basic for Applications в примерах ' Функция расчета заработной платы и проверки премии. Function thePay5(hours As Single, Optional PayRate, _ Optional Sales) As Currency Dim Bonus As Currency Const BonusThreshold = 5000 Const DefaultRate = 5.25 Const BonusValue = 500 If IsMissing(PayRate) Then 'Если опущена, то значение по умолчанию. PayRate = DefaultRate End If If IsMissing(Sales) Then Bonus = 0 'Объем продаж опущен, премии нет. Elself Sales > BonusThreshold Then 'Начисляется премия, если Bonus = BonusValue 'объем продаж > $5000. Else Bonus = 0 'Объем продаж меньше заданного, премии нет. End If thePay5 = hours * PayRate + Bonus 'Сумма зарплаты. End Function Обратите внимание на то, что все операторы, изменяющие величину Вот объединены в одну блочную структуру If. В предыдущих версиях функции theP предполагалось, что сотрудник не заслуживает премии —задавалась Bonus равной — а затем величина Bonus изменялась, если оказывалось, что сотрудник npcMi заслужил. Ситуация, при которой сотрудник не заслуживает премии, ранее проверялась. В функции thePay5() проверяются все ситуации и величина Вон устанавливается в соответствии с результатами этих проверок, что облегчает пониу ние программы. Функцию расчета заработной платы можно доработать, включив нее расчет оплаты сверхурочных. Эта доработка сведется к добавлению еще од» блочной структуры If, рассчитывающей заработную плату в зависимости от следуют, условий: 40 часов в неделю оплачиваются в обычном размере, сверхурочные интервале от 40 до 50 часов в неделю оплачиваются с коэффициентом 1.5, свыше часов — с коэффициентом 2: / ' Функция расчета заработной платы, сверхурочных и проверки премии. г Function thePay6(hours As Single, Optional PayRate, _ Optional Sales) As Currency 160
Глава. 9 Применение блочных структур If Dim Bonus As Currency, Wages As Currency Const BonusThreshold = 5000 Const DefaultRate =5.25 Const BonusValue =500 If IsMissing(PayRate) Then 'Если опущена, то значение по умолчанию. PayRate = DefaultRate End If If IsMissing(Sales) Then Bonus = 0 'Объем продаж опущен, премии нет. Elself Sales > BonusThreshold Then 'Начисляется премия, если Bonus = BonusValue 'объем продаж > $5000. Else Bonus = 0 'Объем продаж меньше заданного, премии нет. End If If hours <= 40 Then Wages = hours * PayRate Elself (hours > 40) And (hours <= 50) Then Wages = 40 * PayRate 4- (hours — 40) * 1.5 * PayRate Else Wages = 40 * PayRate 4- 10 * 1.5 * PayRate _ + (hours — 50) * 2 * PayRate End If thePay6 = Wages 4- Bonus 'Сумма зарплаты. End Function Примечание: Формулы, используемые в этом примере для расчета заработной платы, полностью соответствуют описанию. Например, формула расчета заработной платы для недельного рабочего времени из интервала 40-50 часов имеет следующий вид: Wages = 40 * PayRate 4- (hours - 4 0) * 1.5 * PayRate После несложных алгебраических преобразований эта формула примет следующий вид: Wages = (hours * 1.5 - 20) * PayRate Очевидно, что обе формулы дадут одинаковый результат, но вторая формула менее понятна. Если не требуется минимизировать время выполнения кода, то имеет смысл использовать первую формулу, которая вычисляется несколько дольше, но значительно проще для понимания. 6Вильям Дж. Орвис 161
Visual Basic for Applications в примерах Чтобы проверить расчет сверхурочных, воспользуйтесь предыдущей текстом процедурой — скопируйте и модифицируйте ее следующим образом: 1 ' Процедура проверки расчета сверхурочных. t Sub TestProc4() Dim theHoursl As Single, theHours2 As Single, theHours3 As Single Dim theRate As Currency Dim Payl As Currency, Pay2 As Currency, Pay3 As Currency theHoursl = 40 theHours2 = 45 theHours3 = 60 theRate = 8.75 Payl = thePay6(theHoursl, theRate) Pay2 = thePayG (th.eHours2, theRate) Pay3 = thePay6(theHours3, theRate) Debug.Print Format(Payl, "$#,##0.00”), Format(Pay2, ”$#,##0.00”), Format(Pay3, "$#,##0.00") End Sub Результаты выполнения этой процедуры будут1 иметь на панели Immediate такс* вид: Run("TestProc4") $350.00 $415.63 $656.25 Заключение В этой главе мы рассмотрели различные варианты блочных структур If, позволяе щие создавать практически все необходимые управляющие структуры. В следующих главах вы познакомитесь с другими управляющими структурам: упрощающими решение большинства стандартных ситуаций. Это касается цикло: описываемых в части 5. Хотя можно создавать циклы с помощью блочных структу If, гораздо проще это делать, применяя специальные циклические структуры. 162
Глава. 9 Применение блочных стриктур If Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Как проверяется передача функции или процедуре необязательного аргумента? 2. Почему использование в процедурах констант (вроде DefaultRate) более эффек¬ тивно, чем использование литеральных значений? 3. Сколько условий Elself можно использовать в блочной структуре If? 4. Сколько условий Else можно использовать в блочной структуре If? Упражнения для закрепления материала 1. Перепишите следующую вложенную блочную структуру If в одноблочную структуру If: If theValue = 7 Then. If yourValue > 5 Then 'Делайте что-нибудь интересное. End If End If 2. Создайте функцию thePay, в которой заданы два уровня премиальных: $500 за объем продаж от $5000 до $10000, и $1000 за объем продаж свыше $10000. 3. Создайте процедуру thePay, использующую значение по умолчанию, если величи¬ на Pay Rate равна 0 или опущена. 163
Глава 10 Применение структуры Select Case Структура Select Case —это особый вид управляющей структуры И. Струю Select Case применяется, когда одна величина участвует во всех логических cpai ниях и определяет, какой блок кода будет выполняться. Эту задачу можно реши помощью блочной структуры If, но значительно проще использовать Select Case, данной главы вы узнаете, как делать следующее: > Применять структуры Select Case > Задавать варианты > Применять условие Case Else Что такое варианты? Структура Select Case —это специальная управляющая структура, применяе в том случае, когда одна величина участвует во многих сравнениях для выбора од! из блоков кода. Наиболее часто Select Case используется в тех случаях, к( сравниваемая величина является целым числом —селектором или индексом. Значе селектора или индекса и выбирает тот блок кода, который будет выполняться. Структура Select Case имеет следующий синтаксис: Select Case величина Case сравнение 1 блок опера ,Г1оров 1 Case сравнение2 блок опера торов 2 другие варианты End Select В этом синтаксисе величина представляет собой переменную или вираже! сравниваемое с некоторым цифровым либо строковым значением, а вираже сравнение являются списком из одного или более значений, с которыми сравнив^ величина. При выполнении оператора Select Case вычисляется значение велич1 которое затем сравнивается со списком сравнение!. При совпадении выполняется 6 операторов /; при несовпадении управление переходит к следующему оператору ( и величина сравнивается со значением из списка сравнение 2. При совпаде выполняется блок операторов 2 и т.д. Если значение величина не совпало, 164
Глава 10. Применение структуры Select Case управление переходит к оператору, следующему за оператором End Select. Как и в случае блочного оператора If, выполняется только блок кода, следующий за тем оператором Case, со значением из которого сравнилось значение величина, независимо оттого, первый это оператор Case или последний. Формат значений сравнение очень гибкий, он объединяет множество различных возможностей. Если сравнение является списком значений, разделенных запятыми, то совпадение в операторе Case происходит в том случае, когда одно из значений списка совпадает со значением величина. Элемент списка может иметь вид величина1 То величина2. Эта форма означает, что значения расположены в интервале от величина! до величина2 включительно. Элемент также может иметь вид Is операция величинаЗ, где операция может быть одной из операций сравнения, приведенных в табл. 8.1, за исключением Is и Like. Например, следующие операторы Case допустимы: Case 25 Case 1, 3, 5, 7 Case "Марианна” Case 1/ 2, 7 To 9, 11 Case 3,. 5., Is >= ■- 7 Case Is < o, 1, 2, 3, Is > 10 Первый оператор Case выбирается только в том случае, если значение величина равно 25; второй —если 1, 3, 5 или 7; третий —если тексту ’’Марианна"; четвертый — если 1,2, 11 или любому числу от 7 до 9; пятый — если 3 либо 5, а также любому числу, большему или равному 7. Последний оператор Case выбирается, если значение величина меньше 0, равно 1, 2 или 3, в интервале от 5 до 7 или больше 10. Хотя оператор Select Case можно использовать для выбора значений любого типа или интервала, его в основном применяют для выбора из списка перечислимых значений. Список перечисленных значений —это список опций, в котором каждой опции соответствует целое число, идентифицирующее ее. Перечисленные значения можно отождествить с константами, а затем использовать их в вычислениях. Так, в гл. 7 вы создали функцию-"наклейку" для вычисления размера платежа по ссуде. Эта функция была разработана для ежемесячных платежей, но возможно также вычислить размеры платежей с периодичностью раз в два месяца, раз в квартал, раз в полугодие и за год. Чтобы сделать эту функцию более универсальной, добавьте список перечисленных значений, которые задают длительность периода, а затем используйте этот список в качестве аргумента функции. Используйте Select Case для расчетов платежей за выбранный период. 165
Visual Basic for Applications в примерах В первую очередь объявите перечисленные значения: Option Explicit Const yearly = О Const monthly = 1 Const bimonthly - 2 Const quarterly = 3 Const semiannually = 4 Затем создайте новую функцию (см. ниже). Обратите внимание на то, что я функция использует константы вместо величин, заданных в явном виде: t ' Функция — наклейка для расчета платежа. Function thePmt2(theRate As Single, nyrs As Integer, _ pv As Currency, PeriodType As Integer) As Currency ' PeriodType — это код продолжительности периода. ' PeriodType = 0 годовой платеж ' = 1 месячный платеж ' = 2 двухмесячный платеж ' = 3 квартальный платеж ' - 4 полугодовой платеж Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. Select Case PeriodType Case yearly nper = nyrs rate = theRate Case monthly nper = nyrs * 12 rate = theRate / 12 Case bimonthly nper = nyrs * 6 rate = theRate / 6 Case quarterly nper = nyrs * 4 rate = theRate / 4 Case semiannually nper = nyrs * 2 rate = theRate / 2 166
Глава 10. Применение структуры Select Case End Select 'Расчет платежа и преобразование его в положительное число. thePmt2 = -Application.pmt(rate, nper, pv, fv) . End Function Структура Select Case выбирает корректную форму вычислений для заданного периода и производит присвоение соответствующих значений аргументам. Теперь создайте процедуру, проверяющую работу новой функции: 'Процедура проверки функции thePmt2() Sub TestProc5 () Dim rate As Single, theYears As Integer, presval As Currency Dim Payl As Currency, Pay2 As Currency, Pay3 As Currency Dim Pay4 As Currency, Pay5 As Currency rate =0.07 '7% в год theYears = 15 'Ссуда на 15 лет presval = 130000 'Размер ссуды $130,000.00 Payl = thePmt2(rate, Pay2 = thePmt2(rate, РауЗ = thePmt2(rate, Pay4 = thePmt2(rate, Pay5 = thePmt2(rate, theYears, theYears, theYears, theYears, theYears, presval, presval, presval, presval, presval, yearly) monthly) bimonthly) quarterly) semiannually) Debug.Print "Годовой платеж " & Format(Payl, "$#,##0.00") Debug.Print "Месячный платеж = " & Format(Pay2, "$#,##0.00") Debug.Print "Двухмесячный платеж = " & Format(РауЗ, "$#,##0.00") Debug.Print "Квартальный платеж = " & Format(Pay4, "$#,##0.00") Debug.Print "Полугодовой платеж = " & Format(Pay5, "$#,##0.00") End Sub Эта процедура вычисляет платеж по той же самой ссуде, но для пяти различных периодов платежей. Запустив эту функцию в панели Immediate, вы получите следую¬ щие результаты: Run(”TestProc5 ") Годовой платеж = $14,273.30 Месячный платеж = $1,168.48 Двухмесячный платеж = $2,340.79 Квартальный платеж = $3,516.94 Полугодовой платеж = $7,068.27 167
Visual Basic for Applications в примерах Применение условия Case Else Так же, как и в блочной структуре If, можно использовать специальный операт Case, определяющий любые величины, которые не совпадают ни с одним из друг операторов Case. Для этого включите условие Case Else последним блоком пер оператором End Select. Все величины, не совпавшие ни с одним оператором Са совпадут с оператором Case Else. Например, функция thePmt2() отображает список перечисленных значений, которого может быть выбран период платежа. Однако, если будет задано значеш не вошедшее в список, нужно завершить функцию и возвратить код ошибки. Что! обнаружить ошибку, в функции используется оператор Case Else. Чтобы присвои возвращаемому значению код ошибки, применяется CVErrO. Функция thePmt3i являющаяся модифицированной функцией thePmt2(), использует предопределенн; константу для возврата кода ошибки Excel #VALUE!, что позволит использовг функцию thePmt3() как совместно с другими процедурами Visual Basic, так и качестве функции таблицы Excel. Эта модификация включает и другие изменен функции: теперь она возвращает значение типа Variant, что дает возможна возвращать как коды ошибок, так и числовые значения. Модифицированная функц имеет следующий вид: ' Функция — наклейка для расчета платежа. pv As Currency, ! PeriodType — PeriodType = код продолжительности периода, годовой платеж месячный платеж двухмесячный платеж квартальный платеж полугодовой платеж t г t t Function thePmt3(theRate As Single, nyrs As Integer, PeriodType As Integer) As Variant это 0 1 2 3 4 Любое другое значение приведет к ошибке. Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. Select Case PeriodType Case yearly nper = nyrs rate = theRate Case monthly nper = nyrs * 12 rate = theRate / 12 Case bimonthly 168
Глава 10. Применение структуры Select Case nper = nyrs * 6 rate - theRate / 6 Case quarterly nper - nyrs * 4 rate = theRate / 4 Case semiannually nper = nyrs * 2 rate = theRate / 2 Case Else 'Ошибка! thePmt3 = CVErr(xlErrValue) 'Присваивает код ошибки Exit Function 'Возврат ошибки. End Select 'Расчет платежа и преобразование его в положительное число. thePmt3 = -Application.pmt(rate, nper, pv, fv) End Function Примечания: 1. Чтобы посмотреть константы, определяющие коды ошибок Excel, используйте Object Browser, выбрав библиотеку Excel и объект Constants. Все константы кодов ошибок Excel начинаются с xlErr, затем следует имя ошибки. Другой возмож¬ ностью является применение функции Error () без аргумента, после чего процедура завершается сообщением об ошибке, вместо того чтобы передать вызвавшей ее программе код ошибки. Однако, сделав это, вы не сможете использовать свою функцию в таблице. 2. В данной процедуре оператор Exit Function вызывает немедленное завершение функции и возврат в вызвавшую ее процедуру. Аналогично этому оператору используется оператор Exit Sub, вызывающий немедленное завершение процеду¬ ры. Используйте операторы Exit только в особых случаях, таких как ошибочные ситуации, ибо эти операторы разрушают логическую структуру программы. Чтобы учесть возвращаемый код ошибки, необходимо изменить текстовую проце¬ дуру. Прежде чем использовать возвращенное функцией значение для печати резуль¬ тата проверьте, является ли это значение ошибочным. Если не сделать этого, могут возникнуть дополнительные ошибки —при попытке обработать код ошибки. Следующая процедура проверяет два значения, передаваемые функции: одно нормальное, а второе, выходящее за пределы диапазона значений. Отдельная проце¬ дура Print It () производит проверку результатов и их печать на панели Immediate. Функция IsErrorO производит проверку и возвращает True, если аргумент — код ошибки. 169
Visual Basic for Applications в примерах Option Explicit Const yearly = 0 Const monthly = 1 Const bimonthly = 2 Const quarterly = 3 Const semiannually = 4 t 'Процедура проверки функции thePmt3() i Sub TestProc6() Dim rate As Single, theYears As Integer, presval As Currency Dim Payl, Pay2 'Для получения кода ошибки должны быть типа Variant. rate =0.07 '7% в год theYears = 15 'Ссуда на 15 лет presval = 130000 'Размер ссуды $130,000.00 Payl = thePmt3(rate, theYears, presval, yearly) Pay2 = thePmt3(rate, theYears, presval, 5) 'Содержит ошибку Printlt Payl Printlt Pay2 End Sub Sub Printlt(aValue) 'Перед печатью проверяет значение на код ошибки. If IsError(aValue) Then Debug. Print ’’Код ошибки = ’’; aValue Else Debug. Print Format (aValue, ’’$#,##0.00’’) End If End Sub Выполнив эту процедуру, на панели Immediate увидите следующие результаты: Run(”TestProc6”) $14,273.30 Error Value = Error 2015 Эту процедуру можно использовать также в таблице (см. рис. 10.1). 170
Глава, 10, Применение структуры Select Case □ FUNCEXMP.XLS . - QI A В c 1 0 \ 1 El 57 58 Результат Формула 59 $14,273.30 =thePmt3(7%,15,130000,0) 80 $1J 68.48 =thePmt3(7%,15,130000,1) 81 $2,340.79 =thePmt3(7%,15,130000,2) 82 $3,516.94 =thePmt3(7%,15,130000,3) 83 $7,068.27 =thePmt3(7%,15,130000,4) 84 #VALUE! =thePmt3(7%,15,130000,5) 65 86 нН ИНК MO dule2 £ Modulel \ Sheetl / Sheet2 / Shee|*[ JHHHB □ Рис. 10.1. Результаты работы функции thePmt3() в таблице. Функции были переданы допустимые значения продолжительности периода и одно значение, выходящее за пределы диапазона Заключение В данной главе описана управляющая структура Select Case — специальная структура, предназначенная для выбора блока кода, основанного на сравнении с единичным значением. В трех главах части IV вы познакомились со структурирован¬ ными управляющими структурами, а из следующей главы — узнаете, что такое неструктурированные управляющие структуры и почему их не нужно использовать. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Является ли оператор Case Is Like "??[aeiou]*” допустимым? 2. С учетом двух операторов Case, приведенных ниже, укажите, какие блоки кода выполняются, если Value = 13 и если Value = 17: Select Case Value Case 11, 14, 15 To 19 Blokl Case 11 To 15, 17 Blok2 End Select 171
Visual Basic for Applications в примерах 3. Что такое перечислимые значения? Чем полезно их применение? 4. Для чего может быть использован оператор Case Else? 5. Назовите имя констан™ Excel, возвращающей ошибку #NUM! в функции CVErrO Упражнения для закрепления материала 1. Перепишите другие финансовые функции-"наклейки” из гл. 7 для учета различны: периодов платежей. 2. Перепишите функцию thePay6() из предыдущей главы для расчета сверхурочных заменив блочную структуру If структурой Select Case. 3. Напишите блочную структуру If, решающую ту же задачу, что и структура Selec Case в функции thePmt3(). 172
Глава И Не применяйте неструктурированные переходы Неструктурированные переходы были частью BASIC с самого начала, а структури¬ рованные управляющие структуры —более позднее дополнение. Языки программирова¬ ния изначально включали в себя неструктурированные переходы, так как они позволяли эмулировать основные электронные возможности компьютеров. Но такие переходы чрезвычайно осложняют программу, в то время как структурированные управляющие структуры позволяют создавать гораздо более простые для понимания и оформления коды. Это и определяет название данной главы, из которой вы узнаете: ► Что такое неструктурированные переходы > Почему их следует избегать Что такое неструктурированный переход? Неструктурированный переход — это структура, которая может применить пере¬ ход в любом месте процедуры. Простейшим из неструктурированных переходов является оператор GoTo. Оператор GoTo имеет следующий синтаксис: GoTо метка где метка —метка или маркер некоторой строки в процедуре. Метки записываются в начале строки, причем после имени метки записывается двоеточие. Это двоеточие, однако, не включается в имя метки, используемое в операторе GoTo. Другим неструктурированным переходом является логическое ветвление, имеющее следующий синтаксис: If выражение Then GoTo метка Это ветвление очень похоже на оператор GoTo, но переход осуществляется только в том случае, если значение выражение равно True. Операторы GoSub-Return являются оригинальным соглашением вызова процедур BASIC. Оператор GoSub имеет следующий синтаксис: GoSub метка где метка имеет тот же смысл, что и в операторе GoTo. Разница между операторами GoTo и GoSub заключается в том, что после применения оператора GoSub должен применяться оператор Return, возвращающий управление оператору, который следует за последним выполнившимся оператором GoSub. 173
Visual Basic for Applications в примерах Следующие два варианта операторов GoTo и GoSub называются вычисляемым переходами: On величина GoTo метка1, метка2, меткаЗ, ... On величина GoSub метка1, метка2, меткаЗ, ... В этих операторах величина является целым числом, и если величина = 1, то переход осуществляется на метка!\ если величина = 2, — то на метка2 и т.д. Проблемы с неструктурированными переходами Очевидно, что структурированные переходы разделяют программу на смежные блоки и выбирают блок согласно некоторому логическому критерию. При структури рованном переходе однозначно известно, как определить код, который будет испол¬ няться, когда заданная логическая величина будет иметь значение True. При неструк турированном переходе не всегда можно уверенно указать блок кода, который будет выполняться после перехода. Если потребуется скорректировать программу и ван встретится оператор GoTo, то где искать конец этой ветви? Если повезет —он окажется рядом, но вряд ли это произойдет. Можно создавать структурированные коды с неструктурированными переходами,I однако, это требует жесткой самодисциплины. Например, можно использовать не структурированные переходы при разработке функции thePmt3() из гл. 10. Для этого переделайте программу, например, следующим образом: ' Функция — наклейка,использующая неструктурированные переходы. Function thePmt4(theRate As Single, nyrs As Integer, pv As Currency, PeriodType As Integer) As Variant t код продолжительности периода, годовой платеж месячный платеж двухмесячный платеж квартальный платеж полугодовой платеж г t f ! ! Г PeriodType — это PeriodType = 0 = 1 = 2 = 3 = 4 Любое другое значение приведет к ошибке. Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. If PeriodType <> yearly Then GoTo lmonth nper = nyrs rate = theRate 774
GoTo getval lmonth: If PeryodType <> monthly Then GoTo lbimonth nper = nyrs * 12 rate = theRate / 12 GoTo getval lbimonth: If PeryodType <> bimonthly Then GoTo lquarter nper = nyrs * 6 rate = theRate / 6 GoTo getval lquarter: If PeryodType <> quarterly Then GoTo lsemiannual nper = nyrs * 4 rate = theRate / 4 GoTo getval lsemiannual: If PeryodType <> semiannually Then GoTo lelse nper = nyrs * 2 rate = theRate / 2 GoTo getval lelse: thePmt4 = CVErr(xlErrValue) 'Присваивает код ошибки GoTo done 'Выход из функции getval: 'Расчет платежа и преобразование его в положительное число. thePmt4 = -Application.pmt(rate, nper, pv, fv) done: End Function’ В данном случае перечисленные значения последовательны, что позволяет пере¬ делать функцию следующим образом: I 1 Функция — наклейка,использующая неструктурированные переходы. t Function thePmt5 (theRate As Single, nyrs As Integer, _ pv As Currency, PeriodType As Integer) As Variant ' PeriodType — это код продолжительности периода. ' PeriodType = 0 годовой платеж ' = 1 месячный платеж 175
Visucd B^ic ^or iicauons в примерах f8S8«®WSe«»C6585«e5«!«e«as««»«e5W5S«»5SeU«aa*^^ ' = 2 двухмесячный платеж ' = 3 квартальный платеж ' = 4 полугодовой платеж ' Любое другое значение приведет к ошибке. Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. On PeriodType +1 GoTo lyear, lmonth, lbimonth, _ lquarter, lsemiannual ' Сюда попадаем при ошибочном PeriodType. thePmt5 = CVErr(xlErrValue) 'Присваивает код ошибки GoTo done 'Выход из функции lyear: nper = nyrs rate = theRate GoTo getval lmonth: nper = nyrs * 12 rate = theRate / 12 GoTo getval lbimonth: nper = nyrs * 6 rate = theRate. / 6 GoTo getval lquarter: nper = nyrs * 4 rate = theRate / 4 GoTo getval lsemiannual: nper = nyrs * 2 rate = theRate / 2 GoTo getval getval: 'Расчет платежа и преобразование его в положительное число. thePmtb = -Application.pmt(rate, nper, pv, fv) done: End Function Хотя модернизированная функция и не выглядит слишком пугающе, напомним, что создание таких функций или процедур требует жесткой дисциплины и большой осторожности. Если процедура создается на протяжении длительного периода, новые опции добавляются в нее в различное время по ходу процесса. Программисты обычно 176
Глава 11. Не применяйте неструктурированные переходы ищут наиболее быстрые и легкие пути переделки процедуры и могут добавлять новые опции в ее конец, поставив затем в нужных местах неструктурированные переходы. В этом случае функция может приобрести следующий вид: ' Функция — наклейка, использующая технику ’’спагетти". Function thePmt6 (theRate As Single, nyrs As Integer, _ pv As Currency, PeriodType — PeriodType = I 1 г г t код продолжительности периода, годовой платеж месячный платеж двухмесячный'платеж квартальный платеж полугодовой платеж PeriodType As Integer) As Variant это 0 1 2 3 4 Любое другое значение приведет к ошибке. Dim nper As Integer, fv As Currency, rate As Single fv = 0 'Предположим, что ссуда выплачена. If PeriodType = montly Then GoTo lmonth If PeriodType <> yearly Then GoTo lelse nper = nyrs rate = theRate GoTo getval lmonth: nper = nyrs * 12 rate = theRate / 12 GoTo getval lelse: If PeryodType = bimonthly Then GoTo lbimonth If PeryodType = quarterly Then GoTo lquarter If PeryodType = semiannually Then GoTo 1semiannual thePmt6 = CVErr(xlErrvalue) 'Присваивает код ошибки GoTo done 'Выход из функции getval: 'Расчет платежа и преобразование его в положительное число. thePmt6 = -Application.pmt(rate, nper, pv, fv) GoTo done lbimonth: nper = nyrs * 6 rate = theRate / 6 177
Igogetval: GoTo getval 1quarter: nper = nyrs * 4 rate = theRate / 4 Iqtr: GoTo Igogetval Isemiannual: nper = nyrs * 2 rate = theRate / 2 GoTo lqtr done: End Function Все три последние функции дадут одинаковый результат. Однако очень трудно сообразить, что будет делать последняя из них, или пошагово проследить за ходом вычислений, так как управление будет непредсказуемо "прыгать” по всей процедуре Такое программирование презрительно называют "спагетти”: если начертить линию выполнения на распечатке, то она будет выглядеть так же запутанно, как лежащие на тарелке спагетти. Представьте, сколько хлопот доставит вам такая процедура, состоя щая из нескольких тысяч строк кода. Вас может заинтересовать, почему неструктурированные переходы были включены в Visual Basic. Ответ прост — чтобы обеспечить совместимость с более старыми версиями BASIC. Программы, написанные на GW-BASIC или BASICA, должны выполняться в модуле Visual Basic с минимальными переделками. Кроме того, процедуры обнаружения ошибок Visual Basic часто нуждаются в использовании неструктурированных переходов. Применяйте структурированные переходы, если это возможно Ситуаций, требующих применения неструктурированных переходов, очень мало Любые обычные вычисления можно осуществить с помощью структурированных переходов, причем значительно легче, чем с неструктурированными. Кроме того, программа со структурированными переходами более понятна. Единственными процедурами, требующими неструктурированных переходов, яв¬ ляются процедуры обработки ошибок. Такие процедуры могут применять переходы между большими блоками программы, чтобы пропустить нормальные вычисления и обработать ошибку. Однако, если только это возможно, старайтесь для обработки ошибок применять структурированные переходы. 178
Глава 11. Не применяйте неструктурированные переходы Заключение В данной главе рассмотрено неструктурированное программирование и аргументы против его применения. Этим завершается часть IV книги. В части V вам предстоит узнать о повторяемых структурах. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. I. У вас есть выбор — использовать структурированный или неструктурированный переход, причем неструктурированный переход проще программировать. Какому из этих переходов вы отдадите предпочтение? Упражнение для закрепления материала 1. Выполните примеры из этой главы, чтобы проверить, действительно ли они дают одинаковые результаты.
Часть V Циклы и повторяемые структуры
Глава 12 Применение вычисляемых циклов В этой части книги рассматривается, что такое повторяемые структуры и как они используются для сокращения размера кода, который необходимо написать для решения задачи. Повторяемые структуры известны под общим названием циклы, объединяющим три различных типа циклов: вычисляемые, логически прерываемые и объектного типа. Данная глава охватывает вычисляемые циклы, а две следующие — логически прерываемые циклы и циклы объектного типа. Вы узнаете: > Что такое вычисляемые циклы > Как применять цикл For-Next > Как использовать счетчик цикла Что такое вы числяемый цикл Вычисляемый цикл предназначен для повторения одного блока кода заданное количество раз. Вычисляемые циклы применяются в тех случаях, когда заранее известно, сколько раз нужно выполнить блок кода. Например, если требуется ((юрматировать десять последовательных ячеек таблицы, то можно применить вычис¬ ляемый цикл, позволяющий поочередно изменить формат каждой из ячеек. Применение циклов For-Next Вычисляемые циклы реализуются в Visual Basic с помощью циклической структу¬ ры For-Next, имеющей следующий синтаксис: For переменна я_цикла = начало То конец Step шаг . блок кода Exit For . блок кода Next переменна я_цикла В данном случае переменная_цикла является именем переменной, которая считает количество шагов цикла. Переменные начало и конец определяют начальное и 183
Visual Basic for Applications в примерах конечное значения переменная_цнкла, а шаг — величину наращивания перемет ная_цикла после каждого выполнения цикла. Когда в программе встречается цикл For-Next, переменная_цикла получает значе¬ ние начало, затем выполняется блок кода вплоть до оператора Next. Далее перемет ная_цикла увеличивается на величину шаг и сравнивается со значением конец. Есл значение переменная_цикла больше, чем значение конец, то цикл прерывается; управление переходит к оператору, следующему за оператором Next. Если значени: переменная_цикла меньше или равно значению конец, то блок кода цикла выполняете еще раз. Так продолжается до тех пор, пока значение переменная_цикла не станг больше значения конец. Когда встречается оператор Exit For, цикл немедленно прерывается и управленк переходит к оператору, следующему за оператором Next. Обычно оператор Exit Fc применяется для прекращения процесса поиска чего-нибудь, когда вы искали эт. циклически и нашли. Если зарезервированное слово Step и величина шаг опущены, то величин: переменная_цикла наращивается на 1 после каждого шага цикла. Если величина копъ меньше величины началом величина шаг отрицательна, цикл считается по убывающей а не по возрастающей. Использование счетчика цикла Счетчик цикла — это обычная переменная, доступная в любом месте цикла; используемая в блоке его кода для выбора различных величин или ячеек. Счетчиков цикла обычно является целое число, используемое в качестве индекса массик переменных или в качестве аргумента метода Cells для выбора ячейки таблицы. После завершения цикла значение счетчика цикла больше значения конец (ил; меньше значения конец, если цикл считался по убывающей). Этот фактор удобн: использовать, чтобы определить, завершился ли цикл нормально или был преждевре менно прерван оператором Exit For. Предупреждение: Поскольку переменная цикла является обычной переменной, то ее можно изменит: в любом месте блока кода цикла. Однако изменений переменной цикла необходим избегать: такие изменения легко могут привести к непредсказуемым результатах Например, следующая конструкция будет выполняться всегда: For I = 1 То 10 1 = 1-1 Веер Next I 184
Глава 12. Применение вычисляемых циклов В качестве примера рассмотрим процедуру, которая заполняет текущий выбор случайными числами, применяя два встроенных цикла For-Next для области ячеек в текущем выборе. Эту процедуру можно использовать и в целях подготовки некоторых ячеек для различных вычислений либо для заполнения области ячеек различными вычисляемыми значениями, полученными с помощью различных функций или формул -в зависимости от ваших потребностей. Option Explicit ' Запись случайных чисел в текущий выбор. Sub StickRandomf) Dim numRows As Integer, numCols As Integer Dim theRow As Integer, theCol As Integer 'Определение размера текущего выбора. numRows - Selection.Rows.Count numCols = Selection.Columns.Count Randomize 'Инициализация генератора случайных чисел. For theRow = 1 To numRows For theCol = 1 To numCols Selection.Cells(theRow, theCol).Value = Rnd Next theCol Next theRow End Sub Вначале процедура определяет количество строк и колонок в текущем выборе, применяя методы Rows и Columns к объекту Selection Range. Метод Rows возвращает набор всех строк в текущем выборе, a Columns —набор, содержащий все колонки. Свойство Count возвращает количество объектов в каждом наборе. Количество строк и колонок задают верхние пределы переменных цикла в двух встроенных циклах. Внешний цикл пошагово обрабатывает строки, а внутренний — колонки в текущем выборе. Значения переменных цикла theRow и theCol начинаются с 1 и увеличиваются на 1 после каждого шага соответствующего цикла. Вначале внешний цикл выбирает строку, затем внутренний цикл пошагово обрабатывает эту строку по одной колонке. Для записи случайного числа в каждую ячейку текущего выбора используются метод Cells, свойство Value и функция Rnd(). Для запуска этой процедуры выберите пустую таблицу, затем выберите в этой таблице область ячеек, а потом укажите директиву Tools/Macro. Отобразится диало¬ говое окно Macro. Выберите процедуру StickRandom, нажмите командную кнопку Run. В ходе выполнения процедуры случайные числа одно за другим будут записы¬ ваться в ячейки (см. рис. 12.1). 185
Visual Basic for Applications в примерах ia !□ I'Wf О 783666 О 079365:' О 337701' О 546307' О 354211: О 663862Г О 440748: О 71106: 0.832145; О 377863; О 390122’ О 759828 О 905921Т О 095185 О 650545* О 319093 О 638468; О 82398 О 497311 О 699976 0.344216 О 027702' О 308481 О 126959 0 311195 О 399641' ТГ734304. О 120352 О 981535" 0.226244 0^497991 * 0.047439 О 32583* О 64098 О 923434* 0’033348|| 0.244576 О 690995 О 112081' О 267727 О 63153 0032378 О 170851 О 617614 I е j i . • I i f Sheetl / Sheet2 / Sheet3\Sheet</^J»J|^J Рис. 12.1. Таблица после выполнения процедуры Stick Random, поместившей случайные числа в каждую ячейку текущего выбора Теперь, когда у вас есть область ячеек, заполненных некоторыми значения*, можно создать процедуру для циклической обработки содержимого этих ячк копирования значений в массив, определения среднего арифметического элемен? массива и отображения этого среднего арифметического в окне сообщения. Привс: процедуру, обеспечивающую выполнение указанных действий: Option Explicit ' Процедура определяет среднее арифметическое ' всех ячеек текущего выбора и отображает это ' значение в окне сообщения. г Sub BlockAverage() Dim numRows As Integer, numCols As Integer Dim theRow As Integer, theCol As Integer Dim I As Integer, J As Integer Dim theAverage As Single, theSum As Single Dim myArrayO As Single 'Определение размера текущего выбора. numRows = Selection.Rows.Count numCols = Selection.Columns.Count ReDim myArray(numRows, numCols) 'Копирование содержимого ячеек в массив. For theRow = 1 То numRows For theCol = 1 To numCols myArray(theRow, theCol) = Selection.Cells(theRow, theCol).Value 186
Глава 12. Применение вычисляемых циклов Next theCol Next theRow ' Определение среднего арифметического элементов массива. theSum = О For I = 1 То numRows For J = 1 То numCols theSum = theSum + myArray(I, J) Next J Next I theAverage = theSum / (numRows * numCols) MsgBox "Среднее арифметическое = ” & Str(theAverage) End Sub Примечание: Переменная типа массив my Array () определена в операторе Dim без индексов, задающих размер массива. Такой массив называется динамическим массивом и переопределяется в процессе выполнения процедуры оператором ReDim в массив нужного размера. Динамические массивы наиболее удобно использовать в тех случаях, когда неизвестно, какого размера массив потребуется и вы не хотите напрасно расходовать память, определяя массив максимально возможного размера. Даже если зафиксировать размер такого массива некоторой максимальной вели¬ чиной, вполне может оказаться, что вам нужен массив еще большего размера. Используя динамические массивы, вы избавите себя от многих неприятностей, связанных с фиксацией размеров массивов. Единственным неудобством использо¬ вания динамических массивов является то, что вам придется дважды определить массив, прежде чем вы сможете его использовать. Вначале эта процедура определяет размер выбора (тем же способом, что и предыдущая)-, затем переопределяет динамический массив шуАггауО в соответствии с полученными значениями. В процедуре используются два вложенных цикла для копирования значений из ячеек в массив. После завершения копирования вторая пара вложенных циклов пошагово обрабатывает каждый элемент массива, суммируя их значения. В конце процедура рассчитывает среднее арифметическое и отображает его в окне сообщения, используя функцию MsgBoxO. Для запуска этой процедуры выберите некоторые ячейки, а затем директиву Tools/Macro. В диалоговом окне Macro выберите процедуру BlockAverage и нажмите командную кнопку Run. Когда среднее арифметическое элементов массива my Array будет рассчитано, результат отобразится в диалоговом окне. Функция MsgBoxO отображает текст, указанный в качестве ее аргумента, в окне сообщения с одной командной кнопкой ОК, как изображено на рис. 12.2. Для окончания процедуры следует нажать командную кнопку ОК. 187
Visual Basic for Applications в примерах Рис. 12.2. Определение среднего арифметического содержимого ячеек текущего Bbidopi В другом примере определяется размер интереса — суммы, которую нул выплатить за использование ссуды — в платеже за сто пятидесятый месяц из расч размера ссуды в $130000, взятой на 15 лет под 7%. Известно, что месячный плат при этих условиях составляет $1168.48 (см. рис. 7.3). Для определения разм! интереса в платеже за сто пятидесятый месяц необходимо вычислить размер интер в каждом платеже, вычесть интерес из суммы платежа, чтобы определить сум: направляемую в счет погашения ссуды; и затем вычесть результат из величины ссу; чтобы рассчитать дебетовое сальдо счета. Это необходимо выполнить 150 раз, чтс получить размер интереса в платеже за сто пятидесятый месяц. Ниже привед< функция, обеспечивающая решение указанной задачи: Option Explicit t ' Расчет интереса за указанный месяц. Function GetInt(ByVal PresVal As Currency, rate As Double, PmtNum As Integer, Pmt As Currency) As Currency Dim Interest As Currency, Principal As Currency Dim I As Integer For I = 1 To PmtNum Interest = PresVal * rate Principal = Pmt — Interest PresVal = PresVal — Principal 'Интерес в этом платеже. 'В счет погашения ссуды 'Дебетовое сальдо. Next I Getlnt = Interest End Function 188
Глава 12. Применение вычисляемых циклов Функция получает начальные значения в аргументах вызова. Расчет размера интереса производится в цикле от 1 до необходимого номера платежа (т.е. платежа за соответствующий месяц). В данном примере был применен вычисляемый цикл: точно задано количество его шагов. Два первых оператора в блоке кода цикла рассчитывают интерес и сумму, направляемую в счет погашения ссуды, а третий рассчитывает дебетовое сальдо, вычитая полученную сумму погашения из текущего остатка счета. Величина Pres Vai должна быть передана функции значением (ByVai), а не адресом, так как в процессе расчета она постоянно изменяется, а изменять значение аргументов вызова некорректно. После окончания цикла переменная Interest будет содержать размер интереса в платеже номер PmtNum, что является требуемым результатом. Для проверки этой процедуры запишите формулу с функцией GetlntO в ячейку таблицы (см. рис. 12.3), а затем преобразуйте содержимое этой ячейки в формат представления денежных единиц. В 18 19 20 21 КН Результат $192.78 I ► I Sheet2 \5Иее(3/~~ФинансовывФдикции|>[ С FUNCEXMP.XLS Функция =Getlnt(130000,7 %/12,160,1168.48 Рис. 12.3. Применение функции GetlntO для расчета размера интереса в платеже за сто пятидесятый месяц по ссуде $130000, взятой на 15 лет под 7% при месячном платеже $1168.48 Заключение В этой главе вы познакомились со структурой For-Next, являющейся вычисляемой повторяемой структурой или циклом. Такие структуры применяются для выполнения блока кода заданное количество раз. В следующей главе описаны логические преры¬ ваемые циклы, которые выполняются до тех пор, пока не изменится состояние заданного условия. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое вычисляемый цикл? 2. Для чего предназначен оператор Exit For? 189
Visual Basic for Applications в примерах 3. Какой размер шага используется, если опущены зарезервированное слово Step величина шаг? 4. Обычно не рекомендуется изменять переменную цикла в блоке кода цикла. Mow ли вы придумать ситуацию, в которой такое изменение переменной цикла буд? логически обосновано? Упражнения для закрепления материала 1. Напишите функцию для расчета факториала аргумента. Факториал п (п!) — т число, являющееся произведением всех целых чисел от 1 до п. п! = 1*2*3*...п 2. Напишите функцию, рассчитывающую общий размер интереса по ссуде меле двумя платежами, например между сто двадцатым и сто сороковым месяцем. 3. Рассмотрите функцию MultiplyEm4() из гл. 7. г ' Функция умножения массива, элементы которого — тоже массивы. Function MultiplyEm4(ParamArray theArray()) As Single Dim Product As Single, Part, Value Product = 1 For Each Part In theArray For Each Value In Part Product = Product * Value Next Value Next Part MuitipiyEm4 = Product End Function Перепишите эту функцию, заменив циклы For-Each на циклы For-Next. 190
Глава 13 Применение логически прерываемых циклов Из гл. 12 вы узнали о вычисляемых циклах — одном из трех типов повторяемых структур. Вторым типом повторяемых структур в Visual Basic являются логически прерываемые циклы. Существуют два типа логически прерываемых циклов: цикл Do-Loop и цикл While-Wend. Оба этих типа описываются в этой главе. Цикл Do-Loop является наиболее универсальным из логических циклов и может обрабатывать все логически прерываемые циклические структуры. Цикл While-Wend менее универсален и обычно включается для обеспечения совместимости с более старыми версиями BASIC. В настоящей главе описаны: > Логически прерываемые циклы > Повторяемые структуры Do-Loop > Повторяемые структуры While-Wend Что такое логически прерываемые циклы? Логически прерываемые циклы — это повторяемые структуры, которые преры¬ ваются, если удовлетворяется некоторое условие. Если вычисляемые циклы выпол¬ няют его определенное количество раз, то логически прерываемый цикл может выполнять блок кода неопределенное количество раз, или вообще не выполнять, в зависимости от состояния логического условия, прерывающего цикл. Вы можете применять логически прерываемую структуру взамен вычисляемого цикла путем наращивания переменной после каждого шага цикла и проверки ее значения в условии прерывания цикла, однако такая конструкция весьма громоздка. Логически прерываемые циклы используются в тех случаях, когда количество шагов цикла заранее не известно, например при чтении данных из дискового файла неизвестной длины либо при коммуникации с периферийным устройством или другой программой. В этих случаях условием прерывания цикла является отсутствие данных, необходимых для обработки. Кроме того, логически прерываемые циклы можно применять для создания цикла обработки события. Цикл обработки события — это логически прерываемый цикл с литеральным значением True в качестве условия. В связи с тем, что условие неизменно, такой цикл никогда не прерывается. Цикл обработки события обычно содержит вызовы процедур, проверяющих факт наступления некоторого события, например запись в ячейку или выполнение директивы. Если одно из этих событий наступило, то для его обработки вызывается процедура; в противном случае цикл продолжает выполняться, ожидая наступления какого-нибудь события. Многие программы, например текстовые процессо¬ ры и электронные таблицы, работают подобным образом, непрерывно выполняя цикл обработки события и ожидая, пока вы что-нибудь сделаете.
Visual Basic for Applications в примерах Применение циклов Do-Loop Цикл Do-Loop является наиболее универсальным из логически прерываем циклов. Существуют четыре конфигурации этого цикла: условие True в нано условие True в конце, условие False в начале и условие False в конце. Ниже привел синтаксис всех четырех конфигураций: ' Условие True в начале. г Do While условие ...блок кода Exit Do ...блок кода Loop ' Условие True в конце. f ' Do ...блок кода Exit Do ...блок кода Loop While условие Л ' Условие False в начале. Do Until условие ...блок кода Exit Do ...блок кода Loop | f ! ' Условие False в конце. Do ...блок кода Exit Do ...блок кода Loop Until условие 192
Глава 13, Применение логически прерываемых циклов Применение зарезервированных слов While и Until Зарезервированные слова While и Until определяют логику условия, причем While указывает, что цикл будет выполняться до тех пор, пока условие принимает значение True, a Until указывает, что цикл будет выполняться до тех пор, пока условие не станет True. Отношение между While и Until можно выразить следующим образом: While условие = Until Not условие Применение оператора Exit Do Оператор Exit Do предназначен для преждевременного прекращения цикла и обычно применяется в логической структуре (вроде оператора If), которая проверяет альтернативное условие прерывания цикла, например ошибку. Запись условия в начале цикла Запись условия в начале или в конце цикла определяет, где это условие будет проверяться. Когда условие проверяется в начале цикла, цикл выполняется (или не выполняется), если условие исходно удовлетворено. Такой вид цикла удобно приме¬ нять в тех случаях, когда цикл не должен выполняться до тех пор, пока условие не будет выполнено. Например, при чтении дискового файла можно использовать функцию EOF() для проверки очередной части файла в поисках, маркера его конца. Если вы попытаетесь прочесть маркер конца файла, ваша программа остановится с ошибкой; таким образом, перед тем, как прочесть из файла какие-либо данные, необходимо проверять очеред¬ ную часть файла в поисках маркера конца файла. Чтобы сделать это, можно использовать нечто, подобное Open "Myfile.TXT" For Input As #1 'Открытие файла. Do Until EOF(l) 'Проверка на конец файла. Input #1, А$ 'Если нет, то чтение данных. 'Код преобразования величины А$ Loop Close #1 'Закрытие файла по концу работы. В этом фрагменте кода функция ЕОЕ() проверяет маркер конца файла. Если вы еще не дошли до конца файла, функция EOF() возвратит False и цикл выполнится, прочитав из файла строку и обработав ее. Если функция EOF() обнаружит маркер конца файла, то она возвратит True и цикл прервется. 7 Вильям Дж. Орвис 193
Visual Basic for Applications в примерах Запись условия в конце цикла Запись условия в конце цикла означает, что цикл выполнится хотя бы один раз Этот вид цикла применяется в тех случаях, когда цикл должен быть выполнен хот* бы один раз, чтобы сформировать условие, которое затем будет проверяться. Ое используется в основном для поиска, например, конкретного значения в массиве. Так, следующая процедура находит в массиве первый положительный элемент: ' Поиск первого положительного f Function FirstPos(theArray) As Dim J As Integer, Value As J = LBound(theArray) - 1 Do J = J + 1 Value = theArray(J) Loop Until Value > 0 FirstPos - Value End Function элемента массива. Single Single 'Инициализация J. 'Начало цикла. 'Наращивание J. 'Выбор элемента массива 'Проверка элемента. В этой процедуре theArray является переменной типа Variant, и следовательно позволяет передать из таблицы массив. Функция LBoundO возвращает нижний предел индекса массива. Переменная J устанавливается на единицу меньше этого предела, так как в начале цикла она будет увеличена на 1, что и даст значение нижнего предела индекса массива. Цикл начинается оператором Do, затем наращивается переменная] и из массива выделяется элемент с соответствующим индексом. В конце цикла это! элемент проверяется, и если он положительный, то цикл прерывается, причем переменная Value равна положительному элементу массива, а переменная J —индексу этого элемента массива. Такая процедура предполагает, что один из элементов массива будет иметь положительное значение. Если это точно не известно, то необходимо проверять значение переменной J и прерывать цикл, когда J превысит верхний предел индекса массива. Иначе вы получите ошибку выполнения 9 "Subscript out of range". Чтобы проверить переменную J, процедуру нужно модифицировать следующим образом: ' Поиск первого положительного элемента массива. г Function FirstPos2(theArray) As Single Dim J As Integer, Value As Single 194
Глава 13. Применение логически прерываемые циклов J = LBound(theArray) — Do 'Инициализация J. 'Начало цикла. J = J + 1 'Наращивание J. If J > UBound(theArray) Then 'Превышение верхнего предела? Value = CVErr(xlErrValue) 'Установка кода ошибки, если нет Exit Do End If Value = theArray(J) Loop Until Value > 0 FirstPos2 = Value 'ни одного положительного элемента. 'Прерывание цикла. 'Выбор элемента массива. 'Проверка элемента. End Function В этой версии процедуры значение переменной J после каждого наращивания сравнивается с верхним пределом индекса массива, полученным с помощью функции UBound(). Если переменная J превысит верхний предел, то переменной Value будет присвоен код ошибки #VALUE!, чтобы показать, что ни один элемент массива не соответствует заданному условию. В этом случае цикл будет прерван оператором Exit Do. Обратите внимание на то, что теперь функция возвращает значение типа Variant, -это позволяет передать код ошибки. На рис. 13.1 отражены результаты использования функций FirstPosO и FirstPos2() в таблице. Обратите внимание на второй пример использования функции FirstPos2(); так как функция получила массив аргументов, не содержащий положительных аргументов, она возвратила код ошибки. D . FUNCEXMP.XLS | А В 1 С Г П I 24 25 Результат ^Функция 26 4:=FirstPos({-1,-13 5,-8,4,7,-12}) 27 4;=FirstPos2({-1,-13.5,-8,4,7,-12}j 28 #VALUE! i=FirstPos2({-1,-13.5/-8,-4,-7,-12}) 29 30 Mj< ► | Sheet3/ Финансовые Функции / Modulj * | Рис. 13.1. Использование функций FirstPosO и FirstPos2() в таблице Применение циклов While-Wend Цикл While-Wend — это реликт, сохранившийся от старых версий BASIC. Функционально этот цикл полностью эквивалентен структуре Do While-Loop. Цикл While-Wend имеет следующий синтаксис: 195
Visual Basic for Applications в примерах While условие Wend ! Обратите внимание на то, что в структуре While-Wend отсутствует операторй позволяющий прервать цикл до его окончания, а также отсутствует возможно] записать условие в конце цикла. В остальном он работает аналогично струкг.) Do-Loop. Заключение В этой главе вы ознакомились с логически прерываемыми циклами. В отличим вычисляемых циклов, которые прекращаются после определенного количества onej ций (см. гл. 12), логически прерываемые циклы продолжаются до тех пор, пз| соблюдается логическое условие. В следующей главе описан последний из трехтй повторяемых структур —циклы объектного типа. j Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое логически прерываемый цикл? 2. Что означают зарезервированные слова While и Until в структуре Do-Loop? 3. С какой целью условие записывается в конце цикла, а с какой целью —в нача.7 Упражнения для закрепления материала 1. Перепишите функцию FirstPos2() таким образом, чтобы она начинала прове- массива с верхнего предела индекса и оканчивала поиск первого положительн:? элемента нижним пределом индекса массива. 2. Напишите функцию, которая получает в качестве аргумента строку и возврат: номер позиции, содержащей последнюю (самую правую) букву А. Эта функе аналогична функции InStrO, но проверять строку будет не слева, а справа. 3. Перепишите процедуру BlockAverageO из гл. 12, заменив циклы For-Next цикла» Do-Loop. 196
Глава 14 Применение циклов объектного типа Циклы объектного типа являются последним из трех типов повторяемых структур, допустимых в Visual Basic. Для создания циклов объектного типа используется структура For Each, которая применяется к массивам и наборам объектов. В примерах, приведенных в книге, эти циклы встречаются очень часто. Из этой главы вы узнаете: > Как работают циклы объектного типа > Как цикл For Each применяется к объектам > Как цикл For Each применяется к массивам Что такое циклы объектного типа? Циклы объектного типа очень похожи на вычисляемые циклы (см. гл. 12), так как тоже выполняются определенное количество раз. Отличие проявляется в том случае, когда цикл объектного типа применяют к набору объектов и он выполняется по одному разу для каждого объекта набора. Если вы используете циклы объектного типа, то нет необходимости знать, сколько объектов включает в себя набор —это определяется автоматически. Счетчик цикла содержит не величину, определяющую количество итераций (шагов) цикла, а объект из набора. Кроме того, циклы объектного типа можно применить к массивам. В этом случае цикл будет выполняться для каждого элемента массива и переменная цикла будет содержать значение этого элемента. Применение цикла For Each к набору Циклы объектного типа реализуются структурой For Each. Чтобы применить цикл For Each к набору, используется синтаксис For Each элемент In набор В этом синтаксисе элемент представляет собой переменную типа Variant, что обеспечивает возможность содержать объект. Переменная набор является некоторым набором объектов. В процессе выполнения цикла объект выбирается из набора и помещается в переменную элемент, и это позволяет использовать переменную элемент в блоке кода цикла для осуществления необходимых действий с объектом. Аналогично циклу For-Next оператор Exit For прерывает цикл до его окончания. 197
Visual Basic for Applications в примерах | Предупреждение: Будьте осторожны, производя в теле цикла For Each любые изменения количества элементов набора. Упорядочение элементов набора отчасти произвольное, и новъв\ элемент может в данном цикле не обработаться. Очевидно, что структуру For Each очень удобно применять в различных ситуациях, особенно когда массив или область ячеек передаются пользовательской функции. Если не использовать структуру For Each, то придется определять, массив или набор ячеек (ссылок на ячейки) был передан; любая ссылка на ячейку перед обработкой должна быть преобразована в массив. Например, в следующей функции MultiplyEm6() для реализации функции MultiplyEm5() из гл. 7 применяется цикл For-Next. Позднее вы увидите реализа¬ цию той же функции с применением структуры For Each. ' Функция умножает любой список значений. Function Mui tipi уЕтб (Par amAr ray theArray()) As Single Dim Product As Single Dim I As Integer, IMax As Integer, IMin As Integer Dim J As Integer, JMax As Integer, JMin As Integer Dim К As Integer, KMax As Integer, KMin As Integer Product = 1 IMax = UBound(theArray) IMin = LBound(theArray) For I = IMin To IMax If TypeName(Array(I) ) 'Определение верхнего предела индекса. 'Определение нижнего предела индекса. 'Обработка массива параметров. "Range" Then 'Область ячеек? JMax = theArray(I).Columns.Count 'Определение количества колонок. KMax = theArray(I).Columns.Count 'Определение количества строк. For J = 1 To JMax 'Обработка колонок. For К = 1 To KMax 'Обработка строк. Product = Product 1 * theArray (I). Cells (К, J) ' Расчет произведения. Next К Next J Elself Right(TypeName(theArray(I)), 2) = "()" Then 'Массив? JMax = UBound(theArray(I)) 'Определение верхнего предела индекса. JMin = LBound(theArray(I)) 'Определение нижнего предела индекса. For J = JMin To JMax 'Обработка элементов массива. Product = Product * theArray(I)(J) 'Расчет произведения. Next J Else 'Это единичное значение. 198
Глава 14. Применение циклов объектного типа Product = Product * theArray(I) 'Расчет произведения. End If Next I MultiplyEm6 = Product End Function Вначале функция MuitiplyEm6() должна определить количество элементов в массиве параметров и начать циклическую обработку этих элементов. Затем блочный оператор If проверяет, область ячеек, массив или единичная величина переданы функции, и вычисляет произведение для каждого типа аргументов отдельно. Примечание; Функция MultiplyEm6() имеет меньше возможностей, чем функция Multiply- Еш5(). Функция MultiplyEm6() не может обрабатывать вертикальные массивы вида {2; 4; 23; 76; 5} и двумерные массивы вида {2, 4, 23, 76, 5; 1, 2, 3, 4, 5}, так как процедура определения размерности массива в Visual Basic достаточно сложна. Чтобы определить размерность массива, должна быть предусмотрена возможность прерывания по ошибке, с помощью которой можно экспериментальным путем установить количество строк и столбцов массива. Если вы получаете ошибку, значит, размеры массива превышают допустимые. Прерывание по ошибке этого типа рассматривается в гл. 23 "Применение прерываний по ошибке для реакции на непредвиденные события". Приведенная ниже функция является той же функцией MultiplyEm6(), но вместо цикла For-Next в ней применена структура For Each. Обратите внимание на то, что эта версия значительно короче и понятнее: ' Функция умножает любой список значений. Function Mui tipi уЕт5 (Par amAr ray theArray() ) As Single Dim Product As Single, Part, Value Product = 1 For Each Part In theArray If TypeName(Part) = "Range" Or Right(TypeName (Part), 2) = "()" Then For Each Value In Part Product = Product * Value Next Value Else Product = Product * Part End If Next Part 199
Visual Basic for Applications в примерах MultiplyEm5 = Product End Function Применение цикла For Each к массиву Для применения цикла For Each к массиву используется практически тон синтаксис, что и для набора объектов, за исключением того, что переменная элем не обязательно должна быть типа Variant, а может быть того же типа, что и элемен1 массива. При каждом шаге цикла переменная элемент содержит значение очереди: элемента массива. Структура For Each часто применяется в тех случаях, когда нужно обработать; элементы массива, и нет необходимости знать вид массива или к какому элемег нужен доступ в данный момент. Вы можете сложить все элементы массива или наг наименьший из его элементов. В общем, если не нужно определять индекс элемег массива, то лучше применить структуру For Each, обеспечивающую необходим результат с помощью простейшего кода. Так, следующая функция суммирует все элементы массива. Обратите внимание; то, что функция одинаково хорошо обрабатывает как ссылки на область, так и маш. заданный в явном виде: г ' Суммирование элементов массива. Function SumArгay(theArray) Dim Element, theSum theSum = 0 For Each Element In theArray theSum = theSum + Element Next Element SumArray = theSum End Function На рис. 14.1 отображены результаты применения этой функции к массиву знач; ний. Чтобы ввести этот массив, вы записываете в качестве аргументов функции ссылк на область, содержащую массив исходных данных, выбираете эти ссылки, зате нажимаете клавишу Ctrl и, удерживая ее нажатой, нажимаете клавишу =, чтоб заменить ссылки значениями, заданными в явном виде. 200
Глава 14. Применение циклов объектного типа IQ '' FUNCtXMPXLS^ 7 , BI А В С D I Ё I F 2 L . _ 3 Массив I I 4 0.614777 0.88697344 0.462657 S 0.2462875 0.30085242 0.1082881 6 0.2100899 0.01985782 0.1825341 I I 7 I 8 Результат I ; 9 3.0323173 ’=SumArray({0.6147769 68955993,0.886973440647125,0.4 i 6265697479248; 0.246287524700165,0.300852417945862, 0.108288109302521;0.210089921951294,0.019857823848 7244,0.1825340986251831) 10 1.1 I ' ( i I | mN | ► | ►![/ Module4 / Sheet 4 \ Sheet5 / Sheet6 / Sheet7 |*| □ Рис. 14.1. Суммирование элементов массива с помощью функции Sum Array () Примечание: Единственное, чего нельзя сделать с массивом с помощью цикла For Each, — это изменить содержимое элемента. Используя объекты, вы можете получить доступ к свойству Value и изменить его. Когда используются массивы, переменная цикла содержит значение элемента массива, но изменение этого значения не изменяет соответствующий элемент массива. Заключение Вы узнали о реализации циклов объектного типа с помощью структуры For Each. Цикл For Each выполняется по одному разу для каждого объекта набора или каждого элемента массива. Во время каждой итерации переменная цикла содержит объект или значение элемента массива. Данная глава завершает часть V, ознакомившую вас с повторяемыми структурами. В части VI рассматриваются ввод и вывод, использование диалоговых окон и файловой системы. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое цикл объектного типа? 2. Что содержит переменная цикла во время выполнения его итерации, если цикл был применен к набору объектов? 201
Visual Basic for Applications в примерах 3. Можно ли изменить значение элемента массива, используя цикл For Each? Упражнения для закрепления материала 1. Перепишите следующую функцию FirstPos2() из гл. 13, применив цикл Forb ' Поиск первого положительного элемента массива. Function FirstPos2(theArray) As Single Dim J As Integer, Value As Single J = LBound(theArray) -1 'Инициализация J. Do 'Начало цикла. J = J + 1 'Наращивание J. If J > UBound(theArray) Then 'Превышение верхнего предела? Value = CVErr (xlErrValue) 'Установка кода ошибки, если и; Exit Do End If Value = theArray(J) Loop Until Value > 0 FirstPos2 = Value 'ни одного положительного элемен: 'Прерывание цикла. 'Выбор элемента массива. 'Проверка элемента. End Function 2. Напишите функцию, возвращающую количество положительных элементов! массиве, заданном в явном виде или в виде ссылок на ячейки. 3. Напишите функцию, возвращающую среднее квадратичное отклонение всех знаг ний массива, заданного в явном виде или в виде ссылок на ячейки. Используй: следующую формулу: = V п 1 где с —это среднее квадратичное отклонение, п —количество элементов массив или ссылок на область, а х —значение элемента.
Часть VI Ввод и вывод
Глава 15 Применение встроенных диалоговых окон Передача данных программе и получение данных от программы зачастую представ¬ ляют собой непростую задачу. В зависимости от конкретного применения программы создается код, реализующий пользовательский интерфейс, и эта часть программы является едва ли не самой важной. В профессионально разработанных программах объем кода, реализующего пользовательский интерфейс, занимает около 90% общего объема кода, и только 10% используется для обработки данных. Анализируя Excel или любую другую коммерческую программу, вы легко убедитесь в справедливости этого утверждения. Из настоящей части книги вы узнаете о различных способах организации ввода и вывода данных —от диалоговых окон до дисковых файлов. Visual Basic предоставляет возможность применять диалоговые окна для получе¬ ния данных от пользователя и для передачи ему информации. Кроме собственных встроенных диалоговых окон, Visual Basic может использовать все диалоговые окна Excel или Project. Дополнительно предоставляется возможность использовать диало¬ говые окна пользователя. Эта проблема обсуждается в гл. 17. В данной главе описано, как применять: > Встроенные диалоговые окна сообщений > Встроенные диалоговые окна ввода данных > Диалоговые окна Excel Применение функции MsgBoxO Функция MsgBoxO обеспечивает создание диалогового окна, подобного простому окну сообщения. Если окно сообщения всего лишь отображает сообщение для пользо¬ вателя, то диалоговое окно требует от пользователя и некоторых действий по вводу данных. Оно отображает строку текста, а затем ожидает, пока пользователь нажмет командную кнопку. В простейшем случае диалоговое окно содержит одну командную кнопку ОК, которую пользователь должен нажать для подтверждения приема сооб¬ щения и закрытия диалогового окна. Можно расширить возможности такого окна, добавив некоторое количество командных кнопок и придав им некоторое возвращаемое значение, указывающее, какую командную кнопку нажал пользователь. Создание простого окна сообщения Для создания простого окна сообщения применяется зарезервированное слово MsgBox, за ним следует текст, который будет отображен в виде сообщения. При создании простого окна сообщения используется функция в форме оператора, так как любое возвращенное значение будет проигнорировано и нет необходимости в передаче аргументов. 205
Visual Basic for Applications в примерах Если требуется отобразить числа, используются функции Str() или Format! позволяющие преобразовать число в символьное представление. Вы уже встречал с этой формой функций MsgBoxO в процедуре BlockAverage из гл. 12. Фунш MsgBoxO использовалась для отображения результатов вычисления: Option Explicit г ' Процедура определяет среднее арифметическое ' всех ячеек текущего выбора и отображает это ' значение в окне сообщения. г Sub BlockAverage() Dim numRows As Integer, numCols As Integer Dim theRow As Integer, theCol As Integer Dim I As Integer, J As Integer Dim theAverage As Single, theSum As Single . Dim myArray () As Single 'Определение размера текущего выбора. numRows = Selection.Rows.Count numCols = Selection.Columns.Count ReDim myArray(numRows, numCols) 'Копирование содержимого ячеек в массив. For theRow = 1 То numRows For theCol = 1 To numCols myArray(theRow, theCol) = Selection.Cells(theRow, theCol).Value Next theCol Next theRow ' Определение среднего арифметического элементов массива. theSum = О For I = 1 То numRows For J = 1 To numCols theSum = theSum + myArray(I, J) Next J Next I theAverage = theSum / (numRows * numCols) MsgBox ’’Среднее арифметическое = ” & Str (theAverage) End Sub Функция StrO используется для преобразования числа (среднего квадратичной отклонения) в символьное представление. Результат преобразования объединяется ( текстом и отображается функцией MsgBoxO в виде окна сообщения (рис. 12.2). 206
Глава 15. Применение встроенных диалоговых окон Добавление командных кнопок в окно сообщения для создания диалогового окна В окно сообщения можно добавить несколько командных кнопок, что позволит расширить его возможности и преобразовать его в диалоговое окно. Вы можете добавить командные кнопки, использовав необязательные аргументы функции MsgBoxO. Эти дополнительные кнопки позволяют пользователю выбрать направле¬ ние действий или вариант ответа на простой вопрос. Коды этих командных кнопок передаются в качестве второго аргумента функции (первым аргументом является текст, отображаемый в диалоговом окне) или в качестве поименованного аргумента buttons. Список кодов и имен констант, допустимых в Visual Basic, приведен в табл. 15.1. Дополнительные коды позволяют задать коман¬ дную кнопку по умолчанию, указать пиктограмму и объявить диалоговое окно модальным. Таблица 15.1. Коды задания командных кнопок и пиктограмм в функции MsgBoxO Код Константа Описание Коды командных кнопок 0 vbOKOnly OK 1 vbOKCancel OK и Cancel 2 vbAbortRetry Ignore Abort, Retry и Ignore 3 vbYesNoCancel Yes, No и Cancel 4 vbYesNo Yes и No 5 vbRetry Cancel Retry и Cancel Коды активности по умолчанию 0 vbDefaultButton 1 Активная первая 256 vbDefaultButton2 Активная вторая 512 vbDefaultButton3 Активная третья Коды пиктограмм 16 vbCritical Важное сообщение 32 vbQuestion Предупредительный запрос 48 vbExclamation Предупредительное сообщение 64 vbln formation Информационное сообщение Коды модальности окна 0 vbApplicationModal Программное модальное 4096 vbSystemModal Системное модальное 207
Visual Basic for Applications в примерах Чтобы использовать коды, приведенные в табл. 15.1, вначале выберите командную кнопку или кнопки, которые необходимо отобразить. Затем, если вы выбрали более одной командной кнопки, следует определить, которая из них будет активной пе умолчанию. Командная кнопка, активная по умолчанию, автоматически считаете нажатой, если пользователь после отображения диалогового окна нажал клавишу Enter. Коды, приведенные в части "Коды активности по умолчанию” табл. 15.1, позволяют указать, которая из командных кнопок (первая, вторая или третья) будет активной по умолчанию. Командные кнопки помещаются в диалоговое окно в том же порядке, в каком они указаны в части "Коды командных кнопок" табл. 15.1. Затем, если вы хотите добавить в диалоговое окно пиктограмму, укажите соответ ствующий код из части "Коды пиктограмм" табл. 15.1. Кроме того, вы можете решить, будет ли диалоговое окно модальным. Если диалоговое окно не модальное, то пользователь может переключиться на другое окно продолжив таким образом работу с программой без закрытия диалогового окна. Если же диалоговое окно задано как модальное, то пользователь должен закрыть его (завершить с ним работу), прежде чем продолжать работу с данной программой. Существуют два типа модальных диалоговых окон — программные модальные и системные модальные. Пользователь должен закрыть программное модальное диалоговое окно, прежде чем продолжить работу с программой, отобразившей это окно; хотя в этом случае разрешается переключиться на другую программу, не закрыв данное диалоговое окно. Системное модальное Диалоговое окно требует, чтобы пользователь закрыл его, прежде чем делать что-либо еще. Наконец, вы складываете все коды вместе (для всех командных кнопок и опций), а затем используете эту сумму в качестве аргумента функции MsgBoxO для создания диалогового окна. Так, если вы хотите создать диалоговое окно, включающее командные кнопки Yes и No (вторая будет активной по умолчанию), включить в диалоговое окно пиктограмму "Внимание" и сделать это окно программным модальным, то для создания кода необходимо использовать следующий набор встроенных констант: theCode = vbYesNo + vbDefaultButton2 + vbExclamation + _ vbApp1icationModa1 который соответствует 4 + 256 + 48 + 0 = 308 Однако, чтобы сделать код более очевидным, следует использовать поименованные константы, а не числа. Можно использовать, например, любой из следующих операторов MsgBoxO: 208
Глава 15. Применение встроенных диалоговых окон MsgBox prompt:="Вы действительно хотите это сделать?", _ buttons:=theCode MsgBox "Вы действительно хотите это сделать?", theCode Каждый из этих операторов обеспечит создание диалогового окна, изображенного на рис. 15.1. Чтобы создать такое диалоговое окно, запишите любой из приведенных операторов на панели Immediate окна Debug. Рис. 15.1. Диалоговое окно, созданное с помощью кода командных кнопок 308 Получение значений от функции Msg В ох Диалоговое окно, изображенное на рис. 15.1, не очень удобно использовать, если не определить, какую командную кнопку нажал пользователь для его закрытия. А чтобы это определить, нужно использовать функциональную форму MsgBoxO и проверить возвращенное функцией значение. Возможные значения, возвращаемые функцией, и соответствующие им константы Visual Basic приведены в табл. 15.2. Таблица 15.2. Значения, возвращаемые функцией MsgBoxO Код Константа Командная кнопка 1 vbOK OK 2 vb С ап cel Cancel 3 vbAbort Abort 4 vb Retry Retry 5 vb Ignore Ignore 6 vbYes Yes 7 vbNo No Так, чтобы использовать в процедуре диалоговое окно, описанное в предыдущем разделе, можно создать следующую процедуру: 209
Visual Basic for Applications в примерах Sub MakeDialogl() Dim theCode As Integer, theReply As Integer theCode = vbYesNo + vbDefaultButton2 + vbExclamation + _ vbApplicationModal theReply = MsgBox(prompt:="Вы действительно хотите это сделать?", Buttons:=theCode) Select Case theReply Case vbYes 'Он действительно хочет это сделать, придется продолжить. 'Блок кода для ответа Yes. Debug.Print "Да” Case vbNo 'Блок кода для ответа No. Debug.Print "Нет" End Select End Sub Эта процедура вычисляет код командных кнопок и пиктограмм диалогового окна отображает это диалоговое окно и сохраняет возвращенное значение в переменной theReply. Затем применяется структура Select Case, выбирающая блок кода, который соответствует возвращенному значению. Запустите эту процедуру на панели Immediate окна Debug — и она напечатает "Да" или "Нет" в зависимости от того, какук командную кнопку вы нажмете для закрытия диалогового окна. Запись заголовка в диалоговое окно При создании диалогового окна функцией MsgBoxO используется заголовок nt умолчанию "Microsoft Excel". Заголовок —это текст, отображаемый в поле заголовка в верхней части диалогового окна. Чтобы изменить заголовок, необходимо добавить в вызов функции MsgBoxO еще один строковый аргумент, содержащий новый заголовок. Если вы используете форму списка аргументов, то заголовок будет третьим аргументом; в противном случае используйте title:= "текст заголовка”. Например, можно добавить заголовок в созданное диалоговое окно: Sub MakeDialog2() Dim theCode As Integer, theReply As Integer theCode = vbYesNo + vhDefaultButton2 + vbExclamation + _ vbApplicationModal theReply = MsgBox(prompt:="Вы действительно хотите это сделать?", Buttons:=theCode, _ Title:="Относительно той глупости, которую Вы собрались сделать") 210
Глава 15. Применение встроенных диалоговых окон Select Case theReply Case vbYes 'Он действительно хочет это сделать, придется продолжить. 'Блок кода для ответа Yes. Debug.Print "Да” Case vbNo 'Блок кода для ответа No. Debug.Print "Нет" End Select End Sub На рис. 15.2 изображено диалоговое окно, созданное этой процедурой. Относительно той глупости, которую Вы собрались сделать Вы действительно хотите это сделать? Рис. 15.2. Диалоговое окно, созданное функцией MsgBoxO, с заголовком, заданным пользователем Применение функции In put Box Функция InputBoxO создает встроенное диалоговое окно другого типа. Базовое окно ввода содержит окно редактирования текста, командную кнопку ОК и командную кнопку Cancel. Когда вы применяете эту функцию, лцэбой текст, записываемый в текстовое окно диалогового окна, будет возвращен функцией после того, как пользо¬ ватель нажмет командную кнопку ОК. Если же пользователь нажмет командную кнопку Cancel, то будет возвращена пустая строка (""). Аналогично функции MsgBoxO первым аргументом функции InputBoxO будет строка текста, применяемая в качестве запроса на ввод в диалоговом окне. Функция InputBoxO не использует аргумент buttons] таким образом, вторым аргументом является заголовок. Третий аргумент, default, содержит любой текст по умолчанию, который вы можете отобразить в окне редактирования текста при открытии диалого¬ вого окна. Все аргументы должны быть строками текста, и значение, возвращаемое функции, также является строкой текста. Если вы предполагаете, что функция возвратит число, то ваша процедура должна использовать функцию ValO для преобразования числа, возвращенного функцией InputBoxO, из символьного пред¬ ставления в двоичное, пригодное для использования в программе. 211
Visual Basic for Applications в примерах i ! i Так, следующая процедура отображает два диалоговых окна: первое для ввог имени пользователя, а второе —для ввода возраста пользователя; затем вычисляет!: отображает возраст пользователя в днях: ' Расчет возраста в днях на основании возраста в годах. г Sub AgeCalculator() Dim theReply As String, thePrompt As String Dim theTitle As String, theDefault As String Dim theAge As Single, OKFlag As Boolean Dim theName As String thePrompt = "Введите Ваше имя, пожалуйста." theTitle = "Персональный информационный диалог" theDefault = "Имя" 'Цикл ожидания ввода имени пользователя. Do theReply = InputBox(thePrompt, theTitle, theDefault) If theReply = "" Then Exit Sub 'Нажата командная кнопка Cancel. theReply = Trim(theReply) 'Удаление пробелов с двух сторон строки. 'Проверка на строку пробелов или пробел в строке. If (theReply = "") Or (InStr(theReply, " ") <> 0) Then MsgBox "Непонятно, попробуйте еще раз, пожалуйста.", , theTitle OKFlag = False Elself theReply = theDefault Then 'Пользователь просто нажал Enter. MsgBox "Напечатайте что-нибудь и попробуйте еще раз, пожалуйста." OKFlag = False Else 'Ввод завершен нормально. theName = theReply OKFlag = True End If Loop Until OKFlag 'Теперь получим имя пользователя. thePrompt = "Здравствуйте, " & theReply & ". Введите Ваш возраст, пожалуйста." 'Цикл ожидания ввода корректного числа. Do theReply = InputBox(thePrompt, theTitle) If theReply = "" Then Exit Sub 'Нажата командная кнопка Cancel. theAge = Vai(theReply) 'Преобразование строки в число. 212
Глава 15. Применение встроенных диалоговых окон If Not IsNumeric(theReply) Then 'Введено не число. MsgBox "Введите число и попробуйте еще раз, пожалуйста.’’, , theTitle OKFlag = False 'Проверка корректности введенного числа. Elself (theAge < 1) Or (theAge > 120) Then MsgBox- "He верится, чтобы Вам было ’’ & Str(theAge) & _ " лет. Попробуйте еще раз, пожалуйста.’’, , theTitle OKFlag = False Else 'Похоже на возраст. OKFlag = True End If Loop Until OKFlag 'Расчет приблизительного возраста в днях. MsgBox "Вам приблизительно " & Format (theAge * 365, "#,###’’) & " дней.", _ , theTitle End Sub Эта процедура состоит из двух частей. Первая часть обеспечивает ввод имени пользователя, а вторая —ввод его возраста и расчет приблизительного возраста в днях. Вначале процедура отображает диалоговое окно, изображенное на рис. 15.3. Это окно запрашивает имя пользователя. После того как функция InputBoxO возвратит значение, процедура проверяет это возвращенное значение на пустую строку Пустая строка означает, что пользователь нажал командную кнопку Cancel. В этом случае оператор Exit Sub прекращает выполнение процедуры. i Wг Персоналы1ый информационный' диалог Введите Вайк» имя. пожалуйста. | | Cancel | |Вася | Ряс. 15.3. Диалоговое окно, созданное функцией InputBoxO, запрашивает имя пользователя Иногда пользователи записывают в диалоговое окно ошибочные данные и, следо¬ вательно, необходимо контролировать правильность вводимых данных. В данном случае процедура пытается использовать значения, возвращенные из диалогового окна, поэтому нужно контролировать эти значения с точки зрения их обоснованности и правильности типа (строка или число). Является ли значение обоснованным, зависит 213
Visited Basic for Applications в примерах от того,, что вы пытаетесь получить от пользователя. Если число —то вам необходг проверять его величину, чтобы определить, попадает ли она в допустимый диапаз чисел. Если это строка —нужно проверять, сколько символов или слов она содерж Другими словами, то, что вы проверяете, зависит от типа получаемой информации Хорошим решением в целях создания структуры, осуществляющей контроль, яо ся объединение цикла DO-Loop с блочным оператором If. Цикл использует флаг ОКЕ указывающий на то, что пользователь будет повторять ввод данных. Блочный опера: If проверяет, является ли строка, возвращенная функцией InputBoxO, допустимой.Ь нет, то оператор If присваивает флагу OKFlag значение False, чтобы цикл повтори] еще раз. Если строка является допустимой, то OKFlag устанавливается в True, щ прерывается, а возвращенное значение передается для дальнейшей обработки. Количество проверок в данной структуре можно легко увеличить, добавив допе нительные блоки Elself. Обычно вы пишите отрицательные логические проверки считаете, что значение соответствует требованиям программы, если оно прошлоз проверки. Если же использовать положительную логику, то первая проверка, возв: тившая True, завершит весь блок контроля. В процедуре AgeCalculatorO блочный оператор If вначале проверяет, равно возвращенное значение строке пробелов или содержит ли оно пробел в строке. Проб в строке указывает на то, что пользователь ввел более чем одно слово, что невозмож в случае ввода имени. Затем производится проверка на строку по умолчанию, котор указывает на то, что пользователь просто нажал клавишу Enter, не записав ничеп поле редактирования текста. Если имела место любая из этих ситуаций, то появш одно из диалоговых окон, изображенных на рис. 15.4. Персональный информационный диалог Непонятно, попробуйте ещё раз, пожалуйста. Microsoft Excel Напечатайте что-нибудь и попробуйте еще раз, пожалуйста. Рис. 15.4. Процедура AgeCalculatorO отобразит одно из этих диалоговых окон, если обнаружит противоречия в имени, возвращенном функцией InputBoxO 214
Глава 15. Применение встроенных диалоговых окон Вторая часть процедуры вставляет имя, полученное из первой ее части в запрос на ввод в диалоговом окне, изображенном на рис. 15.5. Затем проверяется введенная пользователем строка. Если она пустая, значит пользователь нажал командную кнопку Cancel; если нет, то строка преобразовывается в число и это число проверяется на обоснованность величины. Для таких проверок применяются цикл Do-Loop и блочная структура If —подобно тому, как это делалось в первой части процедуры. Здравствуйте, Вася. Введите Ваш возраст, пожалуйста. | Cancel j Рис. 15.5. Диалоговое окно, созданное функцией InputBoxO, запрашивает возраст пользователя Эта структура объединяет две проверки. Первая проверка призвана определить отсутствие числовых данных, которое означает ввод текста вместо числа. Второй проверкой определяется, попало ли введенное число в заданный интервал. Если число больше 120 или меньше 1, то возраст считается недопустимым. Если процедура обнаружит ошибку, то отобразит одно из диалоговых окон, приведенных на рис. 15.6. Если же введенное число попало в интервал допустимых значений, то вычисляется возраст пользователя в днях, результат преобразовывается в символьный формат и отображается в диалоговом окне, приведенном на рис. 15.7. Персональный информационный диалог Введите число и попробуйте еще раз. пожалуйста. Персональный информационный диалог Не верится, чтобы Вам было 400 лет. Попробуйте еще раз, пожалуйста. Рис. 15.6. Процедура AgeCalculatorO отобразит одно из этих диалоговых окон, если обнаружит ошибку в возрасте пользователя, возвращенном функцией InputBoxO 215
Visual Basic for Applications в примерах Персональный информационный диалог || Ван приблизительно 3,650 дней. Рис. 15.7. Диалоговое окно, отображающее рассчитанный возраст пользователя в днях Применение диалоговых окон других типов В Visual Basic существуют еще два типа встроенных диалоговых окон: GetOpen-, Filename и GetSaveAsFilename. Эти окна не открывают файл и не сохраняют его. Оне только позволяют указать его имя и путь. Команды, создающие такие диалоговые окна, являются не операторами Visual Basic, а методами объекта Application. Операторы, создающие эти два типа диалоговых окон, имеют следующий синтаксис theFilename = Application.GetOpenFilename(файловый_фильтр, _ j индекс_фильтра, заголовок) ? theFilename = Application. SaveAsFilename (начальное_имя_файла f _ ; файловый_фильтр, ■ индекС—фильтра, заголовок) Оба метода применяются к объекту Application и возвращают имя файла и путь.| выбранные пользователем. Диалоговые окна, аналогичные этим, отображаются, когда; используются директивы Excel File/Open и File/Save As. В этих диалоговых окна\ имеются командные кнопки Open или Save и Cancel. Если пользователь нажме: командную кнопку Cancel, методы возвратят значение False. Аргумент файловый—фильтр определяет, какие файловые фильтры перечисляются в выпадающем списке File Туре диалогового окна. В списке файлов диалогового окна появляются только те файлы, которые совпали с выбранным файловым фильтром Файловый фильтр состоит из двух частей —текста и фильтра. Текст отображается в выпадающем списке File Туре, а фильтр используется для выбора файлов из списка файлов. В качестве примера рассмотрим аргумент: Все файлы (*.*), *.* Текст "Все файлы (*.*)" отобразится в выпадающем списке File Туре, а является действительным фильтром, выбирающим и отображающим список всех файлов текущей директории. 216
Глава 15. Применение встроенных диалоговых окон Рассмотрим другой пример файлового фильтра: Все файлы (*.*), * . *, Рабочие папки Excel (*.XLS), *.XLS Этот фильтр отображает два элемента в выпадающем списке File Туре: ’’Все файлы (*.*)” и ’’Рабочие папки Excel (*.XLS)”. Аргумент индекс—фильтра является целым числом, задающим в выпадающем списке File Туре файловый фильтр по умолчанию. Аргумент заголовок аналогичен используемому для функции InputBoxO. Аргумент начальное_имя_файла определяет начальное, заданное по умолчанию имя файла, которое помещают в окно редактиро¬ вания текста. Вызов диалоговых окон приложения Программы на Visual Basic могут вызывать все диалоговые окна, существующие в Excel. Однако эти диалоговые окна действуют не так, как описанные выше и возвращающие значения программе на Visual Basic без изменений какой-либо таблицы или ячейки. Если вы вызываете диалоговое окно Excel, то оно не возвращает значения программе на Visual Basic, а осуществляет свои нормальные функции. Например, если выполнить действия, заданные в диалоговом окне Format Number, для некоторых выбранных ячеек таблицы, то эти действия будут применены к этим ячейкам и не возвратят никакого значения программе на Visual Basic. Предупреждение: В связи с тем, что диалоговые окна приложения применяются к текущему отображаемому объекту, будьте осторожны и не отображайте диалоговое окно вне контекста: так как попытка будет неудачной и приведет к ошибке выполнения. Попытайтесь, например, отобразить диалоговое окно Format Number на активной странице модуля и получите ошибку. Для вызова диалогового окна приложения пользуйтесь набором Dialogs' объекта Application. Набор Dialogs получает в качестве аргумента число-индекс, выбирающее отображаемое диалоговое окно. Все индексы соответствуют определенным константам Excel, именующим выбранное диалоговое окно. Все эти константы начинаются с xlDialog, затем следует имя диалогового окна. Например, константой для диалогового окна Format Number является xlDialogFormatNumber. Для поиска доступных диало¬ говых окон и констант воспользуйтесь Object Browser, выбрав объект Constants библиотеки Excel (нужные константы начинаются с xlDialog). 217
Visual Basic for Applications в примерах Следующая процедура отображает диалоговое окно FormatNumber: ' Проверка диалога 2 ' Отображает диалоговое окно Format Number. г Sub TestDialog2() Application.Dialogs(xlDialogFormatNumber). Show End Sub Чтобы выполнить эту процедуру, запишите ее на странице модуля, переключитесь на таблицу, выберите ячейку, укажите директиву Tools/Macro, выберите процедуру TestDialog2, затем нажмите командную кнопку Run. В результате выполнения этой процедуры отобразится диалоговое окно, приведенное на рис. 15.8. Если вы забудете выбрать ячейку таблицы перед запуском процедуры, попытка отображения диалого¬ вого окна приведет к ошибке. Рис. 15.8. Диалоговое окно Excel отображается с помощью метода Show 218
Глава 15. Применение встроенных диалоговых окон Заключение Диалоговые окна являются основным средством организации обмена небольшими объемами данных между пользователем и выполняющейся программой. Две функции MsgBoxO и InputBoxO создают встроенные диалоговые окна, позволяющие управ¬ лять большинством простейших обменов данными. Методы GetOpenFilenameO и GetSaveAsFilename() создают диалоговые окна, позволяющие задать имя файла, подлежащего открытию или сохранению. Кроме того, допустимыми являются диало¬ говые окна пользователя, описываемые в гл. 17 "Создание диалоговых окон пользо¬ вателя". Кроме встроенных диалоговых окон Visual Basic, можно отобразить любое из аналоговых окон программы Excel для осуществления их нормальных функций. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Какое значение возвратит функция MsgBoxO, если нажать командную кнопку ОК? 2. Какое кодовое число нужно использовать в качестве аргумента, чтобы добавить командные кнопки Yes и No в диалоговое окно, создаваемое функцией MsgBoxO? 3. Как отобразить число с помощью функции MsgBoxO? I Как изменить заголовок диалогового окна? J. Объясните, что такое модальное диалоговое окно. (. Объясните разницу между системным и программным модальными диалоговыми окнами. Упражнения для закрепления материала I. Напишите список констант, которые необходимо суммировать, чтобы включить в диалоговое окно, создаваемое функцией MsgBoxO, командные кнопки Abort, Retry и Ignore, указать командную кнопку Retry в качестве активной по умолчанию и отобразить пиктограмму Critical Message. Затем напишите неболь¬ шую процедуру, отображающее это диалоговое окно, с некоторым произвольным текстом. 1. Напишите процедуру, отображающую диалоговое окно с тремя командными кнопками и осуществляющую переход на три различные подпрограммы в 219
Visual Basic for Applications в примерах •зависимости от того, какую командную кнопку вы нажмете. В каждой из л подпрограмм отобразите еще одно диалоговое окно, сообщающее какую кнопку; нажали. 3. Напишите процедуру, использующую функцию InputBoxO для получения пользователя целого числа в интервале от 1 до 10. Проверьте это число ста зрения попадания в интервал и предложите пользователю повторить ввод, ес оно некорректно. Окончанием процедуры будет нажатие пользователем команде кнопки Cancel. 4. Напишите процедуру, использующую метод GetOpenFilenameO и отображаю^ строку, возвращенную этим методом. Попробуйте метод для различных файл: чтобы увидеть, что он возвращает в каждом конкретном случае. Работает ли он другому, если выбранный файл находится в текущей директории? 220
Глава 16 Применение рабочих таблиц Основным средством ввода и вывода данных в Excel является таблица. Таблицу удобно применять также в качестве бланка данных для программ на Visual Basic. Кроме того, в качестве бланков данных можно использовать таблицы Project, однако в связи с тем, что эти таблицы более специфичны, чем таблицы Excel, они не столь легко адаптируются к программам на Visual Basic. С другой стороны, одной из основных целей проектирования таблиц Excel было как можно лучше адаптировать их к применению в качестве бланков данных. Из этой главы вы узнаете, как делать следующее: > Применять таблицу Excel в качестве внешнего интерфейса программ на Visual Basic > Определять в таблице области ввода > Помещать в таблицу командные кнопки и другие объекты Определение областей ввода Первым шагом применения таблиц Excel в качестве бланка данных является определение необходимого количества областей ввода с последующим определением их в таблице. При определении областей ввода необходимо учитывать, какие из них будут изменяться пользователем, а какие — нет. Выполняемая программа получает доступ к ячейкам данных способами, описанными ранее. В качестве примера попробуйте разработать внешний интерфейс базы данных персональных контактов. База данных содержит информацию о персональных контак¬ тах, включая имя, адрес и другую необходимую информацию (семейное положение, симпатии и антипатии — т.е. то, что поможет вам при следующем контакте с этим человеком). В зависимости от рода ваших занятий и того времени, которое вы хотите потратить на разработку и сопровождение своей информации, вы включаете в базу данных соответствующее количество информационных блоков о своих персональных контак¬ тах. Перед началом работы обычно проводится небольшой анализ соотношения стоимости хранения и сопровождения базы данных и выгоды от использования этих данных. В этом примере по каждому контакту сохраняются имя, адрес, телефон и e-mail. Дополнительно следует включить два поля — ’’Сослаться на" и "Примечание" - для хранения ссылки на источник и подробной и различной дополнительной информации. Кроме того, можно добавить любые другие поля. 221
Visual Basic for Applications в примерах Чтобы создать бланк данных, выполните следующие действия: 1. Создайте новую рабочую папку PCD.XLS, затем переключитесь на пустую табля и переименуйте ее в "КонтактыВВ”, использовав директиву Format/Sheet/Renan 2. Используя рис. 16.1 в качестве образца, запишите заголовки, заключите ячейки рамку и отрегулируйте ширину колонок и строк бланка данных. 3. Выберите ячейки В4.С14, укажите директиву Format/Cells, выберите корепк Alignment, выберите опцию Left, затем нажмите командную кнопку ОК. 4. Выберите ячейку ВИ, укажите директиву Format/Cells, выберите корепк Alignment, выберите опцию Right, затем нажмите командную кнопку ОК. 5. Назовите ячейки в соответствии с приведенной ниже таблицей. Для этого выбери ячейки В4:С14 и укажите директиву Insert/Names/Create. Затем для изменениями ячеек, не соответствующих таблице, укажите директиву Insert/Names/Define. Ячейка Имя С4 Имя С5 Адрес С6 Город С7 Страна С8 Индекс С9 Телефон СЮ Е_ Mai/Сеть СИ E_Mai/Адрес С12 Ссылка С13 Примечание С14 НомЗап 6. Выберите ячейки С4:С13, укажите директиву Format/Cells, выберите кореша Protection, отключите контрольный индикатор Locked, затем нажмите команда)! кнопку ОК. 7. Выберите ячейки В1:С1, укажите директиву Format/Cells, выберите кореша Alignment, выберите опцию Center Across Selection, затем нажмите команда)! кнопку ОК. 8. Выберите ячейку С13, укажите директиву Format/Cells, включите контрольна индикатор Wrap Text, затем нажмите командную кнопку ОК. 222
Глава 16. Применение рабочих таблиц Таким образом, вы создали часть табличного бланка, предназначенного для ввода данных. Позже вы отключите шапки строк и колонок и защитите таблицу, чтобы пользователь мог изменять только те ячейки, которые разблокированы в п. 6. I D|^|B| |х|^|свИ FH ЕЁИ1 [Щ0h°°* IS РВД |А^ся im ШЕЕ ЕЕ® EES® |s|%l, WI Е@ Е0 , L13 JH | А В I с 1 База данных персональных контактов 2 I 3 Информация о контакте 4 Имя: 5 Адрес: б Город: 7 Страна: 8 Индекс: 0 Телефон: 10 E-Mail: Сеть: 11 Адрес: 12 Сослаться на: 13 Примечание: 14 Запись № 141 4| ►I IHi^lCoHTaKTbiBB/ КонтзктыБД / Sheet3 / Sheet 4 / She Ready D I Ряс. 16. /. Макет табличного бланка данных для базы данных персональных контактов Следующим шагом будет получение данных из формы и их сохранение. Сущес¬ твует несколько возможностей сохранить данные: во внутреннем массиве данных, в базе данных Excel в другой таблице, во внешнем файле данных или в файле внешней базы данных. Различные варианты хранения предусматривают различные соотноше¬ ния между скоростью доступа и размером базы данных. Если данные сохраняются в памяти, т.е. во внутреннем массиве или в другой странице, то доступ к этим данным потребует значительно меньше времени, чем если бы вы сохраняли их в дисковом файле. С другой стороны, в памяти можно сохранить значительно меньше данных, тем в дисковом файле. В этой первой версии базы данных персональных контактов мы используем внутренний массив данных. В последующих главах рассмотрены другие варианты сохранения данных. 223
Visual Basic for Applications в примерах В качестве примера, чтобы получить данные из таблицы "КонтактыВВ", ад модуль, именуемый "КонтактыБД" и содержащий следующую программу: ' Программа работы с базой данных персональных контактов. ' Версия для табличного бланка. Option Explicit Option Base 1 Type DBEntry 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 1. 'Определяет структуру записи базы данных. Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type 'Размер типа 256 байтов. Dim theDB() As DBEntry Dim numEntries As Integer Dim theEntryNum As Integer 'Массив базы данных. 'Общее количество записей. 'Текущая отображаемая запись ' Инициализация базы данных. Sub Initializelt () numEntries = 0 ReDim theDB(1) theEntryNum = 0 End Sub ' Процедура Auto_open, вызываемая при каждом открытии ' рабочей папки. г Sub Auto_Open() Initializelt 'Вызов процедуры инициализации. End Sub 224
Глава 16. Применение рабочим таблиц ' Пересылка записи в базу данных. Sub EntryToDB(anEntry As Integer) With Sheets('’КонтактыВВ") theDB(anEntry).Name = .Range("Имя”).Value theDB(anEntry).Address = . Range (’’Адрес" ) .Value theDB(anEntry).City = .Range("Город").Value theDB(anEntry).State = .Range("Страна").Value theDB(anEntry).Zip = .Range("Индекс").Value theDB(anEntry).Phone = .Range("Телефон").Value theDB(anEntry).Net = .Range("E_Mail_CeTb").Value theDB (anEntry).NetAddr = .Range ("Е_МаИ_Адрес") .Value theDB(anEntry).Referral = .Range("Ссылка").Value theDB(anEntry).Notes = .Range("Примечание").Value theDB(anEntry).RecNo = anEntry End With End Sub ' Пересылка записи в бланк. Sub DBToEntry(anEntry As Integer) With Sheets("КонтактыВВ") .Range("Имя").Value = theDB(anEntry).Name .Range("Адрес").Value = theDB(anEntry).Address .Range("Город").Value = theDB(anEntry).City .Range("Страна").Value = theDB(anEntry).State .Range("Индекс").Value = theDB(anEntry).Zip .Range("Телефон").Value = theDB(anEntry).Phone .Range("E_Mail_CeTb").Value = theDB(anEntry).Net .Range("E—Mail—Адрес").Value = theDB(anEntry).NetAddr .Range("Ссылка").Value = theDB(anEntry).Referral .Range("Примечание").Value = theDB(anEntry).Notes .Unprotect .Range("НомЗап").Formula = theDB(anEntry).RecNo .Protect End With End Sub i ' Добавляет новую запись. 8Вильям Дж. Орвис 225
Visual Basic for Applications в примерах Sub NewEntry_Click() numEntries = numEntries + 1 theEntryNum = numEntries ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub ! ' Изменяет запись. Sub UpdateEntry_Click() If theEntryNum = 0 Then Exit Sub 'Если еще нет записей, то выход. EntryToDB theEntryNum 'Изменение текущей записи. End Sub г ' Продвижение на одну запись вперед. t Sub Forward_Click() If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = theEntryNum + 1 'Проверка номера записи. If theEntryNum > numEntries Then theEntryNum = numEntries DBToEntry theEntryNum End Sub ! ' Продвижение на одну запись назад. г Sub Backward_Click() If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = theEntryNum — 1 'Проверка номера записи. If theEntryNum < 1 Then theEntryNum = 1 DBToEntry theEntryNum End Sub t 'Удаляет запись. Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer 226
Глава 16. Применение рабочих таблиц Dim theMsg As String If numEntries = 0 Then Exit Sub 'Еще нет-записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries theDB (I — 1) = theDB (I) theDB (I — 1) . RecNo = 1—1 Next I numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) End If DBToEntry theEntryNum End Sub Вначале определим некоторые переменные, включая массив для хранения данных о контактах: ' Программа работы с базой данных персональных контактов. ' Версия для табличного бланка. Option Explicit Option Base 1 Type DBEntry 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 'Определяет структуру записи базы данных. 1. Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 227
’ NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type 'Размер типа 256 байтов. Dim theDBO As DBEntry Dim numEntries As Integer Dim theEntryNum As Integer 'Массив базы данных. 'Общее количество записей. 'Текущая отображаемая запись После операторов Option Explicit и Option Base 1, расположенных в верхней чая модуля, следует определение пользовательского типа данных DBEntry, котор описывает формат хранения данных, полученных из таблицы. Все строки определи как строки фиксированной длины. Хотя в первой версии базы данных не нуж использовать строки фиксированной длины, это потребуется в следующих версия Для внутренних массивов применение строк фиксированной длины значительно мен эффективно, чем строк переменной длины. Действительно, строки фиксированн! длины требуют больше памяти, чем строки переменной длины, так как должв сохранять все пустые, неиспользуемые пробелы в строке. Общий размер пользой тельского типа данных составляет 256 байтов —особое число, оно объясняется в г 19 ’’Запись данных в дисковый файл произвольного доступа". Массив базы данных theDBO объявляется как динамический с элементами та DBEntry. Объявив массив динамическим, без списка индексов элементов, вы преди лагаете, что укажете его размерность позже — когда вам потребуется памятью хранения этого массива. После объявления массива следует объявление друп переменных. Объявив эти переменные в верхней части модуля, вы сделали я доступными для всех процедур модуля. Первая процедура —инициализация базы данных, задающая начальные значеня глобальным переменным и массиву: ' Инициализация базы данных. Sub Initialized () numEntries = О ReDim theDB(l) theEntryNum = О End Sub Эта процедура устанавливает все переменные в 0. Процедура инициализащ запускается всего один раз — при запуске программы работы с базой данньп Запускать эту процедуру можно вручную, используя директиву Tools/Marco кад 228
Глава 16, Применение рабочих таблиц раз, когда вы запускаете программу работы с базой данных, однако можно случайно забыть это сделать. Чтобы застраховать себя от такой ситуации, создайте макрос, который будет запускаться автоматически при открытии рабочей папки: ' Процедура Auto_open, вызываемая при каждом открытии ' рабочей папки. Sub Auto_Open () InitializeIt 'Вызов процедуры инициализации. End Sub Любая процедура с именем Auto_Open автоматически выполняется при открытии содержащей ее рабочей папки. Эта процедура Auto_Open просто запускает процедуру инициализации. Затем вам потребуются две процедуры для обмена данными между таблицей и массивом. Эти две процедуры применяются не пользователем, — они вызываются из других процедур, которым нужно переслать данные. ' Пересылка записи в базу данных. Sub EntryToDB (anEntry As Integer) With Sheets("КонтактыВВ”) theDB(anEntry).Name = .Range("Имя”).Value theDB(anEntry).Address = .Range("Адрес").Value theDB(anEntry).City = .Range("Город”).Value theDB(anEntry).State = .Range("Страна”).Value theDB(anEntry).Zip = .Range("Индекс").Value theDB(anEntry).Phone = .Range("Телефон").Value theDB(anEntry).Net = .Range("E_Mail_CeTb").Value theDB (anEntry) .NetAddr = . Range ("Е_МаИ_Адрес" ) .Value theDB(anEntry).Referral = .Range("Ссылка").Value theDB(anEntry).Notes = .Range("Примечание").Value theDB(anEntry).RecNo = anEntry End With End Sub ' Пересылка записи в бланк. Sub DBToEntry (anEntry As Integer) 229
Visual Basic for Applications в примерах With Sheets("КонтактыВВ") .Range("Имя").Value = theDB(anEntry).Name .Range("Адрес").Value = theDB(anEntry).Address .Range("Город").Value = theDB(anEntry).City .Range("Страна").Value = theDB(anEntry).State .Range("Индекс").Value = theDB(anEntry).Zip .Range("Телефон").Value = theDB(anEntry).Phone .Range("E_Mail_CeTb").Value = theDB(anEntry).Net . Range ( "Е_МаИ_Адрес" ) .Value = theDB (anEntry) .NetAddr .Range("Ссылка").Value = theDB(anEntry).Referral .Range("Примечание").Value = theDB(anEntry).Notes .Unprotect .Range("НомЗап").Formula = theDB(anEntry).RecNo .Protect End With End Sub Первая процедура EntryToDB перемещает вводимые данные из табличного бланк в массив. Оператор With задает имя таблицы, содержащей исходные данные. Проце дура пересылает эти данные в элемент anEntry глобального массива theDB(). Втора процедура, DBToEntry, выполняет обратную функцию. Переменная RecNo содержит номер записи в массиве. Этот номер определяется! процедуре, а не задается пользователем. Поскольку вы защитили бланк даннк включая этот элемент, процедура должна использовать команду Unprotect, изменив значение RecNo (ячейка С14), а затем использовать команду Protect для повторна защиты таблицы. Размещение командных кнопок в таблице Следующим шагом является добавление в таблицу командных кнопок и подклк чение их к процедурам, обеспечивающим функционирование программы. Применяв командные кнопки, подключенные к процедурам, намного удобнее, чем использовав для запуска процедуры директивы Tools/Macro. Чтобы подключить командную кнопку, укажите пиктограмму Drawing,для отоб ражения пиктографического меню Drawing, затем укажите пиктограмму Button! изобразите в таблице командную кнопку. После того как вы окончите изображав командную кнопку, автоматически появится диалоговое окно Assing Macro. Эп диалоговое окно применяется для выбора процедуры, подключаемой к командно! кнопке, или для создания заголовка новой процедуры. 230
Глава 16. Применение рабочих таблиц Примечание: Обычно процедуры обработки событий называют по имени события, которое они обрабатывают. Так, для реакции на нажатие командной кнопки имя_кнопки запускается процедура с именем имя_кнопки_ Click. Теперь добавим процедуру, копирующую новые записи в массив данных. Эта процедура создает новую запись в массиве, где содержатся данные, отображаемые в таблице: ' Добавляет новую запись. Sub NewEntry_Click () numEntries = numEntries + 1 theEntryNum = numEntries ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub Процедура NewEntry_Click добавляет единицу к количеству записей, устанавли¬ вает номер текущей записи равным номеру новой записи и переопределяет размер массива данных с учетом новой записи. Затем она копирует данные из таблицы в массив (в новую запись), после чего копирует новую запись назад в таблицу, чтобы убедиться, что сохранение данных в массиве прошло успешно, и чтобы увеличить номер записи в таблице. Вторая процедура, UpdateEntry_Click, проверяет номер текущей записи, и если эта запись не первая, то просто переписывает данные из таблицы в текущую запись. Чтобы подключить указанные процедуры к командным кнопкам в таблице, необходимо выполнить следующие действия: 1. Переключитесь в таблицу. 2. Отобразите пиктографическое меню Drawing, для чего укажите директиву View/Toolbars, включите контрольный индикатор Drawing и нажмите командную кнопку ОК. В качестве альтернативы можно указать пиктограмму Drawing пиктографического меню Standard. 3. Используя рис. 16.2 в качестве образца, укажите пиктограмму Button и изобразите в таблице командную кнопку "Записать". 4. Когда появится диалоговое окно Assing Macro, выберите процедуру New- Entry_Click и нажмите командную кнопку ОК. 231
Visual Basic for Applications в примерах 5. Выберите заголовок командной кнопки и измените его на "Записать”. 6. Тем же способом (см. п. 3 — 5) создайте командную кнопку ’’Изменить' подключите ее к процедуре UpdateEntry_Click и переименуйте эту команда)! кнопку в ’’Изменить". Р MicrosoftExcel-PCT.XLS file Edit View Insert Format Jools Qata Window Help Idi^Ihi | хрюкай 03 |s|z.| Mill |^|1дИ E18 А В I С D Е F G 7 1 База данных персональных контактов 2 I 3 Информация о контакте Записать ] 4 Имя: ! S Адрес: Изменить I 6 Город: k Z i 7 Страна: « и 8 Индекс: ‘ i 9 Телефон: Удалить 1 10 E-Mail: Сеть: 11 Адрес 12 Сослаться на: Примечание: №1 0 с 0 io !<=» I к I In»-. Im Iга,"Irx.I >-*l 1 г-l 1 l*__l. 1 14 Запись № Iй 1 R ||‘Qj|4]|iQ|ln|Li||LI||Y]|±| ШТЕ КонгактыВВ /КонгактыБД / Sheet2 / Sheet3 Ready Рис. 16.2. Макет командных кнопок в таблице для базы данных персональных контактов Примечание: Выбрав командную кнопку, вы можете использовать мышь для изменения раз® ров и формы этой кнопки, выбора и изменения текста ее заголовка. Если нажал командную кнопку, не выбрав ее, то вместо выбора кнопки выполнится подклк ченная к ней процедура. Чтобы выбрать командную кнопку или другой объем подключенный к процедуре, без выполнения этой процедуры, нужно использовал средство выбора, инициируемое нажатием пиктограммы Select пиктографическое меню Drawing. Альтернативным способом выбора является следующий: нажал клавишу Ctrl и, удерживая ее нажатой, выбрать командную кнопку. 232
Глава 16. Применение рабочих таблиц Теперь программа позволяет добавлять и изменять данные в вашей базе данных, но у вас нет возможности передвигаться от одной записи к другой. Чтобы добиться возможности передвигаться вперед и назад относительно текущей записи, необходимо добавить еще две процедуры: I ' Продвижение на одну запись вперед. Sub Forward_Click () If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = theEntryNum + 1 'Проверка номера записи. If theEntryNum > numEntries Then theEntryNum = numEntries DBToSntry theEntryNum End Sub ' Продвижение на одну запись назад. Sub Backward—Click () If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = theEntryNum — 1 'Проверка номера записи. If theEntryNum < 1 Then theEntryNum = 1 DBToEntry theEntryNum End Sub Эти процедуры наращивают или уменьшают на 1 переменную theEntryNum и проверяют ее на выход за пределы интервала. Затем они отображают выбранную запись. Следующая процедура удаляет запись: 'Удаляет запись. Sub DeleteEntry_Click () Dim I As Integer, buttons As Integer Dim theMsg As String If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = "Вы действительно хотите удалить текущую запись?" 233
Visual Basic for Applications в примерах If MsgBox (theMsg, buttons, ’’Удаление записи") = vhNo Then Exit Sub 'Пользователь подтвердил удаление. If.theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries - 1 ReDim Preserve theDB(numEntries) theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries theDB (I - 1) = theDB(I) theDB(I - 1).RecNo =1-1 Next I numEntries = numEntries - 1 ReDim Preserve theDB(numEntries) End if DBToEntry theEntryNum End Sub Процедура удаления записи намного сложнее, чем другие процедуры: она должк не только удалять запись, но и перемещать вверх записи, расположенные после удаленной, чтобы ликвидировать образованный в результате этого удаления зазор. Вначале процедура отображает диалоговое окно, запрашивающее, подтверждает л пользователь свое намерение удалить текущую запись. Если пользователь ответит No, к процедура немедленно прервется. Процедура имеет три главные ветви. Выбор одной и: этих ветвей, управляющих процессом удаления записи, зависит от того, какую запись: массиве следует удалить. Если удалению подлежит последняя запись, то выбираете первая ветвь. Последняя запись просто удаляется, и размер массива уменьшается на одну запись. Если в массиве нет ни одной записи (т.е. пользователь нажал эту командную кнопку по ошибке), то используется вторая ветвь, содержащая только оператор выход: из процедуры. Последняя ветвь используется в том случае, когда запись, подлежащая удалению, является не последней записью массива. Вначале все записи после удаляемо?1 перемещаются вверх на одну запись, затем размер массива уменьшается на одну запись Добавьте еще три командных кнопки к таблице (см. рис. 16.2) и подключите их к трем новым процедурам, как описано выше. Блокирование таблицы Следующий шаг обеспечивает очистку макета табличного бланка данных и блоки¬ рование таблицы с тем, чтобы предотвратить попытки пользователя записать данные вне полей бланка. 234
Глава 16. Применение рабочих таблиц Чтобы очистить и защитить бланк данных, необходимо выполнить следующие действия: 1. Выберите корешок "Контакты В В", чтобы отобразить табличный бланк данных. 2. Укажите директиву Tools/Options, выберите корешок View, отключите контроль¬ ные индикаторы Gridlines, Row & Column Headers, Horizontal Scroll Bar, Vertical Scroll Bar и Sheet Tabs, затем нажмите командную кнопку OK. 3. Отрегулируйте ширину и высоту окна с учетом рационального размещения в нем бланка и командных кнопок. 4. Укажите директиву Tools/Protection/Protect Sheet, в появившемся диалоговом окне нажмите командную кнопку ОК. Таблица приобретает вид, изображенный на рис. 16.3, но без каких-либо данных в бланке. Вы можете сразу попробовать работу программы, записав некоторые данные в бланк и нажав командную кнопку "Записать”, которая инициирует запись этих данных в массив. Не создавайте большого количества записей, —эта версия програм¬ мы еще не имеет возможностей сохранить эти данные. Попробуйте нажимать две командные кнопки —стрелки для перемещения от одной записи к другой. Попробуйте также изменить запись и проверьте работу командной кнопки "Изменить", затем удалите запись с помощью командной кнопки "Удалить". ЕЯ База данных персональных контактов Информация о контакте | Имя: |Сидоренко Василий 1 Адрес: ул. Садовая, 5,кв. 176 Г ород: Киев Страна: Ук Индекс: 252001 Телефон: (044) 123-4567 E-Mail: Сеть: Relcom Адрес: vasia@nippel.kiev.su Сослаться на: Шефа Примечание: Потенциальный пользователь Visual Basic for Applications Запись № 1 Записать ( Изменить ] CTCZ3 Удалить ) Рис. 16.3. Завершенный табличный бланк с записанными данными для базы данных персональных контактов 235
Visual Basic for Applications в примерах Заключение В этой главе вы ознакомились с применением таблицы в качестве бланка данньп Доступ к ячейкам таблицы обеспечивается простым способом — использована набора Sheets и объектов Range. Можно поместить в таблицу командные кнопкиi подключить их к процедурам, — это упростит манипулирование данными в таблице В следующей главе будет разработан пользовательский бланк данных, позволяй- щий реализовать те же функции, что и табличный бланк данных, разработанный: этой главе. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Почему нужно называть ячейки таблицы и использовать эти имена в процедура: на Visual Basic, вместо того, чтобы просто использовать ссылки на ячейки? 2. Каково соотношение между скоростью доступа и размером базы данных пр: сохранении данных во внутреннем массиве и внешнем дисковом файле? 3. Для чего предназначен оператор Option Base 1? 4. Как следует назвать процедуру, которую вы бы хотели запускать при каждое открытии рабочей папки? 5. Как выбрать командную кнопку в таблице без выполнения подключенной к ней процедуры? Упражнения для закрепления материала 1. Добавьте командные кнопки быстрого продвижения вперед и назад (на 10 записей за один раз) для базы данных персональных контактов. 2. Напишите версию программы для базы данных персональных контактов, сохра¬ няющую данные в другой таблице, а не в массиве. Рекомендация: нужно только изменить объявления массива и процедуры DBToEntry и EntryToDB. 3. Напишите процедуру поиска записи, содержащей заданное имя в поле имени. 236
Глава 17 Создание диалоговых окон пользователя До сих пор в качестве первичного средства ввода/вывода использовались ячейки таблицы или панель Immediate окна Debug. Будучи хорошим средством для проведе¬ ния экспериментов с функциями и величинами, панель Immediate, тем не менее, — плохой интерфейс для программ. Хорошим интерфейсом является таблица, однако для создания пользовательского программного интерфейса в Visual Basic имеется возможность применять диалоговые окна пользователя. Из этой главы вы узнаете: ► Как применять страницу диалога для изображения диалогового окна ► Какие существуют окна ввода и вывода и как подключать их к диалоговому окну ► Как подключать процедуры к диалоговым окнам пользователя Открытие новой страницы диалога Возможности диалоговых окон, которые были рассмотрены ранее, в известной степени ограничены. Вы применяли диалоговые окна для обмена единичными элемен¬ тами данных между программой и пользователем. Между тем, часто требуется передать более одного элемента данных, особенно в тех случаях, когда программа принимает информацию от пользователя. Примером такой программы является база данных персональных контактов, разработанная в гл. 16. Для получения от пользователя нескольких элементов данных при разработке этой базы данных использовался табличный бланк данных. Если же для этих целей использовать встроенные диалоговые окна, то функцию InputBoxO придется вызывать восемь раз, что существенно замедлит работу и запутает пользо¬ вателя. Отображение записи также является проблемой, так как вам нужно видеть всю запись одновременно. Для решения проблем такого рода применяются страницы диалога, позволяющие создавать диалоговые окна пользователя. Страница диалога полностью отличается от всех других страниц, используемых Excel. Она является средой, позволяющей изобразить диалоговое окно, разместить в нем командные кнопки, текст и поля редактирования. Для создания диалогового окна вначале откройте страницу диалога, используя директиву Excel Insert/Macro/Dialog или директиву Project Tools/Customize/Forms. На рис. 17.1 изображена новая страница диалога Excel. 237
Visual Basic for Applications в примерах Microsoft Excel - PCLXLS ”T file Edit View Insert Format Tools Qata VYIndow Help №Tffll №1 E0 [Wl ®111 IE [W?1 Рис. 17.1. Новая, чистая страница диалога, созданная в Excel Новый, чистый бланк расположен в центре экрана (как и все окна Visual Basic), а пиктографическое меню Forms исходно расположено слева. Каждая пиктограмма пиктографического меню дает возможность изобразить на бланке различные объекты, позволяющие создать диалоговое окно пользователя. Функции каждого элемента пиктографического меню приведены в табл. 17.1. Таблица 17.1. Элементы пиктографического меню Forms Пиктограмма Средство Описание Label (Метка) Edit Box (Окно редактирования) Group Box (Групповое окно) Создает текстовую метку Создает текстовое окно с редактируемым содержимым Визуально объединяет в группу командные кнопки и определяет область для кнопок выбора 238
Глава 17. Создание диалоговых окон пользователя Пиктограмма Средство Описание Create Button (Командная кнопка) Check Box (Контрольный индикатор) Option Button (Кнопка выбора) List Box (Окно списка) Drop-Down (Выпадающее окно) Combination List Edit (Комбинированное окно список-редактирование) Combination Drop-Down Edit (Комбинированное окно выпадающий список-редактиро¬ вание) Scroll Ваг (Линейка прокрутки) Spinner (Спиннер) Пиктограммы запуска Control Properties (Свойства объекта) Edit Code (Редактирование программы) Toggle Grid (Переключатель координатной сетки) Run Dialog (Запуск диалога) Создает командную кнопку Создает контрольный индикатор с заголовком Создает кнопку выбора с заголов¬ ком для исключающего выбора Создает окно списка Создает выпадающее окно списка Создает комбинированное окно список-редактирование Создает комбинированное окно выпадающий список-редактиро¬ вание Создает линейку прокрутки Создает спиннер и редактирования Отображает свойства выбранно¬ го объекта Отображает код подключенный к объекту Включает и выключает коордиу натную сетку Запускает диалоговое окно 239
Visual Basic for Applications в примерах Чистый бланк, открываемый Dialog Editor в Project, подобен изображенномую рис. 17.1, но по сравнению с Excel Project предлагает очень ограниченный выбор объектов, которые можно разместить в бланке. В пиктографическом меню Projec: доступны только такие объекты, как метки, окна редактирования текста и группы. Помещение объектов в бланк Чтобы создать диалоговое окно пользователя, выберите в пиктографическом менк нужный вам объект, переместите его в бланк, затем заключите в прямоугольник (щелкните кнопкой мыши и растяните прямоугольник вокруг объекта). Вы можете перемещать и видоизменять объект и после того, как поместите его в бланк. Чтобк переместить объект, выберите его и перетащите за край на нужное место. Чтобы видоизменить объект, выберите его, а затем воспользуйтесь манипулятором редакти¬ рования. Манипуляторы редактирования — это маленькие черные прямоугольники, появляющиеся вокруг выбранного объекта. Примеры всех объектов, которые можне подключить к бланку, приведены на рис. 17.2. Q ~ Это бланк Это метка Это окно редактирования. 8 нем может быть несколько строк текста, который может редактироваться. -Групповое окно @ Это кнопки (2) выбора. Одна Q из них может 0 быть выбрана. Это окно списка. Ком. кнопка Каждая строка - отдельный элемент. < Спиннер [Комбинированное | Комбиниоованно'Е |КбмбинйроЕ>анно^]1В □ Контрольный |Выпад, список [±| индикатор Q Может выбираться любое кол-во окно список- редактирование. ОКНО выпадающий список- редактирование. Л Линейка прокрутки 4- ♦ ♦ 4 Рис. 17.2. Пользовательский бланк с примерами объектов, которые можно в него поместить Объект "Командная кнопка " Новый бланк, изображенный на рис. 17.1, исходно имеет подключенные коман¬ дные кнопки ОК и Cancel. Чтобы подключить другие командные кнопки, выберите пиктограмму Create Button пиктографического меню Forms. Можно изменить назва¬ ние новой командной кнопки, выбрав его и записав новое название. Чтобы командная 240
Глава 17. Создание диалоговых окон пользователя кнопка инициировала какие-нибудь действия, ее необходимо подключить к процедуре, для чего нужно выбрать командную кнопку, а затем указать директиву Tools/Assign Macro. В появившемся окне Assign Macro надо выбрать процедуру, которую вы хотите подключить к кнопке, или нажать командную кнопку New для создания заголовка новой процедуры. Кроме инициации подключенных к ним процедур, командные кнопки диалогового окна обладают несколькими специальными свойствами: Default, Cancel, Dismiss и Help, определяющими реакцию на нажатие этих командных кнопок. Свойство Default указывает на командную кнопку, которая автоматически считает¬ ся нажатой, если пользователь нажал клавишу Enter. Только одна командная кнопка в бланке может иметь установленное свойство Default. Свойство Cancel указывает на командную кнопку, которая автоматически считается нажатой, если пользователь нажал клавишу Esc. Аналогично свойству Default, свойство Cancel может быть установлено только для одной командной кнопки в бланке. Если вы установите свойство Dismiss, то диалоговое окно, содержащее данную командную кнопку, гасится после завершения подключенной к этой командной кнопке процедуры (диалоговое окно можно также погасить методом Hide). Свойство Help превращает командную кнопку в кнопку помощи. Пользователь может нажать кнопку помощи для вызова программы Help. Чтобы изменить свойства объекта бланка, выберите этот объект, а затем укажите директиву Format/Object и выберите корешок Control. В качестве альтернативного способа можно выбрать пиктограмму Control Properties пиктографического меню Forms. Диалоговое окно, появившееся в ответ на ваши действия, будет отображать только те свойства, которые можно изменить в данный момент. Среди множества свойств объектов бланка есть свойства, позволяющие изменить внешний вид объекта него поведение. Используйте интерактивную подсказку для получения полного списка свойств и методов каждого объекта. Примечание: Чтобы получить доступ к объектам диалогового окна, необходимо знать имя каждого объекта. Когда вы изображаете объекты в диалоговом окне, Visual Basic присваивает этим объектам имена по умолчанию (Label 2 или Button 3). При выборе объекта его имя появляется в окне Name, расположенном в левом углу строки редактирования. Чтобы изменить имя объекта, можно использовать окно Name или указать директиву Insert/Name/Define. Для объектов бланка эти методы несколько отличаются. Если вы, выбрав объект, запишете имя в окно Name, а затем нажмете клавишу Enter, то измените имя объекта, первоначально присвоенное ему по умолчанию Visual Basic. Директива Insert/Name/Define создает новое имя, ссылающееся на имя по умолча¬ нию, и объект остается названным именем по умолчанию. Оба метода дают практи¬ чески одинаковые результаты, но при использовании второго метода Visual Basic для обнаружения указанного объекта вынужден просматривать два имени. 241
Visual Basic for Applications в примерах Объект "Метка" Метка — это строка текста, которая не может редактироваться пользователи Обычно метка используется для посылки сообщения пользователю или для дополи тельной информации о других кнопках и окнах бланка. Пользователь не мож редактировать метку, однако это может сделать программа. Чтобы создать метку, выберите пиктограмму Label, переместите метку на блат, затем установите нужный размер. Теперь выберите метку и запишите в нее текс который хотите отобразить. Для изменения текста метки программным путем необходимо изменить свойсв метки Caption. Так, следующая процедура изменяет текст метки после того, кг пользователь нажмет командную кнопку: ' Button2_Click Macro Sub Button2_Click() Sheets("Dialogl").Labels("Label 4").Caption = _ "Доброе утро, Остап Ибрагимович!" End Sub Примечание: Можно получить доступ ко всем объектам бланка, используя наборы, такие ка LabelsO или EditBoxesO. Аргументом для набора может быть либо число, М имя, указывающее на тот объект в наборе, к которому необходим доступ. Чиа указанное в имени объекта по умолчанию (в предыдущем примере это "4" в имею "Label 4”) не соответствует месту объекта в наборе, это, скорее, номер объект бланке. Допустим, что процедура Button2_Click подключена к командной кнопке "Сменг метки" на странице Dialogl. Метка с именем Label 4 изображена на бланке. Начальна имя метки соответствует ее заголовку (рис. 17.3). Если вы выберете пиктограмму Rui Dialog пиктографического меню Forms, а затем нажмете командную кнопку "Смен; метки" в диалоговом окне, то метка изменится (рис. 17.4). Примечание: Вы можете создать примеры, приведенные в этом разделе, изобразив командны кнопки и окна на бланке, подключив к кнопкам соответствующие процедуры,: затем инициировав диалоговое окно пиктограммой Run Dialog пиктографичесш меню Forms. Теперь, нажав командную кнопку, можно выполнить подключение к этой кнопке процедуру. 242
Глава 17. Создание диалоговых окон пользователя Dialog Caption Label 4 I 1 | Cancel | Смена Метки Рис. 17.3. Диалоговое окно с меткой Dialog Caption Доброе утро, Остап Ибрагимович! I ик ‘ | Cancel j Смена Метки Рис. 17.4. Изменив программным путем свойство Caption, вы изменили текст метки. Объект "Окно редактирования" Окно редактирования является многострочным окном (см. рис. 17.2, левый верхний угол), содержащим текст, который может редактироваться пользователем. Содержимое окна редактирования может изменяться как пользователем, так и про¬ граммным путем (подобно метке). Чтобы изменить содержимое окна редактирования программным путем, применяется процедура, подобная той, что применялась для изменения метки, но использующая вместо набора LabelsO набор EditBoxesO, а вместо свойства Caption — свойство Text. Кроме того, окно редактирования обладает свойством MultiLine. Если нужно, чтобы в тексте окна редактирования использовались переносы слов на новую строку или чтобы текст в окне опускался на одну строку при нажатии клавиши Enter, то свойство MultiLine необходимо установить в True. Окно редактирования обладает также свойством InputType, ограничивающим тип данных, который пользователь 243
Visual Basic for Applications в примерах может записать в окно. Свойство InputType может иметь одно из следующих постоя? ных значений: xlFormula, xllnteger, xlNumber, xIReference или xlText. По умолчанш значением свойства является xlText. Вы можете установить эти свойства в ваша программе или с помощью директивы Format/Object. Объект "Групповое окно" Групповое окно визуально объединяет в группу другие объекты бланка. Кро* того, оно определяет группу кнопок выбора, как изображено в верхней части рис. 17.'. Для создания группового окна выберите пиктограмму Group Box, а затем изобрази? групповое окно на бланке. Групповое окно должно быть изображено на бланке до топ как вы начнете изображать кнопки, которые нужно объединить в группу. Для задания строки текста, располагающейся в верхней области границы окна, применяете свойство Caption. Объект "Контрольный индикатор" Контрольный индикатор (см. рис. 17.2, слева) позволяет пользователю выбрат? одну или несколько опций. Одновременно может использоваться любое количеств контрольных индикаторов. Когда контрольный индикатор включен, его свойств Value установлено в True; в противном случае — в False. Доступ к свойству Valu: можно получить, использовав набор CheckBoxesO, например: isChecked - Sheets("Dialogl”)•Checkboxes("Check Box 4").Value Свойство контрольного индикатора Caption определяет строку текста, располагав щуюся справа от индикатора. Объект "Кнопка выбора" Кнопка выбора позволяет пользователю выбрать одну опцию из списка взаимоис¬ ключающих опций. В отличие от контрольных индикаторов может быть выбран: только одна из группы кнопок выбора, так как выбор одной кнопки автоматически отключает выбор любой другой кнопки из этой группы. Кнопки выбора известны ка? "радиокнопки": их действие очень напоминает действие кнопок автомобильного ра диоприемника (в старых моделях автомобилей). Группа кнопок выбора объединяет все кнопки выбора, расположенные в одно? групповом окне или на всем бланке (если они не объединены групповым окном). Для создания нескольких групп на бланке используются групповые окна, объединяющие все кнопки выбора одной группы. При создании группы кнопок выбора вначале изобразите на бланке групповое окно, а затем в этом окне изобразите необходимое количество кнопок выбора (см. рис. 17.2). 244
Глава 17. Создание диалоговых окон пользователя Как и для контрольного индикатора, свойство Value кнопки выбора устанавли¬ вается в True в результате нажатия этой кнопки пользователем. Доступ к этому свойству обеспечивается аналогично описанному для контрольного индикатора, с тем отличием, что вместо набора CheckBoxesO используется набор OptionButtonsO. Свойство Caption определяет текст, располагающийся справа от кнопки выбора. Примечание: Контрольные индикаторы, кнопки выбора, окна списка, линейки прокрутки и спиннеры обладают свойством LinkedCell, объединяющим свойство Value управ¬ ляющего элемента и значение ячейки таблицы. Изменение свойства Value управляющего элемента или изменение значения ячейки автоматически изменит связанный объект. Объект "Окно списка ” Окно списка предоставляет пользователю список значений для выбора. Пользова¬ тель не имеет возможности редактировать содержимое окна списка и может выбрать только один элемент из списка. Применение окна списка несколько сложнее приме¬ нения других объектов, так как отсутствует простая возможность добавления в список элементов. Добавить элементы в список, отображаемый в окне списка, можно только программным путем. Существует несколько способов добавить элементы в окно списка. Свойство списка ListFillRange содержит ссылку на область таблицы, в ячейках которой записаны элементы списка. Установив это свойство равным строке, содержащей ссылки на некоторые ячейки таблицы, вы поместите содержимое этих ячеек в список. Кроме того, свойство ListFillRange можно установить с помощью директивы Format/Object. Более общим методом заполнения списка является использование свойства ListO окна списка. Свойство ListO аналогично массиву строк, каждый элемент которого содержит элемент окна списка. Изменение любого элемента свойства ListO изменяет соответствующий элемент в окне списка, как будто все элементы окна списка заме¬ няются на соответствующие элементы свойства ListO. Применение свойства ListO, таким образом, является значительно более удобным, так как список полностью инициализируется и все элементы, оставшиеся в нем от предыдущих применений, удаляются. Еще одним общим способом добавления элементов к списку является применение метода Additem. Этот метод использует два аргумента —строку текста, вставляемую в список, и индекс, определяющий место нового элемента в списке. Если индекс опущен, новый'элемент будет добавлен в конец списка. 245
Visual, Basic for Applications в примерах Предупреждение: Если вы сначала заполнили список, использовав свойство ListFi 11 Rango, изменение даже одного элемента с помощью свойства List() разрывает связь меа окном списка и областью таблицы. После применения свойства List() или мы Add Item в списке останутся только те элементы, которые были помещены в и этими свойством и методом. Применять свойство List() без разрыва связей мок только для просмотра содержимого списка. Так, приведенная ниже процедура вначале присваивает свойству List массив им» чтобы поместить эти имена в окно списка. Затем применяется метод Addlte позволяющий вставить имя Вася на место третьего элемента списка. Результат рабо процедуры отображен на рис. 17.5. 7 Добавление элемента в список. Sub AddList_Click() Sheets("Dialog2").ListBoxes("List Box 8").List = _ Array("Ипполит", "Остап", "Тихон", "Виктор") Sheets("Dialog2").ListBoxes("List Box 8").Additem "Вася", 3 End Sub Рис. 17.5. Добавление элемента в окно списка с помощью свойства List О и метода Addlte Для удаления элементов из списка применяется метод Removeltern, используют два аргумента — индекс и счетчик. Аргумент индекс определяет номер элемента спиа с которого начинается удаление, а счетчик — количество элементов, подлежат удалению. Если аргумент счетчик опущен, то по умолчанию он принимается равныл Для удаления всех элементов списка применяется метод RemoveAliItems или Reniovelt с индексом, равным 1, и счетчиком, превышающим количество элементов списка. 246
Глава 17, Создание диалоговых окон пользователя Следующая процедура удаляет третий элемент из окна списка, изображенного на рис. 17.5. Результирующий список, из которого удалено имя Вася, приведен на рис. 17.6. ' Удаление элемента из списка. Sub RemoveList_Click() Sheets("Dialog2").ListBoxes("List Box 8").RemoveItem Index:=3, Count:=1 End Sub Dialog Caption Г~ОЁ~~1 Ипполит Остап Тихон ♦ | Caned | Виктор | Добавить | | (Удалить*| Рис. 17.6. Удаление третьего элемента из окна списка с помощью метода Removeitem Для определения номера элемента, выбранного из списка пользователем, приме¬ няются свойства Listindex или Value окна списка. Эти свойства содержат номер элемента, выбранного из списка. Чтобы увидеть сам элемент, выберите его из свойства List с помощью свойства Listindex: With Sheets ( "Dialog 2" ). ListBoxes ( "List Box 1") theContents = .List(.Listindex) End With Окно списка List Box 1 находится на странице диалога Dialog 2, а переменной theContents присваивается содержимое текущего выбранного элемента в окне списка. Обратите внимание на то, что полная спецификация объекта ’’Окно списка” (Sheets(”Dialog2”).ListBoxes("List Box Г’)) применяется и к свойству List, и к свойству Listindex. Точки перед каждым свойством связывают свойство с аргументом оператора With. 247
Visual Basic for Applications в примерах Объект 'Выпадающее окно" Выпадающее окно практически аналогично окну списка, за исключением того,! окно списка размещает список в прокручиваемом окне, а выпадающее окно создз выпадающий список. Доступ к выпадающему окну отличается от доступа к окну от только тем, что вместо набора ListBoxesO применяется набор DropDownsO. Комбинированный объект "Окно список-редактирование" Комбинированное окно список-редактирование объединяет окно списка и ок редактирования. Эти два окна связываются таким образом, что если выбирать элегс в окне списка, то он автоматически появится в окне редактирования, где этот элеме можно изменить. В комбинированном окне список-редактирование окно списка и ок редактирования являются двумя отдельными окнами с различными именами и отде: ными свойствами. Доступ к свойствам каждого окна осуществляется независимо, помощью методов доступа к окнам списка и окнам редактирования, описанных вып Комбинированный объект "Окно выпадающий список-редактирование" Комбинированное окно выпадающий список-редактирование представляет сой выпадающий список с элементами, которые можно редактировать. Хотя это окно ил; те же возможности, что и комбинированное окно список-редактирование, оно является объединением двух отдельных объектов. Это окно представляет соб выпадающий список, доступ к которому подобен доступу к другим выпадают спискам, с тем отличием, что данное окно обладает свойством Text, позволяют! редактировать содержимое элементов. Если выбран элемент из списка, то свойства списка Value и Listindex содерх индекс выбранного элемента, а конструкция ,List(.Listindex) обеспечивает доступ тексту выбранного элемента. Если вы редактируете элемент или пишете в редактир) мую часть окна, то свойства Value и Listindex равны 0, а свойство Text содерм текст отредактированного элемента. Предупреждение: После того как элемент комбинированного окна выпадающий список-редактщв ние скорректирован, не пытайтесь получить значения .List(.Listindex). Cnoiicn Listindex в этом случае имеет зна чение 0, а конструкция List(O) приведет к ouiufa так как в списке отсутствует элемент 0. Объект "Линейка прокрутки" Пиктограмма Scroll Ваг создает на бланке вертикальную или горизонталью линейку прокрутки. Линейка прокрутки может рассматриваться как индикатор пр крутки. Ее свойство Value содержит число, определяющее положение ползунка: 248
Глава 17. Создание диалоговых окон пользователя линейке прокрутки. Когда вы перемещаете ползунок линейки прокрутки (т.е. прокру¬ чиваете окно), свойство линейки Value пропорционально изменяется. Линейку про¬ крутки можно также использовать в качестве индикатора вывода, так как при изменении значения свойства Value ползунок линейки пропорционально перемещает¬ ся. Доступ к линейкам прокрутки можно получить, используя набор ScrollBarsO. Свойства Min и Мах, управляющие диапазоном значений, возвращаемых свойством Value, доступны как из программы, так и с помощью диалогового окна Format Object. Присвоенные этим двум свойствам минимальная и максимальная величины определяют диапазон значений, возвращаемых свойством Value линейки прокрутки. Свойства Large- Change и Small Change содержат величину изменения свойства Value при нажатии на тело линейки (прокрутка страницы) или на кнопку со стрелкой (инкрементная прокрутка). Все эти свойства обычно содержат значения в виде целых чисел, которые при необходи¬ мости могут быть преобразованы в нужный программе тип. Объект "Спиннер" Спиннер действует аналогично линейке прокрутки, но индикация положения ползунка в свойстве Value и свойство LargeChange отсутствуют. Имеющиеся свойства Min, Мах и SmallChange идентичны аналогичным свойствам линейки прокрутки. Свойство Value спиннера обычно связано со свойством Text окна редактирования, чтобы пользователь мог изменить редактируемое число посредством нажатия кнопок спиннера. Доступ к спиннерам осуществляется с помощью набора SpinnersO. Подключение процедур к объектам диалогового окна пользователя Большинство элементов управления бланка обладают свойством On Action. Свой¬ ство On Action содержит имя процедуры, выполняемой при изменении данного эле¬ мента управления. Процедура, имя которой указано в свойстве OnAction -командной кнопки, выполняется при нажатии на эту командную кнопку. Процедура, подключен¬ ная к списку или выпадающему списку, выполняется при выборе элемента списка. Процедура, подключенная к текстовому окну, выполняется, если содержимое этого окна было изменено любым способом. Чтобы в процессе проектирования установить значение свойства OnAction, выберите элемент управления, укажите директиву Tools/Assign. Macro, а затем выберите в диалоговом окне Assign Macro процедуру, подключаемую к данному элементу управления. Кроме того, значение свойства OnAction можно установить программным путем, присвоив свойству текстовую строку, содержащую имя под¬ ключаемой процедуры. 249
Visual Basic for Applications в примерах Отображение диалогового окна пользователя Для отображения диалогового окна пользователя существуют несколько способ® зависящих от ваших потребностей. Для отображения бланка при активной страна диалога укажите директиву Tools/Run/Dialog или выберите пиктограмму Run Diala пиктографического меню Forms. Этот метод применяется в первую очередь д: отладки диалогового окна и проверки его функционирования. Для отображения описи диалоговых окон пользователя в Project выберите директиву Tools/Customize/Form Из этого списка выберите окно, которое хотите отобразить. Для активизации диалогового окна пользователя из программы на Visual Basil применяется метод Show. Этот метод имеет следующий синтаксис: объект.Show где объект — страница диалога. Выполнение метода Show аналогично вызову процедуры, причем управлениее возвращается к оператору, следующему за оператором, который содержит метод Show до тех пор, пока диалоговое окно не будет закрыто. Чтобы закрыть диалоговое окне необходимо установить в True свойство Dismiss одной из командных кнопок ил вызвать метод Hide страницы диалога. Метод Hide имеет тот же синтаксис, что и мето Show. Для отображения диалогового окна пользователя в Project применяется оператс Form или CustomForms. Оператор Form имеет следующий синтаксис: Form (имя_диа лога) где имя_диалога — строка, содержащая имя диалогового окна, подлежащего ок бражению. Когда выполняется оператор CustomForms без аргумента, отображаете такое же диалоговое окно, как и при выполнении директивы Tools/Customize/Form Оно позволяет выбрать диалоговое окно, подлежащее отображению. Создание диалогового окна пользователя для базы данных персональных контактов База данных персональных контактов, разработанная в предыдущей главе, исполь зовала в качестве интерфейса пользователя таблицу. Чтобы продемонстрироват: применение элементов управления, описанных в настоящей главе, необходимо изме нить интерфейс пользователя с учетом создания диалогового окна пользователя. Для создания диалогового окна пользователя необходимо выполнить следующв действия: 250
Глава 17, Создание диалоговых окон пользователя 1. Откройте рабочую папку PCD.XLS. Если вы хотите сохранить предыдущую версию, то перед тем, как продолжить, скопируйте рабочую папку с измененным именем. 2. Укажите директиву Insert/Macro/Dialog для создания страницы диалога. Ука¬ жите директиву Format/Sheet/Rename (или выполните двойной щелчок мышью на корешке страницы) для переименования страницы диалога в "КонтактыДиа¬ лог". Выберите Dialog Caption и измените его на "База данных персональных контактов". 3. Выберите и удалите командную кнопку Cancel. Выберите командную кнопку ОК и измените ее название на "Выход". 4. Используя в качестве образца рис. 17.7, изобразите, назовите и добавьте текст к следующим объектам бланка. Для того, чтобы изменить имя, выберите объект, изображенный на бланке, а затем — имя объекта в окне Name строки редактиро¬ вания, запишите новое имя и нажмите клавишу Enter. Чтобы создать корректный порядок табуляторов, добавляйте объекты в следующем порядке: Элемент управления Имя элемента Заголовок Group Box Label Контактная информация Edit Box Label ДИмя Имя Edit Box Label ДАдрес Адрес Edit Box Label ДГород Город Edit Box Label ДСтрана Страна Edit Box Label Д Индекс Индекс Edit Box Label ДТелефон Телефон Combination ДЕ_МаП_Сеть E-Mail: Сеть Drop-Down Edit Edit Box Label ДЕ_МаП_Адрес Адрес Edit Box Label ДСсылка Сослаться на Edit Box Label ДПримечание Примечание Label ДНомЗап Запись # 251
Visual Basic for Applications в примерах Примечание: Порядок табуляторов определяет, какой объект бланка будет выбран, если польза ватель нажмет клавишу Tab. При простом вводе данных порядок табулятор* обычно используется для перемещения от поля к полю. Исходно он соответствуй порядку, в котором объекты помещались на бланк. Чтобы изменить поряди* табуляторов, выберите объект, укажите директиву Format/Placement, а затем - директиву субменю Bring to Front или Send to Back. 5. Выберите окно редактирования "Примечание", укажите директиву Format/Object выберите корешок Control, включите контрольные индикаторы Multiline Edit! Vertical Scrollbar, затем нажмите командную кнопку ОК. 6. Добавьте в диалоговое окно следующие командные кнопки, измените их названия как описано ниже, а затем используйте директиву Tools/Assign Macro до подключения этих кнопок к соответствующим процедурам: Командная кнопка Подключаемая процедура Записать NewEntry_Click Изменить UpdateEntry_Click Удалить DeleteEntry_Click Найти имя FindName Click 7. Добавьте в бланк линейку прокрутки, назовите ее "ПрокруткаЗап", затем подк> чите ее к процедуре ScrollEnties_Click (процедуру вы создадите позже, а сейма просто запишите это имя в диалоговом окне Assign Macro). Рис. 17.7. Макет бланка пользователя для базы данных персональных контактов 252
Глава 17. Создание диалоговых окон пользователя Теперь, когда проектирование бланка завершено, вы должны изменить процедуры, чтобы использовать этот бланк вместо таблицы. Основные изменения коснутся двух процедур, содержащих все операторы, обеспечивающие доступ к интерфейсу пользо¬ вателя. Вы должны слегка модифицировать процедуру Initializelt для отображения нового диалогового окна и инициализации линейки прокрутки при открытии таблицы. Следует также модифицировать процедуры NewEntry_Click и DeleteEntry_Click для изменения диапазона линейки прокрутки. Кроме того, для управления линейкой прокрутки и поиска имени нужны две новых процедуры. Все остальные процедуры остаются без изменений. ' Программа работы с базой данных персональных контактов. ' Версия для страницы диалога. Option Explicit Option Base 1 Type DBEntry 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 1 'Определяет структуру записи базы данных. Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type 'Размер типа 256 байтов. Dim theDB () As DBEntry Dim numEntries As Integer Dim theEntryNum As Integer 'Массив базы данных. 'Общее количество записей. 'Текущая отображаемая запись ' Инициализация базы данных. Sub Initializelt () numEntries = 0 ReDim theDB(1) theEntryNum = 0 Sheets ("КонтактыДиалог") .DropDowns ("ДЕ_МаИ_Сеть") .List = 253
Visual Basic for Applications в примерах Array("Relcom”, "Internet", "CompuServe", "Prodigy") Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Min = 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = 1 Sheets("КонтактыДиалог").Show End Sub ' Процедура Auto_open, вызываемая при каждом открытии ' рабочей папки. t Sub Auto_Open() Initializelt 'Вызов процедуры инициализации. End Sub г ' Пересылка записи в базу данных. Sub EntryToDB(anEntry As Integer) With Sheets("КонтактыДиалог") theDB(anEntry).Name = .EditBoxes("ДИмя").Text theDB(anEntry).Address = .EditBoxes("ДАдрес").Text theDB(anEntry).City = .EditBoxes("ДГород").Text theDB(anEntry).State = .EditBoxes("ДСтрана").Text theDB(anEntry).Zip = .EditBoxes("ДИндекс").Text theDB(anEntry).Phone = .EditBoxes("ДТелефон").Text theDB (anEntry).Net = .DropDowns ("ДЕ_МаИ_Сеть") .Text theDB (anEntry) . NetAddr = . EditBoxes ("ДЕ_МаИ_Адрес" ). Text theDB(anEntry).Referral = .EditBoxes("ДСсылка").Text theDB(anEntry).Notes = .EditBoxes("ДПримечание").Text theDB(anEntry).RecNo = anEntry End With End Sub r ' Пересылка записи в бланк. f Sub DBToEntry(anEntry As Integer) With Sheets("КонтактыДиалог”) .EditBoxes("ДИмя").Text = theDB(anEntry).Name .EditBoxes("ДАдрес").Text = theDB(anEntry).Address .EditBoxes("ДГород").Text = theDB(anEntry).City .EditBoxes("ДСтрана").Text = theDB(anEntry).State .EditBoxes("ДИндекс").Text = theDB(anEntry).Zip 254
Глава 17. Создание диалоговых окон пользователя .EditBoxes("ДТелефон").Text = theDB(anEntry).Phone . DropDowns ( "ДЕ_МаИ_Сеть" ) . Text = theDB (anEntry). Net . EditBoxes ( "ДЕ_МаИ_Адрес") .Text = theDB(anEntry).NetAddr . EditBoxes("ДСсылка").Text = theDB(anEntry) .Referral . EditBoxes ("ДПримечание") .Text = theDB (anEntry) .Notes . Labels ("ДНомЗап" ). Caption = ’’Запись № " & Str (theDB (anEntry). RecNo) . ScrollBars("ПрокруткаЗап") .Value = anEntry End With End Sub ' Добавляет новую запись. Sub NewEntry_Click () numEntries = numEntries + 1 Sheets("КонтактыДиалог") .ScrollBars(’’ПрокруткаЗап").Max = numEntries theEntryNum = numEntries ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub 'Изменяет запись. Sub UpdateEntry_Click () If theEntryNum = 0 Then Exit Sub 'Если еще нет записей, то выход. EntryToDB theEntryNum 'Изменение текущей записи. End Sub 'Удаляет запись. Sub DeleteEntry_Click () Dim I As Integer, buttons As Integer Dim theMsg As String If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. 255
Visual Basic for Applications в примерах If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries theDB(I —1) = theDB(I) theDB (I — 1) . RecNo = 1—1 Next I numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) End If Sheets("КонтактыДиалог") .ScrollBars("ПрокруткаЗап") .Value = theEntryR Sheets ("КонтактыДиалог") . ScrollBars (’’ПрокруткаЗап" ) .Max = numEntrie DBToEntry theEntryNum End Sub ' Прокрутка записей. / t Sub ScrollEntries_Click() If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets ("КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .VaL DBToEntry theEntryNum End Sub t ' Поиск имени. г r Sub FindName_Click() Static theName As String Dim I As Integer Const Textcomparison = 1 If numEntries = 0 Then Exit Sub 'Еще нет записей. theName = InputBox ("Введите имя для поиска", "Поиск имени", theName If theEntryNum о numEntries Then For I = theEntryNum + 1 To numEntries 256
Глава 17. Создание диалоговых окон пользователя If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox "Имя: ” & theName & " не найдено.", , "Поиск имени" End Sub Процедуру Initializelt необходимо изменить, добавив к предыдущей версии четыре оператора, выделенных жирным шрифтом: ' ' Инициализация базы данных. Sub Initializelt () numEntries = О ReDim theDB(1) theEntryNum = 0 Sheets("КонтактыДиалог") .DropDowns (" ДЕ_Ма±1_Сеть") .List = _ Array("Relcom” , "Internet”, "CompuServe", "Prodigy") Sheets (" КонтактыДиалог") . ScrollBars (" ПрокруткаЗап") .Min = 1 Sheets (" КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .Max = 1 Sheets("КонтактыДиалог").Show End Sub Первый из этих новых операторов загружает список в комбинированное окно выпадающий список-редактирование. Последующие два оператора инициализируют свой¬ ства Min и Мах линейки прокрутки. Последний добавленный оператор использует метод Show для отображения диалогового окна пользователя. Чтобы закрыть диалоговое окно, 9Вильям Дж. Орвис 257
Visual Basic for Applications в примерах установите свойство Dismiss командной кнопки ’’Выход" диалогового окна в Ъ (проверьте это в диалоговом окне Format Object); после нажатия этой командна кнопки диалоговое окно закроется. В качестве альтернативы можно использоод метод Hide, применение которого аналогично применению метода Show. Наиболее связаны с интерфейсом пользователя процедуры EntryToDB и DBToEt try. Измените ссылки в каждой процедуре для использования диалогового окз следующим образом (изменения предыдущей версии выделены жирным шрифтом) г ’ Пересылка записи в базу данных. Sub EntryToDB(anEntry As Integer) With Sheets("КонтактыДиалог") theDB(anEntry).Name = .EditBoxes("ДИмя").Text theDB(anEntry).Address = .EditBoxes("ДАдрес").Text theDB(anEntry).City = .EditBoxes("ДГород").Text theDB(anEntry).State = .EditBoxes("ДСтрана").Text theDB(anEntry).Zip = .EditBoxes("ДИндекс").Text theDB(anEntry) .Phone = .EditBoxes("ДТелефон").Text theDB(anEntry).Net = .DropDowns(”ДЕ_Ма±1_Сеть").Text theDB(anEntry).NetAddr = .EditBoxes ("ДЕ_Ма±1_Адрес"). Text theDB(anEntry).Referral - .EditBoxes("ДСсылка").Text theDB(anEntry).Notes = .EditBoxes("ДПримечание").Text theDB(anEntry).RecNo = anEntry End With End Sub ' Пересылка записи в бланк. г Sub DBToEntrу(anEntry As Integer) With Sheets("КонтактцДиалог") .EditBoxes("ДИмя").Text = theDB(anEntry).Name .EditBoxes("ДАдрес").Text = theDB(anEntry).Address .EditBoxes("ДГород").Text = theDB(anEntry).City . EditBoxes("ДСтрана") .Text = theDB(anEntry) .State .EditBoxes("ДИндекс").Text - theDB(anEntry).Zip .EditBoxes("ДТелефон").Text = theDB(anEntry).Phone .DropDowns("ДЕ_Ма11_Сеть").Text = theDB(anEntry).Net .EditBoxes ("ДЕ_Ма11_Адрес") .Text = theDB(anEntry).NetAddr .EditBoxes("ДСсылка").Text = theDB(anEntry).Referral 258
Глава 17. Создание диалоговых окон пользователе .EditBoxes ("ДПримечание") .Text = theDB(anEntry).Notes . Labels ("ДНомЗап") .Caption - "Запись № " & Str (theDB (anEntry) .RedNo) .ScrollBars("ПрокруткаЗап").Value = anEntry End With End Sub Процедуры NewEntry_Click и DeleteEntry_Click изменяют количество записей в базе данных. Следовательно, вы должны добавить оператор изменения диапазона линейки прокрутки (добавленные строки выделены жирным шрифтом): I ' Добавляет новую запись. Sub NewEntry_Click() numEntries = numEntries + 1 Sheets("КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .Max = numEntries theEntryNum = numEntries ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub i 'Удаляет запись. Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer Dim theMsg As String If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = ”Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. 259
Visual Basic for Applications в примерах For I = theEntryNum + 1 To numEntries theDB(I — 1) = theDB(I) theDB(I — 1) . RecNo = 1—1 Next I numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) End If Sheets("КонтактыДиалог") .ScrollBars("ПрокруткаЗап") .Value = theEntryNum Sheets (” КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .Max = numEntries DBToEntry theEntryNum End Sub Новая процедура ScrollEntries_Click управляет линейкой прокрутки. Действие этой процедуры подобно действиям процедур Forward_Click и Backward_Click. Вначале процедура проверяет наличие записей, а затем изменяет переменную theEn¬ tryNum, присваивая ей текущее значение свойства Value линейки прокрутки. Прове рять минимальное и максимальное значения переменной theEntryNum больше не нужно: линейка прокрутки автоматически поддерживает диапазон значений. ' Прокрутка записей. t г Sub ScrollEntries_Click() If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets ("КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .Value DBToEntry theEntryNum End Sub Следующая новая процедура добавляет возможность поиска полного имени в базе данных без пошагового просмотра каждой записи. Процедура FindName_Click отоб¬ ражает окно ввода имени, которое необходимо найти, а затем ищет первую запись, содержащую это имя. г ' Поиск имени. Sub FindName__Click () Static theName As String Dim I As Integer 260
Глава 17. Создание диалоговых окон пользователя Const Textcomparison = 1 If numEntries = 0 Then Exit Sub 'Еще нет записей. theName = InputBox("Введите имя для поиска", "Поиск имени", theName) If theEntryNum оnumEntries Then For I = theEntryNum + 1 To numEntries If InStrfl, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox "Имя: " & theName & " не найдено.", , "Поиск имени" End Sub Эта процедура состоит из двух частей. Первая часть осуществляет поиск от текущей позиции до конца базы данных, а вторая —от начала базы данных до текущей позиции. Таким образом, последовательно ищется местоположение следующей записи с совпа¬ дающим именем. Если вы будете использовать только один вид поиска —от начала базы данных, то всегда будете находить одну и ту же запись, содержащую искомое имя. Например, если в вашей базе данных есть Иван Иванов, Петр Иванов и Алексей Иванов, а в окне ввода записано "Иванов”, то вы всегда будете находить только запись с именем Иван Иванов, так как она будет первой записью от начала базы данных, содержащей текст "Иванов”. Метод, примененный в процедуре, дает возможность продолжить поиск следующей записи, что позволит вам найти и Петра, и Алексея Ивановых. Первые несколько строк процедуры объявляют некоторые переменные. Перемен¬ ная theName объявлена как Static; следовательно, она не исчезает между вызовами процедуры. Переменная theName содержит строку текста, который вы ищете, переда¬ вая ее в качестве значения по умолчанию функции InputBoxO. Благодаря такому 261
Visual Basic for Applications в примерах способу использования переменной при каждом запуске процедуры предыдущи строка поиска автоматически помещается в диалоговое окно, что позволяет не пере писывать строку поиска при множественном поиске одной и той же строки. После применения функции InputBoxO ожидается строка, которую необходим искать. Проверяется текущая позиция в базе данных, и если она не последняяд выполняется первый цикл поиска: For I = theEntryNum + 1 То numEntries If 'InStr(1, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If I Next I I Первый цикл поиска начинается с позиции, следующей за текущей позицией в б» данных, и продолжается до конца базы данных. Функция InStrO сравнивает строи в переменной theName со строкой в поле Name записи базы данных. Сравнение тексе производится независимо от того, строчные это буквы или прописные, так н применяется опция TextComparison. Если строка в поле имени найдена (InStr()<>0| то процедура определяет номер данной записи как номер текущей записи, отображая эту запись и завершает свою работу. Если запись не найдена, то процедура переходя к выполнению второго цикла поиска. I Второй цикл поиска выполняется в том случае, если первый не принес успей Действует он аналогично первому циклу, но интервалы значений переменной! использованной в операторе For, отличаются: в первом цикле интервал I был от запив следующей за текущей, и до конца базы данных, во втором цикле этот интери начинается от начала базы данных и оканчивается текущей записью. Если и вторя цикл поиска не дал результата, то процедура вызывает функцию MsgBoxO дя отображения окна, сообщающего о неудаче. Затем она завершает свою рдботу. I Сохраните рабочую папку, а затем запустите базу данных персональных кони! тов. Для запуска процедуры Initializelt используйте директиву Tools/Macro. Если! закроете рабочую папку, а потом опять откроете ее, то процедура выполни® автоматически (см. рис. 17.8). I 262
Глава 17. Создание диалоговых окон пользователя Рис. 17.8. Диалоговое окно является интерфейсом пользователя базы данных персональных контактов (версия для диалогового окна) Применение объектов диалогового окна в таблице Все объекты диалогового окна, за исключением окон редактирования, могут быть подключены к таблице таким же образом, как к странице диалога. Для этого переключитесь на таблицу, в которую хотите поместить элементы управления, отоб¬ разите пиктографическое меню Forms и изобразите объекты тем же способом, что и на странице диалога. Некоторые элементы управления, приведенные на рис. 17.2, за исключением окон редактирования, подключены к таблице (см. рис. 17.9). Рис. 17.9. Таблица с элементами управления, подключенными к ней тем же способом, что и к странице диалога 263
Visual Basic for Applications в примерам Заключение В этой главе рассмотрено применение страниц диалога для создания интерфейса пользователя прикладной программы. В отличие от таблицы диалоговое окно пользо¬ вателя предоставляет возможность создать значительно более изысканный интерфейс, не загроможденный ячейками, линиями сетки, шапками строк и колонок и т.п. Вы создали диалоговые окна пользователя на странице диалога, изобразили на них объекты управления, а затем подключили процедуры к этим объектам. Для отобра¬ жения диалогового окна пользователя был применен метод Show; а для его закрытия — метод Hide бланка или свойство Dismiss элемента управления. Следующая глава познакомит вас с дисковыми файлами и их применением в целях сохранения данных. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. t. Как отобразить диалоговое окно пользователя в Excel? В Project? 2. Как открыть новую страницу диалога? 3. Какие две командные кнопки имеет чистый бланк на странице диалога? 4. Объясните разницу между меткой и окном редактирования. 5. Что такое групповое окно? 6. Объясните разницу между кнопками выбора и контрольными индикаторами. 7. Объясните разницу между линейками прокрутки и Спиннерами. 8. Что представляет собой свойство Default командной кнопки? Свойство Cancel? 9. Что такое порядок табуляторов? Упражнения для закрепления материала 1. Создайте диалоговое окно, предназначенное для ввода вашего имени и номера телефона, а затем примените для их отображения функцию MsgBoxO. 2. Доработайте процедуру FindName_Click, предоставив пользователю возможность выбрать поле для поиска, а не искать только имя. 264
Глава 17. Создание диалоговых окон пользователя 3. Создайте процедуру, добавляющую новые записи, вводимые с помощью^ окна редактирования, в список комбинированного окна выпадающий список-редакти¬ рование, затем подключите новую процедуру к выпадающему окну. 4. Доработайте процедуру FindName_Click, обеспечив использование копии бланка базы данных персональных контактов для включения строки поиска в одно или более полей. Добавьте две командных кнопки поиска —командную кнопку And, возвращающую запись только в том случае, когда все поля совпали, и командную кнопку Or, возвращающую запись, у которой совпало хотя бы одно поле. 265
Глава 18 Запись данных в файл последовательного доступа До сих пор вы сохраняли программы в виде части рабочих папок Excel или Project, а любые данные, используемые этими программами, находились в таблице. Данные сохранялись в памяти, но они исчезали после завершения работы программы. В настоящей и последующей главах описано, как сохранять данные в дисковом файле, чтобы в дальнейшем их можно было использовать вновь. Из этой главы вы узнаете: > Чем отличаются дисковые файлы последовательного и произвольного доступа > Как открыть файл последовательного доступа > Как записать данные в файл последовательного доступа > Как прочитать данные из файла последовательного доступа Файлы последовательного и произвольного доступа В Visual Basic существуют два основных типа дисковых файлов — последователь¬ ного и произвольного доступа. Собственно, таких типов — три, если считать таблицы, которые в Visual Basic также используются для хранения данных. Однако в Visual Basic отсутствует возможность прямо читать из файлов таблиц или писать в них; можно только записывать данные в открытую таблицу, а затем сохранять ее с помощью Excel. Способы, которыми записываются данные в файлы последовательного и произ¬ вольного доступа, значительно отличаются друг от друга. Файлы последовательного доступа читаются и пишутся последовательно, от начала файла до его конца. Текстовые файлы, такие как README, встречающиеся в большинстве прикладных программ, являются файлами последовательного доступа. Для того, чтобы получить доступ к определенной части информации в последовательном файле, необходимо начать от начала файла и читать последовательно, запись за записью, до тех пор, пока не будет найдена требуемая информация. Файлы произвольного доступа основаны на записях постоянной длины, что позволяет читать и писать информацию произвольно, в любом порядке. Последова¬ тельный файл можно либо читать, либо писать, а файл произвольного доступа — читать и писать одновременно. Большинство программ работы с базами данных используют файлы произвольного доступа, так как они позволяют напрямую прочесть любую запись в базе данных без предварительного чтения всех предшествующих ей записей файла. После чтения записи программа базы данных может изменить эту запись и записать ее на то же место на диске, не разрушая другие записи файла. Подобные программы, созданные на Visual Basic, используют те же возможности. 266
Глава 18. Запись данных в файл последовательного доступа Ограничением файлов произвольного доступа является фиксированная длина эписи. Однако это ограничение позволяет Visual Basic быстро обнаружить запись на жке. Например, для чтения восьмой записи файла Visual Basic пропускает семь полных записей, а затем читает следующую. Если бы записи не имели фиксированной пины, то пришлось бы поддерживать отдельный индекс, определяющий, где кончает¬ ся одна запись и начинается другая. Чтение и запись последовательных файлов Последовательный файл является наиболее общим типом файла. Любой файл, который читается в память целиком, должен быть последовательным. Большинство неформатированных текстовых файлов являются последовательными —так же, как а большинство файлов программ. В Visual Basic текстовые файлы также представляют собой файлы последовательного доступа. Данные в этих файлах записаны в форме строк символов ANSI. Если вы откроете последовательный файл Visual Basic в текстовом процессоре, то сможете убедиться, что этот файл пригоден для чтения. Последовательные файлы не являются эффективным средством хранения число¬ вых величин, так как числа в них хранятся в символьном формате. Однако у таких файлов есть определенные достоинства. Например, последовательные файлы можно редактировать с помощью текстового процессора и не нужны никакие специальные программы .для их отображения или модификации. Открытие файла Прежде чем Visual Basic сможет что-либо сделать с дисковым файлом, вы должны открыть этот файл и подключить к нему файловое число. Файловые числа —это небольшие целые числа, которые связываются с файлом при его открытии. Команды чтения и записи используют файловые числа для указания файла, в который надо писать или из которого надо читать. Для открытия последовательного файла используется оператор Open, имеющий следующий синтаксис: Open имя_файла For режим As файловое_число Аргумент имя_файла является строкой, содержащей имя и путь файла, который вы хотите открыть. Если файл находится в текущей директории, то достаточно указывать только его имя. Если файл находится не в текущей директории, то для него необходимо указывать полный путь. Для того чтобы файл, подлежащий открытию, выбирал пользователь, обычно прибегают к функции GetOpenFilenameO, отображаю¬ щей стандартное диалоговое окно File Open, как описано в разд. ’’Применение диалоговых окон других типов" гл. 15. Строка, возвращаемая этой функцией, содержит имя файла и путь. Эти данные затем используются в качестве аргумента оператора Open. 267
Visual Basic for Applications в примерах Аргумент режим определяет тип файла, который вы хотите открыть, и способ открытия файла. Режим должен принимать одно из литеральных значений: Input, Output или Append (для последовательных файлов), либо Random (для файлов произвольного доступа). Кроме того, в особых ситуациях может применяться режим двоичного доступа. Режим Input открывает файл для чтения, а режим Output —для записи. Оба режима открывают файл последовательного доступа с его начала. Режим Append открывает файл с конца —с тем, чтобы при записи новых данных не пришлось переписывать все записи от начала файла. Попытки открыть несуществующий файл с режимом Input приведут к ошибке; однако если вы открываете несуществующий файл с режимами Output или Append, то файл будет создан. Аргумент файловое_число определяет число, которое вы хотите присвоить файлу. Обычно первому файлу присваивают 1, второму —2 и т.д. Если файл закрывается, то освободившееся файловое число может быть использовано повторно; но не пытай¬ тесь открыть файл, используя файловое число, которое уже присвоено открытому файлу. Если вы не уверены в том, какое файловое число является допустимым в данный момент, то для присвоения некоторой переменной допустимого файлового числа, воспользуйтесь функцией FreeFile. Затем эта переменная используется в качестве аргумента оператора Open, а также операторов чтения и записи файла. Так, следую щие строки кода открывают последовательный файл для чтения: FileNuml = FreeFile Open "myfile.txt" For Input As FileNuml В данном примере функция FreeFile использована для выбора следующего допус¬ тимого файлового числа, а затем файл с именем MYFILE.TXT открывается с начала для чтения. Закрытие файла После того как вы завершили работу с файлом, его необходимо закрыть с помощью оператора Close. В качестве аргумента оператора Close используется файловое число, указывающее на то, какой файл нужно закрыть. Ниже приведен синтаксис оператора Close: Close #файловое_число Если опустить аргумент файловое_число, то Visual Basic закроет все открытые файлы. 268
Глава 18. Запись данных в файл последовательного доступа Печать в файл Если файл открыт, то для записи в него данных можно использовать оператор Print. Данные, записываемые оператором Print, представляют собой текст, подобный тому, который выдается на принтер. Для примера, попробуйте выполнить следующие операторы на панели Immediate окна Debug: Print "Общий привет" Общий привет Print 5 5 а = 5 Print а 5 Вы, очевидно, помните, что когда оператор Print применяется на панели Immedi¬ ate, его результат появляется в следующей строке. Точно таким же образом оператор Print "печатает” в дисковый файл, только его результаты появляются не на панели Immediate, а в этом файле. Если печатаете некоторый текст, то этот текст записывается в файл. Если вы печатаете число, это число помещается в файл. Если вы печатаете переменную, то в файл помещается значение этой переменной. Если поместить в строку для печати несколько элементов, разделенных запятыми, то они будут напечатаны в блоках, заполненных пробелами и разделенных фиксиро¬ ванными табулостопами через каждые 14 символов: Print 1, 2, 3, 4 12 3 4 Print "Один", "Два", "Три", "Четыре" Один Два Три Четыре Однако если вместо запятых использовать символ (;), то напечатанные элемента не будут содержать пробелов: Print 1; 2; 3; 4 12 3 4 Print "Один";"Два";"Три";"Четыре" Од ин Д в аТ риЧ е тыр е 269
Visual Basic for Applications в примерах Примечание: Если разделить символом (;) числа в операторе Print, то напечатанная строка будет содержать по два пробела между числами. Эти пробелы появляются в результате того, что оператор Print преобразовывает каждое число в строку текста, добавляя к нему ведущий и конечный пробелы, что в результате и дает два пробела между числами. Для управления печатью следует использовать функцию FormatO. Функция FormatO получает два аргумента, а возвращает отформатированную строку. Первым аргументом является переменная, содержащая число, которое нужно напечатать, а второй аргумент содержит строку форматирования, определяющую форму, в которой нужно напечатать это число. Строка форматирования подобна тому, что вы исполь¬ зовали в Excel для форматирования содержимого ячеек. Символы (#) используются в качестве цифровых заполнителей; запятые (,) указывают на то, что каждые три символа отделяются запятой; точка (.) указывает положение десятичной точки, а нуль (0) — позиции обязательного символа. Для получения полного списка символов форматирования пользуйтесь интерактивной подсказкой. Следующие операторы, выполненные на панели Immediate окна Debug, демон¬ стрируют работу функции FormatO: а - 1234.567 Print а 1234.567 Print a, Format (а, ’’#,###.00"), Format (а, ’’#.0") 1234.567 1,234.57 1234.6 а = 0.1234 Print, a, Format(a, ’’#,###.00") , Format (а, "0.00" ) , Format (а, "00.00") 0.1234 .12 0.12 00.12 а - 1234 Print a, Format(а,”#,###.00"), Format(а,"0.00"), Format(а,"00.0") 1234 1,234.00 1234.00 1234.0 Вначале переменной присваивается числовое значение, затем эта переменная печатается с применением различных форматов. Обратите внимание на то, что если число не помещается в форматированной строке, оно округляется. Кроме того, отметьте, что для заполнения обязательных для печати позиций используются ведущие и конечные пробелы. 270
Глава 18. Запись данных в файл последовательного доступа Для печати в дисковый файл вместо панели Immediate после зарезервированного слова Print необходимо записать символ (#), файловое число и запятую: Print #файловое_число, список_аргументов где фапловос_число определяет открытый файл, а список_аргументов содержит то, что вы хотите напечатать. Так, для печати предыдущих значений в файл SCRATCH.DAT с помощью команд, записанных на панели Immediate окна Debug, вначале нужно выбрать файловое число, азатем открыть указанный файл для записи: Open "SCRATCH.DAT" For Output As 1 Затем вы печатаете в файл данные, после чего закрываете его: Print #1, "Общий привет" Print #1, 5 а = 5 Print #1, a Print #1, 1, 2, 3, 4 Print #1, 1; 2; 3; 4 Print #1, "Один", "Два", "Три", "Четыре" Print #1, "Один"; "Два"; "Три"; "Четыре" Print #1, а Print #1, a, Format(а, "#,###.00), Format(а, "#,0") a = 0.1234 Print #1, a, Format(а,"#,###.00),Format(а, ”0, 00") , Format(а, "00.00") a = 1234. Print #1, а,- Format (а, "#,###. 00) , Format (а, "0, 00") , Format (а, "00.0" Close #1 Если теперь вы используете текстовый процессор для открытия файла SCRATCH.DAT, то увидите, что данные, которые напечатаны в файл, полностью соответствуют предыдущим результатам на панели Immediate. 271
Visual Basic for Applications в примерам I р|^Гн1 №1^1 I x l^leF^l к *1 *1 ПаТ^П |m |м| мТ^]ыГ] [¥] Hjjjjil Э [jg] | Hormal lB|Ti»B«llewH«—n||g[i2~||a|B|z|u||K|»|a|a||>=|:=|»|»||g t • 2 * i'3‘ i • 4 ■ 1*5* i • 6 ' I • 7 ' i*8' I ■ 3 ■ I • Ю ' i ll - i ■ 12 • i • 13 • I ■ 14 • ij^ 15 ■ I • L - I ■ 1~ t * 2 * i • 3 • i • 4 - I - 5 • i • 6 ■ I • 7 - i - 8 • 1*9' I • Ю 1 i 11 - i ■ 12 • i • 13 • I Общий привет 5 5 12 3 4 1 2 3 4 | Один Два Три Четыре ОдинДваТриЧетыре 5 5 5.00 5.0 0.1234 .12 0.12 00.12 1234 1,234.00 1234.00 1234.0 ЭВО» |\[□|oh|Q|E|^№| IWRm таЖ)ТЖ|н|й| Раде 1 Sec 1 1/1 | At 4.4cm Ln 5 Col 14 | 12:29 | WP Рис. 18.1. Файл SCRATCH.DAT, созданный для записи данных оператором Print, отображен с помощью текстового процессора Microsoft Word Запись в файл При использовании оператора Print данные, записываемые в дисковый файл, форматируются в пригодную для чтения форму, подобную текстовому документу. Однако если вы предполагаете читать эти данные программами на Visual Basic, то их следует записывать не оператором Print, а оператором Write. Оператор Write работает подобно оператору Print, но сохраняет существовавшие кавычки и разделяет выводи¬ мые данные запятыми. Эти запятые и кавычки упрощают Visual Basic определение конца одного элемента данных и начала другого. Если предполагается сохранить данные, а затем прочесть их программой, то вместо оператора Print воспользуйтесь оператором Write. Вы не сможете проверить работу оператора Write на панели Immediate, так как Write не печатает на эту панель. Однако панель Immediate можно использовать аналогично тому, как это сделано в предыдущем примере для записи данных в дисковый файл SCRATCH.DAT, чтобы затем просмотреть эти данные с помощью текстового процессора. 272
Глава 18. Запись данных в файл последовательного доступа 1» Так, приведенные ниже операторы, выполненные на панели Immediate, записы¬ вают в дисковый файл те же самые данные, которые использовались в примере для оператора Print, но применяя оператор Write. После выполнения этих операторов можно просмотреть дисковый файл с помощью текстового процессора. FNum = FreeFile Open "SCRATCH.DAT" For Output As FNum Write #FNum, "Общий привет” Write #FNum, 5 а= 5 Write #FNum, a Write #FNum, 1, 2, 3 z 4 Write #FNum, 1; 2; 3 ; 4 Write #FNum, "Один", "Два", "Три", "Четыре" Write #FNum, "Один"; "Два"; "Три"; "Четыре" Write #FNum, a Write #FNum, a, Format(a, "# ,###.00), Format(а, "#,0" a = 0. 1234 Write #FNum, a, Format (a, "#, ###.00), Format (a, "0, 00”) t Format (a, "00.00") a = 1234. Write #FNum, a, Format(a,"#,###.00), Format(a, "0, 00") , Format(a, "00.0") Close #FNum Обратите внимание на различия в результатах работы операторов Write (рис. 18.2) и Print (рис. 18.1). Оператор Write отделяет каждый элемент данных запятой и заключает каждую строковую величину в кавычки. Строки, возвращенные функцией FormatO, также заключаются в кавычки. Использование кавычек и запятых для разделения отдельных элементов данных значительно упрощает применение оператора Input (описываемого в следующем разделе). Без них было бы практически невозможно определить, где кончается один записанный элемент и начинается следующий. Обратите внимание также на то, что вместо конкретного файлового числа (как в предыдущем примере) здесь используется функция FreeFile, позволяющая определить свободное файловое число. Это число сохраняется в переменной FNum, а затем эта переменная используется везде, где требуется файловое число. 273
Visual Basic for Applications в примерах Q MicfosoftWord-SCRATCH.DAT IBSBl5aSIH^sSCTinSSoaEg3IHinS3|glE] [iran ] B |t««.HewHo^g a QFJ B [BR |U ||8|»|ДЖ||‘Е|;Е|Я!|^][^ ■•$■ ' ■!■■■ г - ■ Ч'.ч. ■•«•■ ■ T - ■■»■■■»•.»■■ - и , чг- ■ ■<»• ■ ■»■ -Ж»IZE "Общий привет" 5 5 1ДД4 U3,4 “Один", "Два",'Три"," Четыре " " Один", "Два", "Три"," Четыре " 5 5,”5.00”, ”5.0” 0.1234, ”.12”, ”0.12”, ”00.12” 1234, ”1,234.00”, ”1234.00”, ”1234.0” BSD» SZ |\|a|o|~\|G|E|gi|tb| |^|L/|^|k| [ЖЮЖЖ] Page 1 Sec 1 1/1 I At 7.2cm Ln 11 Cd 37 | 12:55 [ WP Рис. 18.2. Файл SCRATCH.DAT, созданный для записи данных оператором Write, отображен с помощью текстового процессора Чтение из файла Для чтения данных из файла применяются операторы Input и Lineinput. Первым аргументом каждого из этих операторов являются символ (#) и файловое число, затем следует запятая, отделяющая указатель файла, подлежащего чтению. Далее следуют аргументы — имена переменных, которым присваиваются прочитанные из файла значения. В операторе Input переменные, следующие после запятой, определяют порядок и количество данных, которые будут прочитаны из файла. Пробелы и табуляторы, расположенные в начале строки, игнорируются. Если переменная должна принимать числовые значения, то Visual Basic пытается прочесть из файла число. Первый непустой символ рассматривается как начало числа, и Visual Basic продолжает чтение до тех пор, пока не встретится какой-нибудь символ, который не может быть частью числа (например, пробел, запятая, алфавитный символ или конец строки). Если переменная является строковой, то Visual Basic читает эту строку начиная с первого непустого символа и прекращает чтение, если встречает запятую или конец строки 274
Глава 18. Запись данных в файл последовательного доступа ш первый непустой символ является символом (”), то первым символом, прочитай¬ ся в строку, будет первый символ после символа (”) и чтение будет продолжено до ?хпор, пока не встретится второй символ (”) или конец строки, т.е. будут прочитаны кссимволы, заключенные в кавычки, включая запятые. Как видите, операторы Write и Input спроектированы для совместной работы в целях алией данных в файл и последующего их чтения. С другой стороны, оператор Print проектирован для создания текстового файла, подлежащего чтению пользователем. Оператор Line Input является дополнением оператора Input. Оператор Line Input усваивает единичному строковому аргументу все символы, обнаруженные в единич- зш строке файла. Строковому аргументу —переменной присваиваются все символы, млючая начальные пробелы, запятые и кавычки. Оператор Line Input применяется ня чтения в программу точной копии содержимого строки файла. Единственными доводами, не пересылаемыми оператором Line Input, являются символы возврата яретки и перевода строки, рассматриваемые как конец строки. Например, на панели Immediate используем операторы Input и Line Input для яения содержимого текстового файла SCRATCH.TXT, изображенного на рис. 18.3. Нужно сначала открыть этот файл, а уже затем читать его с помощью операторов input и Line Input. Первая строка файла читается с помощью таких операторов: Леп ’’SCRATCH. TXT” For Input As 100 .’nput #100, B$, C$, D$ Print B$ iMCJIO Print C$ изображенное здесь Print D$ равно 12.643 и т.д. Первый оператор открывает файл, а второй читает из первой строки файла в три отдельные строковые переменные. Следующие операторы печатают на панели Imme¬ diate содержимое этих переменных. Первая строка файла имеет вид: Число, изображенное здесь, равно 12.64 3 и т.д. Первая строковая переменная получает текст, расположенный от начала строки до первой запятой, вторая —текст, расположенный между первой и второй запятыми; третья строковая переменная получает текст, расположенный после второй запятой и до конца строки. Вторая строка файла выглядит так: "Число, изображенное здесь, равно 12.643 и т.д." 275
Visual Basic for Applications в примерах Поскольку эта строка заключена в кавычки, то может быть прочитана одних оператором Input в одну строковую переменную, а затем содержимое этой переменно)! может быть напечатано на панели Immediate: Input #100, В$ Print В$ Число, изображенное здесь, равно 12.643 и т.д. а 'V: Microsoft Word' SCRATH.TXT ■31 -i Die Edit View Insert Format Iools ТдЫе Window Help №1 Н) | X |R»|e|<7| [>n ±| 0.11 Сй |Ч»| Д®|н |4i|i* I IT] |ioox 114 W (Normal | Щ (Time® New Roman | [±J |12 | [±] | В | I | U | | £ |S (^ |S | ||E |:E 1*1 |e| 1 • f • 2 • i • 3 • i • ♦ • i • в • i • 6 ■ • • 7 • i • в • i • 9 • i • Ю • i • 11 • i • 12 • i • t3 • • • И • ij ; 15 • i • ♦ Число, изображенное здесь, равно 12.643 и т.д. " Число, изображенное здесь, равно 12.643 и т.д." "Число, изображенное здесь, равно" 12.643 и т.д. "Число, изображенное здесь, равно" 12.643 и т.д. 123 456 789 123.45 678.9 Число, изображенное здесь, равно 12.643 и т.д. Число, изображенное здесь, равно 12^643 и т.д. ЕЗНПЖ V/P Рис. 18.3. Содержимое текстового файла SCRATCH.TXT Ниже приведена следующая строка файла: "Число, изображенное здесь, равно" 12.643 и т.д. Обратите внимание на то, что первая часть строки заключена в кавычки, а вторая — нет. Если на панели Immediate вы прочтете эту строку в строковую и числовую переменные, то строковая переменная получит данные, заключенные в кавычки (но без символов (")), а числовая переменная —число. Если опять прочесть из файла в строковую переменную, то эта переменная получит оставшуюся часть строки. 276
Глава 18. Запись данных в файл последовательного доступа Input #100, В$, а Print В$ Число, изображенное здесь, равно Print а 12.643 Input #100, В$ Print В$ и т.д. Следующая строка файла является копией предыдущей. Если прочесть ее опера¬ тором Line Input на панели Immediate, то вся строка, включая запятые и кавычки, будет передана в строковую переменную: Line Input #100, В$ Print В$ "Число, изображенное здесь, равно” 12.643 и т.д. Пятая строка файла содержит три числа, разделенные пробелами: 123 456 789 Если прочесть эту строку оператором Input в три числовые переменные, то эти переменные получат следующие значения (пробелы между числами будут восприняты просто как разделители): Input #100, е, f, g Print е 123 Print f 456 Print g 789 Следующая строка также содержит числа, но дополнительно — много пробелов в начале строки и между числами: 123.45 678.9 Если прочесть эту строку оператором Input в две числовые переменные, то пробелы также будут проигнорированы. 277
Visual Basic for Applications в примерах Input #100, e, f Print e 123.45 Print f 678.9 Начало седьмой строки тоже содержит много пробелов: Число, изображенное здесь, равно 12.643 и т.д. Прочтите эту строку на панели Immediate с единичной строковой переменной в качестве аргумента оператора Input. Начальные пробелы будут проигнорированы, а переменная получит текст начиная с первого непустого символа и до первой запятой В этой ситуации гораздо удобнее применять оператор Line Input: он позволит прочесть все от начала строки и до ее конца. Теперь, используя оператор Line Input, можно прочесть остаток строки (начиная от текущей позиции и до конца строки): Input #100, В$ Print В$ Число Line Input #100, В$ Print В$ изображенное здесь, равно 12.643 и т.д. Последняя строка файла идентична предыдущей строке. Если прочесть ее опера¬ тором Line Input на панели Immediate, то получим всю строку, включая пустые пробелы в начале: Line Input #100, В$ Print В$ Число, изображенное здесь, равно 12.643 и т.д. Close #100 Сохранение базы данных персональных контактов в последовательном файле После того, как вы завершили (см. предыдущую главу) работу с текущей версией базы данных персональных контактов, вы потеряли все ваши данные, так как программа сохраняла их в памяти компьютера. Эта версия программы очень неудобна в применении потому, что перезапуская программу, вы будете вынуждены повторно вводить все данные. Чтобы сделать программу более полезной, необходимо копировать 278
Глава 18. Запись данных в файл последовательного доступа вазу данных из памяти компьютера в дисковый файл перед завершением работы врограммы и базу данных из дискового файла в память компьютера — при повторном к запуске. Следующий пример добавляет к программе работы с базой данных герсональных контактов указанные возможности. Вначале сохраните предыдущую версию программы в файле с другим именем. Затем добавьте в бланк две командные кнопки: одну для записи базы данных в исковый файл, а вторую —для открытия этого файла и чтения данных в память юмпьютера. На рис. 18.4 такие кнопки добавлены в правую верхнюю часть диалого- вого окна. Рис. 18.4. Макет диалогового окна для версии базы данных персональных контактов, работающей с последовательным файлом Теперь нужно подключить две новые командные кнопки к процедурам, обеспечи¬ вающим запись данных в файл и чтение их из файла. Ниже приведен листинг модифицированной программы. Изменения, внесенные в предыдущую версию про¬ граммы, выделены жирным шрифтом. Две новые процедуры, Save_Click и Open Click, должны быть подключены к командным кнопкам "Сохр." и "Откр." бланка. 279
Visual Basic for Applications в примерах ! ' Программа работы с базой данных персональных контактов. ' Версия для последовательного файла. 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 1 'Определяет структуру записи базы данных. Option Explicit Option Base 1 Type DBEntry Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type Dim theDB() As DBEntry Dim numEntries As Integer Dim theEntryNum As Integer Dim theFil eName As String t ' Инициализация базы данных. 'Размер типа 256 байтов. 'Массив базы данных. 'Общее количество записей. 'Текущая отображаемая запись. 'Текущее имя файла. Sub Initializelt () numEntries = 0 ReDim theDB(1) theEntryNum = 0 Sheets(’’КонтактыДиалог") .DropDowns("ДЕ_Ма11_Сеть") .List = _ Array("Relcom", "Internet", "CompuServe", "Prodigy") Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Min = 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = 1 theFileName = "" With Sheets (”КонтактыДиалог") .EditBoxes ("ДИмя") .Text = .EditBoxes("ДАдрес").Text = "" .EditBoxes("ДГород").Text = "" .EditBoxes ("ДСтрана"). Text = " 280
Глава 18. Запись данных в файл последовательного доступа .EditBoxes ("ДИндекс"). Text = "” .EditBoxes ("ДТелефон") .Text = "" . DropDowns (" ДЕ_Ма±1_Сеть ") . Text = " " . Edi tBoxes (" ДЕ_Мад.1__Адрес") . Text = " " .EditBoxes (" ДСсылка") . Text = " " . EditBoxes (’’ ДПримечание") . Text = " " .Labels ("ДНомЗап") .Caption = "Запись № " End With Sheets("КонтактыДиалог").Show 2nd Sub ' Процедура Auto_open, вызываемая при каждом открытии 'рабочей папки. Sub Auto_Open () Initializelt 'Вызов процедуры инициализации. 2nd Sub 'Пересылка записи в базу данных. Sub EntryToDB (anEntry As Integer) With Sheets("КонтактыДиалог") theDB(anEntry).Name = .EditBoxes("ДИмя").Text theDB(anEntry).Address = .EditBoxes("ДАдрес").Text theDB(anEntry).City = .EditBoxes("ДГород").Text theDB(anEntry).State - .EditBoxes("ДСтрана").Text theDB(anEntry).Zip = .EditBoxes("ДИндекс”).Text theDB(anEntry).Phone = .EditBoxes("ДТелефон").Text theDB(anEntry).Net = . DropDowns ("ДЕ_МаИ_Сеть ") . Text theDB(anEntry).NetAddr = . EditBoxes ("ДЕ_МаИ_Адрес" ). Text theDB(anEntry).Referral = .EditBoxes("ДСсылка").Text theDB(anEntry).Notes = .EditBoxes("ДПримечание").Text theDB(anEntry).RecNo = anEntry End With 2nd Sub 'Пересылка записи в бланк. Sub DBToEntry (anEntry As Integer) With Sheets("КонтактыДиалог") 281
Visual Basic for Applications в ,EditBoxes("ДИмя").Text = theDB(anEntry).Name .EditBoxes("ДАдрес").Text = theDB(anEntry) ..Address .EditBoxes("ДГород").Text = theDB(anEntry).City .EditBoxes("ДСтрана").Text = theDB(anEntry).State .EditBoxes("ДИндекс").Text = theDB(anEntry).Zip .EditBoxes("ДТелефон").Text = theDB(anEntry).Phone . DropDowns ( "ДЕ_МаИ_Сеть" ) . Text = theDB(anEntry).Net . EditBoxes ( "ДЕ_Ма11_Адрес"). Text = theDB (anEntry) . NetAddr .EditBoxes("ДСсылка”).Text = theDB(anEntry).Referral .EditBoxes("ДПримечание”).Text = theDB(anEntry).Notes . Labels ("ДНомЗап" ). Caption = "Запись № " & Str (theDB (anEntry) . RecNc .ScrollBars("ПрокруткаЗап").Value = anEntry End With End Sub 9 ' Добавляет новую запись. i Sub NewEntry_Click() numEntries = numEntries + 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = numEntries ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub ' Изменяет запись. ! Sub UpdateEntry_Click() If theEntryNum = 0 Then Exit Sub 'Если еще нет записей, то выход. EntryToDB theEntryNum 'Изменение текущей записи. End Sub 'Удаляет запись. Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer Dim theMsg As String If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись. 282
Глава 18. Запись данных в файл последовательного доступа buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries theDB(I — 1) = theDB(I) theDB (I — 1) .RecNo = 1—1 Next I. numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) End If Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Value = theEntryNum Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries DBToEntry theEntryNum End Sub ' Прокрутка записей. Sub ScrollEntries_Click () If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets ("КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .Value DBToEntry theEntryNum End Sub ' Поиск имени. Sub FindName_Click () Static theName As String Dim I As Integer Const Textcomparison = 1 If numEntries = 0 Then Exit Sub 'Еще нет записей. 283
Visual Basic for Applications в примерах theName = InputBox("Введите имя для поиска", "Поиск имени", theName) - If theEntryNum о numEntries Then For I = theEntryNum + 1 To numEntries If InStrfl, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum If InStrfl, theDBfl).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox "Имя: " & theName & " не найдено.", , "Поиск имени" End Sub е Сохранение базы данных. / Sub Save_Click() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer If numEntries = 0 Then 'Есть информация для сохранения? MsgBox "Вам еще нечего сохранять.", , "PCD" Exit Sub End If 'Получение имени файла для данных. theFilter = "Базы данных PCD (*.PCD), *.PCD" Answer ~ Application. Gets aveAsFilename (theFileName, theFilter, 1) If Answer = False Then Exit Sub ' Пользователь нажал Cancel. theFileName = Trim (Answer) 'Удаление ведущих и конечных пробелов. ' Проверка имени файла на расширение . PCD theDot = InStr(theFileName, ".") If theDot == 0 Then 'Расширение не указано, добавляем .PCD. 284
Глава 18. Запись данных в файл последовательного доступа theFileName = theFileName & ".PCD” Else ' Какое-то расширение указано, удаляем его и добавляем . PCD. theFileName = Left (theFileName, theDot — 1) & ’’.PCD" End If fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Output As fileNum 'Вначале сохраним количество записей. Write #fileNum, numEntries 'Теперь обработаем в цикле Каждый элемент базы данных 'и запишем все компоненты в файл. For EntryNum = 1 То numEntries With theDB(EntryNum) Write #fileNum, .Name Write #fileNum, .Address Write #fileNum, .City Write #fileNum, .State Write #fileNum, . Zip Write #fileNum, .Phone Write #fileNum, .Net Write #fileNum, .NetAddr Write #fileNum, .Referral Write #fileNum, .Notes Write #fileNum, .RecNo End With Next EntryNum Close #fileNum 'Конец сохранения базы данных. MsgBox "База данных сохранена в файле " & theFileName, , "PCD" d Sub Открытие файла базы данных. b Open_Click () Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer Dim buttons As Integer, theMsg As String Dim Flag As Boolean If numEntries <> 0 Then 'Вначале проверяем информацию для сохранения, buttons = vbYesNoCancel + vbQuestion + vbApplicationModal theMsg = "Сохранить текущую базу данных?" Answer = MsgBox(theMsg, buttons, "PCD") 285
Visual Basic for Applications в примерах, If Answer = vbCancel Then 'Пользователь нажал Cancel, выход. Exit Sub Elself Answer = vbYes Then 'Пользователь нажал Yes, сохраняем базу. Save_Click End If End If 'Получение имени файла данных. theFilter = ’’База данных PCD (*.PCD), *.PCD" Do Answer = Application.GetOpenFilename(theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. Answer = Trim (Answer) 'Удаление ведущих и конечных пробелов. theDot = InStr (Answer, ” .) If theDot = 0 Then 'Некорректное имя файла — нет расширения. MsgBox ”Вы должны выбирать только файлы .PCD.’’, , "PCD" Flag = False Elself Right (Answer, 4) <> ’’.PCD" Then 'Некорректное расширение. MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Else Flag = True End If Loop Until Flag theFileName = Answer fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Input As fileNum 'Вначале читаем количество записей. Input #fileNum, numEntries 'Теперь подготовим массив для базы данных. ReDim theDB(numEntries) 'Теперь обработаем в цикле каждый элемент базы данных 'и прочтем все компоненты из файла. For EntryNum = 1 То numEntries With theDB(EntryNum) Input #fileNum, .Name Input #fileNum, .Address Input #fileNum, .City Input #fileNum, .State Input #fileNum, . Zip Input #fileNum, .Phone 286
Глава 18. Запись данных в файл последовательного доступа Input Input Input Input Input End With #fileNum, #fileNum, #fileNum, #fileNum, #fileNum, .Net .NetAddr .Referral .Notes .RecNo Next EntryNum Close #fileNum 'Конец сохранения базы данных. MsgBox "Файл " & theFileName & " успешно открыт.", , "PCD" Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = 1 'Пересылка первой записи базы данных в бланк. DBToEntry theEntryNum End Sub Первое изменение произведено на уровне модуля и определяет переменную :heFileName как глобальную: Sim theFileName As String 'Текущее имя файла. Переменная theFileName сохраняет имя текущего открытого файла, чтобы затем, фи сохранении базы данных, программа могла использовать это имя по умолчанию. Пледующие изменения касаются процедуры инициализации: theFileName = "" With Sheets("КонтактыДиалог") .EditBoxes("ДИмя").Text = "" .EditBoxes("ДАдрес").Text = "" .EditBoxes("ДГород").Text = "" .EditBoxes("ДСтрана").Text = "" .EditBoxes("ДИндекс").Text = "" .EditBoxes("ДТелефон").Text = "" .DropDowns ("ДЕ_МаИ_Сеть") .Text = "" . EditBoxes ("ДЕ_МаИ_Адрес" ). Text = "" .EditBoxes("ДСсылка").Text = "" .EditBoxes("ДПримечание").Text = "" .Labels("ДНомЗап").Caption = "Запись № " End With 287
Visual Basic for Applications в примерах Когда программа запускается впервые, открытого файла для базы данных еще не существует и, следовательно, процедура инициализации присваивает переменной theFileName пустую строку. Дополнительно все текстовые окна бланка очищаются от любых данных, оставшихся от предыдущего запуска программы. Затем следуют две процедуры, которые вы подключили к командным кнопкам "Сохр." и "ОткрЛ Первая процедура, Save_Click, записывает базу данных в дисковый файл. Совет: Процедуры Save_Click и Ореп_СИск очень похожи, поэтому при их создании воспользуйтесь "горячими клавишами" для копирования, чтобы сократить объен ввода текста процедур. После того как вы записали первую процедуру, прости скопируйте ее, а затем вставьте в конец модуля и скорректируйте копию дм создания второй процедуры. Первый блок процедуры объявляет локальные переменные и проверяет наличие информации для сохранения: ' Сохранение базы данных. г Sub Save_Click() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer If numEntries = 0 Then 'Есть информация для сохранения? MsgBox "Вам еще нечего сохранять.", , "PCD" Exit Sub End If Переменная Answer объявлена переменной типа Variant, так как она может получать как значение Boolean, так и значение String, возвращенные методом GetSaveAsFilename. Если в памяти отсутствует информация для сохранения, то процедура отображает диалоговое окно, изображенное на рис. 18.5, а затем завершает свою работу. Рис. 18.5. Диалоговое окно, отображенное в случае отсутствия информации для сохранения 288
Глава 18. Запись данных в файл последовательного доступа Следующий блок кода получает от пользователя имя файла: 'Получение имени файла для данных. theFilter = ’’Базы данных PCD (*.PCD), *.PCD’’ Answer = Application. GetSaveAsFilename (theFileName, theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. theFileName = Trim(Answer) 'Удаление ведущих и конечных пробелов. 'Проверка имени файла на расширение .PCD theDot = InStr (theFileName, ’’.’’) If theDot = 0 Then 'Расширение не указано, добавляем .PCD. theFileName = theFileName & ".PCD" Else 'Какое-то расширение указано, удаляем его и добавляем .PCD. theFileName = Left (theFileName, theDot — 1) & ".PCD" End If Этот код вначале готовит для диалогового окна, отображаемого методом GetSaveAs- Filename, фильтр, позволяющий отобразить имена файлов с расширением .PCD. Если существует глобальное значение имени файла, то программа использует его в качестве имени по умолчанию в диалоговом окне File Save As, как показано на рис. 18.6. Переменная Answer возвращает имя файла, выбранное пользователем. Код проверяет значение этой переменной на логическую величину False, указывающую на то, что пользователь нажал командную кнопку Cancel. Если пользователь нажал эту кнопку, то процедура завершает свою работу. Затем она проверяет расширение имени файла. Если пользователь не указал расширения, то процедура добавляет к имени файла расширение PCD по умолчанию; если пользователь указал расширение, то процедура заменяет это расширение на .PCD. Процедура определяет расширение путем поиска символа (.). Теперь у вас есть имя файла, которое можно использовать для его открытия. fhrsctMies: Рис. 18.6. Диалоговое окно File Save As, созданное методом GetSaveAsFilename Вильям Дж. Орвис 289
Visual Basic for Applications в примерах Вначале вы должны сохранить количество записей, чтобы при последующе» открытии этого файла для чтения в память процедура моглд прочесть это количество и определить, сколько записей будет прочитано. fileNum = FreeFileO 'Получение файлового числа и открытие файла. Open theFileName For Output As fileNum 'Вначале сохраним количество записей. Write #fileNum, numEntries Предупреждение: Прежде чем начать читать данные из файла, следует узнать, сколько данные содержится в файле. Если вы попробуете прочесть больше данных, чем содержим в файле, то получите ошибку. Если определить количество данных в файле ш удается, воспользуйтесь функцией ЕОЕ() для проверки файла перед каждых чтением из него. Если функция ЕОЕ() возвратит True, то вы дошли до конт файла и предпринимать попытки продолжить чтение не следует. Затем добавляете цикл обработки всех записей базы данных и блочную структуру With, сохраняющую каждый элемент записи в дисковом файле: 'Теперь обработаем в цикле каждый элемент базы данных 'и запишем все компоненты в файл. For EntryNum = 1 То numEntries With theDB(EntryNum) Write #fileNum, . Name Write #fileNum, .Address Write #fileNum, .City Write #fileNum, . State Write #fileNum, . Zip Write #fileNum, .Phone Write #fileNum, .Net Write #fileNum, .NetAddr Write #fileNum, .Referral Write #fileNum, .Notes Write #fileNum, .RecNo End With Next EntryNum Close #fileNum 'Конец сохранения базы данных. MsgBox "База данных сохранена в файле " & theFileName, , "PCD" End Sub 290
Глава 18. Запись данных в файл последовательного доступа После того как все записи базы данных будут сохранены в дисковом файле, этот файл закрывается и отображается диалоговое окно (рис. 18.7). База данных сохранена в файле l:\EXCEL\MYDB1.PCD Рис. 18.7. Диалоговое окно, отображаемое после успешной записи базы данных Процедура Open_Click в основном идентична процедуре Save_Click, с тем отли¬ чием, что она читает данные из файла, а не пишет их в файл: ' Открытие файла базы данных. Sub Open_Click() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer Dim buttons As Integer, theMsg As String Dim Flag As Boolean If numEntries <> 0 Then 'Вначале проверяем информацию для сохранения, buttons = vbYesNoCancel + vbQuestion + vbApplicationModal theMsg = "Сохранить текущую базу данных?” Answer =•MsgBox(theMsg, buttons, "PCD") If Answer = vbCancel Then 'Пользователь нажал Cancel, выход. Exit Sub Elself Answer == vbYes Then 'Пользователь нажал Yes, сохраняем базу. Save_Click End If End If Первый блок процедуры также объявляет локальные переменные. Затем процеду¬ ра проверяет существование базы данных в памяти компьютера и отображает диало¬ говое окно, приведенное на рис. 18.8. Это окно предоставляет пользователю возможность сохранить имеющиеся в памяти компьютера данные, прежде чем будет открыт новый файл. В диалоговом окне имеются три командные кнопки —Yes, No и 291
Visual Basic for Applications в Cancel. Если пользователь нажал командную кнопку Cancel, то процедура завершает свою работу. Если он нажал командную кнопку Yes, то для записи текущих данных в дисковый файл вызывается процедура Save_Click. Если пользователь нажал коман¬ дную кнопку No, то процедура продолжает свою работу со следующего блока. Рис. 18.8. Диалоговое окно, предоставляющее пользователю возможность сохранить существующие данные, перед тем как будет открыт новый файл Теперь вы знаете, что пользователь хочет открыть файл; следовательно, можно применить метод GetOpenFilename для открытия .PCD файла: ' Получение имени файла данных. theFilter = "База данных PCD (*.PCD), *.PCD" Do Answer = Application.GetOpenFilename(theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. Answer = Trim(Answer) 'Удаление ведущих и конечных пробелов. theDot = InStr(Answer, • ".") If theDot = 0 Then 'Некорректное имя файла — нет расширения. MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Elself Right(Answer, 4) <> ".PCD" Then 'Некорректное расширение. MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Else Flag = True End If Loop Until Flag theFileName = Answer Для запроса имени файла и проверки его допустимости используется структура Do-Loop. Перед тем как имя файла будет передано оператору Open, проверяется расширение .PCD. На рис. 18.9 изображено диалоговое окно, создаваемое методом GetOpenFilename. 292
Глава 18. Запись данных в файл последовательного доступа Fife jjaete: ^jfectofies: 0 2ЁВШ1ИНЙН i:\excel p “ ■ ■ * mydbl.pcd | foiA It | Cancel J Cjexemptes l^jexcelcbi CDlibfwy Co «etup CD»ktMl T Drives: T 1 g«]i: compressed | s List Fifes of J>pe: [база данных PCD (*.PCD) ]Jj Рис. 18.9. Диалоговое окно File Open, созданное методом GetOpenFilename Теперь у вас есть имя файла и вы можете, открыв его, прочесть количество записей, азатем и сами записи: fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Input As fileNum 'Вначале читаем количество записей. Input #fileNum, numEntries 'Теперь подготовим массив для базы данных. ReDim theDB(numEntries) 'Теперь обработаем в цикле каждый элемент базы данных 'и прочтем все компоненты из файла. For EntryNum = 1 То numEntries With theDB(EntryNum) Input #fileNum, .Name Input #fileNum, .Address Input #fileNum, .City Input #fileNum, .State Input #fileNum, . Zip Input #fileNum, .Phone Input #fileNum, .Net Input #fileNum, .NetAddr Input #fileNum, . Ref erra. Input #fileNum, .Notes Input #fileNum, .RecNo End With 293
Visual Basic for Applications в примерах Next EntryNum Close #fileNum 'Конец сохранения базы данных. MsgBox "Файл " & theFileName & " успешно открыт.", , "PCD" Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = 1 'Пересылка первой записи базы данных в бланк. DBToEntry theEntryNum End Sub Вы видите, почему количество записей должно быть первым элементом, сохраненные в файле: это число нужно для переопределения размера массива и для управления цикло* чтения остальных данных. После завершения процедуры появляется диалоговое окне (рис. 18.10), линейка прокрутки обновляется и первая запись помещается в бланк. Рис. 18.10. Диалоговое окно PCD, отображенное после успешного открытия и загрузки файла в память компьютера Если после сохранения этой программы, вы запустите ее, введете данные, приве¬ денные на рис. 18.11, нажмете командную кнопку "Сохр.", затем с помощью текстового процессора откроете файл, в котором были сохранены данные, то вы увидите данные в виде текста (см. рис. 18.12). База данных персональных контактов -Контактная информация Имя |Смирнов Петр | Адрес [улТверская-Я мекая, 23В | Город [Москва ~| Страна, индекс|Ро| 1123456 | Телефонр123-4567,123-4568 | EzMad: Сеть [Relcom |[±^ Адрес [peet^nippel. msk. «и | Сослаться на [Лоханкина Вас. Примечание П отенциальныА покупатель книг по программированию на Visual Basic. | Выход | L Сохр | | Откр | | Записать | £j^3MewiHTb7J [♦ДНИИЗ I Удалить | | Найти имя | Запись № 1 Рис. 18.11. Бланк базы данных персональных контактов с некоторыми данными 294
Глава 18. Запись данных в файл последовательного доступа Рис. 18.12. Дисковый файл с сохраненными данными, приведенными на рис. 18.11 Заключение В этой главе вы познакомились с последовательными дисковыми файлами. После¬ довательный дисковый файл —это текстовый файл, который читается или пишется непрерывным потоком байтов от начала и до конца. Последовательные файлы открываются оператором Open, читаются операторами Input и Line Input, а пишутся операторами Print и Write. Применяются последовательные файлы в первую очередь для информации, которая загружается в память или пишется на диск одним блоком. Программы, нуждающиеся в доступе к произвольным частям файла, должны исполь¬ зовать файлы произвольного доступа, описываемые в следующей главе. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Для чего предназначена функция FreeFile? 2. Как данные записываются в последовательный файл? 295
Visual Basic for Applications в примерах 3. Для чего предназначен спецификатор Append аргумента режим оператора Open? 4. Как вы открываете файл для записи в него данных? Для чтения данных из него? 5. Что вы должны сделать после того, как завершили использование файла? 6. Чем различаются операторы Print и Write? 7. Чем различаются операторы Input и Line Input? 8. Объясните разницу между файлами последовательного и произвольного доступа. Упражнения для закрепления материала 1. Модифицируйте процедуру Save_Click программы работы с базой данных персо¬ нальных контактов для отображения диалогового окна с предупреждением о необходимости переписать существующий файл. Диалоговое окно должно предла¬ гать две опции — отменить запись или переписать файл. Совет: просмотрите функцию Dir. 2. Модифицируйте процедуру Open_Click для отображения диалогового окна, изоб¬ раженного на рис. 18.8, только в том случае, если данные в памяти были модифицированы. Совет: создайте глобальный флаг, исходно установленный в False и изменяемый на True в случае, если какая-либо процедура изменит базу данных. 3. Добавьте к базе данных персональных контактов директиву слияния, которая открывает файл и объединяет его содержимое с данными, содержащимися в памяти компьютера. 4. Добавьте или модифицируйте процедуру, проверяющую, была ли запись модифи¬ цирована, если пользователь воспользовался линейкой прокрутки для перехода к другой записи. Если запись была модифицирована, то процедура должна отобра¬ зить диалоговое окно, запрашивающее подтверждение намерения изменить теку¬ щую запись, прежде чем перейти к следующей. 296
Глава 19 Запись данных в файл произвольного доступа Дисковые файлы произвольного доступа значительно отличаются от дисковых файлов последовательного доступа. Файл произвольного доступа рассматривается как список записей одинаковой длины, каждая из которых может быть прочитана или записана в любой момент времени и в любом порядке. Из этой главы вы узнаете, как делать следующее: ► Открывать файл произвольного доступа ► Записывать данные в файл произвольного доступа ► Читать данные из файла произвольного доступа Чтение и запись файлов произвольного доступа Файлы произвольного доступа рассматриваются как последовательность записей постоянной длины, причем каждая запись является независимой от всех других записей файла. Длина записи определяется в операторе Open, а тип данных, указан¬ ный пользователем, определяет ее содержимое. Файлы произвольного доступа исполь¬ зуются в основном для хранения двоичных представлений чисел, а не текстов. Применение двоичных чисел экономит пространство файла по сравнению с символь¬ ным представлением тех же чисел. Кроме того, двоичные числа загружаются в память компьютера значительно быстрее, так как не требуют преобразования перед исполь¬ зованием. Открытие файла Открытие файла произвольного доступа подобно открытию файла последователь¬ ного доступа оператором Open, с двумя существенными отличиями: аргумент режима всегда равен Random, а в конце оператора находится аргумент Len = длина_записи. Для открытия файла произвольного доступа используется следующий оператор Open: Open имя_файла For Random As файлов о е_ число Ъеп=длина_записи где им#—файла — строка, содержащая имя и путь файла, подлежащего открытию; файловое—число —число, которое вы хотите присвоить файлу; длина_записи —длина записи произвольного доступа в байтах. 297
Visual Basic for Applications в примерах Примечание: Оптимальная длина записи должна либо быть кратной размеру дискового сектора, либо делить указанный размер нацело. Большинство дисковых накопителей имеют сектора размером 512 или 1024 байтов; следовательно, оптимальная длина записи должна быть степенью 2, например 32, 64, 128, 256 и т.д. Такая длина является целесообразной потому, что компьютер записывает и читает данные секторами одинаковой длины. Хотя программа может запросить чтение из дискового файла всего одного байта, компьютер на самом деле прочитает в буфер памяти один или более секторов. Когда ваша программа читает данные из дискового файла, то она читает их из буфера. Когда программа прочитывает весь буфер, компьютер читает в этот буфер следующие несколько секторов из файла. Аналогичным образом данные записываются в дисковый файл: сначала они записываются в дисковый буфер памяти, а когда он заполняется, то записывается на диск. Чтобы понять, почему степень 2 является оптимальной длиной записи, предполо¬ жим, что размер дискового сектора — 512 байтов, а длина записи файла — 260 байтов. При такой длине записи большинство записей этого файла будут располо¬ жены в двух секторах, и для чтения из дискового файла такой записи потребуется прочесть два сектора. Например, запись 2 использует байты с 261 по 512 первого сектора и с 1 по 8 — второго. Применение вместо 260-байтных записей 256-байтных обеспечит упаковку в один дисковый сектор двух полных записей, что позволит для ввода одной записи читать всего один сектор. Так, следующие операторы открывают файл произвольного доступа для чтения или записи: FileNuml = FreeFile Open "myfile.txt" For Random As FileNuml Len = 256 Этот блок кода открывает файл произвольного доступа MYFILE.TXT с записями длиной 256 байтов. Функция FreeFile выбирает свободное файловое число. Примечание: Двоичные файлы подобны файлам произвольного доступа, с тем отличием, что длина записи двоичного файла равна одному байту и величина, записываемая на диск, всегда помещается в смежные байты. Закрытие файла произвольного доступа Файл произвольного доступа, так же как и последовательный файл, закрывается оператором Close: Close # файловое_число где файловое_число —файловое число, указанное при открытии этого файла. 298
Глава 19. Запись данных в файл произвольного доступа Применение оператора Туре для определения записи Оператора Туре, который вы применяли для объявления типа, определяемого чользователем, применяется также для объявления содержимого переменных типа запись, используемых для работ с файлом произвольного доступа. Оператор Туре, используемый для объявления переменных типа запись, подобен применяемому для объявления типа, который определяется пользователем, за исключением того, что при сбъявлении переменных типа запись вы не используете тип Variant и учитываете, что зсе строковые переменные должны иметь постоянную длину. Максимальная длина записи, определенной оператором Туре, должна быть не больше длины записи, объявленной оператором Open. Однако, если вы будете использовать типы меньшей пины, чем объявлено при открытии файла, то при сохранении каждой записи на пеке разность между длиной типа и длиной записи будет расходоваться впустую. Если в операторе Туре использовать строки переменной длины, то для указания пины строки потребуются два дополнительных байта на каждую строку. Если использовать переменные типа Variant, то также нужно будет добавить два байта (для задания типа) к байтам, содержащим значение переменной. Если вы используете строки переменной длины и переменные типа Variant в типе, который должен быть сохранен в записи, то общая длина типа всегда должна быть не больше длины записи; в противном случае получите ошибку. Поскольку невозможно быть уверенным в том, что общая длина переменных в записи будет соответствовать данному условию, длина каждой переменной обычно объявляется в типе. Так, приведенный ниже оператор Туре из программы работы с базой данных персональных контактов определяет запись длиной в 256 байтов. Каждая строка имеет постоянную длину, что в обычном операторе Туре не требуется. Type DBEntry ' Определяет структуру записи базы данных. Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String ★ 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type 'Размер типа 25 6 байтов. 299
Чтобы узнать длину типа, определяемого пользователем, необходимо либо сложить длины всех переменных, либо объявить переменную указанного типа, а затем применить к ней функцию Len(). Например, если вы запустите процедуру Initializelt базы данных персональных контактов с контрольной точкой после объявления массива theDB(), а затем на панели Immediate окна Debug используете функцию Len(), то получите следующее: Len(theDB(1) ) 256 Вывод данных в запись В отличие от файлов последовательного доступа для сохранения данных в файлах произвольного доступа операторы Print или Write не применяются. Вместо них применяется оператор Put. Этот оператор имеет три аргумента — файловое число, номер записи и переменную типа запись: Put # файлов о е_ число, номер_записи, переменная Так, следующий оператор Put сохраняет содержимое theDB(l) в третьей записи файла, открытого с файловым числом 1: Put #1, 3, theDB(1) Если вы опустите номер записи в операторе Put, то будет использован номер, следующий за тем, который был использован в последнем из предыдущих операторов Get, Put или Seek. Ввод данных из записи Для выборки записи из дискового файла и загрузки ее в переменную типа запись применяется оператор Get. Оператор Get имеет такой же синтаксис, как и оператор Put. Он также имеет три аргумента — файловое число, номер записи и переменную типа запись, получающую данные из записи на диске: Get #файловое_число, номер_записи, переменная Реорганизация данных в файле произвольного доступа При необходимости реорганизовать данные в последовательном файле потребуется сначала прочесть его, затем реорганизовать данные, а потом переписать файл на диске в соответствии с новой организацией. Файлы произвольного доступа предоставляют для этих целей дополнительную возможность —использование индекса, что позволит реорганизовывать индекс, а не сам файл. 300
Глава 19. Запись данных в файл произвольного доступа Чтобы создать индекс, вначале создается массив целых чисел, в котором каждый элемент содержит один (свой) номер записи файла произвольного доступа. Затем этот массив, позволяющий определить номер записи, используется для получения доступа к записи файла произвольного доступа. Такая организация хранения записи файла называется индексированные записи. Например, если переменная I содержит номер требуемой записи, а переменная типа запись называется RecVar, то обычный доступ к файлу произвольного доступа обеспечивается следующим оператором Get: Get #fileNum, I, RecVar Если массив индексов называется thelndexO, то метод доступа изменяется таким образом: Get #fileNum, thelndex(I), RecVar Первоначально каждый элемент массива thelndexO содержит свой собственный номер: thelndex(l) = 1 thelndex(2) = 2 thelndex(3) = 3 Результат использования такого массива для доступа к записям файла идентичен прямому доступу к записям. Предположим, вам потребовалось упорядочить записи в алфавитном порядке, для чего необходимо поменять местами вторую и третью записи. Вы можете, физически поменять местами записи на диске либо просто изменить значения в массиве индексов следующим образом: thelndex(l) = 1 thelndex(2) = 3 thelndex(3) = 2 301
Visual Basic for Applications в примерах Применяя массив индексов для доступа к файлу, вы будете получать записи в соответствии с указанным порядком, а не в порядке их физического расположения на диске. Индексированные записи очень удобны для группирования и повторного использо¬ вания удаленных записей. Для удаления записи, переместите ее номер в конец массива индексов, а затем оставшиеся индексы переместите на один элемент вверх. Следующий индексный номер определяется на границе использованных и свободных записей. Если нужно записать новую запись, то, прежде чем ее разместить, следует проверить, имеет ли массив индексов записи, пригодные для повторного использования. При работе с индексированными записями нужно решить, что делать с индексом. Его можно сохранить в отдельном последовательном файле или разместить в виде нескольких записей в начале файла произвольного доступа. Запись базы данных персональных контактов в файле произвольного доступа Как вы уже догадались, базу данных персональных контактов можно легко адаптировать для работы с файлами произвольного доступа. Пока ваша база данных невелика и может целиком поместиться в памяти компьютера, хранение ее в последо¬ вательном файле упрощает и убыстряет доступ к записям, так как они постоянно находятся в памяти компьютера. Однако, если база будет увеличиваться, вы будете вынуждены переключиться на файл произвольного доступа и хранить все записи, кроме той, которая отображается в данный момент, на диске. Для того чтобы определить размер базы данных, достаточно просто умножить размер записи на количество записей. Для базы данных персональных контактов умножьте 256 байтов на количество записей и сравните полученный результат с объемом памяти вашего компьютера. В большинстве случаев вы увидите, что более целесообразно использовать файл произвольного доступа. Изменения базы данных персональных контактов заключаются в том, что при использовании файлов произвольного доступа потребуется заменить массив theDB() переменной типа запись theDB; командная кнопка ”Сохр." больше не будет нужна, так как база данных будет постоянно храниться на диске; вместо нее потребуется новая кнопка "Созд." для создания нового файла базы данных. Если вы хотите сохранить предыдущую версию программы, запишите ее на диск под другим именем. Затем измените название командной кнопки "Сохр." на "Созд." (см. рис. 19.1) и подключите эту командную кнопку к процедуре New_Click, созданную вместо процедуры Save_Click. Подключите командную кнопку "Выход" к процедуре Quit_Ciick, которую вы создадите. Следующим шагом будет изменение программы в соответствии с приведенным ниже листингом. Изменения предыдущей версии программы выделены жирным шрифтом: 302
Глава 19. Запись данных в файл произвольного доступа База данных персональных контактов г | [Выход; I Имя | | | | | Созд | | Откр | Адрес] | Примечание | “ i 1 Записать | Г орал | | | Изменить | Страна, индекс| | | | И I»1 мдирИ ♦ 1 Т елефон] | У | Удалить | Е:Мвй: Сеть | |[±j Ц | Найти имя | Адрес| | ffl Запись № Рис. 19.1. Макет бланка базы данных персональных контактов для версии, использующей файл произвольного доступа 'Программа работы с базой данных персональных контактов. ' Версия для файла произвольного доступа. Option Explicit Option Base 1 Type DBEntry 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 1 'Определяет структуру записи базы данных. Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type 'Размер типа 256 байтов. Type FirstReсТуре 'Определяет структуру первой Записи файла. numEntries As Integer End Type 'Остальная часть записи свободна для дополнений. Dim FirstRec As FirstReciype 'Переменная типа запись для первой записи. 303
Visual Basic for Applications в примерах Dim theDB As DBEntrv ' Переменная типа Запись. Dim theEntryNum As Integer Dim theFileName As String Dim fileNum As Integer 'Текущая отображаемая запись. 'Текущее имя файла. ' Файловое число для открытого файла. ' Инициализация базы данных. Sub Initializelt() FirstRec.numEntries = 0 ' ReDim thedb(l) theEntryNum = 0 Sheets ("КонтактыДиалог”) .DropDowns ("ДЕ_МаИ_Сеть ") .List = _ Array("Relcom", "Internet", "CompuServe", "Prodigy") 'Установим значение Min = 2, т.к. первая запись файла 'содержит общее количество записей. Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Min = 2 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Мах = 2 theFileName = "" With Sheets("КонтактыДиалог") .EditBoxes("ДИмя").Text = "" .EditBoxes("ДАдрес").Text = "" .EditBoxes("ДГород").Text = "" .EditBoxes("ДСтрана").Text = "" .EditBoxes("ДИндекс").Text = "" .EditBoxes("ДТелефон").Text = "" . DropDowns ("ДЕ_МаИ_Сеть"). Text = "" . EditBoxes ("ДЕ_МаИ_Адрес" ). Text = "" .EditBoxes("ДСсылка").Text = "" .EditBoxes("ДПримечание").Text = "" .Labels("ДНомЗап").Caption = "Запись № " End With Sheets("КонтактыДиалог").Show End Sub r ' Процедура Auto_open, вызываемая при каждом открытии ' рабочей папки. Sub Auto_Open() Initializelt 'Вызов процедуры инициализации. End Sub 304
Глава 19. Запись данных в файл произвольного доступа ' Пересылка записи в базу данных. Sub EntryToDB (anEntry As Integer) With Sheets("КонтактыДиалог") theDB.Name = .EditBoxes("ДИмя").Text theDB. Address = .EditBoxes("ДАдрес").Text theDB.City = .EditBoxes("ДГород").Text theDB.State = .EditBoxes("ДСтрана").Text theDB.Zip = .EditBoxes("ДИндекс").Text theDB.Phone = .EditBoxes("ДТелефон").Text theDB.Net = . DropDowns ("ДЕ_МаИ_Сеть") . Text theDB.NetAddr = . EditBoxes ( "ДЕ_Ма11_Адрес").Text theDB.Referral = .EditBoxes("ДСсылка").Text theDB.Notes = .EditBoxes("ДПримечание").Text theDB.RecNo = anEntry End With Put #fileNum, anEntry, theDB End Sub ' Пересылка записи в бланк. Sub DBToEntry(anEntry As Integer) Get #fileNum, anEntry, theDB With Sheets("КонтактыДиалог") . EditBoxes("ДИмя").Text = theDB.Name .EditBoxes("ДАдрес").Text = theDB.Address .EditBoxes("ДГород").Text = theDB.City .EditBoxes("ДСтрана").Text = theDB.State . EditBoxes("ДИндекс").Text = theDB.Zip .EditBoxes("ДТелефон").Text = theDB.Phone .DropDowns("ДЕ_Ма11_Сеть").Text = theDB.Net .EditBoxes ("ДЕ_МаИ_Адрес") .Text = theDB.NetAddr . EditBoxes("ДСсылка") .Text = theDB.Referral . EditBoxes("ДПримечание") .Text = theDB.Notes . Labels("ДНомЗап").Caption = "Запись № " & Str(theDB.RecNo — 1) .ScrollBars("ПрокруткаЗап”).Value = anEntry End With End Sub 305
Visual Basic for Applications в примерах ' Добавляет новую запись. г Sub NewEntry_Click() If FirstRec.numEntries = 0 Then 'Проверяет, открыт ли файл. MsgBox "Вначале откройте файл.", , "PCD" Exit Sub End If Fir stRec. numEntries = Firs tRec. numEntries + 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = _ FirstRec.numEntries theEntryNum = Firs tRec. numEntries Put #fileNum, 1, FirstRec EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub f ' Изменяет запись. t Sub UpdateEntry_Click() 'Если еще нет записей, то выход. If FirstRec.numEntries < 2 Then Exit Sub EntryToDB theEntryNum 'Изменение текущей записи. End Sub 'Удаляет запись. Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer Dim theMsg As String If FirstRec.numEntries < 2 Then Exit Sub 'Еще нет Записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo 4- vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = FirstRec.numEntries Then 'Это последняя запись. FirstRec. numEntries = Fi raffipc. mimEnt.ri ея — 1 theEntryNum = FirstRec.numEntries 306
Глава 19. Запись данных в файл произвольного доступа Put #fileNum, 1, FirstRec Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To FirstRec.numEntries Get #fileNum, I, theDB theDB.RecNo =1—1 Put #fileNum, I, theDB Next I FirstRec. numEntries = FirstRec. numEntries — 1 Put #fileNum, 1, theDB End If Sheets("КонтактыДиалог”) ,ScrollBars("ПрокруткаЗап") .Value = theEntryNum Sheets ("КонтактыДиалог”). ScrollBars (’’ПрокруткаЗап" ). Max = _ FirstRec.numEntries DBToEntry theEntryNum ind Sub ' Прокрутка записей. Sub ScrollEntries_Click () If FirstRec.numEntries < 2 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап”).Value DBToEntry theEntryNum End Sub ' Поиск имени. Sub FindName_Click () Static theName As String Dim I As Integer Const Textcomparison = 1 If FirstRec.numEntries < 2 Then F.xi t Sub 'Еще нет записей. theName = InputBox("Введите имя для поиска”, "Поиск имени", theName) If theEntryNum о FirstRec.numEntries Then Get #fileNum, I, theDB For I = theEntryNum + 1 To FirstRec.numEntries If InStr (1, theDB.Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. 307
Visual Basic for Applications в примерах theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 2 To theEntryNum Get # fileNum., I, theDB If InStr(1, theDB.Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox "Имя: " & theName & " не найдено.", , "Поиск имени" End Sub f 1 Создание нового файла базы данных. t Sub New_Click() Dim theFilter As String, Answer, theDot As Integer 'Получение имени файла для данных. theFilter = "Базы данных PCD (*.PCD), *.PCD" Answer = Application.GetSaveAsFilename(theFileName, theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. theFileName = Trim(Answer) 'Удаление ведущих и конечных пробелов. ' Проверка имени файла на расширение .PCD theDot = InStr(theFileName, ".") If theDot = 0 Then 'Расширение не указано, добавляем .PCD. theFileName = theFileName & ".PCD" Else 'Какое-то расширение указано, удаляем его и добавляем .PCD. theFileName = Left (theFileName, theDot — 1) & ".PCD" End If Close 'Закрытие существующего файла. fileNum — FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Random As fileNum Len = 256 FirstRec.numEntries = 1 'Помечает файл базы данных как пустой. theEntryNum = О 308
Глава 19. Запись данных в файл произвольного доступа Put #fileNum, 1, FirstRec 'Сохранение количества записей. 'Задание значения Min линейки прокрутки. Sheets ("КонтактыДиалог") . ScrollBars (”ПрокруткаЗап") .Min = 2 'Очистка бланка. With Sheets("КонтактыДиалог") .EditBoxes("ДИмя").Text = "" .EditBoxes ("ДАдрес") .Text = "" .Edi tBoxes("ДГород") .Text = "" . EditBoxes (" ДСтрана") . Text = " " .EditBoxes (" ДИндекс") . Text = " " .EditBoxes("ДТелефон") .Text = . DropDowns (" ДЕ_Ма11_Сеть ") . Text = " " . EditBoxes (" ДЕ_Ма11_Адрес") . Text = " " .EditBoxes (" ДСсылка") . Text = " " . EditBoxes (" ДПримечание") . Text = " " .Labels("ДНомЭan").Caption = "Запись № " End With MsgBox "Создан новый файл базы данных: " & theFileName, , "PCD" End Sub i ' Открытие файла базы данных. Sub Open_Click() Dim theFilter As String, Answer, theDot As Integer Dim Flag As Boolean ’Получение имени файла данных. theFilter = ’’База данных PCD (*.PCD), *.PCD" Do Answer = Application.GetOpenFilename(theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. Answer = Trim(Answer) 'Удаление ведущих и конечных пробелов. theDot = InStr (Answer, ’’. ” ) If theDot = 0 Then 'Некорректное имя файла — нет расширения. MsgBox "Вы должны выбирать только файлы .PCD.’’, , "PCD" Flag = False Elself Right(Answer, 4) <> ’’.PCD" Then 'Некорректное расширение. MsgBox "Вы должны выбирать только файлы .PCD.’’, , "PCD" Flag = False Else Flag = True 309
Visual Basic for Applications в примерах End If Loop Until Flag theFileName = Answer Close 'Закрывает предыдущий файл данных. fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Random As fileNum Len =256 'Вначале читаем количество записей. Get #fileNum, 1, FirstRec MsgBox "Файл " & theFileName & ’’ успешно открыт.”, , "PCD” Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = _ FirstRec.numEntries If FirstRec.numEntries > 1 Then 'Если в файле есть записи базы данных theEntryNum — 2 ' Пересылка первой записи базы данных в бланк. DBToEntry theEntryNum End If End Sub ' Процедура завершает выполнение программы. / Sub Quit_Cliok() 'Закрывает все открытые файлы. Close End Sub Примечание: Многие из приведенных ниже выдержек содержат выделенные жирным шрифтом изменения, включающие строки, которые должны быть удалены из предыдущей версии программы. Эти строки преобразованы в строки комментариев, чтобы вы могли видеть все изменения, которые необходимо произвести для создания данной версии программы. Эта программа открывает файл данных процедурами New_Click и Open_Click, а закрывает его процедурой Quit_Click. В качестве альтернативного подхода можно держать файл закрытым и открывать только тогда, когда нужно прочесть или записать запись. Этот второй подход занимает несколько больше времени, так как требует открытия файла для каждого доступа к нему. Однако он имеет немаловажное преимущество: если программа терпит крах, то маловероятно, что в этот момент файл данных будет открыт и, следовательно, что он будет разрушен. Чтобы реализовать эту альтернативную версию программы, окружите каждый оператор Get и Put (или небольшие блоки, включающие эти операторы) операторами Open и Close. 310
Глава 19. Запись данных в файл произвольного доступа Преобразование версии программы, сохраняющей данные в массиве, в программу, охраняющую данные в файле произвольного доступа, потребует весьма незначитель¬ ных изменений. Первой проблемой является определение места сохранения перемен¬ ной numEntries, которая не является частью какой-либо записи и не входит напрямую I структуру записи файла. Эту проблему можно решить, зарезервировав первую йпись файла для параметров настройки и переместив первую запись данных во вторую апись файла. Чтобы быть уверенным в том, что первая запись файла никогда не тобразится, следует внести в программу небольшие изменения. В секции объявлений модуля замените объявление массива theDB() объявлением еременной типа запись theDB. Во всей программе удалите от theDB круглые скобки добавьте оператор Get для загрузки в theDB соответствующей записи. Определите эвый тип переменной, позволяющий сохранить numEntries в первой записи файла, определите переменную типа запись с именем FirstRec как переменную этого нового [па. Замените во всей программе numEntries на FirstRec.numEntries. Поскольку одна ременная не заполнит всю первую запись, можно использовать оставшееся простран- во для хранения других параметров настройки, например имени базы данных. Кроме го, в заголовке программы объявляется глобальная переменная fileNum, содержащая тивное файловое число. Программа работы с базой данных персональных контактов. Версия для файла произвольного доступа.’ tion Explicit tion Base 1 ?e DBEntry 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 'Определяет структуру записи базы данных. 1. Name As String * 25 Address As* String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer 1 Type 'Размер типа 256 байтов. e FirstRecType 'Определяет структуру первой записи файла. numEntries As Integer 311
Visual Basic for Applications в примерах End Type 'Остальная часть записи свободна для дополнении. Dim FirstRec As FirstRecType 'Переменная типа запись для первой записи. Dim theDB As DBEntry ' Переменная типа запись. 'Dim thedb() As DBEntry 'Dim numEntries As Integer Dim theEntryNum As Integer Dim theFileName As String Dim fileNum As Integer 'Массив базы данных. 'Общее количество записей. 'Текущая отображаемая запись. 'Текущее имя файла. ' Файловое число для открытого файла Удалите оператор ReDim в процедуре Initialized, так как массив больше не используется. Кроме того, измените начальные минимальное и максимальное значения для линейки прокрутки на 2, поскольку первой записью базы данных является вторая запись файла. ' Инициализация базы данных. Sub Initializelt () FirstRec.numEntries = О ' ReDim thedb(l) theEntryNum = 0 Sheets("КонтактыДиалог").DropDowns("ДЕ_Ма11_Сеть").List = _ Array("Relcom""Internet", "CompuServe", "Prodigy") 'Установим значение Min = 2, т.к. первая запись файла 'содержит общее количество записей. Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Min = 2 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Мах = 2 theFileName = "” With Sheets("КонтактыДиалог") .EditBoxes("ДИмя").Text = "" .EditBoxes("ДАдрес").Text = "" .EditBoxes("ДГород").Text = "" .EditBoxes("ДСтрана").Text = "" .EditBoxes("ДИндекс").Text = "" .EditBoxes("ДТелефоН").Text = "" . DropDowns ("ДЕ_МаИ_Сеть "). Text = "" .EditBoxes ("ДЕ_МаИ_Адрес") .Text = "" .EditBoxes("ДСсылка").Text = "" .EditBoxes("ДПримечание").Text = "" .Labels("ДНомЗап").Caption = "Запись № " End With 312
Глава 19. Запись данных в файл произвольного доступа Sheets("КонтактыДиалог").Show 2nd Sub ' Процедура Auto__open, вызываемая при каждом открытии ' рабочей папки. Sub Auto_Open () InitializeIt 'Вызов процедуры инициализации. ■nd Sub Выполните несколько изменений процедуры NewEntry_Click. Прежде чем вста¬ вить новую запись, необходимо убедиться в том, что файл открыт. Если он не открыт, то добавить новую запись нельзя. До тех пор, пока файл не будет открыт, переменная numEntries равна 0, что позволяет использовать ее для проверки факта открытия файла. Затем нарастите количество записей и используйте оператор Put для сохране¬ ния этого количества в первой записи файла. ' Добавляет новую запись. Sub NewEntry_Click () If FirstRec.numEntries — 0 Then 'Проверяет, открыт ли файл. MsgBox "Вначале откройте файл.", , "PCD" Exit Sub End If FirstRec.numEntries = FirstRec. numEntries + 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = _ FirstRec.numEntries theEntryNum = FirstRec.numEntries ' ReDim Preserve thedb (numEntries) 'Увеличение массива. Put #fileNum, 1, FirstRec EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub Процедура UpdateEntry_Click должна проверять существование записи, так как изменить несуществующую запись нельзя. Поскольку первой записью базы данных является вторая запись файла и, следовательно, если numEntries меньше 2, то никаких действий производить не нужно. 373
Visual Basic for Applications в примерах ' Изменяет запись. Sub UpdateEntry_Click() 'Если еще нет записей, то выход. If FirstRec.numEntries < 2 Then Exi t Sub EntryToDB theEntryNum 'Изменение текущей записи. End Sub В процедурах EntryToDB и DBToEntry замените массив thedbO на перемените типа запись theDB. Процедура EntryToDB использует оператор Put для записи данных в файл, а процедура DBToEntry использует оператор Get для чтения данные из файла. При отображении номера записи процедурой DBToEntry необходимо вычесть из него 1, так как в бланке должен быть отображен номер записи базы данных а не дискового файла. ' Пересылка записи в базу данных. г Sub EntryToDB(anEntry As Integer) With Sheets("КонтактыДиалог") theDB.Name = .EditBoxes("ДИмя").Text theDB.Address = .EditBoxes("ДАдрес").Text theDB.City = .EditBoxes("ДГород").Text theDB.State = .EditBoxes("ДСтрана").Text theDB.Zip = . EditBoxes ("ДИндекс").Text theDB.Phone = .EditBoxes("ДТелефон").Text theDB.Net = .DropDowns("ДЕ_Ма11_Сеть").Text theDB.NetAddr = .EditBoxes("ДЕ_Ма11_Адрес").Text theDB.Referral = .EditBoxes("ДСсылка").Text theDB.Notes = .EditBoxes("ДПримечание”).Text theDB.RecNo = anEntry End With Put #fileNum, anEntry, theDB End Sub F ' Пересылка записи в бланк. Sub DBToEntry(anEntry As Integer) Get #fileNum, anEntry, theDB With Sheets("КонтактыДиалог") 314
Глава 19. Запись данных в файл произвольного доступа . EditBoxes ("ДИмя" ). Text = theDB.Name . EditBoxes (’’ДАдрес" ). Text = theDB.Address .EditBoxes("ДГород").Text = theDB.City .EditBoxes("ДСтрана").Text = theDB.State .EditBoxes("ДИндекс").Text = theDB.Zip .EditBoxes("ДТелефон").Text = theDB.Phone . DropDowns ( "ДЕ_МаИ_Сеть" ) . Text = theDB.Net . EditBoxes("ДЕ_Ма11_Адрес") .Text = theDB.NetAddr .EditBoxes("ДСсылка").Text = theDB.Referral .EditBoxes("ДПримечание").Text = theDB.Notes .Labels("ДНомЗап").Caption = "Запись № ’’ & Str (theDB. RecNo — 1) .ScrollBars("ПрокруткаЗап").Value = anEntry End With End Sub Процедура Scrol!Entries_Click должна, аналогично процедуре UpdateEntry_Click, проверять существование записей, так как нельзя перейти к несуществующей записи. ' Прокрутка записей. Sub ScrollEntries_Click() If Firs tR.ec. numEntries < 2 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Value DBToEntry theEntryNum End Sub Добавьте в процедуру DeleteEntry контроль существования записи. Когда изме¬ няется значение numEntries, оператор Put сохраняет новое значение в первой записи файла. Для перемещения оставшихся записей на одну вверх используйте операторы Get и Put. 'Удаляет запись. Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer Dim theMsg As String If FirstRec.numEntries < 2 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + 315
Visual Basic for Applications в примерах vbAppli cat i onModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = FirstRec.numEntries Then 'Это последняя запись. FirstRec.numEntries = FirstRec.numEntries - 1 ' ReDim Preserve theDB(numEntries) theEntryNum = FirstRec.numEntries Put #fileNum, 1, FirstRec Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To FirstRec.numEntries Get #fileNum, I, theDB theDB.RecNo =1-1 Put #fileNum, I, theDB ' theDB(I - 1) = theDB(I) Next I FirstRec.numEntries = FirstRec.numEntries - 1 ' ReDim Preserve theDB(numEntries) Put #fileNum, 1, theDB End If Sheets ("КонтактыДиалог") .ScrollBars ("ПрокруткаЗап") .Value = theEntryNum Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = _ FirstRec.numEntries DBToEntry theEntryNum @ME - End Sub Процедура FindName_Click также должна проверять существование записей. Добавьте оператор Get для загрузки записи, в которой процедура будет искать требуемую строку. ' Поиск имени. г Sub FindName_Click() Static theName As String Dim I As Integer Const Textcomparison = 1 If FirstRec.numEntries < 2 Then Exit Sub 'Еще нет записей. theName = InputBox("Введите имя для поиска", "Поиск имени", theName) 316
Глава 19. Запись данных в файл произвольного доступа If theEntryNum о FirstRec. numEntries Then Get #fileNum, I, theDB For I = theEntryNum + 1 To FirstRec.numEntries If InStr(1, theDB.Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 2 To theEntryNum Get #f ileNum, I, theDB If InStr(1, theDB.Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox ’’Имя: ” & theName & ” не найдено.”, , "Поиск имени" End Sub Замените процедуру Save_Click на New_Click, позволяющую создать новый файл базы данных: ' Создание нового файла базы данных. iub New_Click() Sub Save_Clic.k() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer If numEntries = 0 Then 'Есть информация для сохранения? MsgBox "Вам еще нечего созфанять.", , "PCD" Exit Sub End If 'Получение имени файла для данных. theFilter = "Базы данных PCD (*.PCD), *.PCD" Answer = Application.GetSaveAsFilename(theFileName, theFilter, 1) 317
Visual Basic for Applications в примерах If Answer = False Then Exit Sub 'Пользователь нажал Cancel. theFileName = Trim (Answer) 'Удаление ведущих и конечных пробело: ' Проверка имени файла на расширение .PCD theDot = InStr(theFileName, If theDot = 0 Then 'Расширение не указано, добавляем .PCD. theFileName = theFileName & ".PCD" Else 'Какое-то расширение указано, удаляем его и добавляем .PCD. theFileName = Left(theFileName, theDot - 1) & ".PCD" End If Close 'Закрытие существующего файла. fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Random As fileNum Len =256 FirstRec.numEntries = 1 'Помечает файл базы данных как пустой. theEntryNum = О Put #fileNum, 1, FirstRec 'Сохранение количества записей. ' Задание значения Min линейки прокрутки. Sheets (" КонтактыДиалог") . ScrollBars (" ПрокруткаЗап") .Min = 2 ' Очистка бланка. With Sheets("КонтактыДиалог") .EditBoxes ("ДИмя") .Text = "" .EditBoxes ("ДАдрес") .Text = "" .EditBoxes ("ДГород") .Text = "" .EditBoxes ("ДСарана"). Text = "" .EditBoxes (" ДИндекс") . Text = " " .EditBoxes ("ДТелефон") .Text = "" . DropDowns (" ДЕ_Ма11_Сеть ") . Text = " " .EditBoxes (" ДЕ_Ма11_Адрес") . Text = " " . EditBoxes (" ДСсыпка") . Text = " " .EditBoxes (" ДПримечание" ) . Text = " " .Labels ("ДНомЗап") .Caption = "Запись М» " End With 'Вначале сохраним количество записей. Write #fileNum, numEntries 'Теперь обработаем в цикле каждый элемент базы данных 'и запишем все компоненты в файл. For EntryNum = 1 То numEntries With theDB (EntryNum) Write #fileNum, .Name Write #fileNum, .Address Write #fileNum, .City 318
Глава 19. Запись данным в файл произвольного доступа Write #f ileNum, Write #fileNum, Write #fileNum, Write #f ileNum, Write #fileNum, Write #fileNum, Write #fileNum, .State . Zip .Phone .Net .NetAddr .Referral .Notes .RecNo Write #fileNum, End With 1 Next EntryNum ' Close #fileNum 'Конец сохранения базы данных. MsgBox "Создан новый файл базы данных: " & theFileName, End Sub в pcD" Первая половина процедуры в основном работает так же, как и прежде, получая имя для нового файла. Однако теперь процедура не проверяет существование файла, который должен быть сохранен, так как эта версия программы сохраняет данные в ходе работы с базой. Вторая половина процедуры открывает файл, инициализирует первую запись и очищает бланк. Блок программы, сохраняющий базу данных, теперь также не нужен. Примечание: Блок кода, очищающий бланк, может быть скопирован из процедуры инициали¬ зации. Если потребуется очищать бланк более часто, то следует выделить этот код в отдельную процедуру и при необходимости вызывать ее. Процедура Open_Click работает аналогично предыдущей версии: ' Открытие файла базы данных. Sub Open_Click () Dim theFilter As String, ' Dim fileNum As Integer, ' Dim buttons As Integer, Dim Flag As Boolean ' If numEntries <> 0 Then Answer, theDot As Integer , EntryNum As Integer theMsg As String ' Вначале проверяем информацию для сохранения. buttons = vbYesNoCancel + vb Quest! on + vbApplicationModal th eMsg - "Сохранить текущую базу данных?" Answer - MsgBox(theMsg, buttons, "PCD") If Answer = vbCaneel Then 'Пользователь нажал Cancel, выход. Exit Sub 319
Visual Basic for Applications в примерах Elself Answer = vbYes Then ' Пользователь нажал Yes, сохраняем базу. Save_Click End If End If 'Получение имени файла данных. theFilter = ’’База данных PCD (*.PCD), *.PCD” Do Answer = Application.GetOpenFilename(theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. Answer = Trim(Answer) 'Удаление ведущих и конечных пробелов. theDot = InStr (Answer, ” .) If theDot = 0 Then 'Некорректное имя файла — нет расширения. MsgBox ”Вы должны выбирать только файлы .PCD.’’, , "PCD” Flag = False Elself Right(Answer, 4) <> ".PCD" Then 'Некорректное расширение. MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Else Flag = True End If Loop UnLil Flag theFileName = Answer Close 'Закрывает предыдущий файл данных. fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Random As fileNum Len = 256 'Вначале читаем количество записей. Get #fileNum, 1, FirstRec 'Теперь подготовим массив для базы данных. ReDim theDB(numEntries) 'Теперь обработаем в цикле каждый элемент базы данных 'и прочтем все компоненты из файла. For EntryNum = 1 То numEntries With theDB (EntryNum) / Input # fileNum, .Name f Input # fileNum, .Address t Input # fileNum, . City f Input # fileNum, .State f Input # fileNum, . Zip ! Input # fileNum, .Phone f Input # fileNum, .Net 320
Глава 19. Запись данных в файл произвольного доступа I I Input #fileNum, Input #fileNum, Input #fileNum, Input #fileNum, End With .NetAddr .Referral .Notes .RecNo Next EntryNum Close #fileNum 'Конец сохранения базы данных. MsgBox "Файл " & theFileName & " успешно открыт."z , "PCD" Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = FirstRec.numEntries If FirstRec.numEntries > 1 Then 'Если в файле есть записи базы данных theEntryNum = 2 ' Пересылка первой записи базы данных в бланк. DBToEntry theEntryNum End If End Sub Первая половина процедуры получает и проверяет имя файла. Проверять наличие несохраненных данных больше не нужно, так как все данные сохраняются по ходу работы. Вторая половина процедуры открывает файл и читает первую запись файла в переменную типа запись FirstRec для получения значения numEntries. Теперь может быть создан файл, не содержащий ни одной записи; следовательно, перед отображе¬ нием первой записи базы данных необходимо проверять существование записей. Процедура Quit_Click — новая процедура, закрывающая файл данных, если пользователь нажал командную кнопку "Выход". Без этой процедуры файл может остаться открытым, что приведет к ошибке. ' Процедура завершает выполнение программы. Sub Quit_Click() 'Закрывает все открытые файлы. Close End Sub Примечание: Если при работе с этой программой, вы получите ошибку типа "File already open " ("Файлуже открыт"), то наиболее вероятно, что программа аварийно завершилась с открытым файлом. Когда вы запустите эту программу повторно, она попробует открыть уже открытый файл и получит ошибку. Чтобы исправить это, закройте все открытые файлы, выполнив команду Close на панели Immediate окна Debug. 11 Вильям Дж. Орвис 321
Visual Basic for Applications в примерах Внеся все описанные изменения, вы преобразуете программу работы с базой данных персональных контактов для использования дисковых файлов произвольного доступа. Сохраните рабочую папку, а затем запустите программу, чтобы проверить ее работу. Не забывайте создавать файл, перед тем как вводить данные в бланк. Заключение В этой главе рассмотрено применение дисковых файлов произвольного доступа для записи данных. Файлы произвольного доступа значительно отличаются от последовательных файлов. Как вы помните из гл. 18, последовательные файлы манипулируют данными, рассматриваемыми в качестве непрерывного потока от начала файла и до его конца. Файлы произвольного доступа манипулируют данными в виде независимых блоков, называемых записями, которые могут быть сохранены или получены из дискового файла в любом порядке. При открытии файлов произвольного доступа используется режим Random, а спецификатор Len = длина_записи определяет длину записи файла. Для чтения и записи данных в файл применяются операторы Get и Put. Следующая глава расскажет о записи данных в файлы таблиц. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Как открыть файл произвольного доступа? 2. Для чего предназначен спецификатор Len =в операторе открытия файла произ¬ вольного доступа? 3. Какие два оператора применяются для чтения и записи данных из файлов произвольного доступа? 4. Если вы читаете запись из файла произвольного доступа, то должны ли вы сохранить ее на том же самом месте? 5. Что такое ”переменная типа запись"'} Чем она отличается от обычной переменной? 6. Что такое "индексированная запись"') Упражнения для закрепления материала 1. Выделите из программы работы с базой данных персональных контактов код, очищающий бланк, и преобразуйте его в отдельную процедуру. Затем вызовите эту процедуру в тех двух местах, откуда выделен код. 322
Глава 19. Запись данных в файл произвольного доступа Добавьте в процедуры NewEntry_Click, UpdateEntry_Click, ScrollEntries_Click, DeleteEntry_Click и FindName_Click сообщения для пользователя о том, что из-за отсутствия доступных данных никаких действий произведено не будет. Добавьте процедуры, которые отслеживают количество символов, записываемых в поля бланка и издающих звуковой сигнал, если пользователь пытается ввести больше символов, чем определено для этого поля. Напишите процедуру, обеспечивающую преобразования содержимого поля "Стра¬ на" в прописные буквы, вне зависимости от того, что ввел пользователь. Автома¬ тически измените любой текст, записанный строчными буквами: строчные буквы замените прописными. Замените поле "Страна" выпадающим списком, содержащим аббревиатуры стран (по вашему усмотрению). Измените метод сохранения, предусмотрев использование индексированных запи¬ сей. Перемещайте индекс удаленной записи в конец массива, вместо того чтобы переписывать содержимое оставшихся записей. Прежде чем добавлять новые записи в файл, используйте удаленные. Напишите процедуру, упорядочивающую записи в алфавитном порядке (по фамилии). 323
Глава 20 Другие способы записи данных Дисковые файлы последовательного и произвольного доступа являются основны ми средствами хранения данных для программ на Visual Basic. Однако данные на диске можно сохранить и другими способами, например в таблицах открытой рабочей папки или в файлах внешней базы данных. Доступ к таблице обеспечивается методами, которые неоднократно упоминались в нашей книге. Для доступа к внешней базе данных используется средство Microsoft Access. Из этой главы вы узнаете, как делать следующее: > Записывать данные в таблицы > Создавать, открывать и сохранять таблицы из программы > Получать доступ к файлам внешней базы данных Запись данных в таблицу Таблицы доступны любой программе на Visual Basic, запускаемой в Excel. Программам на Visual Basic также доступны таблицы Project, однако эти таблицы не настолько гибкие, как таблицы Excel. Для того чтобы использовать таблицу в качестве места хранения данных, применяется техника доступа к ячейкам, описанная в предыдущих главах; кроме того, Visual Basic имеет доступ ко всем директивам Excel, включая директивы открытия и сохранения таблиц и рабочих папок. Открытие или создание таблицы или рабочей папки Способ создания новой таблицы или рабочей папки вам уже известен: к наборам Worksheets или Workbooks применяется метод Add. Например, следующий блок кода позволяет добавить новую таблицу "Контакты- Данные" к рабочей папке Воок2: Sub addWorksheet() Workbooks("Воок2") .Worksheets.Add Active Sheet .Name = ’’КонтактыДанные" End Sub Метод Add имеет несколько опций, позволяющих задавать место новой таблицы в рабочей папке. Для получения полного списка опций метода Add воспользуйтесь интерактивной подсказкой. Чтобы добавить таблицу к текущей рабочей папке, следует вместо набора Workbooks использовать объект Active Workbook. 324
Глава 20. Другие способы записи данных Так, для создания новой рабочей папки, сохранения ее под заданным именем, переименования первой таблицы и сохранения этих изменений можно написать процедуру, подобную следующей: I Sub addWorkbook () Workbooks.Add ActiveWorkbook.SaveAS "PCDDATA.XLS" Worksheets(1).Name = "КонтактыДанные" End Sub Для открытия существующей рабочей папки и выбора таблицы "КонтактыДанные" потребуется следующая процедура: Sub getWorkbook() Workbooks. Open ’’PCDDATA.XLS" Worksheets("КонтактыДанные").Select End Sub Для открытия заданной рабочей папки применяется метод GetOpenFilename. Однако, если вы работаете с рабочими папками и таблицами, гораздо проще вызвать диалоговое окно Open и поручить Excel управлять выбором и открытием файла. Чтобы отобразить диалоговое окно Open, потребуется процедура Sub openWorkbook () Application.Dialogs(xlDialogOpen).Show End Sub Файл, выбранный пользователем в диалоговом окне, будет открыт и станет активной рабочей папкой. Чтобы определить его имя, проверяется свойство Name объекта ActiveWorkbook. В том случае, когда пользователь нажал командную кнопку Cancel, метод Show возвратит значение False. Чтобы использовать для сохранения открытой рабочей папки диалоговое окно Save As, потребуется процедура Sub saveWorkbook() Application.Dialogs(xlDialogSaveAs).Show End Sub 325
Visual Basic for Applications в примерах Запись данных в области таблицы Вы уже знаете, как записываются данные в ячейки таблицы: объект Range используется для доступа к ячейкам. К методу Cells прибегают для выбора конкретных ячеек. <Этот метод имеет два целых аргумента —строка и колонка. Обратите внимание на то, что метод Cells применяется к текущему объекту Range, а не к таблице; следовательно, Cells(l,l) не обязательно обозначает ячейку А1. Например, если ячейка В5 является текущим объектом Range, то Cells(l, 1) ссылается на В5, а Cells(2,3) —на D6. При записи в известную единичную ячейку таблицы назовите эту ячейку, а затем используйте это имя для выбора ячейки методом Range. При записи области ячеек назовите ячейку в левом верхнем углу области, используйте это имя для выбора ячейки методом Range в качестве объекта Range, а затем для получения доступа к остальным ячейкам области применяйте к этому объекту Range метод Cells. Например, если numEntries является именем ячейки таблицы "КонтактыДанные", то доступ к значению этой ячейки можно получить следующим образом: Sheets("КонтактыДанные") .Range("numEntries") .Value Если DBTop — верхняя ячейка области ячеек той же таблицы, то доступ к остальным ячейкам области можно получить следующим образом: Sheets("КонтактыДанные") .Range("DBTop").Cells(I, J) .Value где I и J —номера строки и колонки ячейки, считая вниз и вправо от DBTop. Предупреждение: Таблица пытается интерпретировать любые значения, которые вы записываете в ее ячейки. Например, если число имеет ведущие нули, таблица удаляет их, а если стром похожа на формулу, таблица вычисляет ее или рассматривает как данные. Чтобы из таблицы получить корректные значения, нужно преобразовать некоторые значения в символьный формат. Или перед тем как записать строку в ячейку, добавить в ее начало апостроф, так как этот символ всегда предваряет строку символов. Сохранение базы данных персональных контактов в таблице Приведенная ниже версия базы данных персональных контактов использует в качестве места хранения данных таблицу. Новые и измененные строки программы выделены в листинге жирным шрифтом. Таблица является частью рабочей папки, содержащей эту программу работы с базой данных, и, следовательно, открывается при открытии рабочей папки и сохраняется вместе с рабочей папкой. Очевидное преиму¬ щество такой схемы состоит в том, что теперь данные являются частью таблицы и, значит, можно применять директивы Data Excel для поиска и сортировки этих данных. 326
Глава 20. Другие способы записи данных 'Программа работы с базой данных персональных контактов. ' Версия для сохранения в таблице. Option Explicit 'Заставляет объявлять все переменные. Option Base 1 'Устанавливает начальный индекс массива в 1. Const pcdName - 2, pcdAddress = 3, pcdCity = 4 Const pcdState = 5, pcdZip = 6, pcdPhone = 7 Const pcdNet = 8, pcdNetAddr = 9, pcdReferral = 10 Const pcdNotes = 11, pcdRecNo ~ 1 Const pcdNumFields = 11 Dim numEntries As Integer Dim theEntryNum As Integer 1 Количество полей в базе данных. 'Общее количество записей. 'Текущая отображаемая запись. 1 Инициализация базы данных. Sub Initializelt () Dim theMaxVal As Integer numEntries = Sheets (” КонтактыДанные") . Range (" numEntries ”) . Value If numEntries = 0 Then theEntryNum = 0 theMaxVal = 1 ClrFrm Else theEntryNum = 1 theMaxVal = numEntries DBToEntry theEntryNum End If Sheets ("КонтактыДиалог"). DropDowns ("ДЕ_Ма11__Сеть"). List = _ Array("Relcom", "Internet", "CompuServe", "Prodigy") Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Min = 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = theMaxVal Sheets("КонтактыДиалог").Show End Sub ' Очистка бланка. Sub ClrFrm () With Sheets("КонтактыДиалог") . EditBoxes (’’ ДИмя") . Text = 327
Visual Basic for Applications в примерах . Edi tBoxes (” ДАдрес ") . Text = " " . EditBoxes (" ДГород") . Text = " " .EditBoxes ("ДСтрана"). Text = " " .EditBoxes (" ДИндекс”) . Text = " " .EditBoxes ("ДТелефон") .Text = "" .DropDowns ("ДЕ_Ма11_Сеть ") .Text — "" .EditBoxes ("ДЕ_Ма11_Адрес"). Text = "" .EditBoxes (" ДСсылка") . Text = " " .EditBoxes ("ДПримечание"). Text = "" .Labels ("ДНомЗan") .Caption = "Запись N« " End With End Sub r ' Процедура Auto_open, вызываемая при каждом открытии ' рабочей папки. г Sub Auto_Open() InitializeIt 'Вызов процедуры инициализации. End Sub ' Пересылка записи в базу данных. 9 Sub End pcdName).Value = .EditBoxes("ДИмя”).Text pcdAddress) .Value = .EditBoxes("ДАдрес").Text pcdCity).Value = .EditBoxes("ДГород").Text pcdState).Value = .EditBoxes("ДСтрана").Text pcdZip).Value = .EditBoxes("ДИндекс”).Text pcdPhone).Value = .EditBoxes("ДТелефон").Text pcdNet) .Value = . DropDowns ( "ДЕ_МаИ_Сеть" ). Text EntryToDB(anEntry As Integer) Dim theRow Set theRow = Sheets ("КонтактыДанные") .Range ("DBTop") . Cells (anEntry, 1) With Sheets("КонтактыДиалог") theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells (1, pcdNetAddr) .Value = .EditBoxes ("ДЕ_МаИ_Адрес") .Text theRow. Cells (1, pcdRef err al) .Value = . EditBoxes ("ДСсылка” ). Text theRow.Cells(1, pcdNotes) .Value = . EditBoxes ( "ДПримечание").Text theRow. Cells (1, pcdRecNo) .Value = anEntry End With Sub 328
Глава 20. Другие способы записи данных 'Пересылка записи в бланк. Sub DBToEntry (anEntry As Integer) Dim theRow Set theRow = Sheets ("КонтактыДанные") , Range ("DBTop") .Cells (anEntry, 1) With Sheets("КонтактыДиалог”) .EditBoxes("ДИмя").Text = theRow. Cells (1, pcdName) .Value .EditBoxes("ДАдрес").Text = theRow. Cells (1, pcdAddress) .Value .EditBoxes("ДГород").Text = theRow.Cells(1, pcdCity).Value .EditBoxes("ДСтрана”).Text = theRow.Cells(1, pcdState).Value .EditBoxes("ДИндекс").Text = _ Format(theRow.Cells(1, pcdZip).Value, "000000") .EditBoxes("ДТелефон").Text = theRow.Cells(1, pcdPhone) .Value . DropDowns ( "ДЕ_МаИ_Сеть" ) . Text = theRow. Cells (1, pcdNet) .Value . EditBoxes ( "ДЕ—МаЫ—Адрес") .Text = theRow.Cells (1, pcdNetAddr) .Value .EditBoxes("ДСсылка") .Text = theRow.Cells(1, pcdReferral) .Value .EditBoxes("ДПримечание").Text = theRow.Cells(1, pcdNotes).Value .Labels("ДНомЗап").Caption = _ "Запись S’ " & Str (theRow.Cells (1, pcdRecNo) .Value) . ScrollBars("ПрокруткаЗап") .Value = anEntry End With End Sub ' Добавляет новую запись. Sub NewEntry_Click () numEntries =• numEntries + 1 Sheets (" КонтактыДанные") . Range (" numEntries ") .Value = numEntries Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = numEntries EntryToDB- theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub i ' Изменяет запись. Sub UpdateEntry_Click () If theEntryNum = 0 Then Exit Sub 'Если еще нет записей, то выход. EntryToDB theEntryNum 'Изменение текущей записи. 329
Visual Basic for Applications в примерах End Sub f 'Удаляет запись. г Sub DeleteEntry__Click () Dim I As Integer, buttons As Integer, J As Integer Dim theMsg As String, theRow If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApp1i c at i onModa1 theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries - 1 Sheets (" КонтактыДанные") . Range (" numEntries ") . Value = numEntries theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries 'Цикл обработки строк. Set theRow = Sheets ("КонтактыДанные") .Range("DBTop") .Cells (I, 1) For J = 1 To pcdNumFields 'Цикл обработки колонок. theRow.Cells (1, J) .Value = theRow.Cells (2 , J) .Value Next J theRow.Cells (1, pcdRecNo) .Value = 1-1 Next I numEntries = numEntries - 1 Sheets ("КонтактыДанные") .Range("numEntries") .Value = numEntries End If Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Value = theEntryNum Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries DBToEntry theEntryNum End Sub r ' Прокрутка записей. 330
Глава 20. Другие способы записи данных Sub ScrollEntries_Click () If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets("КонтактыДиалог") .ScrollBars("ПрокруткаЗап").Value DBToEntry theEntryNum End Sub ' Поиск имени. Sub FindName_Click () Static theName As String Dim I As Integer, theRow Const Textcomparison = 1 If numEntries = 0 Then Exit Sub 'Еще нет записей. theName = InputBox(’’Введите имя для поиска”, "Поиск имени", theName) If theEntryNum о numEntries Then For I = theEntryNum + 1 To numEntries Set theRow = Sheets("КонтактыДанные") .Range("DBTop") .Cells (I, 1) If InStr(l, theRow.Cells(1, pcdName) .Value, _ theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum Set theRow = Sheets (’’ КонтактыДанные") . Range ("DBTop") . Cells (1, 1) If InStr(l, theRow. Cells (1, pcdName) .Value, __ theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox "Имя: " & theName & " не найдено.", , "Поиск имени" End Sub 331
Visual Basic for Applications в примерах Для создания этой версии программы работы с базой данных персональных контактов выполните следующие действия: 1. Вызовите копию версии программы для последовательного файла, которую вы разработали в гл. 18. 2. Удалите командные кнопки ”Откр.” и "Сохр.”, так как табличная версия базы данных автоматически открывается и сохраняется вместе с рабочей папкой. 3. Выберите пустую таблицу и переименуйте ее в "КонтактыДанные”. 4. Запишите метки в следующие ячейки (как изображено на рис. 20.1): Ячейка Метка А1 КолЗаписей = А2 База Данных АЗ НомЗап ВЗ Имя СЗ Адрес D3 Город ЕЗ Страна F3 Индекс G3 Телефон НЗ Сеть 13 Сет. адрес J3 Ссылка КЗ Примечание 332
Глава 20, Другие способы записи данным га- - , М1сгоШ£^се1 -PC03.XLS J ' QS “| File Edit View insert Format Tools JJata VVindow Help |ф | Arial Cjh 1[±]|io ||±]|b|/|h||^|s|s|S||V]%| , |^|^||n|i||<ta|t||'i|±| DBTop 14 1 , , . , , A b|c|d|e|f|g|h| i | j | к Г? КолЗаписей =| База Данных ■ НомЗап (Имя ; 2 ; : i ; ; : • : i : 1 : • : (Адрес (Город !Страна=ИндекаТелефон:Сеть:Сет.адрес=Ссылка;Примечание i J j I j ! ’ ■ l i 4 I I МИГ ► 1>I|Z КонтактыВВ / КонтактыБД \ Ко нгакты Данное/ Sg»| Ready Рис. 20.1. Макет таблицы, используемой для хранения данных в табличной версии базы данных персональных контактов 5. Назовите ячейку Bl numEntries, а ячейку А4 —DBTop. 6. Измените код модуля соответственно предыдущему листингу. Приведенные ниже описания облегчат вам определение строк, подлежащих удалению и изменению. В этих описаниях новые и изменяемые строки выделены жирным шрифтом, а удаляемые строки преобразованы в комментарии (в начале этих строк стоит апостроф) и также выделены жирным шрифтом. Тип, определенны^ пользователем, и переменная типа массив удалены из заголов¬ ка модуля, —они больше не нужны. Серия констант определяет номера колонок для поименованных полей базы данных. Применение этих констант повышает читабель¬ ность программы. 333
Visual Basic for Applications в примерах ' Программа работы с базой данных персональных контактов. ' Версия для сохранения в таблице. Option Explicit Option Base 1 'Type DBEntry 'Заставляет объявлять все переменные. 'Устанавливает начальный индекс массива в 1 'Определяет структуру записи базы данных. ' Name As String * 25 ' Address As String * 25 ' City As String * 15 ' State As String * 2 ' Zip As String * 10 ' Phone As String * 20 ' Net As String * 10 ' NetAddr As String * 25 * Referral As String * 25 ' Notes As String * 97 ' RecNo As Integer 'End Type 'Размер типа 256 байтов. 'Dim theDBO As DBEntry 'Массив базы данных. Const pcdName = 2, pcdAddress = 3, pcdCity = 4 Const pcdState = 5, pcdZip « 6, pcdPhone = 7 Const pcdNet = 8, pcdNetAddr = 9, pcdReferral = 10 Const pcdNotes = 11, pcdRecNo = 1 Const pcdNumFields = 11 Dim numEntries As Integer Dim theEntryNum As Integer 'Dim theFileName As String ' Количество полей в базе данных. 'Общее количество записей. 'Текущая отображаемая запись. 'Текущее имя файла. Процедура Initializelt не обнуляет количество записей в базе данных, а обеспечи¬ вает связь с данными, содержащимися в таблице. Затем процедура загружает первую запись. Вначале она загружает из таблицы количество записей, а затем проверяет наличие записей в таблице. Если записей еще нет, процедура очищает бланк, если есть — загружает в бланк первую запись. Чтобы сделать процедуру более читабельной, блок кода, очищающий бланк, выделен в отдельную процедуру. ' Инициализация базы данных. Sub Initializelt() Dim theMaxVal As Integer 334
Глава 20. Другие способы записи данных ' numEntries = О numEntries = Sheets (" КонтактыДанные") . Range (" numEntries ") . Value ' ReDim theDB (1) If numEntries = 0 Then theEntryNum = 0 theMaxVal = 1 ClrFrm Else theEntryNum = 1 theMaxVal = numEntries DBToEntry theEntryNum End If Sheets("КонтактыДиалог").DropDowns("ДЕ_Ма11_Сеть").List = _ Array("Relcom", "Internet", "CompuServe", "Prodigy") Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Min = 1 ' Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап”).Max = theMaxVal Sheets("КонтактыДиалог").Show ' theFileName = "" End Sub ' Очистка бланка. Sub ClrFrm () With Sheets(”КонтактыДиалог") . Edi tBoxes (” ДИмя ”) . Text = ” " .EditBoxes ("ДАдрес") .Text = "" .EditBoxes ("ДГород") .Text = "" .EditBoxes ("ДСтрана"). Text = "" .EditBoxes ("ДИндекс") . Text = "" .EditBoxes ("ДТелефон") .Text « "" . DropDowns (" ДЕ_Ма11__Сеть ") . Text = " " .EditBoxes (" ДЕ_Ма11_Адрес") . Text = " " .EditBoxes ("ДСсылка"). Text = "" .EditBoxes ("ДПримечание"). Text = "" .Labels("ДНомЗап") .Caption = "Запись № " End With End Sub Процедуры Auto_Open и UpdateEntry Click не изменились. В процедуре NewEn- try_Click удалена ссылка на theDB и вставлен оператор, изменяющий значение ячейки таблицы numEntries после того, как переменная numEntries увеличилась на 1. 335
Visual Basic for Applications в примерах ' Добавляет новую запись. г Sub NewEntry_Click() numEntries = numEntries + 1 Sheets (” КонтактыДанные") . Range (" numEntries ") . Value - numEntries Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = numEntries ' ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub Процедуры EntryToDB и DBToEntry значительно изменяются, чтобы обеспечить хранение данных в таблице, а не в массиве: ' Пересылка записи в базу данных. е Sub End pcdName).Value = .EditBoxes("ДИмя").Text pcdAddress).Value = .EditBoxes("ДАдрес").Text pcdCity) .Value = .EditBoxes("ДГород") .Text pcdState) .Value = .EditBoxes("ДСтрана").Text pcdZip).Value = .EditBoxes("ДИндекс").Text pcdPhone).Value = .EditBoxes("ДТелефон").Text pcdNet) .Value = . DropDowns ( "ДЕ_МаИ_Сеть" ). Text EntryToDB(anEntry As Integer) Dim theRow Set theRow = Sheets ("КонтактыДанные") .Range ("DBTop" ) . Cells (anEntry, 1) With Sheets("КонтактыДиалог") theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells(1, theRow.Cells (1, pcdNetAddr) .Value = .EditBoxes ("ДЕ_Ма11_Адрес" ) .Text theRow.Cells(1, pcdReferral).Value = .EditBoxes("ДСсылка").Text theRow.Cells(1, pcdNotes).Value = .EditBoxes("ДПримечание").Text theRow. Cells (1, pcdRecNo) .Value = anEntry End With Sub ' Пересылка записи в бланк. г Sub DBToEntry(anEntry As Integer) Dim theRow 336
Глава 20. Другие способы записи данных Set theRow = Sheets (” КонтактыДанные”) . Range (”DBTop") . Cells (anEntry, 1) With Sheets("КонтактыДиалог") .EditBoxes("ДИмя").Text = theRow.Cells(1, pcdName) .Value .EditBoxes("ДАдрес").Text = theRow.Cells(1, pcdAddress) .Value . EditBoxes ( "ДГород" ). Text = theRow. Cells (1, pcdCity) .Value .EditBoxes("ДСтрана").Text = theRow.Cells(1, pcdState).Value .EditBoxes("ДИндекс").Text = _ Format(theRow.Cells (1, pcdZip) .Value, ”000000”) .EditBoxes("ДТелефон").Text = theRow.Cells(1, pcdPhone) .Value . DropDowns ("ДЕ_Ма11_Сеть" ) . Text = theRow. Cells (1, pcdNet) .Value . EditBoxes ( "ДЕ_МаИ_Адрес") .Text = theRow.Cells (1, pcdNetAddr) .Value . EditBoxes ( "ДСсылка") .Text = theRow.Cells (1, pcdReferral) .Value .EditBoxes("ДПримечание").Text = theRow.Cells(1, pcdNotes).Value .Labels("ДНомЗап").Caption = _ "Запись I» " & Str (theRow.Cells (1, pcdRecNo) .Value) . ScrollBars("ПрокруткаЗап") .Value = anEntry End With End Sub Чтобы сократить объем ввода текста программы для блочной структуры With, объектная переменная theRow определена как ссылка на область для первой ячейки I строки, в которой будут сохранены данные. Ячейка таблицы, расположенная в левом верхнем углу области сохранения данных, названа DBTop. Для определения строки, в которой будут записаны данные, относительно ячейки DBTop применяется метод Cells. Процедура сохраняет данные в таблице, используя операторы блочной структуры With. Объектная переменная treRow выбирает строку, в которой будут записаны данные, а метод Cells обеспечивает перемещение по этой строке для записи элементов данных. Для выбора колонки используется константа, например pcdNapie, определен¬ ная в заголовке модуля. Процедура DBToEntry работает аналогичным образом, с тем отличием, что получает данные из таблицы, а не пересылает их в нее. Для сохранения ведущих нулей в поле индекса применяется оператор Format. Процедура ScrollEntries_Click не изменяется, а процедура DeleteEntry_Click должна быть изменена подобно процедуре EntryToDB, чтобы обеспечить пересылку данных из одной записи базы данных в другую. Процедура обеспечивает пересылку данных с помощью двух циклов, из которых один обрабатывает строки, а второй — колонки таблицы. После пересылки данных значение numEntries уменьшается в таблице на 1. 337
Visual Basic for Applications в 'Удаляет запись. t Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer, J As Integer Dim theMsg As String, theRow If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись, buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbAppli cat i onModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries — 1 Sheets ("КонтактыДанные") .Range("numEntries") . Value = numEntries ' ReDim Preserve theDB(numEntries) theEntryNum = numEntries Elself theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries 'Цикл обработки строк. Set theRow = Sheets (" КонтактыДанные") . Range ("DBTop") . Cells (1, 1) For J - 1 To pfidWninFi el Ня ' Цикл обработки колонок. theRow. Cells (1, J) .Value = theRow.Cells (2 , J) .Value Next J theRow.Cells (1, pcdRecNo) .Value =1-1 ' theDB(I - 1) = theDB (I) ' theDB(I - 1).RecNo =1-1 Next I numEntries = numEntries - 1 ' ReDim Preserve theDB(numEntries) Sheets ("КонтактыДанные") .Range("numEntries") .Value = numEntries End If Sheets ("КонтактыДиалor") .ScrollBars ("ПрокруткаЗап") .Value = theEntryNum Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries DBToEntry theEntryNum End Sub 338
Глава 20. Другие способы записи данных Процедура FindName_Click также изменяется подобно процедуре DBToEntry, чтобы выделить содержимое поля имени из таблицы и обеспечить возможность сравнения со строкой поиска: ' Поиск имени. Sub FindName_Click () Static theName As String Dim I As Integer, theRow Const Textcomparison = 1 If numEntries = 0 Then Exit Sub 'Еще нет записей. theName = InputBox("Введите имя для поиска", "Поиск имени", theName) If theEntryNum о numEntries Then For I = theEntryNum 4- 1 To numEntries Set theRow = Sheets ("КонтактыДанные") .Range("DBTop") .Cells (I, 1) If InStr (1, theRow.Cells (1, pcdName) .Value, _ theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. I theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum Set theRow = Sheets ("КонтактыДанные") .Range("DBTop") .Cells (1, 1) If InStr(l, theRow. Cells (1, pcdName) .Value, _ theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I 'Запись не найдена. MsgBox "Имя: " & theName & " не найдено.", , "Поиск имени" End Sub 339
Visual Basic for Applications в примерах После запуска новой версии программы и работы с ней вы увидите, что программа разместила данные в таблице (см. рис. 20.2). Каждая строка таблицы является записью, содержащей данные из одного бланка, а каждая колонка содержит данные из одного поля бланка. После того как данные будут записаны в таблицу, можно использовать директиву Excel Data/Sort для сортировки записей или директиву Excel Data/Form для поиска в базе данных. Microsoft gxcel: PCD3.XLS ' -| file £dit View insert Format Tools Data V£indow Help I dI^IhI |g|[fr|y| ИН ИИ E23 (ШИЗ i100* га lAndCT. ~~|f±i |w~~ir±lI д k I в I|s|s|s|g||$|%|, |taps]|c|*||<b|i|Г£11| J22 1£ г 1 ' ■ А В | С | D Е F G н 1 J I к 7 1 КолЗаписей = ......Ж L i 2 База Данным 1 .1 J 3 НомЗап Имя IАдрес |Город Страна Индекс Телефон Сеть Сет. адрес Ссылка1Примечани< 4 1 Иван^ ул. Крее1 Киев Ук 252001 >123-4567 Reicc ivan@nipp ПетроваШюбит соба 5 2 Петра прос. rN Киев Ук 252056 234-5678 Relcc peet@nipp Ивановз'Любит соба 6 3 Сидсцпрос.ЩКиев Ук 252056 234-5689 Reicc sidor@nipf ИвановзЛюбит рыбе 7 4 Нико^прос.П^Киев Ук 252167 234-5690 RelcC п1к@п1рре=СидоройЛюбит змей 8 5 ВасиЛул.Мир/арькс Ук 334555 987-654 Reicc nik@nippejНиколаеСтрастный i 9 б Иван^ул.Кре^Киев Ук 252001 123-4567 Reicc ivan@nippineTpoBaifl>o6MT соба 10 7 Сидо|прос.П^Киев Ук 252056 234-5689 Reicc sidor@nip|!HBaHOBg.nrc6MT рыбе 11 8 Исае^пер.Су^Одесс Ук 524536 445-667 Comp ?оп@гнрраШтирли|1 Азартный ai 12 9 Макс|пер.Кут|Томск Ро 524536 445-667 Comp maxQnippi Штирлиц Азартный ai 13 10 В ас ид ул. Мир! Хе ре оь Ук 334555 987^657 Reicc уа5УЭ8@п‘Никола$Ищет книги 14 11 Насргбул.Ст^Ташке! Уз 665774 994-667 Prodi hog@nipp^BOHflei Интересует! 15 12 Чапа^ул.БеЛ’Самар Ро 352647 994-667 Prodi сЬа@п1рр^Бормен1Интересует( 15 13 Georg 123 Ab (New Yt US 12345 994-667 Prodi део@п1ррс'Штирли1 Интересует! 17 14 Фед^ул.МикМосквз Ро 112223 - —-g1 Intern fed@nippвШeфa ^Интересует! 18 15; BacHji ул. Мир! Xepcoh Ук 334555 987-657 RelcC vasvasQniHMKora^HineT книги 1G | КонгактыВВ / КомтактыБД \ Контакт мДошм Г □ . 1 Ready Рис. 20.2. Данные, полученные с помощью табличной версии базы данных персональных контактов, записаны в таблице "КонтактыДанные" Запись данных во внешнюю базу данных Вы можете использовать Visual Basic для доступа к файлу базы данных, создан¬ ному с помощью программы, работающей с этой базой данных. Однако в этом случае для проверки и изменения файла базы данных потребуются и Visual Basic, и программа работы с базой данных. Для управления внешней базой данных из программы на Visual Basic требуется знания языка баз данных SQL (Structured Query Language). В настоящем разделе не 340
Глава 20. Другие способы записи данных описывается язык SQL, однако, здесь вы познакомитесь с расширениями Visual Basic, позволяющими связаться с внешней базой данных. Чтобы получить доступ к файлу внешней базы данных, в Excel должно быть инсталлировано дополнение ODBC (Open Database Connectivity). Чтобы инсталли¬ ровать это средство, укажите директиву Tools/Add-Ins, затем выберите в списке Add-Ins Available средство ODBC Add-In, а потом нажмите командную кнопку ОК. После того как средство будет инсталлировано, выберите директиву Tools/References. Чтобы установить ссылки на это средство, подключите библиотеку XLODBC.XLA. Теперь вы можете выполнять функции Visual Basic, содержащиеся в XLODBC.XLA. Для получения подробной информации о инсталляции драйверов и средства ODBC воспользуйтесь интерактивной подсказкой (см. рис. 20.3). S ” ' Add-Ins ' ' J Add-Ins Available: 0 Analysis Т oolPak Analysis ToolPak VBA П AutoSave J Crosstab sheet function J MS Excel 4.0 Add-in Functions 0MS Excel 4.0 Analysis Functions MS Excel 4.0 Analysis Tools £] Report Manager 2] Slideshow Template i 0K I | Cancel | I Bto*™ I | Help | г-MS Query Add-In Work with Microsoft Query to gather external data. Рис. 20.3. Диалоговое окно Add-Ins, используемое для подключения ODBC После того как вы инсталлировали и связали ODBC, можно выполнять в программах на Visual Basic функции, записанные в этом файле. Список допустимых функций приведен в табл. 20.1. Таблица 20.1. Функции ODBC Функция Описание SQLOpen Открывает связи с внешними исходными данными и возвращает ID связи, подобный файловому числу. SQLBind Определяет, куда пересылать результаты, ожидаемые от внешних исходных данных. Это место является областью таблицы. 341
Visual Basic for Applications в примерах Функция Описание SQLClose Закрывает связь с внешними исходными данными. SQLError Возвращает подробную информацию об ошибке в вызове функции ODBC. SQLExecQuery Посылает запрос на открытие внешних исходных данных на языке SQL. SQLGetSchema Запрашивает информацию о структуре внешних исходных данных. SQLRequest Открывает связь с внешними исходными данными, посылает запрос, получает результаты и закрывает связь. SQLRetrieve Получает результаты предыдущего запроса и помещает их в таблицу. SQLRetrieveToFile Получает результаты предыдущего запроса и помещает их в файл. Аргументы, которые вы будете использовать для указанных функций, определяют¬ ся типом базы данных, к которой вы получаете доступ (например, dBASE или FoxPro). Чтобы определить, какие аргументы нужны конкретному драйверу, пользуйтесь документацией на этот драйвер и его интерактивной подсказкой. Кроме того, для доступа к внешней базе данных необходимо знать SQL. Примечание: ODBC отличается возможностью создавать новые базы данных в Excel или Visual Basic. Хотя это средство позволяет добавлять или удалять записи, вы можете создать или модифицировать структуру файла базы данных только с помощью программы работы с базой данных. Совет: Если вы не знаете языка SQL, то можете использовать программу Microsoft Query, входящую в Excel. Query обеспечивает все манипуляции с базой данных с помощью графического интерфейса. Можно вызвать Query из таблицы для помещения результатов запроса в таблицу. Заключение В этой главе вы ознакомились с записью данных в таблицу Excel. Применение объекта Range и методов Range и Cells облегчило доступ к областям ячеек для записи или получения данных. Данные, записанные в таблице, автоматически сохраняются или открываются при сохранении либо открытии рабочей папки, содержащей данную таблицу. 342
Глава 20. Другие способы записи данным Кроме того, вы ознакомились с возможностью доступа к файлам внешней базы данных. Чтобы использовать эту возможность, необходимо знать стандартный язык базы данных SQL. В качестве более легкого способа можно прибегнуть к входящей в Excel программе Microsoft Query. Данная глава завершает часть VI книги. В части VII рассмотрены отладка и прерывания по ошибке. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Как добавить новую таблицу в активную рабочую папку? 2. Как переименовать таблицу? 3. Как открыть существующую таблицу, зная ее имя? 4. Как открыть существующую рабочую папку, если пользователь знает ее имя? 5. Если активным объектом Range является ячейка К13, то на какую ячейку указывает метод Cells(5,7), примененный к этому объекту? Упражнения для закрепления материала 1. Введите несколько различных записей в базу данных персональных контактов. Затем завершите программу и переключитесь в таблицу ” Контакты Данные", выберите базу данных, включая наименования полей в строке 3, затем укажите директиву Data/Form. Попробуйте поиск и отображение различных частей базы данных. 2. Запустите Microsoft Query, откройте одну из баз данных — примеров, затем попробуйте поиск и отображение записей. Откройте Query из Excel и возвратите выбранные записи в область таблицы. 3. Добавьте в бланк "КонтактыДанные" командную кнопку, позволяющую сортиро¬ вать базу данных с помощью табличных функций Excel. 4. Перепишите процедуру DeleteEntry_Click для того чтобы она могла использовать директивы Edit/Сору и Ed it/Paste для выбора области базы данных после удаленной строки и перемещения этой области вверх. 5. Перепишите процедуру DeleteEntry_Click, используя для удаления строки дирек¬ тиву Edit/Delete. 6. Перепишите процедуру FindName_Click, используя для поиска директиву Edit/Find.
Часть VII Отладка процедуры
Глава 21 Что делать, если ваша программа не работает Часть VII этой книги охватывает вопросы отладки программы на Visual Basic. В главах 21 и 22 описана проверка программ с помощью встроенных средств отладки. В гл. 23 рассматриваются прерывания по ошибкам, позволяющие программе управлять непредвиденными ситуациями. Представьте себе, что вы запускаете свою новейшую, великолепную программу, которая должна сделать вас богатым и знаменитым, нажимаете командную кнопку ОК - и в появившемся диалоговом окне читаете о том, что ваша программа потерпела крах. Что вы будете делать? Плакать? Запомните, что слезы не должны попасть на клавиатуру — они электропроводны. Побьете свой компьютер? Это облегчит ваши страдания лишь на очень короткое время: жесткий диск от такого обращения может и "позабыть” вашу замечательную программу, что заставит вас плакать намного дольше. В данной главе изложены способы обнаружения причин, вызывающих крах программы. Из следующей главы вы узнаете о том, что нужно делать, если ваша программа работает не так, как вы планировали. Мы расскажем как: ► Использовать средства отладки в окне Debug ► Проверять значения переменных ► Следить за ходом выполнения программы ► Экспериментировать с программой, используя панель Immediate Что такое отладка? Отладка является второй (после разработки программы) и самой веселой частью программирования. Отладка — это искусство обнаружения и устранения ошибок. Ошибки — это не маленькие живые существа, ползающие внутри вашего компьютера и причиняющие массу неприятностей — это просто ошибки в вашей программе, вызывающие ее неправильную работу или даже крах. Такой ошибкой может быть пропущенная запятая или слово. Ошибка может быть и результатом нескольких описок в программе. Отладка — это скорее искусство, чем наука, так как не существует однозначного набора действий, позволяющих в любом случае обнаружить ошибку. Если бы такой набор был, то была бы и возможность написать программу, управляющую отладкой; однако на сегодняшний день обнаружение и устранение ошибки в программе требует применения всех ваших знаний, опыта и интуиции. Именно при отладке вы компен¬ сируете долгие часы записи операторов комментариев и разработки "осторожных” блочных структур. Дополнительных затрат времени потребует и обнаружение ошибок, 347
Visual Basic for Applications в примерах так как для того, чтобы устранить ошибку, ее вначале необходимо обнаружить. Любой программный проект обозримого размера должен предусматривать затраты времени на отладку в объеме, не меньшем, чем на программирование. Существуют три основных вида ошибок в программах: синтаксические ошибки, ошибки выполнения и логические ошибки. Синтаксическая ошибка представляет собой опечатку в операторе, опущенный обязательный аргумент или использование аргумента некорректного типа. Обычно интерпретатор с Visual Basic автоматически обнаруживает синтаксические ошибки в вашей программе. Ошибки, касающиеся синтаксиса оператора или ключевого слова, обычно проявляются в процессе редактирования программы. Как только вы завершае¬ те редактирование одного оператора и перемещаетесь к другому, интерпретатор с Visual Basic проверяет отредактированный оператор с точки зрения корректности синтаксиса. Если некоторые знаки вроде запятых или круглых скобок опущены или находятся не на своем месте, интерпретатор немедленно отображает диалоговое окно, сообщающее об ошибке. Кроме того, интерпретатор обнаруживает любые синтаксические ошибки при первом запуске программы после ее редактирования. Преобразовывая программу в p-код, интерпретатор обнаруживает любые ошибки на блочном уровне. Синтаксичес¬ кие ошибки блочного уровня относятся к блоку операторов, и интерпретатор не может обнаружить их до тех пор, пока вы не окончите создавать блок. К синтаксическим ошибкам блочного уровня относятся применение аргумента некорректного типа, пропуск оператора, завершающего циклическую структуру или блочную структуру If. Примечание: Программы на Visual Basic компилируются не в машинный код, а в промежуточный код, известный как p-код. Машинный код представляет собой команды, которые микропроцессор может выполнять непосредственно. Р-код — это машинно-незави¬ симый командный код, предназначенный для выполнения интерпретатором. Интерпретатор с p-кода значительно более эффективен, чем традиционные интер¬ претаторы с BASIC, так как производит синтаксический анализ. Ошибка выполнения имеет место в случае применения некорректного значения, например при попытке деления на 0 или извлечения квадратного корня из отрица¬ тельного числа. Очевидно, что такие ошибки могут проявиться только в процессе выполнения программы, когда значение будет вычислено. При обнаружении ошибки выполнения отображается диалоговое окно, содержащее код ошибки и ее краткое описание. Каждой ошибке выполнения присвоен код, который облегчает ее иденти¬ фикацию. Перечень всех кодов ошибок Visual Basic приведен в Приложении В. Логическая ошибка —это ошибка в логике программы, а не в синтаксисе ее кода. В случае логической ошибки выполнение программы продолжается зачастую без видимых нарушений, но дает некорректный результат. Логические ошибки крайне трудно обнаружить, так как компьютер делает именно то, что вы ему указали (даже 348
Глава 21. Что делать, если ваша программа не работает если вы имели ввиду другое). Кроме того, логические ошибки могут проявляться и как синтаксические, когда рассчитанные значения не обеспечивают достоверности дальнейших вычислений. Причиной логической ошибки могут стать и строки, вызы¬ вающие ошибку выполнения, возможно также, что причину следует искать где-то очень далеко от этого места. Следовательно, обнаружение логических ошибок потре¬ бует, как уже упоминалось, всех ваших знаний и опыта. Ваша программа потерпела крах Для доступа к основным средствам отладки в Visual Basic применяется окно Debug. Вы уже неоднократно пользовались окном Debug для проверки различных функций и методов, при этом открывали это окно, указывая директиву View/Debug Window. Если программа потерпела крах, то появляется диалоговое окно Macro Error. Это окно позволяет выбирать между завершением программы, ее продолжением, переходом к строке кода, вызвавшей крах, открытием окна Debug или получением подсказки. Например, выполнение следующей процедуры вызовет появление диалогового окна Macro Error, изображенного на рис. 21.1. Sub testBugs() А = 1 В = О С = А / В End Sub Командная кнопка End диалогового окна Macro Error позволяет завершить выполнение программы. Она используется в тех случаях, когда вы хотите завершить программу без каких-либо действий по отладке активной программы или если вы знаете причину ошибки. Командная кнопка Continue при большинстве ошибок недоступна, так как код находится в состоянии, когда его выполнение не может быть продолжено. Командная кнопка Goto открывает страницу макроса и выделяет опера¬ тор, вызвавший ошибку. Эта командная кнопка используется в тех случаях, когда причина ошибки известна и вы хотите скорректировать соответствующий оператор. Командная кнопка Help отображает подсказку, разъясняющую ошибку. Рис. 21.1. Диалоговое окно Macro Error с сообщением об ошибке 349
Visual Basic for Applications в примерах Командная кнопка Debug используется в тех случаях, когда в выполняемой программе необходимо обнаружить ошибки. После того как вы нажимаете командную кнопку Debug, появляется окно Debug, изображенное на рис. 21.2. Рис. 21.2. Диалоговое окно Debug с кодом, отображенным на кодовой панели В нижней части окна Debug расположена кодовая панель, на которой отображается блок кода, включающий оператор, вызвавший ошибку. Этот оператор выделяется на кодовой панели рамкой. Если вы просмотрите код процедуры testBugs, отображенный на кодовой панели, то увидите, что деление на 0 привело к ошибке. Однако в большинстве ситуаций причина ошибки не столь очевидна. Над кодовой панелью располагаются панели Immediate и Watch, доступ к которым обеспечивается выбором соответствующего корешка. Кроме указанных корешков в верхней части окна Debug находится окно Procedure, отображающее имя активной процедуры. Проверка к изменение переменных на панели Immediate Изучая эту книгу, вы уже неоднократно использовали панель Immediate. Вы выполняли на панели Immediate многие команды Visual Basic и печатали результаты их выполнения с помощью специальных операторов. Применение в программе опера¬ торов Debug.Print также влечет за собой печать на панели Immediate. Дополнительно на этой панели можно напечатать значение любой переменной активной процедуры, рассчитать простую формулу, включающую эту переменную, или изменить значение этой переменной, а затем продолжить выполнение программы, используя это новое значение. 350
Глава 21. Что делать, если ваша программа не работает Предупреждение: Обычно не следует изменять значение переменной выполняемой программы. Если это возможно, скорректируйте данную программу, а затем перезапустите ее. На рис. 21.3 изображена панель Immediate, на которой отражены значения переменных А и В, затем оператором присваивания изменено значение В, после чего напечатано значение А/В. В данном случае вычисление значения выражения A/В не приводит к ошибке, что позволяет продолжить выполнение программы с данной точки, указав директиву Run/ Continue либо нажав пиктограмму Resume Macro пиктогра¬ фического меню Visual Basic. Примечание: Записав на панели Immediate "?" или "Print", вы указываете, что следующие значения должны быть напечатаны. Рис. 21.3. Панель Immediate с напечатанными значениями переменных и результатов вычислении Панель Immediate используется для проверки вычисления выражений, что позво¬ ляет выявить причину некорректной работы программы. Если крах произошел в результате расчетов по сложной формуле, попробуйте провести расчеты отдельных частей этой формулы, что позволит выявить ту часть, которая вызывает ошибку. После того как вы определили переменную, содержащую ошибочное значение, попробуйте просмотреть, в каком месте процедуры эта переменная получила данное ошибочное значение. 351
Visual Basic for Applications в примерах Проверка переменных с помощью средства Instant Watch Использование средства Instant Watch — это еще один способ определить блок кода, вызывающего ошибку. Это средство позволяет проверять значения различных переменных и выражений (так же, как вы это делали на панели Immediate). Применение средства Instant Watch значительно проще, чем использование панели Immediate, если переменная или формула, которую вы хотите отобразить, видны на кодовой панели (как в данном случае). Однако указанное средство имеет один недостаток: не позволяет хранить распечатку предыдущих значений (как панель Immediate). Выберите переменную на кодовой панели, затем укажите директиву Tools/Instant Watch или нажмите пиктограмму Instant Watch пиктографического меню Visual Basic. Появится диалоговое окно Instant Watch, отображающее эту переменную и ее значение (см. рис. 21.4). Рис. 21.4. Диалоговое окно Instant Watch, отображающее текущее значение переменной А Диалоговое окно Instant Watch позволяет отображать значения как отдельных переменных, так и коротких выражений. Например, если вы выберете формулу А/В и укажете директиву Tools/Instant Watch, то появится диалоговое окно Instant Watch, изображенное на рис. 21.5. Рис. 21.5. Диалоговое окно Instant Watch, отображающее текущее значение выражения А/В 352
Глава 21. Что делать, если ваша программа не работает Проверка переменных на панели Watch Диалоговое окно Instant Watch, изображенное на рис. 21.4 и 21.5. имеет коман¬ дную кнопку Add. Если вы нажмете эту кнопку или выберете переменную либо выражение, а затем укажете директиву Tools/Add Watch, то зададите выбранную переменную или выражения как отслеживаемую переменную, которая отобразится на панели Watch окна Debug. Диалоговое окно Instant Watch отображает также текущее значение выражения, но его необходимо закрыть прежде, чем вы сможете продолжить отладку. А удалять отслеживаемые переменные из панели Watch для продолжения отладки не требуется, и отслеживаемая переменная продолжает отображать текущее значение выражения (см. рис. 21.6). Если значение отслеживаемой переменной изменится, то это новое значение отобразится на панели Watch. Колонка Context панели Watch содержит имя процедуры, в которой данной переменной было присвоено это значение. Если две переменные находятся в двух различных процедурах, то отображается только та, имя которой указано в колонке Context. Чтобы увидеть текущие значения обеих переменных, поместите эти перемен¬ ные на панель Watch. I Watch I Immediate ] Expression | Value to A 1 WA/B 0.5 MB 2 M C Empty Debug - BUGS.XLS.Modylet ; ) I Context Module! testBugs Module! testBugs Module! testBugs Module! testBugs Sub testBugsQ A - 1 В = 0 Я- A / H End Sub Рис. 21.6. Панель Watch окна Debug, отображающая текущее значение выбранных переменных и выражений 12 Вильям Дж. Орвис 353
Visual Basic for Applications в примерах Примечание: Если процедура, указанная в колонке Context, не входит в активную цепочку вызовов, то на панели Watch вместо значений ее переменных отображается предложение "Out of context" ("Вне контекста"). Если процедура не входит в активную цепочку вызовов, то ни одна из ее переменных не входит в активный контекст программы и, следова¬ тельно, еще не имеет никакого значения. Активная цепочка вызовов — это список, включающий все процедуры, активные на момент остановки выполнения программы, т.е. все процедуры, не завершившие свое выполнение по той причине, что ожидают возврата управления от вызванной подпрограммы. Этот список называется цепочкой вызовов, так как одна процедура вызывает следующую, та, в свою очередь, - следующую и так вплоть до активной процедуры. На рис. 21.8 изображена цепочка вызовов, отображенная в диалоговом окне Calls. Получение списка процедур в диалоговом окне Calls Окно Procedure, расположенное в верхней части окна Debug, отображает имя текущей активной процедуры. Нажатие на кнопку, находящуюся справа от окна Procedure, вызывает отображение диалогового окна Calls, содержащего текущую активную цепочку вызовов. В большинстве случаев в этой цепочке находится только одна текущая процедура. Диалоговое окно Calls очень удобно использовать, так как оно позволяет определить путь, который привел программу к этой процедуре. Если диалоговое окно Calls содержит только имя текущей процедуры, то это означает, что текущая процедура была запущена на выполнение непосредственно либо директивой Run/Start, либо директивой Tools/Macro, либо выбором элемента управлениям которому подключена текущая процедура. Например, следующие три процедуры при запуске процедуры testBugs2 форми¬ руют цепочку вызовов: Sub testBugs2() А = 1 testProc2 А End Sub t Sub testProc2(A) testProc3 A End Sub Sub testProc3(A) В = 0 С = A / В End Sub 354
Глава 21. Что делать, если ваша программа не работает Процедура testBugs2 определяет переменную А и передает ее значение процедуре testProc2, которая передает это значение процедуре testProcS. Процедура testProc3 определяет переменную В, равную 0, а затем вычисляет значение выражения А/В, что приводит к ошибке деления на 0. На рис. 21.7 изображено диалоговое окно Debug, отображающее данную ситуацию, а также значения различных переменных и выра¬ жений на панели Watch. Обратите внимание на то, что переменная А в процедуре testBugs, не входящей в активную цепочку вызовов, находится вне контекста, а переменная С в активной процедуре не имеет значения, так как вычисление выражения А/В привело к ошибке. Debug BUGSJ<|LS.Modulel I Contort [ Watch 1 Immediate Expression | Value М А Out of context Module-!. testBugs М А 1 Module1.te$tProc2 М А 1 Module! testProc3 И А/В <Division by zero> Module-! ,tastProc3 м в 0 Module! tesiProc3 W с Empty Module! testProc3 Sub testProc3(А) В - О £ - а / End Sub Рис. 21.7. Окно Debug, отображающее оператор, вызвавший ошибку, и значения переменных на момент краха программы В окне Procedure указано имя текущей процедуры —testProc3. Нажатие на кнопку справа от данного окна вызывает отображение диалогового окна Calls (см. рис. 21.8). Диалоговое окно Calls содержит список процедур в том порядке, в котором строилась . активная цепочка вызовов (снизу вверх). 355
Visual Basic for Applications в примерах Рис. 21.8. Диалоговое окно Calls, отображающее цепочку вызовов на момент краха процедуры testProc3 Диалоговое окно Cails позволяет проследить путь, которым программа пришла к точке. Если вы не уверены в аргументах какого-либо из вызовов, то выберите имя вызвавшей процедуры и нажмите командную кнопку Show. Это позволит переместить¬ ся к оператору, вызвавшему интересующую вас процедуру, а также проверить значения переменных и выражений, переданных следующей процедуре. Заключение Из этой главы вы узнали о том, что происходит, если в программе обнаруживаются ошибки, а также о том, как использовать окно Debug для отслеживания значений переменных и выражений. Значения этих переменных и выражений позволяют определить причину ошибки. В следующей главе рассказывается как продолжить отслеживание значений пере¬ менных и выражений при продолжении выполнения программы. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое синтаксическая ошибка? Как она возникает? 2. Что такое ошибка выполнения? Как она возникает? 3. Что такое логическая ошибка? Как она возникает? 4. Что такое отладка? 5. Что отображается в окне Procedure? 6. Объясните разницу между мгновенно отслеживаемой и отслеживаемой перемен¬ ными? 356
Глава 21. Что делать, если ваша программа не работает Упражнение для закрепления материала I. Попробуйте выполнить следующие примеры, вызывающие различные ошибки; проанализируйте диалоговые окна и значения переменных в ваших процедурах: Вычисление квадратного корня из -1. Передача аргумента типа Variant процедуре, ожидающей значения типа Single. Передача аргумента типа String процедуре, ожидающей число. Вычисления с очень большим числом. 357
Глава 22 Применение контрольных точек и точек слежения В предыдущей главе вы остановили выполняемую программу, задав заведомо некорректные вычисления. Умышленный ввод в программу ошибки — наиболее легкий путь остановить ее выполнение. Из этой главы вы узнаете, как запускать и останавливать выполнение программы, чтобы получить возможность проверить значе¬ ния переменных. Здесь описаны: > Режим прерывания > Применение контрольных точек для задания режима прерывания > Применение точек слежения для задания режима прерывания > Пошаговое выполнение программы Что такое режим прерывания? Когда в процессе выполнения программы обнаруживается ошибка, выполнение программы прекращается и отображается диалоговое окно Macro Error, то программа переходит в режим прерывания. В отличие от нормального завершения программы, когда очищаются все переменные, режим прерывания —это просто пауза в выполне¬ нии программы; следовательно, на момент перехода программы в режим прерывания все переменные сохраняют свои значения. Кроме того, известен оператор, с которого будет продолжено выполнение программы (если это потребуется). Режим прерывания также называется режимом ожидания. Продолжить выполнение программы, находящейся в режиме прерывания, можно либо указав директиву Run/Continue, либо нажав пиктограмму Resume Macro пиктографического меню Visual Basic. Если вы не измените значений некоторых переменных в то время, когда программа находится в режиме прерывания, то продолжение выполнения программы приведет к тем же результатам, что и выполне¬ ние без перехода в режим прерывания. Перевести выполняемую программу в режим прерывания можно несколькими способами. Существует только одно ограничение: программа должна быть выполняе¬ мым кодом Visual Basic и не должна находиться в состоянии ожидания реакции пользователя на диалоговое окно. В предыдущей главе вы ознакомились с тем, как ошибки переводят программу в режим ожидания. Кроме этого, прервать выполнение программы и перевести ее в режим прерывания можно нажатием клавиш Ctrl-Break, введением в программу оператора Stop, заданием контрольной точки или точки слежения. 358
Глава 22. Применение контрольных точек и точек слежения Прерывание программы нажатием клавиш Ctrl-Break или оператором Stop Нажатие клавиш Ctrl-Break является простейшим способом перевода программы в режим прерывания. Если вы сделаете это в момент выполнения программы на Visual Basic, то появится диалоговое окно Macro Error (см. рис. 22.1). Обратите внимание на то, что это окно содержит сообщение, поясняющее причину прерывания. Если вы нажмете командную кнопку End, то выполнение программы будет прекращено; если нажать командную кнопку Continue, то оно будет продолжено; нажатие командной кнопки Goto вызовет переход к оператору, выполнение которого было прервано; нажатие командной кнопки Help активизирует интерактивную подсказку. Рис. 22.1. Диалоговое окно Macro Error, появившееся в результате нажатия клавиш Ctrl-Break Если вы нажмете командную кнопку Debug, то откроется окно Debug и оператор, который должен был выполняться в момент прерывания, будет заключен в рамку. Например, следующая программа рассчитывает все возможные варианты размена одного доллара и записывает количество монет каждого вида в ячейки строки таблицы: Option Explicit Option Base 1 ! Определение всех вариантов размена $1.00 Sub findCoins() Dim Row As Integer, halves As Integer Dim quarters As Integer, dimes As Integer Dim nickels As Integer, theSum As Integer Row = 1 For halves 2 To 0 Step -1 For quarters = 4 To 0 Step -1 For dimes = 10 To 0 Step -1 359
Visual Basic for Applications в примерах For nickels = 20 To 0 Step -1 theSum = halves * 50 4- quarters * 25 + dimes * 10 + nickels * 5 If theSum = 100 Then With Worksheets ( "Sheetl" ) . Range ("A4 ’’) .Cells(Row, .Cells(Row, .Cells(Row, .Cells(Row, .Cells(Row, .Cells(Row, End With 1) .Value 2) .Value 3) .Value 4) .Value 5) .Value 6) .Value = Row = halves = quarters = dimes = nickels = theSum Row = Row + 1 End If Next nickels Next dimes Next quarters Next halves End Sub Программа обрабатывает в цикле все возможные количества монет и отображает в таблице те комбинации, которые в сумме дают один доллар. На рис. 22.2 изображено окно Debug, открытое для данной программы. _[ Watch T Immediate 1 |findCoins I Value Б 5 1 0 0 100 Expression 6rf Row Ь6 dimes 6rf halves nickels 6tf quarters 6rf theSum I Context Modulel. findCoins Modulel .findCoins Modulel. findCoins Modulel .findCoins Moddel .findCoins Modulel findCoins .Cells(Row, .Cells(Row, .Cells(Row, .Cells(Row, End With ^OW ” Row 4- 1] End If Next nickels 3) .Value ■ quarters 4) .Value ■ dimes 5) .Value - nickels 6) .Value = theSum Рис. 22.2. Окно Debug, открытое после нажатия клавиш Ctrl-Break и последующего выбора командной кнопки Debug 360
Глава 22. Применение контрольных точек и точек слежения С этого момента вы можете создавать отслеживаемые переменные или отслеживае¬ мые выражения для проверки значений переменных (как изображено на рис. 22.2, панель Watch). Отслеживаемое выражение подобно отслеживаемой переменной, но отслеживает значение не переменной, а формулы. Значение формулы также отобра¬ жается на панели Watch. Применение в программе оператора Stop аналогично нажатию клавиш Ctrl-Break, за исключением того, что когда в программе встречается оператор Stop, то сразу отображается диалоговое окно Debug, а не Macro Error. Определенное неудобство применения оператора Stop заключается в том, что для продолжения программы необходимо вначале завершить ее, затем удалить оператор Stop, а уже после этого запустить программу с начала без данного оператора. Значительно удобнее прерывать выполнение программ, используя контрольные точки. Установка и удаление контрольных точек Контрольные точки отмечают те места в программе, в которых она должна перейти в режим прерывания. В отличие от оператора Stop контрольные точки могут быть добавлены или удалены без завершения программы. Кроме того, контрольные точки не запоминаются в программе и исчезают после окончания ее работы и закрытия рабочей папки, содержащей программу. Чтобы установить контрольную точку, выберите оператор, на котором вы хотите остановить программу, а затем укажите директиву Run/Toggle Breakpoint либо нажмите клавишу F9 или пиктограмму Toggle Breakpoint пиктографического меню Visual Basic. Чтобы удалить контрольную точку, укажите директиву Run/Toggle Breakpoint либо нажмите клавишу F9 или пиктограмму Toggle Breakpoint пиктогра¬ фического меню Visual Basic. Чтобы удалить все контрольные точки, установленные в программе, укажите директиву Run/Clear All Breakpoints. Обратите внимание на то, что удалять все контрольные точки перед завершением или сохранением программы не нужно, так как они не сохраняются в программе. Контрольная точка устанавли¬ вается непосредственно перед выбранным оператором; следовательно, этот оператор и будет тем, с которого продолжится выполнение программы. Например, выберите в процедуре findCoins оператор If, а затем укажите директиву Run/Toggle Breakpoint. Фон оператора изменится на красный, что указывает на установленную контрольную точку (см. рис. 22.3). 361
Visual Basic for Applications в примерах Edit View Insert Run Tools Window Help M N| ► | ►|\MfldMtel/'Sheet1 / Sheet2 / Sheet3 / SheeM / S|»[ Рис. 22.3. Процедура find Coins с контрольной точкой, установленной на операторе If. На цветном мониторе оператор, содержащий контрольную точку, выделен красным фоном 1) .Value = Row 2) .Value « halves 3) .Value ■» quarters 4) .Value » climes 5) .Value « nickels 6) .Value “ theSum For halves e 2 To 0 Step -1 For quarters ■ 4 To 0 Step -1 For dimes ■ 10 To 0 Step ~1 For nickels - 20 To 0 Step -1 theSum “ halves * 50 + quarters + nickels * 5 MHMMMHTHTWSffiWWTi'bilL * !№ With Worksheets ("Sheet 1") . Range (,rA4") .Cells (Row, .Cells(Row. .Cells(Row, .Cells(Row, .Cells(Row, .Cells(Row, End With Row = Row + 1 End If Next nickels Next dimes Если вы запустите программу директивой Run/Start или нажав пиктограмму Run Macro пиктографического меню Visual Basic, то программа будет выполняться до тех пор, пока не дойдет до контрольной точки; после этого она перейдет в режим прерывания и отобразится окно Debug (см. рис. 22.4). С этого момента вы можете проверять значения переменных и выражений. 362
Глава 22. Применение контрольных точек и точек слежения I Watch Г" Immediate I Contort Expie»» ion [findCoins lvalue М Rov* 1 Modulel .findCoins Ь6 dimes 10 Modulel .findCoins 6rf halves 2 Modulel findCoins W nickels 20 Modulel .findCoins Ъ6 quarters 4 Modulel. findCoins theSum 400 Modulel .findCoins theSum = halves * SO + quarters * 25 + climes « 10 + nickels * 5 |lf theSum - 100 Theij ил With Worksheets("Sheet 1").Range("A4") .CellsfRow, 1).Value * Row .CellsfRow, 2).Value - halves .CellsfRow, 3).Value » quarters .Cells(Row, 4).Value “ dimes Рис. 22.4. Окно Debug с процедурой find Coins, прерванной на контрольной точке Запуск программы после контрольной точки Если вы укажете директиву Run/Continue или нажмете пиктограмму Resume Macro пиктографического меню Visual Basic, то выполнение процедуры продолжится с места останова и будет продолжаться до тех пор, пока опять не встретится контрольная точка (см. рис. 22.5). Обратите внимание на то, что значение переменной nickels (переменной внутреннего цикла) уменьшилось на 1, а переменной theSum —на 5. Рис. 22.5. Окно Debug с процедурой find Со ins, вторично остановлено на контрольной точке 363
Visual Basic for Applications в примерах Вы можете установить в программе несколько контрольных точек, чтобы выпол¬ нить программу по частям, или запустить программу по шагам, используя пошаговые директивы. Существуют две пошаговые директивы: Run/Step Into (F8) и Run/Step Over (Shift-F8). Обеим этим директивам соответствуют пиктограммы пиктографичес¬ кого меню Visual Basic. Директива Run/Step Into выполняет один оператор программы, после чего программа переходит в режим прерывания. Если процедура вызывает другую проце¬ дуру, то директива Run/Step Into переходит на вызванную процедуру и выполняет один ее оператор. Директива Run/Step Over работает во многом аналогично директиве Run/Step Into, но если происходит вызов другой процедуры, то директива Run/Step Over вычисляет все операторы вызванной процедуры и не останавливает программу до тех пор, пока управление не будет возвращено вызвавшей процедуре. Эта директива вызывает как бы "перешагивание" вызова процедуры, хотя в действительности вызов выполняется. Например, указав для ситуации, изображенной на рис. 22.5, любую из пошаговых директив, вы получите ситуацию, отраженную на рис. 22.6, когда точка выполнения переместится к оператору End If. Точка выполнения переместится к этому оператору, так как выражение в операторе If имеет значение False; следовательно, весь блок операторов в структуре If будет пропущен. Если вы опять укажете пошаговую директиву, то получите результат, изображенный на рис. 22.7; в этом случае точка выполнения перешла к следующему оператору. / Debug -COINS.XLS.Morfule1 ? Vaiue Context [ Watch T Immediate ] Ь6 Row 1 Modulel. findCoins LJ I М dimes 10 Modulel.findCoins ■ halves 2 Modulel .findCoins И nickels 19 Modulel.findCoins И 6x5 quarters 4 Modulel.findCoins I 6x5 theSum 395 Modulel .findCoins l7l .CelIs(Row, 2).Value ” halves l + l .Cells(Row, 3).Value ” quarters .Cells(Row, 4).Value m dimes .Cells(Row, 5).Value “ nickels .Cells(Row, 6).Value ” theSum End With Row e Rew + 1 HJ !nd I Рис. 22.6. Окно Debug с процедурой findCoins после нажатия пиктограммы Step Into пиктографического меню Visual Basic. Точка выполнения переместилась к концу блочной структуры If 364
Глава 22. Применение контрольных точек и точек слежения Рис. 22.7. Окно Debug с процедурой find Coins после повторного нажатия пиктограммы Step Into пиктографического меню Visual Basic. Точка выполнения переместилась вниз на одну строку Таким образом, можно использовать контрольные точки в тех местах программы, которые вызвали осложнения, а затем использовать пошаговые директивы для осто¬ рожного продолжения в пределах выбранной зоны, проверяя значения переменных в поисках причины ошибки. Установка точек слежения Ранее вы установили контрольную точку внутри циклов, а затем пошагово выполняли программу. Как только циклы выполнились в первый раз, программа прервалась на контрольной точке, что позволило проверить переменные. Чтобы выполнить цикл еще раз, вы продолжили выполнение программы, которая повторно прервалась на контрольной точке. Если вы хотите увидеть, что произошло после третьей или четвертой итерации цикла, то достаточно просто указать директиву Run/Continue. Но что делать, если вас интересуют значения переменных после тысячи или десяти тысяч итераций циклов? Вам, безусловно, не понравится десять тысяч раз указывать директиву Run/Continue. Для решения задач этого типа используйте вместо контрольной точки точку слежения. Точка слежения представляет собой комбинацию отслеживаемого выраже¬ ния и контрольной точки, позволяющую отслеживать значение переменной или выражения и переводить программу в режим прерывания, если произошло указанное изменение: выражение изменило свое значение на True или вообще изменило значение. 365
Visual Basic for Applications в примерах Чтобы установить точку слежения, необходимо выбрать переменную, которую вы хотите отслеживать, а затем указать директиву Tools/Add Watch, Указанная дирек¬ тива отображает диалоговое окно Add Watch (рис. 22.8), которое позволяет установить точку слежения и ее опции. Рис. 22.8. Диалоговое окно Add Watch Предположим, вы хотите прервать процедуру find Coins, если количество пятаков равно 3, а четвертаков — 1. Необходимо заполнить диалоговое окно так, как изображено на рис. 22.9. В части Watch Зуре включите кнопку выбора Break When Value Is True. Затем продолжите выполнение программы, нажав командную кнопку ОК и указав директиву Run/Continue. Рис. 22.9. Диалоговое окно Add Watch, указывающее прерывание программы, если nickels = 3 and quarters = 1 366
Однако вы забыли удалить контрольную точку на операторе If, и процедура опять прервалась. Укажите директиву Run/Toggle Breakpoint — и она удалит эту точку. Теперь опять укажите директиву Run/Continue. Через некоторое время появится окно Debug, изображенное на рис. 22.10. ( Welch У Immediate *| Debug - COINS.XLS.Modul(H •5e- nickels ». J'Ahd pijarier$ = 1 -Jn.ie • For quarters = 4 To 0 Step -1 For dimes - 10 To 0 Step -1 For nickels ■ 20 To 0 Step -1 theSum = halves * 50 + quarters + nickels ♦ 5 If theSum ~ 100 Then With Worksheets("Sheet 1").Range("A4") .Cells(Row, 1).Value Meddle VliridC pins ♦ Рис. 22.10. Окно Debug с процедурой findCoins, прерванной на точке слежения Обратите внимание на то, что значения переменных nickels и quarters равны указанным в диалоговом окне Add Watch. Кроме того, точка слежения выделена на панели Watch, чтобы указать причину перевода программы в режим прерывания. Примечание: В отличие от контрольных точек точки слежения устанавливаются не для опера¬ торов, а для процедур. Где бы ни находилась в процедуре точка выполнения, если отслеживаемое выражение примет значение True, то процедура будет переведена в режим прерывания. На рис. 22.10 заключена в рамку строка на кодовой панели, содержащая оператор, следующий за оператором For, который изменил значение переменной nickels. Таким образом, как только переменная nickels получила требуемое значение, процедура прервалась. Как и ранее, вы можете проверить значения переменных и пошагово выполнить процедуру, чтобы определить причину ошибки. 367
Visual Basic for Applications в примерах Применение оператора Debug.Print для отслеживания изменений значений переменных Иногда в процессе выполнения программы нужно отслеживать значения перемен¬ ной или переменных. Вместо того чтобы прерывать программу после каждого шагай проверять это значение, поместите в программу оператор Debug.Print для печати значений на панели Immediate окна Debug. Отладочный код вроде оператора Print обычно помещается внутри блочного оператора If. В качестве аргумента блочного оператора If используется глобальная отладочная переменная. Вы можете отключить код, изменив значение отладочной переменной на False. В противном случае для отключения отладочного кода придется либо удалить этот код, либо преобразовать его в строки комментариев. Так, переменная DebuglsOn является глобальной переменной типа Boolean, управ¬ ляющей выполнением следующего блока кода: If DebuglsOn Then Debug.Print "В точке 1” ... Debug.Print "Значение A: ";A End If В начале проверки программы значение переменной DebuglsOn устанавливается в True. После завершения проверки достаточно просто изменить значение переменной DebuglsOn на False, и отладочный код никогда не будет выполняться. Если вы поместили в программу более одного оператора Debug.Print, то полезно вставить еще и оператор печати сообщения, указывающего на то, какой из операторов выполняется. Кроме того можно использовать эти операторы для регистрации хода выполнения сложной программы, печатая сообщения о переходе к наиболее важным ветвям программы. Использование звукового сигнала для отслеживания хода выполнения программы Зачастую вы не хотите прерывать выполнение программы, но вам необходимо знать, какая часть программы выполняется в данный момент. Используйте оператор Веер для подачи одного или нескольких системных звуковых сигналов в интересую¬ щих вас местах программы. После оператора Веер нужно добавить короткую паузу, иначе звуковые сигналы сольются и вы не сможете отличить один от другого. Так, приведенная ниже процедура использует в качестве аргумента целое число, задающее количество звуковых сигналов. Эту процедуру можно вызывать в тех местах программы, которые вас интересуют. 368
Глава 22. Применение контрольных точек и точек слежения ' Процедура doBeeps Sub doBeeps(numBeeps As Integer) I Dim I As Integer, J As Integer For 1 = 1 To jiumBeeps Beep For J = 1 To 500 'Короткая пауза. Next J Next I For J = 1 To 1000 'Более длинная пауза в конце. Next J End Sub Первый цикл J задает короткую паузу, чтобы несколько звуковых сигналов не сливались. Второй цикл J задает длинную паузу в конце. Вы можете изменить верхний предел переменных циклов в зависимости от быстродействия вашего компьютера, но помните, что паузы должны быть короткими, однако достаточными для того, чтобы подсчитать звуковые сигналы. Заключение Из этой главы вы узнали о том, как проверять переменные в выполняемой программе и как отслеживать изменения, приводящие к сбою. Применяя контрольные точки, точки слежения и пошаговые директивы, вы прерывали программу, а затем пошагово выполняли ее. Использование операторов Debug.Print и Веер позволит следить за выполнением программ в динамике. Из следующей главы вы узнаете о том, как ловить ошибки, чтобы программа могла обрабатывать их, а не попадать в аварию. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Что такое режим прерывания? 2. Что такое контрольная точка? 3. Чем отличается отслеживаемое выражение от точки слежения? 4. Для чего предназначен оператор Stop? 5. Сколько существует различных способов разменять один доллар? 369
Visual Basic for Applications в примерах 6. Как можно удалить контрольную точку? Все контрольные точки? 7. Чем директива Step Into отличается от директивы Step Over? Упражнения для закрепления материала 1. Вставьте в процедуру findCoins вызов процедуры doBeeps, чтобы процедура издавала звуковой сигнал при каждом изменении значения переменной Row. 2. Установите в программе базы данных персональных контактов контрольные точки и проследите изменения некоторых переменных. 3. Установите в процедуре findCoins точку слежения, прерывающую выполнение программы, если значение переменной dimes равно 2. Затем добавьте точку слежения, прерывающую выполнение программы, если количество десятиценто¬ вых монет изменится. 4. Вставьте в процедуру findCoins операторы Debug.Print, печатающие комбинации монет, не вошедшие в таблицу. 370
Глава 23 Применение прерываний по ошибке для реакции на непредвиденные события Когда в программе обнаруживается ошибка, активизируется обработчик ошибок Visual Basic. Он останавливает программу и отображает диалоговое окно Macro Error, содержащее код ошибки и ее описание. С этого момента пользователь обычно не имеет иного выбора, кроме завершения программы и потери всей несохраненной информа¬ ции. Это не совсем то, что вы хотели бы предложить пользователям своей замечатель¬ ной программы. Как программист, вы могли бы переключиться на окно Debug и сохранить данные, но вы не можете предполагать, что и пользователь будет способен осуществить такие действия. Что же делать, чтобы обрабатывать такие непредвиден¬ ные ситуации и устранять возникшие проблемы или хотя бы предоставить пользова¬ телю возможность корректно выйти из программы? Конечно, установить прерывания по ошибке 1 Из этой главы вы узнаете: > Как установить прерывание по ошибке > Как создать обработчик ошибок > Что такое иерархия обработчиков ошибок Что такое прерывание по ошибке? Прерывание по ошибке перехватывает ошибки до того, как они поступят к обработчику ошибок Visual Basic и вызовут появление сообщения об ошибке. По существу, прерывания по ошибке являются частью обработчика ошибок Visual Basic. Когда происходит ошибка, управление переходит от программы пользователя к обработчику ошибок Visual Basic. Он получает от системы код ошибки и проверяет наличие разрешенного прерывания по ошибке в программе пользователя. Если прерывание по ошибке разрешено, то управление передается программе обработки ошибок. Если не разрешено, то обработчик ошибок Visual Basic отображает диалоговое окно с кодом ошибки и сообщением для пользователя. Следовательно, чтобы установить прерывание по ошибке, необходимы обработчик ошибок и разрешение прерывания по ошибке. Обработчик ошибок — это программа, обрабатывающая ошибки, а разрешение прерывания по ошибке —оператор, включаю¬ щий прерывание по ошибке и идентифицирующий соответствующую программу обработки ошибок. 371
Visual Basic for Applications в примерах Для чего нужны прерывания по ошибке? Для чего нужны прерывания по ошибке, когда есть обработчик ошибок Visual Basic? В большинстве случаев обработчик ошибок Visual Basic работает просто великолепно! Когда программа терпит крах по любой причине, то остается только перезапустить программу и попробовать еще раз. Рассмотрим версию программы работы с базой данных персональных контактов, разработанную для последователь¬ ного файла. Эта программа хранит базу данных в памяти компьютера до тех пор, пока вы, выбрав командную кнопку "Сохр.", не запишете данные на диск. Что произойдет, если вы запишете порядка 100 новых записей й попробуете сохранить их на дискете, которую забыли вставить в дисковод? Поскольку дисковод пуст, вы получите ошибку "Drive not ready" ("Дисковод не готов") и ваша программа потерпит крах. Если вы выберете командную кнопку End диалогового окна Macro Error, то потеряете все записи, находящиеся в памяти компьютера. Вы, конечно, можете перезапустить программу и перезаписать все 100 записей, но более простым решением будет передать управление обработчику ошибок, который предложит вам вставить дискету, а затем вернет управление в ту точку программы, где произошла ошибка, чтобы программа могла продолжить свою работу. Процедуры, работающие с дисковыми файлами, имеют много общего с точки зрения обработки ошибок. Когда процедура пытается получить доступ к диску, может возникнуть множество неожиданных проблем: конец файла; удаление из дисковода дискеты в то время, когда программа начинает на нее запись; дискета защищена, полная или отсутствует в дисководе. Таким образом, данные процедуры, несомненно, нуждаются в универсальном обработчике ошибок. Вы можете создать процедуру, пытающуюся проверять каждую предполагаемую ошибку перед тем, как обратиться к диску, но часто такая проверка сама становится причиной ошибки. С другой стороны, пользователи вашей программы не только совершат все те ошибки, которые вы придумали, но и несколько сотен других — которые вы и не предполагали. Еще одной общей группой (с точки зрения обработки ошибок) являются процедуры численных расчетов, особенно процедуры, производящие итерационные вычисления. Итерационные вычисления — это численные методы, повторяющие расчеты по определенной формуле и использующие результат предыдущего расчета в качестве исходных данных для следующего. Примером такой процедуры может служить метод последовательных приближений для вычисления корней уравнения. Итерационные процедуры периодически получают расходящиеся результаты, что может стать причиной переполнения или потери значимости. Кроме того, при вычислениях может произойти попытка деления на 0 или извлечения квадратного корня из отрицательного числа. Следовательно, вам необходим обработчик ошибок, который будет перехватывать и корректировать ошибки такого рода, что позволит вашей программе избежать краха. 372
Глава 23. Применение прерываний по ошибке Разрешение прерывания по ошибке В Visual Basic прерывание по ошибке разрешается оператором On Error: On Error GoTo метка Вы помещаете этот оператор в тех местах программы, где хотите включить прерывание по ошибке, а аргумент метка указывает на метку в начале обработчика ошибок. Этот оператор не выполняет обработку ошибки, а только разрешает преры¬ вание для выполнения обработки ошибки, если в программе встретится ошибка. Ограничением является то, что и разрешение прерывания по ошибке, и обработчик ошибок должны быть в одной процедуре. Ниже приведен общий макет процедуры с обработчиком ошибок: Sub аРгос() On Error GoTo theErrorHandler 'Включение прерывания. г ' Операторы, осуществляющие нормальное выполнение процедуры. г Exit Sub 'Нормальное завершение процедуры. theErrorHandler: 'Начало обработчика ошибок. ' Операторы обработчика ошибок. г Resume 'Завершение обработчика ошибок. End Sub Вы можете не использовать этот макет, но обязательно отделяйте код обработчика ошибок от нормального кода процедуры с помощью оператора Exit Sub, чтобы нормально выполняемая процедура случайно не запустила на выполнение код обра¬ ботчика ошибок. Для функций используется аналогичный макет, но вместо оператора Exit Sub применяется оператор Exit Function. Прерывания по ошибке запрещаются, когда процедура, содержащая их, заканчи¬ вает свою работу. Чтобы отключить или запретить прерывание по ошибке, в процеду¬ ре, содержащей его, используется следующий оператор: On Error GoTo О В каждый конкретный момент времени в процедуре может быть активным только один обработчик ошибок. Если у вас для различных блоков кода есть различные обработчики ошибок, то вы должны запретить разрешенный обработчик прежде, чем разрешить другой. 373
Visual Basic for Applications в примерах Как выбираются прерывания по ошибке? В одной процедуре в данный момент времени может быть активным только один обработчик ошибок. Однако в других процедурах из активной цепочки вызовов также могут быть активные обработчики ошибок —таким образом формируется иерархия обработчиков ошибок (см. рис. 23.1). Вспомните, что активная цепочка вызовов — это список имен активных процедур в том порядке, в котором они вызывались вплоть до текущей выполняемой процедуры. Рис. 23. /. Иерархия обработчиков ошибок в двух активных цепочках вызовов. Активные цепочки вызовов составляют процедуры А и В или процедуры А, С, D и Е Когда управление от программы передается обработчику ошибок Visual Basic, то последний вначале проверяет, есть ли в текущей активной процедуре активный обработчик ошибок. Если такового нет, то обработчик ошибок Visual Basic переклю¬ чается на процедуру, которая вызвала процедуру с ошибкой, и проверяет ее. Если и в этой процедуре нет активного обработчика ошибок, то обработчик ошибок Visual Basic проверяет предыдущую процедуру в цепочке. Этот процесс продолжается вплоть до начала цепочки вызовов. Если во всей цепочке вызовов не удалось обнаружить активный обработчик ошибок, то обработчик ошибок Visual Basic сам обрабатывает эту ошибку, отображая диалоговое окно Macro Error. Учитывая путь, которым Visual Basic ищет обработчики ошибок, каждая проце¬ дура может не включать код, обрабатывающий все возможные ошибки. Наиболее общие ошибки могут обрабатываться в начале цепочки вызовов, а более специфические — в тех процедурах, в которых они возникают. 374
Глава 23. Применение прерываний по ошибке Например, иерархия, изображенная на рис. 23.1, охватывает две активные цепочки вызовов. Если активной является процедура В, то активную цепочку вызовов форми¬ руют процедуры А и В. Обработка всех ошибок, возникающих в процедурах А и В, производится в процедуре А, так как только она содержит активный обработчик ошибок. Если активной является процедура Е, то цепочка вызовов состоит из процедур А, С, D и Е. Если в процедуре Е возникает ошибка, то вначале происходит Прерывание 2 в процедуре Е. Если Прерывание 2 не обрабатывает данную ошибку, то система ищет активное прерывание по ошибке в процедуре D. Поскольку процедура D не содержит активного прерывания по ошибке, то система проверяет процедуру С. Обнаружив в процедуре С Прерывание 1, система передает ошибку ему на обработку. Если и Прерывание 1 не обрабатывает эту ошибку, то он возвращается в систему, которая проверяет процедуру А на активное прерывание по ошибке, находит Основное прерывание и передает ошибку ему. Если и Основное прерывание не может обработать эту ошибку и возвращает ее в систему, то система отображает диалоговое окно Macro Error. Создание обработчика ошибок В общем виде структура обработчика ошибок состоит из заголовка, тела и концовки. Заголовок содержит метку начала обработчика, на которую указывает оператор On Error. Тело обработчика содержит код, обрабатывающий ошибки, а концовка состоит из оператора Resume, возвращающего управление выполняемой программе. Примечание: В программах на Visual Basic меткой является слово или число в начале строки, за которым следует двоеточие. Кроме того, в этой строке может размещаться и какой-нибудь оператор, расположенный после метки, однако вообще оператор принято размещать на следующей строке. Когда выполняется обработчик ошибок, то прерывание этого обработчика запре¬ щено. Если ошибка произошла в вашем обработчике ошибок, то управление возвра¬ щается к обработчику ошибок Visual Basic, который ищет другой обработчик ошибок для обработки этой ошибки или отображает диалоговое окно Macro Error. Если же обработчик ошибок доработал до конца и выполнился оператор Resume, то соответ¬ ствующее прерывание по ошибке опять разрешается. Обычно обработчик ошибок вначале проверяет ошибку, чтобы определить, может ли он ее обработать. Для получения кода ошибки используется функция Err. Список кодов ошибок Visual Basic приведен в Приложении В. Если полученный код ошибки передать в качестве аргумента функции Error(), то она возвратит строку, содержащую описание этой ошибки (аналогичное выводимому в диалоговом окне Macro Error). 375
Visual Basic for Applications в примерах В теле обработчика обычно используется структура Select Case или блочный оператор If, выбирающий соответствующий блок кода для обработки произошедшей ошибки. Например, для процедуры, открывающей файл, можно создать обработчик оши¬ бок, подобный приведенному ниже: г ' Получение имени файла и открытие заданного файла. г Sub OpenAFile(filenum As Integer, filename) Const errBadFileNameOrNumber = 52 Const errFileNotFound = 53 Const errBadFileMode =54 Const errFileAlreadyOpen = 55 Const errDevicelOError = 57 Const errTooManyFiles = 67 Const errDeviceUnavailable = 68 Const errPermissionDenied = 7Q Const errDiskNotReady = 71 Const errPathFileAccessError = 75 Const errPathNotFound = 76 ' Эти ошибки при открытии файла не возникают: t ' Const errFileAlreadyExists = 58 ' Const errDiskFull = 61 ' Const errlnternalError =51 ' Const errBadRecordLength =59 ' Const errBadRecordNumber = 63 ' Const errCantRenameWithDifferentDrive = 74 ' Const errlnputPastEndOfFile = 62 f ' Разрешение обработчика ошибок. On Error GoTo DiskErrorHandler filenum = FreeFile() TryAgain: filename = InputBox("Ввод имени файла") If filename = "" Then Exit Sub 'Пользователь нажал Cancel. Open filename For Input As filenum Exit Sub 'Нормальное завершение процедуры. 376
Глава 23. Применение прерываний по ошибке ' Начало обработчика ошибок. DiskErrorHandler: Select Case err Case errBadFileNameOrNumber MsgBox ’’Некорректное имя файла, попробуйте еще раз.” Resume TryAgain Case errFileNotFound MsgBox ’’Нет файла с таким именем, попробуйте еще раз." Resume TryAgain Case errBadFileMode MsgBox "Этот файл нельзя открыть для ввода, попробуйте еще раз.” Resume TryAgain Case errFileAlreadyOpen MsgBox "Этот файл уже открыт, попробуйте еще раз." Resume TryAgain Case errDevicelOError MsgBox "Ошибка ввода/вывода, попробуйте еще раз." Resume TryAgain Case errTooManyFiles MsgBox "Отрыто слишком много файлов, закройте хоть один." Resume Next Case errDeviceUnavailable MsgBox "Данный дисковод недоступен,попробуйте еще раз." Resume TryAgain Case errPermissionDenied MsgBox "Этот диск защищен, попробуйте еще раз." Resume TryAgain Case errDiskNotReady MsgBox "Вставьте в дисковод дискету и нажмите клавишу Enter." Resume Case errPathFileAccessError MsgBox "Этот файл недоступен, попробуйте еще раз." Resume TryAgain Case errPathNotFound MsgBox "Заданный путь некорректен, попробуйте еще раз." Resume TryAgain Case Else 'Незапланированная ошибка, возврат в систему. Error err End Select End Sub 377
Visual Basic for Applications в примерах Этот обработчик ошибок просто возвращает управление на блок отображения окна ввода, чтобы пользователь мог повторить имя файла. Более сложный обработчик ошибок мог бы предложить пользователю выбор: попробовать еще раз, завершить работу, сохранить данные и завершить или продолжить работу. В примере обработчика ошибок вычислений приведенная ниже функция вычисляет значение Sin(x)/x, а для обработки ошибки деления на 0 использует прерывание по ошибке. В случае этой ошибки функция получает значение 1, что является корректным значением для Sin(0)/0. Если же происходит ошибка, отличная от деления на 0, то обработчик ошибок отображает окно сообщения и возвращает ошибку обработчику ошибок Visual Basic. t ' Расчет Sin(x)/x г Function SinXOverX(X As Double) As Double Const errDivideByZero = 11 On Error GoTo Fixlt SinXOverX = Sin(X) / X Exit Function Fixlt: If err = errDivideByZero Then SinXOverX = 1 Resume Next Else MsgBox "Незапланированная ошибка " & err & ", " & Error(err) & ", в функции Sin(x)/x ." Error err End If End Function Возврат управления из обработчика ошибок Любой обработчик ошибок должен заканчиваться оператором Exit Sub, Exit Function, Resume или Error. Оператор Exit Sub (или Exit Function) запрещает данное прерывание по ошибке и возвращает управление процедуре, вызвавшей процедуру с ошибкой. Естественно, прежде чем вернуть управление вызвавшей процедуре, вы должны решить задачу, ставшую причиной ошибки. 378
Глава 23. Применение прерываний по ошибке Оператор Resume вновь разрешает данное прерывание по ошибке и возвращает управление текущей процедуре. Путь возврата управления от обработчика ошибок является нормальным, если этот обработчик находится в процедуре, вызвавшей ошибку. Существуют три формы оператора Resume: Resume Resume Next Resume метка Первая форма возвращает управление оператору, ставшему причиной ошибки, что позволяет повторить этот оператор. Эта форма используется в тех случаях, когда причина ошибки обнаружена, устранена и можно попробовать еще раз. Вторая форма передает управление оператору, следующему за тем, который вызвал ошибку. Третья форма передает управление оператору, помеченному заданной меткой. Если прерывание по ошибке находится не в той процедуре, которая стала причиной ошибки, то вы можете использовать только третью форму оператора Resume, так как две первые формы дадут непредсказуемые результаты. В процедуре OpenAFile (см. предыдущий раздел) в основном используется третья форма оператора Resume, так как большинство ошибок требуют, чтобы пользователь выбрал другое имя файла, перед тем как вновь выполнить оператор Open. Если новое имя файла вы получили в обработчике ошибок, то можно вернуть управление оператору Open, использовав первую форму оператора Resume. Оператор Error, отличающийся от функции Error, имитирует ошибку, используя код ошибки в качестве аргумента и возвращает управление обработчику ошибок Visual Basic. Этот оператор применяется для возврата ошибки в систему и передачи системе функций обработки данной ошибки. Обработка непредвиденных ошибок Что делать, если ваш обработчик ошибок получает ошибку, которую не умеет обрабатывать? Первая мысль — вернуть эту ошибку в систему и пусть система ее обрабатывает. В качестве альтернативного способа можно создать процедуру — "ловушку", которая корректно завершит вашу программу, сохранив данные и закрыв файлы. Такую "ловушку" обычно применяют при обработке ошибок или перед возвратом ошибки в систему (в блоке Case Else структуры Select Case или в блоке Else блочного оператора If). Для возврата управления обработчику ошибок Visual Basic используется оператор Error с функцией Err в качестве аргумента: Error Err 379
Visual Basic for Applications в примерах Этот оператор имитирует ту же ошибку, которая вызвала передачу управления обработчику ошибок, и возвращает эту ошибку в систему. Теперь обработчик ошибок Visual Basic должен либо найти другой обработчик ошибок, либо обработать эту ошибку самостоятельно, так как обработчик ошибок в этой процедуре уже использован и данное прерывание запрещено. Этот оператор используется для передачи ошибки вверх по активной цепочке вызовов для обработки этой ошибки в одной из процедур цепочки. Используя иерархию обработчиков ошибок таким образом, вы помещаете блок нормального завершения в обработчик ошибок первой процедуры цепочки вызовов. Когда ошибка происходит в одной из процедур этой цепочки вызовов, процедура может либо обработать эту ошибку, либо вернуть ее по цепочке. Если ошибка пройдет по цепочке до первой процедуры и не будет обработана, управление получит "блок корректного завершения", который и завершит выполнение программы, сохранив данные и закрыв файлы. Создание ошибок, определенных пользователем Кроме возврата вызвавшей процедуре оригинального кода ошибки вы имеете возможность создать и вернуть ошибку, определенную пользователем. Диапазон кодов ошибок — от 1 до 65535, но Visual Basic использует только первые несколько тысяч. Вы можете использовать любой из незанятых кодов для создания собственной программно-заданной ошибки. Совет: Хотя текущая версия Visual Basic использует всего несколько первых тысяч кодов ошибок, вы никогда не узнаете, какие из них могут использовать текущие версии Поскольку существующий список кодов ошибок может расширяться, вы должны избегать использования для ошибок, определенных пользователем, младших кодов. Начните с 20000 или 30000 и увеличивайте коды либо начните с 65535 и уменьшайте коды. Это позволит избежать конфликтов с будущими версиями Visual Basic. Ошибки, определенные пользователем, применяются для возврата вверх по цепоч¬ ке вызовов. Например, ошибка деления на 0 может потребовать некоторых общих изменений в начале программы, но если вы передадите эту ошибку по цепочке, то один из обработчиков ошибок может просто скорректировать ее, вместо того чтобы передать в начало программы. Передача нечисловых кодов ошибок в переменных Кроме прерывания программы ошибкой, определенной пользователем, и примене¬ ния этого прерывания для передачи ошибки вызвавшей процедуре вы можете при¬ своить переменной нечисловой код ошибки, а затем вернуть значение этой переменной 380
Глава 23. Применение прерываний по ошибке вызвавшей процедуре, использовав для этих целей один из аргументов процедуры (или имя функции). Вы можете вернуть в переменной один из двух типов кодов ошибок: коды ошибок Excel и коды ошибок, определенные пользователем. В обоих случаях переменная должна быть типа Variant, чтобы иметь возможность передать код ошибки, отличный от числа. Использование кодов ошибок Excel В гл. 10 вы использовали коды ошибок Excel для возврата ошибок из пользова¬ тельских функций в таблицу. Точно так же можно передать такие ошибки другой процедуре. Нечисловые коды ошибок создаются функцией CVErrO, получающей в качестве аргумента числовой код ошибки. Список встроенных констант, используемых этой функцией для возврата ошибок Excel, приведен в табл. 23.1. В качестве альтернативы применению функции CVErrO можно просто заключить нечисловой код ошибки в квадратные скобки. Такая техника дает те же результаты, что и применение функции. Например, результаты следующих двух операторов идентичны: theResult = CVErr(xlErrRef) theResult = [#REF!] Таблица 23.1. Константы, используемые в функции CVErrO для возврата кодов ошибок Excel Константа Значение Код ошибки xlErrDivO 2007 #DIV/0! xlErrNA 2042 #N/A xlErrName 2029 #NAME? xlErrNull 2000 #NULL! xlErrNum 2036 #NUM! xlErrRef 2023 #REF! xlErrValue 2015 #VALUE! Для проверки переменных на код ошибки используется функция IsErrorO, которая возвращает True, если аргумент —код ошибки, и False —если нет. Любые вычисления, которые вы произведете с переменной, содержащей код ошибки, дадут в результате тот же код ошибки. Следите за тем, чтобы переменные, которые могут содержать коды ошибок, были типа Variant. 381
Передача в переменных ошибок, определенных пользователем Кроме семи кодов ошибок Excel, приведенных в табл. 23.1, можно использовать любые другие числа для создания нечисловых кодов ошибок, определенных пользо¬ вателем; причем числовые коды должны быть в диапазоне от 1 до 65535, а для создания нечисловых кодов используется функция CVErrO (точно так же, как для кодов ошибок Excel). Переменные должны быть типа Variant, а для определения ошибки используется функция IsErrorO. Предупреждение: Если для определения кода ошибки, переданного в переменной, вы используете логическое выражение, то сравнивайте этот код с другим нечисловым кодом ошибки, созданным функцией CVErrO, а не с числовым кодом. Обработка пользовательских прерываний После того как пользователь нажал клавиши Ctrl-Break, программа прерывается и отображается диалоговое окно Macro Error. Даже если существует разрешенный обработчик ошибок, то в случае нажатия клавиш Ctrl-Break программа его игнорирует С этого момента единственными для пользователя являются варианты продолжения или завершения работы программы. Вы могли бы переключиться на окно Debug, но большинство пользователей этого делать не умеют. Когда пользователь нажимает клавиши Ctrl-Break, то обычно не предполагает завершать программу, а только хочет остановить текущую операцию и вернуть ситуацию, в которой программа ничего не будет делать, ожидая от него.директив. Обработчик ошибок управляет такими ситуациями точно так же, как любой другой ошибкой. Отличие состоит лишь в том, что свойству EnableCancelKey объекта Application должно быть присвоено значение константы xlErrorHandler. Если это сделано, нажатие клавиш Ctrl-Break становится аналогичным любой ошибке, а функция Err возвращает код ошибки 18. Следовательно, теперь можно создать обработчик ошибки, позволяющий выбирать пути, отличные от простого завершения программы. Для того чтобы вернуть предыдущее значение свойства EnableCancelKey, присвойте ему значение константы xllnterrupt. Кроме того, свойство EnableCancelKey восстанавливается после окончания работы изменившей его процедуры. Помимо возможности обрабатывать пользовательские прерывания вы имеете воз¬ можность запретить эти прерывания, присвоив свойству EnableCancelKey значение константы xlDisabled. Однако этот способ чреват последствиями, так как вы можете создать процедуру, которая никогда не вернется к пользователю. Если такая проце¬ дура застрянет в бесконечном цикле, а вы запретили пользовательские прерывания, то единственным способом завершить эту процедуру будет перезагрузка компьютера. 382
Глава 23. Применение прерываний по ошибке Задержка обработки ошибки Существуют процедуры, время выполнения которых критично. Останавливать такую процедуру, чтобы обработать возникшую ошибку, не всегда целесообразно. Для процедур такого типа (временные операции или коммуникационные процедуры) обработку ошибки можно задержать до тех пор, пока эта процедура или операция не завершится. В таком случае используется оператор, разрешающий прерывания по ошибке: On Error Resume Next Это прерывание оператор, вызвавший ошибку, просто пропускает и передает управление следующему оператору. После того как критичная по времени операция завершится, можно получить код ошибки с помощью функции Err и затем обработать данную ошибку. Предупреждение: Такая техника несколько рискованна, так как функция Егг вернет код только последней ошибки. Если в процессе выполнения критичной по времени процедуры или операции произошло несколько ошибок, то все они, кроме последней, будут потеряны. Применяйте этот оператор очень осторожно. Применение обработчика ошибок для определения количества размерностей массива Когда разработанная вами процедура получает в качестве аргумента массив, особенно из таблицы, то, как правило, количество размерностей этого массива не определено. Простого способа прямо определить количество размерностей массива не существует, а функции UBound() и LBound() позволяют определить только количес¬ тво элементов в любой из размерностей. Тем не менее, можно определить количество размерностей массива, разрешив прерывание по ошибке и обратившись к размерности. Если такой размерности нет, то произойдет ошибка; если ошибки нет, то размерность есть. Следующая функция использует прерывание по ошибке для определения количес¬ тва размерностей массива. При получении любой другой ошибки она возвращает код ошибки Excel #VALUE! ' Определение количества размерностей массива. Function GetDimensions(theArray) Dim dummy, I As Integer Const errSubscriptOutOfRange = 9 383
Visual Basic for Applications в примерах On Error GoTo trapDim For I = 1 To 100 dummy = UBound(theArray, I) Next I GetDimensions = CVErr(xlErrValue) Exit Function trapDim: If err = errSubscriptOutOfRange Then GetDimensions = I — 1 Else GetDimensions = CVErr(xlErrValue) End If End Function Включение обработчика ошибок в базу данных персональных контактов Версия программы работы с базой данных персональных контактов, разработанная для последовательного файла, имеет две критические точки, которые, могут стать причиной ошибки. Эти точки находятся в процедурах Open_Click и FindName__CIick. В процедуре Open_Click причиной ошибки может стать попытка открыть файл, созданный не этой программой. Например, открытие файла произвольного доступа в этой программе приведет к ошибке. Время, которое потребуется процедуре FindName_Click для поиска записи, прямо зависит от размеров базы данных. При больших размерах базы данных пользователь может захотеть прервать эту процедуру, чтобы продолжить свою работу с базой, не ожидая результатов поиска. В этом случае программа должна обрабатывать прерыва¬ ния по нажатии клавиш Ctrl-Break, завершая процедуру FindName_Click, а не всю программу. Для реализации изложенного вызовите копию версии программы работы с базой данных персональных контактов для последовательного файла, которую вы создали в гл. 18. Модернизированный код процедуры приведен в виде следующего листинга (изменения выделены жирным шрифтом): г ' Программа работы с базой данных персональных контактов. '.Версия для последовательного файла ' с прерываниями по ошибке. г Option Explicit 'Заставляет объявлять все переменные. Option Base 1 'Устанавливает начальный индекс массива в 1. 384
Глава 23. Применение прерываний по ошибке Type DBEntry 'Определяет структуру записи базы данных. Name As String * 25 Address As String * 25 City As String * 15 State As String * 2 Zip As String * 10 Phone As String * 20 Net As String * 10 NetAddr As String * 25 Referral As String * 25 Notes As String * 97 RecNo As Integer End Type 'Размер типа 256 байтов. Dim theDBO As DBEntry Dim nurnEntries As Integer Dim theEntryNum As Integer Dim theFileName As String 'Массив базы данных. 'Общее количество записей. 'Текущая отображаемая запись 'Текущее имя файла. ' Инициализация базы данных. I Sub Initializelt() nuiuEntrie3 = 0 ReDim theDB(1) theEntryNum = 0 Sheets("КонтактыДиалог").DropDowns("ДЕ_Ма11_Сеть").List = _ Array("Relcom" , "Internet", "CompuServe", "Prodigy") Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап”).Min = 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = 1 theFileName = "" Clear-Entries Sheets("КонтактыДиалог").Show End Sub ' Очистка диалогового окна. Sub Clear Entries () Wi th Sheets(”КонтактыДиалог”) .EditBoxes ("ДИмя").Text = .EditBoxes("ДАдрес”) .Text = " ” . Edi tBoxes (" ДГород ”) . Text = ” " 13 Вильям Дж. Орвис 385
Visual Basic for Applications в примерах .EditBoxes ("ДСорана"). Text = ’"' .EditBoxes ("ДИндекс"). Text = " ” .EditBoxes (" ДТелефон") . Text ~ .DropDowns (" ДЕ_Ма±1_Сеть") . Text = " ” .EditBoxes (" ДЕ__Ма11_Адрес"). Text = .EditBoxes ("ДСсылка"). Text = ’’ " .EditBoxes (” ДПримечание") . Text = ” " .Labels("ДНомЗап").Caption = "Запись I» " End With End Sub f ' Процедура Auto_open, вызываемая при каждом открытии ' рабочей папки. г Sub Auto_Open() Initializelt 'Вызов процедуры инициализации. End Sub г ’ Пересылка записи в базу данных. F Sub EntryToDB(anEntry As Integer) With Sheets("КонтактыДиалог") theDB(anEntry).Name = .EditBoxes("ДИмя").Text theDB(anEntry).Address = .EditBoxes("ДАдрес").Text theDB(anEntry).City = .EditBoxes("ДГород").Text theDB(anEntry).State = .EditBoxes("ДСтрана").Text theDB (anEntry) . Zip = . EditBoxes (’’ДИндекс" ). Text theDB(anEntry).Phone = .EditBoxes("ДТелефон").Text theDB (anEntry) . Net = . DropDowns ("ДЕ_МаИ_Сеть "). Text theDB (anEntry). NetAddr = . EditBoxes (”ДЕ_МаИ_Адрес" ). Text theDB(anEntry).Referral = .EditBoxes("ДСсылка").Text theDB(anEntry).Notes = .EditBoxes("ДПримечание").Text theDB(anEntry).RecNo = anEntry End With End Sub e ' Пересылка записи в бланк. г Sub DBToEntry(anEntry As.Integer) With Sheets("КонтактыДиалог") 386
Глава 23. Применение прерываний по ошибке .EditBoxes("ДИмя").Text = theDB(anEntry).Name . EditBoxes (’’ДАдрес” ). Text = theDB (anEntry) .Address .EditBoxes("ДГород").Text = theDB(anEntry).City .EditBoxes("ДСтрана").Text = theDB(anEntry).State .EditBoxes("ДИндекс").Text = theDB(anEntry).Zip .EditBoxes("ДТелефон").Text = theDB(anEntry).Phone . DropDowns ( "ДЕ_МаЫ_Сеть" ) . Text = theDB (anEntry). Net .EditBoxes("ДЕ_Ма11_Адрес").Text = theDB(anEntry).NetAddr .EditBoxes("ДСсылка").Text = theDB(anEntry).Referral .EditBoxes("ДПримечание").Text = theDB(anEntry).Notes .Labels("ДНомЗап").Caption = "Запись № " & Str(theDB(anEntry).RecNo) . ScrollBars("ПрокруткаЗап") .Value = anEntry End With End Sub ' Добавляет новую запись. Sub NewEntry_Click() numEntries = numEntries + 1 Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = numEntries ReDim Preserve theDB(numEntries) 'Увеличение массива. EntryToDB theEntryNum 'Пересылка записи в массив. DBToEntry theEntryNum 'Чтение записи и отображение theEntryNum. End Sub ' Изменяет запись. Sub UpdateEntry_Click() If theEntryNum = 0 Then Exit Sub 'Если еще нет записей, то выход. EntryToDB theEntryNum 'Изменение текущей записи. End Sub ’Удаляет запись. Sub DeleteEntry_Click() Dim I As Integer, buttons As Integer Dim theMsg As String If numEntries = 0 Then Exit Sub 'Еще нет записей. 'Убедимся, что пользователь действительно хочет удалить запись. 387
Visual Basic for Applications в примерах buttons = vbYesNo + vbDefaultButton2 + vbQuestion + _ vbApplicationModal theMsg = "Вы действительно хотите удалить текущую запись?" If MsgBox(theMsg, buttons, "Удаление записи") = vbNo Then Exit Sub 'Пользователь подтвердил удаление. If theEntryNum = numEntries Then 'Это последняя запись. numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) theEntryNum = numEntries El self theEntryNum = 0 Then 'Нечего удалять. Exit Sub Else 'Перемещение оставшихся записей. For I = theEntryNum + 1 To numEntries theDB(I — 1) = theDB(I) theDB (I — 1) . RecNo = 1—1 Next I numEntries = numEntries — 1 ReDim Preserve theDB(numEntries) End If Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Value = theEntryNum Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries DBToEntry theEntryNum End Sub 9 f Прокрутка записей. Sub ScrollEntries_Click() If numEntries = 0 Then Exit Sub 'Еще нет записей. theEntryNum = Sheets ("КонтактыДиалог") . ScrollBars ("ПрокруткаЗап") .Value DBToEntry theEntryNum End Sub ' Поиск имени. Sub ,FindName_Click () Static theName As String 388
Г^ава 23. Применение прерываний по ошибке Dim I As Integer Const Textcomparison = 1 Const errUserlnterrupt = 18 ' Разрешение прерываний по Ctri-Break. Application. EnableCancelKey = xlErrorHandler On Error GoTo Handlein ter rupt 'Включение обработки прерывании. If nuiuEntries = 0 Then Exit Sub 'Еще нет записей. theName = InputBox("Введите имя для поиска", "Поиск имени", theName) If theEntryNum о numEntries Then For I = theEntryNum + 1 To numEntries If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. On Error Go То 0 'Выключение обработки прерывании. Application.EnableCancelKey = xlDisabled 'Запрет прерываний. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. On Error GoТо 0 'Выключение обработки прерываний. Application.EnableCancelKey = xlDisabled 'Запрет прерываний. theEntryNum = I DBToEntry theEntryNum Exit Sub ‘ End If Next I On Error GoTo 0 'Выключение обработки прерываний. 'Включение нормальных прерываний. Application. EnableCancelKey = xllnterrupt 'Запись не найдена. MsgBox "Имя: " & theName & " не найдено.", , "Поиск имени" Exit Sub Handieinterrupt: 'Обработчик прерываний по Ctrl-Break. ' Выход из процедуры, если это прерывание по Ctrl-Break. If Err = errUserlnterrupt Then F.xi t Sub 389
Visual Basic for Applications в примерах Else 'Если не прерывание no Ctrl-Break, то возврат ошибки в систему. 'Включение нормальных прерывании. Application. EnableCancelKey = xllnterrupt Error Err End If End Sub ' Сохранение базы данных. Sub Save_Click() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer If numEntries = 0 Then 'Есть информация для сохранения? MsgBox "Вам еще нечего сохранять.", , "PCD" Exit Sub End If 'Получение имени файла для данных. theFilter = "Базы данных PCD (*.PCD), *.PCD" Answer = Application.GetSaveAsFilename(theFileName, theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. theFileName = Trim(Answer) 'Удаление ведущих и конечных пробелов. 'Проверка имени файла на расширение .PCD theDot = InStr(theFileName, ".") If theDot = 0 Then ^Расширение не указано, добавляем .PCD. theFileName = theFileName & ".PCD" Else 'Какое-то расширение указано, удаляем его и добавляем .PCD. theFileName = Left(theFileName, theDot — 1) & ".PCD" End If fileNum = FreeFile() 'Получение файлового числа и открытие файла. Open theFileName For Output As fileNum 'Вначале сохраним количество записей. Write #fileNum, numEntries 'Теперь обработаем в цикле каждый элемент базы данных 'и запишем все компоненты в файл. For EntryNum = 1 То numEntries With theDB(EntryNum) Write Write Write Write .Name .Address .City .State #fileNum, #fileNum, #fileNum, #fileNum, 390
Глава 23, Применение прерываний по ошибке Write #fileNum, . Zip Write #fileNum, .Phone Write #fileNum, .Net Write #fileNum, .NetAddr Write #fileNum, .Referral Write #fileNum, .Notes Write #fileNum, .RecNo End With Next EntryNum Close #fileNum 'Конец сохранения базы данных. MsgBox "База данных сохранена в файле " & theFileName, , "PCD" End Sub ' Открытие файла базы данных. Sub Open_Click() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer Dim buttons As Integer, theMsg As String Dim Flag As Boolean Const errlnputPastEndOfFile = 62 Const err Subs crip tOutOfRange = 9 If numEntries <> 0 Then 'Вначале проверяем информацию для сохранения, buttons = vbYesNoCancel + vbQuestion + vbApplicationModal theMsg = "Сохранить текущую базу данных?" Answer = MsgBox(theMsg, buttons, "PCD") If Answer = vbCancel Then 'Пользователь нажал Cancel, выход. Exit Sub Elself Answer = vbYes Then 'Пользователь нажал Yes, сохраняем базу. Save_Click End If End If 'Получение имя файла данных. theFilter = "База данных PCD (*.PCD), *.PCD" Do Answer = Application.GetOpenFilename(theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. Answer = Trim(Answer) 'Удаление ведущих и конечных пробелов. theDot = InStr(Answer, ".") If theDot = 0 Then 'Некорректное имя файла — нет расширения. 391
Visual Basic for Applications в примерах MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Elself Right(Answer, 4) <> ".PCD" Then 'Некорректное расширение. MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Else Flag = True End If Loop Until Flag theFileName = Answer fileNum = FreeFile() 'Получение файлового числа и открытие файла. On Error GoТо FixBadFile 'Включение прерывания по ошибке. Open theFileName For Input As fileNum 'Вначале читаем количество записей. Input #fileNum, numEntries 'Теперь подготовим массив для базы данных. ReDim theDB(numEntries) 'Теперь обработаем в цикле каждый элемент базы данных 'и прочтем все компоненты из файла. For EntryNum = 1 То numEntries With theDB(EntryNum) Input #fileNum, .Name Input #fileNum, .Address Input #fileNum, .City Input #fileNum, . State Input #fileNum, . Zip Input #fileNum, .Phone Input #fileNum, .Net Input #fileNum, .NetAddr Input #fileNum, .Referra Input #fileNum, .Notes Input #fileNum, .RecNo End With Next EntryNum Close #fileNum 'Конец сохранения базы данных. On Error Go To 0 'Выключение прерывания по ошибке. MsgBox "Файл " & theFileName & " успешно открыт.", , "PCD" Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Max = numEntries theEntryNum = 1 'Пересылка первой записи базы данных в бланк. DBToEntry theEntryNum 392
Глава 23. Применение прерываний по ошибке Exit Sub FixBadFile: 'Обработчик ошибок. Select Case Err Case errlnputPastEndOfFile 'Некорректный размер файла. MsgBox "Конец файла" 4 Chr(13) & _ "Неправильный файл, проверьте имя файла и попробуйте еще раз.", _ , "Ошибка PCD" Case err Subs crip tOutOfRange 'Некорректное количество записей. MsgBox "Индекс превысил диапазон" & Chr(13) & _ "Неправильный файл, проверьте имя файла и попробуйте еще раз.", _ , "Ошибка PCD" Case Else 'Непредвиденная ошибка. MsgBox "Непредвиденная ошибка. " & Chr(13) & "Код ошибки: " _ & Err & Chr(13) & "Описание: " £ Error (Err) , , "Ошибка PCD" End Select Close theFileName = "" theEntryNum = 0 numEntries = 0 ClearEntries End Sub Вначале из процедуры Initialized удалите блок кода, очищающий бланк, а затем преобразуйте его в отдельную процедуру ClearEntries. Это изменение имеет смысл, так как вам потребуется очищать бланк в нескольких местах, а отдельная процедура может просто вызываться при необходимости. t ' Очистка- диалогового окна. Г Sub ClearEntries() With Sheets("КонтактыДиалог” ) .EditBoxes("ДИмя").Text = .EditBoxes("ДАдрес”).Text = "" .EditBoxes("ДГород”).Text = .EditBoxes("ДСтрана”).Text = "" .EditBoxes("ДИндекс”).Text = "" .EditBoxes("ДТелефон").Text = "" . DropDowns ("ДЕ_МаИ_Сеть ”) . Text = . EditBoxes ("ДЕ_МаИ_Адрес” ). Text = .EditBoxes("ДСсылка”).Text = "" 393
Visual Basic for Applications в примерах .EditBoxes("ДПримечание").Text = "" .Labels("ДНомЗап").Caption = "Запись № " End With End Sub В процедуре FindName_Click нужно разрешит!) пользовательские прерывания и прерывание по ошибке, обрабатывающее пользовательские прерывания. Обработчик ошибок допишите в конец процедуры: f ' Поиск имени. г Sub FindName_Click() Static theName As String Dim I As Integer Const Textcomparison = 1 Const errUserlnterrupt = 18 f Разрешение прерывании no Ctrl-Break. Application. EnableCancelKey = xlErrorHandler On Error GoTo Handieinterrupt 'Включение обработки прерывании. If numEntries = 0 Then Exit Sub 'Еще нет записей. theName - InputBox("Введите имя для поиска", "Поиск имени", theName) If theEntryNum о numEntries Then For I = theEntryNum + 1 To numEntries If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. On Error GoTo 0 'Выключение обработки прерывании. Application.EnableCancelKey = xlDisabled 'Запрет прерывании. theEntryNum = I DBToEntry theEntryNum Exit Sub End If Next I End If For I = 1 To theEntryNum If InStr(l, theDB(I).Name, theName, Textcomparison) <> 0 Then 'Поиск записи с заданным именем. On Error GoTo 0 'Выключение обработки прерываний. Application.EnableCancelKey = xlDisabled 'Запрет прерывании. theEntryNum = I DBToEntry theEntryNum 394
Глава 23. Применение прерываний по ошибке Exit Sub End If Next I On Error Go To 0 'Выключение обработки прерывании. 'Включение нормальных прерывании. Application.EnableCancelKey = xllnterrupt 'Запись не найдена. MsgBox ’’Имя: ’’ & theName & ” не найдено.’’, , ’’Поиск имени" Exit Sub Handieinterrupt: 'Обработчик прерывании по Ctrl-Break. ' Выход из процедуры, если это прерывание по С tri-Break. If Err - errUserlnterrupt Then Exit Sub Else 'Если He прерывание no Ctrl-Break, то возврат ошибки в систему. 'Включение нормальных прерывании. Application. EnableCancelKey = xllnterrupt Error Err End If End Sub После того как завершилась часть процедуры, обеспечивающая поиск, отключите прерывание по ошибке и запретите пользовательские прерывания до тех пор, пока бланк не обновится. В противном случае пользователь может нажать клавиши Ctrl-Break в процессе обновления формы и случайно завершить программу. Обработчик пользовательского прерывания просто завершит процедуру Find- Name_Click. При получении любой другой ошибки он возвратит ее обработчику ошибок Visual Basic. В процедуре Open_Click необходимо включить прерывание по ошибке непосред¬ ственно перед открытием файла, а отключить это прерывание сразу после закрытия файла. Поместите обработчик ошибок в конец процедуры: ' Открытие файла базы данных. Sub Open_Click() Dim theFilter As String, Answer, theDot As Integer Dim fileNum As Integer, EntryNum As Integer Dim buttons As Integer, theMsg As String Dim Flag As Boolean Const errInputPastEndOfFile = 62 Const errSubscriptOutOfRange = 9 395
Visual Basic for Applications в примерах If numEntries <> 0 Then 'Вначале проверяем информацию для сохранения, buttons = vbYesNoCancel + vbQuestion + vbApplicationModal theMsg = ’’Сохранить текущую базу данных?" Answer = MsgBox(theMsg, buttons, "PCD") If Answer = vbCancel Then 'Пользователь нажал Cancel, выход. Exit Sub Elself Answer = vbYes Then 'Пользователь нажал Yes, сохраняем базу. Save_Click End If End If ' Получение имени файла данных. theFilter = "База данных PCD (*.PCD), *.PCD" Do Answer = Application.GetOpenFilename(theFilter, 1) If Answer = False Then Exit Sub 'Пользователь нажал Cancel. Answer = Trim(Answer) 'Удаление ведущих и конечных пробелов. theDot = InStr(Answer, ".") If theDot = 0 Then 'Некорректное имя файла — нет расширения. MsgBox "Вы должны выбирать только файлы .PCD.", , "PCD" Flag = False Elself Right(Answer, 4) <> ".PCD" Then 'Некорректное расширение. MsgBox "Вы должны выбирать только файлы .PCD.’’, , "PCD" Flag = False Else Flag = True End If Loop Until Flag theFileName = Answer fileNum = FreeFile() 'Получение файлового числа и открытие файла. On Error GoТо FixBadFile 'Включение прерывания по ошибке. Open theFileName For Input As fileNum 'Вначале читаем количество записей. Input #fileNum, numEntries 'Теперь подготовим массив для базы данных. ReDim theDB(numEntries) 'Теперь обработаем в цикле каждый элемент базы данных 'и прочтем все компоненты из файла. For EntryNum = 1 То numEntries With theDB(EntryNum) Input #fileNum, .Name 396
Глава 23. Применение прерываний по ошибке Input #fileNum, .Address Input #fileNum, . City Input #fileNum, .State Input #fileNum, . Zip Input tffileNum, .Phone Input #fileNum, . Net Input #fileNum, .NetAddr Input #fileNum, .Referral Input #fileNum, .Notes Input #fileNum, .RecNo End With Next EntryNum Close #fileNum 'Конец сохранения базы данных. On Error Go То 0 'Выключение прерывания по ошибке. MsgBox ’’Файл " & theFileName & " успешно открыт.”, , "PCD” Sheets("КонтактыДиалог").ScrollBars("ПрокруткаЗап").Мах = numEntries theEntryNum = 1 'Пересылка первой записи базы данных в бланк. DBToEntry theEntryNum Exit Sub FixBadFile: 'Обработчик ошибок. Select Case Err Case errlnputPastEndOfFile 'Некорректный размер файла. MsgBox "Конец файла" & Chr(13) & _ "Неправильный файл, проверьте имя файла и попробуйте еще раз.". , "Ошибка PCD" Case err Subs crip tOutOfRange 'Некорректное количество записей. MsgBox "Индекс превысил диапазон" & Chr(13) & _ "Неправильный файл, проверьте имя файла и попробуйте еще раз.", , "Ошибка PCD" Case Else 'Непредвиденная ошибка. MsgBox "Непредвиденная ошибка. " & Chr(13) & "Код ошибки: " _ & Err & Chr(13) & "Описание: " & Error(Err), , "Ошибка PCD" End Select Close theFileName - "" theEntryNum = 0 numEntries = 0 ClearEntries Encl Sub 397
Visual Basic for Applications в примерах Этот обработчик ошибок обрабатывает две ошибки: "Input passed end of file" (62) и "Subscript out of range” (9). Обе эти ошибки указывают на то, что программа открыла файл, отличный от последовательного файла базы данных .PCD. При обнаружении любой из этих ошибок обработчик ошибок отображает одно из окон сообщения, приведенных на рис. 23.2 или рис. 23.3. Любые другие ошибки вызывают отображение окна сообщения, приведенного на рис. 23.4, которое содержит сообщение о непредви¬ денной ошибке, код этой ошибки и поясняющий текст. После того как вы нажмете командную кнопку ОК на любом из этих трех окон, файл будет закрыт, а бланк очищен. Ошибка,PCD Конец файла Неправильный файл, проверьте имя файла и попробуйте еще раз. Рис. 23.2. Окно сообщения, отображающее ошибку с кодом 62 : Ошибка PCD Индекс превысил диапазон Неправильный файл, проверьте имя файла и попробуйте еще раз. Рис. 23.3. Окно сообщения, отображающее ошибку с кодом 9 Ошибка PCD Непредвиденная ошибка. Код ошибки: 7 Описание: Out of memory Рис. 23.4. Окно сообщения, отображающее непредвиденную ошибку 398
Глава 23. Применение прерываний по ошибке ff/.' Примечание: Нет необходимости проверять имя файла, устройство и путь (ошибки с кодами с 51 по 76), так как все это контролируется методом GetOpenFilename. То же относится и к процедуре Save_Click, в которой вы использовали метод GetSaveAs- Filename. Если вы работаете в режиме разделения ресурсов, то можете контроли¬ ровать ошибки с кодом 75 и, в процедуре Save_Click, можете контролировать ошибки с кодами 61 и 71. Заключение Из этой главы вы узнали о том, как обрабатывать ошибки. Прерывания по ошибке позволяют вашей программе обрабатывать различные ошибки, вместо того чтобы позволить системе просто остановить программу. Прерывания по ошибке особенно важны в процедурах, работающих с файловой системой, так как основные проблемы возникают именно в процессе доступа к файлам. Эта глава завершает часть VII "Отладка процедуры". Часть VIII описывает некоторые особенности расширения языка Visual Basic. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Как разрешить в процедуре прерывание по ошибке? 2. Из каких трех частей состоит обработчик ошибок? 3. Какие две функции используются для конкретизации ошибки? 4. Для чего помещают оператор Exit Sub или Exit Function между нормальной процедурой и обработчиком ошибок? 5. Как можно вернуть ошибку в систему? 6. Назовите три события, отключающие прерывание по ошибке. 7. Как запретить пользовательские прерывания по нажатии клавиш Ctrl-Break? Упражнения для закрепления материала 1. Добавьте прерывание по ошибке и обработчик ошибок к процедуре Open_Click программы работы с базой данных персональных контактов (версия для файла произвольного доступа). 2. Добавьте прерывания по ошибке и обработчики ошибок к программе работы с базой данных персональных контактов (версия для файла произвольного доступа). 399
Visual Basic for Applications в примерах 3. Напишите процедуру, печатающую все коды ошибок и их описания на панели Immediate окна Debug. Печатайте только ошибки Visual Basic. 4. Поместите прерывания по ошибке в процедуры DBToEntry и EntryToDB програм¬ мы работы с базой данных персональных контактов (версия для файла произволь¬ ного доступа). 5. Напишите процедуру, вызывающую процедуру, которая также вызывает процеду¬ ру. Поместите в каждую процедуру прерывание по ошибке, а затем используйте оператор Error для имитации различных ошибок. С помощью окна Debug просле¬ дуйте по ходу выполнения программы, использовав пошаговые директивы. Попро¬ буйте различные варианты места расположения ошибок, а также разрешенные и запрещенные прерывания. Поместите в обработчике ошибок оператор Error Егг для передачи ошибки по цепочке, чтобы увидеть, как управление передается от процедуры к процедуре в зависимости от ошибки.
Часть VIII Особенности расширения языка
Глава 24 Создание пользовательских и пиктографических меню Из части VIII этой книги вы узнаете о некоторых особенностях расширения языка Visual Basic. Последующие главы ознакомят вас с DDE и OLE, а данная — с тем, как создавать пользовательские и пиктографические меню. Большинство программ Windows используют выпадающие меню, облегчающие доступ к директивам программы и управляющие этой программой. Более современный подход —пиктографические меню, обеспечивающие более простой доступ к основным директивам программы. Visual Basic позволяет создавать ваши собственные пользо¬ вательские и пиктографические меню. Из этой главы вы узнаете, как делать следующее: > Добавлять директивы к существующим меню > Создавать пользовательские меню > Добавлять пиктограммы к существующим пиктографическим меню > Создавать пользовательские пиктографические меню Макет меню директив На рис. 24.1 изображен макет типового меню. Такие меню подключены к строке меню. Каждое меню содержит один или. несколько элементов меню. Некоторые из элементов меню являются директивами, а другие —подменю. Подменю так же. как и меню, содержит элементы меню. Клавиша доступа Строка меню - Undo Delete Can'1 Redo Ctrl+Z F4 Cui CtrHX Copy Paste Ctrl+V^ Clear Delete Delete Sheet Move or Copy Sheet... Горизонтальным разделитель _" Горячая" клавиша Элемент меню (активный) Элемент меню (неактивным) Insert Run Tools Window Help Sheet \ Find... Replace... Ctrl+F Ctrl+H Hide Unhide... j Элемент подменю Рис. 24.1. Макет меню 403
Visual Basic for Applications в примерах Excel предоставляет девять встроенных строк меню и три контекстных меню. Эти меню перечислены в табл. 24.1 совместно с именами констант Excel, которые исполь¬ зуются для активизации этих меню. Таблица 24.1. Встроенные и контекстные меню Excel Строка меню Константа Описание Worksheet xlWorksheet Отображается при активной таблице, макротаблице или странице диалога Ch art xlChart Отображается при активной диаграмме No Documents xlNoDocuments Отображается, если открыт не документ Visual Basic Module xlModule Отображается при активной странице модуля Visual Basic :iortcut Menus 1 Отображается для ячейки, колонки, пиктографического меню или пиктограммы aortcut Menus 2 Отображается для изображенного объекта, командной кнопки или текстового поля Shortcut Menus 3 Отображается для диаграммы, серии диаграмм, текста диаграммы, зоны черчения, оси, линии, сетки, низа или легенды Info xllnfo Отображается при активном окне Info Short Worksheet xl WorksheetShort Аналогично кратким меню табличного меню Excel 3 Short Chart xlChartShort Аналогично кратким меню диаграммного меню Excel 3 XL4 Worksheet xlWorksheet4 Табличное меню Ехсе14 XU Chart xlChart4 Диаграммное меню Ехсе14 Встроенные строки меню отображаются только тогда, когда активной является ст!>зница соответствующего типа. Это значит, что табличное меню отображается только в активной таблице, а диаграммное —при активной странице диаграмм. Учитывайте эго при разработке дополнений ко встроенной системе меню. 404
Глава 24. Создание меню Три контекстных меню отображаются, если пользователь выберет определенный объект и нажмет правую кнопку мыши. Последние четыре меню включены для обеспечения совместимости с макросами, разработанными для версий 3 и 4 Excel. В дополнение к этим встроенным строкам меню можно создать до 15 пользовательских строк меню. К каждой строке меню подключены выпадающие меню. Количество меню, которые можно подключить к одной строке меню, зависит от длины наименований меню и типа монитора. Одна строка меню обычно включает от 7 до 10 меню. Если вам нужно большее количество меню, то сгруппируйте их в виде подменю одного меню. Каждое меню состоит из элементов, которые либо связаны с директивой, либо являются заголовком подменю. Заголовки подменю помечаются маленьким черным треугольником справа от наименования элемента. Элементы меню, открывающие диалоговое окно вместо прямого вызова директивы, помечены многоточием (...) в конце наименования элемента. Кроме указанного элементом меню является также горизонтальный разделитель, который не вызывает директив, а просто визуально разделяет элементы меню на группы. Меню и элементы меню могут быть активными и неактивными. Неактивный элемент меню отображается на экране в сером цвете и не может быть выбран. Кроме того, элементы меню могут быть маркированы. Маркированный элемент меню отме¬ чается галочкой слева от наименования элемента. Вы можете применять такие галочки для любых целей, но обычно они используются для переключаемых элементов меню. Если такой элемент включен, то он маркирован; если не включен, то и не маркирован. Например, в меню Excel View маркируются элементы Formula Ваг и Status Ваг, что указывает, отображается данная строка или нет. Большинство меню и элементов меню имеют клавиши доступа, отмеченные подчер киванием соответствующей буквы в их наименованиях. Эти клавиши доступа позво¬ ляют выбрать меню или элемент меню с помощью клавиатуры, а не мыши. Чтобы выбрать меню таким способом, нажмите клавишу Alt и, удерживая ее нажатой, нажмите клавишу доступа для данного меню. Для того чтобы выбрать элемент из активного меню, просто нажмите клавишу доступа для требуемого элемента. Для некоторых элементов встроенных меню определены так называемые "горячие” клавиши, приведенные справа от наименования элемента. Нажатие "горячей" клавиши вызывает выполнение директивы, связанной с этим элементом. "Горячие" клавиши не добавляются к пользовательским элементам меню автоматически, — вы должны добавлять их самостоятельно. Создание меню с помощью Menu Editor В Excel существуют два способа добавлять меню, строки меню и элементы меню: с помощью Menu Editor и программным путем (на Visual Basic). Применение Menu Editor является наиболее простым способом. Однако меню, созданные программным 405
Visual Basic for Applications в примерах путем, более мобильны, так как воссоздаются при каждом запуске создающей их программы, а меню, созданные с помощью Menu Editor, подключаются только к гой рабочей папке, в которой вы их создали, и не копируются в другую рабочую папку вслед за программой. Существует и третий способ подключить директивы к строке меню: директива Tools/Macro/Options позволяет подключать процедуры в конец меню Tools. Однако возможности этой директивы ограничены помещением элементов меню в определенное место. Чтобы активизировать Menu Editor, переключитесь на страницу модуля и укажите директиву Tools/Menu Editor. Появится диалоговое окно Menu Editor, в котором вы можете выбрать меню для редактирования. Диалоговое окно, изображенное на рис. 24.2, отображает выбранное для редактирования меню Edit строки меню Visual Basic Module Это меню изображено на рис. 24.1, причем выбраны те же самые элементы Рис. 24.2. Диалоговое окно Menu Editor с выбранным для редактирования меню Edit В верхней части диалогового окна Macro Editor расположены два окна редактирова¬ ния, позволяющие задать заголовок и имя макроса, который вы хотите подключить к текущему выбранному элементу меню. Для того чтобы редактировать определенный элемент меню, выберите строку меню из выпадающего списка Menu Bars, затем меню из списка Menus, потом элемент из списка Menu Items, и если этот элемент является подменю, то выберите элемент подменю из списка Submenu Items. Теперь можно редактировать или удалять этот элемент. Если вы редактируете встроенное меню, то командная кнопка Restore АП восстановит первоначальное состояние данного меню. 406
Глава 24. Создание меню Добавление элемента к существующему меню В большинстве случаев для программы на Visual Basic к меню нужно добавить всего одну или две директивы, что проще всего сделать, добавив эти директивы к существующему меню. Если вы разработали программу на Visual Basic, дополняющую Excel, то используйте встроенные меню, чтобы при активизации пользователем определенной страницы, для которой вы создали новые директивы, программа автоматически сделала эти директивы доступными. В противном случае вы должны подключить к свойству страницы OnSheetActivate некоторый код, который заменит меню, заданное по умолчанию, вашим меню. Чтобы добавить элемент к существующему меню, выполните следующие действия. 1. Откройте или переключитесь на страницу модуля и укажите директиву Tools/Menu Editor. 2. В выпадающем списке Menu Bars и списке Menus выберите строку меню и меню (из существующих). 3. В списке Menu Items выберите место, в которое хотите поместить ваш элемент. Затем нажмите командную кнопку Insert. В списке элементов освободится место для ввода нового элемента (см. рис. 24.3). Если вы не укажите место, то новый элемент будет помещен в конец списка. Рис. 24.3. Диалоговое окно Menu Editor после нажатия командной кнопки Insert 407
Visual Haste for Applications в примерах i В окне Caption запишите заголовок, поместив символ (&) перед буквой, обозна¬ чающей клавишу доступа. Учтите, что амперсанд не появится в наименовании •элемента, но буква, следующая за ним, будет подчеркнута. Предупреждение: При выборе буквы для клавиши доступа убедитесь, что выбрали букву, не используемую в данном меню. Если у вас будут два элемента с одинаковой клавишей доступа, то пользователь сможет применить эту клавишу для выбора только первого из этих элементов. Выбрать второй элемент пользователь сможет исключительно с помощью клавиш управления курсором и клавиши Enter. Это предупреждение относится также и к клавишам доступа к меню. з Если новый элемент меню будет инициировать директиву, то выберите из выпа¬ дающего списка Macro имя процедуры, подключаемой к этому элементу, или запишите в окне Macro имя новой процедуры, которую вы создадите для этого элемента (см. рис. 24.4). Если этот элемент является заголовком подменю, то укажите мышью на список Submenu Items, затем нажмите командную кнопку insert, запишите заголовки каждого элемента подменю и подключите к ним с<>ответствующие п роцедуры. Н : Menu Editor (ВоокЦ ' Caption: |Мо(я Директива 1 | Insert If OK—I Macro: I MyCommandCUck ||±l 1 Pde<e | | Cancel | Menu £ars: (Visual Basic Module 1*1 1 Rest ote AN | | Help | Menus: Menu Items: Submenu Items: (File ________ (View (Insert (Run 1 Tools (Window (Help (ЕгмЗ of menu bar) ♦ (Undo l + l Can*t Repeat |~~] 1ИЗЯ1ЙН355Э5ИИИ (End of submenu) T Cult ■ (Copy H (Paste И Cle(ar И De(lete Sheet H (Move or Copy Sheet..J + | □ Show Deleted Items Рис. 24.4. Диалоговое окно Menu Editor с новым элементом, добавленным к Edit и подключенным к процедуре Нажмите командную кнопку ОК. Теперь новый элемент меню добавлен и вы можете выбрать это меню и посмотреть на свой новый элемент.
Глава 24, Создание меню На рис. 24.5 изображено меню, полученное в результате описанных дештвнй Обратите внимание на то, что буква "я” в слове "Моя” подчеркнута, так как в диалоговом окне, изображенном на рис. 24.4, вы определили эту букву как клавишу доступа, записав перед ней амперсанд. ) HFIЕ0 (НИШ ЖИ [ Microsoft Excel,- BookT Can'1 Red© М Моя Директива Cui Сору Easte Clear Ctrb-X CtrhC CtrHV Delete Delete Sheet Move or Copy Sheet... £heet ► Eind... CtrHF Replace... Ctri+H ТТЙ1 |g|»|s|S!l«l%l. Wl[й]*]tfliWl Щ | ► | ►l\Modu<e1 /Sheet! / Sheet2 / Sheet3 / Sheet4 / S|Ф Undoes last command Рис. 24.5. Новый элемент "Моя Директива”, добавленный к существую!нему меню Eda Примечание: Имейте в виду, что встроенные строки меню изменятся, если переключиться на таблицы на страницу диаграмм или модуля. Если вы хотите, чтобы новая директива была доступна в меню всех этих страниц, то необходимо добавить со в меню всех трех строк. Если вы хотите, чтобы она была в контекстных меню, то добавьте ее к тем контекстным меню, в которых она должна появляться. Чтобы добавить элемент к подменю или к контекстному меню, необходимо выполнить те же действия, что и для создания элемента меню, с тем отличием, чт- перед нажатием командной кнопки Insert нужно выбрать это подменю или конге! стное меню. 409
Visual Basic for Applications в примерах Добавление меню к существующей строке меню Кроме возможности добавить новые элементы к существующему меню, вы имеете возможность добавить новое меню к существующей строке меню. Действия по добавлению нового меню почти аналогичны добавлению к меню нового элемента. Для того чтобы добавить к существующей строке новое меню, выполните следую¬ щие действия. 1. Создайте или переключитесь на страницу модуля и укажите директиву Tools/Menu Editor. 2. В выпадающем списке Menu Bars выберите одну из существующих строк меню. 3. В списке Menus определите место, куда вы хотите поместить пользовательское меню. Следите за тем, чтобы указать место именно в списке Menus, а не в списке Menu Items, в противном случае вместо нового меню вы создадите новый элемент. Так же, как и для элементов меню, если вы не определите место нового меню, то оно будет помещено в конец списка, т.е. в правой части строки меню, перед меню Help. Меню Help всегда является самым правым меню, независимо от того, куда вы поместите новое меню. 4. Нажмите командную кнопку Insert. В списке меню освободится место, в которое можно поместить заголовок нового меню. 5. Запишите заголовок в окне Caption. Перед буквой, которую вы хотите определить как клавишу доступа, запишите амперсанд (&). На рис. 24.6, например, амперсанд записан перед буквой ”Д”, что задает эту букву как клавишу доступа к меню директив. Подключить процедуру к меню невозможно. 6. Нажмите командную кнопку ОК. Новое меню будет добавлено к существующей строке меню так, как изображено на рис. 24.7. После добавления нового меню к нему нужно добавить элементы и подменю способом, описанным в предыдущем разделе. Вы можете добавить к меню элементы и подменю перед тем, как в п.6 нажать командную кнопку ОК. Учитывайте ограни¬ чение, указанное для элементов встроенных меню: отображаемая строка меню зависит от вида активной страницы. Помещение меню в одну из встроенных строк не помещает его автоматически в другие встроенные строки меню. 410
Глава 24. Создание меню Рис. 24.6. Диалоговое окно Menu Editor с добавленным новым меню "| Eile Edit View Insert Bun Tools Window Директивы Help |D|^|B||a|Bi|y||X |^|(S|^F^|^]7nfinn]|Liis|lg|^ll If ! ns i га ГГГ7ПГ ftiraia iw?i ин га Рис. 24.7. Новое меню "Директивы4, добавленное к существующей строке меню Visual Basic Module Добавление новой строки меню Если вы создаете пользовательскую программу с большим количеством меню, то имеет смысл создать пользовательскую строку меню, включающую только те дирек¬ тивы, которые потребуются пользователю вашей программы. Новая строка меню добавляется практически тем же способом, что и новые меню или элементы меню. Для того чтобы создать новую строку меню, выполните следующие действия. 1. Создайте или переключитесь на страницу модуля, затем укажите директиву Tools/Menu Editor. 2. В выпадающем списке Menu Bars выберите любую из перечисленных строк меню. Это позволит отменить выбор элементов в списках Menus и Menu Items. Если вы не отмените выбор этих элементов, то вместо новой строки создайте новое меню или новый элемент. Не имеет никакого значения, какую строку меню вы выбрали, так как новая строка всегда добавляется в конец списка строк меню. 411
Visual Baltic for Applications в примерах 3. Нажмите командную кнопку Insert. Затем запишите имя новой строки меню в окне Caption, как изображено на рис. 24.8. Это имя используется исключительно для выбора строки меню и никогда не отображается. 4 Добавьте к новой строке меню любые меню, элементы меню и подменю способом, описанным выше. Затем нажмите командную кнопку ОК. После того как вы нажмете кнопку ОК, новая строка меню не отобразится, т.к. она не является одной из встроенных строк меню, которые отображаются автоматически. Рис. 24.8. Диалоговое окно Menu Editor с новой строкой меню МуВаг! Отображение пользовательской строки меню Чтобы отобразить пользовательскую строку меню, нужно активизировать ее, использовав метод Activate объекта MenuBar. Обычно созданную для этих целей процедуру включают в процедуру инициализации программы или подключают к свойству OnShcetActivate страницы, содержащей вашу программу. Так, следующая короткая процедура выбирает строку меню Му Bari из набора MenuBars и активизирует ее: ■ Отображение пользовательской строки меню. Sub DisplayMenu() Application.MenuBars("MyBarl") .Activate End Sub 412
Глава 24. Создание дииюо Строка меню содержит только одно меню "Директивы”. Запуск этой процедуры отображает данную строку меню, как изображено на рис. 24.9. Пользовательские строки меню не изменяются автоматически при смене активной страницы. После гого как вы активизировали пользовательскую строку меню, можно переключаться таблицу, диаграммы и т.п., а пользовательская строка меню все равно будет отобра¬ жаться. Следовательно, после того как отобразилась пользовательская строка меню, нужно активизировать одну из встроенных строк меню для возобновления их аваома тического переключения. Удаление пользовательской строки меню Удалить пользовательскую строку меню сложнее, чем отобразить. Для того чтобы удалить пользовательскую строку меню и вновь отобразить встроенную строк}’ мыно, нужно активизировать одну из его встроенных строк. Однако вы должны активизировать именно ту строку, которая нужна. Excel отображает определенные строки милю в зависимости от вида активной страницы. Попытка активизировать не ту строку меню, которая нужна, вызовет ошибку. Если вы определили вид активной страницы, то можете просто применить метод Activate к определенной строке меню. Для того чтобы определить вид активной страницы, примените к свойству ActiveSheet функцию TypeNamev). Рис. 24.9. Новая строка меню с одним меню "Директивы” 413
Visual Basic for Applications в примерах Следующая процедура определяет вид активной страницы, а затем активизирует соответствующую строку меню, используя встроенные константы из табл. 24.1: ’ Восстановление строк меню, заданных по умолчанию. г Sub ResetMenu() Select Case TypeName(ActiveSheet) Case "Worksheet”, "Dialogsheet" Application.MenuBars(xlWorksheet) .Activate Case "Module" Application.MenuBars(xlModule).Activate Case "Chart" Application.MenuBars(xlChart).Activate End Select End Sub Процедуры, отменяющие пользовательскую строку меню, в основном помещают в одном из следующих типов процедур: > Процедура завершения, которая выполняется при завершении вашей программы. > Процедура, подключенная к свойству OnSheetDeactivate, удаляющая пользова¬ тельскую строку меню, если страница, в которой применялась эта строка, перестает быть активной. Создание меню программным путем Кроме создания меню с помощью Menu Editor вы можете создавать их програм- мным путем. Хотя использование программ не столь наглядно, как использование Menu Editor, результаты этот способ дает те же. Как уже отмечалось, меню, созданное программным путем, переносится значительно проще, чем созданное с помощью Menu Editor. Если вы используете программу, то можете создавать такие же меню в других программах, просто скопировав в них данный код. Доступ к строкам меню, меню и элементам меню Элемент меню является частью набора Menuitems меню, а меню —частью набора Menus строки меню, которая является частью набора MenuBars объекта Application. Итак, если следовать данной цепочке наборов, то доступ к строке меню можно получить следующим образом: Application.MenuBars(ИмяСтроки) 414
Глава 24. Создание меню Доступ к меню обеспечивается следующим образом: Application.Menus{ИмяМеню} Доступ к элементу меню обеспечивается следующим образом: Application.Menus {ИмяМеню} .Menultems {ИмяЭлемента} Объект Application обычно можно опускать, но наборы опускать нельзя, так как они обеспечивают доступ к корректному элементу меню. ИмяСтроки, ИмяМеню и ИмяЭлемента являются строками, содержащими имена строки меню, меню или элемента меню; либо индексное число строки меню, меню или элемента меню в указанном наборе. Индексные числа для встроенных строк меню Excel могут быть представлены в виде констант Excel, приведенных в табл. 24.1. Индексные числа для меню соответствуют порядку, в котором эти меню отображаются на данной строке меню. Самому левому меню (обычно File) соответствует индексное число 1, следующему за ним (слева направо) —индексное число 2 и т.д. Элементы меню перечисляются сверху вниз, причем самому верхнему элементу меню соответствует индексное число 1, следующему за ним (сверху вниз) — индексное число 2 и т.д. Горизон¬ тальные разделители также считаются отдельными элементами меню. Однако для повышения читабельности программы старайтесь с максимальной возможностью использовать имена. Добавление элемента к существующему меню Для того чтобы добавить элементы к существующему меню, применяется метод Add. Метод Add использует несколько аргументов, позволяющих задать в одном операторе некоторые свойства нового элемента меню. Кроме того, можно установить каждое из этих свойств отдельно, добавив сначала новый элемент меню с помощью метода Add, а затем получив доступ к нужным свойствам этого нового элемента. Если вы применяете метод Add к элементу меню, то в качестве поименованных аргументов можете использовать Caption, On Action, ShortcutKey, Before и Restore. Аргумент Caption устанавливает свойство Caption данного элемента меню. Этому аргументу присваивается строка, содержащая имя данного элемента меню. Амперсанд, записанный в этой строке перед каким-либо символом, указывает на то, что данный символ будет использоваться в качестве клавиши доступа. Символ дефис (-) указывает на то, что этот элемент — горизонтальный разделитель. Аргумент On Action устанавливает свойство On Action данного элемента меню. Этому аргументу присваивается строка, содержащая имя процедуры, которая будет запущена при выборе этого объекта. 415
Visual Basic for Applications в примерах Аргумент ShortcutKey определяет ’’горячую" клавишу для данного элемента меню, но пока этот аргумент может использоваться только для компьютеров Macintosh. Аргумент Before указывает место в списке элементов, куда будет помещен новый элемент. Элемент, занимающий данную позицию в списке элементов меню, будет перемещен вниз, чтобы освободить место для нового элемента. Этот аргумент может быть либо индексным числом, либо строкой, которая содержит имя элемента, занимающего указанную позицию. Если вы опустите этот аргумент, то новый элемент меню будет помещен в конец меню. Аргумент Restore восстанавливает удаленный элемент одного из встроенных меню. Чтобы восстановить удаленный элемент, необходимо присвоить имя этого элемента аргументу Caption, а аргумент Restore установить в True. Если вы добавите элемент меню с именем удаленного элемента, но не установите аргумент Restore в True, то создадите элемент, определенный пользователем, а не восстановите удаленный. Например, элемент меню, изображенный на рис. 24.5, который вы добавили с помощью Menu Editor, может быть добавлен и с помощью следующего кода: f Добавление элемента меню. Sub AduMenuIteiu() Menubars(xlModule) .Menus("Edit") .MenuIterns .Add _ Caption:="Мо&я директива", _ Onaction: ^"MyCommand", _ before:=3 End Sub Этот код выбирает строку меню с помощью встроенной константы xl Module, затем из набора Menus выбирает меню Edit. Аргумент Caption устанавливает заголовок нового элемента — "Мо&я Директива", задавая "я" в качестве клавиши доступа. При выборе данного элемента будет запущена процедура MyCommand. Этот элемент будет помещен третьим в список элементов меню. В данном случае вы должны указать место нового элемента с помощью индексного числа, так как третьим элементом существующего меню является горизонтальный разделитель, не имеющий имени. Чтобы удалить этот элемент из меню, примените к нему метод Delete. Следующая процедура удаляет элемент меню, добавленный в предыдущем примере. ' Удаление элемента меню. Sub DeleteMenuItem() MenuBcirs(xlModule) .Menus("Edit") .Menulterns("Моя директива") .Delete End Sub 416
Глава 24, Создание меню Обрагите внимание на то, что при выборе элемента из набора заголовок элемента не содержит амперсанда, определяющего клавишу доступа. Добавление меню к существующей строке меню Добавить меню к существующей строке меню можно тем же способом, которым вы добавляли элемент к меню. Чтобы добавить меню к строке меню, необходимо применить к набору Menus данной строки метод Add. Вы можете использовать все аргументы метода, за исключением аргумента On Action. Кроме того, аргумент Reset восстанавливает указанное меню в виде, заданном по умолчанию. Так, следующая процедура добавляет к строке меню Visual Basic Module, изобра¬ женной на рис. 24.7, меню "Директивы": г ' Отображение пользовательского меню. г Sub DisplayMenul() MenuBars(xlModule) .Menus.Add _ Caption: =’’&Директивы” , _ before:=”Help" End Sub Следующая процедура удаляет меню ’’Директивы": ' Удаление пользовательского меню. Sub DeleteMenul() MenuBars(xlModule).Menus("Директивы").Delete End Sub Добавление новой строки меню Добавление новой строки меню подобно добавлению новых меню или элементов. В данном случае метод Add использует только один аргумент — Name, который устанавливает имя новой строки меню (аналогично аргументу Caption для меню или элементов). Так, следующая процедура создает и отображает строку меню, изображенную на рис. 24.9. г ' Создание пользовательской строки меню. 14 Вильям Дж. Орвис 417
Visual Basic for Applications в примерах Sub CreateBar() MenuBars.Add Name:="MyBar2" MenuBars(”MyBar2”).Menus.Add Caption:="&Директивы" Application.MenuBars(“MyBar2”).Activate End Sub Чтобы удалить пользовательскую строку меню, сначала необходимо запустить процедуру ResetMenu (приведенную выше ) или подобную ей процедуру, отображаю¬ щую различные строки меню, а затем применить к набору MenuBars метод Delete. Если строка меню, которую вы хотите удалить из набора, в настоящей момент отображается, то вы должны погасить ее прежде чем удалять, так как удалить отображаемую строку меню нельзя. Кроме того, если вы попробуете применить метод Add для создания новой строки меню, а строка с таким именем уже существует, то получите ошибку. В данном случае необходимо вначале применить метод Delete для удаления указанной строки меню, а затем с помощью метода Add создать новую строку с тем же именем. Например, следующая процедура удаляет строку меню МуВаг2 из набора MenuBars. Для того чтобы эта процедура работала, строка меню МуВаг2 не должна отображаться: г ' Удаление пользовательской строки меню. Sub DeleteBar() MenuBars ("MyBar2’’) .Delete End Sub Изменение свойств и элементов меню Меню и элементы меню обладают различными свойствами, управляющими видом и доступом. И меню, и элементы меню обладают свойством Caption, содержащим отображаемое имя меню или элемента. Это имя можно изменить в любое время, просто изменив данное свойство. Элементы меню обладают свойством Checked, значению True которого соответ¬ ствует галочка слева от имени данного элемента. Если элементы меню представляют некоторые опции, то свойство Checked позволяет указать, какие из элементов выбра¬ ны, а какие нет. Меню и элементы меню обладают свойством Enabled. Если свойство Enabled принимает значение False, то соответствующее меню или элемент отображаются в сером цвете и недоступны для выбора пользователем. Если меню недоступно, то пользователь не может выбрать ни один из его элементов. 418
Глава 24. Создание меню Добавление "горячих" клавиш к директивам меню "Горячие” клавиши для элементов меню, определенных пользователем, не поддер¬ живаются Visual Basic автоматически (как это делается для элементов встроенных меню). Для того чтобы добавить "горячую" клавишу, нужно добавить ее наименование к свойству Caption данного элемента меню. Обычно имя элемента меню помещается слева, а "горячая" клавиша — справа. Для того чтобы данная "горячая" клавиша стала доступной, необходимо выполнить метод ОпКеу, использующий два аргумента. Первым аргументом является строка, которая содержит комбинацию клавиш, подключаемую к процедуре, а вторым — строка, которая содержит имя процедуры, выполняемой при нажатии пользователем указанной комбинации клавиш. Предупреждение: Если вы сделаете элемент меню недоступным путем установки его свойства Enable в False, то это еще не отменяет доступность "горячей” клавиши. Для того чтобы данная "горячая” клавиша также стала недоступной, нужно повторно выполнить метод ОпКеу без задания второго аргумента. Коды специальных и модифицирующих клавиш приведены в табл. 24.2. Таблица 24.2. Коды клавиш, используемые в методе ОпКеу Клавиша Код Backspace {BACKSPACE}, {BS} Break {BREAK} CapsLock {CAPSLOCK} Clear {CLEAR} Delete или Del {DELETE}, {DEL} Стрелка вниз {DOWN} End {END} Enter дополнительной клавиатуры {ENTER} Enter основной клавиатуры - Esc {ESCAPE}, {ESC} Help {HELP} Home {HOME} Insert или Ins {INSERT} 419
Visual Basic for Applications в примерах Клавиша Код Стрелка влево {LEFT} Num Lock {NUMLOCK} Page Down {PGDN} Page Up {PGUP} Return {RETURN} Стрелка вправо {RIGHT} Scroll Lock {SCROLLLOCK} Табуляция {TAB} Стрелка вверх {UP} F1...F15 {F1}...{F15} Коды модифицирующих клавиш Shift + Ctrl - Alt или Option % Примечание Для того чтобы использовать коды модифицирующих клавиш ~,+Д и % в строке, заключите их в фигурные скобки (например, {-}). Следующие три процедуры создают элемент меню с "горячей" клавишей, затем делают этот элемент и клавишу недоступными, а потом —вновь доступными: г ' Создание "горячей" клавиши. Sub CreateShortcut() MenuBars(xlModule) .Menus ("Edit" ) .Menu Items .Add _ Caption:="Выполнить Ctrl-a", _ OnAction:="DoItProc", _ before:=3 Application.OnKey "Ла", "DoItProc" 'Подключение Ctrl-a к DoItProc. End Sub 420
Глава 24. Создание меню ' Деакгивизация элемента и "горячей" клавиши. ! Sub DisableShortcut() Menubars(xlModule) .Menus("Edit") .Menultems("Выполнить Ctrl-a”). _ Enabled = False Application.OnKey ”'xa", "" 'Отмена Ctrl-a. End Sub f ' Активизация "горячей" клавиши. г Sub EnableShortcut() MenuBars (xlModule) .Menus ("Edit") .Menuitems ("Выполнить Ctrl-a") . _ Enabled = True Application.OnKey "Ла", "DoItProc" End Sub Модификация контекстных меню Контекстные меню работают подобно обычным меню, с тем отличием, что они не подключаются к видимым пиктографическим меню. Контекстные меню появляются, если щелкнуть на объекте правой кнопкой мыши. Данный объект определяет появ¬ ляющееся контекстное меню. Для того чтобы изменить элементы контекстного меню,, можно использовать Menu Editor или набор ShortcutMenus (для изменения программным путем). Для доступа к набору ShortcutMenus в качестве аргумента используется константа Excel, а возвращается выбранное меню. Константа определяет меню, к которому необходим доступ, а ее имя содержит имя объекта, при выборе которого появляется это меню. Список констант приведен в табл. 24.3. После выбора меню его можно изменять с помощью тех же команд, которые применялись для добавления и удаления элементов меню. Таблица 24.3. Константы, выбирающие контекстные меню и объекты, выбор которых отображает эти меню Константа Объект xlAxis xl Button xl ChartSeries Оси диаграммы Командная кнопка Серия диаграмм 421
Visual Basic for Applications в примерах Константа Объект xlChartTitles Название диаграммы xlColumn Header Колонка xlDebugCodePane Кодовая панель окна Debug xl Desktop Панель экрана xlDialogSheet Страница диалога x 1 DrawingObj ect Изображенный объект x I EntireChart Диаграмма в целом xlFloor Низ диаграммы xlGridline Координатная сетка диаграммы xllmmediatePane Панель Immediate xl Legend Легенда диаграммы xlMacrosheetCell Ячейка макротаблицы xlModule Модуль xlPlotArea Зона черчения диаграммы xlRowHeader Строка xlTextBox Текстовое окно xlTitleBar Строка заголовка xlToolbar Пиктографическое меню xlToolbarButton Пиктограмма меню’ xl Watch Pane Панель Watch xlWorkbookTab Корешок Workbook xl WorksheetCell Ячейка таблицы Доступ к пиктографическим меню Пиктографические меню отображаются и удаляются, в общем, аналогично обыч¬ ным меню. Для доступа к пиктографическому меню используется набор Toolbars. Для 422
Глава 24. Создание меню доступа к встроенным пиктографическим меню в качестве аргумента набора Toolbars применяются имена этих меню (см. табл. 24.4). Для пиктографических меню исполь¬ зуются имена, а не числа, так как компоненты набора Toolbars могут изменяться, а соответственно будет изменяться и их нумерация в наборе. Для того чтобы отобразить существующее пиктографическое меню, необходимо установить его свойство Visible в True; установив свойство Visible в False, вы погасите данное пиктографическое меню. Таблица 24.4. Имена встроенных пиктографических меню Имя Содержимое Standard Средства доступа к файлам, вырезания, вставки и печати. Обычно отображается в верхней части экрана Formatting Средства форматирования ячеек. Обычно отображается под меню Standard Query and Pivot Средства для запросов и таблиц основных элементов Chart Средства редактирования и изменения типов диаграмм Drawing Средства изображения линий, прямоугольников, команд¬ ных кнопок и других графических объектов TipWizard Средства доступа к текущим советам Forms Средства изображения элементов управления на бланке Stop Recording Командная кнопка остановки макрорекордера Visual Basic Средства запуска и остановки программ; средства отладки Auditing Средства обнаружения исходной информации для контроля корректности вычислений WorkGroup Средства обнаружения и пересылки файлов Microsoft Средства переключения на другие программы фирмы Microsoft Full Screen Командная кнопка —переключатель между полноэкранным и обычным отображением таблицы Так, первая из следующих процедур отображает пиктографическое меню Micro¬ soft, а вторая —гасит его: 423
Visual Basic for Applications в примерам ' Отображение пиктографического меню. Sub DisplayToolbar() Application.Toolbars("Microsoft").Visible = True End Sub ' Отключение пиктографического меню. Sub HideToolbar() Application.Toolbars("Microsoft”).Visible = False End Sub Добавление пиктограмм к пиктографическому меню Если вам нужно добавить пиктограмму к пиктографическому меню, то сделать это проще всего с помощью диалогового окна Customize. Вначале отобразите пиктографи¬ ческое меню, к которому вы хотите добавить пиктограмму, а затем укажите директиву View/Toolbars. В диалоговом окне Toolbars нажмите командную кнопку Customize. Появится диалоговое окно Customize, изображенное на рис. 24.10. Обратите внимание на то, что в данном диалоговом окне выбрана пиктограмма Save и ее функции описаны в нижней часта диалогового окна. ustomize Select a category: then click on a button to «ее its description. Drag the button onto any toolbar. I I Categories: ЕШНИНЯНИГЕ] EditГ1 Formula U Formatting H T ext Formatting H Drawing H Macro И Charting И Utility И Data И TipWizard Щ Help | т Description: Saves changes made to active document Рис. 24.10. Диалоговое окно Customize, позволяющее добавлять пиктограммы к пиктографическим меню 424
Глава 24. Создание меню В окне Categories выберите интересующую вас категорию, а затем ознакомьтесь с функциями отображенных пиктограмм, нажимая на эти пиктограммы. Выбрав пиктог¬ рамму, которую вы хотите добавить к пиктографическому меню, нажмите ее и перетащите на отображенное пиктографическое меню. Для того чтобы подключить пиктограммы меню к процедурам на Visual Basic, выберите в диалоговом окне Customize категорию Custom. Все пиктограммы, относя¬ щиеся к этой категории (см. рис. 24.11), не подключены ни к одной процедуре. После того как вы перетащите любую из этих пиктограмм на пиктографическое меню, появится диалоговое окно Assign Macro, позволяющее подключить к этой пиктограмме процедуру. Select a category; than click on a button to see its description. Drag the button onto any toobar. Categories: Formatting Щ T ext Formatting M Drawing ■ Macro ■ Charting И Utility ■ Data ■ TipWizard ■ Auditing ■ Forms П r-Buttons I M-e|P I па [Г |И|й0|х|,&] ф N@|e|®| (5 Р ф в а eksi |©| V ♦ * ♦ Description: Creates a button to which you can assign a macro Рис. 24.11. Диалоговое окно Customize с пиктограммами категории Custom, доступными для подключения к процедурам на Visual Basic Создание пользовательских пиктографических меню Для создания нового пиктографического меню укажите директиву View/Tool- bars, отображающую диалоговое окно Toolbars, изображенное на рис. 24.12. Затем в окне Toolbar Name запишите имя нового меню. Нажмите командную кнопку New. Появятся новое пиктографическое меню и диалоговое окно Customize. Перетащите на новое пиктографическое меню необходимые пиктограммы (новое пиктографи¬ ческое меню будет расширяться, позволяя подключать новые пиктограммы, как изображено на рис. 24.13). После того как вы перетащите на меню новую пиктог¬ рамму, появится диалоговое окно Assign Macro, позволяющее подключить к этой пиктограмме процедуру. 425
Visual Basic for Applications в примерах Toolbars И I I odbm □ Large Buttons И Show ToolTips Рис. 24.12. Диалоговое окно Toolbars, позволяющее создать пользовательское пиктографическое меню Рис. 24.13. Пользовательское пиктографическое меню Создание пиктограмм с помощью Button Image Editor Предположим, что в созданном вами пиктографическом меню вам не нравится изображение одной из пиктограмм. Например, пусть это будет пиктограмма, изобра¬ женная справа на пиктографическом меню, приведенном на рис. 24.13. Может быть вы предпочтете отображать что-нибудь более привлекательное, чем это нахмуренное лицо. Для того чтобы, изменить изображение пиктограммы, используется редактор Button Image Editor. Обнаружить этот редактор весьма непросто, так как он не подключен ни к одной из директив обычного меню. Он доступен только из контек¬ стного меню Buttons. Чтобы осуществить доступ к редактору Button Image Editor и отредактировать с его помощью пиктограмму, выполните следующие действия. 1. Убедитесь, что подлежащая редактированию пиктограмма находится на пиктогра¬ фическом меню, отображаемом в данный момент. 2. Укажите директиву View/Toolbars/Customize, отображающую диалоговое окно Customize. 426
Глава 24. Создание меню 3. После того как будут отображены и диалоговое окно Customize, и пиктографичес¬ кое меню, которое содержит подлежащую редактированию пиктограмму, щелкните на указанной пиктограмме правой кнопкой мыши. Появится контекстное меню Buttons. 4. Укажите в контекстном меню директиву Edit Button Image. Появится диалоговое окно Button Editor, изображенное на рис. 24.14. " Preview Рис. 24.14. Диалоговое окно Button Editor с изображением пиктограммы, подлежащей редактированию 5. Для того чтобы изменить изображение пиктограммы, вначале выберите цвет в окне Colors, а затем закрасьте этим цветом соответствующие зоны изображения. 6. После того как вы завершите коррекцию изображения (см., например, рис. 24.15), нажмите командную кнопку ОК. Скорректированное изображение заменит сущес¬ твующую пиктограмму в пиктографическом меню. 427
Visual Basic for Applications в примерах Рис. 24.15. Диалоговое окно Button Editor с новым изображением пиктограммы Подключение пиктографического меню к рабочей папке Пиктографические меню, созданные с помощью диалоговых окон Toolbars и Customize, подключены к Visual Basic, но не к рабочей папке, открытой во время создания этих меню. Однако вы можете подключить пользовательское пиктографи¬ ческое меню к рабочей папке, чтобы затем это меню было доступно при каэкдом открытии данной рабочей папки пользователем. Для того чтобы подключить пользовательское пиктографическое меню к рабочей папке, выполните следующие действия. 1. Укажите директиву Toolls/Attach Toolbars, изображающую диалоговое окно Attach Toolbars. В окне Custom Toolbars этого диалогового окна перечислены все пользовательские пиктографические меню. 2. Выберите одно из перечисленных пиктографических меню и нажмите командную кнопку Сору. Указанное пиктографическое меню появится также в окне Toolbars in Workbook, как изображено на рис. 24.16. 3. Для подключения указанного пользовательского пиктографического меню к теку¬ щей активной рабочей папке нажмите командную кнопку ОК. 428
Глава 24. Создание меню Рис. 24.16. Пользовательское пиктографическое меню и диалоговое окно Attach Toolbars, позволяющее подключить это меню к рабочей папке Управление пиктографическими меню и пиктограммами программным путем Управлять пиктографическими меню и пиктограммами программным путем весьма непросто. Хотя для этих целей применяются те же команды, что и для управления обычными меню, но пиктограммы пронумерованы и определить, какой номер за какой пиктограммой закреплен, очень сложно. Наиболее удобно создавать пиктографичес¬ кие меню программным путем, включив макрорекордер и записывая процесс создания пиктографического меню с помощью диалоговых окон Toolbars и Customize. Новые пиктографические меню создаются путем применения метода Add к набору Toolbars. Метод использует всего один аргумент: имя нового пиктографического меню. Если вы не укажите имя нового пиктографического меню, то Visual Basic присвоит ему имя по умолчанию: Toolbar 1, Toolbar 2 и т.д. Чтобы добавить пиктограмму к пиктографическому меню, примените к набору ToolbarButtons метод Add. В этом случае метод Add использует пять аргументов: Button, Before, On Action, Enabled и Pushed. 429
Visual Basic for Applications в примерах Аргумент Button устанавливает номер пиктограммы, подключаемой к данному пиктографическому меню. Этот номер сохраняется в свойстве ID данной пиктограммы. Аргумент Before устанавливает номер, определяющий местоположение данной пиктограммы в меню, считая слева направо и сверху вниз. Этот аргумент идентичен аналогичному аргументу, используемому для меню и элементов меню. Аргумент OnAction устанавливает имя процедуры, запускаемой в результате нажатия пользователем данной пиктограммы. Аргумент Enabled задает значение свойства Enabled. Это значение может быть либо True, либо False и указывает, доступна данная пиктограмма или нет. Аргумент Pushed устанавливает значение свойства Pushed данной пиктограммы. Это значение может быть либо True, либо False и указывает, выглядит данная пиктограмма нажатой или нет. Нажатая пиктограмма дополняется тенью сверху и слева (взамен тени снизу и справа), и она несколько светлее. Так, приведенная ниже программа создает пользовательское пиктографическое меню, изображенное на рис. 24.17. Она является результатом записи макрЬрекордером процесса создания пиктографического меню, перемещения этого меню, редактирова¬ ния пиктограммы и подключения меню к таблице. f * Запись создания пиктографического меню. t Sub RecordToolbar() Toolbars.Add Name:="Спец. средства” Toolbars(21).Visible = True With Application .ShowToolTips = True .LargeButtons = False .ColorButtons = True End With Toolbars("Спец, средства").ToolbarButtons.Add Button:=76, before:=l Toolbars("Спец, средства").ToolbarButtons.Add Button:=77, before:=2 Toolbars("Спец, средства").ToolbarButtons.Add Button:=78, before:=3 Toolbars("Спец, средства").ToolbarButtons .Add Button:=215, before:=4 Toolbars("Спец, средства").ToolbarButtons(4).OnAction = "DoItProc" With Toolbars("Спец, средства") .Left = 25 .Top = 162 End With Toolbars("Спец, средства").ToolbarButtons(4).Edit End Sub 430
Глава 24. Создание меню Рис. 24.17. Пользовательское пиктографическое меню, созданное при включенном макрорекоредере Рассмотрим эту программу по частям, что позволит понять действия различных команд и диалоговых окон. Первый оператор использует метод Add для создания нового пиктографического меню, второй оператор делает это меню видимым, а следующий блок из пяти операторов устанавливает значения некоторых свойств: Toolbars.Add Ыате:="Спец. средства” Toolbars(21).Visible = True With Application .ShowToolTips = True .LargeButtons = False .ColorButtons = True End With Для создания нового пиктографического меню нужны только два первых опера¬ тора. Остальные операторы являются результатом создания нового пиктографического меню с помощью диалогового окна Toolbars. Число 21 во втором операторе зафикси¬ ровано макрорекордером для выбора данного пиктографического меню и может быть заменено именем последнего, тем более что номер, присвоенный этому меню, может изменяться. Приведенный ниже блок из четырех операторов является результатом использо¬ вания диалогового окна Customize для добавления к данному пиктографическому меню четырех пиктограмм. Пятый оператор — это результат подключения процедуры DoItProc к четвертой пиктограмме меню с помощью диалогового окна Assign Macro. Toolbars("Спец, средства").ToolbarButtons.Add Button:=76, before:=l Toolbars("Спец, средства").ToolbarButtons.Add Button:=77, before:=2 Toolbars("Спец, средства").ToolbarButtons.Add Button:=78, before:=3 Toolbars("Спец, средства").ToolbarButtons.Add Button:=215, before:=4 Toolbars("Спец, средства").ToolbarButtons(4).OnAction = "DoItProc" Следующие четыре оператора являются результатом перемещения нового пикто¬ графического меню в нижнюю левую часть экрана. Эти операторы изменяют значения свойств Left и Тор данного меню: 431
Visual Basic for Applications в примерах With Toolbars("Спец, средства") .Left = 25 .Top = 162 End With Примечание: Большинство элементов управления и изображаемых объектов обладают свойства¬ ми Тор и Left, определяющими местоположение этих объектов на экране. Значение этих свойств измеряются в точках от верхнего левого угла объекта до верхнего левого угла контейнера, содержащего данный объект. Каждая точка равна 1/72 дюйма, а контейнер, содержащий объект, является объектом,к которому подклю¬ чен данный объект или в котором он расположен. Например, для командной кнопки бланка контейнером выступает бланк, а для пиктографического меню, отображенного на экране, — рабочая, зона таблицы. Благодаря тому, что этими свойствами можно управлять программным путем, вы можете перемещать пиктог¬ раммы и объекты по всему экрану для создания специальных эффектов. Следующая строка появилась в результате запуска редактора Button Image Editor: Toolbars("Спец, средства").ToolbarButtons(4).Edit Эта строка только отображает диалоговое окно Button Editor, но изменения, произведенные с четвертой пиктограммой, не были включены в запись. Кроме того, данное пиктографическое меню не подключено к текущей рабочей папке. Эти действия не могут быть выполнены программным путем, их необходимо осуществлять вручную. Заключение Из данной главы вы узнали о создании и редактировании строк меню, меню и элементов меню. Большинство из этих задач решаются применением метода Add для создания новых объектов и метода Delete — для их удаления. Кроме того, вы ознакомились с созданием и редактированием пиктографических меню и пиктограмм. В следующей главе описано как создавать пользовательские объекты с помощью Visual Basic. . Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Назовите три объекта, составляющие меню. 2. Можно ли подключить к меню процедуру? 3. Что такое контекстное меню? 432
Глава 24. Создание меню 4. Как активизировать Button Image Editor? 5. Как создать и отобразить пользовательскую строку меню? Упражнения для закрепления материала 1. Создайте пользовательскую строку меню, применяющую в качестве прототипа строку меню Visual Basic Module и включающую меню и элементы меню, анало¬ гичные меню File и Edit указанной строки. Подключите элементы этих меню к фиктивным процедурам. 2. Создайте пользовательское пиктографическое меню, включающее все ваши люби¬ мые директивы. 3. Напишите процедуру, маркирующую и отменяющую маркировку элемента меню при выборе данного элемента пользователем. 4. Напишите процедуру, делающую недоступными другие элементы меню, если элемент меню, созданного в упражнении 3, маркирован. 5. Напишите процедуру, заставляющую пиктограмму прыгать по экрану после того, как пользователь нажмет эту пиктограмму. Используйте пару случайных чисел, определяющих местоположение пиктограммы. 6. Напишите процедуру, отображающую ID —номера любых пиктограмм, помещен¬ ных в пользовательское пиктографическое меню. 433
Глава 25 Создание пользовательских объектов Хотя Visual Basic располагает достаточными возможностями для манипулирования объектами, он не позволяет создавать новые объекты только программными средства¬ ми. Тем не менее Visual Basic способен точно имитировать функционирование объекта. Из этой главы вы узнаете: > Как Visual Basic имитирует объекты > Как создавать процедуры пользовательских свойств > Как создавать пользовательские методы Определение пользовательского объекта Программный пользовательский объект в Visual Basic привязан к модулю. Имя модуля является именем объекта, и применяется для доступа к свойствам объекта. Если объекты стандартные, то следует начинать с класса объекта, а затем, использовав порождающую функцию вроде метода ADD, можно создавать объекты этого класса. В Visual Basic класс создать нельзя: следовательно, надо создавать временный пользовательский объект. Однако после того, как вы его создадите, доступ к нему ничем не будет отличаться от доступа к любому другому объекту. Возможно, будущие версии Visual Basic for Applications позволят создавать структуры классов, но пока,, к сожалению, это невозможно. Поскольку вы создаете объекты Visual Basic в модулях, они являются подключен¬ ными к текущей открытой рабочей папке. Однако вам редко потребуется использовать для доступа к этим объектам набор Workbooks, так как они создаются в виде множества процедур в модуле и Visual Basic всегда знает, как обнаружить эти процедуры. Для того чтобы создать пользовательский объект, начните с нового модуля. Не используйте существующий модуль, поскольку объединение обычных процедур с пользователь¬ ским объектом приведет к ошибке. Для присвоения модулю имени создаваемого пользовательского объекта воспользуйтесь директивой Edit/Sheet/Rename или два¬ жды щелкните мышью на корешке модуля. Для объявления глобальных переменных модуля как частных используйте в заголовке модуля оператор Private, а не Dim. Таким образом, вы защитите все данные объекта от доступа внешних процедур. Единствен¬ ным способом доступа к данным, сохраненным в модуле, является доступ к его свойствам и методам. Любые процедуры, входящие в состав этого модуля, могут использоваться только процедурами, входящими в его состав, и также должны быть объявлены как частные. Кроме того, в заголовок процедуры можно поместить оператор Option Private Module, что сделает свойства и методы данного объекта доступными только из 434
Глава 25. Создание пользовательских объектов Текущего проекта, но не из модулей и процедур, не входящих в данный проект. Проект включает в себя все модули рабочей папки и все рабочие папки, подключенные к данной рабочей папке директивой Tools/References. Создание пользовательских свойств Свойства создаются с помощью процедур Property. Существуют три типа указан¬ ных процедур: Property Let, Property Get и Property Set. Процедуры Property Let управляют вводом данных в объект, что определяет эти свойства как получающие данные. Эти процедуры появляются в левой части формул. Процедуры Property Get управляют выводом данных из объекта и появляются в правой части формул. Процедуры Property Let и Property Get могут иметь одно и то же имя. В этом случае выбор используемой процедуры определяется направлением передачи данных. Процедуры Property Set подобны процедурам Property Let, но передают не значения, а объекты. Использование процедуры Property Get с результатом, объяв¬ ленным как Object, позволяет передавать объекты из пользовательского объекта. Процедуры Property Let Процедуры Property Let позволяют присваивать свойствам значения. С помощью этих процедур можно создавать свойства, получающие и накапливающие данные. Свойства, объявленные процедурами Property Let, всегда появляются в левой части формулы и могут получать значения, являющиеся результатом вычислений по данной формуле. Объявление процедуры Property Let в основном идентично объявлению подпрог¬ раммы, с тем отличием, что вам следует использовать зарезервированные слова Property Let вместо Sub. Процедура Property Let имеет следующий синтаксис: Property Let имя_свойства (аргументы) ' Некоторые операторы. Exit Property ' Другие операторы. End Property Имя процедуры имя_свойства является именем свойства; для указания контейнера данного свойства используется имя модуля, содержащего эту процедуру. В списке аргументов должен быть хотя бы один аргумент, позволяющий получить значение данного свойства. Если у вас несколько аргументов, то самый правый из них предназначен для получения значения данного свойства. Любые другие аргументы передаются заключенными в круглые скобки и подключенными к имени свойства. В зависимости от ваших потребностей синтаксис данной процедуры может быть расши¬ рен (более подробную информацию можно посмотреть в интерактивной подсказке). 435
Visual Basic for Applications в примерах Например, следующая процедура, находящаяся в модуле BankAccount, объявляет свойство Deposit: Property Let Deposit(ByVai Received As Currency) Index = Index + 1 theDeposit(Index) = Received End Property Чтобы использовать это свойство в другой процедуре, применяют оператор, подобный следующему: BankAccount.Deposit =250 В результате выполнения данного оператора число 250 будет передано процедуре Deposit, которая сохранит его в переменной Received. Обратите внимание на то, что эти свойства не требуют использования литеральных значений (вроде 250), —вместо этого можно использовать любую переменную или формулу, позволяющую рассчитать значение данного свойства. Обратите внимание также на то, что данный аргумент объявлен как By Vai, следовательно, данные, переданные процедуре, были скопиро¬ ваны, что защитило переменную в вызывающей процедуре от изменений. Процедура Deposit наращивает глобальную переменную Index и сохраняет переданный депозит в глобальном массиве theDeposit. Чтобы изменить значение предыдущего депозита, нужно создать другую процедуру, принимающую кроме нового значения депозита еще и индекс, который указывает на то, какой из депозитов должен быть изменен. Вы можете написать эту процедуру так: Property Let Deposits(anlndex As Long, ByVai Received As Currency) theDeposit(anlndex) = Received End Property Чтобы использовать данное свойство в операторе, можно написать нечто подобное BankAccount.Deposits(10) = 250 Этот оператор передает переменной anlndex значение 10, а переменной Received — значение 250. Затем эти две переменные используются для изменения значения десятого элемента массива theDeposit(). Процедуры Property Get Процедуры Property Get представляют собой дополнение процедур Property Let. Они предназначены для передачи данных из объекта. Синтаксис процедур Property 436
Глава 25. Создание пользовательских объектов Get в основном идентичен синтаксису функций, в которых данные передаются из функции в ее имени: Property Get имя_ свойств a (аргументы) As тип ' Некоторые операторы. Exit Property ' Другие операторы. End Property Аргументы и тип имеют те же значения, что и для функций. Так же, как и в функции, оператор в теле данной процедуры должен присвоить передаваемое значение переменной, имя которой является именем процедуры. Именно это значение процедура и вернет вызывающей программе. Обратите внимание на то, что аргументы передаются процедуре, а результат возвращается в имени процедуры в качестве значения свойства. Так процедура Property Get Balance() As Currency Balance = theBalance End Property возвращает значение остатка на счете из объекта CheckBook. Для доступа к этой процедуре из другой процедуры можно использовать оператор CashAvail = CheckBook.Balance После вызова процедуры Balance текущее значение остатка на счете будет присвое¬ но имени процедуры/а затем передано переменной CashAvail вызывающей программы. Процедуры Property Let и Property Get могут иметь одно и то же же имя, что позволит указать пару процедур, обменивающуюся данными в обоих направлениях. Поскольку значение свойства Balance обычно является вычисляемой величиной, то это свойство имеет лишь одну процедуру Property Get, определяющую это свойство как доступное только для чтения. Свойство Deposits может как читаться, так и изменяться, а следовательно имеет кроме описанной выше процедуры Property Let и соответствующую процедуру Property Get: Property Get Deposits(anlndex As Long) As Currency Deposits = theDeposit(anlndex) End Property Для использования данного свойства может применяться оператор: aDeposit = BankAccount.Deposits(10) 437
Visual Basic for Applications в примерах Этот оператор позволяет получить значение десятого депозита и присвоить его переменной aDeposit. Процедуры Property Let и Property Get можно использовать в одной формуле. В этом случае Visual Basic определяет порядок вызова процедур, задавая таким образом направление передачи данных. Например, оператор BankAccount.Deposits (27) = BankAccount.Deposits(27) + 125 позволяет добавить к двадцать седьмому депозиту 125. Этот оператор вначале вызывает процедуру Property Get для получения предыдущего значения данного депозита, затем добавляет к этому значению 125, после чего передает новое значение процедуре Property Let, которая сохраняет это новое значение в объекте BankAccount. Процедуры Property Set Процедуры Property Set в основном идентичны процедурам Property Let, за исключением того, что передают объекты, а не значения. Процедура Property Set имеет следующий синтаксис: Property Set имя_ свойств а (аргументы) ' Некоторые операторы. Exit Property ' Другие операторы. End Property Список аргументов аналогичен списку в процедуре Property Let. Самый правый аргумент предназначен для получения объекта от вызывающей процедуры и должен быть типа Variant или объявлен As Object. Все остальные аргументы передаются самой процедуре. Например, объект Check Book может отображать список операций в таблице. Чтобы выполнить указанные действия, объект вначале должен получить количество опера¬ ций, которые перечислены в списке, а затем —объектную ссылку на область ячеек таблицы. Для того чтобы подготовиться к печати этого списка, можно написать процедуру подобную процедуре Property Set PrintRange(startindex As Long, numTrans As Long, _ aPrintRange As Object) theStartlndex = startindex theNumTrans = numTrans Set thePrintRange = aPrintRange End Property 438
Глава 25. Создание пользовательские объектов Обратите внимание на использование оператора Set, сохраняющего объект aPrint- Range в переменной объектного типа thePrintRange. Эта процедура могла бы и сама сформировать такой список, однако вы можете создать метод, формирующий этот список, используя данные, которые переданы этой процедуре. Для вызова указанной процедуры можно использовать оператор, подобный сле¬ дующему: Checkbook.PrintRange(12, 10) = Sheets("Sheetl).Range("В5") В этом операторе переменной start Index передается значение 12, переменной numTrans — 10, а ссылка на область В5 таблицы Sheetl передается как объект aPrintRange. Эти три значения сохраняются в глобальных переменных процедуры Property Set. Для возврата объекта вызвавшей программе используется процедура Property Get, в которой возвращаемое значение задано типа Object. Создание пользовательских методов Пользовательские методы являются наиболее простой в реализации частью объек¬ та, поскольку представляют собой просто общедоступные процедуры, находящиеся в модуле, который содержит процедуры Property. Эти процедуры имеют доступ ко всем глобальным переменным модуля, которые являются частными данными объекта. Применение пользовательского объекта В качестве примера применения пользовательского объекта рассмотрим сравнение двух инвестиций, позволяющее определить, какая из них является более прибыльной. Вначале создайте объект InvTable, рассчитывающий инвестиционную таблицу для двух инвестиций, а затем отображающий результаты в таблице Excel. Сравнению подлежат две следующие инвестиции: > Накопление наличных средств на накопительном счете с ежемесячным начисле¬ нием сложного процента. > Предоставление ссуды под сложный процент с выплатой ежегодных взносов, а затем помещение полученных наличных средств на тот же накопительный счет, что и в предыдущем варианте. Ниже приведена программа, рассчитывающая будущие значения для указанных вариантов инвестиций, что позволит определить, какая из них является более прибыль¬ ной. Сохраните эту программу в модуле InvTable. Для проверки этого объекта потребуется другая процедура, расположенная в другом модуле и предназначенная для получения свойств данного объекта и вызова его методов. 439
Visual Basic for Applications в примерах ' Создание пользовательского объекта. ' Свойства: ' Presentvalue = Текущее значение инвестиции. ' Years = Общее количество лет для двух инвестиций. ' Ratel = Процентная ставка для первой инвестиции. ' Rate2 = Процентная ставка для второй инвестиции. ' TLCell = Ссылка на верхнюю левую ячейку таблицы. ' Предполагаются помесячные платежи для обеих инвестиций. ' Методы: ' DrawTable = Изображение таблицы. Option Private Module Option Explicit 'Ограничивает доступ к текущему проекту. 'Заставляет'объявлять все переменные. Private Private Private Private Private Private thePresVal As Currency theYears As Single theRatel As Single theRate2 As Single theTLCell As DataFlags(5) 'Прячет все, кроме вызовов свойств. Const Const Const Const Const dtPresVal = 1 dtYears = 2 dtRatel = 3 dtRate2 = 4 dtTLCell = 5 Obj ect As 'Массив флагов, указывающих на то 'какие переменные объявлены. 'Константы, идентифицирующие элементы 'массива DataFlags. Boolean ' Начало определения процедур Property. г ' Ввод текущего значения инвестиции. Property Let Presentvalue(ByVai data As Currency) If IsNumeric(data) Then thePresVal = data DataFlags(dtPresVal) = True Else MsgBox "Текущее значение должно быть числом.", , "Ошибка Изображения Таблицы " End If 440
Глава 25. Создание пользовательских объектов End Property г ' Вывод текущего значения инвестиции. г Property Get Presentvalue() As Currency Presentvalue = thePresVal End Property r ' Ввод количества лет. г Property Let Years(ByVai data As Single) If IsNumeric(data) Then theYears = data DataFlags(dtYears) = True Else MsgBox "Количество лет должно быть числом.", _ , "Ошибка Изображения Таблицы " End If End Property г ' Вывод количества лет. t Property Get Years() As Single Years = theYears End Property ' Ввод процентной ставки для первой инвестиции. Property Let Ratel(ByVai data As Single) If IsNumeric(data) Then theRatel = data DataFlags(dtRatel) = True Else MsgBox "Процентная ставка 1 должна быть числом.", _ , "Ошибка Изображения Таблицы " End If End Property г ' Вывод процентной ставки для первой инвестиции. 441
Visual Basic for Applications в примерах Property Get Ratel() As Single Ratel = theRatel End Property ' Ввод процентной ставки для второй инвестиции. 1 Property Let Rate2(ByVai data As Single) If IsNumeric(data) Then theRate2 = data DataFlags(dtRate2) = True Else MsgBox ’’Процентная ставка 2 должна быть числом.”, , "Ошибка Изображения Таблицы ” End If End Property i ' Вывод процентной ставки для второй инвестиции. г Property Get Ra.te2() As Single Rate2 = theRate2 End Property ' Ввод ссылки на область таблицы. / Property Set TLCell(ByVai data As Object) If TypeName(data) = "Range" Then Set theTLCell = data DataFlags(dtTLCell) = True Else MsgBox "Ссылка на область таблицы должна быть ссылкой Range.", _ , "Ошибка Изображения Таблицы ’’ End If End Property ' Вывод ссылки на область таблицы. t Property Get TLCell() As Object Set TLCell = theTLCell End Property 442
Глава 25. Создание пользовательских объектов ' Начало определения метода. г ' Создание метода. ' Изображение таблицы. г Sub DrawTable() ' Имитация метода. ' Проверка исходных данных. If Not DataFlags(dtTLCell) Then MsgBox "He определена область таблицы.", _ , "Ошибка Изображения Таблицы " Elself Not DataFlags(dtPresVal) Then MsgBox "He определено текущее значение инвестиции.", _ , "Ошибка Изображения Таблицы " Elself Not DataFlags(dtYears) Then MsgBox "He определено количество лет.", _ , "Ошибка Изображения Таблицы " Elself Not DataFlags(dtRatel) Then MsgBox "He определена процентная ставка для первой инвестиции.", , "Ошибка Изображения Таблицы " Elself Not DataFlags(dtRate2) Then MsgBox "He определена процентная ставка для второй инвестиции.", , "Ошибка Изображения Таблицы " Else 'Данные определены. Можно изображать таблицу. MakeTable End If End Sub f ' Начало определения частной вспомогательной процедуры. ' Эта процедура недоступна вне данного модуля. е ' Изображение таблицы. г Private Sub MakeTable() Dim thePmt As Currency With theTLCell .FormulaRlCl = "Сравнение инвестиций" .Columns("A:A").Columnwidth = 20 .Range("A2").FormulaRlCl = "1. Помесячно" 443
Visual Basic for Applications в примерах .Range("АЗ").FormulaRlCl = "2. Ежегодно" .Range("Bl").FormulaRlCl = "Текущее значение" .Range("Cl").FormulaRlCl = "Ставка" .Range("DI").FormulaRlCl = "Лет" .Range("El").FormulaRlCl = "Платеж" .Range("Fl").FormulaRlCl = "Будущее значение" .Columns("F:F").ColumnWidth = 16.29 .Columns("B:B").ColumnWidth = 16.14 .Columns("C:C").Columnwidth = 6.57 .Columns("E:E").ColumnWidth = 10 .Columns("D:D").ColumnWidth = 3.24 With .Range("Al :F3") .Borders(xlLeft).Weight = xlThin .Borders(xlRight).Weight = xlThin .Borders(xlTop).Weight = xlThin .Borders(xlBottom) .Weight = xlThin .BorderAround Weight:=xlMedi urn, Colorindex:=xlAutomatic End With With .Range("B2:F3") .Borders(xlLeft) .Weight = xlThin .Borders(xlTop).Weight = xlThin .BorderAround Weight:=xlMedium, Colorindex:=xlAutomatic End With .Range("E2:F3") .NumberFormat = "$#,##0.00_);($#,##0.00)" .Range("B2:B3").NumberFormat = "$#,##0.00_) ;($#,##0.00)" .Range("C2:C3").NumberFormat = "0.00%" With .Range("E2").Interior .Colorindex = 0 .Pattern = 17 .PatternColorlndex = xlAutomatic End With 'Запись данных в ячейки. .Range("В2").Formula = thePresVal .Range("B3").Formula = 0 .Range("C2").Formula = theRatel .Range("C3").Formula = theRate2 .Range("D2:D3").Formula = theYears ’ Формула для денег, помещаемый в банк под ежемесячные проценты. .Range("F2").Formula = Application.Fv(theRatel / 12, _ theYears * 12, 0, -thePresVal) 444
Глава 25. Создание пользовательских объектов ' Формула для ссуды с возвратом платежей раз в год с последующим ' помещением их в банк под проценты. thePmt = Application.Pmt(theRate2, theYears, -thePresVal) .Range("E3").Formula = thePmt .Range("F3").Formula = Application.Fv(theRatel, _ theYears, -thePmt, 0) End With End Sub Вначале обратите внимание на заголовок модуля: ' Создание пользовательской процедуры. ! ' Свойства: ' Presentvalue = Текущее значение инвестиции. ' Years = Общее количество лет для двух инвестиций. ' Ratel = Процентная ставка для первой инвестиции. ' Rate2 = Процентная ставка для второй инвестиции. ' TLCell = Ссылка на верхнюю левую ячейку таблицы. ' Предполагаются помесячные платежи для обеих инвестиций. ' Методы: ' DrawTable = Изображение таблицы. Option Private Module Option Explicit Private Private Private Private Private Private Const Const Const Const Const 'Ограничивает доступ к текущему проекту. 'Заставляет объявлять все переменные. thePresVal As Currency theYears As Single theRatel As Single theRate2 As Single theTLCell As DataFlags(5) dtPresVal = 1 dtYears = 2 dtRatel = 3 dtRate2 = 4 dtTLCell = 5 'Прячет все, кроме вызовов свойств. Obj ect As 'Массив флагов, указывающих на то, 'какие переменные объявлены. 'Константы, идентифицирующие элементы 'массива DataFlags. Boolean В этом заголовке модуль объявлен как частный, и все его глобальные переменные являются частными для данного модуля. Это означает, что ни одна из процедур, расположенных вне этого модуля, не может получить доступ к любым данным модуля. 445
Visual Basic for Applications в примерах Единственным способом доступа к этим данным является использование свойств данного объекта. Приведем процедуры, определяющие свойства этого объекта: ' Начало определения процедур Property. г ’ Ввод текущего значения инвестиции. г Property Let Presentvalue(ByVai data As Currency) If IsNumeric(data) Then thePresVal = data DataFlags(dtPresVal) = True Else MsgBox "Текущее значение должно быть числом.", , "Ошибка Изображения Таблицы " End If End Property i * Вывод текущего значения инвестиции. г Property Get Presentvalue() As Currency Presentvalue = thePresVal End Property f ' Ввод количества лет. г Property Let Years(ByVai data As Single) If IsNumeric(data) Then theYears = data DataFlags(dtYears) = True , Else MsgBox "Количество лет должно быть числом.", _ , "Ошибка Изображения Таблицы " End If End Property f Вывод количества лет. 446
Глава 25. Создание пользовательских объектов Property Get Years() As Single Years = theYears End Property t ' Ввод процентной ставки для первой инвестиции. г Property Let Ratel(ByVai data As Single) If IsNumeric(data) Then theRatel = data DataFlags(dtRatel) = True Else MsgBox "Процентная ставка 1 должна быть числом.", _ , "Ошибка Изображения Таблицы " End If End Property г ' Вывод процентной ставки для первой инвестиции. Property Get Ratel() As Single Ratel = theRatel End Property ’ Ввод процентной ставки для второй .инвестиции. 1 Property Let Rate2(ByVai data As Single) If IsNumeric(data) Then theRate2 = data DataFlags(dtRate2) = True Else . MsgBox "Процентная ставка 2 должна быть числом.", _ , "Ошибка Изображения Таблицы " End If End Property г ' Вывод процентной ставки для второй инвестиции. f Property Get Rate2() As Single Rate2 = theRate2 End Property 447
Visual Basic for Applications в примерах ' Ввод ссылки на область таблицы. ? Property Set TLCell(ByVal data As Object) If TypeNaiue (data) = ’’Range" Then Set theTLCell = data DataFlags(dtTLCell) = True Else MsgBox "Ссылка на область таблицы должна быть ссылкой Range.’’, _ , "Ошибка Изображения Таблицы " End If End Property г ' Вывод ссылки на область таблица. Property Get TLCell() As Object Set TLCell = theTLCell End Property Для каждого свойства существуют две процедуры: одна для получения данных объектом, а вторая —для передачи данных из объекта. Процедуры, которые получают данные, должны осуществлять определенную проверку переданных значений, позво¬ ляющую убедиться в корректности типа и диапазона. Если результат такой проверки будет отрицательным, то эти процедуры должны вызывать ошибку выполнения, используя оператор ЕггогО, либо отображать диалоговое окно, поясняющее возник¬ шую проблему. В нашем примере значение заданного свойства не изменяется, а процедура просто отображает диалоговое окно и завершает свою работу. Такие пары процедур существуют для каждого из пяти свойств объекта: Present- Value, Years, Ratel, Rate2 и TLCell. Первые четыре свойства проверяют полученные значения на числа. Если эти числа будут несколько "странными”, то они приведут к "странным” результатам, однако процедуры этого не проверяют. Если у данного свойства существует определенный диапазон значений, то он также должен проверять¬ ся соответствующей процедурой. Поскольку свойство TLCell(Top Left Cell —верхняя левая ячейка) является ссылкой на область, то это проверяется соответствующей процедурой. Если процедуре, определяющей свойство данного объекта, было передано коррек¬ тное значение, то она устанавливает в True соответствующий элемент массива Data¬ Flags. Константы, определенные в заголовке модуля, позволяют выбрать корректный элемент массива для каждой из процедур. После того как все элементы данного массива получаг значение True, таблица может быть изображена. Следующая процедура DrawTable является единственным методом данного объекта: 448
Глава 25. Создание пользовательских объектов * Начало определения метода. г ’ Создание метода. ' Изображение таблицы. ! Sub DrawTable() ' Имитация метода. ' Проверка исходных данных. If Not DataFlags(dtTLCell) Then MsgBox "He определена область таблицы.”, _ , ’’Ошибка Изображения Таблицы " Elself Not DataFlags(dtPresVal) Then MsgBox "He определено текущее значение инвестиции.", _ , "Ошибка Изображения Таблицы ’’ Elself Not DataFlags(dtYears) Then MsgBox "He определено количество лет.", _ , "Ошибка Изображения Таблицы ’’ Elself Not DataFlags(dtRatel) Then MsgBox "He определена процентная ставка для первой инвестиции.", _ , "Ошибка Изображения Таблицы " Elself Not DataFlags(dtRate2) Then MsgBox "He определена процентная ставка для второй инвестиции.", _ , "Ошибка Изображения Таблицы ’’ Else 'Данные определены. Можно изображать таблицу. MakeTable End If End Sub Метод DrawTable вначале проверяет, все ли данные введены. Для этой проверки анализируются значения всех элементов массива DataFlags, которые должны быть установлены в True. Если нет, то метод отображает сообщение об ошибке; в противном случае — вызывает процедуру MakeTable, изображающую в таблице Excel инвести¬ ционную таблицу. 15 Вильям Дж. Орвис 449
Visual Basic for Applications в примерах Ниже приведена процедура MakeTable: t ' Начало определения частной вспомогательной процедуры. * Эта процедура недоступна вне данного модуля. г ' Изображение таблицы. г Private Sub MakeTable() Dim thePmt As Currency With theTLCell .FormulaRlCl = "Сравнение инвестиций" . Columns ( "A:A”).Columnwidth = 20 . Range("A2").FormulaRlCl = "1. Помесячно" .Range("A3”).FormulaRlCl = "2. Ежегодно" .Range("Bl").FormulaRlCl = "Текущее значение" .Range("Cl").FormulaRlCl = "Ставка" .Range(”D1").FormulaRlCl = "Лет" .Range("El").FormulaRlCl = "Платеж" .Range("Fl").FormulaRlCl = "Будущее значение" .Columns("F:F").Columnwidth = 16.29 .Columns("В:B").Columnwidth = 16.14 .Columns("C:C").Columnwidth = 6.57 .Columns("E:E").Columnwidth = 10 .Columns("D:D").Columnwidth = 3.24 With .Range("Al:F3") .Borders(xlLeft).Weight = xlThin .Borders(xlRight).Weight = xlThin .Borders(xlTop).Weight = xlThin .Borders(xlBottom).Weight = xlThin .BorderAround Weight:=xlMedium, Colorindex:=xlAutomatic End With With .Range("B2:F3") .Borders(xlLeft).Weight = xlThin .Borders(xlTop).Weight = xlThin .BorderAround Weight:=xlMedium, Colorindex:=xlAutomatic End With .Range("E2:F3").NumberFormat = "$#,##0.00_);($#,##0.00)" .Range("B2:B3") .NumberFormat = "$#,##0.00_); ($#, ##0.00)" .Range("C2:C3").NumberFormat = "0.00%" 450
Глава 25, Создание пользовательских объектов With .Range("Е2").Interior .Colorindex - О .Pattern - 17 .PatternColorlndex = xlAutomatic End With 'Запись данных в ячейки. .Range("В2").Formula = thePresVal .Range("ВЗ").Formula = 0 .Range(”C2").Formula = theRatel .Range("C3")•Formula = theRate2 .Range("D2:D3”).Formula = theYears ' Формула для денег, помещаемых в банк под ежемесячные проценты. . Range("F2").Formula = Application.Fv(theRatel / 12, _ theYears .* 12, 0, -thePresVal) ' Формула для ссуды с возвратом платежей раз в год с последующим 1 помещением их в банк под проценты. thePmt = Application.Pmt(theRate2, theYears, -thePresVal) .Range("E3").Formula = thePmt .Range("F3").Formula = Application.Fv(theRatel, _ theYears, -thePmt, 0) End With End Sub Процедура MakeTable является вспомогательной подпрограммой, а следовательно, она объявлена как Private и доступна только из других процедур модуля. Вначале эта процедура записывает все заголовки, изменяет ширину колонок и форматирует ячейки. Для создания этой части процедуры использовался макрорекордер, записав¬ ший процесс создания таблицы. Можно создать эту часть процедуры и вручную, но сделать это с помощью макрорекордера намного проще, так как вы сразу можете определить, подходит вам такая таблица или нет. Оставшаяся часть процедуры рассчитывает значения, которые должны быть помещены в ячейки. Для расчета будущего значения первой инвестиции и платежа по второй используются табличные функции. Платеж по второй инвестиции был вставлен в расчет для определения будущего значения этой инвестиции, формируемого за счет перевода платежей по второй инвестиции на накопительный счет. Обратите внимание на то, что ссылки, использованные в этой процедуре, являются относительными к ячейке А1. Если применить их к ссылке на область, переданной процедуре в виде значения свойства TLCell, то Visual Basic приведет местоположение таблицы в соответствие со значением TLCell. Следовательно, если TLCell является ссылкой на область, заданной как В7, то ссылка на А1 будет преобразована в ссылку на В7, ссылка на В2 будет преобразована в ссылку на С8 и т.д. 451
Visual Basic for Applications в примерах Чтобы проверить новый объект, поместите в другой модуль следующую процедуру, а затем выполните ее: ' Проверка нового объекта. Sub testlt() InvTable.Presentvalue = 10000 '$10,000 InvTable.Ratel = 0.1 '10% InvTable.Rate2 =0.12 '12% InvTable.Years =10 '10 лет. Set InvTable.TLCell = Sheets("SheetlRange("B7") InvTable.DrawTable End Sub Эта процедура устанавливает значения всех свойств объекта, а затем вызывает метод DrawTable. Если вы запустите эту процедуру, а затем переключитесь в таблицу Sheetl, то увидите результат, отраженный на рис. 25.1. Обратите внимание на то, что вторая инвестиция является более выгодной. Командная кнопка в таблице подключена к процедуре testlt, что упрощает запуск этой процедуры и проверку пользовательского объекта. в. г а .Microsoft Excel - J 3R0PERTY.XLS v / -| Е»с Edit View Insert Format Tools Data Window Help h| Israel FFl Г* П*1 liHill |L£|a)|4| [ioox |[±1 |Ф|¥?| | Arial Cjw ||±J 1» Р~[~Ц| L®. G24!il I A 1 В C 0 E F 1 G [♦ 1 2 3 4 5 6 i ... Гпр i — вверить объект ] 7 Сравнение инвестиций Текущее значение Ставка Лет Платеж Будущее значение 8 1. Помесячно $10,000.00 10.00% 10 $27,070.42 9 2. Ежегодно $0.00 12.00% 10 $1,769.84 $28,206.69 10 11 12 13 14 15 16 17 18 -i i ........ i i i I - j I I t==d > - - - - -I I I I I 1 \ -—\ L. I r— I 1 1 ] 1 1... Г ...| 1 i j 1 Н| 1 ■ ^1 м I ◄ | ► I M|\ Modute2 / InvTable \ Shoetl / Shee(2 / Shee<3 / Sh<J l*LJ □ Ready Рис. 25.1. Таблица сравнения инвестиции, созданная объектом InwTable 452
Глава 25. Создание пользовательских объектов Чтобы проверить контроль ошибок в процедурах Property, внесите ошибки в некоторые операторы процедуры testlt, а затем опять выполните ее. Заключение Из этой главы вы узнали, как создавать пользовательские объекты с помощью Visual Basic. Хотя пользовательские объекты в Visual Basic не являются объектами с точки зрения объектно-ориентированного программирования, они тем не менее ими¬ тируют большинство функциональных особенностей объекта. Эти особенности позво¬ ляют использовать данную структуру для программирования в объектно-ориентиро¬ ванной среде. Из следующей главы вы узнаете, как использовать DDE и OLE для межпрограм¬ мных коммуникаций. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Где помещается пользовательский объект? 2. Для чего предназначена процедура Property Let? 3. Для чего предназначена процедура Property Get? 4. Для чего предназначена процедура Property Set? 5. Как вернуть из процедуры ссылку на объект? 6. Как создать метод для пользовательского объекта? Упражнения для закрепления материала 1. Создайте пользовательский объект Stephanie, обладающий свойствами Birthdate, Birthplace и Age. Свойство Age сделайте доступным только для чтения и возвра¬ щающим текущий возраст, рассчитанный от Birthdate до текущего времени, полученного из системы. 2. Усложните объект CheckBook, создание которого описано в начале этой главы. Добавьте процедуры, позволяющие получать количество чеков, вводить данные из чеков, вводить депозиты, получать баланс и т.д. Внесите необходимые проверки в процедуры ввода данных в объект. 453
Глава 26 Межпрограммные коммуникации Современные прикладные программы могут связываться между собой и обмени¬ ваться информацией. Коммуникационные возможности охватывают как передачу нажатий клавиш или обмен данными с помощью Dynamic Data Exchange (DDE) (Динамический обмен данными), так и создание полностью связанных программ с помощью Object Linking and Embedding (OLE) (Связь и внедрение объектов). Из этой главы вы узнаете как делать следующее: > Инициировать связь DDE > Обмениваться информацией и директивами с помощью DDE > Инициировать сеанс OLE > Управлять объектами в другой программе > Передавать другой программе нажатия клавиш Что такое межпрограммная коммуникация? Межпрограммная коммуникация — это связь между двумя программами. Меж¬ программные коммуникации осуществляются с различными целями, которые зависят в основном от возможностей программ, участвующих в коммуникациях. Существуют два стандартных метода организации межпрограммной коммуникации: DDE и OLE. Кроме того, операционная система Windows позволяет передавать нажатия клавиш большинству программ, работающих под управлением Windows. DDE является более старым стандартом. Он создает канал обмена данными между двумя программами. Обычно по этому каналу передается текст, но некоторые програм¬ мы могут передавать двоичные данные и данные других типов, применяемые в основном для создания графики. Visual Basic for Applications обеспечивает передачу по каналу DDE только текста и чисел. Очень удобно использовать DDE для передачи небольших порций данных из одной программы в другую. Более современный стандарт OLE может быть использован только для объектно- ориентированных программ. Межпрограммная коммуникация, организованная с по¬ мощью OLE, позволяет обмениваться объектами. При обмене данными между двумя программами обе программы должны знать, как управлять этими данными, как их обрабатывать и отображать. Если вы обмениваетесь объектами, то программа, полу¬ чившая объект, не должна ничего знать о данных этого объекта. Все, что нужно знать об этих данных, а также методы их обработки и отображения, заключены в данном объекте. Таким образом, вы можете редактировать таблицу Excel, связанную с документом Word (как изображено на рис. 26.1), точно так же, как если бы работали с Excel. Word не должен уметь редактировать таблицу —он только предоставляет место для размещения этой таблицы и посылает к ней запросы. 454
Глава 26. Межпрограммные коммуникации Q Microsoft Word - Documeritl DB 1ЕВга1^]Иа1НИПЮ|^пвуЦМ1.^|1.:ь1Е~И1|р]д||Г11пг?11п1сд] | • ♦ |"йг [Н кв. III кв. IV кв. Всего Иванов 92.89 61.64 6.42 34.36 195.31 Петров 1.81 28.44 24.94 32.53 87.72 Сидоров 96.40 91.96 27.39 70.92 286.67 Лоханкин 68.31 25.31 40.2? 27.87 161.73 Итого 259.41 207.35 98.99 165.68 731.43 При ее редактировании вы переключаетесь в Excel, после завершения редактирования вы возвращаетесь в Word. |\|o|o|>|G|a|^|4h| ®0SS l^l^lici^lialal |^H|2k|g| 0ЕШ® Pape 1 Sec 1 V1 | At 71cm Ln 6 Col 25 | 1&54 [ WP Рис. 26.1. Таблица Excel, связанная с документом Word посредством OLE Обычно инициация межпрограммных коммуникаций производится посредством копирования объекта в одной программе и вставки связи с этим объектом в другую программу. Таким образом, если вы внедрили таблицу Excel в документ Word, то процессом коммуникации управляет Word, что является простейшим методом органи¬ зации коммуникаций такого рода. С другой стороны, для специальных приложений коммуникационный процесс может инициироваться и осуществляться под управле¬ нием Excel. Что такое DDE? Динамический обмен данными, или DDE, обеспечивает простейший канал пере¬ дачи сообщений между двумя программами, работающими под управлением Windows. Канал DDE —это связь (вроде трубопровода), служащая для передачи информации. Обычно по этому каналу передается текст, но некоторые программы могут передавать также графику. В Visual Basic for Applications по каналу DDE может передаваться только текст или числа. 455
Visual Basic for Applications в примерах Во всех коммуникациях на базе DDE участвуют две программы: сервер DDE и клиент DDE. Сервер DDE —это программа, содержащая данные, которые необходи¬ мы программе-клиенту. Программа-клиент инициирует обмен по коммуникации на базе DDE, запросив данные от сервера. Обычно информация направляется от сервера к клиенту. Однако существуют директивы DDE, позволяющие передавать данные и от клиента к серверу. Кроме того, данные, посылаемые серверу, могут быть директивами, которые указывают серверу на необходимость выполнить некоторые действия, напри¬ мер открыть файл. Предупреждение: При установлении двухсторонней связи программа может быть и клиентом, и сервером. Однако будьте осторожны и не создавайте замкнутого соединения, задавая один и тот же объект и сервером, и клиентом на обоих концах связи. Дело в том, что при изменении данных на сервере последний будет автоматически изменять данные клиента; следовательно, первый сервер будет изменять данные второго сервера, который затем будет изменять данные первого сервера и т.д. Ваша система зациклится на бесконечных обновлениях данных. Связи DDE работают в двух ’’температурных” режимах — горячем и холодном. Горячие связи DDE автоматически обновляют данные клиента при изменении связан¬ ных данных на сервере. Холодные связи обновляют данные только в том случае, когда серверу посылается директива DDERequest. Visual Basic for Applications автоматичес¬ ки управляет только холодными связями. Горячие связи необходимо организовывать программным путем. Применение DDE Чтобы использовать DDE, вначале нужно установить коммуникационный канал между двумя программами. Чтобы установить этот канал и определить, что будет по нему передаваться, потребуются три элемента информации: программа, тема и эле¬ мент. Значение этих трех элементов зависит от той программы, с помощью которой вы пытаетесь осуществить коммуникацию, и используемых этой программой способов обработки данных. Программа —это имя программы-сервера, с которой вы хотите связаться. Програм¬ мы, поддерживающие DDE, зарегистрированы в операционной системе Windows. Имя такой программы не обязательно является тем именем, которое отображается в окне Program Manager. Имена программ, зарегистрированных в системе Windows, приве¬ дены в табл. 26.1. 456
Глава 26. Межпрограммные коммуникации Таблица 26.1. Зарегистрированные имена программ, поддерживающих DDE Программа Зарегистрированное имя Word for Windows WinWord Excel Excel Project Project Access MS Access FoxPro FoxPro Windows Program Manager ProgMan Visual Basic for Windows имя программы* *Для интерпретированной программы имя_программы является именем окна Project этой программы; для компилированной программы имя_программы —это имя исполняемого файла без расширения .EXE. Тема —это имя документа, содержащего необходимые данные, а элемент —некий логический элемент этого документа. Для Excel тема — это имя таблицы, а элемент — область ячеек. Для программы на Visual Basic for Windows тема —это имя бланка, а элемент — элемент управления в этом бланке, содержащий необходимые данные. Для Word тема —это имя документа, а элемент —либо закладка, либо селектор Word Basic вроде \StartOfDoc, указывающий на начало документа (Word Basic — это встроенное в текущую версию Word средство программирования на BASIC). Для получения перечня всех возможных тем и элементов обратитесь к документации на программу, которую вы хотите использовать, или посмотрите интерактивную подсказ¬ ку данной программы. Открытие коммуникационного канала DDE Чтобы открыть канал DDE с другой программой, эта программа в первую очередь должна быть запущена. Для запуска используйте операторы Shell и Арр Activate. Будьте осторожны при использовании оператора Shell: эта команда запускает программу несин¬ хронно и следующий оператор вашей программы может выполниться прежде, чем начнет работать вторая программа. После команды Shell потребуется пауза, которая позволит пропустить время инициализации и старта указанной программы. Для инициализации связи между двумя выполняющимися программами исполь¬ зуется метод DDEInitiate, который имеет следующий синтаксис: канал = DDEInitiate(программа, тема) 457
Visual Basic for Applications в примерах Аргументы программа и тема являются строками. Чтобы определить, какие значения аргументов используются, обратитесь к документации на программу. Этот метод возвра¬ щает номер канала, используемый в других командах Visual Basic для идентификации данного коммуникационного канала. При выполнении метода устанавливается соединение DDE между Visual Basic for Applications и другой программой. Закрытие канала DDE После завершения использования канала DDE его необходимо закрыть с помощью метода DDETerminate, имеющего следующий синтаксис: DDETerminate (канал) В данном случае канал — это номер канала, полученный от метода DDEInitiate. Получение информации от сервера Чтобы запросить данные от сервера по открытому коммуникационному каналу, используется метод DDERequest, имеющий следующий синтаксис: Переменная = DDERequest (канал, элемент) Аргумент канал является номером канала, возвращенным методом DDEInitiate, а элемент указывает на определенную часть необходимых данных. Переменная пред¬ ставляет собой имя переменной, принимающей указанные данные. Эта переменная должна быть типа Variant. В качестве примера создайте в Excel страницу диалога, которая использует DDE для получения строки текста из Word. В Word выделите предложение, укажите директиву Edit/Bookmark и назовите полученную закладку BookMarkl. Затем переключитесь в Excel и запишите в модуле три приведенные ниже процедуры. Замените в этих процедурах ссылку на документ 26VBAOR.DOC ссылкой на один из ваших документов. Option Explicit Dim ChannelNum As Integer Dim Result t ' Установление связи DDE c Word и получение предложения. г Sub OpenChannel() 'Запуск Word и открытие документа 26VBAOR.DOC. 'Замените имя документа и путь на атрибуты одного из 'ваших документов. 458
Глава 26. Межпрограммные коммуникации ChannelNum = DDEInitiate("WinWord", "I:\BHV_VBA\26VBAOR.DOC") 'Отображение номера канала в диалоговом окне. Dialogsheets("DDEDialog").EditBoxes("DDEChannelBox").Text = _ ChannelNum End Sub f ' Получение данных. Sub GetData() Result = DDERequest(ChannelNum, "BookMarkl’’) 'Запрос данных. 'Отображение данных в диалоговом окне. Dialogsheets("DDEDialog").EditBoxes("DDEDataBox").Text - Result End Sub ' Закрытие связи. Sub Closechannel() DDETerminate (ChannelNum) End Sub Создайте страницу диалога, назовите ее DDEDialog; добавьте к ней три командные кнопки, две метки и два текстовых окна, как изображено на рис. 26.2. Назовите текстовое окно, расположенное под меткой "Результат”, DDEDataBox, а второе текстовое окно — DDEChannelBox. Для подключения командной кнопки ’’Открыть DDE" к процедуре OpenChannel, командной кнопки "Принять данные" —к процедуре GetData и командной кнопки "Закрыть DDE" — к процедуре CloseChannel исполь¬ зуйте директиву Tools/Assign Macro. Рис. 26.2. Макет диалогового окна "Тестер DDE" 459
Visual Basic for Applications в примерах На пиктографическом меню Forms нажмите пиктограмму Run Dialog, затем на бланке нажмите командную кнопку "Открыть DDE". Нажатие этой кнопки иниции¬ рует выполнение процедуры Open Channel, которая открывает канал к программе Word for Windows (WinWord), затем запрашивает документ 26VBAOR.DOC и отображает номер канала в окне DDEChannelBox. Нажатие командной кнопки "Принять данные" инициирует выполнение процедуры GetData, запрашивающей содержимое закладки BookMarkl. Если текст закладки будет обнаружен, то он отобразится в окне DDEData, как изображено на рис. 26.3. Н ' Тестер DDE | Открыть DDE | Закрыть DDE | Результат Например, создайте в Escel страницу диалога, которая использует DDE для получения 1 ок 1 | Cancel | Номер канала Рис. 26.3. Диалоговое окно "Тестер DDE" после получения содержимого закладки BookMarkl Для закрытия канала DDE нажмите командную кнопку "Закрыть DDE". Передача информации серверу Чтобы послать информацию серверу, используется метод DDEPoke. Этот метод имеет следующий синтаксис: DDEPoke(канал, элемент, данные) Аргумент элемент указывает текст на сервере, который подлежит замене, а аргумент данные содержит текст, заменяющий текст на сервере. Единственной пробле¬ мой при использовании этого метода является то, что аргумент данные должен быть областью таблицы и не может быть простой строкой. Возможно, в будущих версиях Excel эта проблема будет устранена. 460
Глава 26. Межпрограммные коммуникации Для примера, добавьте процедуру, посылающую программе Word некоторый текст, который заменяет текст, маркированный как закладка BookMarkl. Вначале добавьте к бланку командную кнопку "Послать данные", как изображено на рис. 26.4. Затем подключите эту кнопку к следующей процедуре: ' Пересылка данных. г Sub Pokechannel() 'Копирование данных из диалогового окна в ячейку таблицы. Sheets("Sheetl").Cells(1, 1).Formula = _ Dialogsheets("DDEDialog").EditBoxes("DDEDataBox").Text 'Создание ссылки на ячейку таблицы. Set Result = Sheets("Sheetl").Cells(1, 1) 'Пересылка ссылки на ячейку связанной программе для 'замены текста, маркированного как BookMarkl. Application.DDEPoke ChannelNum, "BookMarkl", Result End Sub Рис. 26.4. Диалоговое окно "Тестер DDE" с подключенной командной кнопкой "Послать данные" На пиктографическом меню Forms нажмите пиктограмму Run Dialog, затем на бланке нажмите командную кнопку "Открыть DDE" для подключения к Word. Для копирования текущего содержимого закладки BookMarkl в текстовое окно "Результат" нажмите командную кнопку "Принять данные" (см. рис. 26.5). В верхней части экрана расположено окно Word for Windows, на котором видна строка текста, маркированная как BookMarkl. В нижней части экрана расположено окно Excel с диалоговым окном "Тестер DDE", на котором в текстовом окне "Результат" виден тот же текст. 461
Visual Basic for Applications в примерах Рис. 26.5. Использование диалогового окна 'Тестер DDE''для получения из программы Word for Windows текста, маркированного как BookMarkl Теперь текст, находящийся в текстовом окне “Результат", можно редактировать. Чтобы этот отредактированный текст заменил собой текст, маркированный как BookMarkl, нажмите командную кнопку "Послать данные” (см. рис. 26.6). Процедура PokeChannel сначала копирует содержимое текстового окна в ячейку А1 таблицы Sheetl. Затем это значение загружается как объект в переменную Result. Запомните, что метод DDEPoke должен получить в качестве аргумента не строку, а ссылку на область ячеек; следовательно, эти "лишние" действия обеспечивают замену текста ссылкой на область ячеек, содержащих этот текст. Затем метод DDEPoke передает этот текст в Word, где он заменяет собой текст, маркированный как BookMarkl. Для закрытия связи нажмите командную кнопку "Закрыть DDE". 462
Глава 26. Межпрограммные коммуникации Рис. 26.6. Текст документа Word for Windows заменен текстом из окна "Результат" диалогового окна "Тестер DDE" Excel Передача директив серверу Кроме передачи программе некоторых данных, связь DDE позволяет передать и директивы. Директивы, которые вы хотите передать программе, должны быть на командном языке той программы, которой вы их передаете. Чтобы передать директивы подключенной программе, используется метод DDEE- xecute, имеющий следующий синтаксис: DDEExecute {канал, команд на я_с трока) Аргумент канал является номером канала DDE, а аргумент командная—строка — строкой, содержащей директиву для подключенной программы. Перешлите программе Word for Windows директиву File/Print Preview. Для этого добавьте к диалоговому окну "Тестер DDE" командную кнопку "ПредПросмотр" и подключите ее к следующей процедуре: 463
Visual Basic for Applications в примерах ' Включение Print Preview. f Sub Previewlt() 'Пересылка связанной программе директивы предварительного просмотра. DDEExecute ChannelNum, "[FilePrintPreview]" End Sub Обратите внимание на то, что директива File/Print Preview заключена в квадрат¬ ные скобки. Если вы опустите скобки, то Word вставит эту директиву как текст в точку вставки, а не выполнит ее как директиву. Применение OLE OLE имеет значительно больше возможностей, чем DDE. Если DDE просто обеспечивает обмен данными между программами, то OLE позволяет передавать объекты. Существуют два различных типа OLE; Linking and Embedding (Связь и внедрение) и OLE Automation (Автоматизация OLE). С помощью Linking and Embedding вы внедряете объекты одной программы в документы другой. На рис. 26.1 отображен пример внедрения таблицы Excel в документ Word. Внедряемые объекты создаются копированием объекта в одной программе, а затем вклеиванием связи с этим объектом в другую программу. После того как вы связали и внедрили объект, вы можете редактировать этот объект в породившей его программе. Программа, отображая внедренный объект, не должна ничего знать о том, как он создается или изменяется, —она только отображает этот объект. OLE Automation позволяет одному объекту управлять другой программой посред¬ ством доступа к ее объектам и методам. При использовании OLE Automation объекты и методы подключенной программы становятся расширением языка Visual Basic. Применение OLE Automation Прежде чем использовать объекты и методы другой программы, вы должны все узнать об этих объектах. Если данная программа содержит библиотеку объектов, зарегистрированную в системе и доступную, вы можете использовать Object Browser для ознакомления с объектами и методами, включенными в эту библиотеку. Если программа не разрешает доступ к библиотеке, то вы должны обратиться к документа¬ ции на программу —чтобы ознакомиться с доступными объектами и методами. Для получения объектов программы с помощью Object Browser необходимо зарегис¬ трировать эту программу в Excel. Вначале укажите директиву Tools/References для отображения диалогового окна References. Затем включите контрольный индикатор, 464
Глава 26. Межпрограммные коммуникации соответствующий данной программе, в окне списка Available References. После того как программа зарегистрирована, все ее объекты становятся доступными Visual Basic. Теперь вы можете использовать Object Browser для изучения объектов другой программы. Кроме того, Object Browser отображает имя, под которым зарегистриро¬ вана в системе данная программа. Например, программа Project зарегистрирована под именем MSProject. Предупреждение: Имя, используемое в целях регистрации программы для коммуникаций OLE, отличается от имени, используемого для DDE. Это объясняется тем, что DDE связывается с программой, a OLE — с объектами из библиотеки ее объектов. Так, приведенная ниже процедура использует как объекты Excel, так и объекты Project. Вначале она использует объекты Project для старта Project, открытия файла и копирования значения поля Name первой задачи (Task), затем —объекты Excel для записи этого имени в ячейку А1 таблицы Sheet3. После этого процедура использует метод Project для закрытия текущего проекта и завершения программы Project. Если вы хотите попробовать работу этой процедуры, то подставьте в нее имя одного из ваших файлов проекта. г ' Пример применения OLE Automation. f Sub GetTaskO Dim msProj As Object, aTask As String 'Запуск программы Project и открытие файла. ' MSRroject.Application.FileOpen Name :="I: \BHV_VBA\BOOKPLAN .MPP" 'Сохранение имени первой задачи в переменной. ' aTask *= MSProject.Application.Projects(1).Task(1).Name 'Отображение имени задачи в ячейке таблицы. Sheets("Sheet3").Range("Al”).Value = aTask 'Закрытие файла и завершение программы Project. 'MSProject.Application.FileExit End Sub Объекты другой программы могут иметь те же имена, что и объекты в вашей программе; следовательно, для доступа к объектам другой программы надежнее пользоваться полными их именами. Например, если вы запустите в Excel Visual Basic и выполните оператор Application.Name 465
Visual Basic for Applications в примерах то получите имя Excel. Но если в Excel вы выполните оператор MSProj ect.Application.Name то получите имя Project. Для доступа к любому объекту, находящемуся вне текущей программы, исполь¬ зуйте полную ссылку на него, что не только даст возможность уверенно получить нужный объект, но и повысить читабельность вашей программы. Примечание: Многие программы остаются невидимыми после старта с помощью OLE Automation. Такие программы не отображаются и в списке задач (Task List), что не позволяет перейти в эти программы и просмотреть открытые документы. Чтобы сделать такую программу видимой после старта с помощью OLE, установите свойство Visible этой программы в True. Например, чтобы сделать программу Project видимой и отобразить ее в списке задач, можно использовать оператор MSProject.Application.Visible = True Создание и открытие объектов Для программ, управляемых посредством OLE Automation, существуют два метода создания новых объектов или открытия существующих объектов: CreateObjectO и GetObject(). В общем случае объектами, к которым вы получаете доступ с помощью указанных методов, являются документы подключенных программ. Для полностью OLE-согласованных программ, таких как Project и Excel, открытие или создание новых файлов осуществляется методами связанных объектов, а не методами Create- Object и GetObject. Для частично OLE-согласованных программ вроде Word создание связей с документами обеспечивается с помощью методов CreateObject и GetObject. Метод CreateObject имеет следующий синтаксис: Set объектна я_переменная = CreateObject{класс) где объектная—переменная представляет собой любую переменную, объявленную как Object или Variant, а класс является классом создаваемого объекта. Аргумент класс состоит из двух частей, разделенных точкой: Регистрационное_имя. Объект где Регистрационное—имя — это имя, под которым программа зарегистрирована в системе, например Excel, MSProject или Word, а Объект является типом создаваемого объекта. Для Excel тип объекта может принимать значения Application, §heet или Chart. Для Project типом объекта может быть Project, для Word —Basic. 466
Глава 26. Межпрограммные коммуникации Так, следующий оператор запускает Excel и создает новую таблицу: Set XLSheet = CreateObject("Excel.Sheet”) Если Excel уже запущен и рабочая папка уже открыта, то этот оператор добавляет новую таблицу к открытой рабочей папке. Если Excel не запущен, то оператор запускает Excel с новой рабочей папкой. Следующий оператор запускает Project с новой чистой страницей проекта: Set ProjSheet = CreateObject("MSProject.Project") Следующий оператор запускает Word и возвращает объект, на который можно ссылаться как на Word Basic: Set Wordsheet = CreateObject("Word.Basic") Примечание: Поскольку программа Word не полностью OLE — согласована, то ссылкой является Word Basic, а не конкретный документ. Word Basic встроен в текущую версию Word средством программирования на BASIC. Большинство (но не все) директив Word Basic доступны связанной программе. Эти директивы пересылают¬ ся программе Word как методы объекта Word Basic. Фактически Word интерпре¬ тирует эти директивы, применяя их к текущему документу как к объекту. Метод GetObject открывает существующий объект. Метод имеет следующий синтаксис: Set Объектная_переменная = GetObject (Имя_файла, класс} где аргументы Объектная_переменная и класс имеют те же значения, что и для метода CreateObject. Аргумент Имя файла содержит имя файла и путь, необходимые для открытия объекта. Обычно этот аргумент является именем файла, содержащего документ связанной программы. Если расширение имени файла позволяет определить класс объекта, то аргумент класс можно опустить. Например, если имя файла имеет расширение .XLC, то известно, что это расширение присуще файлу с диаграммами Excel. Метод GetObject не запускает программу, если она не запущена на момент выполнения метода. Например, следующий оператор открывает таблицу COSTS.XLS в директории PROJ3 диска D: Set XLSheet = GetObject("D:\PROJ3\COSTS.XLS", "Excel.Sheet") 467
Внедрение объектов в программу Простейшим методом внедрения объекта одной программы в другую является копирование этого объекта вручную в первой программе, а затем вклеивание связи с ним в другую программу. Однако аналогичные действия можно осуществить програм¬ мным путем на Visual Basic. Так, приведенная ниже процедура внедряет таблицу Excel в документ Word, как показано на рис. 26.1. Для связи с Word посредством этой процедуры ваша таблица в Excel должна находиться в ячейках A1:F6 таблицы Sheet2. Этот пример был создан при помощи макрорекордера в Excel и Word, путем записи процесса связи с последующим копированием и редактированием записанных макросов: / ' Вклеивание связанной таблицы. Sub LinkToWord() Dim WordObj As Object, ChannelNum As Integer 'Выбор и копирование таблицы в Excel. Sheets("Sheet2").Select Range("Al:F6").Select Selection.Copy 'Запуск и активизация программы Word, открытие нового документа. Set WordObj = CreateObject("Word.Basic") AppActivate "Microsoft Word" WordObj.FileNew 'Применение DDE для передачи Word директивы EditPasteSpecial 'для вклеивания и связи таблицы, скопированной из Excel. ChannelNum = Application.DDEInitiate("WinWord", "System”) . DDEExecute ChannelNum, "[EditPasteSpecial .Link = 1, .Class = ""Ex¬ cel . Sheet . 5 "" , .DataType = ""Object""]" DDETerminate ChannelNum End Sub К сожалению, эта процедура не столь проста, как хотелось бы, так как Word имеет некоторую особенность, которая, мы надеемся, будет изменена в последующих вер¬ сиях. Этой особенностью является директива EditPasteSpecial, недоступная посред¬ ством OLE. Первые несколько строк процедуры выбирают таблицу, расположенную в ячейках A1:F6 таблицы Sheet2 Excel, а затем копируют эту таблицу в буфер обмена Windows. Оператор Set использует метод CreateObject для запуска Word и получения ссылки на объект. Оператор AppActivate перемещает Word на передний план и помещает его в список задач. Если вы не включите этот оператор, то программа Word завершится вместе с завершением данной процедуры. 468
Глава 26. Межпрограммные коммуникации Следующий оператор использует директиву FileNew для создания нового документа. После этой директивы надо было бы использовать директиву Word Basic EditPasteSpecial для вклеивания связи OLE в данный документ. К сожале¬ нию, Word Basic не является объектно-ориентированным и лишь некоторые из его директив доступны посредством OLE для программ вне Word. В текущей версии Word (6) директива EditPastSpecial для OLE недоступна. Доступной является директива EditPaste, однако она вклеивает только числа, но не связь. Для решения этой проблемы откройте связь DDE и передайте директиву EditPastSpecial с помощью метода DDEExecute. Аргументы директивы EditPastSpecial объявляют эту связь для таблицы Excel. Проще всего проверить директивы и опции Word, включив Macro Recorder Word и записав все действия. Затем вы можете переместить эти директивы на страницу модуля Excel, добавить к ним объект Word Basic и обрабатывать полученный объект как стандартный. Большинство директив позволяют осуществлять подобные действия, но не EditPasteSpecial, которая недоступна для OLE. Если вы попробуете выполнить оператор, который не работает, то Visual Basic вернет код ошибки, указывающий, какие методы являются недоступными. Примечание: В процедуре LinkToWord обратите внимание на пары кавычек и квадратные скобки. Каждая пара кавычек представляет собой единичные кавычки, передавае¬ мые в строке в Word, что подобно помещению кавычек в литеральную строку. Без этой возможности кавычки отметили бы конец строки вместо того, чтобы заклю¬ чить строку. Квадратные скобки указывают программе Word на то, что передавае¬ мая строка должна быть выполнена как директива, а не вставлена в документ в качестве строки текста. Кроме того, квадратные скобки предотвращают попытки Visual Basic вычислить содержимое директивы Word Basic, что может вызвать синтаксические ошибки или привести к некорректной работе вашей процедуры. Результат работы указанной процедуры подобен изображенному на рис. 26.7, где таблица из Excel была скопирована и связана с документом Word. Примечание: Экспериментируя с объектами OLE и программами, вы можете столкнуться с одной проблемой — разрушением системы. При проверке вашей новой программы вы будете сталкиваться с ее крахом до тех пор, пока не устраните последнюю ошибку. Каждый из этих крахов будет оставлять связь, которая не может быть использо¬ вана, или будет неизвестно что делать. В таком случае дальнейшая работа Windows будет вряд ли возможной, и вам придется перезапустить Windows для устранения этой путаницы. 469
Visual Basic for Applications в примерах Рис. 26.7. Таблица из Excel Сна заднем плане) скопирована и связана с документом Word (на переднем плане) Приме чание: Поскольку связи OLE подвержены ошибкам, вы должны уметь определять, какая из процедур обработки ошибок необходима для предотвращения краха вашей программы из-за ошибок связи OLE (см. гл. 23). Ваша процедура обработки ошибки может попробовать повторно установить связь или завершить процедуру — в зависимости от того, какая директива явилась причиной ошибки. Если вы попытаетесь установить связь повторно, то используйте счетчик, который позволит после определенного количества попыток (например 10) завершить процедуру. В противном случае ваша процедура может выполняться бесконечно, пытаясь уста¬ новить поврежденную связь. Передача нажатий клавиш программам под Windows Последним способом передачи информации другим программам является передача нажатий клавиш. Передача нажатий клавиш используется только тогда, когда нельзя использовать ни DDE, ни OLE. Учтите, что передать нажатия клавиш программе, выполняемой в окне DOS, нельзя. 470
Глава 26. Межпрограммные коммуникации Оператор Send Keys в качестве своих аргументов передает любые символы. Эти символы передаются любой программе, имеющей активное окно; следовательно, перед передачей нажатий клавиш программе ее необходимо активизировать. Для активизации запущенной программы используется оператор AppActivate, за которым следует заголовок окна программы, которое вы хотите переместить на передний план. Если эта программа не запущена, то для ее запуска воспользуйтесь оператором Shell. Оператор AppActivate имеет следующий синтаксис: AppActivate За голов о к_ Окна где Заголовок—Окна является заголовком окна программы. Если у вас есть объектная ссылка на эту программу, то ее нужно использовать для получения заголовка. Так, для активизации программы Project можно использовать следующую конструкцию: AppActivate MSProject.Application.Caption Когда у вас нет объектной ссылки, можно использовать текст — если известен заголовок окна программы. Например, для программы Word используется заголовок ’’Microsoft Word—", за которым следует имя текущего открытого документа. Следова¬ тельно, если вы используете оператор AppActivate "Microsoft Word" то система активизирует первый же запущенный экземпляр Word, независимо от того, какой документ открыт. Если открыт всего один экземпляр Word, то проблем не возникнет; но если одновременно запущены несколько экземпляров Word, то вы должны задавать аргумент более точно, чтобы получить доступ к нужному экземпляру, так как оператор AppActivate сравнивает текст своего аргумента с заголовками открытых окон и активизирует первое окно, у которого заголовок совпал с аргументом. Для того чтобы совпасть со строкой аргумента, заголовок окна программы должен иметь не меньше символов, чем эта строка. Оператор Shell имеет следующий синтаксис: IDoKHa = Shell(путь, Стиль_окна) Аргумент путь содержит путь и имя файла запускаемой программы. Аргумент Стиль—окна является необязательным и определяет способ открытия окна программы. Опции аргумента Стиль_окна приведены в табл. 26.2. 471
Visual Basic for Applications в примерах Таблица 26.2. Допустимые значения аргумента Стиль_окна оператора Shell Код Описание 1, 5, 9 Обычное с фокусом 2 Минимизированное с фокусом 3 Максимизированное с фокусом 4, 8 Обычное без фокуса 6, 7 Минимизированное без фокуса После того как оператор Shell запустит программу, он возвращает номер окна, который может бьггь использован вместо имени окна в качестве аргумента оператора AppActivate. Например, следующий оператор запускает программу Word в максимизированном окне на переднем плане: hWind = Shell ("I:\WINWORD\WINWORD. EXE", 3) Оператор Send Keys имеет следующий синтаксис: SendKeys строка, ждать Аргумент строка содержит нажатия клавиш, которые вы хотите передать програм¬ ме, а аргумент ждать указывает на то, когда эти нажатия клавиш будут переданы. Если вы опустите аргумент ждать или установите его в False, то указанные нажатия клавиш будут помещены в буфер и ваша программа продолжит свое выполнение. Содержимое буфера не будет передано активной программе до тех пор, пока программа не перейдет в состояние ожидания или не будет выполнен оператор DoEvents. Если аргумент ждать установлен в True, то нажатия клавиш будут переданы немедленно. Программа перейдет в состояние ожидания нажатия пользователем командной кнопки или выбора директивы меню. Оператор DoEvents вызывает небольшую паузу в выполнении вашей программы на Visual Basic и передачу управления системе для выполнения необходимых действий, включая передачу нажатий клавиш другой про¬ грамме. Коды неотображаемых клавиш соответствуют кодам, приведенным в табл. 24.2 для метода On Key. Так, приведенная ниже процедура запускает программу Word, активизирует ее и передает ей некоторые нажатия клавиш. Этот способ не совсем обычен для доступа к Word, так как Word позволяет использовать DDE и OLE, являющиеся значительно более эффективными. 472
Глава 26. Межпрограммные коммуникации Option Explicit г ' Передача нажатии клавиш. г Sub SendText() Dim hWind Const normalFocus = 1 hWind = Shell("I:\WINWORD\WINWORD.EXE", normalFocus) 'Запуск Word. Application. Wait Now + TimeValue (’’00 : 00 : 05’’) 'Пауза в 5 секунд. AppActivate hWind 'Активизация окна Word. SendKeys "Доброе утро, мир!{ENTER}" End Sub Оператор Shell запускает Word, а метод Wait позволяет задать небольшую паузу для запуска Word перед тем, как активизировать его оператором AppActivate. Если вы попытаетесь активизировать несуществующее окно, то получите ошибку. После того как данная программа будет активизирована, оператор SendKey передаст ей некоторые символы и клавишу Enter. Для передачи клавиши Enter используется код {ENTER}, как указано в табл. 24.2. После завершения работы процедуры экран Word имеет вид, изображенный на рис. 26.8. “»^il^l[di^¥lewlnser^^^rni3^Tool^^Tiibi^^yVlndowHelp Гр |о» I и I lalBJy] I *1 *1 [а1*| I esi | ив |— | Normal | L ■ » • 1 * I ■ 2 ■ 1*3* i*4' i ■ 5 • i*6' i • 7 • i ■ 8 ■ i ■ 9 ■ i ' 10 • i ■ 11 ■ i ■ 12 ' i • 13 ■ Доброе утро, mhdI ♦ р Page 1 Sec 1 + ♦ I Рис. 26.8. Программа Word была запущена оператором Shell, и ей были переданы нажатия клавиш "Доброе утро, мир/", а также клавиша Enter 473
Visual Basic for Applications в примерах Заключение Эта глава ознакомила вас с межпрограммными коммуникациями. Visual Basic предоставляет три способа организации межпрограммных коммуникаций: применение DDE, применение OLE и передача нажатий клавиш. OLE, или Связь и внедрение объектов, является наиболее мощным способом организации связи с другой програм¬ мой. С помощью OLE вы можете внедрять объекты одной программы в другую программу. После того как вы внедрили в вашу программу объекты из другой программы, вам не нужно знать, как манипулировать этими объектами и отображать их, так как всеми этими действиями будет управлять исходная программа. С помощью OLE Automation ваша программа на Visual Basic может использовать объекты другой программы как свои собственные. DDE, или Динамический обмен данными, является более старым способом связи с другой программой. DDE просто обеспечивает передачу данных из одной программы в другую. В отличие от OLE, DDE передает только данные и предполагает, что обе программы знают, как манипулировать этими данными и отображать их. Простейшим коммуникационным способом является передача нажатий клавиш. Этот способ является однонаправленным и просто имитирует работу пользователя на клавиатуре. Вопросы для повторения Ответы на эти вопросы приведены в приложении А. 1. Объясните разницу между DDE и OLE. 2. Какой вид передачи данных обеспечивает связь DDE? 3. Какой вид передачи данных обеспечивает связь OLE? 4. Как передать данные по каналу DDE? 5. Как передать директивы по каналу DDE? 6. Как передать директивы по каналу OLE? 7. В какой программе вы должны использовать методы CreateObject и GetObject для создания объекта OLE, ссылающегося на документ? 8. В чем отличие между вставкой таблицы в документ Word и внедрением таблицы в документ Word? 474
Глава 26. Межпрограммные коммуникации Упражнения для закрепления материала 1. Напишите процедуру, использующую OLE для внедрения списка задач Project в таблицу Excel. 2. Напишите процедуру, использующую DDE для запуска Word, открытия неболь¬ шого документа и копирования содержимого этого документа в таблицу Excel. 3. Напишите процедуру, использующую DDE для запуска Project и записи в некоторые поля данных из ячеек таблицы Excel. 4. Создайте процедуру, использующую оператор Send Keys для демонстрации другой программы, например Word. Пусть эта процедура запустит Word или другую программу, откроет файл, отредактирует его, а затем закроет файл и завершит программу. Для выбора элементов меню и перемещения по документу вы должны использовать клавиатуру, так как не можете использовать мышь. 5. Напишите процедуру, использующую OLE для вставки в документ Word рисунка плана Project и ри сушка таблицы Excel. 6. Напишите процедуру, использующую OLE для внедрения в документ Word плана Project и таблицы Excel. Поздравляю вас —вы дошли до конца этой книги. Я надеюсь, что вы нашли ее полезной и теперь можете насладиться своим умением управлять компьютером, заставляя его делать то, что вам нужно.
Приложение А Ответы на вопросы для повторения Ответы на вопросы главы 1 1. Для продолжения оператора, занимающего более одной строки, в Visual Basic используется символ подчеркивания, следующий после пробела. 2. Новый макрос можно сохранить в текущей рабочей папке, в любой другой рабочей папке и в глобальном модуле. 3. Абсолютные ссылки всегда указывают на конкретную ячейку, куда бы ни была перемещена или скопирована содержащая их формула. Относительные ссылки указывают местополо¬ жение ячейки относительно ячейки, содержащей эти ссылки. Когда формула, содержащая относительную ссылку, копируется в другую ячейку, позиция ячейки, на которую указывает относительная ссылка в оригинальной формуле, копируется в результирующую формулу. Например, если формула в ячейке С5 содержит относительную ссылку на ячейку F7 (две строки вниз и три колонки вправо) и эта формула будет скопирована в ячейку В2, то результирующая формула будет содержать относительную ссылку на ячейку Е4 (две строки вниз и три колонки вправо). Ответы на вопросы главы 2 1. Объекты содержат данные (свойства) и код, который знает, как манипулировать этими данными (методы). 2. Программный объект представляет собой блок кода и данные. Вы можете получить доступ к данным, используя свойства объекта, и манипулировать этими данными, используя методы объекты. Программный объект часто отображает реальный, физический объект, например кнопку или лист бумаги. 3. Метод является внешней доступной процедурой, оперирующей с данными объекта. Под "внешней доступностью" подразумевается, что другая процедура, находящаяся вне объекта, может выполнить метод. Вы выполняете метод с помощью следующей конструкции: объект.метод 4. Свойство является значением некоторых данных объекта, доступным процедурам вне объекта. Чтобы прочесть значение свойства, необходимо разместить его в правой части формулы, а чтобы изменить значение свойства —в левой части. В обоих случаях доступ к свойству можно получить с помощью следующей конструкции: объект.свойство 477
Visual Basic for Applications в примерах Ответы на вопросы главы 3 1. Набор объединяет все объекты определенного класса. Например, набор Workbooks объе¬ диняет все текущие открытые рабочие папки, а набор Worksheets —все таблицы в открытой рабочей папке. 2. Доступ к объекту набора можно получить, использовав число, определяющее позицию этого объекта в наборе, или строку, содержащую имя объекта. 3. Для определения количества компонентов в наборе используется свойство Count этого набора. 4. Для выбора объекта Range таблицы применяются методы Range, Cells или Offset. 5. Если ячейка В7 является текущей активной ячейкой, а вам нужно получить доступ к ячейке F12, то применяется один из следующих операторов (с соответствующими аргументами): ActiveCell.Cells(5, 6) ActiveCell.Offset(4,5) Ответы на вопросы главы 4 1. Тип данных определяет количество цифр или других данных, которые будут храниться в памяти. Он определяет количество используемых байтов и способ их разбиения на различные части числа. Тип переменной определяет, к какому типу данных принадлежит переменная. 2. Тип данных Currency использует восемь байтов памяти. 3. Область видимости переменной состоит из тех процедур и модулей, в которых имеется возможность доступа к этой переменной. 4. Вы можете заставить себя объявлять все переменные в модуле, поместив в верхнюю его часть оператор Option Explicit. Ответы на вопросы главы 5 1. Оператор присваивания обеспечивает сохранение некоторого значения в области памяти, выделенной для переменной. Он присваивает переменной значение. 2. При вычислении формулы Visual Basic вначале преобразовывает все числа в формуле к наиболее точному типу, а затем производит вычисления. 3. Вы должны объявить объектную переменную в операторе объявления As Object или As Variant. Чтобы присвоить объектной переменной значение, необходимо использовать оператор Set. 4. Большинство операций производят математические действия (сложение, умножение и т.д.). Операция отрицания изменяет знак величины. Операция конкатенации объединяет две строки в одну. 478
Приложение А. Ответы на вопросы для повторения 5. Приоритетом операции называется порядок, определяющий последовательность выполне¬ ния операций. Чтобы изменить порядок вычисления формулы, нужно использовать круглые скобки. Круглые скобки изменяют приоритет операции. 6. Для округления -3.4 до -3 используется функция CInt(). Ответы на вопросы главы 6 1. Процедура является наименьшей из применяемых компьютерных программ, которые вы можете написать на Visual Basic, и в зависимости от решаемой задачи объединяет от одного до нескольких операторов Visual Basic. Прикладная программа может состоять из одной или нескольких процедур —в зависимости от сложности проблемы. 2. В главе 6 описаны три типа процедур: общие процедуры, командные процедуры и процедуры обработки событий. Общие процедуры решают задачи, но не модифицируют ячейки таблицы. Командные процедуры также решают задачи, но их целью является модификация таблицы. Командные процедуры представляют собой записанные действия с таблицей. Процедуры обработки событий являются общими процедурами, выполняемыми при наступлении некоторого события, например нажатия командной кнопки. ’ 3. Процедура должна начинаться оператором Sub и оканчиваться оператором End Sub. 4. Макрорекордер создает командные процедуры. 5. Вызовом процедуры является действие, передающее процедуре управление для ее выпол¬ нения. Вы вызываете процедуру из другой процедуры путем записи ее имени и, при необходимости —некоторых аргументов в виде отдельного, оператора. 6. Сохранить значения переменных от одного вызова процедуры до другого можно, поместив зарезервированное слово Static в операторы объявления этих переменных. 7. Чтобы сделать процедуру невидимой из других модулей, необходимо поместить зарезерви¬ рованное слово Private в оператор начала этой процедуры. 8. При передаче аргументов адресом вы передаете процедуре адрес области памяти, в которой сохранена эта переменная, что позволяет процедуре изменять значение переменной. 9. При передаче аргументов значением вы передаете процедуре только значение указанной переменной, сохраняя саму переменную от любых изменений со стороны процедуры. Этот способ позволяет защитить значение переменной от несанкционированных изменений. 10. Для передачи аргумента адресом никаких специальных действий не требуется, так как этот метод передачи величин применяется по умолчанию. Чтобы передать величину значением, необходимо перед именем переменной в процедуре поместить зарезервированное слово ByVai или заключить имя переменной в вызове процедуры в круглые скобки. И. Для вызова процедуры из другой рабочей папки необходимо подключить эту папку к текущей рабочей папке директивой Tools/References, а затем указать имя этой папки, заключенное в квадратные скобки, перед именем вызываемой процедуры. 479
Ответы на вопросы главы 7 1. Функция выполняет те же действия, что и процедура, но возвращает полученное значение в своем имени. Это позволяет использовать функцию в формуле. 2. Тип данных в*функции устанавливается предложением As Туре в правой части оператора начала функции. 3. Перед тем как завершить работу функции, необходимо оператором присваивания присвоить полученное значение имени функции. Это значение и возвратит функция. 4. Чтобы выполнить процедуру в окне Debug, нужно переключиться на панель Immediate и записать на ней оператор Run ( ”имя_процедуры") где имя—процедуры —имя той процедуры, которую вы хотите выполнить. 5. Функции, которые предполагается использовать в таблице, должны только возвращать значения и не должны изменять содержимое других ячеек таблицы. Ответы на вопросы главы 8 1. Компьютер отличается от простого калькулятора тем, что предоставляет возможность принимать решения и на основании этих решений изменять последовательность вычислений. 2. Последовательность выполнения программы определяет список последовательно выполняе¬ мых операторов. 3. Составной оператор объединяет в одной строке два или более основных оператора, разделенных двоеточием. 4. Двоичное сравнение различает прописные и строчные буквы, а символьное сравнение — нет. По умолчанию применяется двоичное сравнение. 5. Результаты указанных операций сравнения: 5 >= 5 True 9 < 7 False 27.8 = 27.85 False 13.5 <> 11.3 True "X" = "х" (двоичное сравнение) False "Y" = "у” (символьное сравнение) True > "а" (двоичное сравнение) False "Pat” <= ’’Wilbur" (двоичное сравнение) True 480
Приложение А. Ответы на вопросы для повторения Ответы на вопросы главы 9 1. Передача функции или процедуре дополнительного аргумента проверяется путем вызова функции IsMissingO. 2. Использование в процедурах констант (вроде DefaultRate) вместо литеральных значений делает текст процедуры более очевидным и читабельным. 3. В блочной структуре If можно использовать любое необходимое количество условий Elself. 4. В блочной структуре If можно использовать только одно условие Else. Ответы на вопросы главы 10 1. Указанный оператор Case является недопустимым, так как использовать операцию Is Like запрещено. 2. С учетом указанных операторов Case если Value = 13, то выполняется В1оск2, а если Value = 17, то Block 1. 3. Перечисленные значения определяют список выборов, причем каждому выбору соответ¬ ствует числовое 'значение. Это позволяет легко осуществлять выбор, применяя указанное числовое значение в операторе Select Case. 4. Блок кода, следующий за оператором Case Else, выполняется в том случае, когда не был выполнен ни один из блоков кода, следующих за другими операторами Case. 5. Константа Excel, возвращающая ошибку #NUM! в функции CVErrO, имеет имя xIErrNum. Ответы на вопросы главы 11 1. Вам следует выбрать структурированный переход, так как его лег<е понять и отладить. Ответы на вопросы главы 12 1. Вычисляемый цикл —это структура, которая повторяется заданное количество раз. Цикл For-Next является вычисляемым циклом. 2. Оператор Exit For вызывает немедленное прерывание цикла For-Next. 3. Если опущены зарезервированное слово Step и величина шаг, то по умолчанию величина шага цикла принимается равной 1. 4. Обычно не следует изменять переменную цикла в блоке кода цикла, и ситуаций, в которых это изменение будет логически обоснованно, практически не существует. Единственной ситуацией, позволяющей изменить величину переменной цикла, является обнаружение ошибки, которую можно исправить программным путем. В этом случае можно попробовать повторить последнюю итерацию цикла с корректным значением. 16 Вильям Дж. Орвис 481
Visual Basic for Applications в примерах Ответы на вопросы главы 13 1. Логически прерываемый цикл —это логическая структура, которая повторяется до тех пор, пока выполняется некоторое логическое условие. 2. Зарезервированные слова While и Until определяют способ проверки условия при принятии решения о выполнении цикла. Зарезервированное слово While указывает на то, что цикл будет выполняться до тех пор, пока условие принимает значение True, а зарезервированное слово Until — на то, что цикл будет выполняться до тех пор, пока условие принимает значение False. 3. Условие, записанное в конце цикла, определяет, что цикл всегда выполнится хотя бы один раз, а условие, записанное в начале, —что цикл может не выполниться ни разу. Ответы на вопросы главы 14 1. Цикл объектного типа —это циклическая структура, которая выполняется по одному разу для каждого объекта набора. 2. Если цикл For Each был применен к набору объектов, то переменная цикла содержит объект, определяемый итерацией цикла. 3. Если цикл For Each был применен к единичному объекту, то он будет вычисляться один раз. 4. Использовать цикл For Each для изменения элемента массива нельзя, так как переменная цикла содержит только значение элемента массива, а не сам элемент. Ответы на вопросы главы 15 1. Если вы нажмете командную кнопку ОК, то функция MsgBoxO возвратит число 1. 2. Чтобы диалоговое окно, создаваемое функцией MsgBoxO, имело командные кнопки Yes и No, применяется константа vbYesNo, равная 4. 3. Чтобы отобразить число с помощью функции MsgBoxO, необходимо вначале преобразовать его в строку, а затем использовать эту строку в функции. 4. Для изменения заголовка диалогового окна нужно указать этот новый заголовок в качестве значения аргумента Title. 5. Модальным является диалоговое окно, которое должно быть закрыто прежде, чем можно будет продолжить работу с программой. 6. Системное модальное диалоговое окно не позволит делать что-либо до тех пор, пока не будет закрыто. Программное диалоговое окно нужно закрыть, чтобы продолжить работу именно с той программой, которая отобразила это диалоговое окно. 482
Приложение А. Ответы на вопросы для повторения Ответы на вопросы главы 16 1. Называть ячейки таблицы и использовать эти имена в процедурах на Visual Basic вместо использования ссылок на ячейки нужно для того, чтобы эти процедуры работали без изменений, если вы переместите ячейки. 2. Сохранение данных во внутреннем массиве ускоряет время доступа, но ограничивает объем данных, а сохранение данных в дисковом файле замедляет время доступа, но практически не ограничивает объем данных. 3. Оператор Option Base 1, помещенный в верхней части процедуры, определяет, что все массивы, объявленные без точного указания нижнего предела индекса, будут иметь в качестве нижнего предела индекса значение 1, а не 0 (по умолчанию). 4. Чтобы процедура запускалась при каждом открытии рабочей папки, она должна называться Auto_Open. 5. Для того чтобы выбрать в таблице командную кнопку без выполнения подключенной к ней процедуре, используйте либо средство выбора из пиктографического меню Drawing либо нажмите и удерживайте нажатой клавишу Ctrl при выборе командной кнопки. Ответы на вопросы главы 17 1. Чтобы отобразить диалоговое окно пользователя в Excel, применяйте метод Show. В Project для этих целей используется оператор Form.. 2. Открыть новую страницу диалога можно с помощью директивы Insert/Macro/ Dialog. 3. Новый чистый бланк на странице диалога исходно имеет командные кнопки ОК и Cancel. 4. Метка отображает на бланке текст, который пользователь не может редактировать. Текст, отображаемый в окне редактирования, может быть отредактирован пользователем. 5. Групповое окно применяется для визуального объединения частей бланка в группы и для определения элементов группы клавиш выбора. 6. Кнопки выбора позволяют выбрать только один из многих вариантов опций, а контроль¬ ные индикаторы позволяют выбрать независимые опции. Пользователь может выбрать только одну кнопку выбора в группе, но любое количество контрольных индикаторов одновременно. 7. В отличие от спиннеров линейки прокрутки имеют ползунок, позволяющий быстро пере¬ мещаться от одного конца диапазона до другого и затем указывают текущее положение ползунка в диапазоне. 8. Командная кнопка, у которой установлено свойство Default, автоматически считается нажатой, если пользователь нажал клавишу Enter. Командная кнопка, у которой установлено свойство Cancel, автоматически считается нажатой, если пользователь нажал клавишу Esc. 483
Visual Basic for Applications в примерах 9. Порядок табуляторов определяет порядок выбора объектов бланка при нажатии пользова¬ телем клавиши Tab. Клавиша Tab применяется для перемещения от поля к полю при вводе данных в бланк. Ответы на вопросы главы 18 1. Функция FreeFile возвращает следующее допустимое файловое число для использования в операторе Open. 2. Данные записываются в последовательный файл в виде серий текстовых символов, начиная от начала файла и вплоть до его конца. 3. Спецификатор Append аргумента режим оператора Open указывает на необходимость переместить файловый указатель в конец файла, чтобы любые данные, записываемые в файл, добавлялись к уже существующим без необходимости переписывать последние. 4. Для того чтобы открыть файл для записи данных, используется оператор Open со спецификаторами Output или Append. Чтобы открыть файл для чтения, используется оператор Open со спецификатором Input. 5. После того как использование файла завершается, этот файл должен быть закрыт опера¬ тором Close. 6. Оператор Print пишет текст в файл в том виде, в котором его удобно читать пользователю, а оператор Write, заключая текст в кавычки и отделяя элементы строки запятыми, преобразо¬ вывает файл в форму, удобную для чтения программой. 7. Оператор Input читает в отдельные переменные различные элементы строки файла, а оператор Line Input —всю строку целиком в одну строковую переменную. 8. Последовательный файл читается и пишется одним потоком символов от его начала и до конца. Файл произвольного доступа читается и пишется блоками постоянной длины в любом порядке. Ответы на вопросы главы 19 1. Файл произвольного доступа открывается оператором Open с предложением For Random. 2. Спецификатор Len = определяет в операторе Open длину записи (в байтах) файла произвольного доступа. 3. Для чтения и записи данных в файлы произвольного доступа применяются операторы Get и Put. 4. Если вы читаете запись из файла произвольного доступа, то сохранять ее на том же самом месте не требуется. 5. Переменная типа запись —это структура данных, предназначенная для сохранения данных в записи файла произвольного доступа. Эта структура объединяет в одной записи файла все отдельные значения, которые вы предполагаете сохранить. 484
Приложение А. Ответы на вопросы для повторения 6. Индексированная запись —это метод сохранения данных в файле произвольного доступа. Согласно этому методу при реорганизации данных в файле физическое перемещение записей не требуется, так как порядок записей в файле контролируется массивом индексных значений, что позволяет упорядочивать только этот массив. Ответы на вопросы главы 20 1. Добавить новую таблицу в активную рабочую папку позволяет применение метода Add к набору Worksheets. 2. Для переименования таблицы необходимо изменить свойство Name соответствующего объекта-таблицы. 3. Чтобы открыть существующую таблицу, зная ее имя, следует применить метод Open к набору Worksheets. 4. Чтобы открыть существующую рабочую папку с именем, известным пользователю, нужно методом GetOpenFilename отобразить диалоговое окно File Open, позволяющее пользова¬ телю выбрать и открыть файл. 5. Если активным объектом Range является ячейка К13, то метод Cells(5,7) указывает на ячейку Q17. Ответы на вопросы главы 21 1. Синтаксическая ошибка —это ошибка в записи команды или в типе переменной в операторе присваивания. В большинстве случаев к синтаксическим ошибкам приводят потерянные знаки пунктуации или ключевые слова, а также применение переменной некорректного типа в формуле или в вызове функции. Большинство синтаксических ошибок обнаруживаются сразу после окончания записи оператора. Остальные ошибки проявляются при первом запуске процедуры после ее редактирования (при преобразовании ее в р-код). 2. К ошибкам выполнения относятся все ошибки, которые невозможно обнаружить при проверке синтаксиса единичного оператора. Оги ошибки вызываются некорректными значениями пере¬ менных, участвующих в вычислениях (попытка деления на 0 или извлечения квадратного корня из отрицательного числа), или проблемами с именами файлов, путями или дисковыми накопителями. Ошибки возникают в процессе выполнения программы. 3. Логическая ошибка — это когда ваша программа работает не так, как вы предполагали. Логические ошибки возникают в выполняющейся программе, но не обязательно приводят к ее краху: программа просто может делать не то, что вы от нее ожидаете. 4. Отладка —это искусство обнаружения и устранения ошибок в программе. 5. Окно Procedure отображает имя текущей процедуры. Если вы нажмете кнопку справа от этого окна, то отобразится список всех текущих активных процедур. 6. Когда вы выбираете мгновенно отслеживаемую переменную, ее текущее значение отобра¬ жается в диалоговом окне. Прежде чем продолжить работу с программой, вы должны закрыть это окно. Отслеживаемая переменная отображается на панели Watch окна Debug, и ее текущее значение продолжает отображаться до тех пор, пока программа выполняется. 485
Visual Basic for Applications в примерах Ответы на вопросы главы 22 1. Режим прерывания —это останов выполнения программы, вызванный ошибкой, операто¬ ром Stop или нажатием клавиш Ctrl-Break. Режим прерывания является просто паузой в выполнении программы, и все значения переменных на момент перехода программы в этот режим сохраняются. 2. Контрольная точка маркирует оператор в программе; когда программа доходит до этого оператора, то она переходит в режим прерывания. 3. Отслеживаемое выражение отображает значение выражения или переменной на панели Watch окна Debug. Точка слежения является логическим выражением или значением, которое переводит программу в режим прерывания, если логическое выражение получает значение True или указанное значение изменится. 4. Оператор Stop переводит программу в режим прерывания. 5. Вы можете разменять доллар 40 различными способами. 6. Контрольная точка удаляется путем выбора маркированного оператора и указания дирек¬ тивы Run/Toggle Breakpoint. Директива Run/Clear All Breakpoints удаляет все контроль¬ ные точки. 7. Обе директивы выполняют один оператор, после чего возвращают программу в режим прерывания, но директива Step Into следует за ходом выполнения от процедуры к процедуре, а директива Step Over выполняется в рамках одной процедуры, рассматривая вызов процедуры как один оператор. Ответы на вопросы главы 23 1. Вы разрешаете в процедуре прерывание по ошибке, поместив в нее оператор On Error GoTo. 2. Обработчик ошибок состоит из трех частей — заголовка, тела обработчика и концовки. Заголовок содержит метку начала обработчика, указанную в операторе On Error. Тело обработчика содержит код, обрабатывающий ошибки, а концовка — оператор Resume, возвращающий управление программе. 3. Функция Егг возвращает код последней ошибки, а функция ЕггогО —текстовое описание ошибки, заданное кодом в качестве ее аргумента. 4. Вы помещаете оператор Exit Sub или Exit Function между нормальной процедурой и обработчиком ошибок, чтобы быть уверенным в том, что обработчик ошибок не будет случайно инициирован нормально выполняющейся программой. 5. Чтобы вернуть ошибку в систему, используется оператор Error с кодом этой ошибки в качестве аргумента. Обратите внимание на то, что оператор Error отличается от функции ЕггогО. 6. Прерывание по ошибке отключает следующие события: выполнение оператора On Error GoTo 0, ошибка и завершение процедуры. 486
Приложение А. Ответы на вопросы для повторения 7. Пользовательские прерывания по нажатии клавиш Ctrl-Break можно запретить, присвоив свойству EnableCancelKey объекта Application значение константы xlDisabled. Ответы на вопросы главы 24 1. Объектами, составляющими меню, являются строка меню, меню и элемент меню. 2. Пет, подключить процедуру к меню невозможно. Ее можно подключить лишь к элементу меню. 3. Контекстное меню — это меню, появляющееся в результате щелчка на объекте правой кнопкой мыши. 4. Чтобы активизировать Button Image Editor необходимо выполнить следующее: а) вначале убедитесь, что пиктограмма, которую вы хотите редактировать, отображается на одном из пиктографических меню; б) укажите директиву View/ToolBars/Customize для отображения диалогового окна Customize; в) после того как будут отображены и диалоговое окно Customize, и пиктографическое меню, содержащее подлежащую редактированию пиктограмму, щелкните на указанной программе правой кнопкой мыши. Появится контекстное меню Buttons; г) выберите из контекстного меню директиву Edit Button Image. Появится диалоговое окно Button Editor; д) отредактируйте изображение пиктограммы, для чего вначале выберите цвет, а затем заполните этим цветом нужные зоны изображения; е) после завершения редактирования изображения нажмите командную кнопку ОК. Новое изображение пиктограммы появится в пиктографическом меню взамен старого; 5. Чтобы создать пользовательскую строку меню, вначале необходимо применить к набору MenuBars метод Add, указав имя новой строки меню, например: MenuBars.Add Name:="МуВаг2" затем добавить заголовки меню для данной строки, применив к набору Menus метод Add: MenuBars("MyBar2").Menus.Add Caption:=”&Директивы" И наконец, чтобы отобразить новую строку меню, используйте метод Activate: Application.MenuBars("MyBar2").Activate 487
Visual Basic for Applications в примерах Ответы на вопросы главы 25 1. Пользовательский объект помещается в модуле. 2. Процедура Property Let создает функцию, изменяющую значение свойства пользователь¬ ского объекта. 3. Процедура Property Get создает функцию, возвращающую значение свойства пользователь¬ ского объекта. 4. Процедура Property Set подобна процедуре Property Let, но используется в тех случаях, когда свойство является скорее объектом, чем числовой величиной. 5. Чтобы вернуть из процедуры ссылку на объект, используется процедура Property Get с установленным типом процедуры Object или Variant. Проверьте, что для присвоения возвращаемого объекта переменной типа Object или Variant вы использовали оператор Set. 6. Чтобы создать метод для пользовательского объекта, просто создайте в модуле, содержащем этот объект, процедуру Public Sub с именем метода. Ответы на вопросы главы 26 1. DDE обеспечивает связь между двумя программами, поддерживая передачу данных между ними, a OLE —поддерживая передачу объектов между ними. 2. Связь DDE обеспечивает передачу данных только между двумя программами. При исполь¬ зовании совместно с Visual Basic for Applications связь DDE позволяет передавать только текст или числа. Другие программы могут передавать более сложные объекты, например изображения. 3. Связь OLE позволяет передавать объекты между двумя программами; следовательно, для редактирования связанного объекта вы можете использовать его свойства и методы. 4. Для передачи данных серверу по каналу DDE используется метод DDEPok, для получения данных от сервера —метод DDERequest. 5. Для передачи директив по каналу DDE используется метод DDEExecute. 6. Передать директивы по каналу DDE нельзя. Можно непосредственно использовать методы связанной программы. 7. В Word 6.0. Вы должны использовать методы CreateObject и GetObject для создания объекта OLE, ссылающегося на документы, так как Word 6.0 не является полностью объектно-ориентированной программой. 8. Если вы вставляете таблицу в документ Word, то вставляете только текст этой таблицы. Редактируя эту таблицу, вы изменяете только ее текст. Если вы внедряете таблицу в документ Word, то вставляете в этот документ объект Range, содержащий эту таблицу. Если вы редактируете эту таблицу, то управление редактированием, включая пересчет ячеек и форматирование результатов, берет на себя Excel. 488
Приложение В Таблица кодов ANSI Данная таблица содержит список кодов, используемых при хранении символов в памяти компьютера или в файлах. Таблица кодов ANSI (American National Standards Institute) содержит расширенный набор кодов ASCII (American Standard Codes for Information Interchange). Первые 128 кодов ASCII были разработаны для телетайпных коммуникаций. Первые 32 кода — управляющие, хотя только четыре из них используются в программах под Windows. Коды от 32 до 127 принадлежат обычным алфавитно-цифровым символам английского алфавита, специальным символам и знакам операций. Коды от 128 и до 255 принадлежат дополнительному набору символов. Обратите внимание, на то, что дополнительные символы, используемые программами под MS-DOS, отличаются от дополнительных символов, используемых программами под Windows. Коды в таблице расположены построчно слева направо,, сверху вниз. 1 и $ % & 1 ( ) * + > - / 0 1 2 3 4 5 б 7 8 9 > < = > ? @ А В С D Е F G и [ J К L М N о Р Q R S Т и V W X Y Z [ \ ] * 4 а b с d е f б h 1 J к 1 m п 0 Р q г S t и V W X У Z { 1 } - ъ Г г г ... г £ %о Л> < № К 7! и ь • •• • - УЬ > 1Ь к h Ц У у J Я г § Ё © € ч - - ■ I * I i Г т ё № е j S S 1 А Б в г д Е Ж 3 И й К л М Н о п р с Т У ф X ц Ч Ш ш Ъ ы Ь э Ю Я а 6 в г д е ж 3 и й к л м н 0 п р с т У ф X ц ч ш щ ъ ы ь э ю я 489
Приложение С Коды ошибок Visual Basic Если в программе обнаруживается ошибка и не существует доступного обработчика ошибок (см. гл. 23), то отображается диалоговое окно Macro Error, содержащее код ошибки Visual Basic и сообщение об этой ошибке. Функция Err возвращает код последней ошибки, обнаруженной в программе. Функция Error() возвращает сообще¬ ние об ошибке для любого кода ошибки, заданного в качестве ее аргумента. Исклю¬ чение составляют коды от 1000 до 1006, которые в данной версии Visual Basic не возвращают сообщений об ошибке. Код ошибки Сообщение об ошибке и причина 3 Return Without GoSub (Return без GoSub) Процедура пытается использовать оператор Return для возврата из под¬ программы, для перехода на которую не был использован оператор GoSub. 5 Invalid procedure call (Недопустимый вызов процедуры) Эта ошибка обычно происходит из-за недопустимого числа, использованного в качестве аргумента числовой функции или некорректной опции в вызове процедуры. 6 Overflow (Переполнение) В процессе вычислений получено число, которое превышает максимально допустимое для переменной данного типа. 7 Out of memory (Нехватка памяти) Вашей программе требуется больше памяти, чем это возможно. Попробуйте уменьшить объем требуемой памяти за счет освобождения переменных (осо¬ бенно массивов и строк), которые больше не используются. 9 Subscript out of range (Индекс вне диапазона) Для выбора элемента массива вы использовали индекс, который либо больше, либо меньше номеров элементов, заданных при объявлении данного массива. 490
Приложение С. Коды ошибок Visual Basic Код ошибки Сообщение об ошибке и причина 10 Duplicate definition (Повторное определение) Вы объявили переменную, которая уже объявлена в данной процедуре. Для переобъявления переменной типа массив можете использовать оператор ReDim. И Division by zero (Деление на 0) В операции деления делителем является 0, что недопустимо. 12 Precision lost converting Variant (Потеря точности при преобразовании переменной типа Variant) Число, записанное в переменной типа Variant, имеет точность, не позво¬ ляющую корректно преобразовать его в числовой тип. 13 Type mismatch (Несовпадение типов) Вы пытаетесь присвоить переменной значение неправильного типа. Вы можете преобразовать большинство чисел, следовательно, эта ошибка обычно является результатом попытки сохранить число в строковой пере¬ менной, строку в числовой переменной, что-либо кроме объекта в объектной переменной или объект в переменной необъектного типа. В переменной типа Variant можно сохранить любую величину. Кроме того, эта ошибка по¬ является в результате вызова функции с аргументом некорректного типа. 14 Out of string space (Нехватка пространства для строки) Невозможно разместить строку, так как она слишком велика или потому, что вашей программе не хватает памяти. Чтобы уменьшать объем памяти, по¬ пробуйте освободить некоторые переменные, которые уже не используются. 16 String expression too complex (Строковое выражение слишком сложное) Строки, возвращаемые функциями, и записываемые непосредственно в стро¬ ковую формулу, сохраняются во временных строках. Эта ошибка возникает тогда, когда таких строк слишком много. Попробуйте сохранять строки в переменных и использовать в формуле эти переменные. 17 Can't perform requested operation (Невозможно выполнить требуемое действие) Ошибка возникает в режиме прерывания при попытке изменения, делающей текущее состояние программы некорректным. Чтобы сделать это изменение, завершите программу, внесите изменения, а затем перезапустите программу. 491
Visual Basic for Applications в примерах Код ошибки 18 20 28 35 47 48 49 51 Сообщение об ошибке и причина User interrupt occurred (Пользовательское прерывание) Пользователь нажал клавиши Ctrl-Break. Эта ошибка используется для обработки пользовательских прерываний. Resume without error (Resume без ошибки) В программе встретился оператор Resume, но не обнаружено ошибки. Эта ошибка обычно указывает на то, что управление случайно перешло на обработчик ошибок. Out of stack space (Нехватка стека) Стек используется для хранения адресов возврата и локальных переменных при вызовах процедур. Объявите переменные на уровне модуля и уменьшите вложенность вызываемых процедур. Sub or Function not Defined (Процедура или функция не определена) Вы вызвали процедуру, которая не определена: Эта ошибка обычно возникает при опечатке в имени переменной или некорректном применении круглых скобок. Too many DLL application clients (Слишком много клиентов DLL) Слишком много программ пытаются использовать одну DLL. Error in loading DLL (Ошибка при загрузке DLL) DLL не может быть загружена, вероятно потому, что вы некорректно объявили ее или использовали ошибочное имя файла. Bad DLL calling convention (Ошибочное соглашение при вызове DLL) Вы использовали некорректные аргументы в вызове DLL. Проверьте корректность объявления состояния процедуры и типов переда¬ ваемых величин. Internal error (Внутренняя ошибка) При попытке доступа к файлу что-то не сработало. Попробуйте повторить доступ к этому файлу. 492
Приложение С. Коды ошибок Visual Basic Код ошибки 52 53 54 55 57 58 59 61 Сообщение об ошибке и причина Bad file name or number (Ошибочное имя файла или файловое число) Вы использовали ошибочное имя файла или файловое число, которое не было объявлено в операторе Open. Проверьте это число и символы в имени файла. File not found (Файл не обнаружен) Файл, указанный в операторах Open, Kill или Name, не существует. Вероятно, в имени файла опечатка или задана некорректная директория. Bad file mode (Ошибочный режим) Вы пытаетесь писать в файл, открытый для чтения, или читать из файла, открытого для записи, либо применяете операторы Get или Put для по¬ следовательного файла. File already open (Файл уже открыт) Вы пытаетесь открыть уже открытый файл. Эта ошибка появляется также после перезапуска программы, которая не закрыла свои файлы. Чтобы закрыть все открытые файлы, запишите на панели Immediate окна Debug оператор Close. Device I/O error (Ошибка ввода/вывода) Устройство сообщает о проблеме. Попробуйте повторить действия. Эта проблема вызвана работой технических средств. File already exists (Файл уже существует) Вы пытаетесь создать новый файл, но уже существует файл с таким же именем. Bad record length (Ошибочная длина записи) Переменная типа запись, которую вы используете для файла произвольного доступа, имеет большую длину, чем длина записи, объявленная в операторе Open. Очевидно, вы не учли двухбайтные спецификаторы для всех пе¬ ременных типа Variant и строк переменной длины. Disk full (Диск заполнен) Диск, на который вы пытаетесь записать данные, уже заполнен. Попробуйте другой диск или удалите с этого диска некоторые файлы. 493
Visual Basic for Applications в примерах Код ошибки 62 63 67 68 70 71 74 75 76 Сообщение об ошибке и причина Input past end of file (Прочитан конец файла) Вы пытаетесь читать после того, как была прочитана последняя запись файла. Если вам неизвестно количество записей в файле, то используйте функцию EOF() для проверки конца файла перед попыткой прочесть запись. Bad record number (Ошибочный номер записи) Вы использовали отрицательный номер записи. Too many files (Слишком много файлов) Вы открыли слишком много файлов одновременно или каталог данного диска заполнен до отказа. Закройте некоторые файлы или добавьте больше фай¬ ловых буферов в файл CONFIG.SYS. Device unavailable (Устройство недоступно) Заданный дисковод или другое устройство недоступны. Ошибка обычно воз¬ никает потому, что из сменного накопителя был удален носитель информации. Permission denied (Отказ в доступе) Вы пытаетесь записать в файл, защищенный от записи. Disk not ready (Дисковод не готов) В дисководе нет дискеты или не закрыта дверца дисковода. Can't rename with different drive (Невозможно переименовать с другим дисководом) Вы пытаетесь переименовать файл с другой буквой дисковода в новом имени файла. Если вам нужно переместить файл на другой дисковод, то скопируйте его. Path/File access error (Ошибка доступа к файлу) Некорректное имя файла или путь. Проверьте параметры доступа. Path not found (Несуществующий путь) Задан несуществующий путь. Проверьте аргументы доступа. 494
Приложение С. Коды ошибок Visual Basic Код ошибки Сообщение об ошибке и причина 91 Object variable not Set (Объектная переменная не задана) Вы пытаетесь использовать необъявленную объектную переменную. 92 For loop not initialized (Цикл For не инициализирован), Вы перескочили в середину цикла For-Next и пытаетесь выполнить оператор Next без инициализации оператора For. 93 Invalid pattern string (Ошибочный шаблон) Вы использовали в операторе Like ошибочную шаблонную строку. Проверьте синтаксис оператора. 94 Invalid use of Null (Ошибочное использование Null) Вы пытаетесь присвоить значение переменной или формуле, имеющей зна¬ чение Null. Вероятно, вы использовали неинициализированную переменную типа Variant. 323 Can't load module; invalid format (Невозможно загрузить модуль; ошибочный формат) Вы не можете сохранить указанный модуль Visual Basic как текст. Некоторые версии Visual Basic сохраняют модули в двоичном формате. Пе¬ резапишите модуль в текстовом формате и попробуйте еще раз. 423 Property or method not found (Свойство или метод не существуют) Объект не имеет указанного свойства или метода. Проверьте синтаксис или используйте Object Browser для проверки существующих свойств и методов данного объекта. 424 Object required (Требуется объект) Вы использовали число или строку вместо объекта. 430 Class doesn't support OLE Automation (Класс не поддерживает OLE Automation) Вы подключились к программе, имеющей класс переменных, который не поддерживает OLE Automation. Проверьте документацию на эту программу. 495
Visual Basic for Applications в примерах Код ошибки 438 440 445 446 447 448 449 450 Сообщение об ошибке и причина Object doesn't support this property or method (Объект не поддерживает это свойство или метод) Объект не имеет указанного свойства или метода. Проверьте синтаксис или используйте Object Browser для проверки существующих методов и свойств. OLE Automation error (Ошибка OLE Automation) Директива OLE Automation не работает, и программа, которой вы пытаетесь управлять, вернула этот код ошибки. Существует множество причин этой ошибки. Переключитесь на эту программу и проверьте,не отображает ли она сообщения об ошибке. Object doesn't support this action (Объект не поддерживает это действие) Объект не имеет указанного свойства или метода. Проверьте синтаксис или используйте Object Browser для проверки существующих методов и свойств. Object doesn't support named arguments (Объект не поддерживает поименованные аргументы) Данный объект не поддерживает поименованные аргументы. Используйте позиционные аргументы для доступа к этому объекту. Object doesn't support current locale setting (Объект не поддерживает текущую локальную установку) Установка языка для этого объекта отличается от использованной в вашей программе. Проверьте документацию на данный объект. Named argument not found (Поименованный аргумент не существует) Вы использовали поименованный аргумент, который для данного объекта не существует. Проверьте синтаксис и убедитесь, что вы использовали кор¬ ректную библиотеку, так как объекты в разных библиотеках часто имеют одно и то же имя. Argument not optional (Обязательный аргумент) Вы опустили обязательный аргумент. Wrong number of arguments (Ошибочное количество аргументов) Вы использовали слишком много или слишком мало аргументов. 496
Приложение С. Коды ошибок Visual Basic Код ошибки Сообщение об ошибке и причина 451 Object not a collection (Объекта нет в наборе) Вы пытаетесь выбрать компонент набора, но объекта, к которому вы пытаетесь получить доступ, нет. Это ошибка, вероятно, явилась следствием орфогра¬ фической ошибки, так как имя объекта и имя включающего его набора часто отличаются всего одной буквой, как, например, объект Sheet и набор Sheets. 452 Invalid ordinal (Ошибочный порядковый номер) При выборе процедуры из DLL вы пытаетесь использовать индексное число, которое не указывает ни на одну процедуру в этой библиотеке. Проверьте порядковый нбмер или вызовите процедуру по имени. 453 Function not defined in specified DLL (В заданной DLL эта функция не определена) Вы ссылаетесь на функцию из библиотеки DLL, однако в данной библиотеке такой функции нет. Проверьте правильность написания имени функции и имени библиотеки. 454 Code resource not found (Указанного программного ресурса не существует) В программах для компьютеров Macintosh вы задали программный ресурс, который эта программа не может найти. Проверьте синтаксис. 455 Code resource lock error (Ошибка при захвате программного ресурса) В программах для компьютеров Macintosh вы пытаетесь захватить программный ресурс, который не может быть захвачен. Проверьте синтаксис и документацию. 1000 Classname does not have propertyname property (Класс не имеет данного свойства) Объект не имеет указанного свойства. Проверьте синтаксис или используйте Object Browser для проверки существующих методов и свойств. 1001 Classname does not have methodname method (Класс не имеет данного метода) Объект не имеет указанного метода. Проверьте синтаксис или используйте Object Browser для проверки существующих методов и свойств. 1002 Missing required argument argumentname (Отсутствует обязательный аргумент имя__аргумента) Отсутствует обязательный аргумент с указанным именем. 497
Visual Basic for Applications в примерах Код ошибки Сообщение об ошибке и причина 1003 Invalid number of arguments (Ошибочное количество аргументов) В вызове процедуры задано ошибочное количество аргументов. 1004, Methodname method of classname class failed (Метод имя метода класса имя класса завершился ошибкой) Указанный метод ошибочно завершился. Обычно это происходит потому, что вы выбрали из набора несуществующий компонент, а затем попытались применить к этому компоненту указанный метод; или потому, что объект не содержит данных. 1005 Unable to set the propertyname property of the classname class (Невозможно установить свойство имя свойства для класса имя класса) Попытка установить значение указанного свойства завершилась ошибкой. Обычно это происходит потому, что вы выбрали из набора несуществующий компонент, а затем попытались установить свойство данного компонента; или потому, что объект не содержит данных. 1006 Unable to get the propertyname property of the classname class (Невозможно получить свойство имя свойства класса имя класса) Попытка получить значение указанного свойства завершилась ошибкой. Обычно это происходит от того, что вы выбрали из набора несуществующий компонент, а затем попытались получить значение свойства данного ком¬ понента; или потому, что объект не содержит данных.
Глоссарий Absolute reference (абсолютная ссылка). Ссылка на определенную ячейку таблицы, оста¬ ющаяся неизменной при копировании формулы. См. также относительная ссылка. Active call chain (активная цепочка вызовов). Список всех процедур программы, не за¬ вершивших выполнение и ожидающих возврата управления от вызванной подпрограммы, включая процедуру, активную на момент останова программы. Этот список называется це¬ почкой, так как одна процедура вызывает другую, та вызывает следующую и т.д., формируя таким образом цепочку от первой выполняющейся процедуры и до текущей процедуры. Active object (активный объект). Верхняя страница рабочей папки или таблицы, активная ячейка объекта Range или выбранный графический объект. Assignment statement (оператор присваивания). Оператор, состоящий из переменной, рас¬ положенной слева, знака равенства и формулы, расположенной справа. Результат вычисления формулы присваивается (или сохраняется в) этой переменной. Biased integer (смещенное целое). Целое число, которое может принимать положительные и отрицательные значения. Bit (бит). Минимальный элемент памяти компьютера. Может принимать значения 0 или 1. Block of code (блок кода). См. программный блок. Boolean. Тип данных, позволяющий хранить логические значения True или False. Bug (ошибка). Ошибка в программе, вызывающая ее крах или некорректную работу. Byte (байт). Восемь битов памяти, обрабатываемые как единое целое. Call chain (цепочка вызовов). См. активная цепочка вызовов. Calling a procedure (вызов процедуры). Выполнение процедуры путем помещения ее имени в тело другой процедуры. CD-ROM memory (ПЗУ на компакт-диске). Оптический диск, содержащий программы или данные, которые занесены на него во время изготовления и не могут быть перезаписаны. Central processing unit (CPU) (центральный процессор). Большая интегральная микросхема, осуществляющая функции преобразования и управления, а также большинство арифметических операций в компьютере. Code block (программный блок). Непрерывная группа операторов, предназначенная для совместного выполнения. Collection (набор). Контейнер, содержащий все объекты определенного класса. Command procedure (командная процедура). Процедура, осуществляющая изменение до¬ кументов других программ и расширяющая возможности этих программ. 499
Visual Basic for Applications в примерах Construct (конструкция). Фрагмент кода, объединяющий два или более базовых элементов языка и предназначенный для осуществления определенных функций. Эти функции обычно больше, чем базовые элементы языка, но меньше, чем процедуры. Control structure (управляющая структура). Любая структура, изменяющая ход выполнения программы. Включает операторы If, структурированные переходы и повторяемые структуры. Counted loop (вычисляемый цикл). Цикл, повторяющий единичный блок кода определенное количество раз. CPU. См. центральный процессор. Currency. Тип данных, позволяющих хранить значения денежных величин. Data structure (структура данных). Структура, объединяющая несколько типов данных и доступная как единая сущность. Data type (тип данных). Спецификация, определяющая, как элемент данных будет храниться в памяти компьютера. Debugging (отладка). Искусство обнаружения и устранения ошибок в программе. Default button (командная кнопка, активная по умолчанию). Командная кнопка диалогового окна, которая автоматически считается нажатой, если пользователь нажал клавишу Enter. Design time (время разработки). Время, затраченное на ввод программы в компьютер и изображение бланков. Время разработки продолжается до пуска вашей программы. Discrete mathematics (дискретная математика). Математическая система, в которой числа имеют ограниченное количество цифр. Дискретная математика используется в компьютерах, так как количество цифр ограничивается возможностями технических средств. Disk memory (дисковая память). Память, используемая для хранения программ и данных. Дисковая память напоминает магнитофон, который записывает и воспроизводит программы и данные. Дисковая память не стирается после отключения питания. DLL. См. динамически подключаемая библиотека. Double. Тип данных, позволяющий хранить числа с плавающей точкой двойной точности. Dynamic array (динамический массив). Массив, который объявляется в программе, но не получает места в памяти. Выделение памяти для динамического массива происходит в процессе работы программы, когда определяется конкретный размер динамического массива. Dynamic Link Library (DLL) (динамически подключаемая библиотека). Библиотека спе¬ циального вида, подключаемая к выполняемой программе во время ее выполнения (а не при компиляции программы). Edit bar (строка редактирования). Строка в верхней части таблицы, отображающая содержимое выбранной ячейки и позволяющая редактировать это содержимое. Edit handles (манипуляторы редактирования). Маленькие черные прямоугольники, появ¬ ляющиеся вокруг выбранного объекта и позволяющие изменять его форму. Enumerated values (перечисленные значения). Список опций, в котором каждой опции соответствует целочисленное значение, идентифицирующее ее. 500
Глоссарий Error handler (обработчик ошибок). Блок кода в процедуре, который вызывается в случае возникновения ошибки выполнения. Работа этого блока разрешается оператором On Error. Error trap (прерывание по ошибке). Прерывание, перехватывающее ошибки выполнения и обрабатывающее их вместо обработчика ошибок Visual Basic. Чтобы включить прерывание по ошибке, используется оператор On Error. Event procedure (процедура обработки событий). Процедура, связанная с определенным событием, и выполняемая при наступлении данного события. Exponent (порядок). Число, определяющее положение десятичной точки в числе с плавающей точкой. "Feature" (особенность). Термин для логических ошибок и упущений в коммерческих программах. File number (файловое число). Небольшое целое число, которое ассоциируется с файлом при его открытии, а затем используется в командах чтения или записи для идентификации файла. Form (бланк). Окно Visual Basic, используемое в качестве фона документов и диалоговых окон. Function (функция). Процедура, возвращающая значение в своем имени. Glue function (функция-"наклейка"). Функция, переопределяющая или модифицирующая аргументы функции для упрощения ее использования. Hexadecimal (шестнадцатиричная система счисления). Система счисления с основанием 16. Indexed record (индексированная запись). Метод косвенных ссылок на пронумерованные записи, использующий массив индексов. Доступ к записи файла производится с помощью индекса, предоставляющего ссылку на эту запись. Массив индексов позволяет сортировать записи файла без физического перемещения реальных записей на диске. Сортируются только элементы массива индексов. Индексированные записи в основном используются для файлов произвольного доступа. Inheritance (наследование). Передача свойств и методов большего объекта меньшему, который содержится в большем объекте. Instant watch variable (мгновенно отслеживаемая переменная). Переменная, текущее значение которой временно отображается в диалоговом окне в результате применения директивы Tools/Instant Watch. Integer. Тип данных, позволяющий хранить только целые числа. List (список). Упорядоченная последовательность произвольных элементов. Literal value (литеральное значение). Реальное значение (число или строка), которое вы можете записать непосредственно в программу. Например, в формуле X = 3 число 3 является литеральным значением. Logical error (логическая ошибка). Ошибка в логике программы, при которой программа выполняется, но приводит к некорректным результатам. Logically terminated loop (логически прерываемый цикл). Повторяемая структура, которая прерывается, если удовлетворяется логическое условие. Long. Тип данных, позволяющий хранить целые числа двойной точности. 501
Mantissa (мантисса). Значащие разряды числа с плавающей точкой. Method (метод). Некая операция над объектом, принадлежащая этому объекту. Modal dialog box (модальное диалоговое окно). Диалоговое окно, которое необходимо закрыть прежде, чем продолжить работу с отобразившей его программой. Modularized code (модульная программа). Программа, организованная в виде блоков, причем каждый блок решает строго определенную задачу, включая ввод и вывод информации. Name box (окно имени). Окно, расположенное в левой части строки редактирования. Содержит имя текущего выбранного объекта. Для того чтобы изменить имя выбранного объекта, необходимо записать это новое имя в окно имени и нажать клавишу Enter. Native machine code (собственный машинный код). Код, который может быть выполнен центральным процессором непосредственно. Null. Величина, указывающая на то, что переменная не имеет действительного значения. Numeric overflow (переполнение). Ситуация, возникающая тогда, когда полученный результат превышает максимальную величину, допустимую для заданного типа переменной. Numeric underflow (потеря значимости). Ситуация, возникающая тогда, когда результат операции получается меньше нижней границы диапазона для данного типа переменных. Эта ситуация не всегда приводит к ошибке, так как для замены малой величины может ис¬ пользоваться 0, но если эта малая величина важна, то результат в этом случае будет некорректным. Object (объект). Программная абстракция, в структуре которой содержатся данные и код, который знает, как манипулировать этими -данными. Объекты часто эмулируют реальные, физические объекты. Object Linking and Embedding(OLE) (связь и внедрение объектов). Методология обмена данными, при которой программные объекты передаются между программами. Object-oriented programming(OOP) (объектно-ориентированное программирование). Методоло¬ гия программирования, в которой данные и код, который знает, как манипулировать этими данными, объединяются,в одной структуре, именуемой объект. Octal (восьмеричная система счисления). Система счисления с основанием 8. ODBC (Open Database Connectivity) (связь с открытой базой данных). Средство, входящее в Excel и позволяющее получить доступ к данным, хранящимся в файлах внешней базы данных. OLE. См. связь и внедрение объектов. OOP. См. объектно-ориентированное программирование. P-code (p-код). Промежуточный код, вырабатываемый некоторыми компиляторами. Р-код требует специального интерпретатора, который читает команды p-кода, а затем выполняет эквивалентный машинный код. P-код предназначен для обеспечения переносимости программ, так как для нового процессора потребуется только создать новый интерпретатор. Path (путь). Упорядоченный список имен директорий, разделенный символами (\) и ука¬ зывающий местоположение файла. 502
Глоссарий Plain procedure (простая процедура). Стандартная процедура Visual Basic, не производящая изменений вне программы. Часто просто называется процедура. Procedure (процедура). Наименьшая компьютерная программа, пригодная для применения, которую вы можете написать на Visual Basic. Programmer (программист) Человек, создающий программы. Отличается от пользователя, который их использует, хотя пользователь и программист могут быть одним человеком. Project (проект). Все модули рабочей папки, в также все рабочие папки, подключенные к данной рабочей папке директивой Tools/References. Property (свойство). Данные, содержащиеся в программном объекте. Свойства могут быть данными, которыми объект манипулирует или данными, управляющими тем, как объект выглядит, или как он себя ведет. RAM. См. оперативная память Random access memory (RAM) (оперативная память). Электронная память компьютера, предназначенная для хранения программ и данных в процессе выполнения. Содержимое оперативной памяти исчезает после отключения питания. Range object (объект Range). Ячейка или прямоугольная группа ячеек таблицы. Real-only memory (ROM) (постоянное запоминающеё устройство (ПЗУ)). Постоянная элек¬ тронная память компьютера, предназначенная для хранения кода запуска. Изменить со¬ держимое ПЗУ нельзя, оно сохраняется и после отключения питания. Record variable (переменная типа запись). Структура данных, предназначенная для хранения различных переменных в записи файла произвольного доступа. Relative reference (относительная ссылка). Ссылка на ячейку таблицы относительно ячейки, содержащей эту ссылку. Значение этой ссылки изменяется, если формула, содержащая эту ссылку, копируется в другую ячейку. Repeating structure (повторяемая структура). Одна или несколько циклических структур, выполняющие блок кода некоторое количество раз. Примерами повторяемых структур являются вычисляемые и логически прерываемые циклы. ROM. См. постоянное запоминающее устройство. Runtime (время выполнения). Время, в течение которого выполняется программа. Runtime error (ошибка выполнения). Ошибка, произошедшая в процессе выполнения про¬ граммы из-за некорректного применения некоторого значения, ошибки внешнего устройства и т.п. Примерами ошибок выполнения могут служить деление на 0, выполнение оператора End If до того, как выполнится оператор If или попытка открыть несуществующий файл. Scope of a variable (область видимости переменной). Список процедур, в которых эта переменная доступна. Simple integer (просто целое число). Целое число, которое может принимать только поло¬ жительные значения. Single. Тип данных, позволяющий хранить числа с плавающей точкой. 503
Visual Basic for Applications в примерах SQL. См. язык структурированных запросов. Statement (оператор). Элемент текста программы, выражающий целостное, законченное действие. Static array (статический массив). Массив определенного размера, заданного при разработке. Размер статического массива изменяется посредством редактирования программы. Strictly typed language (язык со строгим контролем типов). Язык, требующий объявления каждой используемой переменной. String (строка). Последовательность символов ANSI, обрабатываемая как единое целое. Кроме того, String —тип данных, позволяющий хранить строки. Structured branch (структурированный переход). Одна или несколько структур, содержащих различные блоки кода, которые выбираются для выполнения в зависимости от логического условия. Для организации структурированных переходов применяются блочные структуры If и структуры Select Case. Structured Query Language (SQL) (язык структурированных запросов). Промышленный стандарт языка запросов к программам управления базами данных. Substring (подстрока). Часть строки. Syntax error (синтаксическая ошибка). Ошибка, причиной которой может стать опечатка в операторе, опущенный обязательный аргумент или применение аргумента некорректного типа. Task List (список задач). Список всех программ, выполняющихся под Windows в данный момент. Thread of execution (ход выполнения программы). Последовательность шагов, которыми программа выполняется от начала и до конца. Ход выполнения может изменяться в зависимости от условий, влияющих на управляющие структуры в программе. Truth table (таблица истинности). Таблица, отражающая логические выходы логической функции для всех возможных входов. Unstructured branch (неструктурированный переход). Структура, осуществляющая переход на любое место в процедуре. Неструктурированный переход создается оператором GoTo. User (пользователь). Человек, использующий программу. Отличается от программиста, ко¬ торый создает программы; хотя пользователь и программист могут быть одним человеком. Variable (переменная). Поименованная область в памяти компьютера, предназначенная для хранения некоторых значений. Variable type (тип переменной). Спецификация типа данных, которые могут храниться в данной переменной. Variant. Тип данных, позволяющий хранить значения любого типа. Watch variable (отслеживаемая переменная). Переменная, текущее значение которой ото¬ бражается на панели Watch окна Debug.
Предметный указатель А Абсолютные ссылки 19 Активная цепочка вызовов 354 Активный объект 58 Б Бланк данных 221 Блок кода 152 Блокирование таблицы 234 Булевы операции 148 В Видимые характеристики 43 Внешняя база данных 340 Встроенная функция Pmt() 126 Встроенные константы 80 Вызов диалогового окна приложения 217 Вызов процедуры 109 Вызов процедуры из другого модуля 109 Вызов процедуры из другой рабочей папки 110 Вызов функции 124 Вычисляемый переход 174 Вычисляемый цикл 183 Г "Горячая" клавиша 419 Графический объект 40 д Диалоговое окно Add Watch 366 - Assign Macro 37 - Calls 354 • - Customize 38, 424 - GetOpenFilename 216 - GetSaveAsFilename 216 - Macro Error 349 - Object Browser 46 — References 110 Динамический массив 187 Директива Data/Form 340 - Data/Sort 340 - Edit Fill Down 34 - File/Close 52 - File/New 19, 50 — File/Open 52,216 - File/Save As 216 - Format/Cel Is 21, 222 - Format/Object 241 — Format/Placement 252 - Format/Sheet/Rename 222,251 - Help/Contents - Insert/Macro/Dialog - Insert/Macro/Module - Insert/Names/Create - Insert/Names/Define - Run/Clear All Breakpoints - Run/Continue - Run/Start - Run/Step Into - Run/Step Over - Run/Toggle Breakpoint - Tools/Add Watch - Tools/Add-Ins - Tools/Assign Macro - Tools/Instant Watch - Tools/Macro 20,37, - Tools/Menu Editor - Tools/Options 23, - Tools/Protection/Protect Sheet - Tools/Record Macro/Mark Position for Recording - Tools/Record Macro/Record At Mark - Tools/Record Macro/Record 19, 47 19, 47 New Macro 19 - Tools/References 110, 341 - Tools/Run/Dialog 250 - View/Debug Window 87, 349 - View/Object Browser 46 - View/Toolbars 37, 38, 231 - Window/Unhide 20 - Run/Continue 363 Добавление командной кнопки 207 Добавление компонентов в набор 51 Доступ к компоненту набора 49 Доступ к функциям таблиц Excel 127 Доступ к элементам структуры 81 3 Заголовок диалогового окна 210 Задержка обработки ошибки 383 Запуск рекордера 19 Зарезервированное слово Until 193 Зарезервированное слово While 193 Зарезервированные слова Property Let 435 Значение, возвращаемое функцией MsgBoxO 209 И Имя переменной 75 Индекс 300 Индексированная запись 301 Интерактивная подсказка 45 505
Visual Basic for Applications в примерах к Канал обмена данными 454 Канал DDE 457 Класс 45 Ключевое слово Const 79 Ключевое слово Optional 118 Ключевое слово РагашАггау 119 Ключевое слово Private 112 Ключевое слово Public 77 Ключевое слово Set 85 Код командной кнопки 207 Код ошибки 168 Код пиктограммы 208 Коды ANSI 74, 146 Командная процедура 108 Командная кнопка, активная по умолчанию 208 Комбинированный объект "Окно 248 выпадающий список-редактирование - "Окно список-редактирование" 248 Комментарий 32, 84 Конструкция 43 Контейнер 42 Контекстное меню 421 Контрольная точка 361 Л Логическая величина 145 Логическая ошибка 348 Логически прерываемый цикл 191 Логические выражения 148 Логические проверки 148 Логическое ветвление 173 М Макрорекордер 18 Маю исса 73 Массив 78 Массив индексов 301 Математические функции 90 Метод Activate 58, 412 - Add • 51, 324, 415 - Additem 245 - BorderAround 66 - Cells 55 - Close 52 — Columns 185 - Copy 66 - CreateObjectO 466 - Cut 66 - DDEExecute 463 - DDEPoke 460 - Delete 52, 416 - GetObjectO 466 - GetOpenFilename 216, 292 - GetSaveAsFilename 216, 288 - Hide 250 - Offset 57 - OnKey - Paste 419 66 - Printout 66 - Range 53 - RemoveAll Items 246 - Removeitem 246 - Rows 185 - Select 58 - Show 250 Многоблочная структура If-Then-Elself 157 Модальное диалоговое окно 208 Модуль 42 н Набор 49 Набор CheckBoxesO 244 Набор Columns 34 Набор Dialogs 217 Набор DropDownsO 248 Набор EditBoxesO 243 Набор Label s() 243 Набор MenuBars 412, 414 Набор Menuitems 414 Набор Menus 414 Набор OptionButtonsO 245 Набор ShortcutMenus 421 Набор SpinnersO 249 Набор Toolbars 422 Наследование 44 Необязательный аргумент 118 Неопределенное количество аргументов 119 Неструктурированный переход 173 О Область видимости переменной 76 Обработчик ошибок 371 Общая процедура 108 Общие математические функции 94 Объект 42 Объект "Выпадающее окно" 248 - "Групповое окно" 244 - "Линейка прокрутки" 248 — "Метка" 242 - "Кнопка выбора" 244 - "Командная кнопка" 240 - "Контрольный индикатор" 244 - "Окно редактирования" 243 - "Окно списка" 245 - "Спиннер 249 - Application 109, 216 - MenuBar 412 - Range 53 Объектная переменная 85 Объектно-ориентированное программирование 41 Объявление глобальной переменной 77 Объявление массива 78 Объявление переменной 75, 76 Одноблочная структура If 152 506
Предметный указатель Окно Debug 87 - Help 45 Оператор объявления процедуры 107 Оператор присваивания 83 Оператор AppActivate 457, 468 - Веер 368 - Close 268, 298 - DDETerminate 458 - Debug.Print 368 - Dim 76 - End Sub 107 - End Type 80 - End With 60 - Error 379 - Exit Do 193 - Exit For 184 - Exit Function 378 - Exit Sub 378 - For Each 50 - Get 300 - GoSub 173 - GoTo 173 -If 144 — Input 274 - Line Input 274 - On Error 373 - Open 267, 297 - Option Compare Text 146 - Option Explicit 76 - Option Private Module 434 - Print 269 - Put 300 - ReDim 187 - Resume 375, 379 - SendKeys 471 - Set 439 - Shell 457 - Stop 96 -Type 80, 299 -With 36, 60 - Write 272 Операция возведения в степень 87 - вычисления остатка от деления 87 - вычитания 87 — деления 87 - деления нацело 87 - конкатенации 87 — сложения 87 - умножения 87 Операция And 149 - Imp 149 - Like 146 - NEqv 149 -Not 149 - NXor 149 - Or 149 Определение константы 80 Определение областей ввода 221 Отладка 347 Относительные ссылки 19 Ошибка блочного уровня 348 Ошибка выполнения 348 Ошибка, определенная пользователем 380 п Панель Immediate 87 Передача величин адресом 115 Передача величин значением 115 Переменная 74 Переменная типа запись 299 Переопределение динамического массива 187 Перечисленные значения 165 Пиктографическое меню Drawing 231 - Forms 238 Подключение процедуры к командной кнопке 37 Подменю 403 Подстановочные символы 146 Поименованный список 114 Пользовательский метод 439 Пользовательский объект 434, 439 Пользовательский тип данных 80 Порядок 73 Порядок табуляторов 252 Преобразование аргумента в формулу 116 Преобразование типов данных 101 Прерывание по ошибке 371 Применение цикла For Each к массиву 200 - к набору 197 Принятие решений 143 Приоритет операции 86 Программа Microsoft Query 342 Программный блок 152 Проект 435 ’ Процедура 107 Процедура инициализации 228 - обработки- событий 108 Процедура MessageBeepO 137 - Property Get 436 - Property Let 435 - Property Set 438 P Размещение командной кнопки в таблице 230 Разрешение прерывания по ошибке 371 Редактор Button Emage Editor 426 Режим двоичного доступа 268 - прерывания 358 Режим Append 268 - Input 268 - Output 268 - Random 268, 297 С Свойства 43 Сервер 458 Свойства Listindex 247 Свойство ActiveCell 58, 59 - ActiveSheet 59 507
- ActiveWorkbook 58 - Cancel 241 — Caption 242, 244, 245, 418 - Checked 418 - ColumnWidth 34 - Count 51, 185 - Default 241 - Dismiss 241, 250 - Enabled 418 - Formula 61 -Help 241 - InputType 243 - LargeChange 249 - Left 431 - LineStyle - Linkecfcell 66 245 - List 245 - ListFillRange 245 - Max 249 - Min 249 - MultiLine 243 - NumberFormat 33 - OnAction 249 - On Sheet Activate 109 - OnSheetDeactivate 109, 414 - Selection 33,58 - Small Change 249 - Text 243, 249 - Top -Value 61, 431 244, 245, 247, 249 - Visible 423 Синтаксическая ошибка 348 Смещенное целое число 73 Создание функции 122 Составной оператор 144 Средство Instant Watch 352 ''Спагетти" 178 Список, разделенный запятыми 114 Страница диалога 237 Строка меню 403 Строковые функции 94 Структура delect Case 164 Счетчик цикла 184 т Таблица истинности 149 - погашения ссуды 19 Тип данных 71 Тип данных функции 123 Тип данных Any 137 - Array - Boolean 74 72 - Currency 73 - Date 73 - Double 73 - Integer 73 - Long 73 — Object 74 - Single 73 - String 74 - Variant 74 Точка слежения 365 У Удаление компонентов из набора 52 Условие 193 Условие Case Else 168 - Elself 157 - Else 159 Ф Файл произвольного доступа 266 - последовательного доступа 266 Файловый фильтр 216 Файловое число 267 Форматирование ячейки 62 Функции библиотек DLL 135 Функции даты 98 Функции таблиц 125 Функция 122 Функция Abs() 92 - Asc() 96 - Atn() 91 - CBoolO 102 - CCurO 102 - CDateO 102 - CDblO 102 - Chr() 96 - CIntO 102 - CLng() 102 - Cost) 91 - CSngO 102 - CStrO 102 - CVarO 102 - CVErrO 102, 168, 381 - Date() 99 - DateSerial () 100 - DateValueO 100 - DayO 100 - DDEInitiate 457 - DDERequest 458 - EOF() 193, 290 -Err 375 - ErrorO 169, 375 - ExpO 91 - Fix() 92 - FormatO 100, 153, 270 - FreeFile 268 - GetCurrentTimeO 137 - GetOpenFilenameO 267 - Hex() 96 - HourO 100 - InputBoxO 211 - InstrO 95 - Int() 92 - IsArrayO 148 - IsDateO 148 508
Предметный указатель - IsEmptyO 148 - For-Next 183 - IsErrorO 148 - While-Wend 195 - IsMissingO 118, 148 - IsNullO 148 - IsNumericO 148 4 - IsObjectO - LBoundO - Lease () 148 194 95 Частная процедура Число с плавающей точкой 112 92 - LeftO 96 - Len() 95, 300 - LogO 91, 127 Э - LSetO 95 Элемент меню 403 - LTrimO 96 - Mid() 96 - MinuteO 100 - MonthO 100 D - MsgBoxO 205 - Now() 99 DDE 454 - Oct() 96 DLL (Динамически подключаемые - Randomize 91 библиотеки) 135 - RightO 96 - Rnd() 91 M - RSetO 95 - RTrimO 96 Menu Editor 405 - SecondO 100 - Sgn() 92 - Sin() 91 N — Space() - SqrO 95 91 Null 146 - Str() 89, 96 - StrCompO 95 О - StringO 95 - TanO 91 Object Browser 46 - TimeO 99 OLE 454 - Timer 100 Open Database Connectivity 341 - TimeSerialO 100 - TimeValueO 100 - Trim() 96 - TypeNameO 133 P - UBoundO 195 р-код 348 - UCaseO 95 - Val() 96 - WeekdayO 100 s - YearO - zLogO 100 127 Structured Query Language 340 Ц и Целое число без знака 73 USER.DLL 137 Цикл обработки события 191 Цикл объектного типа 197 v Цикл Do-Loop 192 - For Each 197 Visual Basic for Applications 17
Содержание Введение 9 Часть I. Введение в Visual Basic for Applications 15 Глава 1. Добро пожаловать в Visual Basic for Applications 17 Глава 2. Объекты, свойства и методы 41 Глава 3. Доступ к объектам Excel из Visual Basic 49 Часть II. Основные элементы языка 69 Глава 4. Типы данных и переменные 71 Глава 5. Применение операторов присваивания и встроенных функций 83 Часть III. Функции и процедуры 105 Глава 6. Создание и применение процедур 107 Глава 7. Создание и применение функций 122 Часть IV. Управление выполнением программы 141 Глава 8. Принятие решений 143 Глава 9. Применение блочных структур If 152 Глава 10. Применение структур Select Case 164 Глава 11. Не применяйте неструктурированные переходы 173 Часть V. Циклы и повторяемые структуры 181 Глава 12. Применение вычисляемых циклов 183 Глава 13. Применение логически прерываемых циклов 191 Глава 14. Применение циклов объектного типа 197 510
Visual Basic for Applications в примерах Часть VI. Ввод и вывод 203 Глава 15. Применение встроенных диалоговых окон 205 Глава 16. Применение рабочих таблиц 221 Глава 17. Создание диалоговых окон пользователя 237 Глава 18. Запись данных в дисковый файл последовательного доступа 266 Глава 19. Запись в дисковый файл произвольного доступа 297 Глава 20. Другие способы записи данных 324 Часть VII. Отладка процедуры 345 Глава 21. Что делать, если ваша программа не работает 347 Глава 22. Применение контрольных точек и точек слежения 358 Глава 23. Применение прерываний по ошибке для реакции на непредвиденные события 371 Часть VIII. Особенности расширения языка 401 Глава 24. Создание пользовательских и пиктографических меню 403 Глава 25. Создание пользовательских объектов 434 Глава 26. Межпрограммные коммуникации 454 Приложение А. Ответы на вопросы 477 Приложение В. Таблица кодов ANSI 489 Приложение С. Коды ошибок Visual Basic 490 Глоссарий 499 Предметный указатель 505
Вильям Дж. Орвис VISUAL BASIC FOR APPLICATIONS НА ПРИМЕРАХ Подписано в печать 22.05.95. Формат 70x100 Цб- Печ. л. 32. Бумага офсетная. Печать офсетная. Тираж 14000 экз. Заказ 82 9 Издательство «БИНОМ», 1995 г. Москва, ул. Новослободская, д. 50/1, стр. 1а. Лицензия на издательскую деятельность № 063367 от 20 мая 1994 г. Отпечатано с готовых диапозитивов в полиграфической фирме «Красный пролетарий» 103473, Москва, Краснопролетарская, 16
Научитесь самостоятельно программировать на Visual Basic for Applications! В этом Вам поможет VISUAL BASIC for APPLICATIONS на ПРИМЕРАХ Вы сможете быстро приобрести навыки конструирования и программирования приложений. Построенная на простых и ясных примерах, эта книга гарантирует быстрое обучение программированию. Подход издательства QUE существенно упрощает задачу постижения таинств программирования. Вы приобщаетесь к сонму VBA-экспертов на практических задачах использования средств VBA для расширения возможностей Excel. Вполне реальные ^проблемы, решаемые в рамках изложенных в книге примеров, покажут Вдм, как пишутся надежные и производительные макросы. Изучение книги позволит вам создать ряд собственных заготовок, которые безусловно окажутся полезными в практике программирования. «Visual Basic for Applications на примерах» делает обучение простым L Множество ценных л. замечаний, рекомендаций и предложений ■ Полные исходные тексты программ и примеры их работы 0 Вопросы и упражнения на закрепление материала L Пошаговая технология разработки реальных приложений Категория: Программирование/Макросы Предмет: Visual Basic for Applications