Книга
Книга
УДКОО4-43 ББК32.973.26-018 К85
Крейг Дж. К. и Уэбб Дж.
К85 Microsoft Visual Basic 6.0. Мастерская разработчика/Пер. с англ. — 5-е изд. — М.: Издательско-торговый дом «Русская Редакция», 2001. — 720 с.: ил.
ISBN 5-7502-0127-9
Книга состоит из 3 частей (34 главы) и предметного указателя. Написанная живо и доходчиво, она позволит освоить множество полезных приемов программирования, в том числе объектно-ориентированного, и научит, как создаватъ 32-разрядные приложения для Windows 95/98 и Windows NT — от экранных заставок до программ, ориентированных на Интернет. Кроме нт. Вы узнаете, как расширитъ возможности языка за счет функций Win32 API и вос- полъзоватъся преимуществами технологии ActiveX.
Книга предназначена всем программистам, которые имеют базовые знания по элементам языка Visual Basic, желают изучитъ Visual Basic версии 6.0 и стремятся повысить квалификацию. Она в полной мере оправдывает свое название — это действительно •мастерская разработчика».
УДК 004.43
ББК 32.973.26-018
Подготовлено к печати издательско-торговым домом •Русская Редакция» по лицензионному договору с Microsoft Corporation, Редмонд, Вашингтон, США.
ActiveX, JScrlpt, MSDN. Visual Basic, Visual C++, Visual FoxPro, Visual J++, Visual SourceSafe, Visual Studio, Windows. Windows NT и Word являются либо товарными знаками, либо охраняемыми товарными знаками Microsoft Corporation. Все другие товарные знаки являются собственностью соответствующих фирм.
© Оригинальное издание на английском языке, Джон Кларк Крейг и Джефф Уэбб, 1998
© Перевод на русский язык, Microsoft Corporation, 1998-2001
ISBN 1-57231-883-Х (англ)ISBN 5-7502-0127-9ISBN 1-57231-883-Х (англ)ISBN 5-7502-0127-9© Оформление и подготовка к изданию, издательско-торговый дом «Русская Редакция», 2001
ISBN 1-57231-883-Х (англ)
ISBN 5-7502-0127-9
ISBN 1-57231-883-Х (англ)
ISBN 5-7502-0127-9
Оглавление
Благодарности XV
ВведениегXVI
ЧАСТЬ I
ПРИСТУПАЯ К РАБОТЕ С VISUAL BASIC1
ГЛАВА 1
Что нового в Visual Basic 63
Выпуски Visual Basic 63
Интегрированная среда разработки4
Настоящий компилятор7
ActiveX8
Новинки, связанные с Интернетом8
Новые и модифицированные элементы управления9
Средства объектно-ориентированного программирования11
Изменения в языке11
Доступ к данным12
Интернет.13
Г Ji а ь А 2
Стиль программирования14
Присвоение описательных имен14
Префиксы для элементов управления15
Имена переменных17
Объявление переменных19
Меню20
Имена классов20
Контроль типов данных20
Ограничение диапазона действия элементов программы21
Комментарии22
Дополнительные источники информации22
ЧАСТЬ II
ДОРОГОЙ ДЖОН, КАК?23
ГЛАВА 3
Переменные25
Эмулировать беззнаковые целые?25
Использование переменных типа Long26
Упаковка беззнаковых байтовых значений с применением структур26
Работать с Булевыми значениями?28
Использовать байтовые массивы?29
Передача байтовых массивов вместо строк30
Обмен данными между строками и байтовыми массивами33
Работать с датами и временем?33
Применение элементов управления DTPicker и Calendar33
Запись в переменные типа Date35
Отображение даты и времени36
Выборка компонентов36
Вычисления с использованием дат и времени36
Проверка допустимости даты и времени37
Работать с переменными типаVariant?37
Циклы For Each38
Гибкий тип для передачи параметров39
Функции, связанные с типом Variant39
Empty и Null39
Приведение типов данных39
Работать со строками?40
Замена символов в строке40
Разбиение и объединение строк41
Применение фильтров42
"Поиск строк42
Работать с объектами?43
Новые объекты43
Существующие объекты44
Операции над объектами45
Уничтожение объектов45
Работать с предопределенными константами?46
Константы компилятора46
■Константы Visual Basic46
.Пользовательские константы47
Перечислимые типы48
Флаги и битовые маски48
Создавать UDT-структуры?49
Выравнивание50
Создавать новые типы данных с помощью классов?50
Создание нового типа данных51
Использование нового типа данных52
ГЛАВА 4
Параметры54
Использовать именованные аргументы?54
Использовать необязательные параметры?55
Передавать параметры -массивы?56
Передавать в параметре произвольный тип данных?56
Использовать в параметрах перечислимые типы?57
ГЛАВА 5
Объектно-ориентированное программирование60
Выбрать между EXE- и DLL-сервером ActiveX?60
Разместить все свои объекты во внешних ActiveX-компонентах?61
Создать новый объект?61
Пример модуля класса: Loan62
Использовать мой новый объект?68
Назначить объекту свойство, используемое по умолчанию?71
Создать и использовать ЕХЕ-сервер ActiveX?71
Пример ЕХЕ-сервера ActiveX: компонент Chance72
DICE.CLS72
Тестирование ActiveX-компонента, реализованного ' как ЕХЕ-сервер74
Создать объект, способный отображать формы?75
Event, WithEvents и RaiseEvent .. . 78
Работать с наборами объектов?78
Пример набора: SolarSys179
Класс Star 81
Класс Planets81
Класс Planet84
Класс Moons84
Класс Moon85
Как работают вложенные наборы85
Использовать полиморфизм?88
Применять дружественные методы? 89
Г Л А В А 6■
ActiveX-элементы,90
Создать ActiveX-элемент?90
Этапы разработки ActiveX-элемента90
Создание проекта ActiveX-элемента91
Конструирование интерфейса91
Изменение размеров элемента управления92
Добавление свойств, методов и событий93
Программирование функциональности элемента управления95
Отладить элемент управления?97
Откомпилировать и зарегистрировать элемент управления?99
Создать свойство, доступное на этапе разработки?101
Вывести диалоговое окно Property Pages?104
Загрузить свойство асинхронно?108
Создать элемент управления для использования с базой данных? . , 110
Использовать элемент управления DataRepeater?114
Создать контейнерный элемент управления?116
ГЛАВА 7
Использование компонентов для Интернета .119
Выбрать подходящий ActiveX-компонент?119
Представить уровни интернет-протоколов?120
Настроить доступ к сети?121
Обеспечить связь через Winsock?123
Вещание по протоколу UDP124
Беседа «один-на-одии» по протоколу TCP127
Создать 131
Перехват ошибок132
Управлять браузером Internet Explorer?135
Г Л А В А 8
Создание компонентов для Интернета138
Создавать ActiveX-элементы, рассчитанные на применение в Интернете?139
Использовать ActiveX-элементы с VBScript?140
Создавать DHTML-документы?142
Создавать ActiveX-документы?145
Создавать Web-классы?147
ГЛАВА 9
Создание приложений для Интернета 149
Выбрать тип приложения?149
Создать DHTML-приложение?151
Получение ввода в DHTML-приложениях152
Отображение результатов в DHTML-приложениях153
Создать IIS приложение?156
Отображение HTML-шаблонов158
Получение ввода в ИЗ-пциложениях161
Отображение результатов в US-приложениях162
Создать приложение на базе ActiveX-документа?164
Получение ввода в ActiveX-документах165
Отображение результатов в ActiveX-документах167
Устанавливать ActiveX-документы по Интернету?168
Устанавливать DHTML-приложения по Интернету?172
Распространять IIS-приложения по Интернету?172
ГЛАВА 10
API-функции•. . v173
Вызывать API-функции?173
Объявления173
Объявление 32-разрядных функций174
Строки:175
Передать адрес процедуры в API-функцию?176
Интерпретировать ByVai. ByRef и As Any в объявлении API-функции?178
Упростить вставку объявлений API-функций?179
Получить информацию о системе через API-функции?179
Определение версии операционной системы с применением элемента управления Syslnfo.179
Определение системных цветов180
. Определение типа процессора182
■ Определение времени работы системы183
Определение типов дисков184
Вызывать API-функции из кода ActiveX-элемента?185
Добавление объявлений API-функций в ActiveX-элемент.185
Расширение существующего элемента управления186
ГЛАВА 11
Мультимедиа191
Воспроизвести WAV-файл?191
Функция mciExecute191
Элемент управления Multimedia192
Воспроизвести AVl-фаил?192
Функция mciExecute192
Элемент управления Multimedia193
Проигрывать звуковые компакт-диски?194
ГЛАВА 12
Окна, диалоговые окна и прочие формы•. . . .197
Добавить стандартное диалоговое окно About?197
Шаблон формы для диалогового окна About200
Автоматически позиционировать форму на экране?201
Создать «плавающее» окно?203
. Модальный режим203
" Динамический режим203
Режим «поверх остальных».204
Создать экран-заставку?206
Шаблон формы для экрана-заставки207
Использовать элементы TabStrip и SSTab?208
Элемент управления SSTab209
Заставить форму мигать, чтобы привлечь внимание пользователя?.210
Переместить элемент управления в новый контейнер?212
ГЛАВА 13■
Визуальный интерфейс213
Использовать «облегченные» элементы управления?213
дооааи1ь горизонтальную полосу прокрутки в окно списка?214
Создать панель инструментов?216
Динамически модифицировать форму?218
Динамически настраивать меню?219
Удалить строку заголовка с формы?220
Создать индикатор прогресса?220
Разработка собственного индикатора прогресса222
Использовать элемент управления Slider?223
Использовать элемент управления UpDown?224
Использовать элемент управления FlatScrollBar?226
Использовать элемент управления Сооюаг?229
ГЛАВА 14
Графические методы231
Вычислить «цветовую" константу по RGB-. HSV- или НЗ^значениям9231
Преобразовывать единицы измерения?241
Создать фон с плавным переходом из синего в черный?241
Выделять ппямлутпьные области "Пезинпйым,. контуром?243
Создавать «горячие» участки на графических изображениях?246
Быстро нарисовать многоугольник?247
Нарисовать эллипс?251
Закрасить произвольный замкнутый участок?254
Поворачивать растровое изображение?257
Прокручивать графические изображения?259
Использовать BitBIt для создания анимации?261
Использовать для анимации объекты Picture?265
Работать с элементом управления Animation?267
Точно позиционировать текст в окне рисунка?268
Произвольно масштабировать шрифты?269
Повернуть текст на произвольный угол?270
Вывести текст несколькими шрифтами в одном окне рисунка?274
Г Л А В А 1 5■
Файловый ввод/вывод277
Эффективно переименовать, скопировать или удалить файл?277
Работать с каталогами и путями?278
MkDir, ChDir и RmDir278
CurDir и Арр.Path278
Dir278
Ускорить файловый ввод/вывод?.281
Работать с двоичными файлами?281
UDT-структуры281
Строки283
Байтовые массивы285
Взаимосвязь строк и байтовых массивов285
Использовать объект"FiieSystemObject?287
Работа с дисками289
Работа с палками,290
Работа с файлами291
ГпдRА 16
Реестр294
Считывать и записывать данные в реестре?294
Сохранить состояние приложения?298
Сопоставить с приложением определенный тип файлов?299
Извлечение аргументов из командной строки302
ГЛАВА 17
Справочные системы, подсказки и мастера303
Добавить подсказки по интерфейсным объектам?303
Добавить строку состояния в программу?304
Выводить при запуске «Совет дня»?305
Использовать мастера?306
Создать справочный файл в формате WinHelp?308
Базовые принципы подготовки исходного текста309
в •Создание файла проекта311
1Компиляция и тестирование справочного файла.311
’Связывание идентификаторов312
Полнотекстовый поиск и оглавления312
Создание окон справочной системы314
Использование макросов314
Подключить справочный файл к проекту через API-функцию WinHelp?315
Подключить к проекту контекстно-зависимую справку?318
Подключить справочный файл к проекту через элемент управления CommonDialog?31S
Включить режим WhatsThisHeip?320
Метод WhatsThrsMode320
Создать справочную систему в форматеHTMLHelp?321
Преобразование файлов проектов из формата WinHelp В формат HTML Help322
Использование элемента управленияHTMLHelp323
ГЛАВА 18
Защита326
Включить в программу скрытое окно с именами авторов?326
Создать диалоговое окно Password?328
.Зашифроватьпароль или другой текст?330
" Класс Cipher330
'Объект Cipherв действии331
Защита данных в реестре335
Работать со средствами защиты в Интернете?337
ГЛАВА 19
Мышь338
Изменить курсор мыши?338
Создать нестандартный курсор мыши?340
Вывести на экран курсор с анимацией?.341
Определить координаты курсора мыши?342
Г Л А У А 20
Клавиатура344
Изменить поведение клавиши Enter?344
Определить состояние клавиш-модификаторов?345
Создать "Горячие» клавиши?346
ГЛАВА 2 1
Программирование элементов TextBox и RichTextBox . . . 348
Отобразить содержимое файла?348
.Создать простой текстовый редактор?350
Обнаружить изменение текста?352
Уместить в элементе управления TextBox более 64 Кб текста?353
Выбрать шрифт для TextBox или RichTextBox в период выполнения? . . 356
ГЛАВА 22
Многодокументный интерфейс358
Создать MDI-приложение?.',. .356
MDI-форма358
Дочерние формы358
Свойства ActiveForm и ActiveControl359
Ключевое слово Me359
Свойство Tag359
Фундаментальные особенности MDI-формы360
Добавить эмблему на MDI-форму?360
ГЛАВА 23
Доступ к базам данных363
Использовать мастера при разработке баз данных?363
Подключить приложение к базе данных через элемент управления Data?365
Создание базы данных с помощью Visual Data Manager365
Создание пользовательского интерфейса367
Запуск приложения368
Подключить приложение к базе данных через DAO?36S
DAO368
Создание базы данных через ПАО370
Доступ к базе данных371
Создать отчет?374
ГЛАВА 24
ActiveX-объекты из других приложений375
Использовать ActiveX, чтобы проверить правописание?375
Проверка правописания средствами Microsoft Word375
Проверка правописания средствами Microsoft Excel379
Раннее и позднее связывание381
Применить ActiveX для подсчета слов?382
Получить доступ к математическим функциям Microsoft Excel? . . . 383
Г Л А В А 25
Экранные заставки385
Создать экранную заставку?385
Избежать одновременного запуска двух экземпляров заставки?389
Погасить курсор мыши при запуске заставки?390
Обнаружить перемещение мыши, чтобы завершить экранную заставку?392
Обнаружить нажатие клавиши?393
Использовать в заставке изображение с дисплея?393
Дополнить заставку поддержкой паролей и настройки параметров? . . 398
Г Л А В А 2 6
Разработка проектов401
Захватить изображение выполняемой формы и сохранить его в файле?401
Вставка графики в редактор Paint401
Команды Save As и Сору То401
Использовать файлы ресурсов?403
Создание файла ресурсов403
Использование файла ресурсов в приложении404
В каких случаях применяются файлыресурсов406
Использовать строковую базу данных для ,,,,-^национализации» приложения?407
Г Л А В А 27
Изощренные приемы программирования411-
Создать ActiveX DLL на Visual Basic?412
Объект Fraction412
Тестирование в среде разработки415
Создание и использование конечного DLL-модуля417
Создать DLL а С?417
Два С-файла418
Тестирование DLL420
Создать приложение, выполняемое удаленно?423
Создание удаленного приложения423
Регистрация удаленного приложения425
Выполнение удаленного приложения426
Доступ к удаленному приложению428
Возврат ошибок из удаленных приложений428
Отладка удаленных приложений429
Выявление и устранение проблем при использовании удаленной автоматизации.429
Создать надстройку для среды разработки Visual Basic?431
Базовые концепции431
Разработка надстройки432
Первый запуск надстройки437
Использование надстройки438
Ввести в приложение поддержку сценариев439
Элемент управления Script440
Добавление объектов и макропроцедур442
Обработка ошибок периода компиляции443
" Выполнение макросов445
Обработка ошибок периода выполнения445
Передача строк в методы446
Создание макросов447
Передать объекту UDT-структуру?449
Г Л А В А 28
Другие приемы*452
Создать связанный список?452
Определить “разрядность» операционной системы?455
Перезапустить Windows?457
Набрать телефонный номер ' программы?457
Использовать строчный перехват ошибок?459
Строчный перехват ошибок459
Ч А С Т Ь III."
ПРИЛОЖЕНИЯ-ПРИМЕРЫ"463
Г Л А В А 29* .
Г рафика465
Приложение HSVHSL465
Приложение Animate482
ANICLOCK.FRM485
ANIGLOBE.FRM488
Приложение Lottery.492
LOTTERY.FRM493
RANDOM.CLS.499
Приложение MySaver501
MYSAVER.BAS503
MYSAVER.FRM506
MYSETUP.FRM514
ГЛАВА 30
Средства разработки518
Приложение ColorBat518
ORBAR.FRM519
Приложение APIAddin521
Преобразование файла WIN32API.TXT.522
Состав приложения APIAddin525
CONNECT.DSR525
APIADDIN.FRM528
Компиляция надстройки533
Приложение Metric533
Состав приложения Metric535
METRIC.BAS535
METRIC.FRM537
CONVERT.FRM539
METRIC.HLP541
Приложение ScripDem'.543
SCRIPDEM.FRM544
Г Л A В A 3 1
Дата и время552
Приложение VBCal552
Это — мастер!552
Элемент управления DTPicker554
Элемент управления MonthView554
Элемент управления Calendar555
VBCALWIZ.BAS555
Приложение VBCIock562
VBCLOCK.FRM563
Использование реестра569
VBCLOCK2.FRM569
ABOUT2,FRM574
Приложение NISTTime577
Как работает NISTTime577
N1STTIME.FRM .. .579
ГЛАВА 32
Базы данных585
Приложение AreaCode585
AREACODE.FRM586
Приложение Пя1яПптп593
DATADUMP.FRM594
Тестирование приложения DataDump597
Приложение Jot597
«Типашиплвяние- дочерней формы598
«Горячие» клавиши в MDI-форме59S
Создание базы данных599
- Центрирование изображения на MDI-форме600
JOT.FRM601
, Панели инструментов, кнопки и подсказки603
SPLASH.FRM607
' NOTE.FRM.60В
Тестирование приложения Jot.611
ГЛАВА 33
Утилиты612
Приложение MousePtr612
MOUSEPTR.FRM613
Приложение ShowTell619
SHOWTELLFRM.621
FRMIMA6E.FRM624
Приложение WjndChil625
WINDCHILFRM626
Г Л А В А 3 4.
Сложные приложения634
Приложение Messages634
Синтаксис файла сообщений634
Для чего нужны файлы сообщений?636
MESbAoEb.FHM637
MSG.CLS639
MSG.FRM642
Приложение Secret644
Как работает приложение Secret?645
SECRET. FRM647
VIEW. F RM654
CIPHER.CLS656
Приложение BitPack658
Генерация таблицы простых чисел (решето Эратосфена)659
Создание файлов проекта BitPack.DLL659
BITPACK. FRM661
Приложение Dialogs.667
Особенности приложения Dialogs670
Состав приложения Dialogs672
DIALOGS. FRM.672
DLGEGG.FRM680
Предметный указатель..682
БЛАГОДАРНОСТИ
IVIicrosoft Visual Basic 6.0 — очередной шаг вперед в традиционном для Microsoft развитии Бейсика. Всего несколько лет назад многие (и кто знает его досконально, и кто лишь думает, что знает его) сходились во мнении, что Бейсик — «игрушечный • языкдля написания коротких программ на скорую руку, мало что значащий в разработке коммерческих или промышленных приложений. Так вот, пусть теперь кто-нибудь только посмеет сказать, что современный Visual Basic подпадает под эту категорию!
Мне очень приятно сообщить, что Visual Basic 6.0 отвечает целям и запросам самых взыскательных и требовательных разработчиков современных приложений. Это отличный инструмент для создания ‘XctiveX-компонентов и полноценных автономных 32-раз- рядных Windows-приложений. Не требуя длительного обучения, Visual Basic 6.0 — с его проверенной временем высокопродуктивной средой программирования и способностью создавать быстрые, эффективные приложения и компоненты — может все!
Я признателен всем сотрудникам Microsoft Press, помогавшим мне в работе над книгой. Некоторые из них сыграли исключительно важную роль и заслуживают особой благодарности. Научный редактор МаркЯнг (Marc Young) был, как всегда, на высоте. Отлично поработала и Памела Хейфи (Pamela Hafey), выпускающий редактор. А Эрик Стру (Eric Stroo) - с его чутьем и умением руководить — вновь не дал нам сбиться с курса.
Это второе издание в соавторстве с Джеффом Уэббом (JeffWebb). Каки в прошлый раз, мне было по-настоящему приятно работать с ним — содержание этой книги значительно улучшилось благодаря его таланту, мастерству и знаниям. Вряд ли бы у меня получилась такая книга без Джеффа, быстро схватывающего и доходчиво объясняющего многие новые и сложные вещи.
Двоих читателей следует упомянуть особо, их вклад в улучшение нескольких программ просто неоценим. Дэннис Борг (Dennis Borg) предоставил исходный код для преобразований, связанных с цветовой моделью нзт (Hue. Saturation, Luminosity — оттенок, насыщенность, яркость"), и помог в отладке моих программ. Спасибо, Дэннис! Дилан Уоллес (Dylan Wallace) долго общался со мной по электронной почте, поясняя, как усовершенствовать экранные заставки и, в частности, режим их предварительного просмотра. Спасибо, Дилан! Оба они — настоящие эксперты в своем деле, и мне было интересно поучиться у них.
ВВЕДЕНИЕ
О это издание мы добавили много новой информации по Интернету, программированию баз данных и разработке ActiveX-элементов. Кроме того, учтя пожелания читателей, мы переработали большую часть материалов из предыдущего издания. Как и в прошлый раз, купив эту книгу, Вы получаете двух авторов по цене одного. (Джефф Уэбб уже написал несколько прекрасных книг по тематике, связанной с Visual Basic). Вдвоем нам удается не отставать от постоянных изменений в мире программирования на Visual Basic. Теперь это такой многоцелевой программный продукт, что только коллективный труд позволяет должным образом освещать > * । ^ - перспективы и охватывать важнейшую тематику. За время совместных исследований мы многому научились и надеемся, что Вы найдете на этих страницах массу интересной информации о программировании на Visual Basic.
В самом начале мы постарались четко, без всякой рекламной шумихи описать новые возможности Visual Basic 6. Вы сами оцените, насколько эффективнее сможете реализовать свои проекты его средствами. В этой версии действительно много изменений, и, чтобы ознакомиться с ними, прочтите (или хотя бы пролистайте) главу 1 «Что нового в Visual Basic. 6».
Ссылки на Microsoft Windows 95 и Microsoft Windows NT, встречающиеся в этой книге, относятся и к Microsoft Windows 98. Любые программы, написанные на Visual Basic для Windows 95, как правило, работают и в Windows 1 ts
Стиль программирования
В книгу включена глава по стандартному стилю программирования — эти сведения пришлось добывать по крохам у экспертов Microsoft и из других источников. Мы не предлагаем слишком уж жестко придерживаться изложенных в ней правил — там, где пунктуально выполняют все правила, обычно пропадает творческая атмосфера и вообще скучно, — просто собранная нами информация поможет группам разработчиков лучше понима ть друг друга и обмениваться фрагментами кода. Исходные тексты всех программ, написанных нами, близки к этому стилю.
Что не вошло в это издание
В него не вошли вводные материалы по истории Бейсика и часть информации об использовании Visual Basic, рассчитанная исключительно на начинающих. Так мы расчистили место для материя-
лов поинтереснее! Сведений вводного характера хватает в других книгах по программированию на Бейсике. Мы же обнаружили, что наши читатели достаточно знакомы с Visual Basic и стремятся найти новую информацию, чтобы повысить свою квалификацию.
«Как сделать то-то и то-то»
Выступая на конференциях и семинарах, мы заметили, что большинство вопросов сводится по сути к одному: -Как сделать то-то и то-то». Программисты на Visual Basic могут теперь использовать массу полезных, но не всегда хорошо документированных трюков и приемов; кроме того, Visual Basic б зачастую позволяет решать те же задачи гораздо эффективнее предыдущих версий. Поэтому мы подобрали для части II --Дорогой Джон, как?.. * новые материалы.
Кстати, после долгих споров мы решили — простоты ради - оставить вопросы в той же форме, что и в предыдущих изданиях, т. е. в виде «Дорогой Джон...», но заметьте; во многих случаях «Дорогой Джефф...» было бы точнее. От всяких «Дорогой Дж. и Дж...», «Уважаемые Джефф и Джон...» и «Эй, вы, парни!..» мы отказались. И по той же причине употребляем в основном тексте книги местоимение «я» вместо «мы» — даже несмотря на то, что нас двое.
Программы-примеры
Содержание компакт-диска, прилагаемого к книге, изменилось и расширилось. В него включен не только программный код из всех глав, но и сама книга в электронном виде. Visual Basic — в высшей степени многообразная среда разработки, и мы старались приводить примеры, которые не только бы иллюстрировали основные области ее применения, но и были бы полезны. В итоге мы ограничили круг рассматриваемых тем и глубину их изложения. Скажем, вполне работоспособный пример программы, управляющей базой данных, относительно несложен, потому что весь набор встроенных в Visual Basic средств программирования баз данных охватить в одной книге попросту немыслимо. Здесь Вы изучите фундаментальные основы программирования баз данных на Visual Basic, но, если Ваши интересы сосредоточены именно в этой области, Вы, вероятно, захотите прочитать и какую-нибудь другую книгу, целиком посвященную данному предмету.
Программирование для 32-разрядных версий Windows
Visual Basic 4 поставляли в 16- и 32-разрядных версиях, что позволяло использовать его в операционных системах Windows 3.1, Windows 95 и Window - NT. В Visual Basic б нет 16-разрядной версии. Поэтому основное внимание мы уделяем программированию на Visual Basic б для 32-разрядных версий Windows.
Книга небесполезна и в том случае, если Вы создаете программы для Windows NT. Конечно, отдельные вызовы API-функций системного уровня потребуют определенной модификации, но большинство программ-примеров будет работать в Windows NT без всякой переделки.
Компакт-диск, прилагаемый к книге
| Этот диск содержит все файлы с исходным кодом, проекты,
I формы и прочие файлы программ, представленных в части Н и Ш Этот код можно откомпилировать и скомпонова ть в исполняемые файлы, включить в собственные приложения или просто посмотреть как он работает. Вы можете копировать файлы иди загружать проекты прямо с компакт-диска. Если Вы предпочтете сначала скопировать файлы проектов /
, на жесткий диск, то легко найдете нужное — мы создали ■ ■; рук
' туру огов, имена которых соответствую •. номерам глав.
। Кроме того, на компакт-диске находится полная электронная версия нашей книги, которую можно читать на экране,
- упражняясь с примерами. Она подготовлена в формате HTML,
I снабжена средствами поиска и позволяет быстро находить нужную информацию (процедуры, определения и т. д.; едва ли не одним щелчком мыши. Подробнее об использовании компакт-диска, прилагаемого к книге, см, файл READ-ME.TXT. .
ПРИСТУПАЯ К РАБОТЕ С VISUAL BASIC
Созданный в 1963 году, Бейсик был первым языком, который позволил программисту сосредоточиться на методах и алгоритмах решения своих задач, не отвлекаясь на алгоритмы управления аппаратными средствами компьютеров. Microsoft Visual Basic далеко ушел в своем развитии от предка, но положенная в его основу философия осталась прежней. Программирование для Microsoft Windows на С или C++ известно своей сложностью, но тех же результатов программисты могут добиться с помощью Visual Basic, затратив на обучение гораздо меньше времени. Visual Basic - передовая и высокоэффективная система разработки приложений для Windows, требующая минимум средств и усилий.
В прошлом одним из самых крупных недостатков Бейсика было низкое быстродействие написанных на нем программ. Но сегодня созданные на Visual Basic приложения и компоненты можно компилировать с помощью оптимизирующего компилятора, ядро которого идентично применяемому в языке программирования Microsoft С. И пусть больше никто не говорит, что Visual Basic — «игрушечный» язык!
ГЛАВА 1
Что нового в Visual Basic 6
ТТосле запуска Visual Basic б на экране появляется диалоговое окно New Project (создать проект), показанное на рис. 1-1. Как видите, кроме «Standard EXE», Visual Basic позволяет создавать множество типов компонентов, включая ActiveX DLL, элементы управления на базе ActiveX (будем называть их для краткости ActiveX-элементами) и ActiveX-документы, применяемые в интрасетях и Интернете.
I-"- Harifdl *t«f<J
Г" il<jn1.ii<*- Wi deiog г. *« .'uhre
Рис. 1-1 Диалоговое окно New Project в Visual Basic б
В этой главе я перечислю все новинки и изменения, превратившие Visual Basic в очень мощный и простой в использовании инструмент. Ко многим из них мы с Вами еще не раз вернемся. Хотя в этой и последующих главах я постараюсь охватить максимально широкую тематику, по каким-то темам может понадобиться более подробная информация. В таком случае советую обратиться к документации Visual Basic, заглянуть в Visual Basic Books Online и на Web-узел корпорации Microsoft (http://microsoft.com).
Выпуски Visual Basic 6
Сейчас Visual Basic поставляется в трех 32-разрядных версиях, или выпусках, — каждый последующий является расширением предыду-
щего. Во все выпуски входит комплект Visual Basic Books Online — полная электронная документация по Visual Basic в мультимедийном формате, основанном на HTML.
Visual Basic Learning Edition позволяет легко создавать мощные 32-разрядные приложения для Microsoft Windows 95, Microsoft Windows 98 и Microsoft Windows NT. В него включены все встроенные элементы управления, а также «сетка» (grid control), «набор вкладок» (tab control) и элементы управления, связанные с доступом к данным (data-bound controls) .
Visual Basic Professional Edition содержит все средства Learning Edition плюс ActiveX-элементы, в том числе предназначенные для работы в Интернете. Поставляется дополнительная документация: Component Tools Guide и Data Access Guide.
Visual Basic Enterprise Edition включает те же средства, что и Visual Basic Professional Edition, плюс Automation Manager, Component Manager, средства управления базами данных, проектно-ориентированная версия Microsoft Visual SourceSafe и др.
Интегрированная среда разработки
Интегрированная среда разработки Visual Basic с каждой версией становится все лучше и лучше. Мне очень нравится возможность выбора между режимом SDI (Single Document Interface — однодокументный интерфейс) и М!Ч (Multiple Document Interface — многодокументный интерфейс), а также загрузки сразу нескольких проектов как единой группы. Режим SDI знаком тем, кто работал с Visual Basic еще до версии 5. В нем окна открываются независимо друг от друга (рис. 1-2); при этом между окнами Visual Basic виден и рабочий стол, и окна ранее открытых приложений. Режим MDI, устанавливаемый теперь по умолчанию (рис. 1-3), делает среду разработки очень похожей на Microsoft Word, Microsoft Excel или Microsoft Access. В этом режиме появляется одно большое окно для всего приложения, а в нем — несколько дочерних, ограниченных рамками основного окна. Поначалу режим MDI кажется непривычным, но поработайте в нем, и он наверняка понравится Вам больше, чем SD1.
Object Browser (рис. 1-4) — мощный инструмент среды программирования Visual Basic. Вы можете быстро переходить из Object Browser в модули и процедуры своего проекта. Благодаря встроенным средствам поиска, секции описания и ряду других особенностей Object Browser находить и изучать компоненты, которые включены (или могут быть включены) в проект, намного легче.
Прим, перев.: Для краткости мы будем использовать в дальнейшем термин «элементы управления, связанные сданными».
V L-i1' ^ '^ ^рпч* 2*ug Ь/ Sri" 1ц;гп уда 6*Ч« JUM** tiP
Рис. 1-2 Интегрированная среда разработки в SDl-реэгсиме
ZSljJZSljJ(Itw} 'P»:urf< tn*^.M!h>iJs«ilrrr2*^.(Itw} 'P»:urf< tn*^.M!h>iJs«ilrrr2*^.
ZSljJ
ZSljJ
(Itw} '
P»:urf< tn*^.M!h>iJs«ilrrr2*^.
(Itw} '
P»:urf< tn*^.M!h>iJs«ilrrr2*^.
?ч- pianat<jbjeet - « Add (^Earth’11
М1~л р lane-cObJfrtt .МеигеиеГ - 12756 ,М« - 5.97361+24 4 Л#1С| TIDOnS мpitmen
With .Boons
Createtacoa
Sec MconCteject - .Add|"LuDi"| fat 3ti!№! CE It Я fSSpfrttUs* with raoonObiecc .Piamatec - 3476 .fleas - 7.3SE+22 End 6±th
Ind. Kith EndtJieh
Set pinnetcoject. ■ .AtiLj^Xacs"]
Рис. 1-3 Интегрированная среда разработки в MDI-режиме
Code Editor (редактор исходного текста) поддерживает несколько по-настоящему интересных возможностей. При наборе текста на экране возникают < ши гн. которые ускоряют ввод ключевых слов, помогают выбирать свойства или методы, доступные для данного элемента управления или объекта, и т. д. Примеры таких средств — Auto List Members (список компонентов) (рис. 1-5) и Auto Quick Info (краткие сведения) (рис. 1-6).
^rh41B^las$Ffanflt8
ммьег 01 ;■* snejTij.
Рис. 1-4 Усовершенствованный Object Browser
iritl pieaetc«>]ei5t .Dletoeter = 127SS
. Маяз - 5.97Э6Е+21
'A-JJ tiicorijs to this pliu«c 71th ,Koons
End 1
End Tith
"^teiCi: tvth's nt^H Set moonObgecc - . Add ("Lua»")
Set ft- -bis of tea at cperU t^
7 it h. г ay о IMS* j e r r.
Рис. 1-5 Вонг так работает средство Auto List Members
Рис. 1-6 А так — средство Auto Quick InfoРис. 1-6 А так — средство Auto Quick Info
Рис. 1-6 А так — средство Auto Quick Info
Рис. 1-6 А так — средство Auto Quick Info
К числу других изменений можно отнести более совершенные отладочные окна и панели инс трументов, стыкуемые окна, улучшенную цветовую палитру, индикаторы текстовых полей, помогающие при отладке кода (с их помощью можно, например, вставлять точки прерывания), вызов из меню справочной информации с интер-
нет-сервера Microsoft и возможность закомментировать или раскомментировать все строки в выделенном блоке. Последнее, по- моему, очень полезно при разработке программы — обратите внимание на кнопки Comment Block (закомментировать блок) и Uncomment Block (раскомментировать блок) на панели инструментов Edit (правка).
Кроме того, предлагаются утилиты, помогающие разрабатывать приложения. Например, в Visual Basic Enterprise Edition включена новая утилита Application Performance Explorer (APE), написанная на Visual Basic. Она удобна при разработке, планировании развертывания и настройке распределенных клиент-серверных приложений. АРЕ позволяет прогонять автоматизированные тесты типа «а что, если?» и исследовать производительность многозвенного приложения в сетях с разной топологией; при этом учитываются такие факторы, как пропускная способность сети, частота запросов, требования к передаче данных, дисковая емкость сервера и т. д. Исходный текст этой программы можно использовать как учебное пособие и модифицировать под свои задачи. . .
Настоящий компилятор
Компилятор, генерирующий машинный («родной») код, — одно из важнейших средств Visual Basic. Почему оно так важно? Ну, отчасти потому, что придает Visual Basic респектабельность в глазах тех, кто не хотел работать с ним, считая его игрушкой, но в большей мере потому, что позволяет делать на Visual Basic вещи, прежде немыслимые. Например, до недавнего времени Visual Basic не годился для программирования игр с высокоскоростной анимацией, преобразования трехмерных изображений, обработки освещенности объектов и т. д.
Рис. 1-7 Вкладка Compile диалогового окна Project Properties
Параметры компилятора настраиваются на вкладке Compile диалогового окна Project Properties (свойства проекта), открываемого одноименной командой из меню Project (рис. 1-7).
Все приложения, написанные на Visual +ги после компиляции работают быстрее, хотя наибольший выигрыш от нового компилятора получают программы, требующие интенсивных числовых расчетов, обработки трехмерных изображений и др. Компилятор в Visual Basic построен по той же технологии, что и в Visual C++, и при желании откомпилированный код можно отлаживать в среде Visual C++. , .
ActiveX ;
ActiveX — ученое словечко, относящееся к технологиям, которые раньше связывали с термином OLE. Microsoft назвала так технологии, основанные на COM (Component Object Model — модель компонентных объектов). Теперь, работая в Visual Basic, можно создавать на базе ActiveX элементы управления, документы и DLL, а также программы, предоставляющие XctivcX-объекты другим приложениям, и т. д. Даже сам Xi Basic и его среда разработки состоят из компонентов, построенных на базе ActiveX. С технологией ActiveX Вы еще не раз встретитесь и в этой книге, и практически везде, где пишут о Visual Basic. Она лежит в основе всех компонентов Visual Basic — как встроенных, таки создаваемых Вами.
Новинки, связанные с Интернетом
Очевидно, технологии Интернета и интрасетей занимают сейчас особое место в политике и долгосрочных целях Microsoft. Толчком к использованию Visual Basic для Интернета и интрасетей послужило то, что еще до выпуска окончательной версии Visual Basic на Web-узле Microsoft появился бесплатно распространяемый специальный выпуск Visual Basic — Control Creation Edition (ССЕ). Эта урезанная версия Visual Basic позволяла создавать элементы управления ActiveX, предназначенные для тесного взаимодействия с Microsoft Internet Explorer. В Visual Basic б вошли все возможности ССЕ плюс многое другое. С помощью Visual Basic — на основе существующих браузеров — можно создавать мощные приложения для Интернета, не ограниченные рамками стандартных HTML-документов.
Visual Basic позволяет Вам формировать собственные ActiveX-документы. Они создаются почти так же, как и другие формы Visual Basic, но базируются на таких контейнерах ActiveX-документов, как Internet Explorer. Эти документы поддерживают гиперссылки, подстановку меню (menu negotiation) в браузер, расширение справочного меню и другие, весьма мощные средства. Но, пожалуй, самое главное — это возможность создания полноценных приложений, работающих в Интернете или интрасети.
Новые и модифицированные элементы управления
Элементы управления — неотъемлемая часть Visual Basic. Как и в прежних версиях Visual Basic, совершенствование языка идет по пути разработки новых элементов управления и улучшения уже имеющихся.
ActiveX-элементы — одна из самых «горячих» концепций в Visual Basic. Вы можете скомбинировать несколько существующих элементов или разработать свои и использовать их локально или в Интернете. В ближайшие годы Вы еще не раз услышите об ActiveX-элементах и их применении в Интернете. В Visual Basic 6 появилось несколько новых ActiveX-элементов. Но сначала я расскажу о тех, которые поставляются со всеми его выпусками.
и ADO (ActiveX Data Objects) Data обеспечивает более гибкое и эффективное подключение к любому источнику данных и требует минимального объема дополнительного кода. Кроме того, он позволяет «на лету» модифицировать источники данных.
и DataGrid, функционально аналогичный старому элементу управления DBGrid, поддерживает Unicode, а также упрощает просмотр и модификацию наборов данных.
il DataList и DataCombo — усовершенствованные версии элементов управления DBLism DBCombo — обеспечивают динамическое переключение источников данных.
й Hierarchical FlexGrid создан на базе прежнего элемента FlexGrid и дает возможность отображать иерархические наборы записей, сформированные из нескольких (разных) таблиц.
* ImageCombo — аналог элемента ComboBox с тем отличием, что позволяет добавлять в список изображения.
* ImageList теперь поддерживает графические файлы в формате GIF.
А сейчас я перечислю новые и усовершенствованные элементы управления, поставляемые только с профессиональным выпуском Visual Basic б и выпуском для предприятий.
я CoolBar позволяет создавать новомодные панели инструментов, настраиваемые пользователем (Вы увидите их в среде Visual Basic).
DTPicker — раскрывающийся календарь для выбора и ввода дат и времени.
DataRepeater — совершенно новый способ структуризации информации. отображаемой из.источников данных. Он «несет на себе» ряд элементов управления, связанных с данными, — в том числе TextBox и CheckBox — и позволяет представлять базы данных по аналогии с формами Access.
я FlatScrollBar предоставляет новый тип экранных объектов - плоскую полосу прокрутки.
ListView теперь позволяет добавлять подчиненные элементы в свой новый набор ListSubltems.
MonthView формирует лист календаря на один месяц и дает возможность пользователю выбирать как отдельные даты, так и их диапазоны.
MS Chart теперь поддерживает прямую связь с источником данных.
ProgressBat усовершенствован для поддержки плавной прокрутки, а также размещения как в горизонтальном, таки в вертикальном положении.
№ Script — новый мощный элемент управления, дополняющий Ваши приложения средствами написания сценариев, доступными конечным пользователям. Поддерживаются VBScript, Microsoft JScript или определенные Вами функции.
и Slider теперь поддерживает всплывающие подсказки (ToolTips) и связанные окна.
в TabStrip дополнен рядом новых возможностей, в том числе вставки элементов управления, подсветки, поддержки плоских кнопок и др.
я В TreeView введена поддержка флажков, выделения строк и др.
У многих существующих элементов управления в Visual Basic б появились новые возможности. Рис. I-S иллюстрирует пример одного из таких новшеств — теперь почти все видимые элементы управления поддерживают всплывающие подсказки. Кроме того, все большее число элементов управления дополняется поддержкой операций drag-and-drop, доступа к данным и средств для работы в Интернете.
м ТцнГГуеИшиат^а^оп
Гт-!»; со"жгпс till3i has а 7саflnri]
Рис. 1-8 Свойство ToolTipTextmenepb стандартно , для большинства элементов управления
Одно из моих любимых свойств всех элементов управления, поддерживающих вывод изображений, — их способность показывать картинки в стандартных форматах (JPEG.GIF и др.), принятых в Интернете (рис. 1-9).
Решив использовать какие-то элементы управления, обязательно изучите все их свойства, методы и события — наверняка найдете массу новых и заманчивых возможностей!
Рис. 1-9 Все элементы управления, способные выводить изображения, теперь поддерживают форматы JPEG и GIF
Средства объектно-ориентированного программирования
Модули классов, впервые появившиеся в Visual Basic 4, позволили создавать и использовать объекты. В Visual Basic 5 модули классов усовершенствовали, а в язык ввели много новых средств объектноориентированного программирования (ООП). Как ни странно, в Visual Basic б в этом плане почти ничего не изменилось. Но ясно, что твердый фундамент ООП, заложенный в предыдущих версиях Visual Basic, будет играть все более важную роль в разработке серьезных приложений — и сегодня, и завтра.
Если Вам пока не знакомы эти средства ООП, сейчас самое время примкнуть к победившей партии! На протяжении всей книги я буду стараться приводить практические примеры объектно-ориентированного кода и модулей, но делать это так, чтобы Вы не утонули в новых концепциях структурной организации программ. ООП привело к появлению целого пласта новой терминологии. И если Вы еще плохо представляете, что стоит за терминами полиморфизм, дружественные функции, инкапсуляция, интерфейсы, наборы и т. и., не паникуйте! В свое время я все подробно объясню и проиллюстрирую на примерах, что поможет твердо усвоить эту новую и очень важную часть языка.
Изменения в языке "
В языке Visual Basic усовершенствован ряд существующих функций и появилось несколько новых, значительно упрощающих некоторые виды распространенных задач программирования. Весьма кстати, например, пять новых строковых функций. Они еще не раз встретятся Вам в этой книге, но я перечислю их здесь просто для справки: Filter, InStrRev, Join, Replace и Split. Ближе к концу книги смотрите в оба, и Вы поймете, на какие чудеса они способны и как этого добиться!
Кроме строковых функций, хочу прямо сейчас упомянуть и об одной новой, очень важной, математической функции: Round, которая позволяет округлять любое число до заданного количества разрядов. Такая операция была возможна и раньше — за счет строковых преобразований или заумной математики, но функция Round упрощает ее и выполняет гораздо быстрее. С помощью этой функции легко реализуется такая часто встречающаяся задача, как округление денежных сумм с точностью до цента, что я и продемонстрирую на примере объекта Loan в главе 5 «Объектно-ориентированное программирование*.
Целый букет новых возможностей открывает функция CallByName, которая обеспечивает доступ к свойству или методу объекта по ссылке на его имя, хранящееся в строковой переменной.
Новые объекты FileSystem предоставляют Вам принципиально новый способ взаимодействия написанных на Visual Basic приложений с файловой системой. Объектно-ориентированный подход к перечислению файлов и работе с дисками, папками, файлами и всеми их свойствами прямо позаимствован из мира C++. Этот подход имеет свои преимущества и упрощает некоторые весьма сложные, но распространенные задачи программирования, относящиеся к файловой системе.
Несколько усовершенствований, внесенных в язык Visual Basic, освобождают от отдельных ограничений прежних версий. Пользовательские типы данных (UDT) теперь допустимы в качестве аргументов и типов значений, возвращаемых открытыми свойствами и методами. Функции и процедуры-свойства могут возвращать массивы целиком. Амассивы переменного размера (также называемые SAFEARRAY) и ■ ■ присутствовать в левой части оператора присвоения.
В Visual Basic б есть и другие новинки, например, в Visual Basic for Applications добавлен целый ряд функций и свойств. Везде, где только можно, я буду стараться использовать новейшие функции, средства и способы реализации тех или иных задач.
Доступ к данным
Новая модель доступа к объектам данных ADO проще и надежнее, чем предыдущие версии, лучше интегрирована с другими средствами и приложениями Microsoft. Интерфейс теперь одинаков независимо от того, где находится информация — в локальной или удаленной базе данных. Кроме того, поддерживаются иерархические наборы записей.
Для поддержки новой модели ADO с Visual Basic теперь поставляется новый элемент управления ADO Data. Он позволяет быстро создавать приложения, работающие с базами данных, и требует минимального объема дополнительного кода.
Провайдеры данных (data providers) и их потребители (data consumers) можно связывать друг с другом большим числом способов, чем раньше. С базой данных теперь разрешается связывать едва ли не любой объект. А такое средство, как Binding Manager, заметно облегчает связывание визуальных и не-визуальных объектов.
Элементы управления Data Report Designer — многоцелевой набор средств для эффективного формирования и вывода отчетов. Б набор входят шесть элементов управления, разработанных специально для вычисления и форматирования данных в отчетах, выводимых на печать.
Нестандартные элементы управления (user controls) можно создавать как источники данных, что позволяет связывать с ними другие элементы управления.
Новое окно Data View (в среде Visual Basic) обеспечивает просмотр всех связей с базой данных, сформированных в Вашем проекте на данный момент.
Новый мастер Data Form Wizard автоматически генерирует формы Visual Basic с соответствующими элементами управления и процедурами для управления информацией, получаемой из таблиц и запросов базы данных.
Несколько : ювых и усовершенствованных элементов управления предлагают более удобные способы для просмотра и редактирования наборов записей из баз данных. К таким элементам относятся DataGrid, DataRepeater и Hierarchical FlexGrid.
Интернет
Интернет играет все более важную роль в разработке современных приложений, и Visual Basic 6 предоставляет много новых и улучшенных средств, которые помогают создавать программы, способные работать с Интернетом.
Вы можете формировать содержимое Web-страниц, используя новый объект DHTMLPageDesigner, который дает возможность размещать элементы управления на странице методом drag-and-drop и писать код для их обработки.
Visual Basic Enterprise Edition позволяет создавать серверные приложения с поддержкой Dynamic HTML (DHTML).
Мастер Package and Deployment Wizard (ранее известный под названием Setup i izaru) упаковывает файлы Вашего приложения для распространения на дискетах, компакт-дисках и через Web либо создает дистрибутив на локальном или сетевом диске.
Многие усовершенствованные средства Visual Basic 6 поддерживают загрузку и развертывание по сети, а также все возможности Аснеел-документов с применением технологии Internet Explorer 4.
ГЛАВА 2
Стиль программирования
Простота работы с Visual Basic часто маскирует необходимость в едином стиле программирования, но эта необходимость растет пропорционально размерам создаваемых программ. Совсем несложно отладить программу из 100 строк, труднее — из 1000 и почти невозможно — из 10 000, если только Вы не придерживаетесь определенной системы. Когда же над программой работает не один человек или ее предполагается поддерживать длительное время, тем более не обойтись без соблюдения каких-то правил.
Главное в стиле программирования — логичность и четкость. Хороший стиль снижает вероятность ошибок и упрощает восприятие исходного текста. Он включает в себя схему именования, контроль типов, область действия переменных и комментарии. Связанные с ним правила таковы:
* присваивайте элементам описательные имена;’
всегда используйте оператор Option Explicit;
присваивайте каждой переменной и каждому параметру наиболее подходящие типы данных;
в ограничивайте диапазон действия элементов программы (применяйте процедуры-свойства вместо глобальных данных);
и пишите комментарии в тексте программы.
Пусть эти правила станут Вашей второй натурой: Вы должны соблюдать их на подсознательном уровне. А теперь рассмотрим каждое правило подробнее.
Присвоение описательных имен
Легко ли «говорить» на Бейсике? Все зависит от того, с кем Вы разговариваете, какова Ваша квалификация и насколько Вы аккуратны. Когда-то давным-давно номера строки операторы GOTO легко превращали текст программы в клубок спагетти. К счастью, современные версии Бейсика структурированы гораздо лучше и даже объектно-ориентированны, хотя при желании и сегодня можно устроить полную кашу!
За последние несколько лет высказывалось немало предложений по стандартному стилю программирования и некоторые из них были не очень-то хороши. Но ломать копья из-за этого, на мой
взгляд, не стоит. Достаточно привыкнуть к нескольким сравнительно простым и стандартным приемам, и Вы, вполне вероятно, заслужите репутацию пишущего легкочитаемый код. Если Вы работаете не один, а в команде, Ваши усилия, затраченные на упрощение восприятия кода, окупятся сторицей.
Префиксы для элементов управления
Один из простейших способов прослыть знатоком — начинать имя каждого элемента управления в тексте программы со стандартного трехбуквенного префикса. Это действительно улучшает читаемость кода. Вдумайтесь, к чему относится процедура BuffaloClick? К кнопке? К рисунку? К переключателю? Не зная, какому объекту на форме присвоено имя Buffalo, Вы будете сильно озадачены'. Но стоит написать cmdBujfalo_Clickm тут же станет понятно, что процедура относится к кнопке, picBuffalo CUck— к элементу управления PictureBox («окно рисунка») и т. д. Список общепринятых префиксов публиковали уже несколько раз, я повторю его здесь в слегка расширенном виде.
Стандартные префиксы компонентов Visual Basic
Префикс
Компонент
ado
ADO Data
ant cal cbo cb сЬЗ ebb
dp cm3 cmd
Animation
Calendar
ComboBox■
Chart"
3D CheckBox
CheckBox
Картинка (picture clip)
3D CommandButton
CommandButton
com
Comm"'
con ctr fiat db dbc dbd dbe
Container (DAO)■
Control■
Data■
Database (DAO)
DaraCombo
DataGrid
Ядро базы данных (DAO)
dbgrd dbi
Сетка, связанная с данными (data-bound grid) DataList
см. след. стр.
Прим, перев.: Здесь Б оригинале обыгрывается слово buffalo, глагольная форма КОТОрОГО перевОДИТСЯ как «-озадачивать^, «мистифицировать*.
2—1204
Стандартные префиксы компонентов Visual Basic (продвижение)
Префикс
Компонент
dir dig doc tire dtp dtp Я fid flex tri fra firm fib g«u gpb gra grd № bdr bsb
Us img imgcbo ix key 1Ы tin 1st tsi' luchk Iwcbo Iwcmd lufra Iwhsb heist hvopt hvtxt Iwt’sb mci med mnu mpm
Список каталогов (directory list box)
Стандартное диалоговое окно (common dialog box) Document (DAO) Список дисков (drive list box) DataRepeater■
DTPicker Список файлов (file list box)
Field (DAO)?■
Hierarchical FlexGrid. "
3D Frame■'
Frame Form.
FlatScrollBar Gauge■
Группа переключателей (group push button).
Graph";.
Grid.'
Group (DAO)"■■ ■
Header-
HScroliBar imageList.-. ■
Image■ .v.
ImageCombo. -.
Index (DAO)■
Состояние клавиши(key status).
Label Line'
ListBox■
ListView•■•
Lightweight CheckBox*■■,
Lightweight ComboBox Lightweight CommandButton Lightweight Frame Lightweight HScroliBar Lightweight ListBox.
Lightweight OptionButton Lightweight TextBox.
Lightweight VScrollBar Multimedia MCI. .
Masked Edit..
Меню, ,,,
MAPI Messages
Стандартные префиксы компонентов Visual Basic (продолжение)
Префикс
Компонент ’
mps mst mtw ole орЗ opt out pic pnl prg prm
ФУ rd rec re! rtf shp sld spn sta
SV5 tbd tbs tlb tmr tre txt usr vsb wsp
MAPISession
Tabbed Dialog■
MonthView
OLE■■
3D OptionButton
OptionButton
Outline,
PictureBox"‘
3D Panel
ProgressBar
Parameter (DAO)
QueryDef (DAO)' '"7
RemoteData'■
RecordSet (DAO)'
Relation (DAO)- /- ц, . . .
RichTextBox■
Shape
Slider
Наборный счетчик (spin button)
StatusBar
Syslnfo
TableDef (DAO)
Tab Strip.■
Toolbar■
Timer: ’
Treeview■
TextBox
User (DAO)
VScrollBar
Workspace (DAO)
Имена переменных
Некоторые — особенно из числа программистов на С — предлагают именовать все переменные по так называемой венгерской системе, аналогичной той, что применяется для префиксов компонентов (см. предыдущую таблицу). Эти предложения вызывают у меня смешанные чувства. С одной стороны, вроде бы удобно видеть тип переменной, читая текст программы, а с другой, такая система зачастую слишком громоздка.
С самого начала у программистов на Microsoft Basic была возможность именовать переменные с использованием суффикса, обозначающего тип данных. Например, Х% — переменная целого типа,
X! — переменная вещественного типа одинарной точности, a XS — строка. Какая система лучше — венгерская или эта, по-моему, дело вкуса. Мне попадались схемы именования, в которых»венгерские» префиксы переменных уточнялись даже диапазоном действия, но подобная детализация избыточна.
В следующей таблице перечислены суффиксы, обозначающие типы данных. Там же указаны и более новые типы, для которых суффиксов нет, что, кстати, подтверждает мое мнение насчет постепенного отказа Microsoft от суффиксов переменных.
Стандартные суффиксы для различных типов данных
Суффикс
Тип данных
"%
Integer (2-байтовый целый со знаком)
&
Long (4-байтовый целый со знаком)
@
Currency (8-байтовый денежный»)
I
Single (4-байтовый вещественный одинарной точности)
*
Double (8-байтовый вещественный двойной точности)
5
String (строчный)
нет
Boolean (Булев)■ -’ -С*
нет
Byte (байтовый)
нет
Collection (набор)■. ■
нет
. Date (дата).
нет
Decimal (28-разрядный тип Variant). .
нет
Objeci (объектный)
нет
Variant (произвольный)
Если Вы считаете, что венгерская система именования переменных — истина в последней инстанции, то специально для Вас я перечислю здесь и префиксы, принятые в этой нотации. Кстати, для некоторых типов данных предусмотрено более одного префикса, что позволяет обозначать сферу применения конкретной переменной. Например, стандартный 16-битный целый тип со знаком можно назвать Boolean, Handle, Index, Integer или Word в зависимости от того, как Вы используете данную переменную.
Тип данных Object — универсальный, он не относится к конкретному объекту, чье имя Вам известно; например, процедура может принимать объект как параметр, не «зная» заранее, какого типа этот объект. Если же Вам известен тип объекта, сформируйте префикс по имени класса данного объекта (см. раздел «Имена классов»).
Префиксы, обозначающие тип данных в венгерской системе
Префикс Тип данных blnBoolean
bytByte.
енгCurrency
Префиксы, обозначающие тип данных в венгерской системе (продолжение)
Префикс
Тип данных
dbl
Double
dec
Decimal
dfm
Date (Time)
SHg
Single.
b
Описатель (handle)
г
Индекс (index)
Ing
Long
int
Integer
obj
Object (универсальный)
str
String
u
Беззнаковый
uing
Беззнаковый Long
Wit
Variant
turd
Слово (word)
Объявление переменных . .
Следить за типами переменных помогает оператор Option Explicit. Может быть, Вы даже предпочтете, чтобы он автоматически появлялся в каждом создаваемом модуле, — лично я советую поступать именно так. Для этого выберите из меню Tools (сервис) команду Options (параметры), щелкните ярлычок Editor (редактор) и пометьте флажок Require Variable Declaration (требовать объявления переменных).
Оператор Option Explicit запрещает ссылки на переменные, не объявленные в явном виде. Переменные лучше всего объявлял!, оператором Dim, указывая при этом их тип. Тогда переменные легко читать и отличать от элементов управления и других объектов, в именах которых есть свои префиксы, а чтобы узнать тип нужной переменной, достаточно бросить взгляд на блок операторов Dim в начале модуля или процедуры.
ВНИМАНИЕ Весьма распространенная и очень опасная ошибка — чаще всего ее совершают те, кто знает язык программирования С, — некорректное объявление оператором Dim сразу нескольких переменных одного типа. Не выйдет! Вы должны явно определять тип каждой переменной в операторе Ошг.Читайтедалыпе! ,
Эти объявления переменных корректны: " "
Dim intA As Integer, sngB As Single, dMC As Double Dim OK, E!, F#
Но вот следующее объявление приведет к созданию двух переменных типа Variant (tntlw inij^ одной переменной типа Integer (mt К; вместо предполагавшихся трех переменных типа Integer:
Dim inti, IntJ, intK As Integer
В итоге Ваша программа может работать не так, как Вы ожидали, и при этом будет очень непросто выявить причину ее некорректного поведения.
Меню . ■
Для элементе)г. меню тоже есть несколько схем именования. Я предпочитаю начинать их со стандартных на сегодняшний день префиксов щ;,?,. Может быть. Вы привыкли добавлять название элемента и меню, к которому он относится, — например, mnuHelpAbout соответствует команде About из меню Help. Я же пользуюсь сокращениями и вместо mnuHelpAbout пишу что-то вроде m-nuAbout. В большинстве случаев понять, какую позицию в структуре меню занимает тот или иной элемент, достаточно просто, и, на мой взгляд, незачем усложнять их имена. - -
Имена классов
Создавая новые классы, присваивайте им описательные имена, а объявляя объектные переменные, указывайте префиксы, обозначающие их классы. Обычно ставят уникальный трехбуквенный префикс, но при необходимости можно использовать и более длинный. Иначе говоря, составьте свою схему именования объектов. Microsoft предлагает именовать классы целыми словами, которые начинаются с заглавных букв:
Примеры именования классов и объектных переменных
Имя класса
Префикс объектной переменной
Пример имени объектной переменной
Loan
Ion
lonBoat
Planet
pint
pintEartb,
EmployeeRecorcl
ere ' г
е rcSales
DailySpecial
dspec
dspecTuesday
Контроль типов данных
Старайтесь присваивать конкретной переменной наиболее подходящий ей тип. Это предложение несколько отличается от принципа использования «наименьшего из возможных» типов. Например, тип Boolean должен занимать всего 1 бит — True или False, правильно? Но на деле у переменной типа Boolean такой же размер, как и у переменной типа Integer, т. е. 2 байта.
Так зачем же таскать лишний багаж? А затем, что в наше время надежность важнее компактности. Даже если Вы разрабатываете приложения для Интернета (где размер файлов очень важен), все равно лучше использовать оптимальные типы данных и тем самым повысить надежность и читаемость кода. Переменная типа Boolean всегда содержит одно из двух значений — True или False. Наименьший из возможных типов, Byte, тоже позволяет хранить одно из этих значений, но где гарантия, что он ч хранит нечто совсем иное? Указав тип Boolean, Вы сразу же расставите все по своим местам, и программа станет чуточку яснее, а значит, и надежнее.
Применение наиболее подходящих типов особенно важно для объектных переменных. Объявив такую переменную с именем конкретного класса, а не как универсальный тип Object, Выдадите компилятору возможность оптимизировать доступ к соответствующему объекту. (Специфика этого процесса не настолько проста, чтобы обсуждать ее здесь, — см. главу 24 «ActiveX-объекты из других приложений».)
Кроме того, указывайте типы всех параметров в процедуре. Это поможет обнаружить ее некорректное использование. Помните: необязательные параметры теперь могут быть не только типа Variant. Этого не было в Visual Basic 4, так что неплохо бы пройтись по всем процедурам, написанным Вами на той версии языка, и проверить, нельзя ли каким-то параметрам присвоить другие типы.
Ограничение диапазона действия элементов программы
Диапазон действия (scope) подразумевает видимость переменной, процедуры или объекта другим процедурам в программе. В старину все данные были глобальными — Вы получали или изменяли значение любой переменной в любом месте программы. Потом, когда в Бейсик ввели подпрограммы и процедуры типа Function, появились 2 диапазона действия — данные могли быть глобальными или локальными в пределах процедуры. А теперь возможны 4 диапазона:
универсальный — через MicrosoftActiveX элемент виден всем выполняемым приложениям;
я глобальный — элемент виден всем процедурам во всех модулях проекта;
s модульный — элемент виден всем процедурам в пределах данного МОДУЛЯ;
локальный — переменная видна лишь содержащей ее процедуре.
Ограничивая диапазон элемента, Вы получаете возможность контролировать его изменение. Чем шире этот диапазон, тем осторожнее следует действовать. Потому-то и нужно максимально сужать его. И, кстати, процедуры-свойства позволяют, по сути, отка-
заться от глобальных переменных. Эти процедуры вводят дополнительный уровень контроля за глобальными данными, проверяя допустимость значения и права вызывающей процедуры на изменение данных.
Комментарии
Хорошие комментарии — приятный сюрприз любому, кто хоть раз пробовал разобраться в чужой программе. Не так давно отхватил я на свою голову контракт на подготовку'руководства по новому языку программирования, разработанному одной компанией для сугубо внутренних целей. Думаете была какая-нибудь спецификация? Нет. Образцы программ? Тоже нет. Комментарии в исходном коде? Нет, нет и еще раз нет. Ведущий разработчик и я провели времени вместе куда больше, чем со своими семьями, — вот уж пришлось поработать! Но нужно ли это? Лучше сразу писать комментарии. Чтобы структурировать их Е рамках процедуры, используйте несколько приемов:
И пишите для процедуры заголовок, в котором указывайте, что делает данная процедура, и перечисляйте используемые ею другие процедуры и глобальные данные;
я перед операторами Case и прочими управляющими конструкциями кратко описывайте возможные варианты и соответствующие им действия;
я перед циклами комментируйте их предназначение и условия выхода;
я придумайте систему «ключевых слов» в комментариях и помечайте ими связанные блоки кода, чтобы при необходимости их можно было быстро найти и что-то изменить.
Дополнительные источники информации
Более подробную информацию о стандартных схемах именования и стиле программирования ищите ■ справочной системе Visual Basic в разделе Visual Basic Coding Conventions. Приведенных там рекомендаций придерживаются многие корпорации и программисты, и, если Вы намерены работать в большом коллективе, нет смысла цепляться за собственные «стандарты
ЧАСТЬ II
ДОРОГОЙ ДЖОН, КАК?.. .
Одна из сильных сторон Visual Basic — его гибкость. Если Вам нужно сделать нечто такое, на что в Visual Basic нет готовых рецептов, Вы непременно добьетесь 1 своего, «извернувшись» за счет функций API (интерфейса прикладного программирования) или нетривиального применения элементов управления.
- Для этой части книги я отобрал самые интересные вопросы типа «Дорогой Джон, как?..». Отвечая на них, я, естественно, использовал лишь новейшие приемы программирования. Например, можно написать процедуру, заменяющую строкой все экземпляры како- ■ го-либо символа, но в Visual Basic теперь есть встроенная функция Replace, которая делает это сама. Не держитесь за устаревшие приемы — осваивайте возможности Visual Basic 6!
Переменные
Новые строковые функции Visual Basic уменьшают потребность в том багаже, который большинство из нас таскает за собой еще со времен BASICA. Распространенные строковые операции вроде удаления пробелов, замены табуляторов или разбиения строк на лексемы теперь поддерживает сам Visual Basic.
Visual Basic б предоставляет также элементы управления для выбора и отображения дат в форматах календаря и списка, Элементы Calendar и DTPicker упрощают прием дат от пользователей и освобождают программиста от большей части кропотливой работы, необходимой для отображения дат г привлекательном виде.
Я расскажу, как использовать такие средства, и продемонстрирую некоторые приемы работы с переменными, отнюдь не новые для этой версии Visual Basic. Будут рассмотрены операции с Variani- переменными и предопределенными константами. эмуляция беззнаковых целых в вызовах функций Windows API, создание структур пользовательского типа (иОТ-структур; и многое другое.
Эмулировать беззнаковые целые?
Увы, Visual Basic не поддерживает беззнаковые 16-битные целые — тип данных, часто используемый в API-вызовах. Но не беда — есть способы обойти этот недостаток.
Целочисленные типы данных в Visual Basic представлены тремя разновидностями: Long, Integer и Byte. Переменные типа Long принимают 32-битные числа со знаком в диапазоне от -2 147 483 648 до +2 147 483 647. Чаще всего используется тип Integer, позволяющий хранить 16-битнмсчисла со знаком в диапазоне от -?2 768 до +32 767. Переменные типа Byte хранят 8-битные беззнаковые значения в диапазоне от О до 255. Заметьте, что только Byte является беззнаковым целочисленным типом.
Беззнаковые 16-битные целые необходимы для вызова многих API-функций. Можно, конечно, пойти напролом и передавать этим функциям «знаковые» переменные, но тогда Вам придется создать обработчик отрицательных значений. Существует несколько подходов к эмуляции беззнаковых 16-битных целых, и я расскажу о двух лучших.
ПРИМЕЧАНИЕ Компилируя программу в машинный код, Вы можете отключить контроль переполнения целочисленных типов, В некоторых случаях это позволит выполнять некорректные вычисления, так что внимятельно просмотрите свою программу, прежде чем отключать данный параметр. Но положительный результат его отключения — ускорение целочисленных операций и возможность более эффективного представления беззнаковых значений в определенных видах вычислений. .
Использование переменных типа Long "
В ряде случаев можно манипулировать беззнаковыми целыми в диапазоне от 0 до 65 535, храня их в переменных типа Long. Когда наступит пора присвоить это значение переменной типа Integer — заменителю беззнаковой 16-битной переменной, воспользуйтесь следующей формулой:
intShort = HnaLnna And AH7FFF&1 - (Inglong And &H8000&)
где intShort— переменная типа Integer, передаваемая API-функции, a IngLong — переменная типа Long, хранящая нужное (беззнаковое) значение. Преобразование 32-битного значения в 16-битное беззнаковое реализуется побитовыми операциями с помощью оператора And и шестнадцатеричных битовых масок.
Чтобы записать в переменную типа Long значение целой переменной Integer, использованной для эмуляции беззнаковой 16-битной переменной (эта операция обратна только что рассмотренной), действуйте так:
IngLong = intShort And AHFFFFA
Учтите, что 16-битное беззнаковое значение (intSborf), хранимое в переменной типа Integer, может быть интерпретировано как отрицательное. Поэтому описанные выше преобразования следует проводить непосредственно до и после вызова API-функции, ожидающей беззнакового 16-битного целого. Таким образом, в своей программе Вы работаете с беззнаковым целым, хранящимся в переменной типа Long (lngLong),a передаете его API-функциям в переменной типа Integer (intSborf).
Упаковка беззнаковых байтовых значений с применением структур
В Visual Basic нет конструкции Union, как в С, но ее функциональность легко имитировать, копируя байты между UDT-структурами оператором LSet. Это позволяет записывать беззнаковые байтовые значения в целые со знаком и извлекать их оттуда. Вот фрагмент кода, демонстрирующий этот прием:
Option Explicit'" ■ ■. '
Private Type UnsignedlntType
lo As Byte
hi as Byte
End Type"
Private Type SlgnedlntType
n As Integer
End Type
Каждая из показанных UDT-структур хранит по 2 байта. Хоть области памяти и не перекрываются, как в случае Union в языке С, их двоичное содержимое несложно перемещать между соответствующими им переменными:
Private Sub Form_Click()'
' создаем UDT-переменные
Dim intu As UnsignedlntType
Dim intS As SlgnedlntType
' инициализируем старший и младший байты, создавая тем самым целое intu.hl=231
intU.1о= 123
копируем двоичные данные в другую структуру
LSet intS = intU
Print intS.n, LntU.hi; intU.lo
' присваиваем целое и извлекаем старший и младший байты
intS.n = intS.n - 1' уменьшаем целое, создавая новое значение
' копируем обратно в другую структуру
LSet intU = intS
Print intS.n, intu.hi; intU.lo
'End Sub
Если Вы, поместив эти два фрагмента кода в модуль формы, щелкнете ее, на ней появятся целое число со знаком (-6277) и два составляющих его байтовых значения (231 и 123). После какой-нибудь операции над целым (в данном случае — вычитания единицы) будут выведены новые результаты: целое со знаком (-6278) и его старший и младший байты (231 и 122). Все операции реализуются объявлением двух переменных — intU и intS — с применением определенных ранее UDT-структур. Значения intUhi^ intU.lo присваиваются старшему и младшему байтам переменной intU. Затем двоичное содержимое переменной int U копируется в intS оператором LSet и полученное в результате целое со знаком выводится на форму. Наконец, целое значение в intS уменьшается на единицу (просто чтобы получить значение, отличное от исходного) и intS копируется обратно в intU — вот так можно разбить целое со знаком на 2 беззнаковых байтовых значения.
Добавив на форму текстовые поля и надписи, Вы получите калькулятор, преобразующий целое в составные байты и наоборот (рис. 3-1).
Заметьте, что LSet способен копировать любое двоичное содержимое одной UDT-струкчуры в другую. А это позволяет обрабатывать двоичное содержимое некой области памяти как данные разных типов.
Рис. 3-1 Целое число со знаком, состоящее ■ из двух беззнаковых байтов -
ВНИМАНИЕ В Microsoft Windows 95 и Microsoft Windows NT элементы Ваших I DT-структур могут быть выровнены в памяти не совсем так, как Вы ожидали. Это происходит из-за того, что каждый элемент выравнивается по границе, кратной 4 байтам, с добавлением недостающих байтов. Перемещая данные из одной структуры в другую оператором LSet, поэкспериментируйте сначала и убедитесь, что байты располагаются именно так, как Вы хотели. Будьте осторожны!
• раздел «Дорогой Джон, как... использовать С, чтобы создать DLL?» в главе 27 «Изощренные приемы программирования» — о создании DLL на Microsoft Visual C++. Язык С идеален для перекомпоновки байтов в целых значениях.
JT? <+ --н, как..,
Работать с Булевыми значениями?
Значения Булева типа хранят одну из констант — True (истина) или False (ложь) — и больше ничего. Булевы переменные обычно используют для хранения результатов сравнений и других логических проверок. Например, следующая процедура присваивает результат логического сравнения двух величин Булевой переменной blnTestResult и выводит это значение на экран:
Private Sub Form^ClickO'. ■ ■
Dim blnTestResult as Boolean ''
blnTestResu = 123 < 246-1' ' :
Print blnTestResult ■■- Лj'
End Sub:< . ■■■, . ।
В данном случае ня экране появится значение True. Когда Вы выводите значение Булевой переменной на экран или принтер, всегда появляется либо True, либо False.
ВНИМАНИЕ Бойтесь неявных приведений типов!
Visual Basic поддерживает многие виды автоматического приведения типов, а это значит, что Вы можете присвоить значение любого типа практически любой переменной. Скажем, хотя Булевы переменные принимают в качестве значений только True или False, есть возможность присвоить такой переменной число или даже строку. Но результаты могут оказаться весьма неожиданными — будьте осторожны!
Небольшой пример. Фрагмент кода, приведенный ниже, выведет на экран 'True, True и False, т. е. bytAравно True, intB равно True, но ЬуМне равно intB' Причина такой странности не столь очевидна и связана с внутренним приведением разнотипных переменных bytA и intB к одному типу при проверке их на равенство.
Private Sub Form_Click()
Dim bytA As Byte
Dim intB As Integer
bytA = True
intB = True■
Print bytA = True
Print intB = True■, ' '
Print bytA = intB.■
End Sub. -■
Сложные логические выражения можно составить с помощью операторов And, Or и Not. Логические выражения объединяют в себе несколько операций сравнения, давая в результате True или False. Например:
If blnExit And Not blnChanged Then End
Эта строка кода завершает программу, если blnExit равно True и в то же время blnChanged — False. Для тех же целей пригодны и арифметические операции, но логические делают код короче и яснее. Кроме того, Булевы переменные в таких выражениях позволяют избежать нежелательных приведений типов.
П П П rt f* Zl *1 fa-
Использовать байтовые массивы?
Едва ли не главной причиной введения в язык байтовых массивов было стремление обеспечить обмен буферами двоичных данных с 32-разрядными API-функциями. Одно из различий между 16- и 32- разрядными программами, написанными на Visual Basic, в том, что строки, используемые 32-разрядными версиями, содержат символы Unicode, а это требует двух байтов на символ. Система сама преобра-
зуетдвухбайтовые символы Unicode в однобайтовые симв олы AN SI, но если строка содержит двоичные данные, ее содержимое после такого преобразования становится бессмысленным. Чтобы избежать проблем, возьмите за правило передавать в строках только символьные данные, а двоичные данные — в байтовых массивах.
Передача байтовых массивов вместо строк
Байтовые массивы содержат беззнаковые байтовые значения в диапазоне от 0 до 255. В отличие от строк содержимое таких массивов никогда не преобразуется операционной системой. Байтовые массивы можно передавать вместо строк многим API-функциям.
Следующий код, вызывающий API-функцию GetWindowsDirec- tory и таким образом определяющий пггь к каталогу Windows, демонстрирует, какие изменения нужно внести в объявление функции и ее параметров. Код представлен в двух версиях: первая передает строку, в которой функция возвращает путь к каталогу Windows, а вторая с той же целью передает байтовый массив. Разместите любую версию этого кода в модуле формы, запустите программу, щелкните форму, и Вы увидите путь к каталогу Windows на своем компьютере по аналогии с тем, что показано на рис. 3-2.
* ЧШЙ Рийй .wdi-r^uvUrJ^* ЧШЙ Рийй .wdi-r^uvUrJ^
* ЧШЙ Рийй .wdi-r^uvUrJ^
* ЧШЙ Рийй .wdi-r^uvUrJ^
Рис. 3-2 API-функция, возвращающая путь к каталогу Windows
Подметив различия двух версий, Вы поймете суть отличий параметров-строк и параметров — байтовых массивов. Вот пример с использованием строк:
Option Explicit
Private Declare Function GetWindowsDirectory _
Lib kernel32‘_,
Alias "GetWindowsDirectoryA" ( _.■
ByVai IpBufferAs String, _.
ByVal nSlze As Long _■ ‘'
) As Long■
Private Sub Form_Click() , •
Dim intN As Integer '■ '
Dim strA As String
устанавливаем размер строковой переменной
strA = Space$(256)■
intN = GetWLndowsDirectory(strA, 256}
отсекаем лишние символы
strA = Left$(strA, intN) :i.-
Print strA1-
End Sub
В этой версии функция возвращает путь к каталогу Windows в строковом параметре рВц^'егПеред ее вызовом я установил длину с троки равной 256 символам, и получив результат, отсек лишние сим- золы в строке.
ВНИМАНИЕ Прежде чем вызыватьАР1-функцию, всегда устанавливайте размер строки или байтового массива, которые она должна заполнить данными. Ваша программа, скорее всего, «рухнет», если Вы забудете про это!
А вот пример с использованием байтового массива:
Option Explicit
Private Declare Function GetWindowsDirectory _
Lib kerne!32 _
Alia? fiorWi пг(Г1\»/еП1 rent л гуД " (
ByRef IpBuffer As Byte, _, . ...
Byvar nSize As Long _ ) As Long
Private Sub Form_Click()
■Dim intN As Integer■ ■
Dim bytBuffer() As Byte
Dim strA As String,
' устанавливаем размер байтового массива
bytBuffer = Space$(256)
intN = uHLniuijowsDirectory(bytBuffer(0), 256)
strA = StrConv (bytBuffer , vbUnicode) отсекаем лишние символы.
strA = Left$(strA, intN)
■Print strA
End Sub
Присмотритесь к объявлению API-функции GetWindowsDirectory во втором примере. Первый параметр вместо строки, передаваемой по значению (ByVai), теперь принимает байтовый массив, передаваемый по ссылке (ByRef). Первоначально его объявление было таким:
ByVai IpBuffer As String
А в новой версии оно выглядит иначе:
ByRef bytBuffer As Byte
При передаче строковых параметров API-функциям модификатор By Vai необходим потому, что строковая переменная в действительности определяет место в памяти, где хранится адрес содержимого строки (в терминологии С — указатель на указатель). Моди-
фикатор ByVai означает, что передается содержимое области памяти, расположенной по адресу, который идентифицируется именем строки; иначе говоря, передается адрес, по которому находится фактическое содержимое строки. Во втором варианте вызова функции я передаю bylBufferiomo при использовании модификатора ByRef влечет передачу адреса первого байта массига. Результат в обоих случаях одинаков: функция GetWindowsDirectory слепо записывает в указанный буфер путь к каталоп Windows.
В коде присутствует важная строка, требующая пояснений:
strA = StrConvtbytBuffer, vbUnicode)*>
Эта команда преобразует двоичное содержимое байтового массива Б допустимую строку Visual Basic. Возможно и прямое присвоение динамических байтовых массивов строкам или, наоборот, строк динамическим байтовым массивам, как показано ниже: bytBuffer = strA strB = bytBuffer
Если Вы присвоите строку динамическому байтовому массиву, то число байтов в массиве будет вдвое больше количества символов в строке. Это происходит из-за того, что строки Visual Basic содержат символы Unicode, а каждый такой символ занимает 2 байта. Когда в элементы байтового массива преобразуются символы ASCII, каждый второй байт массива оказывается нулевым. (Второй байт используется для поддержки других наборов символов, скажем, алфавитов европейских и азиатских языков, что важно при написании приложений с многоязыковой поддержкой.) Однако API-функция GetWindowsDirectory возвращает буфер не в формате Unicode, и мы должны преобразовать его содержимое в формат Unicode вызовом функции StrConv, как и показано выше. В первой версии эту рутинную операцию выполняет сам Visual Basic — в момент заполнения строкового параметра.
Преобразование в Unicode «расширяет» каждый символ в буфере до двух байтов и тем самым удваивает число байтов в конечной строке. Это не столь очевидно, особенно если учесть, что значения выражений Len(tirA) и иВоипб(ф72Ц;у/<ч'Цосле преобразования массива в строку совпадают. Однако LenB(srrA) показывает значение, вдвое большее, чем функция ОВоипс1(&у/Вг^г)Вся штука в том, что Len возвращает число символов в строке, a LenB — количество байтов в строке. Длина Unicode-строки в символах (вспомним, что таковыми являются все строки в 32-разрядной версии Visual Basic) составляет только половину от количества байтов в строке, поскольку каждый Unicode-символ состоит из двух байтов.
Итак, преобразуя параметр API-функции из строкового типа в тип байтового массива, замените ключевое слово ByVai на ByRef, передайте первый байт массива вместо имени строки и, если двоич-
ные данные следует преобразовать в строку, используйте функцию StrConv с константой ubUnicode.
Обмен данными между строками и байтовыми массивами
Чтобы упростить обмен данными между строками и байтовыми массивами, разработчики Visual Basic предусмотрели особый случай присвоения динамических байтовых массивов строкам, и наоборот.
ПРИМЕЧАНИЕ Присвоить строку байтовому массиву можно только в том случае, если этот массив — динамический, а не фиксированный.
Простейший способ создать динамический массив байтов — указать при его объявлении пустые скобки, например так:
Dim bytBuffer() As Byte
В то же время следующий оператор Dim объявляет фиксированный массив байтов, пригодный для самых разных задач, но только не для присвоения строк. Попытка присвоить ему строку дает ошибку. Dim bytBuffег<80} As Byte
1^11о
Работать с датами и временем?
Переменная типа Date занимает 8 байтов, в которых размещается информация не только о дате, но и о точном времени. Выводя на экран содержимое переменной типа Date, Вы, как по волшебству, видите строковое представление года, месяца, дня, часа, минут и секунд, и все эти данные хранятся в 8 байтах! Конкретный формат вывода даты и времени зависит от стандартов, выбранных в Вашей системе. В последующих примерах я полагаю, что значения даты и времени всегда хранятся в переменных типа Date.
Применение элементов управления DTPicker и Calendar
Эти элементы управления очень удобны в получении и представлении информации, связанной с датами. DTPicker входит в Microsoft Windows Common Controls-2 (MSCOMCT2.OCX), a Microsoft Calendar содержится в отдельном файле MSCALOCX.
Если Вы хотите выводить или получать даты в окне списка, применяйте DTPicker. Пользователь, щелкнув этот элемент управления, откроет небольшую форму календарика и сможет выбрать из нее нужную дату (рис. 3-3).
А если Вы предпочитаете выводить или получать даты на отдельной странице календаря, используйте элемент управления Calendar. Обычно он занимает больше места, чем DTPicker, зато его возможности гораздо шире (рис. 3-4).
Рис. 3-3 DTPicker позволяет не только выбирать даты, но и вводить их в свое текстовое поле
* VBC<| Watt a D^nГ*"
Рис. 3-4 Элемент управления Calendar позволяет использовать разные шрифты, изменять вид подсветки выбранной даты
и предоставляет другие возможности
По умолчанию оба этих элемента управления показывают текущую дату. Чтобы программно изменить отображаемую дату, присвойте нужное значение свойству Value элемента управления. Но прежде чем присваивать дату элементу Calendar, обязательно удалите информацию о времени — иначе возникнет ошибка. Вот пример правильного и неправильного присвоения текущей даты элементу Calendar:
calDate. Value = Date ' правильный способ
calDate.Value = Now ' неправильный способ; даст ошибку ’
DTPicker таких ограничений не имеет, что, кстати, может вызвать проблемы при совместном использовании обоих элементов управления. Следующий фрагменткодаиллюстрирует, какинфор- мация о времени, записанная в DTPicker (cltpDate),приводит к ошибке после ее передачи в Calendar (catoate\.
dtpDate.Value = Date
calDate.Value = dtpDate.Value 1 пока все нормально dtpDate.Value - Now
calDate.Value = dtpDate.Value 1 а здесь возникнет ошибка!
Запись в переменные типа Date
Чтобы ввести значения даты и времени в такую переменную, заключите нужную информацию в символы *. Если Вы укажете в программе строку в этом формате, Visual Basic проверит ее синтаксис. При некорректных дате или времени Вы тут же получите сообщение об ошибке. Вот пример, в котором Date-переменной dtmD присваивается значение даты и времени:
Dim dtmD As Date
dtmD = #11/17/96 6:19:20 PM#
Это значение переменной dtmD используется и в других примерах.
Преобразовать числовые значения даты и времени в тип Date позволяют несколько функций. Например, DateSerial сводит числовые значения года, месяца и дня в одно значение типа Date. Функция TimeSerial аналогичным образом объединяет значения часов, минут и секунд.
dtmD = DateSerial(1996r 11, 17),
dtmD = TimeSerial(18, 19. 20)
Чтобы скомбинировать в одной переменной типа Date и дату, и время, просто сложите результаты, возвращаемые соответствующими ФУНКЦИЯМИ:
dtmD = DateSerial(1996, 11, 17) + TimeSerial) 18, 19, 20)
Для преобразования строкового представления даты или времени в значение типа Date предназначены функции DateValuc и TimeValue:
dtmD = DateValue('T1/17/96")■
dtmD = TimeValue("18:19:20")
И вновь, чтобы скомбинировать дату и время в одной переменной, просто сложите результаты этих функций:
dtmD = DateValue("Nov-17-96") + TlmeValue('6:19:20 PM")
Для строкового представления даты и времени существует множество форматов, но при оценке допустимости формата надо учитывать национальные стандарты, установленные в операционной системе. Тис порядок номеров месяца и дня в разных странах может быть прямо противоположным.
Отображение даты и времени '
Функция Format обеспечивает высокую гибкость в преобразовании переменной типа Date в строку, пригодную для вывода на экран или принтер. Вот пример с использованием предопределенных именованных форматов:
Format(dtmD, "General Date")
’ 11/17/96 6:19:20 PM
Format(dtmD, "Long Date")
' Sunday, November 17, 1996
Format(dtmD, "Medium Date")
' 17-NOV-96
Format(dtmD, "Short Date")
' 11/17/96
Format(dtmD, "Long Time")
' 6:19:20 PM'
Format(dtmD, "Medium Time")
'06:19PM
Format(dtmD, "Short Time")
'18:19
Помните, что результаты зависят от региональных стандартов, выбранных в Вашей системе. Запустите этот код и посмотрите, насколько отличаются Ваши результаты от приведенных здесь.
При отображении даты и времени можно использовать не только именованные, но и собственные форматы. Следующая строка кода форматирует компоненты даты и времени и сохраняет результат в строковой переменной.
strA = Format(dtmD, "m/d/yyyy hh:mm AM/PM") ' 11/17/1996 06:19 PM
Пользовательские форматы очень гибки. Так, чтобы сформировать название месяца из какой-либо даты, напишите: strHonthNams - Format(dtmD, "mmmm") ' November
Подробнее о многочисленных вариантах пользовательских форматов см. раздел справочной системы, посвященный функции Format.
Выборка компонентов
Извлечь отдельные компоненты из переменной типа Date позволяют несколько функций. Следующий пример послужит кратким «справочником» по этой группе функций:
Print Month(dtmD) '11.
Print Day(dtmD) '17 v ,,-.
Print Yaar(dtmD) ' 1996' ‘'
Print Hour(dtmD) '181■
Print Minute(dtmD)'19
Print Second(dtmD)'20■
Print WeekDay(dtmD)'1
Для значений, возвращаемых функцией WeekDay, в Visual Basic предусмотрен набор встроенных констант: vbSunctoyравна 1, vbMon- <Шу — 2 и так до rbSaturday. равной 7.
Вычисления с использованием дат и времени
Переменную типа Date Вы можете напрямую использовать в математических вычислениях, но не забывайте, что единица в ней -
день. Например, очень легко написать программу, вычисляющую возраст человека в днях (рис. 3-5). Для этого просто нужно вычесть дату его рождения, сохраненную в переменной типа Date, из результата, возвращаемого Date — системной функцией, которая сообщает текущую дату.
^:^м*1>лад <N i>,t^^ р*мRFiEi
Рис. 3-5 Переменные типа Date позволяют вычислять количество дней между двумя датами
Для преобразования времени в дату удобна функция TimeSerial. Допустим, Вы хотите узнать точную дату и время через 10 000 минут от данного момента. Вот как это сделать, не углубляясь в дебри математики:
dtmD = Now + TimeSerial(O, 10000, 0}
Здесь TimeSerial возвращает значение, представляющее комбинацию 0 часов, 10 000 минут и О секунд. Это значение суммируется с результатом функции Now, и Вы получаете переменную типа Date с нужными датой и временем.
Проверка допустимости даты и времени
Присваивая переменной типа Date значения даты и времени, введенные пользователем, можно перехватывать ошибку «Туре mismatch» (несоответствие типов), генерируемую Visual Basic при недопустимости одного из этих значений. Другой подход состоит в применении элемента управления Calendar, позволяющего выбирать лишь допустимую дату (со страницы на один месяц).
См. также...
• Программу-примерУВСа! из главы 31 «Дата ивремя», в которой представлено диалоговое окно для выбора даты; Вы можете встроить его в свою программу.
Дпвпгой Джон7 как.,. ; г Работать с переменными типа Variant?
Тип Variant чрезвычайно гибок (некоторые считают, что даже слишком) и тем не менее открывает ряд возможностей по более четкой структуризации данных.
Любые переменные, тип которых не объявлен в явном виде, относятся к типу Variant. В переменных типа Variant (будем называть их Variant-переменными) можно хранить практически все, что угод-
но, в том числе массивы, объекты, I ЮТ-структуры и другие Variant - переменные. Массивы всегда содержат элементы одного типа, а это значит, что в одном массиве нельзя смешивать, скажем, строки и числа. Но массив Variant-переменных позволяет обойти это ограничение. Обратите внимание на фрагмент кода, приведенный ниже. В нем создается массив типа Variant, в который заносится целое число, строка и другая Variant-переменная. Чтобы подчеркнуть его гибкость, я записываю в третий элемент первого Variant-массива другой Variant-массив. Так что в Variant-переменной можно хранить практически все!. -. -
Option Explicit.
Private Sub For*_CHck()
Dim i ' по умолчанию это Variant Dim vntMatn(1 To 3} As Variant Dim intXAs Integer,л
Dim strA As String,
Dim vntArray(1 To 20) As Variant ' заполняем первичные переменные 1"
strA = "This is a test. ” For i = 1 To 20
vntArray(i) = i A 2■
Next i ' записываем все в основной массив Variant vntMain(1) = intX.
vntMain(2) = strA vntNain(3) = vntArrayO.
' отображаем образцы содержимого основного массива Print vntMain(1) ' О'
Print vntNaln(2) ' This is a test. Print vntNain(3)(17) ' 289 End Sub•
В последнем операторе Print я обращаюсь к элементу Variant - массива, который в свою очередь является элементом другого массива. Элемент vntMain(3)( П) выглядит, да и работает, как элемент двухмерного массива, но смысл индексации во многом отличен. Этот прием — эффективный способ создания многомерных массивов с разным числом размерностей и даже с разнотипными элементами.
Циклы For Each
Тип Variant играет важную роль в циклах For Each...Next, позволяющих перебирать элементы набора (collection) или обычного массива. Допустимые типы переменной, служащей для доступа к каждому элементу, — Object или Variant. Перебирая элементы массива. Вы должны использовать переменную типа Variant, а перебирая элементы набора, можете использовать переменную чипа Variant или Object.
Гибкий тип для передачи параметров
Тип Variant часто назначается параметрам — особенно для свойств объектов, которые позволяют устанавливать один из нескольких типов данных. Вы можете передать через Variant-переменную практически любые данные.
Функции, связанные с типом Variant .
Вам не помешает знать о нескольких функциях, полезных при работе с Variant-переменными. Функция lypeName возвращает строку, которая описывает текущее содержимое Variant-переменной. Семейство is-функций — таких как IsNumerk и IsObject — позволяет быстро проверять содержимое Variant-переменной на соответствие определенному типу. Подробнее об этих и других функциях, связанных с Variant-переменными, см. справочную систему.
Empty и Null
Вы должны хорошо понимать разницу между значениями Empty и Null переменной типа Variant. Она равна Empty, пока ей не присвоено значение какого-нибудь типа, но Null — специальный индикатор, сообщающий, что Variant-переменная не содержит значимых данных. Null можно присвоить переменной явно, и тогда результатом любой математической операции с ее участием тоже будет Null. Чаще всего это значение используется при работе с базами данных, где оно указывает на отсутствие какой-либо информации.
Приведение типов данных
Тин Variant очень гибок, но требует крайне осторожного подхода к автоматическому преобразованию данных. Например, кое-что в следующей программе может оказаться для Вас неожиданным: Option Explicit
Private Sub Form_Click()
Dim vntA, vntB
vntA = "123"
vntB = True
' 122' 123True' 0’ False' 122' 123True' 0’ FalsePrint vntA + vntB
' 122
' 123True
' 0
’ False
' 122
' 123True
' 0
’ False
Print vntA & vntB
Print vntA And vntB = 0
Print vntB Ar-1 vntA = 0
End Sub
Первый оператор Print обрабатывает содержимое двух Variant - переменных как числовые значения, а второй — как строки. Последние два оператора Print выдают разные результаты из-за иерархии приоритетов операций, которая вовсе не очевидна. Максимум, что я могу посоветовать, — быть осторожным и тщательно прове-
рять написанный код; если в чем-то сомневаетесь, лучше напишите по-другому.
Дорогой Джон, как»,-
Работатьсостроками?"
В язык Visual Basic встроены пять новых строковых функций.
* Replace — заменяет одну группу символов другой. Это резко упрощает замену всех вхождений строки в тексте (текстовом поле или других источниках текста).
• Split — разбивает строку на массив строк меньшей длины, или лексем (tokens), исходя из указанного разделителя (пробела, табулятора или другого символа).
₽ Join — выполняет операцию, противоположную тому, что делает Split. Преобразует массив лексем в единую строку, где каждая лексема отделяется от соседней заданным разделителем.
я Filter — используется совместно с функциями Split и Join. Ищет в массиве строк заданную подстроку и возвращает массив строк, содержащих эту подстроку.
и InStrRev — в противоположность функции InStr — позволяет просматривать строку от конца к началу. Это полезно в программах, где нужно организовать операции поиска и замены в обоих направлениях: вперед (InStr) и назад (InStrRev).
На рис. 3-6 показано окно программы StringFun.VBP, которая демонстрирует все новые строковые функции. Операции, выполняемые этой программой, описываются в следующих разделах.
Рис. 3-6 Поочередно щелкая кнопки в нижней части окна ' StringFun.VBP, Вы увидите демонстрацию работы каждой из пяти новых строковых функций■
Замена символов в строке.■
Одна из самых распространенных задач программирования — преобразование текста из одного формата в другой. В большинстве преобразований нужно заменять одни разделители (табуляторы,
пробелы, скобки и т. д.) другими. Функция Replace обрабатывает операции такого типа «одним махом». Следующая процедура иллюстрирует, каке помощью Replace выполнить простейшее преобразование — заменить пробелы символами возврата каретки и наоборот. ' cmdReplace_Cilck использует Replace для глобального поиска и замены Private Sub cmdReplace_Click()
Static blnReplacad As Boolean
' заменяем пробелы символами возврата каретки
If Not blnReplaced Then
txtString = Replace(txtString, " ", vbCrLf) ' заменяем символы возврата каретки пробелами Else
txtString = Replace(txtString, vbCrLf, " ")
End If
blnReplaced = Not blnReplaced
End Sub
Разбиение и объединение строк
Такие операции — не редкость при извлечении информации из текстового ноля и ее переупорядочивании. Используя функцию Split совместно с функцией Join, можно разбивать текст на отдельные слова, изменять их порядок и выводить на экран, как показано на следующем примере.
' cmdReverse, используя Split и Join, меняет ' порядок слов в текстовом поле на обратный Private Sub cmdReverse_Click() .
Dim strForward() As String
Dim strReverse() As String
Dim intCount As Integer
Dim intUpper As Integer
' разбиваем текст на лексемы
strForward = Split(txtString, " ")■
' инициализируем другой массив
IntUpper = U Bound (st г Forward)
ReDim strfieverse(intUpper)
' меняем порядок слов в strReverse на обратный
For IntCount = 0 То IntUpper strReverse{intUpper - intCount) = strForward(intCount)
Next
' соединяем лексемы в единый текст, -.
txtString = Join(strReverse, " ")
End Sub
Главное назначение Split и Join заключается в преобразовании строк. Первая преобразует строку в массив лексем, а вторая — массив лексем в единую строку. Превратив строку г массив неких элементов, Вы можете переупорядочить его с помощью одной из стандартных процедур сортировки массивов либо извлечь определен-
ные элементы, используя фильтр, как описывается в следующем разделе." .
Применение фильтров
Функция Filter принимает массив строк и возвращает массив элементов, содержащих указанную подстроку. Обычно она используется совместно с функциями Split и Join при обработке списков элементов в текстовом поле или другом источнике текста. Показанная ниже процедура иллюстрирует, как «фильтровать» слова, вводимые в текстовом поле.
' cmdFilter„Click, вызывая Filter, отображает только та слова, - ' в которых присутствует заданная группа символов; это позволяет ' ' сужать список элементов, вводимых пользователем Private Sub cmdFilter..Click()
Dim strFilter As String
Dim strWords() As String
' получаем подстроку (группу символов) для поиска strFilter = InputBox("Show only words containing:") ' если ввод отменен, выходим■
If strFilter = "" Then Exit Subн ■
' избавляемся от символов возврата каретки.
txtString = ReplaceftxtString, vbCrLf, " ")'
' 'преобразуем строку в массив отдельных слов strWords = Split(txtString, " ")
' получаем список слов, содержащих подстроку из strFilter strWords = Filter(strWords, strFilter)
' отображаем список в текстовом поле'
txtString = Joln(strWords) , . - End Sub...
Поиск строк
Поиск определенного слова или предложения в блоке текста - стандартная операция, необходимая в текстовых процессорах, справочных системах и т. д. Функция InSri позволяет вести поиск заданной строки в блоке текста от его начала к концу, а с появлением функции InStrRev стал возможен и поиск в обратном направлении (от конца к началу). Следующие две процедуры демонстрируют эти виды поиска в текстовом поле.
' cmdBack_Click и cmdForward.Click ведут поиск заданной строки ' в текстовом поле с помощью InStr ("вперед") и InStrRev ("назад") ' и иллюстрируют приемы избирательной замены текста. . ... ;
Private Sub cmdBack_Click()
Dim strFind As String,"
On Error GoTo errNotFoundRev ’г '*’■ .
' получаем строку для поиска'
strFind = InputBoxC'String to find:". "Search Backward") txtString.SelStart = lnStrRev(txtString, strHnd, ..;.
txtStrlng.SelStart) - 1. . ,. ,,,,
txtStrlng.SelLength = Len(strFind)■ '
Exit Sub
errNotFoundRev:
MsgBox strFind & " not found.", vblnformation
End Sub
Private Sub cmdForward_Click()r. . .■
Dim strFind As String
On Error GoTo errNotFoundFor - - । .v!.
' получаем строку для поиска strFind = InputBox("String to find:", "Search Forward") txtStrlng. SelStart = lnStr(txtString.SelStart + 1, _ txtString, strFind) - 1
txtString.SelLength = Len(strFind)
Exit Sub
errNotFoundFor:
MsgBox strFind & " not found.", vblnformation
End Sub
Весьма странно, что у функций InSir и InStrRev разный порядок аргументов. В InStr позиция, с которой начинается поиск, указывается в первом аргументе, а в InStrRev — в третьем. Из-за такого разнобоя очень легко перепутать аргументы при совместном использовании этих функций.
Дорогой Джон, как...
Работать с объектами?.
Объектные переменные позволяют не только хранить информацию, но и выполнять операции. Поэтому работа с этими переменными требует учитывать ряд особенностей.
Новые объекты.... , .
Объекты объявляются несколько иначе, чем остальные переменные. Visual Basic инициализирует большинство переменных при первом их использовании. Скажем, Вы объявляете оператором Dim переменную тина Integer и, как только впервые используете эту переменную. Visual Basic автоматически инициализирует ее нулевым значением. Объекты так не инициализируются. Если Вы объявите объектную переменную и проверите ее тип, то получите «Nothing»:
Dim fmX As Forml
Debug.Print TypeName(frinX) ' отображается "Nothing"
Объектная переменная ни на что не годна, пока Вы не создадите экземпляр соответствующего объекта. Для создания нового объекта предназначено ключевое слово New:
Dim frmX As Forml
Debug.Print TypeName(frmX) ' отображается "Nothing"■
' отображается "Forml"' отображается "Forml"Set frmX = New Form) Debug.Print TypeName(frmX)
' отображается "Forml"
' отображается "Forml"
Простое объявление объектной переменной лишь создает переменную указанного типа, и. пока Вы не присвоите ей какой-либо объект, она содержит значение Nothing.
Ключевое слово New можно использовать и в объявлении объектной переменной:
Dim frmX As New Forml’■
Debug.Print TypeName(frmX) ' отображается "Forml"-
При таком объявлении ключевое слово New сообщает Visual Basic создать новый объект, как только он встретит ссылку на объектную переменную в исполняемом коде, а до тех пор установить ее как Nothing. В приведенном выше примере, поскольку оператор Dim не является исполняемым, Visual Basic откладывает создание объекта до выполнения оператора Debug-Print. Эта тонкость может показаться не слишком значимой, но иногда дает неожиданные результаты. Так, в следующем фрагменте кода Вы не получите «Nothing», хотя, наверное, предполагали обратное:
'Dim f rmX As New Forml’
Set frmX = Nothing
Debug. Print TypeNamettrmX) ' отображается "Forml"
На самом деле этот код создает экземпляр Forml в операторе Set и тут же уничтожает его. Потом, выполняя оператор Debug.Print, со- здяет новый экземпляр объекта. Чтобы избежать подобных фокусов, не объявляйте объектные переменные с ключевым словом New. Если хотите получать ожидаемые результаты, делайте так:■
Dim frmX As FormlSet frmX = New FormlDebug.Print TypeNametfrmX)Set frmX = NothingDebug.Print TypeName(frmX)Как правило, синтаксис;Dim ... As NewDim frmX As FormlSet frmX = New FormlDebug.Print TypeNametfrmX)Set frmX = NothingDebug.Print TypeName(frmX)Как правило, синтаксис;Dim ... As New' создается экземпляр формы' отображается "Forml"' уничтожается экземпляр формы' отображается "Nothing"' создается экземпляр формы' отображается "Forml"' уничтожается экземпляр формы' отображается "Nothing"
Dim frmX As Forml
Set frmX = New Forml
Debug.Print TypeNametfrmX)
Set frmX = Nothing
Debug.Print TypeName(frmX)
Как правило, синтаксис;
Dim ... As New
Dim frmX As Forml
Set frmX = New Forml
Debug.Print TypeNametfrmX)
Set frmX = Nothing
Debug.Print TypeName(frmX)
Как правило, синтаксис;
Dim ... As New
' создается экземпляр формы
' отображается "Forml"
' уничтожается экземпляр формы
' отображается "Nothing"
' создается экземпляр формы
' отображается "Forml"
' уничтожается экземпляр формы
' отображается "Nothing"
следует применять лишь для создания объектов, которые будут существовать вплоть до выхода из области видимости того элемента программы (модуля, процедуры и т. д.), где они объявлены, а в остальных случаях используйте синтаксис: .
Dim .,. As .,.
Set . . . New , , .
Существующие объекты ‘ ’
Объектная переменная ссылается на объект, но не является объектом. Оператор Set позволяет переназначить объект, на который ссылается объектная переменная;
Dim frmX As New Forml Dim frmY As New Forml Set frmX = frmYDim frmX As New Forml Dim frmY As New Forml Set frmX = frmY' создаем новый объект
Dim frmX As New Forml Dim frmY As New Forml Set frmX = frmY
Dim frmX As New Forml Dim frmY As New Forml Set frmX = frmY
' создаем еще один экземпляр
' frmX и frmY ссыпаются теперь на один
' и тот же объект
' Присваивая переменной ссылку на объект, следует применять именно Set, а не простое присвоение Дело в том, что у большинства объектов есть свойство, используемое по умолчанию (default property), и Set указывает Visual Basic присвоить ссылку на объект, а не установить значение такого свойства.
Операции над объектами
Объекты сравниваются оператором Is. Он сообщает, ссылаются ли две объектные переменные на один и тот же объект. Вот пример, иллюстрирующий этот оператор:
Dim frmX As New Forml Dim frmY As New Forml Debug. Print frmX Is frmY Set frmX = frmYDebug. Print frmX Is frmYDim frmX As New Forml Dim frmY As New Forml Debug. Print frmX Is frmY Set frmX = frmYDebug. Print frmX Is frmY' создаем новый объект
Dim frmX As New Forml Dim frmY As New Forml Debug. Print frmX Is frmY Set frmX = frmY
Debug. Print frmX Is frmY
Dim frmX As New Forml Dim frmY As New Forml Debug. Print frmX Is frmY Set frmX = frmY
Debug. Print frmX Is frmY
' создаем еще один экземпляр
' отображается False
' frmX и frmY ссылаются теперь на один и тот же объект
' отображается Тrue
Уничтожение объектов
Сколько живет объект? Пока не станет Nothing!
Объекты не уничтожаются, пока существует хоть одна переменная, которая ссылается на них, или пока — как в случае с объектами-формами — они видимы, Обычно Вы можете управлять видимостью объекта через сн> свойство Visible. Чтобы избавиться от объекта, присвойте его переменной другой объект или Nothing:
Dim
frmX As New Forml
Dim
frmY As Object
Set
frmY = frmX
Set
frmX = Nothing
Set
frmY = Nothing
' создаем новый объект
' объявляем переменную типа Object
' frmX и frmY ссылаются теперь на один ' и тот же объект
1 frmY все еще содержит ссылку,
' и объект пока существует
' объект уничтожается
По крайней мере, так должно быть. Но некоторые объекты не столь корректны. Ранние версии отдельных объектов (в том числе и от Microsoft) самоуничтожались отнюдь не всегда. Сейчас объекты «стали получше», но все равно следует быть очень внимательным, работая с объектами, не имеющими визуального представления. В общем, я бы сказал таю объекты, создаваемые в Visual Basic, ведут ■себя корректно, а объекты в других продуктах — обычно корректно.
Си. также...
• Главу 5 «Объектно-ориентированноепрограммирование» — под
робнее об объектах.
Допетой Джон. как...
Работать с предопределенными константами?
В Visual Basic фактически несколько видов констант, включая определенные в операционной системе.
Константы компилятора
Если Вам нужно приложение как в 16-,так и в 32-разрядной версии, используйте Visual Basic 4 — только эта версия позволяет создавать программы для Windows 3-1 (16-разрядные), Windows 95 и Windows NT 3.51 (32-разрядные). Чтобы создать приложение, выполняемое и в 16-, и в 32-разрядной операционной системе, необходимы константы компилятора w ш 16 и Win32. Они дают возможность определять, в какой операционной системе Вы разрабатываете свое приложение, и выдираз ь соответствующие блоки кода (удовлетворяют) I,. заданным условиям).
Эти константы указываются в директивах I Г.. *Then...#ElseH позволяют компилировать лишь избранные блоки кода. Visual Basic версий 5 и 6 по-прежнему поддерживает эти константы, но Win32 всегда True, a Winl6 всегда False, так как эти версии работают исключительно в 32-разрядных операционных системах Windows.
Константы Visual Basic
В Visual Basic громадный список предопределенных констант практически на все случаи жизни. Чтобы увидеть дисзунные константы, щелкните кнопку Object Browser, в верхнем списке выберите VBA или VB, а в списке классов — нужную группу констант. Теперь в списке компонентов (справа) будут перечислены константы с соответствующим значком и префиксом vb, как показано на рис. 3-7.
г мшг мш^£|№»йэ^£|№»йэ*< ffU^. of MrJiM^Horfh*^' ■11 MljArol 2I voi?an?elО vblgnore a. Mtnu"tSvOOk zj VORMry*< ffU^. of MrJiM^Horfh*^' ■11 MljArol 2I voi?an?elО vblgnore a. Mtnu"tSvOOk zj VORMryi^nrn;»i ir- 1 iih‘ WtaertncdeOr^ifUi^nrn;»i ir- 1 iih‘ WtaertncdeOr^ifU
г мш
г мш
^£|№»йэ
^£|№»йэ
*< ffU^. of MrJiM^Horfh*^' ■11 MljArol 2I voi?an?el
О vblgnore a. Mtnu"
tSvOOk zj VORMry
*< ffU^. of MrJiM^Horfh*^' ■11 MljArol 2I voi?an?el
О vblgnore a. Mtnu"
tSvOOk zj VORMry
i^nrn;»i ir- 1 iih
‘ WtaertncdeOr^ifU
i^nrn;»i ir- 1 iih
‘ WtaertncdeOr^ifU
Рис. 3-7 Встроенные константы, показываемые в окне Object Browser
Заметьте, что у многих компонентов тоже есть свои константы, и это окно — именно то место, где с ними можно ознакомиться.
Вы обнаружите, что почти для каждого элемента управления и параметра функции предопределены свои константы, например: ubModaiw rbModeltiSsmvR метода Show формы, vbRed, ubBluen vbGre- №для обозначения цветов, применяемых в большинстве графических методов, и даже t^bCr, vbLf,vbCrLf ияЬТаЬ для часто используемых неотображаемых символов. Следующий фрагмент кода иллюстрирует, как включить в текстовую строку символы возврата каретки (Сг) и перевода строки (If):
strA = "Line one” & vbCrLf & "Line two"
Начиная писать числовое значение в качестве аргумента свойства или метода, остановитесь и проверьте, не определена ли в системе константа, которую Вы могли бы использовать вместо числового значения. Это поможет сделать программу «читабельнее» и удобнее в сопровождении.
Пользовательские константы
Как и в предыдущих версиях Visual Basic, Вы можете определять в программе собственные константы с помощью ключевого слова Const, а также создавать константы условной компиляции, используя директиву *Const (она, хоть и похожа на ключевое слово Const, предназначена совсем для других целей). Константы, созданные директивой *Const, ПрИМеНЯЮТСЯ ТОЛЬКО В КОНСТРУКЦИЯХ ^Tf ^ТТ.-п *Else. И наоборот, константы, определенные ключевым словом Const, не могут быть константами условной компиляции.
Некоторые из предопределенных констант очень полезны при формировании сложных строковых констант. Так, в следующем примере константа выводится на форму как колонка цифр. В предыдущих версиях Visual Basic такой формат был бы невозможен без написания дополнительного кода, а сейчас достаточно единственного оператора.
Option Explicit
Private Sub Гогт_С11ск()
Const DIGITS = _
"1" & vbCrLf & _
"2" & vbCrLf & _
"3" A vbCrLf & _.
"4" & vbCrLf & _.
"5" & vbCrLf & _
"6" & vbCrLf & _
"7" & vbCrLf & _
"8" 4 VbCrLf &_
"9” & vbCrLf
Print DIGITS
End Sub
Определить диапазон действия констант, объявленных на уровне модуля, позволяют ключевые слова Private и Public. Константы, объявленные как Private, видимы лишь внутри модуля, а Public-константы доступны ■ - всем проекте. Эти ключевые слова не годятся для определения констант внутри процедур. Константа, объявленная в процедуре, всегда видима только в этой процедуре.
Перечислимые типы
Перечислимые типы (Enum — для краткости) дают возможность связывать целые значения с именами. Это полезно в процедурах- свойствах. Например, у свойства SpellOption могут быть такие допустимые значения:
' объявления на уровне модуля класса MailDialog..
Public Enum CheckedState
Unchecked' перечисление по умолчанию начинается с О
Checked' 1
Grayed' 2 и т. д.
CantDeselect = 255' (можноуказывать и специфические значения)
End Enum
Используя этот Enum в объявлении параметров, можно определить свойства, которые устанавливают или возвращают состояние флажков в диалоговых окнах:
Public Property Let SpellOptionCSetting As CheckedState)
' устанавливаем состояние флажка Check Spelling
' (проверять правописание)':
frmSendMail. chkSpelling. Value = Setting"■
End Property1
Public Property Get SpellOptionQ As CheckedState
' возвращаем состояние флажка Check Spelling
SpellOption = frmSendMail.chkSpelling.Value.
End Property
Enum можно использовать в любых модулях. В Object Browser они появляются в списке классов, а набор их допустимых значений — в списке компонентов. Но в других проектах видимы только Enum, объявленные как Public в открытых модулях классов.
Флаги и битовые маски .
Логические операции And, Or и Not чаще всего применяют для комбинирования логических сравнений в управляющих конструкциях. Например, оператор If с помощью операции And позволяет проверить несколько условий:.
If blnExit And Not blnChanged Then End
Логика — это прекрасно, но по-настоящему хороший оператор должен допускать и побитовые операции, т. е. сравнение каждого
бита двух чисел. Побитовые операции очень удобны, если Вы хотите включить несколько смысловых значений в одно числовое — флаг. Хороший пример флага — свойство Flags диалогового окна Print (печать): ,
DIgPrint.Flags = cdlPDCollate Or cdlPDNoSelection
В большинстве случаев комбинирование флагов операцией Or эквивалентно их сложению (+). Однако, если значения флагов конфликтуют, Вы получите при сложении другой результат, например, 1 + 1 = 2, al Or 1 = 1. Этого не произойдет, если значения флагов хорошо продуманы, но операция Or все же безопаснее.
Обнулить флаги или проверить значение отдельного флага позволяет операция And. Этот код проверяет, выбрана ли в диалоговом окне Print печать в файл:
' проверяем, печатать ли в файл
If dIgPrint. Flags And CdlPDPrintToFile Then
Здесь с помощью операции And проверяется, установлен ли 5-й бит (значение константы cdlPDPrintToFile равно &Н2й). если да, выражение дает True. При такой проверке флагов используется битовая маска — набор проверяемых битов, например 01101101 в двоичном виде или &H6D в шестнадцатеричном. Если в проверяемом значении биты установлены также, как в маске, то говорят, что маска «подходит» значению.
Именно маскированием мы и преобразовывали целые со знаком в беззнаковые значения. Целые со знаком принимают значения от &Н8000 до &H7FFF (или от -32 768 до +32 767 в десятичном виде). Маскируя определенные биты, можно преобразовать целое со знаком в беззнаковое:
intShort = (IngLong And &H7FFF4) - (IngLong And 4H80004)
Допогом Джон, как... "
Создавать UDT-структуры?
Для объявления UDT-структуры данных предназначено ключевое слово Туре. Заметьте, что оператор Туре не создает структуру, а только описывает ее. Создание переменных — экземпляров структуры осуществляется оператором Dim.
Определить откоытую UDT-структуру можно в стандартном модуле или в открытом модуле класса. Внутри модуля формы или класса Вы должны определять ее только как закрытую (Private) для данного модуля. Комбинация с типом Variant дает удивительно гибкие структуры. Некоторые возможности демонстрирует этот фрагмент кода: Option Explicit
Private Type typX
а<) As Integer b As String c As Variant
End Type
Private Sub Fonn_Click()
' создаем переменную типа typX'
Dim X As typX
' изменяем размер динамического массива внутри структуры
ReDiin Х.а(22 То 33)
' присваиваем значения полям структуры
Х.а(27) = 29
Х.Ь = "abc"
' вставляем целый массив в структуру
Dim у<100) As Double
у(ЭЭ) = 4 * Atn(1)'
Х.с = у()
' проверяем отдельные элементы структуры
PrintPrintPrintEnd SubPrintPrintPrintEnd SubХ.а(27)Х.ЬХ.с(ЗЗ)Х.а(27)Х.ЬХ.с(ЗЗ)29
End Sub
End Sub
Х.а(27)
Х.Ь
Х.с(ЗЗ)
Х.а(27)
Х.Ь
Х.с(ЗЗ)
' abc
' 3.14159265358979
Обратите внимание на третье поле структуры typX, объявленное как Variant, и вспомните, что этот тип позволяет хранить практически любые значения, а следовательно, в период выполнения можно создать массив и вставить его в UDT-структуру, присвоив полю типа Variant.
Выравнивание
Стремясь во всем соответствовать стандартам 32-разрядной операционной системы, Visual Basic 6 выравнивает элементы UDT-структур по границам, кратным 4 байтам, — этот процесс называют также выравниванием по двойным словам (DWORD alignment). Таким образом, объем памяти, в действительности занимаемый структурой, может оказаться больше, чем ожидалось. Но еще важнее то, что старый трюк с передачей оператором LSet двоичных данных из одной UDT-структуры в другую может сработать некорректно.
Вы должны понимать, что здесь кроется потенциальный источник проблем, и, прежде чем прибегнуть к каким-либо трюкам, тщательно тестируйте код. Иначе могут появиться такие ошибки, которые потом очень трудно отловить.
Дорогой Джон, как.».. .
Создавать новые типы данных с помощью классов?
Для начала немного терминологии. Классы, или модули классов, определяют объекты — точно также, как UDT-структуры определяют
пользовательские типы данных. Реальный объект — это экземпляр класса. Свойства класса определяют, что будет хранить объект, а методы — какие операции он сможет выполнять.
Классы могут быть и простыми, и очень сложными. Начинать, конечно, лучше с простого. Здесь Вы узнаете, как создать беззнаковый целый тип данных, используя модуль класса. Более сложные аспекты ООП см. в главе 5 «Объектно-ориентированное программирование».
Создание нового типа данных
В первом разделе этой главы я показал, как эмулировать в Visual Basic беззнаковые целые. Включите тот код в класс L Ьн, и Вы создадите новый «фундаментальный» тип данных для работы с беззнаковыми целыми.
Объявления на уровне модуля класса UInt, показанные ниже, определяют UDT-структуры и закрытые переменные для хранения старшего и младшего байтов целого значения. Считается хорошим тоном описывать назначение класса, а также включать комментарии в текст его модуля. Это делает класс более понятным и пригодным для повторного использования.
' уровень модуля класса UInt. ■■
' реализует беззнаковый целый тип.
' методы:
Signed (возвращает целое значение со знаком)
' свойства:
’ Value (по умолчанию)
’ HiByte
1 LoByte
' UDT-структуры для возврата старшего и младшего байтов
Private Type UnsignedlntType
Io As Byte■
hi As Byte. >. ■■ r. ;-
End Type-.. ..
Private Type SignedlntType
n As Integer
End Type
' внутренние переменные для младшего и старшего байтов
Private mnValue As SignedlntType
private muValue As UnsignedlntType
Класс использует закрытую переменную mnValue уровня модуля для хранения фактического значения свойства, но доступ к ней контролируется процедурами-свойствами Let Value и Get Value, показан-
ными ниже. Эти процедуры выполняют и преобразования, необходимые для представления беззнакового целого.
Property Let Value(lngln As Long)
mnValue.n = (Ingin And &H7FFF4) - (Ingin And &H8000&)
iLSet muValue = mnValue
End Property
Property Get Value() As Long
Value = mnValue.n And &HFFFF&
End Property'
У каждого свойства класса UInt есть Let-процедура, где свойству присваивается значение, и Get-процедура, которая возвращает значение свойства. Именно так и реализованы свойства HiByte и LoByte: Property Let HiByte(bytIn As Byte)
muValue.hi = bytin
LSet mnValue = muValue■
End Property
Property Get HiByte() As Byte
HiByte = muValue.hi
End Property
Property Let LoByte(bytln As Byte)
muValue. Io = bytln
LSet mnValue = muValue
End Property- ■ ■ .■ •
Property Get LoByteO As Byte
LoByte = muValue. Io
End Property
И, наконец, у класса UInt имеется открытая функция, которая возвращает целое значение со знаком:
Function Signed() As Integer
Signed = mnValue.n■
End Function
Использование нового типа данных’
Перед работой с классом UInt не забудьте объявить соответствующую объектную переменную с ключевым словом New — это создаст новый экземпляр класса.
Dim uNewVar As New UInt
Свойство Value класса UInt — используемое no умолчанию. (Об определении свойства, используемого по умолчанию, см. главу 5 «Объектно-ориентированное программирование».) Поэтому имя свойства Value можно опустить, как здесь:
’ устанавливаем значение свойства, используемого по умолчанию uNewVar = 64552
А к свойствам HiByte и LoByte Вы обращаетесь так же, как и к любому другому свойству в Visual Basic:
' устанавливаем старший байт.
uNewVar.HiByte = iHFF..
' получаем значение младшего байта Print Hex(uNewVar. LoByte)
ГЛАВА 4
Параметры
ТПараметры в Visual Basic бывают обязательные и необязательные. Они принимают данные или одного типа или любого, включая пользовательские типы, массивы и перечислимые константы г Enum). Такая гибкость поневоле требует мудрого подхода к определению параметров для того, чтобы не передавать в процедуры недопустимых значений.
В этой главе я расскажу, как и когда использовать именованные и необязательные параметры, а также как объявлять типы данных в заголовках процедур. Большая часть этих языковых средств была добавлена и усовершенствована еще в прошлых версиях Visual Basic. Хоть они и не новы для нас, все же большинство программистов, вероятнее всего, не настолько широко применяют их в повседневной работе и могут просто не знать о некоторых их особенностях и возможностях.
ПРИМЕЧАНИЕ В этой главе аргументом считается константа, переменная или выражение, передаваемые процедуре, а параметром — переменная, которая объявлена в заголовке процедуры и которой присваивается аргумент при передаче его в процедуру.
—......
Использовать именованные аргументы?
В Visual Basic (как, впрочем, и почти в любом языке программирования) принято передавать аргументы по позициям соответствующих параметров в заголовке процедуры, разделяя их запятыми. Так, если Вы пишете процедуру, принимающую параметры, допустим, sngRt-d. sngGreen и sugBlue, то передаете ей эти три значения и подразумеваете, что первый аргумент всегда представляет sngRed, второй — sngGreen, а последний — sngBlue.
Теперь посмотрим на показанную ниже процедуру и вызывающий ее код. Я использовал так называемые именованные аргументы, и это позволило передать их в обратном порядке!
Option Explicit1
Private Sub Form€olor(sngRed As Single, sngGreen As Single, _
■ sngBlue As Single)
BackColor = RGB(sngRed * 256, sngGreen * 256, sngBlue * 256)
End Sub
Private Sub Form„Click()
FormColor sngBlue:=0, sngGreen:=O.5, sngRed:=1 ' коричневый End Sub
Истинная ценность именованных аргументов не только в возможности их передачи в произвольном порядке. Они разрешают четко указывать и передавать подмножество параметров в процедуру с большим числом параметров. Но последнее преимущество именованных аргументов проявляется, только когда отдельные параметры процедуры необязательны, а это подводит нас к следующему вопросу.
Д ’ -.
Использовать необязательные параметры?
Необязательные параметры особенно полезны, когда у процедуры длинный список параметров. Слегка модифицируем предыдущий пример и посмотрим, как это все работает:
Option Explicit
Private Sub FormColor( _
Optional sngRed = 0, _> r ■ г
Optional sngGreen = 0, _
Optional sngBlue = 0 _
)
BackColor = RGB(sngRed * 256, sngGreen * 256, sngBlue * 256} End Sub
Private Sub Form_Click()
FormColor sngGreen: =0.5' зеленый цвет средней насыщенности
End Sub
Добавив ключевое слово Optional к объявлениям параметров в процедуре FormColor, мы получили возможность передавать все или отдельные параметры как именованные аргументы, четко указывая, какие, собственно, параметры передаются. В этом примере я передал лишь параметр sngGreen, зная, что по умолчанию остальные параметры получат нулевое значение.
Заметьте, что значения по умолчанию определяются при объявлении параметров. В Visual Basic 4 каждый необязательный параметр приходилось проверять вызовом функции IsMissing и лишь потом можно было присвоить ему значение по умолчанию. Теперь необязательные параметры могут быть не только типа Variant. Но в том случае, когда какой-то параметр объявлен с ключевым словом
Optional, все последующие в списке параметры тоже должны быть необязательными.
См. также...
Приложение Metric в главе 30 «Средстваразработки», в котором демонстрируется применение необязательных параметров.
Дорогой Джон, как...
Передавать параметры-массивы?
Если Вы объявите последний параметр процедуры как массив Variant-переменных с ключевым словом ParamArray, то сможете передавать переменное число аргументов. Следующая программа демонстрирует этот прием — она преобразует любые аргументы, передаваемые функции MakeVerticalList, в строку, содержащую их список в формате с вертикальным расположением:
Option Explicit
Private Function NakeVerticalList(ParamArray vntNO) As String
Dim strA As String, i As Integer
For i = LBound(vntN) To UBound(vntN)
StrA = StrA + vbCrLf + CStr(vntN(i))
Next i
MakeVerticalList = strA,■1
End Function
Private Sub Form_Click()'
Dim intA As Inteaer„'
Dim sngB As Singler
Dim strC As String
intA = 123
sngB = 3.1416
■strC = "This is a test."_
Print MakeVerticalList(A, В, C)"
Print HakeVerticalList("This", "time", "well", "pass five", _ "string arguments.")
End Sub
Заметьте, что я вызывал MakeVerticalList дважды: первый раз с 3 аргументами разных типов, а второй — с 5 строковыми аргументами.
Результаты, выводимые программой (Вы должны щелкнуть форму), показаны на рис. 4-1.
Длйг^глй Ожпм
Передавать в параметре произвольный тип данных?
Вспомним, что переменные типа Variant могут содержать данные практически любого типа, какой только можно себе представить.
Это позволяет передавать данные произвольного типа, если параметр объявлен как Variant.
Здесь я передаю процедуре VariantTest целое, строку и массив:
Option Explicit.
Private Sub VariantTest(vntV) 4'
Print TypeName(vntV)
End Sub
Private Sub Form_Click()
Dim dhiAfn 4, 5, 6, 7) As Double -
VariantTest 123
VariantTest "This is a test string"
VariantTest dbiA
End Sub
Variant-параметр rntV— единственный параметр процедуры VariantTest — принимает все, что ни передай. А функция TypeName сообщает тип переданного значения.
Результаты выполнения этой программы показаны на рис. 4-2.
Рис. 4-1 Результаты передачи в функцию MakeVerticalList сначала трех, а затем пяти аргументов
Рис. 4-2 Результат работы процедуры, использующей параметр типа Variant
Использовать в параметрах перечислимые типы?
Перечислимый тип позволяет ограничить диапазон допустимых значений параметра. Он определяет набор символических значе-
ний — почти так же, как и Const, — но название перечислимого типа можно включить в объявление параметра процедуры. При этом, когда Вы будете писать оператор, который вызывает такую процедуру, средство Auto List Members покажет список допустимых значений для аргумента. Чтобы увидеть, как это происходит, наберите КОД:
Enum Number"
Zero
One-u
TWO" .'"l
Three
End Enum■■
Sub ShowNumber(Value As Number)L "
MsgBox Value"i ' ‘
■End Sub.."
Sub Form_Load()
ShowNumber One"
End Sub
Стоит лишь в процедуре обработки события Form_Load ввести ShowNumber, как Visual Basic откроет список значений перечислимого типа, из которого можно выбрать нужное. Нечто похожее происходит, когда перечислимый тип используется в списке параметров какого-либо свойства нестандартного элемента управления. Следующий код создает для такого элемента управления свойство Value и показывает его в окне свойств;
Enum Number"..
Zero
One.
Two. ■-
End Enum"'
Dim mValue As Number■'
Property Get Value() As Number
Value = mValue'
End Property
Property Let Value(Setting As Number)
■mValue = Setting
End Property.
Private Sub UserControl„AeadProperties(PropBag As PropertyBag)
mValue = PropBag.ReadProperty("Value", One)
End Sub,
PropBag.WriteProperty "Value", mValue, One End Sub
Процедуры обработки событий ReadProperties и WriteProperries обеспечивают работу со свойством Value в окне Properties (свойства) данного элемента управления. Выбрав в нем свойство Value, Вы увидите список допустимых значений перечислимого типа Number: Zero, One и Two (рис. 4-3).
Рис. 4-3 Список допустимых значений свойства Value, открываемый в окне Properties
Еще один бит полезной информации о перечислимых типах — они автоматически приводят значения с плавающей точкой кдлин- ным целым. Например, если процедуре ShowNumber передать значение 0.9, она покажет 1. Перечислимые типы не предусматривают какого-то особого контроля типов, чудно принимая и недопустимые значения, поэтому Вы должны сами заботиться о проверке входных значений.
ГЛАВАГЛАВА55
ГЛАВА
ГЛАВА
5
5
Объектно-ориентированное программирование-
Из всех возможностей Visual Basic наиболее важна, пожалуй, поддержка настоящего объектно-ориентированного программирования (ООП). Это обширная тема, которой уделяется немалое внимание во многих руководствах Microsoft^ не стану их пересказывать, а рассмотрю основные концепции ООП, иллюстрируя их несложными примерами. Так у Вас появится база для самостоятельного изучения этой темы.
ООП в Visual Basic — вещь просто очаровательная. Модули классов позволяют структурировать программы ранее недоступными способами, наборы — формировать гибкие структуры данных для хранения создаваемых объектов и операций над ними, а технология ActiveX обеспечивает разделение объектов между приложениями и их (объектов) повторное использование. Вы увидите, насколько ООП упрощает структуру больших и сложных проектов.
Если Вы только-только приступаете к изучению средств Visual Basic, связанных с ООП, Вас ждет море новой информации. Немного настойчивости, экспериментов с примерами из этой или другой книги, и Вы освоитесь. Со мной это случилось, когда я вдруг почувствовал, что создавать объекты чертовски увлекательно!
В этой главе мы с Вами создадим образцы объекта и ЕХЕ-серве- ра на базе ActiveX, рассмотрим один из способов работы с наборами объектов и познакомимся с полиморфизмом и дружественными методами...
рее--, 1-. -,
Выбрать между EXE- и DLL-сервером ActiveX? *
EXE- и DLL-серверы АсПуеХпозволяют включать объекты в компоненты, способные предоставлять эти объекты другим приложениям через Автоматизацию (Automation).
Клиентское приложение, которое создает и использует экземпляры объектов, предоставляемые ЕХЕ-сервером ActiveX, оперирует с ними как с внешними (out-of-processy, т. е. клиент и сервер исполняются в разных процессах. .
Что касается DLL-сервера ActiveX, то он не может работать как автономное приложение и предоставляет клиентскому приложению динамически подключаемую библиотеку объектов, с которыми клиент взаимодействует как с внутренними (in-process). Таким образом, код DLL-сервера ActiveX выполняется в том же процессе, что и клиентское приложение, и программа работает быстрее и эффективнее. Оба типа ActiveX-компонентов одинаково полезны, и Visual Basic позволяет создавать как те, так и другие, не прибегая к С или каким-то иным языкам.
Дорогой Джон, как...
Разместить все свои объекты во внешних ActiveX-компонентах?
А это и не нужно! Вокруг объектов в ActiveX-компонентах много шумихи — не без причин, надо сказать, — но создание собственных объектов лучше начать с простого добавления модулей классов к проектам обычных исполняемых программ.
Объекты, определенные в модулях классов проекта, доступны только в нем. (Если Вы хотите, чтобы экземпляры Ваших объектов можно было создавать и из других приложений, не спешите — мы еще вернемся к ActiveX-компонентам.) Ваша программа может создать один или несколько экземпляров любого объекта, определенного в модуле класса, при этом каждый экземпляр получает свой набор данных. Одно из преимуществ этих объектов в том, что память, занятая каждым экземпляром, освобождается сразу после разрушения объекта. Но самое важное, что принесло нам ООП, — улучшение структуры программ, организации и «читабельности» их кода. Избавление Бейсика от номеров строк около десяти лет назад было первым крупным шагом в верном направлении, и введение в язык объектов — событие не меньшего масштаба.
В следующих двух разделах мы рассмотрим сравнительно несложный пример объекта, создаваемого в стандартной исполняемой программе. Это простейший способ начать работу с объектами. Дорогой Джон, как...
Создать новый объект?
Если кратко — создать модуль класса. Модули классов открывают новые горизонты в структурировании программ. Все тонкости работы с модулями классов и объектами, создаваемыми с их помощью, подробно описаны в руководствах и справочной системе. Я же приведу простой пример — лишь для того, чтобы пробудить в Вас интерес и сориентировать в дальнейшем обучении. Кстати, в книге довольно много рабочих примеров модулей классов; они
позволяют создавать структурированный код, и мне нравится использовать их.
Пример модуля класса: loan
Чтобы продемонстрировать многие важные особенности объектов, я написал сравнительно простой модуль класса, позволяющий создавать экземпляры объекта Loan. Он предназначен для составления графика платежей в течение всего срока займа (loan). Вы можете добавить к этому классу новые методы и свойства или изменить его реализацию, я же стремился максимально упростить его и подготовить нечто вроде наглядного пособия.
Чтобы создать модуль класса, откройте новый проект стандартной ЕХЕ-программы. выберите из меню Project команду Add Class Module (добавить модуль класса) и в появившемся диалоговом окне дважды щелкните значок Class Module. Наберите код, приведенный ниже, измените значение свойства Name на Loan и сохраните модуль как файл LOAN.CLS.
' LOAN.CLS - модуль класса, матрица объектов Loan
Option Explicit
' эта переменная известна лишь в самом нодуле класса_
Private mintMonths As Integer ' срок займа в месяцах,
’ свойство для чтений и записи: Principal
Public Principal As Currency
' свойство для чтения и записи: AnnuallnterestRate
Public AnnuallnterestRate As Single■
' свойство Д' я чтения и записи: Months
' эта процедура устанавливает значение свойства Months
Property Let Honths(intMonths As Integer)■
mintMonths = intMonths
End Property
эта процедура возвращает текущее значение свойства Months
Property Get Months() As Integer.,.
Months = mintMonths■
End Property■‘'
' свойство для чтения и записи: Years. ■ -
" эта процедура устанавливает значение свойства Years Property Let Years(intYears As Integer)
mintMonths = intYears * 12.
End Property.
' эта процедура возвращает текущее значение свойства Years .Property Get Years() As Integer
Years = Round(mintMonths / 12#, 0)
End Property■ ■ : ■'
’’ "~" свойство только для чтения: Payment
" эта процедура возвращает текущее значение свойства Payment
Property Get PaymentO As Currency
Dim sngNonthlylnterestRate As Single
1 проверяем, заданы ли значения всех свойств
If PropertiesAreLoadedO Then
sngNonthlylnterestRate = Annuallntere=rnara / 1200
Payment = (-sngNonthlylnterestRate * Principal) / _ ((sngNonthlylnterestRate + 1) A(-mintHonths) - 1)
Else
Payment = 0
End If■
End Property '
' свойство толвко для чтения: Balances)
' эта процедура возвращает массив ежемесячных остатков по займу
Property Get Balanced As Currency()
Dim intCount As Integer
Dim curPayment As Currency.- ..
ReDim curBalance(O) As Currency
If PropertiesAreLoadedO Then
ReDimcurBalance(mintMonths)
curBalance(O) = Principal
curPayment = Round(Payment, 2)' округляем до ближайшего пенни
For intCount = 1 To mintHonths
curBalance(intCount) = curBalance(intCount - 1} * _
(1 + AnnuallnterestRate / 1200)
curBalance(intCount) = curBalance(intCount) - curPayment
curBalance(intCount) = Round(curBalance(intCountj, 2)
Next intCount
End If
Balance = curBalance
End Property
' метод: Reset-
' заново инициализирует все свойства"
Public Sub ResetC)
Principal = 0
AnnuallnterestRate = 0
mintHonths = 0
End Sub
' закрытая функция, которая проверяет, присвоены ли значения всем
' свойствам
Private Function PropertiesAreLoadedO As Boolean
If Principal > 0 And AnnuallnterestRate > 0 And mintMonths > 0 Then
PropertiesAreLoaded = True
End If. .
End Function
Объекты Loan, как и другие объекты, обладают свойствами и методами. Простейший способ создать свойство для чтения и записи — объявить открытую переменную. Principal и AnnuallnterestRate, объявленные в верхней части листинга LOAN.CLS, как раз и являются такими свойствами:■
Public Principal As Currency
Public AnnuallnterestRate As Single,-„4
Одна переменная — mintMonths — объявлена закрытой (как Private), так что извне о ней никто не подозревает, а Вы можете быть уверены, что ее значение полностью контролируется внутренним кодом объекта Loan..
Private mintMonths As Integer ' срок займа в месяцах■ г
ПРИМЕЧАНИЕ Вас, наверное, заинтересовал префикс mints имени этой переменной. Частица т указывает, что переменная объявлена на уровне модуля, а частица mt — что у нее тип Integer. В то же время Вы, вероятно, хотите спросить, почему открытым переменным не присвоено префиксов по венгерской нотации. Дело в том, что открытые переменные ведут себя как свойства, а по принятым стандартам в именах свойств, открываемых внешнему миру, следует избегать префиксов. . Все встанет на свои места, когда Вы вспомните, что весь смысл префиксов — улучшить читаемость внутреннего кода в мо- . дуле класса или каком-то другом модуле. С другой стороны, . свойства предоставляются внешним пользователям, и здесь принципиально важнее другое: имя должно отражать назна- ,;. чение свойства.
Переменная mintMonths содержит значение — срок займа в месяцах. Я vi< >г бы объявить эту переменную открытой, но предусмотрел у объекта Loan два взаимосвязанных свойства (Years и Months), каждое из которых позволяет определить срок займа. А они устанавливают в коде значение mintMonths. Рассмотрим, как это делается.
Оператор Property Let дает возможность определить свойство, реагирующее на присвоение ему значения. Простые свойства вроде Principal просто принимают указанные значения, но свойство Months определено так, что можно добавить код, выполняемый всякий раз, когда этому свойству присваивается значение. В нашем случае это простой оператор присвоения числа месяцев локальной переменной mintMonths:.
Property Let Months(intMonths As Integer)
ffiintMonths = intMonths
End Property.
Операторы Property Let и Property Get образуют в паре одно свойство объекта, доступное и для чтения, и для записи. Так, оператор Property Get Months обеспечивает доступ к текущему значению свойства Months:
Property Get MonthsO As Integer■
Months = mintMonths
End Property
Остановитесь и поразмыслите над тем, что я сейчас сделал. Извне свойство Months представляется простой переменной, которая запоминает присвоенное ей значение и возвращает его при обращении к данному свойству. Но посмотрев на реализацию свойства Months в модуле Loan, Вы увидите, что Months — не просто переменная. Запись или чтение этого свойства активизирует код (его пишете Вы), позволяющий выполнить практически все. что только можно представить.
Если оставить оператор Property Let без парного ему оператора Property Get, соответствующее свойство будет доступно лишь для записи. И наоборот, Property Get без парного ему Property Let определяет свойство, доступное только для чтения. Это полезно для некоторых свойств и относится, например, к свойству Payment, о котором я расскажу чуть позже.
Свойство Years, показанное ниже, — альтернативный способ определить срок займа. Значение, присвоенное свойству Years, заметьте, умножается на 12 и тем самым преобразуется в месяцы. Эта операция скрыта от пользователя.
Property Let Years(intYears As Integer)
mintMonths = intYears * 12
End Property
Property GetYearsO As Integer, _.-
Years = Round(mintMonths / 12», 0)
End Property
ПРИМЕЧАНИЕ Функция Round — новинка Visual Basic 6. Я применил ее в процедуре Property Get Years для округления вычисляемой величины до целого значения, т. е. до ближайшего числа полных лет.
Сравните процедуры-свойства Years и Months, и Вы поймете, зачем я ввел локальную переменную mintMonths для хранения срока займа независимо от свойств, которые устанавливают это значение. Еще раз повторю: реализация свойств Years и Months инкапсулирована в объекте Loan и подробности того, как эти свойства справля-. ются со своими функциями, скрыты от пользователей объекта.
Обращение к свойству Payment объекта Loan запускает достаточно сложные вычисления, и сумма платежа рассчитывается, исходя
66 часть и дорогой джон, как?..
из текущих значений других свойств. Этот пример, кстати, четко иллюстрирует, зачем нужны операторы Property Let/Get, когда свойство можно создать и простым объявлением открытой переменной в модуле класса.
Property Get Payment<) As Currency
Dim sngMonthlylnterestRate As Single
' проверяем, заданы ли значения всех свойств
If PropertiesAreLoaded() Then. ■
sngMonthlylnterestRate = AnnuallnterestRate / 1200.
Payment = (-sngMonthlylnterestRate - Principal) / _'
((sngMonthlylnterestRate + 1} A (-mintMonths) - 1)
Else'"'r
Payment = 0
End If
End Property
Свойство Balance доступно только для чтения, так как я не написал для Property Get парный ему Property Let.
Property Get Balance() As CurrencyO
Dim IntCount As Integer
Dim curPayment As Currency■
ReDim curBalance(O) As Currency
If PropertiesAreLoadedO Then
ReDim curBalance(mintMonths) curBalance(O) = Principal curPayment = Round(Payment, 2}' округляем до ближайшего пенни
For IntCount = 1 To mintMonths
curBalance(intCount) = curBalance(intCount - 1) * _
(1 + AnnuallnterestRate / 1200).
curBalance(intCount) = curBalance(intCount) - curPayment curBalance(intCount) = Round(curBalance(intCount), 2)
Next IntCount
End If"
Balance = curBalance
End Property""“...
Свойство Balance демонстрирует две новинки Visual Basic 6. Кроме новой функции Round, используемой и в других местах класса Loan, обратите внимание на свойство Balance, которое возвращает числовой массив, а не единственное значение. Присмотритесь к объявлению Property Get для этого свойства. Тип возвращаемого значения определен как As Currency(). Круглые скобки означают, что возвращаемое значение является массивом. В коде свойства Balance я объявляю локальный динамический массив типа Currency — curBalance — и после его переопределения и заполнения присваиваю переменной Balance, имя которой совпадает с именем свойства. Вот и все! Массив ежемесячных остатков по займу возвращается вызывающей программе.
Если что-то пойдет не так (например, пользователь забудет инициализировать какое-нибудь свойство), Balance вернет массив с нулевой размерностью и единственным нулевым значением. Это простейший способ обработки возможных ошибок. Я ведь хотел показать простой и ясный пример объекта, а не т<> как лучше обрабатывать ошибки.
Методы, как и свойства, — важная часть интерфейса объекта, предоставляемого внешним пользователям. Чтобы Вы прочувствовали, как они работают, я добавил в объект Loan один простой метод, Reset. Это обыкновенная Sub-процедура, обнуляющая значения всех свойств объекта Loan. Вам незачем вызывать этот метод, если только Вы не используете один и тот же экземпляр объекта в нескольких циклах вычислений.
Public Sub Beset()
Principal = О
AnnuallnterestRate = О
mintMonths = О
End Sub
ПРИМЕЧАНИЕ Обработчики событий ClasslniiializenClass
Terminate позволяют автоматически инициализировать переменные и выполнять любые другие операции в момент создания и разрушения объекта.
И, наконец, чтобы показать, как пишутся процедуры, скрытые от внешних пользователей, я включил в свой объект закрытую функцию PropertiesAreLoaded. Такие процедуры можно вызывать только из кода внутри класса. В данном случае мне нужно было проверять состояние свойств объекта в нескольких местах и я выделил соответствующий код в одну вспомогательную процедуру.
' закрытая функция, которая проверяет, присвоены ли значения ' всем свойствам
Private Function PropertiesAreLoadedQ As Boolean
If Principal > 0 And AnnuallnterestRate > 0 And mintMonths > 0 Then PropertiesAreLoaded = True
End If
End Function
ПРИМЕЧАНИЕ Если Вы явно не присвоите возвращаемое значение процедуре типа Function, она вернет значение по умолчанию, принятое для данного типа. Например, Variant - функции возвращают в таких случаях Empty, целочисленные — 0, а Булевы — False. Вот почему, когда проверка условия дает False, я не присваиваю функции PropertiesAreLoaded возвращаемого значения.
Следующий раздел «Дорогой Джон, как... использовать мой новый объект?», где дается пример работы с объектами.
Дор^—~ - Использовать мой новый объект?1
Модуль класса Loan сам по себе объекта не создает, а только определяет его или, иначе говоря, предоставляет матрицу, с которой программа по мере необходимости штампует нужное количество реальных объектов Loan. Завершим предыдущий пример, включив модуль LOAN.CLS в какой-нибудь простой проект.
Откройте новый проект стандартной ЕХЕ-программы, содержащий всего одну форму. Она будет элементарной, так как ее единственное назначение — создать объект Loan и продемонстрировать его работу. Измените свойство формы Caption на Please Click on This Form,добавьте в проект модуль класса LOAN.CLS, созданный в предыдущем разделе, а в модуль формы введите следующий код.
Private Sub Form_Click()
Dim loanTest As New Loan.
Dim intCount As Integer‘
Dim curBalanceQ As Currency
' очищаем форму
Cis:
устанавливаем параметры -условия займа,,,
loanTest. Reset loanTest. Principal = 1000..
' loanTest. Months = 12 loanTest.AnnuallnterestRate = 8.5 выводим используемые параметры■!
Print Principal; ", , Forraat(loanTest.Principal, "Currency") г Print "No. Months: ", , loanTest.Months Print "(Years:) ", , loanTest.Years Print "Interest Rate:", , Format(loanTest.AnnuallnterestRate _ / 100, "Percent")
Print 'Monthly Payment: ", Format(loanTest.Payment, "Currency")
Print_
получаемнассив ежемесячных остатков по займу'
curBalanceQ = loanTest. BalanceQ7 "
’ выводим график платежей^ '■
For intCount = Bound(curBalance) То UBound(curBalance).
Print "Month: "; intCount, Brint "Balance: "; Format(curBalance(lntCount), "Currency")
Next intCountrEnd Sub'
Рис. 5-1 иллюстрирует форму на этапе разработки.■
* PiitvCtf Г-йгп»С frwmjДГ^ТЕЗ
ПИ2ИЕ1Е™^а0
Рис. 5-1 Форма, предназначенная для демонстрации объекта Loan
Новый экземпляр объекта Loan — loanTest — создается при щелчке формы и уничтожается автоматически, как и любая другая локальная переменная, при выходе за пределы видимости.
Dim loanTest As New Loan'
Следующие три строки кода присваивают значения трем свойствам, определенным нами для объекта Loan. Вернитесь к тексту модуля класса ! - ип. если Вы подзабыли, чем отличается свойство Months от свойств Principal и AnnuallnterestRate.
LoanTest. Principal = 1000
loanTest.Months = 12.. . ,
loanTest.AnnuallnterestRate =6.5"
Выводя результаты, Вы обращаетесь к свойствам объекта Loan. В большинстве случаев объект loanTest* просто возвращает значения, уже хранящиеся в объекте, но обращение к свойству loanTest.- Payment запускает сложные вычисления. Впрочем, вызывающий код пребывает в счастливом неведении насчет того, когда, где и как выполняются эти вычисления.
Print "Principal: ", , Format(loanTest.Principal, "Currency")
Print "No. Months: ", , loanTest.Months
Print "(Years:) ", , loanTest.Years
Print "Interest Rate: ", , Format(loanTest.AnnuallnterestRate _
/ 100, "Percent")
Print "Monthly Payment: ", Format(loanTest.Payment, "Currency") Print
Вызывая свойство Balance. Вы заставляете объект loanTest создать и вернуть массив ежемесячных остатков по займу:
Прим, псрсв.: точнее, экземпляр loanTest объекта Loan.
’ получаем массив ежемесячных остатков по займу
CurBalanceu = loanTest. Balance()
Послс-лние несколько строк нашего примера выводят таблицу долговых сумм, остающихся к концу каждого месяца:
' выводим график платежей
For intCount = LBound(curBalance) То UBound(curBalance)
Print "Month: "; intCount,
Print "Balance: "; Format(curBalance(intCount), "Currency")
Next intCount
При выходе из процедуры Torm_Click все переменные, объявленные в ней, автоматически уничтожаются системой. Это касается и экземпляра объекта Loan со всем его кодом и данными.
a Wirts СЬсК .if* Т foe Feee W|^|a Wirts СЬсК .if* Т foe Feee W|^|На рис. 5-2 показаны результаты выполнения программы. Ее, кстати, можно значительно усовершенствовать, не трогая модуль класса Loan.
a Wirts СЬсК .if* Т foe Feee W|^|
a Wirts СЬсК .if* Т foe Feee W|^|
Мш*ЯШ»
TiWj1
kt'aiR.lL’?^
йё-#гУВ*гкй-К.ЕМгГф
ti-Jrlii1tl^M'tSliiji1^
Wab1В*№о.t?57S8-
Horitr.4U*rti$6WC3
56<$»ш!№з360
№h^--6BeJjirce tfUlM
kertk..^SetoiaP&H
HWii яБатях» 13*2 ry
Н^-ЙЕг'»±е. Я57 И
ЙМг- 141ЕФагОА 1172-to
ЙагиП. 11S^^Q-:^
frigid. 12 Иигг.г lfl№ Y
Рис. 5-2 Результаты программы, исполъзуюгцей объект Loan
Правильно сконструированный объект, описанный в модуле класса, позволит Вам расширить Visual Basic, добавляя в свою копилку все новые и щ и-т.п- программируемые объекты. Как только Вы добились от объекта требуемой функциональности, задокументируйте его интерфейс — набор открытых свойств и методов. А реализация объекта остается для пользователя -черным ящиком» и, вообще говоря, его не интересует. Представьте, что объект заключен в непроницаемую оболочку и только с: интерфейс доступен для обозрения. В ООП это называется инкапсуляцией. Объект отвечает за корректность реализации своей функциональности, а. вызывающая программа — за корректность использования его интерфейса. Инкапсуляция значительно упрощает разработку и отладку больших приложений. В этом и заключается красота и сила ООП!
Класс Loan мы просто добавили к проекту как еще один модуль. Это прекрасный способ использования модулей классов, но один или несколько таких модулей можно скомпоновать в ActiveX-компоненты.
См, также...' ' —-
Раздел «Дорогой Джон, как... создать ииспользоватьЕХЕ-сервер ■ ActiveX?» и главу 27 «Изощренныеприемыпрограммирования» —
подробнее об ActiveX-компонентах.
Программу-пример Lottery в главе 29 «Графика», где демонстрируется использование объектов.
Назначить объекту используемое по умолчанию?
Лично я всегда устанавливаю свойства явно, но некоторые предпочитают сокращать тексты программ, опуская имена свойств, используемых по умолчанию- Например, у элемента управления Text - Box по умолчанию используется свойство Text, а значит, его можно установить как явно, так и неявно. Скажем, эти две строки кода равнозначны:
Textl.Text = "This string is assigned to the Text property."
Text! = 'This string is assigned to the Text property."
Хоть это и не очень хорошо задокументировано, но Visual Basic теперь позволяет Вам назначать своим объектам свойство, используемое по умолчанию, — если Вам нравится такой стиль. Посмотрим, как это делается, на примере свойства Principal объекта Loan. Активизируйте в проекте окно кода для класса Loan и выберите из меню Tools (сервис) команду Procedure Attributes (атрибуты процедуры) — появится одноименное диалоговое окно. В списке Name (имя) выберите свойство Principal и щелкните кнопку Advanced (дополнительно). В списке ProcedurelD (идентификатор процедуры) выберите строку (Default) [(поумолчанию)] и щелкните кнопку ОК. С этого момента свойство Principal объекта Loan становится используемым по умолчанию.
Чтобы проверить корректность работы свойства, получившего статус используемого по умолчанию, активизируйте окно кода для формы, созданной для тестирования объекта Loan, и найдите в нем строку, где свойству Principal присваивается значение 1000. Удалите в этой строке Principal, оставив:
LoanTest = 1000
Запустите тестовое приложение и убедитесь, что оно по-прежнему корректно работает.
Дорогой Джон, как...
Создать и использовать ЕХЕ-сервер ActiveX?
EXE- и DLL-серверы ActiveX — вещи довольно близкие. В главе 27 «Изощренные приемы программирования» я покажу, как создавать ActiveX DLL. а здесь мы рассмотрим создание ЕХЕ-сервера ActiveX.
Такой сервер можно запустить как обычную программу, но, помимо этого, он предоставляет объекты для других приложений. Я уже говорил, что эти объекты работают как внешние, т. е. выполняются в своем адресном пространстве и в потоке, отличном от потока клиентского приложения.
Пример ЕХЕ-сервера ActiveX: компонент Chance
Попробуем построить простейший ЕХЕ-сервер ActiveX, содержащий определение одного объекта или, точнее, одного класса. Класс служит шаблоном, по которому создаются экземпляры объекта. Подготовив ЕХЕ-сервер ActiveX, мы запустим его, и он автоматически зарегистрируется в системе. Потом мы напишем на Visual Basic второе, более заурядное приложение, которое создает несколько объектов нашего класса и манипулирует их свойствами и методами.
Назовем объект Dice (игральная кость), а ЕХЕ-сервер ActiveX — Chance (шанс). Хоть я и дал объекту' имя Dice, но предусмотрел в нем свойства, позволяющие клиентской программе устанавливать число сторон, так что игральная кость может превратиться и в подобие монеты (2 стороны), и в двенадцатигранник.
DICE.CLS-
Итак, приступим. Откройте новый проект и дважды щелкните значок ActiveX EXE в диалоговом окне New Project. Введите в модуль класса код, приведенный ниже, установите свойство Name класса. какУ/се и сохраните этот модуль под именем DICE.CLS, а проект — под именем CHANCE.VBP:
Option Explicit
' объявляем свойства, определяющие диапазон допустимых значений
Public Smallest As Integer
Public Largest As Integer
' объявляем свойство, доступное только для чтения; его значение - ' результат броска игральной кости•
Public Property Get Valued As Integer
Value = Int(Rnd * (Largest - Smallest + 1)) + Smallest’’ -*
End Property
' метод для перемешивания последовательности случайных чисел.)
Public Sub Shuffled> _
Randomize
End Sub’ 1
Этот модуль класса определяет 3 свойства и 1 метод объекта Dice. Свойства Smallest и Largest реализованы как открытые переменные, с помощью которых клиентское приложение (мы создадим его чуть позже) задает диапазон возвращаемых значений, в нашем
случае — число сторон игральной кости. Именно так зачастую и реализуются свойства объектов — как открытые переменные.
Value — еще одно свойство объекта Dice, но здесь перед возвратом значения клиентскому приложению желательно провести кое- какие вычисления. Оператор Property Get как раз и предоставляет механизм для запуска любых операций при запросе значения свойства. В данном случае для имитации броска игральной кости генерируется случайное число, возвращаемое как значение свойства. (Для математиков это число не является подлинно случайным, но достаточно «случайно» для наших целей.) Свойство Value доступно клиентскому приложению только для чтения, так как у соответствующего оператора Property Get нет парного оператора Property Let. Наличие последнего позволило бы клиентскому приложению присвоить новое значение свойству Value. Но в этом примере мы лишь имитируем случайный результат броска игральной кости, и нам не нужно, чтобы клиентское приложение устанавливало значение этого свойства.
Shuffle — простой метод, добавленный мной для того, чтобы у объекта был хоть один метод. Его вызов вносит элемент случайности в генератор псевдослучайных чисел, и это делает непредсказуемыми последующие броски игральной кости.
Теперь добавим код, выполняемый при запуске ЕХЕ-сервера ActiveX (компонента Chance). Выберите из меню Project команду Add Module и дважды щелкните значок Module в диалоговом окне Add Module. Вставьте в новый модуль следующий код и сохраните его как CHANCE.BAS;
Option Explicit"
Sub NainC)
Dim diceTest As New Dice
diceTest.Smallest = 1
diceTest.Largest = 6
diceTest.Shuffle
MsgBox "A roll of two dice: " &
diceTest.Value 4 "," & diceTest.Value,
0, "Message from the Chance ActiveX EXE"
End Sub■
Выберите из меню Project команду Projectl Properties, чтобы открыть одноименное диалоговое окно. В списке Startup Object (стартовый объект) щелкните строку Sub Main, в поле Project Name (имя проекта) введите Chance, а в поле Project Description (описание проекта) — Chance ~ ActiveX EXE Example. Щелкните кнопку ОК. Наконец, выберите из меню File команду Маке Chance.exe (собрать Chance.exe). В диалоговом окне Make Project (собрать проект) укажите, куда поместить EXE-файл, и щелкните кнопку OK. Visual Basic
скомпилирует и автоматически зарегистрирует ЕХЕ-сервер ActiveX с именем Chance.
Тестирование ActiveX-компонента, реализованного как ЕХЕ-сервер
Теперь, когда ЕХЕ-сервер ActiveX скомпилирован и зарегистрирован, к нему можно обращаться и использовать его объекты из любого приложения, которое поддерживает технологию ActiveX. Продемонстрируем это, создав приложение на Visual Basic, способное задействоватьданныйЕХЕ-cepBepActiveX.
Откройте новый проект стандартной ЕХЕ-программы и сохраните ее форму как DICEDEMO.FRM, а сам проект — как DICEDE- MO.VBP. Измените свойство Caption формы на Taking a Chance with Our ActiveX EXE и добавьте на форму кнопку. Я назвал ее Roll ’em (брось их) и сдвинул в правый верхний угол формы. Добавьте к модулю формы следующий код и сохраните проект: Option Explicit Dim diceTest As Mew Dice- .. -
Private Sub Cowmand1_Click() diceTest.Shuffle'
diceTest.Smallest = 1 diceTest.Largest = 2 Print "Coin: diceTest.Value,■ -
diceTest.largest =6■ •
Print "Dice:" 4 diceTest.Value, . diceTest.Largest = 12■
Print "Dodecahedron-.11 4 diceTest.Value End Sub
Рис. 5-3 иллюстрирует форму на этапе разработки.
Рис. 5-3 Форма DiceDemo
Форма создает экземпляр объекта Dice, поэтому нужно установить ссылку на компонент Chance, чтобы в текущем проекте было известно, где определен объект Dice. Выберите из меню Project команду References (ссылки) и найдите в одноименном диалоговом окне строку Chance - ActiveX EXE Example. (Эго то самое описание,
которое мы закрепили за проектом Chance.) Пометьте флажок в этой строке и щелкните кнопку ОК.
Теперь все должно работать! Запустите программу и понаблюдайте, что происходит. Сначала появляется тестовая форма с кнопкой Roll 'em. Щелчок этой кнопки приводит к созданию объекта Dice, и на форме показываются результаты бросков двух-, шести- и двенадцатигранной кости. Рис. 5-4 иллюстрирует, что получится,
если щелкнуть кнопку несколько раз.если щелкнуть кнопку несколько раз.Рис. 5-4 Результат работы программы DiceDemo после нескольких щелчков кнопки Roll ’emРис. 5-4 Результат работы программы DiceDemo после нескольких щелчков кнопки Roll ’em
если щелкнуть кнопку несколько раз.
если щелкнуть кнопку несколько раз.
Рис. 5-4 Результат работы программы DiceDemo после нескольких щелчков кнопки Roll ’em
Рис. 5-4 Результат работы программы DiceDemo после нескольких щелчков кнопки Roll ’em
После первого щелчка кнопки появляется одно важное сообщение. Как только ЕХЕ-сервер Chance загружается в память, его стартовая процедура Sub Main открывает окно и с помощью своего объекта Dice тоже «бросает» пару игральных костей. Выглядит это окно так, как показано на рис. 5-5. . 1--
;7 •; I) -;7 •; I) -ЙдМВДО iron d№ CteiritiiAtitveM EXE @;
;7 •; I) -
;7 •; I) -
Airi<ifhtottce' S,j "’-У
Рис. 5-5 Сообщение, выводимое ЕХЕ-серверомСИапсе при загрузке в память
Данное окно появляется лишь раз, когда компонент Chance впервые загружается в память. Если Вас это заинтересовало, проверьте: Chance выгружается из памяти, как только уничтожается последний из его объектов (что происходит при завершении приложения DiceDemo).
Дорогой Джон, как...
Создать объект, способный отображать формы?
У объектов Loan и Dice, рассмотренных выше, нет визуального интерфейса вроде формы. Многие объекты тоже не имеют визуального интерфейса, но бывают случаи, когда нужен объект, который через форму получал бы данные от пользователя.
Добавить форму в проект EXE- или DLL-сервера ActiveX неоюж- но, но следует учитывать, что объект, выводящий формы, должен
тщательно контролировать их создание и уничтожение. Клиентское приложение никогда напрямую не взаимодействует с формой внутри ActiveX-компонента (она доступна лишь самому компоненту), поэтому очень важно, чтобы объект корректно управлял своей формой. Иначе экземпляры формы скрытно «засядут-^ в памяти, попусту транжиря ресурсы и временами приводя к непредсказуемым результатам.
Ниже приведено определение модуля класса для объекта User. Этот объект, предназначенный для сбора данных об имени и пароле пользователя, выводит модальную форму с двумя текстовыми полями txtNamew txtPassword и кнопкой и К (ее кодовое имя cmdOK).
• USER,CIS ' "'".Name Property Get Name(),
Name = frmPass. txtName■
End Property
' .Password
Property Get Password()• 1'
Password = f rmPass. txtPasswordr. .■
|End Property"
' """.Showv
Sub Show()JI; ■•I
frmPass.Show vbModal'
End Sub, ~,
Private Sub Class_Termlnate()
' выгружаем форму при уничтожении объекта,
Unload f rm Pass. ,
End Sub
Форма/rmPass содержит лишь один обработчик события, который скрывает форму при щелчке кнопки ОК. Это позволит, проверив имя и пароль пользователя, быстро отобразить форму повторно, если окажется, что введенные данные неправильны. При этом данные в полях формы останутся нетронутыми, и пользователь легко исправит их.
' FRMPASS.FRMi ^- ,"
Private Sub cmdOK_Click()- , ... : ...г?". ■•
iHide■..,■..■;>
End Sub
Чтобы использовать объект, клиентское приложение просто создает его экземпляр и вызывает метод Show. В следующем коде это происходит при щелчке кнопки cmdSignOn. Обратите внимание на синтаксис ссылки на класс User, реализованный в проекте PasswordSample.■■
Private Sub cmdSignOn_Cllck()■■ -‘' ' "• <
‘ создаем экземпляр объекта
Dim usrSignOn As New PasswordSample.User
цикл ожидания ввода пароля Guest
Do While usrSignOn. Password <> "Guest"
отображаем диалоговое окно
usrSignOn.Show
Loop
End Sub
Пример формы User Object показан на рис. 5-6,
Рис. 5-6 Пример объекта, отображающего форму
Чтобы убедиться в важности контроля за созданием и уничтожением формы, удалите у объекта User процедуру ClassTerminate, а потом создайте этот объект из клиентского приложения, как показано выше. Когда процедура обработки события cmdSignOnClick завершится, переменная usrSignOn выйдет из области видимости и будет уничтожена, но форма fivnPass останется, а значит, продолжится выполнение и скрытого экземпляра сервера данного объекта.
Если сервер объекта — ЕХЕ-файл, этот скрытый экземпляр Вы увидите в окне диспетчера задач Windows, нажав клавиши Ctrl+Alt+Del. Закройте скрытый экземпляр, устраните ошибку и перекомпилируйте исправленную версию. Сделать это нужно обязательно и как можно скорее, чтобы не допустить повторения проблемы, поскольку обнаружить скрытые экземпляры серверов объектов достаточно трудно.
Б общем случае создание и уничтожение форм лучше всего контролировать через обработчики Classlnitialize и Class Terminate. Мой объект был совсем крошечным, и я сделал так, что форма неявно создавалась в первой же вызванной процедуре (свойстве или методе). Для явного создания формы следовало бы ввести процедуру Classlnitialize:
- добавьте к USER.CLS, чтобы явно создааать форму
Private Sub Class_Initialize()
‘ создаем форму сразу после создания объекта, вызывая метод Show
Show
End Sub
ПРИМЕЧАНИЕ Возьмите за правило всегда явно создавать и уничтожать формы в объектах. Это сэкономит массу времени на отладке.
Event, WithEvents и RaiseEvent
В Visual Basic есть несколько операторов для определения в создаваемом объекте пользовательских событий. Я упоминаю о них именно здесь потому, что эти средства позволяют реализовать механизм взаимодействия закрытых форм, принадлежащих объектам в ActiveX-компонентах, с клиентскими приложениями. Генерация события в закрытой форме порождает немедленный отклик в ActiveX-объекте, владеющем данной формой, а этот объект в свою очередь возбуждает событие в клиентском приложении. Хороший пример такого сценария приведен в разделе «Adding a Form to the ThingDemo Project» электронной документации Books Online.
Дорогой Джон, как... ■
Работать с наборами объектов?
Встроенный в язык объект Collection позволяет включать объекты (или элементы данных любого типа) в набор, с которым можно обращаться как с единой сущностью. Объект Collection допустимо рассматривать как очень гибкий массив, способный содержать практически все, ■ том числе объекты и другие наборы. Но самое распространенное применение объектов класса Collection — поддержка наборов с произвольным количеством экземпляров объекта определенного типа. Как раз это и продемонстрирует следующий пример.
В созданных Вами объектах Visual Basic наборы можно использовать самыми разными способами. Некоторые из них «опаснее» других, и поэтому конкретные способы выбирают в зависимости от того, насколько глубоко хотят застраховаться от ошибок. Я советую внимательно прочесть разделы Books Online по этой теме и получить представление о подходе, предлагаемом Microsoft. Я применил лишь слегка модифицированную версию методики Microsoft и мне кажется, что Вам тоже будет полезно придерживаться их рекомендаций. (Чтобы найти нужные разделы, проведите поиск по одному из словосочетаний: House ofStran\ House ofStricks или House of Bricks}
В следующем примере я представлю простой способ реализации наборов пользовательских объектов внутри других объектов. Этот способ достаточно надежен и легко адаптируется под конкретную задачу. Мы создадим модель Солнечной системы, которая включает объект верхнего уровня Star (звезда), тот — набор Planets (планеты), а каждый объект Planet — набор Moons (спутники). Объекты Moon крайне просты и содержат всего несколько свойств.
Пример набора: SolarSys -’-
Запустите Visual Basic и откройте новый проект стандартной ЕХЕ- программы. Установите свойство формы Name как SolarSys и сохраните ее под именем SOLARSYS.FRM, а сам проект — под именем SOLARSYS.VBP. Измените свойство формы Caption на Collections E.vwnle. добавьте на форму кнопку с именем cnidRHildSolmSvstpnm. установите ее свойство Caption КАК Build Solar System. Затем вставьте показанный ниже код и сохраните проект.
Option Explicit
Public starObject As New Star
Private Sub cmdBuildSolarSystem_Click()
' готовим ссылки на рабочие объекты
Dim planetobject As Planet
Dim moonObject As Moon*
' создаем в Солнечной системе планеты и их спутники
With StarObject. Planets
' создаем первую планету"
Set planetobject = .Add("Mercury")
' устанавливаем некоторые из ее свойств
With planetobject
.Diameter = 4880
.Mass = 3.3E+23'. „
End With
’ создаем вторую планету
Set planetObject = .Add("Venu3")
' устанавливаем некоторые из ее свойств
With planetObject
.Diameter = 12104
.Mass = 4.869Е+24'
End With'
' создаем третью планету
Set planetObject = .Add("Earth")
' устанавливаем некоторые из ее свойств
With planetObject
.Diameter = 12756
.Mass = 5.9736E+24
' создаем спутники этой планеты
With .Moons
' создаем Луну для Земли.
Set moonObject = .Add("Luna")
' устанавливаем некоторые из ее свойств With moonObject
.Diameter= 3476
.Mass = 7.35E+22
End With
End With'
End With ■ ' создаем четвертую планету.
Set planetobject = . Add("Mars")'
' устанавливаем некоторые из ее свойств'
with planetObject .Diameter = 6794 .Mass = 6.4219Е+23-
' создаем спутники этой планеты 'with . Moo tn.■
' создаем первый спутник Марса Set moonObjecn = . auui, rnuuus )■'
' устанавливаем некоторые из его свойств.
with moonObject.
.Diameter = 22
Kaon = 1.08E+16"
End with1 ■
' создаем аорой спутник Марса■
Set moonObject = данг-Пт mnc-1 ' устанавливаем некоторые его свойств with moonObject■ 1
-.-*.Diameter = 13.. ... •
,..Mass = 1.Se+15.
,End with
End with.
End with■'.''
End with ' отключаем кнопку1 ■■
cmdBuildSolarSystem.Enabled = False ’ отображаем результатыk
Print "Planet", "Moon", "Diameter (km)", "Mass (kg)"' :
Print Stringtiuu, " For Each planetobject In starObject. Planets-‘' ■
with planetobject Print .Name, , .Diameter, .Mass End with.
For Each moonObject In planetObject.Moons'.
with moonObject.
Print , .Name, .Diameter, .Mass-
. End with
Next moonObject
Next planetObject11
' напрямую обращаемся к некоторым свойствам' : '.
Print■
Print "The Earth's moon has a diameter of ";
Print starObject.Planets("Earth").Noons("Luna").Diameter; .. ,- Print " kilometers.",
Print "Mars has"; ''.-kiG)
Print starObject. Planets(”Mars"). Moons.Count;',fi
Print " moons."._ |r
End Sub’
Рис. 5-7 иллюстрирует форму SolarSys на этапе разработки.
Bud ^clar.Rm^i
Рис. 5-7 Форма SolarSys, демонстрирующая вложенные наборы объектов
Мы еще вернемся к этому коду, а пока определим требуемые объекты и их наборы. ,
Класс Star''’ "
Выберите из меню Project команду Add Class Module и в появившемся диалоговом окне дважды щелкните значок Class Module. Установите свойство Name класса к.и; Star, вставьте приведенный ниже код и сохраните модуль как файл STAR.CLS.
STAR.CLS■-
Option Explicit-
Public Name As String
Private mplanets As New Planets,-
Public Property Get Planets() As Planet
Set Planets = mPlanets.. •
End Property■
В объекте Star объявляется и содержится набор объектов-планет. Класс Planets мы рассмотрим чуть позже, а пока имейте в виду, что этот класс определяет набор объектов Planet, в свою очередь определяемых классом Planet.
Заметьте, что набор Planets объявлен закрытым (как mPlanets^y. тем самым защищен от прямого доступа извне. Внешние процедуры обращаются к этому набору через свойство Planets, у которого есть процедура Property Get, но нет парной процедуры Property Let. Для обращения к отдельным объектам Planet в наборе Planets нам нужен доступ только для чтения.
Класс Planets
Все планеты в Солнечной системе представлены как объекты Planet в наборе Planets. Обратите внимание, что по принятому стандарту
наборам присваиваются те же имена, что и содержащимся в них объектам, — только во множественном числе.
Добавьте в проект новый модуль класса, присвойте ему имя Planets, введите код, показанный ниже, и сохраните его под именем PLANETS.CLS.
Option Explicit
Private mcolPlanets As New Collection■
Public Function Add(strName As String) As Planet
Dim PlanetNew As New Planet,
PlanetNew.Name = strName'
mcolPlanets.Add PlanetNew, strName
Set Add = PlanetNew
End Function•
Public Sub Delete(strName As String)
mcolPlanets. Remove strName
End Sub. ■"
Public Function Count() As Long.
Count = mcolPlanets. Count ,, ’
End Function■ .
Public Function Item(strName As String) As Planet'
Set Item = mcolPlanets.Item(strHame)'' "
End Function
Public Function NewEnum()As IUnknown■■
Set NewEnum = mcolPlanets.[_NewEnum].
End Function
Впервые начав создавать наборы объектов, я поддался соблазну скомбинировать каждый объект и его набор в одном модуле класса. Все работало, но, отделив набор от содержащихся в нем объектов, Вы избавитесь от уймы скрытых проблем и странных «глюков». Вот почему в этой программе присутствуют два класса — Planets и Planet. Вместе они создают довольно надежный набор объектов- планет. Аналогичным образом, как Вы сами скоро убедитесь, устроен и набор объектов-спутников (классы Moons и Moon).
Класс набора Planets инкапсулирует все, что связано с созданием и управлением отдельными объектами Нэпе! Настоящий объект Collection, mcoIPkiiitiis, объявлен в этом модуле класса закрытым, чтобы инкапсулировать и защитить его от внешнего воздействия. Методы, стандартные для любого набора, — Aild. Delete, Count, Item и NewEnum — образуют интерфейс для взаимодействия с набором объектов-планет. Реализуя методы, я применил пару специфических приемов, о которых Вам не помешает знать, так что дзвайте-ка пройдемся по этим процедурам.
Метод Add предназначен для создания новой «планеты1 в наборе. В данном примере имя каждого объекта-планеты, передаваемое методу Add, используется двояко: для установки свойства Name объекта-планеты и как ключ, по которому индексируется объект. Ключ может отличаться от имени, как в примерах наборов, описанных в Visual Basic Books Online. У каждого элемента. хранящегося в наборе, должен быть уникальный строковый ключ, и один из способов обеспечить уникальность каждого ключа — возложить эту задачу на класс набора, автоматически поддерживающий свой набор ключей. Я предпочел более простой и естественный способ, при котором объект индексируется по имени. 1 [рзг.д. попытка добавить в такой набор два объекта-планеты с одинаковым именем даст ошибку. Вероятно, Вы дополните код метода Add обработкой ошибок. вызванных дублированием имен планет. Я же предпочел не усложнять код.
Процедура Count возвращает количество планет в наборе Planets, а процедура I к. к к удаляет из него указанный объект. Наверное, и здесь стоит добавить код для обработки ошибок — на случай попытки удалить несуществующий объект.
Процедура Item возвращает ссылку на объект-планету по заданному имени. В ней применяется первый из специфических приемов, о которых я хотел бы упомянуть. Если Вы сделаете процедуру Item свойством по умолчанию, то синтаксис доступа к конкретной планете будет естественнее. Например, Вы можете обратиться к объекту-планете Earth (Земля) через свойство Item так: starObject.- PlanetsJtem(->Eartb»);B.o, придав свойству Item статус используемого по умолчанию, Вы сократите синтаксис до s/rtrC)bjec!Plmiets.(<Eartb*). Такой синтаксис гораздо лучше сочетается со стандартным синтаксисом объектов многих других Windows-приложений.
ПРИМЕЧАНИЕ Чтобы сделать какую-то процедуру-свойство используемой по умолчанию, выберите из меню Tools команду Procedure Attributes. В списке Name укажите требуемую процедуру и щелкните кнопку Advanced для доступа к дополнительным параметрам. Теперь в списке ProcedurelD выберите строку (Default) и щелкните кнопку ОК. У класса может быть лишь одна процедура, используемая по умолчанию.
Второй прием, достойный упоминания, — перечисление объектов набора в цикле For Each. Здесь не все так просто, поэтому соблюдайте мои инструкции. Прежде всего в класс набора надо ввести специальную процедуру NcwEnum интерфейса IUnknown, как показано в исходном коде класса Planets. Имя этой процедуры предваряется знаком подчеркивания и заключается в квадратные скобки. Затем Вы скрываете функцию NcwEnum и разрешаете ее поддержку в своем классе, действуя так, как рассказывается в следующем примечании.
ПРИМЕЧАНИЕ Чтобы скрыть процедуру NewEnum и разре- . шить ее поддержку в своем классе, выберите из меню Tools команду Procedure Attributes. В списке Name укажите строку ..: NewEnum, щелкните кнопку Advanced, установите флажок Hide This Member (скрыть этот элемент) и введите г. поле ProcedurelD специальный код -4. С этого момента Ваш набор можно будет перебирать в цикле For Each.- ■
Класс Planet"
Вставьте в проект SolarSys второй модуль класса, установите его свойство Name как Planet и сохраните модуль под именем PLA- NET.CLS. Этот класс определяет объекты Planet, находящиеся внутри набора Planets. Добавьте в модуль следующий код:
Option Explicit■'
Public Name As String'*' ’
Public Diameter As Long,., ■■
Public Mass As Single. .Vr‘
Private mHoons As New Moons 4l t ■■ • ’ / ।/r t.
Public Property Get Moons() As Moons1
Set Moons = mHoons
End Property
У каждого объекта Planet имеется несколько свойств (Name, Diameter и Mass) и набор Moons, содержащий произвольное число объектов-спутников. И вновь настоящий набор объявляется закрытым, чтобы предотвратить запись в него недопустимых ссылок. Вы ссылаетесь на объекты-с путники данной планеты через свойство Moons только для чтения. .. . :
Класс Moons
Этот класс относится к классам наборов; его структура очень похожа на структуру класса Planets. Добавьте новый модуль класса в проект SolarSys, установите его свойство Name как Moons, вставьте показанный ниже код и сохраните модуль под именем MOONS.CLS.
Option Explicit■ ■ _ ■ ■ । j'
Private mcolMoons As New Collection.
Public Function Add(strName As String) As Moon
Dim MoonNew As New Moon■‘
MoonNew. Name = strName■ * ;>4 ,
mcolMoons.Add MoonNew, strName
Set Add = MoonNew.
End Function* ■ ■ t:лапч
Public Sub Delete(strName As String) V- '■
mcolMoons.Remove strName
End Sub.■
Public Function Count)) As Long■■ .'
Count = mcolMoons.Count
End Function
Public Function Item(strName As String) As Moon
Set Item = mcolMoons.Item(strName)■
End Function.
Public Function NewEnum() As IUnknown ■_
Set NewEnum = mcolMoons.[_NewEnum] .,
End Function. '
Класс Moon
Этот класс крайне прост. У каждого объекта Moon есть свойства Name, Diameter и Mass — вот и все. Вставьте в проект очередной модуль класса, установите его свойство Name как Moon, сохраните модуль под именем MOON.CLS и добавьте код:
Option Explicit
Public Name As String
Public Diameter As Long■
Public Hass As Single•■
Как работают вложенные наборы
Теперь внимательно рассмотрим код в модуле формы SolarSys. При щелчке кнопки Build Solar System создается новый экземпляр объекта Star с именем >!druDjecL Поскольку мы не собираемся создавать более одного экземпляра объекта Star, то и не станем устанавливать его свойство Name. (Если б мы писали программу для регистрации всех звездных систем, упоминаемых в каком-нибудь сборнике научно-фантастических рассказов, то создали бы множество объектов Star, и вот тогда-то уникальное имя для каждого из них было бы весьма полезно.)
Чтобы не нагромождать слишком много кода, я добавил лишь первые четыре планеты нашей Солнечной системы и их спутники. Хотите — создайте полную модель сами! Следующий код добавляет первую планету с именем Mercury (Меркурий), у которой нет спутников:
With starObject.Planets
создаем первую планету
Set planetObject = .Add("Mercury")
1 устанавливаем некоторые из ее свойств
With planetobject■■■ .
. Diameter = 4880111Л
' .Nass = 3.3E+23'
End With
Я применил оператор With, стремясь упростить ссылки на вложенные объекты. Но можно было бы получить прямой доступ к свойствам объекта Mercury, явно указав цепочку ссылок через оператор-точку. При полной нотации строка, в которой устанавливается значение свойства Diameter объекта Mercury, выглядела бы так: starObject.PlanetsC'Mercury").Diameter = 4880
Иными словами, здесь значение 4880 присваивается свойству Diameter объекта Mercury набора Planets объекта Star с именем starObject. Уф! Теперь Вы видите, как удобен оператор With? Более того, блоки With, уменьшая число операций разыменования (de-referen- cing), требуемых для доступа к каждому объекту и его свойствам, не только упрощают восприятие программы, но также и ускоряют ее выполнение.
Следующий блок кода повторяет описанный процесс, добавляя второй объект Planet — Venus (Венера), у которого тоже нет объектов-спутников. Так что пропустим его и перейдем сразу к третьей планете, Earth (Земля), и ее спутнику, Luna (Луна). Вот как выглядит код, добавляющий эту планету и ее спутник во вложенные наборы: ' создаем третью планету
Set planetobject = .Add("Earth"). ;v4
' устанавливаем некоторые из ее свойств.
With planetobject,.
.Diameter = 12756' ’
■Mass = 5.9736E+24
' добавляем спутник этой планеты'
With -Moons
' создаем Луну
Set moonObject = .Add("Luna")1
' устанавливаем некоторые из ее свойств
With moonObject■
.Diametar = 3476
.Mass = 7.35Е+227 -
End With.. .
' End With:' 1 '
End With
Еще раз обратите внимание, как удобны вложенные конструкции With при ссылках на элементы вложенных наборов.
Построив упрощенную модель Солнечной системы (как наборы объектов, содержащиеся в других наборах), Вы можете обратиться к этой структуре и вывести имена всех вложенных объектов: . .
' отображаем информацию
Print "Planet", "Moon", "Diameter (km)", "Mass (kg)"
Print String(100, "-")
For Each planetobject In starObject.Planets.
With planetobject
Print .Name, , .Diameter, ,Hass
End With
For Each moonObject In planetObject.Moons
With moonObject
Print , .Name, .Diameter, .Mass
End With
Next moonObject
Next planetObject
Активизировав поддержку синтаксиса For Each (об этом я уже рассказывал), мы облегчили ссылку на свойства планет и спутников. Благодаря этому код, приведенный выше, совершенно понятен и легко читается. Для сравнения я обращаюсь к некоторым свойствам напрямую, используя полную нотацию с точками:
' напрямую обращаемся к некоторым свойствам
Print "The Earth's moon has a diameter of ";
Print starObject.Planets("Earth").Noons("Luna").Diameter;
Print " kilometers."
Print "Mars has";
Prtnt starObject.Planets("Mars").Moons.Count;
Print " moons."
Имена объектов отображаются на форме SolarSys после щелчка кнопки, как показано на рис. 5-8.
ВЕГ—
Уе-i»'ЛС*4,Й«-24.
Phcye22r •
tiMew11’•«it
Рис. 5-8 Наборы Planets и Moons упрощенной Солнечной системы
Предложенный здесь прием легко адаптируется под многие другие задачи. Как я уже говорил, в класс набора можно включить код, который предотвращает случайные попытки добавить несколько одноименных объектов, и более серьезно обрабатывать ошибки, связанные с отсутствием объектов. Но я представил лишь концептуальную базу, на которой Вы построите что-то свое, и поэтому старался не отвлекаться на второстепенные детали. Во многих ситуациях этот метод дает практически все, что нужно.
ДОРОГОЙ Пжпм. КЙК... ;
Использовать полиморфизм?
В новом мире ООП целое море новой терминологии и концепций! ВзяТь хотя бы полиморфизм. Что он значит и чем полезен?
В самых общих чертах, полиморфизм означает, что несколько объектов поддерживают одни и те же интерфейсные элементы. Например, Вы могли бы создать множество объектов, каждый из которых поддерживает «стандартный- набор файловых функций. Все эти объекты предоставляли бы методы и свойства с идентичными именами, скажем, FileName, Read и Write, что позволило бы упростить и стандартизировать эти объекты, а также сделать их более предсказуемыми и согласованными.
Полиморфизм в Visual Basic трактуется несколько иначе, чем в C++. Исторически сложилось так, что в большинстве языков, поддерживающих ООП, методы и свойства одного объекта наследуются новым объектом. Последний может потом модифицировать элементы унаследованного интерфейса или предоставить их клиентскому приложению в неизменном виде («как есть»).
Но Visual Basic не использует наследование для поддержки полиморфизма. Вместо этого он позволяет создать интерфейс, который есть не что иное, как набор определений взаимосвязанных свойств и методов, включенный в абстрактный класс. Последний — всего-навсего модуль класса, содержащий пустые оболочки свойств и методов. Этот особый класс потом объявляется в модулях классов, готовых реализовать его элементы интерфейса с применением ключевого слова Implements.
Я предпочитаю рассматривать абстрактный класс как своего рода соглашение. Несколько модулей классов «принимают» условия данного соглашения, заявляя об этом ключевым словом Implements. Клиентское приложение объявляет экземпляры этих объектов, в том числе объектную переменную с типом абстрактного класса. Обращаясь к элементам интерфейса через объект абстрактного класса, можно быть уверенным, что вызванные методы или свойства будут выполняться корректно — каждым объектом по-своему.
Лучший способ освоить эти концепции — поработать с ними. Пример, приведенный в Books Online, дает отличные возможности для изучения и экспериментов. Проведите поиск в Books Online по слову 71'гл;то5^иг(тиранозавр) либо — если Вам некогда или лень набирать такое длинное слово — по Flea (блоха). Пояснения насчет того, как объекты «блоха * и «тиранозавр», так сказать, договорились через полиморфизм Visual Basic реализовать метод Bite (укусить), помогут понять и запомнить все, что нужно.
Q^fJ^jf^al П ъь# #ь.ел V^S.W '
Применять дружественные методы?
Visual Basic 4 впервые познакомил нас с объектами, определяемыми в модулях классов, их свойствами и методами. Но программисты вскоре заметили небольшой изъян в том, как компонент предоставляет свойства и методы собственных модулей классов клиентским приложениям: класс не мог предоставить такие методы другим объектам своего компонента, не открыв их внешним программам.
Эта проблема была решена за счет ключевого слова Friend, которое применительно к методу означает, что данный метод доступен всем классам внутри компонента, но не клиентским приложениям.
Компоненты часто предоставляют набор взаимосвязанных объектов и именно дружественные методы позволяют им четко взаимодействовать друг с другом, не открывая деталей этого механизма клиентским приложениям.
ГЛАВА 6
ActiveX-элементы
JVLnornt аналитики приписывают быстрый успех Visual Basic возможности использовать нестандартные элементы управления (custom controls). И легко понять почему: Вы не привязаны к одному поставщику инструментальных средств, а выбираете лучшее, что есть на рынке.
Более того, Visual Basic позволяет Вам самому создавать нестандартные элементы управления (называемые маментямиуправле- ния на базе ActiveX, или - в краткой форме — АсИгеХ-элемента- ми). ActiveX-элементы, написанные на Visual Basic, выглядят и действуют точно так же, как и написанные на С- Поэтому Вы можете распространять их среди своих коллег, программирующих на С, чтобы они пользовались ими из Microsoft Visual C++.
ActiveX-элементы можно применя т ■ практически везде — на Web-CTpmnmax. просматриваемых с помощью Internet Explorer, в документах Microsoft Excel и Microsoft Word, в СУБД Microsoft Access и Visual FoxPro и, конечно же, в Visual Basic. Microsoft v мш C++ и Microsoft Visual J++.
В этой главе я расскажу, как создавать ActiveX-элементы, и опишу специфику их программирования. Все примеры кода из этой главы Вы найдете на компакт-диске, прилагаемом к книге. .
Дорогой Джон, как... ■,' "
Создать ActiveX-элемент?
AcmeA Control Interface Wizard (мастер интерфейса ActiveX-элементов) позволяет создать новый элемент управления на базе существующего. Но код, генерируемый мастером, труден для понимания, И поэтому МЫ создадим ч I ivrX-.O..'H Mt и вручную. А как только Вы разберетесь в составных частях ActiveX-элемента, работать с мастером Вам станет значительно легче.
Этапы разработки ActiveX-элемента .... . .1
Чтобы создать ActiveX-элемент:.
Создайте новый проект ActiveX-элемента.■
В окне liserCuninu нарисуйте визуальный интерфейс элемента управления, используя элементы из Toolbox так же, как и при разработке формы.
В окне кода напишите обработчик события Resize, чтобы при изменении размеров элемента управления соответственно модифицировать его визуальное представление.
Добавьте свойства, методы и события, которые элемент управления предоставит клиентской программе. Они определяют программный интерфейс элемента.
Напишите код, реализующий функциональность разрабатываемого элемента.
Добавьте новый проект стандартной ЕХЕ-программы для отладки созданного элемента.
Выберите из меню File команду Маке, чтобы скомпилировать элемент в ОСХ-файл.
На примере элемента управления Blinker (BLINKER.VBP) я подробно рассмотрю первые 6 операций. Blinker (’.-мигалка») заставляет мигать окна или элементы управления, что привлекает к ним внимание. Компиляция и отладка рассматриваются в разделах: «Дорогой Джон, как... отладить элемент управления?» и «Дорогой Джон, как... откомпилировать и зарегистрировать элемент управления?»
Создание проекта ActiveX-элемента
Чтобы создать проект ActiveX-элемента:
Выберите из меню File команду New Project, и появится диалоговое окно New Project.
Дважды щелкните значок ActiveX Control. Visual Basic создаст новый проект и откроет окно Lserconirol.
Выберите из меню Project команду Project Properties, чтобы открыть одноименное диалоговое окно.
В поле Project Name введите имя проекта: VB6WfeSampa в поле Project Description — описание проекта: VB6 Workshop Blinker Sample Control. Затем щелкните кнопку ОК.
В окне свойств UserControl установите свойство Name как Blinker. Это имя класса элемента управления; оно используется при именовании экземпляров данного элемента, добавляемых на форму: Blinkerl. ВИнкег2 и т. д.
Выберите из меню File команду Save Project и сохраните элемент управления Blinker как BL1NKER.CTL, а проект — как BLINKER.VBP.
Конструирование интерфейса
Элемент Blinker заставит мигать другие элементы управления и окна в Вашем приложении, привлекая к ним внимание пользователя. Реализовать это не так трудно, как кажется, что для примера хорошо. Blinker использует ActiveX-элемент UpDown. а также встроенные элементы TexlBox и Timer (рис. 6-1).
Чтобы создать визуальный интерфейс элемента Blinker:"
Выберите из меню Project команду Components, и на экране появится одноименное диалоговое окно.
На вкладке Controls пометьте флажок MicrosoftWindows Common Controls-2 6.0 (MSCOMCT2.OCX) и щелкните кнопку ОК. Visual Basic добавит на панель Toolbox элемент управления Up- Down и ряд других стандартных элементов управления.
В окне UserControl нарисуйте текстовое поле. В окне Properties установите его свойство Name как ixiRuie, а значение свойства Textприравняйтенулю.
Разместите элемент управления UpDown рядом с текстовым полем и установите его свойство Name как ttpdnRarp
Добавьте элемент управления Timer и установите его свойство Name как tmrBlmk.
Рис. 6-1 Визуальный интерфейс элемента управления Blinker, состоящего из одного ActiveX- и двух встроенных элементов
Изменение размеров элемента управления
Можете особенно не стараться, рисуя интерфейс элемента управления Blinker, — все равно размеры и расположение визуальных элементов задаются в коде. Вставив элемент Blinker на форму, пользователь сможет сжимать и растягивать его, пока не добьется нужных размеров. Изменение размера генерирует событие, обрабатываемое процедурой UserControl_Resize, которая и устанавливает
соответствующим образом размеры и расположение элементов визуального интерфейса.
Ниже приведен код, обрабатывающий изменение размеров элемента Blinker:
' код для визуального интерфейса элемента управления
Private Sub IlserControl ResizeQ
' корректно размещаем -изуапьные элементы
' в окне UserControl
updnRate.Top = 0■■•
txtRate.Top = О
txtRate.Left = О
' изменяем размеры визуальных элементов при
’ изменении размеров самого элемента управления
updnRate.Height = Height
txtRate Heinht = Height
' регулируем ширину элемента управленияНрОоип
' максимум до 240 тейпов
If Width > 480 Then
ирапиате.иютп = 240.
'Else-
updnRate.Width = Width \ 2■
'End If
' устанавливаем ширину текстового поля
txtRate.Width = Width - updnRate.Width
' смещаем элемент управления UpDown < правому краю
' текстового поля
updnRate.Left = txtRate.Width
End Sub
Как видите, я чуть-чуть пофантазировал в процедуре, изменяющей размеры элемента управления UpDown. Вместо того чтобы зафиксировать его ширину на 240 твипах, я предусмотрел возможность ее уменьшения, пока ширина элемента Blinker не превышает 480 твипов. Ваш код, обрабатывающий изменение размеров элемента, может быть проще или сложнее — дело Ваше.
Добавление свойств, методов и событий
Помимо стандартных свойств, определяющих размеры, позицию, видимость и присущих всем элементам управления, у Blinker есть свойство TargetObjeci которое указывает мигающий объект, и Interval, которое задает частоту мигания объекта. Кроме того, у Blinker имеются событие Blinked, генерируемое после очередного мигания, и метод Blink, устанавливающий свойства TargetObject и Interval.
Свойства, методы и события ActiveX-элемента создаются точно также, каки у любого другого объекта. Следующий код определяет TargetObject, Interval, Blinked и Blink для элемента управления Blinker:
Option Explicit
' объявление API-функции Windows, заставляющей мигнуть окно
Private Declare Function Flashwindow _■
Lib "user32" ( _
ByVai hwnd As Long, _ ByVai blnvert As Long _
} As Long
' определение события Blinked .= "- ■-
Public Event Blinked 0"
1 внутренние переменные
Private mobjTarget As Object
Private mlngForeg round As Long b. : ' ■
Private mlngBackground As Long
Private mblnlnitialized As Boolean
' открытые константы кодов ошибок
Public Enum BlinkerErrors blkCantBlink = 4001.■
blkObjNotFound = 4002i■
End Enum
' исходный код свойств и методов элемента управления•'
' .Targetobject
Public Property Set TargetObject(Setting As Object)
If TypeName(Setting) = "Nothing" Then Exit Property записываем во внутреннюю переменную ссылку на целевойобъект
Set mobjTarget = Setting
End Property
Public Property Get TargetObject() As Object Set Targetobject = mobjTarget
End Property■ ■ '
' """.Interval■
Public Property Let Interval(Setting As Integer)
' настраиваем элемент управления UpOo^ это .вводит
' к автоматическому обновлению элементов TextBox и Timer updnRate.Value = Setting
End Property'
Public Property Get IntervalO As Integer
Interval = updnRate.Value
End Property1 -
' .Blink
Sub Blink(TargetObject As Object, Interval As Integer)
' делегируем свойствам Targetobject и Interval■
Set Me.Targetobject = Targetobject
Me.Interval = Interval.,--. ' : "
End Sub■" ■
Процедура Property Set Targetobject, определяя объект, который должен мигать, присваивает ссылку на него внутренней объектной переменной. Свойство Interval просто устанавливает и считывает значение UpDown. Это в свою очередь изменяет значение текстового поля и тем самым модифицирует значение свойства Interval таймера. Событие Blinked здесь только объявляется, а генерируется из обработчика события Timer таймера. Далее я покажу обработчики событий таймера и текстового поля — там-то все и происходит.
Программирование функциональности элемента управления
Пока что Blinker, хоть и смотрится отлично, ничего не делает. Его функция — обеспечить мигание окна или элемента управления — определяется кодом обработчиков события Timer таймера и Change текстового поля:
' код, определяющий функциональность элемента управления
Private Sub updnRate_Change()
' обновляем содержимое текстового поля
txtRate.Text = updnRate.Value
End Sub
Private Sub tmrBlink_Timar()■
On Error GoTo errTimer.
' счетчик чередования миганий .
Static blnOdd As Boolean'
blnOdd = Not blnOdd
' если объект - форма, используем API-функцию FlashWindow
If TypeOf mobjTarget Is Form Then
FlashWindow mobjTarnRt hund. CLng(blnOdd)
' если объект - элемент управления, обмениваем значения цветов
Elself TypeOf mobjTarget Is Control Then
If Not mblnlnitialized Then
mlngForeground = mobjTarget.ForeColor mlngBackground = mobjTarget.BackColor
-mblnlnitialized = True
End If
If blnOdd Then
mobjTarget.ForeColor = mlngBackground-
mobjTarget. BackColor = mlngForeground
Else
mobjTarget. ForeColor = mlngForeground mobjTarget. BackColor = mlngBackground End If
Else
Set mobjTarget = Nothing
GoTo errTimer.
End If
' генерируем событие Blinked ‘'
RaiseEvent Blinked
Exit Sub
errTimer:
If TypeName(mobjTarget) * "Nothing" Then
Err.Raise blkObjNotFound, "Blinker control", _
"Target object is not valid for use with this control." Else
Err.Raise blkCantBlink, "Blinker control", _
"Object can't blink,"
End If
End Sub
Private Sub + :tRate_Change()
' устанавливаем свойство Interval элемента управления Tinier
' в соответствии со значением в текстовом поле
If txtRate = 0 Then
tmrBlink.Interval = 0. .■
tmrBlink.Enabled = False "
mblnlnitlalized = False*
' если мигание отключено, возвращаем объект ' в исходное состояние
If TypeOf niuujidryoL Is Form Then1
FlashWindow mobjTarget.hwnd, Chng(False)■'
Elself TypeOf mobjTarget Is Control Then"
mobjTarget. ForeColor = mlngForeground mobjTarget.BackColor = mlngBackground
End If
Else'
tmrBlink. Enabled = True '■'*
tmrBlink. Interval = 1000 \ txtRate- ■
End If,■ •
End Sub
Обработчик события Change элемента I fpDown просто копирует значение последнего в текстовое поле. Процедура, обрабатывающая событие Timer таймера, обеспечивает мигание так: если целевой объект ■ форма, вызывает \ PI-функ! шю FlashWindow, а если это видимый элемент управления, обменивает значения свойств ForeColor и BackColor формы и элемента. (Кстати, использование API-функции FlashWindow эффективнее обмена значений свойств ForeColor и BackColor.) Обработчик события Change текстового ноля определяет частоту мигания.
itiO См. также...
• Главу 12 «Окна, диалоговые окна и прочие формы» — подробнее об API-функции FlashWindow. .
1Г|г^ГЮ»~^й ОжПМ. ВГ5$»Г-..
Отладить элемент управления?
Рис. 6-2 пели бы создадите проект ActiveX-элементаРис. 6-2 пели бы создадите проект ActiveX-элементаСоздав ActiveX-элемент, Вы, наверное, захотите его отладить. По умолчанию Visual Basic б предлагает отладку ActiveX-элементов в Internet Explorer, отображая их в окне браузера после выбора из меню Run команды Start (рис. 6-2). Хотя это позволяет выяснить, как будет выглядеть элемент управления в период выполнения, отладка в таком режиме — идея не самая удачная, потому что проверить свойства и методы элемента здесь не так-то просто.
Рис. 6-2 пели бы создадите проект ActiveX-элемента
Рис. 6-2 пели бы создадите проект ActiveX-элемента
и запустите его, Visual Basic б покажет элемент управления в окне Internet Explorer
Чтобы по-настоящему протестировать ActiveX-элемент, создайте группу проектов, в которую входят тестовый проект и проект ActiveX-элемента:
Загрузив проект элемента управления, сохраните его, выбрав из меню File команду Save Project.
Выберите из меню File команду New Project.
Дважды щелкните значок Standard EXE. Visual Basic создаст новый проект с единственной формой. В нем Вы и протестируете свой ActiveX-элемент.
Выберите из меню File команду Add Project.
В диалоговом окне Add Project на вкладке Existing (существующий) выберите проект своего ActiveX-элемента и щелкните кнопку Open.
Закройте окно UserControl, если оно открыто. Сразу после его закрытия Visual Basic активизирует на панели Toolbox значок созданного ВамиАсНуеХ-элемента.
Щелкните этот значок в Toolbox и разместите элемент управления на форме тестового проекта.
Напишите код тестового проекта, манипулирующий свойствами и методами этого элемента.
Запустите тестовый проект.
Следующий код позволяет протестировать элемент Blinker (blnfe- Test), размещенный на форме с текстовым полем txtstuff-.
Option Explicit..,
Private Sub Form_Load()
' определяем мигающий объект'.
blnkTest.Blink txtstuff, 1
End Sub
Private Sub Form_Click()
' прекращаем мигание
blnkTest.Interval = 0■ .
End Sub'"
Private Sub blnkTest_Blinked()
Static intCount As Integer
intCount = intCount + 11■ ■
Caption = "Blinked " & intCount & " times"
End Sub
Элемент Blinker в процессе тестирования показан на рис. 6-3.
Рис. 6-3 Тестирование элемента управления Blinker ,
При такой отладке элемента управления можно отслеживать выполнение кода ActiveX-элемента из тестового проекта. При этом Вы можете устанавливать точки прерывания, выполнять код в пошаговом режиме и использовать контрольные выражения и переменные, как показано на рис. 6-4.
Часть кода элемента управления исполняется перед выполнением тестового проекта. Чтобы убедиться в этом, поставьте точку прерывания в процедуре UserControl_Resize до размещения элемента управления на форме. Visual Basic перейдет в эту точку, как только Вы отпустите кнопку мыши, добавив ActiveX-элемент на форму.
Вы можете изменить код своего ActiveX-элемента в любой момент, но помните, что при открытии окна UserControl элемент ста-
нет недоступным как на тестовой форме, так и в Toolbox. Чтобы вновь сделать его доступным, закройте окно UserConirol.
Рис. 6-4 Элемент управления Blinker при отладке из тестового проекта
Если Вы уже поместили свой элемент на тестовую форму, а затем добавили к нему какое-нибудь свойство действующее на этапе разработки, то элемент управления на форме блокируется. Тот же эффект Вы подучите, прервав выполнение кода ActiveX-элемента. Элемент снова активизируется при запуске тестового проекта.
ПРИМЕЧАНИЕ При инициализации ActiveX-элемента в период выполнения свойства других элементов управления на той же форме могут быть доступны, а могут быть и недоступны коду ActiveX-элемента. Ссылка на элемент управления, помещенный на форму до тестируемого ActiveX-элемента, иногда приводит к зависанию приложения. Похоже, это ошибка Visual Basic.
Ооеогшз Лжок. как..
Откомпилировать и зарегистрировать элемент управления?
Компиляция ActiveX-элемента — операция очень простая, но сначала надо реипиь. компилировать его в машинный код или псевдокод (p-код). Машинный код исполняется быстрее, но p-код дает OCX-файл меньшего размера.
В случае элемента управления Blinker эти различия минимальны. Поскольку его работа зависит от событий таймера, скорость вы-
полнения не имеет особого значения, а разница между ОСХ-фай- лами с машинным и p-кодом составляет всего 5 килобайт (Кб).
Конечно, если Вы загружаете элемент управления по Интернету, разница даже в 5 Кб может оказаться решающей. Поэтому все элементы управления, которые Вы собираетесь использовать в приложениях для Интернета, лучше компилировать в р-код.
Чтобы установить параметры компилятора и скомпилировать созданный Вами элемент управления сделайте следующее.
Выберите проект элемента управления в окне Project Explorer. ■ Затем выберите из меню File команду Маке, дополненную именем проекта (например, Make Blinker.OCX), и появится диалоговое окно Make Project.
Щелкните кнопку Options (параметры), чтобы открыть диалоговое окно Project Properties.
Активизируйте вкладку Compile и выберите метод компиляции и параметры оптимизации кода, после чего щелкните кнопку ОК.
В диалоговом окне Make Project укажите каталог и имя файла, в который следует поместить скомпилированный элемент управления, и щелкните кнопку ОК, чтобы начать компиляцию.
По окончании компиляции Visual Basic автоматически регистрирует элемент управления на Вашем компьютере. Зарегистрированный элемент управления появляется в диалоговом окне Components (рис. 6-5).
iJshodi-'ave Flash1 jTreeCtf OLE Controlmcdtis□ VB 6 Application Wizard□vesoeta-orm WizardiJshodi-'ave Flash1 jTreeCtf OLE Controlmcdtis□ VB 6 Application Wizard□vesoeta-orm Wizard'•Эь w-rktep tinker ^тре Cgrtol'•Эь w-rktep tinker ^тре Cgrtol11= “Г11= “Гс?с?
iJshodi-'ave Flash
1 jTreeCtf OLE Controlmcdtis
□ VB 6 Application Wizard
□vesoeta-orm Wizard
iJshodi-'ave Flash
1 jTreeCtf OLE Controlmcdtis
□ VB 6 Application Wizard
□vesoeta-orm Wizard
'•Эь w-rktep tinker ^тре Cgrtol
'•Эь w-rktep tinker ^тре Cgrtol
11= “Г
11= “Г
с?
с?
331 -Wang Image Ad™ Control C Wang image Edi Control 1 1 Wang IfiageSGsnCoritrti
3
3
, Wang jr acf Tr игйл=а " tr
Г s^ted 3twtsii*.
■ ^a^'jb^i'sif^Bf Sir* Ginnit "
: Lectiin. C'iyB6tev\W*96WLIN<EiliBUIirEP6CX
Рис. 6-5 Диалоговое окно Components, в котором перечисляются все зарегистрированные элементы управления
Если Ваш элемент управления — часть приложения, которое Вы планируете распространять, Package and Deployment Wizard (мастер упаковки и распространения) обеспечит регистрацию этого
элемента при установке приложения на другом компьютере. А если Вы распространяете свой элемент управления без установочной программы, нужно будет установить DLL исполняющей системы (runtime DLL) Visual Basic, MSCOMCT2.OCX и собственно BLINKE- R.OCX, а затем зарегистрировать MSCOMCT2.OCX и В LINKER. OCX с помощью утилиты REGOCX32.EXE, которая находится в каталоге \COMMON\EOOLS\VB \REGLTILS на компакт-диске Visual Studio. Следующая команда регистрирует ActiveX-элемент Blinker: REG0CX32.EXE BLINKER.OCX
Лопогой Джон. КПК...
Создать свойство, доступное на этапе разработки?
Свойства Targetobject и Interval элемента управления Blinker могут быть установлены только в период выполнения. Чтобы создать свойство, которое можно было бы установить на этапе разработки из окна свойств, в процедурах обработки событий ReadProperties и WriteProperties следует воспользоваться объектом PropertyBag: ' определяем Interval как свойство, доступное на этапе разработки Private Sub UserControl_ReadPropertles(PropBag As PropertyBag)
updnfiate.Value = PropBag.ReadProperty("Interval", 0)
End Sub■...
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WrlteProperty "Interval", updnfiate.Value, 0 End Sub
Рис, 6-6 Объект PropertyBag позволяет добавлять свойства в окно Properties
Этот код добавляет свойство Interval в список свойств элемента управления Blinker, доступных в окне Properties, как показано на рис. б-б.
Вам также следует добавить оператор PropertyChanged в процедуру Property Let Interval, чтобы Visual Basic сохранил значение свойства, измененного на этапе разработки. Вот как выглядит код модифицированной процедуры:
.Interval
Public Property Let Interval(Setting As Integer),
' настраиваем элемент управления UpDown; это приводит'
' к автоматическому обновлению элементов TextBox и Timer updnRate. Value = Setting
' обновляем значение свойства, установленного на этапе разработки
PropertyChanged "Interval"
End Property
И, наконец, нужно позаботиться о том, чтобы изменение значения свойства Interval на этапе разработки не привело к генерации события таймера. Узнать, находится элемент управления в режиме разработки или он выполняется клиентским приложением, можно, проверив свойство Ambient.LserMod<- объекта UserControl. Это свойство равно False на этапе разработки и True в период выполнения. Чтобы избежать появления ошибок при записи в свойство Interval ненулевого значения на этапе разработки, внесем изменения в процедуру обработки события Change текстового поля:
Private Sub txtRate_Change()
' выход, если мы находимся в режиме разработки
If Not UserControl.Ambient.UserNode Then Exit Sub
‘ устанавливаем свойство Interval элемента управления Timer
' в соответствии со значением в текстовом поле
If txtRate = 0 Then
tmrBlink.Interval = О
tmrBlink. Enabled = False mblnlnitialized = False
■ir ’ если мигание отключено, возвращаем объект ., ,.. 'в исходное состояние
If TypeOf mobjTarget Is Form Then_
FlashWindow mobjTarget.hwnd, CLng(False)
Elself TypeOf mobjTarget Is Control Then mobjTarget.ForeColor = mlngForeground
- mobjTarget.BackColor = mlngBackground
End If- -
Else'
tmrBlink.Enabled = True"
tmrBlink. Interval = 1000 \ txtRate
End If,a
End Sub
ПРИМЕЧАНИЕ Свойство UserMode недоступно в обработчике события Initialize элемента управления.
Чтобы свойство Targetobject стало доступно на этапе разработки, прибегнем к одному трюку. Так как окно Properties не показывает объекты, создадим новое свойство, которое принимает строку, используемую в свою очередь для установки Targetobject. Это свойство — Targetstring — уже может появиться в окне Properties.
1 .Targetstring
Public Property Let TargetString(Setting As String)
If UserControl. Parent.Name = Setting Then
Set TargetObject = UserControl.Parent
Elself Setting <> "" Then
Set TargetObject = UserControl.Parent.Controls(Setting)
End If
End Property
Public Property Get TargetString() As String
If TypeName(mobjTarget) <> "Nothing" Then
Targetstring = mobjTarget. Name
Else
Targetstring = ”"
End If■
End Property
Кроме того, добавим в процедуру Property Set TargetObject оператор PropertyChanged:
' .TargetObject
Public Property Set TargetObject(Setting As Object)
If TypeName(Setting) = "Nothing" Then Exit Property
' записываем во внутреннюю переменную ссылку на целевой объект
Set mobjTarget = Setting
' значение свойства изменилось
PropertyChanged "Targetobject"
End Property
А теперь, чтобы добавить свойство Targetstring в окно Properties, отредактируйте процедуры обработки событий ReadProperties и WriteProperties:
' получаем значения, установленные на этапе разработки
Private Sub UserControl_ReadProperties(PropBag As propertyBag)
updnRate.Value = PropBag. ReadPropertyC'Interval", 0)
Targetstring = PropBag.ReadProperty("Targetstring", "")
End Sub’ ’
' сохраняем значения, установленные на этапе разработки
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "Interval", updnRate.Value, 0
PropBag.WriteProperty "Targetstring", Targetstring, ""
End Sub
Дорогой Джон, как... 1
Вывести диалоговое окно Property Pages?
Страницы свойств (property pages) позволяют определять свойства, доступные на этапе разработки ActiveX-элемента, в диалоговом окне с несколькими вкладками, а не из окна Properties среды Visual Basic. Такие страницы полезны для элементов управления, имеющих группы взаимосвязанных свойств; они упрощают доступ к свойствам, и пользователю не приходится подолгу прокручивать содержимое окна Properties в поисках нужного.
На страницах свойств можно отображать и списки допустимых значений, определяемых при разработке. Например, страница свойств элемента управления Blinker показывает список объектов на форме — допустимых значений свойства Targetstring (рис. 6- 7).
yes
Рис. 6-7 Страница свойств элемента у правления Blinker
iCilfti^'&iJki^ Р^ •С
Рис. 6-8 Диалоговое окно Connect Property Pages связывает страницу свойств с элементомуправления
Чтобы добавить страницу свойств к элементу управления, в окне Project Explorer выберите проект данного элемента, из меню Project — команду Add Property Page (добавить страницу свойств) и дважды щелкните значок Property Page. Присвойте странице свойств имя и задайте текст, показываемый на ярлычке страницы свойств,
установив соответствующим образом свойства Name и Caption в окне Properties. Чтобы связать страницу свойств с элементом управления, откройте его окно CserControl и выберите этот элемент управления. В окне Properties дважды щелкните свойство PropertyPages, чтобы открыть диалоговое окно Connect Property Pages (присоединить страницы свойств), показанное на рис. 6-8. Выберите нужную страницу свойств в списке Available Property Pages (доступные страницы свойств) и щелкните кнопку ОК.
Как только Вы свяжете страницу свойств с ActiveX-элементом, Visual Basic добавит строку (Custom) в список свойств, доступных на этапе разработки данного ActiveX-элемента. Двойной щелчок этой строки откроет страницу свойств.
Как и в случае окна UserControl, открытие окна страницы свойств означает, что Вы переключаетесь в режим конструирования (design mode) и можете размещать в этом окне элементы управления и писать код, реагирующий на события, — точно также, как Вы делали это для формы. У страницы свойств есть встроенный набор Selec- tedControls; из него можно получить экземпляр элемента управления, к которому относится эта страница свойств. Данные, показываемые вэлементах управления на странице свойств, инициализируйте в процедуре обработки события SelectionChanged.
Приведенный ниже код устанавливает начальные значения свойств Interval и Targetobject, которые показываются на странице свойств Blinker в текстовом поле txtinterval и поле со списком cmb Targetstring.
Private Sub PropertyPage_SelectionChanged()
' устанавливаем значение в поле Interval на странице свойств
' соответственно значению одноименного свойства элемента управления txtinterval = SalectedControls(O). Interval
' формируем список объектов для свойства Targetstring
Dim frmParent As Form
Dim ctrlndexAs Control
Dim strTarget As String
' получаем ссылку на форму, на которой размещен элемент управления Set frmParent = SelectedControls(O). Parent
strTarget = SelectedControls(O). Targetstring■
If strTarget <> "" Then
' добавляем текущее значение свойства в поле со списком cmbTargetString. List(O) = strTarget
End If
If frmParent,Name <> strTarget Then
' добавляем имя формы в поле со списком
cmbTargetSt ring.AddItem f rmParent.Name
End If
' добавляем название каждого элемента управления,
' расположенного на форме, а поле со списком
For Each ctrlndex In frmParent.Controls1
■ исключаем элемент управления Blinker' -
If TypeName(ctrlndex) О "Blinker" Or _
ctrlndex.Name = strTarget Then
cmbTargetString.Additem ctrlndex.Name
End If
Next ctrlndex
' показываем текущее значение Targetstring
cmbTargetString.Listindex = 0
End Sub
. ПРИМЕЧАНИЕ Набор SelectcdControls не доступен при обработке события Initialize страницы свойств.
Заметьте, что SelectedControls(O)возвращает объект, выбранный пользователем в данный момент, в нашем случае — элемент управления Blinker. Мне пришлось добавить в Blinker свойство Parent, чтобы страница свойств могла получить информацию о контейнере элемента Blinker. Исходный код свойства Parent элемента управления Blinker выглядит так:
' .Parent
Public Property Get ParentO As Object
Sat Parent = UserControl.Parent
End Property
Процедура обработки события ApplyChanges страницы свойств служит для записи значений, установленных на странице, в свойства АсИуеХ-элс-мснт а Следующий код считывает такие значения и помещает их в свойства ActiveX-элемента:
Private Sub РropertyPage_ApplyChanges()
' сохраняем значения со страницы свойств
' в свойствах элемента управления
Select0dControls(O).Interval = txtinterval.Text
SelectedControls(O).Targetstring = _
cmbTargetString.List _
(cmbTargetString.Listindex)
End Sub
Вы должны уведомлять страницу свойств о том, что пользователь изменил на ней значения. Встроенное свойство Changed указывает Visual Basic обработать изменения свойств, когда пользователь щелкнет кнопку СК или Apply. Процедуры, показанные далее, устанавливают свойство Changed, если какое-либо значение на странице свойств элемента Blinker изменяется.
Private Sub txtInterval_Change() л" с
Changed = True'<
End Sub.
Private Sub cmbTargetString_Change(). ,p
Changed = True.
End Sub. . ,...
Страницу свойств можно открыть несколькими способами: щелкнуть элемент управления правой кнопкой мыши и выбрать из контекстного меню команду Properties; дважды щелкнуть свойство (Custom) в окне Properties; щелкнуть кнопку с многоточием в поле значений свойства (Custom).
Чтобы добавить кнопку с многоточием к какому-либо свойству в окне Properties:
переключитесь в окно исходного кода ActiveX-элемента;
выберите из меню Tools команду Procedure Attributes, чтобы открыть одноименное диалоговое окно;
щелкните кнопку Advanced;
выберите нужное свойство в списке Name, а в списке Use This Page In Property Browser (использовать эту страницу при просмотре свойств) — страницу, которую Вы хотите связать с данным свойством (рис. 6-9);
щелкните кнопку ОК.
Рис. 6-9 Диалоговое окно Procedure Attributes позволяет связать страницу свойств с определенным свойством
Так, чтобы открыть страницу свойств элемента управления Blinker, показанную на рис. 6-10, щелкните кнопку с многоточием в поле значений свойства Targetstring.
При открытии страницы свойств щелчком кнопки с многоточием фокус ввода должен устанавливаться на соответствующее свойство. Это действие реализуется в обработчике события EditProperty страницы свойств. Вот пример такой процедуры, устанавливающей фокус на нужное поле после щелчка кнопки с многоточием в поле значений свойства Targetstring или Interval элемента управления Blinker:
Private Sub PropertyPage_EditProperty(PropsrtyName As String)
' устанавливаем фокус на соответствующий элемент управления
Select Case PropertyName
Case "Targetbcring
cmbTargetString.SetFocus
Case "Interval"
'txtinterval.SetFocus
Case Else
End Select
End Sub
Рис. 6-10 Диалоговое окно Property Pages элемента управления Blinker, открытое щелчком кнопки с многоточием
Дорогой Джон, как.,.
Загрузить свойство асинхронно?
ActiveX-элементы, используемые на Web-страницах, часто загружают значения своих свойств асинхронно. Это позволяет просматривать содержимое Web-страницы в Web-браузере, пока идет фоновая пересылка графики и других данных.
На рис. 6-11 показан элемент управления Asynchronous Animation, созданный на базе элемента управления Animation из библиотеки стандартных элементов управления Microsoft Windows Common Concrols-2 6.0 (MSCOMCT2.OCX).
Свойство AVIFile элемента управления Asynchronous Animation принимает строку, которая может содержать как спецификацию локального файла, так и универсальный указатель на ресурсы (Uniform
Resource Locator, URL). Метод AsyncRead начинает передачу файла на локальный компьютер, записывая его в каталог \Vmdows\Temp под именем, формируемым самим Visual Вао^ Исходный код свойства AVIFile таков:
Option Explicit
Dim mstrAVISourceFile As String •
Dis mstrTempAVIFile As String ,
’ "".AVIFile
Property Let AVIFile(Setting As String)
If UserCr"11'™'1 Mbient. UserMode _
And Len(Setting) Then
AsyncRead Setting, vbAsyncTypeFile, "AVIFile" mstrAVISourceFile = Setting
End If
End Property
Property Get AVIFile() As String AVIFile = mstrAVISourceFile
End Property
Рис. 6-11 Элемент управления Asynchronous Animation, который воспроизводит АУ1~файл, загруженный в фоновом режиме
По завершении пересылки метод AsyncRead генерирует событие AsyncReadComplete. Процедура обработки -vyncReadCompletc обрабатывает все асинхронные события элемента управления. Переход к обработке события от нужного асинхронного свойства обеспечивает оператор Select Case, в котором анализируется значение свойства AsyncProp.PropertyNamc. В данном примере обработчик события AsyncReadComplete открывает и воспроизводит AVI-файл, используя элемент управления Animation с именем aniControl-.
' базовый обработчик всех событий AsyncReadComplete
Private Sub UserControl_AsyncReadComplete _
(Async Prop As Async Property)
Select Case AsyncProp. PropertyName
для свойства AVIFile
' Case "AVIFile"
получаем имя временного файла■
mstrTempAVIFile = AsyncProp. Value
открываем файл
aniControl.Open mstrTempAVIFile
' воспроизводим анимационный ролик aniControl.Play
Case Else
End Select
End Sub
По завершении работы элемента управления обязательно удалите все созданные Вами временные файлы. Хорошо продуманные приложения для Интернета не должны забивать диски на компьютерах пользователей бесполезными временными файлами. Этот код закрывает AVI-файл и удаляет временный файл с данными:
Private Sub UserControl_Terminate()
' удаляем временный файл-
If Len(mstrTempAVIFile) Then
aniControl.Close.
Kill mstrTempAVIFile" '-
End If
End Sub
Использовать элемент управления Asynchronous Animation очень легко: поместите его на форму и установите свойство AVIFile в обработчике какого-нибудь события. Например, код, приведенный ниже, запускает анимационный ролик Find File с компакт-диска Visual Studio в момент загрузки формы (aaniFindFile — имя элемента управления Asynchronous Animation):
Private Sub Fonn_Load()
aaniFindFile. AVIFile = "d:\common\graphics\avis\flndfile.avi''
End Sub■ ■
Ю См. также....‘"
• Главу 8 «Создание компонентов для Интернета» — о том, как внедрить элемент управления Asynchronous Animation на Web- страницу.
Дорогой Джон, как...
Создать элемент управления для использования с базой данных?
ActiveX-элементы способны отображать информацию из записей базы данных, позволяя связывать с данными одно или несколько свойств. Связывание с данными (data binding) — формирование зависимости между свойством элемента '.правления и каким-либо полем записи или запроса базы данных. Например, свойство Interval элемента Blinker, созданного в предыдущих разделах этой главы, могло бы быть связано с числовым полем базы данных. Чтобы установить связь между свойством элемента управления и какими- либо данными:
загрузите в Visual Basic проект элемента управления-..-
выберите из меню Tools команду Procedure Attributes. Появится одноименное диалоговое окно;
щелкните кнопку Advanced. Visual Basic покажет полный вариант диалогового окна Procedure Attributes (рис. 6-9);
в списке Name укажите имя свойства, связываемого с данными;
установите флажок Property Is Data Bound (свойство связано с данными). Затем пометьте другие флажки в группе Data Binding (связывание с данными), описанные ниже, и щелкните кнопку ОК. □ Установите флажок This Property Binds То DataField (это свойство связывается с полем данных), если это свойство Вашего элемента управления — главное или единственное, связанное сданными. Если же у элемента управления несколько свойств, связываемых с данными, пометьте этот флажок для свойства, которое будет содержать важнейшее поле из записи базы данных. Так или иначе, но этот флажок можно активизировать только для одного свойства элемента управления.
Q Установите флажок Show In DataBindings Collection At Design Time (показывать в наборе DataBindings на этапе разработки), чтобы данное свойство появлялось в диалоговом окне Data Bindings (связи с данными) на этапе разработки. А если свойство должно настраиваться только в период выполнения, оставьте этот флажок неактивным.
Установите флажок Property Will Call t ainPropertyChangc Before Changing (свойство будет вызывать CanPropertyChange перед изменением), если пользователь или программа может изменять данные, отображаемые через это свойство. Тогда будет проверяться допустимость данных, записываемых в это свойство.
Q Установите флажок Update Immediate (обновлять немедленно), чтобы любые изменения данных ь свойстве сразу отражались на поле, связанном с этим свойством.
fcOwi WS
tedusation includes-а Е~ г. р^споЬщ-яот labdpo Г|
Istele LlmvB<$iEy in 19?0 She else compleled "The Ail [of the Odd Cafl," |Чагсу|$атесг<я(оГТ9а«ггм^г • ||Шнм'ВПс8i
Ti4l <1-1^ r>L ы^л-т.
Рис. 6-12 Элемент управления Employee предназначен для отображения полей из набора записей Employees UNWIND
5—1204
Я создал простой элемент управления Employee (рис. 6-12) специально для использования с базой данных NW1ND, поставляемой с Visual Basic.
Элемент Employee (DatCtl.VBP) состоит из нескольких меток и текстового поля, которое показывает данные из таблицы Employees базы данных NXVTND Свойства FirstName, LastName, HireDate и Notes этого элемента определены так:
' раздел определений свойств',
Public Property Get FirstNameO
FirstName = IbLFlrstName.Caption.
End Property■
Public Property Let FlrstName(Settfng)
IblFirstName. Caption = Setting
PropertyChanged FirstName
End Property
Public Property Get LastNameO As String
LastName = "LblLastName. Caption
End Property'
Public Property Let LastName(Setting As String)■'
IblLastName.Caption = Setting
PropertyChanged LastName.
End Property'
Public Property Get HireDatef) As String'
HireDate = IblHireDate. Caption
End Property.
Public Property Let HireDate(Setting As String)
IblHireDate.Caption = Setting■' ■
PropertyChanged HireDate"
End Property..
Public Property Get Notes() As String
Notes - txtNotes.Text
End Property-
Public Property Let Notes(Setting As String)
txtNotes.Text = Setting
PropertyChanged Notes"
End Property
Заметьте: все процедуры Property Let содержат оператор Pro- pertyChanged. Согласно документации Visual Basic Вы должны включать этот оператор в любые свойства, связываемые с данными, — даже если они доступны лишь в период выполнения.
Чтобы использовать элемент управления Employee сделайте следующее.
Откройте новый проект стандартной ЕХЕ-программы.
Добавьте в него проект DatCtl.VBP, чтобы создать новую группу проектов.
Поместите на форму элемент управления Data и настройте его свойство DataBaseName на базу данных NWIND.MDB (она находится в каталоге программ Visual Basic). В списке значений свойства RccordSource выберите Employees.
Поместите на форму элемент управления Employee и в поле значений его свойства DataBindingsщелкните annuo. с многоточием. Visual Basic откроет диалоговое окно Data Bindings (рис. 6-13).
Рис. 6-13 Для связывания свойств с конкретными полями в наборе записей используйте диалоговое окно Data Bindings
В списке Property Name выберите свойство FirstName, в списке Data Source (источник данных) — Data 1 и в списке Data Field (поле данных) — поле FirstName. ,
Рис. 6-14 Совместное использование элементов управления Employee и Data для отображения записей из ВД NW1NDMDB
Повторите операцию из и. 5 для установки всех свойств элемента Employee и щелкните кнопку ОК.
Запустите проект. При «пролистывании •• записей в элементе Data элемент Employee будет показывать информацию из базы данных N WIND (рис. 6-14)..
-’V 1^л * а ш.
Использовать элемент управления DataRepeater?
Элемент DataRepeater — своего рода контейнер для создаваемых Вами AcLiveX-элсментов. связанных с данными. Чтобы задейство-
11- DataRepeater, Вы должны сначала создать и откомпилировать соответствующий элемент управления (вроде Employee, о котором я рассказывал в предыдущем разделе).
DataRepeater позволяет заменять страничные представления (page views) базы данных прокручиваемым списком. В этом плане он похож на FlexGrid, но дает возможность вставлять в сетку дополнительные элементы управления. Рис. 6-15 иллюстрирует, как выглядит одна и та же таблица базы данных на форме и в элементах управления FlexGridH DataRepeater.
Рис. 6-15 Нестандартная форма и элементы управления FlexGrid и DataRepeater — три способа просмотра таблицы Employees базы данных NW1ND
ПРИМЕЧАНИЕ DataRepealer требует совместимого источника данных типа элемента управления ADO Data. Со встроенным элементом управления Data он не работает.
Чтобы использовать элемент управления DataRepeater сделайте следующее.
Выберите из меню Project команду Components. Visual Basic откроет диалоговое окно Components.
Установите флажки в строках Microsoft DataRepeater Control 6.0 и Microsoi l ADO Data Control 6.0, потом щелкните кнопку OK. Visual Basic добавит на панель Toolbox элементы управления ADO Data и DataRepeater.
Поместите на форму элемент управления DataRepeater.
Поместите на ту же форму элемент управления ADO Data.
В поле значений свойства Connectionstring элемента ADO Data щелкните кнопку с многоточием, чтобы открыть диалоговое окно Property Pages.
Выберите параметр Use ODBC Data Source Name (использовать имя источника данных ODBC) и либо укажите в списке источник данных (и тогда пропустите этапы 7-11), либо создайте новый источник данных, щелкнув кнопку New. (В пп. 11 я поясняю, как создать новый источник данных ODBC.)
В окне Create New Data Source (создание нового источника данных) выберите переключатель System Data Source (AppliesТо This Machine Only) [системный источник данных (применим только к этой машине)) и щелкните кнопку Next (далее).
В списке Name выберите Microsoft Access Driver (*.MDB) щелкните кнопку Next, затем — кнопку Finish (готово).
В поле Data Source Name окна ODBC Microsoft Access Setup (установка Microsoft Access ODBC) введите имя источника данных.
Щелкните кнопку Select (выбрать) и укажите базу данных Access для этого источника данных. Щелкните кнопку ОК, чтобы закрыть окно ODBC Microsoft Access Setup.
.Активизируйте параметр Use ODBC Data Source Name, выберите в списке источник данных, который Вы создали на этапе 9, и щелкните кнопку ОК.
В поле значений свойства RecordSource элемента ADO Data щелкните кнопку с многоточием, чтобы открыть диалоговое окно Property Pages.
13-В списке Command Туре (тип команды) выберите строку 2 - adCmdTablc. а в списке Table Or Stored Procedure Name (имя таблицы или хранимой процедуры) — нужную таблицу; потом щелкните кнопку ОК
Выберите элемент управления DataRepeater и установите его свойство DataSource наимя элемента управления ADO Data, который Вы разместили на форме в п. 4.
Настройте свойство RepeatedControlName элемента DataRepeater на имя ActiveX-элемента, связанного с данными., ,
В поле значений свойства (Custom) элемента DataRepeater щелкните кнопку с многоточием, чтобы открыть диалоговое окно Property Pages, и выберите вкладку RepcaterBindings (рис. 16).
В списке ююрспуМатс укажите имя нужного свойства, ав списке DataField — поле записи, которое будет отображаться данным свойством; затем щелкните кнопку Add.
&*^^ *ti^&^^1 For* ^Atm^
AitlxBte^fotLg^hi '" ’ ’ 'J .Ркря^и»’ BftirW fa»AitlxBte^fotLg^hi '" ’ ’ 'J .Ркря^и»’ BftirW fa»3-3 гг-
AitlxBte^fotLg^hi '" ’ ’ 'J .Ркря^и»’ BftirW fa»
AitlxBte^fotLg^hi '" ’ ’ 'J .Ркря^и»’ BftirW fa»
-। дь f J | нл ■ *
Рис. 6-16 Свяжите повторяемые свойства (repeated properties) с полями записи в диалоговом окне Property Pages для мементауправления DataRepeater
18. Повторите опсоа ш по из п. 8 для всех свойств элемента управления, связанных с данными. Закончив, щелкните кнопку ОК.
В период выполнения Вы сможете прокручивав г записи, используя кнопки со стрелками в элементе ADO Data или полосу прокрутки в элементе Data Repeater. Не исключено, что именно по этой причине Вы предпочтете скрывать элемент ADO Data в период выполнения.
Дорогой Джон, как...
Создать контейнерный элемент управления? .
Если свойство i .ontrolContainer нестандартного элемента управления установить как True, объекты, оазметснныг поверх этого элемента, можно перемещать и масштабировать как единую группу. К контейнерным элементам управления, поставляемым с Visual Basic, относятся, например Microsoft Tabbed Dialog и DataRepeater. На рис. 6-17 показан простой контейнерный элемент управления, созданный в Visual Basic с применением элементов Shape и Label.
Код элемента Containr (Containr.VBP) обрабатывае т изменение размеров рамки так:
Private Sub UserControl_Resize()
' подгоняем рамку под размеры элемента управления
shpFrame.Width = UserControl.ScaleWidth - shpFrame.Left
shpFrame.Height = UserControl.ScaleHaight - shpFrame.Top
End Sub
Заголовок элемента Containr инициализируется через объект Ех- lender (он обеспечивает доступ ко встроенным свойствам, которые Visual Basic и другие контейнерные элементы управления открывают всем объектам). Объект Extender доступен в процедуре InitProperties, но, если Вы попытаетесь обратиться к нему на более ранних стадиях жизненного цикла элемента управления (например, из обработчика события Initialize), возникнет ошибка. Следующий код отображает имя элемента управления, присваиваемое самим Visual Basic при создании экземпляра этого элемента:
Private Sub UserControl_InitProparties()
' отображаем соответствующий заголовок
IblTitle.Caption = Extender.Name
End Sub
Рис. 6-17 Свойство ControlContain er элемента управления Containrycmanoeaeno как True, поэтому он имитирует поведение элемента управления Frame
Свойство Caption устанавливает и возвращает заголовок, показываемый меткой IblTitle. Это свойство, предназначенное для чтения и записи, доступно на этапе разработки, поэтому для него предусмотрены процедуры Property Let и Get, а также обработчики событий ReadProperties и WriteProperties;
Private Sub UserControl_ReadFroperties(PropBag As PropertySag)
IblTitle.Caption = PropBag.RBadPropertyC'Caption") End Sub
Private Sub UserControl_WriteProperties(RropBag As PropertyBag)
PropBag.WriteProperty "Caption", IblTitle.Caption
End Sub. ■ ■
' раздел определений свойств1 ‘
Public Property Get Caption() As String'
Caption = IblTitle.Caption
End Property.
Public Property Let CaptiontSetting As String)
IblTitle.Caption = Setting
End Property. x
Свойство СогДгоквозвращаетнаборэлементовуправления, содержащихся в элементе Containr. Это единственное, чего не предусматривает стандартный элемент Frame, и я включил этот пример специально для того, чтобы продемонстрировать, как использовать набор ContainedControls. Последний создается Visual Basic, когда Вы устанавливаете свойство ControlContainer как этого набора нет у элементов управления других типов. Определение свойства Controls (только для чтения) выглядит так:
' свойство только для чтения
Public Property Get Controls() As Collection
Set Controls = (JserControl.ContainedControls
End Property
ГЛАВА 7
Использование компонентов для Интернета
Говоря о программировании для Интернета, разные люди имеют в виду разные вещи. Применительно к коммерческому программному обеспечению это может означать разработку новых клиент- серверных средств, использующих Web. Отдел продажи маркетинга может понимать под этим термином создание Web-страницы, способной принимать заказы на продукцию, продаваемую через Интернет, а отдел кадров — публикацию некоего справочника для сотрудников, работающего как автономное приложение в интрасети.
Из-за таких расхождений я разбил тематику по программированию для Интернета на три главы.
В этой главе я покажу, как ActiveX-элементы, поставляемые с Visual Basic, позволяют решать самые разные задачи, начиная с обращения к низкоуровневым коммуникационным протоколам и заканчивая созданием собственного Web-браузера на основе Internet Explorer (IE).
В главе 8 «Создание компонентов для Интернета» я объясню, как создаются контейнеры для «содержания», распространяемого по Интернету. В этой же главе будут рассмотрены вопросы, связанные с созданием ActiveX-элементов и Web-классов, которые можно использовать в Интернете.
А в главе 9 «Создание приложений для Интернета» мы, опираясь на информацию из глав 7 и >< реализуем на практике все три класса приложений для Интернета.
Дорогой Джон, как...
Выбрать подходящий ActiveX-компонент?
Visual Basic (Professional и Enterprise Editions) включает три ActiveX- элемента, которые обеспечивают доступ ко всем уровням связи через Интернет.
& Элемент управления Winsock (MSWINSCK.OCX) предоставляет низкоуровневый доступ к TCP и UDP — сетевым протоколам, применяемым в Интернете. С помощью этого элемента управления создаются программы для «бесед» и прямой передачи данных между двумя и более компьютерами, объединенными сетью.
я Элемент управления Тщепкч Transfer (MSINET.OCX) позволяет копировать файлы с HTTP- и FTP-серверов, просматривать на них каталоги и получать данные как синхронно, так и .юиихрон- но. Этот элемент управления нредназначендля создания средств просмотра FTP-серверов и обмена файлами.
■ Элемент управления WebBrowser (SHDOCVW.DLL) заключает в себе все возможности IE. Используйте его для расширения существующих приложений средствами Интернет-браузера или для управления IE из другого приложения посредством Автоматизации.
ПРИМЕЧАНИЕ Элемент WebBrowser в диалоговом окне Components представляется строкой Microsoft Internet Controls и устанавливается вместе с IE. Версия 4.0 последнего находится на компакт-диске Visual Studio.
В следующих разделах мы поговорим о разных уровнях доступа к Интернету и рассмотрим примеры использования каждого из этих элементов управления.
■ Дорогой Джои, как...
Представить уровни интернет-протоколов?
Интернет — система, связывающая постоянно растущее число различных компьютерных сетей. Чтобы охватить все это многообразие, в Интернете принят целый набор протоколов. Подобно дипломатическим протоколам, они определяют характеристики каждого уровня Интернета. Иерархия некоторых из важнейших протоколов показана на рис. 7-1.
Транспортный уровень обозначает границу, где кончается Ваше приложение и начинается собственно Интернет. Он состоит из , протоколов UDP (User DaiagramProtocol) и TCP (Transmission Control Protocol). Эти два протокола определяют, как приложения обмениваются данными по Интернету, в том числе как устанавливается соединение и какданные разбиваются на отдельные пакеты.
Вы можете использовать протокол i I ш или TCP для прямой связи по Интернету. Например, передавать данные в некоем специфическом для Вашего приложения формате или рассылать простое сообщение на множество машин. Поскольку эти протоколы работают на более низком уровне, чем Ваше приложение, они не приводят тем издержкам, которые свойственны протоколам прикладного уровня (скажем, HTTP или FTP).
Как я уже говорил, элемент управления Winsock обеспечивает прямой доступ к протоколам < I Ши TCP, а элемент управления Inter- netTransfer ■ к протоколам FTP и HTTP. Элемент управления WebBrowser интерпретирует данные, передаваемые по FTP или HTTP, и форматирует : i \ так же, как и IE. Выбор конкретного элемента уп-
равнения зависит от типа создаваемого Вами приложения, и на эту тему мы поговорим в следующих разделах.
Физический уровеньФизический уровеньfiber, RS232, V.42fiber, RS232, V.42Рис. 7-1 Протокап разных уровней помогают справиться со сложностью связи по ИнтернетуРис. 7-1 Протокап разных уровней помогают справиться со сложностью связи по Интернету
Физический уровень
Физический уровень
fiber, RS232, V.42
fiber, RS232, V.42
Рис. 7-1 Протокап разных уровней помогают справиться со сложностью связи по Интернету
Рис. 7-1 Протокап разных уровней помогают справиться со сложностью связи по Интернету
Дорогой Джон, как...
Настроить доступ к сети?
Операции с большинством компонентов для Интернета, рассматриваемых в этой и двух последующих главах, потребуют доступа минимум кдвум компьютерам, подключенным ксети и выполняющим протокол TCP/IP (Transmission Control in ок )col/Internet Protocol). Этот протокол входит в состав Windows 95 и Windows NT, но Бы должны убедиться, что . ■; установлен. Для этого дважды щелкните значок Network (Сеть) : окне Control Panel (Панель управления), выберите вкладку Protocols (Протоколы) в Windows NT или Configuration (Конфигурация) в Windows 95 и проверьте список протоколов, установленных на Вашем компьютере (рис. 7-2).
Если полноценной сети у Вас нет, создайте в Windows NT Server с помощью службы к v. (Remote Access Server) сервер удаленного доступа и подключитесь к этой машине по телефонной линии, ис-
пользуя утилиту Dial-Up Networking (Удаленный доступ к сети), ■Служба RAS устанавливается как компонент Windows NT Server, а утилита Dial-Up Networking поставляется с Windows NT и Windows 95 как стандартная программа.
Рис, 7-2 Для использования элемента управления Winsock надо установить протокол TCP/IP
iNii^ut^ Hl'N ■И:1.’:S7f7
biiFswte|ew [ми**»»4Й^*5|<
км
f- £y^*ilP'ad^tih№*№tiPtef|liy
FWeti [ k ’ с 6 Г
’рЙ^Йдаед^ p-——
Рис. 7-3 Диалоговое окно TCP/IPProperties позволяет ■установить фиксированный IP-адрес колтъютера, к которому Вы будете подключаться через RAS и Dial- Up Networking
Работая с RAS и утилитой удаленного доступа к сети, Вы скорее всего не сможете использовать ^дружественные имена» при подключении к другим компьютерам в сети. Вместо этого Вам придется указывать фиксированный IP-адрес, который можно определить через диалоговое окно TCP/IP Properties (Свойства: TCP/IP) (рис. 7-3).
IP-адрес применим везде, где ожидается имя компьютера. Например, в свойстве RemoteHost элемента управления Winsock, в поле Address (Адрес) браузера IE или в методе OpenL'RL элемента управления Internet Transfer.
Для публикации файлов на одной из машин и просмотра их с другой посредством браузера IE Вы, вероятно, установите Microsoft PersonaiWeb Server (PWS) или Microsoft Internet Information Server .(IIS). Это весьма удобно для переноса файлов с одной машины на другую, и, кроме того, позволит отлаживать активные серверные страницы (Active Server Pages, ASP) и тестировать пакет интернет - компонентов перед их распространением. PWS и IIS входят в набор серверных компонентов, поставляемых с Microsoft Visual Mudio.
Дорогой Джон. как...
Обеспечить связь через Winsock?
Приложения соединяются с Интернетом по IP-адресу и номеру порта. Комбинация IP-адреса и номера порта называется сокетом (socket). Для установления связи между двумя компьютерами Вы должны создать соответствующую пару сокетов. А чтобы создать такие сокеты для работы с протоколом UDP или TCP, можно воспользоваться элементом управления Winsock (MSWINSCK.OCX). Выбор конкретного протокола зависит от типа нужной Вам связи.
.■ UDP обеспечивает безадресную связь (не требуя соединения с определенным компьютером). Вы посылаете данные, не зная, кто и когда примет их. Этот протокол подходит для сетевых файловых сервисов, широковещательных сообщений по сети и приложений, позволяющих вести «беседы».
8 TCP требует устанавливать соединение с другим компьютером перед обменом данными и закрывать его по завершении сеанса. Этот протокол предусматривает передачу данных отдельными пакетами и проверку корректности их приема. Он удобен для передачи файлов и других больших потоков данных, посылки и приема электронной почты, а также для установления прямой связи между двумя компьютерами.
В следующем разделе я покажу, как на основе элемента управления Winsock создать интернет-приложение, рассчитанное на протокол UDP или TCP Однако, прежде чем браться за разработку собственной системы электронной почты, Вы должны осознать, что Winsock — средство, предназначенное для низкоуровневого про-
граммирования. и что для Visual Basic уже реализованы средства поддержки протоколов прикладного уровня. К последним относятся Internet Transfer (MSINET.OCX) для FTP и HTTP, а также ряд коммерческих элементов управления для POP (Post Office Protocol) и передачи звука и трехмерных изображений. Информацию об элементах управления, созданных сторонними разработчиками, Вы найдете в Интернете. Вот несколько адресов, которые послужат Вам ■отправной точкой:
■ Mabry Software, Inc. — Internet Pack for POP, Finger, Whois и другие элементы управления (_bttp://wwiu.mcibry.corn);
■ devSoft Inc. — IP'Works! for POP, SMTP, DNS и другие элементы управления (hnp://ujww.dei!-soft.com)\
и Template Graphics Software, Inc. — продукты из семейства TGS 3D Developer Toolkit (bttp://wuiw.tgs.com).'
Вещание по протоколу UDP
Использование элемента ■ правления Winsock c UDP требует двух подготовительных процедур: одной — для передачи данных, другой — для приема.
Чтобы передать данные, добавьте элемент Winsock на форму и действуйте по следующей схеме:
запишите в свойство RemotePort номер порта, используемого другими машинами для приема данных, посылаемых Вами;
установите свойство RemoteHost. Его значением должен быть IP- адрес или имяхост-компьютера, который будет принимать Ваши данные;
откройте методом Bind локальный порт, через который будут отправлены данные;
передайте данный методом SendData.
Чтобы принять данные по протоколу UDP:
запишите в свойство RemotePort номер порта, указанный при вызове метода Bind в и. 3 предыдущей процедуры;
откройте методом Bind локальный порт, через который будут приняты данные. Номер порта должен совпадать со значением, 3anHcaHHbiMBCBOftcTBoRemotePortBn. 1 предыдущейпроцедуры;
вызовите метод GetData из обработчика события DataArrival, чтобы получить поступившие данные.
Пример
Поскольку идентифицировать удаленный хост при приеме данных не требуется, UDP позволяет получать их и на других компьютерах. Связь типа <многие-к-одному»дает возможность создавать административные средства для отслеживания состояния рабочих станций
в сети. Рассмотрим пример такого средства — программу Systems Status Monitor (SYSSTAT.VBP), окно которой показано на рис. 7-4-
Командная строка, введенная на локальной машине
SysStat.exe woribatl
Удаленная машина
идНухЦЕёв» Siaiuri Илги|(тг «тайн*! • • •
1
5hc*wm I Р Save-«гн5*т»ley
Рис. 7-4 Systems Status Monitor передает на машину администратора сведения о свободном дисковом пространстве на рабочих станциях
Systems Status Monitor выполняет две функции. При запуске из командной строки вычисляет размер свободного дискового пространства (в процентах) на локальном компьютере и передает его значение на заданный удаленный компьютер. А при обычном запуске выступает' ь роли пассивного получателя информации, поступающей от рабочих станций.
Процедура Porm_Load в Systems Status Monitor демонстрирует настройку элемента WinsclCkдля приема или передачи данных в зависимости от способа запуска программы.
Option Explicit
' создаем объект для файла журнала
Dim fsystog As New Scripting. FileSystemObj ect... -
' s командной строке указывается имя или IP-адрес машины, ' которой посылаются данные
Private Sub Form_Load()
' запускаем программу в режиме передачи,
' ВОЛИ в командной строке что-то задано
If Command О Then
' указываем порт на удаленной машине sckUDP.RenotePort = 1002
' идентифицируем хост, которому посылаются данные
SCkl/DP. RemoteHost = Command
' выполняем привязку к локальному порту,
' через который передаются данные
sckUDP.Bind 1001 ' посылаем информацию sckUDP. SendPata ShowStats ' ■• завершаем программу,
■ Unload Me
' запускаем программу в режиме приема,■
'если в командной строке ничего не задано"
Else. ■:'
' указываем порт на удаленной машине, который' "
' мы будем "слушать"
. sckUDP.RemotePoit = 1001
' указываем локальный порт, через который принимается данные sckUDP. Bind 1002
■' отображаем полученную информацию и имя компьютера,
Не.Caption = Me.Caption & "; " & sckUDP.LocalHostName
End If‘'
End Sub
Обработчик события Error элемента Winsock вызывается, если нет соединения с Интернетом или если имя удаленного хоста задано неправильно. Поскольку Systems s-.iiTs Monitor использует протокол LjDL, контроль ошибок минимален. Когда возникает какая-нибудь ошибка, просто выводится соответствующее сообщение. ' обрабатываем ошибки передачи
Private Sub sckUDP_Error(ByVal Number As Integer, _
Description As String, ByVai Scode As Long, _
ByVai Source As String, ByVai HelpFile As String, _
ByVai HelpContext As Long, CancelDisplay As Boolean _
).
' выводим сообщения об ошибках'
MsgBox Number & " " 4 Description, vbCritical, App.Title
End Sub
Обработчик события 1’.n.Arrival элемента Winsock вызывается в момент поступления данных в порт, к которому была осуществлена привязка. Метод GetData извлекает поступившие данные и очищает приемный буфер, освобождая место для следующей порции данных в очереди.
' обрабатываем данные по мере поступления
Private Sub sckUDP_UaraArrivan.Byvar uytesTotal As Long)
Dim strData As String
' извлекаем данные
- sckUDP. GetData strDataV'Ts "
' отображаем их
ShowText strData
' если флажок помечен, сохраняем данные в файле журнала
.If chklog. Value Then
fsysLog.CreateTextFile(CurDir & "\stats.log", _
‘ True),Write txtReceive.Text
End If ■
End Sub
Данные передаются по протоколу C’DP единой порцией, размер которой зависит от параметров сети — поэкспериментируйте, если хотите подобрать его верхний предел. Однако обмениваться большими объемами данных при использовании UDP неразумно. В этом случае переходите на протокол TCP.
Беседа «один-на-один» по протоколу TCP
Применяя элемент Winsock с протоколом TCP, Вы должны устанавливать соединение до передачи или приема данных. При этом одна машина должна выдать запрос на соединение, а другая - принять его.
Чтобы выдать запрос, добавьте элемент Winsock на форму и действуйте по следующей схеме:
установите свойство RemoteHost. Его значением должен быть IP- адрес или имя хост-компьютера, который будет принимать Ваши данные;
запишите в свойство RemotePort номер порта, используемого другими машинами для приема данных, посылаемых Вами;
вызовите метод Connect, чтобы выдать запрос на соединение с другой машиной;
передайте данные методом SendData,
Чтобы принять запрос на соединение:
запишите в свойство Localport номер порта, указанный в свойстве RemotePort в п. 2 предыдущей процедуры;
откройте методом Listen локальный порт для приема запросов на соединение;
вызовите метод Accept из обработчика события ConnectionRequest, чтобы установить соединение.
Установив соединение, можно обмениваться данными, используя методы SendData и GetData — так же, как и по протоколу (."DP. Но в отличие от последнего протокол TCP требует перед созданием нового соединения закрывать существующее (для чего применяется метод Close).
Пример
Программа-пример Chat (CHAT.VBP) демонстрирует, какиспользо- вать Winsock с протоколом TCP для создания соединения между двумя компьютерами. После того как соединение установлено, пользователи этих компьютеров могут обмениваться сообщениями, набирая текст в поле (рис. 7-5).
Первый компьютер
iltii чи1: 18
Рис. 7-5 Программа Chat позволяет двум пользователям обмениваться сообщениями по протоколу TCP
Программа Chat в обработчике FormJLoad указывает локальный порт, который она будет -слушать», и переходит в режим ожидания запроса на соединение.
Option Explicit
' ждем запросов на соединение
Private Sub Form_Load()
' задаем порт, который мы будем "слушать"
sckTCP,Localport = 1002■■
' переходим в режим ожидания
sckTCP.Listen
' обновляем строку состояния"
ShowText "Listening"'
End Sub''
Чтобы инициировать соединение, вызовите метод Connect. Программа Chat позволяет указать имя или IP-адрес вызываемого компьютера (ввод данных осуществляется через InputBox). закрыть любое существующее соединение, а потом установить новое:
Private Sub mnuConnect_Click()
Dim StrRemoteHost As String'
' получаем имя или IP-адрес вызываемого компьютера
strRemoteHost = InputBox("Enter name or IP address of computer " 4 _
"to connect to.", vbOKCancel) •.._.
' выходим, если была отмена-.
If strRemoteHost = "" Then Exit Sub
' закрываем любое открытое соединение
sckTCP.Close
' устанавливаем имя вызываемого компьютера
sckTCP-RemoteRost = strRemoteHost
' задаем номер порта на удаленном хосте
sckTCP.RemotePort = 1002
" так вроде бы удается избежать некоторых ошибок TCP
DoEvents
' посылаем запрос на соединение
sckTCP. Connect
End Sub
Когда «слушающая» машина получает запрос на соединение от другой машины, возникает событие ConnectionRequest. Программа Chat принимает все запросы:.
Private Sub sckTCP_ConnectionRequest(ByVai requestID As Long)
sckTCP.Close
sckTCP.Accept requestID
ShowText "Accepting request from " & sckTCP.RemoteHostIP End Sub
Как только «слушающая» машина принимает запрос на соединение, любой из двух компьютеров может передавать данные методом SendData. Для ввода посылаемых и отображения получаемых сообщений программа Chat использует поле ixtCbo!. Обработчик события KeyPress, показанный ниже, отслеживает все, что вводит пользователь, и, обнаружив нажатие клавиши Enter, отправляет сообщение.
Private Sub txtChat_KeyPress(KeyAscii As Integer)
Static strSend As String
4 проверяем, установлено ли соединение•
If sckTCP.State <> sckConnected Then Exit Sub
' посылаем данные при нажатии клавиши Enter
If KeyAscii = Asc(vbCr) Then
1 посылаем строку
sckTCP.SendData strSend
' очищаем переменную
strSend = ""■-
Else
отслеживаем все, что вводит пользователь strSend = strSend & Chr(KeyAscii)..
End If
End Sub
Метод SendData генерирует событие DataArrival на принимающей стороне соединения. Программа Chat извлекает информацию из очереди сообщений методом GetData и отображает ее в поле txtCbak
Private Sub sckTCP_DataArrival(ByVal bytesTotal As Long)■
Dim strText As String■-
' получаем данные sckTCP.GetData strText ' отображаем полученные данные■
txtChat = txtChat i "»" 4 strText & vbCrLf ' перемещаем курсор в конец текста txtChat. SelStart = Len(txtChat) ShowText "Bytes received: " & bytesTotal End Sub
Для завершения сеанса связи вызовите метод Close. В моей программе отключение и переход в режим ожидания осуществляется через меню:
Private Sub mnuDlsconnect_Click()’
sckTCP.Close'
DoEvents'"
sckTCP.Listen■ '
ShowText "Listen"-
End Sub
Вызов метода Close генерирует событие Close на другой машине. В его обработчике программа Chat возвращается в режим ожидания запросов на новое соединение:■
Private Sub sckTCP_Close()
ShowText "Close"■
' когда удаленная машина закрывает соединение, ' возвращаемся в режим ожидания sckTCP.Close.
sckTCP.Listen*_
ShowText "Listen"■
End Sub
TCP-соединения предоставляют гораздо больше информации об ошибках, чем UDP-соединения, поэтому ее очень важно выводить на экран. Любая ошибка при установлении или использовании соединения вызывает событие Error. В программе Chat ошибки выводятся на экран так:..
1 отображаем информацию об ошибках■* ■ ■
Private Sub sckTCP„Error(6yVal Number As Integer, _, .. -
Description As String, RvVal Scode As Long, _-■
ByVai Source As String, ByVai HelpFile As String, _.
ByVai HelpContext As Long, CancelDisplay As Boolean _ )
ShowText "Error " & Number & " " & Description■ ■
End Sub
Подробнее о событиях, возникающих в процессе работы с ТСР- соединением, см. исходный текст программы-примера Chat (CHAT.VBP) на компакт-диске, прилагаемом к книге. Дополнитель-
ную информацию на этот счет можно найти и в разделе «Using the Winsock Control» справочной системы Visual Basic.
Дорогой Джон, как..."
Создать FTP-браузер?
В разделе справочной системы Visual Basic, посвященном элементууп- равления Internet Transfer, предлагаются фрагменты кода, подходящие для создания FTP-браузера, но, увы, они слишком разрозненные. Это серьезный недостаток, поскольку элемент Internet Transfer является асинхронным, а именно взаимодействие событий и кода обработки ошибок — самый сложный аспект работы с этим элементом.
На рис. 7-6 показан простой FTP-браузер, созданный мной из двух текстовых полей и элемента управления Internet Transfer. Вы вводите URL сервера FTP в первое поле (Address), после чего выбираете файл или каталог из второго поля. При выборе каталога программа выводит список его файлов и подкаталогов, а при выборе файла — сохраняет его в каталоге Windows\Temp.
Рис. 7-6 Простой FTP-браузер, созданный на основе элемента управления Internet Transfer
Текстовое поле Address выполняет запрос сразу после нажатия клавиши Enter. При этом устанавливается свойство URE элемента Internet Transfer и вызывается его метод Execute. При запросе конкретного файла метод OpenURL действует также, как и метод Execute, но запрос содержимого каталога приводит к тому, что он возвращает исходный HTML-код. Поэтому в данном примере я избегал метода OpenURL. А теперь рассмотрим обработчик события KeyPress для поля txtAddress.
Private Sub txtAddress_KeyPress(KeyAscii As Integer)
If KeyAscii = Asc(vbCr) Then
"глотаем" нажатие клавиши'
KeyAscii = О
выделяем текст,
txtAddress.SelStart = О
txtAddress.SelLength = Len(txtAddress)
■ On Error R,'Tn errOpenURL1'
"’ устанавливаем FTP-адрес.»
IneiBrowse.uML = txtAddress•
1 получаем каталог’
InetBrowse.Execute , "Dir "
txtAddress = inetBrowse.URL
End If'
Exit Sub■ 1;
errOpenURL:
Select Case Err,Number Case icBadUrl■
MsgBox "Bad address. Please reenter."
Case IcConnectFalled, LcConnectionAborted, IcCannotConnect
MsgBox "Unable to connect to network."
Case IcInetTimeout’
MsgBox "Connection timed out."' .
Case icExecuting1
■1 отменяем предыдущий запрос
inetBrowse-Cancel
' щзоверяем, сработала ли отмена.
If InetBrowse.StiliExecuting Then
Caption = "Couldn't cancel reguest."
1 вновь посылаем запрос
Else
Resume.
End If”‘
Case Else
Debug.Print Err.Number, Err.Description
End Select;
End Sub■
Перехват ошибок
Крайне важно перехватывать ошибки, которые могут произойти после выдачи запроса. Особого внимания требует ошибка icExecuting. Элемент Internet Transfer, обрабатывая все запросы асинхронно, не поддерживает одновременной обработки двух и более запросов. Если Вы отменяете незавершенный запрос, обязательно проверьте свойство StillExecuting перед выходом из обработчика ошибок, как показано выше. Некоторые запросы нельзя отменить, поэтому простое выполнение оператора Resume приведет к бесконечному циклу!
Следующий пример демонстрирует процедуру обработки события DblClick текстового поля txtContentsp котором отображаются списки каталогов и файлов. Процедура формирует строку URL и выполняет команду Dir. если выбран подкаталог, или Get, если выбран файл. ,
Private Sub txtContents_DblClick() '‘Т"-.- .■ ■
’ просматриваем выбранный каталог ;,; • If txtContents. SelLength Then ’ если выделен каталог...
If Right(txtContents. SelText, 1} = T Then 1 добавляем выделенный элемент к адресу txtAddress = txtAddress & "7" & _
Left(txtContents.SelText, _ txtContents.SelLength - 1) ’ перехват ошибок (важен!)■ ■■• ■
. On Error GoTo errBrowse ' показываем каталог mstrDir = RightftxtAddress, Len(txtAddress) _ - Len(inetBrowse.URL))
InetBrowse.Execute , "Dlr " & mstrDir & 7*" ' иначе - это файл; считываем его ■ Else
DimstrFilenarne
формируем полный путь к файлу
mstrDir = Right(txtAddress, Len(txtAddress) _
- Len(inetBrowse.URL)) & 7" &_ txtContents. SelText
mstrDir = Right(mstrDir, Len(mstrDir) - 1) strFilename = mstrDir■
Do
strFilename = Right(strFilename, -
Len(strFilename) - InStr(strFilename, 7"))
Loop Until InStr(strFilenAmA "/") - о 1 считываем файл InetBrowse.Execute , "Get & mstrDir &
" " & ntstrTempDir & strFilename
End If
End If
Exit Subr,
errBrowse:' "'
If Err = icExecuting Then"
отменяем предыдущий запрос InetBrowse.Cancel 1 проверяем, сработала ли отмена ' ■■'
If InetBrowse.StillExecuting Then
Caption = "Couldn't cancel request."
Else
Resume
End If
Else
выводим информацию об ошибке
Debug. Print Err & "& ■ rr. Description
End If"
End Sub
Методы Execute и OpenURL генерируют событие StateChanged — хотя OpenURL вроде бы синхронный метод, событие StateChanged все же возникает, а это порождает проблему реентерабельности.
Следующий код периодически обновляет заголовок формы, держа нас в курсе выполнения запроса, и считывает содержимое каталога методом GetChunk, если текущая команда — Dir:
Private Sub lnatBrowse_StateChanged(ByVal State As Integer)
Select Case State'
CaseicError■*'
Debug. Print inetBrowse. ResponseCode & " ” & _
’inetBrowse.Responseinfo
• ’ Case icResolvingHost, icRequesting, IcRequestSent'
■ ,;'Caption = "Searching...".
Case icHostResolved'.
.Caption = "Found."
Case icReceivingResponse, IcResponseReceived.
Caption = "Receiving data."
Case icResponseCompleted Dim strBuffer As String ■' получаем данные
strBuffer = inetBrowse. GetChunk(1024) ' если данные - каталог, выводим его If strBuffer О "" Then
Caption = "Completed." txtContents = strBuffer Else
"Caption = "File saved in 4 mstrTempDir & "."
End If
Case icConnecting, icConnected Caption = "Connecting.''
Case icDisconnecting
Case icDisconnected.
Case Else"
Debug.Print State1■
End Select'
End Sub
ПРИМ ЕЧ АН И E Вдокументацииутверждается,чтометодСе1- Chunk работает с командой Get метода Execute, но в команде Get определяется как файл-источник, так и файл-приемник, поэтому для копирования файла с сервера метод Get- Chunk просто не нужен.
Полный код FTP-браузера, включая объявление API-функции GetTempPath и процедуру Form_Load, см. на компакт-диске, прила
гаемом к книге.
ДпГ6Р%ГА^ Пжгьб-®. КЛК,,,
Управлять браузером Internet Explorer?
Интересная особенность элемента управления WebBrowser в том, что его исходный файл, SHDOCVW.DLL, является в то же время библиотекой типов для IE. Библиотека типов содержит всю информацию, нужную для создания и управления ActiveX-объектами посредством Автоматизации (ранее известной как OLE-автоматизация). Установив в своем проекте ссылку на SHDOCVW.DLL, Вы сможете запускать и контролировать экземпляры IE.
Этот прием весьма полезен при отладке интернет-приложения, написанного на Visual Basic. По умолчанию IE кэширует Web-страницы, но при отладке желательно получать свежие версии Web- страниц. Надстройка-пример WebTool (рис. 7-7) запускает экземпляр 1Е с отключенным кэшированием страниц и тем самым облегчает отладку интернет-приложений.
Рис. 7-7 Программа-надстройка WebTool всегда загружает свежие Web-страницы, не кэшируя их
Помимо стандартного класса подключения (CLS) и стартового модуля регистрации (BAS), которые есть у любой программы-надстройки, WebTool (WEBTOOL.VBP) включает форму с текстовым полем для ввода URL и кнопками Forward (вперед) и Back (назад).
Код, приведенный ниже, запускает экземпляр IE и помещает в поле txtAddre.ssnyTb к каталогу, где Visual Basic хранит временные VBD-файлы при отладке интернет-приложений (о VBD-файлах — см, главу 9 «Создание приложений для Интернета»),
Option Explicit'J
' удаляет кэшированную страницу и устанавливает для метода Navigate
1 флаг navNoRaadFromCache-
Private Declare Function DaleteUrlCacheEntry Lib __.
"WlnInet.DLL" (strURLAs String) As Boolean''
1 указываем путь к каталогу, где установлен VB.EXE,
■Const VBPath = "file://C‘.\ProgramFiles\DevStudio\V6\"•
1 создаем объектную переменную, которая ссылается на IE;
' заметьте: чтобы осуществить привязку к событию ieView_Quit,
1 нужно использовать объект "_У1"
Private WithEvents leView As SHDocVw.WebBrowser_V1
' для IE3 использовалось следующее объявление:
' Private WithEvents ieView As IntarnatExplarer■
Private Sub Form_Load()
' устанавливаем ссылку на объект приложения■'
Set ieView = GetObject("", "InternetExplorer.Application")
' Internet Explorer должен быть видим
IeView,Visible = True
' начинаем с каталога, где установлен VB.EXE, поскольку
' при отладке VBD-файлы хранятся именно там
txtAddress = VBPath
End Sub
К сожалению, нельзя вызвать функцию GetObject с пустым первым аргументом, чтобы получить ссылку на уже выполняемый экземпляр IE, — приходится запускать новый. Кроме того, этот экземпляр нужно сделать видимым, установив его свойство Visible как True.
Следующий код реализует навигацию. API-функция DeleteUrl- CacheEntrv гарантирует, что метод Navigate загрузит новую версию Web-страницы, не пытаясь использовать содержимое кэша браузера на Вашем компьютере.
Private Sub txtAddress_KeyPress(KeyAscii As Integer)
Dim blnResult As Boolean
If KeyAscii = Asc(vbCr) Then
' "глотаем" нажатие клавиши (чтобы не пропустить
' данный символ в элемент управления)
KeyAscii = О
' выделяем текст-.
txtAddress.SelLength = Len(txtAddress)
' удаляем URL, если он имеется в кэше
blnResult = DeletetlrlCacheEntry(ByVal txtAddress) 1 ' ieView. Navigate txtAddress' *
End If' 1
End SubJ ‘ 1 . ‘''
Объектная переменная ieView объявлена с ключевым словом WithEvents, поэтому наша форма может перехватывать события от IE. Так, событие CommandStateChange используется для включения или отключения кнопок Forward и Back:
Private Sub ieView_CommandStateChange ( _
ByVai Coimnand As lone, _
ByVai Enable As Boolean _
)-
' включаем или отключаем кнопки Back и Forward в зависимости
' от того, есть ли адрес для перехода в данном направлении
Select Case Command
Case CSC_NAVIGATEBACK
cmdBack. Enabled = Enable
Case CSC_NAVIGATEFORWARD
cmdForward.Enabled = Enable
Case CSC_UPDATECOMMANDS '‘
End Select
End Sub.^ .
Private Sub cmdBack„Click()
leView.GoBack
End Sub
Private Sub cmdForward_Click()
ieView,GoForward
End Sub
Когда Web-страница полностью выведена на экран, IE генерирует событие NavigateComplete, а когда пользователь закрывает приложение, — событие Quit. Код программы-надстройки WebTool обрабатывает оба эти события:
Private Sub ieView_NavigatBComplete(ByVal URL As String)
' записываем в поле Address конечный адрес
txt Address. Text = URL
txtAddress.Sellength = Len(txtAddress)
' выводим название Web-страницы в заголовке формы
Caption = ieView, LocationName
End Sub
Private Sub ieView_Guit(Cancel As Boolean)
' завершаем это приложение, если пользователь
' закрывает Internet Explorer.
End
End Subтс- -
Полный код программы-надстройки WebTool см. на компактдиске, прилагаемом к книге. '
к См. также...
• Главу Т1 «Изощренные приемьтрограммирования» — подробнее о создании программ-надстроек,
ГЛАВА 8
Создание компонентов для Интернета
В предыдущей главе мы рассмотрели, как использовать интернет- компоненты, поставляемые с Visual Basic. Они предоставляют клиентские и одноранговые сервисы для написания файловых серверов, браузеров и приложений, позволяющих вести «беседы» по Интернету. В этой главе я расскажу о том, как создавать собственные компоненты, способные работать в Интернете. Эти компоненты могут быть нескольких видов.
-, ActiveX-элементы. Расширяют возможности любого интернет- приложения независимо от того, что именно они используют: HTML (HyperText Markup Language), DHTML (Dynamic HTML) или ActiveX-документы. Основные сведения о создании этих компонентов изложены в главе б «ActiveX-элементы», а здесь обсуждается специфика их использования в Интернете.
HTML-страницы. Формат большей части «содержания» Интернета. В Visual Basic нет средств, напрямую поддерживающих написание HTML-страниц, зато есть компоненты, которые можно использовать на таких страницах (например, ActiveX-элементы и сценарии на VBScript). Я поясню, как задействовать эти средства в HTML.
DHTML-страницы. Новый тип компонентов, которые позволяет создавать Visual Basic. Я расскажу, как работать с DHTML Page Designer и о чем следует помнить, используя DHTML-страницы в своем приложении.
i<6 ActiveX-документы. Появились в результате первой попытки — еще до того, как был предложен стандарт DHTML, — ввести в Visual Basic поддержку динамических Web-страниц. Эти компоненты отличаются как от форм Visual Basic, так и от DHTML, а значит, и у них есть своя специфика.
S Web-классы. Управляют серверными объектами в приложениях Internet Information Server (IIS). Эти классы работают под управлением IIS и требуют использования его модели объектов.
В этой главе поясняется, как создать или улучшить перечисленные компоненты в Visual Basic. Ес тематика тесно связана с тем, что рассматривается в главе 9 «Создание приложений для Интернета», и, по-видимому, стоит прочесть обе главы, если Вы хотите получить представление о всем спектре задач программирования. связанных с Интернетом.
Дорогой Джон, как...
Создавать ActiveX-элементы, рассчитанные на применение в Интернете?
ActiveX-элементы можно использовать в любом приложении Visual Basic, в том числе ориентированном на Интернет. Однако в создании элементов управления, рассчитанных на применение в Интернете, есть своя специфика.
Цифровая подпись. В любом исполняемом компоненте должна быть цифровая подпись, которая идентифицирует автора и подтверждает безопасность запуска этого компонента и его использования в сценариях. Цифровую подпись можно приобрести, например, у VeriSign <,bitp://ivww.i'erisign.comy Модель потоков. ActiveX-элемент должен базироваться на модели потоков (threading model), совместимой со структурой хост-приложения. Однопоточные приложения могут использовать либо однопоточные элементы (single threading controls), либо элементы с разделенными потоками (apartment threading controls), но приложения, построенные на модели разделенных потоков, — только элементы с разделенными потоками. DHTML-приложения относятся ко вторым. Асинхронная загрузка. По умолчанию все объекты загружаются по Интернету асинхронно. Прежде чем ссылаться на объекты в своем коде, убедитесь, что они инициализированы. Проверить, существует ли нужный объект, позволяет функция IsNull.
Упаковка перед распространением. У ActiveX-элементов, устанавливаемых через Интернет, должна быть соответствующая программа установки. Для создания нужных САВ- и НТМ- файлов проще всего воспользоваться мастером Package and Deployment Wizard (см. главу 9 «Создание приложений для Интернета»),
Элемент управления Asynchronous Animation, созданный нами в главе 6, рассчитан на применение в Интернете. Вы не раз встретите его в следующих разделах — в примерах компонентов, построенных на HTML, DHTML и ActiveX-документах.
Дорогой Джон. как...
Использовать ActiveX-элементы с VBScript?
HTML-документы — самый распространенный вид данных в Интернете. Они могут включать ActiveX-элементы, а также апплеты, написанные на VBScript, JScript или Java и реагирующие на определенные события. Пример HTML-документа (htAni.HTM), содержащего элемент управления Asynchronous Animation, показан на рис. 8-1.
HTML-коддля этой Web-страницы приведен ниже, Заметьте, что Asynchronous Animation и стандартные элементы включаются в HTML-код тэгом <OBJECT> и программируются на VBScript.
<HTML>-
<р>
<OBJECT ID="asyncAnimatioп■, WIDTH=200 HEIGHT=80
CLASSIO="CLSID:1B9C2FBC-0156-1102-AO61-0OAA0O5754FD"
</object>■:
OBJECT ID="dlgFile" HEIGHT=50 WIDTH=5O■
0LASSID="CLSID:F9043C85-F6F2-101A-A3C9-08OO2B2F49FB"
</OBJECT>' '
<P>
<INPUT TYPE="BUTTON" VALUE="Vlew Animation" NAME="butVlew"> -
<SCRIPT LANGUAGE="VBScrlpt">■
' обработчик событий onClick для встроенной кнопки
Sub butView_onCllck
1 проверяем, существуют ли объекты■ -
If IsNull(dlgFlle) Or IsNull(asyncAnimation) Than Exit Sub -
1 получаем файл.
dlgFile.ShowOpen
strFile = dlgFile.FileName
если было введено имя файла... 1■■
If strFile о "" Then
... отображаем его в элементе asyncAnimation asyncAnimation.AVIFile = strFile
‘ сбрасываем имя файла.
dlgFile.FileName = ""
End If''
End Sub-.
</SCRIPT></HTML></SCRIPT></HTML>Рис. 8-1 Элемент управления Asynchronous Animation, встроенный в Web-страницуРис. 8-1 Элемент управления Asynchronous Animation, встроенный в Web-страницу
</SCRIPT>
</HTML>
</SCRIPT>
</HTML>
Рис. 8-1 Элемент управления Asynchronous Animation, встроенный в Web-страницу
Рис. 8-1 Элемент управления Asynchronous Animation, встроенный в Web-страницу
Дорогой Джон, как...
Создавать DHTML-документы?
DHTML включен в новую версию HTML — 4.0. Предоставляя программируемую модель объектов документов (Document Object Model, DOM) и каскадные таблицы стилей (Cascading Style Sheets, CSS), DHTML позволяет добиться большей интерактивности Web- страниц.
ПРИМЕЧАНИЕ СейчассуществуютдвереализацииОНТМЕ— от Microsoft и Netscape. World Wide Web Consortium (W3C) изучает эти стандарты и публикует рекомендации на своем Web-узле: http://www.w3c.org. Новые версии браузеров Microsoft и Netscape, возможно, будут поддерживать единый стандарт DHTML.
В Visual Basic появился новый конструктор для разработки DHTML- страниц — DHTML Page Designer (рис. 8-2). Он отображает иерархическую схему страницы и предоставляет область редактирования, где можно вводить текст и добавлять объекты.
ЛИВИЙ!ЛИВИЙ!AntVia**»! - MiG«!».ult УЙЦмй &»&)й" W.AntVia**»! - MiG«!».ult УЙЦмй &»&)й" W.£S <tft $«w &rtect ^m« yH4 Bun ^ц«т c^ywi £&6Ь fifths ML*d^ tii^-k • ?'£S <tft $«w &rtect ^m« yH4 Bun ^ц«т c^ywi £&6Ь fifths ML*d^ tii^-k • ?'Рис. 8-2 Создание новой Web-страницы в DHTML Page DesignerРис. 8-2 Создание новой Web-страницы в DHTML Page Designer'<СгЙУ<И>'<СгЙУ<И>
ЛИВИЙ!
ЛИВИЙ!
AntVia**»! - MiG«!».ult УЙЦмй &»&)й" W.
AntVia**»! - MiG«!».ult УЙЦмй &»&)й" W.
£S <tft $«w &rtect ^m« yH4 Bun ^ц«т c^ywi £&6Ь fifths ML*d^ tii^-k • ?'
£S <tft $«w &rtect ^m« yH4 Bun ^ц«т c^ywi £&6Ь fifths ML*d^ tii^-k • ?'
Рис. 8-2 Создание новой Web-страницы в DHTML Page Designer
Рис. 8-2 Создание новой Web-страницы в DHTML Page Designer
'<СгЙУ<И>
'<СгЙУ<И>
В работе с DHTML Page Designer тоже есть своя специфика. Здесь многое делается не так, как Вы привыкли, создавая в среде Visual Basic формы, нестандартные элементы управления, ActiveX- документы или страницы свойств. Область редактирования больше ■напоминает клиентскую среду, связанную с браузером. Вот основные отличия, которые Вы заметите, работая с DHTML Page Designer.
в Любой элемент управления на DHTML-странице идентифицируется свойством ID, а не Name. Конструктор DHTML автоматически назначает идентификатор каждому элементу, добавляемому на страницу. Если Вы вдруг измените идентификатор элемента, Вам придется использовать некое уникальное имя и заботиться об этом самостоятельно, так как конструктор не проверяет уникальность имен. И учтите, что Visual Basic не выдаст сообщения об ошибке, если Вы случайно присвоите одинаковый идентификатор двум разным элементам.
и Встроенные элементы управления Visual Basic на DHTML-странице не доступны. Используйте вместо них HTML- или ActiveX- элементы.
в При отладке, когда элемент Visual Basic открывает какое-нибудь окно, Internet Explorer иногда перестает отвечать. Тогда, чтобы увидеть это окно, Вы должны переключиться в Visual Basic, и пока Вы его не закроете, Internet Explorer отвечать не будет. В скомпилированном приложении такой проблемы не возникает. На рис. 8-3 показана DHTML-страница, созданная в Visual Basic
с применением элемента Asynchronous Animation, Common Dialog и HTML bui ню Исходный коддля этого примера Вы найдете в проекте dhAniVBPHa компакт-диске, прилагаемом к книге.
.О 2Оа Q3 §iv»f*i*s.И»т*Ue№ Fwyk*^В^уChwih.О 2Оа Q3 §iv»f*i*s.И»т*Ue№ Fwyk*^В^уChwihРис. 8-3 Animation Viewer воспроизводит №1и^о№-ани,мацию на ^'ёЬ-стратще:исполъзую1цеи DHTMLРис. 8-3 Animation Viewer воспроизводит №1и^о№-ани,мацию на ^'ёЬ-стратще:исполъзую1цеи DHTML
.О 2Оа Q3 §
iv»f*i*s.И»т*Ue№ Fwyk*^В^уChwih
.О 2Оа Q3 §
iv»f*i*s.И»т*Ue№ Fwyk*^В^уChwih
Рис. 8-3 Animation Viewer воспроизводит №1и^о№-ани,мацию на ^'ёЬ-стратще:исполъзую1цеи DHTML
Рис. 8-3 Animation Viewer воспроизводит №1и^о№-ани,мацию на ^'ёЬ-стратще:исполъзую1цеи DHTML
Я специально взял пример, аналогичный предыдущему, чтобы продемонстрировать разницу в использовании элементов управления и написании HTML- и DHTML-кода. Хотя готовые страницы выглядят в браузере совершенно одинаково, код Visual Basic в dhAni.VBP несколько отличается от кода VBScript в htAni.VBP. DHTML-страница до пускает объявления типов переменных и пара-
метров; кроме того, ссылки на элементы надо уточнять ссылкой на. объект Document:■ ■
воспроизводит Windows-анимацию на Web-странице•
Private Function butView_onClick() As Boolean
1 проверяем, существуют ли объекты
If IsNull(dlgFile) Or IsNull(asyncAnimation) Then Exit Function .
Dim strFllename As String.
' открываем диалоговое окно Open File■ '.
Document. dlgFile. ShowOpen"
' получаем имя файла'
strFllename = Document.dlgFile.FileName•'
если выбран файл...■ v
If strFllename <> "" Then..
.1 ...отображаем его в элементе asyncAnimation
Document. asyncAnlmatlon.AVIFile = strFllename.
End If'.'■
End Function■
Private Sub asyncAnimation_FileError(Number As Long, _
Description As String).
1 выводим сообщение об ошибке
MsgBox "File is not a valid animation.", _ vbOKOnly And vbCritical, "Error " & Number End Sub
Наверное, Вы заметили, что обработчик события onCIick для кнопки butViewявляется процедурой типа Function, а не Sub. В DHTML принято, что обработчики событий возвращают значения, активизирующие операции по умолчанию. Например, если Вы пишете обработчик onCIick для гиперссылки, сделайте так, чтобы он возвращал True. — тогда браузер, обработав Ваш код, перейдет по этой гиперссылке. А иначе она будет проигнорирована. Сказанное поясняет следующий код:
Private Function lnkHome_onClick() As Boolean' ■
Dim intGo As Integer
' предлагаем выбор
intGo = NsgBoxC'Do you want to go home?", vbYesNo)
1 если пользователь выбирает "да"...
If intGo = vbYes Then
r 1 ... разрешаем операцию по умолчанию
' (переходим по гиперссылке)
lnkHome_OnClick = Тг--
Else"
...отменяем операцию по умолчанию■
■;' -1 (не переходим по гиперссылке)
1пкноте_ипипск = raise,. .
End If
End Function•" " '.
Подробнее о DHTML, включая и то, как добавить в DHTML-приложение несколько страниц, см. главу 9 «Создание приложений для Интернета».
ДЖОНу КЙК,^'
Создавать ActiveX-документы?
ActiveX-документ — это комбинация пользовательских документов, форм, модулей кода и любых других компонентов, которые Вы решите добавить. П&гьзоватеяъск^мдоюумент (user document) —уникальный контейнер Visual Basic, рассчитанный на отображение в браузере.
ПРИМЕЧАНИЕ ActiveX-документы не подпадают под действие стандартов World Wide Web Consortium (W3C), но являются частью стратегии Microsoft в области ActiveX-компонентов, каковая уже стала стандартом «де факто» в индустрии программного обеспечения. (Толчком к развитию этой стратегии послужила концепция OLE.) ActiveX-документы поддерживаются браузером Internet Explorer, но не Netscape Navigator.
Пользовательские документы очень похожи на формы. В них можно помещать любые встроенные в Visual Basic элементы управления — причем точно так же, как и на формы. Отличия проявляются, когда дело доходит до написания кода. Поскольку эти документы загружаются и выгружаются по велению браузера, учитывайте, ЧТО:
я прежде чем ссылаться из одного объекта на другой, следует дождаться загрузки всех объектов. Например, пользователь может щелкнуть кнопку, которая ссылается на некий элемент управления, а он еще только загружается. Поэтому перед ссылкой на. любой объект вызывайте функцию IsNull и проверяйте, существует ЛИ ОН;
и данные в других пользовательских документах не становятся доступными сами по себе. Если какие-то элементы данных совместно используются несколькими такими документами, храните их в глобальных переменных или в разделяемом объекте.
На рис. 8-4 показан давно знакомый нам элемент управления Asynchronous Animation, выполняемый браузером в ActiveX-документе. Последний выглядит так же, как и ранее созданные HTML- и DHTML-страницы. Исходный код для этого примера Вы найдете в проекте axAni.VBP на компакт-диске, прилагаемом к книге.
Код для пользовательского документа, показанный ниже, был бы идентичен примеру с формой.
- воспроизводит Windows-анимацию на Web-странице
Sub cmdView_Click()
Dim strFilename As String
проверяем, существуют ли объекты -
If IsNuLL(dlgFile) Or IsNulHasvnr,Animation) Then Exit Sub ' открываем диалоговое окно Open File
dlgFile.ShowOpen
получаем имя файла
strFilename = dlgFile.FileName
' если выбранфайп...
If strFilename <> "" Then
...отображаем его в элементе asyncAnimation asyncAnimation.AVIFile = strFilename
End If
End Sub
iPrivate Sub asyncAnimation_FileError(Number As Long, _,.
Description As String)
1 выводим сообщение об ошибке
HsgBox "File is not a valid animation,", vbOKOnly And vbCritical, "Error & Number
End Sub
Рис, 8-4 Animation Viewer воспроизводит ^тйотз-анимацию на Web-странице, исполъзукяцейАсРиеХ-документ (где-томы уже видели это!)
Программистам на Visual Basic создавать ActiveX-документы определенно легче, чем приложения, использующие HTML или DHTML, но поддерживать актуальность информации в ActiveX-документах труднее. В публикации материалов HTML и DHTML эффективнее, чем ActiveX-документы, так как эти форматы поддерживаются целым рядом текстовых редакторов и не требуют перекомпиляции страниц после изменения их содержимого. Сравнительные преимущества приложений каждого типа обсуждаются в главе 9 «Создание приложений для Интернета». г
Л^и^ер^гк» П ъишл ^ГЭКе»«'
СоздаватьУУеЬ-классы?
Web-класс — просто класс, который управляет созданием и разрушением объектов, используемых IIS-приложением. Visual Basic автоматически создает такой класс, когда Вы запускаете новое IIS- приложение.
ПРИМЕЧАНИЕ Дм поддержки Web-классов установите Internet Information Server (IIS) или PersonaiWeb Server (PWS). Все, что в этом разделе говорится об IIS, полностью относится и к PWS.
Вы можете легко модифицировать исходный Web-класс и выводить HTML-строки через браузер. Например, эта процедура обработки события WebClassStart создает объект Dice и отображает результаты двух «бросков» игральных костей:
Private Sub WebClass_Start()
Dim sQuote As String
’ создаем объект Dice
Dim diceObject As New Dice
определяем символ кавычек для формирования HTML-строк
sQuote = Chr$(34)
устанавливаем счетчик для этой страницы
Sassion("Tries") = SessionC’Tries") + 1
' пишем ответ пользователю
With Response
.Write "<HTML>"
.Write "<B0DY>"
■Write "<H1><F0NT FACE=" & sQuote A "Arial" & sQuote & _
">Chance Try 11 & Session("TriBs") & "</ЮМГ></Н1>” .Write "<P>You rolled a " & diceObject.Roll & _
" and a" &diceObject.Roll &"."
.Write "</P>"
.Write "<P>Refresh or reload to roll again. </P>"
.Write "</BODY>"
.Write •,</HTML>".. -
End With.
End Sub,.'• •
Когда Вы запустите программу Chance (CHANCE.VBP), Visual Basic сформирует виртуальный каталог для Вашего проекта в IIS, создаст активную серверную страницу (ASP), которая ссылается на Web-класс, и выполнит эту ASP Потом будет запущен Internet Explorer, где Вы и увидите результаты (рис. 8-5).
Кроме того, Chance иллюстрирует, как с помощью объекта Session сохраняются значения свойств. Следующая строка создает в объекте Session свойство Tries и увеличивает его значение на 1: SessionC'Tries") = SessionC'Tries") + 1
"~ - О □ й. ® ®. <3 " ^
Chance — Try 1
You rolled a 5 and a 4
Refresh or reload to cell again
Рис. 8-5 Internet Explorer -брошен;» игральные кости при каждом щелчке кнопки Refresh
Чтобы сохранить это значение между обновлениями, свойству StateManagementWeb-класса присваивается значение wcRetainlns- tance. Если поменять это значение на wcNoState. Web-класс перестанет сохранять информацию об объекте Session, и число бросков игральных костей, отображаемое на странице, будет всегда равно 1.
Чтобы передать браузеру HTML-строку, программа Chance вызывает метод Write объекта Response. В данном примере, где я хотел лишь продемонстрировать понятие Web-класса, этот способ удобен, но для отображения больших объемов информации или для обновления «содержания» не годится. Последнюю проблему можно решить за счет хранения информации в отдельных файлах HTML- шаблонов. Об этом приеме я расскажу в следующей главе.
ГЛАВА 9
Создание приложений для Интернета
V isual Basic позволяет создавать три типа приложений, выполняемых браузером.
• DHTML-приложения. Загружаются по Интернету и выполняются на компьютере пользователя. В таких приложениях применяются элементы управления HTML 4,0, гиперссылки и средства форматирования отображаемой информации.
■ IIS-приложения. Выполняются на интернет-сервере и обмениваются данными с компьютером пользователя. Работают с моделью объектов IIS (Internet Information Server) и ActiveX-объектами, установленными на интернет-сервере.
в Приложения на основе ActiveX-документов. Загружаются по Интернету и выполняются на компьютере пользователя. Б ActiveX-документах применяются стандартные элементы управления Visual Basic и другие ActiveX-объекты. Приложения этого типа обеспечивают полное управление браузером, в том числе контроль внешнего вида его окна и подстановку своих меню.
Я расскажу о создании многостраничных приложений (multipage apphcationsj с применением каждой из перечисленных технологий. При этом я использую один и тот же базовый пример - College Cost Calculator (калькулятор для расчета стоимости обучения в колледже). Все версии этого калькулятора находятся на компакт-диске, прилагаемом к книге.
Дорогой Джон, как...
Выбрать тип приложения?
Такое разнообразие способов создания интернет-приложений обусловлено самим характером развития Интернета. Microsoft, Netscape, Sun Microsystems и другие компании выпускали множество конкурирующих инструментальных средств и все претендовали на то, чтобы именно их продукт стал стандартом в области разработки для Интернета.
Но единого четкого стандарта нет до сих пор. Вместо этого Вы должны исходить из конкретной задачи. Visual Basic предлагает 4
подхода к созданию интернет-приложений: HTML с применением ActiveX-элементов и VBScript, DHTML, ЛспгеХ-документы и IIS-приложения. Выбирайте оптимальный подход, учитывая следующие соображения.
Тип браузера. Если Ваше приложение должно работать и с Netscape Navigator, и с Internet Explorer (IE), создавайте его как I1S- приложение. Netscape не поддерживает ActiveX-объекты в HTML, VBScript, реализацию Microsoft DHTML и ActiveX-документы.
Место обработки. IIS-приложения рассчитаны на обработку сервером. Идеальны для доступа к данным, хранящимся на сервере, Но возможны проблемы с быстродействием на очень занятых узлах. ActiveX-документы. DHTML и HTML с ActiveX-объектами выполняются на клиентской машине (предварительно их надо загрузить).
Простота в сопровождении. Приложения, которые выводят многотекста, должны использовать i г. DHTML или HTML. Приложения этих типов — в отличие от ActiveX-документов — позволяют помещать информацию в отдельные файлы без перекомпиляции и повторного распространения исполняемых компонентов.
Контроль со стороны клиента. ActiveX-документы дают возможность управлять браузером и открывают неограниченный доступ к файловой системе на компьютере пользователя. IIS-, DHTML- и HTML-приложения не могут модифицировать браузер и имеют весьма ограниченный доступ к файловой системе.
Безопасность. В любой исполняемый компонент вроде ActiveX-документа или элемента можно включить цифровую подпись, удостоверяющую, что данный компонент не устроит хаоса на клиентской машине. Но пользователи крайне неохотно принимают компоненты от незнакомых им авторов. IlS-црило- жения такой проблемы не создают, поскольку их исполняемые части работают на сервере, а не на клиентской машине.
Будущие стандарты. Microsoft и Netscape сейчас поддерживают несовместимые реализации DHTML. Проблема, вероятно, будет снята, когда Netscape выпустит Navigator '■11 Как обещает Netscape, Navigator 5.0 будет поддерживать и ActiveX-компоненты.
Если Вы создаете интернет-приложение, предназначенное для World Wide Web, то реального выбора у Вас по сути нет и Вы должны использовать IIS, потому что Netscape Navigator ничего другого не поддерживает. А если Вы создаете приложение, выполняемое в интрасети, можете выбирать любую технологию, так как в этом случае намного проще диктовать пользователям, с каким браузером они должны работать.
Будущие версии IE и Netscape Navigator должны стать более совместимыми друг с другом, и поэтому какая-то часть изложенных мной соображений, вероятно, утратит свою значимость. Впрочем, рассуждать на эту тему пока преждевременно.Будущие версии IE и Netscape Navigator должны стать более совместимыми друг с другом, и поэтому какая-то часть изложенных мной соображений, вероятно, утратит свою значимость. Впрочем, рассуждать на эту тему пока преждевременно.Дорогой Джон, как...Создать DHTML-приложение?Дорогой Джон, как...Создать DHTML-приложение?Проект DHTML-приложения содержит по одному DHTML-конструктору на каждую HTML-страницу и может включать модули классов, кода, форм и других исполняемых компонентов. После компиляции исполняемые компоненты помещаются в отдельный DLL-файл, на который ссылаются все HTML-файлы приложения. Это позволяет отделить информацию от исполняемых компонентов. Рис. 9-1 иллюстрирует приложение Future Shock (DHFSHOCK.VBP) на этапе разработки.Возможности DHTML-конструктора в редактировании крайне ограниченны. Чтобы задействовать другие средства редактирования, Вам придется связать конструктор с внешним HTML-файлом. Для этого выберите IDHTMLPageDesigner в окне Properticsnукажите в свойстве SourceFile существующий 1 iTML-файл. После того как Visual Basic загрузит HTML-файл, Вы сможете отредактировать его, щелкнув на панели инструментов кнопку Launch Editor (запустить редактор).Проект DHTML-приложения содержит по одному DHTML-конструктору на каждую HTML-страницу и может включать модули классов, кода, форм и других исполняемых компонентов. После компиляции исполняемые компоненты помещаются в отдельный DLL-файл, на который ссылаются все HTML-файлы приложения. Это позволяет отделить информацию от исполняемых компонентов. Рис. 9-1 иллюстрирует приложение Future Shock (DHFSHOCK.VBP) на этапе разработки.Возможности DHTML-конструктора в редактировании крайне ограниченны. Чтобы задействовать другие средства редактирования, Вам придется связать конструктор с внешним HTML-файлом. Для этого выберите IDHTMLPageDesigner в окне Properticsnукажите в свойстве SourceFile существующий 1 iTML-файл. После того как Visual Basic загрузит HTML-файл, Вы сможете отредактировать его, щелкнув на панели инструментов кнопку Launch Editor (запустить редактор).№ I* йен f»^ *дгче Xmq *3* Swrt Dt№^.. &»k 4dd-b*s. Иф* 4*№ I* йен f»^ *дгче Xmq *3* Swrt Dt№^.. &»k 4dd-b*s. Иф* 4*at-.-i- - ^Hat-.-i- - ^HF3!ЗГ” ^ ^- в ■F3!ЗГ” ^ ^- в ■1-^1 iJtureSW't <DK1-^1 iJtureSW't <DK•V. ■ : .t'-'•V. ■ : .t'-'r_ ^y Dvcumenf.Ц BDOYtCo^eCcurtCAntoocI , -MTHI iCoflegeCdstC^cJaioiJ•&1HR II1' rl УТP |C^d'T name 1I''Si MUMmINH^W:F if R [Child's age )I5KL biAge INPUT ()Ы |«f F (Typeof college; In tla I '..^ lelCoUnpeSELECT [In»m ST P^^soCTantpaiairr ।1j^ jelFiehspon SELECT [1-[ If P U^^e of housing HoffteDt ■ •^ xetHoyting SELECT Hb,r_ ^y Dvcumenf.Ц BDOYtCo^eCcurtCAntoocI , -MTHI iCoflegeCdstC^cJaioiJ•&1HR II1' rl УТP |C^d'T name 1I''Si MUMmINH^W:F if R [Child's age )I5KL biAge INPUT ()Ы |«f F (Typeof college; In tla I '..^ lelCoUnpeSELECT [In»m ST P^^soCTantpaiairr ।1j^ jelFiehspon SELECT [1-[ If P U^^e of housing HoffteDt ■ •^ xetHoyting SELECT Hb,я**8ййая**8ййаCh№ age CjCh№ age CjД2?_й.!^^9еI ifi-^^jste'puti'liE :оНевэ*1Д2?_й.!^^9еI ifi-^^jste'puti'liE :оНевэ*1.1 bull eVc INPUT |).1 bull eVc INPUT |)Др» of trensportgtrofh |aik= ДДр» of trensportgtrofh |aik= ДiLiLbllbll4дамд4дамдETitfhieinTvfn^^AlArtl** l-Zut^rii^^ETitfhieinTvfn^^AlArtl** l-Zut^rii^^:еЫпс*ч:еЫпс*чJJ
Будущие версии IE и Netscape Navigator должны стать более совместимыми друг с другом, и поэтому какая-то часть изложенных мной соображений, вероятно, утратит свою значимость. Впрочем, рассуждать на эту тему пока преждевременно.
Будущие версии IE и Netscape Navigator должны стать более совместимыми друг с другом, и поэтому какая-то часть изложенных мной соображений, вероятно, утратит свою значимость. Впрочем, рассуждать на эту тему пока преждевременно.
Дорогой Джон, как...
Создать DHTML-приложение?
Дорогой Джон, как...
Создать DHTML-приложение?
Проект DHTML-приложения содержит по одному DHTML-конструктору на каждую HTML-страницу и может включать модули классов, кода, форм и других исполняемых компонентов. После компиляции исполняемые компоненты помещаются в отдельный DLL-файл, на который ссылаются все HTML-файлы приложения. Это позволяет отделить информацию от исполняемых компонентов. Рис. 9-1 иллюстрирует приложение Future Shock (DHFSHOCK.VBP) на этапе разработки.
Возможности DHTML-конструктора в редактировании крайне ограниченны. Чтобы задействовать другие средства редактирования, Вам придется связать конструктор с внешним HTML-файлом. Для этого выберите IDHTMLPageDesigner в окне Properticsnукажите в свойстве SourceFile существующий 1 iTML-файл. После того как Visual Basic загрузит HTML-файл, Вы сможете отредактировать его, щелкнув на панели инструментов кнопку Launch Editor (запустить редактор).
Проект DHTML-приложения содержит по одному DHTML-конструктору на каждую HTML-страницу и может включать модули классов, кода, форм и других исполняемых компонентов. После компиляции исполняемые компоненты помещаются в отдельный DLL-файл, на который ссылаются все HTML-файлы приложения. Это позволяет отделить информацию от исполняемых компонентов. Рис. 9-1 иллюстрирует приложение Future Shock (DHFSHOCK.VBP) на этапе разработки.
Возможности DHTML-конструктора в редактировании крайне ограниченны. Чтобы задействовать другие средства редактирования, Вам придется связать конструктор с внешним HTML-файлом. Для этого выберите IDHTMLPageDesigner в окне Properticsnукажите в свойстве SourceFile существующий 1 iTML-файл. После того как Visual Basic загрузит HTML-файл, Вы сможете отредактировать его, щелкнув на панели инструментов кнопку Launch Editor (запустить редактор).
№ I* йен f»^ *дгче Xmq *3* Swrt Dt№^.. &»k 4dd-b*s. Иф* 4*
№ I* йен f»^ *дгче Xmq *3* Swrt Dt№^.. &»k 4dd-b*s. Иф* 4*
at-.-i- - ^H
at-.-i- - ^H
F3!ЗГ” ^ ^- в ■
F3!ЗГ” ^ ^- в ■
1-^1 iJtureSW't <DK
1-^1 iJtureSW't <DK
•V. ■ : .t'-'
•V. ■ : .t'-'
r_ ^y Dvcumenf
.Ц BDOYtCo^eCcurtCAntoocI , -MTHI iCoflegeCdstC^cJaioiJ•
&1HR II1
' rl УТP |C^d'T name 1I
''Si MUMmINH^W:
F if R [Child's age )I
5KL biAge INPUT ()
Ы |«f F (Typeof college; In tla I '.
.^ lelCoUnpeSELECT [In
»m ST P^^soCTantpaiairr ।1
j^ jelFiehspon SELECT [1
-[ If P U^^e of housing HoffteDt ■ •^ xetHoyting SELECT Hb,
r_ ^y Dvcumenf
.Ц BDOYtCo^eCcurtCAntoocI , -MTHI iCoflegeCdstC^cJaioiJ•
&1HR II1
' rl УТP |C^d'T name 1I
''Si MUMmINH^W:
F if R [Child's age )I
5KL biAge INPUT ()
Ы |«f F (Typeof college; In tla I '.
.^ lelCoUnpeSELECT [In
»m ST P^^soCTantpaiairr ।1
j^ jelFiehspon SELECT [1
-[ If P U^^e of housing HoffteDt ■ •^ xetHoyting SELECT Hb,
я**8ййа
я**8ййа
Ch№ age Cj
Ch№ age Cj
Д2?_й.!^^9е
I ifi-^^jste'puti'liE :оНевэ*1
Д2?_й.!^^9е
I ifi-^^jste'puti'liE :оНевэ*1
.1 bull eVc INPUT |)
.1 bull eVc INPUT |)
Др» of trensportgtrofh |aik= Д
Др» of trensportgtrofh |aik= Д
iL
iL
bll
bll
4дамд
4дамд
ETitfhieinTvfn^^
AlArtl** l-Zut^rii^^
ETitfhieinTvfn^^
AlArtl** l-Zut^rii^^
:еЫпс*ч
:еЫпс*ч
J
J
Рис. 9-1 Каждый DHTML-конструктор обрабатывает одну HTML-страницу-
Получение ввода в DHTML-приложениях
Чтобы сделать какой-то элемент DHTML-страницы программируемым, запишите идентификатор в его свойство ID. Элементы, уко- торых есть события, требуют установки этого свойства — только тогда их события доступны в Visual Basic. У каждого элемента управления на форме конструктора dbc«k (рис. 9-1) имеется свой идентификатор, как показано в этом HTML-коде:
<1 DOCTYPE HTML PUBLIC "-//W3C//DTD ИЗ HTML//EN"?
<HTML>
<HEAD>:t
<META content="text/html; " http-equiv=Content-TypexTITLE>Future Shock! </TITLE>
<META content3'"MSHTML 4.72.2106.6"' name=GENERATOfl>’ '
</HEAD>;"
<80DY bgColOr-#ffffff>
<H1 id=’"'>College Cost Calculator</H1>
<HR>
<P?Child's name:
<INPUT id=txtName maxLength=20 name=sName>'
<P>Child's age:
CINPUT id=txtAge maxlength=2 name=sAge size=2>
<P?Type of college: <SELECT id=selCollege name=1 Co Hege?
<OPTIOr selected value=O?In-state public college. .
. <0PH0N value=1>0ut-of-state public college гпрттпы t/ab.usosprivate college</SELECT^
<P>Type of transportation: <SELECT id=selTransport naBe=iTransport> rOPTION selected value=0>Bike
<OPTIOM value=1>Bus
<OPTION value=2>Used car
<OPTION value=3>Naw car</SELECT>
<P>Type of housing: <SELECT Ld=selHousing name=iHousing>
COPTION selected value=0>Homa
<OPTION value31>Dorm. •■.
<0PTI0N value=2?Apartment</SELECT>’
<PXINPUT id=butCalc type=button value=Calculate> </PX/BODY></HTML>
Определение идентификаторов позволяет кнопке Calculate реагировать на щелчки, а Вам — и .■ и.чс я. значения, вводимые в текстовые поля или выбираемые в списках. Обработчик события onClick извлекает данные со страницы dhCalc и вычисляет расходы нс обучение в колледже, используя объект Child:
Private Function butCalc„onClick() As Boolean■
Dim vntPayment, vntLumpSum, vntTotalCost
Dim chdScfnolar As FutureShock.Child ' проверяем данные
If Len(txtName.Value) = 0 Or Len(txtAge.Value) = 0 Then BaseWindow.alert "You must enter name and age information," Exit Function
End и;■ '
' создаем объект Child,
. Sei cnaucholar = New "utureShock. Child ' устанавливай свойства объекта
* chdScholar.Name = txtName. Value
chdScholar. Age = txtAge.Value
chdScholar.College = selCollege.Value
chdScholar.Transport = sell ransport.Value
chdScholar, Housing = selHousing.Value
вычисляем расходы
■chdScholar, CollegeCost vntPayment, vntLumpSum, vntTotaiCost '' устанавливаем свойства состояния,
’’ чтобы данные были достушы и на следующей странице
PutProperty BaseWindow. Document, "Name", txtName. Value
PutProperty BaseWindow. Document, "Payment", vntPayment PutProperty BaseWindow. Document, "LumpSum", vntLumpSum PutProperty BaseWindow.Document, "TotalCost", vntTotaiCost 1 выводим страницу с резулътатами
BaseWindow. Navigate "dhflesult.htm"
!End Function
Отображение результатов в DHTML-приложениях
Получив данные и выполнив нужные расчеты, Вы должны вывести результаты. Как Вы ссылаетесь на другие страницы в DHTML-приложении, зависит от организации файлов в Вашем проекте. Если Вы храните источник HTML-данных во внешнем НТМ-файлс, то при выводе этой страницы указываете имя соответствующего файла.
Например, в приложении Future Shock источник данных для страницы dbResult хранится в dhResult.HTM. Это упрощает редактирование файла с применением внешнего HTML-редактора Процедура обработки события butCalc_onClick выводит страницу dbRe- sult так:
BaseWindow.Navigate "dhResult.htm"
Если источник данных HTML хранится в файле конструктора (DSR), Вы должны указывать имя проекта и знак подчеркивания перед именем файла. Следующий код иллюстрирует обработчики события onCtick для двух DHTML-страниц, которые просто обеспечивают переход между собой назад и вперед:
' код для DHTHLPaget,
Private Function Button1_onClick() As Boolean
BaseWindow.Navigate "DHTMLProject J)HTMLPage2.HTML"
End Function
' код для DHTMLPage2
Private Function Button2_onClick() As Boolean
BaseWindow. Navigate "DHTMLProJect_DHTMLPage1.HTML"
End Function
Перед вызовом метода Navigate обязательно вызывайте метод PutProperty для сохранения данных, которые надо сделать доступными следующей странице. Прекратив отображение какой-либо страницы, Вы теряете доступ к ее элементам и любым данным, хранившимся в ее локальных переменных. В приложении Future Shock модуль кода MODDHTMLBAS содержит определения методов Put- Property и GetProperty. Этот модуль создается автоматически при открытии нового проекта DHTML-приложения.
' PutProperty:■_
1 сохраняет информацию в файле "cookie".,
' Обязательные параметры:
’ ссылка на сохраняемое свойство и его значение,’
' Необязателвные параметры:
" expires: указывает дату, после которой сохраненные данные 1теряют актуалвноств.*
Public Sub PutProperty(objDocument As HTMLDocument, _
strName As String, vntValue As Variant, _
Optional Expires As Date)
objDocument.cookie = StrName & "=" & CStr<vntValue) & _ IIf(CLng(Expires) = 0,"; expires=" 4_
Format(CStr(Expires), "ddd, dd-mmu-yy hh:mm: ss") &„
, "GMT") ' &_“
End SubL
' GetProperty:'
'считывает значение сохраненного свойства.
' Обязателвные параметры:
'ссылка на сохраненное саойство.
Возвращаемое значение:
текущее значение свойства. Если указанное свойство найти v не удалосв или если данные потеряли актуалвноств, функция * возвращает пустую строку.
Public Function GetPropertyCobjDocument As HTMLDocument, _
strName As String) As Variant
Dim aryCookies() As String
Dim strCookie As Variant
On Local Error GoTo NextCookie
' разбиваем объект "cookie" документа
' на массив отдельных "cookie"
aryCookies = Split(objDocument.cookie, ";")
For Each strCookie In aryCookies
If TrimfVBA. Left(strCookie, InStr(strCookie, "=") - 1)) „
= Trim(strNaine) Then
GetProparty = Trim(Hid(strCookie, „
hInStг(strCookie, "=") + 1))
Exit Function
End If
NextCookie:
Err = 0',
Next strCookie.
End Function■ ■■
Тэги <SPAN> на странице dbResult помечают места, где отображаются результаты вычислений. Используйте тэги <SPAN>, если хотите выводить результаты в одну строку с другим текстом. А если Вы предпочитаете выводить результаты на отдельных строках, применяйте тэги <DIV>. Вот как выглядят определения для dbResult;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD W3 HTML//EN">
<HTML>
<HEAD> -
<META content="text/html; " http-equiv=Content-
TypexTITLE>Future Shock!</TITLE>
СМЕТА content=""MSHTML 4.72.2106.6 " name=GENERATOR>
</HEAD>
CBODY bgColor=#ffffff ld="">
<H1>College Cost Calculater</H1>
<HR>
<P>Amount you need to invest today to pay for <SPAN Ld=spnName1>Name
</SPAN> to go to college: CSPAN ld=spnLumpSum>LumpSumc/SPAN>
<P>Aitiount you need to save each month to send CSPAN id=spnName2>Name
</SPAN> to college: CSPAN id=spnPayment>Paymentc/SPAN>
CPX/P>
<P>Total amount you need to have by the time CSPAN id=spnName3>Name
C/SPAN> is 18: CSPAN id=spnTotal€ost>TotalCostc/SPAN>
cpxlNPUT id=butBack type=buttan value="Go Backrr>c/Px/eODY>c/HTML>
Подстановку информации в тэги <SPAN> Вы реализуете в обработчике события onReadyStateChanged объекта Document. Это событие возникает после создания элементов на странице, но перед отображением на экране. Попытка использовать DHTMLPage-события Load или Initialize приведет к ошибке, так как элементов страницы на этих стадиях еще нет. Собственно подстановка информации в тэги <SPAN> осуществляется через свойство InnerText:
' это событие происходит перед самым выводом страницы на экран Private Sub Document_onReadyStateChange()
Dim vntName, vntPayment, vntTotalCost, vntLumpSum
' считываем значения свойств из modDHTML
vntName = GetProperty(BaseWindow.Document, "Name")
vntPayment = GetProperty(BaseWindow.Document, "Payment") vntTotalCost = GetProperty(BaseWlndow.Document, "TotalCost”) vntiumpSum = GetPropertyCBaseWindow.Document, "LumpSum")
' отображаем значения через тэги <SPAN>
With BaseWindow. Document' ,
. All("spnNamBl").InnerText = vntName
.All("spnName2").InnerText= vntName.
.All("spnName3"). InnerText - vntName
.AUCspnPayinent").InnerText = vntPayinent
.All("spnTotalCost"). InnerText = vntTotalCost■
-All("spnLumpSum"). InnerText = vntluoipSum
End With
End Sub
И, наконец, кнопка в нижней части страницы результатов позволяет вернуться на страницу dbCalc, применяя метод Back объекта History:
Private Function butBack„onClick() As Boolean
' возвращаемся на предыдущую страницу
BaseWindow.History.Back
End Function■
К t гой Д .к...
Создать US-приложение?
В lIS-приложснпях информация хранится в ASP (Active Server Pages) — стандартных текстовых файлах с расширением ASP, которые содержат код HTML и сценариев. ASP-файлы обрабатываются интер- нет-сервером в период выполнения, а информация пересылается клиентским браузерам в формате HTML. Исполняемые компоненты приложения содержатся в DLL-файле, выполняемом только на интернет-сервере. Последний берет на себя всю обработку, а клиентские браузеры просто обмениваются с ним данными в формате HTML. Как действует такая структура, показано на рис. 9-2.
ПРИМЕЧАНИЕ Для разработки IIS-приложения необходима операционная система Windows NT с IIS или Windows 95 с Personal Web Server (PWS).
На этапе разработки IIS-приложение состоит из одного конструктора Web-класса и произвольного количества файлов HTML- I шаблонов, модулей классов и модулей кода. В таких приложениях обычно нет форм, поскольку они не выводят информацию непосредственно на клиентских машинах.
Чтобы создать IIS-приложение, откройте новый проект Visual Basi< и в диалоговом окне New Project дважды щелкните значок IIS Application. Visual Basic создаст новый конструктор Web-класса со следующим кодом:■
СП -
Private Sub WebClass_Start(), _
Dim sQuote As String" ' ' J
sOuote = Chr$(34)',:: ' 1
' Write a reply to the user ■,,. f ;ч .
With Response■1
.Write "<HTML>"
.Write "<body>"
.Write "<h1><font face=" & sQuote & "Arial" & sQuote & ">WebClass1' s Starting Page</fontx/h1>"
■ Write "<p>This response was created in the Start " & , "event of WebClasst.</p>"
■ ..Write "</body>"
.Write "</html>"-
End With
End Sub•
Клиентский браузерКлиентский браузерno Интернетуno ИнтернетуИнтернет-серверИнтернет-серверWeb-классОбъект Investment Объект Child Объект CostsWeb-классОбъект Investment Объект Child Объект CostsРис. 9-2 Web-классыуправляют объектами, через которые ASP взаимодействуют с браузерамиРис. 9-2 Web-классыуправляют объектами, через которые ASP взаимодействуют с браузерами
Клиентский браузер
Клиентский браузер
no Интернету
no Интернету
Интернет-сервер
Интернет-сервер
Web-класс
Объект Investment Объект Child Объект Costs
Web-класс
Объект Investment Объект Child Объект Costs
Рис. 9-2 Web-классыуправляют объектами, через которые ASP взаимодействуют с браузерами
Рис. 9-2 Web-классыуправляют объектами, через которые ASP взаимодействуют с браузерами
В этом коде для вывода заголовка и другого текста через браузер используется ASP-объект Response. После его запуска Visual Basic:
создает виртуальный каталог для проекта (если он еще не создан);
создает ASP-файл для данного Web-класса;
обрабатывает ASP-файл и посылает результат клиентскому браузеру для отображения на экране.
ASP-документ, генерируемый Visual Basic, проверяет, выполняется ли диспетчер Web-классов, и, если да, получает ссылку на экземпляр модуля поддержки Web-классов (webclas^ runtime), а затем создает экземпляр Вашего Web-класса. Для кода Web-класса, показанного выше, генерируется такой ASP-документ:
Server.ScriptTimeout=600
Response.Buffsr=True
Response.Expires=O
If (VarType(Application(,'-WC-WebClassManager")) = 0) Then
Application.Lock,
If (VarType(Application("'WC-WebClassManager'‘)) = 0) Then " Set Application("'WC'WebClassManager”) = _
Server.CreateObject("WebClassRuntime.WebClassManager")
End If
Application.Unlock
End If
Applicatlon("'WC'WebClassManager").ProcessNoStateWebClass _
"Project!.WebClassl", _
Server, _,
Application, _"
Session, _■
Request, _.■
Response■■■
X>'■.
Обратите внимание (это очень важно), что сам по себе VSP-файл никакого текста в браузере не отображает. За эту операцию отвечает исполняемый код в Web-классе.
я В Visual Basic 5 Вы писали свой интерфейс в ASP-файле, используя ■;:к-ма горы HTML, и ссылались на серверные объекты через операторы VBScript, находившиеся в том же ASP-файле.
* Visual Basic 6 позволяет писать интерфейс в Web-классе, посылая HTML-данные браузеру методами Write и WtiieTemplatc. А внутри Web-класса серверные объекты доступны языковым средствам самого Visual Basic.
В следующих разделах Вы увидите, как с помощью Web-класса получить информацию от пользователя, обработать ее и сообщить результат.
Отображение HTML-шаблонов
Первое, что Вы заметите в коде, сгенерированном Visual Basic для нового Web-класса, — HTML-информация пересылается браузеру с использованием метода Write объекта Response. Этот способ хорош для простого примера, но в реальной программе слишком утоми-
телен. К счастью, Visual Basic позволяет добавлять НТМ-файлы в Ваш Web-класскакWeb-элементы HTML-шаблона.
Чтобы включить НТМ-файл в Web-класс, щелкните кнопку Add HTML Template Webitem (добавить Web-элемент HTML-шаблона) в окне конструктора. Web-класс с двумя HTML-шаблонами показан на рис. 9-3.
I ,w5)Hiti Itoeenft ^*^ taw t*i^| y<Me$>ert wcfflwet №йОе#||I ,w5)Hiti Itoeenft ^*^ taw t*i^| y<Me$>ert wcfflwet №йОе#||. B^r 6» >" y-cNcr thr^*' tt1*^ ^^ tb^1* COje» tn* ^s™ #*. ate*nJjsij>1 StiTLkih r ML ' erolas V/eti ■emi.• Я hteCeicj g| KrffieSLt ■EuHofn VebiLirnt. B^r 6» >" y-cNcr thr^*' tt1*^ ^^ tb^1* COje» tn* ^s™ #*. ate*nJjsij>1 StiTLkih r ML ' erolas V/eti ■emi.• Я hteCeicj g| KrffieSLt ■EuHofn VebiLirnt^M-1 b^/lnnC^c^M-1 b^/lnnC^c1MKLву>илМи№ ACTION1MKLву>илМи№ ACTIONP S' PP S' PrutureShock (iLStShnrk.ybp)-•J OessModiJ*!' Д Chu (Chid ris) Я CoeL 6 CCcwtMk),S lrr*esnert i^ invest .tie у IrutureShock (iLStShnrk.ybp)-•J OessModiJ*!' Д Chu (Chid ris) Я CoeL 6 CCcwtMk),S lrr*esnert i^ invest .tie у I13132121
I ,w5)Hiti Itoeenft ^*^ taw t*i^| y<Me$>ert wcfflwet №йОе#||
I ,w5)Hiti Itoeenft ^*^ taw t*i^| y<Me$>ert wcfflwet №йОе#||
. B^r 6» >" y-cNcr thr^*' tt1*^ ^^ tb^1* COje» tn* ^s™ #*. ate*nJjsij
>1 StiTL
kih r ML ' erolas V/eti ■emi
.• Я hteCeic
j g| KrffieSLt ■EuHofn VebiLirnt
. B^r 6» >" y-cNcr thr^*' tt1*^ ^^ tb^1* COje» tn* ^s™ #*. ate*nJjsij
>1 StiTL
kih r ML ' erolas V/eti ■emi
.• Я hteCeic
j g| KrffieSLt ■EuHofn VebiLirnt
^M-1 b^/lnnC^c
^M-1 b^/lnnC^c
1MKL
ву>илМи№ ACTION
1MKL
ву>илМи№ ACTION
P S' P
P S' P
rutureShock (iLStShnrk.ybp)
-•J OessModiJ*!
' Д Chu (Chid ris) Я CoeL 6 CCcwtMk),
S lrr*esnert i^ invest .tie у I
rutureShock (iLStShnrk.ybp)
-•J OessModiJ*!
' Д Chu (Chid ris) Я CoeL 6 CCcwtMk),
S lrr*esnert i^ invest .tie у I
13
13
21
21
cJcJ| hoiicdt. 2*ebitTi^ ~
cJ
cJ
ij‘Narr#.ibrir.:
TasPrvFb:At 1«ф ti обиикцед tumeton: wtae ^T# be^ne •■th Тв^в6< i4' beTasPrvFb:At 1«ф ti обиикцед tumeton: wtae ^T# be^ne •■th Тв^в6< i4' beJ^cet^^attfn^tfF^isi
TasPrvFb:
At 1«ф ti обиикцед tumeton: wtae ^T# be^ne •■th Тв^в6< i4' be
TasPrvFb:
At 1«ф ti обиикцед tumeton: wtae ^T# be^ne •■th Тв^в6< i4' be
ISKi
Рис. 9-3 Конструктор позволяет включать в Web-класс HTML-страницы, изображения и другие данные
Шаблон btmCalc показывает в браузере форму с полями для ввода данных:
<HTML>
<HEAD><TITLE>Future Shock! </HTLEx/HEAD>
<ВОВГ BGCOLOR=#FFFFFF>
<H1>Colle0e Cost Calculator</H1>.
<HR>
<FORM METHOD="POST" NAME="frmCalc">
<p>
Child's name: <INPUT TYPE=TEXT SIZE=20 MAXLENGTH=20 NAME="strName">
<p>
Child's age: <INPUT TYPE=TEXT SIZE=2 MAXLENGTH=2 NAME="strAge">
<P>
Type ol college:
<SELECT NAMt.="optCollege">
kOPTION VALUE=O>In-state public college
<OPTION VALUE=1>Out-of-state public college
<OPTION VALUE=2>Private college
</SELECT>
<р>
Type of transportation:
<SELECT NAME="optTransport">
«OPTION VALUE=O>Bik0
«OPTION VALUE=1>8us
«OPTION VALUE=2>Used car
«OPTION VALUE=3>New car
</SELECT>
<P>
Type of housing:
«SELECT NAME="optHousing">
«OPTION VALUE=0>Home
COPTION VALUE=1>Donn.
«OPTION VALUE=2>Apartinent•
«/SELECT?
<P>
«INPUT TYPE="SUBMIT" VALUE="Calculate" NAME=’’butCalc">
«/FORM?,
</BODY>
</HTML>
Для отображения этого HTML-шаблона из Web-класса вызывайте метод WnteTemplate шаблона. Например, чтобы выводить btmCalc при запуске Web-класса, замените стандартную процедуру обработки события WebClass Start на:
■ О □ Й $ 3 CM' S .
College Cost CalculatorCollege Cost Calculator• Step TWe^i Hen* Sew»- r#N<= H-ientii thwtttf й^цкеп.:
College Cost Calculator
College Cost Calculator
Child's name- jporian
Child's age: |b
Type of college {^n^ete^ubfcCGVeg^' ^
Type oftransportati^tL j Bike ^^
Type of housing-: Гнотпе Ш|
Рис. 9-4 Шаблон ЫшСа1с показывает HTML-форму для приема данных от пользователя
Private Sub WebClass_Start()'•
' показываем страницу калькулятора
htmCalc.WriteTemplate
End Sub
Этот код выводит форму College Cost Calculator (рис. 9-4).
Получение ввода в IIS-приложениях
Для приема данных, вводимых на шаблоне btmCalc, Вы должны сопоставить обработчик с событием по умолчанию для HTML-формы и написать код, который обращается к полям формы. Придерживайтесь при этом следующей схемы.
На левой панели окна конструктора Web-класса выберите HTML- шаблон, содержащий форму.
На правой панели этого окна щелкните правой кнопкой имя формы (в данном случае — /гтСя/с);появится контекстное меню.
Выберите команду View Code. Visual Basic создаст процедуру обработки события, если таковой еще нет, и сопоставит ее с событием по умолчанию для формы.
nPHME4AHnEWeb-KTaccbipearnpyK)THaco6biTnfl,KOTopbie браузер посылает серверу (например, на передачу формы или запрос страницы). Другие события - вроде onClick объекта HTML Button — происходят только на клиентской машине. Если Вы хотите обрабатывать и клиентские события, включите на страницу обработчики нужных событий, написанные на VBScript.
Для доступа к данным, введенным на форму, используйте набор Form объекта Request. Каждый элемент этого набора сопоставлен с элементом на форме через атрибут NAME в HTML-шаблоне. Например, следующий код считывает данные из всех полей формы, вычисляет по ним расходы на обучение в колледже и отображает шаблон htmResuU-.
Private Sub htmCalc_frmCalc()
' проверяем данные
If Len(Request. FormC’strName")) = 0 Or _ Len(Request. FormfstrAge")) = 0 Then Response.Write "<p>Name and age are required.</p>" Exit Sub
End If
' создаем объектную переменную
Set tnchdScholar = New Child
' считываем данные с формы
With Request
mvntName = . Form("strName")
mchdScholar. Name = . FormCstrName")
mchdScholar.Age = . Form("strAge")■
. mchdScholar. College = . Form("optCollege")
' mchdScholar.Transport = .Form("optTransport")-
mchdScholar. Housing = . FormC'optHousing")■ •
■ End With
1 вычисляем расходы
mchdScholar.CollegeCost mvntPayment. mvntLumpSum, mvntTotalCost
1 пишем результат, что вызывает событие ReplaceToken
htmResult.WrlteTemplate■
End Sub■1
ПРИМЕЧАНИЕ Если DHTML-приложения для идентификации элементов в коде используют атрибут ID, то Web-классы - атрибут NAME. Этот нюанс иногда приводит к путанице.
Отображение результатов в IIS-приложениях
Получив и обработав данные, Вы показываете результаты через браузер. Для этого проще всего воспользоваться методом Write объекта Response. Вот как можно вывести полную стоимость обучения в колледже, подсчитанную в предыдущем разделе:
Private Sub htinCalc_f rmCalc()
' здесь код опущен (совпадает с предыдущим фрагментом)
Dim sQuote As String
sQuote = Chr$(34)
выводим результат, используя Response.Write
With Response
.Write "<HTHL>"'
.Write "<body>".
.Write "<hixfont face=" & sQuote & ’'Arial" & sQuote A _ ">Results</fontx/h1>"
.Write "<p>The cost of sending " & mvntName & _
." to college is: " & mvntTotalCost & ”</p>"
.Write "</body>"
.Write "</html>"
End With
End Sub
Способ получше, позволяющий выводить большие объемы текста, — использование метода WriteTemplate шаблона. Вы подставляете нужные данные в переменные шаблона, помечаемые тэгами <WC@> и </WC@»:
<HTHL>
<HEADXHTLE>Future Shock! </ТП1Ех/НЕА0>■
<B0DY BGCOLOR=#FFFFFF>■
<H1>Results</H1>-'
<HR>
Amount you need to invest today to pay for <WC@>strName</WC@> to
go to college: <WC@>LumpSum</WC@>.’
<Р>.. . .-
Amount you need to save each month to send <WC@>strName</W№> to college: <WC9>Payment</WC9>.
<P>
Total amount you need to have by the time <WC@>strName</ Ю> is 18: <WC@>TotalCost</WC@>.
<p> <WC9>htmCalc</WC@>Go Back.</a>
</FORM>
</BODY>
</HTNL>■
Visual Basic, прежде чем записывать шаблон в браузер, проверяет наличие тэгов подстановки. Если таковые есть, Visual Basic генерирует событие ProcessTag. Код, показанный ниже, демонстрирует, как с помощью оператора Select Case заменить переменные в предыдущем HTML-шаблоне соответствующими данными.
Private Sub htmResult_ProcessTag(ByVal TagName As String, _ TagContents As String, SendTags As Boolean) ' удаление лишних пробелов и символов CR/LF ' (похоже, "жучок" в Web-классах) TagContents = Trim(TagContents) TagContents = Replace(TagContents, vbCrlf, "") ' заменяем строки в шаблоне Select Case TagContents,
Case "strName"■
TagContents = mvntName. ■
Case "Payment" TagContents = mvntPayment Case "LumpSum" TagContents = mvntLumpSum Case "TotalCost" TagContents = mvntTotalCost Case "htmCalc" ’ создаем ссылку на htmCalc
TagContents = ' <a href=" & GetURL(htmCalc) & ">" Case Else End Select1.
End Sub * возвращаем URL для Webltemi Function GetURL(htm0b1 ect As Object) As String Dim StrURL As String ' получаем URL (и все прочее) strURL = URLFor(htmObject) 1 отсекаем текст, не относящийся непосредственно к URL strURL - Mid(strURL, 1, InStr(1, strURL, "?") - 1) 1 возвращаем раэультат GetURL = strURL End Function
Visual Basic генерирует событие ProcessTag всякий раз, когда встречает в шаблоне тэг подстановки. Чтобы увидеть, как это все работает, попробуйте выполнить код пошагово. Достигнув конца шаблона, Visual Basic выведет модифицированные HTML-данные (рис. 9-5).
Results
Amountyou need to invest today to pay for Sophie to goto college $92,214,57i
Amount you need to save each month to send Sophie to college $2.325.85.
Total amount you need to have by the time Sophie 18 $249,628 r'9
G&Sack
Рис.9-5 Отправитъребенка в колледж (или написать;
эту программу?) — дело нешуточное.
Дорогой Джон, как..."
Создать приложение на базе ActiveX-документа?
Такие приложения отображают информацию и реагируют на события, задействуя пользовательские документы Visual Basic. Последние во многом похожи на формы, но есть и существенные отличия.
। ■ Доступ к данным в пользовательских документах из других частей Вашего приложения требует специальных мер. Нельзя обращаться к значению свойства в пользовательском документе извне этого документа.
в Приложениям, базирующимся на таких документах, для запуска необходим IE. Их нельзя отлаживать, используялишь среду Visual Basic.
в Скомпилированные приложения должны запускаться из НТМ- файла, который загружает компоненты приложения перед отображением файлов VBD (Visual Basic Document). Нельзя запустить скомпилированный ЕХЕ-файл или просмотреть VBD-файлы из IE безданного НТМ-файла.
Подробнее об этих отличиях — в следующих разделах. .
Получение ввода в ActiveX-документах •■■ :
Приложение Future Shock (AXES HOCK.VBP) рассчитывает стоимость обучения в колледже. Оно содержит 2 пользовательских документа, показанных на рис. 9-6. Документ udCalc (udCalc.DOB) собирает исходные данные для вычисления будущих затрат на колледж, а документ udResult (udResult.DOB) отображает результаты этих вычислений.
Future Shock!Future Shock!J ~^ РГо|^Г(11ОТ5Ьоск>¥Ьр7 Я tbcjMedutaJ ?.£^О«**1Л*нJ ~^ РГо|^Г(11ОТ5Ьоск>¥Ьр7 Я tbcjMedutaJ ?.£^О«**1Л*н
Future Shock!
Future Shock!
J ~^ РГо|^Г(11ОТ5Ьоск>¥Ьр7 Я tbcjMeduta
J ?.£^О«**1Л*н
J ~^ РГо|^Г(11ОТ5Ьоск>¥Ьр7 Я tbcjMeduta
J ?.£^О«**1Л*н
‘ College Cost Calculator
с dd’т яп'Chud-'r»с dd’т яп'Chud-'r»№№№№Гур* tf <ti ПбГур* tf <ti ПбFuture Shock!Future Shock!Jfcl:Jfcl:College Cost CalculatorCollege Cost Calculator1u*#sult -iserfucuie-ir*tiiib<T *1u*#sult -iserfucuie-ir*tiiib<T *1 Type of Haw:1 Type of Haw:.sJtpl yjtn tww.sJtpl yjtn tww.••£olSu^.••£olSu^И аиноо:^И аиноо:^
с dd’т яп'Chud-'r»
с dd’т яп'Chud-'r»
№№
№№
Гур* tf <ti Пб
Гур* tf <ti Пб
Future Shock!
Future Shock!
Jfcl:
Jfcl:
College Cost Calculator
College Cost Calculator
1u*#sult -iserfucuie-ir
*tiiib<T *
1u*#sult -iserfucuie-ir
*tiiib<T *
1 Type of Haw:
1 Type of Haw:
.sJtpl yjtn tww
.sJtpl yjtn tww
.••£olSu^
.••£olSu^
И аиноо:^
И аиноо:^
:~ ТГО11 AiT*
J*№J*№f*WT)
J*№
J*№
:^^Д>ТЕ Elm nine • " ■ it^Fift enobwi
Рис. 9-6 Пользовательские документы для сбора данных и вывода результатов ^
В разделении информации оба пользовательских документа полагаются на глобальный объект gchdScholar, объявленный в модуле DECLARES.BAS:
' глобальный объект, обеспечивающий разделение данных ' между несколькими пользовательскими документами Public gchdScholar As New Child
Объект gchdScholar — экземпляр класса Child, определенного в CHILD.CLS. Он включает свойства для каждого поля данных в пользовательском документе udCalc. Последний устанавливает значения этих свойств и при щелчке кнопки Calculate вызывает переход в пользовательский документ udResult.
Переход по какому-либо адресу в процессе отладки в среде Visual Basic требует кода, отличного от кода в скомпилированной версии интсрнет-приложения. При отладке \isu,i! Basic сохраняет временную копию каждого скомпилированного пользовательского документа (VBD-файл) в своем основном каталоге. Когда приложе-
ние готово, VBD-файлы устанавливаются на интернет-сервере. Для учета этих особенностей используются директивы условной компиляции *If...Then...,,Else.
Посмотрим, как udCak инициализирует свои элементы управления, устанавливает свойства объекта gchdScholar и вызывает переход в iidResult.
Option Explicit
flConst DEBUG_NODE = True
Private Sub UserDocunrent_InitializeO ' инициализируем списки cmbCollege.Addltam "In-state public college", chdState cmbCollege.Addltem "Out-of-state public college", chdOutOfState cmbCollege.Addltem "Private college", chdPrivate cmbTransport.Additem "Bike", chdBike cmbTransport.Addltem "Bus", chdBus cmbTransport.Addltem "Used car", chdtlsedCar cmbTransport.Addltem "New car", chdNewCar cmbHousing.Addltem "Home", chdHome.
cmbHousing.Addltem "Dormitory", chdDorm...
cmbHousing.Addltem "Apartment", chdApartment cmbCollege.Listindex = 0
cmbTransport-Listindex = 0 cmbHousing.Listindex = 0
End Sub_
' отображаем Web-страницу Costs Private Sub cmuuaicuiate_CncK() ' проверяем данные
If Len(txtName) = 0 Or Len(txtAge) = 0 Then MsgBox "Name and age are required." Exit Sub_
End If■_
' устанавливаем свойства экземпляра класса Child -^'
gchdScholar.Age = txtAge gchdScholar. Name = txtName gchdScholar.Transport = cmbTransport.Listindex gchdScholar.Housing = cmbHousing.Listindex gchdScholar.College = cmbCollege.Listindex ' навигация реализуется по-разному в отладочной и конечной версиях #If DEBUG_MODE Then
’ при отладке исполвзуем локалвный адрес Hyperlink.Navigate!о "C:\Program Files\” & _ "Microsoft Visual Studio\VB98\udResult,VBD" tfElse
' в конечной версии исполвзуем сетевой адрес'
Hyperlink.NavigateTo "http:\\6.6.6.6\" & _-
"vb6dev\chap09\AXFShock\udResult.VBD"
#End if■
End Sub■■'
В каждый пользовательский документ встроен объект Hyperlink, обеспечивающий переходы по URL и «перелистывание» (вперед/на- зад) кэша документов IE. Метод NavigateTo в какой-то мере аналогичен методу Show форм.
Отображение результатов в ActiveX-документах
Когда IE впервые переходит к документу udResult, возникает событие Initialize, код обработки которого вписывает имя будущего ученика в соответствующую метку документа. Это событие генерируется лишь раз в каждом сеансе работы с приложением.
Далее происходит событие Show. Код его обработки в пользовательском документе указывает объекту Child рассчитать стоимость обучения в колледже (умненький «ребенок»!), а затем выводит результаты в метках документа. Обработчик события Show срабатывает всякий раз, когда отображается этот документ.
Процедуры обработки событий Initialize и Show, а также щелчка кнопки Back в документе udResult приведены ниже.
Option Explicit
' используйте событие Initialize для инициализации информации,
' неизменной в течение всего сеанса
Private Sub userOocument_Initialize()
' записываем имя а метку
IbIPaymentDesc = Left(IbIPaymentDesc, _
lnStr(1, IbIPaymentDesc, "?") - 1) &_ gchdScholar.Name & _
flight(IbIPaymentDesc, _
Len(lblPaymentDesc) - _
InStr(1,IbIPaymentDesc,"?"))
End Sub
' используйте событие Show для обновления информации ' при повторном посещении этой страницы пользователем 'Private Sub UserDocument_Show()
Dim LumpSum, Payment, TotalCost
' вычисляем стоимость обучения в колледже
gchdScholar.CollegeCost Payment, LumpSum, TotalCost
' показываем результат
IblLumpSum = LumpSum.
bl Payment = Payment
IblTotalCost = TotalCost
End Sub
' возврат к странице ввода исходных данных
Private Sub cmdBack_Click()
Hyperlink.GoBack
End Sub'
Я бы. конечно, мог поместить весь код процедуры UserDocu- mentlnitialize в процедуру обработки события Show, но мне хотелось продемонстрировать разницу между этими событиями: если Вы щелкнете кнопку Back, введете исходные данные для другого ученика и «нажмете» кнопку Calculate, то udRe.su!? пересчитает расходы, но не изменит имя ученика во втором поле. Этот маленький «жучок» говорит о том, что для установки статических данных документа следует использовать событие Initialize, а для установки данных, подлежащих обновлению при каждом обращении к странице, — событие Show.
Исходный код классов Child (CH1LD.CLS), Costs (COSTS.CLS) и Investment (INVEST.CLS) см, на компакт-диске, прилагаемом к книге.
См. также...
• Следующий раздел — об установке интернет-приложений по сети.
Устанавливать ActiveX-документы по Интернету?
АсйееХ-д/экемснты — прежде чем их можно будет отображать в IE — надо установить и зарегистрировать. Если Вы попытаетесь загрузить ActiveX-документ (VBD-файл) в IE, предварительно не установив этот документ, IE предположит, что Вы хотите скопировать VBD-файл на свой диск и откроет диалоговое окно File Download (загрузка файла), показанное на рис. 9-7. Если Вы пометите переключатель Open This File From Its Current Location (открыть данный файл из того места, где он находится), IE скопирует файл, а затем предложит выбрать приложение, которое откроет этот файл.
I ,к | Ст :*| Мт» Ну | ■
Рис. 9-7 Если Вы откроете VBD-файл через Интернет, IE предположит, что Вы хотите скопировать этот файл
Для создания программы, которая установит Ваши ActiveX-документы, используйте Package and Deployment Wizard.
Чтобы запустить Package and Deployment Wizard, выберите из меню Start программную группу Microsoft Visual Basic 6.0, затем — группу Microsoft Visual Basic 6.0 Tools и, наконец, — Package and Deployment Wizard.
Щелкните кнопку Browse, укажите файл проекта (VBP) своего приложения на базе ActiveX-документ:; и выберите кнопку Package (упаковать). Мастер проверит файлы проекта и, если исходные файлы окажутся новее скомпилированных, предложит перекомпилировать приложение. Потом откроет диалоговое окно Packaging Script (сценарий упаковки).
В диалоговом окне Packaging Script введите имя сценария упаковки и щелкните кнопку Next — появится диалоговое окно Package Туре (тип пакета).
В диалоговом окне Package Туре выберите Internet Package (пакет для Интернета) и щелкните кнопку Next — появится диалоговое окно Package Folder (папка для размещения пакета).
В диалоговом окне Package Folder укажите корневую папку на интернет-сервере, на котором будет размещено Ваше приложение. В качестве альтернативы можно сохранить пакет в какой- нибудь папке на локальной машине, переместив его на сервер позднее. Щелкнув кнопку Next, Вы увидите диалоговое окно Included Files (включаемые файлы).
Диалоговое окно Included Files позволяет включать в пакет другие файлы, которые могут понадобиться Вашему приложению. Щелкнув кнопку Add, Вы сможете добавить файлы данных, не обнаруженные мастером при сканировании VBP-файла проекта на этапе 2. По завершении этих операций щелкните кнопку Next — появится диалоговое окно File Source (источник файлов).
Диалоговое окно File Source позволяет использовать Microsoft - серверы для исполняющей системы (runtime) Visual Basic и сопутствующих дистрибутивных файлов. Если Вы разворачиваете свое приложение в интрасети, то, вероятно, выберете параметр Download From Alternate Web Site (загружать с альтернативного Web-узла), чтобы можно было копировать файлы исполняющей системы Visual Basic и поддержки Автоматизации с текущего сервера или из другого участка сети. А если Ваше приложение распространяется по Интернету, Вы, наверное, предпочтете параметр Download From Microsoft Web Site (загружать с Web-узла Microsoft), чтобы у пользователей всегда были самые последние версии этих файлов. Щелкните кнопку Next — появится диалоговое окно Safety Settings (параметры безопасности).
Диалоговое окно Safety Settings позволяет указать для каждого компонента, безопасен ли он для инициализации или использования в сценариях . В случае приложения Future Shock выберите Yes в обоих разделах — Safe For Initialization (безопасен для инициализации) и Safe For Scripting (безопасен для использования в сценариях). Щелкните кнопку Next — появится диалоговое окно Finished.
Как только Вы щелкнете кнопку Finish (готово), мастер начнет сборку дистрибутива и покажет отчет по всем созданным файлам. На основе выбранных параметров, мастер генерирует файлы (их типы описаны в таблице). Файлы первых трех типов следует скопировать на свой интернет-узел.
Типы файлов, генерируемых мастером Package and Deployment Wizard’!
Тип файлов Описание
САВСжатые версии исполняемых и вспомогательных файлов
приложения.
НТМСгенерированная Web-страница, которая автоматически
устанавливает на машине пользователя содержимое _ САВ-файла, а затем открывает уже \ с тановленные АФЫ-файльг в браузере.■
VBDОткомпилированные пользовательские документы.
DDF Файл проекта, на основе которого Package and " ' Depl оу me г, i Wizard создает CAB-файл.
INFИнформационный файл, позволяющий настраивать '
процесс установки приложения.
ВАТ Командный файл для повторной сборки приложения из командной строки без графического пользовательского интерфейса Package and Deployment Wizard.
Как только Вы скопируете файлы дистрибутива на интернет- узел, пользователи смогут открыть сгенерированный ИТМ-файл и тем самым запустить приложение. Этот файл устанавливает и регистрирует компоненты приложения на машине пользователя, а потом отображает ссылки для каждого ActiveX-документа в Вашем приложении.
Вероятно, Вы захотите модифицировать сгенерированный ИТМ- файл так, чтобы при открытии страницы появлялся первый ActiveX- документ. В следующем HTML-коде показаны изменения, внесенные в ИТМ-файл, сгенерированный для Future Shock. Эти изменения выделены курсивом. Кроме того, я сократил листинг, выкинув некоторые блоки комментариев, которые создал мастер,
Прим, персю Иначе говоря, установив соответствующиепараметры, Вы гарантируете конечному пользователю. что Ваши компоненты не нанесут вреда после установки, "И 1страдпи и запуска в его системе, А пользователь вправе как пог* . так и не поверии. Вам,
<HTML> "'
<HEAD>
<TITLE>AXFShock.CAB</TITLE>.
</HEAD>.
<BODY>
cOBJECT IO="Child"
CLASSID="CLSID: 8EC98958-O3B9-11D2-8E90-000000000000"
CODEBASE="AXFShock.CABWversion=1,0,0,0">
</OBJECT>
cOBJECT ID="Costs"
CLASSIO=”CLSID:8EC98950-03B9-11D2-8E90-000000000000"
CODEBASE="AXFShock.CAB#version=1,0,0,0">
</OBJECT>’
COBJECT ID="Investment"
CLASSID="CLSID:8EC9895F-O3B9-11D2-8E90-0OOO00000OOO"
GODEBASE="AXFShock.CAB#version=1,0, 0,0">
</OBJECT>
<a href=udCalc.VBOudCalc.VBD</a>
<a href=udResult. VBOudflesult. VBD</a>
<SCRIPT U№UAGE="VBScript">
Sub Window_0nload.,
Document. Navigate "udCalc. VBD"
End Sub
</SCRIPT>•
</BODY>
</HTHL>
Атрибуты CLASSID в НТМ-файле — не что иное, как идентификаторы классов компонентов, используемых в приложении Future Shock. Важно помнить, что Visual Basic генерирует новые идентификаторы при каждой компиляции данного приложения, И поэтому после любого изменения его кода и перекомпиляции надо вновь запускать Package and Deployment Wizard.
Создав программу установки и скопировав на интернет-узел НТМ-, САВ- и VBD-файлы, приложение можно запустить, для чего:
в поле Address браузера IE укажите ЕКЬдля НТМ-файла своего приложения. IE проверит, зарегистрированы ли на локальной машине ActiveX-компоненты, нужные приложению. Если нет, IE попытается установить их из САВ-файлов;
в зависимости от уровня безопасности, заданного в IE, и информации, собранной Package and Deployment Wizard, браузер может выводить предупреждение перед установкой каждого компонента. Если Вы ответите «да» на все предупреждения, IE завершит установку и покажет первый документ Вашего приложения.
" - он, как...
Устанавливать DHTML-приложения по Интернету? "
DHTML-приложения, как и ActiveX-документы, перед запуском через Интернет тоже надо упаковать. Для этого Вы выполняете практически те же операции, что и в предыдущем разделе.
Package and Deployment Wizard обрабатываетОНТМЕ-приложе- ния несколько иначе, чем ActiveX-документы. Главное отличие в том, что мастер модифицирует НТМ-файлы, создаваемые при компиляции DHTML-проекта. Он изменяет первые строки этих файлов так, чтобы они ссылались на генерируемые им САВ-файлы. И при открытии НТМ-файла через Интернет на машине пользователя устанавливаются соответствующие исполняемые файлы.
Кроме того, не следует модифицировать сгенерированный мастером НТМ-файл, как это делалось в случае ActiveX-документов.
Дорогой Джон, как...
Распространять IIS-приложения по Интернету?
Поскольку исполняемые компоненты IIS-приложения остаются на сервере, предпринимать что-либо особенное для их установки через Интернет не требуется. Вы просто копируете файлы скомпилированного приложения на интернет-сервер, на который будут обращаться Ваши пользователи.
Скомпилированное IIS-приложение состоит из ASP-файла, Glo- bal.ASAn одного или нескольких DLL- или ЕХЕ-файлов. Не храните исполняемые файлы (DLL и EXE) в общедоступном каталоге — они запускаются только на сервере.
ГЛАВАГЛАВА1 О1 О
ГЛАВА
ГЛАВА
1 О
1 О
API-функции , ■
Сильная сторона Visual Basic в том, что он позволяет вызывать процедуры из DLL, включая функции Windows API. Эти функции и их синтаксис описаны в справочной системе Platform SDK. Доступ к тысячам функций Windows API и DLL-модулей существенно расширяет возможности Visual Basic, и благодаря этому он оставляет далеко позади многие другие языки программирования.
Здесь я поясню, как объявлять эти функции в легкочитаемом формате, и продемонстрирую несколько полезных функций, чтобы Вы освоились в работе с API-интерфейсом. Подскажу и места, на которых легко подскользя^ься, экспериментируя с вызовами API-функций.
№^^^ —- " "X...
Вызывать API-функции?
Чтобы использовать API-функции, просто объявите их в своей программе, а потом вызывайте также, как и функции Visual Basic. Об этом и пойдет речь в следующих разделах.
Объявления
Поскольку API-функции — внешние для Visual Basic, передвызовом их нужно явно объявить. Синтаксис оператора Declare хорошо описан в справочной системе, но я обнаружил несколько интересных приемов, которые, может быть, Вам пригодятся.
Объявления многих API-функций весьма длинны. Раньше Вы или мирились с длинными объявлениями, или как-то ужимали их, чтобы хоть большая их часть была видна на экране. Возьмем для примера стандартное объявление API-функции GetTempFileName. В прежних версиях Visual Basic (до версии 4) оно размещалось на одной строке:
Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFlleNameA" (ByVal LpszPath As String, ByVal IpPrefixString As String, ByVal wtlnique As Long, ByVal IpTempFileName As String) As Long
А вот сокращенная версия этого объявления, тоже записанная в одну строку (короче, но уже невразумительно):
Private Declare Function GetTempFiieNameS Lib "kernels?" Alias "GetTempFlleNameA" (ByVai Pth$, ByVai Prf$, ByVai Unql, ByVai FrM)
Visual Basic позволяет форматировать такие объявления иначе. Вы сохраняете длинные (внятные) имена параметров, но размещаете их на нескольких, более коротких строках, целиком видимых в окне редактирования. Ключ к этому формату — признак продолжения строки. Взгляните на то же объявление в новом формате (конкретная компоновка — дело вкуса):
Private Declare Function GetTempFileName _
Lib "kernels?" Alias "GetTempFileNameA" ( _
ByVai IpszPath As String,
ByVai IpPrefixString As String, _■
ByVai «Unique As Long,
ByVai IpTempFileNafne As String _•"
} As Long
Этого формата я и придерживаюсь при объявлении API-функций во всех примерах.
Объявление 32-разрядных функций
Заметьте, что в предыдущем примере объявление функции GetTempFileName в действительности ссылается на функцию GetTempFile- NameA. В Windows 95 имена 32-разрядных функций, имеющих строковые параметры, отличаются от имен соответствующих 16-разряд- ных функций, так как новые функции переписаны с учетом спецификаций 32-разрядного кодирования — даже несмотря на то что в них все еще используются ANSI-символы (отсюда и буква А в конце). Кстати, 32-разрядные версии Visual Basic чувствительны к регистру букв в именах функций Windows API.
ПРИМЕЧАНИЕ Для MicrosoftWindows NT определен еще один набор функций. В этой 32-разрядной операционной системе функции системных DLL оперируют строками в формате Unicode. Поэтому к исходным именам функций добавляется суффикс 1F(от wide characters — широкие» символы). Windows 95 не поддерживает Unicode-версии функций.
Чтобы гарантировать корректное объявление API-функций, возьмите файл WJN32API.TXT (в каталоге \Program Files\Microsoft.Visual Siudiu\Comrnon\Tools\Winapi; и импортируйте из него объявления функций в свою программу. Эго можно сделать несколькими способами. Например, загрузить WIN32API.TXT WordPadи скопировать нужные объявления вручную, заодно отредактировав их так, какя уже говорил. Или воспользоваться надстройкой API Viewer, поставляемой с Visual Basic.
API Viewer позволяет загружать текстовый файл или базу данных с объявлениями API-функций и легко их просматривать. Вы отби-
раете нужное, копируете в буфер обмена и вставляете в свою программу. API Viewer можно запустить, выбрав API TextViewerB программной группе Microsoft Visual Basic 6.0 Tools или команду API Viewer из меню Add-Ins. (Чтобы эта команда появилась в меню AddIns, выберите сначала команду Add-In Manager, затем VB 6 API Viewer и щелкните кнопку ОК.)
В главе 30 ‘Средства разработки» я расскажу, как создать надстройку для среды Visual Basic, помогающую вставлять в программы объявленияАР1-функций.
Строки
При передаче строк API-функциям как аргументов Вас подстерегают несколько опасностей. Одна из них — API-функции не выделяют память для строк. Перед вызовом Вы должны сами выделить память, достаточную для наиболее длинной строки, какая только может быть возвращена в данном вызове. В коде, приведенном ниже, объявляется функция GetWindowsDirectory, возвращающая путь к каталогу Windows. Строковый буфер для этого пути должен быть достаточно велик, чтобы вместить данные, возвращаемые Get- WindowsDirectory. Поэтому сначала вызывается функция Space и длина строковой переменной strWmPetb устанавливается равной 144 байтам.
Option Explicit
Private Declare Function GetWindowsOirectory _
Lib "kernel32" Alias "GetWindowsDirectoryA" ( _
ByVai IpBuffer As String, _
ByVai nSize As Long _
) As Long
Private Sub Form_Click()
Dim strWinPath As String
Dim IngRtn As Long
Const MAXWINPATH = 144
strWinPath = Space(MAXWINPATH)
IngRtn = GetWindowsDirectory(strWinPath, MAXWINPATH) strWinPath = Laft(strWinPath, IngRtn) ' отсекаем лишнее Print strWinPath
End Sub
Внесите этот фрагмент кода в программу, щелкните форму, и Вы увидите путь к каталогу Windows, возвращенный API-функцией Get- WindowsDirectory (рис. 10-1).
Заметьте также, что возвращаемые строки заканчиваются нулевым байтом. Данная функция сама возвращает длину строки, но при вызове многих других API-функций Вы ничего не узнаете об истинной длине строковых данных, пока не найдете в строке нулевой
байт. Чтобы отсечь лишние пробелы, если API-функция не сообщает длину возвращаемой строки, сделайте так:
strWinPath = LeftistrWinPath, InStr(strWinPath, Chr(O)) - 1)
Рис. 10-1 Путь к каталогу Windows, возвращенный API-функцией GetWindowsDirectoryРис. 10-1 Путь к каталогу Windows, возвращенный API-функцией GetWindowsDirectory
Рис. 10-1 Путь к каталогу Windows, возвращенный API-функцией GetWindowsDirectory
Рис. 10-1 Путь к каталогу Windows, возвращенный API-функцией GetWindowsDirectory
См. также...
» Приложение АРlAddin в главе 30 «Средства разработки», которое демонстрирует, как копировать и вставлять в свой код API-функции в удобочитаемом формате.
Передать адрес процедуры в API -функцию?
Передача адреса процедуры Visual Basic в API-функцию до версии 5 требовала знаний, доступных лишь узкому кругу посвященных. Но в Visual Basic 5 был введен оператор AddressOf, позволяющий программировать на Visual В.цк в стиле С. Вы передаете адрес процедуры Visual Basic API-функции, которая при выполнении вызывает эту процедуру. В языке С этот процесс называется обратньш-вызо- вом (callback).
Параметры процедуры Visual Basic определяются API-функцией. Так, в коде, приведенном ниже, API-функция LnumChildWindowsBbi- зывает функцию Visual Basic ChildWindowProc, которая должна находиться в модуле кода (BAS1, а не в модуле формы или класса. Если в поле Startup Object окна свойств проекта указана Sub Main, после запуска программы в окне Immediate перечисляются описатели (handles) окон в среде разработки.
Option Explicit
Private Declare Function GetActlveWindOw .
Lib User32 () As Long
Private Declare Function EnumChildWlndows
Lib "Ussr32'' ( _
ByVai hWnd As Long, _;
ByVai IpWndProc As Long, _'■ ’
ByVai Ip As Long .;-
) As Long
SubMain().. • :
Dim hWnd As Long-
Dim IngX As Long
' получаем описатель активного окна
hWnd = GetActivewinaowt)
If (hWnd) Then
' вызываем API-функцию EnumChildWindows, а та вызывает
' ChildWindowProc для каждого дочернего окна и завершается
IngX = EnumChiidWindows(hWnd, AddressOf ChildWindowProc, 0) End If
End Sub.,,
' вызывается API-функцией EnumChildWindows
Function ChildWinaowproct _,
ByVai hWnd As Long, _'
ByVai Ip As Long _
) As Long
' параметры hWnd и Ip передаются функцией EnumChildWindows
Debug.Print "Window: ": hWnd
' сообщаем, успешен ли вызов (а стиле С: 1 - True, 0 - False)
ChildWindowProc - 1
End Function
EnumChildWindows передает параметры bWndw Ip в ChildWindowProc. Формат вызываемой процедуры описывается в справочной системе Platform SDK (см. функцию EnumChildWindows). Имена вызываемых процедур должны заканчиваться суффиксом Proc. Он подсказывает, что обращение к данной процедуре происходит не напрямую, а через обратный вызов.
При отладке функции обратного вызова иногда приходишь просто в замешательство. Так, предыдущая программа перечисляет описатели всех окон в среде разработки Visual Basic (если она выполняется в Visual Basic). Но когда Вы скомпилируете программу и запустите ее как автономный ЕХЕ-файл, список описателей окон будет совсем другим.
Работая с оператором AddressOf, не забывайте о двух ключевых моментах.
■ Оператор AddressOf можно использовать только в модуле кода (BAS) и только как часть аргумента процедуры. Это препятствует передаче адресов процедур, входящих в состав объекта или формы.
и Процедура, на которую ссылается оператор AddressOf, должна находиться в том же проекте, что и AddressOf.
FSда."
Интерпретировать ByVal, ByRef и As Any в объявлении API-функции?
Этот вопрос не столь важен в программировании на 32-разрядной версии Visual Basic, так как большинство объявлений As Any заменены явными типами данных. Тем не менее я опишу весьма специфический случай ■ API-функцию WinHelp, которой это как раз и касается.
У многих 16-разрядных API-функций (и у отдельных 32-разряд- ных) один или несколько параметров объявляются как As Any, а не как данные конкретного типа (скажем, Integer или String). Эго связано с тем, что такие параметры — в зависимости от обстоятельств — принимают данные разныхтипов. Например, следующий фрагмент кода отображает справочный файл Visual Basic вызовом API-функции WinHelp. Обратите внимание на четвертый параметр этой функции, объявленный как As Any. (Подробнее об этой функции см. главу 17 «Справочные системы, подсказки и мастера».) В зависимости от значения wCommand данный параметр принимает либо длинное целое, либо указатель на строку.
Option Explicit■■,
Const vbHelpContents = 3.
Private Declare Function WinHelp _
Lib "User32" Alias "WinHelpA" ( _1
■ByVal hWnd As Long, _'
ByVal IpHelpFile As String,
ByVal wCommand As Long, _
ByRef dwData As Any .
) As Long1■
Private Sub Form_Click().
Dim IngX As Long, IngY As Long
IngX = WinHelp(For*1 .hWnd, "metric.hip". vbHelpContents, _
ByVal IngY)'
End Sub
По умолчанию все параметры As Any функций Windows API объявляются как передаваемые по ссылке. (Я добавил в свои объявления ByRef для большей ясности, но мог бы и опустить это ключевое слово.) В тоже npi мя перед переменной, передаваемой как аргумент вместо параметра As Any, обычно ставится ключевое слово ByVal. А значит, Вы должны уделить особое внимание тому, как трактуется данный параметр в точке вызова функции. Неверное использование ByVal и ByRef может привести к аварийному завершению приложения. Взгляните на вызов WinHelp в примере: длинное це-
лое !ngY (четвертый параметр) передается с модификатором ByVai, а это гарантирует передачу нуля как длинного целого значения.
Дорогой Джон, как... . ■
Упростить вставку объявлений API-функций?
В комплект Visual i’^i^ix входит надстройка API Viewer (APILO- AD.EXE), содержащаяся Б каталоге \Program FilesvMicrosofi Visual Studio\Common\Took\Winapi; она упрощает просмотр длинного списка констант, объявлений API-функций и связанных с ними определений структур, а также позволяет вставлять выбранные фрагменты этого списка в текст программы. Надстройка API Viewer ио умолчанию делает все объявления открытыми. Чтобы использовать API-функции в модулях форм, классов, нестандартных элементов управления или пользовательски\ документах, Вы должны поставить перед соответствующими операторами Declare ключевое слово Private.
Дорогой Джон, как..
Получить информацию о системе через API-функции?
API-функции позволяют легко получить массу информации, которую Windows обычно скрывает от приложений, написанных на Visual Basic. В предыдущих разделах я показал, как вызываются API- функции из программ на Visual Basic, Теперь рассмотрим некоторые приемы и API-функции, обеспечивающие доступ к системной информации.
Определение версии операционной системы с применением элемента управления Syslnfo
Большой объем системной информации, ранее доступный только через Windows API, теперь можно получить от элемента управления Syslnfo. Он предоставляет данные о версии системы, платформе и системных событиях. Syslnfo помогает писать код, различающий Windows NT и Windows 95, но особенно полезен для приложений, которые обрабатывают события Plug-and-Play, возникающие при вставке PCMCIA-плат (PC Card) в ноутбуки и другие портативные компьютеры.
Чтобы задействовать элемент управления Syslnfo в программе, откройте диалоговое окно Components (из меню РвяесА и активизируйте флажок в строке Microsoft Syslnfo Control 6.0. Как только Вы установите ссылку на элемент управления Syslnfo, он появится в Toolbox, и его можно будет разместить на форме. Следующий код определяет платформу и номер ее версии (экземпляру Syslnfo присвоено ИМЯ ДГЗО.9);
Option Explicit
Private Sub Form_Cltck()„
Dim strMsg As String■
' получаем информацию о платформе и версии
Select Case sysOS.OSPlatform
Case О
strMsg = "Unidentified"
Case 1
strMsg = "Windows 95, version " & CStr($ysOS,OSVersion) Case 2
strMsg = "Windows NT, version " 4 CStr(sysOS. OSVersion) End Select
' показываем полученную информацию
Print strMsg
End Sub
Щелкните форму, и Вы увидите результат (рис. 10-2).
Рис. 10-2 Элемент управления 5у$1т1ропозволяет идентифицировать операционную систему и определить номер ее версии
Увы, Syslnfo ничего не сообщает о системных i щетах, типе процессора, времени работы системыидр. Поэтому мырассмотрим, какполучитьэтуинформацию через функции Windows API.
Определение системных цветов
API-функция GetSysColor возвращает RGB-цвет таких элементов окон,какзаголовки, меню,рамкиит.д. (всего 29 компонентов). Для этого нужно передать GetSysColor соответствующую константу. Ниже приведен список этих констант, а также пример программы, демонстрирующей работу GetSysColor, — она окрашивает фон главной формы в цвет рабочего стола. Некоторые компоненты доступны лишь в определенных версиях Windows, например константы от COLOR 3DDKSHADOW и до конца списка предназначены только для Windows 95.
Option Explicit
Private Enum SysColor
. COLOR_SCROLLBAR = 0
COLOR_BACKGROUND = 1
COLOR_ACUVECAPTION = 2
COLOR.INACTIVECAPTION = 3
C0L0R_MENU = 4"-■.
COLOR-WINDOW =5.’'
C0L0R_WIND0WFRAME =6; -
. C0L0R_NENUTEXT = 7'
COLOR-WINDOWT -8.. „ ,
C0L0R_CAPTI0NTEXT =9; < .
COLOR_ACTIVEBORDER =10
COLOR_INACTIVEBORDER = 11; .
COLOR-APPWORKSPACE = 12"
COLOR_HIGHLIGHT =13-
COLOR.HIGHLIGHTTEXT = 14
COLOR-BTNFACE =15■ -
COLOR_BTNSHADOW = 16
COLOR_GRAYTEXT = 17.
COLOR_BTNTEXT = 18
COLOR„INACTIVECAPTIONTEXT = 19
COLOR_BINHIGHLIGHT =20.
„ C0L0R_3DDKSHAD0W = 21
COLOR_3DLIGHT = 22
COLOR_INFOTEXT =23. "
COLOR_INFOBK = 24•
COLOR-DESKTOP = COLOR-BACKGROUND
COLOR_3DFACE = COLOR-BTNFACE
C0L0R„3DSHAD0W = COLOR_BTNSHADOW
CO LOR-3DHIGH LIGHT = COLOR_0TNHIGHLIGHT■
C0L0R-3DHILIGHT = C0L0R_3DHIGHLIGHT
COLOR-BTNHILIGHT = COLOR-BTNHIGHLIGHT
End Enum
Private Declare Function GetSysColor _
Lib "user32" ( -
ByVai nlndex As Long _'
) As Long
Private Sub Form_Cllck()
Dim IngSystemColor As Long
Dim intRed As Integer, intGreen As Integer, intBlue As Integer ' поучаем цвет рабочего стола
IngSystemColor - 4U^y<,vULor(C0L0R_DESKT0P)
' устанавливаем фон формы таким же, как цвет рабочего стола he, Ьасксоюг = IngSystemColor
' расщепляем значение цвета на составляющие ' 1 ColorSplit IngSystemColor, intRed, intGreen, intBlue
Print "R, G,B = ”; intRed, intGreen, intBlue
End Sub
Function ColorSplitClngRGBMix As Long, _
intR As Integer, intG As Integer, intB As Integer)
' извлекаем составляющие из RGB-значения
intR = IngRGBMix And 4HFF
intG = (IngRGBMix \ &H100) And &HFF
intB = (IngRGBMix \ &H100OO) And 4HFF
End Function
Обратите внимание на функцию ColorSplit, которая извлекает из RGB-цвета составляющие его значения (уровни интенсивности красного, зеленого и синего). Она пригодится и в других операциях над графическими изображениями. Кстати, в Visual Basic есть обратная функция — RGB, которая возвращает длинное целое, сформированное из значений интенсивности красного, зеленого и синего цветов, находящихся в пределах от 0 до 255.
На рис. 10-3 показан результат, получаемый после щелчка формы.
Рис. 10-3 Вызовом API-функции Се15ухСо1огфон формы изменен на цвет рабочего стола
См. также...■
• Главу 14 "Графика» — о другом подходе к модификации цветовых схем.
Определение типа процессора
Чтобы определить тип процессора, программа-пример из этого раздела вызывает API-функцию GetSystemlnfo. Внимательно изучив структуру SYSTEM INFO, Вы обнаружите, что эта функция возвращает и другие, очень полезные сведения о системе. Учитывая появление более совершенных компьютеров, структура SYSTEM_INFO ■сообщает даже число процессоров в системе-
Option Explicit
■Private Type SYSTEM„INFQ
dwOeoiID As Long
dwPageSlzo As Long''
IpMinlmumApplicationAddress As Long IpMaxinumApplicationAddress As Long dwActiveProoessorMask As Long dwNumberOfProcesSors As Long dwProcessorType As Long dwAllocatlonGranularity As Long dwfleserved As Long
End Type
Private Declare Sub GetSystemlnfo Lib "kernel32" ( _
IpSystemlnfo As SYSTEM INFO
Private Sub Forti_Click()’-
Dim Sys As SYSTEM_INFO
GetSystemlnfo Sys
Print "Processor type: "; Sys. dwProcessorType
Print "Number of processors: "; Sys.dwNumberOfProcessors
End Sub
На рис. 10-4 показан результат, получаемый после щелчка формы.
ipei>e ti'tir 1^р^?:^Йв?^
Рис. 10-4 API-функция GetSystetnlnfo позволяет определить тип и число процессоров в системе
Определение времени работы системы
API-функция Get! ickCount возвращает число миллисекунд, истекшее с момента загрузки Windows. Ее точность зависит от системы, но практически всегда она возвращает значение куда более точное, чем функция Timer самого Visual Basic. Последняя сообщает, сколько секунд прошло с полуночи. Дробная часть этого значения создает иллюзию точности до миллисекунд, но на деле Visual Basic обновляет значение, возвращаемое функцией Timer, всего 18.2 раз в секунду. А на моем компьютере (с процессором Pentium) значение, возвращаемое функцией GetTickCount, обновляется 100 раз в секунду.
Другое преимущество GetTickCount перед Timer — нет проблемы перехода через границы суток. Timer возвращает в полночь О, а значение GetTickCount обнуляется только через 49.7 дней непрерывной работы компьютера. Б программе, приведенной ниже, обработчик события Click формы вызывает GetTickCount, и на форме отображается время в миллисекундах, истекшее с момента загрузки Windows. Процесс повторяется еще 9 раз, чтобы продемонстрировать частотуобновления значения, возвращаемого GetTickCount.
Option Explicit
Private Declare Function GetTickCount _
Lib "kerne 132" ( _
) As Long
Private Sub Form^ClickO
Dim i As Integer, IngMS As Long
Print "Time elapsed since Windows was started:"
Fori = 1 To 1O
IngMS = GetTickCount
Do While IngMS = GetTickCount
Print IngMS; ‘ milliseconds"
Next i
End Sub
На рис. 10-5 показан результат, получаемый по щелчку формы.
Рис. 10-5 Время, прошедшее с момента загрузки Windows (сообщаемое функцией GetTickCount) и его обновленные значения
Определение типов дисков
Определить, сколько у компьютера дисководов, жестких дисков и подключен ли он к сетевым дискам, позволяет API-функция GetDriveType. Она полезна, скажем, в программе, которая ищет определенный файл на всех доступных дисковых устройствах. Вот пример, где функция GetDriveType используется для определения дисковых устройств, имеющихся в системе-.
Option Explicit
' константы, соответствующие возможным значениям, ' возвращаемым функцией GetDriveType
Const DRIVE_REMOVABLE = 2
Const DRIVEFIXED = 3
Const DRIVE_REMOTE = 4
Const DRIVE_CDHOM = 5
Const DR IVE_RAMDISK = 6
Private Declare Function GetDriveType _.u.
Lib "Kernel32" Alias "GetDrlveTypeA" ( _ ByVai nDrivs As String _
) As Long'"
Private Sub Form_Click().
Dim i As Integer, LngDrive As Long, strD As String
For i = о то 25 ' проверяем все возможные диски от А до Z
strD = Chr(i + 65) & ":\"
IngDrive = GetDriveTypeCstrD).
Select Case IngDrive
Case DRIVE.REMOVABLE
Print "Drive " & strD & " is removable."
Case DRIVE,FIXED
Print '’Drive " & strD & ’’ is fixed."
Case DRIVE_REMOTE
Print "Drive " & strD & " is remote."
Case DRIVE.CDROM'
Print "Drive " & strD & " is CD-ROM."
Case DRIVE.RAMDISK
Print "Drive " & strD & " is RAM disk."
Case Else
End Select
Next i'
End Sub
Щелкнув форму, Вы увидите список дисковых устройств на своем компьютере (рис. 10-6).
Рис. 10-6 API-функция GetDriveType сообщает типы всех дисковых устройств в системе
П^.«.«^«Л л ■^алГ1 .icv м —т
Вызывать API-функции из кода ActiveX-элемента?
Элемент управления Sy.slnfo оставляет за бортом немало полезной информации. Например, с его помощью можно обнаружить, что пользователь изменил системные цвета, но узнать, какие именно, не удастся — придется вызывать функции Windows API.
К счастью, Visual Basic теперь позволяет создавать свои собственные элементы управления и вызывать оттуда API-функции. А значит, теперь можно «упаковать» вызовы часто используемых API- функций в методы и свойства элементов управления и обращаться к этим функциям без всякой возни с длинными и запутанными объявлениями.
Добавление объявлений API-функций в ActiveX-элемент
Объявления API-функций включаются в модуль нестандартного элемента управления так же, как и в любой другой модуль. В простейшем случае все, что от Вас требуется, — создать новый проект Acti-
veX-элсмента, объявить API-функции на уровне модуля и вызывать их из кода открытых процедур модуля. Вот пример, в котором создается метод GetTicks элемента управления TickControl: г Option Explicit
' модуль элемента управления TickControl
' объявление API-функции
Private Declare Function GetTickCount _
Lib "kernel32" ( _
) As Long-
Public Function GetTieksO As Long■
' возвращаем число миллисекунд с момента загрузки Windows
GetTicks = GetTickCount■
End Function
Заметьте, что API-функцию надо объявлять как Private. GetTicks служит просто оболочкой API-функции GetTickCount, чтобы га была доступна из других модулей.
См. также...
• Главу б «ActiveX-элементы» — подробнее о создании элементов управления.
Расширение существующего элемента управления
Элементы управления могут содержать другие элементы управления, что очень удобно, если Вы хотите расширить возможности существующего элемента, но не знаете его исходного кода. Только учтите, что на компьютере пользователя надо установить оба элемента управления.
Чтобы добавить новые свойства и методы, например, элементу Syslnfo, следует создать надкласс. Термин создание надкккси (super - classing) означает, что новый объект включает в себя существующий, поддерживая его возможности и вместе с тем предоставляя новые свойства и методы.
На рис. 10-7 показан элемент Syslnfo. помещенный в окно UserControl. Добавим код, расширяющий его, т. е. создадим надкласс.
ho^ и г^и^и^ (littfiKkssriv^ RF:
Рис. 10-7 Расширение элемента Syslnfo созданием надкласса
Создавая надкласс, надо воспроизвести все свойства, методы и события базового элемента управления. Код, реализующий эту задачу, тривиален, но набирать его утомительно, что отлично видно даже из следующего примера:
' делегируем (отсылаем к) свойствам элемента Syslnfo
Public Property Get ACStatusO As Integer
ACStatus = sysOldSysInfo.ACStatus
End Property
Public Property Get BatteryFullTimeO As Long
BatteryFullTime = sysOldSysInfo.BatteryFullTime
End Property
Public Property Get BatteryLifePercentO As Integer
BatteryLifePercent = sysOldSysInfo.BatteryLifePercent
End Property
Public Property Get BatteryLlfeTimeO As Long
BatteryLifeTime = sysOldSysInfo.BatteryLifeTime
End Property
Public Property Get BatteryStatus() As Integer
BatteryStatus=. sysOldSysInfo. BatteryStatus
End Property
Public Property Get OSBuild() As Integer■
OSBuild = sysOldSysInfo.OSBuild
End Property
Public Property Get OSPIatform() As Integer
OSPlatform = sysOldSysInfo.OSPlatform
End Property
Public Property Get OSVersionO As Single
OSVersion = sysOldSysInfo.OSVersion
End Property
Public Property Get ScrollBarSizeO As Single
ScrollBarSize = sysOldSysInfo.ScrollBarSize
End Property
Public Property Get WorkAreaHaight<) As Single
WorkAreaHeight = sysOldSysInfo.WorkAreaHaight
End Property
Public Property Get WorkAreaLeftO As Single
WorkAreaLeft = sysOldSysInfo.WorkAreaLeft
End Property
Public Property Get WorkAreaTopO As Single
WorkAreaTop = sysOldSysInfo.WorkAreaTop
End Property
Public Property Get WorkAreaWidthO As Single .
WorkAreaWidth = sysOldSysInfo.WorkAreaWidth
End Property
Воспроизведение свойств и методов таким способом называется делегированием. Свойства вроде Name и Parent делегировать не требуется, так как их предоставляет контейнер.
Чтобы воспроизвести все события элемента Syslnfo в элементе Enchanced Syslnfo, объявляйте каждое событие оператором Event и генерируйте новое событие оператором RaiseEvent в обработчиках соответствующих событий элемента Syslnfo. И вновь: код тривиален, но «многословен». Здесь приведена его краткая версия, а полную — см. на компакт-диске, прилагаемом к книге.
Option Explicit
' объявление событий
Event ConfigChangeCancelledQ
Event ConflgChangedC „
OldConfigNum As Long, _
NawConfigNum As Long) "
Event DeviceArrival( _
DeviceType As Long, _
DevicelD As Long, _
DeviceName As String, _
DeviceData As Long)
Event DevlceOtherEvent( „, .-
DeviceType As Long, _
EventName As String, _*
DataPointer As Long)■■
Event DeviceQuaryRemovat _
DeviceType As Long, _
DevicelD As Long, _-
iDeviceName As String, _
DeviceData As Long, _
Cancel As Boolean)
' и t. д. и t. и.
' генерируем все события Syslnfo в новом элементе управления
Private Sub QldSysInfo_ConfigChangeCancelLed()
RaiseEvent ConfigChangeCancelled
End Sub
Private Sub 01dSysInfo_ConfigChanged( _
ByVai OldConfigNum As Long, _
ByVai NewConfigNum As Long)
RaiseEvent ConfigChanged(01dConfigNum, NewConfigNum) Bld Sub
Private Sub 01dSysinfo_DeviceArrlval( _.
ByVai DeviceType As Long, _t -
ByVai DevicelD As Long, .
ByVai DeviceName As String, _
ByVai DeviceData As Long)
RaiseEvent DeviceArrlvaKDeviceType, DevicelD, DeviceName, DeviceData) End Sub
Prlva+a sub rievtinfo DeviceOtharEventt _
ByVai DeviceType As Long, _
ByVal EventName As String, _
ByVal DataPointer As Long)• ■ ■■
RaiseEvent DeviceOtherEvent(DevlceType, EventName, DataPointer) End Sub
Private Sub 01dSysInfo_DeviceQueryRemove( _
ByVal DeviceType As Long, _
ByVal DevicelD As Long, _':
ByVal DeviceName As String, _.
ByVal DeviceData As Long, _’
Cancel As Boolean)
RaiseEvent DeviceQueryRemove(DeviceType, DevicelD, DeviceName, _ DeviceData, Cancel)
End Sub
■ И T. д. и T. n.
Каждое событие элемента Syslnfo генерирует событие с тем же именем в новом элементе. И только после воспроизведения всех событий, свойств и методов Syslnfo можно написать кое-что от себя: ' объявления констант для системных цветов
Public Enum SystemColor
COLOR_SCROLLBAR = о.
COLOR-BACKGROUND = 1’•
COLOR.ACTIVECAPTION = 2*
COLOR.INACTIVECAPTION = 3
COLOfl_MENU = 4.
COLOR -WINDOW = 5.
COLOR_WINDOWFRAME = 6
COLOR_MENUTEXT = 7
COLOR_WINDOWTEXT = 8
COLOR_CAPTIONTEXT = 9
COLOR.. ACTIVEBORDER = 10
' остальные константы опущены для краткости End Enum
Private Declare Function GetSysColor _•
Lib "user32" { _
ByVal nlndex As Long _" .
) As Long■'
Public Function GetSystemColor(nIndex As SystemColor) As Long
GetSystemColor = GetSysColor(nlndex)
End Function""
Метод GetSystemColor возвращает цвета, установленные для заданного объекта Windows. Когда Вы используете этот элемент управления л новом приложении, значения, указанные в Enum, появляются в окне Object Browser.
'у См. также...
ActiveX Control Interface Wizard (мастер интерфейсов ActiveX- элементов) — о более простом способе создания интерфейсов. Чтобы добавить этот мастер г среду Visual Basic, выберите из меню Add-Ins команду Add-In Manager и пометьте соответствующий флажок.
ГЛАВАГЛАВА1 11 1
ГЛАВА
ГЛАВА
1 1
1 1
Мультимедиа
JD этой главе я продемонстрирую, как проигрывать звуковые файлы, компакт-диски и видеоклипы Примеры кода, приведенные здесь, потребуют звуковой платы и соответствующих драйверов. Кроме того, для воспроизведения звука понадобится какой-нибудь WAV- файл, а для воспроизведения видео — AVI-файл.
ПРИМЕЧАНИЕ Большое количество аудио- и видеофайлов Вы найдете на своем установочном компакт-диске Microsoft Windows 95. Щелкните кнопку Start правой кнопкой мыши и выберите команду Find (поиск), чтобы отыскать WAV- и AVI - файлы на компакт-диске,
Воспроизвести WAV-файл?
Проиграть звуковой файл можно несколькими способами. Мы рассмотрим два простейших: один построен на вызове API-функции mciExecute, предоставляемой Microsoft Windows, другой — на использовании элемента управления Multimedia, поставляемом с Visual Basic 6,
ФункциятаЕхеыЛе
Следующий код показывает, как объявить API-функцию mciExecute и как с ее помощью воспроизвести WAV-файл. Чтобы проверить работу этой программы, откройте новый проект, введите код в модуль пустой формы, запустите приложение и щелкните форму. Только не забудьте изменить путь и имя файла в команде mciExecute для ссылки на один из WAV-файлов в своей системе.
Option Explicit
Private Declare Function mciExecute _'
Lib "winnun.dll" ( _.
ByVai IpstrCommand As String _
) As Long
Private Sub Form_Click()
Dim intX
intX = »clExecute("Play C:\WindDws\Hedia\0ffice97\whoosh.wav")
' замените этот файл тем, что есть у Вас
End Sub''
Эта же функция позволяет посылать мультимедийным устройствам и другие команды — чуть позже Вы увидите, что она способна проигрывать даже видеоклипы.
Элемент управления Multimedia
Этот элемент прекрасно воспроизводит звуковые файлы. Минимально изменив программу, Бы сможете использовать его для про* игрывания файлов AVI (Audio-Video Interleaved) и МММ (Multimedia Movie), а также для управления мультимедийными устройствами. Пока рассмотрим воспроизведение звуковых файлов.
Откройте новый проект и вставьте на пустую форму элемент управления Multimedia (имя его класса, которое совпадает с текстом всплывающей подсказки, — MMControl). Если его нет на Вашей панели Toolbox, выберите из меню Project команду Components и в одноименном диалоговом окне на вкладке Controls пометьте флажок в строке Microsoft Multimedia Control 6.0. Добавив элемент на форму, присвойте ему имя и установите его свойство Visible как False. Мой нрпу демонстрирует этот элемент управления Б том варианте, когда он невидим в период выполнения. Описание всех кнопок элемента управления Multimedia и того, как обращаться с его «видимым вариантом», см. в справочной системе Visual Basic. Option Explicit
Private Sub Fonn_Click()
With mciTest
.FileName = "C:\Windows\Medla\Office97\whoosh.wav"
' замените этот файл тем, что есть у Вас .Command = "Sound"
End Withi
End Sub
Ппппглй Лжпи МЯК...
Воспроизвести AVI-файл?
Это на удивление просто, если система настроена на проигрывание таких файлов. Используйте API-функцию mciExecute или элемент управления Multimedia.
Функция mciExecute
Следующий код показывает, как объявить API-функцию mciExecute и как с ее помощью воспроизвести AVI-файл. Чтобы проверить работу этой программы, откройте новый проект, введите код в модуль пустой формы, запустите приложение и щелкните форму. Допустим, компакт-диск с дистрибутивом Windows 95 находится в приводе
CD-ROM с буквой D. Попробуем воспроизвести файл Welcomel.avi. Заметьте, что код, приведенный ниже, практически идентичен коду, проигрывающему WAV-файл. ,
Option Explicit - .
Private Declare Function mciExecuto
Lib winmm.dll" ( _
ByVai IpstrCommand As String _
) As Long
Private Sub Form_Cllck()
Dim intX
intX = mclExecute("Play D:\Funstuff\Videos\Welcoma1.avi")
' замените этот файл тем, что есть у Вас End Sub
Рис. 11-1 иллюстрирует форму и созданное на ней окно, и котором проигрывается видеоклип.
Рис. 11-1 Демонстрация видеозаписи начинается после щелчка формы
Элемент управления Multimedia
Этот элемент отлично воспроизводит и видеофайлы. Описание работы с элементом управления Multimedia, когда он видим на форме в период выполнения, см. в справочной системе Visual Basic, а я расскажу, как использовать его в «невидимом варианте», программно контролируя воспроизведение видеозаписи.
Откройте новый проект, вставьте на пустую форму элемент Multimedia, присвойте ему имя mciTesto установите его свойство Visible как False. Введите следующий код, запустите программу и щелкните форму. Не забудьте модифицировать значение свойства FileName, чтобы оно указывало на Ваш AVI-файл.
Option Explicit
Private Sub Form_Click()
With mciTest
.FileName = "D:\Funstuff\Vldeos\tfelcom0l.avi"
.Command = "Open"...
-Command = "Play"
EndWith
End Sub
Private Sub mciTest_Done(NotifyCode As Integer)
mciTest.Command = "Close"
End Sub
Поначалу я поставил команду Close непосредственно за командой Play и у меня ничего не вышло — проигрывание видеозаписи тут же обрывалось. Проблему удалось решить, когда я вынес команду Close в процедуру обработки события Done. Оказывается, система автоматически уведомляет программу об окончании видеозаписи, и именно в этот момент следует выполнить команду Close.
Дорогой Джон, как...
Проигрывать звуковые компакт-диски?
Элемент управления Multimedia облегчает и эту задачу. Вы можете установить его свойство Visible как True. и пусть пользователь обращается с ним, как с обычным проигрывателем компакт-дисков. Или сделать элемент невидимым, чтобы все происходило «за кулисами». Тогда Вы сможете создать собственный интерфейс для воспроизведения музыки, настроив его так, как нравится.
Э&-^"' !№|-|t!^9<I Рцпда! U^W :В* -A»Y С^е* ^Л ^Ms вУ to #*Э&-^"' !№|-|t!^9<I Рцпда! U^W :В* -A»Y С^е* ^Л ^Ms вУ to #*ItihtLtoTItihtLtoTE*wm | Г В*~|.эУ.'|~ г TV:E*wm | Г В*~|.эУ.'|~ г TV:ш р ■о-я Д'ш р ■о-я Д'3 'STFjh l1 • R 'rrr^TrQ'iaicil^3 'STFjh l1 • R 'rrr^TrQ'iaicil^ImcMuMt TW.orirelImcMuMt TW.orirelw *w *"is"is. Sekc-.ibeity EqckVfsUg ЙВсгсЖ^пе. Sekc-.ibeity EqckVfsUg ЙВсгсЖ^пе -MwwRft hiK^c^^ -MwwRft hiK^c^^
Э&-^"' !№|-|t!^9<I Рцпда! U^W :В* -A»Y С^е* ^Л ^Ms вУ to #*
Э&-^"' !№|-|t!^9<I Рцпда! U^W :В* -A»Y С^е* ^Л ^Ms вУ to #*
ItihtLtoT
ItihtLtoT
E*wm | Г В*~|.э
У.'|~ г TV:
E*wm | Г В*~|.э
У.'|~ г TV:
ш р ■о-я Д'
ш р ■о-я Д'
3 'STFjh l
1 • R 'rrr^TrQ'iaicil^
3 'STFjh l
1 • R 'rrr^TrQ'iaicil^
ImcMuMt TW.orirel
ImcMuMt TW.orirel
w *
w *
"is
"is
. Sekc-.ibei
ty EqckVfsUg ЙВсгсЖ^пе
. Sekc-.ibei
ty EqckVfsUg ЙВсгсЖ^пе
-MwwRft hiK^c^^
-MwwRft hiK^c^^
Рис. 11-2 Форма приложения Music на этапе разработки
В моем примере используется второй вариант, но я постараюсь предельно упростить его. Как показано на рис. 11-2, приложение Music выводит небольшую форму с кнопками Previous и Next. При запуске оно начинает автоматически проигрывать первую дорожку (песню) с музыкального компакт-диска, вставленного в привод CD-ROM, В верхней части формы отображается общее количество дорожек и номер текущей. При закрытии формы воспроизведение прекращается.
Реализация столь скромной функциональности не требует много кода — впрочем, как и реализация других, более интересных возможностей, если они Вам нужны. У элемента управления Mulii- media масса полезных свойств и методов, позволяющих делать практически все, что угодно. Я же задействовал лишь тот минимум, который необходим для воспроизведения музыки.
Итак, откройте новый проект стандартной ЕХЕ-программы, установите свойство Name формы как frmMusic, сохраните ее под именем MUS1C.FRM. а проект — под именем MUSIC. VBP. Теперь установите свойство 1 лр’" "I формы как Music Machine (чтоб заголовок в глаза бросался) и добавьте 5 элементов управления: метку iblMusic, таймер tmrMusic, элемент Multimedia с именем mciMusic и кнопки cmdPrevH cmdNexl. Рис. 11 -? иллюстрирует форму на этапе разработки, а рис. 11-3 — в действии, когда проигрывается компакт-диск. Введите в модуль формы такой код:
• MUSIC.FRM'■
Option Explicit
Private Sub cmdNext_Click()’
mciMusic,Command = "Next"■
End Sub
Private Sub cmdPrev_Click()-
mciMusic.Command = "Prev"'
End Sub
Private Sub Form_Load(): "■
With mciMusic
.DeviceType = "CDAudio"
.Command = "Open";
.Command = "Play", ■,
End With
End Sub,
Private Sub Form_tlnload(Cancel As Integer) .
With mciMusic
. Command = "Stop"
.Command = "Close"
End With
End Sub
Private Sub tmrMuslC_Timer()
With molNuslC
IblMusic.Caption = "Track No,: " & .Track 4 vbCrlf & "No. Tracks: " & .Tracks
End With
End Sub'
Рис. 11-3 Форма приложения Music в период выполнения, когда воспроизводится аудио компакт-диск
Свойство DeviceType элемента управления Multimedia устанавливается как СОЛкгй'о для настройки этого элемента на воспроизведение музыкальных компакт-дисков, а его свойства Tracks и Track используются для отображения информации о содержании текущего компакт-диска. Заметьте, что в приложении Music чаще всего применяется свойство Command, которое и позволяет реально ,он- тролировать мультимедийные устройства. Я записыва в него команды Open, Play, Next, Prev, Stop и Close, демонстрируя минимальную функциональность данного элемента управления. Однако он поддерживает и другие команды, равно как и другие свойства и методы, — читайте электронную документацию.
ГЛАВАГЛАВА1 21 2
ГЛАВА
ГЛАВА
1 2
1 2
Окна, диалоговые окна и прочие формы
В этой главе я продемонстрирую несколько полезных приемов в работе с формами. Некоторые из них давно уже стали стандартом (вспомним автоматическое позиционирование форм на экране), другие — поновее (например, создание форм с набором вкладок).
Д^^""^ ^он, как...
Добавить стандартное диалоговое окно About?
Диалоговое окно About (о программе) — один из стандартных элементов многих Windows-приложений. Обычно оно открывается командой About из меню Help. Проверьте любое Windows-приложение, и Вы почти наверняка найдете эту команду.
В программу несложно добавить диалоговое окно About собственной разработки. И не обязательно как-то изощряться — по сути, достаточно одной функции MsgBox. Но, если Вы хотите, чтобы оно выглядело профессионально, постройте его на основе формы.
Создав форму и присвоив свойству Name значение About, я сохранил ее в файле ABOUT.FRM. Эту форму легко загрузить в любой проект. На ней размещены 4 элемента управления Label — IblHea- ding, IblAppUcationJbTVBvx lblCopyrigbt,a также кнопка cmdOK. При выводе на экран эта форма выглядит примерно так, как на рис, 12-1, — все зависит от того, какие строки ей переданы.
•ЗЕ” ."4
Microsoft Visual Basic 6.0 developer's Workshop
АВСЮрЕМ ..
ГэдеУ f* lFF& Jci-irt Oak
Рис. 12-1 Стандартное диалоговое окно About
Код для формы About:"""
Option Explicit' '
Private Sub cmdOK_Click()•"
' убираем форму About
Unload Me
End Sub
Private Sub Form_Load() ' У„
' центрируем форму”
Left = (Screen.Width - Width) \ 2’ ■’
Top = (Screen.Height - Height) \ 2
' присваиваем исходные значения
lblApplication.Caption = Application -"
IblHeading.Caption = "- Heading
IblCopyright.Caption = "- Copyright -”
End Sub
Public Sub DisplayO
' показываем форму как модальную.
Show vbModal
End Sub
Property Let Heading(Heading As Strinnl
' определяем строковое свойство для Heading
IblHeading.Caption = Heading
End Property,
Property Let Application(Application As String)
' определяем строковое свойство для Application
lblApplication.Caption = Application
End Property'
Property Let Copyright (Copyright As String)
формируем "копирайт"
IblCopyright.Caption = "Copyright © " & Copyright
End Property
Заметьте, что строки присваиваются меткам Heading, Application и Copyright через процедуры Property Let. В этом случае вызывающая программа может присвоить эквивалентным свойствам любые строки, а Вам не придется волноваться об открытых переменных или напрямую ссылаться на свойства Caption меток на форме About. Например, чтобы вызывающая программа поместила в метку 1ЫАр- plication строку ABOLTDEM, следовало бы написать такой код:
Прим, гтсрев.: Метки IblVB в этой программе нет — автор, по-видимому, просто ошибся. Кроме того, код, лриме,чс1 г■ гы>' ниже, ■ .держится на компакт-диске не в каталоге СНАР12, а в каталоге COMMON (файл About.frm).
About.IblApplication.Caption = "ABOUTDEM"
Но благодаря тому, что форма - -срабатывается как объект и используются процедуры Property Let, код упрощается:.
About.Application = "ABOUTDEM"
Другое преимущество процедур-свойств — в момент присвоения значения свойству объект может инициировать те или иные операции. В данном случае, когда свойство Copyright записывается строка, она объединяется с другой строкой, и на экране появляется стандартное сообщение об авторских правах.
В коде формы About применяется и один открытый метод - Display, заменяющий стандартный метод Show. Я сделал так, чтобы Вы не гадали, какой должна быть форма — модальной или немодальной. Диалоговое окно About принято выводить на экран как модальную форму (не закрыв пользователь не может работать с другими формами в приложении), а для этого нужно указывать константу иЬМойы.'как аргумент метода Show. Метод Display можно, расширить, введя, например, поддержку автоматического закрытия диалогового окна About по истечении определенного времени (реализуется на основе таймера). В ООП за выполнение каких-либо операций ответственны, как правило, сами объекты, и логика метода Display не противоречит этим стандартам.
Если Вы хотите создать диалоговое окно About в совершенно другом стиле, не стесняйтесь, модифицируйте форму About так, как велит Вам сердце, Только не забудьте заменить исходные строковые значения свойств на подходящие Вашей программе — они устанавливаются в процедуре Form_Load.
Свое диалоговое окно About я использую во многих демонстрационных приложениях из части III. Добавить его в проект очень легко: создайте элемент About в стандартном меню Help и напишите несколько строчек кода, активизирующих форму About при выборе этого элемента.
Чтобы задействовать мой код, откройте новый проект и включите в него файл ABOUT.FRM. Для простейшего примера, в котором выводится диалоговое окно About, аналогичное показанному на рис. 12-1, создайте стартовую форму с меню, откуда можно вызвать форму About, и введите код:
Option Explicit
Private Sub mnuAbout_Click()
' устанавливаем свойства
About. Heading = "Microsoft Visual Basic 6.0 Developer's Workshop"
About.Application = "ABOUTDEM"
About.Copyright = "1998 John Clark Craig and Jeff Webb"
1 вызываем метод
About.Display
End Sub
Шаблон формы для диалогового окна About
С Visual Basic поставляется множество шаблонов форм, которые можно вставлять в проекты и адаптировать под свои задачи. Один из таких шаблонов — форма для диалогового окна About. Чем хорош ее шаблон, так это кнопкой System Info (о системе), хорошо знакомой Вам по диалоговым окнам About во многих приложениях Microsoft. Более того, шаблон содержит весь код, необходимый для корректной работы кнопки Ьу-и in Info, — никакого дополнительного кода от Вас не потребуется. Шаблоны форм хранятся в подкаталоге TEMPLATE основного каталога Visual Basic.
Чтобы добавить форму для диалогового окна About в свой проект, выберите из меню Project команду Add Form (добавить форму) — появится одноименное диалоговое окно, показанное на рис. 12-2.
Й** | Exitltig |
i I7
Г Dunnite*rm ькакф jri khe ДО*** " ~ ’' "
Рис. 12-2 Диалоговое окно Add Form, открывающее доступ к шаблонам форм
Выберите на вкладке New значок About Dialog и щелкните кнопку Open — в проект будет добавлена форма для диалогового окна About. Шаблон этой формы можно адаптировать, например так: Option Explicit
Private Sub mnuAbout_Click()
About.Caption = App.Title "’’
About.IblTitle = "ABOUTDEM"
About. IblVersion = "Version " & App. Major 4 "," & App. Minor _
& "," & App.Revision
About.IblDescription = "Microsoft Visual Basic 6.0 " & _
" Developer's Workshop"
About.IblDisclaimer = "Copyright © 1998 John Clark Craig " & _ "and Jeff Webb"■
About,Show
End Sub'■
На рис. 12-3 показан результат модификации шаблона.
•^ubWlh^.
Рис. 12-3 Диалоговое окно About, созданное по шаблону формы About Dialog
См. также.,.
• Приложение Dialogs в главе 34 «Сложныеприложения», где демонстрируется использование диалогового окна About.
Дорпгяй Ижон. как...
Автоматически позиционировать форму на экране?
Лучшее место для кода, который позиционирует формуна экране, — обработчик события Load формы. При этом координаты формы устанавливаются до того, как она станет видима. Чтобы отцентрировать позицию формы, просто добавьте в процедуру Form Load две строки, определяющие нужные координаты левого верхнего угла формы:
Private Sub Form_Load()
' центрируем позицию формы.
Left = (Screen.Width - Width) \ 2;
Top = (Screen.Height - Height) \ 2
End Sub-
ПРИМЕЧАНИЕ Здесь применяется целочисленное деление (\); оно быстрее деления с плавающей точкой. Кроме того, результат автоматически округляется до ближайшего целого значения, что очень удобно во многих ситуациях. Поэтому используйте операцию \ вместо / везде, где только можно.
На рис. 12-4 представлена" форма, отцентрированная на экране.
Если Вам нужно разместить форму где-то в другом месте экрана, лучше написать универсальную процедуру Пример такой процедуры (Locate) показан ниже. Она позволяет вывести форму в любой участок экрана по абсолютным координатам.
Рис. 12-4 Образец формы, отцентрированной на экране
Private Sub Form_Load()
' выводим форму в левую четверть/верхнюю треть экрана
Locate 1/4, 1/3
End SubJ
Private Sub Locate( _
Optional Xadjust As Single =0.5, _
Optional Yadjust As Single = 0.5 _
).■
Left = Xadjust * Screen.Width - Width \ 2
Top = Yadjust * Screen.Height - Height \ 2
End Sub
Параметры Xadjust и Yadjust выражают доли горизонтального и вертикального размеров экрана. При вызове процедуры Locate без аргументов подставляются значения по умолчанию — в данном случае они таковы, что форма будет размещена в центре экрана.
Еще один способ позиционировать форму на экране — использовать средство Visual Basic, окно Form Layout (разметка формы), показанное на рис. 12-5. Если этого окна нет, выберите из меню View команду Form Layout Window (окно разметки формы).
Окно Form Layout позволяет визуально разместить форму в любом месте экрана. Щелкнув изображение экрана правой кнопкой мыши, выберите из контекстного меню команду Resolution Guides '(ограничители), и в окне появятся пунктирные линии, подсказывающие размеры экрана при нескольких разрешениях (640 х цаи, 800 х 600 и 1024768). Увы, хоть это и простейший способ пози-
ционировать форму, он привязан к конкретному разрешению. Если Вы хотите центрировать форму на экране независимо от его разрешения, используйте код, приведенный выше.
Рис. 12-5 Окно Form Layout.
См. также...''
• Приложение Dialogs в главе 34-Сложные приложения», где де-монстрируется один из способов позиционирования формы.
ДОРОГОЙ
Создать «плавающее» окно?
Вопрос не совсем корректный, так как и модальная, и «плавающая» (динамическая), и «самая верхняя» (topmost) формы всегда видны «поверх» остальных форм и окон. Поэтому правильнее рассмотреть несколько способов управления формами.
Модальный режим
Для программного вывода формы на экран применяется метод Show. Если в его необязательном аргументе указана константа ubModal, пользователю не удастся игнорировать такую форму, чтобы поработать с другими компонентами приложения. В каком-то смысле этот режим можно назвать модальным в рамках приложения (application modal).•- *
frmlest.Show vbNodal
Заметьте, в модальной форме надо предусмотреть некий — прямой или косвенный — способ ее скрытия или выгрузки, чтобы приложение могло возобновить свою работу с этой точки.
Динамический режим
Динамический режим, на мой взгляд, несколько отличается от режима «поверх остальных», хотя эти термины часто употребляют как синонимы. Считайте динамическое окно постоянно -всплывающим на поверхность».
Чтобы превратить форму в динамическую, добавьте на нее элемент управления Tinier и установите его свойство Interval в соответствии с частотой, с которой форма должна «всплывать наверх». Интервал порядка 500 мс (0.5 с) вполне подойдет. Вставьте в процедуру Timerl_Timer строку, вызывающую метод ZOrder:
Private Sub Timer1_Tlmer()
ZOrder
End Sub
Режим «ППНАПУ ЛПТЯПМШУ»
Чтобы форма оставалась «поверх» всех окон даже при переключении в другие приложения, используйте API-функцию SetWindowPos. При этом способе форма выглядит лучше (если хотите, профессиональнее), чем при предыдущем. Кроме того, всю «грязную работу» можно спихнуть на саму Windows. Ваша форма будет - поверх» других форм и окон до тех пор, пока Вы не закроете ее. Требуемые вызовы функции SetWindowPos я упаковал в свойство ОпТор формы, а следующий код обращается к свойству ОпТор из процедур Form_Load и Form_Unload:
Option Explicit
' флаги для функции SetWindowPos
Private Const SWP_NOSIZE = AHI
Private Const 3WP_N0M0VE = AH2,.
.Private Const SWP_NOZORDER = &H4J
Private Const SWP_NQREDRAW = AHB' "
Private Const SWP_NOACTIVATE &H10'
Private Const SWP_FRAMECHANGED = &H20' ' "
Private Const SWP_SHOWWINDOW = AH40
Private Const SWP„HIDEWINDOW = AH80
Private Const SWP.NOCOPYBITS = AH100''
Private Const SWP_NOOWNERZORDER = &H200
Private Const SWP_DRAWFRAME = SWP_FRAMECHANGED
Private Const SWP_NOREPOSITION = SWP_NOOWNERZORDER
hwndInsertAfter-значения для функции SetWindowPos
IPrivate
Const
HWND_TOP = 0
Private
Const
HWND_BOTTOM = 1-
Private
Const
HWND.TOPMOST = -1.'
Private
Const
HWND_NOTOPMOST = -2'
Private Declare Function SetWindowPos _
Lib "user32" (i-
ByVaL hwnd As Long, _"
ByVai hWndlnsertAfter As Long, _, ,
ByVai x As Long, _
ByVai у As Long, _.J
ByVai ex As Long, _'1L
ByVai cy As Long,1
ByVai wFlags As Long _'
) As Long■ 1 .W
Private mbOnTop As Boolean.1
""".ОпТор"■
Private Property Let OnTop(Setting As Boolean)
' устанавливаем значение свойства ОпТор формы
If Setting Then
' переводим эту форму на самый верх
SetWindowPos hwnd, HWND.TOPMOST, _
О, О, О, О, SWP_NOMOVE Or SWP_NOSIZE
Else
" отменяем у формы статус "самой верхней"
SetWindowPos hwnd, HWND_NOTOPMOST, _
О, О, О, О, SWP.NOMOVE Or SWP_NOSIZE
End If
mbOnTop = SettingI-',,.,
'End Property
Private Property Get OnTopO As Boolean
' возвращаем закрытую переменную, установленную в Property Let
ОпТор = mbOnTop;.
End Property' ‘
Private Sub Form_Load()
' размещаем форму "поверх" остальных
ОпТор = True..
End Sub'
Private Sub Form_Unload(Cancel As Integer)
' возвращаем форму в обычный Z-порЯДОК
ОпТор = False
End Sub
Я включил код, возвращающий форму в нормальный Z-порядок, в процедуру Form_Unload, но Вы вправе поместить его в любую другую часть своей программы. Например, сделать так, чтобы форму можно было переключать из обычного состояния в режим «поверх остальных», и наоборот. Упаковка вызовов SetWindowPos в процедуры-свойства упрощает такое переключение. Вот строка кода, устанавливающая или сбрасывающая свойство ОпТор в зависимости от его начального значения:.
ОпТор = Not ОпТор
Не обязательно включать в код все показанные выше SWP-koh- станты. Я это сделал только для Вашего удобства — чтобы Вы увидели, какие флаги принимает функция SetWindowPos. Вы ведь можете создать для формы какие-то другие свойства, в которых понадобятся и остальные константы. При необходимости комбинируйте их побитовой операцией Or с параметром wFlags функции SetWindowPos.
Дорогой Джон« как...1
Создать экран-заставку?
Очень часто большие приложения грузятся по несколько секунд, и конкретное время зависит от объема необходимых ему инициализирующих операций и быстродействия пользовательской системы. Лучший способ скрасить вынужденное ожидание — выводить на этот период соответствующую эмблему, торговый знак или какое- то другое изображение, называемое экраном-заставкой (splash screen). Рассмотрим простейший вариант экрана-заставки, в качестве которого выступает форма/1та5£>/гыД.
Option Explicit
Private Sub --rm i^hzx
' показываем эту форму
Show■
" показываем экран-заставку
frmSplash.Show’"
Do Events-.
' выполняем длительные инициализирующие операции.., Initialize
' удаляем экран-застааку
Unload frmSplash■
End Sub.
Эту процедуру следует вставить в модуль стартовой формы приложения. В качестве гаковой обычно выступает главная форма, остающаяся активной до тех пор, пока выполняется приложение. Весь код, управляющий экраном-заставкой, можно разместить прямо в процедуре Form_Load. поскольку Visual Basic позволяет нам в какой- то мере контролировать порядок событий. Первый метод Show заставляет Windows нарисовать на экране главную форму, что при нормальном ходе вещей происходит уже после завершения процедуры Form Load. Следующий метод Show отображает экран-заставку (это Ваша форма с именем frmSplasb). За ним я поставил вызов функции DoEvents, что гарантирует немедленную прорисовку всех элементов формы/>?„57>/«л7> Функция DoEvents заставляет Visual Basic уступить управление операционной системе на период, необходимый ей для выполнения ;з < - отложенных операций. Гипотетическая функция Initialize олицетворяет собой группу длительных операций, осуществляемых приложением при запуске: инициализацию данных, загрузку в память ресурсов и форм и т. д. По завершении инициализации форма frmSplasb выгружается, и все идет своим чередом.
Парис. 1 ■ <■ показан пример экрана-заставки, размещенного в центре главной формы приложения.
Рис. 12-6 Экран-заставка гипотетического приложения в действии
Шаблон формы для экрана-заставки
Как я уже говорил, с Visual Basic поставляется масса шаблонов все- лозчгжлы.х форм. В их число входит и шаблон формы для экрана- заставки. Чтобы добавить такую форму в проект, выберите из меню Project команду Add Form. На вкладке New щелкните значок Splash Screen, а затем — кнопку Open. В проекте появится форма Splash Screen.
Вот пример . . как адаптировать шаблон формы splash. Screen под свои задачи:
Option Explicit' ■
Private Sub Form_Load()
1 определяем надписи на экране-заставке
frmSplash.IblLicenseTo = Арр.LegalTrademarks
frmSplash. IblCompanyProduct = App.ProductName
frmSplash.IblPlatform = "Windows 95"
frmSplash.IblCopyright = App.LegalCopyright
frmSplash.IblCompany = App,ComnanvName
frmSplash.IbIWarning = "Warning: This program is protected " & _ "by copyright law, so du, l copy" ■
отображаем экран-заставку,
frmSplash.Show
DoEvents
выполняем длительные инициализирующие операции...
Initialize
удаляем экран-заставку
Unload ।rmSplash
End Sub
Обратите внимание на используемый здесь объект Арр. Он обеспечивает доступ к информации о Вашем приложении. Большая часть этой информации указывается на этапе разработки на вкладке Маке диалогового окна Project Properties (которое открывается одноименной командой из меню Project).
В шаблоне формы Splash Screen уже есть код:
Private Sub Form_KeyPress(KevAscii As Integer)
Unload Me
End Sub
Private Sub «orm_Load()
IblVersion.Caption = "Version ” & App.Major & "." &
App.Minor & '." & App.Revision
IblProductName.Caption = App.Title
End Sub
Private Sub Framet_Click()•
Unload He End Sub
Заметьте, что его процедура Form Load сама указывает версию и название продукта, исходя из данных, определенных на вкладке Маке диалогового окна Project Properties.
Пример экрана-заставки, созданного по шаблону формы Splash Screen, показан на рис. 12-"'..
SplashDemo Product
Windows 95Version 1.0.0Cflptd Ipf 8О1436 J*: CIwk Jlr iigWindows 95Version 1.0.0Cflptd Ipf 8О1436 J*: CIwk Jlr iigSplashDemo
Windows 95
Version 1.0.0
Cflptd Ipf 8О1436 J*: CIwk Jlr iig
Windows 95
Version 1.0.0
Cflptd Ipf 8О1436 J*: CIwk Jlr iig
^гис^йгт pnevwto с^ти^.Щтл^чйМ.спру
Рис. 12-7 Экран-заставка, созданный по шаблону формы Splash Screen■
j^^ См. также...
• Приложение Jot в главе 32«Базы данных», где демонстрируется управление экраном-заставкой.
Дорогой Джон. как,..
Использовать элементы TabStrip и SSTab?
Visual Basic предоставляет элемент управления TabStrip («набор ярлычков») в файле COMCTLJ2.OCXh SSTab («набор вкладок») в файле TABCTL32.OCX. Последний — более совершенная версия элемента Sheridan SSTab из Visual Basic 4.•■■
Вы можете добавить эти элементы управления на панель Toolbox через диалоговое окно Components: для TabStrip пометьте флажок в строке Microsoft Windows Common Controls, а для SSTab — в строке Microsoft Tabbed Dialog Control.
Элемент управления SSTab
Работать c SSTab гораздо проще, чем с TabStrip. Главное различие между ними в том, что TabStrip не предоставляет контейнеров для элементов управления, размещаемых на вкладках. Применяя TabStrip, обычно приходится добавлять на каждую вкладку контейнер Frame или PiclureBox как массив элементов управления. После этого Вы вставляете в контейнеры элементы управления и пишете код, который отображает соответствующий контейнер при выборе связанного с ним ярлычка.
Используя SSTab (рис. 12-8). Вы просто рисуете на вкладках нужные элементы управления. В период выполнения содержимое вкладки показывается автоматически после щелчка связанного с ней ярлычка. Каждый элемент управления следует рисовать в пределах одной из вкладок — нельзя вычертить его где-то на форме, а потом взять и переместить на вкладку. Иначе Вы получите самостоятельный элемент управления, не связанный ни с какой вкладкой, и, кроме того, он появится поверх SSTab.
У элемента управления SSTab есть даже свойство ToolTipText, что выгодно отличает его (элемент) от версии SSTab, поставлявшейся с Visual Basic 4. Для свойства Style предусмотрено 2 значения, определяющих внешний вид SSTab: ssStyleTabbedDialogw ssStyleProperty- Page. Если выбран первый стиль, вкладки выглядят, как в некоторых приложениях MicrosoftWindows ~ 1 а если второй — как в Microsoft Windows 95.
УСйм^йг • ‘^ =| ТлрЬ [УСйм^йг • ‘^ =| ТлрЬ [:Р ^i®#dxieI" 'Дид- marusГ Sta-ti*#:Р ^i®#dxieI" 'Дид- marusГ Sta-ti*#: UpdaJe L-J л,<■ V дг?|рйчгГ* Naw: UpdaJe L-J л,<■ V дг?|рйчгГ* Naw
УСйм^йг • ‘^ =| ТлрЬ [
УСйм^йг • ‘^ =| ТлрЬ [
:Р ^i®#dxie
I" 'Дид- marus
Г Sta-ti*#
:Р ^i®#dxie
I" 'Дид- marus
Г Sta-ti*#
: UpdaJe L-J л,
<■ V дг?|рйчг
Г* Naw
: UpdaJe L-J л,
<■ V дг?|рйчг
Г* Naw
Рис. 12-8 Элемент управления SSTab в действии
Единственный случай, когда элемент управления SSTab не применим, — программирование для 16-разрядной Windows. Тогда можно использовать лишь 16-разрядную версию SSTab из Visual Basic 4.
Дооогой Джон. как...
Заставить форму мигать, чтобы привлечь внимание пользователя?.
Нечто в таком духе позволяет сделать API-функция ILi'diWindow — она заставляет мигать не само окно, конечно, а его заголовок. Иногда, в критических ситуациях требующих немедленной реакции ио.тьзовагеля. это оказывается очень полезным.
Чтобы опробовать этот прием, создайте 2 формы: frmControl и frmFlasb. На форму frmFlasb добавьте элемент управления Timer и присвойте ему имя tmrFlash. В модуль формы frmFlasb введите код: Option Explicit
Private Declare Function FlashWindow _."
Lib "user32" { _■
IByVal hwnd As Long, __.•
ByVai blnvert As Long _
} As Long, ■
Private Sub Form_Load()....
tmrFlash.Enabled = False
End Sub,"
Private Sub tmrFlash_TimBr()' '
Dim IngRtn As Long'*
IngRtn = FlashWlndow(hwnd, CLng(True))■
End Sub.
Property Let Rate(intPerSecond As Integer)■1
tmrFlash. Interval = 1000 / IntPerSecond
End Property■
Property Let FLash(blnState As Boolean)'..'
tmrFlash. Enabled = blnState-
End Property
На форму frmControl добавьте 3 кнопки (cmdFast, cmdSlow и cmdStof^ и измените значения их свойств Caption соответственно тому, что показано на рис. 12-9. В модуль формы frmControl введите код:
Option Explicit-' • "
Private Sub cmdFast_Click()‘
frmFlash.Rate =5' '
frmFlash.Flash = True
End Sub’.
Private Sub cmdStop_Click() frmFlash,Flash = False
End Sub
Private Sub cmdSlow_Click() frmFlash.Rate = 1 frmFlash.Flash = True
End Sub
Private Sub Form_Load()■J
frmFlash, Show'
End Sub
Рис. 12-9 иллюстрирует эти две формы в действии.
Рис. 12-9 ФормаfrmControl управляет миганием заголовка формы frmFlash
На рис. 12-9 фокус ввода сосредоточен на формеfrmControl, но мигает заголовок формы frmFlash. Вместе с заголовком формы frmFlash мигает и ее значок на панели задач (если, конечно, эта панель присутствует на экране). При этом значок формы мигает, даже если формл frmFlash скрыта другими окнами.
Следуя духу и букве ООП, параметры Rate (частота мигания) и Flash (признак мигания) я представил как свойства «мигающей» формы. Благодаря этому можно задать частоту мигания из любого места программы — достаточно присвоить нужное целочисленное значение свойству Rate формы. Ну а свойство Flash позволяет либо активизировать мигание (True), либо прекратить его (Рй/я?).Как устанавливаются эти свойства, видно из исходного кода для формы frmControl.
CM1 также-"'
Программу-пример Blinker в главе 6‘ActiveX-эле менты >>, где показано приблизительно то же самое, но применительно к ActiveX-элементам.
Приложение Messages в главе 34 «Сложные приложения» — практический пример использования «мигающей* формы.
Лоппгпй Лжпн. KS1IC„„ " ■ Переместить элемент управления в новый контейнер?
Эта весьма интересная возможность Visual Basic открывает простор для самых смелых экспериментов. Контейнерные элементы управления, к каковым сейчас относятся рамки и окна рисунков, позволяют размещать на себе другие элементы управления. Свойство Container большинства элементов допускает не только чтение, но и запись, а значит, элемент можно заставить -перепрыгнуть» в другой контейнер!
Чтобы увидеть, как это все работает, добавьте на пустую форму 2 элемента управления Frame и присвойте им имена fraLeftw. ра- Right. Посередине левой рамки нарисуйте кнопку cmdjump.^> итоге форма должна выглядеть примерно так, как на рис. 12-10.
Рис. 12-10 Свойство Container позволяет в период выполнения перемещать элементы управления на другие объекты
И, наконец, добавьте в модуль формы следующий код:
Option Explicit
Private Sub cmdJump_Click()
Set cmdJump.Container = fraRight
End Sub
Запустив программу и щелкнув кнопку, Вы увидите, что кнопка перескочила в середину правой рамки. Впечатляет? А ведь эта бесхитростная демонстрация — лишь намек на колоссальные возможности Visual Basic..--
ГЛАВАГЛАВА1 31 3
ГЛАВА
ГЛАВА
1 3
1 3
Визуальный интерфейс
^Фундаментальные изменения в Бейсике Microsoft обозначила словом Visual (визуальный). Главная причина успеха Visual Basic в том, что он позволил создавать программы с улучшенным и простым в использовании визуальным интерфейсом. Visual Basic ввел два заметных усовершенствования в визуальный интерфейс программ. Во-первых, у многих элементов управления появилось свойство ToolTipText, что резко упростило создание подсказок по интерфейсным элементам, а во-вторых, открыт доступ к множеству элементов управления Microsoft Windows 95, включая ''nDo^i1
В этой главе я расскажу, как улучшить пользовательский интерфейс программ за счет средств Visual Basic и Windows, и продемонстрирую несколько творческих приемов программирования.
Дорогой Джон, как,,,
Использовать «облегченные» элементы управления?
Элементы управления Shape, Line, Label и Image, встроенные в Visual Basic, всегда были безоконными, или ■ юдегченнымп • (lightweight). Теперь появились безоконные версии элементов управления:
■ CheckBox (флажок);.
в ComboBox (поле со списком);
в CommandButton (кнопка);
Frame (рамка);
Я HScroliBar (горизонтальная полоса прокрутки);
В ListBox (список);
OptionButton (переключатель);
В TextBox (текстовое поле);
В VScrollBar (вертикальная полоса прокрутки).
У безоконного элемента управления нет свойства hWnd. Если Ваше приложение не обращается к API-функциям, требующим этого свойства элементов управления, то «облегченные» версии позволят более экономно расходовать ресурсы компьютера и ускорить загрузку приложения. Кроме того, подобные элементы управления не поддерживают механизм DDE (Dynamic i i Exchange). А в остальном они ничем не отличаются от своих стандартных версий.
Каждый из элементов управления, перечисленных выше, доступен в обеих версиях — нормальной (оконной) и новой «облегченной* (безоконной). Если функциональность стандартной версии элемента управления не нужна, советую использовать его «облегченную» версию.■
Дорогой Джон, как.»-
Добавить горизонтальную полосу прокрутки в окно списка?
Иногда трудно редсказать ширину текста, который появится ; окне списка. Если строки слишком длинны, в окно нужно добавить горизонтальную полосу прокрутки. Эта задача решается с помощью API-функции SendMessage.
Откройте новый проект, добавьте на форму элемент управления ListBox и кнопку. Присвойте им имена агтд-гги пшКЬгЬМ ktсоответственно. Сделайте окно списка достаточно широким — «нажатие» кнопки сузит его при демонстрации.
Включите в свой модуль объявление API-функции SendMessage:
Private Const LB_SETHORIZONTALEXTENT = &H194
Private Declare Function SendMessage _
Lib "user32“ Alias "SendMessageA" ( _
ByVai hwnd As Long,
ByVai wMsg As Long,
ByVai wParam As Integer, _
ByVai IParam As Long'
) As Long
SendMessage принимает 4 аргумента. Параметр hwnd определяет описатель окна, а дл№& — сообщение, посылаемое окну. В этом примере сообщение будет константой, указывающей добавить горизонтальную полосу прокрутки в окно списка. Параметры wParam и IParam определяют информацию, связанную с сообщением. В данном случае wParam задает ширину окна списка (в пикселах), при которой следует добавить горизонтальную полосу прокрутки, а IParam не используется.
Назначение кнопки — сужать ширину окна списка на 10% при каждом ее щелчке. Когда окно сузится настолько, что строки текста перестанут умещаться в нем, в нижней его части автоматически появится горизон тальная полоса прокрутки.
Private Sub cmdShrinkList_Click()
IstTest.Width = IstTest.Width * 9 \ 10
End Sub
Добавление горизонтальной полосы прокрутки предусматривается еще в момент загрузки формы. Код, приведенный ниже, ини-
циализирует список и добавляет горизонтальную полосу прокрутки, вызывая функцию SendMessagc. Так как SendMessagc- ожидает значения «-Раптщ в пикселах, следует работать именно в этих единицах. Установите свойство ScaleMode формы как vbPixels. Пороговая ширина списка, при которой появляется горизонтальная полоса г юкругки, определяется по самой длинной строке методом TextWidth; он возвращает результат в единицах, заданных свойством ScaleMode формы, — в данном случае в пикселах.
Private Sub Form_Load()
Dim strLongest As String, IngRtn As Long
' устанавливаем свойство ScaleMode
ScaleMode = vbPixels
' инициализируем список
strLongest = "This is a list of months of the year"
IstTest. Addltein strLongest
IstTest.Addltem "January"
IstTest.Addltem "February"
IstTest. Additem "March"
IstTest.Additem "April"
IstTest. Addltem"May"1
IstTest.Additem "June",
IstTest.AddIten "July"'
IstTest,Additem "August".
IstTest.Additem "September"• •-
IstTest.Additem "October" 1 •
IstTest.Additem "November" - ■
IstTest. Addltenf'December".
' подгоняем размер шрифта на форме под его размер в списке
Fermi.Font.Size = IstTest.Font.Size
' задаем пороговую ширину, при которой должна появляться
' горизонтальная полоса прокрутки
IngRtn = SendNessage(lstTest.hwnd, LB_SETHORIZONTALEXTENT, _ Forml.Textwidth(strLongest), ByVal OA)
End Sub
Запустите программу и, если при первом появлении список еще достаточно широк, щелкните khoukv Shrink List (сузить список) — может быть, это придется сделать несколько раз, — чтобы появилась горизонтальная полоса прокрутки. При каждом «нажатии» кнопки список сужается на in' На рис. 13-1 и 13-2 показан один и тот же список с горизонтальной полосой прокрутки и без нее.
Метод Text Width возвращает ширину строки при текущем шрифте, установленном для формы, элемента PictureBox или объекта Fi и in. ■ но не применим к спискам. Чтобы обойти это ограничение и добиться автоподстройки размера шрифта в списке, я копирую значение свойства Size объекта Font списка в свойство Size объекта Font формы и только потом вызываю метод TextWidth формы для определения и установки пороговой ширины, при которой должна
появляться горизонтальная полоса прокрутки. Если Вы собираетесь изменять и другие свойства объекта Font списка, их значения надо тоже скопировать в соответствующие свойства объекта Font формы.
fiFibnKl^tfiFibnKl^tРис. 13-1 Самая длинная строка пока умещается в окне списка, и горизонтальная полоса прокрутки не появляетсяРис. 13-1 Самая длинная строка пока умещается в окне списка, и горизонтальная полоса прокрутки не появляетсяРис. 13-2 Окно списка слишкомузко для самой длинной строки — появляется горизонтальная полоса прокруткиРис. 13-2 Окно списка слишкомузко для самой длинной строки — появляется горизонтальная полоса прокрутки
fiFibnKl^t
fiFibnKl^t
Рис. 13-1 Самая длинная строка пока умещается в окне списка, и горизонтальная полоса прокрутки не появляется
Рис. 13-1 Самая длинная строка пока умещается в окне списка, и горизонтальная полоса прокрутки не появляется
Рис. 13-2 Окно списка слишкомузко для самой длинной строки — появляется горизонтальная полоса прокрутки
Рис. 13-2 Окно списка слишкомузко для самой длинной строки — появляется горизонтальная полоса прокрутки
Создать панель инструментов?
Для создания панели инструментов в 32-разрядной программе я бы предложил использовать элемент управления Toolbar, поставляемый с Visual Basic как часть Microsoft Windows Common Controls б.О (MSCOMCTLOCX). Чтобы разместить ня одной из таких панелей серию значков кнопок, придется добавить на форму и элемент управления imago! isi Он будет хранить набор изображений, на которые Вы ссылаетесь по индексу или ключу. Вы можете устанавливать его свойства, в частности добавлять изооражения, в диалоговом окне Property Pages. Открывается оно очень просто: выделив размещенный на форме элемент Toolbar или ImageEist, дважды щелкните в окне Properties свойство Custom. Диалоговое окно Property Pages для элемента Toolbar позволяет вставлять кнопки, добавлять изображения (значки) из элемента ImageEist, указывать имена ключей, определять подсказки и др. Диалоговое окно гэ < ■] ■<, i \ Pages для элемента ImageEist дает возможность добавлять изображения, указывать их
размер и цветовые параметры.
Хороший источник информации об использовании элементов управления Toolbar и ImageList — справочная система Visual Basic. А я приведу небольшой пример, чтобы показать, как пишется код для обработки щелчков кнопок.
■Option Explicit
iPrivate Sub tlbT8st_ButtonCllck(ByVal Button As ComctlLlb.Button)
'Select Case Button.Key
Case Is = "anchor"
MsgBox "You clicked the Anchor button."
Case Is = "umbrella"
MsgBox "You clicked the Umbrella button."
Case Is = "phone"
MsgBox "You clicked the Phone button."
Case Is - "cherry"
MsgBox "You clicked the Cherry button."
Case Is = '"atom"
MsgBox "You clicked the Atom button."
Case Is = "airplane"
MsgBox "You clicked the Airplane button."
End Select
End Sub
Взгляните на элемент Toolbar co значками из сопоставленного с ним элемента ImageList. Рис. 13-3 иллюстрирует результат «нажатия» кнопки Airplane.
Рис. 13-3 Элемент управления Тоо1Ьагпосле щелчка ' кнопки Airplane
Если Вы хотите добиться большей гибкости или если Вы работаете Б одной из прежних, 16-разрядных версий Visual Basic, попробуйте имитировать панель инструментов, скомбинировав элементы управления PictureBox, Image и, возможно, SSPanel. Добавьте на форму элемент PictureBox, установите его свойство Align как 1 - Align Тор и расставьте кнопки внутри этого элемента. Значение 1 - Align Тор заставит его вытянуться по верхней строке формы (даже при изменении ее размеров).
Вероятно, в свойстве BackColor элемента PictureBox следует указать светло-серый цвет (если он еще не установлен). Попробуйте также поварьировать значения свойства BorderStyle — это может
улучшить внешний вид «панели инструментов». Ну и, разумеется, закрепите за каждой кнопкой подсказку, как описано в главе 17 «Справочные системы, подсказки и мастера».
J^j1 См. также...
Раздел «Дорогой Джон, как... добавить строку состояния в программу?» в главе 17 «Справочные- истомы, подсказки и мастера».
Раздел«Дорогой Джон, как... добавить подсказки по интерфейсным объектам?» в главе 17 «Справочные системы, подсказки и мастера».
Приложение Dialogs в главе 34«Сложные приложения», где тоже используется панель инструментов.
Дорогой Джон, как...
Динамически модифицировать форму?
Тут все просто. Б период выполнения можно изменить свойство Тор или Teft элемента управления так, чтобы убрать его с видимой поверхности контейнера — формы или другого элемента управления, (Надежнее сдвинуть элемент на расстояние, в 2 раза большее ширины или высоты контейнера.) Если Вы сохраните исходную позицию элемента в его свойстве Tag, то впоследствии легко вернете этот элемент на прежнее место, Вот пример, в котором кнопка cmdButton скрывается после ее «нажатия» и вновь появляется после щелчка формы:
Option Explicit.
Private Sub cndButton ClickO-
cmdButton.Tag = cmdButton. Left■
cmdButton.Left = Screen-Width * 2
End Sub
Private Sub Form_CLick().
cmdButton.Left = cmdButton.Tag
End Sub
Вы можете позиционировать и масштабировать элементы управления методом Move. При этом Вы используете свойства EcaleWidth и ScaleHeight контейнера. Такой прием позволяет работать с масштабируемой формой, сохраняя расположение элементов оправления относительно друг друга при изменении ее размеров. Код, необходимый для реализации этой задачи, поместите в обработчик события Resize формы.
VD См. также..."
Раздел «Дорогой Джон, как... автоматически позиционировать форму на экране?» в главе ! ' «Окна, диалоговые окна и прочие формы» — о размещении форм.
Раздел «Дорогой Джон, как... использовать элементы управления
'набор вкладок’ и 'набор ярлычков'? - в главе I. 2 «Окна, диалоговые окна и прочие формы» — о создании динамичных форм на основе элементов Tab Strip и SSTab.
Дорогой Джон, как..«
Динамически настраивать меню?
Visual Basic позволяет редактировать объекты меню в окне Properties так же, как свойства других элементов управления. Многие об этой возможности даже и не подозревают, а ведь она значительно облегчает управление меню. Разумеется, исходное меню все равно надо создать в Menu Editor. Чтобы добраться до свойств какого-либо меню, выберите нужное в верхнем списке окна Properties.
ПРИМЕЧАНИЕ Еще один способ — использовать массивы объектов меню, что дает возможность модифицировать меню в период выполнения. Этот способ исчерпывающе описан в документации Visual Basic.
Вот несложный, но малоизвестный трюк, на котором построен прием, упрощающий создание нескольких наборов одного и того же меню. Если установить свойство Visible меню как Mse то меню и все его подменю будут скрыты — как в период выполнения, так и на этапе разработки. А значит, Вы вправе создать, например, 2 -. ию File с разными наборами команд, для чего первому меню присвойте имя nmuFilel, а второму — nmuFile2. Названия обоих меню могут совпадать (File, в данном случае), но значения их свойств Name должны быть уникальны. Наборы элементов этих меню могут совершенно различаться или частично совпадать — главное, чтобы значения свойств Name каждого их элемента тоже были уникальны.
Теперь, в период выполнения или на этапе разработки, переключайте значения свойств Visible этих двух меню, чтобы одно было видимо, другое — скрыто. Таким способом можно сформировать даже не два, а несколько меню File и, модифицируя .значения их свойств Visible, показывать на форме лишь нужное в данный момент. Кстати, Вас абсолютно не должны волновать значения свойств Visible элементов этих меню: если меню невидимо, невидимы и все его элементы.
Эту концепцию можно распространить на всю систему меню. В зависимости от текущего состояния программы Вы показываете или скрываете меню, их элементы и даже заменяете целые наборы меню. Например, на рис. 1 ■ |и 13-5 показаны 2 уникальных набора меню, поочередно появляющихся на форме при -нажатии» кнопки Toggle Menus (переключить меню).
Рис. 13-4 Так выглядит форма с первым из двух уникальных наборов меню
Рис. 13-5 А так — со вторым
Дорогой Пжгш. илис...
Удалить строку заголовка с формы?
Прежде чем удалить строку заголовка с формы на этапе разработки, надо убрать все элементы меню. Потом установить свойство ControlBox как False, удалить текст из свойства Caption и присвоить свойству BorderStyle значение 1 - / :A.;i Single — получится форма с одинарной рамкой и без заголовка. Или просто установить свойство BorderStyle как О - None. Тогда у формы не будет ни рамки, ни заголовка — даже если какие-то из свойств ControlBox, Мах- Button и MinButton установлены как /о , а в свойство Caption записана строка текста. Если Вы хотите, чтобы и в этом случае у формы была рамка, нарисуйте ее сами.
К© См. также...
Раздел «Дорогой Джон, как... добавить подсказки по интерфейсным объектам7-' в главе 17 «Справочные системы, подсказки и мастера», где иллюстрируется второй способ удаления строки заголовка с формы... < .
Дорогой Джон, как.... .
Создать индикатор прогресса?"
Чтобы научиться работать с элементом управления ProgressBar (индикатор прогресса), включенным в MicrosoftWindows Common Cont-
rols 6.0 (MSCOMCTL.OCX), лучше всего написать простую программу с этим элементом и проверить, на что он способен. Вот пример, в котором создается трехминутный таймер и используется элемент ProgressBar, — пригодится для варки яиц.
Добавьте на новую форму метку IblState, кнопку cmdStart. таймер tmrTest vt элемент ProgressBar с именем^Л^. Запишите в свойство Caption метки , ■ >■ n-д Ready to start к выберите нужный Вам шрифт. Свойству Enabled таймера присвойте False, а его же свойству г nerval — значение 1000 мс. Свойства элемента ProgressBar я не трогал.
Введите в модуль формы код:
Option Explicit'
Private msnpStartTime As Single
Private Const intSeconds As Integer = 180
Private Sub cmdStart_Click()
prgEgg. Value = 0
msngStartTime = 0
tmrTest.Enabled = True
End Sub
Private Sub tmrTest ТттнгИ
Dim sngPercent As Single
Il msngStartTime = 0! Then msngStartTime = Timer End It
sngPercent = 100 * (Timer - msngStartTime) / intSeconds
It sngPercent < 100 Then
prgEgg.Value = sngPercent
IblState.Caption = ’’Cooking..."
Else
prgEgg.Value = 100
IblState.Caption = "Done!"
Beep
tmrTest.Enabled = False
prgEgg.Value = 0!
Bad If End Sub
Рис. 13-6 иллюстрирует форму в период выполнения, когда яйцо сварилось на две трети.
Вероятно, при тестировании Вы временно замените делитель 180 (столько секунд в 3 минутах) на какое-нибудь число поменьше, скажем 10, но я бы не советовал есть яйцо, варившееся всего 10 секунд.
Вы многое узнаете об элементе управления ProgressBar, экспериментируя с этой программой. Например, количество «кирпичи-
ков» на полоске индикатора регулируется изменением ее высоты — эффект, и который не поверишь, пока не проверишь.
w'.IfHiHJ WBris^TMititf)
iinrmimni—
LOT"
Рис. 13-6 Трехминутный таймер — индикатор прогресса в действии
Разработка собственного индикатора прогресса'
ProgressBar — еще один 32-разрядный элемент управления, рассчитанный лишь на приложения для Windows 95 и Windows NT. Но не отчаивайтесь, если Вы до сих пор разрабатываете 16-разрядные программы. Используя пару вложенных элементов PicturcBox, легко создать собственный индикатор прогресса.
Слегка модифицируйте предыдущий пример. Замените ProgressBar на PictureBox с именемр/cProgress. Нарисуйте второй PictureBox (picFill) внутри первого. Задайте нужные значения свойств Back- Color обоих элементов. Я предпочел темно-синий цвет дляpicFill и белый дляpicProgress. Не нравится — подберите любые другие цвета. Если Вы установите свойство ScaleWidth элемента picProgress как 100, а его свойство ScaleProperty — как 1. то в тексте программы почти ничего менять не придется. Вот новый вариант кода: Option Explicit
Private msngStartTime As Single
Private Const intSeconds As Integer - 180
Private Sub cmoStart_Click()’■ •
msngStartTime = 0."
tmrTest.Enabled = True
End Sub_
.Private Sub Form_Load() ..-
picFill.Move 0, 0, 0, 0 '■' ’ "■''
End Sub
Private Sub tmrTest_TiiiierO
Dim sngPercent As Single.
It msngStartTime = 0! Then r-
msngStartTime = Timer
End If
sngPercent = 100 * (Timer - msngStartTime) / intSeconds
If sngPercent < 100 Then. .
picFlll.Move О, 0, sngPercent, 1
IblState. Caption = "Cooking..."
Else
IblState.Caption = "Done!"
Beep
tmrTast.Enabled = False
picFlll.Move О, О, О, О
End If
End Sub
В этом индикаторе прогресса заполнение вдет ровной полоской, а не «кирпичиками*. но в остальном он ничем не отличается от элемента ProgressBar.
Рис. 13-7 иллюстрирует работу программы «для варки яиц» с новым индикатором прогресса.
Рис. 13-7 Так выглядит наш доморощенный индикатор прогресса
Допг>г^й Пжон. как».
Использовать элемент управления Slider?
Элемент управления Slider (ползунок) входит в MSCOMCTL.OCX. Во многом напоминая полосу прокрутки, он гораздо удобнее для ввода цифровых значений, выбираемых из заданного диапазона. В полосы прокрутки заложен чисто качественный подход к выбору значений из диапазона с наглядной, но весьма приблизительной обратной связью, а в ползунки — количественный подход, обеспечивающий выбор точного значения или поддиапазона из определенного диапазона.
Интересная и уникальная особенность элемента управления Slider — возможность выбора не только единствен пои-, значения, но и некоего поддиапазона значений. В последнем случае свойство SelectRange устанавливается как True, а поддиапазон определяется через свойства SelStart и SelLength Microsoft предлагает разработчикам программно устанавливать свойства поддиапазона, когда пользователь перемещает ползунок, удерживая клавишу Shift. Эта методика хорошо проиллюстрирована в Visual Basic Books Online.
Рис. ; • изображает гипотетическое рнложенп- реализующее поиск в базе данных по определенным критериям, Из огромного списка пользователь выбирает города, расположенные в некоем
диапазоне широт. Элемент управления Slider ; . .. нельзя лучше подходит для таких задач. В то же время он удобен и для выбора единственного значения, например для эсгглпровки громкости звука. Свойства и методы элемента Slider отлично описаны в справочной системе, и я отсылаю Вас к ней.
.: *ЦиКМ«!<*1*езМM<Wnw4Vijte« WU .0
Рис. 13-8 Элемент управления Slider, используемый для выбора поддиапазона значений
Использовать элемент управления UpDown?
UpDown (наборный счетчик) ~ стандартный элемент управления в Windows 95 и Windows NT, упрощающий изменение числового значения в связанном элементе управления. UpDown содержится в Microsoft Windows Common Controls-2 6.0 (MSCOMCT2.OCX) Взгляните на рис. 13-0. где показано текстовое поле, связанное с элементом UpDown.
Рис. 13-9 Элемент управления UpDown, используемый для изменения числового значения в текстовом поле
UpDown заменил одну из функций полосы прокрутки — изменение значения в связанном текстовом поле. Раньше такая задача потребовала бы написания дополнительного кода, а теперь, при использовании элемента UpDown, достаточно установить несколько параметров в его диалоговом окне Property Pages на вкладке Buddy (связанный элемент) (рис. 13-Ю).
Чтобы связать UpDown с другим элементом управления сделайте следующее.
Добавьте элемент управления UpDown на панель Toolbox и нарисуйте на форме этот элемент и тот, который хотите с ним связать.
Выделите элемент UpDown и в окне Properties дважды щелкните свойство Custom.
В диалоговом окне Property Pages выберите вкладку Buddy.
В поле Buddy Control (связанный элемент управления) введите имя элемента, связываемого с UpDown. В качестве альтернативы можно пометить флажок AutoBuddy (автоматически связывать элемент управления), тогда UpDown выберет на роль своего «приятеля» (buddy) предшествующий ему в Tab-порядке ’ эле- ментуправления.
В списке Buddy Property (свойство связанного элемента) укажите свойство целевого элемента, на которое Вы хотите воздействовать. Обычно выбирается Default или Value. Заметьте, что после этого автоматически устанавливается флажок SyncBuddy (синхронизировать со связанным элементом), который сообщает, что свойство Value элемента UpDown теперь синхронизируется с выбранным свойством связанного элемента управления.
Щелкните кнопку ОК.■
Hfr&M fcdT
Рис. 13-Ю Связывание элемента управления UpDown с другим элементом управления
В период выполнения Вы увидите, что значение в связанном текстовом поле увеличивается или уменьшается на единицу при каждом щелчке верхней или нижней кнопки элемента управления UpDown. Но не исключено, что сначала придется указать начальное значение в связанном элементе управления, так как, например, в текстовом поле по умолчанию появляется строка Тс«Н и остается в нем, пока Вы не щелкнете элемент UpDown.
Чтобы задать диапазон счетчика и шаг его изменения, переключитесь на вкладку Scrolling (прокрутка) диалогового окна Property Pages элемента UpDown (рис. 13-11). Если флажок Wrap (Крутовой набор) сброшен, то счетчик, дойдя до максимального или минимального значения, перестает реагировать на щелчки соответствующей кнопки. А установка флажка Wrap позволяет набирать значе-
Прим. персв.: Здесь имеется в виду порядок переключения между элементами управления при нажатии клавиши Тab.
ния как бы по кругу. Иными словами, как только Вы установите максимальное значение счетчика и еще раз щелкнете верхнюю кнопку, его значение будет сброшено до минимального, и наоборот.
Рис. 13-11 Определение диапазона наборного счетчика и шага его изменения
См. также...
Пример ActiveX-элемента Blinker в главе 6« ActiveX-элементы», гдепоказано одно из применений элемента управления UpDown.
Дорогой Джон, как...
Использовать элемент управления FlatScrollBar?
FlatScrollBar (плоская полоса прокрутки) — усовершенствованная версия элементов управления IIScrollBar и VScrollBar, которая позволяет довести внешний вид приложений, написанных на Visual Basic, до уровня Internet Explorer. Свойство Orientation дает возможность размещать FlatScrollBar как горизонтально, так и вертикально, поэтому отдельные элементы вроде HScrollBar и VScrollBar не требуются.
Новые свойства обеспечивают больший контроль над внешним видом полосы прокрутки этого типа. Как я уже говорил, свойство Orientation определяет положение FlatScrollBar (горизонтальное или вертикальное). Свойство Appearance задает внешний вид полосы прокрутки — плоский, как в Internet Explorer, объемный, как у обычных полос прокрутки, или особый двухмерный, когда стрелки становятся объемными только при прохождении над ними курсора мыши. А свойство Arrows разрешает избирательно отключать стрелки на концах полосы прокрутки.
Лучший способ разобраться в том, какдействуют эти свойства, — поэкспериментировать с ними и понаблюдать за ч ем, что происходит при изменении их значений. А потому откройте «свсж\ г ■ ■ формуй вставьте на нее элемент FlatScrollBar. (Если этого элемента на панели Toolbox нет, выберите из меню Project команду Components
и в одноименном диалоговом окне установите флажок в строке Microsoft Windows Common Controls-2 6.0.) Добавьте на форму 3 кнопки (cmdOrientation, cmdAppearancew cmdArrotvs)^ присвойте им названия, как на рис. 13-12. Затем вставьте метку IblState — она будет отображать текущие значения исследуемых свойств. И, наконец, свяжите все элементы управления кодом, приведенным ниже.
'a* eNd^t ЙЛА* №'a* eNd^t ЙЛА* №Рис. 13-12 Форма для демонстрации элемента управленияFlaiScrollBarРис. 13-12 Форма для демонстрации элемента управленияFlaiScrollBar“™—~——“™—~——1 №Ь» Гг^^Л V^i^tW О' ^ :.'.-^бйт^ рогпк ct *1 Mt-stim т» • '1 №Ь» Гг^^Л V^i^tW О' ^ :.'.-^бйт^ рогпк ct *1 Mt-stim т» • '33
'a* eNd^t ЙЛА* №
'a* eNd^t ЙЛА* №
Рис. 13-12 Форма для демонстрации элемента управления
FlaiScrollBar
Рис. 13-12 Форма для демонстрации элемента управления
FlaiScrollBar
“™—~——
“™—~——
1 №Ь» Гг^^Л V^i^tW О' ^ :.
'.-^бйт^ рогпк ct *1 Mt-stim т» • '
1 №Ь» Гг^^Л V^i^tW О' ^ :.
'.-^бйт^ рогпк ct *1 Mt-stim т» • '
3
3
Option Explicit
Private Sub cmdAppearance_Click()
fsbDemo.Appearance = (fsbDemo,Appearance + 1) Mod 3
Display
End Sub
Private Sub cmdArrows_Click()
fsbDemo.Arrows = (fsbDemo.Arrows+ 1) Mod 3
Display
End Sub
Private Sub cmdOrientation_Click()
fsbDemo.Orientation = (fsbDemo.Orientation+ 1) Hod 2
Display
End Sub‘
Private Sub Display()
IblState.Caption = _
"Orientation:" & fsbDemo.Orientation & vbCrlf & "Appearance:" & fsbDemo.Appearance & vbCrlf & _ "Arrows:” & fsbDemo.Arrows
End Sub
Private Sub Form_Load()
Display
End Sub
Запустите программу и, щелкая кнопки, проследите, какдейству- ет каждое значение свойств Orientation, Appearance и Arrows элемента управления FlatScrollBar. Например, рис. 15-15 иллюстрирует внешний вид FlatScrollBar сразу после появления формы на экране, а рис. 13-14 — после щелчка кнопки Orientation.
Рис. 13-13 Элемент управления FlatScrollBar со значениями свойств по умолчанию
йроовы* 1
Рис. 13-14 Элемент управления FlatScrollBar после щелчка кнопки Orientation
Работая с этой программой, Вы могли не заметить разницу между значениями 1 и 2 свойства Appearance — элемент FlatScrollBar в обоих случаях выглядит плоским. Разница станет очевидной, только когда Вы «проведете» курсором мыши над стрелками полосы прокрутки. При значении свойства Appearance, равном 1, ничего особенного не произойдет, но изменив его на 2. Вы увидите, что стрелки как бы всплывают, приобретая объемный вид.
Допппзй 1Вжои. как...
Использовать элемент управления CoolBar?
CoolBar имитирует поведение панелей инструментов в Internet Explorer. Этот элемент управления — контейнер набора объектов Band, каждый из которых сам является небольшой панелью инструментов. Любой объект Band в период выполнения можно перемещать, таская его за левый край. Если Вы никогда не переупорядочивали эти объекты в Internet Explorer, советую попробовать прямо сейчас — тогда Вам будет легче понять, как работает новый элемент управления CoolBar. Обратите внимание на рис. I ' I б и I '■ I' > переупорядочив меню и панели инструментов (объекты Band) в Internet Explorer, я кардинально изменил внешний вид своего браузера.
F^fdiilce йсь
■> i£ ti5 ^' Я
**£h fooHei Hlilary CheinatiH fi*cean
hew е» №• fof«*»r>hew е» №• fof«*»r>-Tl»p iCteri*-Tl»p iCteri*Welcome!Welcome!WiM»O*B|
hew е» №• fof«*»r>
hew е» №• fof«*»r>
-Tl»p iCteri*
-Tl»p iCteri*
Welcome!
Welcome!
This рада i£ эГ! introduction tt Internet Start, vojr gateway td Internet- Tt 1^111 appe-зг only once on your screen»|
be?*be?*fr?W ^6?jrd Hw w-^fpfr?W ^6?jrd Hw w-^fpthe internet start, (m^b rortims etarpthiriq that riibit рвор^ I warn *d tin on the MtEmet, indudinQ sLjfn^ 3rd seiir±itiij #оч
be?*
be?*
fr?W ^6?jrd Hw w-^fp
fr?W ^6?jrd Hw w-^fp
^L-
Рис. 13-15 Меню и панели инструментов (объекты Band) в Internet Explorer до переупорядочения
Элемент управления CoolBar обладает множеством свойств, которые обеспечивают широкие возможности по его настройке. Так, CoolBar допускает добавление любого разумного количества объектов Band, а свойство Oriciii ai i< ч позволяет показывать все эти объекты как в стандартном горизонтальном расположении, так и в нестандартном вертикальном. Объекты Band идентифицируются значением их свойства Index, но имеют и свойство Position, которое автоматически отслеживаем порядок объектов Band внутри CoolBar при их перемещении пользователем. Свойство Caption обеспечивает вывод текста — он показывается справа от полоски, за которую Вы перемещаете всю панель. пиисыиаи, эти свойства можно еще долго, но лучший способ понять, как они работают. — поэкспериментировать с элементом CoolBar.
fTSSSv?;Ei- JifTSSSv?;Ei- Ji■•ч*' [j- Ffr«»TiitT Ь*<г.■•ч*' [j- Ffr«»TiitT Ь*<г.
fTSSS
v?;Ei- Ji
fTSSS
v?;Ei- Ji
■•ч*' [j- Ffr«»TiitT Ь*<г.
■•ч*' [j- Ffr«»TiitT Ь*<г.
Welcome’
rus page 15 an intraductiDn to Internet Start, vourgatewavtcl Internet Lt will aypaar only once nn your screen.I
"ne internet atari pegs contains everything that mastpeople I wjntta do ar the Internet indudirta^urring ana sesrenmg fori beftsires rr^m arouna the world, vqlI' Start page distills rr@vJ Irteme^dowr, to ore simple caqa thatteu canpets^rw- to] ггя^т ?c_r ii^scs'
Рис. 13-16 Меню и панели инструментов (объекты Band) в Internet Explorer после переупорядочения
Кроме того, есть свойства, цозш ыяюшис выводить мозаичное фоновое изображение либо на CoolBar, либо на каждом объекте Band. Например, в некоторых версиях Internet Explorer на фоне объектов Band, содержащих меню и панели инструментов, показывался весьма интересный, но едва заметный светло-серый узор. И такое оформление тоже можно имитировать с помощью элемента управления СооШаг.
ГЛАВАГЛАВА1 41 4
ГЛАВА
ГЛАВА
1 4
1 4
Графические методы
V isu.tl Basic предоставляет богатый набор средств для работы с графикой, в том числе метод PaintPicture — «дружественную» оболочку часто используемой API-функции BitBlt. Вы не раз встретитесь с ним в данной главе, потому что это настоящая «рабочая лошадка», помогающая эффективно обрабатывать графические изображения. Иногда я вызываю BitBlt прямую, так как это ускоряет анимацию. Здесь же продемонстрирую и несколько других, очень полезных API-функций, включая те, что позволяют выделять прямоугольные области «резиновым» контуром, быстро рисовать многоугольники и вообще устраивать всяческие фокусы с изображениями. При этом API-функции зачастую лучше инкапсулировать в тот или иной объект, что я и делаю практически везде.
Доеог^й Джон» как...
Вычислить «цветовую» константу по RGB-, HSV- или HSL-значениям?
В Visual I' цвет указывается единственным числом — его RGB- значением. Битовая матрица этого числа образуется из трех величин, соответствующих уровням интенсивности цветов: красного, зеленого и синего (отсюда и появился акроним RGB: Red, Green, Blue). Каждая такая величина укладывается в диапазон от О до 255. Функция RGB позволяет комбинировать их в одно число, определяющее нужный цвет, но встроенных функций, которые извлекали бы из заданного RGB-значения цвета три его составляющие, нет. Поэтому я решил создать класс для решения такой задачи. Введите код, показанный ниже, в модуль класса и присвойте классу имя RGB.
' RGB. CIS
Option Explicit
Private mlngColor As Long
" Color (свойство)
Property Let ColorQngColor As Long)
mlngColor = IngColor
End Property.
' Red (свойство)
Property Get Red() As Byte
Red = mlngColor And &HFF’
End Property-
' Greer (свойство)
Property Get GreenO As Byte,
Green = (mlngColor \ 4H100) And &HFF-.
End Property
' Blue (свойство)".
Property Get Bluet) As Byte' .
Blue = (mlngColor \ 4H10000) And &HFF
•End Property
Чтобы не усложнять этот класс, я сделал свойство Color только для записи, а свойства, связанные стремя составляющими цвета, — только для чтения. Хотите поупражняться, определите все свойства и для чтения, и для записи.
Следующий код демонстрирует, как использовать RGB-объект для извлечения красной, зеленой и синей составляющих из RGB- значения цвета:
Option Explicit
Private Sub Form_Click(). ,, ,
Dim 'UbDerso As New RGB
Dim mlngColor As Long•
1 комбинируем R, G и В в значение цвета
mlngColor = RGB (17, 53, 220)
Print "’Color:", mlngColor
' используем RGB-объект для извлечения компонентов
rgbDemo.Color = mlngColor-^
Print "Red:"', rgbDemo.Red ---
Print "Green:", rgbDemo.Green
Print "Blue:", rgbDemo.Blue
End Sub- ,
Кроме RGB-модели, широко распространена классификация цветов по оттенку (hue), насыщенности (saturation) и интенсивности (value) — HSV, а также по оттенку (hue), насыщенности (saturation) и яркости (luminosity) нч Кому-то эти модели кажутся более понятными, и в некоторых случаях с HSV- или 1 [SL-значениями цветов работать действительно легче. Например, закат солнца проще всего выразить набором красных цветов разной интенсивности, но постоянных по оттенку и насыщенности. Оттенок определяет относительную позицию цвета Б спектре и в системе HSV соответствует угловому смещению для данного цвета на цветовом колесе» (color wheel). Оттенок принимает значения от О до 360. Насыщенность определяет чистоту цвета и выражается в процен-
тах от 0 (нет i гнета! до 100 (чистый цвет, заданный числовым значением оттенка). Интенсивность — яркость цвета, выраженная в процентах от 0 (черный) до 100 (белый).
Вот класс, полезный для преобразования значений цвета из RGB в HSV, и наоборот;
HSV.CLS
Option Explicit-,
' свойства для RGB-цвета"•
Private mintRed As Integer<
Private mintGreen As Integer-.
Private mlntBlue As Integer
' свойства для HSV-цвета
Private msngHue As Single' ' "
Private msngSaturation As Single■
Private msngValue As Single
' для управления преобразованиями-
Private mintCalc As Integer...
Private Const RGB2HSV = 1,.
Private Const HSV2RGB = 2
' ""“Hue, ‘ 1
Property Let Hue(intHue As Integer)“ v
msngHue = intHue.11
mintCalc = HSV2RGB"
End Property
Property Get Hue() As Integer"1
If mintCalc = RGB2HSV Then CalcHSV
Hue = msngHue
End Property
' Saturation■
Property Let Saturation(intSaturation As Integer)
msngSaturation = IntSaturation
mintCalc = HSV2RGB
End Property-
Property Get Saturation As Integerl p
If mintCalc = RGB2HSV Then CalcHSV ....
Saturation = msngSaturation'
End Property
' Value
Property Let ValueflntValue As Integer)
msngValue = intValue
mintCalc = HSV2RGB
End Property
Property Get Value() As Integer
If mintCalc = RGB2HSV Then CalcHSV Value = mangValue
End Property
1 —Red
iProperty Let Red(intRed As Integer), mintRed = intRed mintCalc = RGB2HSV
End Property
Property Get Red() As Integer
If mintCalc = HSV2RGB Then CalcRGB Red = mintRed
End Property
' Green
Property Let Green(intGreen As Integer) mlntGreen = intGreen mintCalc = RGB2HSV
End Property
Property Get Green() As Integer
If mintCalc = HSV2RGB Then CalcRGB Green = mlntGreen
End Property
""'Blue
Property Let Biue(intBiue As Integer) mintBlue = intBlue mintCalc = HGB2HSV
End Property
Property Get Blue() As Integer
If mintCalc = HSV2RGB Then CalcRGB Blue = mintBlue
End Property
• преобразует RGB в HSV
Private
Sub CalcHSVO
Dim
sngRed As Single
Dim
sngGreen
As Single
Dim
sngBlue ,
As Single
Dim
sngHx As
Single
Dim
sngNn As
Single
Dim
sngDelta
As Single
Dim
sngVa As
Single
Dim
sngSa As
Single
Dim
sngRc As
Single
Dim
sngGc As
Single
Dim
sngBc As
Single
sngRed = mintRed / 255
sngGreen = mintGreen / 255"
sngBlue = mintBlue / 255•■;,,
snghx = sngRed
If sngGreen > sngMx Then sngHx = sngGreen If sngBlue > sngMx Then sngMx = sngBlue
sngMn = sngRed
If sngGreen < sngMn Then sngMn = sngGreen If sngBlue < sngMn Then sngMn = sngBlue sngDelta = sngMx - sngMn
sngVa = sngHx
If sngMx Then
sngSa = sngDelta / sngHx ,>„ ,-..
Else,
sngSa = 0''
End If:" -
If sngSa = 0 Then ■
msngHue = 0
Else
sngRc = (sngHx - sngRed) / sngDelta sngGc = (sngMx - sngGreen) / sngDelta sngBc = (sngMx - sngBlue) / sngDelta Select Case sngHx
Case sngRed
msngHue = sngBc - sngGc
Case sngGreen
msngHue = 2 + sngRc - sngBc
Case sngBlue
msngHue = 4 + sngGc - sngRc
End Select
msngHue = msngHue * 60
If msngHue < 0 Then msngHue = msngHue + 360 Bad If
imsngSaturation = sngSa * 100
msngValue = sngVa * 100
mintCalc = 0■
End Sub
преобразует HSV в RGB
Private Sub CalcflGBO
Dim sngSaturation As Single
Dim sngValue As Single
Dim sngHue As Single
'Dim inti As Integer
Dim sngF As Single
Dim sngP as Single
Dim'sngQ As Single
Dim sngT As Single
Dim sngRed As Single
Dim sngGreen As Single
Dim sngBlue As Single
sngSaturation = msngSaturation / 100
sngValue = msngValue / 100
If msngSaturation = 0 Then
sngRed = sngValue
sngGreen=sngValue
sngBlue = sngValue
Else
sngHue = msngHue /60
Il sngHue = 6 Then sngHue = 0
inti = Int(sngHue)
sngF =sngHue -inti
sngP =sngValue*(1!-sngSaturation)
sngQ =sngValue*(1!-(sngSaturation*sngF))
sngT =sngValue*(1!-(sngSaturation*(1!-sngF)))
Select Case inti
Case 0
sngRed = sngValue
sngGreen = sngT
sngBlue = sngP .
Case 1
sngRed = sngQ.-
sngGreen = sngValue
sngBlue = sngP ',
Case 2
sngRed = sngP
sngGreen = sngValue
sngBlue = sngT■,
Case 3-'
.sngRed = sngP■
sngGreen ■ sngQ ,■. ,
sngBlue=sngValue
. Case 4
sngRed = sngT
sngGreen = sngP■
sngBlue= sngValue
Casa 5
sngRed = sngValue
sngGreen = sngP■'
sngBlue - sngQ■-
End Select■■
End II
mintHed = Int(255.9999 * sngRed)-
mintGreen = Int(255.9999 * sngGreen)'■
mintBlue = Int(255.9999 * sngBlue)
mintCalc = 0
End Sub
Модель HST применяется в программе Microsoft Paint для создания пользовательских цветов из полной палитры. Она очень похожа на модель ।' f. но яркость в ней обрабатывается несколько иначе, чем интенсивность в HSV. Вот как выглядит класс для преобразования значений цвета из RGB в HSL, и наоборот:
'HSL.CLS
Option Explicit
’ свойства для RGB-цвета'
Private mintRed As Integer
Private mintGreen As Integer ,_.,].,
Private .nintBlue As Integer
1 свойства для HSL-цвета.
Private '-siiunue As Single
Private msngSaturation As Single,
Private msngLuminosity As Single •'
1 для управления преобразованиями
Private mintCalc As Integer'■.
Private Const RGB2HSL = 1
Private Const HSL2RGB = 2
Hue_,■
Property Let HuefintHue As Integer)
msngHue = (intHue / 240!) * 3601
mintCalc = HSL2RGB■
End Property
Property Get Hue() As Integer■
If mintCalc = RGB2HSL Then CalcHSL
Hue = ГтяпдНие/ 360!) * 240!
End Property■ ■
' Saturation
Property Let Saturation(ihtSaturation As Integer)
msngSaturation = intSaturation / 240!
mintCalc = HSL2RGB, „.-.._
End Property' ".
Property Get Saturation() As Integer ...
If mintCalc = RGB2HSL Then CalcHSL ' '
Saturation = msngSaturation * 240!
End Property
' Luminosity
Property Let Luminosity(intLuminosity As Integer)
mangLuminosity = intLuminosity / 240!
mintCalc = HSL2RGB
End Property
Property Get Luminoslty() As Integer
If mintCalc = RGB2HSL Then CalcHSL
Luminosity = msngLuminoslty * 240!
End Property
' Red
Property Let Red(lntRed As Integer)
mintRed = intRed
mintCalc = RGB2HSL
End Property
Property Get Red() As Integer
If mintCalc = HSL2RGB Then CalcRGB
Red = mintRed■
End Property
' Green.
Property Let Green(intoreen As Integer) .
mintGreen = IntGreen-
mintCalc = RGB2HSL
End Property
Property Get Green: as Integer
If mintCalc = HSL2RGB Then CalcRGB
Green = mintGreen.
'End Property
' ""Blue'
Property Let Blue(intBlue As Integer)
mintBlue = intBlue
mintCalc = RGB2HSL
End Property
Property Get Blue() As Integer
If mintCalc ■■ HSL2RGB Then CalcRGB
Blue mintBlue
End Property
Private Sub CalcHSLO■
Dim sngMx As Single.
Dim sngHn As Single
Dim sngDelta As Single'
Dim sngPctRed As Single'
Dim snyrcT.j.-ri gs single
Dim sngPctBlu As Single
sngPctRed = mintRed / 255
sngPctGrn = mintGreen / 255
sngPctBlu = mintBlue / 255
sngMx = sngMaxOf(sngMaxOf(sngPctRed, sngPctGrn), sngPctBlu) sngMn = sngMinOf(sngMlnOf(sngPctRed, sngPctGrn), sngPctBlu) sngDelta = sngMx - sngMn
msngLuminoslty = (sngMx + sngMn) / 2
If sngMx = sngMn Then■
msngSaturation = 0
Else-'.
msngSaturation = 1
End If
If msngLuminosity <= 0.5 Then
If msngSaturation > 0 Then msngSaturation = sngDelta / (sngMx + sngMn)
End If
Else
If msngSaturation > 0 Then msngSaturation = sngDelta / (2 - sngMx - sngMn) End If
End If"■,
If msngSaturation Then
If sngPctRed = sngMx Then msngHue = (sngPctGrn - sngPctBlu) / sngDelta
End If
If sngPctGrn = sngMx Then
ntsngnue = 2 + (sngPctBlu - sngPctRed) / sngDelta Bad If
If sngPctBlu = sngMx Then
msngHue = 4 + (sngPctRed - sngPctGrn) / sngDelta Bid If
msngHue = msngHue * 60
Bid If
If msngHue < 0 Then msngHue = msngHue + 360 mintCalc = 0
End Sub1
Private Sub CalcRGBQ
Dim sngM1 As Single
Dim sngM2 As Single-
Dim sngPctRed As Single
Dim sngPctGrn As Single
Dim sngPctBlu As Single
If msnghuminosity 0.5 Then
sngM2 = msnghuminosity * (1! ; msngSaturation) Else
sngM2 = (msnghuminosity msngSaturation) _ - (msnghuminosity * msngSaturation).
End If
sngMi = 2! * msnghuminosity - sngM2
If msngSaturation = 0! Then sngPctRed ■ msnghuminosity sngPctGrn = msnghuminosity SngPCtBlU = H.o.^Lum^.joiL,
Else
sngPctRed = rgbVal(sngM1. sngM2, msngHue + 120!)
- sngPctGrn = rgbVal(sngM1, sngM2, msngHue)
sngPctBlu = rgbVal(sngM1, sngM2, msngHue - 1201)
End If
mintRed = Int(255.9999 * sngPctRed)'
mintGreen = Int(255.9999 * sngPctGrn)'
mintBlue = Int(255.9999 * sngPctBlu),
mintCalc = 0''
End Sub1
Private Function rgbVal(sngN1 As Single, sngN2 As Single, _
sngHue As Single) As Single■
If sngHue > 360 Then
sngHue = sngHue - 360."
Elself sngHue < 0 Then
sngHue = sngHue + 360■. '
End If- ■
If sngHue < 60 Then■
rgbVai = sngN1 + (sngN2 - sngN1) * sngHue / 60
Elself sngHue < 180 Then
rgbVai = sngN2
Elself sngHue < 240 Then
rgbVai = sngN1 + (sngN2 - sngN1) * (240 - sngHue) / 60
Else
rgbVai = sngN1
End If
End Function■
Private Function sngMaxOf(sngV1 As Single, sngV2 As Single) As Single
SngMaxOf = TIf(sngV1 > sngV2, sngV1, sngV2)
End Function"
Private Function sngMinOf(sngV1 As Single, sngV2 As Single) As Single sngMinOf = Hf(sngV1 < sngV2, sngV1, sngV2)
End Function
Чтобы задействовать класс HSV или HSL в своем приложении, создайте его экземпляр и — в зависимости оттого, какие значения Вам известны, — установите либо свойства Red, Blue, либо Hue. Saturation, Value, либо Hue, Saturation, Luminosity. Потом считайте свойства, которые возвращают значения для другой цветовой модели. Использование HSV- и HSL-объектов иллюстрирует приложение HSVHSLb главе 29 ‘Трафика-.
А теперь наглядный пример; посмотрите на цвета, доступные в Microsoft Windows 95 и Microsoft Windows NT 4, Откройте Control Panel (Панель управления), дважды щелкните значок Display (Экран) и выберите в диалоговом окне Display Properties (Свойства: Экран) вкладку Appearance (Оформление). Щелкните кнопку Color (Цвет), которая находится справа внизу. В списке цветов щелкните кнопку
Other (Другие). В итоге появится диалоговое окно Color (Цвет), позволяющее выбрать любой цвет из возможных в данной системе и показывающее целочисленные значения для RGB и list Цветовая модель I Ы выбранная Microsoft, использует целочисленные значения в диапазоне от 0 до 240 — они применяются и в моем классе HSL.
Дорогой Джон, как...
Преобразовывать единицы измерения?
Форму, элемент управления PiciutcBox или объект Printer можно масштабировать через < войство ScaleMode, применяя собственные единицы измерения либо приближенное представление твипов, пунктов, пикселов, символов, дюймов, миллиметров или сантиметров. Я сказал «приближенное представлениепотому что для экрана компьютера эти единицы обычно аппроксимируются. Для объекта-принтера (при работе с высокоразрешающим принтером) единицы вычисляются гораздо точнее. Вот как связаны между собой некоторые единицы:
1 дюйм = 1440 гвипов.. .
1 см = 567 твипов '■
дюйм = 72 пт .■
дюйм= 2.54 см..
см = 10 мм■
Символ — особая единица, размерность которой по горизонтали отличается от размерности по вертикали:
символ i .Ф твипов по горизонтали
1 символ = 240 твипов по вертикали
Определить число твипов в пикселе для какого-либо объекта помогают два очень полезных свойства Visual Basic, показанных в таблице. Конечное значение зависит от выбранного Вами разрешения экрана. Количество пикселов по горизонтали может не совпадать с количеством пикселов по вертикали, и именно поэтому в Visual Basic предусмотрено два сходных свойства.
СвойствоВозвращаемое значение
TwipsPerPLxelXЧисло твипов в пикселе по горизонтали
TwipsPerPixelY Число твипов в пикселе по вертикали
Используя эти свойства и взаимосвязи, о которых я говорил, Вы легко преобразуете одни единицы измерения в другие.
ДорОГО**
Создать фон с плавным переходом из синего в черный?
Код. приведенный ниже, рисует фон на форме тонкими полосками оттенков синего — от светлого до темного. Самое сложное — до-
питься плавного перехода не только при 16- и 32-разрядном представлении цвета (т. е. в режимах High-color и True-color), но и в режиме 256 цветов. В последнем режиме плавный переход от светло-синего к черному требует upnMrinuiю так называемых "составных цветов (dithering colors). Метод Line в Visual Basic не позволяет прочерчивать прямые линии составным цветом, но разрешает заполнять таким цветом прямоугольники. При этом свойство DrawStyle формы надо установить как i -bliwisible, а ее же свойство ScaleMode — как ibPixeh, Свойство DrawStyle определяет стиль линий (значение ЫттаЬк- не дает обводить черным контуром каждый синий прямоугольник). Значение vhPixels свойства ScaleModc позволяет вычислять размеры прямоугольников в пикселах, избегая ошибок округления; благодаря этому прямоугольники располагаются точно один под другим — без зазоров или наложения. .
Option Explicit
Private Sub Form_Paint()" '
Dim IngY As Long
Dim IngScaleHeight As Long
Dim IngScaleWidth As Long■
ScaleMode - vbPixels
IngScaleHeight = ScaleHeight
ingscaiewiptn = ScaleWidth
DrawStyle = vblnvisible
FIHStyle = vbFSSolv’
For ingY = 0 To IngScaleHeight"
FillColor = RGB(0, 0, 255 - (IngY* 255) \ IngScaleHeight) Line (-1, IngY - 1)-(lngScaleWidth, IngY + 1), , В
Next tngr■
End Sub
Эта процедура заполняет форму любого размера оттенками синего. Для полноты впечатления растяните форму на весь экран, указав в ее свойстве BorderStyle значение О - None, , в свойстве WindowStatc — значение 2 - Maximized.
У Вас огромный простор для экспериментов. Вычисления FillColor можно модифицировать так, чтобы форма заполнялась оттенками, например, красного, а не синего. Или выводить цвета в обратном порядке: светло-синий внизу, черный сверху. Эффект перехода оттенков синего представлен на рис. 14-L
См. также..., .
Приложение Dialogs в главе 34 «Сложные приложения», где демонстрируется аналогичный переход фона.
Рис. 14-1 Форма с плавным переходом фона из синего в черныйРис. 14-1 Форма с плавным переходом фона из синего в черный
Рис. 14-1 Форма с плавным переходом фона из синего в черный
Рис. 14-1 Форма с плавным переходом фона из синего в черный
Дорогой Джон, как.»
Выделять прямоугольные области «резиновым» контуром?
Б этом деле поможет API-функция DrawFucusRect. Создайте форму, установите ее свойство AutoRedraw как True и winre код, показанный ниже. Запустите программу и, удерживая левую кнопку мыши, перемещайте ее в любом направлении. Вы увиди те прямоугольный контур, изменяющий свои размеры в соответствии с Вашими действиями. Кактолько Вы отпустите кнопку, размеры прямоугольника зафиксируются и его контур станет красным.
Option Explicit
Private Type RECT
Left As Long■
Top As Long■,
Right As Long Bottom As Long
End Type
Private Declare Function DrawFocusRect Lib "user32" ( _
ByVai hdc As Long, _
IpRect As RECT _ ) As Long
Private FocusRec As RECT Private sngX1 As Single Private sngY1 As Single Private sngX2 As Single Private sngY2 As Single
ожидаемые API-функциейожидаемые API-функциейPrivate Sub Form_Load(}
ожидаемые API-функцией
ожидаемые API-функцией
1 используем единицы,
Me.ScaleHode = vbPixels1
End Sub
'Private Sub Form_MouseDown( _
Button As Integer, _
Shift As Integer, _
X As Single,
Y As Single _
)
1 убедимся, что нажата именно левая кнопка мыши
If (Button And vbleftButton) = 0 Then Exit Sub'
1 задаем начальные координаты прямоугольника
sngXl = X
sngYl = Y■-
End Sub
Private Sub Form MnuReHove( _- - ■
Button As Integer, _■;
Shift As Integer, .
X As Single,1-
Y As Single _;
1 убедимся что нажата именно левая кнопка мыши
If (Button And vbLeftButton) = 0 Then Exit Sub.
' стираем прямоугольный контур, если он уже есть
If (sngX2 о 0) Or (sngY2 о 0) Then
DrawFocusRect He. hdc, FocusRec
End If
' обновляем координаты.
sngX2 = X
sngY2 = Y
1 обновляем прямоугольный контур.
FocusRec.Left = sngXl.
FocusRec.Top = sngYl
FocusRec.Right = sngX2
FocusRec.Bottom = sngY2
' подстройка при расширении в другом направлении
If sngY2 < sngYl Then Swap FocusRec.Top, FocusRec.Bottom
If sngX2 < sngXl Then Swap FocusRec.Left, FocusRec.Right
1 рисуем контур
DrawFocusRect Me.hdc, FocusRec '
RefreshIJ1* '•.
End Sub•.-• ■ A " ■
l( >мтг . ■•..
Private Sub '‘orm^HouseUpt _ i,r।■
Button As Integer, _.4;
Shift As Integer, _;.,, (.
X As Single, _,.
Y As Single _.
' убедимся, что нажата именно левая кнопка мыши If (Button And vbLeftButton) = 0 Then Exit Sub ' стираем прямоугольный контур, если он уже есть If FocusRec.Right Or FocusRec.Bottom Then
DrawFocusRect Me.hdc, FocusRec
End If
' рисуем конечный прямоугольник красным Line (sngXf, sngY1)-( sngX2, sngY2), vbRed, В ' обнуляем координаты прямоугольника
sngXf = О
sngY1 = О<.■
sngX2 = О, * ..
sngY2 = О
End Sub
Private Sub Swap(vntA As Variant, vntB As Variant) Dim vntT As Variant vntT = vntA vntA = vntB vntB = vntT End Sub
Рис. 14-2 Выделение прямоугольной области «резиновым» контурам
API-фyнкцияDrawFocusRectpиcyeтпpямoyгoльныйкoнтyp пунктирными линиями. При перемещении мыши Ваш код перед вычерчиванием нового контура должен стирать предыдущий. К счастью, DrawFocusRect работает в - режиме» Хог, что делает эту задачу крайне простой. Если Вы повторно вызовете DrawFocusRect, передав ей те же координаты, прямоугольный контур исчезнет. В процедуре FormMouseMove. приведенной выше, DrawFocusRect вызывается дважды: первый раз, чтобы стереть предыдущий прямоугольник, второй — чтобы нарисовать следующий.
246246частьн дорогойджон. как?..
246
246
Действия, связанные с мышью, в Visual Basic классифицируются событиями MouseUp, MouseDown и MouseMove — моя программа использует их все. MouseDown отмечает начало прямоугольного контура, MouseMove сообщает об изменении размера контура (при нажатой левой кнопке мыши). a MouseUp указывав , на завершение выделения прямоугольной области.
В этом примере прямоугольный контур создается в результате действий с мышью лишь на самой форме, но не в элементах управления, которые могли бы быть на ней. Чтобы те же операции стали возможны, скажем, на элементе rictureBo.\ продублируйте мой код для его процедур обработки «мышиных» событий.
Дорогой Джон, как..
Создавать «горячие» участки на графических изображениях?"
Для этой задачи эффективен элемент управления Image. Рассмотрим ее решение на примере.
Сначала я вычертил на форме элемент Image (.imgWortd^ загрузил в него изображение WORLD.BMP, потом аккуратно нарисовал поверх него еще 4 элемента Image — imgNoribAmerKa, imgSoutb- Amerkd, imgEnropen imgAfrku.— охватывающих соответствующие части света. И, наконец, добавил на форму метку IblHotSpots.
На рис. 14-3 показан образец получившейся в итоге формы.
Рис. 14-3 «Горячие» участки, видимые пи этапе разработкиРис. 14-3 «Горячие» участки, видимые пи этапе разработки
Рис. 14-3 «Горячие» участки, видимые пи этапе разработки
Рис. 14-3 «Горячие» участки, видимые пи этапе разработки
При щелчке 'орячегс участка связанный с ним текст показывается в метке:,
Option Explicit<
Private Sub imgAfrlca_Cllck()
IblHotSpots.Caption = "Africa".
End Subм’
Private Sub lmgEurope_Cllck() "
IblHotSpots.Caption = "Europe" 1 '
End Sub
Private Sub lmgNorthAmerlca_Click()
IblHotSpots. Caption = "North America"
End Sub
Private Sub iragSouthAmerica_Cllck()
IblHotSpots.Caption = "South America"
End Sub
Private Sub lmgWorld_Click()
IblHotSpots.Caption = "The World"'
End Sub
На рис. 14-4 показано, что получится, если щелкнуть прямоугольник, соответствующий Африке.
Контуры, ограничивающие элемент управления . г. ? видны на этапе разработке, но скрываются при выполнении программы. Поскольку эти элементы, формирующие «горячие» участки, нарисованы поверх основного изображения, щелчки воздействуют именно на них.
Рис. 14-4 Активизирован «горячий» участок — Африка
Для большей интерактивности я записал в свойства ToolTipText каждого элемента Image названия соответствующих континентов.
См. акже...
ПриложениеVBClock в главе 31 «Дата ивремя», где тоже демонстрируются «горячие» участки на графических изображениях.
Дорогой Джои, как...
Быстро нарисовать многоугольник?
Метод Line позволяет соединить последовательность точек прямыми линиями и вернуться в начальную точку, в результате чего образуется замкнутый многоугольник. Но API-функция Polygon работает быстрее и обладает тем преимуществом, что умеет эффективно закрашивать внутреннюю область многоугольника. Класс PoJy-
gon значительно упрощает рисование многоугольников, инкапсулируя эту API-функцию и необходимые ей данные:
1 POLYGOH.CLS
Option Explicit
Private Type POINTAPI
x As Long,
у As Long
End Type.
Private Declare Function Polygon _
Lib "gdi32" (
ByVai hdc As Long,
IpPoint As POINTAPI, _'''.
ByVai nCount As Long _".
) As Long,
закрытые переменные уровня модуля.
Private mobjDevice As Object1.
Private m^nnAzi As Single
Private msngSYl As Single
Private IIJ^I |улпа LIL As Single
Private nisngYRatii As Single
Private maPointArrayC) As POINTAPI
, ’ Device (свойство)
Property Set Device(objDevice As Object)
Dim ogSX2 As Single'
Dim sngSYS As Sinni.e.1
Dim sngPXS As Single'
Dim sngPY2 As Single", ,
Dim IntScaleMode As Integer ',
Set mobjDevice = objDevice
With mobjDevice
' получаем параметры, определяющие текущий масштаб
intScaleMode = .ScaleHode
msngSXI = .ScaleLeft
msngSYl = .ScaleTop''
sngSX2 = msngSXI + .ScaleWidth
sngSYi = msngSYl + .ScaleHeight
■«;:1 временно устанавливаем пиксельный режим
..ScaleHode = vbPixels
■1 получаем параметры,определяющие текущий масштаб
для пиксельного режима.
,sngPX2 = .Scalewidth-
, *sngPY2 = .ScaleHeight
г ' восстанавливаем исходньй масштаб_
If IntScaleMode = 0 Then'
mobjDevice.Scale (msngSXI, msngSYl)-(sngSX2, sngSYS) i :'
Else'■
mob]Device.ScaleMode = intScaleMode■
Bid If
' вычисляем масштабные коэффициенты только один раз msngXflatio = sngPX2 / (sngSX2 - msngSXI)
msngYRatio = sngPY2 / (sngSY2 - msngSYI).
End With
Ere Property
' ""Point X,Y (метод)
Public Sub Point(sngX As Single, sngY As Single)
Dim IngN As Long
IngN = UBound(maPointArray) + 1
ReDim Preserve maPointArray(IngN) maPointArray(lngN). x = XtoP(sngX) ®aPointArray(lngN).y = YtoP(sngY)
End Sub
' ""Draw (метод)
Public Sub DrawO"
'Polygon mob]Device.hdc, maPolntArray(l), UBound(ihaPointArray)
ReDim maPointArray(0)
End Sub
' выражает Х-координату в пикселах
Private Function XtoP(sngX As Single) As Long
XtoP = (sngX - msngSXI) * msngXRatio
End Function.
' выражает Y-координату в пикселах
Private Function YtoP(sngY As Single) As Long.
YtoP = (sngY - msngSYI) * msngYRatio' '
End Function'
инициализация.,
Private Sub Class_Initlalize().
ReDim inaPointArray(O)л
End Sub
Стремясь упростить работу с объектом Polygon, я учел особенности обработки графики в Visual Basic. API-функция Polygon принимает координаты только в пикселах, a Visual Basic разрешает вывод на графическое устройство и в других единицах. К счастью, перед вызовом API-функций, предназначенных для отрисовки линий, окружностей и прочих графических примитивов. Visual Basic сам преобразует пользовательские единицы в пикселы, не обращаясь к соответствующим низкоуровневым API-функциям. Объект Polygon поступает точно так же, и, как показывает опыт, это самый эффективный способ, если программа работает не с пикселами, а
с какими-то другими единицами. Заметьте, что масштабные коэффициенты вычисляются лишь раз, в процедуре-свойстве Set Device. На их основе каждая параХ- и У-координат многоугольника, заданная в методе Point, преобразуется в пикселы. Единственный подвох здесь — после каждого изменения масштаба или координатных единиц на устройстве надо тут же устанавливать свойство Device.
В Visual Basic используются стандартные кисти и контексты графическихустройств (graphical device contexts, GDCs), поэтому извн' очень легко получить текущие значения параметров типа P i П- Style, FillColor и др. По сути, ничего такого особенного в объекте Polygon делать не приходится — API-функция Polygon автоматически оперирует текущими значениями параметров для данного устройства вывода. А это значит, что один и тот же объект Polygon позволяет рисовать на форме многоугольники с разной заливкой Вы просто изменяете значения свойств Fillstyle и FillColor формы.
Следующий код демонстрирует использование объекта Polygon. В данном примере создается многоугольник случайной формы с 17 вершинами и закрашивается цветом, выбираемым тоже случайно. Чтобы проверить это на практике, введите показанный ниже код в модуль новой формы и добавьте на нее элемент управления PictureBox с именем picTest. (У модуля класса должно быть имя Polygon.) Запустите программу и щелкните окно рисунка.
■ POLYTEST.FRM
Option Explicit,
Dim polyTest As New Polygon
Private Sub Form_Load()
1 каждый раз создаем уникальный многоугольник
Randomize
' можно использовать любые единицы и параметры GDC
With picTest
.Move 0, 0, ьсатеянпп, scaieHeight'
. ScaleMode = vblnches,
FillStyle = vbSolid■
End With''
End Sub■'
Private Sub picTest_Click()>"
Dim IntT As Integer
' указываем элемент PictureBox как устройство,.
' на которое выведется многоугольники
Set polyTest.Device = picTest.-
With picTest
' при каждом щелчке очищаем окно рисунка
.Cis.
' формируем случайный многоугольник с 17 вершинами
For inti = 1 То 17
polyTest.Point Rnd * .асаleWidth, find * .ScaleHeight Next inti
1 выб!фаем цвет по случайному закону
.FlllColOr = RGB(Hnd * 256, Rnd * 256, Rnd * 256)
" рисуем многоуголвник и закрашиваем его
polyTest.Draw
End With:'
End Sub■
Рис. 14-5 иллюстрирует многоугольник с 17 вершинами, созданный с помощью объекта Polygon.Рис. 14-5 иллюстрирует многоугольник с 17 вершинами, созданный с помощью объекта Polygon.
Рис. 14-5 иллюстрирует многоугольник с 17 вершинами, созданный с помощью объекта Polygon.
Рис. 14-5 иллюстрирует многоугольник с 17 вершинами, созданный с помощью объекта Polygon.
Рис. 14-5 Многоугольник, нарисованный с помощью объекта Polygon
Раздел «Дорогой Джон, как... нарисовать эллипс?» далее в этой главе.
Раздел «Дорогой Джон, как... закрасить произвольный замкнутый участок?» далее в этой главе.
Дорогой Джон, как...
Нарисоватьэллипс?
Метод Circle, встроенный в Visual Basic, рисует эллипс, исходя из координат его центра, радиуса и аспекта (соотношения радиусов по осям X и Y). Однако во многих случаях приходится рисовать эллипс по ограничивающему прямоугольнику, и, если использовать метод Circle, вычисления будут слишком сложны и чреваты ошибками..
К счастью, рисовать эллипсы по ограничивающему прямоугольнику можно способом получше, и именно такой алгоритм реализует API-функция Ellipse. Б сущности. Visual Basic сам использует эту функцию, рисуя окружности и эллипсы; перед ее вызовом он преобразует поступившую информацию (координаты центра, радиус и аспект) в координаты ограничивающего прямоугольника. Я инкапсулировал API-функцию Ellipse в класс Ellipse, который позво-
ляет без проблем рисовать эллипсы по ограничивающим прямоугольникам.
' ELLIPSE. CLS..,
Option Explicit'
Private Declare Function Ellipse _
Lib "gdi32" ( _,.
ByVai hdc As Long, „.1
ByVai
XI
As
Long, ..
ByVai
Y1
As
Long, _
By Vai
X2
As
Long, _
By Vai
Y2
As
Long _
) As Long, .
' закрытые переменные урспнч модуля
Private mobjDevice As Object
Private msngSXI As Single
PrivateAsSingle
■Private msngXRatio As Single 1 ,
■Private msngYRatio As Single1
' Device (свойство)
Property Set Device(objDevice As Object)
Dim sngSX2 As Single
Dim sngSY2 As Single
Dim sngPX2 As Single
Dim sngPY2 As Single
Dim jntScaleMode As Integer
Set mobjDevice = objDevice
With mobjDevice
' получаем параметры, определяющие текущий масштаб intScaleMode = .ScaleMode
тслгт.су-i = . ScaleLeft
msngSYI = .ScaleTop
sngSX2 = msngSXI t .Scalewidth
sngSY2 = msngSYI + .ScaleHeight
' временноустанавливаем пиксельный режим
'.ScaleMode= vbPixels
' получаемпараметры, определяющие текущиймасштаб
' для пиксельного режима
. sngPX2 = .ScaleWidth_
sngPY2 = .ScaleHeight
' восстанавливаем исходный масштаб
If IntScaleMode = 0 Then
mobjDevice. Scale (msngSXI, msngSYI)-(sngSX2, sngSY2)
Else
mobjDevice.ScaleMode = intScaleMode
End If
' вычисляем масштабные коэффициенты только один раз msngXRatio = sngPX2 / (sngSX2 - msngSXI)
лзпдтиатю = sngPY2 / (sngSY2 - msngSYl)
End With
End Property
’ DrawX1,Y1,X2,Y2 (метод) -
Public Sub Drawl
sngXl As Single, _"
sngYl As Single, _
sngX2 As Single, _-
sngY2 As Single _1
)
Ellipse mobjDevice.hdc, XtoP(sngXI), YtoP(sngYI), XtoP(sngX2), YtoP(sngY2)
End Sub
' выражает Х-координату в пикселах
Private Function XtoP(sngX As Single) As Long
XtoP = (sngX - msngSXI) * msngXRatio
End Function.
' выражает Y-координату в пикселах
Private Function YtoP(sngY As Single) As Long
YtoP = (sngY - msngSYl) * msngYRatio
End Function
Большая часть кода в модуле класса Ellipse идентична коду недавно рассмотренного класса Polygon. Например, в процедуре-свойстве Device выполняются те же вычисления, связанные с единицами измерения координат. Таким образом, класс Ellipse (или Polygon) можно использовать как шаблон при создании других графических объектов, тоже основанных на API-функциях, Кроме того, вызовы подходящихАРЕфункций можно включить как методы в отдельный, всеобъемлющий класс Graphics, у которого есть одно свойство Device и несколько методов для рисования разных объектов, допустим, Polygon и Ellipse. Я поместил API-функции Polygon и Ellipse в разные классы только для того, чтобы четко показать реализацию каждого из них.
Программа, приведенная ниже, демонстрирует работу с объектом Ellipse, рисуя 3 вложенных эллипса. Между прочим, сделать то же самое методом Circle, встроенным в Visual Basic, на уднвлещие трудно — и все из-за аспектов, малейшее изменение которых влияет на размеры эллипсов. Чтобы проверить объект Ellipse на практике, добавьте на форму элемент PictureBox с именемpicTest и введите в модуль формы следующий код. Модулю, содержащему класс Ellipse, следует присвоить то же имя — Ellipse. Запустите програм-
мп щелкните окно рисунка, и в нем появятся 3 вложенные друг в друга эллипса.
Option Explicit
Dim ellipseTest As New Ellipse
Private Sub picTest_Click() picTest.ScaleMode = vbCentimeters Set ellipseTest. Device = picTest ellipseTest.Draw 1, 1, 7, 4 ellipseTest.Draw 2, 1, 6, 4 ellipseTest.Draw 2, 2, 6, 3
End Sub
Рис. 14-6иллюстрирует результат выполнения этой программы.
Рис, 14-6 Вложенные эллипсы, нарисованные по координатам ограничивающих прямоугольников
О См. также...
Раздел «Дорогой Джон, сп. . быстро нарисовать многоугольник?; ранее в этой главе.
Закрасить произвольный замкнутый участок?
API-функция Polygon, уже рассмотренная в этой главе, способна закрасить область, только если известны координаты этой области. Иногда такие координаты — тайна за семью печатями, зато известен цвет границ, и этой информации вполне достаточно для заливки области с помощью API-функции FloodFiJI Она эмулирует старый оператор Paint из ранних версий Бейсика, который закрашивал произвольные участки, ограниченные определенным цветом.
Следующий класс и программа-пример показывают, как использовать API-функцию Р1ообРШдля заливки области на пересечении окружности и прямоугольника:.
. рйтмт oi е
Option Explicit-
Private Declare Function FloodFill _''
Lib(
ByVai hdc As Long, _-
ByVai x As Long, _■
ByVai у As Long, _
ByVai crColor As Long _
) As Long
' закрытые переменные уровня модуля i Private mobjDevice As Object
Private msngSX As Single■
Private msngSY1 As Single
Private msngXBatio As Single.
Private msngYRatio As Single'
Property Set Device(objDevice As Object) ( '
Dim sngSX2 As Single1
Dim sngSY2 As Single. ' '
Dim sngPX2 As Single.
Dim sngPY2 As Single.
Dim intScaleMode As Integer
Set SobjDevice = objDevice
With mobjDevice ' получаем параметры, определяющие текущий масштаб
IntScaleMode = .ScaleMode,
msngSXI = .ScaleLeft'|
msngSYI = .ScaleTop sngSX2 = msngSXI + .ScaleWidth sngSY2 = msngSYI + .ScaleHeight ' временно устанавливаем пиксельный режим .ScaleMode = vbPixels 1 получаем параметры, определяющие текущий масштаб ' для пиксельного режима sngPX2 = .Scalewidth sngPY2 = .ScaleHeight 1 восстанавливаем исходный масштаб If IntScaleMode = 0 Then
mobjDevice.Scale (msngSXI, fnsngSY1)-(sngSX2, sngSY2) Else
mobjDevice.ScaleMode = intScaleMode End If ' вычисляем масштабные коэффициенты только один раз msngXRatio = sngPX2 / (sngSX2 - msngSXI) msngYRatio = sngPY2 / (sngSY2 - msngSYI)
End With
'End Property"
' Flood x v (метод)
Public Sub Flood(sngX As Single, sngY As Single)
FloodFill mobjDevice.hdc, XtoP(sngX), YtoP(sngY), _-:
mobjDevlce.ForeColor End Sub
' выражает Х-координату в пикселах
Private Function XtoP(sngX As Single) As Long XtoP = (sngX - msngSXI) * msngXRatio
End Function
' выражает Y-координату в пикселах
Private Function YtoP(sngY As Single) As Long
YtoP = (sngY - msngSY1> * msngYRatlo
End Function■■
А теперь взгляните на код, который вычерчивает окружность и прямоугольник красным цветом и закрашивае т зеленым область их пересечения:
Option Explicit.
Dim paintTest As New Paint■'
Private Sub picTest_Click()"L<
picTest.ScaleMode = vblnches ' вычерчиваем красным цветом окружность ' и пересекающийся с ней прямоугольник picTest. Li (0.5, и.5)-(2, 2), vbRed, В picTest. Circle (2, 1), 0.7, vbRed‘
' готовимся к запивке области их пересечения‘
picTest.FillStyle = vbFSSolid ' стиль заливки'
picTest.FillColor = vbGreen ' цвет заливки.
picTest.ForeColor = vbRed ' граничный цвет Set paintTest.Device = picTest paintTest.Flood 1.7, 0.9..
' восстанавливаем исходный стиль заливки picTest.FillStyle = vbFSTransparent End Sub
Граничный цвет определяется текущим значением свойства ForeColor, а цвет заливки — текущими значениями свойств FillStyle и FillColor. Закрашивание начинается с указанных ЛЭ-координш и прекращается на пикселах, цвет которых совпадает со значением свойства ForeColor. Это очень удобно при заливке произвольных замкнутых участков изображения.
Чтобы проверить эту программу, присвойте классу имя Paint, добавьте на форму элемент PictureBox (picTest) и введите в модуль формы предыдущий код. Запустив программу, щелкните окно рисунка; должно появиться изображение, как на рис. 14-7.
Рис. 14-7 Произвольныйучасток изображения, закрашенный с помощью объекта Paint
^О См. также...
• Раздел "Дорогой Джон, как... быстро нарисоватьмногоугольник?» ранее в этой главе.
Дорогой Джон, как».
Поворачивать растровое изображение?
В Win32 API вроде бы есть 32-разрядная функция PlgBlt, предназначенная для универсальных полигональных преобразований. В Windows 95 она задокументирована, но не реализована. Вызвав ее, я ничего не добился, т. е. PlgBlt пока что просто заглушка. Возьмите эту функцию на заметку: она должна работать гораздо эффективнее, чем тот способ вращения изображений, о котором я собираюсь рассказать.
Код, представленный ниже, поворачивает картинку на любой угол, пиксел за пикселом копируя исходное изображение из одного окна рисунка в другое. Естественно, он работает довольно медленно, и Вы, наверное, предпочтете использовать его для обработки картинок лишь на этапе разработки своего приложения.
Для чтения и записи пикселов применяются методы Point и PSet (моя программа, кстати, прекрасно их иллюстрирует). Перед работой с этими методами установите свойство ScaleMode как ubPixels.
Чтобы опробовать программу в действии, откройте новый проект и добавьте на форму 2 элемента PictureBox (picOneH picTwoy а также кнопку cmdRotate. Закрепите заpicOne какую-нибудь растровую картинку (через свойство Picture) и подгоните размеры элемента управления под размеры картинки. (Программа копирует лишь ■ ту часть изображения, которая видна в окне рисунка.) Измените размерыф/сЛс^ так, чтобы он стал чуть ширеpicOne. Угол поворота задан в коде и составляет 45°, но Вы можете взять любое допустимое значение. Введите код в модуль формы и запустите программу.
Теперь щелкните кнопку, и во втором окне постепенно проявится повернутое изображение.
Option Explicit.
Const PI = 3.14159265358979 Const ANGLE = 45
'Private Sub cnidRotate_Click()
Dim intX As Integer, intY As Integer
Dim intX1 As Integer, intYI As Integer
Dim JblX2 As Double, dblY2 As Double
Dim dblX3 As Double, dblY3 As Double Dim dblThetaOeu As Double
Dim dbIThetaRad As Double■ .
' задаем угол поворота dbIThetaDeg = ANGLE■..-
' пересчитываем его в радианы dbIThetaRad dbIThetaDeg * PI / 180' ' устанавливаем пиксельный режим picOne.ScaleHode = vbPixels picTwo.ScaleHode = vbPixels
For intX = 0 To picTwo.ScaleWidth intX1 = intX - picTwo.ScaleWidth \ 2 For intY = 0 To picTwo. ScaleHeight "IntYI = intY - picTwo.ScaleHeight \ 2
' поворачиваем изображение на угол dbIThetaRad dblX2 = intX1 * Cost-dbIThetaRad) + tntY1 * Sin(-dbIThetaRad) dblY2 = intYI * Cos(-dblThetaRad) - intX1 * Sin(-dblThetaRad) ' приводим к центру окна рисунка dblX3 - dblX2 + picOne. ScaleWidth \ 2.
dblY3 = dblY2 + picOne. ScaleHeight \ 2 ' если в picOne есть эта точка, устанавливаем ее цвет в picTwo
If dblXS > 0 And dblX3 < picOne.ScaleWidth - 1 _
And dblY3 > 0 And dblY3 < picOne. ScaleHeight - 1 Then . ,picTwo.PSet(intX, intY), picOne.Point(dblX3, dblY3)
End If
Next intY-
Next intX End Sub
Получив преобразованную картинку, Вы, вероятно, захотите сохранить ее оператором SavePicture в каком-нибудь файле. Но в таком случае не забудьте сначала установить свойство AutoRedtaW элемента picTwo как True. Вот пример использования оператора SavePicture:
SavePicture picTwo.Image, "C:\FINISHROT. BMP"
Рис. 14-8 показывает- состояние программы сразу после запуска, а рис. 14-9 — после «нажатия» кнопки Rotate (исходная картинка поворачивается на 45°).
Рис. 14-8 Исходное изображение
Рис, 14-9 Копия повернута на 45*
Дорогой Джон, как».
Прокручивать графические изображения?
Метод l aintPicture. встроенный в Visual Basic, упрощает многие операции над графическими изображениями. Именно этот метод я и использую в следующем примере для отображения крупной картинки в небольшом окне и ее плавной прокрутки. Исходное изображение западного полушария выводится в полноразмерном окне рисунка, а ее копия — в аналогичном окне, но поменьше (рис. 14-Ю).
ПРИМЕЧАНИЕ Для этой программы не обязательно, чтобы исходное изображение было видимо. Закрепив за первым элементом PictureBox большую растровую картинку, Вы можете на этапе разработки установить его свойство Visible как False. Нужное изображение все равно появится во втором элементе PictureBox (меньшего размера).
Рис. 14-10 Прокрутка крупного изображения в небольшом окне рисунка
Итак, добавьте на новую форму 2 элемента PictureBox разного размера. Тому, что побольше, присвойте имя picOne, а тому, что поменьше, — pic lieu. Вставьте вертикальную (i-'sbScroll и горизонтальную (ДхЬ5сго//)полосы прокрутки (рис. 14-10). Расположите их рядом е picHco — они будут контролировать содержимое в окне этого элемента. Загрузите в свойство Picture элемента ри< ни■ какой- нибудь файл с растровым изображением и отмасштабируйте этот элемент под размеры картинки. Окно picTu'лбудет показывать лишь малую часть изображения изpicOne. Программа прокручивает изображение в picTwo, копируя соответствующую прямоугольную область изpicOne методом PaintPicture.
Введите в модуль формы код, представленный ниже. Заметьте, что программа генерирует событие Change для полосы прокрутки из процедуры обработки ее события Scroll. Это позволяет, перемещая движок на полосе, плавно прокручивать изображение в режиме реального времени.
Option Explicit . ■
Private Sub Form_Load()
hsbScroll.Max = picOne.ScaleWidth - picTwo.ScaleWidth
hsbScroll.LargeChange=hsbScroll.Max\10
hsbScroll.SmallChange=hsbScroll.Max\25
vsbScroll.Max = picOne.ScaleHeight - picTwo.ScaleHeight vsbScroll.LargeChange=vsbScroll.Max\10
vsbScroll.SmallChange=vsbScroll.Max\25
End Sub
Private Sub hsbScroll_Change()
UpdatePicTwo
End Sub
Private Sub hsbScroll_Scroll()
hsbScroll_Change End Sub
Private Sub vsbScroll_Change() UpdatePicTwo
End Sub
Private Sub vsbScroll_Scroll() vsbScroll_Change
End Sub
Private Sub UpdatePicTwo()
picTwo.PaintPicture plcOne.Picture, 0, 0, _ picTwo.ScaleWidth, picTwo.ScaleHeight, _ hsbScroll.Value, vsbScroll.Value, _ picTwo.ScaleWidth, picTwo.ScaleHeight, _
■ vbSrcCopy
End Sub
Дорогой Джон, как...
Использовать BitBIt для создания анимации?
Метод PainrPicturc вполне прилично работает и в этом случае. Он позволяет перемещать графические объекты нерегулярной формы, не затрагивая фона, Надо сказать, метод PaintPicture в Visual Basic эквивалентен API-функции BitBIt. (Собственно, проверив параметры на допустимость и преобразовав текущие единицы, он просто вызывает BitBIt.) Единственный изъян PaintPic lure —его медлительность. Вызывая BitBIt напрямую. Вы сможете избежать лишних вычислений. выполняемых PaintPicture перед обращением ко все той же BitBIt. В итоге Вы добьетесь более быстрой и плавной лшма- ции — вот почему г своей программе-примере я вызываю не PaintPicture, a BitBIt.
API-функция BitBIt позволяет быстро перемещать прямоугольный блок пикселов из одного окна рисунка в другое или с одной формы на другую, а также передавать его объекту-принтеру. BitBIt требует описателя контекста устройства (hDC) как для источника изображения, такидля приемника, Соответствующее свойство, hDC, есть у всех объектов PictureBox, Form и Printer. Но учтите: если свойство \uioRediaxv установлено как Гг-к свойство hDC элемента PictureBox указывает на его свойство Image, а не Picture. ППедгвшнче- ние (image) — «закулисная" копия того, что Вы видите на экране;
оно хранится в памяти, и при свойстве AutoRedraw (элемента PictureBox). равном True, BitBlt все операции выполняет именно над ним. Результаты этих операций отражаются на рисунке, только когда система обновляет содержимое его окна. В данном случае этот процесс играет нам на руку, помогая избежать мерцания изображения и прочих проблем.
Следующая программа реализует анимацию НЛО; она перемещает «тарелку» по облакам Windows 95, не нарушая фона. Эта операция требует трех элементов PictureBox- один хранит изображение НЛО, второй — маску, накладываемую на фон в участке вывода изображения НЛО, а третий временно запоминает участок фона, который надо будет восстановить после пролета НЛО. Этот прием позволяет анимирован, и куда более сложные объекты, но пока сделаем, что попроще.
1 bitblt. ': ' ■
Option Explicit
Private Declare Function BitBlt „
Lib "gdi32" { _-
ByVai hDestDC As Long, _.
ByVai x As Long, ByVai у As Long,■
Byval nWidth As Long, Byval nHeight as Long, _
Byval hSrcDC As Long, __
Byval xSrc As Long, Byval ySrc As Long, _
Byval ownop as Long _
) As Long■■
Private Sub cmdAnimate_Click()’
Static IngX As Long, IngY As Long■
Static IngW as Long, innH as Long■
Static blnBackSave,! As Boolean■'
Dim LngRtn as Long■
' показываем "песочные часы", пока работает программа
Screen.MousePoinreг = vonourglass
1 определяем начальный участок
IngX = -picUfo. ScaleWidth,
IngY = picClouds.ScaleHeight
'1 сохраняем размеры в локальных переменных'
’*■' " (только раз - для ускорения работы)1
IngW = picUfо,ScaleWidth■
InnH = picUf о.ScaleHeight1"
1 цикл анимации НЛОИф ■• •j
Do.■
восстанавливаем фон (кроме того случая,.
1 когда объект выводится впервые)- ,
Il blnBackSaved = True Then,
IngRtn = BitBlt(picClouds.hDC, IngX, IngY, IngW, lngH, _ picBack.hDC, 0, 0, vbSrcCopy)
' прекращаем анимацию, когда НЛО достигает границ
If IngX > picClouds. ScaleWidth Then blnBackSaved = False picClouds.Refresh
ExitDo■ ,
End If'
End If
' перемещаем НЛО в новый участок
IngX = IngX + 1
If IngX < 0.5 * picClouds.ScaleWidr-
Or IngX > 0.8 * picClouds.ScaleWidth Then
IngY = IngY - 1
Else
LngY = IngY + 1
End If
' сохраняем фон на новом участке
IngRtn = BitBlt(picBack.hDC, 0, 0, IngW, IngH, _ picClouds.hOC, IngX. IngY, vbSrcCopy)
blnBackSaved = True ' применяем маску IngRtn = BitBlt(picClouds.hDC, IngX, IngY, IngW, IngH, _ picUfoMask. hDC, 0, 0, vbSrcAnd)
рисуем НЛО
IngRtn = BitBlt(picClouds.hDC, IngX, IngY, IngW, IngH, _ picUfo.hDC, 0, 0, vbSrcPaint)
picClouds.Refresh
Loop
' восстанавливаем форму курсора мыши ■
Screen. MousePointer = vbDefault End Sub
Чтобы посмотреть, как работает эта программа, откройте новый проект, добавьте на форму кнопку cmdAntmate \\ 4 элемента управления PicturcBox:/;/cG/uu<Ys, piclifo,picLyoMampicBack. Теперь Вам понадобятся 3 растровых картинки. Одну (большую) я использую как фон, взяв файл CLOUDS.BMP с установочного компакт-диска Windows 95. Остальные две (ЕШО и маску) я нарисовал сам в Windows Paint. Все эти файлы Вы найдете на компакт-диске, прилагаемом к книге. Свойство Picture элемента/исС7ои<А; должно указывать на CLOUDS.BMP, а его свойство AutoRedraw установите как True. Свойства Picture элементов picUfua picBack должны указывать на растровое изображение НЛО, то же свойство элементаpicUfoMask- на маску. Отмасштабируйте эти три элемента так, чтобы они были одинаковы, и разместите их в верхней части формы, как показано на рис. 14-11. Установите у всех четырех элементов PictureBox свойство AutoSize как True и свойство ScaleMode — как 3 - Pixel. Вот так выглядит форма на этапе разработки:
Рис. 14-11 Пример анимации с памтцъю функции BitBlt: так выглядит форма на этапе разработки ' '
Растровые изображения НЛО и его маски надо было создать особым образом. НЛО я нарисовал на черном фоне; сам объект мог быть любых цветов, кроме черного. В таком случае на экране появляются все цветные точки, составляющие изображение НЛО, а черные точки при анимации заменяются соответствующим фоном.
Маску я создал по следующему принципу: там, где в изображении НЛО черные точки, ставил белые, но все его белые или цветные точки заменял в маске на черные. Таким образом, маска является черно-белым негативом исходного изображения.
Теперь посмотрим, как это все работает. Восстанавливаем предварительно сохраненный фон и тем самым стираем НЛО в текущем участке. (Этот этап цюпут ьис ня при первом выводе НЛО.) Сохраняем фон в следующих координатах НЛО, закрепляя его за элемен- TOMpicBack. Это позволит восстановить фон после очередного смещения НЛО. На каждый новый участок фонового изображения накладываем маску с использованием Булевой операции And (пикселы копируются последовательно, один за другим). Тип операции определяется последним параметром функции BitBlt. Результат логического «И» таков: на фоновое изображение «переходят* лишь черные точки маски. В этот момент на экране проступает негатив НЛО, но тут же исчезает, поскольку начинается следующая операция — заметить его Вы не успеете.
На заключительном этапе в подготовленный участок выводится исходное изображение НЛО (изpicUjov применением побитовой операции Or. Точки НЛО, отличные от черного, появляются там, где фон зачернен, а его черные точки принимают цвета соответствующих точек фона; в итоге НЛО парит в облаках.
Описанный процесс неоднократно повторяется в цикле, что приводит к плавному пролету НЛО слева направо. На рис. 14-12 — НЛО в середине своей траектории.
Рис. 14-12 Пример анимации с помощью функции BitBlt: НЛО движется на фоне облаков
Если Вам не удается корректно выводить маску НЛО, это может быть связано с видеодрайвером. Попробуйте заменить его и проверьте, не исчезла ли проблема. .
j^ Си. также...
• Приложение Lottery в главе 29 «Графика», где тоже демонстрируется процесс анимации.
Дорогой Джон, как...
Использовать для анимации объекты Picture?
Свойства Picture были у соответствующих объектов во всех версиях Visual Basic, но в версии 4 появился независимый объект Picture, существенно расширивший возможности операций над изображениями. Позволяя загружать и хранить изображения, он избавляет от необходимости использовать в этих целях элементы управления PiciurcBox или Image. А как только Вам понадобится вывести на экран изображение, содержащееся в объекте Picture, скопируйте его в видимый элемент управления. , ,; . ,
ПРИМЕЧАНИЕ Кроме объекта Picture, Visual Basic предоставляет элемент управления ImaccLisi. где можно хранить набор изображений из файла ресурсов. Применение объектов Picture или элементов управления ImageList вместо PictureBox или Image способно значительно ускорить программы, манипулирующие изображениями.
Я решил дать Вам образец кода для экспериментов. Эта программа создает массив объектов Picture и загружает в них изображения из некого набора в момент загрузки формы. Элемент управления Timer с именем ^тгАпгтйГенастроен на максимальную частоту срабатывания (т. е. в его свойство Interval записано значение в 1 мс).
Примерно с этой частотой он заставляет программу поочередно присваивать каждый объект Picture одному элементу управления PictureBox (piciest).Результат — плавная анимация изображений, созданных Вами и считываемых из последовательности файлов.
Option Explicit
Const NUMFRAMES = 15
Dim picArray(1 To NUMFRAMES) As Picture
Private Sub Form_Load(),
Dim strFile As String
Dim inti As Integer■
For inti = 1 To NUMFRAMES"
strFile = App.Path & "\GLO8E" & Format(intl) 4 ".BMP" -
Set picArray(intl) = LoadPicture(strFile)..
Next inti■
End Sub■
Private Sub tmrAnimate_Timer()
Static intN As Integer
intN = (IntN Mod NUMFRAMES) + 1'’
picTest. Picture = picArray( intN)
End Sub
Я модифицировал приложение AniGlobe (см. главу 29 «Графика»), чтобы создать 15 изображений глобуса — GLOBE1.BMP. GLO- ВЕ2.ВМР и так до GLOBE 15.BMP. Эти изображения я загрузил в массив из 15 объектов Picture; программа поочередно копирует их и отображает в единственном элементе PictureBox. Свойство AuioSize последнего должно быть True, что позволяет добиться его автоматической подгонки под анимационные кадры. Рис. 14-13 иллюстрирует процесс анимации, «замороженной» где-то на середине.
(У*.
Рис. 14-13 Простая анимационная последовательность, полученная из массива объектов Picture
Работать с элементом управления Animation?
С Windows 95 к нам пришли все эти крошечные анимационные ролики — фонарики, высвечивающие папки, корзинки, выбрасывающие скомканные документы, листочки, перелетающие из одной корзинки в другую, и т. д. Элемент управления Animation в Visual Basic позволяет легко добавить в программу любой из таких роликов. Следующий код загружает и воспроизводит 2 анимационных ролика — с фонариком и с опустошаемой корзинкой:
Option Explicit
Private Sub Form_Load()
anmOne. Open App.Path & "\SEARCH.AVI"
anmOne.Play
anniTwo.Open App.Path & "\FILENUKE.AVI"
anmTwo.Play
End Sub
Чтобы запустить эту программу, добавьте на новую форму 2 элемента Animation — аптОпе и аптТию. Элемент Animation, входящий ■ : состав Microsoft Windows Common Control.s-2 6.0 (MSCO- MCT2.OCX), обеспечивает программное управление анимацией. Для загрузки анимационных роликов я применил метод Open, а для их воспроизведения — метод Play без параметров, что приводит к непрерывному проигрыванию до закрытия формы. Оба анимационных ролика в процессе воспроизведения показаны на рис. 14-14.
Рис. 14-14 Системные анимационные ролики, проигрываемые с помощью элемента управления Animation
Каждая анимация выполняется в своем потоке, а значит, Ваша программа может параллельно заниматься другими делами.
Не все AVI-файлы можно воспроизводить через элемент Animation. Анимации должны быть без звукового сопровождения, иначе Вы получите сообщение об ошибке. Тонкости работы с этим элементом см. в справочной системе.
J^^^O^^ 11 ^4гГ^йГ’
Точно позиционировать текст в окне рисунка?'
Одно из преимуществ элемента PictureBox над текстовым полем или меткой заключается в том, что он позволяет выводить текст в произвольную позицию — любыми шрифтами и цветами, да еще и перемешивать с графикой. Характеристики шрифта Вы изменяете через стандартные свойства элемента PictureBox, а для вывода текста используете свойства ScaleWidth и ScaleHeight в сочетании с методами TextWidth и TcxtHeight.
Взгляните на код, который демонстрирует, как центрировать строку в определенной точке и как выравнивать ее по правому нижнему углу окна рисунка, Чтобы понаблюдать за работой этой программы, добавьте на новую форму элемент PictureBox с именем picTest, введите код и, запустив программу, попробуйте поварьиро- вать размер формы.
Option Explicit- '--
Private Sub Form_Resize()'
Dim intX As Integer
Dim intY As Integer'
Dim strA As String
1 перемещаем окно рисунка■
picTest.Move 0, 0, Scalewidth, ScaleHeight
1 стираем его щежнее содержимое
picTest.Cis
' определяем центр окна..
intX = picTest.ScaleWidth \ 2.
intY = picTest.ScaleHeight \ 2
1 рисуем в центре окружность (дм сравнения)
picTest.Circle (intX, intY), 700
1 выводим строку по центру окна
strA = "CENTER"■
picTest.uurгeutx = intX - picTest.TextWidth(strA) \ 2;
picTest.CurrentY = intY - picTest.TextHeight(strA) \ 2
picTest.Print strA
1 определяем координаты правого нижнего угла окна
intX = picTest.Scalewidth.
intY = picTest.ScaleHeight
' выводим туда строку1
strA = "Dower-right corner..."
picTest.CurrentX = intX - picTest.TextWidth(strA) picTest.CurrentY = intY - picTest.TextHeight(strA) picTest.Print strA
End Sub
Метод TextWidth возвращает эффективную длину всей строки с учетом текущих параметров шрифта, числа символов в строке и
пропорциональных расстояний между ними. Поэтому применение данного метода требует передачи всей строки перед самым ее выводом на экран, но уже после установки всех свойств, связанных со шрифтом.
Результат выполнения программы показан на рис. 14-15.
Рис. 14-15 Вывод текста в строго определенные позиции окна рисунка
Приведенный мной пример демонстрирует общий принцип точного вывода текста в строго определенные позиции. По тому же принципу Вы можете выводить надстрочные символы, размечать оси графиков и решать другие сходные задачи.
Пподглы Яч-гми ^r-aw
Произвольно масштабировать шрифты?
Прежние версии Visual Basic ограничивали нас набором фиксированных размеров шрифтов — даже если это были «масштабируемые до бесконечности» шрифты TrueType. Теперь в свойстве Size объекта Font, принадлежащего графическому устройству вывода, можно указать любой размер шрифта TrueType или PostScript. Попробуем ввести код, приведенный ниже, модуль пустой формы и присвоить ее свойству Font какой-нибудь шрифт TrueType.
Option Explicit
Private Sub Form_Click()■
.Dim sngSize As Single* - ’ 1 ■ .■
sngSize = 1
Do■
sngSize = sngSize *1.2’■. ■
Me.Font = "Garamond".
Me.Font.Size = sngSize.■
Me. Print "Garamond - sngSize
Loop Until sngSize > 100!
End Sub
На рис. 14-16 показано то, что получится после того, как Вы щелкнете форму.
Ftw&h4- 5 TFititoi cjAtmcnd- 10.6^3?
Gshmicnd- 12-03®^^
Garamond - 1 В. A 0702
Garamond - 18.4S843
Garamond - 22.18611
Garamond - 26.62333
Garamond- 31.948
Garamond- 38.3376
Рис. 14-16 Один и тот же текст, отображаемый на форме TrueType-шрифтом Garamond разных размеров
j^<f>^^^^.tA П ^иь^^м »»■ ay
Повернуть текст на произвольный угол?
Сам Visual Basic такой возможности не предусматривает, но несколько API-функций помогут ее реализовать. Я создал класс Rotator, определяющий объект, который инкапсулирует 5 API-функций, тип данных LOGFONT и парочку констант; все вместе они позволяют легко поворачивать текст, выводимый на экран или на принтер.
' ROTATOR.CLS
Option Explicit■■
1 API-константы
Private Const LF„FACESIZE = 32
Private Const LOGPIXELSY = 90
Private Type LOGFONT
IfHeight As Long
IfWidth As Long
LfEscapement As Long. .
LfOrientation As Long'
IfWeight As Long
Ifltalic As Byte
Ifllnderline As Byte IfStrikeOuT As Byte IfCharSet As Byte. IfOutPrecision As Byte. IfClipPrecision As Byte. IfOuaMty As Byte IsngPitchAndFamily As Byte lfFaceName(LF_FACESIZE - 1) As Byte
End Type-
Private Declare Function SelectObject
Lib "gdi32" (
ByVal hdc As Long, ByVai hobject As Long
) As Long
Private Declare Function DeleteObject
Lib "gdi32" ( _
ByVal hObject As Long
) As Long
Private Declare Function CreateFontlndirect _
Lib "gdi32" Alias "CreateFontlndirectA" (
IpLogFont As LOGFONT
) As Long
Private Declare Function TextOut _.
Lib "gdi32" Alias iexiOutA" (
ByVal hdc As Long,
ByVal x As Long,
IByVal у As Long,
IByVal IpString As String,
ByVal nCount As Long
) As Long
Private Declare Function GetDeviceCaps
Lib "gdi32" ( _
ByVal hdc As Long, _a
ByVal intlndex As Long
) As Long 'J:
закрытые переменные уровня модуля
Private mobjDevice As Object
Private msngSXI As Single
Private msngSYl As Single
Private msngXRatiu As Single
Private msngYRatio As Single
Private mllFont As LOGFONT
Private mintAngle As Integer
’ '""Angle (свойство)
Property Let Angle(intAngle As Integer)
mintAngle = intAngle
End Property
Property Get Angle() As Integer
Angle = mintAngle
End Property
' Label (метод)
Public Sub LabeKsText As String)■
Dim .ngFont As Long1
Dim IngOldFont As Long
Dim IngRes As Long
Dim bytBuf(1 As Byte
Dim inti As Integer
Dim strFontName As String
' формируем имя шрифта, декодируя его из Unicode... ;
strFontName = mobjDevice.Font.Name
bytBuf = StrConv(strFontName & Chr$(0), vbFromUnicode)
For inti = 0 To UBound(bytBuf).
mlfFont.LfFaceName(intl) = bytBuf(intl)
Next inti
преобразуем известный размер шрифта в нужные единицы■
mlfFont.lfHeight = mobjDevice.Font.Size * _
GetDeviceCaps(mobjDevice.hdc, LOGPIXELSY) \ 72
если требуется, устанавливаем курсив7
И mobjDevice,Font.Italic = True Then mlfFont.lfltalic = 1■ ‘
Else
mlfFont.lfltalic = 0
End If■
' если требуется, устанавливаем подчеркивание
If mobjDevice.Font.Underline = True Then
mlfFont.IfUnderline = 1
Else
mlfFont.IfUnderline = 0■
End If
' если требуется, устанавливаем зачеркивание
If mobjDevice.Font.Strikethrough = True Then mlfFont.IfStrikeOut = 1.
Else
mlfFont,IfStrikeOut = 0-
End If-
* если требуется, устанавливаем полужирное начертание,
используя толщину (weight) шрифта;■
mlfFont.IfWeight = mobjDevice.Font.Weight
задаем угол поворота шрифта
mlfFont.IfEscapement = CLng(mintAngle * 10#)■■
mlfFont.IfOrientatian = mlfFont.IfEscapement
' формируем новый, временный шрифт и выводим строку текста
IngFont = CreateFontlndirect(mlfFont)
IngOldFont = SelectObject(mobjDevice.hdc, IngFont)
IngRes = TextOut(mobjDevice.hdc. XtoP(mobjDevice.CurrentX), YtoP(mobjDevice.CurrentY), sText, Len(sText))
IngFont = SelectObject(mobjDevice.hdc, IngOldFont)'
DelsteObject IngFont
End Sub■■
' Device (свойство)
Property Set Device(objDevice As Object) .■
Dim sngSX2 As Single.•
Dim sngSY2 As Single
Dim sngPX2 As Single
Dim sngPY2 As Single
Dim intScaleMode As Integer
Set mobjDevice = objDevice
With mobjDevice
' получаем параметры, определяющие текущий масштаб IntScaleMode = .ScaisMoae
msngSXI = .ScaleLeft
msngSYI = .ScaleTop■ -
sngSX2 = msngSXI + .ScaleWidth 1
sngSY2 = msngSYI + .ScaleHeight
временно устанавливаем пиксельный режим
.ScaleMode = vbPixels
' получаем параметры, определяющие текущий масштаб
.1 для пиксельного режима
sngPX2 = .ScaleWidth
sngPY2 = .ScaleHeight''
восстанавливаем исходный масштаб
Il IntScaleMode = 0 Then
mobjDevice.Scale (msngSXI, msngSYI)-(sngSX2, sngSY2)
Else
mobjDevice.ScaleMode = IntScaleMode
End II ' вычисляем масштабные коэффициенты только один раз msngXRatio = sngPX2 / (sngSX2 - msngSXI) msngYRatio = sngPY2 / (sngSY2 - msngSYI)
End With
End Property'
' выражает Х-координату в пикселах
Private Function XtoP(sngX As Single) As Long
XtoP = (sngX - msngSXI) * msngXRatio
End Function
' выражает Y-координату в пикселах
Private Function YtoP(sngY As Single) As Long
YtoP = (sngY - msngSYI) * msngYRatio
End Function
Несмотря на то что объект Rotator содержит уйму весьма сложного кода, работать с ним очень легко. Как — иллюстрирует небольшая программа приведенная ниже. Она создает экземпляр объекта Rotator и указывает, что устройством вывода будет элемент управления PictureBox (/мсТб^оДалее записывает в свойство Angle объекта нужный угол поворота и передает строку текста методу Label.
Заметьте, что имя и размер шрифта, а также позиция вывода текста (СиггстХи CurrentT ) определяются «шрифтовыми» свойствами элемента PictureBox; объект Rotator использует их текущие значения. В чисто иллюстративных целях я показал и то, как программно установить свойства шрифта (Bold, Italic, Underline, Strikethrough и Weight), но закомментировал соответствующие строки. Вы потом сами поэкспериментируете с ними.
Option Explicit
Dim rotTest As New Rotator '
Private Sub picTest_Click()
Dim intA As Integer■
устанавливаем параметры шрифта для элемента PictureBox
picTest.Scale (-1, -1)-(1, 1}
With picTest
.CurrentX = 0
. CurrentY = 0
With .Font
.Name = "Courier New".
. Size = 11'
’.Bold = True
‘. Italic = True. ■
'. Strikethrough = True
.Underline = True
' Weight = 1000
End With."
En With' ' -:
1 связываем объект Rotator с элементом PictureBox■
Set rot lest.Device = picTest■
' выводим одну и ту же строку разньми. углами. с
For intA = 0 То 359 Step 15; ■
rotTest.Angle = intA.
rotTest.Label Space(4) & picTest.Font.Name & Str(intA)
Next intA'
End Sub
Результат выполнения этой программы показан на рис. 14-17.
Вывести текст несколькими шрифтами в одном окне рисунка?
Элемент управления PictureBox обладает полным набором свойств для установки параметров шрифта. В отличие от элементов управления TextBox и Label, PictureBox позволяет изменять значения этих свойств «на лету», не влияя на текст, уже выведенный в окне рисунка. Вы просто указываете свойства шрифта и выводите текст, затем
модифицируете нужные свойства и выводите новый текст. Пример вывода нескольких строк разными шрифтами представлен на рис. 14-18.
Рис. 14-17 Тем п;, выведенный под разными углами с помощью объекта Rotator
1№Й I -ДЯЯМЩИШММИВ^ИЙ^ШММППё^^
АЙЗ!^
Garamond': ■
Bold, Italic, Underline, and so on
Рис. 14-18 Творческое сочетание разных стилей текста в одном окне рисунка
Поддерживая преемственную совместимость, элементы PictureBox унаследовали от прежних версий Visual Kisi- и ряд устаревших свойств вроде l ontNamc или FontSize. Однако свойство Font теперь само является объектом и соответственно предоставляет целый набор собственных свойств. Ими я и пользуюсь в программе, которая сформировала изображение, показанное на рис. 14-18. Кстати, можно создать независимый объект Font и одной командой назначить весь набор его свойств любому объекту, имеющему свойство Font. Но подробнее на эту тему см. справочную систему.
Option Explicit
Private Sub picTest_Click()"
picTest.Print "Default font properties"
picTest.Font.Name = "WingDings”
picTest. Font. Size = 18.
picTest.Print "WingDings"
picTest.Font.Name = "Arial"
picTest.Print "Arial"
picTest. Font. Name = "Garamond"
picTest.Print "Garamond" •
picTest.Font.Bold = True
picTest.Font.Italic = True
picTest.Font.Underline = True
picTest. Font. Name = "Arial"
picTest.Print "Bold, Italic, Underline, and so on"l
End Sub
Откройте новый проект и добавьте на форму элемент управления PictureBox с imeHeMpicTest. Введите показанный выше код в модуль формы и. запустив программу, щелкните окно рисунка.
ПРИМЕЧАНИЕ Возможности вывода текста несколькими < шрифтами у элемента управления RichTextBox гораздо шире.
И хотя произвольное позиционирование текста для него не ." предусмотрено, он все же позволяет выравнивать его влево, ’ вправо и по центру. Чтобы добавить этот элемент управления , на панель Toolbox, откройте диалоговое окно Components и .. пометьте флажок в строке Microsoft Rich Texlbox Control 6.0.
• Главу? 1 “Программирование элементов ТехсВох и RichTextBox» — об элементе управления RichTextBox (текстовое поле с форматированием) .
ГЛАВА 15
■ Файловый ввод/вывод
V isual Basic предусматривает несколько способов чтения и записи файлов. Я расскажу о самых эффективных и опишу несколько функций, которых часто не замечают.
П#-4. а ^^ чая> Ь 8 к# "4 -s
Эффективно переименовать, скопировать или удалить файл?
Для переименования файлов в Visual Basic предусмотрен оператор Name, для копирования — FileCopi и для удаления — Kill. Следующий блок кода демонстрирует их применение на практике. Когда Вы щелкнете форму, программа создаст простой текстовый файл FILEA, переименует его в FILEB и скопирует FILEB в FILEC.TXT г'опр- рации проходят в каталоге, где находится программа); потом выведет на экран содержимое FILEC.TXT и, наконец, удалит оба файла - FILEB и FILEC.TXT.
Option Explicit■
Private Sub Form_Click()'..
Dim strA As String" 1
Dim intFileNum As Integer
' создаем крошечный файл
intFileNum = FreeFile
Open "FILEA" For Output As flintFileNum
Print #intFileNum, "This is a test..."
Close ttintFileNum
переименовываем его
Name "FILEA" As "FILEB""
1 копируем
FileCopy "FILEB", App.Pathh "\FILEC.TXT11
' считываем и показываем содержимое конечного файла
Open Арр.Path 4 "\FILEC.TXT" For Input As IfintFlleNuin
Line Input ttintFileNum, strA
Close #lrtFileNum
MsgBox strA, vbOKOnly, "Contents of the Renamed and Copied File"
1 удаляем все файлы
Kill "FILEB"
Kill App.Path & "\FILEC.TXT"
End Sub
Как показано на рис. 15-1, содержимое конечного файла появляется в информационном окне — чтобы Вы убедились в корректности демонстрируемых операций. Щелкните кнопку ОК, и файлы FILEB и FILEC.TXT будутудалены.
Рис. 15-1 Содержимое переименованного, а затем■
скопированного файла
Дорогой Джон. как...
Работать с каталогами и путями?
В Visual Basic есть несколько операторов, имитирующих некоторые команды операционной системы MS-DOS, но не требующих выхода в ее командную оболочку или каких-нибудь еще, не менее замысловатых действий. Эти операторы рассматриваются в следующих разделах.
MkDir, ChDir и RmDir
Оператор MkDir создает новый каталог, ChDir осуществляет смену текущего рабочего каталога, RmDir удаляет каталог. Вам шакомы одноименные команды MS-DOS? Смело работайте и с этими операторами. Нет? Читайте соответствующие разделы справочной системы.
CurDir и App.Path■ - •
Используя фтакцию CurDir, приложение может определить текущий рабочий каталог на любом диске. Но зачастую важнее знать не текущий каталог, а тот, где находится приложение. Эту информацию предоставляет свойство Path объекта Арр. Например, следующие две строки кода позволят приложению определить и текущий каталог, и каталог, в котором оно содержится. В последнем обычно размещают все файлы данных, необходимые самому приложению. strCurDirectory = CurDir,.
st rAppDi rectory = App.Path.
Dir'
Функция Dir — мощное средство поиска файлов или каталогов на любом дисковом устройстве. Подробно эта функция описана в справочной системе, я же хочу обратить Ваше внимание лишь на одно. Если Вы вызываете Dir с параметрами, указывающими путь или шаблон имен файлов, а потом вызываете ее повторно, но уже без параметров, то функция возвращает следующий файл, который соответствует значениям ранее заданных параметров. Тем самым Вы
получаете возможность последовательно идентифицировать все файлы в каталоге. Эту важную особенность функции Dir демонстрирует код, приведенный ниже. Он перечисляет все обычные файлы (скрытые и системные, а также файлы каталогов пропускаются) во всех каталогах надиске С и сохраняет их список в текстовом файле FTT.PTRFF.TXT Введите этот код в модуль новой формы, запустите программу, и в текущем рабочем каталоге появится файл FILE- TREE.TXT. Закончив работу, программа автоматически завершится.
Option Explicit
Sub RecurseTree(CurrentPath As String)
Dim intN As Integer, intDirectory As Integer
Dim strFlleName As String, strDirectoryList() As String
' сначала перечисляем все обычные файлы в текущем каталоге
strFlleName = Dir(CurrentPath)
Do While strFlleName <> ""'1 r
Print til, Currentpath & strFlleName
strFlleName = Dir
Loop
теперь формируем временннй сшсок подкаталогов
strFlleName = Dir(CurrentPath, vbDirectory)'
Do While strFlleName о ""
' игнорируем текущий и родительские каталоги,
а также файлподкачки Windows NT
Il strFlleName о ”." And StrFlleName <> ".." And _ strFlleName <> "pagefile,sys" Then
игнорируем все, что отличается от каталогов
Il GetAttr(CurrentPath & strFlleName) _
And vbDirectory Then
intDirectory = IntDirectory + 1
ReDim Preserve strDirectoryList(intDirectory) strDirectorylist(intDirectory) = CurrentPath & strFlleName End II
End II
strFlleName = Dir
' обрабатываем другие события
DoEvents.,.
Loop"" "
1 рекурсивно обрабатнваем кажднй каталог'
For intN = 1 То intDirectory
RecurseTree strDirectoryList(lntN) & "\"''
Next intN' ■
End Sub■ - -
Private Sub Form.LoadO
Dim strStartPath As String
Me.Show
Print "Working..."
Me.Mousepointer = vbHourglass.
strStartPath = "C:\"
Open ” FILETREE .TXT" For Output As #1
RecurseTree strStartPath
Close 41
Me.MousePointer =vbDefault
Unload Me
End Sub
Эта программа иллюстрирует заодно и применение рекурсивных процедур в Visual Basic. В данном случае глубина вложения рекурсивных вызовов RecurseTree определяется глубиной вложения подкаталогов.
Вероятно, Вас удивило, почему RecurseTree каждый раз переопределяет динамический массив имен подкаталогов вместо того, чтобы попросту вызывать саму себя, обнаружив очередной подкаталог. Дело в том, что функция Dir, вызываемая без параметров, запоминает последний найденный ею путь и файл, готовясь к следующему вызову; причем делает это независимо оттого, откуда Вы к ней обращаетесь. Но, возвращая управление после вложенного рекурсивного вызова, Dir теряет информацию о последнем файле, найденном в текущем каталоге. Чтобы обойти эту проблему, при каждом вложенном вызове Dir я переопределяю динамический строковый массив sb'DirectoryLisl (со списком подкаталогов), сохраняя его текущее содержимое и добавляя новую строку. Здесь тот самый случай, когда какие-то особенности языковых средств могут довести до белого каления, пока не поймешь, что происходит!
После запуска этой программы найдите файл FILETREE.TXT со списком всех файлов во всех каталогах на диске С. (Потерпите — выполнение программы может затянуться на несколько минут.)
и*»* г«* о™.
ЙЙШ sifAl м| I ИВ! ■ 1 %|. . .
С: \ СОПИШЬ. СОК " " '" _~.
C:\COH3AND.6O5''’Я'
CihHSBXTCK.QLbt ' ♦ Л ’ •В
C1M1SBKTCH, 1№1 . '■■
С-ЛМПОбХЕс.ВАтК
C:\CCMTIG.STSВ'
C;\AUTDEXEC.SM5-■
C:\og73etup.log.
C:\GAGfO6.rP3'"-Щ
С^АТГОПЕС, ВАК
Рис. 15-2 Содержимое файла FUETREE.TXTe WordPad
Рис. 15-2 иллюстрирует пример содержимого FILETREE.TXT в WordPad.
Лопогой Пжон. как...
Ускорить файловый ввод/вывод?■'
Лучший способ ускорить операции файлового ввода/вывода — везде, где только можно, использовать режим двоичного доступа к файлам. В этом режиме открывается любой файл — даже текстовый.
Кроме того, чтобы повысить скорость обработки файлов, старайтесь по возможности вместо чтения и записи из одного файла в другой применять команду FileCopy.
См. также...'
Раздел «Дорогой Джон, как... эффективно переименовать, скопировать или удалить файл?» ранее в этой главе — о работе с файлами.
Раздел «Дорогой Джон, как... работать с двоичными файлами?» далее в этой главе — о двоичном режиме доступа к файлам.
Дорогой Джон, как...
Работать с двоичными файлами? ■
Файловые операции в режиме двоичного доступа в Visual Basic выполняются быстро. По возможности я стараюсь считывать и записывать файлы операторами Get и Put. Они гораздо эффективнее обычных операторов Input, Output и связанных с ними функций — даже несмотря на необходимость дополнительной обработки данных, загружаемых в переменные. А с появлением структур UDT (user-defined type) и очень длинных строк, допустимых в 32-разряд- ных системах, файловые операции в режиме двоичного доступа стали еще эффективнее. Рассмотрим их на нескольких примерах.
UDT-структуры
Такие структуры считываются и записываются в открытые для двоичного доступа файлы одним махом. Например, программа, приведенная ниже, после щелчка кнопки cmdPut помещает в текстовый файл EMPLOYEE.TXT одну и ту же запись 10 раз подряд, а после «нажатия» кнопки cmdGet — считывает содержимое этого файла, Option Explicit
Private Type Employee■
f irstName As String * 20
Middlelnitial As String * 1
LastName As String * 20
Age As Byte
Retired As Boolean■
Street As String * 30
City As String * 20
State As String * 2
Comments As String * 200.
End Type■
Private Sub cmdPut_Ciick()'
Dim Emp As Employee
Dim intN As Integer
Open App.Path & "\EMPLOYEE.TXT" For Binary As #1
For intN = 1 To 10.
Emp.Age = 14'
Emp. City = "Redmond"
Emp.Comments = "He is a smart guy,""
Emp.FirstName = "Willy"
Emp.LastName = "Doors”
Emp.Middlelnitial = "G"-
. Emp. Retired = False
Emp.State = "WA"
Emp.Street = "One Macrohard Way"
Прим, перев.г в предыдущей строке обыгрывается адрес
штаб-квартиры Microsoft - One Microsoft Way, Redmond, Ж..
Put 11, , Emp
Next intN
Closeffl.
End Sub
Private Sub cmdGet_CliCk()
Dim Emp(l To 10) As Employee
Dim intN As Integer;
Open App.Path 4 "\EMPLOYEE.TXT” For Binary As #1
For intN = 1 To 10
Get #1, , Emp(intN)
Print Emp(intN).FirstName
Next intN...
Close #1' .
End Sub
Рис. 15-3 Запись и чтение UDT-структурыданных в файл, открытый для доступа в двоичном режиме
На рис. 15-3 показано, что именно выводит программа после «нажатия» кнопок Put Data (записать данные) и Get Data (получить данные).
В данном примере программа, чтобы считать 10 записей, обращается к файлу EMPLOYEE.TXT 10 раз, но наделе считывает из него 2960 байтов. Оператор Get единовременно извлекает из файла 1 запись, а каждая запись в UDT содержит 296 байтов. 1
ВНИМАНИЕ Как правило, данные из двоичного файла следует считывать, используя туже UDT-структуру, что и при записи в этот файл. Начиная с Visual Basic 4, организация данных в памяти и в двоичных файлах усложнилась и стала непредсказуемой, что вызвано переходом на Unicode и 32-раз- рядную модель программирования.
32-разрядные версии Visual Basic внутренне манипулируют всеми строковыми данными (даже в UDT-структурах) как с двухбайтовыми символами Unicode. Выполняя операции файлового ввода/вывода, Visual Basic преобразует Unicode- символы в однобайтовые ANSI, радикально изменяя размер структур, содержащих строки.
Чтобы добиться максимально эффективного ввода/вывода в 32-разрядных операционных системах, элементы UDT- ctpvktvp выравниваются по границам, кратным двойному слову, и дополняются недостающими байтами. А это тоже сказывается на конкретной организации данных внутри UDT- структур.
Будьте осторожны!
Строки
Быстрый и гибкий способ чтения блока данных из файла — открыть его в режиме двоичного доступа, а затем с помощью оператора Get поместить весь блок данных в какую-нибудь строковую переменную. Чтобы считать из файла заданное число байтов, подготовьте строку соответствующей длины. Дело в том, что оператор Get считывает байты до тех пор, пока не заполнит строку, и поэтому, если строка состоит лишь из одного символа, будет считан всего 1 байт. По тому же принципу в строку длиной i о ООО символов оператор Get загружает из файла 10 000 байтов (если они, конечно, есть). Вот программа, которая демонстрирует этот способ на примере текстового файла TEST.TXT;
Option Explicit
Private Sub Forn_Click()■ .
Dim strA As String * 1
Open App. Path & “\TEST.TXT" For Binary As #1
Get #1, , StrA
Print strA.
Close #1'
End Sub1
Private SubText 1 _Click()
Dim strB As String
Open App.Path & "\TEST.TXT" For Binary As #1■
strB = Space(L0F(1))■ ■
Get #1, , strB
Text 1. Text = strB’
Close 81
End Sub
В процедуре Form Click программа считывает 1 байт из файла TEST.TXT в строку с фиксированной длиной 1 байт, а в процедуре TextlClick — заполняет строку strB пробелами, подгоняя ее длину под размер файла. Далее оператор Get загружает в нее данные из TEST.TXT, и, поскольку размеры переменной strB и файла совпадают, все содержимое файла связывается с этой переменной.
ПРИМЕЧАНИЕ Microsoft предупреждает нас, что этот способ опасен! Преобразования, связанные с символами Unicode, ■ могут повредить данные, загружаемые из двоичного файла в строки. Однако тестирование файлов, содержащих все 256 возможных байтовых значений, не выявило ничего неожиданного. Поскольку каждый байт файла рассматривается как ANSI-символ, при загрузке в строку он дополняется нулевым байтом и тем самым превращается в Unicode-символ. В итоге все работает так же, как и в прежних версиях Visual Basic, но что будет в последующих версиях — неизвестно. Поэто- ' му, если хотите избежать проблем и в будущем, используйте для таких операций вместо строк байтовые массивы.
Кстати, раньше, в 16-разрядных версиях Windows, длина строк ограничивалась примерно 65 535 байтами, а теперь, > Windows NT и Windows 95, — может достигать порядка 2 миллиардов байтов. Таким образом, используя оператор Get, большинство файлов можно целиком загрузить в единственную строку. Надо только убедиться перед обращением к оператору Get, что длина строки соответствует размеру файла (вызовите для этого функцию EOF, как в программе, приведенной выше).
См. также...
• Приложение Secret в главе 34 «Сложныеприложения», где на практике демонстрируется файловый ввод/вывод Б режиме двоичного доступа.
Байтовые массивы- '
Для обработки каждого байта в файле можно загрузить файл в строку (см. выше), а затем с помощью функции ASC преобразовать каждый символ строки в его числовой ASCII-код. Но с введением в Visual Basic переменных типа Byte тот же вид обработки можно реализовать гораздо эффективнее.
В следующем примере я создаю динамический байтовый массив, переопределяя его в зависимости от размера файла, и передаю оператору Get, чтобы загрузить в этот массив весь файл:
Option Explicit
Private Sub Form.,Click()■
Dim intN As Integer
Open App.Path & 'ATEST.TXT" For Binary As #1
ReDim bytBufrl To LOF(1)) As Byte
Get #1, , bytBuf()
For intN = LBound(bytBuf) To UBound(bytBuf)
Print Chr(bytBuf(lntN));
Next intN
Close #1-
End Sub
Взаимосвязь строк и байтовых массивов
Предыдущий пример намекает на определенную взаимосвязь строк и байтовых массивов. Более того, строки и байтовые массивы — в каком-то смысле два проявления одной сущности. Понимание этого факта очень важно для эффективной работы с байтовыми массивами, и я, пожалуй, задержусь на данной теме.
Программисты будут все чаще и чаще использовать вместо строк байтовые массивы везде, где необходимы операции над байтами. Переход упрощается благодаря тому, что многие команды и функции, которые раньше принимали только строковые параметры, теперь принимают и байтовые массивы. Кроме того, как показано ниже, можно присвоить строку байтовому массиву, и наоборот. Option Explicit.
Private Sub Form_Click()_
Dim intN As Integer...
Dim st i A As String, strC As String
Dim bytB() As Byte
StrA = "ABC"-
bytB() = strA
' выводится; 65 0 66 0 67 0
For intN = LBound(bytB) To UBound(bytB)
Print bytB(intN);
Next intN
strC = bytB()
' выводится: ABC
Print strC
' обратите внимание на фактическую длину строки
Print strA, Len(strA), LenB(strA)
End Sub
Щелкните форму, и программа выведет данные, представленные на рис. 15-4.
Г '^^
к- р se о о?:
All
Рис. 15-4 Операции со строками и байтовыми массивами
Заметьте, что я объявил байтовый массив bytB() как динамический. Намереваясь присваивать строки байтовым массивам, всегда объявляйте последние динамическими. Тогда Visual Basic сможет автоматически подгонять их размеры под длину строк.
Советую внимательно изучить предыдущую программу. Это поможет понять, как строковые переменные преобразуются в памяти в Unicode-символы. Трехбайтовая строка АВС копируется из strA в bytB(/а содержимое массива выводится на экран. Вместо 5 байтов со значениями 65, 66 и 67 (ASCII-коды букв Л, В и С) мы обнаруживаем 6 байтов. Почему? А потому, что грехсимвочьняя строка хранится в памяти как шестибайтовая в формате Unicode и ее содержимое в момент присвоения байтовому массиву уже не преобразуется обратно в трехбайтовое ANSI-представление. Таким образом, программа иллюстрирует, как Visual Basic манипулирует в памяти с Unicode-строками.
Далее шестибайтовый массив присваивается второй строке, strC. В конце программы я поставил команду Print, которая выводит на экран содержимое строки strA и ее длину в символах и байтах (эту информацию сообщают соответственно функции ten и LenB).
Функция SlrConv
StrConv — очень удобная функция, позволяющая управлять преобразованием строковых данных между ANSI- и Unicode-представлениями. Следующий код демонстрирует применение этой функции: Option Explicit
Private Sub Form Olick()
Dim intN As Integer Dim strA As String
Dim bytB() As Byte '--
StrA = "ABC" bytB() = StrConv(strA, vbFromUnicode) - 1 выводится: 65 66 67.
For intN = LBound(bytB) To UBound(bytB) Print bytB(intN);
Next intN
strA = bytB() ' выводится знак вопроса
'Print strA
strA = StrConv(bytB(), vbUnicode) ' выводится: ABC 3 6 Print strA, Len(strA), LenB(strA) End Sub
Рис. 15-5 иллюстрирует вывод программы после щелчка формы.
Рис. 15-5 Использование функции StrConv для строк и байтовых массивов
ПРИМЕЧАНИЕ А что если присвоить строке массив типа Byte, байты которого не дополнены нулями в соответствии с внутренним форматом Unicode? Байты будут переданы в строку, но, когда Вы попытаетесь вывести ее на экран, Visual Basic, недоумевая, что, черт возьми, делать со столь странными символами, просто заменит строку знаком вопроса.
Константы vbFromUnicode и vbUnicode — две из нескольких, определенных для функции StrConv. Информацию о них см. в справочной системе.
Разобравшись во взаимосвязи байтовых массивов и строк и в том, каке помощью StrConv управлять преобразованиями ANSI/ Unicode, Вы сможете задействовать весь потенциал файлового вво- да/вывода в режиме двоичного доступа.
Дорогой Джон, как...
Использовать объект FileSystemObject?
Библиотека Microsoft Scripting Runtime (SCRRUN.DLL) позволяет создавать, копировать и удалять папки и файлы через объект FileSystemObject. Для работы с этим объектом в приложении:
выберите из меню Project команду References — появится одноименное диалоговое окно;
установите флажок в строке Microsoft Scripting Runtime и щелкните кнопку ОК;
создайте в программе экземпляр объекта FileSystemObject-, после этого Вы сможете пользоваться его свойствами и методами для операций с хранилищами.
У объекта FileSystemObject имеются 4 подчиненных объекта, которые поддерживают следующие операции.
Области применения объектов, предоставляемых FileSystemObject
Набор
Объект
Описание
Drives
Drive
Возвращает информацию о дисковых устройствах г системе и обеспечивает доступ к подчиненным объектам.
Folders
Folder
Позволяете, сдавать. копировать, перемещать и удалять папки; сообщает местонахождение папок Temporary, Windows и System; обеспечи- ваетнавигациюпопапкам.
Files
File
Позволяет создавать, копировать, перемещать и удалять файлы, а также получать и изменять атрибуты файлов.
—•
Textstream
Позволяетзаписыватьтекстовые данные в файл или считывать их оттуда.
Иерархия объектов файловой системы сравнительно проста. Например, чтобы создать временный текстовый файл, достаточно всего нескольких строчек кода:
Sub WriteTempFiief)
Dim fsysTemp As New FileSystemObject
Dim tstrTemp As TextStream
Set tstrTemp = fsysTemp.CreateTextFileffsysTemp.GetTempName)
tstrTemp,Write "This is some temp text." End Sub
nctatw folder C \VM95System fcikkr. E swiNSIAS^TE^Tempts folder CWIN954EMP £*jfrenl toder CM ampCtwr» d^edoy ctr-teiw 3300252 tp»ienctatw folder C \VM95System fcikkr. E swiNSIAS^TE^Tempts folder CWIN954EMP £*jfrenl toder CM ampCtwr» d^edoy ctr-teiw 3300252 tp»ieFlintsFlints
nctatw folder C \VM95
System fcikkr. E swiNSIAS^TE^
Tempts folder CWIN954EMP £*jfrenl toder CM amp
Ctwr» d^edoy ctr-teiw 3300252 tp»ie
nctatw folder C \VM95
System fcikkr. E swiNSIAS^TE^
Tempts folder CWIN954EMP £*jfrenl toder CM amp
Ctwr» d^edoy ctr-teiw 3300252 tp»ie
Flints
Flints
- QrotiiiUi^ li Lr^tukon J aipenNt Г a^Fw
Рис. 15-6 ПрограммаРПе System Demo показывает. как работать с дисками, папками и файлами через объект FileSystemObject
- .Единственное ограничение объектов файловой системы в том, что у них нет методов для чтения и записи двоичных файлов. Объект TextStream, каки подразумевает его имя, предназначен для чтения и записи только текстовых файлов — построчно или целиком.
В следующих разделах на примере программы File System Demo (FILESYS.VBP) я покажу, как работать с дисками, папками и файлами, используя объект FileSystemObject. Рис. 15-6 иллюстрирует эту программу в действии.
Работа с дисками
Метод Drives объекта FileSystemObject позволяет анализировать состояние дисковых устройств в системе. Но прежде чем опрашивать дисковое устройство, Вы должны проверить свойство IsReady соответствующего ему объекта и убедиться, что данное устройство установлено в системе и что в нем есть диск. Код, приведенный ниже, создает экземпляр объекта FileSystemObject, перебирает его набор Drives и сообщает объем свободного места на дисках, готовых к работе.
' создаем экземпляр объекта FileSystemObject
Dim mfsysObject As New Scripting.FileSystemObject
Private Sub cmdCheckDrives_Click()
' объявляем объект Drive■
Dim drvltem As Drive
' добавляем заголовки в текстовое поле
txtData = "Drive" & vbTab & "Free space" & vbCrLF
' изменяем форму курсора мыши на песочные часы
MousePointer = vbHourglass
' проверяем каждое дисковое устройство
For Each drvltem In mfsysObject. Drives .■
обновляем текстовое поле. •:
DoEvents
если диск готов к работе, можно выяснить
' размер свободного места на нем
If drvltem. IsReady Then
txtData = txtData & _
drvltem.Driveletter & vbTab & _ drvltem. FreeSpace & vbCrLf
' иначе сообщаем, что диск не готов Else
txtData = txtData & _
drvltem.Driveletter & vbTab & _
"Not Ready." & vbCrLf
End If
Next drvltem
' восстанавливаем исходную форму курсора мыши
MousePointer = vbDefault
End Sub
Операторы MousePointer и DoEvents, присутствующие в этом коде, отдают должное тому факту, что проверка состояния дискового устройства может занять какое-то время особенно на CD- ROM, а также съемных и сетевых дисках. Если же Вы хотите перечислить дисковые устройства лишь с несъемными дисками, то свойство DrivcType позволит заметно ускорить эту операцию:
If drvItem.DriveType = Fixed Then■
' здесь проверяем размер свободного пространства...
End If
Ele объединяйте проверку свойств DriveType и IsReadyB одном операторе If, иначе будут выполнены оба теста и Вы не добьетесь никакого ускорения.
Работа с папками
Объект iileSysLemObject позволяет получать имена папок Windows, System и Temporary, не обращаясь к Windows АРЕ Однако у него нет метода, который сообщал бы текущий каталог, так как в Visual Basic уже есть функция CurDir Следующий код выводит информацию о некоторых папках в системе:
SubcmdListFolders_Click()‘
Dim fldObject As Folder
выводим информацию о папках’
txtData = "Windows folder: " & _
mfsysObject.GetSpecialFolder(WindowsFolder) & vbCrLf &_ "System folder: " & _
mfsysObject.GetSpecialFolder(SystemFolder) & vbCrLf & _ "Temporary folder: ” & _
mfsvsObject.GetSpecialFolder(TemDoraryFolder) & vbCrLf & _ "Current folder: " & CurDir 4 vbCrLf
' получаем объект текущей папки. . .
Set fldObject = mfsysObject.GetFolder(CurDir)
' и выводим кое-какую информацию о нем
txtData = txtData & "Current directory contains " & _
fldObject.Size & " bytes."
End Sub
Заметьте, что метод GetFolder возвращает ссылку на объект Folder, получив которую Вы можете скопировать, переместить или удалить соответствующую папку, используя методы этого объекта. Так, процедура, показанная ниже, перемещает текущий каталог в корневой, а потом возвращает его на место.
Sub HoveFolder()
Dim fldCurrent As Folder.
1 получаем текущую папку■"
Set fldCurrent = mfsysObject.GetFolder(CurDir)
1 перемещаем ее в корневую
fldCurrent.Nove ”\".-
' возвращаем на место- •. ■.
fldCurrent.Move CurDir'
End Sub'
Если Вы хотите убедиться, что этот код на самом деле перемещает пайку в корневой каталог и возвращает ее обратно, выполните его пошагово. Методы Move, Сору и Delete действуют настолько быстро, что вызывать их даже как-то страшновато!
Работа с файлами
Применяя объект FileSystemObject для создания и модификации файлов, очень важно понимать разницу между объектами File и TextStream. Первый позволяет получать и изменять местонахождение и атрибуты файла, а второй — считывать и записывать в него текст.
Следующий код получает имя открываемого файла через элемент управления CommonDialog, а затем выводит текст из этого файла в текстовое поле:
Sub cmdOpenFile_Cllck()
' объявляем объект текстового потока
Dim tstrOpen As Textstream
Dim strFileName As String
' открываем стандартное диалоговое окно
dlgFile,ShowOpen
strFileName = dlgFile. Filename
' проверяем, было ли указано имя файла
If strFileName = "" Then Exit Sub
' проверяем, нет ли уже такого файла
If Not mfsysObject. FileExists(strFileName) Then
Dim intCreate As Integer
IntCreate = Msg8ox(“File not found. Create ft?". vbYesNo)
If intCreate = vbNo Then
Exit Sub■. .
End If■ .- -
End If
' открываем текстовый поток
Set tstrOpen = mfsysObject.OpenTextFile(strFileName, _
ForReading, True)
проверяем, не нулевая ли длина у данного файла
If isirupeii.Aitnat/fStieatn fiitm
очищаем текстовое поле, но ничего не считываем,
так как у файла нулевая длина
txtData
Else
считываем и отображаем текстовый поток
txtData = tstrOpen.ReadAll
End If■
' закрываем поток
tStrOpfcn.
End Sub
Одна из интересных особенностей этого кода — вызов метода FileExists. Многие из нас привыкли проверять, существует ли тот или иной файл, через функцию Dir, которой передается имя нужного файла. Метод FileExists делает практически то же самое — только четко и в явном виду
Другая интересная особенность — использование AtEndOfStream после открытия файла, что позволяет проверить, содержит ли он какие-нибудь данные Такая проверка перед считыванием из файла очень важна, чтобы избежать ошибок из-за попыток чтения пустого или только что созданного файла.
Код, предназначенный для сохранения файла, аналогичен предыдущему.
Sub cmdSaveFile„Click()
■' объявляем объект текстового потока
Cim tstrSave As Textstream." " ' '
iDim strFileName As String
' открываем стандартное диалоговое окно
dlПKila ShowSave
strFileName = dlgFile.FileName
1 проверяем, было ли указано имя файла
Tf strFileName = Then Exit Sub
1 проверяем, нет ли уже такого файла;
If mfsysObject.FileExists(strFlleName) Then
Dim intOverwrite As Integer
1 запрашиваем подтверждение на перезапись существующего файла intOverwrite = MsgBox("File already exists. " & _
"Overwrite it?". vbYesNo)
' если пользователь выбирает No, выходим из этой процедуры
If intOverwrite = vbNo Then
Exit Sub
End If
End If
' открываем текстовый поток..,<
Set tstrSave = mfsysObject. OpenTextFile(strFileNaise, _-
ForWriting, True)
' сохраняем...
tstrSave.Write txtData.
' и закрываем
tstrSave.Close
End Sub
Работая с объектами TextStream и File, зачастую приходится самому выяснять имя используемого файла. Объект TextStream его скрывает, а ссылку на объект l ib проще всего получить через метод GetFile, который требует в качестве аргумента имя файла. Вот как узнать и сохранить имя временного файла, чтобы потом этот файл можно было удалить:
’ эта процедура создает временный файл ;•.
Sub WriteTeirpFileO
Dim fsysTemp As New FileSystemObject
Dim tstrTemp As TextStream
Dim striempName As String
' получаем имя временного файла strTempName = fsysTemp.GetTempName 1 создаем текстовый поток
Set tstrTemp = fsysTemp.CreateTextFile(strTempName) ' записываем в поток
tstrTemp.Write "This is some temp text."
закрываем его
tstrTemp.Close■
удаляем файл
fsysTemp.GetFile(strTempName).Delete
End Sub
ГЛАВАГЛАВА1 61 6
ГЛАВА
ГЛАВА
1 6
1 6
Реестр,
И.ниииа,|изиркю>ии,-(1№) файлы, в которых большинство 16-раз- рядных Windows-приложений сохраняло свои рабочие параметры, в 32-разрядных Microsoft Windows 95 и Microsoft Windows NT заменены реестром — общесистемной базой данных, предназначенной для записи аналогичных параметров всех 32-разрядных приложений. Visual Basic упрощает переход с INI-файлов на реестр, так как предоставляет целый набор функций для чтения и записи данных в реестре. Что обычно хранят в реестре? Выбранные пользователем цш ia, шрифты, позиции окон и тому подобную информацию. Здесь я расскажу о функциях, связанных с реестром, и продемонстрирую их работу.
Считывать и записывать данные в реестре?
Для операций с реестром в Visual Basic предусмотрено 4 функции: GetSetting, SaveSetting, DeleteSetting и Get All Settings. Наиболее важны, на мой взг1яд. первые три.
Эти функции позволяют получать (считывать) или сохранять (записывать) строковые данные (значения параметров) в реестре иудалятьлюбую ненужную информацию. GetAllSettings — особая функция, которая возвращает значения всех параметров из раздела реестра за один вызов. Она является расширением функции GetSetting, и я не стану рассматривать ее — читайте справочную систему.
Чтобы продемонстрировать работу первых трех функций, я создал класс Registry и небольшую тестовую программу, позволяющую получать и сохранять значения параметров в реестре. Объект, определяемый классом Registry, обладает свойствами, эквивалентны- мипapaмeтpaмфyнкцийGetSetting, SaveSettingH DeleteSetting, атак - же методами, предназначенными для чтения, записи и удаления элементов реестра в соответствии со значениями свойств. У объекта всего 4 строковых свойства: AppName, Section, Key и Setting. Свойства и методы объекта Registry реализуются так:
' REGISTRY. CLS.'.
Option Explicit
Private mstrAppName As String -щЛ ..v .0*.
"Private mstrSection As String& -‘‘
Private mstrKey As String■ .* :
Private mstrSetting As String“.
" AppName (свойство)'■ u
IPublic Property Get AppNameO As String
AppName = mstrAppName
End Property
Public Property Let AppName(ByVal strAppName As String) mstrAppName = strAppName
End Property,,, .
" Section (свойство)
Public Property Get Section() As String Section = mstrSection:
End Property'
Public Property Let Section(ByVal strSection As String) mstrSection = strSection
End Property'1- -мр-г>
. , ■
' Key (свойство)■
Public Property Get Key() As String
Key = mstrKey
End Property
Public Property Let Key(ByVal strKey As String) mstrKey = strKey
End Property_
<i: on ' Setting (свойство)
Public Property Get SettingO As String
Setting = mstrSetting
End Property
Public Property Let Setting(ByVai strSetting As String) fnctr$otfing = strSetting
End Property
' """RegGet (метод)
Public Sub RegGetQ ■mstrSetting = GetSetting(mstrAppName, mstrSection, mstrKey) End Sub
' """RegSave (метод)
Public Sub RegSaveO SaveSetting mstrAppName, mstrSection, mstrKey, mstrSetting End Sub
' ""RegDel (метод)
Public Sub RegDel{) DeleteSetting mstrAppName, mstrSection, mstrKey 'End Sub
Посмотрим, как работает экземпляр объекта Registry. Откройте новый проект стандартной ЕХЕ-нрограммы и добавьте в него модуль класса. Измените свойство Name класса на Registry, введите показанный выше код и сохраните файл под именем REGISTRY.CLS. Потом измените свойство Name стартовой формы программы на RegistryJFormlvi сохраните ее под тем же именем. (Сам проект я сохранил как REGISTRY_TEST.VBP.) Добавьте на форму 2 текстовых поля (txtKey и txtSetting) и 3 кнопки VcmdStwe.cTndGetvi cmdDeh Если хотите, вставьте и пару меток, как на рис. 16-1. Введите в модуль формы следующий код:■
Option Explicit
Dim mregTest As New Registry
Private Sub cmdSave_Click()
mregTest.Key = txtKey.Text
mregTest.Setting = txtSetting.Text
mregTest.RegSave
End Sub.
Private Sub cmdGet_CLick() mregTest.Key = txtKey.Text mregTest.RegGet mtxtSetting.Text = mregTest.Setting
End Sub
Private Sub cmdDel_Click() mregTest.Key = txtKey.Text mregTest.RegDel
End Sub.:
Private Sub Form_Load()
mregTest.AppName = App.Title
mregTest. Section = "Testing"
End Sub
Рис. 16-1 Стартовая форма проекта Registry^Test на этапе разработки- - 'Рис. 16-1 Стартовая форма проекта Registry^Test на этапе разработки- - 'Рис. 16-1 иллюстрирует форму на этапе разработки.
Рис. 16-1 Стартовая форма проекта Registry^Test на этапе разработки- - '
Рис. 16-1 Стартовая форма проекта Registry^Test на этапе разработки- - '
Заметьте, что в процедуре Form_Load4 присвоил значение свойства Title объекта Арр тестовой программы свойству AppName объекта mregTca (класса Registry) и «зашил» Б код значение свойства Section — Testing. Разумеется, Вы можете изменить эти свойства по своему желанию.
Запустив программу, введите строки в поля Key (параметр) и Setting (значение) и щелкните кнопку Regsave. Чтобы получить доступ к записанному параметру, введите его имя в поле Key, щелкните кнопку RegGet, и Вы увидите значение в поле Setting. Наконец, для удаления параметра укажите его в поле Key и щелкните кнопку RegDel.
Рис. 16-2 демонстрирует тестовую программу в период выполнения.
Рис. 16-2 Стартовая форма проекта Regis!ry Test в период выполнения
А теперь несколько замечаний насчет внутреннего кода объекта Registry. Метод RegGet вызывает функцию GetSetting, у которой есть необязательный параметр default (я предпочел его здесь не использовать). Если присвоить параметру default какую-нибудь строку, то при отсутствии в реестре указанного параметра функция GetSetting возвращает именно эту строку. А если параметр default опущен, GetSetting в той же ситуации возвращает пустую строку — этот вариант меня вполне устраивает.
Лучший способ наглядно представить, что происходит в реестре при записи в него параметров, — запустить редактор реестра, поставляемый с Windows 95 и Windows NT версии 4. «Нажмите» кнопку Start на панели задач, выберите команду Run (Выполнить), введите Regedit и щелкните кнопку ОК. Как показано на рис. 16-3, функции Visual Basic, связанные с реестром, всегда записывают и считывают параметры в разделе HKEY_CURRENT_USER\Software\VB and VBA Program Settings, и именно там Вы должны искать свои параметры. Заметьте, что свойства AppName и Section создают дополнительные подразделы для сохраняемых параметров. В данном случае значение свойства AppName равно Registry _Те$1, а свойства Section — Testing. На правой панели редактора реестра Вы увидите список параметров и их значений. Я ввел значения для трех пара-
SI О JtithMTiSI О JtithMTi•tl ■-] МютхлИ££* NetscapeЯ Г 1 ODBCG VS and VBA P-о jam Settings $L1API Dвddмt^QflLcэde,•tl ■-] МютхлИ££* NetscapeЯ Г 1 ODBCG VS and VBA P-о jam Settings $L1API Dвddмt^QflLcэde,•Stan» . . _ ^Default) ^|Apptas Й) Oranges «9 Peaches•Stan» . . _ ^Default) ^|Apptas Й) Oranges «9 PeachesDara lvalue not -ми "17 bushels" ■197 bottef” "275 crates"Dara lvalue not -ми "17 bushels" ■197 bottef” "275 crates"метров: Apples, Oranges и Peaches. Экспериментируя с программой Registry_Test, после каждого сохранения или -.дзления параметров не забывайте обновлять содержимое в окне редактора реестра. Для этого нажимайте клавишу F5 или выбирайте из меню View (Вид) команду Refresh (Обновить).
SI О JtithMTi
SI О JtithMTi
•tl ■-] МютхлИ
££* Netscape
Я Г 1 ODBC
G VS and VBA P-о jam Settings $L1API Dвddмt^QflLcэde,
•tl ■-] МютхлИ
££* Netscape
Я Г 1 ODBC
G VS and VBA P-о jam Settings $L1API Dвddмt^QflLcэde,
•Stan» . . _ ^Default) ^|Apptas Й) Oranges «9 Peaches
•Stan» . . _ ^Default) ^|Apptas Й) Oranges «9 Peaches
Dara lvalue not -ми "17 bushels" ■197 bottef” "275 crates"
Dara lvalue not -ми "17 bushels" ■197 bottef” "275 crates"
i£ Cj API Views
f+j LJ Appiieatier PelfofmanceEMplofei
ft! CJ Microsoft Visual Basic Addns
; Ф CJ MyV/ebEfWiUi
Cj Projecil
* Cj Regisiy Test
S Ji WOROPAP.IW
^ЙпмлЛЖЕ-C1JFHEH ..^ЙпмлЛЖЕ-C1JFHEH ..ih^ai».v ь л ы vt«. Fi ^m am ^jris^iaiitii-j; Ish* T e?»^ ;^T
^ЙпмлЛЖЕ-C1JFHEH ..
^ЙпмлЛЖЕ-C1JFHEH ..
Рис. 16-3 Содержимое реестра в окне Registry Editor (Редактор реестра) ,
ПриложениеМу5ауег вглаве 29 «Графика» и приложение VBClock в главе 31 «Дата и время», где тоже демонстрируется работа с реестром.
Дорогой Джон, как...
Сохранить состояние приложения?
Ответ на этот вопрос — развитие предыдущего. Для сохранения текущего состояния приложения используйте метод RegSave объекта Registry, а для его восстановления — метод RegGet. Соответствующий код лучше всего поместить в процедуры formal Шоас1и Form_ Load. Тогда при загрузке формы (но перед ее появлением на экране) можно будет восстановить ее предыдущие размеры, позицию, цвет и другие параметры, относящиеся к состоянию приложения, а перед самой выгрузкой формы — сохранить их текущие значения.
См. также...
Раздел «Дорогой Джон, как... считывать и записывать данные в реестре?» ранее в этой главе — о манипуляциях с реестром.
Приложение MySaver в главе 29 «Графика» и приложение VBClock в главе 51 «Дата и время» — о сохранении и восстановлении состояния программы с использованием реестра.
Дорогой Джон, как...
Сопоставить с приложением определенный тип файлов?
Реестр хранит информацию и о том, как Windows реагирует на события, возникающие на рабочем столе и в Windows Explorer. Например, если Вы дважды щелкнете файл MYFILE.TXT. расположенный на рабочем столе, Windows откроет его в Notepad, Связь между '1YI фаллами и NOTEPAD.EXE определена в реестре.
Чтобы проанализировать в реестре записи, определяющие связь приложения с определенным типом файлов, сначала найдите этот тип файлов в редакторе реестра (ищите в ветви HKEYCLASSESRO- ОТ). Выделите раздел, имя которого совпадает с типом файлов, и выберите из меню Registry (Реестр) команду Export Registry File (Экспорт файла реестра) — появится одноименное диалоговое окно, показанное на рис. 16-d. Укажите имя файла, убедитесь, что в разделе Export Range (Диапазон экспорта) активен переключатель Selected Branch (Выбранный раздел), и щелкните кнопку Save (Сохранить).
< 5,«Р Е^ё^^Г""^3 si й^КЯ
Л-Е^ийпце Ган•' i? ^sleeiedbryitfiЛ-Е^ийпце Ган•' i? ^sleeiedbryitfiРис. 16-4 Экспорт раздела реестраРис. 16-4 Экспорт раздела реестра•»aw».9* U4» ^:hgr*H' -fl Fl»:w] C6ftW4
Л-Е^ийпце
Ган
•' i? ^sleeiedbryitfi
Л-Е^ийпце
Ган
•' i? ^sleeiedbryitfi
Рис. 16-4 Экспорт раздела реестра
Рис. 16-4 Экспорт раздела реестра
Команда Export Registry File создает текстовый файл с расширением REG. В данном случае экспортируется содержимое разделов .txt и txtfile ветви HKEY_CLASSES_ROOTco слиянием в один REG- файл. Вот как он выглядит (конкретные результаты зависят от конфигурации системы):
REGEDIT4.- ■.
[HKEY_CLASSES_ROOT\.txt]' ”
"Content Type"="text/plain" @="txtfile"
[HKEY_CLASSES_ROOT\.txt\ShellNew]<• ".
”NullFlle"="".
[HKEY_CLASSES_ROOT\txtflle]•"
@="Text Document"
[HKEY_CLASSES„ROOT\txtfil0\DefaultIcon] @="c;\\windows\\SYSTEM\\shell32.dll,-152"
[HKEY_CLASSES_ROOT\txtfile\shen]
[HKEY_CLASSES_ROOT\txtfile\shell\open]
[HKEY_CLASSES_ROOT\txtfile\shell\open\command]
@="c:\\wlndowsWNOTEPAD.EXE КГ'
Вы можете модифицировать экспортированный файл реестра так. чтобы создать новые файловые связи. Например, следующий REG-файл формирует связь между файлами типа MYA и приложением MYAPP.EXE:
REGEDIT4
[HKEY_CLASSES_ROOT\.mya]
"Content Type"="VBRegSaniple"
@="МуАрр"
[HKEY_CLASSES_ROOT\.mya\ShellNew]
"NullFlle"=""
[H KEY_C LASSES„ЯООТ\МуАр p]
a="My File"
[HKEY_CLASSES_ROOT\MyApp\DefaultIcon]
@="c:\\VB6 Workshop\\Samples\\Chapter 16\\MyApp.EXE,0"
[HKEY_CLASSES_ROOT\MyApp\shell]
EHKEY_CLASSES_ROOT\MyApp\shell\open]
[HKEY_CLASSES_ROOT\HyApp\shell\open\command]
@="c:\\VB6 Workshop\\Samples\\Chapter 16\XMyApp.EXE X1"
Элементы в квадратных скобках — это разделы реестра. Элементы справа от знаков равенства формируют структуру данных. Символ @ перед знаком равенства указывает, что параметру присваивается имя (Default) [(По умолчанию)]. Первый элемент для файлов типа М YA сообщает Windows, что информацию о связанном с ними приложении следует искать в разделе [HKEY_CLASSES_ROOT\MyApp].
Некоторые из важных разделов реестра описаны в таблице.
Раздел
Описание
\DefaultIcon
Определяет файл, который содержит значок, отображаемый в оболочке Windows для этого типа файлов. Второй аргумент — индекс значка в списке ресурсов файла. Приложения, написанные на . - Basic, похоже, поддерживают лишь значки с индексом 0.
\Shelf\Open\Command
Командная строка, выполняемая при открытии файла этого типа из оболочки Windows. Аргумент / — имя файла, передаваемое приложению.
\Shell\Print\Command
Командная строка, выполняемая при печати файла этого типа из оболочки Windows. Аргумент й 1 — имя файла, передаваемое приложению.
Для включения этих элементов в реестр запустите Regedit с именем REG-файла в качестве аргумента. Если Вы хотите, чтобы операция прошла скрытно от пользователя, укажите ключ /s. Так, следующая командная строка импортирует г реестр файл MYAPP.REG: Regedit /s MyApp.Reg
ПРИМЕЧАНИЕ Перед модификацией реестра было бы неплохо сделать его резервную копию.
На рис. 16-5 показано содержимое разделов реестра .туаи МуАрр после импорта файла MYAPP.REG.
; '"5j Carron Туре; '"5j Carron Туре"VBfiSflS^rCia""VBfiSflS^rCia"I_J SSehNweI_J SSehNweУ L _l MbWi№ocK Weiiock 1 '-О Mifiid^reduoeLDycumirill 41- Г Д M>Wd biarCsrhan ;f - Г "I tidjAdttZciQaMlУ L _l MbWi№ocK Weiiock 1 '-О Mifiid^reduoeLDycumirill 41- Г Д M>Wd biarCsrhan ;f - Г "I tidjAdttZciQaMlNam Ssn _i*y |6isi4|'с Willi Wait-itocs sbanv^'<MwieiJ 6* u^|» £M ^t1Nam Ssn _i*y |6isi4|'с Willi Wait-itocs sbanv^'<MwieiJ 6* u^|» £M ^t141 t' 1 MjAdtiln^5m«:L |41 t' 1 MjAdtiln^5m«:L |
; '"5j Carron Туре
; '"5j Carron Туре
"VBfiSflS^rCia"
"VBfiSflS^rCia"
I_J SSehNwe
I_J SSehNwe
У L _l MbWi№ocK Weiiock 1 '-О Mifiid^reduoeLDycumirill 41- Г Д M>Wd biarCsrhan ;
f - Г "I tidjAdttZciQaMl
У L _l MbWi№ocK Weiiock 1 '-О Mifiid^reduoeLDycumirill 41- Г Д M>Wd biarCsrhan ;
f - Г "I tidjAdttZciQaMl
Nam Ssn _
i*y |6isi4|'с Willi Wait-itocs sbanv^'<MwieiJ 6* u^|» £M ^t1
Nam Ssn _
i*y |6isi4|'с Willi Wait-itocs sbanv^'<MwieiJ 6* u^|» £M ^t1
41 t' 1 MjAdtiln^5m«:L |
41 t' 1 MjAdtiln^5m«:L |
Рис. 16-5 Содержимсхразделов .mva и МуАрр после импорта файла MYAPPREG
Извлечение аргументов из командной строки
Теперь, когда Вы узнали, как ввести в реестр информацию для команд Open, Print и другие данные, посмотрим, как извлечь и использовать аргументы, указанные в командной строке. Приложения, реагирующие на Open, Print и прочие команды, отдаваемые из оболочки Windows, могут принимать аргументы из командной строки через функцию Command, встроенную в Visual Basic. Вот пример кода: Dim mstrFile As String
Private Sub Form_Load();
' получаем аргумент из командной строки
mstrFile = Command()
если аргумент был, чего-то делаем
if Len(mstrFile) Then
OpenFile 1 или что-то еще
End If
End Sub
Пример извлечения нескольких аргументов из командной строки см. в справочной сисгеме — там, где описывается функция Command.
• Книгу Гюнтера Борна (Gunter Born) Inside the Microsoft Windows 98 Registry (Microsoft Press, 1998) — подробнее о реестре.
ГЛАВАГЛАВА1 71 7
ГЛАВА
ГЛАВА
1 7
1 7
Справочные системы, подсказки и мастера
Средства, помогающие работать с приложением, сопровождают пользователя на всем пути от первого значка до последнего экрана. Подсказки (ToolTips), строки состояния, мастера и справочные системы разных видов — все это ключ к тому, как сделать приложение естественным и понятным. Некоторые из этих средств встроены в Visual Basic, но другие найти не так-то просто. I кшример, утилита Microsoft Help Workshop (HCWEXE) спрятана в каталоге \COM- MON\TOOLS на компакт-диске Visual Studio, a Microsoft HTMT Help Workshop (HHW.EXE) — в самораспаковывающсмся ZIP-архиве HTMT- HELP.EXE в каталоге \HTMTHETP на том же компакт-диске.
В этой главе я расскажу о встроенных в Visual Basic средствах и о создании справочных систем, а том числе в новом HTML-формате. Но не ждите от меня подробных инструкций по созданию справочных файлов — такой объем материалов не втиснуть в рамки единственной главы. Тем более что и Help Workshop, и HTML Help Workshop имеют свои справочные средства (кстати, очень хорошие). Я также поясню, как интегрировать справочную систему в приложение, написанное на Visual Basic. Никогда еще эти приемы не были собраны в одном месте, а теперь вот они — здесь!
д^пог^^ ~ - как..
Добавить подсказки по интерфейсным объектам?
Такие подсказки сообщают названия интерфейсных объектов (кнопок на панелях инструментов, элементов строк состояния и др.). Подсказки появляются, когда пользователь на какое-то время останавливает курсор мыши над каким-нибудь объектом (рис. 17-1).
Все элементы управления, встроенные в \ isual Basic и содержащие визуальные компоненты, имеют свойство ToolTipText От Вас требуется лишь ввести текст в это свойство.
Большинство ActiveX-элементов тоже предусматривает свойство ToolTipText, но те, у которых есть подчиненные компоненты (например, Toolbar или StatusBar), позволяют формировать подсказки через диалоговое окно Property Pages. Так, чтобы добавить подсказки к кнопкам панели инструментов:
вставьте на форму элемент управления Toolbar;
в окне Properties дважды щелкните свойство Custom. Visual Basic откроет диалоговое окно Property Pages для элемента управления Toolbar;
перейдите на вкладку Buttons этого диалогового окна;
щелкните Insert Button, чтобы добавить кнопку к панели инструментов;
в поле ToolTipText введите текст подсказки для этой кнопки;
закончив добавление кнопок к панели инструментов, щелкните ОК.
Подсказки для отдельных секций строк состояния формируются аналогичным образом.
СЗ@ВЕЯвИЯ^^ЯВМЯв111^вЕЕЙ
Рис. 17-1 Так выглядит подсказка в работающей программе
Приложение Jot в главе 32 ■ ;<: -^ данных» — в нем используются подсказки по интерфейсным элементам.
Главу!5«Визуальный интерфейс» о создании панелей инструментов.
Дорогой Джон, как...
Добавить строку состояния в программу?
Используйте для этого элемент управления ScatusBar, содержащийся в MicrosoftWindows Common Controls 6.0 (MSCOMCTL.OCX; Он формирует окно — обычно в виде полоски, занимающей нижнюю строку формы, — и содержит до г- объектов Panel. Последние обладают рядом свойств, позволяющих выводить текст или предопределенные данные (например, автоматически обновляемые время и дату). В каждой секции (panel) текст можно комбинировать с картинками. Для определения числа секций и их свойств служит диалоговое окно Property Pages, которое открывается одним из двух способов: поместив элемент StatusBar на форму, щелкните его правой
кнопкой мыши и выберите из контекстного меню команду Properties или, выделив размещенный элемент StatusBar, выберите в окне Properties свойство Custom и щелкните появившуюся кнопку с многоточием.
На рис. 17-2 показан пример формы с тремя элементами управления StatusBar: один выровнен по ее нижней < л - ч.< остальные два — по верхней. Многие свойства элемента StatusBar и связанных с ним объектов Panel подробно описаны в справочной системе, и я не стану задерживаться на них.
«0 См. также...
Приложение Dialogs в главе 34 «Сложныеприложения», где демонстрируется использование строки состояния.
Рис. 17-2 Пример формы с тремя элементами управления
StatusBar
дорогой Джон, как...
Выводить при запуске «Совет дня»?
Функция «Совет э - ■ (Tip of Ин- Day) в приложениях Microsoft Office помогает новичкам осваивать оптимальные способы работы, удобные комбинации клавиш и т. д.
В Visual Basic имеется шаблон для создания форм Tip of the Day. Пример одной из таких форм показан на рис. 17-3.
Чтобы добавить в приложение форму Tip of the Day, придерживайтесь следующей схемы:
Выберите из меню Project команду Add Form — появится одноименное диалоговое окно.
Укажите шаблон формы Tip of the Day и щелкните кнопку Open. Visual Basic добавит в проект форму, основанную на шаблоне frmTip.frm.
Напишите код, отображающий форму Tip of the Day при запуске приложения.
Создайте файл TIPOFDAY.TXT и сохраните on в каталоге своего проекта.
Рис. 17-3 Программа Tip of Иге Day (TipOJDayVBP) демонстрирует вывод «Совета дня» при запуске
Сама форма Tip of [he Day никакого дополнительного кода не требует, но нужно учесть следующее.
и Форма Tip of the Day сохраняет состояние флажка Show Tips At Startup (советовать при запуске) в реестре — это дает возможность пользователю включать или отключать вывод «Совета дня». Желательно, чтобы то же самое можно было делать через пользовательский интерфейс Вашего приложения.
■ Поскольку/т/77р способна выгружаться сама, Вы должны предусмотреть контроль ошибок при вызове метода Show или проверять реестр, прежде чем пытаться загружать/гтГ/р.
я Если Вы хотите застраховаться от модификации файла TIPOF- DAY.TXT или его случайного удаления, храните соответствующий текст в файле ресурсов, который компилируется вместе с приложением.
Приложение-пример TipOfDay.VBP, содержащееся на моем компакт-диске, демонстрирует, как учесть все эти соображения.
г ■ ■ гой Джон, как...
Использовать мастера?
Мастера сопровождают пользователей в пошаговом выполнении сложных операций. И для таких задач создание мастеров зачастую куда эффективнее, чем написание пухлой документации. Любые сложные, но линейно решаемые задачи — особенно операции типа ♦раз-сделал-и-забыл» (например, конфшурированпс приложения) — вот кандидаты на выполнение »под руководством» мастеров.
Visual Basic предоставляет надстройку Wizard Manager, которая упрощает создание мастеров, генерируя для них шаблон. Последний содержит в стандартном модуле блок кода, позволяющий задействовать мастер как надстройку Visual Basic. Вы можете удалить этот
блок (или просто игнорировать) и использовать шаблон для создания мастеров любых других типов.
VBCal Wizard (рис. 17--), демонстрирует, как создать приложение-мастер на основе нескольких форм.
vbcal WIZARD.
VBCAL WIZARD'
VBCAL WIZARD
Рис. 17-4 Мастер VBCal Wizard (VBCalVBP) выводит экраны Introduction. Step и Finished
Мастера могут быть автономными программами или частью большого приложения. В чем разница между обычной программой и мастером? Мастер — это приложение, которое отличают следую- щиепризнаки.
Экран Introduction, где описывается выполняемая операция.
Здесь же следует сообщать и о любых предварительных действиях, необходимых для проведения данной операции.
Отдельные экраны на каждый этап операции. Мастер последовательно выводит экраны, соответствующие текущим этапам, и обеспечивает переход между ними в обоих направлениях.
Прежде чем перейти к следующему этапу, мастер проверяет данные, введенные на текущем экране.
Экран Finished при завершении работы мастера. Информирует пользователей об успешном выполнении операции.
• Приложение VBCal в главе 31 «Дата и время» — полное описание мастера VBCal Wizard.
Доро^
Создать справочный файл в формате WinHelp?
Help Workshop — большой шаг вперед по ' равнению с устаревшим Help Compiler, работавшим в режиме командной строки. Взять хотя бы то, что Help Workshop без проблем справляется с большими исходными файлами, a Help Compiler, обрабатывая файлы с графическими изображениями, сообщает о нехватке памяти, едва их размер превысь । некий предел. Кроме того. Help Workshop поддерживает файлы с длинными именами и графический пользовательский интерфейс (рис, г . э;.
Рис. 17-5 Microsoft Help Workshop (HCW-EXH)Рис. 17-5 Microsoft Help Workshop (HCW-EXH)
Рис. 17-5 Microsoft Help Workshop (HCW-EXH)
Рис. 17-5 Microsoft Help Workshop (HCW-EXH)
Help Workshop создает файл проекта справочной системы (* HPJ), в котором перечисляются файлы исходных документов, составляющих справочный файл. Исходные документы должны быть сохранены в формате RTF. Вы можете создать их в Microsoft Word или другом текстовом процессоре для Windows, поддерживающем RTF- формат. Help Workshop запускает компилятор (HCRTF.EXE) и по окончании трансляции выводит своего рода отчет с примечания-
ми, предупреждениями и сообщениями об ошибках (если они были), как показано на рис. 17-6.
’■v'ufkfifiAptrviiiltChapler C.nf
HClOi1 Note:*4 of ..V.\..\V06Workghop^Print^hapter 8,rf ;
A рл;.erisformaWd as a bidden disiracter.
НСЖв: Warning; topic №ol ..И^б Wwkshop^rinyiCheptef-fl.rtf NonscrolHng region defined after scrolfing region.
: HC 3030: Warning:
The following Topic Us are net defined in the JMAP1 section af the , IDH_ChaplEf_ ft Topic 1 Of ^V'V-W^WarkshoptPnn^Chaptertl j HCI01(l:Nifl«;
Thefollowina mapped Topic IDs were not used in any topic;
1DH_FOCLOSEMENU...
Рис. 17-6 Отчет, выводимый после компиляции справочного файла
Базовые принципы подготовки исходного текста
Тематические разделы г исходных документах разделяются принудительными разрывами страниц . Чтобы указать заголовок раздела, ключевые слова и уникальный идентификатор, связывающий этот раздел с другими, Вы применяете механизм сносок, а для создания связей с другими разделами — подчеркивание и скрытый текст. Рис. 17-7 иллюстрирует раздел справочной системы с его компонентами.
Какие сноски и элементы форматирования можно вводить в исходный текст для справочного файла, показано в таблице ниже.
Сноски и элементы форматирования, допустимые в исходном тексте
Сноски и элементы
форматированияОписание
$Заголовок раздела.
*Идентификатор ]^аздел ащспользуетсядля связы
вания данногоразделас другими.
ККлючевые слова для системы поиска. Они появят-
ся в Index (предметном указателе) справочного
файла для данного раздела. Ключевые слова отделяются друг от друга точками с запятыми. С помощью макроса KLink можно создать связи между ключевыми словами.
см. след. стр.
Прим, перев.: В MicrosoftWord принудительный разрыв страницы вставляется нажатием клавиш Ctrl+Enter.
Сноски и элементы форматирования, допустимые в исходном тексте (мродаосения)
Сноски и элементы
форматирования Описание
+Последовательность просмотра. Числа, указывае
мые в таких сносках, определяют порядок разделов в справочном файле.
АСвязанное ключевое слово. Аналог сноски К, но
текст сноски не появляется в Index. Эти сноски
IIДвойное подчеркиваниеДвойное подчеркиваниеОдинарное подчеркивание Скрытый текстОдинарное подчеркивание Скрытый текстиспользугтся совместно с макросом л: ।;jи для создания связей между ключевыми словами.
I
I
Двойное подчеркивание
Двойное подчеркивание
Одинарное подчеркивание Скрытый текст
Одинарное подчеркивание Скрытый текст
Макрос. Текст мифл введенный в текст сноски, выполняется при отображении раздела.
Связь с другим разделом. За ней должен быть скрытый текст, указывающий идентификатор раздела, в который следует перейти по данной связи (ссылке).
Связь для вывода всплывающих подсказок (с определениями i. । > .и пь т и т. д.).
Идентифика ri' раздела, на который указывает связь. Должен соответствовать содержимому сноски одного из разделов в проекте справочной системы — иначе при компиляции появится сообщение об ошибке.
Kf ъ^'Л ОД-лг-й
-r?*3E>e Mt ^ ^т tttiibir Ir^if •^ •,^> ^
О^Ы S3i
^^iXim * Ат
и i И
til® fit' ^inkgeet. Qi^fu ЦНр/ -
‘ ■ Т his ■ Is a Topic ЧШеЦ
Toptc iexl, topic, text whoops. э-l.inklDH LinklH
Моте-Тян .'ПТОГ»1яИ/Ду|Ц1у^^1рн_Ц|Г]<| ^
Here i% я rtticra-iirtkU
-hse*»*-
More text, |тюг*1еЯ, А^ц^^Н;
Here is a rracro link
Зйа^МЙЯЕо&ЦПк.ЙТ
I: ^Mr)
’IDKjHvjfl
* Tins ls'8 TQp:C ТЦеЦ■•
♦ 1СКЧ
Рис. 17-7 Анатомия раздела справочной системы
Создание файла проекта
Текстовый файл проекта создается в Help Workshop и содержит информацию о файлах, включаемых в справочный файл, а также ряд других параметров. Ему присваивается расширение HPJ. Чтобы создать новый файл проекта, выберите из меню File команду New — появится одноименное диалоговое окно. Выберите в нем строку Help Project и щелкните кнопку ОК В диалоговом окне Project File Name введите имя для нового файла проекта, укажите каталог, в котором он будет храниться, и щелкните кнопку Save. На рис. 17-8 показано, как выглядит окно созданного проекта.
В окне проекта имеется набор кнопок, открывающих диалоговые окна, в которых задаются те или иные параметры справочного файла. Так, щелкнув кнопку Files, Вы можете добавить в файл проекта любые файлы с исходным текстом.
Рис. 17-8 Окно проекта в Help Workshop
Компиляция и тестирование справочного файла
В данном случае компиляция заключается в том, что из файла проекта и файлов с исходным текстом и графикой формируется собственно справочный файл. Щелкните кнопку Save And Compile (сохранить и откомпилировать) в окне проекта, чтобы запустить компилятор HCRTF.EXE. После компиляции появляется окно с отчетом (в нем \ь >,'vt быть какие-то примечания, предупреждения и сообщения об ошибках). Для проверки созданного справочного файла выберите из меню File команду Run WinHelp (запуск WinHelp), а потом щелкните кнопку View Help (просмотр справочного файла) в диалоговом окне View Help File.
Связывание идентификаторов
Вы вводите идентификаторы разделов в справочном файле как строки. Например, в разделе, описывающем меню File, поставьте сноску * и введите 1DH РНеМепи.Однако в приложении идентификатор раздела указывается в числовом виде. Скажем, в свойстве HelpCcmicxiID для меню I Ос- можно ввести 1001. Help Workshop связывает имя идентификатора с его номером в разделе [МАР] файла проекта справочной системы:
[МАР]''
IDH_FILEMENU=1OQ1; идентификаторы всех меню начинаются с 1000
IDH„FILE0PENMENU=1002
IDH_FILECLOSEMENU=1OO3
Синхронизация содержимого раздела [МАР] с тем, что используется в приложении, очень важна. Некоторые утилиты вроде HelpBreeze генерируют BAS-модуль с этими значениями в виде констант. То же самое легко сделать с помощью перечислимого типа:
Enum HelpContext
IDH_FILEMENU=1001 ; идентификаторы всех меню начинаются с 1000
IDH_FILE0PENMENU=1002
I0H_FILECL0SEHENU=1003
End Enum
Полнотекстовый поиск и оглавления
В справочных системах приложений для Windows 95 и Windows NT появились две новинки: полнотекстовый поиск и оглавления. Полнотекстовый поиск поддерживается 32-разрядным средством просмотра справочных систем (WINIILP32.EXE). При просмотре в WIN- HLP32.EXE эту возможность поддерживают даже справочные файлы, скомпилированные в расчете на Windows 3.1.
С помощью Find Setup Wizard (мастер настройки поиска) операционная система Windows автоматически формирует по справочному файлу словарную базу данных для поиска (рис. 1?-9)
Список Help Contents (содержание справочной системы) формируется из текстового файла, указанного в разделе [OPTIONS] файла проекта. У текстового файла оглавления очень простой формат, например:.
; фрагмент файла HCW.CNT
Getting Started ; раздел верхнего уровня (не является связью)
What is Help Workshop?=BAS_THE_HELP_COMPILER ; раздел второго уровня
Негр Workshop components=SDK_FILES_DOCS
Notational Conventions=SDK-NOTE_CONVENTION
What's New In Version 4.0.'/
WinHelp 4.0'
Redesigned User Interface ; раздел третьего уровня
New Help Topics dialog box=NEW_WINHELP_INTEHhAUb
New context menu=NEW_WINHELP_CONTEXT_MENUS
New Options menu=NEW_WINHELP„OPTIONS_MENU
New annotation capabilltles=NEW_ANNQTATION
Improved copying=NEW_WINHELP_COPY
New printing options=NEW_WINHELP_PRINTING
New ESC key function=NEW_KEY
Background color override=NEW_COLQR
Рис. 17-9 Окно Find Setup Wizard
Для модификации оглавления откройте его исходный файл (CNT) в Help Workshop. Эго позволит редактировать оглавление в том виде, в каком оно появится в справочной системе (рис, 17-10).
Рис. 17-10 Образец текстового файла оглавления
Чтобы создать новый файл оглавления в Help Workshop, выберите из меню File команду New.
Создание окон справочной системы
Ваш справочный файл может отображать тематические разделы в специфических окнах справочной системы, определенных в Help Workshop. Для этого щелкните кнопку Windows в окне проекта и заполните поля в диалоговом окне, показанном на рис. 17-11*.
!Я1™е|«Ч^| k*wn| С* ] iw™(
KMwitpe ГД3
t"' ■-
„ CW»* I.
'У Aj&4i£Bhei$#» Г=
t- — - - —- —=
or "1 J < ■
Рис. 17-11 Диалоговое окно Window Properties, используемое для определения окон справочной системы
Чтобы вывести раздел именно в том окне, которое определено на рис. 17-11, введите >ОпТорза идентификатором данного раздела в скрытом тексте связи. Тогда, щелкнув связь, Вы откроете раздел в окне ОпТор. Аналогичным образом добавляются имена окон и в элементы cN'I-файла: просто наберите >Ои7о/шосле идентификатора нужного раздела. Добавление сноски > в раздел приводит к тому, что после выбора из Index он отображается в нестандартном окне.
Использование макросов
Макросы выполняют в справочной системе особые операции. Вы можете запустить макрос при отображении какого-либо раздела, а также при щелчке кнопки или связи. Чтобы закрепить макрос за связью, предварите его имя сноской ' в скрытом тексте связи. Например, See Atso'KLhikfAPLWinHelpj— когда пользователь щелкнет строку See Also — запустит макрос KLink, а тог откроет список разделов с ключевыми словамиЛР/и WinHelp.
Синеок доступных макросов см. Б справочной системе Help Workshop (HCW.HLP).
Прим, перев.: Сначала Вам придется указать ими окна в диалоговом окис Create a Window и только после этого появится диалоговое окно, показанное на рис. 17-11.
This Is a Topic Title
Topic text, topic, isxtwhoops, a L-nl
Mote text, more text, Anathei ynk
Here /s a macro Jink
Рис. 17-12 Диалоговое окно Topics Found co списком разделов, содержащих указанные ключевые словаРис. 17-12 Диалоговое окно Topics Found co списком разделов, содержащих указанные ключевые слова
Рис. 17-12 Диалоговое окно Topics Found co списком разделов, содержащих указанные ключевые слова
Рис. 17-12 Диалоговое окно Topics Found co списком разделов, содержащих указанные ключевые слова
См. также...
Microsoft Wiadoи-- 95 HelpAutbormg Kit (Microsoft Press, 1995) — подробнее о Windows Help и работе c Help Workshop.
Дорогой Джон, как...
Подключить справочный файл к проекту через API-функцию WinHelp?
Вы можете активизировать справочный файл прямым вызовом API- функции WinHelp — это проверенный временем, но старомодный способ. При этом Вы должны вставить в свою программу объявление функции WinHelp, как показано ниже. В данном примере после того, как Вы щелкнете форму, на экране появляется окно с разделом Contents справочной системы Windows Notepad.
Option Explicit
Private Declare Function WinHelp _
Lib ”user32" Alias "WinHelpA” ( _
ByVai hWnd As Long, .
ByVai IpHelpFiLe As String, _
ByVai «Command As Long, _
ByVai dwData As Long
) As Long
Const HELP_TAB = &HF
з 16 часть 11 дорогой джон, как? ..
Private Sub Forni_Click()
Dim IngX As Long'
IngX = WinHelp(hWnd, "notepad-hip", HELPTAB, 0)
End Sub
В параметре bWndпередается значение свойства hWnd формы Вашего приложения. Параметр IpHelpFile— строка, содержащая имя активизируемого справочного файла, а «'Command— одна из предопределенных констант, управляющая работой функции WinHeip, Большую часть этих констант Вы найдете, во-первых, в Object Browser, выбрав MSComDh- (при ловня. что на панель Toolbox добавлен элемент управления CommonDialog), во-вторых, в справочной системе Visual Basic (см. Help Constants) и, в-третьих, в таблице, приведенной в конце этого раздела. Наконец, смысл параметра dwData зависит от значения параметра wCommand. В частности, если Вы передаете в wCommand константу cdtHelpCuHtv.xt.значение dwData определяет, какой раздел справочного файла отображается на экране.
Для наглядности вызовем функцию Winllelp так, чтобы она активизировала справочный файл Windows Notepad, а затем показывала его конкретный раздел. (Вам придется добавить на панель Toolbox элемент управления CommonDialog.)
Private Sub Гопп_С11ск( )
Dim IngX As Long
IngX = WinHelp(hWnd, "notepad, hip", cdIHelpContext, 1001}
End Sub
В параметре wCommand я указал константу cdIHelpContext, а в dwData — 1001. При запуске справочного файла на экране появится раздел по колонтитулам (header and footer), так как номер 1001 соответствует именно этому разделу. (Некоторые утилиты вроде RoboHclp сами создают BAS-файл, в котором содержатся константы для всех разделов; это здорово экономит время.)
Как я уже говорил, поведение функции WinHelp зависит от констант, указываемых в параметре wCommand, — наиболее распространенные из них приведены в таблице ниже. (Большая часть этих констант определена в Visual Basic для элемента \ правления CommonDialog, откуда и появились префиксы cdl.) Например, чтобы вывести на экран справку о том, как пользоваться самой справочной системой, поставьте константу cdlHetpHetpOnHelp
Константа
Значение
Описание
cdIHelpContext
1
Отображается конкретный раздел.
cdlHelpQnit
2
Закрывается указанный справочный файл.
cdlHelplndex
3
Отображается предметный ил.затсль заданного справочного файла.
Константа
Значение
Описание
HELP FINDER
11
Отображается диалоговое окно Help Topics и активизируется вкладка, выбранная в прошлый раз. Эту константу Вы должны задать ■ • с так как она не относится к числу встроенных.
HELP _ TAB
15
Отображается диалоговое окно Help Topics и активизируется вкладка, чей индекс определяется параметром dwData (0 = вкладка Contents, -2 = вкладка Index и -1 = вкладка Find). Эту константу Вы должны задать ими. так как она . относится к числу встроенных.
cdlHelpConients
3
Отображается раздел Contents текущего справочного файла. Эта константа определена для совместимости со справочными системами ранних версий, новые приложения должны открывать диалоговое окно Help Topics, используя константу HELP FINDER или HELP TAB.
cdlHelpHelpOnHelp
4
Отображается «справка по справке* (т. е. сообщается, как работать с самой справочной системой).
cdlHelpSetlndex
5
Устанавливается текущий индекс для мульти-индсксныч справочных систем.
cdlHelpSetCon ten ts
5
Указанный раздел становится оглавлением (Contents).
cdlHelpContextPopup
8
Отображается раздел, идентифицируемый номером контекста.
cdlHelpForceHle
9
Создается справочный файл, в котором текст отображается лишь одним шрифтом.
cdiHelpKey
257
Отображается справка по конкретному ключевому слову.
cdlHelpCommand'Help
258
Отображается справка по конкретной команде.
cdlHelpPartialKey
261
Вызывается система поиска в Windows Help.
Эти константы предопределены для элемента управления Сот- monDialog. Вы можете скопировать их с свою программу через Objct i Browser либо включить в нее элемент управления CommonDialog (тогда станут доступны все константы с префиксом cdl). В любом случае выберите из меню Project команду ; ^mnonenfs и пометьте флажок в строке Microsoft Common Dialog Control 6.0.
См. также...■
• Приложение Lottery в главе 29 «Графика», где демонстрируется работа с функцией WinHelp.
^[|П1411#>ГПМ Л-M=t^ti *fSikr .
Подключить к проекту контекстно-зависимую справку?
Для форм, компонентов меню и большинства элементов управления предусмотрено свойство MelpContcxtl]). что позволяет при нажатии клавиши F1 переходить в раздел справочной системы, соответствующий данному контексту. Свойство HelpFile объекта Арр указывает полное (вместе с путем) имя справочного файла для всего приложения, а элемент, на котором сосредоточен фокус ввода, определяет, какой справочный раздел должен быть активизирован при нажатии клавиши ! I Если свойство HelpContextID элемента управления равно 0 (по умолчанию), проверяется значение аналогичного свойства его контейнера или формы. Наконец, если и у формы свойство HelpContextID равно О, активизируется раздел Contents справочного файла.
Эта схема прекрасно срабатывает при доступе к контекстно-зависимой справке, вызываемой нажатием клавиши F1, но вот вопрос: как активизировать ■ правочный файл программно, без нажатия F1? Тут пригодится один хитрый трюк (он, кстати, делает свойство HelpContextID более ценным). Просто эмулируйте нажатие клавиши F1, используя оператор SendKeys. Он заставляет Windows посылать сообщения о нажатии клавиш окну, на котором в данный момент сосредоточен фокус. В итоге программа реагирует так, будто клавиша F1 действительно нажата.
Этот прием демонстрируется следующим фрагментом кода:
Private Sub Form_Load()
Арр.HelpFile = "notepad.hip"1
1 End Sub,.
Private Sub cmdHelp_Click()
cmdHelp.HelpContextID = 1001"■
SendKeys "{F1}"* "'
End Sub
Здесь справочный файл указывается в процедуре Form Load, но, если Вам нужен доступ к нескольким справочным файлам, соответствующие путь и имя можно задать в любой точке программы. В процедуре ■. mdHelp_Click Вы активизируете конкретный раздел справочного файла, записывая в свойство HelpContextID номер контекста этого раздела и посылая оператором SendKeys сообщение о нажатии клавиши I I Свойства HelpFileи HelpContextID мож-
но настраивать в период выполнения, как показано в данном примере, либо на этапе разработки.
У элементов меню тоже есть свойство HelpContcxtID, но заданный раздел справочного файла активизируется, только если выделен какой-то элемент меню и клавиша F1 нажата вручную. Если Вы щелкнете команду меню и выполните оператор SendKeys в процедуре обработки события, как показано ниже, то активизируется раздел справочного файла, определяемый свойством HelpContextID, — но не для команды меню, а для элемента управления, на котором сосредоточен фокус ввода:
Private Sub mnuHelp_Click()
SendKeys "{F1}"
End Sub''
Это связано с тем. что после щелчка меню тут же исчезает и фокус ввода возвращается на элемент управления до того, как оператор SendKeys успевает послать сообщение о программном нажатии клавиши F1. В то же время команда меню нормально выводит контекстно-зависимую справку для любого элемента управления — это кажется странным, пока не разберешься в последовательности событий.
Дорогой Джон, как...
Подключить справочный файл к проекту через элемент управления CommonDialog?
Элемент управления CommonDialog обеспечивает гибкий доступ к справочным файлам. Добавьте его на форму, установите соответствующие свойства (чтобы они указывали на нужный справочный файл) и активизируйте справочную систему методом ShowHelp. Вот и все — даже не придется открыва ть какое-нибудь диалоговое окно с помощью этого элемента. Следующий код активизирует справку по справочной системе самой Windows при щелчке кнопки cmdHeip- Conteuis;
Private Sub cmdH01pContents_Click()
dlgCommon.HelpCommand = cdlHelpHelpOnHelp
dlgCommon.ShowHelp
End Sub
ПРИМЕЧАНИЕ Если Вы запишете в свойство HelpCommand константу cdlHelpConteHts.Yio Windows не найдет файл оглавления, на экране появится первый раздел справочного файла.
И еще один пример, где при щелчке элемента меню активизируется заданный раздел справочного файла:
Private Sub mnuHelp_Click()
dlgCommon. HalpFile = "‘notepad, hip"
dlgCommon.HelpComrnand = cdlHelpContext
dlgCommon.HelpContext = 1001■
dlgCommon.ShowHelp■
End Sub
Дорогой Джон, как...
Включить режим WhatsThisHelp?
Windows 95 привнесла в работу справочных файлов несколько интересных особенностей. Одна из них — режим WhatsThisHelp, В заголовке формы, у которой включен этот режим, появляется кнопка со знаком вопроса. Ее * нажатие" дополняет стрелку курсора мыши знаком вопроса. Щелкнув таким курсором какой-нибудь элемент на форме, пользовательактивизируетвсплывающую подсказку, предусмотренную программистом для этого элемента.
Режим WhatsThisHelp включается очень просто. Сначала установите свойства WhaisThisButron и WhatsThisHelp формы как True. Потом измените значение ее свойства BorderStyle на - Fixed Single или 3 - TixedDialog После этого в заголовке формы появится кнопка со знаком вопроса (рис. 17-13).
Рис. 17-13 Кнопка со знаком вопроса активизирует режим - ■WhatsThisHelp
Чтобы определить, какой именно справочный файл будет активизироваться, запишите в свойство HelpFile объекта Арр путь и имя нужного файла. Сделать это можно в период выполнения или на этапе разработки. В последнем случае выберите из меню Project команду Project Properties, в появившемся диалоговом окне щелкните ярлычок General (общие) и в поле 1 Г ip File Name введите полное имя интересующего Вас файла. Для определения справочного файла в период выполнения присвойте его полное имя свойству App.HelpFilc.
Наконец, чтобы указать раздел, отображаемый для данного элемента как всплывающая подсказка, запишите в его свойство Whais- ThisHelpID числовой идентификатор нужного раздела.
Метод WhatsThisMode
Существует и другой способ включения всплывающих подсказок WhatsThisHelp. Вызовом метода WhatsThisMode формы можно программно активизировать специальный курсор мыши со знаком вопроса. Это удобно для включения режима WhatsThisHelp на форме,
чье свойство BorderStyle установлено как 2 ~ Sizable. Рис. 17-14 иллюстрирует форму, у которой режим WhatsThisHelp включен щелчком кнопки Get Help.
Рис. 17-14 Режим WhatsThisHelpcjjopMbi можно активизировать и методом WbatsThisMode
Когда Вы «нажимаете» кнопку Get Help, курсор мыши дополняется знаком вопроса и активизируется режим WhatsThisHelp — так же, как при щелчке кнопки со знаком вопроса в заголовке формы из предыдущего примера. Чтобы реализовать этот способ, мне понадобилось лишь установить свойство WhatsThisHelp формы как True и добавить в процедуру обработки события Click кнопки Get Help единственную строку кода:
Private Sub cmdGetHelp_Cllck(>
WbatsThisMode..1
End Sub■.
Дорогой Джои, как,,.
Создатьсправочнуюсистемувформате HTML Help?
Если Вы только приступаете к созданию приложения, подумайте о разработке для него справочной системы в попом формате Microsoft НТМЕ Help. Именно он используется во всех справочных системах Visual Studio и новой операционной системе Windows 98. Хотя WINHEP32.EXE будет работать и в Windows 98, и в будущих операционных системах Microsoft, его уже вряд ли станут развивать и совершенствовать. Теперь все внимание Microsoft в области разработки справочных систем сосредоточено на HTML Help. Пример справочной системы в этом формате показан на рис. 17-1 >.
Вы можете подготовить справочный файл в формате HTML Help с помощью Microsoft HTML Help Workshop — эта утилита содержится в самораспаковывающемся 7.1Р-архиве HTMLHELP.EXE в каталоге \HTMLHELP на компакт-диске с Visual Studio.
HTML Help Workshop включает следующие компоненты.
HTML Help Workshop (HHW.EXE). Позволяет создавать и редактировать HTML-файлы, предметные указатели и оглавления.
HTML Help Image Editor (FLASH.EXE). Обеспечивает «съемку» изображений с экрана и их обработку.
HTML Help Authoring Guide (HTMLHELP.CHM). Полная документация : i- > HTML Help Workshop и связанным с ним компонентам.
HTML Help Engine (НН.EXE). Отображает содержимое скомпилированных файлов в формате HTML Help (СНМ-фаююг, i.
DLL-модули HTML Help Converter. Преобразуют файлы проектов из формата WinHelp в формат HTML Help.
ActiveX-элемент HTML Help (HHCTRL.OCX). Обеспечивает в HTML- файле поддержку оглавления, предметного указателя, ссылок на другие разделы, контекстных подсказок и других элементов справочной системы в формате HTML Help.
HTML Help Java Applet Control (HHCTRL.CLASS). Обеспечивает в Java-сценариях внутри HTML-файла поддержку оглавления, предметного указателя и ссылок на другие разделы.
HTML Help Authoring DLL. Предоставляет дополнительную информацию о записях в оглавлениях справочных систем.
The basic metric unit ofmass is the gram. Cnt commonly used unitis the Wogran or 1000 .^■•JJll;
1 kiogramis equal to 2.20=162262185 pounds
Рис. 17-15 Так выглядит справочный файл в формате HTML Help
Преобразование файлов проектов из формата WinHelp . в формат HTML Help
HTML Help Workshop включает'мастер, помогающий преобразовывать существующие проекты формата WinHelp в проекты формата HTML Help. Чтобы запустить New Project Wizard, выберите из меню File в HTML Help Workshop команду Open и укажите имя существующего файла WinHelp-проекта (с расширением HPJ). Экран Introduction этого мастера показан на рис. 17-16.
New Project Wizard автоматически преобразует файлы WinHelp- проекта в их HTML-эквиваленты (см. таблицу ниже).
Преобразование большинства стандартных WinHelp-элементов проходит гладко. Однако некоторые WinHelp-макросы и ряд других элементов, содержащихся в исходных файлах, преобразовать не удается. Список проблем, связанных с преобразованием, см. в разделе ReadMe руководства Help Authoring Guide (HTMLIIELP.CHM).
Преобразования, выполняемые мастерам New Project Wizard
Описание файла Файл проекта Файл 1 тения Файл предметного указателяФайлы с исходным текстомОписание файла Файл проекта Файл 1 тения Файл предметного указателяФайлы с исходным текстомТип файлов для Win Hei р Тип файлов для HTML HelpТип файлов для Win Hei р Тип файлов для HTML HelpHPJCNTHPJCNTHHPHHCHHPHHCНет (макросы KLink ННК в исходном файле)Нет (макросы KLink ННК в исходном файле)RTFRTFНТМ (но одному па каждый тематический раздел)НТМ (но одному па каждый тематический раздел)Рис. 17-16 Для преобразования WinHelp-проектав формат HTML Help просто откройте HPJ-файл в HTML Help WorkshopРис. 17-16 Для преобразования WinHelp-проектав формат HTML Help просто откройте HPJ-файл в HTML Help Workshop
Описание файла Файл проекта Файл 1 тения Файл предметного указателя
Файлы с исходным текстом
Описание файла Файл проекта Файл 1 тения Файл предметного указателя
Файлы с исходным текстом
Тип файлов для Win Hei р Тип файлов для HTML Help
Тип файлов для Win Hei р Тип файлов для HTML Help
HPJ
CNT
HPJ
CNT
HHP
HHC
HHP
HHC
Нет (макросы KLink ННК в исходном файле)
Нет (макросы KLink ННК в исходном файле)
RTF
RTF
НТМ (но одному па каждый тематический раздел)
НТМ (но одному па каждый тематический раздел)
Рис. 17-16 Для преобразования WinHelp-проектав формат HTML Help просто откройте HPJ-файл в HTML Help Workshop
Рис. 17-16 Для преобразования WinHelp-проектав формат HTML Help просто откройте HPJ-файл в HTML Help Workshop
Использование элемента управления HTML Help
Этот элемент (ITHCTRL.OCX) позволяет создавать всплывающие меню и диалоговые окна для ссылок See Also в разделах справочной системы, как показано на рис. 17-17.
HTML-код для страницы, изображенной на рис. 17-17, хранится в файле HTMLCTRL.HTM на компакт-диске, прилагаемом к книге. Для удобства его содержимое приведено и здесь:
<HTML>
<HEAD>.
<H1LE>HTML Help Control (HHCtrl.OCX)</TITLE>.
<STYLE>'
BODY { background: white }-
</ST¥LE>'
</HEAD>
<BODY T0PMARGIN=12 LEFTMARGIN=10>
<P>Use the HTML Help control to create buttons that display pop-up menus that display links to other topics, as shown below:</P>
<Р>.
COBJECT ID=hhctrL TYPE="application/x-oleobject"
CLASSID="clsid:adb680a6-d8ff-11cf-9377-00aa003b7a11"
CODEBASE-"file://htmlhelp/files/hhctrl.ocx#Version=4,0,0,21"
WIDTH=32
HEIGHT=32
HSPACE=4 >
<PARAM NAME="Command" VALUE="Related Topics, MENU"?
<PARAH NAME="Button" VALUE="Bitmap: help.bmp">■
<PARAMNAME="Item1"VALUE="Topic1;.. \topics\topic1. htm">'
<PARAMNAME="Item2"VALUE="Topic2;.,\topics\topic2.htm">
<PARAHNAME="Item3"VALUE="Topic3;.,\topics\topic3.htm">
<PARAHNAME="Item4"VALUE="Topic4;,.\topics\topic4.htm">
</OBJECT>.
</P>
<P>The HTML Help control can also display a dialog box containing the See Also links, as shown here:</P>
</P>
<P>
<OBJECT ID=hhctrl TYPE="appllcation/x-oleobject"
CLASSID="clsid:adb880a6-dBff-11cf-9377-00aa003b7a11"
CODEBASE="file;//htmlhelp/files/hhctrl.ocx#Version=4,0,0,21" WIDTH=32
HEIGHT=32!r ' '
ALIGN=B0TT0M
HSPACE=4 >
<PARAH NAME="Command" VALUE="Related Topics"?
<PARAH NAME="Button" VALUE="Text:See Also. . . ">
<PARAH NAME="Item1"VALUE="Topic1; .. \topics\topid . htm">
<PARAM NAME="Item2" VALUE="Topic2; . . \topics\topic2.htm">
<PARAM NAME="Item3" VALUE= "TopicS; . .\topics\topic3.htm''>.
<PARAM NAME="Item4" VALUE="Topic4; . .\topics\topic4.htm">
</OBJECT>"
</P>
</B0DY>''
</HTML>
В этом коде параметр Command определяет, что показывает элемент управления: всплывающее меню (VALUE="Related Topics, MENU") или диалоговое окно (VALUE="Re1ated Topics"), а параметр Button — что отображается на лицевой стороне кнопки этого элемента: растровое изображение, значок или текст.
Элемент управления НТМЕ Help предназначен для использования только с НТМЕ- и DHTML-страницами. Его нельзя загрузить в Visual Basic для вставки на формы или другие контейнеры. Попы-
тавшись это сделать, Вы получите ошибку «Invalid control» (недопустимый элемент управления).
. а □^$ аа.slap.Яои^Ь-n"i«jйаюк«дейм"Heel^ejJ. а □^$ аа.slap.Яои^Ь-n"i«jйаюк«дейм"Heel^ejJ
. а □^$ аа.
slap.Яои^Ь-n"i«jйаюк«дейм"Heel^ejJ
. а □^$ аа.
slap.Яои^Ь-n"i«jйаюк«дейм"Heel^ejJ
Рис. 17-17 Для отображения ссылок на другие разделы (на HTML- или DHTML-страницах) используйте элемент управления HTML HelpРис. 17-17 Для отображения ссылок на другие разделы (на HTML- или DHTML-страницах) используйте элемент управления HTML Help
Рис. 17-17 Для отображения ссылок на другие разделы (на HTML- или DHTML-страницах) используйте элемент управления HTML Help
Рис. 17-17 Для отображения ссылок на другие разделы (на HTML- или DHTML-страницах) используйте элемент управления HTML Help
Use the НТ? ••IL Help control to create buttons that display pop-up menus that display links to other topics, as shown below
P cento) can also display a dialog bOK containing the See jWTlhere.
Web-узел Microsoft по адресу http://vvv.microsoft.com/vorkshop/ autbor/btmlbelp— там Вы найдете обновления для Microsoft HTML Help Workshop и статьи на соответствующие темы.
Книгу The Official Guide to Microsoft HTML Help, подготовленную компанией WexTech Systems и опубликованную Microsoft Press в 1998 году
ГЛАВАГЛАВА1 81 8
ГЛАВА
ГЛАВА
1 8
1 8
Защита
Члюры о правах на интеллектуальную собственность не такая уж и редкость среди разработчиков программного обеспечения. Один из простых и часто применяемых способов, позволяющих доказать авторство, — скрытое диалоговое окно со списком всех, кто принимал участие в создании программного продукта (credits dialog box). Его иногда называют пасхальным яйцам (easter egg). Другой, тоже очень популярный способ — встраивание зашифрованных сообщений с объявлением авторства. Я рассмотрю оба способа.
Кроме того, во многих приложениях, где нужна защита от несанкционированного доступа, используются пароли. Их обычно вводят в специальном диалоговом окне; такие окна в Visual Basic создавать очень легко — я расскажу и об этом.
Дорогой ~ н, как...
Включить в программу скрытое окно с именами авторов?
Эти окна когда называют «пасхальными яйцами», когда вообще никак не называют. Такие секреты скрыты во многих приложениях. Добравшись до чего-то подобного, иногда видишь целое шоу. Творчески сделанные «пасхальные яйца» (вроде того, что показано на рис. 18-Г) бывают очень интересными. .
Рис. 18-1 Образец ^пасхальногояйца- (полусекретное окно с упоминанием авторов)Рис. 18-1 Образец ^пасхальногояйца- (полусекретное окно с упоминанием авторов)
Рис. 18-1 Образец ^пасхальногояйца- (полусекретное окно с упоминанием авторов)
Рис. 18-1 Образец ^пасхальногояйца- (полусекретное окно с упоминанием авторов)
Подобные « пасхальные яйца» в какой-то мере обеспечивают юридическую защиту авторских прав. Если возникают разногласия по
поводу таких прав на тот или иной компонент программного обеспечения, настоящий автор может произвести должное впечатление, щелкнув там-то и там-то и открыв секретное диалоговое окно, которое доказывает его авторство. Ну разве не забавно как-нибудь проделать это на суде?
Создать специальную форму, отображающую всю необходимую информацию, совсем несложно. Фокус в другом: как активизировать эту форму? Я приведу несколько универсальных рецептов и конкретный пример, но простор для творчества здесь бескрайний.
Узнать координаты на форме или интерфейсном элементе, в которых произошел щелчок кнопки мыши, очень легко. События MouseDown и MouseUp сообщают У,У'-координаты курсора мыши в момент щелчка. Аналогичным образом можно определить состояние клавиш-модификаторов (Shift, Ctrl и Alt) и кнопок мыши. Вся эта информация передается в виде параметров процедуре, выполняемой при возникновении события MouseDown или MouseUp. Поэтому Ваше приложение может проверить состояние клавиш-модификаторов и кнопок мыши и активизировать «пасхальное яйцо» только в том случае, если, скажем, правая кнопка мыши нажата при удерживаемой клавише Shift, а курсор находится где-то в сантиметре от правого верхнего угла определенного окна рисунка.
В качестве альтернативы включите статические переменные в обработчик события MouseUp или MouseDown, и тогда Вы сможете обнаруживать специфические последовательности щелчков левой и правой кнопок мыши или, например, 5 щелчков подряд в течение не более 2 секунд.
По аналогии можно скрытно отслеживать специфическую последовательность нажатий определенных клавиш. Для этого установите свойство KeyPreview формы как True и ведите учет последних нажатых клавиш в обработчике события KeyDown. Рассмотрим этот вариант на примере скрытой детекции последовательного ввода букв, образующих слово EGG. Я создал модуль простого класса Egg, проверяющего коды нажатых клавиш. У определяемого им объекта Egg всего одно свойство только для записи — Char. Если в это свойство заносятся коды клавиш £, G и G (именно в таком порядке), объект выводит информационное окно.
' EGG.CIS
Option Explicit
Private mstrKeyPhrase As String * 3.
1 .Char (свойство)
Property Let Char(intKey As Integer)
Select Case IntKey
Case vbKeyE: mstrKeyPhrase = Mid$(mstrKayPhrase, 2) & "E"
Case vbKeyG: mstrKeyPhrase = Mld$(mstrK0yPhrase, 2) & "G"
Case Else: mstrKeyPhrase = "".
End Select
If mstrKeyPhrase = “EGG" Then EasterEgg
End Property
Private Sub EasterEggO
MsgBox "JC and JW were here 111"
End Sub
Чтобы проверить работу объекта Egg, добавьте в модуль стартовой формы код:
Option Explicit,
Dim eggTest As New Egg
Sub Form_Load(),-
Me. Keypreview = True..‘
End Sub1■
Sub Form_KeyDown(intKeyCode As Integer, intShift As Integer) ' eggTest. Char = IntKeyCode■'
End Sub
Процедура Form Load устанавливает свойство KeyPreview формы как True, чтобы можно было проверять нажатие любых клавиш независимо от того, какой элемент управления находится в фокусе ввода. При последовательном нажатии клавиш £, G и G закрытая процедура EasterEgg объекта Egg выводит на экран простое окно с сообщением. Попробуйте самостоятельно доработать эту программу для своих задач.
Процедура FormKeyDowTi активизируется всякий раз, когда форма или один из ее элементов управления получает фокус ввода и нажимается какая-нибудь клавиша. Экземпляр eggTest объекта Egg запоминает последние 3 клавиши, проверяя значение свойства Char, и вызывает закрытую процедуру EasterEgg, обнаружив последовательность EGG..
См. также...‘1
ПpилoжeниeDialogs в главе 34 «Сложные приложения», гдепри- меняется более совершенное -пасхальное яйцо», отображаемое весьма хитроумной серией щелчков.
Дорогой Пжоя. как...
Создать диалоговое окно Password?
В ранних версиях Visual Basic приходилось из кожи вон лезть, чтобы создать диалоговое окно Password (пароль), но уже в версии 3 эта задача стала очень простой.
Наша цель — сформировать диалоговое окно, которое при вводе пароля отображало бы звездочки или другие знаки. Звездочки позволяют видеть количество введенных символов, но не дают возможности подглядеть собственно пароль. Разумеется, программа должна отслеживать, какие символы вводит пользователь на самом деле, — иначе не удастся проверить пароль. Диалоговое окно Password показано на рис. 18-2. -
Рис. 18-2 Типичное диалоговое окно PasswordРис. 18-2 Типичное диалоговое окно Password
Рис. 18-2 Типичное диалоговое окно Password
Рис. 18-2 Типичное диалоговое окно Password
Элемент управления TextBox обычно настраивается так, чтобы пользователь мог вводить текст с клавиатуры. У этого элемента есть свойство Passwordchar. Запишите в него символ, который будет заменять символы пароля при вводе. Реально введенные символы не показываются, но сохраняются в свойстве Text.
Теперь рассмотрим несложный пример. Добавьте на форму текстовое поле txtPasswordи кнопку emdOK. Процедура cmdOKCiick проверяет пароль на совпадение со словом sesame, но Вы можете переделать код на проверку любой строки текста.
Option Explicit . .
Private Sub cmdOK_Click()
If txtPassword.Text <> "sesame" Then
MsgBox "Incorrect password", vbCritical Else
MsgBox "Okay!.. .Correct password"
End If
End Sub
Private Sub Form_load() txtPassword.PasswordChar = "♦" txtPassword,Text = ""
■End Sub
Приложение Secret в главе 34 «Сложныеприложенияо, где этот
прием иллюстрируется гораздо подробнее.
Длплгпм Пжон, как...
Зашифровать пароль или другой текст?
Методы шифрования варьируются от очень простых до чрезвычайно сложных и надежных. Обычно Вам вовсе нс требуется защита того уровня, который нужен Агентству Национальной Безопасности, — Бы ведь только и хотите, чтобы пользователь, просматривая ЕХЕ-файл или файл данных Вашего приложения, не обнаружил какие-нибудь строки, не предназначенные для ■, гм глаз. И вообще, с мощными шифровальными методами надо быть поосторожнее, особенно если Ваше программное обеспечение будет поставляться в другие страны. На экспорт устойчивых к взлому шифров распространяются те же законы, что и на поставку вооружений! Я представлю метод шифрования, основанный на преобразованиях А5СП- кодов символов, — он вполне пригоден для скрытия конфиденциальной информации от просто любопытствующих. Но от хакеров он, конечно, не защитит.
Класс Cipher.
Модуль класса Cipher определяет объект, способный шифровать и дешифровать лишь одну строку единовременно. Рассмотрим свойства и методы этого объекта.
KeyString Свойство только для записи (у процедуры Property Let нет парной процедуры Property Get). Определяет строку — уникальный шифровальный ключ. Когда свойство KeyString установлено, внутреннему генератору случайных чисел Visual Basic передается уникальное значение — зародыш последовательности случайных чисел, формируемый с учетом каждого символа ключа и порядка этих символов.
ПРИМЕЧАНИЕ Чтобы генератор случайных чисел Visual Basic повторял данную последовательность, вызовите функцию Rnd с необязательным параметром, указав в нем отрицательное число, а полученное в результате значение передайте ■ оператору Randomize. Например, Randomize (R>id(-1.23)) ' приводит к тому, что при каждом вызове Rnd генерируется одна и та же последовательность случайных чисел.
। ■ Text Свойство, принимающее шифруемый или дешифруемый текст. Вы можете присвоить ему <• ■ року обычного текста, вызвать методы DoXor или Stretch и сохранить полученный результат в защищенном файле. Впоследствии Вы загрузите из него зашифрованную строку в свойство Text, запишете свойство KeyString ключ, примененный для шифрования данной строки, вызовете методы Shrink и DoXor и выведете на экран конечное содержимое свойства Text.
Я DoXor Метод, обрабатывающий строковое значение свойства Text. Выполняет операцию Хот (исключающее «ИЛИ») над каждым байтом строки и очередным байтом из псевдослучайной последовательности, определенной свойством KeyString. Этот процесс обратим: повторная обработка строки с тем же ключом восстанавливает ее исходное состояние. Подобный процесс лежит в основе многих шифровальных алгоритмов.
8 Stretch Метод, парный методу Shrink. Преобразует строку, содержащую любые неотображаемые байтовые значения, в чуть более длинную строку, в которой все символы могут быть гарантированно выведены на экран. Возьмите, к примеру, строку с 10 символами табуляции (байтовые значения 9). Если Вы попытаетесь напечатать или отобразить такую строку, то не увидите ничего кроме большого прогала в выводе своей программы. Выполняя операцию Хиг над байтами строки с использованием всех байтов из псевдослучайной последовательности, Вы, вполне вероятно, получите байты, соответствующие символам табуляции, возврата каретки, перевода строки и даже псевдографики. Метод Stretch «одалживает» биты в каждой группе из 3 символов и формирует из них 1-й символ, после чего все 4 знака приводятся к диапазону байтовых значений обычных символов. Полученную в итоге строку можно сохранить в реестре или INI- файле, распечатать на бумаге или включить в сообщение, передаваемое через Интернет.
Shrink Метод, отменяющий изменения, внесенные в строку методом Stretch. Строка возвращается в исходное состояние, в котором может содержать любые байтовые значения (от 0 до 255).
ПРИМЕЧАНИЕ Для тех, кто интересуется подобными вещами, замечу, что методы Stretch и Shrink реализованы с применением алгоритма, почти идентичного алгоритму Uuencode. Я сказал «почти», так как при преобразовании строки использовал несколько иное смещение, избегая появления символов пробелов.
Объект Cipher в действии
Класс Cipher шифрует и дешифрует текст по очень простому алгоритму логической операции Хог. выполняемой над байтами строки и последовательности псевдослучайных чисел. В качестве ключа можно использовать любую строку, а каждый уникальный ключ дает уникальный шифр.
Как реализованы свойства и методы класса Cipher, Вы узнаете, просмотрев его листинг. А пока подготовьте форму, добавьте на нее 4 текстовых поля, 4 метки и 2 кнопки. В модуль формы введите код, показанный на следующей странице.
Option Explicit
Private Sub cmdEncrypt_Click() Dim cipherTest As New Cipher cipherTest.KeyString = txtKey.Text cipherTest. Text = txtClear.Text cipherTest.DoXor cipherTest.Stretch txtEncrypted. Text = cipherTest.Text
End Sub
Private Sub cmdDecrypt_Ciick()
Dim cipherTest As New Cipher cipherTest. KeyStH no = txtKev Text cipherTest. Text = txt Encrypted. Text cipherTest. Shrine cipherTest.DoXor txtDecrypted.Text = cipherTest.Text End Sub
Здесь предполагается, что txtClear содержит исходный, открытый текст (его иногда называют клером), txtKey — ключевую строку, txtEncrypted^- зашифрованный вариант исходного текста, txiDec- rypted— текст, полученный после расшифровки. Щелкнув кнопку cmdEncnpi Вы зашифруете текст, а щелкнув кнопку cmdDecrypt— дешифруете. Заметьте, что длина зашифрованного текста составляет примерно 4/3 длины исходной строки; здесь проявляется эффект от применения метода Stretch.
Рис. 18-3 иллюстрирует объект Cipher в действии.
•t3w^W 11 hi к ■ ampfe ciw 1е>'
Det у ре< tf Г ** J—
Рис. 18-3 Объект Cipher, используемый для шифрования и дешифрования текста
Ну а теперь листинг самого класса Cipher:
CIPHER. CLS
Option Explicit.'
Private mstrKey As String ■,■..'
Private mstrText As String '" ' ''
1 .KeyString (свойство)
' строка (ключ), исполвзуемая в шифровании и дешифровании
Public Property Let KeyString(strKey As String)
mstrKey = strKey
Initialize
End Property...
' .Text (свойство)
' принимает шифруемый и дешифруемый текст .
Public Property Let TextfstrText As String) . msttText = strText
End Property
' возвращает зашифрованный илидешифроаанный текст
Public Property Get Text() As String.
Text = mstrText
End Property’
' """.DoXor (метод)
' шифрование/дешифрование на основе побайтовой операции Хот
Public Sub DoXor()
Dim lngC As Long
Dim intB As Long■
Dim IngN As Long<
For IngN = 1 To LenfmstrText)
lngC = Asc(Mid(mstrText, IngN, 1))
intB = Int(Rnd * 256)''
Mid(mstrText, IngN. 1) = Chr(lngC Xor intB)
«ext IngN
End Sub
" .Stretch (метод)
' преобразует любую строку в полноствю отображаемую
Public Sub Stretch()
Dim lngC As Long
Dim IngN As Long
Dim IngJ As Long
Dim IngK As Long
Dim IngA As Long
Dim strB As String
IngA = Len(mstrText),'
■strB = Space(IngA + (lngA + 2) \ 3)
For IngN = 1 To IngA1 ■■ ■ ■
lngC = Asc(Mid(mstrText, IngN, 1)).;
IngJ = IngJ + 1
Mid(strB, IngJ, 1) = Chr((lngC And 63) + 59)
Select Case IngN Hod 3
Case li
IngK = IngK Or ((lngC \ 64) * 16)
Case 2
IngK = IngK Or ((IngC \ 64) * 4)
Case 0
IngK = IngK Or (IngC \ 64)
IngJ = IngJ + 4
Nid(strB, IngJ, 1) = Chr(lngK + 59)
. IngK = 0
End Select
Next IngN'
If IngA Mod 3 Then
■ IngJ = IngJ + I
Mid(strB, IngJ, 1) = Chr(lngK + 59)
End If
mstrText = strB
,End Sub' .. .
' .Shrink (метод)
' выполняет действия, обратные тому, что делает метод Stretch
Public Sub ShrinkO
Dim
IngC As
Long
Dim
IngD As
Long
Dim
IngE As
Long
Dim
IngA As
Long
Dim
lngB As
Long
Dim
IngN As
Long
Dim
IngJ As
Long
Dim
IngK As
Long
Dim
strB As
String
IngA = Len(mstrText)
lngB = IngA - 1 - (IngA- 1) \ 4
strB = Space(lngB)
For IngN = 1 To lngB
IngJ = IngJ + 1
IngC = Asc(Nid(mstrText, IngJ, 1)) -59
Select Case IngN Mod 3
Case 1
IngK = IngK + 4
If IngK > IngA Then IngK = IngA
IngE = Asc(Mid(mstrText, IngK. 1)) - 59
' IngD = ((IngE \ 16) And 3) * 64
Case 2
IngD = ((lngE\ 4) And 3) * 64
Case 0.
IngD = (IngE And 3) * 64
IngJ = IngJ + 1'
End Select
Mid(strB, IngN, 1) = Chr(lngC Or IngD)
Next IngN mstrText = strB
End Sub
' инициализация генератора случайных чисел по ключу
Private Sub Initialize().
Dim IngN As Long
Randomize Rnd(-1)"
For IngN = 1 To len(mstrKey)
Randomize Rnd(-Rnd * Asc(Hid(mstrKey. IngN. 1)))
Next IngN_
End Sub' '..
Используя объект Cipher в своем приложении, перед каждым вызовом метода DoXor обязательно устанавливайте свойство KeyString. Это необходимо для корректной инициализации генератора случайных чисел.
ПРИМЕЧАНИЕ Текст, зашифрованныйтакимспособом, можно спокойно пересылать по модему — никакие управляющие коды или ESC-последовательности не нарушат его целостность. Вам не понадобится сложный метод двоичной передачи данных. Кстати, если Вы не прочь поработать с двоичными данными, можете отказаться от методов мretch и Shrink. Хотя они, вероятно, пригодятся и для других целей — не только в шифровании; например, для передачи двоичных файлов (графических изображений) по обычной электронной почте через Интернет.
Защита данных в реестре
И, наконец, одна идея, связанная с защитой программного обеспечения. Вы можете отслеживать дату установки своего приложения, число его запусков, имя пользователя и другую информацию, записывая ее как зашифрованную строку в реестр (в раздел, относящийся к этому приложению). Имя пользователя и название его организации можно взять как ключевую строку. Это позволит легко проверять, не истекли срок пробной работы с данным приложением, не ..ревышеполи максимальное число его запусков и т. д.
Вот простой пример на основе класса Cipher. Программа, приведенная ниже, позволяет ввести ключевую строку и имя пользователя. Щелкнув кнопку cmdEncryptBw запишете в реестр зашифрованное имя пользователя, а щелкнув кнопку crndDempt^msyne его в расшифрованном виде. Попробуйте запустить эту программу и в качестве имени пользователя ввести Santa Claus. Далее запустите Regedit, найдите раздел реестра User Name и посмотрите, что в нем содержится. Не пытайтесь искать Santa Claus, потому что эта строка зашифрована.
Option Explicit
Private mstrAppName As String
Private mstrSection As String
Private mstrKey As String
Private mstrSetting As String,.
Private Sub cmdEncrypt_Click()
Dim cipherTest As New Cipher
cipherTest. KeyString = txtKey.Text
cipherTest. Text = txtUser.Text
cipherTest.DoXor
cipherTest.Stretch.
mstrSeftino = cipherTest.Text
mstrAppName = App.Title
mstrSection = "Testing"
mstrKey = "User Name"
SaveSetting mstrAppName, mstrSection, mstrKey, mstrSetting
txtEncrypted. Text = cipherTest.Text
End Sub
Private Sub cmdDecrypt_Click()
Dini cipherTest As New Cipher 1
mstrAppName = App.Title
mstrSection = "Testing"
mstrKey = "User Name"
urprier lest, text = GetSetting(mstrAppName, mstrSection, mstrKey)
cipherTest.KeyString = txtKey.Text
cipherTest. Shrink.
cipherTest. DoXor
txtDecrypted.Text = cipherTest.Text
End Sub
Рис. 18-4 иллюстрирует вывод программы.•
wife* fijUaTii ~*
Рис. 18-4 Пример сохранения зашифрованного имени пользователя в реестре'
Вариаций на эту тему можно написать массу, но важно отметить, что строки, зашифрованные с помощью представленного здесь объ-
екта, содержат только отображаемые символы, и у Вас не возникнет никаких проблем при их записи или чтении.
^^ Си. также...
Приложение Secret вгланс 34 «Сложныеприложения». где демонстрируется более совершенный алгоритм шифрования.
Главу 16 «Реестр» — о способах доступа к реестру
Дорогой Джон, как....
Работать со средствами защиты в Интернете?
Если Вам нужна простейшая защита, можете воспользоваться кое- какими идеями из этой главы. Исходя из них, Вы легко дополните свои интернет-приложения средствами элементарного шифрования гскстовых. и двоичных файлов, а также проверки паролей. Но если Вы серьезно озабочены проблемами безопасности, обращайтесь к специалистам! Когда я размышляю о последствиях неправильного или непродуманного подхода к вопросам безопасности в интернете, мне приходит на ум фраза: «Не пытайтесь делать это в домашних условиях!» Даже представить трудно, сколько усилий ушло на разработку технологий защиты, применяемых в Интернете.
Microsoft всегда принимала эти проблемы близко к сердцу. Она располагает экспертами соответствующего уровня — своими и привлеченными в рамках сотрудничества с другими компаниями, которые специализируются на программных решениях в сфере безопасности и которым известны ответы на многие вопросы. Я могу лишь подсказать, с чего лучше начать изучение этой тематики. Прежде всего загляните на Web-страницу http://www.microsoft.com/ security — она послужит отправной точкой в процессе сбора информации о широком спектре технологий защиты, которым действительно можно доверять.
ГЛАВАГЛАВА1 91 9
ГЛАВА
ГЛАВА
1 9
1 9
Мышь
V isual Basic предоставляет I б стандартных курсоров мыши и позволяет определять нестандартные. В этой главе я поясню, как работать с ними, и продемонстрирую, насколько легко создавать свои курсоры.
Дорогой Джон, как... . .
Изменить курсор мыши? ■
У форм и многих видимых элементов управления есть свойство MousePoinicr. которое дает возможность модифицировать внешний вид курсора мыши, когда он попадает на соответствующий экранный объект. Обычно это свойство устанавливается программно, поскольку принято изменять форму курсора мыши в зависимости оттого, что именно делает программа. Если, например, выполняется некая длительная операция, лучше превратить ci т> в «песочные часы 1 Так что на этапе разработки свойство MousePointer должно быть vbDefault.
В Visual Basic имеется целый набор объявлений констант для свойства MousePointer. Вам не придется загружать в проект какой- то специальный файл — эти константы доступны в любой программе, создаваемой на Visual Basic, Чтобы просмотреть их полный список и вставить нужную константу прямо в код, откройте Object Browser, в списке Project/Library выберите библиотеку VBRUN, в списке Classes — строку MousePointerConstants и выделите интересующую Вас гонгтан п" Теперь скопируйте ее, нажав клавиши Ctrl+C, переключитесь в окно кода и вставьте, нажав клавиши Ctrl+V. Эти константы и эффект, вызываемый ими после записи в свойство MousePointer, описаны в следующей таблице.
Константа
Значение
Форма курсора
vbDefault
0
Выбираемая по умолчанию (определяется текущим объектом)
rbAiTOtr
1
Стрелка;. .
vbCrosshair
2
Перекрестие■
eblbeam
3
1-обп:1:шый курсор
vbleonPmtUer
4
Значок
rbSizePointer
5
Крест из двухконечных стрелок
Константа
Значение
Форма курсора
vbSlzeNESW
6
Двухконечная стрелка, размещаемая по диагонали снизувверх
vbSizeNS
7
Вертикальная двухконечная стрелка
vbSizeNWSE
8
Двухконечная стрелка, размещаемая по диагонали сверху вниз
rbSizeWE
9
Горизонтальная двухконечная стрелка
гЪирАггою
10
Стрелка, направленная вверх
I'bHourgtoss
11
Песочные часы
t'bNoDrop
12
Знак запрета (перемещаемый мышью о бьект нельзя о пустить на данный экранный элемент)
г 'bArrowHouryass
13
Стрелка с песочными часами
I’bArrowQuestion
14
Стрелка со знаком вопроса
I’bSizeAll
15
Крестизтонкихдвухконечных стрелок
vbCustom
99
Любой значок, указанный в свойстве
Mouseicon
Обратите внимание на 4 двухконечные стрелки, используемые при изменении размеров экранного объекта. Каждая из них указывает в двух направлениях, а сами направления обозначаются, как на карте, т. е. вверху — Север (N), внизу — Юг (S), слева — Запад (W), справа — Восток (Е). Заметьте также, что в списке стандартных курсоров мыши нет популярного «указующего перста». Visual Basic позволяет создавать свои, нестандартные курсоры мыши, и приложение MousePtTB главе 35 - Утилиты» демонстрирует, как использовать значок в качестве курсора.
Чаще всего свойство MouscPointer применяется для замены обычного курсора на ■ игч очпыр часы», пока программа чем-то занята. Они подсказывают пользователю, что программа вовсе не «зависла», — требуется лишь немного терпения. Пример такого курсора приведен на рис. 19-1. Закончив длительную операцию, программа обязана вернуть курсор в предыдущее состояние.
В
Рис. 19-1 Курсор мыши в виде песочных часов появляется, когда приложение занято
Вот несколько строчек кода, иллюстрирующих этот прием. Более подробный пример — приложение MousePtr в главе 33.
fruiMain.MousePointer = vbHourglass' "
ReturnValue = TimeConsumingFunction().
frmMain.MousePointer = vbDefault
feC^ См. также...
Раздел «Дорогой Джон, как... создать нестандартный курсор мыши?» далее в этой главе — подробнее о создании и использовании нестандартных курсоров.
» Приложение MousePtr в главе 33 -Утилиты-, где Вы сможете поработать с разными курсорами и посмотреть, на что они похожи.
^^^*>г^й Дже& ^.з^,..
Создать нестандартный курсор мыши?
Для этого годится любой файл значка или курсора. Установите свойстве- MousePointer как ■ bCuslom, а в свойство Mouseicon запишите имя файла значка (ICO) или курсора rClTRt Все! При необходимости можно моментально переключаться между стандартными и нестандартным курсорами, просто изменяя значение свойства MousePointer. Но если Вы хотите переключиться на второй нестандартный курсор, назначьте свойству Mouseicon другой значок. На рис. 19-2 показан курсор в виде перста, указующего влево; он взят из коллекции значков, поставляемых с Visual Basic.
,.\GRAPHicsMc®m\№ROwstPoiMTea.^ !
Рис. 19-2 Нестандартный курсор мыши, загруженный■.
из файла POINT02JCO
ПРИМЕЧАНИЕ Свойство Mouseicon не поддерживает курсоры с анимацией. 1
Кроме того, Вы можете загрузить несколько файлов значков в элемент управления Imagel.h.'i (на этапе разработки) и быстро обращаться к ним через свойство Picture его набора Listimages. Элемент ImageList входит в состав Microsoft Windows Common Controls 6.0 (MSCOMCTLOCX). Вот программа, которая с помощью элемента ImageList при каждом щелчке формы отображает очередной курсор:
Option Explicit.
intList = IntList + 1‘
If intList > 3 Then intList = 1'■
MousePolnter = vbCustom'.
Houseicon = ImageListl.ListlMges(intList). Picture
End Sub
Вывести на экран курсор с анимацией?
Только не спрашивайте меня, почему Microsoft не включила в свойство Mouseicon поддержку курсоров с анимацией, — я не знаю. Чтобы отобразить для какого-нибудь окна курсор с анимацией, придется лезть в дебри Windows API. Впрочем, это не так уж и страшно.
API-функция LoadCursorFroniFif позволяет загрузить любой тип курсоров поддерживаемых Microsoft Windows. А чтобы сменить курсор для какого-либо окна, вызовите API-функцию SetClassLong. Option Explicit
’ загружает курсор и создает в-- описатель
Private Declare Function LoadCursorFromFile _
Lib "user32" Alias "LoadCursorFrcimFlleA" ( _
ByVai IpFlleName As String _
) As Long.
' модифицирует информацию о классе для окна
Private Declare Function SetClassLong _
Lib "iiser32" Alias "SetClassLongA" ( _
ByVai hi,.'ird As Long, _
ByVai idnuex As Long, __■
ByVai CwNewLong As Long __
) As Long
' индекс курсора в структуре оконного класса
Private Const GCL_HCURSOR = (-12).
Private MOIdCursor As Long_
Private Sub Form_Load()■■,
Dim hWewCursor As Long
' получаем описатель нового курсора с анимацией
MNewCursor = LoadCursorFromFile _
("С:\WIND0WS\CUflSORS\DHUM.AHI")
' заменяем текущий для данного окна курсор новым
MOIdCursor = SetClassLong(Form1.hwnd, GCl_HCURSOR, MNewCursor) End Sub
Private Sub Form UnloadfCancel As Integer)
' восстанавливаем исходный курсор
MOIdCursor = SetClassLong(Form1.hwnd, GCL_HCURSOR, MOIdCursor)
End Sub
Заметьте: процедура Form_Unload возвращает курсор мыши в исходное состояние. Если Вы не сделаете этого, курсор «барабанная дробь» так и останется закрепленным заданным окном даже в среде разработки. Ele «зашивайте» в код путь к файлу DRHM.ANI, если собираетесь распространять свою программу. Используйте API-функцию GerWindnwsDircciorv чтобы получить реальный каталог Windows на компьютере пользователя.
Дорогой Джон, как»..
Определить координаты курсора мыши?" '
События MouseMove, Mouseup и Mouseuown предоставляют несколько полезных параметров, полное описание которых см. в справочной системе Visual Basic. Любое из этих событий обязательно возвращает А'.У-координаты, сообщающие программе точную позицию курсора мыши на момент его (события) возникновения. В большинстве случаев программе придется запоминать эти значения в каких-нибудь переменных, но все зависит от или. какую именно задачу Вы решаете.
Приведу очень простой пример, наглядно демонстрирующий принцип определения координат курсора. Программа, показанная ниже, позволяет рисовать на форме произвольные линии. Любая линия начинается в точке, в которой Бы нажали кнопку мыши, и заканчивается там, где Вы ее отпустили. Координаты курсора при генерации события MouseDown записываются в переменные . и Y1 уровня модуля, благодаря чему они доступны и в обработчике события MouseUp формы. Когда возникает событие MouseUp, эти переменные используются совместно с локальными переменными X и У;. тем самым Вы получаете начальную и конечную точку линии. Option Explicit
Private Х1, Y1'
Private Sub Form_MouseDownt _
Button As Integer, _
Shift As Integer, _
XAs Single, _.
Y As Single _."
).'
XI = X■
Y1 = Y■.
End Sub
Private Sub Form_MousaUp( _. ,
Button As Integer, _'
Shift As Integer, _
XAs Single, „Y As Single _)"Line <X1, Y1)-(X. Y)End SubXAs Single, „Y As Single _)"Line <X1, Y1)-(X. Y)End SubРис. 19-3 иллюстрирует действие этого элементарного кода.Рис. 19-3 иллюстрирует действие этого элементарного кода.
XAs Single, „
Y As Single _
)"
Line <X1, Y1)-(X. Y)
End Sub
XAs Single, „
Y As Single _
)"
Line <X1, Y1)-(X. Y)
End Sub
Рис. 19-3 иллюстрирует действие этого элементарного кода.
Рис. 19-3 иллюстрирует действие этого элементарного кода.
Рис. 19-3 Используя, события MouseUp и MouseDown, можно рисовать прямые линии
См. также.,.
ПриложениеМоизеРгг вглаве 33 «Утилиты», где тоже демонстрируется, как определять позицию курсора мыши.
Клавиатура
'Одесь Вы найдете несколько полезных (и не всегда очевидных) приемов обработки пользовательского ввода с клавиатуры в приложениях, написанных на Visual Basic.
Изменить поведение клавиши Enter?
Хоть это и расходится со стандартами программирования для Microsoft Windows, иногда нужно, чтобы клавиша Enter действовала подобно клавише Tab, т. е. переключала фокус ввода с текущего элемента управления на какой-то иной. Следующий фрагмент кода реализует этот трюк для текстового поля, но пример можно переделать под любой другой элемент, у которого есть событие KcyPress. Private Sub txtText1_KeyPress(KeyAscll As Integer)
If KeyAscii = vbKeyReturn Then
SendKeys "{tab}"
KeyAscii = 0■
End If-
End Sub
ASCII-код нажатой клавиши передается в обработчик события KeyPress как параметр KeyAscii. Для клавиши Enter этот код равен 13 и соответствует константе vbKeyReturn. Оператор SendKeys позволяет приложению, написанному на Visual Basic, посылать окну, на котором сосредоточен фокус, сообщение о нажатии любой клавиши. (Он же дзет возможность передавать такие сообщения и другим Windows-приложениям.) Строка означает: в данном случае SendKeys посылает сообщение о нажатии Tab; это заставляет окно— :■ сообщения выполнить те же действия, что и при
реальном нажатии Tab. Чтобы переопределить стандартное действие клавиши Enter, не забудьте также присвоить KeyAscii нулевое значение. Если Вы этого не сделаете, элемент управления подаст звуковой сигнал («бин»).
Наконец, чтобы игнорировать все нажатия Enter, когда в фокусе находится определенный элемент управления, просто обнуляйте значение KeyAscii, не применяя оператор SendKeys.
^^^" *^ ГВ ^, -ж -л ^- ^ п.
Определить состояние клавиш-модификаторов?
Событие Keypress не позволяет в момент нажатия обычных клавиш напрямую определять состояние клавиш-модификаторов (Shift, Ctrl и Alt), но их состояние изменяет код символа, что выявить уже вполне возможно. Для прямой детекции состояния клавиш-модификаторов используйте события Keybown и KeyUp. Вы можете воздействовать на состояние этих клавиш в процедурах обработки событий KeyDown и KeyUp или просто отслеживать -л ■сохраняя соответствующие значения в переменных уровня модуля. Я предпочитаю второй вариант, так как тогда могу воздействовать на состояние модификаторов хоть в обработчике события KeyPress, хоть в любом другом блоке кода (в пределах данного модуля).
Программа, приведенная ниже, обновляет значение одной из трех Булевых переменных при нажатии или отпускании соответствующей клавиши-модификатора. Для проверки состояния каждой из таких клавиш я использую константы Visual Basic vbShiftMask, vbCtriMtiskw vbAll Mask, Чтобы код работал на уровне всей формы, установите ее свойство KeyPreview как True. Это позволит — независимо от гого, какой элемент управления активен, — мгновенно получать состояние любой из трех клавиш-модификаторов простой проверкой текущего значения нужной Булевой переменной.
Option Explicit
Private mblnShiftState As Boolean
Private mblnCtrlState As Boolean
Private mblnAltState As Boolean
Private Sub rm_KeyOown(KeyCode As Integer, Shift As Integer) mblnShiftState = (Shift And vbShiftMask) mblnCtrlState = (Shift And vbCtrlMask) mblnAltState = (Shift And vbAltMask).
End Sub.
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) mblnShiftState = (Shift And vbShiftMask) mblnCtrlState = (Shift And vbCtrlMask) mblnAltState = (Shift And vbAltMask)'
End Sub
Private Sub tmrTest_Timer()■.
Cis.
Print "Shift = ": mblnShiftState
Print "Ctrl = , mblnCtrlState
Print "Alt = ”; mblnAltState
End Sub
Чтобы посмотреть, как работает эта программа, создайте новую форму, добавьте на нее таймер hnrTesi и введите код в модуль формы. Запишите в свойство Intervalтаймера значение 100для проверки состояния клавиш-модификаторов через каждые 100 мс. Программа будет сообщать состояние клавиш по мере того, как Вы нажимаете и отпускаете их. Попробуйте также нажать комбинацию клавиш Shift, Ctrl и Alt — Вы увидите, что все три переменные обновляются независимо. На рис. 20-1 показана форма в момент, когда одновременно нажаты клавиши Ctrl и Alt.
Рис. 20-1 Отображение статуса клавиш-модификаторов в режиме реального времени
Дорогой Джон, как..,"
Создать «горячие» клавиши?
Великолепные возможности в этом плане открывает свойство KeyPreview форм Visual Bask. Оно позволяет программе обнаруживать нажатие любой комбинации клавиш — функциональных (с модификаторами или без), а также тех, что расположены на дополнительной цифровой клавиатуре. Общая методика здесь такова. Сначала Вы устанавливаете свойство KeyPreview формы как Т¥ие,а затем пишете код для процедуры обработки события KeyDown формы, который будет проверять нужные клавиши. В коде, приведенном ния«. я контролирую клавиши Fl. F2 и вес модификаторы в комбинации с клавишей F3. Чтобы выяснить, какая клавиша нажата, я использую константы Visual Basic vbKeyFl ,vbKeyF2w ubKeyFi. Заметьте, что такая процедура хорошо работает лишь при проверке одной или нескольких клавиш, но прием, описанный в предыдущем разделе, поможет усовершенствовать ее, если Вам вдруг понадобится проверять клавиши-модификаторы одновременно с группой функциональных клавиш.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyFI.
Print "FT"-
Case vbKeyF2■
Print "F2"
Case vbKeyF3
If (Shift And vbShiftMask) Then
Print "Shift-F3"
Elself (Shift And vbCtrlMask) Then Print trl-F3"
■ Elself (Shift And vbAltHask) Then Print "Alt-F3"
Else
Print " F3"
End If:
End Select*• ■ ■ ■
End Sub
На рис. 20-2 показано, что происходит после запуска программы и нажатия функциональных клавиш — по одиночке и в сочетании с модификаторами. При этом форма перехватывает нажатия клавиш до того, mi. пт успевает получить кнопка Command 1, находящаяся в фокусе ввода.
Рис. 20-2 Демонстрация кода, поддерживающего
«горячие* клавший
См. также....
Раздел «Дорогой Джон, как... определить состояние клавиш-модификаторов?» ранее в этой главе.
Приложение Jot вглавс 32 «Базыданных», гдетоже используются «горячие» клавиши.
ГЛАВА 21
Программирование элементов TextBox и RichTextBox
RichTextBox (текстовое поле с форматированием) — один из самых мощных и полезных элементов управления в Visual Basic. Несколько несложных манипуляций над его свойствами — и он превращается в весьма приличный текстовый редактор, равноценный WordPad (утилите, поставляемой с Windows). Я поясню здесь, как это делается, и покажу, чего можно добиться от элементов управления TextBoxи RichTextBox.
Дорогой Джон, как...
Отобразить содержимое файла?
Для этого можно использовать текстовое поле обычное или с форматированием.СледующийкодзагружаетфайлАиТОЕХЕС.ВАТ и отображает его содержимое в окне с прокруткой. Чтобы опробовать этот пример, добавьте на новую форму элемент управления TextBox или RichTextBox и присвойте ему соответственно имя MTcvt или rtf jest. В случае TextBox установите свойство MultiLine как True и ScrollBars как 3 - Both. Если Вы хотите просмотреть другой файл, а не AUTOEXEC.BAT, измените имя файла в операторе Open. В данном примере я предпочел RichTextBox, но его можно подменить элементов управления TextBox.
Option Explicit■
Private Sub Form_Load().;
Dim strF As String,.••
1 загружаем файл в строку■■
Open "C:\AUTOEXEC.BAT" For Binary As #1,
strF = Space$(LDF(1)).,
Get #1, , strF'
Close #1;.
' отображаем содержимое файла
' в текстовом поле с форматированием
rtfTest.Text = strF.
End Sub
Рис. 21-1 иллюстрирует результат выполнения этой программы.
* Ui^.^p-r j«ie...: ..В1ЙЛ31
JEIHIOFF;
; еем.
• FltM=•:
" REMй
polhiAy*idow$x;\wuidQi*is\COIviMAND:c\ik»'?
He4sl loscdOOILH&WNDOWSSCOMMANDSWSCDEX Ъ |/LD«:T05CDffi1/MlDfl
■tW1 By Windows Setup - LH cVmcUSeX^stem^mcusecxe В lwO,V PSFDNT_FATH-C.-Riyr»iij
BFbt/fGSAUcte^Bce/M*l№tt/l'ype1|
Ч\Рг*дг#тг^ AYR ^\'*J»!^ p^ * vii yr117
li**! PaI^PSIIim T-j-cVt'c'i'
Рис. 21-1 Содержимое файла AUTOEXECJ3A7'. отображаемое в текстовом поле с форматированием
В приведенном выше примерея загрузил весь файл AUTOEXEC. ВАТ в единственную строку, применив режим двоичного доступа, Он удобен тем, что позволяет считывать из файла все байты независимо от их значений. Иными словами, загружая многострочный файл в единственную строку, Вы включаете в нее и управляющие символы (перевода строки и возврата каретки). Подробнее о режиме двоичного доступа к файлам см. главу 15 «Файловый ввод/вывод».
Если Вы считываете RTF-файл, используйте вместо свойства Text элемента RichTextBox его же свойство TextRTF. Тогда коды, определяющие оформление текста, интерпретируются корректно.
И еще один способ формирования «многострочной» строки, о котором следует знать. Считывая файл в режиме последовательного доступа. Вы можете сами сцеплять строки, но при этом должны вставлять в конце каждой из них символы возврата каретки и перевода строки, как показано в следующем коде. Он выполняет ту же задачу, что и предыдущий, но делает это иначе. Файл AUTOEXEC. ВАТ загружается последовательно — строка за строкой; из них формируется единая строка, отображаемая в текстовом поле с форматированием. Сравните эту программу с предыдущим листингом.
Option Explicit
Private Sub Form_Load()"
Dim strA As String, strF As String
'' загружаем файл в строку
Open "C:\AUTOEXEC,ВАТ” For Input As #1
Do Until E0F(1).
Line Input #1, strA■
strF = strF 4 strA & vbCrLf
Loop*1 ■ "’
Close #1
' отображаем содержимое файла"
' в текстовом поле с форматированием
rtfTest.Text = strF
End Sub
ПРИМЕЧАНИЕ До Visual Basic 4 строку с символами возврата каретки и перевода строки приходилось создавать само- ■ ■ ■ стоятельно. Теперь в Visual Basic встроена константа vbCrLf.
В прежних изданиях книги я использовал в обеих программах элемент управления TextBox, не способный оперировать более чем с 64 000 символов. С компактными файлами вроде AUTOEXEC.BAT элемент управления TextBox работает просто прекрасно, но для большей гибкости лучше заменить его на RichTextBox.
См. также...
• Раздел «Дорогой Джон, как... уместить в элементе управления TextBox более 64 Кб текста?» далее в этой главе — о том, как обойти ограничение на размер текста для TextBox.
Создать простой текстовый редактор?
Если в приложении требуется окно полнофункционального текстового процессора, подумайте о средствах OLE, позволяющих внедрить объект документа Microsoft Word. Но если Вас устроит простой текстовый редактор вроде Notepad или WordPad, задействуйте элемент управления TextBox или IlichTcxtBox. Посмотрим, как это делается, и попробуем создать такой редактор. Вот его код:
Option Explicit
Private Sub cmdCut_Click()
Dim strWork As String
Dim Wstart, Wlength
устанавливаем фокус на элемент RichTextBox
rtf Edit. SetFocus
получаем рабочие параметры'
strWork = rtf Edit. Text
Wstart = rtf Edit. SelStart.
Wlength = rtfEdlt.Seliength
' копируем выделенный текст в буфер обмена Clipboard. SetText Mid$(strWork, Wstart + 1. Wlength) 1 удаляем вырезанный текст из окна strWork = Left$(strWork, Wstart) + _
HidS(strWork, Wstart + Wlength + 1)
rtf Edit. Text = strWork
' позиционируем курсор ввода''
rtf Edit-SelStart = Wstartrt? '
End Sub
Private Sub cmdCopy_Click()
' устанавливаем фокус на элемент RichTextBox
rtf Edit. SetFocus
' копируем выделенный текст в буфер обмена Clipboard,SetText rtfEdit.SelText
End Sub1-
Private Sub cmdPaste_Click()
Dim strWork As String, StrClip As String
Dim Wstart, Wlength
' устанавливаем фокус на элемент RichTextBox irtf Edit. SetFocus■
' получаем рабочие параметры strWork = rtf Edit. Text
Wstart = rtf Edit. SelStart'
Wlength = rtf Edit. SelLength
■ удаляем выделенный текст (если таковой есть) ' и вставляем текст из буфера обмена strClip = Clipboard. Get i ахти
strWork = Left$( strWork, Wstart) + strClip + _
Mid$(strWork, Wstart + Wlength + 1) rtf Edit. Text = strWork ' позиционируем курсор ввода rtf Edit. SelStart = Wstart + Len<strClip) End Sub
Этот текстовый редактор в действии показан на рис. 21-2.
। •■•' д:Avetafla ipeedi зКл
1 j.p: LmT*Avenge«wii
Рис. 21-2 Текстовый редактор на основе элемента управления RichTextBox
В данном случае элементу управления RichTextBox присвоено имя rtfEdit. Установите его свойство RightMarginraK, чтобы автоматически переносить текст в окне. Например, следующий код вызывает автоматический перенос, кактолько вводимый пользователем текст достигает правого края окна:
rtfEdit.RightMargin = rtfEdit.Width
Чтобы можно было прокручивать длинные документы, установите свойство ScrollBars как 2 - rtjVertical.
На форме присутствуют 3 кнопки: ondCut, cmdCopyn cmdPaste. Вообще говоря, эти кнопки не обязательны, потому что элемент управления сам обрабатывает операции вырезки, копирования и
вставки при нажатии комбинаций клавиш Cirl+X. Ctrl+C и Ctrl-i-V- соответственно, не требуя при этом никакого дополнительного кода. Я же добавил кнопки в двух целях: показать, какие свойства используются при обработке этих операций редактирования, и просто для большей гибкости. Ведь Вы могли бы, скажем, добавить на форму стандартное меню Edit, и тогда лишь код, приведенный мной, позволил бы без всяких проблем обрабатывать события, связанные с его элементами, — достаточно скопировать нужный код в обработчики события Click соответствующих элементов меню.
И здесь возникает вопрос: в каких же случаях вместо RichTextBox используется TextBox? В 32-разрядных приложениях последний элемент управления применяется главным образом для создания простых полей ввода данных, но в Ю-разрядных — это единственное средство, позволяющее отображать содержимое файлов и принимать текстовый ввод. 32-разрядный элемент управления RichTextBox работает с текстами значительно больших размеров, поддерживает операции drag-and-drop и «форматирование. И вообще, практически все, что может TextBox, элемент RichTextBox делает лучше. Не говоря уж о том, что он позволяет работать со множеством шрифтов одновременно и поддерживает обширный набор встроенных RTF-команд. Да и свойств и методов у него куда больше, чем я продемонстрировал. Но обо всем этом Вы прочтете в справочной системе и печатной документации — пересказывать ее, по- моему, излишне.
См. также,..
• Приложение Jot в главе 32 «Базы данных», где элемент управления RichTextBox тоже используется как текстовый редактор.
Обнаружить изменение текста?
У текстовых полей TextBox и RichTextBox есть свойство Change, и поэтому может показаться, что обнаружить, не изменил ли пользователь содержимое элемента управления, совсем просто. Увы, без небольшого трюка здесь не обойтись. Обычно при загрузке формы — контейнера одного из этих элемент <», свойству Text элемента присваивается начальный текст, что приводит к генерации события Change, хотя пользователь ничего не редактировал. Как тут быть?
Я продемонстрирую решение этой проблемы на примере RichTextBox, но оно подходит и для TextBox. Объявим два Булевых флага. Переменная blnNotFirstFlag принимает значение True после генерации первого события Change, но переменная blnTextCbange- Flag- только если blnNotFirstFlag тоже True. А это возможно лишь при втором событии Change, когда пользователь действительно
что-то изменяет в тексте, связанном с rtjEdit. Переменная blnText- CbangeFlagg.oc'Y'jiYHa в пределах формы, поэтому ее значение легко проверить в обработчике события Unload формы. В следующем примере выгрузка формы запрещается, если содержимое элемента rtjEdit изменено:.
Option Explicit-
Private blnTextChangeFlag As Boolean
Private Sub Form_Load()
rtfEdit.Text = "Edit this string..." blnTextChangeFlag = False
End Sub
Private Sub Form_Unload(Cancel As Integer)
If blnTextChangeFlag = True Then
Cancel = T rue
NsgBox "Save changes first", 0, _ - "(Testing the Change event...)" End If■
End Sub
Private Sub rtfEdit_Change()
Static blnNotFirstFlag As Boolean blnTextChangeFlag = blnNotFirstFlag blnNotFirstFlag = True
End Sub1
Используя этот прием в своем приложении, Вы, наверное, предпримете и другие действия, а не только те, что показаны в моем примере. Если Вы хотите посмотреть, как он работает, разместите на форме элемент управления RichTextBox с именем rtjEdit. Запустив программу, измените текст и попробуйте выгрузить форму нажатием клавиш A11+F4 или щелчком кнопки Close в заголовке формы.
Дорогой Джон, как...
Уместить в элементе управления TextBox более 64 Кб текста?
В 16-разрядных приложениях, написанных на Visual Basic 4, свойство Text элемента управления TextBox накладывает ограничение на размер текста — чуть меньше 64 Кб. (На самом деле, в зависимости от того, как именно Вы используете строковое пространство, это значение может сократиться до 32 Кб.) В Windows 95 и Windows NT ограничение на размер текста легко обойти простой заменой TextBox на RichTextBox. Так как книга посвящена главным образом программированию для 32-разрядных версий Windows, поначалу я решил даже не рассматривать эту тему. Но, поскольку приемы, необ-
ходимыедля решения такой задачи, в частности применение ключевого слова Preserve с оператором ReDim, весьма полезны и для других целей, я в конце концов включил ее в книгу. Если Вы все еще разрабатываете 16-разрядные программы, Вас тем более заинтересует этот раздел. А если Вы создаете исключительно 32-разрядные приложения, все равно прочтите его — почерпнете целый ряд полезных приемов.
Cop^ight IЕ j 1934-98 Miwoeolt CcjporetionCop^ight IЕ j 1934-98 Miwoeolt Ccjporetion: Ie: IeРис. 21-3 Отображение в текстовом поле содержим ого большого файлаРис. 21-3 Отображение в текстовом поле содержим ого большого файлаЧтобы обойти ограничение на длину текстовой строки, поместите текст в какой-нибудь массив и впоследствии присваивайте один из его элементов свойству Text элемента TextBox. При этом размер каждого элемента массива должен быть чуть меньше 64 Кб, но сами массивы даже в 16-разрядной версии Visual Basic могут достигать практически любого размера. Таким образом, Вы можете хранить в массиве очень большой объем текста и по мере необходимости присваивать его отдельные порции текстовому полю. В программе, приведенной ниже, текстовое поле содержит лишь несколько десятков строк, но можно очень быстро загрузить следующую порцию строк из массива, хранящего более 64 Кб текста. На рис. 21-3 показан результат выполнения этой программы на примере текстового файла размером около 668 000 байтов.
Cop^ight IЕ j 1934-98 Miwoeolt Ccjporetion
Cop^ight IЕ j 1934-98 Miwoeolt Ccjporetion
: Ie
: Ie
Рис. 21-3 Отображение в текстовом поле содержим ого большого файла
Рис. 21-3 Отображение в текстовом поле содержим ого большого файла
\MN3GAF4.TXT -*Win32API DedaitfiorrtlwVisual Веж
Пи t tie n requied l« ffw-’У 'iu j Betc E Л recrin d Ihe Др D№i wtirne d Н11Ч1И dl n<f mCrX Mrleti|y«lh rhevnr-;; 60AP?Lnad6i Ths fib i baakwatdi comortbto wthprew of H-» AMLMder will-, Hw «KtRtin 6s Coculerili ti«ti^ k ^JT№al«Pj№h 'Ns Ab
Чтобы воспроизвести мой пример, добавьте на новую форму текстовое поле txtTest и вертикальную полосу прокрутки vsbTest. Установите свойство MultiLine поля как True, а его свойство Scrol- IBars — как 1 - Horizontal. Элемент управления -вертикальная полоса прокрутки* заменяет ту, что встроена в текстовое поле; разместите его вплотную к правому краю текстового поля. Введите в модуль формы следующий код и подставьте в оператор Open имя любого текстового файла, который есть на Вашей машине.
Option Explicit .
Private Const LINES = 15 Private strA() As String
Private Sub Form_Load()।
Dim intN
' загружаем большой текстовый файл
' в динамический строковый массив
Open -C:\WIN32API.TXr For Input As #1 Len = 1024
' Do Until E0F(1)
intN = intN + 1
ReDim Preserve strAfintN + LINES)
Line input #1, strA(intN)
Loop
Close «1
' устанавливаем свойства вертикалвной полосы прокрутки
With vsbTest
.Hin = 1
. Max = intNi
.SmallChange = 1.
.LargeChange = intN \ 10' '
End With’
End Sub. '
Private Sub vsbTest .ChangeO"
Dim inti As Integer-
Dim StrTmp As String
' формируем "экранную"строку из элементов массива
For inti = vsolest.value To vsbTest.Value + LINES
strTmp = strTmp + strA(intl) + vbCrLf
Next inti
txtTest.Text = strTmp
End Sub
При загрузке формы текстовый файл считывается в динамический строковый массив. Этот процесс занимает несколько секунд, поэтому в реальном приложении файл следует загружать параллельно с выполнением других операций — чтобы не нервировать пользователя. Вы можете взять любой текстовый файл размером более 64 Кб, но, если он очень велик, приготовьтесь к ожиданию.
Как только файл загружен в память, программа устанавливает свойства вертикальной полосы прокрутки, учитывая размерность получившегося строкового массива. Содержимое текстового поля обновляется при каждом событии Change вертикальной полосы прокрутки. Программа считывает из массива порцию строк и, вставляя символы возврата каретки и перевода строки, сцепляет их в единую строку, которую копирует в свойство Text текстового поля.
Эта программа лишь отображает содержимое файла и не отслеживает никаких изменений, вносимых в текст. Если Вы хотите преобразовать ее в текстовый редактор, добавьте код, обновляющий строковый массив при изменении содержимого текстового поля.
^l^rtrtifrtM Г! 1к1/М4
Выбрать шрифт для TextBox или RichTextBox в период выполнения?
Самое удобное для пользователя и «защищенное от дурака» средство выбора шрифта при выполнении программы — элемент управления CommonDialog. (Он же позволяет выбирать файлы, изменять цвета, настраивать принтер и др.) Добавьте на новую форму текстовое поле /л7‘/е«, кнопку lUidFonty. элемент управления CommonDialog с именем dlgFonts,а потом введите в модуль формы код: Option Explicit'
Private Sub cmdFont_Cllck()
dlgFonts.Flags = cdlCFScreenFonts. "
dlgFonts.ShowFont
With txtTest.Font1
.Name = dlgFonts.FontName.
.Bold = dlgFonts.FontBold
.Italic = dlgFonts.Fontltalic
.Size = dlgFonts.FontSize
End With
End Sub■
Private Sub Form_Load()■
txtTest.Text = "ABCDEF abcdef 0123456789""
End Sub
Процедура Form_Loacl загружает в текстовое поле просто образец текста — чтобы было на чем поэкспериментировать со шрифтами. Процедура cmdFont_Clickдeлaeт кое-что поинтереснее. Сначала она записывает в свойство Flags элемента CommonDiabg такое значение, чтобы тот отображал текст системным экранным шрифтом (возможные значения свойства Flags см. в справочной системе), потом вызывает метод ShowFont, открывающий стандартное диалоговое окно Font (Шрифт), и, наконец, устанавливает свойства текстового поля в соответствии со значениями, выбранными пользователем в этом диалоговом окне. Содержимое текстового поля до и после выбора шрифта иллюстрируют рис. 21-4 и 21-5, а само диалоговое окно Font показан- на рис. 21-6.
ПРИМЕЧАНИЕ Элемент управления TextBox позволяет использовать лишь один шрифт единовременно. Работу сразу > с несколькими шрифтами поддерживает элемент управления ' RichTextBox. Подробное описание манипуляций над шрифтами в этом элементе управления см. в справочной системе — ищите свойства, начинающиеся с Sei.
Рис. 21-4 Текстовое поле, отображающее текст шрифтом по умолчанию
Рис.21-5 Текстовое шне. отображающее текст выбранным шрифтом
Рис. 21-6 Стандартное диалоговое окно Font
См. также...-
• Раздел <Дорогой Джон, как... произвольно масштабироватьшриф- ты?» в главе 14 Графические методы» — о масштабировании шрифтов.
ГЛАВА 2 2
Многодокументный интерфейс
13 большинстве приложений многодокументный интерфейс (Multiple Document Interface, MDI) просто идеален для структуризации пользовательского интерфейса, но из-за некоторых каверзных нюансов MDI немногим удается задействовать все его преимущества. В этой главе я попытаюсь пролить свет на основные концепции, связанные с разработкой MDI-приложений, и дать тот минимум материала, который позволит Вам самостоятельно продолжить изучение данной темы.
г ?i Джон, как...
Создать MDI-приложение?
Я расскажулишь о базовых концепциях разработки MDI-ириложе- ний. Дополнительную информацию см. в справочной системе или в Visual Basic Books Online
В свое время, только-только начав работу с MDI-формами, я долго мучился, пытаясь сложить разрозненные сведения по этой теме в сколько-нибудь стройную систему. Теперь материал, изложенный в следующих разделах, даст Вам точку опоры, которой у меня не было.
'MDI-форма
Проект Visual Basic может содержать одну MDI-Форму. Она служит контейнером всех дочерних форм. Чтобы добавить в проект MDI- форму, выберите из меню Project команду Add MDI Form и дважды щелкните значок MDI Form. Вы наверняка заметите, что список свойств этой формы ;г . :, чем у стандартной. MDI-форма не обязательно должна быть в приложении стартовой, но, если Вы хотите сделать ее именно таковой, выберите из меню Project команду Project Properties, в одноименном диалоговом окне активизируйте вкладку General и в списке Startup Object укажите MDI-форму.
Дочерние формы ’
В проекте допустимо любое число дочерних форм, содержащихся в MDI-форме (как допустимо и произвольное количество независимых форм). Чтобы создать дочернюю форму, установите свойство MDIChild стандартной формы как True. За загрузку, отображе-
ние, скрытие и выгрузку дочерних форм отвечает Ваша программа. Обратите внимание, что на этапе разработки дочерняя форма выглядит и ведет себя абсолютно так же, каки стандартная. Различия проявляются в период выполнения.
На рис. 22-1 показано выполняемое MiJl-ириложение с набором дочерних форм — некоторые из них свернуты, другие видны полностью.
Рис. 22-1 Пример ли >1-фор.мы с набором дочерних форм
Свойства ActiveForm и ActiveControl
Используя оба этих свойства, MDI-форма может ссылаться на элементы управления, принадлежащие активной дочерней форме. Допустим, в Вашем MDI-приложении на несколькихдочерних формах есть идентичные текстовые поля. Когда в текстовом поле на одной из таких форм пользователь выделяет фрагмент текста и выбирает команду Сору, свойство AciiveForm.ActiveControl.SelText ссылается на текст, выделенный в активном текстовом поле на активной дочерней форме._
Ключевое слово Me' " "
Ключевое слово Me действует как переменная, ссылающаяся на активное дочернее окно. При создании нескольких экземпляров дочерней формы Me дает возможность коду, написанному для исходной формы, работать с ее экземпляром, находящимся в фокусе.
СвойствоТад
Оно позволяет однозначно идентифицировать конкретные экземпляры дочерних форм. Создавая новый экземпляр, записывайте в его свойство Tag определенное число или строку. Тем самым Вы сформируете систему индексов, позволяющую отслеживать экзем-
пляры формы. Если же Вы этого не сделаете, все экземпляры формы будут идентичны друг другу.
Фундаментальные особенности MDI-формы
MD1-форма уникальна в сравнении с другими формами. Поэтому при разработке MDI-приложенпя учитывайте ее особенности.
Дочерние формы всегда появляются только в пределах MDI- формы — даже в свернутом состоянии — и неразрывно связаны с ней: сверните MDI-форму. и они исчезнут с экрана,
“ Когда в фокусе оказывается дочерняя форма, содержащая меню, оно появляется на МОКфорли. В период выполнения ни на одной дочерней форме Вы никогда не увидите их меню — они немедленно мигрируют на родительскую MDI-форму.
В На этапе разработки в MDI-форму можно добавить любое меню, но элементы управления — только те, у которых есть свойство Align. (Впрочем, это ограничение несложно обойти: нарисуйте на MDI-форме окно PictureBox и разместите в нем любые нужные Вам элементы управления.) Если на MDI-форму вставить какой-нибудь элемент управления, дочерние окна не смогут перекрывать ни одной его части.
в Оператор Dim As New позволяет в период выполнения создавать несколько экземпляров одной дочерней формы, что важно для многих MDI-приложений. Как я уже говорил, свойства ActiveForm, ActiveControl и Tag, а также мючер.ос слово Me очень полезны при работе с несколькими экземплярами дочерних форм. Дополнительную информацию на эту тему см. в справочной системе.
и Свойство Picture MDI-формы позволяет использовать любое графическое изображение как фоновую подложку. Это особенно удобно, когда надо показать эмблему компании.
«0 См. также...
ЛА.
Приложение Jot в главе 32* Базы данных» — о «тиражировании* единственной дочерней формы, предназначенной для ввода данных, чтобы пользователь мог редактировать эти данные в нескольких окнах.
Дорогой Джон, как...
Добавить эмблему на MDI-форму?
Поместить изображение в центр MDI-формы непросто — свойство Picture всегда выводит его в верхний левый угол. Вот как обойти эту проблему. Прием, который я собираюсь продемонстрировать, позволит центрировать эмблему даже при изменении размеров MDI- формы. Взгляните на рис. 22-2, где показано гипотетическое при-
ложение в момент запуска. MDI-форма в нем определена как стартовая и отображает в центре эмблему вымышленной компании.
Теперь посмотрим, как это делается. Создайте новый проект, содержащий MDI-форму и одну дочернюю — утаТо^оДобавьте на дочернюю форму элемент управления Image с именем imgLogo. Его нельзя вставить прямо на MDI-форму. а если воспользоваться «посредническими услугами» элемента управления PictureBox, не удастся отцентрировать эмблему после изменения размеров MDI-формы. Но разместив элемент Image на дочерней форме. Вы всегда сможете отцентрировать ее, а значит, и саму эмблему. Весь фокус в том, что Вы подгоняете размеры/rmZogo под размеры изображения, удаляете ее обрамление и заголовок и следите, чтобы она находилась в центре MDI-формы. Установите свойство MDIChild дочерней формы как Trustee же свойство BorderStyle — как О - None и загрузите в imgLogo файл с изображением эмблемы. Пусть Вас не волнует позиция изображения и. frmLogo — об этом позаботится программа. Введите код, показанный ниже, в модуль MDI-формы и запустите программу.
?1У?? 1 l^S^ Ан *=*,.. ;^.x?iv;nt6ii... -... .б^УдЕи
Рис. 22-2 MDI-форма с центрированной эмблемой
Option Explicit■
Private Sub NDIForm_Resize()
' смещаем эмблему в верхний левый угол элемента Image frmLogo.imgLogo.Nove О, О
' подгоняем размер формы под размер изображения
frmLogo.Width = frmLogo.imgLogo.Width
frmLogo.Height = frmLogo.imgLogo.Height
' центрируем форму с эмблемой на MDI-форме 11'
frmLogo.Left = (ScaleWldth - frmLogo.Width) \ 2
frmLogo.Top = (ScaleHeight - frmLogo.Height) \ 2
' показываем эмблему
frmlogo.Show,
End Sub
Изображение смещается в верхний левый угол содержащей его формы. Далее размер формы подгоняется под размер изображения и она размещается по центру MDI-формы, Я включил этот код в процедуру обработки события Resize MDI-формы для оч и. чтобы изображение оставалось в центре даже при изменении ее размеров.
Чп а к .*■lire ЧГ ■
1!
ГЛАВА 23
Доступ к базам данных
13 последних версиях Visual Basic становится все очевиднее эволюция подхода Microsoft к такой области, какдоступ кданным. Весьма мощные среясти । языка, предназначенные для этой цели, делают его идеальным в программировании баз данных. Обсуждение всего, что связано с программированием баз данных, выходит далеко за рамки книги, поэтому я даю здесь лишь краткое введение в эту тему. Более подробную информацию см. в Visual Basic Books Online и в справочной системе. Советую также заглянуть в книжный магазин — наверняка найдете пару книг по базам данных.
В этой главе я расскажу о мастерах в Visual Basic 6, помогающих быстрее освоиться с базами данных и упрощающих доступ к ним, а также представлю два подхода к подключению приложений к базам данных в Visual Basic и опишу новые средства генерации отчетов.
Простейший и самый прямолинейный способ подключения приложения к базе данных основывается на использовании элемента управления Data и требует минимума кода. Второй, более гибкий, — на применении DAO (Data Access Objects — объекты доступа кданным) и обеспечивает полный контроль над любыми базами данных.
Дорогой Джон, как...
Использовать мастера при разработке баз данных?
Применяя инструментальные средства, поставляемые с Visual Basic и Microsoft Access, Вы сэкономите массу времени и сил. Например, в Visual Basic включены два мастера, облегчающих вставку в приложение формы для доступа к данным. Они создают упрощенный вариант типичной формы, предназначенной для операций с базой данных, — если вдуматься, это как раз то, что обычно и надо. Ну а когда требуется что-то посложнее, она послужит своего рода отправной точкой.
Если Вы заранее знаете, что в проект следует включить форму для доступа к данным, то значительно ускорите свою работу, прибегнув к услугам Application Wizard. Чтобы запустить этот мастер, выберите из меню File команду New Project и в появившемся диалоговом окне дважды щелкните значок VB Application Wizard. По-
мимо меню, нескольких видов очень полезных форм и прочих компонентов стандартного приложения, Application Wizard позволяет добавить в новый проект и форму для операций с базой данных. Этот мастер определенно заслуживает самого пристального внимания ■ при разработке типовых приложений Вы сэкономите уйму времени.
Гораздо большей гибкостью в создании форм для операций с базами данных обладает Data Form Wizard, который можно запустить при добавлении в проект новой формы: выберите из меню Project команду Add Form и дважды щелкните значок \ 1: Data Form Wizard. Этот мастер предлагает несколько вариантов визуального представления данных, в зависимости от Вашего выбора запрашивает ту или иную информацию и в конечном счете создает рабочую форму, подключенную к указанной базе данных.
В Visual Basic 6 появилось несколько других предопределенных типов форм для подключения к базам данных. Выбирая из меню Project команду Add Form, обратите внимание на формы типа Data- grid и Query.
Одна из приятных особенностей Data Form Wizard — возможность выбора фундаментальных типов форм для доступа к базам данных. В каких-то случаях достаточно простой формы, единовременно отображающей одну запись из таблицы или запроса, а в каких-то — нужна форма, способная показывать группы записей. Оба типа форм автоматически генерируются этим мастером. Полезен и третий тип формы — Master/Detail. На ней отображается одна (текущая) запись из таблицы или запроса и сама таблица. Текущая запись и таблица связываются через одно из общих полей. Например, на основе демонстрационной базы данных B1BLIO.MDB, расположенной в основном каталоге Visual Basic, Data Form Wizard может создать форму, в верхней части которой появляется информация о выбранном Вами издательстве, а в нижней — связанная таблица с перечислением всех книг, выпущенных этим издательством (рис. 23-1). Когда Вы «перелистываете» записи по издательствам, таблица автоматически заполняется выходными данными соответствующих книг.
Нет смысла подробно описывать работу с Data Form Wizard - советую просто поэкспериментировать с ним на примере базы данных BIBLIO. MDB или NWIND. MDB, которые поставляются с Visual Basic. Использовать его очень легко, уже через несколько минут, уверен, Вы во всем разберетесь. Он здорово экономит время на подготовке форм для доступа к данным.
Еще эффективнее создавать и вести базы данных с помощью Microsoft Access в сочетании с Data Form Wizard. Visual Basic, как Вы увидите в следующих разделах, тоже позволяет создавать базы данных, но ему гю же далеко до возможностей Microsoft Access.
Рис. 23-1 Форма типа Master/Detail, созданная с помощьюРис. 23-1 Форма типа Master/Detail, созданная с помощьюData Form WizardData Form Wizard
Рис. 23-1 Форма типа Master/Detail, созданная с помощью
Рис. 23-1 Форма типа Master/Detail, созданная с помощью
Data Form Wizard
Data Form Wizard
Дорогой Джон, каКч»
Подключить приложение к базе данных через элемент управления Data?
Попробуем создать на основе элемента Data простейшее приложение, управляющее базой данных. Для этого надо сначала создать базу данных, а потом сформировать ее пользовательский интерфейс.
Создание базы данных с помощью Visual Data Manager
Базу данных можно создать программно (чем мы займемся в следующем разделе) или с помощью надстройки Visual Data Manager. Работая с элементом управления Data, я предпочитаю использовать с этой целью именно Visual Data Manager. Он позволяет определить таблицы и поля базы данных.
Чтобы создать базу данных через Visual Data Manager:
выберите из меню Add-Ins команду Visual Data Manager,
если появится диалоговое окно с запросом «Add SYSTEM.MDW (Microsoft Access Security File) to INI ! if ■ . щелкните кнопку No, так как защита файлов в этом примере не понадобится. После этого открывается окно Visual Data Manager;
выберите из меню File надстройки Visual Data Manager команду - New, в первом подменю в качестве типа базы данных укажите Microsoft Access, а во втором — Version 7.0 MDB. В итоге появится диалоговое окно Select Microsoft Access Database То Create (создание базы данных Microsoft Access);
задайте каталог, где будет храниться создаваемая база данных, в поле File Name введите BDAYw щелкните кнопку Save. К имени файла автоматически добавляется расширение MDB. Затем Вы увидите окна Database Window (окно базы данных) и SQL Statement (SQL-оператор). На этом этапе база данных уже существует, но пока не содержит определений таблиц и полей;
щелкните правой кнопкой мыши в окне базы данных и выберите из контекстного меню команду New Table (создать таблицу), чтобы открыть диалоговое окно Table Structure (структура таблицы);
в поле Table Name (имя таблицы) введите Birthdays — имя нашей единственной таблицы;
щелкните кнопку Add Field (добавить поле), чтобы открыть одноименное диалоговое окно. В поле Name введите Name — это первое поле в таблице;
в списке Туре (тип) уже показывается строка Text, и в данном случае ничего менять не требуется;
в поле Size (размер) введите 30, чтобы задать максимальное количество символов, которое может храниться в поле Name таблицы;
щелкните кнопку ОК, и определение поля будет добавлено в список полей текущей таблицы. Теперь можно определить второе поле;
.в поле Name введите Birthday, в списке Туре выберите сэром Date/ Time и щелкните кнопку ОК — в таблице появится второе поле;
щелкните кнопку Close, чтобы закрыть диалоговое окно Add Field. После этого диалоговое окно Table Structure должно выглядеть так, как показано на рис. 23-2;
Рис. 23-2 Добавление полей в таблицу Birthdays
щелкните кнопку Build The Table (сформировать таблицу) для завершения процесса и закрытия таблицы Birthdays. Рис. 23-3 иллюстрирует базу данных В DAY;
Рис. 23-3 База данных BDAY, созданная с помощью Visual Data Manager
закройте Visual Data Manager, чтобы вернуться в Visual Basic.
Ну вот мы и создали базу данных, хотя никаких записей она пока не содержит.
Создание пользовательского интерфейса
Теперь нашей базе данных нужен пользовательский интерфейс. Откройте новый проект стандартной ЕХЕ-нрограммы и нарисуйте на форме Form! два текстовых поля (txtNamew txtBirtbday)^ элемент Data (е/д/В/Эдт). Гели хотите, добавьте метки, как это сделал я, но они не обязательны. Законченная форма, уже содержащая кое- какие данные, показана на рис. 23-4..
* Birthday Ъл|^йм' ' RFII3
Цт» I VI Г1-* ^
ЯИМе и i I "j , | |
IhUD у.IhUD у.
IhUD у.
IhUD у.
Рис. 23-4 Простое приложение, управляющее базой данных. BDAYчерез элемент Data
Главное при использовании элемента Data и других элементов управления, связанных с данными, — корректная установка их свойств. Работу с элементом Data всегда начинайте с записи в его свойство DatabaseName имени файла подключаемой базы. В данном случае
свойство DatabaseName следует установить как BDAYMDB (это тот самый файл, который мы только что создали с помощью Visual Data Manager). Свойств, - RecordSource должно хранить имя таблицы в базе данных, на которую будет ссылаться элемент Data, т. е. Birthdays. При этом действует правило: один элемент Data — одна таблица.
В нашем примере необходимо установить и свойство EOFAction. Чтобы получить возможность добавлять новые записи при выполнении программы, выберите для него значение 2 - Add New Тогда, перейдя к пустой записи в конце базы данных, пользователь сможет заполнить поля Name и Birthday — в базе данных автоматически появится новая запись.
Сам по себе элемент Data мало что делает. С ним надо сопоставить другие элементы управления, и лишь тогда Вы сможете работать с базой данных. Б этом примере, чтобы связать текстовые поля с элементом Data (datBDayi мы используем их свойства DataSource и DataField. Установите свойство DataSource каждого текстового ноля как datBDay. а их свойства DataField инициализируйте именами полей, с которыми сопоставляются эти элементы управления: Name — для LxtNamey. Birthday — для txtBirthday. Кстати, после подключения элемента Data к базе данных свойства DataSource и DataField устанавливаются простым выбором нужного из списка значений, допустимых для этой базы данных.
Запуск приложения
Да, именно так! С базой данных можно работать, не написав ни строчки кода. Запустите приложение, введите новые имя и дату рождения и щелкните кнопку со стрелкой на элементе управления Data, чтобы перейти к следующей записи.
Конечно, этот простой пример не предусматривает удаления или поиска минеей. Чтобы превратить его в настоящее приложение, нужно написать код, который поддерживает соответствующие возможности. -
Дорогой Джон, как». .
Подключить приложение к базе данных через DAO?
Объекты в Visual Basic буквально всюду, и одна из областей, где они наиболее существенно расширили язык, — доступ к данным. Так давайте же используем их для создания еще одного простого приложения, управляющего базой данных, и обойдемся без элемента Data. Но сначала рассмотрим, что представляют собой объекты доступа к данным в Visual Basic.
DAO ‘
Лучший способ понять структуру объектов в Visual Basic — изучить схему, иллюстрирующую их иерархию. Такую схему называют моде-
лью объектов. Иерархия объектов доступа к данным показана на рис. 23-5. Когда я разбирался в объектах доступа к данным, эта схема помогла мне уяснить связи наборов и объектов L.W г после чего было уже нетрудно обращаться к ним, используя подходящий синтаксис.
DBEngine
I
" Works pace(s) — Connection(s) h“ QueryDef(s) tParameter(s) Field(s) Recordset(s)
;L- Field(s)
^ Database(s) „„.■v TableDef(s) tField(s) Index(es) jL™. Field(s)
'— Container(s)
.I» Document(s)
Query Def(s) tParameter(s) Field(s) '—- Recordsef(s) ’
Field(s) :*— Relation(s)"
Field(s)
■—. Group(s)
User s) I— User(s) &ЙЙЯЯ5 Group(s)
— Error(s) Рис. 23-5 Структура объектов доступа к данным в VisualBasic
К именам объектов, которые могут быть наборами, я добавил суффикс (у. Например, в uai 1 есть и объект Workspace, и набор Workspaces, при этом Workspaces (О) является первым объектом Workspace. Обратите внимание, что в DAO лишь объект DBEngine не входит ни в какие наборы.
Образец кода, приведенный Б следующем разделе, иллюстрирует, как объявлять и использовать эти объекты. Сравните синтаксис обращения к ним со структурой, показанной на рис. 23-5. Если Вам что-то не понятно в этом синтаксисе, найдите в справочной системе описание нужных объектов и изучите все примеры, которые Вы там увидите.
Создание базы данных через DAO .
Е1еболыпая программа (см. ниже) создает базу данных Phones, используя лишь объекты Р \^ (без элемента управления Data). Я сделал ее аналогичной предыдущей, чтобы облегчить их сравнение. В данном случае определяются 2 текстовых поля: одно — для имени, другое — для телефонного номера (или любого текста, который Вы пожелаете ввести, скажем, адреса электронной почты).
Откройте новый проект стандартной ЕХЕ-программы и добавьте на форму Form 1 кнопку cmdCreale. Установите ее свойство Caption как Create. Проверьте, есть ли ссылка в проекте на библиотеку DAO, иначе возникнет ошибка. Чтобы установить • ■■ ш, на эту библиотеку, выберите из меню Project команду References и пометьте флажок в строке Microsoft DAO 3.51 Object Library. Введите в модуль формы следующий код (если Вы не хотите, чтобы база данных оказалась в корневом каталоге диска С, измените путь к ее файлу): ., Option Explicit,
Private Sub cmd€reate_Click()
' создаем объектные переменные для объектов DAO
Dim dbWorkspace As Workspace
Dim dbDatabase As Database -.
Dim dbTableDef As TableDefr -
Dim dbName As Field- .• . ■
Dim dbNumber As Field.
' создаем объекты DAO
Set dbWorkspace = DBEngine, Workspaces(O).
Set dbDatabase = dbWorkspace. CreateDatabase( _‘
"C:\PHONES.MDB", dbLangGeneral)
Set dbTableDef = dbDatabase. CreateTableDef("Phones")
Set dbName = dbTableDef.CreateFieldC'Name", dbText).
Set dbNumber = dbTableDef. CreateField("Number", dbText)"
L определяем свойства полей
dbName.Size = 30
dbNumber,Size =30'-
' добавляем каждый объект-поле к его объекту-таблице
dbTableDef.Fields.Append dbName
dbTableDef,Fields.Append dbNumber
' добавляем каждый объект-таблицу к его объекту - базе данных dbDatabase.TableDefs.Append dbTableDef
' закрываем сформированную базу данных
dbDatabase.Close"
MsgBox "Database Created", , "Create Phones" End Sub
Рис. 23-6 иллюстрирует эту программу в действии. Все, что нужно, — разок щелкнуть кнопку Create, и на жестком диске появится новая база данных. Так как в программе достаточно подробные комментарии, я отмечулишь самое главное: в первой половине кода объявляются все объектные переменные, которым присваиваются ссылки на создаваемые объекты DAO. Остальная часть кода оперирует со свойствами и методами этих объектов. Я указываю значения свойств Size двух полей типа Text и устанавливаю связи между соответствующими объектами, используя их методы Append. Вот так определяется структура новой базы данных.
ЙЙЙЙЙЙНяЯ^^МЕоШ ■
Рис. 23-6 База данных создается одним щелчком кнопки
Хотя новая база данных Phones еще не содержит никакой информации, ее внутренняя структура сформирована, и она готова к работе. Мы создали одну таблицу Phones с двумя текстовыми полями — Name и Number, длина которых не может превышать 30 символов.
ПРИМЕЧАНИЕ Если, экспериментируя, Вы захотите начать все сначала, просто удалите файл PHONES.MDB и перезапустите программу.
Доступ к базе данных
Теперь создадим простую программу, способную обращаться к базе данных Phones (и тоже без элемента управления Data). Она позволит переходить от одной записи к другой (в любом направлении), добавлять и редактировать записи (длина текста в каждом поле ограничена 30 символами). Если хотите расширить возможности этой программы, дополните ее кодом для удаления, поиска, сортировки записей и т. д.
Откройте новый проект стандартной ЕХЕ-программы. добавьте на форму 2 кнопки (cmdPreviousu cmdNexfpv установите их свойства Caption как- Previous и Next. Затем вставьте 2 текстовых поля — txtNameH txtNumber. Разметка всех элементов управления хорошо видна на рис. 23-7.
>Шёй£^
|PhjF ' W4 ^mu-w
Рис. 23-7 Програлгмауправляющая базой данных Phones, в дейстеин
И, наконец, введите в модуль формы код, показанный ниже. Запустив программу, Вы получите хоть и простой, но вполне работающий редактор базы данных. Убедитесь, что путь к файлу базы данных Phones, «зашитый» в коде, соответствует ее реальному местонахождению на жестком диске и что в проекте установлена ссылка на Microsoft DAO 3.51 Object Library. Если такой ссылки нет, выберите из меню Project команду References и пометьте флажок в строке Microsoft DAO 3-51 Object Library.
Option Expliciti-
создаем объектные переменные для объектов DAO
Private dbWorkspace As Workspace
Private dbDatabase As Database ' ■1
■Private dbTable As Recordset. f
Private dDName As Field''
Private dbNuntbar As Field
Private Sub dNext_Click()
переходим к следующей записи, только если текущая не пуста
It txtName.Text о " " And txtNumber.Text <> " ' Then
сохраняем все изменения, внесенные в текущую запись
' UpdateRecord
переходим к следующей записи. ,
dbTable.MoveNext
формируем новую запись, если это - конец таблицы . •
If dbTable.EOF Then NewRecord.
1 отображаем данные из записи
DisplayFtelds
End If
1 сохраняем фокус на текстовом поле txtName
txtName. SetFocur.
End Sub
Private Sub cmdPrevious,_Click() '”
1 сохраняем все изменения, внесенные в текущую записи
UpdateRecord
' возвращаемся на одну запись назад
dbTable.MovePrevious
1 выход за первую запись недопустим
If dbTable,BOF Then dbTable,MoveNext
1 отображаем данные из записи
DisplayFields
' сохраняем фокус на текстовом поле txtName
txtName.SetFocus
End Sub
Private Sub Form_Load()'
создаем объекты DAO
Set dbWorkspace = DBEngine.Workspaces(O)
* если нужно, измените путь к базе данных
Set dbDatabase = dbWorkspace.OpenDatabase("С:\PHONES.MDB")
Set dbTable = dbDatabase.OpenRecordset("Phones”, dbOpenTable) ' если база данных совсем новая, используем особую обработку
If dbTable. ВОР And ннтяЫр EOF Then Newfiecord
1 начинаем с первой записи
dbTable.MoveFiг
' отображаем ее данные
DisplayFields_
End Sub- :■
Private Sub NewRecordO
' добавляем новую запись
dbTable.AddNew
1 вставляем пробел в каждое поле
dbTable!Name = " "
dbTable!Number= " "
' обновляем базу данных
dbTable.Update
1 переходим к этой записи
dbTable.MoveLast
End Sub
Private Sub UpdateRecord()
1 готовим таблицу к редактированию
dbTable.Edit
1 копируем содержимое текстовых полей
1 в соответствующие поля записи'
dbTable!Name = txtName.lext
dbTable!Number = txtNumber.Text
' обновляем базу данных■
dbTable.Update
End Sub.
Private Sub DisplayFieldsO
- отображаем содержимое полей записи
’ в текстовых полях на форме
txtName.Text = dbTableiName
txtNumber.Text = dbTableI Number■
End Sub
. Чтобы максимально упростить программу, я предпочел автоматическое обновление текущей записи при каждом щелчке кнопки Previous (предыдущая) или Next (следующая). Посколькутекстовые поля здесь не связаны ни с какими данными, координация их содержимого с тем, что хранится в полях текущей записи, целиком ложится на саму программу. А это, сами понимаете, требует больше кода, чем в случае программ, построенных на элементе управления Data. — зато Вы получаете гораздо большую свободу действий.
Например, я обновляю чекушую запись в момент перехода к следующей или предыдущей .записи. Однако код легко модифицировать так, чтобы текущая запись обновлялась исключительно при изменении содержимого текстового поля или только при щелчке кнопки Update (конечно, эту кнопку надо еще создать). Таким образом, программирование с применением объектов DAO гибче, чем с использованием элемента управления Data, но требует больше усилий.
Дорогой Джон, как...'
Создать отчет?
До Visual Basic 6 ответ на этот вопрос был бы один: использовать программный пакет для генерации отчетов, разработанный сторонней фирмой. Но теперь отчеты можно создавать с помощью самого Visual Basic, иыор.ш тип проекта Data Project. В этом случае создаются 3 объекта, образующие основу Вашего нового проекта: DataEnvironment (поддержка связей, запросов и других интерфейсных элементов базы данных), DataReport (интерактивное создание и модификация оформления печатаемого отчета) и стандартная форма (пользовательский интерфейс).
Объект DataReport отображает области данных (detail section), заголовка (header section) и примечания (footer section), в которые можно добавить любой из шести специальных элементов управления. Последние аналогичны стандартным элементам Label, TextBox, Image, Shape и Line, используемым на обычных формах, но предназначены специально для контейнера DataReport. Единственный элемент управления, который принципиально отличается от стандартных, — Function. Он позволяет выполнять над данными отчета целый ряд математических операций.
ГЛАВА 24
ActiveX-объекты из других приложений .
ActiveX — одна из важнейших разработок Microsoft для Windows и Интернета. Главная роль в ней отводится Автоматизации — технологии, позволяющей приложениям совместно использовать объекты. Число приложений, предоставляющих объекты Автоматизации внешним программам, постоянно растет, и Visual Basic открывает доступ к этим объектам. Кроме того, как я уже говорил в главе 5 «Объектно-ориентированное программирование-». Visual Basic сам дает возможность создавать модули классов, которые определяют ActiveX-объекты. предназначенные для других программ. А это делает Visual Basic логичным выбором для разработчика Windows- приложений.
На ряде примеров я покажу, насколько проще решаются многие распространенные задачи за счет объектов Word и Excel — приложений, в полной мере поддерживающих Автоматизацию.
Дорогой Пжпм. кзи...
Использовать ActiveX, чтобы проверить правописание?
Все приложения Microsoft Office 97 предоставляют ActiveX-объекты, с которыми можно работать из программ, написанных на Visual Basic. Наиболее интересны лю; основных приложения этого пакета: Word и Excel. Каждое из них включает обширную библиотеку объектов. Рассмотрим, как с помощью Автоматизации обратиться из Visual Basic к средствам проверки правописания, встроенным в каждое из этих приложений, и получить уже исправленный текст.
Проверка правописания средствами Microsoft Word
Следующая программа создает объект Document (текстового процессора Word) и, манипулируя некоторыми его свойствами и методами, заставляет проверить правописание введенного текста. (Объекты Document позволяют создавать и контролировать документы Word.) В коде достаточно комментариев, из которых ясно, что именно делает программа, но основная ее концепция такова: создается объект Document, в него вставляется текст и активизируются средства проверки правописания для этого документа, Еезуль-
таты копируются обратно в текстовое поле, замещая исходный текст. Код рассчитан на Microsoft Word .
Option Explicit_
Dim mdocSpell As New Document-
Dim rnblnVisible As Boolean.
i
Private Sub Form_Load()
переменная rnblnVisible используется в процедуре Eorm.Unload,
" чтобы определить, запускала ли эта программа Word
rnblnVisible = mdocSpell. Application. Visible
End Sub"
1 проверяем содержимое текстового поля■
Private Sub cmdSpell_Click()-
■ добавляем текст в объект Range текстового процессора Word mdocSpell,Range,Text = txtSpell
■ ВНИМАНИЕ: перед вызовом метода Checkspelling
надо выполнить следующие две операции:.
1) сделать окно Word видимым
mdocSpell.Application.Visible = True
2) активизировать Word
AppActivate mdocSpell.Application. Caption
" проверяем правописание'
mdocSpell.Range.CheckSpelllng1
' обновляем содержимое текстового поля•
в соответстаи с тем, что получено от Word
txtSpell = mdocSpell.напде.I ext
' отсекаем нулевой символ, добавленный Word txtSpell = Left(txtSpell, Len(txtSpell) - 1).
' активизируем свою программу"
AppActivate Caption
End Sub
* очистка
Private Sub Forre_Unload(Cancel As Integer) проверяем, запускала ли эта программа Word-
If rnblnVisible Then ' закрываем документ mdocSpell.Close savechanges:=False
Else
' закрываем Word
radocSpell.Application.Quit savechanges:=False End If
End Sub,' ■■ ■
Прим, перец.: Чтобы программа работала и с русской версией Microsoft Word 97, вызови! с из меню Сервис команду Язык, затем — Выбрать язык и, указав в списке строку Engl (United States), щелкните кнопку По умолчанию. Изменения сохраните в шаблоне 1 )бычный.с1о1.
Здесь мы активизируем то Word, то свою программу. Метод CheckSpelling — модальный, поэтому код, написанный на Visual Basic, не возооновлш ; свою работу до тех пор, пока пользователь не закроет окно проверки правописания. Перед вызовом любого модального метода окно Word должно быть видимо, иначе пользователь не сможет переключиться в Word и решит, что Ваше приложение «зависло».
Чтобы посмотреть, как работает этот пример, откройте новый проект стандартной НХЕ-программы. Установите на Microsoft Word 8.0 Object Library. Присвойте форме имя frmSpel! i-добавьте на нее кнопку emdSpell'a текстовое поле txtSpell. Введите код, показанный выше, в модуль формы и запустите программу. Рис. 24-1 иллюстрирует программу до щелчка кнопки Check Spelling (виден текст, набранный с ошибкой), рис. 24-2 — диалоговое окно проверки правописания Word в действии, а рис. 24-3 — текст, исправленный средствами Word.
Рис. 24-1 Текст до проверки правописания средствами Word
Рис. 24-2 Коррекция текста средствами проверки правописания в Word
Рис, 24-3 Текст после проверки правописания средствами Word
Средства проверки правописания Word доступны и через глобальный метод GetSpellingSuggestions. Вызывайте его для проверки отдельных слов. Вот фрагмент кода, предлагающий список вариантов, когда пользователь, выделив какое-то слово, нажимает клавишу F7:
Const KEY F7 = 118
' проверяем правописание отдельного слова
Private Sub txtSpall_KeyDown(KeyCoQe As Integer, Shift As Integer)
Dim Corrections
If KeyCode = KEYF7 Then
If txtSpell. SelLength = 0 Then ' выделяем слово SendKeys "+A{Hight}"
■End If
.' проверяем правописание в выделенном фрагменте
Set Corrections = GetSpelllngSuggestions(txtSpell.SelText) ' если слово написано неправильно, предлагаем варианты If Corrections, Count Then
f rmCorrections. Display Corrections End If
End If
End Sub
' процедура, вызываемая формой frmCorrections для замены текста
Friend Sub Replace(Wora As String)
txtSpell.SelText = Word ,
End Sub
Программа отображает набор вариантов для ошибочного слова, используя вторую форму - jrmLwrnxtionsOHa содержит список IstCorrections.а также 2 кнопки — cmdReflacen cmdCance' позволяющие исправить слово или отменить операцию. В модуль формы frmCorrections следует включить такой код:
' процедура, иызываемая формой IrmSpell для отображения
' вариантов, предлагаемых средствами Word
Friend Sub Display(Corrections) ■-.*•
Dim Word"
For Each WordIn Corrections.
IstCorrBCtions.Additem Word- • '
Next Word:
■ выделяем первый вариант'
IstCorrections.aeiectea(O) = True
отображаем форму
Show vbHodal.
End Sub
1 замена слова выбранным вариантом
Private Sub cmdReplace_Click()
frmSpell.Replace IstCorrections.List(lstCorrectlons.Listindex)
Unload Me
End Sub
1 отмена коррекции
Private Sub cmdCancel_Click() Unload Me
End Sub
На рис. 24-4 показано, как действует метод ' ietSpellingSuggestions после выделения ошибочно написанного слова • ehements и нажатия клавиши F7. Вы наверняка заметите, что GetSpellingSuggestions работает быстрее CheckSpelling и что выполнение обоих методов ускоряется, если Word уже запущен. Скорость выполнения может оказаться важным фактором при разработка программы, интенсивно использующей ActiveX-объекты, — большинство таких < >бъектов требуют запуска всего приложения, но некоторые могут работать почти независимо, загружая лишь несколько его модулей.
Рис. 24-4 Метод GetSpellingSuggestionsmeKcmoeoeo процессора Microsoft Word в действии
Проверка правописания средствами Microsoft Excel
Обращение к средствам проверки правописания Excel через Автоматизацию аналогично доступу к подобным средствам Word. В этом
примере программа делает то же самое (рис. 24-5 и 24-6), но код рассчитан на Microsoft Excel 97..
Рис. 24-5 Текст до проверки правописания средствами Excel
ефосдаэег: J
С ей V^jje;:. c.cpi.mft, fttfttrc nftfiFrti&jetf to th* «upnobF urtof- сцуЛеиь r*>tt .
“ А1й^яШ^|!.;(
=Гч?; v",;J I I
Рис. 24-6 Вызванное окно проверки правописания в Excel
Программа создает объект Sheet (электронной таблицы Excel), загружает текстовую строку в ячейку таблицы, активизирует средства проверки правописания Excel, а затем копирует содержимое ячейки обратно в с т.п. текстовое поле.
Option Explicit■...
Dim objXL As Object‘
Private Sub cmdSpell_Click()
' создаем объект Sheet (Excel) через Автоматизацию
Set objXL = CreateObjact("Excel. Sheet")"
' получаем ссылку (из-за явной ошибки в Excel)
Set objXL = objXL. Application,ActiveWorkbook.ActiveSheet
' копируем содержимое текстового поля в ячейку
objXL.Range("A1”).Value = txtSpell.Text
' проверяем правописание , SJ
obi У* xhs^i/^ofin inn
' копируем содержимое ячейки обратно в текстовое поле
txtSpell.Text = oujAl. nanyei ai ) value
' удаляем объект из памяти
Set objXL = Nothing"
■ активизируем свою програмиу ..
AppActivate Caption.
End Sub
Чтобы запустить эту программу, создайте новую форму с кнопкой cmdSpell'w текстовым полем txtSpell, потом введите код, показанный выше, в модуль формы.
Обратите внимание на функцию AppActivate. Если бы она не использовалась и программа работала с уже загруженным Excel, последний остался бы активным и после проверки орфографии. Я же вызываю функцию \ppActivati, встроенную в Visual Basic, и тем самым, завершив работу со средствами проверки правописания Excel, вновь активизирую свою программу.
Обратите внимание и на строку кода:
Set objXL = objXL.Application.ActlveWorkbook.ActiveSheet
Вроде бы она ни к чему, но из-за явного «жучка» в Microsoft Excel 97 ее приходится включать в программу.
Раннее и позднее связывание
Работая с ActiveX-объектами, можно использовать как раннее, так и позднее связывание. При раннем связывании (early binding) Вы указываете конкретные типы ActiveX-объектов (предварительно установив ссылку на соответствующую библиотеку объектов), что позволяет Visual Basic заранее разрешить все ссылки на них. В исполняемый файл Вашего приложения записываются идентификаторы классов ActiveX-объектов. В итоге доступ к объектам ускоряется. Раннее связывание демонстрируется на примере обращения к средствам проверки правописания Word — ссылка на его библиотеку объектов устанавливается через диалоговое окно References среды программирования Visual Basic.
При позднем связывании (late binding) Вы объявляете объектные переменные универсального типа Object, а ссылки на конкретные ActiveX-объекты создаете уже в период выполнения. В этом случае идентификаторы классов ActiveX-объектов не записываются в исполняемый файл приложения, и доступ к объектам занимает несколько больше времени. Позднее связывание показано на примере обращения к средствам проверки правописания Excel — ссылка на его библиотеку объектов устанавливается вызовом метода CreateObject.
Присутствуют ли идентификаторы классов в исполняемом файле — вопрос очень важный, так как в новой версии программного продукта они почти наверняка изменятся. Если будет выпущена новая версия Word или Excel, приложение, использующее их объекты по механизму раннего связывания, придется модифицировать и распространять повторно. А приложения, в которых применяется
механизм позднего связывания, ничего такого не потребуют, но все равно их надо будет протестировать — мало ли как скажутся изменения, внесенные в Word или Excel.
Лопг^лй Джон, как...
Применить ActiveX для подсчета слов?
Рассмотрим .: ■ один пример использования Автоматизации с Word — на этот раз для подсчета слов в строке текста. Доступ к Word и здесь осуществляется через объект Document. Для обращения к диалоговому окну Document Properties (Свойства документа) Word и хранения значений, связанных с ним, создается вторая объектная переменная — dig.
Private Sub cmdWords_Click()
Dim dig As Word. Dialog
' копируем текст в документ Word
mdocSpell.Ranne = txtspell.Text
' создаем объект для хранения значений,
' связанных с диалоговым окном
Set dig = mdocSpell.Application,Dialogs _ (wdDialogDocumentStatistics).
' подсчитываем число слов и знаков
dig.Execute
' показываем результаты через элемент управления Label
Caption = Str(dlg,Words) & " words, " _
4 3tr(dlg.Characters) & " characters"
End Sub
В этом иример< я получил от объекта, который хранит значения, связанные с диалоговым окном, количество слов и знаков, но Вы можете обращаться и к другим его свойствам. Их описание см. в разделах справочной системы Visual Basic текстового процессора Word, посвященных набору Dialogs.
Результат выполнения программы показан на рис. 24-7.
Рис. 24-7 УЛ о, сон слов и знаков средствами Word
Чтобы посмотреть, как работает эта программа, возьмите форму, созданную в разделе «Проверка правописания средствами Micro-
soft Word», и добавит на нее кнопку cmdWord> Запустив программу, наберите десяток слов и щелкните кнопку Count Words (подсчи- . тать слова).
| ЛПГ*ТЪ<5Г0Й ПЖПМ. КГ.ЙК...
Получить доступ к математическим функциям
! Microsoft Excel? 1
Microsoft Excel содержит ряд математических функций, недоступных в Visual Basic. Поэтому процедура, приведенная ниже, создает посредством .Автоматизации объект Sheet и уже через него обращается к одной из функций Excel, чтобы выполнить нужные вычисления на основе переданных объекту значений. Кстати, этот пример иллюстрирует и то, как объекты Excel позволяют дополнять программы, написанные на Visual Basic, весьма мощными вычислительными возможностями.
В данном случае я использую одну пару ячеек объекта Sheet для ввода, а другую — для считывания данных, полученных после математических операций над первой парой ячеек. Функция ATAN2 электронной таблицы Excel возвращает угол между положительной осью Хи линией от начала координат до точки, определяемой значениями первых двух ячеек. Результат вычисляется в радианах, но его нетрудно преобразовать в градусы. В отличие от функции \гп в Visual Basic, которая определяет тот же угол по соотношению сторон треугольника и ] юэтому возвращает значение в пределах от - я/2 до ^/2, ATAN2 использует координаты точки и дает результат в диапазоне от -л до и Благодаря этому становится возможным корректное вычисление квадранта окружности, в котором находится данная точка.
Следующий код создает объект Sheet, записывает координаты точки в две ячейки этого объекта и заставляет Excel вычислить арктангенс с помощью функции ATAN2, а затем вернуть полученное значение программе, написанной на Visual Basic;
Option Explicit
Private Sub csidAngle_Click()■
Dim objXI As Object
' создаем объект Sheet (Excel) посредством Автоматизации
Set objXI = CraateObject("Excel.Sheet")
' эта строка необходима из-за ошибки Excel,
' о которой й уже говорил
Set objXI = objXI.Application.Activeworkbook.ActiveSheet
' записываем в ячейки известные значения
objXI. Hange("A1"). Value = txtX.Text
objXl.Range<"A2"). Value = txtY.Text ' вычисляем результат
obJXl.Range("АЗ").Formula = "=ATAN2(A1,А2)"
objXl.Range("A4").Formula = "=A3*180/PI()”
' и отображаем его через элемент управления Label
IblRadians.Caption = objXl.Range("A3").Value
IblDegrees.Caption = objXl.Range("A4").Value
' удаляем объект ИЗ памяти
Set objXl = Nothing
End Sub
Для запуска программы надо создать новую форму и добавить на нее 2 текстовых поля <LxtX Htxt¥),2 метки (IblRadians и IblDegrees) и кнопку (cmctAngleY Как видно на рис. 24-8, пара ХУ-координат, введенных в текстовые поля, обрабатывается после щелчка кнопки Angle (угол); программа средствами Excel вычисляет угол заданной точки по отношению к оси X. Искомый угол определяется как в радианах, так и в градусах. Результат вычислений копируется из двух ячеек Excel в соответствующие метки на форме. Помимо этих меток, я вставил на форму и другие — они просто поясняют назначение рабочих элементов управления.
Г. I
Н4ЙГСtkfSWfr
даод^гмии 7В 5639651 лет
Рис. 24-8 Вызов математической функции Excel
из программы на Visual Basic
Прием, который я здесь продемонстрировал, можно приспособить под другие вычислительные задачи — любого уровня сложности. Некоторые виды вычислений объект Excel выполняет гораздо эффективнее, чем это сделали бы Вы, написав все необходимые уравнения на Visual Basic.
ГЛАВАГЛАВА2 52 5
ГЛАВА
ГЛАВА
2 5
2 5
Экранные заставки
Создать экранную заставку (screen saver) на Visual Basic довольно легко. Сначала я расскажу, как написать простейшую заставку, а потом мы ее усовершенствуем в соответствии со стандартами Windows 95 — чтобы она (заставка) взаимодействовала с диалоговым окном Display Properties (Свойства: Экран), блокировала запускдру- гих экземпляров, гасила курсор мыши, завершалась при любых действиях пользователя, обеспечивала предварительный просмотр и изменение своих параметров. Кроме того, я покажу, как ускорить обработку графики и использовать в заставке текущее изображение сдисплея. . .
И И е й
Создать экранную заставку?
Изначально экранные заставки предназначались для защиты люминофорного слоя дисплеев от «выжигания» длительно сохраняющимся статичным изображением. Теперь-то они используются больше для развлечения, но по-прежнему строятся на унаследованном из прошлых времен принципе динамичного переключения цвета всех пикселов. По этой причине заставка должна выводить некое постоянно движущееся и меняющееся графическое изображение.
Любое приложение, написанное на Visual Basic, может работать как экранная заставка. Но, конечно, не все программы подходят для этого. Базовая экранная заставка довольно проста: форма, занимающая целый экран, код, создающий динамичное изображение, плюс код, завершающий программу при определенных действиях. Вот и все, что нужно для создания простейшей экранной заставки, хотя Вам, наверное, захочется использовать усовершенствования, рассматриваемые в следующих разделах.
Чтобы Ваша программа работала в среде Windows как заставка, скомпилируйте ее в таковом качестве. Для этого выберите из меню File команду Make EXE и, когда появится диалоговое окно Маке Project, щелкните кнопку Options. На вкладке Маке диалогового окна Project Properties введите в поле Application Title строку SCftVXlVE.- и имя своей экранной заставки. (Например, если она называется
MySaver, следует набрать _v;;r\v.s14№.oI^.^кнопку ОК,
чтобы закрыть диалоговое окно Project Properties и вернуться в диалоговое окно Make Project. Теперь замените поле File Name расширение имени исполняемого файла EXE на SCR и, щелкнув кнопку ОК, Вы получите исполняемую экранную заставку. Скопируйте SCR-файл Б свой каталог Windows’ , чтобы его мог загрузить апплет Display (Экран) из Control Panel (Панель управления).
ПРИМЕЧАНИЕ Если имя файла экранной заставки начинается с двух заглавных букв S, Windows 95 удалит их, и, на- . пример, SSaverl.scr появится в диалоговом окне Display Properties в виде «avert». Так обрабатывали экранные заставки прежние версии Windows, и этот механизм сохранен для совместимости.
На следующем листинге показана программа — простая экранная заставка. Она не поддерживает многие функции, свойственные настоящим заставкам. Чтобы Ваша программа вела себя подобающим образом, она должна завершаться при щелчке или смещении мыши, а также при нажатии любой клавиши. Кроме того, она обязана предотвращать запуск других своих экземпляров, а в Windows 95 — поддерживать настройку параметров, предварительный просмотр и установку пароля на вкладке Screen Saver (Заставка) диалогового окна Display Properties.
В данном случае экранная заставка не поддерживает управление через диалоговое окно Display Properties, кроме параметра Wait (Интервал), обрабатываемого на системном уровне, и не реагирует на перемещение мыши. (Я еще расскажу, как ввести поддержку всех стандартных возможностей.)
Чтобы создать эту заставку, откройте новый проект стандартной ЕХЕ-программы и добавьте стандартный модуль. Присвойте форме v№\jrmMySat:erL а стандартному модулю — modMySaper/и сохраните их соответственно в файлах MySaverl.frm и MySaver l.bas. Вставьте на форму таймер цкг^АчйУой^й'.Запишите в его свойство Interval значение /. а свойство Enabled установите как False. Измените значение свойства BorderStyle формы на О - Хот. а ее свойство WindowState — на 2 - Maximized. Затем введите в модуль формы такой код:
’ MySaverl.frm■
Option Explicit
1 объявляем переменные уровня модуля ■■:
Dim mblnQuit As Boolean.
Прим, nepeir.: В Windows 95/93 этот файл надо скопировать в каталог
WindowsXSysiem..
Private Sub Form_Paint() . •• '"
Ulin ingx As Long
' каждый раз показываем разные изображения
. Randomize
■ инициализируем параметры вывода графики
Scale (0, 0)-(1, 1)
BackColor = vbBlack
основную обработку выполняем в цикле
обновляем изображение
DoGraphics,- ■
’ передаем управление системе
DoEvents) .
Loop Until mblnQuit = True"
’ в этом контексте выйти нельзя: пусть это сделает таймер
tmrExitNotify.Enabled = True
End Sub
Private Sub Form_Click()
mblnQuit = True
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) mblnQuit = True
End Sub'
Private Sub tmrExitNotify_Timer()■
End
End Sub
’ реальное изображение формируется в этой процедуре
Sub DoGraphicsO
- меняем цвет и ширину линий случайным образом If Rnd < 0.03 Then
■ ForeColor = QBColor(Int(Rnd * 16)).
DrawWidth = Int(Rnd *9+1)-
EndJf " рисуем окружность Circle (Rnd, Rnd), Rnd End Sub
Ав модуль MySaverl.bas включите код, показанный ниже (стартовым объектом проекта должна быть Sub Main).
’ MySaverl.bas Option Explicit
- стартовая точка,,.
Public Sub Main().
Dim strCmd As String.
Dim strTwo As String
' анализируем командную строку
strCmd = UCase(Trim(Command))
strTwo = Left(strCmd, 2)
Select Case strTwo
' показываем заставку а нормальном, полноэкранном режиме1
Case "/S"..1
Load frmHySaverl-_,
f rmMySaverl. Show ExitSub
Case Else■
Exit Sub- _,-,"
End Select."-.■
End Sub
Скомпилируйте проект, создав SCR-файл MySaverl.scr, как уже было описано ранее. Поместите полученный файл в каталог Windows. Щелкните рабочий стол правой кнопкой мыши, выберите из контекстного меню команду Properties и в появившемся диалоговом окне Display Properties перейдите на вкладку Screen Saver. В одноименном списке выберите строку MySaverl и, щелкнув кнопку Preview (Просмотр), протестируйте заставку.
После активизации заставка рисует на черном фоне окружности, выбирая их толщину, цвет и диаметр случайным образом (рис. 25-1).
Рис. 25-1 Экранная заставка MySaverl в действии
См. также...
Следующие разделы — об усовершенствовании данной экранной заставки.
Приложение MySaver в главе 29 «Графика», где дан пример более совершенной экранной заставки.
Дорогой Джон, как...
Избежать одновременного запуска двух экземпляров заставки?
Здесь возможны два подхода. Во-первых, Visual Basic предусматривает объект Арр, свойство Previnstance которого равно True, если выполняется предыдущий экземпляр текущей программы. Проверяя это свойство в процедуре Form_Load, Вы сможете быстро завершить новый экземпляр программы и тем самым исключить одновременное выполнение двух и более экземпляров заставки. Вот как это обычно делается в таких программах:
' не допускаем одновременного запуска двух экземпляров
If Арр. Previnstance = True Then
Unload Me .
Exit Sub
End If
Во-вторых, можно вызвать специальную API-функцию, которая информирует операционную систему, что экранная заставка в данный момент уже активна. Тогда система блокирует запуск нового экземпляра уже выполняемой экранной заставки. Именно этот подход я и использую в остальных примерах заставок, приведенных как в данной главе, так и в главе 29 «Графика».
Чтобы увидеть, как работает эта функция, откройте новый проект и сделайте то же самое, что и в проекте MySavei 1, — только переименуйте форму в jrmMySauer2 д стандартный модуль — в morT Му8аиег2и сохраните их в соответствующих файлах. Потом отредактируйте код в модуле modMySarer2 как показано ниже, и скомпилируйте проект, чтобы получить файл MySaver2.scr.
MySaver2.bas
Option Explicit
' константы для некоторых API-функций
Private Const SPI.SETSCREENSAVEACTIVE = 17
Private Declare Function SystemParametersInfo _
Lib "user32" Alias 'SystemParametersInfoA" ( _
ByVal uAction As Long, _
ByVal uParam As Long, _
ByRef IpvParam As Any,
ByVai fuWinlnl As Long _ ) As Long
' стартовая точка...
Public Sub Main()''
Dim IngRet As Long.
Dim IngParam As Long.
Dim strCmd As String
Dim strTwo As String ' анализируем командную строку strCmd = UCase(Trim(Command)) strTwo = Left(strCmd, 2} ' сообщаем системе, что экранная заставка сейчас активна IngRet = SystemParametersInfo( _
SPI_SETSCREENSAVEACTIVE, 1, IngParam, 0)'
Select Case strTwo’1
' показываем заставку в нормальном, полноэкранном режиме*
Case "/S"v ^
Load frmMySaver2■•
frmMySaver2.Show i-.-и • -• ..
Exit Sub■ ■.
Case Else
Exit Sub■ ‘1
End Select--
End Sub
Выберите заставку MySaver2 и сравните ее поведение с предыдущей заставкой, Myiaver 1 Установив параметр Wait равным 1 минуте, Вы заметите, что у MySaverl каждую минуту запускается новый экземпляр, a MySaver2, напротив, продолжает работать плавно и без помех.
Погасить курсор мыши при запуске заставки?
Для этого можно воспользоваться API-функцией ShowCursor. которая позволяет скрывать или показывать курсор мыши. Чтобы погасить курсор мыши, передайте ей False, а чтобы восстановить — True.
ПРИМЕЧАНИЕ Программисты на Visual Basic часто называют курсор (cursor) мыши указателем (pointer). Однако в Visual C++ его всегда называют курсором. И поскольку API-интерфейс ориентирован на языки C/C++, этой функции присвоено имя ShowCursor — а не ShowPoinicr, что было бы логичнее для нас, несчастных VB-программистов.
Вот как выглядит объявление API-функции ShowCursor:■
Private Declare Function ShowCursor _■
Lib ’ user32” (..
ByVai bShow As Long _
) As Long
И вот как она используется для скрытия и показа курсора мыши: ' гасим курсор мыши х = ShowCursor(False)..
' восстанавливаем курсор мыши>,
х = ShowCursor(True)' '
Погасив курсор мыши, обязательно восстановите его при завершении своей программы. Чтобы дополнить экранную заставку этими средствами, создайте проект MySavcr3, скопировав и соответственно переименовав файлы из проекта MySaver2. Замените в коде MySaver3.bas все -пкождшшя/гамТЛ^йГ'е^на/Ьи^ отредактируйте код в модуле формы MySavcr3.frm:
' MySaver3.frmа •.=.
Option Explicit
' API-функция для гашения и восстановления курсора мыши
Private Declare Function ShowCursor _
Lib "user32" ( _.
ByVai bShow As Long _" д -
) As Long
' объявляем переменные уровня модуля Dim mblnQuit As Boolean
Private Sub Form_Paint()
Dim IngRet As Long
каждый раз показываем разные изображения
Randomize
' инициализируем параметры вывода графики
Scale (0, 0)-(1. 1}
BackColor = vbBlack ' гасим курсор мыши IngRet = ShowCursor( False) ' основную обработку выполняем в цикле Doг- ’
1 обновляем изображение
DoGraphic
передаем управление системе.
DoEvents
Loop Until mblnQuit = True
' восстанавливаем курсор ныши
IngRet = ShowCursor(True)
' в этом контексте выйти нельзя; пусть это сделает таймер tmrExitNotify.Enabled = True
End Sub
Private Sub Form_Click() , mblnQult = True" '"
End Sub'
Private Sub Form_KeyOown(KeyCode As Integer, Shift As Integer) mblnQult = True
End Sub
Private Sub tmrExitNotlfy_Timer()
End
End Sub
реальное изображение формируется в этой процедуре
Sub DoGraphicsO
' меняем цвет и ширину линий случайным образом
If Rnd < О.ОЗ Then
ForeColor = OBColor(Int(Rnd * 16))
DrawWidth = lnt(Rnd *9+1)■
End If._
рисуем окружность
Circle(Rnd, Rnd), Rnd
End Sub..
На этот раз при предварительном просмотре MySaver? Вы уже не увидите курсора, хотя по-прежнему сможете завершать экранную заставку щелчком кнопки мыши. -
Дорогой Джон, как...
Обнаружить перемещение 1\/ыши, чтобы завершить экранную заставку?
Вроде бы самое очевидное средство обнаружить перемещение мыши — событие MouseMove, но тут есть одна проблема. Событие MouseMove генерируется и в момент запуска приложения, даже если мышь никто не трогал. Чтобы обойти эту проблему, надо перехватывать первое событие MouseMove и завершать экранную заставку, только когда мышь действительно перемещается. Рассмотрим, как реализуется данный прием.
Private Sub ■ ?rm_MouseMove(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
' отслеживаем последние известные координаты курсора мыши
Static sngX As Single
Static sngY As Single
' при первом "перемещении" просто запоминаем позицию
If sngX 0 And sngY = 0 Then1
sngX = X■.
sngY = Y'
End If
' завершаем программу, если позиция мыши
действительно изменилась
If X О sngX Or Y о sngY Then mblnQuit = True
End If; -
End Sub
В статических переменных sngX и ллдУхранятся начальные координаты курсора мыши. Программа завершается. только если возникает событие MouseMov:. и текущая позиция курсора больше не совпадает с начальной. Вообще говоря, предполагается, что его координаты всегда отличны от нулевых значений, но, даже если в момент запуска экранной заставки он окажется и позиции (О, О), программа все равно корректно завершится при смещении мыши.
Создайте проект MySaver4, скопировав файлы проекта MySaver3.
Замените в коде MySaver4.bas все Bxo^eHRs frmMySaver3HajrmMy- Saver4 и добавьте Б MySaver4.frm обработчик события MouseMove, показанный выше. Скомпилируйте проект, чтобы получить файл MySaver4.scr, Запустив эту версию экранной заставки, Вы заметите, что она завершается, стоит лишь шевельнуть мышку.
Д(>погОМ.
Обнаружить нажатие клавиши?
С этой целью можно использовать событие KcyPress формы, но оно не генерируется при нажатии клавиш-модификаторов (Shift, Ctrl и Alt). Поэтому лучше отслеживать состояние клавиатуры через событие KeyDown формы. Тогда экранную заставку удастся завершить при нажатии действительно любой клавиши, Б том числе одного из модификаторов:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) mblnQuit = True
End Sub
Дорогой Джон, как.»
Использовать в заставке изображение с дисплея?
Многие, очень модные сейчас, заставки воздействуют на текущее экранное изображение, не влияя на выполняемые приложения. Вероятно. Вы видели нечто подобное — изображение на экране вдруг начинает таять или, закручиваясь в водовороте, исчезает в какой- нибудь дыре. Обратившись к нескольким API-функциям, Вы легко скопируете текущее изображение с дисплея на полноэкранную форму, а уж там сможете делать с ним все, что только вздумается. Естественно, исходное изображение остается в целости и сохранности.
Приведенный ниже листинг как раз и демонстрирует этот эффект. Эта экранная заставка аналогична серии MySaverA; но отличается тем, что захватывает текущее дисплейное изображение с помощью API-функций. Обратите внимание на объявления API-функций BitBlt, Get Desktop Window, GetDC и ReleaseDC. нужных для копирования изображения с экрана на форму. Они вызываются в процедуре FormLoad. Новую экранную заставку я назвал MySaverS.
Чтобы скомпилировать это приложение, действуйте как обычно, и скопируйте полученный SCR-файл в каталог Windows. Для корректной работы программы установите следующие свойства формы: AutoRedraw как True, BorderStyle как О - None, К су Preview'как True и WindowState как 2 - Maximized. Добавьте на форму таймер tmrExitNoii^v установите его свойство Enabled как False, а в свойство Interval запишите значение 1.
Вот как выглядит код в Mybaver5.bas:.
' HySaverR.bas
Option Explicit,-
1 константы для некоторых API-функций
Private Const SPI„SETSCREENSAVEACTIVE = 17_
Private Declare Function SystemParametersInfo _
Lib "user32" Alias "SystemParametersInfoA" ( _
ByVai uAction As Long, _.■
ByVai uParam As Long,
ByRei IpvParam As Any, . "L "L"
ByVai fuWinlni As Long _‘
} As Long'-
' стартовая точка,,,*'1 "' '1
Public Sub Main().
Dim IngRet As Long
Dim IngParam As Long
Dim strCmd As String1
Dim BtrTwo As String
' анализируем командную строку'
strCmd = UCase(Trlm(Command)) *1
strTWo = Left(strCmd, 2)
1 сообщаем системе, что экранная заставка сейчас активна
IngRet = SystemParametorsinroL _
SPI_SETSCREENSAVEACTIVE, 1. IngParam, 0)
Select Case strTWo
1 показываем заставку в нормалвном, полноэкранномрежиме
Case "/S”■‘
Load frmNySaverS
fnnMySavar5,Show- '
Exit Sub'
Case Else■
Exit Sub_■.
End Select''
End Sub
А этот код содержится в MySaverS.frm:
' MySaverS.frm
Option Explicit, , ,. r/,:.: -i-j,
' API-функция для гашения и восстановления курсора мыши Private Declare Function ShowCursor _
Lib "user32" ( __ l
ByVal bShow As Long _. " ''
) As Long
' API-функция для получения копии всего экрана 'Private Declare Function BitBlt _
Lib "gdl32" ( _
'ByVal hDestDC As Long, _
'ByVal IngX As Long, ByVai IngY As Long, _
ByVai nWldth As Long, _
ByVal nHeight As Long, _
ByVai hSrcDC As Long,
ByVal IngXSro As Long, ByVal IngYSrc As Long, _
ByVal dwRop As Long _ ) As Long
' API-функция для получения описателя дисплейного изображения Private Declare Function GetDesktopWindow _
Lib "user32" () As Long
' API-функция для получения контекста устройства по описателю Private Declare Function GetDC _
Lib "user32" ( _
ByVal hwnd As Long _.
) As Long
' API-функция, освобождающая контекст устройства
Private Declare Function ReleaseDC _
Lio user,;.. ( _
ByVal hwnd As Long, _
ByVal hdc As Long _ ) As Long
4
' объявляем переменные уровня модуля, ,
Dim mblnQuit As Boolean_
Private Sub Form_Load()
Dim IngX As Long
Dim IngY As Long-
Dim IngXSrc As Long
Dim IngYSrc As Long.
Dim dwRop As Long
Dim hwndSrc As Long
Dim hSrcDC As Long■-
Dim IngRes As Long■ •
Dim IngMI As Long■'
Dim lngM2 As Long ■
Dim IngNI As Long
Dim IngNI As Long
Dim IngPixelColor As Long
Dim IngPixelCount As Long
Dim IngRet As Long
Dim IntPowerOfTwo As Integer
' каждьм раз показываем разные изображения
Randomize
' копируем весь экран вокно рисунка
ScaleHode = vbPixels
Nove 0, 0, Screen.Width + 1, Screen.Height + 1
dwRop = 4HCC0020
hwndSrc = GetDesktopWindow()
hSrcDC = GetDC(hwndSrc)
IngRes - BitBlt(hdc, 0, 0, ScaleWidth, _
ScaleHeight, hSrcDC, 0, 0, dwRop).
IngRes = eleaseDC(hwndSrc, hSrcDC)■
' показываем форму в полноэкранном режиме
Show
1 в первнй раз берем максимальное значение intPowerOfTwo = 128
гасим курсор мнши.
IngRet = ShowO-nrsorfFaieey
' основную обработку внполняем в цикле Do
' настраиваем систему координат
Scale (0, uj-(intPoweruriwo, IntPowerOfTwo)
внбираем чистнй цвет случайннм образом
IngPixelColor = (IngPixelColor *9 + 7) Mod 16
IngPixelCount = О
' алгоритм, позволяющий попасть в каждую точку экрана
IngMI =
Int(Rnd *
(IntPowerOfTwo
\ 4))
* 4
+ 1
1пдМ2 =
Int(Rnd *
(intPowerOfTwo
\ 4))
* 4
+ 1
IngNI =
IntfRnd *
(intPowerOfTwo
\ 2))
* 2
+ 1
lngN2 =
IntCRnd *
(intPowerOfTwo
\ ?))
* 2
+ 1
Do
-IngY - (IngY* lngM2 + lngN2) Mod IntPowerOfTwo
Else
1 даем системе заняться своими делами
DoEvents'
■ " End И"
' заполняем прямоугольный блок одним цветом
Line (IngX, 1иу|^-\хиул + 1, хну i + .;, QBColor(lngPixeLColor), BF
IngPixelCount = IngPixelCount + 1
выходим из этого цикла только при завершении программы If mblnQult = True Then Exit Do
Loop Until IngPixelCount = IntPowerOfTwo * IntPowerOfTwo IntPowerOfTwo 2 (Int(Rnd- 5) +2) 1 передаем управление системе
DoEvents
Loop Until mblnQult = True■
1 восстанавливаем курсор мыши
IngRet = ShowCursorCi" ruej
' в этом контексте выйти нельзя; пусть это сделает таймер
tnrExitNotify.Enabled = True
End Sub
Private Sub Form MouseMove(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
Dim sngXnow As Single
Dim sngYnow As Single
sngXnow = Round(X)
sngYnow = Round(Y)
1 отслеживаем последние известные координаты курсора мыши
Static sngX As Single
Static sngY As Single
1 при пеовом "перемещении" просто запоминаем позицию
If sngX = 0 And sngY = 0 Then
sngX = sngXnow
sngY = sngYnow■■.
End If. .
' завершаем программу, если позиция мыши
1 действительно изменилась
If sngXnow о sngX Or sngYnow <> sngY Then mblnQult = True
End If
End Sub
Private Sub Form_Click() mblnQult = True
End Sub
Private Sub Form_KeyDown(KeyCodB As Integer, Shift As Integer)
mblnQuit = True End Sub
Private Sub tmrExitNctify_Timer()
End
End Sub.
Цикл формирования графики в процедуре Form_Load выводит прямоугольные блоки один. случайно питанного цвета и тем самым вызывает постепенное «растворение» экранного изображения. Этот эффект иллюстрирует рис. л где исходное изображение размывается серым цветом.
Рис. 25-2 Экранная заставка, "растворяющая» исходное изображение на дисплее
См. также...
• Приложение MySaver в главе 29«Графика», где используются дополнительные эффекты.
Дорогой Джон, как,,,
Дополнить заставку поддержкой паролей и настройки параметров?
Запуская экранную заставку. Windows автоматически передает ей в командной строке один из ключей в зависимости от того, в каком режиме стартует эта программа:
/аВ диалоговом окне Display Properties «нажата* кнопка
Change (Сменить), заставляющая изменить пароль.
/р пппп При выборе вкладки Screen Saver ь диалоговом окне Display Properties показывается пример того, как выглядит экранная заставка. Число, указываемое г этом параметре, — hWnd миниатюрной картинки рабочего стола, где показывается образец экранной заставки.
/сВ диалоговом окне Display Properties «нажата кнопка
Settings (Параметры).
/sВ диалоговом окне Display Properties «нажата» кнопка
Preview (Просмотр) или экранная заставка запущена системой по обычной схеме.
Я проверяю эти ключи в процедуре Form_Load и заставляю программу выполнять соответствующие действия. Экранные заставки, представленные в этой главе, реагируют лишь на ключ/s — обнаружив любой другой, они просто завершаются. Конечно, в таких случаях надо хотя бы выводить окна с подходящими сообщениями, например:
' ключ настройки параметров
Case "/С"
' временно восстанавливаем курсор мыши
х = ShowCursorCTrue)
' выводим сообщение
MsgBox "No setup options for this screen saver"
' гасим курсор мыши
x = ShowCursor(False)
' настройка параметров вроде как завершена
Unload Me
Exit Sub
‘ ключ смены пароля
Сазе "/А"
' временно восстанавливаем курсор мыши
х = ShowCursor(True)
' здесь следовало бы получить новый пароль
' и сохранить его, но...
HsgBox "No password for this screen saver"
' гасим курсор мыши
x = ShowCursor(False)
установка нового пароля вроде как завершена
Unload Me
Exit Sub
Я привел здесь лишь схему обработки параметров, передаваемых в командной строке. Полный рабочий пример обработки щ параметров (кроме того, который связан со сменой пароля) см. в приложении MySaverB главе 29 «Графика». Последнее обладает большей частью функциональности, присущей стандартным экранным за-
ставкам, в том числе обеспечивает предварительный просмотр на миниатюрной картинке рабочего стола в диалоговом окне Display Properties (на вкладке Screen Saver).
см. также...
• Приложение MySaver в главе 29 «Графика», где дан пример более совершенной экранной заставки.
ГЛАВА 26
Разработка проектов
Здесь я поделюсь своим опытом, который пригодится Вам при разработке проектов в целом, и в первом разделе продемонстрирую элементарный, но почему-то часто упускаемый из виду способ подготовки иллюстрации для справочных файлов. (О справочных файлах — важной части любого законченного приложения — см. главу 17 л .правочные системы, подсказки и мастера» j
Во втором и третьем разделах рассказывается, как работать с файлами ресурсов и создавать многоязыковые приложения, просто используя базу данных с текстовыми строками на нескольких языках. Эти приемы послужат Вам отправной точкой в разработке приложений для международных рынков.
д “ чои, как...
Захватить изображение выполняемой формы и сохранить его в файле?
Вот простейший способ захватить на дисплее все изображение или его часть и записать в BMP-файл (может быть, Вы уже знаете этот прием). Чтобы скопировать весь экран в буфер обмена, нажмите клавишу Print Screen, а чтобы скопировать лишь активную форму, — клавиши Alt+Print Screen. При этом Вы ничего не услышите и ничего не заметите (разве что небольшую задержку в работе текущей программы), но содержимое экрана или формы окажется в системном буфере обмена. Практически все иллюстрации в книге подготовлены именно так.
Вставка графики в редактор Paint
Чтобы обработать графическое изображение, хранящееся в буфере обмена, запустите редактор Paint и вставьте в него содержимое буфера обмена. Вы должны увидеть в окне Paint верхний левый угол захваченного изображения, как показано на рис. 2< >•• I. При необходимости изображение можно прокрутить в любом направлении и отредактировать.
Команды Save As и Сору То
Чтобы сохранить все изображение в BMP-файле, выберите из меню File (Файл) команду Save As (Сохранить как). Но если Вы хотите
записать в файл лишь фрагмент изображения, выделите его и выберите из меню Edit (Правка) команду Сору То (Копировать в). В отличие от Save As, команда Сору То сохраняет в файле не все изображение, а только его выделенную область. Lu<. рис. 26-2 иллюстрирует выделенный фрагмент исходного изображения, а рис. 26-3 — что получится после его сохранения командой Сору То в ВМР-фай- ле и загрузки из этого файла.
^■Мй^в»евРкпя#Е ^ггйгггептггпг
Рис. 26-1 Часть захваченного изображения, видимая в окне Paint
Рис. 26-2 Выделен прямоугольный фрагмент большого изображения
Рис. 26-3 Загружен фрагмент, сохраненный командой Сору То
Дорогой Джон, как...
Использовать файлы ресурсов?
Изначально поддержка файлов ресурсов была введена в Visual Basic с целью упростить разработку приложений, рассчитанных на международные рынки. Раньше, когда таких файлов не было, приходилось вручную заменять все заголовки, метки и прочие строки в проекте Visual Basic — если Вы хотели перевести пользовательский интерфейс своего приложения на другой язык. (Здесь я, конечно, не имею в виду языки программирования.) Но теперь Вы можете вынести все строки из программы в файл ресурсов, где их гораздо легче модифицировать, — лазить по всему проекту больше не придется. Впрочем, я нашел и другое применение файлам ресурсов: они позволяют напрямую включать в проект многочисленные файлы значков и растровых изображений. Загрузка изображений из файлов ресурсов выполняется значительно быстрее, чем из отдельных внешних файлов.
В каждом проекте Visual Basic допустим один — и только один ■ файл ресурсов. Но Вы можете запихнуть в этот файл столько всякой всячины (строк, значков, растровых картинок, звуковых файлов и даже видеоклипов), сколько нужно для Вашей программы. В следующих разделах я продемонстрирую, как создать небольшой файл ресурсов и покажу, как им пользоваться в приложении.
Создание файла ресурсов
Надстройка Resource Editor (рис. 26-4) позволяет создавать файлы ресурсов через графический пользовательский интерфейс. Загрузи-
те эту надстройку в Visual Basic с помощью Add-In Manager и запустите ее, щелкнув кнопку Resource Editor на панели инструментов.
Рис. 26-4 Надстройка Resource Editor позволяет создавать файлы ресурсов в среде Visual Basic
Когда Вы сохраняете файл ресурсов, Resource Editor автоматически включает его в Ваш проект как RES-файл. Работая с файлами ресурсов в Visual Basic, помните о двух вещах:
■ нельзя использовать первый номер ресурса (1V так как Visual. Basic резервирует его для значка Вашего приложения;
• RES-файл имеет двоичный формат. Чтобы подготовить файл ресурсов в текстовом редакторе, создайте сначала RC-файл, а потом откомпилируйте его с помощью RC.EXE (см. файл RESO- URCE.TXT в каталоге COMMON\TOOLS\VB\RESOURCE на компакт-диске Visual Studio).
Использование файла ресурсов в приложении
Рис. 26-4 иллюстрирует файл ресурсов, подготовленный для программы Moon.VBP Я создал этот простой файл ресурсов, чтобы продемонстрировать загрузку строк и одного типа двоичных файлов (значков Moon). Программа-пример загружает эти ресурсы и показывает (очень приблизительно) фазы Луны в любой заданный период. Откройте новый проект стандартной ЕХЕ-программы и добавьте на форму текстовое поле txtDate. метку IblString, элемент управления «изображение» imgMoonn кнопку cmdPhase (разметку формы см. на рис. 26-5). При желании дополните текстовое поле меткой с подходящим пояснением и измените заголовок формы, каку меня. Теперь добавьте в проект скомпилированный файл ресурсов, для чего выберите из меню Project команду Add File, а потом MOON32.RES.'
Введите в модуль формы код:_
Option Explicit
Private Sub cmdPhass_Ciick()
Dim Phase, ResNuin
1 вычисляем фазу
Phase = CDbl(CVDate(txtDate.Text))
Phase = Phase / 29.530589 + 0.9291176
Phase = Phase - Int(Phase) вычисляем номер ресурса
'ResNuin = 2 + Int(Phase * 8 + 0.5)
Il ResNuin = 10 Then ResNuin = 2
1 загружаем соответствующие значок и строку ImgNoon.Picture = LoadResPicture(ResNum, vbResIcon) LblString.Caption = LoadResString(RasNum)
End Sub
Private Sub Form_Load() txtDate.Text = Date? cmdPhase_Click
End Sub
Рис. 26-5 Форма Approximate Phase of the Moon на этапе разработки
Большая часть кода занята расчетом приблизительной фазы Луны по состоянию на указанную дату. Закончив вычисления, программа загружает соответствующие значок и строку i1; файла ресурсов данного проекта. Эта операция реализуется вызовом функций LoadResPicture и LoadResString. Рис. 26-5 иллюстрирует форму Approximate Phase of the Moon на этапе разработки.
Запустите программу, введите, например, 7-4-1998,и Вы узнаете, как выглядела Луна 4 июля 1998 года (рис. 26-6).
[J1-Я||!|в.*|№^Д|№
I-
Рис. 26-6 npoepaMMaApproximate Phase of the Moon в действии
В каких случаях применяются файлы ресурсов
Как я уже говорил, мотивом введения в Visual Basic файлов ресурсов было стремление Microsoft упростить локализацию приложений. Например, в обработчике события Load формы можно легко загрузить строковые ресурсы в свойства Caption кнопок, форм и меток. Меню, звукозаписи, текстовые поля — продолжите список сами — тоже можно модифицировать при выполнении программы. Большое преимущество такого подхода в том, что проблема подгонки интерфейса приложения под один или несколько иностранных языков сужается до единственной задачи — модификации всех строк во внешнем файле ресурсов (в формате ASCII). Вы передаете этот файл опытному переводчику, а тот создает новый ASCII-файл, даже не умея работать в Visual Basic.
Помимо упрощения локализации, файлы ресурсов обладают и другими преимуществами, о которых тоже стоит упомянуть. Во- первых, ускоряется выполнение программы — графические изображения загружаются из ресурсов быстрее, чем из внешних файлов. А во-вторых, легче манипулировать множеством изображений. В прежних версиях Visual Basic изображения в таких случаях приходилось загружать в адекватное количество элементов управления. Например, значки для фаз Луны пришлое । бы загрузить в отдельные элементы управления Image или PictureBox, а потом, манипулируя этими элементами, отображать на форме нужный значок. Но, начиная с Visual Basic 4, растровые картинки можно хранить в файле ресурсов и поочередно загружать их е единственный элемент управления, избегая тем самым чрезмерного усложнения кода и замедления работы программы из-за манипуляций с большим числом элементов управления.■
Использовать строковую базу данных для «интернационализации» приложения?
Строки, переведенные на несколько языков, можно хранить в специальной базе данных (она создается в Microsoft Access или Visual Basic) и загружать по мере необходимости. Вообще говоря, работать с файлами ресурсов легче, чем программировать базу данных, но, когда пользовательский интерфейс приложения должен быть на нескольких языках, трудно придумать что-то лучшее.
Структура строковой базы данных может быть очень простой. В примере, показанном на рис. 26-7, единственный набор записей содержит все варианты текста для каждого элемента управления на форме. Кнопки-переключатели на стартовой форме позволяют выбрать нужный язык.
Рис. 26-7 Программа Database Language отображает одну и туже форму в трех языковых версиях, используя строковую БД
Записи в базе данных индексируются по имени элемента управления, поэтому цикл For Each позволяет легко получить нужный вариант текста для каждого элемента управления. Затем, вызвав функцию TypcNanu . Вы определяете тип текущего элемента управления и на основе этой информации — с помощью оператора Select Case — решаете, какое свойство следует изменить. Полностью код выглядит так:
Option Explicit
Private Sub cmdShow_Click()
' "переводим" текст формы на выбранный язык
If optLanguage(O) Then
ShowLocal frmf, "English"
Elself optlanguaged) Then
ShowLocal frm1, "French"
Else
ShowLocal frmt, "Italian"
End If
End Sub
' "переводим" строки на форме
Sub ShowLocal(frmLocal As Object, Language As String)
Dim wrkDatabase As Workspace
Dim dbStrings As Database
Dim recStrings As Recordset
Dim vntString As Variant
Dim intCount As Integer
Dim cntlndex As Control ' создаем рабочее пространство (workspace), ' открываем базу данных и получаем набор записей Set wrkDatabase = ^reateWorkspacaC"1, "admin", "”, dbUseJet) Set dbStrings = wrkDatabase.OpenDatabase("strings.mdb") Set recStrings = dbStrings.OpenRecordset("Strings")
' используем имена элементов управления
1 как индексы в наборе записей recStrings.Index = "ControlName" 1 локализуем каждый элемент управления For Each cntlndex In frmLocal,Controls recStrings.Saak "=", cntlndex.Name Select Case TypeName(cntlndex)
для текстовых полей изменяем свойство Text
Case TextBox"
cntlndex.Text = recStrings.Fields(Language)
для остальных - свойство Caption
Case "Label", "OptionButton", "CheckBox", "Frame", "CommandButton" cntlndex.Caption = recStrings.Fields(Language)
для списков изменяем свойство List (запись содержит массив) Case ListBox", "ComboBox"
vntString = NakeArrayCrecStrings.Fialds(Language))
For intCount = 0 To UBound(vntString) cntlndex.Additem vntString(intCount) Next intCount
рисунки, таймеры, полосы прокрутки и т. д. игнорируем Case Else
End Select
Next cntlndex
локализуем имя формы.
recStrings.Seek , frmLocal.Name.
frittcocai. caption = recStrings. Fields(Language)
1 закрываем набор записей и саму базу данных recStrings. Close,,
dbStrings. Close;‘
1 отображаем форму
frmLocal.Show■"‘
nd Sub1
вспомогательная функция для преобразования в массив
списка элементов, отделенных точками с запятыми
Function MakeArray(strSource As String) As Variant
Dim vntTempO
'Dim intCount As Integer, intPos As Integer Do
ReDim Preserve vntTemp(intCount)
intPos = InStr(strSource, ”;")
It intPos Then
vntTemp(intCount) = Left(3trSource, intPos - 1)
strSource = Rtght(strSource, Len(strSource) - (intPos + 1)) intCount = intCount + 1
Else
vntTemp(intCount) = strSource Exit Do
End If
Loop1'
NakeArray = vntTemp■
End Function
Кнопка Edit включена на стартовую форму, чтобы облегчить модификацию базы данных, содержащей текстовые строки. Форму, показанную на рис. 26-8, я создал с помощью Data Form Wizard, используя базу данных STRINGS.MDB, а саму базу данных - в Microsoft Access.
Рис. 26-8 Форма Strings обеспечивает возможность модификации строковой базы данных STRLNGSMDB
Появление кнопки Edit на форме контролируется флагом Debug - Build, что позволяет не допустить модификации строковой базы данных пользователем конечной версии программы. От Вас требуется лишь указать в свойстве Visible кнопки Edit значение False и написать программу, изменяющую его на True, если установлен флаг DebugBuildi
#Const DebugBuild = 1■
ttlf DebugBuild Then
Private Sub Form_Load()
1 кнопку Edit показываем только в отладочных версиях программы cmdEdit.Visible = True
End Sub
Private Sub cmdEdit_Click()
1 отображаем форму для ввода данных в базу
frmStrings.Show
End Sub
#End If
Как видите, таким способом можно создавать программы со встроенными инструментальными средствами, доступными только при отладке.
КС) См. также...
• Разделы»Design ini: International Software» и «General Considerations When Writing International Code» в Visual Basic Books Online — подробнее о локализации приложений.
ГЛАВА 27
Изощренные приемы программирования
Visual Basic — гибкая среда разработки, позволяющая расширять, возможности Ваших приложений самыми разными способами. В этой главе мы рассмотрим несколько приемов -продвинутого» программирования, которые помогут повысить эффективность и надежность приложений.
Сначала я покажу, как создается динамически подключаемая библиотека (DLL); это, кстати, отличный способ ускорить выполнение критических блоков программ, написанных на Visual Basic. До Visual Basic 4 Вы могли разработать DLL только на другом языке программирования, например на С. Последний по-прежнему остается выбором профессионалов, но, используя технологию ActiveX, Вы можете теперь создавать быстродействующие откомпилированные DLL-модули ив Visual Basic. Я продемонстрирую обе методики разработки DLL — на С и на Visual Basic.
Механизм удаленной автоматизации (remote automation), хоть и не нов в Visual Basic, все же рассматривается в этой главе. Он позволяет обращаться с локальной машины к объектам, выполняемым на серверах. В Visual Basic Enterprise Edition можно создавать соответствующие приложения, способные работать на серверах под управлением Microsoft Windows NT и Microsoft Windows 95. На несложном примере я расскажу о всех этапах разработки таких приложений и о некоторых ошибках, возникающих при применении механизма удаленной автоматизации.
Затем я познакомлю Вас с другой особенностью Visual Basic — возможностью создавать надстройки (add-ins) для интегрированной среды программирования. В этом разделе Вы узнаете, как написать простую надстройку, используя сам Visual Basic.
Мы также поговорим о взаимодействии с VBScript-макросами, которые могут работать либо вне Вашего приложения — под управлением Windows Scripting Host (Сервер сценариев) (WScnpt.EXE), либо внутри — под контролем элемента Microsoft Script fMSScript OCX). Макросы позволяют автоматизировать операции, часто выполняемые в приложении, и создавать тестировочные пакеты для программ, предоставляющихАйкеХ-объекты.
В заключительном разделе главы Вы узнаете, как передавать в свойства объектов пользовательские типы (UDT-структуры).
Дорогой Джон, как...
Создать ActiveX DLL на Visual Basic?
Visual Basic позволяет создавать DLL-модули для 32-разрядных Windows-приложений; при этом применяется технология внутренних, или встраиваемых в процесс (in-process), ActiveX-компонентов. Файл, получаемый таким способом, является настоящей DLL и имеет то же расширение. Главное отличие встраиваемой в процесс ActiveX DLL от обычной — интерфейс, который связывает вызывающие программы с начинкой DLL, Для обращения к ActiveX DLL надо объявить переменную конкретного объектного типа и потом уже использовать методы и свойства объекта изданной ActiveX DLL. Но при доступе к старым DLL (унаследованным от прежних версий Windows) приходится составлять группу операторов Declare, описывающих нужные функции из этой библиотеки. Таким образом, DLL-модули ActiveX предоставляют объекты, а не просто функции или процедуры.■.
Объект Fraction
В данном примере мы создадим внутреннюю ActiveX DLL, причем я опишу все этапы этого процесса. Советую также прочесть документацию Visual Basic, где дается подробная техническая информация по динамически подключаемым библиотекам типа ActiveX. Однако, следуя даже только моим инструкциям, Вы вполне сможете реализовать ActiveX DLL и избежать лишних сложностей. Мы подготовим встраиваемый в процесс компонент ActiveX DLL с именем Math, предоставляющий класс Fraction. Этот класс позволит вызывающей программе выполнять простые математические операции над дробями. У объекта Fraction будет 2 открытых свойства, Num и Den, и 4 метода для сложения, вычитания, умножения и деления дробей. Кроме того, один закрытый метод класса Fraction поможет приводить дроби к простейшему виду.
Откройте новый проект, дважды щелкнув значок ActiveX DLL в окне New Project. Visual Basic добавит в проект модуль класса и установит тип проекта как ActiveX DLL. В окне Properties измените имя модуля класса на Fraction. (Им будут пользоваться другие программы, создавая экземпляры объекта Fraction.) Далее выберите из меню File команду Save Fraction As и сохраните модуль класса в файле с именем MATHFRAC.CLS.
В этом проекте будет лишь модуль класса Fraction, но вдруг понадобится добавить в проект определения других объектов, тоже имеющих отношение к математике? Именно поэтому свойство
Name проекта лучше установить как Math, а не Fraction. Для этого выберите из меню Project команду Project Properties и откройте одноименное диалоговое окно. В поле Project Name введите Math и щелкните кнопку ОК. Затем выберите из меню File команду Save Project As и сохраните проект под именем MATHPROJ.VBP.
Вы должны понимать разницу между именами файлов, из которых создается тот или иной компонент, и свойствами Name его составных частей. В данном примере имя файла проекта, MATHPROJ.VBP, значимо лишь на этапе разработки. Компонент, содержащий объект Fraction, идентифицируется в программе по значению свойства Name проекта — Math. Ведь если приложение использует несколько модулей ActiveX DLL, не исключено, что в разных компонентах определены два разных объекта с одинаковым именем, скажем, Fraction. Но Вы можете точно указать конкретный объект Fraction, дополнив ссылку на него именем соответствующего компонента. Тогда Вы создавали бы экземпляр определенного в проекте Math объекта Fraction так:
Public Frac As New Math. Fraction
Если же во всех компонентах приложения определен только один класс Fraction, эту строку можно упростить до:
Public Frac As New Fraction
Теперь мы заполним модуль класса Fraction кодом, реализующим 2 открытых свойства, 1 закрытый и 4 открытых метода:
Option Explicit
Public Num As Integer Public Den As Integer
Public Sub Add(Num2, Den2)
Num = Num * Den2 + Den * Num2
Den = Den * Den2
Reduce
End Sub
Public Sub Sbt(Num2, Den2)
Num * Nub * Den2 - Den * Nu«2"
Den = Den * Den2
Reduce
End Sub
Public Sub Mul(Num2, Den2)
Num = Num * Num2
Den = Den * Den2
Reduce
End Sub
Public Sub Div(Nym2, Den2)
Mui Den2, Num2
End Sub
Private Sub Reduced
Dim s As Integer, t As Integer, u As Integer
s = Abs(Num)
t = Abs(Den)
If t = О Then Exit Sub
Do
U * (• \ t) » t
u = s - u
s = t
t = u
Loop While U > 0
Num = Num \ s
Den = Den \ s
If Den < 0 Then■
Num = -Num
Den = -Den
End If
End Sub
Открытые свойства Num и Den позволяют вызывающей программе устанавливать и получать значения, образующие дробь. Открытые методы Add, Sbt, Mui и Div выполняют математические операции над содержимым свойств Num и Den (первая дробь) и значениями двух передаваемых параметров (вторая дробь). Кстати, методу, отвечающему за вычитание дробей, нельзя присвоить вполне логичное имя Sub, так как Visual Basic резервирует это слово для определения процедур. Закрытый метод Reduce предназначен для приведения дробей к простейшему виду.
Мы уже почти готовы к компиляции и тестированию нашего компонента ActiveX DLL, но сначала надо открыть диалоговое окно свойств проекта. В поле Startup Object пока присутствует строка (None), поскольку мы не включили в проект процедуру Sub Main. Если Вашему объекту требуется ряд инициализирующих операций, добавьте в проект процедуру Sub Main и выберите в списке, связанном с полем Startup Object, строку Sub Main. Нашему объекту Fraction никакая инициализация не нужна, так что не меняйте значение поля Startup Object.
Поле Project Description пустое, но Вы должны всегда вводить в него краткое описание проекта. Этот текст увидят л р\ пг. программисты, когда будут добавлять в свои проекты ссылку на Ваш компонент, и он поможет им понять, для чего предназначен данный компонент. В нашем случае наберите текст Demonstration of Fraction Object или что-то в этом роде и щелкните кнопку ОК.
Последний этап — компиляция проекта Math для создания собственно компонента ActiveX DLL. Выберите из меню File команду Маке MATHPROJ.DLL, в диалоговом окне Маке Рго]ес1укажите каталог, в который будет записана I И.1.. и щелкните кнопку OK. Visual Basic откомпилирует DLL и зарегистрирует ее в Вашей системе. Все — DLL готова к работе! Поскольку Visual Basic позволяет загружать и работать сразу с несколькими проектами, компонент Math можно протестировать в среде разработки прямо сейчас.
Тестирование в среде разработки
Чтобы протестировать ActiveX DLL в среде разработки, выберите из меню File команду Add Project и в одноименном диалоговом окне дважды щелкните значок Standard EXE. (Обратите внимание, что в окне Project Explort-гтеперь перечислены два проекта — ActiveX DLL и Standard EXE.) Новый проект ничем особенным не блещет — мы просто проверим, как работает объект Fraction в компоненте Math. Араз так, пусть имена формы и проекта останутся теми, что предлагаются по умолчанию (Forml и Projectl). Projectl надо сделать стартовым проектом, поскольку прямой запуск компонента Math ничего не даст. Для этого в окне Project Explorer щелкните Projectl правой кнопкой мыши и выберите из контекстного меню команду Set As Start Up (установить как стартовый) — подсветка сместится со строки Math на Projectl.
Теперь, когда в группе проектов нужный из них задан как стартовый, мы должны установить ссылку на компонент Math, иначе его объекты будут недоступны в этом проекте. Выберите из меню Project команду References и в одноименном диалоговом окне пометьте флажок в строке Math. (Эго та самая строка, которую мы указали в свойстве Name проекта MATHPROJ.VBE помните?) Закончив, щелкните кнопку ОК. чтобы закрыть диалоговое окно References.
Прежде чем продолжить работу над тестовым проектом, сохраните все как группу проектов. Понятие «группа проектов» — простое расширение концепции записи модулей и форм как единого проекта. При этом запоминается информация о конкретном составе проектов, загруженных в данный момент. Файлу группы проектов присваивается расширение VBi, Выберите из меню File команду Save Project Group As. Сохраните Forml как FORMl.FRM, проект стандартной ЕХЕ-программы — KaKPROJECTl.VBP, а группу проектов — как MATHDEMO,VBG.
Добавьте на форму (Forml) 6 текстовых полей и 4 кнопки, разместив их примерно так, как показано на рис. 27-1. Кроме того, чтобы немного улучшить внешний вид формы, я вставил 3 элемента управления Line и пару меток. Хотите — добавьте их тоже.
Рис. 27-1 Форма для тестирования нового объекта FractionРис. 27-1 Форма для тестирования нового объекта Fraction
Рис. 27-1 Форма для тестирования нового объекта Fraction
Рис. 27-1 Форма для тестирования нового объекта Fraction
Присвойте текстовым полям имена txtNl,txtDl,txtN2,txtD2,txtN3 и t.xtDF Эти поля соответствуют числителям и знаменателям трех дробей и расположены попарно слева направо. Имена кнопок таковы: cmdAdd, cmdSubtrackcmdMultiplya cmdDivide-. ну а их названия адекватны соответствующим математическим операциям (рис. 27-1). И, наконец, в модуль формы введите код:
Option Explicit . ■
Public Frac As New Hath.Fraction
Private Sub cmdAdd_Click()
Frac.Num = txtNI.Text
Frac.Den = txtDI.Text
Frac.Add txtN2.Text, txtD2.Text txtNS.Text = Frac.Num txtDS.Text = Frac. Den
End Sub
Private Sub cmdDivide„01ick()
Frac.Num = txtNI.Text
Frac.Den = txtDI.Text
Frac.Div txtN2.Text, txtD2.Text txtN3.Text = Frac.Num txtD3.Text = Frac.Den
End Sub
Private Sub cmdMultiply_Click()
Frac.Num = txtNI.Text Frac. Den = txtDI.Text Frac-Hui txtH2.Text, txtD2.Text txtNS.Text= Frac.Num txtDS.Text = Frac.Den
End Sub
Private Sub cmdSubtract_Click()
Frac.Num = txtNI.Text
Frac,Den = txtDI.Text
Frac.Sbt txtN2.Text, txtD2.Text
txtNS.Text = Frac.Num
txtDS.Text = Frac.Den
End Sub
На уровне формы я объявил переменную Ггяс как ссылку на объект типа Fraction, определенный в DLL-компоненте Math. Сам объект Frac определяется на уровне модуля, но реально не создается, пока Вы не щелкнете любую из четырех кнопок, и автоматически уничтожается при выгрузке формы.
Теперь запустите тестовую программу. Введите значения для числителей и знаменателей двух дробей (в левой части диалогового окна) и щелкните одну из кнопок, чтобы выполнить соответствующую математическую операцию. Результат, показываемый в правой части диалогового окна, всегда приводится к простейшей дроби. Рис. 27-2 иллюстрирует, что получится при умножении :. на У<
Рис. 27-2 Тест Fraction Math в действии
Создание и использование конечного DLL-модуля
После отладки ActiveX-компонента Вы, наверное, захотите скомпилировать его в DLL-модуль. Для этого достаточно выбрать из меню File команду Маке MATHPROJ.DLL. Откомпилированная DLL, как я уже говорил, автоматически регистрируется на Вашем компьютере и становится доступной любым приложениям, способным исполь- 3OBarbActiveX-компоненты.
Когда Ваша DLL попадет в руки конечного пользователя, ее надо будет зарегистрировать и в той системе. Только тогда она появится в списке ссылок и только тогда внешнее приложение сможет загрузить ее в свое адресное пространство и работать с объектами, определенными в ней. Простейший способ зарегистрировать DLL в пользовательской системе — создать установочный диск с помощью Package and Deployment Wizard. В этом случае DLL регистрируется при установке приложения автоматически.
Дорогой Джон, как...
Создать DLL на С?
Сильная сторона Visual Basic — та скорость, с которой он позволяет разрабатывать приложения для Windows; большей продуктивно-
стью не обладает, пожалуй, ни одна другая среда программирования. Но, когда дело касается быстродействия критических блоков кода, тут С нет равных. И в таких случаях Вы пишете их как набор функций, помещаемых в обычную (а не в ActiveX) DLL.
Последние версии компиляторов Visual C++ упрощают создание DLL, как никогда раньше, и я решил дать пример DLL-кода, написанного на С; он позволит понять главные этапы процесса разработки DLL. (Если Вы программируете для 16-разрядной Windows или используете версию Visual C++ ниже 2.2, Вам придется модифицировать листинги.) Подробные и глубокие объяснения всех аспектов создания DLL Вы найдете в документации на свой компилятор.
Два С-файла
В проекте Visual C++ понадобятся всего 2 файла; их содержимое показано ниже. Откройте новый проект в 32-разрядной версии Visual C++ и в качестве типа проекта выберите Win32 Dynamic-Link Library. Создайте DEF-файл как часть проекта, введите следующие строки кода и сохраните его под именем MYDLL.DEF.
; Mydll.def
LIBRARY Mydll
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE
EXPORTS
TestByte61
Testinteger62
TestLong63
TestSingle64
TestDouble@5
ReverseStrlng@6
DEF-файл сообщает имена экспортируемых функций. Иными словами, он содержит список функций, которые Вы сможете вызывать из приложений, написанных на Visual Basic.
В данном проекте DLL всего один файл исходного кода на С. Введите код, показанный ниже, в файл, сохраните его как MYDLL.C и убедитесь, что он включен в состав проекта Visual C++.
ffinclude <windows.h>
tfinclude <ole2.h>
BYTE _stdcall TestByte( BYTE a, LPBYTE b } <
b = a + a;
return( *b + a );.
short Stdcall Testlnteger( short a, short far * b ) {
b = a + a;
return( *b + a );
long _stdcall TestLong( long a, LPLONG ъ ) {
b = a + a;
return( *b + a );
float _stdcall TestSingle( float a, float far * b } {
b = a + a;
return( *b + a );
double stdcall TestDouble( double a, double far * b ) {
b = a + a;■ ■
return( *b + a );;'
}
void stdcall ReverseString( BSTR a }
{
int i, lien;
bstr b;■.
LPSTR pA, pB;
ILen = strlen( (LPCSTR)a };
b = SysAllocStringLen( NULL, ILen );
pA = (LPSTR)a;.
pB = (LPSTR)b + ILen - 1;
for ( I = 0; i < ILen; ±++ )
• pB— = *pA++;
pA = (LPSTR)a;"■
pB = (LPSTR)b;
for ( I = 0; I < ILen; I++ )
*pA++ = *pB++;.
SysFreeStringt b );
}
В среде Visual C++ щелкните КНОПКУ Buitd All (собрать все), чтобы откомпилировать и скомпоновать эти файлы проекта и создать
крошечный DLL-модуль MYDLL.DLL Переместите или скопируйте его в Windows-каталог SYSTEM — тогда операторы Declare в программе на Visual Basic смогут автоматически находить этот DLL-файл.
Тестирование DLL
Проверить работу функций из нового DLL-файла несложно. Для этого я открыл новый проект в Visual Basic, добавил на форму кнопку cmdGo и включил в модуль формы такой код:
Option Explicit
Private Declare Function TestByte _
Lib "mydll.dll" < _
, ByVai a As Byte, _
ByRei b As Byte _,
) As Byte■
Private Declare Function Testinteger _
Lib mydll '' ’ ■ ( _
ByVai a As Integer, _..
ByRef b As in itiger _' *
) As Integer
Private Declare Function TestLong _'
Lib "mydll.dll" ( _'
ByVai a As Long, _
ByRei b As Long _.
) As Long
; V й
Private Declare Function Testsingle _-
Lib "mydll.dll" ( _
ByVai a As Single, _.
ByRei b As Single _ ■...J ton
) As Single'
Private Declare Function TestDoubla ri1
Lib "mydll.dll" C _
ByVai a As Double, „■
ByRei b As Double _’
) As Double■
■ ■ t r, ■• , „-ЭО
Private Declare Sub Reversestring ^,,
Lib "mydll.dll" ( _.- ■ 11 -_•
ByVai a As String _*
>ь>
Private Sub cmdGo_Click()
Dim bytA As Byte
Dim bytB As Byte
Dim byte
As
Byte
Dim
intA
As
Integer
Dim
intB
As
Integer
Dim
intC
As
Integer
Dim
IngA
As
Long
Dim
IngB
As
Long
Dim
IngC
As
Long
Dim
sngA
As
Single
Dim
sngB
As
Single
Dim
sngC
As
Single
Dim
dblA
As
Double
Dim
dblB
As
Double
Dim
dbIC
As
Double
Dim
strA
As
String
bytA = 17
byte = TestBytetbytA, bytB)
Print bytA, bytB, byte
intA = 17
intC = Testinteger(IntA, intB)■
Print intA, intB, intC
IngA = 17
IngC = TestLongUngA, IngB)
Print IngA, IngB, IngC
sngA = 17
sngC = TestSingle(sngA, sngB)•
Print sngA, sngB, sngC
dblA = 17
dbIC - TestDouble(dblA, dblB)
Print dblA, dblB, dbIC■
strA = "This string will be reversed"
Print strA
Reversestring (strA)
Print strA
End Sub
Щелчок кнопки Go после запуска программы приводит к вызову каждой из шести DLL-функций, а результаты отображаются на форме (рис. 27-3).
А теперь несколько слов об этой простой DLL. Каждая из пяти числовых функций демонстрирует передачу своего типа данных двумя способами. Первый параметр передается с ключевым словом ByVai, второй — с ключевым словом ByRef. Параметры, передаваемые с ByVai, — в отличие от параметров, передаваемых с ByRef, —
изменять в DLL-функции нельзя. Каждая функция тестовой программы передает в первом параметре числовое значение ■' " которое DLL-функция удваивает и записывает во второй параметр. Кроме того, каждая числовая DLL-функция возвращает сумму двух параметров того же типа данных. На рис. 27-3 видно, как эти значения модифицируются DLL-функциями. В объявлениях тестовой программы явно указывается ключевое слово ByRef, но оно необязательно, поскольку Visual Basic использует его по умолчанию. Однако О\ х J опустить нельзя, и поэтому я решил явно объявлять оба вида параметров, чтобы не было путаницы. Впрочем, это уже вопрос стиля — можете убрать все ключевые слова ByRef.
ОО
Рис. 27-3 Результаты тестирования функций из MYDLLDLL
Наверное, Вы заметили, что в С-функции, ожидающей от Visual Basic параметр типа Integer, я применяю вместо int тип данных short. Это связано с тем, что int в 32-разрядном компиляторе Microsoft С на самом деле 32-битное целое, а вовсе не 16-битное, как тип Integer в Visual Basic. К счастью во всех версиях Microsoft С существует тип данных short, адекватный 16-битному Integer.
Теперь С и Visual Basic внутренне обрабатывают строки как BSTR-типы — только в С строки надо объявить таковыми. В этом примере я передаю строку из Visual Basic в DLL-функцию ReverseString с ключевым словом ByVai. Можно, конечно, использовать и ключевое слово ByRef, но, поскольку BSTR-строки все равно передаются по адресу, Вы почти ничего не выиграете от этого и к тому же не сможете модифицировать строку в DLL-функции.
Обратите внимание, что Reversestring вызывает S\sAllocStringLen и SysFreeString — API-функции, способные оперировать с BSTR- строками и тем упрощающие работу со строками Visual Basic в DLL, написанной на С. Впрочем, это уже выходит за рамки книги — читайте документацию Visual C++. Пример, который я привел здесь, просто поможет с чего-то начать,
tj^ См. также...
• Приложение BitPack в главе 34 «Сложные приложения», где демонстрируется создание и использование еще одной DLL.
Дорогой Пхглн. В^Г^^Г»
Создать приложение, выполняемое удаленно?
Visual Basic Enterprise Edition позволяет создавать приложения, выполняемые на удаленных компьютерах, но предоставляющие свойства и методы своих объектов приложениям, которые работают на локальной машине. Это часть концепции распределенных вычислений (distributed computing), где задача обрабатывается несколькими компьютерами с использованием одного из механизмов технологии ActiveX — удаленной автоматизации.
Практически любое приложение с открытыми свойствами и методами можно сконфигурировать для удаленного выполнения. Удаленное приложение создается по тем же принципам, что и локальные ActiveX-приложения, но параметры компилятора меняются и, кроме того, необходима регистрация как на удаленной, так и на локальной машинах.
В этом разделе я подробно расскажу, как создается и запускается удаленное приложение, отыскивающее простые числа. Оно будет несложным, так как главное в удаленной автоматизации — корректно сконфигурировать системы.
Создание удаленного приложения
Чтобы создать удаленное приложение, откройте проект ActiveX EXE и в диалоговом окне Project Properties на вкладке Component пометьте флажок Remote Server Files (удаленные серверные файлы), как показано на рис. ? " -1 Установка этого флажка заставляет Visual Basic генерировать файлы, которые позволят клиентским машинам обращаться к удаленному приложению. При компиляции Visual Basic создаст регистрационный файл (VBR) и библиотеку типов (TLB) для клиентских машин.
liwis* | Nek* J Сагпу* "*№il | р^цде^ |
. titat.Mp*. ::
fbwttt _-ъ
Yew cannsaoDh?Yew cannsaoDh?^ tpcnrrnattuf^^ tpcnrrnattuf^JJ
Yew cannsaoDh?
Yew cannsaoDh?
^ tpcnrrnattuf^
^ tpcnrrnattuf^
J
J
Рис. 27-4 Вкладка Component диалогового окна Project Properties
Приложение Prime Numbers, отыскивающее простые числа (PRIME.VBP), демонстрирует, как передать задачу, требующую интенсивных вычислений, удаленному компьютеру, Оно предоставляет один объект, Number, со свойством Value типа Long, в которое Вы записываете любое положительное целое число, и тот находит ближайшее простое число, меньшее или равное начальному значению.
Одна из интересных особенностей приложения Prime Numbers — асинхронная обработка. Поиск простых чисел большой величины требует ощутимого времени, и поэтому приложение Prime Numbers позволяет периодически опрашивать свойство Value: если оно возвращает ненулевое значение, это и есть искомый результат. Таким образом, Вы получаете возможность искать простые числа большой величины, не перегружая этой задачей свою локальную машину.
Вот как выглядит модуль класса Number:
■ NUMBER,CIS/
Dim mIMaxNumber As Long
Dim rnlFound As Long
Property Get ValueO As Long
' возвращаем простое число
' (Value = 0, пока идет поиск числа)
Value = rnlFound,
End Property.
Property Let Value(Setting As Long)
' инициализируем переменные уровня модуля mIMaxNumber = Setting rnlFound = 0
' начинаем асинхронные вычисления"-
frmLaunch. Launch Me.
End Property
Friend Sub ProcFindPrime()
Dim Count As Long
For Count = 2 To mIMaxNumber \ 2
DoEvents
If mIMaxNumber Hod Count = 0 Then mIMaxNumber = mIMaxNumber - 1 ProcFindPrime Exit Sub
End If
iNext Count-
rnlFound = mIMaxNumber'
End Sub..
Процедура ProcFindPrime запускается приложением Prime Numbers с помощью таймера после возврата им управления вызывающей программе. Этот прием может показаться странным, тем не
менее он удобен. Форма Launch с именем/пя/дмисй содержит таймер imrLauncb и код:
' LAUNCH. FRM
Dim mnumObject As Number
Public Sub Launch(numObject As Number)
Set mnumObject = numObject
tmrLaunch.Enabled = True
tmrLaunch.Interval = 1
End Sub
Private Sub tmrLaunch_Timer()
' отключаем таймер
tmrLaunch. Enabled = False
' запускаем вычисления в объекте mnumObject.ProcFindPrime
End Sub
Регистрация удаленного приложения■
Удаленное приложение устанавливается на сервере, а регистрируется и на сервере, и на клиентских машинах. Чтобы зарегистрировать такое приложение на сервере, просто запустите его — программы, написанные на Visual Basic, регистрируются сами. А для регистрации на клиентской машине скопируйте его VBR- и TLB-файлы на эту машину и запустите утилиту CLIREG32.EXE (она хранится на установочном компакт-диске в каталоге COMMON\TOOLS\CLIREG). Вот команда, которая регистрирует приложение Prime Numbers на клиентской машине и указывает сервер WOMBAT2:
CLIREG32 PRIME. VBR -t PRIME. TLB -S W0MBAT2
Утилита CUREG52 открывает диалоговое окно, позволяющее модифицировать регистрационные записи для приложения (рис. 27-5).
И;
Smuwftwp*Г
Г ДОдеиДО
giaifirtBi
Jb::I
Рис. 27-5 Диалоговое окно утилиты CIJREG32 позволяет
выбрать сетевой протокол для удаленного доступа
Механизм удаленной автоматизации поддерживает несколько сетевых протоколов: TCP/IP, IPX и NetBEUI. В общем случае, если
обращение по одному протоколу оказывается безуспешным, предпринимается попытка использовать другой поддерживаемый протокол из числа установленных на клиентской машине. Порядок перебора таких протоколов определяется порядком их регистрации.
В данной утилите нет ничего необычного: это просто интерфейс для регистрационного файла, созданного в Visual Basic. Большинство регистрационных файлов в Windows имеют расширение REG, и, если Вы предпочитаете регистрировать приложение с помощью Regedit, замените VBR на REG, удалите первые две строки, вместо них введите REGEDIT4 и добавьте пустую строку. Однако в этом случае Вам придется самому добавить в реестр информацию о сервере и протоколах. Посмотрим, для примера, что содержится в VBR- файлс приложения Prime Numbers:
VB5SERVERINFO
VERSION=1.0.О
HKEY_CLASSES_ROOT\Typalib\{9311AADB-B46F-11D1-8E5B-000000000000}\
.0\0\win32 = Prime.exe
HKEY_CLASSES_ROOT\Typelib\{9311AADB-B46F-11D1-8E5B-000000000000}\-
.0\FLAGS = 0
HKEY_CLASSE8_R00T\Prime. NumbeACLSID =
{9311AADD-B46F-11D1-8E5B-O00O0OOOO000}
HKEY_CLASSES_ROOT\CLSID\{9311AADD-B46F-11D1-8E5B-OOOOOOOOQOOO}\
ProgID = Prime.Number
HKEY_CLASSES_ROOT\CLSID\{9311AADD-B46F-11D1-8E5B-OOOOO0OOOOOO}\
Version = 1.0
HKEY_CLASSES_ROOT\CLSID\{9311AADD-B46F-11D1-8E5B-000000000000}\
Typelib = {9311AADB-B46F-11D1-8E5B-OOOO000O0000}
HKEY_CLASSES_ROOT\CLSID\{9311AADO-B46F-11D1-8E5B-OOOOOOOOQOQO}\
LocalServer32 = Prime.exe
HKEY_CLAS8ES_R00T\INTERFACE\
{9311AADC-B46F-11D1-8E5B-000000000000} = Number
HKEY_CLASSES_ROOT\INTER FACE\
{9311AADC-B46F-11D1-8E5B-000000000000}\ProxyStubClsid =
{00020420-0000-0000-0000-000000000046}
HKEY_CLASSES_ROOT\INTERFACE\
{9311AAD0-B46F-1101-8E5B-000000000000}\ProxyStubClsid32 =
{00020420-0000-0000-0000-000000000046}
HKEY_CI_ASSES_RQ0T\INTERFACE\{9311AA0C-B46F-11D1-8E5B-000000000000}\
Typelib = {9311AADB-B46F-11D1-8E5B-000000000000}
HKEY_CLASSES_R00T\INTERFACE\{9311AADC-B46F-11D1-8E5B-000000000000}\
Typellb\"version" = 1.0
Выполнение удаленного приложения
Доступ к приложению через механизм удаленной автоматизации требует выполнения на сервере диспетчера Автоматизации — Automation Manager (AUTMGR32.EXE) и таких параметров защиты сервера, чтобы пользователь мог выполнять необходимые действия.
Для запуска этого диспетчера откройте меню Start и выберите из подменю Microsoft Visual Basic 6.0 команду Automation Manager. Он открывает небольшое окно, где сообщает состояние сервера, как показано на рис. 27-6.
si«Tt,nfl ^4##гадй1С#Й!*^ J
Рис. 27-6 Окно Automation Manager
Windows NT предусматривает полные профили защиты для каждого пользователя. Чтобы задействовать объекты с сервера удаленной автоматизации, пользователь должен обладать соответствующими правами доступа. Кроме того, запуск приложения (если оно еще не выполняется) требует разрешить на сервере удаленное создание процессов. Настроить системный реестр помогает диспетчер подключения к серверу удаленной автоматизации — Remote Automation Connection Manager (RACMGR32.EXE), окно которого представлено на рис. 27-7.
__F^i*£oAwjfl^^' _ Н!й£Т
№‘ P-asM^i Pui^^m№‘ P-asM^i Pui^^m1111 'J Wr-jeJLxi-t-er ha . tL^or, i■WPic|JerCi6w1111 'J Wr-jeJLxi-t-er ha . tL^or, i■WPic|JerCi6wГчС41ЕхОаЬд5ег'*ея.1 ТнЕкТгк МТкЕкеси1|¥еТ«сквг 1 J&ClP^ODBCCIass mt РЫче и$ТЬгиРго|.РанТЬа£1а?$ Damage ОйСиГ№П1 <№fiv)PiDi FoaiMngrChssMSSfiVRSa S&KVEWT McRaeFlange Docl<w* CTx.T i»»adiorCcetec СТ*. T lensqcrtiOnCtWBueE * ISDREPVfl.SDOE^T O^AVtEpBlaCiKl »^п«(^лд]йой6)п етдкияеДфк^мпЛ Bfi^afe DttUVMiГчС41ЕхОаЬд5ег'*ея.1 ТнЕкТгк МТкЕкеси1|¥еТ«сквг 1 J&ClP^ODBCCIass mt РЫче и$ТЬгиРго|.РанТЬа£1а?$ Damage ОйСиГ№П1 <№fiv)PiDi FoaiMngrChssMSSfiVRSa S&KVEWT McRaeFlange Docl<w* CTx.T i»»adiorCcetec СТ*. T lensqcrtiOnCtWBueE * ISDREPVfl.SDOE^T O^AVtEpBlaCiKl »^п«(^лд]йой6)п етдкияеДфк^мпЛ Bfi^afe DttUVMi• J I Senior <edEi ку^су•.. *- ^wv-й Eledet'^A^• J I Senior <edEi ку^су•.. *- ^wv-й Eledet'^A^2 СЬпЦоля |ferrtcCenMrtm |
№‘ P-asM^i Pui^^m
№‘ P-asM^i Pui^^m
1111 'J Wr-jeJLxi-t-er ha . tL^or, i
■WPic|JerCi6w
1111 'J Wr-jeJLxi-t-er ha . tL^or, i
■WPic|JerCi6w
ГчС41ЕхОаЬд5ег'*ея.1 ТнЕкТгк МТкЕкеси1|¥еТ«сквг 1 J&ClP^ODBCCIass mt РЫче и$ТЬгиРго|.РанТЬа£1а?$ Damage ОйСиГ№П1 <№fiv)PiDi FoaiMngrChss
MSSfiVRSa S&KVEWT McRae
Flange Docl<w* CTx.T i»»adiorCcetec СТ*. T lensqcrtiOnCtWBueE * ISDREPVfl.SDOE^T O^AVtEpBlaCiKl »^п«(^лд]йой6)п етдкияеДфк^мпЛ Bfi^afe DttUVMi
ГчС41ЕхОаЬд5ег'*ея.1 ТнЕкТгк МТкЕкеси1|¥еТ«сквг 1 J&ClP^ODBCCIass mt РЫче и$ТЬгиРго|.РанТЬа£1а?$ Damage ОйСиГ№П1 <№fiv)PiDi FoaiMngrChss
MSSfiVRSa S&KVEWT McRae
Flange Docl<w* CTx.T i»»adiorCcetec СТ*. T lensqcrtiOnCtWBueE * ISDREPVfl.SDOE^T O^AVtEpBlaCiKl »^п«(^лд]йой6)п етдкияеДфк^мпЛ Bfi^afe DttUVMi
• J I Senior <edEi ку^су
•.. *- ^wv-й Eledet'^A^
• J I Senior <edEi ку^су
•.. *- ^wv-й Eledet'^A^
Рис. 27-7 Remote Automation Connection Manager позволяет указать на сервере полномочия пользователя на доступ к удаленному приложению
Для отладки установите флажок Allow All Remote Creates (разрешить все запросы на удаленное создание процессов). Потом, если возникнет такая необходимость, можно будет ограничить доступ к серверному приложению.
ПРИМЕЧАНИЕ Средства ACL (Access Control List) доступны в Remote Automation Connection Manager только на компьютерах под управлением Windows NL.
Доступ к удаленному приложению
Установив свое удаленное приложение и зарегистрировав его на сервере и на клиентских машинах, Бы можете использовать его объекты с сервера точно так же, как и локальные. Для программирования этих объектов надо установить ссылку на TLB-файл приложения, который может храниться на локальном компьютере, поскольку он лишь предоставляет информацию об удаленном приложении и не содержит исполняемого кода.
Следующий код позволяет протестировать приложение Prime Numbers:.
Option Explicit.-
Private Sub Form_Load()Dim x As New Prime. Number x,Value = 42Debug.Print x.ValueEnd SubPrivate Sub Form_Load()Dim x As New Prime. Number x,Value = 42Debug.Print x.ValueEnd SubВывод программы иллюстрирует рис. 27-8.Вывод программы иллюстрирует рис. 27-8.Рис. 27-8Рис. 27-8/’етрльтдт, сообщаемый приложением Prime Numbers/’етрльтдт, сообщаемый приложением Prime Numbers
Private Sub Form_Load()
Dim x As New Prime. Number x,Value = 42
Debug.Print x.Value
End Sub
Private Sub Form_Load()
Dim x As New Prime. Number x,Value = 42
Debug.Print x.Value
End Sub
Вывод программы иллюстрирует рис. 27-8.
Вывод программы иллюстрирует рис. 27-8.
Рис. 27-8
Рис. 27-8
/’етрльтдт, сообщаемый приложением Prime Numbers
/’етрльтдт, сообщаемый приложением Prime Numbers
Возврат ошибок из удаленных приложений
Ошибки, возникающие в удаленном приложении, передаются клиентской программе также, как это делают локальные ActiveX-приложения. Модифицируем код для свойства Value и посмотрим, как возвращаются ошибки клиентской программе:
Property Let Value{Setting As Long)
' генерируем ошибку при отрицательных значениях
If Setting <= 0 Then
Err.Raise 8001, "Prime.Humber", _
"Value must be a positive integer."
Exit Property
End If
' инициализируем переменные уровня модуля
mIMaxNumber = Setting
■IFound = 0
' начинаем асинхронные вычисления
f rinLaunch. Launch Me
End Property
Рис. 27-9 иллюстрирует, как выглядит на экране сообщение об ошибке.
г^,.1?.. гп1имммиииювам
RijHXtb№iX Mil's■
Рис. 27-9 Сообщение об ошибке, отображаемое на экране при записи отрицательного значения
Отладка удаленных приложений
Прежде чем устанавливать удаленное приложение на сервер и тестировать ci ;> работу, следует тщательно отладить это приложение на локальной машине. После того как оно установлено и зарегистрировано на сервере и клиентских машинах, любые изменения потребуют его повторной регистрации на всех компьютерах.
Удаленное приложение может оставаться в памяти сервера и после сеанса удаленного доступа, поэтому перед установкой новых версий будет совсем нелишне проверить наличие фантомных экземпляров этого приложения с помощью Windows NT Task Manager (рис. 27-10) — он вызывается нажатием клавиш (.trl+Alt+DcL Закройте эти экземпляры перед регистрацией новой версии удаленного приложения.
Выявление и устранение проблем при использовании уд»—автоматизации
Увы, сообщения об ошибках при использовании удаленной автоматизации носят слишком общий характер. По ним трудно выявить конкретную проблему, так как одно и то же сообщение может по-
являться в разных ситуациях. В таблице я привел несколько ошибок, связанных с удаленной автоматизацией, и перечислил некоторые из наиболее вероятных причин их появления, исходя из собственного опыта.
0.06 Ct 0:07 2В Cl ОО.Гп CtDO-12 Q0&51 HOQ.DJ 00151 0.01:27 6QM7 ■: г-? m0.06 Ct 0:07 2В Cl ОО.Гп CtDO-12 Q0&51 HOQ.DJ 00151 0.01:27 6QM7 ■: г-? mtt)6r 1160»708Ktt)6r 1160»708K14521 23nnL14521 23nnL<УЙи**ь **«**« | FMxtiHni»!
0.06 Ct 0:07 2В Cl ОО.Гп CtDO-12 Q0&51 HOQ.DJ 00151 0.01:27 6QM7 ■: г-? m
0.06 Ct 0:07 2В Cl ОО.Гп CtDO-12 Q0&51 HOQ.DJ 00151 0.01:27 6QM7 ■: г-? m
tt)6r 1160»
708K
tt)6r 1160»
708K
14521 23nnL
14521 23nnL
120 СВ 13* 00
127 СВ
137 00 Ю СО 197 СО
223 00 № (В 253 09
268 О0
260 00
29В О0 299 00
328 СП
33100
з32 90
43 Я
^TT^Pt^wj
Рис. 27-10 Windows NT Task Manager позволяет проверить наличие фантомных экземпляров удаленного приложения перед установкой его новых версий
Как правило, удаленная автоматизация либо работает хорошо, либо не работает вообще, а большинство проблем достаточно просты — например, вместе с именем сервера указаны две обратные наклонные черты (\\) или в реестре имя сервера ошибочно дополнено именем разделяемого ресурса (share name).
Номер ошибки
Текст сообщения
Вероятные причины
&Н800706ВА
The RPC server is unavailable (RPC-сервер недоступен)
Исполняемый файл удаленного приложения на сервере не найден.
&Н8007О6ВЕ
The remote procedure call failed (вызов удаленной процеду ры не удался)
Сетевое соединение с сервером разорвано или истек заданный период ожидания отклика.
&H800706D9
There are no more endpoints from the endpoint mapper (исчерпан лимит подключений)
На сервере не выполняется Au toma tionManager
(AUTMGR32.EXE).
Дорогой Джон, как...
Создать надстройку для среды разработки Visual Basic?
Надстройка (add-in) Visual Basic — особая программа, подключаемая к среде программирования и расширяющая ее функциональность. Один из самых полезных типов надстроек — «библиотекарь» исходного кода, аналогичный продукту Microsoft Visual SourceSafe, который поставляется с Visual Basic Enterprise Edition.
В подкаталоге SAMPLES на компакт-диске MSDN, поставляемом с Visual Basic, Вы найдете две надстройки-примера, TabOrder и Vis- Data, хотя, на мой взгляд, эти программы могут только запутать новичка — слишком уж изощренно они используют модель объектов Visual Basic Extensibility. Кроме того, в Visual Basic предусмотрен шаблон для создания надстроек — Addin. Но разобраться в его коде, не зная базовых концепций надстроек, тоже очень трудно.
В этом разделе я представлю упрощенный пример, который поможет понять методику создания и выполнения программ-надстроек. Усвоив его, Вы разберетесь и в примерах, включенных в комплект поставки Visual Basic.
Базовые концепции
Как я уже говорил, надстройка Visual Basic — программа, написанная на этом языке и построенная особым образом. Пример, который я собираюсь продемонстрировать, начинает свою работу с процедуры Main, находящейся в стандартном модуле (BAS-файле). Main отвечает за корректность соответствующей записи в файле VBADDIN.INI и больше ничего не делает. Эта запись позволяет Windows найти и загрузить надстройку (когда Вы того пожелаете). Регистрация надстройки обычно осуществляется автоматически при ее установке. Но, поскольку мы создаем некоммерческий продукт, Вы просто запустите ее разок вручную.
При выборе из меню Add-Ins в среде Visual Basic команды AddIn Manager (диспетчер надстроек) появляется список всех надстроек, которые можно загрузить или, напротив, выгрузить из данного экземпляра среды программирования. В нашей надстройке будет создан специальный класс, реализующий интерфейс расширяемости Visual Basic — IDTExtensibility. Для этого мы воспользуемся ключевым словом Implements:
Implements IDTExtensibility
Интерфейс — в данном случае своего рода договор, заключаемый между Вашей программой и Visual Basic; в нем как бы подтверждается, что Ваша программа реализует специфические процедуры, которые сможет вызывать другая программа (т. е. среда Visual Basic). В случае IDTExtensibility Вы должны реализовать 4 процедуры:
15—1204
1Имя процедурыСитуация, в которой вызывается
процедура roTExtensibilityOnAddlnsUpdate При запуске среда Visual Basic
загружает данную надстройку.
Процедура вызывается на пер-
•вых этапах запуска.
TDTExtensibilky_OnStartupComplete Данная надстройка завершила • свою загрузку в среде Visual ь . Basic. Это происходит уже после загрузки самой среды.
IDTExtensibilityOnConnectionПользователь выбрал данную
надстройку в Add-In Manager.
IDTExtensibility_OnDisconnectionПользователь отказался отдан
ной надстройки в Add-In . Manager."
Процедура IDTExtensibilky_OnConnection предусматривает параметр VBInsL который обеспечивает доступ к корневому объекту модели VBE — VBIDE.VBE. Через этот параметр Вы можете модифицировать меню и панели инструментов среды Visual Basic, связывать процедуры с событиями в этой среде и выполнять другие операции.
ПРИМЕЧАНИЕ В Visual Basic 4 для связывания процедур в модуле класса с событиями среды программирования приме- ;., нялся метод < ionncctEvents. Новая модель объектов расширяемости Visual Basic (VBE) предоставляет гораздо больше возможностей..
По-настоящему интересная работа выполняется процедурой MenuHandler Click или любыми другими реагирующими на события процедурами надстройки. В этом примере мы воспользуемся небольшим подмножеством объектной иерархии Visual Basic для поиска и изменения размеров всех кнопок на активной форме в среде разработки. Возможности этой надстройки, конечно, невелики, но лично мне она кажется полезной. Ведь сколько приходится возиться, чтобы все кнопки на формах, задействованных в проекте, были одинаковы, а эта крошечная надстройка мгновенно приводит их к единому стандартному размеру.
Разработка надстройки
Теперь перейдем к тому, как создать относительно простую надстройку, — я расскажу об этом в «рецептурном» стиле. Впервые разрабатывая такую программу, я несколько раз спотыкался на некоторых, не совсем очевидных тонкостях, из-за чего приходилось перелопачивать целые горы документации. Но Вам будет легче: я посвящу Вас в эти тонкости. -•■
Создайте новый проект ActiveX EXE, добавьте второй модуль класса и один стандартный модуль. Последнему присвойте имя Myaddin, а модулям классов — Connect и Sizer. Перед запуском надстройки надо изменить значения ряда важных свойств проекта, но это делается только после ввода в модули исходного кода.
Начнем с Myaddin. Единственное, что делает процедура Main, —■ регистрирует данную надстройку в Windows. Ее код выполняется очень быстро, и программа завершается практически мгновенно, поэтому Вы вряд ли успеете что-нибудь заметить.
Option Explicit.
' объявляем API-функцию для записи в INI-файл
Declare Function WritePrivateProfileString _
Lib "Kernel32" Alias "WritePrivateProfileStringA" ( _
ByValAppName$, _
ByVai KeyNamet, _■:
ByVai keydefaultt, _
ByVai FileNameS _
) As Long
' объявляем API-функцию для чтения из INI-файла
Declare Function GetPrivateProfileString _
Lib "Kernel32" Alias "GetPrivateProfileStringA" ( _
ByVai AppName$, _
ByVai KeyName$, _. "
ByVai keydefault$, _-"
ByVai ReturnStringt, _-
ByVai NumBytes As Long, _
ByVai FileName$ _
) As Long
SubMainO".
Dim strReturn As String
Dim strSection As String
' переходим к работе с файлом VBADDIN.INI
strSection = "Add-Ins32"
StrReturn = String$(255, Chr$(O))
GetPrivateProfileString strSection, _
"cmdSizer. Connect", "NotFound", _
strReturn, Len(strReturn) + 1, "Vbaddin.ini"
If InStr(strReturn, "NotFound") Then
WritePrivateProfileString strSection, "cmdSlzer.Connect", _ "0", "vbaddin.ini"
End If
End Sub
Модуль класса Connect реализует процедуры IDTExtensibility_On- Connection и IDTExtensibility_OnDisconnection, автоматически вы-
зываемые Visual Basic при подключении/отключении данной надстройки или при выходе из сю среды. Вы должны понимать, что уже упомянутая процедура Main выполняется только при установке надстройки, а процедуры, включенные в модуль класса Connect, — при каждой ее загрузке или выгрузке. Ну и если продолжить эту тему, то процедуры во втором модуле класса выполняются при каждом щелчке элемента меню. Четкая классификация этих процедур помогает понять, что происходит в программе. Итак, добавьте в модуль класса Connect следующий код:.
Option Explicit-,. ,
' указываем, что этот класс реализует интерфейс
' расширяемости для Visual Basic.,;
Implements IDTExtensibility•.:,
Dim VBInstance As VBIDE.VBE,
Dim mnuSlze As Office.CommandBarControl■'
Dim SizerHandler As Sizer"
' присвойте этим константам нужные Вам значения■ ■
Const CMDBTNWIDTH = 1200
Const CMDBTNHEIGHT = 400.
Private Sub IDTExtensibility OnConnection _
(ByVai VBInst As Object, _.
ByVai ConnectMode As vbext_ConnectMode, _
ByVai Addlnlnst As VBIDE.Addin, „
custom() As Variant)'
' сохраняем ссылку на этот экземпляр Visual Basic, ' чтобы ссылаться на него впоследствии Set VBInstance = VBInst1
' добавляем элемент а меню Add-Ins среды Visual Basic.
Set mnuSize = VBInstance.CommandRars("Add-Ins").Controls. Add(1) mnuSlze.Caption = "&Size Command Buttons" ‘ создаем объект Sizer
Set SizerHandler = New Sizer.'' J
' устанавливаем СВЯЗЬ между событиями меню и объектом Sizer Set SizerHandler.MenuHandler = „
VBInst.Events.CommandBarEvents(mnuSize)..
' передаем VBInstance объекту Sizer” .
Set SizerHandler.VBInstance = VBInstance
' устанавливаем размеры кнопок■
SizerHandler.ButtonWidth = CMDBTNWIDTH,
SizerHandler. ButtonHeight = CMDBTNHBGHT
End Sub.. .,.
' процедура, которая удаляет элемент меню при отключении' ‘
' данной надстройки в Add-In Manager'.
Private Sub IDTExtensibility_On01sconnection __
(ByVai RemoveMode As VBIDE.vbext_DisconnectMode, _
custom() As Variant)
' удаляем элемент меню
VBInstance. CommandBars("Add-Ins").Controls _
("ASize Command Buttons"). Delete
End Sub
' следующие процедуры-заглушки необходимы, так как
' класс реализует интерфейс IDTExtensibllity
Private Sub IDTExtensibillty_OnAddInsUpdate(custom() As Variant)
End Sub
Private Sub IDTExtensibility_OnStartupComplete(custont() As Variant) End Sub
Заметьте, что я определил в модуле две константы — CMDBTN- WIDTHh CMDBTNHEIGHTpHU задают размеры, под которые подгоняются все кнопки. Вы можете свободно изменять их значения. А еще лучше — если, конечно, не лень — добавить диалоговое окно, позволяющее указывать нужные размеры кнопок «налету». Я, желая максимально упростить пример, использую константы, тем более что их значения устраивают меня практически во всех проектах.
Объект VBInstance полезен в том случае, когда запускается сразу несколько экземпляров среды Visual Basic. Но надстройка присутствует в памяти лишь в одном экземпляре. Сохранив ссылку на объект VBIDE.VBE, передаваемый средой Visual Basic в процедуру ГОТЕх- tensibility_OnConnection, Вы сможете ссылаться на формы и элементы управления в нужном экземпляре Visual Basic.
Модуль класса Sizer содержит код, активизируемый при щелчке элемента меню, созданного Вашей надстройкой. В нем лишь одна процедура — MenuHandler_Click, автоматически вызываемая системой при подключении контейнерного объекта к нужному меню. Введите в модуль класса Sizer такой код:
Option Explicit
' свойства объекта Sizer
Public ButtonWidth As Long
Public ButtonHeight As Long
Public VBInstance As VBIDE.VBE
' объявляем обработчик события, связанного с меню
Public WithEvents MenuHandler As CommandBarEvents
' это событие генерируется при щелчке меню в среде программирования Private Sub MenuHandler_Cllck _
(ByVai CommandBarControl As Object,
Handled As Boolean, CancelDefault As Boolean).
Dim AllControls
Dim Control As Object
' получаем ссылку на набор, содержащий
' все элементы управления на форме
Set AllControls = _
VBInstance.SelectedVBComponent.Designer.VBControls
' для каждого элемента управления на активной форме...
For Each Control In AllControls
' ...если данный элемент - кнопка...
If Control.ClassName = "CommandButton" Then
.' ...модифицируем его размеры
With Control.Properties
.Item("Width") = ButtonWidth . ItemCHeight") = ButtonHeight
End With
End If.
Next Control
End Sub■
При щелчке элемента меню, добавленного надстройкой, эта подпрограмма перебирает все элементы управления, расположенные на текущей форме проекта Visual Basic. Если элемент управления оказывается кнопкой, в его свойства Width и Height записываются предопределенные значения (константы). Потом, когда Вы проверите надстройку на практике, Вы увидите, что все кнопки на текущей форме приводятся к одному (фиксированному размеру.
Прежде чем «апускать программу как надстройку, нужно изменить некоторые свойства ее проекта (рис. 2'-i I). Выберите из меню Project команду Project Properties и в одноименном диалоговом окне щелкните ярлычок General. В списке Startup Object укажите Sub Main, чтобы при первом запуске программы активизировалась процедура Main. В поле Project Name введите cmdSizer. Его содержимое должно совпадать с первой частью строки cmdSizer.Connect, используемой в процедуре Main при регистрации в системе. Если они окажутся разными, Вы не сможете загрузить данную надстройку через Add-In Manager в среде Visual Basic. Это, кстати, первая тонкость, на которой я в свое время споткнулся. В поле Projeci Description наберите Command Button Sizer Add-in. Этот текст появится в Object Browser и поможет идентифицировать объекты, предоставляемые надстройкой.
Теперь перейдите на вкладку Component и в разделе Start Mode (режим запуска) пометьте переключатель ActiveX Component. Надстройка содержит ActiveX-код, активизируемый из внешнего приложения; в данном случае среда Visual Basic будет подключаться к объектам нашей надстройки, используя технологию ActiveX. Наконец, щелкните кнопку ОК, чтобы закрыть диалоговое окно Project Properties.
Fr.PiepiH Piopeitki
Взгеч! Мако | Cntylbj £&#&iiir| ВеЬи&до1
Дг^Т^Г 3 F^r 3
ti^fl»*R,»lCHl*S±JPl
Г- Р1зтёг^ГэЫ(ггал!^^
: г еяедкЫЕяаньп ~^ВЙ?,^И^ ,— —, ;
|5А "::'" Л'^ TiHiift^i |i ^1нш> ;
Рис. 27-11 Свойства проекта надстройки cmdSizer
Еще одна тонкость — не забыть установить ссылку на объекты Microsoft Office и VBIDE.VBE, без которых не может работать эта надстройка. Выберите из меню Project команду References и пометьте флажки в строках Microsoft Office 8.0 Object Library и Microsoft Visual Basic 6.0 Extensibility. Без этих ссылок запуск программы приведет к ошибке.
Свойство Instancing модуля класса Connect должно быть установлено как 5 - MultiUse.Тогда надстройку удастся загружать в несколько экземпляров Visual Basic одновременно. В то же время аналогичное свойство модуля класса Sizer можно установить как 1 - Private.
Первый запуск надстройки
Для регистрации в Windows новую надстройку надо запустить один раз. В процессе отладки это делается из среды Visual Basic. А как только надстройка скомпилирована в ЕХЕ-файл, ее можно зарегистрировать простым запуском в Windows (командой Run из меню Start), которая просто загрузит ЕХЕ-файл надстройки и сделает ActiveX-объекты надстройки доступными другим программам.
Попробуем зарегистрировать надстройку в режиме отладки. Для этого щелкните кнопку Run, потом — Break. В окне Immediate введите Main и нажмите клавишу Enter, чтобы зарегистрировать над- стройкув файле VBADDIN.INI. После этого щелкните кнопку Continue и сверните окно Visual Basic.
Запустите второй экземпляр Visual Basic и попробуйте загрузить новую надстройку через Add-In Manager. Если ошибок не было. Вы увидите в диалоговом окне Add-In Manager имя новой надстройки.
ПРИМЕЧАНИЕ Так как для параметра Start Mode в проекте надстройки указано значение ActiveX Component, процедура
Main не выполняется при запуске программы в среде Visual Basic. Поэтому, чтобы зарегистрировать надстройку, эту процедуру надо вызвать вручную.
Другой подход состоит в том, что Вы создаете ЕХЕ-файл надстройки. Если компиляция прошла успешно, выйдите из среды Visual Basic и запустите полученный файл CMDMZER.EXE (один раз). При этом ничего такого видимого не происходит, он просто регистрируется в системе — вот и все. Затем вновь запустите Visual Basic и приверыс, можно ли теперь загрузить эту надстройку.
Использование надстройки
Чтобы протестировать надстройку, запустите Visual Basic и выберите из меню Add-Ins команду Add-In Manager. Выберите cmdSizer.Con- nect, установите флажок Loaded/Unloaded и щелкните кнопку ОК. В нижней части меню Add-Ins должна появиться новая команда — Size Command Buttons (рис. 27-12).
Рис. 27-12 Надстройка создает новый элемент в меню Add-Ins
&■.--Mil
Рис. 27-13 Кнопки до унификации их размеров
Теперь накидяйте на форму кнопок самых причудливых размеров и проверьте новую команду. Все кнопки должны быть моментально приведены к одному размеру. Форма до выбора команды Size Command Buttons показана на рис. 27-13, а после ее выбора — на рис. Д.-11.
СиитаиЛ 1 Бскигегйй
^Diiryrtk
Рис. 27-14 Кнопки унифицированы надстройкой
Дорогой Джон, как...
Ввести в приложение поддержку сценариев
MicrosoftWindows Scripting Host (WScript.EXE) выполняет сценарии, помещенные в текстовые файлы и написанные на VBScript или JScript. А сценарии позволяют управлять любым приложением, предоставляющим WtiveX-объекты.
ПРИМЕЧАНИЕ Windows Scripting Host можно получить с Web- узла Microsoft bttp://wu4L\microsoppom/scripting/wiruioivsbost.
С помощью сценариев пользователь может автоматизировать операции, выполняемые в приложении, а программист — тестирование своего приложения. Так, сценарий, показанный ниже, создает экземпляр объекта Prime (о котором я уже рассказывал) и выводит на экран список простых чисел.
' TetPrime.VBS
dim i, prime, «script, last, results
1 создаем объекты
set prime = createobject("prime.number")
set wscript = createobject("«script.shell")
for i = 1 to 20
prime.value = i
' ждем ответа
do until prime.value > 0 : loop
if prime.value <> last then
Прим. перс-. Windows Scripting I tost поставляется как компонент операционной системы Windows 98 (в русской версии Windows 98 он называется сервером сценариев).
results = results & " " & prime, value.
end if. ■
last = prime, value next ' выводим результаты «script.popup results,, "Prime Test Script"
Чтобы выполнить этот сценарий, дважды щелкните имя его файла в Windows Explorer или введите команду: wscript tstprime.vbs
Последняя строка в файле TstPrime.VBS отображает результаты (рис. 27-15) вызовом метода PopUp, эквивалентного оператору MsgBox языка Visual Basic.
Рис. 27-15 W'ScriptEXFsunoflHHem тестовый сценарий ' , и отображает результаты
Написание сценариев тестирования особенно удобно применительно к приложениям типа Prime Numbers, которые лишь предоставляют объекты и не имеют пользовательского интерфейса. Но в полной мере потенциал сценариев раскрывается, когда Вы даете возможность пользователям своего приложения писать макросы д ля автоматизации рутинных операций. Об этом мы поговорим в следующем разделе.
Элемент управления Script
Этот элемент позволяет выполнять макросы внутри приложения. Используя его, придерживайтесь такой схемы.
Добавьте на форму элемент управления Script.
Установите его свойство Language как VBScript или JScript — в зависимости от того, на каком языке будут создаваться макросы.
Чтобы указать объекты приложения, которыми можно управлять посредством макросов, вызывайте метод AtldObjeci элемента Script. Эти объекты могут быть закрытыми (формы, классы) или открытыми (ActiveX-объекты).
4. Для загрузки текста с макропроцедурами вызывайте метод AddCode элемента Script. Загруженная макропроцедура выполняется методом Run того же элемента,
ПРИМЕЧАНИЕ Элемент управления Microsoft Script (MSScript.OCX) можно получить с Web-узла Microsoft http:// www. microsoft. com.
При работе с элементом управления Script следует помнить о нескольких, очень важных вещах.
И Текст макросов, загруженных в элемент Script, экспортировать нельзя. Поэтому, если Вы хотите предусмотреть возможность изменения макросов, храните их во внешнем текстовом файле.
Избирательно выгружать объекты или макросы тоже нельзя. Вместо этого Вы должны выгрузить все объекты и макросы методом Reset, а затем загрузить нужное, вызывая методы AddObject и AddCode.
Ошибки периода компиляции могут происходить при загрузке макроса в элемент Script, а ошибки периода выполнения — при его работе под управлением элемента Script. Поэтому Вы должны предусмотреть обработку ошибок в тех местах, где вызываются методы Run и AddCode.
й Параметры методов в тех объектах, к которым обращаются макросы, должны быть типа Variant.
Как обрабатывать все эти ситуации, я продемонстрирую в следующих разделах на примере программы Editor (рис. 27-16).
: Loyon; itik
F^en Hfcr^^ i^1 «tEu
ClHct'nq ' v^dareEr-o1 , IM erliy Vf
Jrseyraied l'. v«<jaWS9i>: Chetkrigfor IFieVersior FieVertion IrdaladOL I.engL^ecI On Welcome On EULA pa Version of Ar RivV^™ - Fife Ven rri • Fib Version
StMceil nTtin flelw
::9iNdFiletM .IStwrS^i
ц^2'^1впР^п|(Р^1^Хррра<Ь»Ч»ч*эт.да* found
PEHQS-^'.iei^geiiE
J ACS ‘ 1 '«Thye! «*. ViJmkrf 4 72 2!« *
' •; ShMciasQl snenel.
j a:ii№* Local IIT I insfal
C:\WINN1 ^y$ierr32\SHELL32DLL version 4 0,1 ЗВ1 4
CAWINN1 \S^wi32\shdwwdll V«mon4.72.21 И 7
«и* SM32dl 0»t№ Setup 0x041В:
tAV/!MttT^B.}'iivTi^\wrnr*Ldll Version 4,7221065 C\WrHHT'S>ih=n324fh*3n.re Vyisicn 4 722106,4 C \WINNT\$ys*em?2\ehdobvw.* Version 4 72 210Б.7
Рис. 27-16 Пршожение-npuMepEditor (Editor.VBP) позволяет писать макросы, модифицирующие текстовые файлы
Добавление объектов и макропроцедур■
Для добавления объектов из своего приложения в элемент управления Script используйте метод AddObjecl. Эти объекты не обязательно должны быть открытыми — с тем же успехом можно добавить закрытые формы и классы. Включенные в элемент Script объекты доступны в коде VBScript по именам, указанным при вызовах метода AddObject. Этот код добавляет в элемент управления Script объект Editor:
Sub LoadObject()
scrVB.AddObject "Editor", medtObject, True
End Sub
Адля добавления макросов, подлежащих выполнению, вызывайте метод AddCode элемента Script, который принимает соответствующий код как строковый аргумент. Макропроцедуры можно добавлять по одной или все сразу. Так, следующий код загружает в элемент Script весь файл Macros.VBS:
Sub LoadNacros()
scrVB.AddCode GetTextC’Nacros. VBS").
End Sub
1 эта функция используется предыдущим кодом для загрузки файла
Private Function GetText(FileName) As String
Dim mfilScript As New Scripting.FileSystemObject
Dim mtexScript As TextStreara
1 создаем текстовый поток из FileSystemObject
Set mtexScript = mfilScript.OpenTextFile(FileName, ForReading, True)■
если файл it/ct или не существует, ничего не читаем
If mtexScript.AtEndOfStream Then
возвращаем пустую строку.
GetText = ""
Else
возвращаем текст из потока в текстовое поле■
GetText = mtexScript.ReadAll .
End If■' "'
1 закрываем текстовый поток .. .
mtexScript. Close■
End Function■, ,
Поскольку элемент Script транслирует код по мере его загрузки, при вызове метода AddCode может возникнуть любая ошибка периода компиляции. Как обрабатывать такие ошибки с помощью объекта Error элемента Script, об этом и пойдет речь в следующем разделе.
Обработка ошибок периода компиляции
Так как пользователь пишет макросы в интерактивном режиме, очень важно позаботиться о каких-то средствах обратной связи на случай, если он допустит ошибку в своем коде. Об ошибках периода компиляции сообщает объект Error элемента управления Script, возвращая в том числе номер строки и столбца в исходном файле, где находится неправильно написанный оператор.
Внутри приложения ошибки периода компиляции происходят, когда метод AddCode загружает макрос с синтаксической ошибкой. Вот пример кода, который проверяет макрос и, обнаружив любую ошибку, вызывает процедуру MacroError;
Private Sub UpdateMacros()
' выгружаем все объекты и макросы
scrVB.Reset
' добавляем объект Editor к элементу Script
scrVB.AddObject "Editor", medtObject, True
' контролируем ошибки при синтаксическом разборе кода
' (в момент его загрузки)
On Error Resume Next
' добавляем новый код в элемент Script1
scrVB.AddCode medtObject.GetText(mstrMacroFile)
' обработчик ошибок вызывается при возникновении
' любой ошибки периода компиляции
If Err Then НасгоЕггог
' включаем макросы в список меню
UpdateMenus
End Sub
Sub NacroErrorO
- используется объект Error элемента Script
With scrVB. Error
' создаем новый экземпляр этой формы■
Dim f гюМасго As New frmEdit■
' устанавливаем свойство MacroMode
f rmMacro.NacroMode = True
' показываем как немодальную frmMacro.Show vbHodeless, Me ' перемещаем курсор на строку, где произошла ошибка frmMacro.medtObject. MoveDown .Line
' перемещаем курсор в столбец, где произошла ошибка
frmMacro.medtObject.MoveRight .Column
показываем информацию об ошибке
MsgBox Join(Array(.Source, .Description! vhCrif), vbCritical
' скрываем форму, чтобы вывести ее потом как модальную frmMacro.Hide
' показываем форму как модальную - код приостанавливается
* до тех пор, пока пользователь не исправит макрос
' frmWacrO.Show vbModal, Me
' модифицируем СПИСОК макросов после исправлений
UpdateMacros
End With
End Sub
В этом коде много чего происходит. И прежде всего, из элемента Script — вызовом метода Reset — выгружаются любой предыдущий код и объекты. Далее загружается объект Editor, вслед за ним — макрос. Любая ошибка при компиляции активизирует процедуру MacroError, которая показывает содержимое файла с этим макросом в другом окне редактора.
В приложении Editor для редактирования макросов и обычных текстовых файлов применяется одна и та же форма. Методы MoveDown и MoveRight, равно как и остальные методы, доступные из макросов, определены в модуле класса Editor. Перемещая курсор в позицию, где при компиляции произошла ошибка, эти методы используют значения свойств Line и Column, предоставляемых объектом Error элемента Script. Пример того, как выглядит форма при возникновении ошибки компиляции, показан на рис. 27-17.
еггг.1
; Ё-5 jsei a deittitfe щг таг r'i
' Sifc HandtedAwnTirneEni ।д'
' ОпЕпогИМЯНв^М
Inline 1/0'
Sub ,
ErdSub^:TOFptwaoMtpi
'Caicesa de iberete щпФте etn ;$£->>л, , </ ' Sub flunTimeEirfl
I nimily = 1 it i EndSub•
'Counis actual lines |iriduding |щемвс$1 - slow.
' Sub CotjntUrwill
Do
■Record starting position
Start * ЕФ^сг Petition
Рис. 27-17 Когда пользователь изменяет макрос, Editor компилирует файл и, если при этом происходит ошибка, . устанавливает курсор в соответствующую позицию в тексте макроса
Приложение Editor показывает окно редактирования макроса сначала как немодальное — чтобы методы MoveDown и MoveRight могли установить курсор в нужную позицию. Потом Editor скрывает окно и вновь выводит его, но уже как модальное, и выполнение процедуры МасгоЕггог приостанавливается до тех пор, пока пользователь не исправит ошибку и не закроет это окно.
Выполнение макросов«।■
Для перебора списка загруженных макросов используйте набор Procedures элемента управления Script. Он позволяет получить имя, число аргументов и информацию о возвращаемом значении каждой процедуры типа Sub или Function. Например, чтобы добавить имена макросов в меню Macros приложения Editor:
1 выгружает все имена из меню Macros и считывает их
' из набора Procedures элемента Script
Private Sub UpdateMenus()
Dim proItem As Procedure
Dim intCount As Integer
' выгружаем элементы меню Macros
For IntCount = 1 To mnuProcedures.UBound
Unload mnuProcedures(intCount)
Next IntCount
IntCount = 0■ - ■
' добавляем имена макросов в меню Macros
For Each proItem In scrVB.Procedures
IntCount = IntCount + 1
Load mnuProcedures(intCount)
mnuProcedures(intCount). Caption = proItem. Name
Next proItem
End Sub"
Чтобы выполнить макрос, вызовите метод Run элемента Script. Этот метод принимает имя макроса и его аргументы и возвращает результат, если макрос написан в виде функции. Приложение Editor не имеет никаких дел с аргументами и возвращаемыми значениями — оно просто запускает макрос, как показано ниже.
Private Sub mnuProcedures_Click(lndexAs Integer)
' включаем обработку ошибок
On Error Resume Next-
' запускаем выбранный макрос
scrVB.Run mnuProcedures(Index).Caption "
' если возникает ошибка, вызываем MacroError
If Err Then MacroError mnuProcedures(Index).Caption
End Sub
Пока работает метод Run, в макросе могут произойти любые ошибки периода выполнения. Рассмотрим, как обрабатываются такие ошибки.■
Обработка ошибок периода выполнения
Большинство ошибок периода выполнения, возникающих в макросе, возвращает полную информацию — точно так же, как и ошибки периода компиляции. Но в отдельных случаях элемент управления Script оказывается не в состоянии оценить, что произошло. Поэто-
му Вы должны всегда передавать обработчику ошибок имя макроса, чтобы отображать хоть какую-то информацию об ошибке.
Следующий код представляет собой расширенную процедуру MacroError, о которой мы уже говорили в разделе «Обработка ошибок периода компиляции». Изменения, внесенные в hi..'. позволяют выводить на экран имя процедуры, в которой возникла ошибка Автоматизации.
Sub Мас гоы г о r(Optlonal Procedure As String = "”)
' используется объект Error элемента Script
With scrVB.Error
' создаем новый экземпляр этой формы
Dim frm Macro As NewfrmEdit
’ устанавливаем свойство MacroHode frmMacro.MacroMode = True
' показываем как немодальную
' f rmMacro. Show vbModeless, Me'
' если код ошибки равен 0, то это ошибка Автоматизации
If .Number = 0 Then.
' если это ошибка Автоматизации, показываем
' процедуру, вызвавшую ошибку
' If Err Then
'MsgBox "The procedure & Procedure & _'
" caused an Automation error.", vbCritical End If
' в ином случае имеется более специфическая информация
' об ошибке, и можно показать строку, где она возникла Else
' перемещаем курсор на строку, где произошла ошибка
frmMacro.medtObject.MoveDown .Line
' перемещаем курсор в столбец, где произошла ошибка
frmMacro.medtObject.MoveRight .Column,
‘' показываем информацию об ошибке
MsgBox Join(Array(.Source, .Description), vbCrlf), vbCritical End If
' скрываем форму, чтобы вывести ее потом как модальную frmMacro.Hide
' показываем форму как модальную - код приостанавливается
' до тех пор, пока пользователь не исправит макрос f rmMacro.Show vbModal, Me
" модифицируем список макросов после исправлений
UpdateMacros-
End With
End Sub:■
Передача строк в методы
В макросах используются переменные одного типа — Variant. Учтите это, создавая объекты, рассчитанные на применение в макро-
сах. Так, показанный ниже метод приведет к ошибке Автоматизации, если Вы попытаетесь передать ему переменную из макроса.
1 Метод Find ищет строку и, найдя, выделяет ее в текущем тексте,
* ВОЛИ строка найдена, возвращает True, а если ее нет - False
Public Function Finatnna iext As String) As Boolean
Dim IngFound As Long
IngFound = InStr(Position + 1, mfrmParent.txtSource, FlndText)
' если какой-то фрагмент уже выделен,
' поиск ограничивается его пределами.
If mfrmParent.txtSource.SelLength Then
If IngFound > mfrmParent.txtSource.SelStart + _ hif rmParent.txtSource.SelLength Then Find = False
Exit Function
End If
Elself IngFound <> 0 Then
mfrmParent.txtSource.SelStart = IngFound - 1 mf rmParent.txtSource.SelLength = Len(FindText) Find = True
Else
Find = False1
End If
End Function
Тонкость здесь в том, что проблема возникает лишь с переменными, но не с литеральными значениями. Например, первый вызов метода Find сработает, второй — даст ошибку:
' FindTest
Sub FindTest()
Findstring = "Howdy"
Editor.Find "Howdy" 1 все пройдет нормально
Editor,Find Findstring 1 возникнет ошибка Автоматизации!
End Sub
Чтобы устранить эту проблему, уберите из объявления параметра метода Find ключевые слова As String:
Public Function Find(FindText) As Boolean
Создание макросов
Написание макросов на VBScript для выполнения элементом Script несколько отличается от написания обычного кода на Visual Basic. В частности, VBScript не поддерживает символ продолжения строки или явные типы данных. Нет в нем и привычных в Visual Basic функций для операций с файлами и каталогами. В общем, переключение с Visual Basic на VBScript (вроде бы близких друг другу) может оказаться настоящей головной болью!.
Макрос, показанный ниже, написан с использованием приложения Editor. Он демонстрирует, как в VBScript-коде в данном случае через свойства и методы объекта Editor — реализуются перечисление файлов, их модификация и обработка ошибок.
' Macros.VBS
' подсчитывает количество строк (вместе с переносами)
Sub CounttinesO,
Do■""
' запоминаем стартовую позицию
Start = Editoi .гиашии■ "
смещаем курсор вниз на 1 строку
Editor.MoveDown
отслеживаем текущее число строкС
NumLines = NumLines + 1
1 повторяем до возврата в начало
Loop Until Editor.Position = Start'
' показываем резулвтат'
MsgBox Editor.FileName & " has " & NumLines 4 11 lines." ■ End Sub-
i ■
заменяет табулятор на 4 пробела. ■
Sub TabsToSpaces()" ,
‘ заменяем все табуляторн группами из 4 пробелов
Editor.ReplaceAll vbTab,:"
End Sub
' внводит список файлов в текущем каталоге■
Sub BuildFixHuioц;
' получаем описок файлов в текущем каталоге
Set CurrentDirFlles = nieList(Editor. directory)
For Each Flleltem In CurrentDirFlles
.. отображаем имя файла
Editor.Insert Flleltem.Name;
' добавляем символы CR/LF-
Editor.Insert vbCrLf
Next
перемещаем курсор на 1 знакоместо назад'
Editor.MoveLeft
удаляем последние символн CR/LF
Editor.Delete
End Sub
1 принудительно генерирует ошибку периода выполнения
Sub HandlBdfiunTimeErr()..
On Error Resume Next.,
Infinity -1/0'.
If Err Then
MsgBox "VBScript error: " & Err.Numbers " " & Err.Description End If
End Sub■
Информацию о языке VBScript см. в VBScript Language Reference на компакт-диске MSDN.
Дорогой Джон, Как...
Передать объекту UDT-структуру?
Visual Basic теперь позволяет передавать массивы и UDT-структуры в свойства объектов и функции (равно как и возвращать их). Передачу массивов мы уже видели на примере класса Loan в главе 5 «Объектно-ориентированное программирование». А сейчас посмотрим, как передавать UDT-структуры.
Откройте новый проект ActiveX DLL и установите свойство Name модуля класса какMidPoint. Введите код, показанный ниже, и сохраните этот модуль под именем MIDPOINT.CLS,
' MIDPOINT.CIS■
Option Explicit.
Public Type typecoordinate
X As Double
Y As Double..
lEnd Type
запоминаем последние две точки
Private mcoordOne As typecoordinate
Private mcuuiurww As typecoordinate
, XY (свойство только для записи).
Property Let XY(coordTest As typecoordinate)
' начальная точка
mcoordTwo = mcoordOne. Li .
конечная точка.
mcoordOne = coordTest
End Property■
• XYMid (свойство только для чтения)
Property Get XYMid() As typecoordinate
возвращаем среднюю точку
XYMid.X = (mcoordOne.X+ mcoordTwo.X) / 2
XYMid.Y = (mcoordOne.Y+ mcoordTwo.Y) / 2
End Property
Обратите внимание на объявление открытого типа, где определяется UDT-структура typeCoordinate. Этот тип будет у всех переменных, передаваемых свойствам объекта Midpoint и возвращаемых из них. Стремясь не усложнять пример, я определил одно свойство (XY) только для записи, которое передает координаты точки объекту MidPoint, и одно свойство (XYMid) только для чтения, которое возвращает координаты точки, находящейся посередине между двумя ранее указанными точками.
Для проверки объекта MidPoint создайте группу проектов, добавив проект стандартной КХЕ-программы к существующему. Для этого выберите из меню File команду Add Project, а в появившемся диалоговом окне — значок Standard EXE. В окне Project Explorer щелкните правой кнопкой мыши имя проекта стандартной ЕХЕ-программы и выберите из контекстного меню команду Set As Start Up. Переименуйте Form l этого проекта в jrmPobii,добавьте в нее приведенный ниже код, запишите в ее свойство Caption строку Please Click on This Form и сохраните форму под именем POINT.FRM.
' POINT.FRM
Option Explicit
Private Sub Form_Click()' .
1 создаем UDT-переменную
Dim coordTest As typecoordinate ' создаем объект"
Dim midpointTest As New MidPoint
’ передаем объекту координаты первой точки
coordTest.X = 3
coordTest.Y = 4
midpointTest.XY = coordTest
1 передаем объекту координаты второй точки
coordTest.X = 7■ $
coordTast.Y = 8'
midpointTest.XY = coordTest
получаем от объекта координаты средней точки coordTest = midpointTest.XYMid Print "Midpoint coordinate is ";
Print coordTest.X
Print ". ";
Print coordTest.Y End Sub
Чтобы эта программа могла работать, в проекте стандартной ЕХЕ-программы надо установить ссылку на проект ActiveX DLL. Поэтому в окне Project Explorer укажите имя проекта стандартной ЕХЕ-программы и выберите из меню Project команду References. В одноименном диалоговом окне пометьте флажок в строке с названием проекта ActiveX DLL и щелкните кнопку'ОК.
После запуска тестового приложения и щелчка его формы создается экземпляр объекта MidPoint UnidpomtTest^ \\ UDT-переменная coordTest. В поля X и Y переменной coordTest вводится первая пара координат и передается свойству XY MidPoint-объекта midpointTest. Объект запоминает эти координаты, и мы точно так же передаем ему вторую пару координат.
ПОТ-структура с координатами средней точки, возвращенная свойством XYMid, присваивается нашей переменной coordTest, после чего программа отображает на форме полученные результаты (рис. 27-18).
^ PH-МС Civ* № 1hn l-«i^ PH-МС Civ* № 1hn l-«iffieHecfdwwkS. в?-ffieHecfdwwkS. в?-
^ PH-МС Civ* № 1hn l-«i
^ PH-МС Civ* № 1hn l-«i
ffieHecfdwwkS. в?-
ffieHecfdwwkS. в?-
Рис. 27-18 Координаты средней точки, полученные с помощью объекта MidPoint
UDT-структура, передаваемая свойствам объекта, в этом примере очень проста и состоит всего лишь из двух чисел, соответствующих Х,У-коордпнагим. По-настоящему Вы оцените этот прием, когда понадобится передавать куда более сложные UDT-структуры.
ГЛАВА 28
Другие приемы
В этой главе мы рассмотрим несколько приемов, которые трудно отнести к чему-то определенному — разве что ко «всякой всячине», — и тем не менее они полезны любому, кто программирует на Visual Basic.
Раньше я часто слышал жалобы на то. что в Бейсике нет указателей и структур данных, без которых не обойтись ни одному квалифицированному программисту на С. Но я уже объяснял, как эффективно структурировать данные с помощью Variant-типов, а теперь покажу, что объект Collection позволяет создавать динамические структуры, допускающие весьма изощренные операции над данными. И для примера продемонстрирую создание эквивалента связанного списка (linked list).
В следующих двух разделах мы обсудим, как идентифицировать операционную систему и как программно перезагрузить компьютер. Эти приемы могут пригодиться при установке или инициализации приложений.
Четвертый раздел посвящен программному набору телефонного номера с помощью команд, посылаемых модему через последовательный порт. Эта распространенная задача решается на удивление просто.
И в заключение я представлю обобщенную методику строчного перехвата ошибок (inline error trapping) — она эффективнее той, которая строится на операторе On Error GoTo. .
Создать связанный список? ■
Связанные списки, позволяющие создавать и переупорядочивать последовательные наборы данных, а также управлять ими, — эффективное средство, часто применяемое в программировании на С. Создавая связанный список структур данных в С, Вы используете переменные-указатели, хранящие адреса элементов этого списка. В Visual Basic нетявныхуказателей, зато есть несколько специфических способов обработки ссылок на объекты и переменные. При этом внутренне Visual Basic, конечно, отслеживает необходимые указатели, но Вам нет нужды беспокоиться о таких деталях — Вы можете сосредоточиться на самих концепциях обработки данных.
Попробуем создать эквивалент связанного списка строк, который позволит вставлять новые элементы, не нарушая их алфавитного порядка. Эта задача очень эффективно решается с помощью объекта Collection. Откройте новый проект, добавьте на пустую форму кнопку cmdBuildList^ введите в модуль формы следующий код.
Option Explicit'
Private coiWords As New Collection
Sub Insert(VAs Variant).
Dim 1 As Variant, j As Variant, k As Variant
определяем, не добавляется ли самый первый элемент
If coiWords.Count = 0 Then,
coiWords.Add V
Exit Sub'
Bad If
* получаем диапазон набора
= 1
] = coiWords.Count
определяем, надо ли вставить этот элемент перед первым
If V <= coiWords.Item(i) Then
coiWords.Add V, before:=1
Exit Sub
Bad If
' определяем, надо ли вставить этот элемент за последним
If V >= coiWords. Item(j) Then
coiWords.Add V, after:=J
Exit Subr.
End If
двоичный поиск точки вставки
Do Until J - 1 <= 1
k = (I + J) \ 2‘
If coiWords. Item(k) < V Then
i = k
Else■'
End If k
Loop'x
’ вставляем элемент на свое место1
coiWords.Add V, before: =j
End Sub
Private Sub cmdMldList_Click()
Dim i As Integer.
Insert "One"
Insert "Two"
Insert "Three"'
Insert "Four"
Insert "Five"
Insert "Six"
Insert "Seven"’
Insert "Eight"r
Insert "Nine"T
Insert "Ten"
For i = 1 To coIWords.Count
Print coIWords.Item(i)
Next i
End Sub
Я объявил Collection-объект colWords на уровне модуля, чтобы он существовал все то время, пока выполняется программа. Если бы я объявил объект Collection в какой-нибудь процедуре, то, как и любые другие локальные переменные, он был бы уничтожен при ее завершении. Объявление объекта на уровне модуля гарантирует его существование до выгрузки соответствующей формы. Кроме того, один и тот же набор обычно обрабатывается несколькими процедурами, а объект, объявленный на уровне модуля, доступен всем процедурам в этом модуле.
Объекты Collection могут содержать элементы двух типов: Object и Variant. Но, конечноже, Variant-переменные позволяютвключать данные самых разных типов, и поэтому в действительности объект Collection может хранить практически все, что угодно. В нашем примере строки передаются и обрабатываются как Variant-переменные. Как создать класс — оболочку объекта Collection, чтобы улучшить контроль за типами данных, включаемых в набор, подробно объясняется в Visual Basic Books Online. Это может оказаться важным, если, например, Вы используете объект Collection внутри ActiveX-компонента и данный объект должен содержать лишь один тип данных. Так, в коде, приведенном выше, ничто не мешает передать в процедуру Insert вместо строк данные какого-нибудь другого типа, хоть это и бессмысленно в контексте того, что делает моя программа.
К элементам набора можно обращаться напрямую по ключевой строке или по индексу, отражающему позицию элемента в списке. В данном случае я применяю индексы: это позволяет вставлять и удалять элементы, а также реализовать почти все операции, обычно выполняемые над настоящими связанными списками. Объект Collection в Visual Basic — подобно связанному списку и в противоположность массиву — обеспечивает эффективную вставку или удаление любых элементов, не расходуя лишней памяти.
Для вставки строки я вызываю метод Add объекта Collection, Два именованных аргумента этого метода (before и after) позволяют вставлять строку до или после позиции, указанной в индексе. Чтобы вставлять строки в алфавитном порядке, я использую оба аргумента. Тогда список при последовательном доступе по индексу объекта Collection автоматически, без дополнительной обработки
возвряш,чет строки по алфавиту. Рис. 28-1 иллюстрирует результаты вывода на форму отсортированного содержимого объекта Collection.
Рис. 28-1 Результаты упорядоченной вставки строк в объект Collection — связанный список
Обязательно изучите в Visual Basic Books Online всю информацию об объекте Collection. Эта очень мощная и гибкая конструкция - особенно в сочетании с типами данных Variant и объектами модулей классов — освободит Вас от многих ограничений Бейсика и позволит использовать приемы, ранее попросту немыслимые.
Дорогой Джон, кам... 1
Определить «разрядность» операционной системы?
Живи мы в сказочном мире — наши компьютеры модернизировались бы по мановению волшебной палочки, все программы были бы на 100% совместимы и каждый купил бы по два экземпляра этой книги. Увы, еще полно машин с 16-разрядными версиями Windows и даже моя мама раскошелилась лишь на один экземпляр.
Существует несколько стратегий поддержки приложений, которые должны работать как в 1о .так и в 32-разрядных операционных системах. Лучшая из них, на мой взгляд, такова: заморозьте разработку 16-разрядных приложений, перенесите код на 32-раз- рядную платформу и в дальнейшем развивайте только их 32-разряд- ные версии. Поддержка эквивалентных версий приложения и для 11 ■ и для 32-разрядных операционных систем либо пойдет во вред 32-разрядной версии, либо удвоит нагрузку на Вас.
Ну ладно, шутки в сторону. Рассмотрим, как отличить 16-разряд- ную систему от 32-разрядной. Для этого придется написать специальную 16-разрядную программу на Visual Basic версии 4 (или ниже), так как Visual Basic более поздних версий не поддерживает 16-разрядные среды. Вот пример, в котором программа-заглушка определяет «разрядность» текущей операционной системы и в зависимости от результатов запускает либо 1G-, либо 32-разрядную программу установки:
' LAUNCH.BAS
Option Explicit
456 ЧАСТЫГ ДОРОГОЙ ДЖОН. КАК?..
Slf Winl6 Then
Declare Function GetVersion Lib "Kernel" () As Long
#Else
Declare Function GetVersion Lib ‘’Kernel32,T {) As Long
#End If
1 процедура демонстрирует, как получить информацию
о версии системы в 16- и 32-разрядной Windows
SubMainO
Dim IngWinlnfo As Long
Dim strWInVer As String
Dim strDosVersion As String
получаем информацию о версии Windows
.IngWinlnfo = GetVersion()J
1 выделяем номер версии Windows из полученного
длинного целого значения
strWinVer = Lo0yte(LoWora(lngwininro)) & ’."&_
HiByte(LoWord(IngWinlnfo))
если номер версии меньше 3.5 (Win NT 3.5),,.
If Val(strWinVei < 3.5 Then
■Shell "Setupl.EXE" 1 запускаем 16-разрядную Setup
Else1 в ином случае
Shell ”Setup 132.EXE"’ затаскаем 32-разрядную Setup
End If
End Sub
Function LoWord(lngArg)
LoWord = IngArg And (IngArg Xor &HFFFFOOOO)
End Function
Function HiWord(lngArg)
If IngArg > &H7FFFFFFF Then-
HiWard = (IngArg And AHFFFFQQQQ) \ &H1OQOQ
Else
HiWord = ((IngArgAnd &HFFFFOOOO) \ AH10000) Xor &HFFFF0000 ’
Bad If
End Function
Function HiByte(intArg)*
HiByte = (intArg And &HFF00) \ &H100
End Function
Function LoByte(intArg)
LoByte = intArg Xor (intArg And 4HFF00)
End Function
Обратите внимание на директивы выбора версии API-функции GetVersion. Они позволяют разрабатывать и отлаживать программу на любой платформе. Но, разумеется, конечную версию программы надо компилировать с применением 16-разрядного Visual Basic.
ПРИМЕЧАНИЕ Чтобы запустить в 32-разрядной системе приложение, скомпилированное с помощью 16-разрядного Visual Basic, установите в ней исполняющую DLL этой версии Visual Basic — VB40016.DLL.
Дорогой Джон* ^C^SSCeie,
Перезапустить Windows? ^ ♦
Вряд ли Вы будете часто перезапускать Windows 95 или Windows NT из своих программ, но в некоторых случаях такое умение может пригодиться — например, чтобы воспрепятствовать попыткам нарушения защиты. Программы установки некоторых специализированных приложений перезапускают Windows для обновления текущих переменных окружения и параметров реестра. Вызвать перезапуск из программного гющ! несложно — только перед тем, как проверять следующий код, сохраните все открытые файлы и в том числе этот проект!
Добавьте на форму кнопку cmdRestart. введите код, показанный ниже, сохраните форму и, запустив программу, щелкните кнопку.
Option Explicit■
Const EWX_SHUTDOWN = t
Const EWX_REBOOT = 2
Const EWX_LOGOFF = 0
Const EWX„FORCE = 4
Private Declare Function ExitWindowsEx Lib "user32"
(ByVal uFlags As Long, ByVal dwReserved As Long) As Long
Private Sub cmdRestart^CllckO*
’ перезапускаем Windows (срабатывает в Windows 95/NT)
ExitWindowsEx EWX_LOGOFF, 0
End Sub
После щелчка кнопки обратного пути уже не будет — система завершает свою работу, и начинается перезагрузка компьютера.
ПРИМЕЧАНИЕ Windows NT требует специальных полномочий от приложения, использующего флаги EWX SHUTDOWN, EWX_REBOOT или EWX FORCE. Об установке таких полномочий для приложения см. описание функции AdjustToken- Privileges в документации Windows API.
Дорогой Джон, как...
Набрать телефонный номер из программы?
Очень простой в обращении элемент управления Microsoft Comm покрывает все потребности программ в последовательной коммуникационной связи. Одна из самых частых задач в программировании такой связи — элементарный набор телефонного номера. Приложение NISTTime в главе 31 «Дата и время» демонстрирует более изощренное использование элемента Comm, а здесь я при-
веду всего несколько строк кода, необходимых для быстрого набора номера.
Чтобы пример был чуточку полезнее, я предполагаю копирование нужного номера в буфер обмена, Это позволяет передать номер из любого приложения, Итак, создайте новый проект, Если элемент управления Comm недоступен. выберите из меню Project команду Components и в одноименном диалоговом окне на вкладке Controls пометьте флажок в строке Microsoft Comm Control 6,0, Вставьте на пустую форму элемент Comm и присвойте ему имя солиОпе; кроме того, добавьте кнопку cmdDial Затем введите код: Option Explicit
Private Sub cmdDial_Click()
Dim strA As String
strA = Clipboard.Get7ext(vbCFText)' ,
If strA = "" Then
MsgBox "Mark and copy a number first."
Exit Sub
End If<
oomOne,CommPort = 1
comOne.Settings = "9600,N,8,Г
comOne.PortOpen = True
comOne.Output = ”ATDT" & strA & vbCr
MsgBox "Dialing " & strA & vbCrLf 4 "Pick up the phone.,.", vbOKOnly, "Dial-A-Phone"
comOne. PortOpen = False
End Sub
Рис, 28-2 иллюстрирует, как работает эта программа,
Если Ваш модем подключен к порту COM2 вместо ( ОМ 1. измените значение свойства CommPort на 2 В отдельных, очень редких случаях, может быть, придется модифицировать значения и других свойств — все зависит от конфигурации Вашего компьютера, Но, как правило, исходные значения свойств Вас устроят,
Рис, 28-2Программа для набора номера ждет,
когда пользователь возьмет трубку
Команда MsgBox приостанавливает программу перед тем, как присвоить свойству PortOpen значение False. Когда пользователь
щелкнет кнопку ОК в окне сообщения, элемент управления Comm «положит трубку». Это дает время взять ее до того, как программа возобновит свою работу и разъединит линию.
См. также...
•Приложение NZSTTime в главе 31 «Дата и время», где элемент уп
равления Comm используется более изощренно.
*+^+- -—- **т*. —- ^Ih.ы jMI.
Д.£7 ^.^ч л
Использовать строчный перехват ошибок?
Обычно в программах, написанных на Visual Basic, код обработки ошибок выделяется в отдельный блок, вход в который обозначается какой-нибудь меткой, а в самом начале процедуры ставится оператор On Error GoTo, вызывающий автоматический переход на указанную метку при любой ошибке. Вот простейший пример такой обработки ошибок:
Private Sub cmdTest„Click()
On Error GoTo ErrorTrap
Print 17/0 ’ математическая ошибка.
Exit Sub
ErrorTrap:
Print "Illegal to divide by zero."
Resume Next■
End Sub
Однако Вы, возможно, предпочтете другой подход к обработке ошибок — строчный, И вот почему. Прежде всего, метки — наследие древних операторов GOTO, превращавших код в спутанный клубок спагетти. Современные методы структурного программирования, которые в основном используются в Visual Basic, предписывают воздерживаться от скачкообразных переходов в коде, а операторы GOTO как раз и приводят к таким скачкам. Не спорю, простые операторы ветвления но ошибке не настолько запутывают программу, как многие другие устаревшие элементы Бейсика, но все равно их работа не слишком очевидна в сравнении с альтернативными методами. Что разочаровывает еще больше (особенно, когда имеешь дело с объектами), так это невозможность выявить, где именно произошла ошибка и какова ее причина. И хотя прежняя переменная Err теперь заменена объектом Err с целым набором свойств, в больших процедурах предоставляемой информации все равно недостаточно.
Строчный перехват ошибок
В программировании на С принято перехватывать ошибки проверкой возвращаемой информации сразу после вызова каждой функ-
ции. Лучший пример такой методики в Visual Basic — проверка ошибок после вызова API-функций. Скажем, вызвав API-функцию mciExecute для воспроизведения звукового файла, можно проверить возвращаемое значение и узнать, был ли ее вызов успешным:
х = mciExecutaCPlay с:\wlndows\tada.wav")
If х = 0 Then MsgBox "There was a problem."
Visual Basic тоже позволяет проверять ошибки отдельно в каждой строке, где они возможны, — я предпочитаю именно этот подход. < hr кстати, рекомендуется и в документации Microsoft при работе с объектами. Весь фокус Б том, что в начале процедуры Вы ставите оператор On Error Resume Next и проверяете ошибки в каждой строке, где могут возникнуть ошибки. Вот подпрограмма, демонстрирующая такой подход:
Option Explicit
Private Sub cmdTest_Cllck()
Dim vntX As Variant, vntY As Variant
For vntX = -3 To 3
vntY = Reciprocal(vntX)
Print "Reciprocal of 11; vntX: " is "; vntY; ""
If IsError(vntY) Then Print Err.Description
Next vntX
End Sub
Function ReclprocaKvntX As Variant)
Dim vntY As Variant
On Error Resume Next
vntY = 1 / vntXh '
If Err.Number = 0 Then
Reciprocal = vntY\
Else
Reciprocal = CVErr(Err,Number)
Bad If
End Function
Здесь я воспользовался тем преимуществом, что Variant-переменная способна хранить значение типа Error, записываемое в нее с помощью функции CVErr. Благодаря этому появляется возможность уведомить вызывающую процедуру об ошибке. Конечно, можно просто возвращать из функции определенное значение, указывающее на ошибку, но неявное уведомление все же лучше, так как некоторые функции могут возвращать любое числовое значение.
Взгляните на рис. 28-3> где показаны результаты запуска этой программы. Заметьте, что она выводит возвращаемое функцией Variant-значение fvn/19 даже при ошибке. В последнем случае вместо числового значения появляется соответствующее сообщение.
* Inin» Lnot Plbuuii^
^tiwiocsi^i * $ ■1
'гЙ»е1р™н|^'£& &5
#|>cii^ tus^MEZH
feel" 4.
iПЯ.
Рис. 28-3 Строчный перехват ошибок позволяет обнаружить деление на нуль и сообщить об этом
И в заключение несколько слов об объекте Err. У него имеется ряд свойств и методов, расширяющих возможности обработчиков ошибок по сравнению с прежней переменной Err. Так, метод Raise позволяет генерировать собственные ошибки, что очень удобно для их передачи из создаваемых Вами объектов. Советую прочесть в документации Visual Basic все, что относится к этому весьма важному объекту.
ПРИЛОЖЕНИЯ- ПРИМЕРЫ
16-1204
Примеры в этой части книги — полноценные автономные приложения, которые демонстрируют многие решения, описанные в предыдущих главах, а также ряд других трюков и приемов. Какие-то из приведенных приложений, уверен, пригодятся Вам в работе, другие послужат хорошим учебным материалом, а третьи просто позабавят. Надеюсь, изучение этих примеров натолкнет Вас на новые идеи и подходы в разработке своих приложений на Visual Basic. Я стремился охватить самые разные типы приложений, и, каковы бы ни были Ваши интересы, Вы наверняка найдете здесь хоть несколько интересных программ. Все формы и исходный код каждого приложения содержатся на компакт-диске, прилагаемом к книге.
Графика
В этой главе представлено несколько интересных приложений, которые демонстрируют некоторые приемы и особенности работы с графикой, рассмотренные в части II. Утилита HSVHSL весьма удобна для выбора цветов. Приложение Animate иллюстрирует несколько способов создания простых анимаций на формах Visual Basic. Приложение Lottery, хоть и не гарантирует выигрыша в миллион долларов, позволяет поиграть с графикой. Наконец, MySaver — полнофункциональная экранная заставка, обеспечивающая настройку множества параметров.
Приложение HSVHSL
HSVHSL — простая утилита, помогающая выбрать любой цвет в системе RGB, НЗУили HSL. Для преобразования значений цвета из одной системы в другую в проект добавлены модули классов HSV.CLS и HSL.CLS. на основе которых создаются экземпляры объектов HSVи HSL (см. главу 14 «Врафические методы»).
Как и многие другие программы, представленные в части III, HSVHSL предусматривает меню, Большая часть элементов меню отключена, так как для них не написано никакого кода — если хотите, добавьте его. Но все элементы меню Help активны; они обеспечивают доступ к стандартному диалоговому окну About и справочному файлу, созданному мной для этого набора программ. (О диалоговых окнах About я рассказывал в главе I 2 «Окна, диалоговые окна и прочие формы» ) Заметьте, что в исходном коде указан относительный путь к справочному файлу. Это позволяет находить справочный файл независимо от конкретного буквенного обозначения привода CD-ROM и даже в том случае, если Бы скопировали содержимое компакт-диска на жесткий диск (не нарушив исходной структуры каталогов).
Одна строка кода может показаться несколько странной. В метке 1ЫСок«\ которая расположена над окном рисунка, отображающим текущий цвет, выводится шестнадцатеричное значение этого цвета (см. процедуру Update). Стандартная для Visual Basic нотация шестнадцатеричных чисел требует префикса &Н, а в программной строке указаны два амперсанда подряд:
IblColor = "Color = " 4 "&&Н” 4 Hex$(RGBColor)
Дело в том, что один амперсанд перед любой буквой заставит элемент управления Label подчеркнуть ее. Эта возможность полезна, когда она действительно нужна, но в данном случае требуется показать сам амперсанд. Поставив два амперсанда подряд, Вы сообщаете Visual Basic, что на экран следует вывести один амперсанд, а не подчеркнутый символ.
Рис. 29-1 иллюстрирует приложение HSVHSL в действии. При перемещении любого ; - >.hi-.i HSVHSL отображает в окне рисунка выбранный цвет и программно подстраивает позиции остальных ползунков. На рис. 29-2 показано окно Project со списком всех форм и модулей, из которых состоит это приложение, а на рис. 29-3 - форма HSVHSL на этапе разработки. Объекты на форме обозначены числами в соответствии с таблицей «Объекты HSVHSL.FRM и значения их свойств».
Рис. 29-1 Приложение HSVHSL в действии
—^'JW^IkaSIBM "
= Q About (About frm),
- ci fmHSVHSL(H5VHSL.ffm) i
= -£3 dsss Modute4
' ® ня. (иа.сЬ)■
^iHSV(!«V,ds)1
Рис. 29-2 Список файлов в проекте HSVHSL
Создавая это приложение, используйте таблицы и исходный код, приведенные ниже. Чтобы облегчить изучение исходного кода, я включил листинги модуля формы About и модулей классов HSV и
HSL, но подробное их описание см. соответственно в главе 12 «Окна, диалоговые = 'кпд и прочие формы» и главе 14 «Графические методы».
Меню и их элементы в HSVHSL.FRM
Название
Имя
Уровень вложения
Параметр Enabled
«File
mnuFile
0
True
«New
mnuNeie
1
false
«Ореп„.
mnuOpen
1
False
«Save
mnuSave
1
False
Save «As™
mnuSaveAs
1
false
—
mnuFileDasbl
\
True
E«xit
mnuExit
1
True
«-Help
mnuHelp
0
True
«Contents
mnuConients
1
True
«SearcbforHelp on...
mnuSearcb
1
True
—
mnuHelpDasbi
1
True
«About...
mnuAboui
1
True
Рис. 29-3 HSVHSLFRMna этапе разработки
Объекты HSVHSL.FRM и значения их свойств
Номер объекта1
Свойство
Значение
Slider
1
Name
sliRGB
Index
0
Max
255
LargeChange
10
Slider
2
Name
sliRGB
Index
1
Max
255.
LargeChange
10
Slider
3
Name-
SliRGB
Index
2
Max
255-
LargeChange
10
Slider
4
Name
sliHSV
"Index
0
Max
359
Slider
5
Name
sliHSV
Index
1
Max
100
Slider
6
Name
sliHSV
Index
2--
Max
100
Slider
7
Name
SliHSL
Index
0
Max
239
Slider
8
Name
sliHSL.
Index
1■
Max
240
Номер, указанный в первой колонке, соответствует порядковому номеру объекта । и форме (рис- 29-3).
Объекты HSVHSL.FRM и значения их свойств (продалмемге)
Номер объекта
Свойство
Значение
Slider
9
Name
sliHSL
Index
2
Label
Max
240
Name
Label 1■
Index
0
Label
Caption
Red
Name
Label 1
Index
7
Label
Caption
Green
Name
Label 1
Index
2
Label
Caption
Blue
Name
Label2
Index
0
Label
Caption
Hue
Name
Label2
Index
1
Label
Caption
Saturation
Name
Label2
Index
2'
Label
Caption
Value
Name
LabeB
Index
0
Label
Caption
Hue
Name
Labels
Index
1
Caption
Saturation
Объекты HSVHSLFRM и значения их свойств (продолжение)
Номер объекта
СвойствоЗначение
Label
NameLabeB
Index2.
CaptionLuminosity
Label
NameIblRGB
Index0
Label
NameIblRGB
Index1
Label
NameIblRGB
Index2
Label
NameIblHSV
Index0
Label
NameIblHSV
Index1
Label
NameIblHSV
Index2
Label
NameIblHSL
Indexо
Label
NameIblHSL
Index1 •
Label
NameIblHSL
Index2
Label
Name■IbIColor.
PictureBox
10
NamepicColor
Исходный код HSVHSLFRM
Option Explicit
Private Declare Function WlnHelp _ Lib "userSZ" Allas "WinHelpA" ( _ ByVai hwnd As Long, _ ByVai IpHelpFile As String, _ ByVai «Command As Long, _■
By Vai dwData As Long _.
) As Long
Dim RGDGolor■
Dim hsvDemo As New HSV Dim hslDemo As Hew HSL■
Private Sub Form_Load()
' устанавливаем начальный цвет - серый With hsvDemo
. Red = 127 .Green * 127 '.Blue= 127
. End With'
With hslDemo .Red = 127 .Green = 127.
.Blue * 127 End With Update End Sob■
Private Sub mnuAbout„Click!).
' настраиваем свойства
About.Application = "HSVHSL" About.Heading =
'"Microsoft Visual Basic 6.0 hevelnuer's Workshop"
About.Copyright = “1993 John Clark Craig and Jeff Webb" About.Display
End Sub
Private Sub mnuContents_Click() WlnHelp hwnd, App.Path & "\.,\.,\Help\Mvbdw.hlp", _ allHelpContents, 0
End Sub
Private Sub f fInuExi t_C1i ek() Unload Me End Sub'
продолжение
Private Sub ninuSBarch_Click()
WinHelp hwnd, Арр.Path 4 "\.Л-.\Help\Mvbdw.hip", cdlHelpPartialKey, 0 End Sub
Sub Update() sliRGB(O). Value = hsvDemo.Red S11RGB(1).Value = hsvDemo.Green S11RGB(2). Value = hsvDemo, Blue sllHSV(O).Value = hsvDemo.Hue sliHSV(l). Value = hsvDemo.Saturation sliHSV(2).Value = hsvDemo.Value sliHSL(O).Value = hslDemo.Hue S11HSL(1).Value = hslDemo.Saturation sltHSL(2).Value = hslDemo.Luminosity обновляем метки для системы RGB
lblRG6(O).Caption = Format$(hsvDemo.Red, "##Q") IblRGB(l).Caption = Format$(hsvDemo.Green, "##0") lblRQB(2).Caption = Format$(hsvDemo.Blue, "##0")
обновляем метки для системы HSV
IblHSV(O).Caption = Format$(hsvDe@o.Hue, "B#0")
IblHSVd).Caption = Format!(hsvDemo.Saturation, "##0") lblHSV(2),Caption = Format$(hsvDemo.Value, "##0")
Обновляем метки для системы HSL
IblHSL(O).Caption = Format$(sllHSL(0).Value, "##0") lblHSL(1).Caption = FomtS(sliHSL(1).Value, "##0") lblHSL(2).Caption = Format$(sllHSL(2).Value, "##0") * показываем выбранный цвет
RSBColor = RGB(hsvDemo.Red, hsvDemo.Green, hsvDemo,Blue) picColor.BackColor = RGBColor * обновляем значение цвета
I IbltolOf - "Color = " & "&№' 4 Hex$(RGBColor) End Sub
Private Sub sliHSL_Soroll(Indsx As Integer) hslDemo.Hue = sliHSL(O).Value hslDemo.Saturation = sliHSL(1).Value hslDemo. Luminosity = sliHSL(2).Value hsvDemo. Red = hslDemo. Red ftsvDerao.Green=hslDemo. Green hsvDemo.Blue = hslDemo,Blue
Update
End Sub
Private Sub sliRGB_Scroll(Index As Integer) hsvDemo,Red = sliRGB(O). Value
продолжение
hsvDemo.Green = sliRGB(1).Value hsvDemo,Blue * sliRGB(2).Value hslOemo.Red = hsvDemo,Red ftsIDemo.Green = hsvDemo, Green hslOemo,Blue = hsvDemo.Blue Update End Sub
Private Sub siiKSV^Sc roll (Index As Integer) hsvDemo.Hue = sliHSV(O).Value hsvDemo. Saturation = sliNSV(l).Value hsvDemo.Value = sliHSV(2).Value hslDemo.Red = hsvDemo.Red hslDemo.Green - hsvDemo.Green hslDemo. Blue - hsvDemo.Blue
Update
End Sub
Исходный код HSV.CLS
' HSV.CLS
Option Explicit
' свойства для RGB-цвета Private minified As Integer Private mintGreen As Integer Private mlntBlue As Integer
свойства для HSV-цвета
Private msngHue As Single
Private msng Satu rat ion As Single Private msngValiM As Single
' для управления преобразованиями Private mintCalc As Integer
Private Const RGS2HSV = 1
Private Const HSV2RG0 * 2
' ~""Hua
Property Let HuefintHus As Integer)
msngHue * intHue
mintCalc = HSV2RGB
End Property
Property Get Hue() As Integer
Pt mintCalc = RGB2H3V Then CalcHSV
Hue = msngHue
продолжение
EndProperty.
Saturation
, Property Let Saturation(intSaturatlon As Integer) msngSatu ration = intSatu ration■
rointCalc = HSV2RGB
End Property
Property Get Saturation) As Integer
If mintCalc = RGB2HSV Then CalcHSV..
Saturation = msngSaturation
End Property
""Value,
Property Let Value(intValue As Integer) ■cnnviiue = intValue.
mintCalc = HSV2RGB
End Property
Property Get Value() As Integer
If mintCalc = RGB2HSV Then CalcHSV
Value * msngValue
End Property,
‘ Red
Property Let Red(intRed As Integer) mintRsd - intRed mintCalc = RGB2HSV
End Property■■
Property Get Red() As Integer
If raintCalc = HSV2RGB Then CalcRGB Red = mintRed
End Property
' Green
Property Let Green(intGreen As Integer)
mintGreen = intGreen
mintCalc = RGB2HSV.
End Property
Property Get GreenO As Integer
:If mintCalc = HSV2RGB Then CalcRGB
Green = mintGreen
End Property
1 Blue
Property Let BluefintBlue As Integer)
Ф. mlntBlue = intBlue
Л mintCalc = RGB2HSV.
продолжение
■End Property
Property Get Blue() As Integer
If ffiintCalc - tiSV2RG0 Then CaicRGB
Blue = ■ ■ 'itBlue
End Property
' преобразует RGB в HSV
Private Sub CalcHSV()
Dim sngRed As Single
Dim sngGreen As Single
Dim sngBlue As Single
Dim sngMx As Single'
Dim sngMn As Single■
Dim sngDelta As Single
Dim sngVa As Single
Dim sngSa As Single"
Dim sngRc As Single
Dim sngGc As Single
Din sngBc As Single
sngRed = minified / 255
sngGreen = mintGreen / 255
sngBlue = mintBlue / 255
sngHx = sngRed
If sngGreen > sngMx Then sngHx = sngGreen
If sngBlue > sngMx Then sngMx = sngBlue sngMn = sngRed
If sngGreen < sngMn Then sngMn = sngGreen
If sngBlue < sngMn Г^<4' sngMn = sngBlue sngDelta = sngHx - sng№i
sngVa = sngMx
If sngMx Then
sngSa = sngDelta / sngMx
■ Else■ -
sngSa = 5
End If
If sngSa = о Then
msngHue = 6
• Else
sngRc = (sngMx - sngRed) / sngDelta sngGc = (sngMx- sngGreen) f sngOelta
sngBc = (sngHx - sngBlue) / sngDelta Select Case sngMx
Case sngRed■
nsngHue = sngBc - sngGc
Case sngGreen
msngHue = 2 + sngRc - sngBc
продолжение
Case sngBlue
rasngWue * 4 + sngGc - sngRc
End Select
msngHue = msngHue * 60
If msngHue < 0 Then msngHue - msngHue + 360
End If
msngSaturation = sngSa * 100
msngValue * sngVa * 100
mintCalc » 6
End Sub
' преобразует HSV в ROB
Private Sub CalcRGBQ
Dim sngSaturation As Single
Dim sngValue As Single
Dim sngHue As Single
Dim inti As Integer
Dim sngF As Single
Dim sngP As Single
Dim sngQ As Single
Dim sngT As Single
Dim sngfled As Sinaie
Dim sngGreeii as Single
Dim sngBlue As Single
sngSaturation = msngSaturation / 100
sngValue * msngValue / 100
If msngSaturation = 0 Then
sngfled = sngValue
sngGreen = sngValue
sngBlue = sngValue
Else
sngHue = msngHue / 60
If sngHue и 6 Then sngHue = 0
inti = Int(sngHue)
sngF * sngHue - inti
sngP = sngValue * (11- sngSaturation)
sngQ * sngValue * (It - (sngSaturation • sngF))
sngT = sngValue * (1! - (sngSaturation* (11 - sngF)))
Select Case inti
Case 01■
sngfled = sngValue
sngGreen = sngT
sngBlue = sngP
Case 1
sngfled = sngQ
sngGreen = sngValue
продолженые
sng&Xue = sngP
Casa 2 sngRed = sngP sngGreen = sngValue sngBlue = sngT Case 3 sngRed = sngP sngGreen » sngQ sngBlue = sngValue
.Casa 4■
sngRed = sngT sngGreen = sngP sngBlue = sngValue Case 5
sngRed = sngValue sngGreen = sngP sngBlue = sngQ End Select
End If
eintRed = Int(255.9999 * sngRed) mintGreen = Int(255.9999 * sngGreen) mintBLue = Int(255.9999* sngBlue) mintCalc = 0
End Sub"
Исходный КОД HSLCLS
■ HSL.CLS■
Option Explicit
' свойства для RGB-цвета
' Private mintHed As Integer Private mintGreen As Integer private mintBlue As Integer
' свойства для HSL-цаета Private nsngttue AS Single Private msngSaturation As Single Private msngLuminosity As Single
' для управления преобразованиями Private mintCalc As Integer Private Const RGB2HSL * 1
' Private Const HSL2RGB = 2
. ' Hue
продолжение
Property Let HuefintHue As Integer) msngHue = (intHue / 240!) * 360!
. mintCalc = HSL2RG8.
' End Property
Property Get Hue() As Integer
If mintCalc = RGB2HSL Then CalcHSL
Hue = (msngHue/ 360!) * 240!
End Property
* Saturation
Property Let SaturationfintSaturation As Integer) flisngSaturation = intSaturation / 240!
mintCalc = HSL2RGB
End Property
Property Get Saturation^ As Integer
If mintCalc = RGB2HSL Then CalcHSL
Saturation • msngSaturation * 240!
End Property
' '""Luminosity
Property Let Luminosity(intluminosity As Integer) lesng Luminosity a intLuminosity / 240!
' mintCalc = hslorgb
End Property
Property Get Luminosity() As Integer
If mintCalc = RGB2HSL Then CalcHSL
■ Luminosity = msngiuminosity * 240!
End Property
' ""Red
Property Let Red(intRed As Integer) minified = intRed fflintCalc = RGB2HSL
End Property
Property Get Red() As Integer
If mintCalc = HSL2RGB Then CalcRGB fled = minified
End Property
' ""Green
Property Let Green(intGraen As Integer) mintGreeri = intGreen mintCalc = RGB2HSL
End Property
Property Get GreenQ As Integer
; If mintCalc - HSL2RGB Then CalcRSB
продолжение
Green = mintsreen ■
End Property
' Blue'" : ■
Property Let Blue(intBlue As Integer)
raintBlue = intBlue
mlntCalc = RGB2HSL
End Property
Property Get Bluet) As Integer
If mintCalC = HSL2RGB Then CalcRGB
Blue = aintBlue.
End Property
Private Sub CalcHSLQ'.
Dim sngMx As Si note■
Dim sngMn As Single
Dim sngDelta As Single
Dim sngpctRed As Single■
Dim sngPctGrn As Single
Dia sngPctBlu As Single
sngPctRed = minified / 255
sngPctGrn = mintGreen / 255
SngPctBlu = mintBlue / 255
sngMx = sngMaxOf(sngMaxOf(sngPctRed, sngPctGrn), sngPctBlu) sngMn = sngMinOf(sngMinOf(sngPctRed, sngPctGrn), sfigPctBlu) sngDelta = sngMx - sngMn
msngLumlnosity = (sngMx+ sngMn) / 2
If sngMx = sngMn Then
msngSaturation = 0
Else
msngSaturation = 1
End If
If msngLuminOslty <= 0.5 Then
If msngSaturation > 0 Then
msngSaturation = sngDelta / (sngMx + sngMn)
Bid If
• Else
If fflsngSaturation > 0 Then
.msngSaturation = sngDelta / (2 - sngMx - sngMn)
End If
Bid If
.If mahgSaturation Then
If sngPctRed = sngMx Then
msnnHue = (sngPctGrn - sngPctBlu) / sngDelta
' Bid If
If sngPctGrn = sngMx Then
продолжение
msngHue - 2 + (sngPctBlu - sngPctRed) / sngDelta :End If
If sngPctBlu - sngHx Then msngHue - 4 + (sngPctRed - sngPctGrn) / sngDelta
End If ffisngHue - msngHue * 60■
End If
If msngHue. < 0 Then msngHue = msngHue 360 mintCalc = О
. End Sub
Private Sub CalcRQBO
Dim sngMI As Single■.
Dim sngH2 As Single■
Dim sngPctRed As Single.
Dim sngPctGrn As Single
Dim sngPctBlu As Single
If msngLuminosity <= 0.5 Then sngM2 = msngLuminosity * (1! + msngSaturation)
Else
sngM2 = (msngLuminosity + msngSaturation) _ - (msngLuminosity * msngSaturation)-
End If
sngM1 = 2! * msngLuminosity - sngM2
If msngSaturation = 0! Then sngPctRed = msngLuminosity sngPctGrn * msngLuminosity.
sngPctBlu = msngLuminosity.
Else'
sngPctRed = rgbVal(sngM1. sngM2, msngHue + 1201) sngPctGrn = rgbVal(sngM1, sngM2, msngHue) sngPctBlu * rgbVal(sngM1, sngH2, msngHue - 1201)
End If
minified = Int(255.9999 * sngPctRed)
mintGreen = Int(255,9999 * sngPctGrn)
mintBlue = Int(255.9999 * sngPctBlu)
mintCalc = 0
End Sub •'
Private Function rgbVal(sngN1 As Single, sngN2 As Single, sngHue As Single) As Single
If sngHue > 360 Then sngHue = sngHue - 360
Elsa sngHue < 0 Then■
sngHue = sngHue + 360
End If
продолжение
If sngHue < 80 Then
rgbVal sngN1 + (sngN2 - sngN1) * sngHue / fifl
Elself sngHue < 180 Then
rgbVal = sngN2
Elself sngHue < 240 Then
rgbVal = sngNt * (sngN2 - sngN1) * (240 - sngHue) / 60
Else.
rgbVal - sngNt
End If■
End Function
Private Function sngMax0f(sngV1 As Single, sngV2 As Single) As Single
sngMaxOf = Hf(sngV1 > sngV2, sngV1, sngV2)
End Function
Private Function sngMin0f(sngV1 As Single, sngV2 As Single) As
Single
sngMinOf = Hf(sngV1 < sngV2, sngVt, sngV2)
End Function
Исходный код ABOUT.FRM
. Option Explicit■
Private Sub cmdOK_Click()
' закрываем форму About
. Unload He'
" End Sub
Private Sub Form_Load() ■
центрируем эту форму
Left = (Screen.Width - win+hi \ 2
Top = (Screen.Heigh-; - Weight) \ 2 ' устанавтваем значения по умолчанию
IblAppllcation.Caption = "-Application -”
IblHeadlng.Caption = "- Heading " IblCopyright.Caption = ' Copyright -" Bid sub'
Public Sub DisplayO
■ „' показываем форму как модальную
Show vbHodal
End Sub
Property Lot Application(Application As String)
продвижение
' определяем строку для поля Application IblApplicatlon. Caption = Application
End Property
Property Let Heading(Heading As String)
' определяем строку для поля Heading IblHeading.Caption = Heading
End Property
Property Let Copyright (Copyright As String)
формируем полную строку для поля Copyright IMCopyright. Caption = "Copyright & Copyright
End Property.
Приложение Animate
Б приложении Animate для работы с графикой используется пара приемов, которые пригодятся Вам при разработке собственных программ. В модуле кода \N1MATE.BAS процедура Main отображает две формы, и каждая демонстрирует один из приемов. Сначала посмотрим весь код, а уж потом «покопаемся» в формах.
Исходный код ANIMATE.BAS
Option Explicit
DefDbl A-Z' <««« ОБРАТИТЕ ВНИМАНИЕ! I I
Public Const PI = 3.14159265358979
Public Const RAOPERDEG = PI / 180
Hain()
i App.HelpFile =App.Path & "\..\..\Help\Hvbdw.hlp”
■ f rmCIock. Show vbModeless
frmGlobe.Show vbModeless
End Sub'
. Sub RotateX(X, Y, Z, Angle)
Dim Radians, Ca, Sa, Ту'
Radians = Angle * RADPERDES■
Ca = Cos(Radians)
■ Sa = Sin(Radians)
Ту = Y * Ca - Z * Sa■
Z = Z * Ca a Y * Sa
У = Ту'
End Sub
f Sub RotateY(X, Y, Z, Angle)
продолжение
Dim Radians, Са, Sa, Тх
Radians = Angle * RADPERDEG
Са = Cos(Radians) Sa = Sin(Radians)
Tx - X * Ca + 2 * Sa
Z - z * Са - X * Sa
EiidXSub 'X.
Sub RotateZ(X, Y, Z, Angle)
Dim Radians, Ca, Sa, Tx ■
Radians = Angle * RADPERDEG
Ca = Cos(Radians)
Sa = Sin(Radians)
Tx = X * Ca - Y * Sa
Y = Y * Ca + X * Sa
X = Tx
End Sub
Sub PolToRac(Radius, Angle, X, Y) Dim Radians
■ Radians = Angle * RADPERDEG
X = Radius * Cos(Radians)
Y = Radius * Sin(Radians)
End Sub
Sub ЙесТоРоЦХ, Y, Radius, Angle) Dim Radians
Radius = Sqr (X * X + Y * Y)
If X * 0 Then
Select Case Y
Case Is > 0
.Angle - 90
Case Is < 0
Angle = -9S
Case Else '
Angle - 0
End Select
Elself Y = 0 Then
Select Case X
Case Is < D
Angle = 180
Case Else
Angle = 0
Bad Select■■■
Else
продолжение
If X < 0 Then
If Y > 0 Then
Radians = Atn(Y / X) * PI'
Else
Radians Atn(Y / ■- PI
.End/f
■ Else’
fiadians = Atn{Y / X)
■ End If
Angle - Radians /flADPERDEG
End If1■
fiEhd sub■
В начале всех модулей этого проекта я указываю оператор Def- Dbl A-Z, чтобы по умолчанию переменные были вещественными двойной точности. Конечно, программа работает и в том случае, если этих операторов нет и переменные по умолчанию получают тип Variant, но при объявлении их как Double все операции слегка ускоряются. Вы, наверное, заметили, что здесь я отошел от венгерской нотации, которую соблюдал довольно строго. Если Вас это смущает, объявите все переменные явно и переименуйте их — получится неплохое упражнение.
В отличие от большинства других приложений в части III, в этой программ: нет меню, и, следовательно, нельзя обратиться к справочному файлу привычным способом. А с другой стороны, это дает повод продемонстрировать, как клавиша F1 связывается со справочным файлом, — в процедуре Main я присваиваю свойству HelpFile объекта Арр полное имя нужного файла.
В качестве альтернативы можно указать имя справочного файла в файле проекта. Выберите из меню Project команду Project Properties, чтобы открыть одноименное диалоговое окно. В поле Help File Name введите имя справочного файла и оставьте в поле Project Help Context ID (идентификатор контекста в проекте справочной системы) нулевое значение — тогда первым будет появляться раздел Contents справочного файла.
Выполнение программы начинается с процедуры Main, а не с какой-либо формы. Для этого в диалоговом окне Project Properties в списке Startup Object я выбрал Sub Main. Блок кода в Main совсем крошечный; его задача — показать обе формы и установить свойство HelpFile приложения. Кроме процедуры Main, в модуле кода содержится целый набор полезных процедур, которые реализуют вращение декартовых координат вокруг каждой из трех осей, а также преобразуют прямоугольные координаты в полярные и наоборот. Они нужны для расчетов, связанных с трехмерной графикой
(при отображении вращающегося глобуса). Но подробности — чуть позже, а пока рассмотрим форму, создающую часы с анимацией.
ANICLOCK.FRM
Эта форма создает часы реального времени, используя таймер и лишь один элемент управления Line. Рис. 29-4 иллюстрирует форму на этапе разработки, а рис. 29-5 — в период выполнения.
Рис. 29-4 Форма AniClock на этапе разработки: используются всего два элемента управления
Рис. 29-5 ФормаAniClock в период выполнения
Вас, наверное, заинтриговало, как же рисуются все детали циферблата часов. Фокус в том, что с помощью оператора Load я делаю 14 копий исходного элемента Line и присваиваю свойствам, определяющим координаты каждого экземпляра, значения начальных и конечных точек линий на циферблате. Двенадцать копий размещаются на постоянном месте, изображая сам циферблат, а остальные три элемента Line, изображающие стрелки часов, обновляют свои позиции каждую секунду. Так и создается иллюзия их движения.
Заметьте, здесь не используется метод Line, и поэтому ни одна из линий не стирается самой программой. Всю работупо стиранию и перерисовке каждой стрелки (в процессе ее движения) берет на себя Visual Basic в тот момент, когда мы обновляем координаты нужного экземпляра элемента управления Line. Как видите, замысловатые графические операторы и команды нужны не всегда — и
простой элемент Line способен на поразительные вещи! Внешний вид итог. изменяется программно — модификацией значений соответствующих свойств. Например, Вы можете сделать линии более тонкими или толстыми, варьируя значение свойства Border Width каждого элемента Line.
Создавая это приложение, используйте таблицу, в которой описаны необходимые элементы управления и значения их свойств.
Объекты ANICLOCK.FRM и значения их свойств
Свойство
Значение
Form
Name Caption MinButton Timer
JrmClock
Animation - Clock
False
Name Interval
Line
tmrCIock
100
Name Index
UnClock 0
Свойство Interval таймера я установил как 100, что равно /1П секунды. Может показаться, что логичнее задать интервал обновления стрелок часов в 1000 мс, или 1 раз в секунду. Увы, эти интервалы не точны; таймеры Visual Basic лишь гарантируют задержку не меньше заданной, но никто не поручится, что все задержки будут строго одинаковы. Из-за вариаций величины интервала секундная стрелка может иногда ^пропускать такт», что приводит к рывкам при ее движении. Чтобы снять проблему нестабильности, я проверяю -наступление новой секунды» 10 раз в секунду. В итоге максимальная ошибка каждого смещения секундной стрелки составляет примерно '/,„секунды, что вполне приемлемо, учитывая особенности визуального восприятия. Почти в 9 из каждых 10 вызовов процедуры, обрабатывающей событие таймера, происходит немедленный выход из нее, так как текущая секунда еще не сменилась.
Основная работа процедуры tmrClockTimer — пересчет и модификация значений свойств XI, ¥1. Х2 и ¥2, определяющих координаты начальных и конечных точек линий. Исходный код для модуля формы AniCIock приведен ниже.
Исходный wANICLOCK.FRM
Option Explicit
DefDbl A-Z. ' «««< ОБРАТИТЕ ВНИМАНИЕ!!!-
продолжение
Private Sub Form„Load()
Width - 4000
Height » 4000
- Left = Screen.Width \ 2 - 4100
Top = (Screen. Height - Height) \ 2
End Sub
Private Sub Form_flesize()
Dim i, Angle
Static Flag As Boolean
If Flag = False Then
Flag = True
Fori =OTo 14
If i > О Than Load linClock(i) .
linClock(i). Visible = True
linClock(i).BorderWidth = 5
LinClock(i).BorderColor = RGB(O, 128, 0) Next i
End If
Fori = OTo14
Scale (-1, -1)-(1. 1)
Angle = i * 2 * Atn{1> / 3
linClock(i).x1 - 0.9 * Cos(Angle)
HnClock(i).y1 = 0.9 * Sin(Angle)
HnClock(i). x2 = Cos(Angle) linClock(i), y2 = Sin(Angle)
Next I
End Sub
Private Sub tmrClock_Timer()
Const HourHand = 0
Const NinuteHand = 13
Const SecondHand - 14
Dim Angle
Static LastSecond
' позиционируем стрелки только no секундам
If Second(Now) = LastSecond Then Exit Sub
LastSecond = Second(Now) ' позиционируем часовую стрелку Angle = -0.5236 * (15 - (Hour(Now) + Mlnute(Now) / 60)) lin€lock(HourHand).x1 =0 linClock(HourHand).y1 =0
HnClock(HourHand).x2 = 0.3 * Cos(Angle) HnClock(HourHand).y2= (КЗ * Sin(Angle) ' позиционируем минутную стрелку
Angle - -0,1047 * (75 - (Hinute(Now) <- Second(Now) / 60))
продолжение
linClock(MinuteWand).х1 = О linClock(MinuteHand).y1 =0 HnClock(MinuteHand),x2 = 0,7 linClock(MinuteHand), у2 =0.7 ' позиционируем секундную стрелку
Angle = -0.1047 * (75 - Second(Now)) HnClock(SecondHand).xi * О HnClock(SecondHand).y1 * 0л
HnClock(SecondHand).x2 = 0.8 * Cos(Anglg) HnClock(SecondHand).y2 - 0.8 » Sln(Angie) End Sub
ANIGLOBEFRM
На этой форме выводится последовательность изображений со сферическими линиями широты и долготы, что создает иллюзию вращения глобуса. Набор изображений хранится и элементе уп па в пения ImageList, и нужное из них быстро копируется в элемент управления PictureBox. Форму AniGlobe с элементами PictureBox, ImageList и Timer, необходимыми для создания эффекта анимации, иллюстрирует рис. 29-6.
Рис. 29-6 Форма AniGlobe на этапе разработкиРис. 29-6 Форма AniGlobe на этапе разработки
Рис. 29-6 Форма AniGlobe на этапе разработки
Рис. 29-6 Форма AniGlobe на этапе разработки
Эта форма обновляет себя при каждом срабатывании таймера tmrGlobe. На первых 1 б итерациях формируются новые изображения с использованием ряда процедур трехмерной графики (см. модуль кода ANIMATE.BAS). После отрисовки каждое изображение быстро сохраняется в ImageList, объявленном на уровне модуля. Как только все I 5 изображений сохранены, программа последовательно отображает их, обновляя свойство Picture элемента PictureBox
так, чтобы оно ссылалось на очередное изображение. Этот прием демонстрирует базовый способ быстрого и плавного вывода анимационны?, последовательностей (рис. 29-7)..
«'Ааммноп >('
Рис. 29-7 Форма AniGlobe в действии
Значения свойств формы AniGlobe и ее элементов управления приведены в таблице,
Объекты ANIGLQBE.FRM и значения их свойств
Номер объекта1
Свойство
Значение
Form
Name
frmGlobe
Timer
Caption
Animation - Spinning Globe
1
Name
tmrGlobe
PictureBox
Interval
1
2
Name
ptcGlobe
ImageList
Auto Red raw
True
3
Name
imlGlobe
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 29-6).’
В исходном коде, представленном ниже, я задал констант:. TILTSOUTH и Т/АТТУчТоппедетярлцис- углы наклона глобуса. Попробуйте модифицировать их значения, и Вы тут же заметите резкие изменения во внешнем виде вращающегося глобуса.
Элемент управления ImageList особенно полезен в сочетании с другими элементами — ListView.ToolBar, TabStrip и t гее View. Дополнительную информацию об этом мощном инструменте для манипуляций с изображениями см, в документации Visual Basic.
ИСХОДНЫЙ КОД ANIGLOBE.FRM
Option Explicit:
Def Bbl A-Z’ ««<« ОБРАТИТЕ ВНИМАНИЕ! 1!
Const TILTSOUTH * 47
Const TILTEAST = -37
Private Sub Form_Load()
Width = 4000
Height * 4000
, Left = Screen,Width \ 2 + 100
Top = (Screen.Height - Height) \ 26
End Sub
Private Sub tmrGlobe_Timer()
film Lat, Lon, Radians
Dim R, A, I
Dim xl. yl, x2, у2■
DimXc(72), Yc(/2), Zc(72)
Dim imgX As Listimage ■■
Static Imageindex, ImageNum
Select Case ImageNum
' "подкачиваем" следующее изображение■
Case -1
Imageindex = (Imageindex Hod 15) + 1
Set picGlobe.Pictui = - ^„^Globe. Listimages _ (Imageindex).Picture
Exit Sub
x1 инициализируемР1сТигеВох
Case 0
picGlobe.Move 0, 0, ScaleWidth, ScaleHeight
picGlobe.Scale (-1.1, 1.1)-(1.1. -1.1)
'Caption = "Animation ... PREPARATION"
ImageNum = ImageNum + 1
Exit «"b:
признак ого, что последнее изображение нарисовано ■1 и сохранено в списке изображений■
Case 16
Caption = "Animation - Spinning Globe"
ImageNum = -1
. Exit Sub
End Select.
стираем все предыдущие рисунки в PictureBox
Set picGlobe.Picture = Nothing
' рисуем контуры глобуса
picGlobe.ForeColor - vbBlue
For i = 0 To 72
■ PolToRec 1, i * 5, Xc{i), Yc(i)
продолжение■ ■
. Next i
For i = 1 То 72
'picGlobe.Llne fXcfi - 1), Yc{i - l))-(Xc(i), Yc(i))
Next i
" вычисляем и вычерчиваем линии широт
picGlobe.ForeColor = vbRed
For Let = -75 To 75 Step 15
1 выражаем широту в радианах
Radians = Lat * RADPERDEG
вычерчиваем окружности пропорционально широте
For i = 0 То 72
PoIToRec Cos(Radians). 1*5, Хс(1), Zc(i)
Yc(i) = Sin(Radians)
' uavn^uocM северный полюс в нашу сторону RotateX Xc(i), Yc(i), Zc(l), TILTSOUTH
■1 наклонным северный полюс вправо
RotateY Xc(i), Yc(i), Zc(i), TItTEAST
Next 1
рисуем переднюю половину вращаемой окружности
For 1 = 1 То 72
И Ze{i) >= 0 Then
PicGlobe.Llne (Xc(i - 1), Yc{i - 1))-(Xo{L), Yc(l)) End If
Next i
(text Lat
вычисляем и вычерчиваем линиидолгот
picGlobe.ForeColor = vbBlue■
For Lon - 0 To 165 Step 15
начинаем с окружности в плоскости xy
For A = & To 72
PoIToRec 1. ft* 5, Xc(A), Ус(А)
■Zc(A)=0
Next A
поворачиваем точки текущей линии долготы For 1 - 0 То 72
RotateY Xc(i), Yc(i), Zc(i) Lon + ImageNum
наклоняем северный полюс в нашу сторону
:' RotateX Xc(i), Yc(i), ■ TILTSOUTH
наклоняем северный полюс вправо
RotateY Xo<i), Yc(i), Zc(i), TILTEAST
Next 1
рисуем переднюю половину вращаемой окружности
For 1 - 1 То 72
If Zc(i) >= 0 Then
picGlobe.Llne (Xc(i - 1), Yc(i - 1))-(Xc(i), Yc(i)) End If
продолжение
Next i
Next Lon
' обновляем состояние PictureBox
plcGlobe.Refresh
picGlobe.Picture = picGlobe.Inage
' добавляем это изображение в наш список изображений
Set imgX - imLGlobe.Listimages.Add(, , picGlobe.Picture) ' готовимся к отрисовке следующего изображения ImageNusi = TmageNu» + t
End Sub
Приложение Lottery
Приложение Lottery моделирует колорадскую лотерею, при розыгрыше которой используются пронумерованные шарики от пинг- понга. С минимальными изменениями приложение можно переделать и под другие азартные игры этого типа. Суть лотереи очень проста: 42 шарика, пронумерованные от 1 до 42, вбрасываются в барабан и перемешиваются. Затем из него случайным образом извлекаются 6 шариков — по одному за раз. Выигрыш получает тот, кто угадает 6 номеров. Он может быть огромен, но шанс угадать все 6 номеров ничтожно мал. так что штат в среднем выигрывает куда больше. Б приложении Lottery при каждом щелчке кнопки Next ball (следующий шарик) из «барабана» извлекается один из шариков и помещается в нижнюю часть окна. Рис. 29-8 иллюстрирует окно приложения после выброса трех шариков. Я сделал зэк что программа имитирует покупку 1000 лотерейных билетов, и Вы сами убедитесь, насколько трудно сорвать банк, даже купив такое количество билетов.
Рис. 29-8 Программа Lottery в действии
Это приложение демонстрирует созданный мной объект Random, формирующий значительно более длинные последовательно-
сти случайных чисел, чем генератор, встроенный в Visual Basic. Здесь же применяется и один из простейших способов анимации, при котором картинка перерисовывается с такой скоростью, что движение всех предметов кажется непрерывным.
Кроме основной формы Lottery, на которой «перемешиваются» шарики, проект с одержитмодуль класса RANDOM.CLS и стандартную форму About. Список файлов в проекте Lottery показан на рис. 29-9-
' . □ About (About.fffli)
‘ FnnLotte'y (Lattery.ftm)
5 С1да Modules
Й ardQffitftdrtiofr.ck) 1
Рис. 29-9 Список файлов в проекте Lottery
LOTTERY.FRM
LOTTERY.FRM — основная, стартовая форма этого приложения. Элемент управления PictureBox с именем pic-Tumble отображает перемешиваемые и выпадающие шарики, две кнопки управляют работой программы, таймер tnirPinpPonfi определяет частоту обновления изображения. Рис. 29-Ю иллюстрирует форму на этапе разработки, а в таблицах описаны элементы меню и объекты, расположенные на этой форме.
Рис. 29-Ю Форма Lottery на этапе разработки
Элементы меню LOTTERY.FRM
Название
Имя
Уровень вложения
Параметр Enabled
№1е
mnuFile
0
True
&Vew
mnuNew
1
False
@Ореп„,
mnuOpen
1
False
®8йге
mnuSave
1
False
Save SAs:..
mnuSaveAs
1
False
—
mnuFileDashl
\
True
ES-xit
mnuExit
1
True
SHelp
mnuHelp
0
Ibue
Contents
nmuContents
1
True
(PSearchfor Help on..
. trinuSearch
1
True
—
mnuHelpDasbl
1
True
iSAbout..
mnuAbout
1
True
Объекты и их свойства в LOITERY.FRM
Тип и номер1
Свойство
Значение
Form
Name
JrmLottery
PictureBox
Caption
Lottery»
1
Name
picltmible
AutoRedraw
True
BackColor
®HOOFFOOOO@-
Height
3600
Timer
Width
3600
2
Name
tmrPmgPong.
CominandButton
Interval
50
3
Name
cmdNextBall
CommandButton
Caption
&-Next ball
4
Name
cmdSample
Caption
<SSample 1000 tickets...
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 29-Ю).
Исходный код LOTTERY.FRM'
Option Explicit
Private Declare Function WinHelp _
Lib "user32" Alias "WinHslpA" ( _ ByVal hwnd As Long,.
■ ByVal IpHelpFlle As String, _
ByVal «Command As nuny, _ ByVal dwData As Long _
■) As Long■
Const MAXNUM = 42
Dim intPPBall(B) As Integer
Dim randDemo As New Random■
Private Sub cmdNextBall_ClickO
Dim inti As Integer
Dim intJ As Integer
'' устанавливаем название кнопки '
cmdNextBa11.Caption = "SNext ball" cmdSample. Visible = False
' получаем текущее количество выбранных шариков
inti = intPPBall(O)
' есш извлеченье все 6 шариков, начинаем снова'
If inti = 6 Then
For inti = 0 To 6
.intPPbaii(inti) = 0
’Next inti
Exit Sub■
End If
' извлекаем следующий шарик с уникальным номером firabNext intPPBall()
' изменяем название кноши
' и делаем видимой кнопку для отображения статистики
If intPPBall(O) - 6 Than
cmdNextBall. Caprion = "Start sever' cmdSample. Visible = True
End If
" End Sub:
Private Sub cmdSample„Click() .■■■■
Dim inti As Integer
Mm IntJ As Integer
Dim intK As Integer'
Dim ji l„ As Integer
Dim intTicket(6) As Integer'
17—1204
продолжение
Dim Hits(6) As Integer
Dim strMsg As String ' меняем форму курсора мыши на “песочные часы" MousePointer = vbHourglass
' имитируем 1000 оипетов с разными комбинациями номеров For inti = 1 То 1000
' создаем билет intTicket(O)= О ForintJ = 1 То 6 GrabNext intTicketO Next intJ
' подсчитываем число совпадений intN = О■
' For intJ * 1 То 6
For intK - ' To 6
If intTicket(intJ) = intPPBall(intK) Then intN = intN + 1
End If Next intK Next intJ ' лЛйплляен статистику Hits(intN) = Hits(intN) + 1 Next iiiLj ' восстанавливаем исходную форму курсора мыши MousePointer = vbDefault ' отображаем итоговую статистику
; strHsg = "Sample of 1000 tickets...' & vbCrLf & vbCrLf strMsg = strHsg & Space$(1G) & "Hits Tally" a vbCrLf For inti = 0 To 6
strHsg = strMso & Space$(12) & Format$(intl) & Space$(6) strMsg = strMsg & Format$(Hits(intI)) 4 vbCrtf
Next inti
HsgBox strMsg, , "Lottery" End Sub
Private Sub ■ t loadi) ' начинаем новую последовательность случайных чисел . Randomize■
randDemo.Shuffle find ' задаем диапазон сттучайньк целых чисел randDemo.Minlnt = 1 randDemo.Maxint = MAXNUM '' центрируем форму
He.Left = (Screen.Width - Me.Width) \ 2 . Me.Top = (Screen.Height - Ne.Height) \ 2 ■'■' скрываем кнопку для отображения статистики
продолжение
‘cudSample. Visible =False
' готовимся к анимации, имитирующей перемешивание шариков
"picTumble.Scale (0,0)-(12, 12)
.picTumble. Fillstyle= vbSolld
picTunble. FillColor = vbWhite picTumble.ForeColor = vbRed
' End Sub
Private Sub picTumble_Paint() Dim inti As Integer Dim sngX As Single Din sngY As Single Dim strN As String ' стираем прежнее изображение picTumble, Cis For inti = 1 To 6
' определяем, был ли выбран шарик
If intPPBall(intl) > 0 Then
sngX = inti *2-1
sngY = 11
strN = Format$(lntRPBall(intT)) Else
sngX = Rnd *10+1
sngY = Rnd * 8 + 3
strN = Format$(randDemo.RandosInt).
End If
' рисуем каждый шарик
'picTumble.Circle (sngX, sngY), 1, vbWhite
picTumble.CurrentX = sngX - picTumble.TextWidth(strN) / 2 picTumhlp Currentv = sngY - picTumble.TextHeight(strN) / 2
' нумеруем их
plcTuiliUJ.e, ri j.iil StrN Next inti
End Sub
Private Sub tmrPingPong_Timer() plcTumble^Palnt
End Sub
Private Sub mnuAbout_Click() ' ... ..аизаем свойства
About.Application = "Lottery" About.Heading =
"Microsoft Visual Basic 6.0 Developer's Workshop"
About.Copyright = "19УЙ John Clark Craig and Jeff Webb" About.Display
продолжение
. End Sub
Private Sub mnuExit_Click{)
Unload Me
End Sub.
Private Sub mnuContents_Click()
WinHelp hwnd, App.Path & "\., V .\Help\Mvbdw. hip", _ cdlHelpContents, 0■
End Sub.
Private Sub innuSearch„Click()
WinHelp hwnd, App.Path & 'V.\..\Help\Mvbdw.hip", _ cdlHelpPartialKey, 0
End Sub
Private Sub GrabNext(intAry() As Integer)
Dim inti As Integer
Dim intJ As Integer
' сохраняем индекс в первом 'вменте массива IntAry(O) = intAry(C; + 1
Inti = intAry(O).
' получаем следующий уникалвный номер шарика
Do
intAryUntl) = randDemo. Randomlnt
■ If inti > 1 Then
For intJ = 1 To inti - 1
If IntAry(intl) = intAry(intJ) Then intAry(intl) = 0
End If
Next IntJ
End If
Loop Until intAry(intl)
En ri Sub
ПРИМЕЧАНИЕ Для активизациисправочногофайла при выборе соответствующих элементов меню Help применяется API-функция WinHelp. В некоторых приложениях части III я связываю элементы меню со справочным фаило.' через элемент управления CommonDialog, даже если они идентичны приведенным здесь. Оба способа дают одинаковые результаты, так что выбор за Вами.
В процедуре Form Load я использую объект типа Random. Этот объектный тип определен в модуле класса Random, о котором мы поговорим в следующем разделе..
RANDOM.CLS■
Модуль класса Random — шаблон для создания объектов типа Random. В его основе лежит алгоритм, который значительно увеличивает длину последовательности случайных чисел, формируемой встроенным в Visual Basic генератором. Для перемешивания случайных чисел при их генерации и специфической инициализации этого процесса я использовал массив типа Double.
В RANDOM.CLS определены открытые свойства:
л Minim — минимальное значение диапазона генерируемых случайных целых;
Maxim — максимальное значение диапазона генерируемых случайных целых;
Random — случайное число в диапазоне от 0 до 1;
Randomlnt — случайное целое в диапазоне от Minlnt до Maxlnt.
Единственный открытый метод в RANDOM.CLS — Shuffle, инициализирующий последовательность случайных чисел.
Еенератор случайных чисел в Visual Basic очень хорош, но я часто слышу о проблемах, связанных с ним. Во-первых, не совсем ясно, как инициализировать его так, чтобы он генерировал повторяющуюся последовательность. О'кэй, открою маленький секрет: есть один простой (правда, довольно хитрый) способ перенастройки генератора случайных чисел Visual Basic на повторяющуюся последовательность. Для этого Randomize надо вызывать сразу после передачи функции Rnd какого-нибудь отрицательного значения, например:
Randomize Find(-7)
Всякий раз, передавая -7 в эти две функции, как показано выше, Вы инициализируете одну и туже последовательность случайных чисел. Я воспользовался этим приемом (слегка изменив его) в методе Shuffle, Передача нулевого или положительного значения дает совершенно непредсказуемую последовательность случайных чисел.
Другая проблема, о которой я слышу (в основном от криптографов), в том, что Visual Basic генерирует случайные числа, не учитывая такие эффекты, как слабые зависимости и энтропия, а это снижает ценность его генератора для целей шифрования. Мой объект Random содержит массив вещественных чисел двойной точности, с помощью которого эффективно тасует и на много порядков расширяет получаемую от Visual Basic последовательность, поддерживая хорошее распределение чисел в диапазоне от 0 до 1. Изучите код процедуры открытого свойства Random, и Вы увидите, как это реализуется.
Процедура-свойство Randomlni модифицирует значение, возвращаемое процедурой Random, формируя целое число в диапазоне, заданном значениями свойств Minim и Maxim. Распределение псевдослучайных целых чисел остается таким же хорошим, как и значений, возвращаемых Random.
В модуле класса Random две закрытые процедуры: Zap (инициализирует массив для метода Shuffle) и Stir («разогревает- генератор).
Исходный код RANDOM.CLS
Option Explicit.
const ARYCNT = 17■
' два простых свойства для чтения и записи
Public Minlnt As 1 nr"i
ETublic Maxlnt As Long
■ ■ ' ' переменные уровни модуля
Private mdblSeed(ARYCHT - 1) As Double'
Private mint? As Integer
Private mintQ As Integer
' метод
Public Sub Shufflemt As Double).
Dim strH As String
Dim inti As Integer
Zap
strN = StrS(dblX)
For Inti = 1 To Len(strN)
Stir 1 / Asc(Mid(strN, inti, 1»
Nextntl■
Randomize Rnd(mdbLSeed(mintP) * Sgn(dbLX))
For _ntl = 1 To ARYCNT * 2.7
StirRnd
Next inti
End Sub
Property Get RandomQ As Double
mintP = (MintP + 1) Mod ARYCNT
fflintQ = (mintQ + 1) Hod ARYCNT
mdblSeed(mintP) - ffidblSeetf(mintP) + mdblSeed(mintQ) + Rnd ffldblSeed(mintP) = radblSeed(mintP)- Int(mdblSeed(mintP)) Random = mdblSeed(mintP>.
End Property
Property Get RandomInfO As Long
Randomlnt = Int(Random() * (Maxlnt - Hlnlnt + 1)) + Minlnt
End Property
продолжение.
'Private Sub Zap ()
Dim As Integer
For inti = 1 To ARYCNT - 1
mdblSeed(intl) = 1 / inti
Next inti
. mintP - ARYCNT \ 2
mintQ = ARYCNT \ 3
If mint? = mintQ Then
fflintP = mintP + 1
Ena If
End Sub■
iPrivate Sub Sti r( dblX As Double)
mintP = (mintP + 1) Mod ARYCNT
mintQ = (mintQ + 1) Mod ARYCNT
mdblSeed(mintP) = mdblSeed(mintP) + mdblSsed(mintQ) + dblX
mdblSe6d(mintP) = mdblSeed(mintP) - Int(mdblSeed(mlntP))
End Sub
Приложение MySaver
Это приложение развивает примеры, приведенные в главе 25 «Экранные заставки». Оно предоставляет гораздо больший ■ ыбор эф- фекгоп. не требуя слишком много дополнительного кода. Рис. 29-11
Рис. 29-11 Приложение Му Silver а действии
иллюстрирует эту заставку в действии, но чтобы получить полное представление о ней, надо перепробовать множество комбинаций разных параметров.
По-видимому, самое важное отличие этой экранной заставки от примеров из главы 25 — дополнительный код, который автоматически демонстрирует заставку ; небольшом окне предварительного просмотра, имитирующем рабочий стол на миниатюрном мониторе. В этом случае операционная система передает заставке пара- метр/Рчцч», где ттп — hWml окна предварительного просмотра. Корректная поддержка подобной функциональности требует применения нескольких API-функций. И в исходном коде MySaver - там, где программа решает, в каком режиме следует показывать экранную заставку (на полном экране или в окне предварительного просмотра), — Вы увидите операторы, проверяющие значение глобальной переменной gblnShow. Эти режимы обрабатываются по- разному, из-за чего и приходится делать такие проверки. Рис. 29-12 иллюстрирует, как выглядит экранная заставка MySaver в миниатюрном окне предварительного просмотра.
Г BlAfUIHJxJ $<;1ЯЙ $*^ | АщиЯАпет^ ^^ГМ-ЙПЛМ I S^^Kjl ]
5й(Ш..5акг
P'^dw^foidiflitiM| &*f Г ^ nimtei
Рис, 29-12 Экранная заставка MySaver, работающая в миниатюрном окне предварительного просмотра
Как показано на рис. 29-13, в этом проекте два модуля форм и один стандартный. MYSAVERBAS содержит стартовую процедуру Main и соответствующий код. МУЗАУЕКРКМ — основная форма экранной заставки, a MYSETUP.FRM активизируется кнопкой Settings на вкладке Screen Saver в диалоговом окне Display Properties.
Е ^ FtxfTn
1 Cl-frmSevpr JjSuVEr.Frml
EL fr«5HtUp(5MitlO frm) &^ Modules
rf£ rrodSSaw (staw.be]
Рис. 29-13 Список файлов в проекте MySaver
MYSAVER.BAS
В этом модуле находятся стартовая процедура Main и объявления нескольких API-функций. Он управляет тем, куда выводится формируемое изображение. Но его главное назначение — анализ параметров командной строки, передаваемых системой при активизации экранной заставки, и выполнение соответствующих действий.
Исходный код MYSAVER.BAS
' MySaver. has
Option Explicit
' структура данных для вывода в прямоугольник Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
' константы для некоторых API-функций
Private
Const
WS_CHILD = &H4000000D
Private
Const
GWL_HWNDPARENT = (-8)
Private
Const
GWL_STYLE = (-16)
Private
Const
HWND_TOP = 04
Private
Const
SWP_NOZORDER = fi»4
Private
Const
SWP_NOACTIVATE = 4H10
Private
Const
SWP_SHCWINDOW = Ш0
'" — объявления API-функций
Private Declare Function GetCLientRect _
Lib "userG 2“ (
ByVai hwnd As Long, _•
IpRect As RECT „
) As Long
Private Deciare Function GetWindowtong _
Lib "user32* Alias "GetWindowLongA" < _
продолжение
3
ByVal hwnd As Long, _
ByVal nlndex As Long .
) As Long
Private Declare Function SetWlndowLong _
Lib er32" Alias "SetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nlndex As Long, _
ByVal dwNewLong As Long _
> As Long
Private Declare Function SetWindowPos _'
Lib ,(
ByVal hwnd As Long, _
ByVal UWndTnsertAfter As Long, _
ByVal X As Long, _
ByVal Y As Long, _
ByVal ex As Long, _
ByVal ey As Long, _
ByVal wFlags As Long _
f ) As Long
Private Declare Function SetParent _
Lib "user32“ ( _
ByVal hWndChild As Long, _
ByVal hWndNewParent As Long _
) As Long
глобальный флаг режима вывода
Public gblnShow As Boolean
' переменные уровня модуля
Private ml noMsnlavHwnd As Long■
Private recDisplay As RECT
' стартовая точка...
Public Sub Main()
Dim strCmd As String
Dim strTwo As String
Dim IngStyle As Long
:Dim IngPreviewHandle As Long
eDim InqParam As Long
анализируем командную строку
ГStrCmd - lir.flSofTrimi’raiTmr.nd))
rstrTwo Left (strCmd, 2)
Select Case strTwo
:1 предварительный просмотр заставки в миниатюрном окне
продолжение
Case "/Р"
' получаем hWnd этого окна
mLngDisplayHwnd = Val(Mld(strCmd, 4))
.1 получаем размеры его прямоугольнике
QetClientReot mlngDlsplayHwnd, recDisplay
загружаем форму для предварительного просмотра
gblnShow = False
Load frmMySaver
получаем НМпЬдля формы
IngPreviewHandle = frmMySaver. hwnd
получаем текущий стиль окна
IngStyle = GetWindowLong(lngPreviewHandle, GWL_STYLE)
комбинируем стильWS_CHIL0 с текущим
'IngStyle = IngStyle Or WS_CHILD
' отображаем окно с новым стилем
SetWindowLong IngPreviewHandle, GWL_S7YLE, IngStyle
1 устанавливаем это окно как родительское
SatParent IngPreviewHandle, mlnqOisplayHwnd.
' сохраняем hWnd родительского окна в оконной структуре
1 формы
'SetWindowLong IngPreviewHandle, GWL_HWNDPARENT, _
mlngOisplayHwn ?
■1 просматриваем экранную заставку в окне
SetWindowPos IngPreviewHandle, „
HWND.TOP, 0&, О&, recDisplay.Right, recDisplay.Bottom, _ SWP_NOZORDER Or SWP_NOACTIVATE Or SWP_SHOWINDOW
Exit Sub
1 настройка параметров заставки
. Case
Load frmMySetup■
Exit Sub
1 установка пароля не реализована
Case VA"
MsgBox "No password is necessary for this Screen Saver", _ vblnformation, "password Information"
Exit Sub
показываем заставку в нормальном, полноэкранном режиме Case , „
gblnShow ■ True
Load frmMySaver
frmMySaver.Show
Licit Sub
1 неизвестные параметры в командной строке
Case rise
Exit Sub
End Select ■"
End Sub
MYSAVER.FRM
Форма MYSAVER.FRM — то место, где разворачиваются все события. Заметьте, я отключил видимые компоненты формы, в частности присвоил свойствам MinButton и MaxButton значение False, a BorderStyle — 0 - None. Благодаря этому форма превращается в холст, занимающий весь экран.
Единствсн.1 пап элемент управления на этой форме (рис. 29-14) — таймер. Графика обновляется в бесконечном цикле при обработке события Paint формы. Visual Basic генерирует ошибку, если программа пытается выгрузить себя в обработчике события P 't'v поэтому я использую таймер: он позволяет быстро выгрузить форму после выхода из бесконечного цикла в обработчике Paint, В обычных условиях я бы не советовал создавать бесконечный цикл в процедуре обработки события Paint, но в данном случае это немного ускоряет обновление графики (по сравнению с тем, когда соответствующую подпрограмму вызывает таймер) и, кроме того, система ведь не взаимодействует с пользователем при работе экранной заставки.
* Si^rw ■ hmSidveu [Feral
Ж-
Рис. 29-14 MYSAVERJERMia этапе разработки
Свойства формы и ее элементов управления описаны в таблице.
ОбъектыМ¥8АУЕК.ЕКМизначенияихсвойств
Свойство
Значение
form
Name
Borders tyle ControlBox MaxButton MinButton Timer
JrmMySarer'
0 - None■
False
False
False1
Name Interval Enabled
tmrFxitNotijy
False
Исходный код MYSAVER.FRM
" NySaver.frm
Option Explicit
1 API-функция для гашения и восстановления курсора мыши Private Declare Function ShowCursor
Lib "userS?"
ByVai Stour As Long _
) As Long
1 API-Функция, сигнализирующая системе об активизации заставки Private Declare Function SysteinParametersInfo
Lib user32" Alias "SystsmParametersInfoA" (
ByVai uAction As Long,
ByVai иРагая As Long, _
ByRef IpvParam As Any,
ByVai fuWinlni As Long _
) As Long
1 константы для API-функции
Private Const SPI.SETSCREENSAVEACTIVE = 17
1 объявляем переменные уровня модуля
Dim mlngXai As Long
Dim mlngYai As Long
Dim mlngXbi As Long
Dim mlngYbi As Long
Dim mlngLineCoun As Long
Dim mlngLineWidth As Long
Dim ulngActionType As Long
Dim mlngXmax As Long
Dim mlngYmax As Long
Dim mlnglnc As Long
Dim mlng€olorNum() As Long
Dim mlngDx1() As Double
Dim mlngDx2() As Double
Dim mlngDyK) As Double
Dim ffllngDy2() As Double
Dim mlngXaO As Long
Dim mlngXb() As Long
Dim mlng¥a() As Long
Dim mlngYb-' As Long
Dim mblnGu As Boolean
Private Sub Form_Load()
Dim IngRet As Long
■ сообщаем системе, что экранная заставка сейчас активна
продолжение
IngRet - SystemParametersInfo( _ SPI_SETSCREENSAVEACTIVE, 0, ByVai 04, 0} .' показываем заставку во весь экран.
' если это >■-■ режим предварительного просмотра
If gblnShow = True Then
Me.WindowState = vbMaximized
End If
End Sub
Private Sub Form_Paint()
Dim IngXAs Long ' в режиме предварительного просмотра устанавливаем ' AutoRedraw как True
If gblnShow = False Then Me. AutoRedraw - True
End If
' каждый раз показываем разные изображения
Randomize
' устанавливаем контрольные значения
mlnglnc = 5
mlngXmax = 300.
mlngYmax = 300 ' получаем текущие настройки из реестра mlngActionType = Val(GetSetting( "MySaver", "Options", _ "Action", "1"))
mlngLineCount =Val(GetSetting("MySaver","Options",_
■"LineCount","1"))
mlnglineWidth =Val(GetSetting("MySaver","Options",_
"LlneWidth", "1"))
' инициализируем параметры вывода "рафики
. With Me
.BackColor =vbBlack nrawWidth =mlngLineWldth End With
Scale (-mlngXmax v'nax)-(mlngXmax, mlngYmax) ' переопределяв!! массивы
fieQlm mlngColorMumiu To mlngLineCount) ■ReDim mlngXaft To mlngLineCount), ®lngXb(1 To mlngLineCount)
■ReDim mlngYa': To mlngLineCount), mlngYb(1 To mlngLineCount)
' операции с типами выше 4 обрабатываются чуть иначе If mlngActionType < 5 Then
ReDim mlngDxt(l To mlngLineCount), „ mlng0x2(1 To mlngLineCount)
ReDim mlngDyf(i To mlng! innConnt), _ mlngDy2(1 To mlngLineCount)
Else
продолжение
.. ' fleDlm mlrtgDx1(D>, mlngDx2(0) ReDlm mlngDyl(0). mlngDy2(0) mlngDxKO)~find*mlnglnc
mlngDx2(0)=Rnd*mlnglnc
mlngDyl(0)=Rnd*mlnglnc
mlngOy2(O)=Rnd*mlnglnc
End If
гасим курсор ^.-.л. если это ш; режим предварительного " просмотра
If gblnShow = True Then.
IngX = ShowCursor(False}
End If
основна? обработка выполняется в цикле Do
1 обновляем изображение
DoGraphics
передаем убавление системе DoEvents
Loop Until mblnQult = True
1 восстанавливаем курсор мыши, если это-п режим 1 щедварительного досмотра
If gblnShow = True Then
IngX = ShowCursor(True)
End If
в этом контексте выйти нельзя: пусть это сделает таймер tmrExltNotify. Enabled = True
End Sub■
Private Sub Form_QueryUnload(Cancel As Integer, _
VnloadMode As Integer)
применение здесь и:,;, похоже, предотвращает утечку памяти End
End Sub
Private Sub Form_Cllck()
' выходим щи щелчке, если это не режим щедварительного просмотра
If gblnShow = True Then mblnQult = True
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) ' выходим при нажатии клавиши, если это не режим 1 щедварительного досмотра
If gblnShow ® True Then mblnQult = True
End Sub
продолжение
Private Sub *»rm_MouseMоve(Button As Integer. Shift As Integer, :X As Single, Y As Single)
Static sngliiiBr As Single
' выходим, если это режим предварительного просмотра
If gblnShow = False ihen Exit Sub
' выходим в любой момент по истечении первых 0.25 секунд
If sngTimer = 0 Then
sngTimer = Timer
Elself Timer > sngTimer + 0.25 Then
mblnQuit = True.
' End If■■
End Sub
Private Sub tmrExitNotify_Timer()
Dim 'R»t дд pong
;' сообщаем системе, что заставка завершила свою работу
Ingnei = uys(.Hiurai а<пиrer an11 из. _
SPI_SETSCBEENSAVEACTIVE, 1, ByVai Ob, 0)
'пора заканчивать
find■
End Sub
реальное изображение формируется в этой процедуре
Sub DoGraphicsO
Dim inti'
Static dblColorTime As Double
' выбираем цвета линий случайным образом
If Timer > dblColorTime Then
ColorReset
If mlngLineCount ■■ 5 Then dhlColorTime = Timer + mlngLineCount * find +0.3
■ Else
*dblColorTime = Timer ■ 5 * find i- 0.3
■ End If
End If
' действуем, исходя из числа линий
. For inti = 1 То mlngLineCount'
' операции с типом ниже 5 обрабатываются специальными1 процедурами■
If mlngActionType < 5 Then
.'' запоминаем конечные точки линий на границах
г-If mlngXa(intl) <= 0 Then
mlngDxf(intl) = mlnglnc * End
End If
продолжение
■ If mlngXb(lntl) <= 0 Then
fnlng0x2(intI) = mlnglnc * find......
End If'
If mlngYaflntl) <= 0 Then.
mlngDyl(lntl) = mlnglnc * find
End If
If mlngYb(intl) <= 0 Then mlngDy2(lntI) = mlnglnc * find
End If
If mlngXa(intl) >= mlngXmax Then ' mlngDxKintl) = -mlnglnc * flnu
End If
If mlngXb{lntI) >= nlngXmax Then mlngDx2(Inti) = -mlnglnc * Rnd End If
If mlngYaflntl) >= mlngYmax Then mlngDyl(intl) - -mlnglnc * Rnd:
End If
If mlngYb(intl) >= mlngYmax Then.
mlngDy2(intI) = -mlnglnc * Rnd
End If
' увеличиваем координаты конечных точек Линий mlngXa(intl) = mlngXa(intl) + mlngDxl(intl) alngXb(lntl) = mlngXb(lntl) + mlngDx2(intI) mlngYaflntl) = ffilngYadntl) * ftlngDyl(intl) mlngYb(intl) = mlngYb(intl) + mlngDy2(intl) " у каящой пинии СВОЙ цвет ForeColor = mlngColorNum(intl)
■ Else
' ДЛЯ операций типа 5 и 6 используется ОДИН и тот же цвет ForeColor = mlngColorNum(O)
End If
' оисуам линии, учитывая тип операции
Select Case mlngActioпТуре Case 1
Line (mlngXa(intl). mlngYa(intl))- _ (mlngXb(intl), ffllngYb(intl))
'Line (-mlngXa(intl), -mlngYa(intl))- _
(-mlngXb(lntl), -mlngYb(intl))
Line (-mlngXa(intl), mlngYa(lntl))- _ ■(-mingXbflntl), mlngYb(lntl))
Line (mlngXa(lntl), -mlngYa(intl))- _ (mlngXb(lntl), -mlngYb(intl))
Case 2
Line (mlngXa(lntl), mlngYa(intl))- _ (mlngXb(intl), ftlngYb(intl)), , В
продолжение
Line (-mlngXa(intl), -mlngYa(intl))- _ (-»lngXb(intI), -mlngYb(intl)), , В
Une (-mlngXa(intl), mlngYa(intl))- _ (-mlngXb(intl), mlngYb(lntl))., , 8
Une (mlngXa(lntl), -mlngYa(lntl))- _
।(mlngXb(lntl), -mlngYb(intl)), , 8
Case 3
Circle (rolngXa(intl), mlngYa(intl)), _ mlngXb(intl)
Circle (-mlngXa(intl),-mlngYa(intl)), _ mlngXb(intl)
Circle (-mlngXa(intl), mlngYa(intl)), BlngXb(intl)
Circle (mlngXa(intl), -mlngYa(intl)), _ mlngXb(lntl)
Case 4
Line (mlngXa(lntl), mlngYa(intl))- (mlngXb(intl), -mlngYb(lntl)) Line -(-mlngXa(intl), -mlngYa(lntl)) Line -(-mlngXb(inti), mlngYb(intl)) Line -(mlngXa(intl), mlngYa(intl))
' операции с типами выше 4 обрабатываются чуть иначе
Case 5, 6
If mlngActionlype = 5 Then
Line (mlngXa(intl), mlngYa(intl))- _ (mlngXb(intl), mlTigYb(intI))r _ Sackcolor
Else
Line (mlngXa(intl). mlngYa(intl))- (mlngXb(intl), ®lngYb(intI)), „ BackColor, В
End If
If mlngXai <= -mlngXmax Then mlngDx1(0) = mlnglnc * find + 1
Bad If
If ulngXbl <= -mlngXmax Then mlngDx2(0) = mlnglnc * Rnd + 1 End If
If mlngYai -mlngYaax Then mlngDyl(O) = mlnglnc * Rnd + 1 End If
If mlngYbi <= -mlngYmax Then mlngDy2(0) = mlnglnc * Rnd + 1
End If
If mlngXai >= mlngXmax Then mlngDxl(O) = -mlnglnc - Rnd + 1
продолжение
End If
If mlngXbi >= mlngXmax Then «ingDx2(0) = -mlnglnc * find + 1
End If
If ulngYal >= ulngYmax Then fflhngOyl(O) = -mlnglnc * And + 1
End If
If mlngYbi >= ffllngYmax Then mlng0y2(0) = -mlnglnc * And + 1
End If
mlngXai = ulngXal + mlngDxl(O) mlngXbi = mlngXbi + mlngDx2(0) inlngYai = mlngYal + tnlngDyl(O)
■mlngYbi =ffllngYbi+ mlngDy2(0)
mlngXa(lntl) = mlngXai mlngXb(lntl) = mlngXbi■
mlngYa(lntl) = mlngYal mlngYb(lntl) = mlngYbi
If mlngActlonType = 5 Then
Line (mlngXa(lntl), mlngYa(lntl))- _ (mlngXb(intl), mlngYb(lntl))
Else
Line (mlngXa( inti),ffllngYa( inti))-. _ (mlngXb(lntl), mlngYuufiLi//, , в
End If
End Select
Next .
End Sub
Sub CalorResetO Dim inti ' выбираем набор цветов случайным образом If mlngActionType <= 4 Then
For Inti = 1 To mlngLlneCount mlngColorNum(lntl) =_
R6B(Rnd * 256, End * 258, And * 256) Next inti
' для операций типа 5 и 6 используем яркие цвета Else
mlngColorHum(O) - QBColor(Int(8 - End) + 8) End If
End Sub
Я предусмотрел б типов анимации с разными числом и i ■ «щи- ной линий. Код для этих режимов отличается незначительно, но эффекты создаются совершенно разные. Например, параметр В,
указанный в методе Line, заставляет одну и туже команду рисовать серик - прямоугольников вместо диагональных линий.
MYSETUP.FflM
Рис. 29-15 Форма MySetup на этапе разработкиРис. 29-15 Форма MySetup на этапе разработкиКак я уже говорил, форма MySetup активизируется после щелчка кнопки Settings, если Вы предварительно выбрали данную заставку. Последнее делается очень просто: щелкните правой кнопкой мыши рабочий стол Windows 95, выберите команду Properties и в диалоговом окне Disphr Properties перейдите на вкладку Screen Saver.
Рис. 29-15 Форма MySetup на этапе разработки
Рис. 29-15 Форма MySetup на этапе разработки
У i«tnfl.l i*s. TF4ro<ed *i Meh car*’ Q ;«№riQ fcrgiy e, wtw^Bi.siwh cw* Bich«. mirtSidd'heaiibdtow
/С РллМедяпп;Мг<Йп^л#^
A ritiUrfl-frie, capsd Ьу-th» «геен
MYSETUP.FRM — диалоговое окно, позволяющее указать один из шести режимов вывода графики и модифицировать любой из них, варьируя число линий и их толщину (в пикселах). Эти параметры считываются из системного реестра оператором tier Serving, а записываются в него оператором SaveSetting. При открытии диалоговое окно всегда показывает текущие значения параметров. Рис. 2у- i > иллюстрирует форму MySetup на этапе разработки.
Значения свойств формы и ее элементов управления приведены в таблице.
Объекты MYSETUP.FRM и значения их свойств
Тип и номер1СвойствоЗначение
Form
NameJrmMySetup
'BorderS tyle3 - Fixed Dialog
CaptionMySaver- Setup
ScaleMode3 - Pixel
Номер, указан ный в первой колонке, соотве тствует порядковому номеру объекта на форме (рис. 29-15).
Объекты MYSETUP.FRM и значения их свойств (продолжение)
Тип и номер
Свойство
Значение
Frame
1
Name
Frame 1 '
Caption
Action
Frame
2
Name
Frames
Caption
Lines
CommandButton
3
Name
cmdOK
Caption
OK
OptionButton
4
Name
optAction
Index
O'
Caption
Drifting lines, mirrored in each corner
OptionButton
■5
Name
optAction
Index
7
Caption
Drifting boxes, mirrored in each comer
OptionButton
6
Name
optAction
Index
2
Caption
Circles, mirrored in each comer
OptionButton
7
Name
optAction
Index
3
Caption
Parallelograms, twisting and turning
OptionButton
8
" Name
optAction
Index
4
Caption
A drifting line, caged by the screen
OptionButton
9
Name
optAction '
Index
5
Caption A
drifting box. frenetic ally caged
Объекты MYSETUP.FRM и значения их свойств (продолжение)
Тип и номерСвойствоЗначение
TextBox
10'
TextBox
Name
txtLineCouni
11
Label
Name
tXtLineWidth
12
Name
Labell
Label
Caption
Count:
b?.
Name
LabeU
Caption
Thickness:
Исходный код MYSETUP.FRM
' MySetup.frm
Option Explicit
Dim mstrAction As String
Private Sub Form_Load()
' центрируем эту форму
Ne.Left = (Screen.Width - Ne,Width) \ 2
Ne.Top * (Screen.Height - Ne.Height) \ 2 ' получаем текущие параметры из реестра mstrAction = GetSettingCMySaver", "Options", "Action", "1") optAction(Val(mstrActlon) - 1).Value = True txtLlneCoupt.Text = 6etSettlng("MySaver", "Options", „ "LineCount", "5")
txtLineWidth.Text = QetSettlngCMySaver", "Options", _ "LineWidth", "1")
Me.Show .■
End Sub'
Private Sub ^CllckO
dim IngN As Long
' проверяем параметр, определяющий число линий
IngN = Val(txtLineCount.Text)
If IngN < 1 Or IngN > 1OOO Then
MsgBox "Line count should be a small positive integer", _ vbExclamation, "MySaver"
Exit Sub
End If
' проверяем параметр, определяющий толщину линий
продолжение
IngN = Val(txtLlneWldth.Text)
If IngN < 1 Or IngN > 100 Then
MsgBox _
""Line thickness should be a small positive integer", _
vbExclamatiOT, "MySaver"
Exit Sub
End If
' сохраняем параметры
SaveSetting "MySaver", "Options", "Action", mstrAction
SaveSetting "MySaver", "Options", "LineCount", _ txtlineCount.Text
SaveSetting "MySaver, "Options", "LineWidth", _ txtltneWidtti.TBxt
' закрываем диалоговое окно Setup
Unload He'
Em! Sub
Private Sub optAction_Click(Index As Integer)
mstrAction = Format(Index + 1)
End Sub
Большая часть кода в модуле этой формы занята чтением и записью параметров реестра. Я указал в операторах GetSetting значения параметров по умолчанию, чтобы подстраховаться на тот случай, если какого-нибудь из параметров еще нет в реестре.
Для создания заставки скомпилируйте программу, присвоив ее исполняемому файлу расширение SCR — например, MYSAVER.SCR. Скопируйте полученный SCR-файл в каталог, где установлена Windows. После этого заставка должна появиться в списке на вкладке Screen Saver диалогового окна Display Properties. Подробнее о заставках и их компиляции см. главу 25 «Экранные заставки».
ГЛАВА 30
Средства разработки
В этой главе представлено четыре приложения. ColorBar пометает настроить монитор так, чтобы Вы видели те же цвета, что и Ваши пользователи. APIAddin — надстройка среды Visual Basic, предназначенная для поиска, копирования и вставки констант, типов и объявлений Win32-фyнкций. Metric демонстрирует один из способов, расширяющих набор средств разработки, — в данном случае большая часть функциональности приложения вынесена в справочный файл, a Visual Basic используется для тех задач, с которыми он справляется лучше всего. И, наконец, ScripDem — приложение, которое создает среду программирования на VBScript или JScript.
Приложение ColorBar
Довольно простое, оно очень полезно при настройке баланса цветов и яркости монитора. Эта программа демонстрирует также пару приемов, которые, вероятно, пригодятся Вам и при разработке других программ. Рис. 30-1 иллюстрирует приложение Colorbar 8 действии, а на рис. 30-2 показан список файлов в этом проекте.
Рис. 30-1 Приложение ColorBar в действии
Запустив ColorBat, Вы увидите форму с 16 прямоугольниками, каждый из которых закрашен одним из 16 основных цветов, определяемых функцией QBCoior. Убедитесь, что желтый блок не выглядит коричневым. (Несколько лет назад у меня был такой монитор — я от него чуть с ума не сошел!) Убедитесь также, что все цвета отличаются друг от друга, например, Вы должны заметить два от т енка серого. Щелкая форму левой кнопкой мыши, Вы поворачиваете цветовые блоки в одну сторону, а щелкая правой кнопкой, — в другую. После 16 щелчков в любом направлении блоки возвращаются в исходное положение. .
CDLORBAR.FRM
Обратите внимание на некоторые особенности этой формы. Свойству MinButton присвоено значение False ~ форму нельзя сворачивать. Всякий раз, когда пользователь изменяет ее размеры, на ней устанавливается новый масштаб для упрощения отрисовки блоков. А оператор Scale дает ошибку, если форма свернута. Так как толку от этой утилиты в свернутом состоянии никакого, я просто отключил кнопку MinButton. Как показано на рис. 30-1, кнопка сворачивания окна видима, даже если свойство MinButton установлено в False, но неактивна.
Единственный объект на форме — таймер (рис. 30-3). Поначалу я глючил код отрисовки блоков в процедуру Paint формы, и все работало, пока я увеличивал размер формы. Но приуменьшении ее размера событие Paint не возникает. Чтобы обойти эту проблему, я активизирую таймер в тех местах программы, где нужно перерисовать цветные блоки. Таймер настроен на генерацию разовых событий, т. е. после обработки события он отключается. Я присваиваю свойству Enabled таймера значение True всякий раз, когда нужно обновить цветовые блоки. А такая необходимость возникает при увеличении/уменьшении размера формы (событие Kesuc) и при щелчках штопок мыши (событие MouseDown).
Создавая это приложение, используйте таблицы и исходный код, приведенные ниже.
Объекты C0LORBAR.FRM и значения их свойств
СвойствоСвойствоЗначение
Свойство
Свойство
Form
JrmColorBar ColorBar False MonitrOLicoJrmColorBar ColorBar False MonitrOLicoName Caption М in But ton Icon
JrmColorBar ColorBar False MonitrOLico
JrmColorBar ColorBar False MonitrOLico
Timer
NameIntervalNameIntervaltmrDrawBars 1
Name
Interval
Name
Interval
Исходный код COLORBAR.FRM
Option Explicit
DimmintColorShift As Integer
Private Sub Fonn„Load()
' центрируем форму на экране
Me,Left = (Screen.Width - He.Width) \ 2
Me.Top = (Screen.Height - Me.Height) \ 2
End Sub'
Private Sub Form_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Г ' сдвигаем цветовые блоки в соответствии с тем,
' какая именно кнопка мыши нажата
mintColorShift * («LntColorShlft - Button *2+19) Mod 16 ' активизируем таймер для отрисовки цветовьк блоков tmrDrawBars,Enabled = True
End Sub■
Private Sub Form„Reslze()
" активизируем таймер для отрисовки цветовых блоков
tmrDrawBars.Enabled = True
End Sub
Private Sub tmrDrawBars_Tiiner()
Dim intX As Integer
Dim intY As Integer' отключаем таймер
tmrDrawBars.Enabled = False
' масштабируем форму (просто для удобства)
продолжение
8с = ° (4, 4)-(0, 0)
формируем цветовые блоки
For iota = 0 То 3'
For intY = 0 То 3
mintColorShift = (mintColorShift+ 1) Mod 16
Line (intX, intY)-(intX + 1. intY + 1), _
QBColor(mintColorShi.ft), BF
Next IntY
Next intX '
End Sub■'
Приложение APlAddin
Приложение APIAddin — надстройка для среды Visual Basic. Я включил его в книгу как действующий • юразец надстройки, и, кроме того, мне не нравится, как структурированы объявления API-функций в файле WIN32API.TXT, — я придерживаюсь своего стиля. На моем компьютере этот файл находится в папке C:\PROGRAM FI- LES\MICROSOFT VISUAL STUDIO\COMMON\TOOLS\WINAPI.
Признак продолжения строки позволяет размещать объявления функций на нескольких строках в удобочитаемом формате. Этой возможностью я пользовался во всей книге, и надстройка APIAddin облегчит Вам вставку API-функций в программы в том же формате. Рис, 30-4 иллюстрирует окно приложения APIAddin, где видны многостроковые объявления нескольких API-функций. Поскольку приложение установлено как надстройка, создаваемое им диалоговое окно доступно непосредственно из меню Add-Ins среды Visual Basic, что позволяет быстро находить, копировать и вставлять объявления API-функций в свои программы.
№ №r^ DeclseSubUjIpizDisbuoStiirig
ПpeoбpaзoвaниeфaйлaWIN32API.TXT
Прежде чем рассматривать само приложение APIAddin, должен заметить, что исходный файл WIN_52API.TXT надо превратить в три специфических файла, загружаемых этой надстройкой в период выполнения. Преобразование осуществляется лишь раз, но эти файлы уже есть на компакт-диске, прилагаемом к книге, так что от Вас ничего не потребуется, Однако я приведу здесь исходный код вспомогательной программы, реализующей такое преобразование, — на случай, если Бы захотите модифицировать формат файлов или получите новую версию WIN32API.TXT.
Введите код, показанный ниже, в модуль новой формы. Я не добавлял никаких элементов управления и не изменял ее свойств. Запустив программу, щелкните форму и дождитесь сообщения «Doner (Кстати, как хорошо создавать простые утилиты для себя, когда нет нужды изощряться с пользовательским интерфейсом.) Чтобы программа нашла файл WIN32API.TXT, Вам придется или скопировать его в каталог этой программы, или отредактировать в коде путь к файлу (см. проект СУТАРТТХУВРна компакт-диске, прилагаемом к книге).
Исходный код программы, преобразующей WIN32APLTXT
Option Explicit
Private Sub Form„Cliok()
Dim strA As String
Dim strT As String
Dim intState As Integer
■Dim inti As Integer
.Dim IntJ As Integer .
Dim intKAs Integer
< ' : Dim intN As Integer
Print "Working...":
■ Open App.Path A "\Win32api.txt" For Input As #1
Open App.Path 4 "\W32cons.txt" for Output As 92
Open App.Path & "\W32type.txt" For Output As #3
:Open App.Path & "\W32decl.txt" For Output As «4
, Do Until £OF{1)
Line Input #1, strA
;■ If lnStr(strA, "Const ') Then intState = 2
If InStr(LTrim(strA), "Type ") = 1 Then
strA = "Private & strA
intState = 3
’ End If: .
If InStr(LTrim(strA), "Declare ") = 1 Then intState = 4
■ End If
продолжение
. If intState = 2 Then inti = lnStr(strA, "’■’) ' If inti > 0 । neo'
strA = Trim(Left(strA, inti - 1)) End If if strA <> "" Then
inti * InStr(strA, "Public") If inti = 1 Then Print #2, strA■
Else'
■Print ft2, "Private " & strA
End If End If End If If intState = 3 Then"
■ If Left(strA, 1) = " " Then
strA = Space(4) & Trim(strA) End If intj = InStr(strA, ) If intj > 0 Then
StrA = RTrim(Left(strA, intj - 1))
End If
Print s’- strA
End If If IntState = 4 Then intN = 2.
If Tri«{strA) = "" Then Print #4, "" Else.
' отсекаем комментарии inti = InStr(strA, ")") .intj = lnStr(intl. strA,)
If intj > inti Then StrA = Trim(Left(strA, intJ. - 1)) ' отбрасываем Alias, если псевдонин совпадает .' с исходным именем функции
inti = lnStr(strA. "Alias") If inti Then
intj = InStr(intI, strA, Chr(34))
IntK = InStr(intJ + 1, strA, Chr(34)) strT = Nid(strA, intJ + 1, IntK - intJ - 1) strT = Spaced) & strT & Space(f) If lnStr(strA, strT) Then ■StrA = Left(strA, inti - 1) t_
Mid(strA, IntK + 1) End If End If
продолжение
' находим "Lib" inti * InStr(strA, " Lib") ' вставляем "Private"
Print #4, "Private Declare & _
Md(strA. 9, inti - 8) & V .
StrA = Mid(strA, inti + 1) ' находим левую скобку inti = InStr(strA, "C>
■Print #4, Left(strA, inti) & " „"
:strA = MidfstrA. inti 4 1)
' находим каждый параметр Do
inti = InStrCstrA, ", ") If inti = 0 Then Exit Do
Print #4, Space(4) & Left(strA, inti) 4 " _"
intN = intN + 1
strA = Hid(strA, inti + 2)
Loop:
находим праву» скобку
inti = InStrCstrA, T)
Print #4, Space(4) & Left(strA, inti - 1) A ”
Print #4, Mid(strA, inti) vBad If■'
End If
If intstate - 2 Then intstate = 0
.. If intstate = 3 And InStr(strA, "Bad туре") > 0 Then
intstate = 0.
Print #3. ""
Bad If
If Intstate = 4 Then
■intstate = 0
Print #4, ""
Bad If
Loop
" Close#1
■Close#2
Close #3.
;Close84
fPrint"Done"
find Sub ■
При запуске этого кода WIN32API.TXT разбивается на 3 файла: Wj2CONS.TXT (список всех констант), W32TYPE.TXT (определения структурных типов) и W32DECL.TXT (объявления API-функций). Программа удаляет все комментарии и пустые строки, чтобы уменьшить размеры этих файлов и ускорить их загрузку. Кроме того, она
добавляет в объявления ключевое слово Private для ограничения диапазона их видимости. Описание API-функций можно найти в Win32 SDK Reference Help или в другом источнике — я ведь стремился не объяснять их назначение, а максимально эффективно представить все объявления.
Создавая эти файлы, я обнаружил один интересный факт. Поначалу я оставил все модификаторы Alias в операторах Declare. Однако Visual Basic, обнаруживая совпадение псевдонима функции с ее исходным именем, автоматически удаляет слово Alias из оператора Declare. Ну а поскольку я стараюсь сократить размер файлов моя программа в таких случаях вырезает модификаторы Alias — сравните содержимое W52DECL.TXT и WIN52API.TXT.
И последнее. Храните эти три текстовых файла в том же каталоге, что и исполняемый файл приложения APIAddii потому что я, упрощая себе жизнь, отыскиваю их через свойство Арр.гат.
Состав приложения APIAddin
Приступая к разработке приложения-надстройки, лучше всего дважды щелкнуть значок Addin в диалоговом окне New Project. Тогда генерируется заготовка, содержащая одну форму и один конструктор надстройки. Именно так я и создавал APIAddin. Я опишу изменения, которые надо внести в эту заготовку.
Сначала переименуйте автоматически созданную форму sfi-mAPI- Addin и сохраните ее как APLADDIN.FRM, а конструктор Connect - как1 'ONNECT.DSK. Наконец, присвойте проекту имя APIAddin и сохраните его как APIADDIN.VBP. Список файлов в проекте после всех изменений показан на рис. 30-5.
[дмлшп^^
|й 'rriiA^t^ivri ^ЙГ-ЕАСа1Г||Ь'1|) )
D Connect fCOmed:.Erv> I
Рис. 30-5 Список файлов в проекте APIAddin
CONNECT.DSR
В этом проекте не так много кода, и конструктор (designer), сгенерированный Visual Basic, требует лишь нескольких изменений. Например, в нем необходима глобальная замена всех вхождений jrmAddln Ha/rmAPIAddin.A внес и другие изменения, поэтому внимательно проверьте каждую строку своего кода на соответствие листингу, приведенному ниже.
Исходный код CONNECT.DSR
Option Explicit. о
As BooleanAs BooleanAs VBIDE.VBEAs Office,CommandBarControl As New frmAPIAddlnAs VBIDE.VBEAs Office,CommandBarControl As New frmAPIAddln'Public FormDisplayed
As Boolean
As Boolean
As VBIDE.VBE
As Office,CommandBarControl As New frmAPIAddln
As VBIDE.VBE
As Office,CommandBarControl As New frmAPIAddln
Public VBInstance
Dim mcbMenuCommandBar
Dim mfrmAPIAddln
Public WithEvents MenuHandler As CommandBarEvents
Sub Hide()
On Error Resume Next■
FormDisplayed = False
mfrmAPIAddln.Hide
End Sub
! Show()'
OR Error Resume Next
If mfrmAPIAddin Is Nothing Then
Set mfrmAPIAddln = New frmAPIAddln
End If
' Set mfrmAPIAddln.VBInstance = VBInstance
Set mfrmAPIAddin.Connect = Me
FormDlsplayed = True mfrmAPIAddln.Show'
End Sub
1 этот метод подключает надстройку к среде VB
Private Sub AddinInstance_0nConnection(8yVal Application As Object,.
ByVai ConnectMode As AddInDesignerObjects.ext_ConnectMode, _
ByVai Addlnlnst As Object, custom() As Variant _
On Error GoTo errorjtandlet
' сохраняем ссылку на экземпляр VB
Set VBInstance = Application
' Вы хотитеобъекты, свойства и методы
надстройки, то здесь самое удачное место для точки прерывания Debuq.Print VBInstance.FullName
If ConnectMode = ext„cm_External Then
1 используется для защска этого мастера
He.Show
Else■
Set fflCDHenuCoimnandBar = AddToAddInCommandBar("APl Addin'')
1 принимаем событие'
Set Me.MenuHandler = _
продолжение
VBInstance. Events. CommandBarEvents(mobNenuCommandBar) End If
If ConnectMode = ext_cm_AfterStartup Then
.If GetSettingfApp.Title, "Settings", „
"DisplayOnConnect", "0") = "1" Then
■ .' форма показывается при подключении
Не.Show■
.End If'
' End If■
ExitSub
error_handler:.
MsgBox Err.Description
End Sub
этот метод удаляет надстройку из среды VB
Private Sub AddinInstance_OnDisconnection(ByVal RemoveMode _ As AddInDesignerObjects.ext_DisconnectMode, „ customO As Variant _
)
On Error Resume Next
" удаляем команду из меню
mcbMenuCommandBar.Delete
' завершаем работу надстройки
If FormDisplayed Then
SaveSetting Арр.Title, "Settings", "DisplayOnConnect", "1"
FormDisplayed = False
Else
SaveSetting App.Title, "Settings", "DisplayOnConnect", "0"
End If
Unload mfrmAPIAddin
Set rmAPIAddin = Nothing'
End Sub.
Private Sub IDTExtensibility_OnStartupComplete(custom() As Variant)
If GetSetting(App.Title, "Settings", _ "DisplayOnConnect", "0") = "1" Then 1 показываем форму при подключении (если параметр ' установлен)
Не.Show
Bid If.
End Sub
' эта процедурае-ызывается при щелчке менюе среде VB
Private Sub MenuHandler_Click(ByVal CommandBarControl As Object, _
18—1204
продолжение
handled As Boolean, CancelDefault As Boolean _
)
Me.Show
End Sub
Function AddToAddInCommandBar(sCaption As String) _ As Office.CommandBarCortrol■
Dim cbMenutoMandBar As Office.CommandBarControl.
. Dim cbMenu As Object
On Error CoTo AddToAddlnCommandBarErr
проверяем меню Add-Ins
Set CbMenu - VBInstance.CommandBarsC'Add-Ins")
If cbHenu Is Nothing Then
не доступно - выходим
Exit Function-
End If
' доступно - добавляем команду в меню
Sst cbMenuCommandBar = cbMenu.Controls.Add(1) ' указываем ее название
CbNenuCommandBar. Caption = sCaptiontn. .
Set AddToAddlnCommandBar = cbMenuCommandBar
Exit Function
AddToAddlnCommandBarErr:
End Function'
Назначение конструктора Connect — обеспечить связь с интегрированной средой Visual Basic. В конструкторе предусмотрен код, связывающий выполнение операций с выбором меню, щелчками кнопок на панели инструментов или другими событиями в среде Visual Basic. Обязательно изучите все пояснения и примеры в Visual Basic Books Online, чтобы понять, по каким принципам следует модифицировать конструктор Connect для своих потребностей.
APIADDIN.FRM
Форма XPlAddin отображаете окне элемента управления RichText- Вох один из трех файлов в зависимости от того, какой переключатель выбран в ее верхней части. Для большей эффективности каждый из трех файлов загружается в строковую переменную, только если он запрошен пользователем, и лишь один раз за время работы приложения. При выборе файла содержимое * ooi ве гитвующей строки копируется в свойство Text элемента RichTextBox. Рис. 30-6 иллюстрирует форму APIAddin на этапе разработки, а рис. 30-7 — в период выполнения (когда выбран список констант).
Эта форма создает экземпляр объекта Connect для подключения к меню Add-Ins среды Visual Basic и отображает константы, типы или объявления API-функций при выборе созданного элемента меню. Разрабатывая форму API Addin, используйте таблицу и исходный код, приведенные ниже.
Рис. 30-6 Форма APIAddin па этапе разработкиРис. 30-6 Форма APIAddin па этапе разработки
Рис. 30-6 Форма APIAddin па этапе разработки
Рис. 30-6 Форма APIAddin па этапе разработки
№wel? Сели " P_C’J ’CiFMtMDHV * f 5] №irvsis Const PRJ0B5TATUS - 6H0 " ftMi? Corel CBJ FEN ■ 1 Corral OEU.SFUSH ■ 2 h»d9 Const 0BJ_OC-3 Const OBJ.METADC = 4 j Const OBJ_PAL= 5 n-nleCaiSpBJI FONT-6 МмЛе Const DbTSlTMAF ■ 7 *eCwtt№ РББШМ
Const Qej„METAF3.£-S vtfeCmuOBJ UEMDC-iQ tie Corot PSJ.EXTPEN-11 Const OBJ EN^METaDC-12 C«ist OBJ.EHHMET№LE *13 nvate C _iE'k M11 n - 1 ^ const ^T LE^ I MULTIPLY-^
Рис. 30-7 Форма APIAddin. отображающая список констант
Объекты APIADDIN.FRM и значения их свойств
Тип и номер’
СВОЙСТВО'
Значение
Form
Name
p-mAPlAddm
Caption
Win32 APIs
OptionButton
BorderStyie
3 - Fixed Dialog
1
Name
optAPI
Caption
Constants
Index
0-
OptionButton
2
Name
optAPI■
Caption
Types
OptionButton
Index
I
3
Name
optAPI'
Caption
Declarations
RtchTextBox
Index
2
4
Name
rtfAPl
HideSelection
False
TextBox
Scrollbars
3 - rtjBotb
CommandButton
Name
txtFind
6
Name
cmdFind
CommandButton
Caption
Find...
7
Name
cmdCopp
Caption
Copy
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 30-6).
Исходный код APIADDIN.FRM
Option Explicit
Public VBInstance As VBIDE.VBE
Public Connect As Connect
продолжение' ‘ '
Private mstrCon As String Private mstrTyp As String Private mstrDec As String
Private Sub cmdCopy_Click().
' копируем выделенный текст в буфер обмена
Clipboard.SetText rtfAPI.SelText ' возвращаемся в проект пользователя Unload Ne End Sub
Private Sub cmdFind_Click()
Dim IngPtr As Long
Dim strFind As String
' устанавливаем фокус ввода на текстовое поле с Форматированием rtfAPI.SatFqcus
' получаем строку поиска strFind - txtFind.Text ' определяем, откуда начинать поиск If rtfAPI.SelLengtf Then
IngPtr = rtfAPI. seiatart + rtf API. Sei Length Else
IngPtr= 0
End If
' используем метод Find элемента управления RichTextBox IngPtr = rtfAPI. Find(strFind, IngPtr)
■ If IngPtr = -1 Then
MsgBox "Search text not found" End If
End Sub
Private Sub Forre_Load()
' при 'показываем объявления
optAPI(2).Value = True txtFind.Text = "" Ne.Show Me.ZOrder End Sub
Private Sub optAPI_Cllck(Index As Integer)■
Select Case Index ' константы
. Case 01
' If mstrCon = " ' Then.
■LoadUp mstrCon, "W32cons.txt""
End If
продолжение
rtfAPI.Text = mstrCon
' структурные типы
If mstrTyp * "" Then
Loadllp mstrTyp, "W32type.txt"
■■ End if.
rtfAPI.Text = mstrTyp
' объявления
If mstrDec = "" Then.
LoadUp mstrDec, "W32decl.txt"
End If
rtfAPI. Text = mstrDec: .
End Select
End Sub
Private Sub LoadUp(sA As Strinc. sFHe As String)
Open App.Path & "V & sFile For Binary As #1
sA = Space(L0F(1))I
Get #1, , sA
Close #1
End Sub■
Private ОД rtfAPI_SelChange()..
If rtfAPI. SelLength Then:-:. :
cmdCopy.Enabled = True
Else
nmdCopy.Enabled - False ;
' End If
End Sub..
Private Sub txtFind_Change()
If Len(txtFind.Text) Then
cmdFind.Enabled = True
Else■
cmdFind. Enabled = False
End If
End Sub'
Программировать текстовое поле с форматированием для этого приложения оказалось делом очень простым. Его полосы прокрутки и без всякого кода позволяют вручную «проматывать» длинный текст; кроме того, он предусматривает свой метод Find, который во многом облегчает реализацию поиска. Так, если найден какой-то фрагмент текста, элемент RichTexcBox автоматически прокручивает документ до нужного места и выделяет этот фрагмент.
Аналогичным образом специфические свойства, относящиеся к выделенному тексту, упрощают идентификацию блока текста, копируемого в буфер обмена. Подробнее на эту тему см. описание свойств SelStart, SelLength и SelText в справочной системе Visual Basic.
Компиляция надстройки
Перед компиляцией проекта любой надстройки надо установить ссылки на Microsoft Visual Basic 6.0 Extensibility и на Microsoft Office 8.0 Object Library. Для этого выберите из меню Project команду References и в диалоговом окне References пометьте флажки в соответствующих строках.
Надстройка является ActiveX-компонентом и может быть собрана либо как DLL-, либо как ЕХЕ-файл. В большинстве случаев (и в проекте APIAddin тоже) создается ActiveX DLL Выберите из меню Project команду APIAddin Properties и в диалоговом окне Project Properties в поле Project Туре укажите ActiveX DLL.
Чтобы скомпилировать надстройку, выберите из меню File команду Маке APIADDIN.DLL. Сохраните полученный файл APIADDIN.DLL в том же каталоге, что и три текстовых файла АРЕ Для их поиска я использую свойство App.Path, и программа полагает, что файлы находятся там же, где DLL. При компиляции автоматически создается запись в реестре, в которой фиксируется местонахождение DLL.
Чтобы проверить работу- надстройки APIAddin, откройте новый проект стандартной ЕХЕ-программы и выберите из меню Add-Ins команду Add-In Manager. Вы должны увиден, в диалоговом окне AddIn Manager новую строку — API Add-In. (Надстройки перечисляются по алфавиту.) Пометьте флажок Loaded/Unloaded в строке API Add-In и щелкните кнопку ОК. Вновь откройте меню Add-Ins и обратите внимание на новый элемент меню API Addin. Выберите его, и Вы увидите диалоговое окно Win32 APIs.
Назначение диалогового окна Win32 APIs — обеспечить простой и быстрый доступ к объявлениям, константам и структурам 32-раз- рядного АРЕ Выбор одного из трех переключателей ; верхней части диалогового окна приводит к отображению соответствующего вида информации. Чтобы найти определенный элемент, введите его частичное или полное имя в поле и щелкните кнопку Find. Отыскав искомый элемент, выделите нужный текст и щелкните кнопку Сору. Диалоговое окно исчезнет, и Вы вернетесь в ■. -. э проект - теперь в него можно вставить текст из буфера обмена.
Приложение Metric
Сравнительно несложное приложение Metric демонстрирует, как скомбинировать программу, написанную на Visual Basic, со справочным файлом и тем самым объединить в конечном продукте лучшие
качества каждого компонента. Именно таким способом я подготовил небольшое пособие но метрической системе мер и весов. Файл METRIC.HLP позволяет легко читать текст, показывает всплывающие окна и предусматривает гипертекстовые ссылки. В то же время файл METRIC.EXE — компонент, написанный на Visual Basic, — проигрывает звуковые файлы и видеоклипы, выполняет метрические преобразования и выводит вопросник для проверки знаний пользователя. В Window 95 справочные файлы способны на гораздо большее, чем в прежних версиях Windows, но в сочетании с Visual Basic их возможности просто безграничны,
При запуске METRIC.EXE сначала проверяет, указаны ли какие- нибудь параметры н командной строке. Если Вы запустили программу двойным щелчком ее значка или имени, то, конечно, никаких параметров нет, и в этом случае она тут же активизирует файл METRIC.HLP. Я включил в окно главного раздела несколько кнопок для возврата в программу METRIC.EXE — каждая из них передает разный набор параметров в командной строке. В зависимости от указанных параметров программа выполняет те или иные действия и завершается, а Вы продолжаете работу со справочным файлом,
METRIC.EXE позволяет воспроизводить из справочного файла звуковой файл или видеоклип, отображать ьилноцениы диалоговое окно, выполнять математические вычисления и вообще делать все, что угодно. Эти возможности и демонстрирует приложение Metric. Так, щелкнув кнопку Video Clip в окне главного раздела. Вы увидите короткий видеоклип, а щелкнув кнопку Quiz (контрольный опрос), диалоговое окно с вопросами (рис. 30-8). Просмотрите листинги с исходным кодом, и Вы убедитесь, что реализуется все это достаточно легко. Заметьте, что файлы METRIC.EXE и METRIC.HLP должны быть в одном каталоге. Кроме того, для просмотра видеоклипа CLOUDS.AVI скопируйте файл с моего компакт-диска в каталог, содержащий приложение Metric.
V А«0гглTive Г* fafiT'
• • :•• !■ *" I ! * • c ut gi A dOlj^ttkoMM fufc^r*
1" t»j 1;Г17 Г qpaIi?^ П Iwln 1T-15(.
Г ^и^КГ'т» Г АмЯП Г йИи^ Т&
Or. f"
Рис. 30-8 Контрольный вопросник, вызываемый из файла METRICHLP
Состав приложения Metric
Исполняемая часть приложения Metric состоит из трех файлов (рис. 30-9).
^ irCcc^arttCT^irt/rinJ ' Q rmMetnc ?/*<*,fat?.
Lt ’.2? ModulesI
«3 basMetrjc (Metric, bes)It
Рис. 30-9 Список файлов в проекте Metric
Модуль METRIC.BAS содержит основную часть кода этого приложения, включая стартовую процедуру Main, где на основе параметров командной строки выбирается конкретная операция. Форма METRIC.FRM демонстрирует пример контрольного вопросника, а форма CONVERT.FRM предназначена для преобразования метров и сантиметров в футы и дюймы.
METRIC.BAS
Модуль кода METRIC. BAS содержит входную процедуру Матдля той части приложения, которая реализуется на Visual Basic. Она вызывает процедуру GetParms, анализирующую параметры в командной строке и возвращающую динамический массив параметров (gslr- Рагт) — его зачастую проще использовать, чем функцию Command, встроенную в Visual Basic. Даже при отсутствии параметров в командной строке gstrPamfO) сообщает полное имя исполняемого файла.
На основе параметров командной строки Main воспроизводит мультимедийный файл, показывает вопросник или выполняет метрические преобразования. Вы можете легко расширить список анализируемых параметров, модифицировав оператор Select Case.
Исходный к од METRIC. BAS
Option Explicit
Private Declare Function WinExec _
Lib kern9132" ( _
ByVal IpCmdLine As String, _
ByVal nCmdShow As Long'
. ) As Long
Private Declare Function nciExecute _
Lib "winmm.dll" ( _'
ByVal IpstrCommand As String_
) As Long
продолжение'
Public gstrParm() As String.
Sub Main()■
GetParms''■
Select Case UBound(gstrParm)
.
TakeAction
TakeAction gstrParm(f).
TakeAction gstrParm(l), gstrPa™<2) End Select::
End Sut>
' Private Sub TakeActionf Optional vntCmd, Optional vntFil) ' если параметров нет, открываем справочный файл If IsMissing(vntCmd) Then
WinExec "Winhelp.exe " & Арр.Path & "\Hetric.hlp", 1 Exit Sub
End If
' первый параметр определяет выполняемое действие
Select Case UCase(vntCmd)
отображаем форму для преобразований единиц измерения
Case■
frmConvert.Show
' проигрываем звуковой файл
Case "WAV"■
Select Case vnttii
Case 1
moi Execute "Play Sound1.wav"
End Select
' воспроизводим видеоклип
■ Case "AVI"
Select Case vntFil
Case 1
mciExecute "Play Clouds.avi"
End Select
показываем форму контролвного вопросника■
Case "®IZ".
f rmMetric.Show
End Select
End Sub.
Private Sub GetParmsM
Dim strCmd As String'
strCmd = Trim,. л & Space(l) 4 Command)
продолжение*'
gstrPar®() = Split(strCsd)' "
If Right (Арр. Path, 1) о "V Then
gstrParm(O) = Арр. Path & "\" & App.EXEName
Else
gstrParm(O) = App.Path & App.EXEName'
End If
End Sub'.
В METRIC.BAS используются две API-функции: одна — для проигрывания мультимедийных файлов, другая — для активизации файла METRIC.HLP. Заметьте, что я применил новый способ запуска справочного файла (о других способах см. главу 17 «Справочные системы, подсказки и мл jepa»;. API-функция WinExec позволяет запустить из программы любое Windows-приложение, в данном случае — WinHelp. Но Вы можете запустить с ее помощью хоть Calculator (иногда это удобно).
Процедура TakeAction демонстрирует работу с необязательными параметрами, допуская вызов как с одним или двумя из них, так и вообще без параметров,
METRIC.FRM
Эта форма выводит пример вопросника при щелчке кнопки Quiz в справочном файле. (О том, как справочный файл обрабатывает этот щелчок, я расскажу чуть позже.) Для упрощения программы я создал вопросник без оценок; в нем появляются три вопроса: один предполагает ответ типа да/нет» (True или False), другой требует письменного ответа (в текстовом поле), а третий — выбрать ответ из нескольких вариантов. Рис. 30-Ю иллюстрирует форму Metric на этапе разработки; как выглядит эта форма при вызове из справочного файла, было показано на рис. 30-8.
Создавая данное приложение, используйте таблицу и исходный код, приведенные ниже.
Объекты METRIC.FRM и значения их свойств
Тип и номер1 Свойство Значение
Г'опп
NameJnnMelrk
■CaptionMetric System - Quiz, 1
' Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 30-10).
Объекты METRIC.FRM и значения их свойств (продолжение)
Тип и номерСвойствоЗначение
Label
1
Name
Label!
Caption
1) A meter is slightly longer than a yard
Label
2
Name
Labels
Caption
2) A hectare is equal to bow many acres?
Label
3
Name
Labels
Caption
3) Select I be correct prefix multiplier ralue(s)
from the following:
Option 1
4
Caption
True
Options 5 lextl
Caption
False
6
CheckI
Text
пусто
7
Caption
exa 10'17
Checks
8
Cbeck3
Caption
giga ЮЛ9
9
Cbeck4
Caption
femto 1C-I5.
10
CbeckS
Caption
krypia I Cl 8
11
СЬесМб
Caption
detect HCl
12
CommandButton
Caption
alto IO1'-18,
13
Name
cmdOK
Caption
OK
Исходный код METRIC! НМ
Option Explicit
Private Sub cmdOK_Cllck()
nsgbox "This axample quiz is not graded."
Unload Me'
End Sub■
CONVERTFRM
Форма Convert — диалоговое окно, позволяющее преобразовывать метры в футы и сантиметры в дюймы. И вновь пример крайне прост. Я включил в -ч л диалоговое окно лишь самое необходимое для того, чтобы дать точку опоры и показать принципы использования Visual Basic для добавления вычислительных средств в справочный файл. Рис. 30-11 иллюстрирует форму CONVERT.FRM на этапе разработки, а рис. 50-12 — в период выполнения (после щелчка одной из кнопок в разделе Conversions справочного файла).
Рис. 30-11 Форма Convert на этапе разработки
ErtMwi- Гог*j |У " '
■fqw**ltnt Wt Г" “■ Eo-jvi^ ^cMs J '~
Рис. 30-12 Диалоговое окно Metric Conversions, вызываемое
из справочного файла Metric
Создавая это приложение, используйте таблицу и исходный код, приведенные ниже.
Объекты CONVERT.FRM и значения их свойств
Тип и номер1
Свойство
Значение
Form
Name1
JrmConveft
Caption
Metric Conversions
Borderstyle
3 - Fixed Dialog
Label
1
Name
IblMeters'
Caption
Enter meters:
Label
2
Name
IblFeet
Caption
Equivalent feet:
Label
S
Name
IblCentimeters
Caption
Enter centimeters:
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 30-11).
Объекты CONVERT.FRM И ЗНачеНИЯ ИХ СВОЙСТВ (продолжение)1
Тип и номер
Свойство
Значение
Label
4
Name
Iblhtches
TextBox
Caption
Equivalent inches:
5
TextBox
Name
txtMeters
6
Name
txtFeet
TextBox
Enabled ‘
False
7
TextBox
Name
txtCentimeters
8
Name
txtinches
Enabled
False
ИсходныйкодСОМУЕРТ.РРМ
Option Explicit:
Private Sub Form_Load()
txtMeters.Text = "0"
txtCentimeters.Text * "0"
End Sub■.
Private Sub txtCentimeters_Change()
txtinches.Text = CStr(CDbl(txtCentimeters.Text) *
0.393700787402)
End Sub
Private Sub txtHeters_Change()
txtFeet.Text = CStr(CDM(txtMeters.Text) * 3.28083989501) End Sub
METRIC.HLP
Может быть, Вас заинтересовало, как METRIC.EXE вызывается из файла METRIC.HLP с разными параметрами командной строки. Объяснять все детали подготовки справочного файла я, конечно, не стану (это выходит за рамки гнигпк но опишу важнейшую часть процесса. Я создал файл METRIC.HLP с помощью утилиты Robo- Help компании Blue Sky Software. (RoboHelp — отличное средство, помогающее «украсить» 32-разрядные приложения Visual Basic полноценными 32-разрядными справочными файлами, в которых при-
меняются все технологические новинки.) RoboHclp упрощает связывание макросов с кнопками или «горячими участками», и эти макросы играют ключевую роль в запуске внешних приложений, например METRIC.EXE. Кнопки и макросы можно включить в справочный файл и с помощью утилиты Microsoft Help Workshop, поставляемой с Visual Basic (HCW.EXE). Кроме того, Вы можете вставить код напрямую, используя механизм сносок в документе формата RTF. Описание соответствующего синтаксиса см. в справочной системе Microsoft Help Workshop. Вот текст включенных в исходный код METRIC.RTF макросов для кнопок, расположенных в окне главного раздела:
Conversions:л ; :
{Dutton Meters to feet, ExecProgram("Metric.exe M2F")}
{button Centimeters to inches, ExecProgram("Metric.exe C2I")}■•
To hear a {button Sound, ExecPrograro("Metric.exe wav 1")} click this button,
and to see a sample {button video clip, ExecProgram("Metric, exe avi TV click here.
When you're ready, click this {button Quiz, ExecProgram("Metric.exe QUIZ , is button.
The Heliic Syxteni
tS£^.k!dH»iI
Cw*ee*onir
^^2^^ii!i£^5iJ
WffiWrl
Рис. 30-13 Главный раздел справочного файла METRICHLP
Компилятор справочных файлов обрабатывает макросы, заключенные в фигурные скобки, и создает кнопки. которые запускают внешнее приложение (METRIC.EXE) с передачей соответствующих параметров в командной строке. Подобные макросы — стандартная функциональность справочных файлов. В документации RoboHelp они рассматриваются очень подробно. Рис. 30-13 иллюстрирует, как выглядит главный раздел справочного файла. В нем появляются все
кнопки, содержащие макросы для вызова приложения METRIC.EXE с разными параметрами.
Приложение ScripDem
Элемент управления Script не поставляется с Visual Basic, но его можно получить с Web-узла Microsoft {www.microsoft.com).После установки этого элемента Вы можете добавить его в свое приложение, выбрав из меню Project команду Components и пометив флажок в строке Microsoft Script Control 1.0.
Элемент Script выполняет исходный код либо VBScript, либо JScript, что дает возможность создать на Visual Basic приложение — автономную среду программирования на VBScript/JScript. Мое приложение ScripDem, хоть и не является полноценной средой программирования, демонстрирует, как создается такая среда, и предоставляет базовую функциональность, позволяющую работать с несложными макросами VBScript или JScript.
Основная форма ScripDem содержит элемент TextBox, предназначенный для ввода исходного кода, и элемент Script, обеспечивающий интерактивное выполнение этого кода. ScripDem обрабатывает синтаксические ошибки и показывает, в чем заключается проблема и на какой строке она возникла. Ошибки периода выполнения отслеживать труднее, но все зависит от их природы. Советую сначала изучить сам язык VBScript, чтобы по возможности избегать ошибок в исходном коде.
Sii>MnMl]Sii>MnMl]C-S#*2iB '2)тед-'*5йеА = "&А rftg«insgbvbCrLf Ь "Side В ±иуВ rrng-er»gtvbCrLfb"HHroteniise ■ "%с MspBох глад. /ScnpD m я феи1'C-S#*2iB '2)тед-'*5йеА = "&А rftg«insgbvbCrLf Ь "Side В ±иуВ rrng-er»gtvbCrLfb"HHroteniise ■ "%с MspBох глад. /ScnpD m я феи1'End SubEnd SubЦдпЛфгОн». HypulЦдпЛфгОн». Hypul
Sii>MnMl]
Sii>MnMl]
C-S#*2iB '2)
тед-'*5йеА = "&А rftg«insgbvbCrLf Ь "Side В ±иуВ rrng-er»gtvbCrLfb"HHroteniise ■ "%с MspBох глад. /ScnpD m я феи1'
C-S#*2iB '2)
тед-'*5йеА = "&А rftg«insgbvbCrLf Ь "Side В ±иуВ rrng-er»gtvbCrLfb"HHroteniise ■ "%с MspBох глад. /ScnpD m я феи1'
End Sub
End Sub
ЦдпЛфгОн». Hypul
ЦдпЛфгОн». Hypul
5td»i х 3
^№*bTiu4elw.S-
Рис. 30-14 Приложение ScripDem в действии (выполнена процедура. написанная на VBScript)
Приложение ScripDem попутно демонстрирует применение элемента Toolbar, изображения кнопок которого хранятся в элементе ImageList. Его кнопки дублируют функциональность меню. но кнопка Run, по сути, просто вызывает одноименное меню как контекст-
ное. Свойство ToolTipText сообщает, для чего предназначена соответствующая кнопка, а свойство Key идентифицирует «нажатую» кнопку (их печения для каждой кнопки одинаковы).
Меню Run (как и кнопка Run) позволяет выбрать либо VBScript, либо JScript — по умолчанию используется VBScript. Рис. 50-1 I иллюстрирует приложение ScripDem в действии (после щелчка кнопки Run появилось окно с результатами выполнения тестовой процедуры), а на рис. 50-15 показан список файлов в проекте ScripDem.
" " Q. гЦкисСяыЫ.ГппУ-’ ■ .
hmScrlppero (ScdpOrm.fnri)
Рис. 30-15 Список файлов в проекте ScripDem
SCRIPDEM.FRM
Как показано па рис. 30-16, форма ScripDem содержит связанные элементы управления Toolbar и Image List, а также TextBox (для редактирования исходного кода), Script (для выполнения этого кода) и ; ommonDialog (для загрузки и сохранения файлов).
!Ехзмшм№иантщш'
> £jt Я* d*
Рис. 30-16 Форма ScripDem на этапе разработки
Создавая это приложение, используйте таблицы и исходный код, приведенные ниже. Кроме того, установите свойство JmageLisi элемента Toolbar как iislbolbar. а затем добавьте к HbTbolbar(экземпляру элемента Toolbar на форме) кнопки в соответствующем порядке. Чтобы сгруппировать кнопки по назначению, я включил и разделители, но они не обязательны. ,
Меню и их элементы в SCRIPDEM.FRM
Название
Имя
Уровень вложения
Параметр Enabled
S-File
mnuFile
0
True
&New
mnuNeiv
1
True
СОреп...
mnuOpen
■ 1
True
&Save.
mnuSaue
1
True
—\
mnuFileSepl
1
True
(FPrint
mnuPnnt
1
True
—
mnuFileSep2
1
True
EtFxit-
mnuExit
1
True
S-Edit
mnuEdit
0
True
СиЙИ
mnuCut
1
True
®€opy
innuCopy
1
True
SPaste
mnuPaste
1
True
&Run
mnuPun
0
True
S-Visuai Basic
mnuVB
1
True
&Java
mnujava
1
True
—
mnuRunSepl
1
True
^Update Procedures
mnuUpdate
1
TVue
—
mnuRunSep2
1
True
SHelp
mnuHelp
0
True
Contents
mnuContents
1
True
@Searcbfor Help on...
mmi'Search
1
True
—
mnuHelpSepl
1
True
S-About...
mnuAbouf
1
True
Объекты SCRIPDEMfRM и значения их свойств
Свойство
Значение
Form
Name
jrmScripDem
Caption
Script Demo
Icon
Toolbar
Wrencb.ico
Name
tlbToolbar
ImageList
ImageList
ilsToolbar
Name
ilsToolbar
Images
New, Open, Save. Print, Cut. Copy, Paste и Run
CommonDialog
Name
cdlOne
Объекты SCRIPDEM.FRM и значения их свойств (продолжение)
Свойство
Значение
TextBox
Name
txtModulej. -
Scrollbars
3 - Both'■
MultiLine
True
Script
Name
scrVR'
Language
VBScript"
Исходный код SCRIPDEM.FRM
• SCRIPDEM'
Option Explicit
01м mfilScript As New Scripting. FileSysteniObject
Dim mtexScript As TextStream
Dim mblnFileChanged As Boolean: .
Private Sub Form_Load()
' центрируем эту форму
' Ha.Move (Screen.Width - Me.Width) \ 2, _
(Screen.Height - Me.Height) \ 2
End Sub■
. Private Sub Forfi„Resize()
' изменяем размер области редактирования так,
' чтобы она заполнила всю форму
, txtModule.Move 0, tlbioolbar.Height, _
Me.ScaleWidth, Me.ScaleHeight - tlbioolbar.Height
End Sub
Private Sub mnuAbout„Click()
' настраиваем свойства и отображаем форму About
About.Application = "ScripDBm"
About.Heading = _
"Microsoft Visual Basic 6.0 Developer’s Workshop" About. Copyright = "1998 John Clark Craig and Jeff tebb" About.Display
End Sub
Private Sub mnuJava_Click() мпиУВ. Checked - False oinuJava, Checked = True scrVB.Language = "JScript"
End Sub.
продолжение'
Private Sub mnu0pen_ClickO
' если предыдущий файл изменился, предлагаем его сохранить If mblnFileChanged Then mnuNew_Click
' открываем стандартное диалоговое окно Орел File cdlOne.DialogTitle = "Open File" cdlOne.ShowOpen
' если пользователь не выбрал файл, выходим из процедуры If cdlOne. FileName = ”" Then Exit Sub
' создаем текстовый поток, используя объект FileSystemObject . Set mtexScript = mfilScript.OpenTextFile( „
cdlOne.FileName, ForReading, True) ' если файл пуст или не существует, ничего не читаем If mtexScript. At End Of St ream Then " очищаем текстовое поле txtNodule.Text =
Else ' загружаем текст из потока в текстовое поле txtNodule.Text = ffltexScript.ReadAII
End If
■' закрываем файл и текстовый поток
Set mtexScript = Nothing
' сбрасываем флаг mblnFileChanged mblnFileChanged = False'
' обновляем список процедур.
mnuUpdate„Click End Sub
Private Sub mnuPrint_Click() ’ просто сбрасываем дамп на принтер Printer. PriirtxtModule.Text Printer. EndDoc
End Sub
Private 5 fflfiuSave_Click()
' открываем стандартное диалоговое окно Save File cdlOne.DialogTitle = "Save File" cdlOne.ShowSave
' при отмене выходим If cdlOne.CancelError Then Exit Sub
' создаем текстовый поток, используя обьект FileSystemObject Set mtexScript = mfilScript.OpenTextFile( _
cdlOne.FileName, ForWriting, True) '' записываем содержимое текстового поля в текстовый поток
mtexScript.Write txtNodule.Text
' закрываем текстовый поток И объект FileSystemObject Set mtexScript = Nothing
продолжение
сбрасываем флаг mblnFileChanged mblnFileChanged = False ' обновляем список процедур roRuUpdateCHGk
End Sub
Private Sub mnuRunSep2„Click(Index As Integer) ' перехватываем ошибки периода выполнения On Error GoTo RunErr ' выполняем выбранную процедуру
scrVB.Run scrVB. Procedures(Index).Name
Exit Sub
RunErr:
HandleError
End Sub
Private Sub mnuSearch_Click()
cdlOne.HelpFile - App.Path & "\.,\..\Help\Mvbdw.hip" cdlOne.HelpCommand = cdlHelpPartialKey cdldne.ShowHelp
End Sub
Private Sub mnuUpdate_Cllok( )
' контролируем ошибки при синтаксическом разборе кода (в момент его загрузки)
On Error Goto LoadErr
' удаляем предыдущий код
scrVB.Reset
добавляем новый код в элемент Script
scrVB,AddCode г^иллло TAXt
Dim proItem As Procedure
Dim intCount As Integer ' выгружаем элементы меню
For intCount - 1 To mnuHunSep2.UBound
Unload mnuRunSep2(intCount)
Hext intCount
intCount = 0
' вносим имена процедур в мен»
For Each proItem In scrVB. Procedures
intCount = intCount + 1
Load snnuRunSep2( intCount)
mnuRunSep2(lntCount).Caption = proItem.Name
Next proItem
Exit Sub
LoadErr:
HandleError
End Sub
продолжение
Private Sub тппМяш minkn;
Dim intSave As Integer
ПрОЬнряея, илмснплоя ЛИ файл
CheckForChange
txtModule.Text * "" mblnFileChanged = False
' обновляем список процедур
mnuUpdate„Click
End Sub
Private Sub ’inuExit_CLick()
Dim intSave As Integer
проверяем, изменился ли файл
CheckForChange
" выгружаем форму (это завершает работу приложения) Unload Не
End Sub
Private Sub mnuVB_Click().;.
mnuVB.Checked = True:
mnuJava,Checked = False•,
scrVB.Language - "VBScript"др
End Sub. „
Private Sub tlbToolbar_ButtonClick(ByVal Button As
MSComctlllb. Button)
Select Case Button.Key
Case "New"■
«inuNew_Click
Case "Open" iiinuOpen_Click
Case "Save"■
mnuSave_Click
Case "Print"
mnuPrint_Click
Case "Out"'■
txtModule.SetFocus
; SendKeys ""x",
,:v Case "Copy"
txtModule,SetFocus
SendKeys "~c"
Case "Paste"'.
txtModule.SetFocus
SendKeys "~v"
Case "Run"
PopupMenu mnuRun
продолжение■
■ Case Else'
MsgBox "Unknown toolbar button clicked!", vbCritical End Select
End Sub,
b' Private Sub txtModule„Change()■
1' устанавливаем флаг mblnFileChanged
mblnFileChanged = True
End Sub■
Private Sub CheckForChangeO
If mblnFileChanged Then,
Dim intSave As Integer ::
' если файл изменился, щедлагаем сохранить изменения
intSave = MsgBox("File has changed, save changes?", _ vbYesNo, "Save Changes?")
;' если пользователь решил сохранить изменения,
vCCF ' активизируем обработчик для меню Save
■If IntSave - vbYes Then
mnuSave_Cllck
Bad If. . ;
У lb End If'■
■ End Sub.■ ■
A Private Sub HandleError().1'1
. Dim intCount As Integerфар.
.. With scrVB Error.
' перемещаем курсор в начало"
; : . ' ■ txtModule, SelStart = О
;.,..' перемещаем курсор на строку, где произошла ошибка
For intCount = 1 То . Line
SendKeys Диоипр, True
Next intCount
■!"' перемещаем курсор в столбец, где произошла ошибка
For IntCount = 1 То .Column
SendKeys "{Right)", True'
Next intCount
■' показываем информацию об ошибке
.If .Number о 0 Then
тMsgBox JointArray(.Source, .Number, .Description)),
vbCritical
U . End If
End With
продолжение
Private Sub mnuContents„Click()
cdlOne. HelpFile = Арр.Path & "V-V .\H01p\Mvbdw.hlp“
cdlOne,HelpCommand = cdlHelpContents
cdlOne.ShowHalp
End Sub
ГЛАВА 31
Дата и время
Т?этой главе представлено несколько приложений, которые относятся к задачам обработки даты и времени: календарь (VBCal),упрощающий выбор дат, аналоговые часы (VBClock), внешне очень броские и показывающие текущее системное время, а также утилита, позволяющая позвони ть в NIST (National Institute of Standards and Technology — Государственный институт стандартов и технологии) и точно настроить системные часы компьютера.
Приложение VBCal ■
Выбор даты — операция очень распространенная в большинстве деловых приложений. Проще всего, конечно, просто набрать дату в нужном шик-. Но тогда придется проверять ее корректность и учитывать региональные стандарты (что, например, означает строка 3/5/99— 5 марта или 3 мая?). Рациональнее показать пользователю календарь, на котором он просто выберет требуемую дату. Приложение VBCal демонстрирует три варианта ввода даты. _
Это - мастер!
Я разработал это приложение в виде простейшего мастера, что само по себе интересно. Метафора мастера, впервые примененная Microsoft в нескольких продуктах, стала общепринятым стандартом во взаимодействии приложений с пользователем при любых последовательно выполняемых операциях. В проекте VBCal содержится пять — внешне практически одинаковых — форм, используемых при пошаговом выполнении действий под руководством мастера. В модуле кода VBCALWIZ.BAS всего три строки кода, обеспечивающие доступность даты, выбранной пользователем, всем формам. На каждой форме присутствуют по две кнопки и элемент оформления (в верхней половине), я также даются пошаговые инструкции. Формы последовательно предлагают ввести или выбрать дату с помощью трех элементов пользовательского интерфейса. Рис. 31-1, 31-2, 31-3, 31-4 и 31-5 иллюстрируют формы мастера на разных этапах его работы.
Рис. 31-1 Мастер VBCal первый этап (вводный экран) г it гп । л п ■■■миам11ив^
VBCAL WIZARD
Рис. 31-2 Мастер VBCal: второй этап (запрос первой даты с применением DTPicker)
jj Deeeefa# 1996 ^jj Deeeefa# 1996 ^VBCAL WIZARD
jj Deeeefa# 1996 ^
jj Deeeefa# 1996 ^
$ti»c? m eeq«Q tifli u^ Чч мгг^е^'^'^т^
■ S 3 )C Я tf !. 'a is 16 17 ie it s -J -1 22 23 2* 2? 7 2b 23 30
jF ' roijef. G/12/ВД
Рис. 31-3 Мастер VBCal: третий этап (запрос второй даты с применением MontbView)
VBCAL WIZARD
Рис. 31-4 Мастер ЛЪС'п!: четвертый этап (запрос третьей даты с применением Calendar)
VBCAL WIZARD
Рис. 31-5 Отчет, выводимый мастером VBCsle конце операции
Элемент управления DTPicker
На втором этапе, после вводного экрана пользователю предлагается указать дату через элемент управления DTPicker, который позволяет либо набрать ее в поле, либо выбрать из небольшого календарика, активизируемого наподобиераскрывающегося списка. Свойство CustomBonnai этого элемента дает возможность определять разнообразные форматы да 1i; (и времени). DTPicker сам обрабатывает все, что связано с выделением полей, проверкой допустимости данных и отображением окна с крошечным календарем. На рис. 31-2 видно, что я использовал собственный формат даты, который включает подсказку и обеспечивает ввод всех четырех цифр, относящихся к году, — это позволяет избежать так называемой проблемы 2000 года. (Она вызвана тем, что раньше под это значение отводили всего два разряда.)
Элемент управления Monthview
На третьем этапе дату можно выбрать с помощью элемента MonthView (рис. 31-3). Вы наверняка заметите, что по умолчанию его окно
с календарем на 1 месяц очень похоже на то, которое отображается элементом DTPicker.
MonthView позволяет управлять своими внешним видом и функциональностью. Так, записав в свойство MonthColumns или MonthRows значение, большее 1, Бы заставите его отображать календарь на соответствующее число месяцев одновременно, располагая их столбиком или друг над другом. Поэкспериментируйте и со свойством Font: простым изменением шрифта можно создать множество стилей оформления календаря.
Элемент управления Calendar
На четвертом этапе дата вводится еще одним способом — с применением элемента Calendar (рис. 31-4). Он тоже показывает календарь на 1 месяц, но имеет гораздо больше свойств, связанных с форматированием, и допускает изменение размеров.
Рядом с элементом Calendar я разместил небольшую кнопку (с меткой Today), чтобы можно было мгновенно перейти к текущей дате. Кроме -ин,добавил код в обработчик события ОЫСНск, что обеспечивает выбор дат двойным щелчком. Рис. 3 i -6 иллюстрирует элемент Calendar, на котором выбрана дата «1 января 2001 года».
January 3001 jjano^p 31 (ай' z
Рис. 31-6 Элемент управления Calendar в действии
VBCALWIZ.BAS
Формы VBCal взаимодействуют друг с другом через 3 открытых переменных, объявленных в модуле VBCALWIZ.BAS. Date-перемен- uuegdtmDatel,gdtmDate2 и gdtmTjated хранят последние даты, выбранные с помощью каждого из трех элементов управления, так как пользователь может переходить с одной формы на другую в любом направлении.
На рис. 31-7 показан список файлов в проекте VBCal. Форма JrmVUCalWiz/является стартовой. Большинство других ф.Ф.вш в этом проекте определяют серию окон, открываемых мастером при пошаговом выполнении инструкций. У всех форм одинаковый размер, одни и те же кнопки и элементы графического оформления —
тем самым создается иллюзия, будто все операции проходят на единственной форме.
Г " ti'Г " ti'
Г " ti'
Г " ti'
[ ^Ьс_^1*1^ъ "(IrbceLvlHi J
-о frmVECahVizl (VBCeWizj frmj . tj. FrmVBCalWi22(VBCaW62.fTni) ,। ^ РгтА<&:^№Э(УВСе1М^
= • • • й FrmVBCdvq'e* ^КаЖг4 fnqj ■ *• El fmwecalWlz5(vac»WB5.frm) g f& №&4к
-A !>»«»№ (WMWb , bej
Рис. 31-7 Список файлов в проекте VBCal
Создавая набор форм для мастера, используйте таблицу и исходный код, приведенные ниже.
Объекты VBCAWIZ1.FRM и значения ИХ свойств
Свойство
Значение
Form
Name Caption BorderStyle Image
jrmVBCaWtzl
VBCal Wizard - Step 1 3 - Fixed Dialog
Name
Picture label
imgCal.
VBCALBMP ''
Name
CommandButton
IblPrompt.
Name Caption Default
CommandButton
cmdNext■
&Next >
True■- ■
Name Caption Cancel
cmdCancel ■ ■
CCancel"
True
Исходный код VBCAWIZ1.FRM
Option Explicit
Private Sub cmdCancel_Click{)
. Unload He
End Sub:
продолжение
Private Sub cmdNext_Click() f™VBCalWiz2, Show
Unload Me
End Sub
Private Sub Form_Load()
' устанавливаем сегодняшнюю дату
If gdtmDatel = 0 Then gdtmDatel = Date gdtmDate2 = Date .
■gdtmDateS = Date■
End If ■
' выводим текст приглашения
L IblPrompt.Caption = "This example wizard & "demonstrates three controls that let & _ "you select dates as it helps you " & _ "calculate the number of days " A _■
"between two dates."
" центрируем форму
Move-on.Width - Width) \ 2, (Screen.Height - Height) \ 2
Show
cmdNext-SetFocus
. End Sub
Объекты VBMWIZ2.FRM и значения их свойств
Свойство
Значение
Form
Name
ргтУВСа1\И"2
Caption
VBCal Wizard - Step 2
BorderStyle
3 - Fixed Dialog.
Image
Name
imgCnl
Picture
VBGALBMP
Label
Name
IblPrompt
Caption
Select thefirst date using the DTPicker control™
CommandButton
Name
cmclNext
Caption
&Next>, ....
Default
True
см. след. стр.
Объекты VBCAWIZ2.FRM и значения их свойств (продолжение)
■Свойство
Значение
CommcmdButton
Name
cmdBack
Caption
< ABack
DTPicker
Name
dtpDemo
Format
3 - dtpCustom
CustomFormat
DctieJormatmm/dd/ypyydMM/dd/yyyy
Исходный код VBCAWIZ2.FRM
Option Explicit
" Private Sub ci»dBack_Cliok() gdtBDatel s dtpDemo.
f rutVBCalWizI. Show.
Unload Me,
End Sub
Private Sub cisdNext_Cliek() gdtmDatet = dtpltemo frmV0CalWiz3. Show Unload Me
End Sub
Private Sub Form_Load() ’ центрируем форму Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \2 Show dtpDemo = gdtmQatet cmdHext.SetFocus
End Sub
Объекты VBCAWIZ3.FRM и значения их свойств
Свойство
Значение
Form
Name
JrmVBCa[Wiz3
Caption
VBCal Wizard - Step 3
BorderStyle
3 - Fixed Dialog•
Image
Name
imgCal
Picture
A^CAI-BMP
Объекты VBCAWIZ3.FRM и значения их свойств
Свойство
Значение
Label
Name
IblPrompt'
Caption
Select the second date using tbe MontbVtew control...
Coni mandButlon
Name
CindNext. ■
Caption
S-Next >'
Default
True
CommandButton
Name
cmdBack,
Caption
< g-Back1'
MonthView
Name
mpmDemo
Исходный код VBCAWIZ3.FRM
Option Explicit
Private Sub cmdBack_ClickQ
gdtmDate2 = mwDemo. Value
frsiVBCalWizZ.Show
Unload Me
End Sub
Private Sub cmdNext_CliekO
gdtmDate2 * mvwDemo.Value<
frmVBCalWiz4.Show
Unload He■■■■■■■■
End Sub
Private Sub Form_LoadQ
' центрируем форму
Move (Screen.Width - Width) \ 2, (Screen.Height - Eteight) \ 2
Show
mvwDewo,Value = gdtmDate2
cmdNext.SetFocus
End Sufi
Private Sub nvwDamo_DateDblClick(ByVal DateDblClicked As Date)
' разрешаем выбирать дату двойным щелчком
cmdNext_Cllck
Bad Sub
Объекты VBCAWIZ4.FRM и значения их свойств
Свойство
Значение
Form
Name
JrmVBCaTWiz4
Caption
VBCal Wizard - Step 4
Borders ryie
3 - Fixed Dialog
Image
Name
imgCal
Picture
VBCALBMP
Label
Name
IblPrompt
Caption
Select a third date using the Calendar control...
CommandButton
Name
cmdNext
Caption
&Next>
Default
True
CommandButton
Name
crndBack
Caption
< Si-Back
CommandButton
Name
cmdToday"
Caption
^Ibday
Calendar
Name
calDemo
Исходный код VBCAWIZ4.FRM
Option Explicit
Private Sub calDeino_DblClick()
' разрешаем выбирать дату двойным щелчков cmdNext_Click
End Sub
Private Sub cmdBack_Click() gdtmDateS = calDemo.Value frmVBCalWiz3.Show
Unload Me
End Sub
Private Sub -rdNext_Click() SdtmDateS = calDemo.Value
продолжение
frmVBCalWlzS.Show .
Unload Me.
End Sub
Private Sub crndToday_Click()
' сбрасываем выбранную дату и устанавливаем текущую calDemo.Value = Date
End Sub
Private Sub Form_Load()
' центрируем форму
Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2 . calDemo.Value = gdtmDateS
End Sub
Объекты VBCAWIZ5.FRM и значения их СВОЙСТВ
Свойство
Значение
Form
Name
JrmVBCalWizS
Caption
VBCal Wizard- Pinal Report
BorderStyle
3 - Fixed Dialog
Label
Name
IblReport
CommandButton
Name
cmdBack
Caption
< (S-Bacfe
CotnmandButton
Name
cmdOK
Caption
еок
Исходный код VBCAWIZ5.FRM
Option Explicit
Private Sub c»dOK_Click()
Unload He
End Sub
Private Sub cmdBack_CliCk() frmVBCalWiz4,Show
Unload Me
End Sub
продолжение
Private Sub Form_load() ' центрируем форму
Hove (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2 IblReport.Caption = "Number of days separating " _
"these dates:" & vbCrlf 4 vbCrlf 4 "Between " „
Format$(gdtmDatel, "min/dd/yyyy") & " and " _
Format$(gdtmDate2, "mm/dd/yyyy") & 11 is " _
4 Abs(gdtmDate1 - gdtmDate2) 4 vbCrlf 4 "Between "
Format$(gdtmDate2. "mm/dd/yyyy") 4 " and
Format$(gdtiiiDate3, "mm/dd/yyyy") 4 " is:
Abs(gdtmDate2 - gdtmDateS) & vbCrlf 4 "Between ' _
Format$(gdtmDate3, "mm/dd/yyyy") 4 " and " _
Format$(gdtmDatel. "mm/dd/yyyy") 4 " is " _
Abs(gdtmDate3 - gdtmDatel)
End Sub
Исходный код VBCALWIZ.BAS
Option
Explicit
Riblic
gdtmDatel As Date
Riblic
gdtmDate2 As Date
№lic
gdt»Date3 As Date
Приложение VBCIock
Это приложение создает весьма необычные по внешнему виду часы, показывающие системное время. Оно демонстрирует также несколько приемов работы с графикой и альтернативный подход к добавлению в программу универсального диалогового окна About. На форму VBCIock помещено красочное растровое изображение (рис. 31-8). Я создал его с помощью одной общедоступной программы, генерирующей фракталы на основе множества мандсльорота, но Вы можете загрузить в окно рисунка picBackGround любое другое изображение по своему вкусу.
Меню Options (параметры) позволяет вручную установить время и изменить цвет стрелок. Для настройки часов я добавил в программу простейшую функцию InputBox, а для изменения цвета стрелок — форму, демонстрирующую создание графических «горячих» участков. (InputBox можно заменить элементом управления DTPicker, что потребует лишь незначительной модификации приема, показанного на примере мастера VBCaJ.) Меню Help обеспечивает доступ к разделам Contents и Search справочного файла.
ПРИМЕЧАНИЕ Прекрасная возможность точно настроить системное время в Вашем компьютере — воспользоваться
приложением NISTTime, которое еще будет представлено в этой главе. Запустив одновременно NISTTime и VBClock, Вы увидите процесс настройки часов в реальном времени.
Рис. 31-8 Приложение VBClock в действии
Из рис, 31-9 видно, какие файлы содержатся в проекте VBClock. В следующих разделах мы подробно рассмотрим каждый из этих трех файлов.
hflirlnc^Jirtifltf^vLiJ^
И Jrnti»ut2 МЬои2./лт>
Ь frmyBdMkt¥8CloCk.frrh)
El ^mVKlQCk2(vgc|Qrk2.Frm)
Рис. 31-9 Список файлов в проекте VBClock
VBCLOCK.FRM
Форма VBClock отображает аналоговые часы и обновляет положение их стрелок раз в секунду. На ней всего три элемента управления: PictureBox (показывает фон и стрелки часов), Timer (обновляет время) и CommonDialog (связывает со справочным файлом). Вы можете загрузить в PictureBox любое растровое изображение и изменить цвета стрелок.
ПРИМЕЧАНИЕ Свойство AutoRedraw элемента PictureBox очень важно. Его надо установить как True, чтобы прорисовка стрелок проходила плавно и естественно. Если AutoRedraw
равно False, то при стирании и вычерчивании стрелок будет заметно мерцание изображения.
Рис. 31-Ю Форма VBQockHa этапе разработки
Рис. 3 1 -10 иллюстрирует форму VBClock на этапе разработки. Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Меню и их элементы в VBCLOCK.FRM
Название
Имя
Уровень вложения
Параметр Enabled
©■File
rrmuFite
0
True
«New
mnuNew
■ 1
False
«Ореп_
mnuOpen
1
False
«•Save
mnuSave
1
False
Save «As-
mnuSaveAs
1
False
—
mnuFileDasbl
1
True
E&xit
mnuExit
1
True
«Options
mnuOption
0
True
«Set Time...
mnuSetTime
1
True
«Hand Colors...
mnuHandCdlors
1
True
«■Help
mnuHelp
0
True
«Contents
nmuContents
1
True
«Search For Help On...
mnuSearcb
1
True
—
mnuHelpDasbl
1
True
t&AbOUt...
tnnuAbout
1
True
Объекты VBCLOCK.FRM и значения их свойств
Свойство
Значение
Form
Name Caption Borderstyle Timer
JrmVBClOck
VBClock
3 - Fixed Dialog
Name
Interval
CommonDiatog
Name
PlCtUreBox
tmrClock
WO
cdlOne
Name
AutoRedraw
AutoSize
Picture
picBachGround True
True
MNDLBRT3MP
Исходный коп VBCLOCK.FHM
Option Explicit
Public gintHourHandColor As Integer
Public gintMinuteHandCoIor As Integer
Public gintSecondHandColor As Integer
Private mlntHnum As Integer Private mlntHnum As Integer Private mintSnum As Integer Private mlngHcoior As Long Private mlHcolor As Long Private mingScolor As Long Private msngHlen As Single Private msngMlen As Single Private msngSlen As Single Private ms trAppnama As String Private mstrSection As String Private mstrKey As String Private mstrSetting As String
Private Const sngPi = 3.141593!
Private Const snglwoPi = sngPi + sngPi Private Const sngHalfPl = sngPi / 2
продолжение
' заполняем всю форму фоновым изображением picBackGround.Move О, О
Me,Width = picBackGround.Width + (Me.Width - ScaleWLdth) Ke.Height = picBackGround.Height + (He,Height - ScaleHeight) ■' изменяем масштаб изображения часов
picBackGround. Scale (-2, -2)-(2, 2) ' центрируем форму
Ke.Left = (Screen.Width - He.Width) \ 2
Me.Top = (Screen.Height - Me.Height) \ 2 ' устанавливаем ширину стрелок s пикселах picBackGround.DrawWIdth :5 ' устанавливаем длину стрелок msngHlen =0.8 msngMlen =1.5 msngSlen = 1
' устанавливаем цвет стрелок на основе параметров ИЗ реестра . mstrAppname = "VBCIock" mstrSection = "Hands"'
mstrKey = "mlngHcolor" mstrSetting = GetSetting(mstrAppname, mstrSection, mstrKey) gintHourHandColor = Val(mstrSetting) fflstrKey = "mlMcolor" mstrSetting = GetSetting(mstrAppnaine, erstrSection, mstrKey) gintHlnuteHandColor = Val(mstrSetting) mstrKey = "mlngScolor" fflstrSetting - GetSetting(mstrAppname, mstrSection, mstrKey) ■ gintSecondHandColor = Val(mstrSetting)
End Sub
Private Sub Form_Unload(Cancel As Integer) ' запоминаем текущие цвета стрелок mstrKey = "mlngHcolor" mstrSetting = Str$(gintHourHandColor)
, SaveSetting mstrAppname, mstrSection, mstrKey, mstrSetting rostrKey = "mlMcolor" mstrSetting = Str$(gintMinutaHandColor) SaveSetting mstrAppname, mstrSection, mstrKey, mstrSetting mstrKey = "mlngScolor" mstrSetting - Str$(gintSecondHandColor) SaveSetting mstrAppname, mstrSection, mstrKey, mstrSetting End SuP
Private Sub mnuAbout„Click() frmAbout2, Display
End Sub
продолжение
Unload Me.
End Sub
Private Sub mnuContents_Click()
GdlQne.HelpFile = App.Path & "\. .\. . \Help\Mvt>dw.hlp" cdlOne.Helpcommand = cdlHelpContents cdlOne.ShowHelp End Sub
Private Sub mnuHandColors_CliGk()
' ' выводим форму для выбора цветов стрелок ,.
frmVBClock2.Show vbModal End Sub'
Private Syb mnuSearch_ClickO
cdlOne,HelpFile = App.Path & "\--\-.\Help\Mvbdw.hl₽" ctflOne. HelpCommand = cdlHelpPartialKey cdlOne,ShowHelp End Sub
Private Sub tmrClock_TimerO
Dim dbIHang As Double Dim dbIHang As Double Dim dblSang As Double Dim dblHx As Double Dim dblHy As Double 01» dblNx As Double Dim dblMy As Double Dim dblSxAs Double Dim г^'Г'. As Double ' следим за текущей секундой ■ Static IntLastSecond As Integer " проверяем наступление новой секунды If Second(Now) = IntLastSecond Then Exit Sub
Else.
intLastSecond = Second(Now) End If■■
' обновляем переменные co значениями времени mintHnum = Hour(Now) . mintMnum = Minute(Now) mintSnum = Second(Now) ' вычисляем угол наклона каждой стрелки dbIHang = sngTwoPi * (mintMnum + raintMnum / 60) / _ 12 - sngHalfPi
dblMang = sngTwoPi * (mintMnum + mintSnum /60) / __
продолжение
60 - sngHalfPi
, dblSang = sngTwoPi * mlntSnuin / 60 - sngHalfPi ' вычисляем начальные и конечные точки каждой стрелки dblHx = msngHlen * Cos(dblHang) dblHy = msngHlen * Sin(dblHang) dblMx = msngHlen * Cos(dblNang) dblHy = msngHlen * Sin(dblMang) dblSx = msngSlen * Cos(dblSang) dblSy = msngSlen * Sin(dblSang) ' восстанавливаем фоновое изображение picBackGround.Cis■
' рисуем новые стрелки picBackGround.Line (0, O)-(dblMx, dblHy), _
OBColor(glntMinuteHandColor)
picBackGround.Line (0, O)-(dblHx, dblHy), _ QBColor(gintHourHandColor)
picBackGround.Line (0, O)-(dblSx, dblSy), „ OBColor(gintSacondHandColor)
End Sub
Private Sub mnuSetTlme_Click()
Dim strPrompt As String
Dim strTitle As String
Dim strDefault As String
Dim strStartTime As String
Dim strTim As String
Dim г Lee As String ' запрашиваем у пользователя новое время strPrompt = "Enter the time, using the format 00:00:00" strTitle = "VBClock" strDefault = Timet strStartTime = strOefault strTim = InputBox$(strPrompt, strTitle, strDefault) ' проверяем, не щелкнул ли пользователь кнопку Cancel ' иди кнопку ОК без изменения времени If strTim * "" Or strTim = strStartTime Then
Exit Sub
End If.
' устанавливаем новое значение времени
On Error GoTo Error!rap
Time = strTim
Exit Sub
ErrorTrap:
strMsg = "The time you entered is invalid, " + strTim MsgBox strMsg. 48, "VBClock" Resume Next End Sub'
Свойству Interval таймера присвоено значение 100 вместо 1000, Положение стрелок нужно обновлять 1 раз в секунду, однако, как я уже говорил в одной из предыдущих глав, установка интервала в 1000 ш: приводит к рывкам при движении стрелок. Это связано с тем, что таймер Visual Basic срабатывает по истечении заданного интервала, как только система сможет его обслужить. В итоге события таймера генерируются с небольшим, но непредсказуемым разбросом по времени. (Чем быстрее Ваш компьютер, тем точнее интервалы срабатывания таймера.)
Чтобы обойти эту проблему, присвойте свойству Interval таймера какое-нибудь значение, меньшее половины секунды. Я выбрал 100 мс. Тогда стрелки обновляются гораздо точнее, с ошибкой не более 0.1 секунды. Этого достаточно для того, чтобы пользователь не заметил никаких отклонений. Процедура обработки события Timer сравнивает системное время с текущей секундой, отображаемой часами. Если новая секунда еще не наступила. процедура тут же завершается, вызывая лишь минимальную задержку в работе системы.
Использование реестра
Программа позволяет изменить цвет стрелок часов и для хранения этой информации использует реестр. Значения цветов считываются из реестра в процедуре Form_Load и сохраняются в нем при выполнении процедуры Form_Unload. Вот Вам и пример того, как записать последнее состояние приложения в реестре. Применяя те же приемы, Вы сможете сохранять в реестре любые параметры своей программы.
VBCLOCK2.FRM
Форма VBClock2 предоставляет графический интерфейс для выбора шшштрех стрелок. Я хотел, чтобы пользователь мог наглядно выбрать один из 16 основных цветов для каждой стрелки, а не изучал их словесные описания. Поэтому я добавил на форму 3 окна рисунка и заполнил каждое из них 16 цветовыми блоками (как работает форма VBClock2, хорошо видно на рис. 31-П). Такой прием прост и незатейлив, зато пользователь точно видит, какой именно цвет он (она) выбирает. И вообще, как говорится, одна картинка стоит двух тысяч байтов!
При выполнении процедуры Form Load я масштабирую окна рисунков и каждое разбиваю на 16 частей. Высота окна укладывается в пределы от 0 до 1, а ширина — от О до 16. Независимо от действительных размеров каждое окно показывает 16 основных цветов одинаковыми прямоугольными блоками. Поэтому, когда пользователь щелкает в любом месте окна рисунка, Х-координата преобразуется в число от 0 до 1 — оно-то и указывает выбранный цвет.
Рис. 31-11 Форма VBClock2, позволяющая подобрать цвета для стрелок часов
Графические «горячие» участки
Процедура обработки события Click не дает нужной информации для точного определения места, в котором произошел щелчок мыши. Лучший способ получить такую информацию — сохранять последнюю известную позицию курсора мыши в глобальной переменной (в данном случае их нужно три — по одной на каждое окно рисунка). Процедура обработки события MouseMovc окна рисунка сообщает A'J-координзты курсора, что позволяет легко обновлять значения глобальных переменных (в моей программе — mintHuurMouseX, mintMinuleMouseX и mintSecondMouseX), через которые отслеживается движение мыши. Их значения при генерации события Click определяют, какой цвет выбрал пользователь. Этот способ очень удобен для создания -горячих- участков на графических изображениях. Используя математические преобразования текущих А, У-координат курсора мыши, можно определить «горячие» участки практически любой формы (даже круглой или многоугольной),
Рис. 31-12 иллюстрирует форму VBClock2 на этапе разработки.
Рис. 31-12 Форма VBClocK^ на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, приведенные ниже.
Объекты VBCLOCK2.FRM и
значения их свойств
Тип и номер'Свойство
Значение
Form
Name
frmVBClock2
Caption
VBClock - Select Colors for Clock
BorderStyle
Hands
3 - Fixed Dialog
Icon
CLOCK02ICO
Label
JN ame
IblHourHand
Caption
Hour band
Alignment 1
- Right Justify
Label
2Name
IblMhiuteHcmd
Caption
Minute hand
Alignment 1
- Right Justify
Label
3Name
IblSecondHand
Caption
Second hand
Alignment 1
- Right Justify
PictureBox
4Name
picHourColor
Picture Box
5Name
picMinuteColor
PictureBox
6Name
picSecondColor
CommandButtcm
1Name
cmdOK
Caption
OK
Default
True
CommandButton
8Name
cmdCancel
Caption
Cancel
Cancel
True
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 51-12).
Исходный код VBCLOCK2.FRM
Option Explicit.
Private ffiintHourMoys6X As TntAOpr:
; Private mintkinuteHouseX As Integer Private mintSecondNousov As Integer Private mlntHourHand As Integer Private mintMinuteHand As integer.
Private mintSecondHand As Integer '.
Private Sub cmdCanceI_Click()
1 выходим, не меняя цвета стрелок
Unload Me
End Sub■
Private Sub cn)dOK_Click()■
переустанавливаем цвета стрелок
frmVBClock.gintHourHandColor = mlntHourHand
. frmVBClock.gintMinuteHandColor = mlntMinuteHand
frmVBClock.gintSecondHandColor = mintSecondHand
' возвращаемся в основную форму
Unload Ив
End Sub. I
Private Sub ; orm_Activate()'
Form_Faint
End Sub
Private Sub Form_Load()
* получаем текущие цвета стрелок
■ mlntHourHand = frmVBClock.gintHourHandColor
mintMinuteHand = frmVBGLock.gintMinuteHandColor mintSecondHand = frmVBClock.gintSecondHandColor
масштабируем окна рисунков picHourColor.Scale (0, fl)-(16, 1) picHlnuteColor. Scale (C 0)-(16, 1) picSecondColor.Saale (Q, O)-(16, 1} End Sub
Private Sub Form_paint()
Sim i As Integer
1 отображаем 16 цветов на каждой цветовой "панели" For i * 015
' рисуем цветные прямоугольники■
picHourColor.Line (i, 0}-(i + 1, 1), QBColor(i), BF picHlnuteColor.Line (1, 0)-(i + 1, 1), QBColor(i}, BF picSecondColor. Line (1, 0)-(i + 1, 1), QBColor(i), BF
■ Next i
продолжение'
"’ ставим метки" на текущих цветах
picHourColor.DrawWidth = 2
picHourColor.Line (mintHourHand + 0.3, 0.5) _
-(mintHourHand + 0.5, 0.7), QBColor(mlntHourHand Xor 15)
picHourColor.Line (mintHourHand + 0.5, 0.7)
-(mintHourHand + 0.8, 0.2), QBColorCmintHourHand aci 15)
picNinuteColor.DrawWidth = 2
picMinuteColor. Line (mintMinuteHand + 0,3, 0.5) _
■-(mintMinuteHand + 0.5, 0.7), aB€olor(mlntMinuteHanc Хот 15)
picMinuteColor.Line (mintMinuteHand + 0.5, 0.7) _
-(mintMinuteHand + 0,8, 0.2), CBColor(mintMinuteKand Xor 15) picSecondColor.DrawWidth = 2
■ picSecondColor. Line (mintSecondHand + 0.3, 0.5) _
-(mintSecondHand + 0.5, 0.7), QBColor(mintSecondHand Xor 15) picSecondColor.Line (mintSecondHand + 0.5, 0.7) _
-(mintSecondHand + 0.8, 0.2), OBColor(mintSecondHand Xor 15)
End Sub
Private Sub picHour'’'-'~- '"ickO
' определяем, какой цвет выбран для часовой стреми
mintHourHand = mintHourHouseX
Form_Paint
End Sub
Private Sub picHourColor_MouseMove(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
отслеживаем движение мыши
mintHourMouseX = Int(X)'
End Sub
Private Sub picMinuteColor,Cllok()
* определяем, какой цвет выбран для минутной стреми
mintMinuteHand = mintMinuteMouseX
. Form_Palnt
End Sub■
Private Sub picMlnuteColor_HouseMoveil1"’"'■■"' As Integer, _
Shift As Integer, X As Single, ¥ As Single)
отслеживаем движение мыши
mintMinuteMouseX = bit (X)■
■ End Sub
Private Sub picSecondColor„Click()
' определяем, какой цвет выбран для секундной стрелки ffllntSecondHand = mintSecondNouseX
Form_Paint
продолжение*
find Sub'
Private Sub picSecondColor_MousBMove(Button As Integer, _ , Shift As Integer. XAs Single, YAs Single) ' отслеживаем движение мыши
. mintSecondMouseX = Int(X)
End Sub
ABOUT2.FRM
В главе 12 «Окна, диалоговые окна и прочие формы» я представил универсальный шаблон диалогового окна About, который Вы можете подключать к своим программам. Чтобы продемонстрировать свойства объекта Арр и показать, как автоматизировать с их помощью заполнение полей в шаблоне диалогового окна About, я создал форму ABOUT2.FRM. Все отображаемые данные модифицируются вызывающей программой; для активизации этой формы достаточно вызвать метод fimAboutZ.Display.
На рис. 31-13 форма About2 показана в том виде, в каком она вызывается приложением VBClock.
Visual Basic 6.0 Developer's Workshop
VBClock
Рис. 31-13 Форма About2, активизированная из VBClock
Когда, где и каким образом указывается информация, отображаемая формой About2? Весь фокус в определении значений свойств объекта Арр на этапе разработки. Выберите из меню Project среды Visual Basic команду Project Properties и в появившемся диалоговом окне щелкните ярлычок Маке (рис. 51-14).
У объекта Арр солидный набор полезных свойств, многие из которых можно установить в диалоговом окне Project Properties, в том числе номер версии и название приложения в разделе Version Information. Значения этих и других его свойств можно изменять и программным способом.
Форма About2 устанавливает свойства Caption своих меток на основе значений четырех свойств объекта Арр:
' формируем текст меток, используя свойства объекта Арр
IbLHeading.Caption = Арр.Title
IblApplication.Caption = Арр.ProductName
IblVB.Caption = App.Comments
IblCopyright.Caption = App. LegalCopyright
" fap^d *i®^' | tofipltl Создав }-^ч
: tSW" ' MpiOT: Зейны 'r-—— -
“ ’_К*' }c.
,Г A|£o IncMiTrwir1
V<!H№ Inlirmi,*1
Sts# тег ceaivHJwi friy^rereej' "" "'
P ^yrrfi*» mlorrMittbn obfitftiiHijiie!! Mb'i^H Gw^obi
j' "ok " I tencrt {
Рис. 31-14 Диалоговое окно Project Properties, позволяющее установить бачьшинство свойств объекта Арр
Хотите — модифицируйте форму About2 для отображения любых других свойств объекта Арр. Я выбрал лишь четыре свойства, чтобы упростить сравнение этого окна с шаблоном. Рис. 31-15 иллюстрирует форму About2 на этапе разработки.
Рис. 31-15 ФормаАЬош2 на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Объекты AB0UT2J RM и значения их свойств
Тип и номер’
Свойство
Значение
Form
Name
fmiAbout2
Caption
About
Label
BorderStyle
3 - Fixed Dialog
1
Name
IblHeading
Alignment
2 - Center
Label
Font
MS Sans Serif, Bold, 12
2
Name
IblApplication
Alignment
2 - Center
Font
MS Sans Serif Regular, 18
Label
ForeColor
SH00008000S-
3
Name
IblVB
Label
Alignment
2 - Center
4
Name
IblCopyrigbt
Line
Alignment
2 - Center
5
ComrnandButton
Name
UnSeparator
6
Name
cmdOK
Caption
OK
Номер, указанный в первой колонке, соответствует порядковому номеру объек
та на форме (рис. 31-15).
Исходный код AB0UT2.FRM
Option Explicit
private Sub cmdOK_Click()
Unload Me
EndSub
Private Sub Form_Load()
' центрируем форму
Left = (Screen.Width - Width) \ 2
Top = (Screen. Height - Height) \2
продолжение'■"
'" формируем текст меток, используя свойства объекта Арр
IblHeading.Caption = Арр.Title IblApplication.Caption = Арр.ProductName IblVB.Caption = Арр.Comments
IbiCopyright. Caption * App. Legal Copy right:
End Sub■
Public Sub DlsplayO
' показываем форму как модальную
Me. Show vbNodal'
■ End Sub■
Приложение NISTTime
Эта утилита автоматически дозванивается и соединяется с NIST, который находится в Боулдере (штат Колорадо). Вы можете использовать ее для установки часов своего компьютера с точностью до 1 секунды. Абсолютной точности Вы не достигнете, так как она ограничивается рядом факторов: скоростью модемного соединения, быстродействием Вашего компьютера, задержками на телефонной линии и т. д. Служба времени NIST пытается учесть эти факторы, посылая символ-чмаркер» за 45 мсдо самого значения времени. Но Б любом случае NISTTime позволит настроить системные часы гораздо точнее, чем это можно сделать вручную.
Как работает NISTTime
Приложение NISTTime контролирует модемное соединение со службой времени NIST через элемент управления Comm, который упрощает корректную настройку параметров модема (см. процедуру Form_Load на листинге NISTTIME.FRM). Раньше эта служба требовала скорости передачи 300 или 1200 бод, но потом поставила модемы, автоматически подстраивающиеся под скорость вызывающего модема. Как видно из исходного кода, я установил скорость своего модема равной 14 400 бод, и все работало хорошо. Если Ваша линия зашумленная или связь неустойчива, попробуйте уменьшить скорость.
ПРИМЕЧАНИЕ При 300 бод NIST посылает сжатую строку информации, которую мое приложение не распознает. Поэтому используйте скорости от 1200 бод и выше. ■
После установки связи служба посылает сообщение-заголовок, за которым следует строка информации, повторяемая раз в секунду. В конце строки передается символ-звездочка, отмечающий точное время. Приложение NISTTime считывает и сохраняет эти данные по мере их поступления, ожидая появления второй звездочки.
Получив второй маркер, приложение извлекает из строки информацию о минутах и секундах и настраивает системные часы в соответствии с этими значениями.
В строке содержатся и другие полезные данные: дата в модифицированных юлианских днях; год, месяц, день и час по всемирному координированному времени (Coordinated Universal Time): поправка в секундах на конец месяца и некоторая другая информация. Все эти данные приложением NISTTime игнорируются, потому что Ваши часы, вероятно, уже установлены и надо лишь уточнить значения минут и секунд.
ПРИМЕЧАНИЕ Заполнымописаниеминформацип. передаваемой этой службой, обращайтесь по тому же номеру с помощью какой-нибудь программы типа HyperTerminai. После установки соединения введите знак вопроса, и Вы получите несколько страниц текста.
Форма NISTTime использует два таймера для управления связью: интервал первого установлен равным 1 мс — для обработки поступающих байтов, а другой срабатывает через I минуту. Обычно NISTTime устанавливает связь, обновляет время и разрывает соединение всего через несколько секунд после набора номера. Но если что-то пойдет не так, второй («одноминутный») таймер прервет связь в конце минуты. Да и служба времени сама отсоединяется примерно через минуту работы — все это гарантирует, что Вы не получите чересчур большой счет за междугородный звонок. Так что второй таймер применяется в качестве дополнительной меры безопасности.
При выполнении утилита NISTTime выводит несколько коротких сообщении. описывающих ее состояние. Она не требует взаимодействия с пользователем и завершается вскоре после настройки времени. На рис. 31-16, 31-17 и 31-18 показана нормальная последовательность сообщений за несколько секунд ее работы.
По умолчанию NISTTime предполагает, что модем подключен к последовательному порту COM I и работает на скорости i ; 400 бод, а также что Вы звоните в Боулдер из другого города (поэтому указывается междугородный код 303). Но я предусмотрел возможность простого изменения этих параметров. Когда Вы в первый раз запускаете NISTTime, в каталоге, где хранится эта утилита, создается специальный текстовый файл NISTTIME.TXT. Бот его исходное содержимое;
PORT 1
TELEPHONE 1-303-494-4774 ' ■ ■
SETTINGS 14400,N,8,1 "
Этот файл можно модифицировать в любом текстовом редакторе (например, Notepad). В моей системе модем подключен к порту COM2-, кроме того, я живу в самом Боулдере, и мне не нужно набирать междугородный код. Поэтому мой файл NISTTIME.TXT выглядит так
■PORT 2
TELEPHONE 494-4774
SETTINGS 14400,N,6,1
ПРИМЕЧАНИЕ Для наблюдения за системным временем запустите программу VBClock из предыдущего раздела или дважды щелкните значок Dale/Time (Дата и время) в окне Control Panel.
ЕС^ВК2В^ЗМ®Я8||вН^К51
Dting |*ftfc$iBJ hi c№te «Т 5^»^
Рис. 31-16 Приложение NISTTime при звонке в NIST
Рис. 31-17 Приложение NISTTime принимает данные для настройки системных часов
Рис. 31-18 Приложение NISTTime отсоединяется от службы времени NIST
NISTTIME.FRM
Размеры у формы NISTTime невелики — только-только чтобы было где разместить два таймера, кнопку, элемент управления Comm и метку для отображения сообщений о ходе операций.
Рис. 31-19 иллюстрирует форму на этапе разработки.
Рис. 31-19 Форма NISTTime на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, приведенные ниже.
Объекты NISTTIME.FRM и значения их свойств
Свойство
Значение
Form
Name
jhnNlSTTime
Caption
NIST - Time Set
BorderStyle
3 - Fixed Dialog■
Icon
CLOCK01JCO
Timer
Name
tmrGetBytes
Interval
7
Enabled
False
Timer
Name
tmrWatcbDog
Interval
60000
Enabled
False
Comm
Name
comContwl
Handshaking
2 - comRTS
CommandButton
Name
cmdCrmcel
Caption
SCancel ■
Label
Name
Ibllnjormatton
Alignment
2 - Center
Исходный код NISTTIME.FRM
г ■ Option Explicit
Const PORT * 1
Const TELEPHONE = "1-303-494-4774"
Const SETTINGS « "14400,N,8,1"
Const BUFSIZ * 3000
Sim LntNistNdxAs Integer
Difl strNistBuf As String * BUFSIZ
Dim strTelephane As String
Dim strSettings As String Dim IntPortAs Integer
Dim str A As String
продолжение
Dim intP As Integer■
Private Sub CBdCancel_Click()■
Unload Ne
End Sub
Г Private Sub Form^LoadO :?' помещаем форму почти в левый верхний угол экрана
Me.Left = (Screen.Width - Не.Width) * 0.1
Me.Top = (Screen.Height - He.Height) * 0.1 ' формируем первое сообщение
Ibllnfcreation.Caption * _ "Dialing National Institute of Standards " & _ "and Technology Telephone Time Service" ' отображаем форму и первое сообщение
Show ' загружаем из текстового файла номер порта и телефонный номер strTelephone = TELEPHONE
intPort = PORT
strSettings = SETTINGS
Open App.Path & "\Nisttime.txt" For Binary As #1
If L0F(1) = 0 Then
Close #1 ' создаем файл при первом запуске
Open Арр. Path 4 "\Nisttime.txt" For Output As #1
. Print #1, "PORT" & Str(intPort) Print *1, "TELEPHONE " & strTelephone
Print #1, "SETTINGS " & strSettings
Bid If
Close #1
Open App.Path & "\Nisttime.txt" For Input As #1
Do Until E0F(1)'
Line Input #1, strA''
strA = UCase$(strA) intP - InStrastrA, "PORT")
If intP Then intPort = Val(Mld(strA, intP + 4»
'intP = InStr{strA, "TELEPHONE")
If intP Than strTelephone = Nid(strA, intP + 9) intP = InStr(strA. "SETTINGS")
If intP Then strSettings = Nid(strA, intP + 8)
Loop
Close #1■
' устанавливаем свойства элемента управления Comm comControl. CommPort = intPort comControl.SETTINGS = strSettings ' указываем считывать буфер целиком
см. след. стр.
продолжение
comControl.InputLen = о
comControl.PortOpan = True
' посылаем команду для дозвона в NIST
comControl.Output = "ATDT" + strTelephone + vbCr ' активизируем таймеры
tmrGetBytes. Enabled = True
tmrWatcbDog. Enabled = True ' включаем кнопку Cancel cmdCancel.Enabled = True End Sub
Private Sub Form_Unload(Cancel As Integer)
' это обычно приводит к тому, что модем прекращает сеанс связи comControl, DTREnable = False
' эта строка также прекращает сеанс связи Pause 1500
' обновляем сообщение для ппгк.зг>ялт1шя
Ibllnformation.Caption = "Hanging up"
Refresh
' посылаем модему убавляющие команды"
comLontroi,Cutput - +++ Pause 1500■
comControl.Output = "ATHO" + vbCrlf
' завершаем связи
. comControl.PortOpen = False
End Sub■'
Private Sub Pause(dbmii31°»n As Double)'
Dim sngEndOfPause As Single
' определяем время окончания задержки
sngEndOfPause = Timer + dblMillisec / 1000
' цикл ожидания
Do
Loop While Timer < sngEndOfPause
End Sub'
Private Sub SetTime(strA As String)
Dim intHo As Integer
Dim intHi As Integer
Dim intSeAs Integer
Dim dblTlmeNow As Double
' получаем текущее значение часов от системы
intHo = Hour(Now)
' извлекаем значения минут и секунд из строки NIST
intHi в Val(Mid(strA, 22, 2))
intSe = Val(Hld(strA, 25, 2))
продолжение
' формируем новое значение времени dblTimeNow = TimeSerial(intHo, intMi, intSe) ' настраиваем системные часы Time = i"ormat(dblTimeNow, "-hh:mm:ss")
End Sub
Private Sub tmrGetBytesJTlmerO
Static bInConnect As Boolean
Dim strTmp As String'
Dim intBytes As Vteger
Dim IntP" As Integer
Dim intP2 As Integer ' проверка полученных байтов
If oomControl.InBufferCount = 0 Then
Exit Sub
Else
strTmp = oomControl.Input intBytes - Len(strTmp) If IntBytes + intNlstNdx >= BUFSIZ Then
Ibllnformation.Caption = "Hanging up" tmrGetBytes.Enabled = False tmrWatchDog.Enabled = False
Unload Ne
Else
Mid(strNistBuf, intNistNdx + 1, intBytes) = strTmp intNistNdx = intHistNdx + intBytas
End If
End If
' проверяем символ, указывающий на установку связи
If binConneot = False Then
If InStr(strNistBuf, "*“ & vbCrlf) Then Ibllnformation.Caption - "Connected. Setting clock" •blnConnect = True
End If
Else
'■ ' проверяем маркеры времени
lntP1 = InStr(strNistBuf, "•")
intP2 = InStr(intP1 + 1, strNistBuf, "«")
' значение времени принято, если найдено два маркера If intP2 > intP1 Then
SetTime Mid(strNistBuf, intP1, intP2 - intP1 + 1} Unload Me
Bid If
Bid If.
Bid Sub
продолжение
Private Sub tmrWatchDog_nmer()
' истек безопасный период ожидания связи
Веер
Unload Me
End Sub
ГЛАВА 32
Базы данных
т т
Visual Basic играет все более важную роль в корпоративных средах — особенно с тех пор, как его средства доступа к базам данных расширились, и он превратился в мощный инструмент для разработки баз данных и их пользовательского интерфейса. В этой главе представлены 3 приложения: AreaCode, DataDump и Jot; они демонстрируют, насколько Visual Basic упрощает работу с базами данных Microsoft Access и других СУБД.
Приложение AreaCode
Оно состоит из двух форм: AREACODE.FRM и ABOUT.FRM. Первая использует два элемента управления Data для доступа к одной и той же базе данных междугородных телефонных кодов. Один из элементов Data (в верхней половине формы) обращается к таблице в базе данныхАКЕАСООЕ.МОВ, созданной в Microsoft Access 97, а другой элемент Data (в нижней половине формы) — к запросу в той же базе данных. Таблица, используемая первым элементом Data, содержит междугородные коды в порядке возрастания (201-970). Запрос предоставляет ту же таблицу, но по аббревиатурам названий штатов в алфавитном порядке f'XK-WYi
В приложении минимум кода, так как свойства элементов управления Data и TextBox упрощают подключение к базе данных. Чтобы создать форму AREACODE.FRM, мне понадобилось лишь нарисовать элементы управления, указать значения их свойств, добавить несколько строк кода для реализации поиска и приложение готово!
Связывание элементов управления с базой данных — процесс двухступенчатый. Сначала элемент Data подключается к файлу базы данных через свойство DatabaseName, а уже потом — к определенной таблице или запросу в этой базе через свойство RecordSourcc. Каждый элемент Data, как правило, связывается с одной таблицей или запросом. Многие другие элементы управления, например Label или ListBox, теперь предусматривают свойства для подключения к базе данных через элемент Data. Я использую несколько элементов TextBox, присваивая их свойствам DataSource имя соответствующего элемента Data, а свойствам DataField — имена нужных полей таблицы или запроса. .
Хотя элемент управления Data обеспечивает навигацию по базе данных, я включил несколько строк кода, желая продемонстрировать другой способ доступа к записям. Текстовое поле ixtAreaCudeB верхней половине формы позволяет вводить трехзначный междугородный код. После ввода трех цифр я помечаю текущую запись закладкой (тогда в случае неудачи при поиске записи можно быстро вернуться к текущей), и изменяю несколько свойств объекта RecordSet, доступного через элемент Data, чтобы при вызове процедуры txtAreaCode_Change немедленно начать поиск нужного междугородного кода.
Аналогичным образом, когда в текстовом поле IxtStateAbbrev 1 указывается двухбуквенная аббревиатура названия штата, программа выводит первую же найденную запись. После этого кнопка Next Record элемента управления Data позволяет просматривать записи с другими кодами, используемыми в этом штате.
Парис. ?2-t показана форма AreaCode после успешного поиска междугородного кода 303 (верхняя половина) и междугородного кода штата Вашингтон (нижняя половина). Заметьте, что обе половины формы работают независимо друг от друга, хотя обращаются к одной и той же базе данных.
LUn И» «им Ludr ПП?Г
:у<^ Пиев сйНк Stme. Stile. мт.
Гй’1 |си" |Ж^ЕШИй
jAxpen, flauldoi, befivBf-Г "'
J । *nc* -State: State опте .
J|WA jWathinglon ’
' tseairto. Tntiaen
_—i
Рис. 32-1 Форма AreaCode после нахождения междугородного кода 303 (верхняя половина) и междугородного кода штата , Вашингтон (нижняя половина)
AREACODE.FRM
Форма AreaCode содержит два почти одинаковых набора элементов управления: первый — для верхней половины формы, второй — для нижней. Оба элемента Data связаны с одной и той же базой данных, а элементы TextBox — с полями базы данных через соответствую-
щий элемент Data. Рядом с каждым TextBox присутствует пояснительная метка (все они разбиты на две группы с помощью двух элементов управления Frame). Форма включает также элемент управления CommonDialog, позволяющий обращаться к справочному файлу методом .Люи НеГр. В процедуре Form_Load я присваиваю имя файла базы данных свойству DatabaseName элемента Data и использую свойство App.Path, чтобы найти этот файл. Если Ваша база данных находится не в каталоге приложения, укажите путь к ней в свойстве DatabaseName.
Рис. 32-2 иллюстрирует форму AreaCode на этапе разработки.
Рис. 32-2 Форма AreaCode на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Меню и их элементы в AREACODE.FRM
Название
Имя
Уровень вложения
Параметр Enabled
iEHF
тгтЕЦе
0
True
^New
mnuNew
1
False
SOpen,.
■mnuOpen
1
False
SSave
mnuSave
1
False
Save 6-As...
mnuSaveAs
1
False
—
mnuFileDashl
I
True
E^xit
mnuExil
1
True
&Help
mnuHelp
0
True
Меню и их элементы в AREACODE,FRM (продолжение)
Название
Имя
Уровень вложения Параметр Enabled
Contents &Searcbfor Help on...
^About-
mmiContents mnuSearcb j miuHelpDash 1 mnuAbout
1True
1True,
1True
1True
Объекты AREACODEFRM и значения их свойств
Тип и номер*
Свойство
Значение
Form
Name
JrmAreaCode•• -
Caption
Area Codes
BorderStyle
3 - Fixed Dialog
CommonDialog
\
Name
cdlOne
Frame
г
Name
fraByAreaCode
Caption
By Area Code' ’
Fore Color
&H000000FF&
Label
Ъ
Name
lblPrompt2
Caption
Enter an area code:
ForeColor
SHOOFFOGOO^F
Label
4
Name
lblAreaCode2
Caption1
Area code:j. >
Label
5
Name"
lblState2
■Caption
State:
Label
6
Name
lblStateName2
Caption
State name:
Label
7
Name
iblCities2
Caption
Cities:
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 32 2).
Объекты AREACODE.FRM и значения их свойств (продолжение)
Тип и номер
Свойство
Значение
TextBox
8
Name
txtAreaCode
TextBox
Alignment
2 - Center
9
Name
txtAreaCode2
DataSource
datAreaCode2
TextBox
DataField
AreaCode
10
Name
txtState2
DataSource
datAreaCode2
TextBox
DataField
State
11
Name
txtStateName2
DataSource
datArea€ode2
TextBox
DataField
StateSame
12
Name
txtCities2
DataSource
datAreaCode2
Data
DataField
Cities
13
Name
datAreaCode2
Frame
RecordSource
AreaCode
14
Name
JraByState
Caption
By State
ForeColor
&H000O0OFPS-
Label
15
Name
IblPromptl
Caption
Enter a state abbreviation:
Label
ForeColor
^HOOFFOOOOe-
16
Name
IblAreaCodel
Label
Caption
Area code?
17
Name
IblStatel
Caption
State:
06beKTbiAREAC0DE.FRM и значения их свойств (гроДиженке)
Тип и номер
Свойство
Значение
Label
18
Name
IblStateN'ame 1
Caption
State name:
Label
19
Name
lb ICitiesl■
Caption
Cities:
TextBox
20
Name
txtStateAbbrei>l"
Alignment
2 - Center
TextBox
21
Name
txtAreaCode 1
DataSource
datAreaCodel
DataField
AreaCode
TextBox
22
Name
LxtStatel■
DataSource
datAreaCodel
DataField
State
TextBox
23
Name
txtStateNamel
DataSource
datAreaCodel
DataField
StateName
TextBox
24
Name
txtCiliesl '
DataSource
datAreaCodel
DataField
Cities
Data
25
Name
datAreaCodel
RecordSource
By State
Исходный КОД AREACODE.FRM
Opti-оп Explicit
Private Sub Form_Load().^
' центрируем эту форму|
Ne.Left = (Screen.Width - Width) \ 2 Me.Top * (Screen.Height - Height) \ 2'
datAreaCodel.DatabaseName = App.Path & "\AreaCode.nidb" datAreaCode2. DatabaseName = App.Path & "\AreaCode.mdb11„
продолжение
End Sub''
Private Sub txtAreaCode_Change()
Dim strBookmark As String
Dim strCriteria As 1 ’ - ожидаем ввода всех трех цифр If Len(txtAreaCode.Text) = 3 Then 1 запоминаем текущую запись strBookmark = datAreaCode2.Recordset.Bookmark - ищем первое совпадение с введенным кодой strCriteria - "ArgaCode = " + tktArgaCode.Text datAreaCode2. Recordset. FindFirst strCriteria - обработка при неудачном поиске If datAreaCodeZ.Recordset.NoMatch Then
Beep
datAreaCode2.Recordset.Bookmark = strBookmark
End If
End if End Sub
Private Sub txtAreaCode_KeyPress(KeyAscli As Integer) If Len(txtAreaCode.Text) = 3 Then
txtAreaCode.Text = ""
End If End Sub
Private Sub txtStateAbbrev1_ChangeO
Dim strBookmark
Dim strCriteria
1 ожидаем ввода двухбуквенной аббревиатуры If Len(txtStateAbbrev1.Text) = 2 Then
1 запоминаем текущую запись
strBookmark = datAreaCodef.Recordset.Bookmark
ищем первое совпадение с введенной аббревиатурой
.strCriteria = "State = ’" + txtStateAbbrevf.Text + " "’
datAreaCodef.Recordset.FindFirst strCriteria
обработка при неудачном поиске
If datAreaCodef.Recordset.NoMatch Then Beep
datAreaCodef.Recordset.Bookmark = strBookmark
End If
End If
End Sub
Private Sub txtStateAbbrevf_KeyPress(KeyAscii As Integer) KayAscii = Asc(UCase$(Chr$(KeyAscil)))
продолжение
If Len(txtStateAbbrev1.Text) = 2 Then
txt St at eAbb rev1.Text = ”"
End If
End Sub.
Private Sub mnuAbout„Cllck()
* настраиваем свойства
About.Application = "AreaCode"■
About.Heading = _
"Microsoft Visual Basic 6.0 Developer's Workshop"
About.Copyright = "1998 John Clark Craig and Jeff Webb"
About,Display
End Sub
Private Sub mntiExit„Click()
UnloadMe
End Sub
. Private Sub mnuContents_Click()
CdlOne.HelpFile = App.Path & "\..\. AHeIp\Hvbdw.hIp"
" cdlOne.HelpCommand = cdlHelpContents
cdlOne.ShowHelp
End Sub
Private Sub mnuSearch_Click()
cdlOne.HelpFile = App.Path & "\.Д..\Help\Mvbdw. hip"
cdlOne.HelpCommapd = cdlHelpPartialKey
cdlOne.ShowHelp
. End Sub■
Этот код сортирует записи AreaCode по именам штатов на основе запроса ByState, существующего в базе данных, но то же самое можно сделать иначе: записать в свойств' ■ RecordSource элемента Data строку с командой на языке SQL. Так, если Вы вставите в процедуру обработки события Load формы строку, показанную ниже, то получите те же результаты.
datAreaCodel.RecordSource = "Select * from AREACODE order by State"
Добавив эту строку в программу, Вы можете удалить запрос ByState из базы данных, так как теперь оба элемента Data обращаются к одной и той же таблице базы данных: один «довольствуется» исходным порядком записей (по междугородным кодам), а другой переупорядочивает их по именам штатов, используя команду SQL.
Приложение DataDump
Visual Basic упрощает программное управление базами данных. Иерархия объектов РАО позволяет обращаться к любой части базы данных и модифицировать ее (описание DAO см. в главе 23 «Доступ к базам данных») Приложение DataDump, представленное здесь, демонстрирует принципы перебора частей этой иерархии для определения структуры любой таблицы в любой базе данных.
В отличие от приложения AreaCode, эта программа не содержит элементов управления Data или других элементов, связанных с данными. DataDump использует лишь TextBox для отображения внутренней структуры базы данных и CommonDialog для выбора нужной базы. Содержимое ее файла анализируется перебором объектов DAO.
Приложение выводит список таблиц в базе данных и их полей. Каждое поле характеризуется именем, типом и размером данных в байтах. Мне эта утилита показалась полезной для быстрого просмотра содержимого таблиц базы данных без запуска Access. При необходимости ее код легко расширить, чтобы получать список запросов, хранящихся в базе данных. Тем, кто хочет поглубже вникнуть в этот предмет, стоит заглянуть в Visual Basic Books Online, где подробно поясняется вся иерархия объектов DAO.
При загрузке форма DataDump запрашивает имя файла базы данных, используя элемент управления CommonDialog (рис. 32-3). Затем анализирует выбранную базу данных и объединяет все результаты в одной строковой переменной. Структура базы данных просматривается лили, раз при запуске программы, а строка с результатами отображается в TextBox при каждом изменении размеров формы.
Рис. 32-3 Элементуправления CommonDialog, позволяющий выбрать файл нужной базы данных
Для удобочитаемости я форматирую выходную строку, используя константы vt>CrZ/цдополнительные пробелы. В текстовом поле
установлен шрифт Courier New, который является фиксированным и благодаря этому обеспечивает правильное выравнивание строк текста. Элемент TextBox имеет также полосы прокрутки, позволяющие просматривать списки данных любой длины, и автоматически подгоняется под размеры формы при каждом их изменении.
На рис, 32-4 показана структура базы данных BIBLIO.MDB, поставляемой с Visual Basic.
Рис. 32-4 ПриложениеВсИаОитр в действии: отображаются таблицы и их поля в BIBLIO.MDBРис. 32-4 ПриложениеВсИаОитр в действии: отображаются таблицы и их поля в BIBLIO.MDB
Рис. 32-4 ПриложениеВсИаОитр в действии: отображаются таблицы и их поля в BIBLIO.MDB
Рис. 32-4 ПриложениеВсИаОитр в действии: отображаются таблицы и их поля в BIBLIO.MDB
DATADUMP.FRM
Рис. 32-5 иллюстрирует форму DataDump на этапе разработки. Изначально элемент управления TextBox значительно меньше самой формы, но в период выполнения подгоняется под ее размеры.
Рис. 32-5 Форма DataDump на этапе разработкиСоздавая эту форму, используйте таблицу и исходный код, приРис. 32-5 Форма DataDump на этапе разработкиСоздавая эту форму, используйте таблицу и исходный код, при
Рис. 32-5 Форма DataDump на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, при
Рис. 32-5 Форма DataDump на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, при
веденные ниже.
Объекты DATADUMP.FRM и значения их свойств
Свойство
Значение
Form
Name
JrniDataDump
Caption
DataDump
CommonDialog
Name
cdlOne
TextBox
Name
txtDB
Font
Courier New
MuliiLine
True
Scrollbars
3 - Both
Исходный код DATADUMP.FRM
Option Explicit
Dim strDBFileName As String
Dim strDB As String
Private Sub Form_Load()
центрируем эту форму
Me.Left = (Screen.Width - Width) \ 2
Me.Top = (Screen.Height - Height) \ 2
запрашиваем имя файла базы данных
cdlOne.DialogTitle = "Select a Database File" cdlOne. Filter = "Database(*. nidb) | *,mdb" cdlOne. CancelError = True
проверка на щелчок кнопки Cancel
On Error Resume Next
cdlOne.ShowOpen
If Err = cdlCancel Then End
готовимся к анализу базы данных strDBFileName = cdlOne.FileName Me.Caption = "DataDump - " & strDBFileName GetStructure
End Sub
Private Sub Form_Resize()
' подгоняем текстовое поле под размеры формы txtDB.MoveO, 0, ScaleWidth, ScaleHeight ' выводим строку с результатами txtDB.Text = strDB
End Sub:
продолжение
Private Sub GetStructureO переменные, используемые в циклах ..
Dim inti As Integer
Dim IntJ As Integer ' объекты базы данных Dim dbDump As Database Dim recDump As Recordset . Dim tbdDump As TableDef v ■c Dim fldDump As Field:
' открываем базу данных
Set dbDump = Workspaces(O).OpenOatabase(strDBFileName) strDB = "Database: " & dbDump.Name & vbCrtf ' обрабатываем каждую таблицу
For inti = 0 To dbDump.TableDefs. Count - 1
Set tbdDump * dbDump.TableDefs(intl)
If Left(tbdDump.Name, 4) <> "MSys" Then ' получаем имя таблицу strDB = strDB & Space(3) & "Table: ".
StrDB = strDB & tbdDump.Name & vbCrLf ' обрабатываем каждое поле в каждой таблице . Т < For IntJ * D То tbdDump.Fields.Count - 1
Set fldDump = tbdDump.Fields(intJ) ' получаем имя поля strDB = strDB & Space(6) & "Field: " strDB = strDB & fldDump.Name & vbCrtf ' получаем тип данных текущего поля strOB - strDB & Sp3ce{9) 4 "Type: " Select Case fldDump.Type Case dbSoolean .strDB ■ strDB & "Boolean" & vbCrtf
'Case dbByte
.strDB = strDB & "Byte" & vbCrLf
Case dblnteger
StrDB = strDB 4 "Integer" & vbCrtf.
Case dbLong strDB = strD& & "Long" & vbCrLf ‘ C.:Case dbCurrency
........ strDB = strDB & "Currency" & vbCrtf ’?C... Case dbSingle
. . :: о : strDB = strDB & "Single" & vbCrtf
'Case dbDouble
strDB = StrDB & "Double" & vbCrtf
' .... Case dbDate
' strDB = strDB & "Date" 4 vbCrLf ,Casa dbText.
strDB = strDB 4 "Text" & vbCfLf
продолжение
■Case dbLongBinary
■strDB = strDB & "LongBinary" & vbCrLf
Case dbMemo strDB = strDB & "Memo" & VbCrLf
Case Else
SstrDB = strDB & "(unknown) & vbCrLf"
End Select ' получаем размер поля в байтах
If fldDump.Type о dbLongBinary And _ fldDump.Type <> dbMemo Then strDB = strDB & Space(9) 4 "Size: " strDB = strDB & fldOump.Size 4 vbCrLf
End If••
Next lntJ
End If
•
Next inti
' закрываем базу данных .dbDump.Close
End Sub
Тестирование приложения DataDump
Для проверки приложения DataDump установите в диалоговом окне References ссылку на Microsoft DAO 3 51 Object Library.
Приложение Jot
Это приложение построено на MDI-форме, чтобы отображать сразу несколько окон с заметками. Переделка в коммерческий продукт, вероятно, потребует значительно расширить его возможности и изменить интерфейс, но и в таком виде оно обладает вполне достаточной функциональностью и — самое главное! — демонстрирует целый ряд средств Visual Basic.
Я включил код, иллюстрирующий, как создавать несколько экземпляров дочерних форм, использовать «горячие» клавиши в MDI- форме, программно создавать базу данных и центрировать изображение на MDI-форме еще одним способом.
Рис. 32-6 показывает, как выглядит приложение Jot при выполнении. Кнопки на панели инструментов позволяют создавать новые окна с заметками и убирать их по окончании работы, а также упорядочивать окна. Когда Вы закрываете Jot, все заметки сохраняются в базе данных и при следующем запуске Jot вновь появляются на экране.
Рис. 32-6 Приложение Jot в действии
«Тиражирование» дочерней формы
Первая кнопка на панели инструментов создает новое, пустое окно для заметок. Делается это так: на этапе разработки я создаю единствен нуто дочернюю форму Note с именем,/г/нЛгоА?и помещаю вызов процедуры MakcNewNotc, создающей новый экземпляр формы Note, в процедуру обработки события ВиЧопСНскдля кнопки панели инструментов. Если Вы взглянете на процедуру MakeNewNote, то заметите две строки кода, которые создают и отображают новый экземпляр формы Note:
Dim newNote As New frmNote
newNote.Show
Все экземпляры этой дочерней формы совместно используют одни и те же процедуры обработки событий. Для ссылки на свойства текущего экземпляра дочерней формы (т. е. на форму Note, находящуюся в фокусе ввода) в этих процедурах применяется ключевое слово Me. Например, обработчик события Unload дочерней формы сохраняет ее свойство Caption в базе данных. Чтобы записать заголовок именно выгружаемого экземпляра, обработчик события Unload ссылается на Me.Caption.
«Горячие» клавиши в MDI-форме
Свойство KeyPreview формы Note установлено как True, поэтому все нажатия клавиш перехватываются до обработки формой или ее объектами. Форма Note обрабатывает только комбинацию клавиш Ctrl+T, нажатие которых обновляет дату и время, отображаемые в заголовке формы, их текущими значениями. Обычно дата и время
каждой заметки соответствуют моменту ее создания. -Горячая * клавиша позволяет изменить заголовок когда угодно. Даже если Вы определите свои «горячие- клавиши, то все равно сможете использовать при редактировании заметок встроенные комбинации клавиш Ctrl+X, Ctrl+C и СпЧ+Удля вырезки, копирования и вставки текста. Команда Ctrl+T действует подобно встроенным. В обработчик события KeyDown легко добавить код для обработки и других клавиш или их комбинаций.
Рис. 32-7 и 32-8 иллюстрируют форму Note до и после нажатия клавиш Ctrl+T.
Even dwghih* amerced dtcb'r actually hit the Eailbtn^ugxtof 1996.
Ihegflgcls onthe moon's &bil. and hence on the Earh nditeclly.wei» jpnptnund Increased Iide? started die great inland iwgration-
Рис. 32-7 Заметка, созданная 4 июля 1997 года
ауи/янкенглм .+ ;; +■<+№Р:
г^бл though the atleiDfd diii'Lechjaly hi 'h&EafihinAi^^t nt lEtlj lb®effects withe moon's ati. and hence on the Earth indirect^.'^We profound1 fnertowd tides sterfetf the great nland miffrin.
Рис. 32-8 Та же заметка, но заголовок ее окна обновлен текущими датой и временем
Создание базы данных
При загрузке форма Jot пытается открыть файл базы данных.ЮТMDB, чтобы считать ранее сохраненные записи. Если такого файла нет (например, при первом запуске приложения), запрос на открытие игнорируется. База данных не создается до тех пор, пока Вы не закроете первую дочернюю форму Note. Когда она закрывается в отсутствие базы данных, программа выполняет специальный блок кода, автоматически создающий нужную базу. Понимание работы этого кода пригодится при программировании '■ ■; ।. о . к базам данных. Ниже приведен код из процедуры обработки события Unload дочерней формы Note, создающий новую базу данных (если таковой еще нет).
' создаем пустую базу данных
Set dbJot: = Workspaces(Q),CreateDatabaseC'Jot.mdb", dbLangGeneral) ' создаем таблицу
Set tbdJot = dbJot.CreateTableDef("JotTable")
создаем первое поле
Set fldJot = tbdJot.CraateField("JotDateTlme", dbDate).
' добавляем его в набор Fields таблицы
tbdJot.Fields.Append fldJot-
' создаем второе поле
Set fldJot = tbdJot.CreateField("JotNote", dbHemo) ' и тоже добавляем в таблицу tbdJot. Fields. Append fldJot
' добавляем новую таблицу в набор TableDefs dbJot.TableDefs.Append tbdJot
* новая база данных готова
Методы Create Data base, CreateTableDefu Create Field, какяспо из их имен, создают объекты Database, TableDef и Field соответственно. Однако они не включают созданные объекты в родительские, а лишь выделяют требуемую память и конструируют их. Понадобится еще один шаг для подключения каждого нового объекта к структуре родительского объекта, его создавшего. В коде, приведенном выше, присутствуют несколько методов Append, добавляющих новые объекты в их наборы. Так, объект TableDef содержит набор Fields, а тот — все объекты Field в определенной таблице. Для вставки только что созданного объекта Field в этот набор применяется метод Append. Как видите, процесс получается двухэтапным: сначала Вы создаете объект, потом включаете его в набор. То же относится и к другим объектам базы данных.
Центрирование изображения на MDI-форме
Не все элементы управления можно размещать на MDI-форме. В общем случае для нее подходят лишь те элементы, у которых есть свойство Align, и они всегда выравниваются по верхнему, нижнему, левому или правому краю формы. Кроме того, любое изображение, присвоенное свойству Picture MDI-формы, выравнивается только по левому верхнему углу формы. Как же программа Jot добивается центрирования растровой картинки на MDI-форме даже при изменении размеров этой формы? Для этого применен один трюк, о котором Вы должны знать.
Дочернюю форму несложно передвинуть в любое место в пределах родительской MDI-формы. Этим я и воспользовался, чтобы центрировать форму Splash в обработчике события Resize формы Jot.
Форма Splash отображает картинку и ничего больше; ее свойство BorderStyle устанавливается как 0 - None, а размеры подгоняются под размеры элемента Image, содержащего собственно картинку. В итоге, когда форма splush размещается в центре MDI-формы, пользователь видит только центрированное изображение. Рис. 32-9 и 32-10 иллюстрируют форму Jot с таким изображением до и после изменения ее размеров.
Рис. 32-9 Форма Jot с изображением по центру
.0» Е№>
Рис. 32-10 Изображение остается в центре даже при изменении размеров формы Jot
J0T.FRM
Как мы видим на рис. 32-11, приложение Jot содержит 4 файла. JOT.FRM — главная, стартовая MDI-форма, и мы рассмотрим ее в первую очередь.
У- АЬоиг (About frmt
S- FnrJot(Jot.Frm) tj'■ frmNote (Note ftm) tf hmSpfash (Splash freni
Рис. 32-11 Список файлов в проекте Jot
Рис. j2-i 2 иллюстрирует форму Jot на этапе разработки.
Рис. 32-12 МО1-форлш.)о1на этапе разработки
Добавьте в проект стандартной ЕХЕ-программы MDI-форму и используйте следующие таблицы для размещения на ней .элементов управления и установки их свойств.
Меню и их элементы в JOT.FRM
Название
Имя
Уровень вложения
Параметр Enabled
<S-File
mnuFile
0
True
&New
mnuNeie
1
False
вОреп-
mnuOpen
1
False
@Save
mnuSaee
1
False
Save. вА?_.
mnuSaveAs
1
False
—
mmd'ileDasbl
1
True
E&xit
mnuExit
1
True
«■Help
mnuHelp
0
True
^Contents
mnuContents
1
True
SSearcb for Help on...
mnuSearcb
1
True
—
mnuHelpDashl
1
True
SAbout-
mnuAbout
1
True
Объекты JOT.FRM и значения их свойств
Тип и номер1
Свойство
Значение
form
Name •
frmjot
Caption
Jot-«-Note
Toolbar
1
Name
tlbToolBar
Помер, указа I (ный в первой колонке, соответствует порядковомупомеруобъек- та на форме (рис, 32-12).''
Объекты JOT.FRM и значения их свойств (продолжение)
Тип и номер Свойство Значение
ImageList
Nameimllcons
CommonDiaiog
NameCCllO ne
Панели инструментов, кнопки и подсказки
Создавая в прежних версиях Visual Basic панели инструментов с кнопками и всплывающими подсказками, приходилось пускаться на всяческие ухищрения или использовать нестандартные элементы управления третьих фирм, но теперь эта задача предельно проста. Рассмотрим, как создать панель инструментов, которая показана на рис. 32-12.
Сначала надо загрузить изображения кнопок в элемент ImageList. Щелкните его правой кнопкой мыши и выберите из контекстного меню команду Properties. Установите переключатель размера изображений на 1бх 16. Перейдите на вкладку Images и добавьте изображение для каждой кнопки панели инструментов, -нажимая* кнопку! 1 г^. 11 Picture.
Бот список изображений, которые надо вставить в ImageList:
Индекс
Изображение
1
2
3
4
5
6
NEW.BMP
DELETE.BMP'
CASCADE.BMP'
TILEH.BMP'
TILEV.BMP
ARNGICON.BMP
Первые два изображения, упомянутые в таблице, — NEW.BMP и DELETE.BMP — находятся в каталоге Common\Crraphics\BitmapS\ Tlbr_w95 на компакт-диске Visual Studio. Изображения CASCA- DE.BMP, TILEH.BMP, TILEV.BMP и ARNGICON.BMP созданы в Microsoft Paint и содержатся на компакт-диске, прилагаемом к книге.
Рис. 32-13 иллюстрирует диалоговое окно Property Pages для элемента ImageList после вставки всех изображений кнопок.
Загрузив изображения в ImageList, подключите его к элементу Toolbar. Щелкните панель инструментов правой кнопкой мыши и вновь выберите команду Properties. В поле ImageList укажите значение iHiilcoH.s. Чтобы создать кнопки и связать их с изображениями из списка, перейдите на вкладку Buttons (рис. 52-14).
Рис. 32-13 Изображениякнопок включены в элемент ImageListРис. 32-13 Изображениякнопок включены в элемент ImageList(йм в^™ [а*— |
Рис. 32-13 Изображениякнопок включены в элемент ImageList
Рис. 32-13 Изображениякнопок включены в элемент ImageList
{jjjptitw I"' Лежнем |" "
г-’пижм 3
■; ■p'";;^ T| №h fPieA^lhi: pT“
киск Г
•BsNoflNfenus ■
■. I j Btf la/11 ^4..j• ‘ ■ j
Рис. 32-14 Диалоговое окно Property Pages для элемента Toolbar
Кнопки добавляются на панель инструментов поочередно (щелчком Insert Button). Для каждой кнопки Вы заполняете поля диалогового окна требуемой информацией. Текст, введенный в поле ТооЬ TipText, появится во всплывающей подсказке для этой кнопки. В поле Key укажите метку; постарайтесь сделать ее информативной, так как этот текст будет задействован в программе при определении кнопки, выбранной пользователем, — от значения метки зависит четкость программного кода. В поле Image введите номер изображения из списка элемента ImageList; это значение связывает кнопку с конкретным изображением.
Свойство Style определяет тип кнопки; обычно оставляют значение О - ibrDefmtU предлагаемое по умолчанию Чтобы вставить промежуток между кнопками на панели инструментов, добавьте еще одну кнопку и установите ее свойство Style как 3 - tbrSeparator. Подобным кнопкам-разделителям не требуется присваивать ни
изображение, ни ключ. Есть и другие свойства, позволяющие управлять внешним видом и поведением кнопок, но в этом примере они не нужны. (Вы сами потом ознакомитесь с полным набором их свойств.)
Параметры кнопок для элемента rtbTbo/Вагпоказаны в таблице.
Index (индекс кнопки)
Key (ключ)
Style (стиль)
ToolTipText
(подсказка)
Index Image (индекс изображения)
1
New
0 - tbrDefault
New
1
2
Delete
0 - tbrDefault
Delete
2
3
3 - tbrSepamtor
О
4
Cascade
0 - tbrDefault
Cascade
3
5
TileHorizontalty
0 - tbrDefault
Tile Horizontally
4
6
TileVertically
О - tbrDeJaull
Tile Vertically
5
7
Arrangelcons
0 - tbrDefault
Arrange Icons
6
Вот и все, что нужно для создания панели инструментов с кнопками и подсказками! Если Вам уже доводилось программировать всплывающие подсказки для кнопок няне лей инструментов в прежних версиях Visual Basic, Вы в полной мере оцените ту легкость, с которой они реализуются теперь.
И, наконец, введите в модуль формы Jot следующий код,
Исходный код JOT.FRM
Option Explicit
Public blnSave As Boolean
Private Sub MDIForm_Load()
' центрируем эту форму на экране
Me. Nove (Screen.Width - Width) \ 2, _
(Screen.Height - Height) \ 2
' загружаем все заметки из базы данных LoadNotes
" выводим изображение-заставку frmSplash.Show
End Sub
Private Sub MDIForni_Ou0ryUnload(Cancel As Integer, UnloadMode As Integer)
' готовимся к записи всех имеющихся заметок blnSave = True
End Sub
Private Sub MDIForm_flesize()
продолжение
' центрируем изображение-заставку на MDI-форме '' frmSplash.Hove (Me.ScaleWidth - frmSplash.Width) \ 2, _
(Me.ScaleHeight - frmSplash.Height) \ 2
End Sub
Private Sub mnuAboutJ)lick()
' устанавливаем свойства и отображаем форму About
About,Application - "Jot"
About,Heading = _
"Microsoft Visual Basic 6.0 Developer's Workshop" About. Copyright = "1998 John Clark Craig and Jeff Webb" About.Display
End Sub■
Private Sub mnuExit_Click() .
Unload Me
End Sub
Private Sub ronuContents_Click()
cdlOne.HelpFile = App.Path & "\.,\,,\Help\Mvbdw.hip" CdlOne. HelpCommand - cdlHelpContents
cdlOne.ShowHelp
End Sub
Private Sub mnuSearch_Cllck()
■ cdlOne. HelpFile * App.Path & "Y A-AHelp\Mvbdw.hlp~ cdlOne.Helpcommand = cdlHelpPartialKey cdlOne.ShowHelp
End Sub
Private Sub LoadNotesO'
Dim dbJot As Database
Dim recJot As Recordset
' открываем базу данных с Предыдущими заметками
On Error Resume Next
Set dbJot = Workspaces(0).0penDatabase("Jot.mdb") ' если такой базы данных нет, ничего не показываем If Err Then Exit Sub On Error GoTo 0
' открываем таблицу заметок
Set recJot = dbJot.OpenRecordset("JotTable")
' RecordCount отличен от О, если есть хоть одна запись If recJot.RecordCount Then
' создаем окна для каждой заметки
Do Until recJot, EOF
MakeNewNote Format(recJot! JotDateTime, ._
продолжение
"General Date"), recJot!JotNote recJot.NoveNext
Loop.
End If
теперь очиищем таблицу
dbJot.Execute "Delete * from JotTable"
закрываем таблицу и базу данных recJot.Close
о dbJot.Close.
End Sub
Private Sub HakeNewNote(sdtDateTlme As string, strNote As String) ' создаем новый экземпляр формы Note.
Dim newNote As New frmNote
' указываем заголовок и содержимое заметки
newNote.rtfNote.Text = strNote ne^-w-o г»пНпП = sdtDateTime ' отображаем новую форму Note
- flewNote.Show
End Sub
Private Sub tlbToolBar_ButtonClick(ByVal Button „
■ .As MSComctlLib.Button)
Select Case Button.Key
Case "New"
MakeNewNote Format(Now, "General Date"), ""
Case "Delete"
Unload ‘-^veFor#
Case "Cascade"'
frmjot.Arrange vbCascade
Case "TileVertically""
frmjot,Arrange vbTlleVertical
Case "TileHorlzontally"■
-frmJot.Arrange vbTileHorlzontal
Case "ArrangeIcons'
frmJot. Ar range vbArrangeicons
Case Else End Select
End sub
SPLASH.FRM
Как я уже говорил, эта форма показывает в центре основной формы эмблему программы. Она содержит 1 элемент Image, и еще на этапе разработки Вы загружаете в него файл JOT.BMP (он находится на компакт-диске, прилагаемом к книге). Рис. 32-15 иллюстри-
рует форму ^pk^h на этапе разработки. Заметьте: у формы нет рамки, так как ее свойство BorderStyle установлено как О - None.
Рис. 32-15 Форма Splash на этапе разработки:
Вставьте элемент управления Image на пустую форму и установите свойства — ее и элемента управления — в соответствии со значениями, приведенными в таблице.
Объекты SPLASHJRM и значения их свойств
СвойствоЗначение
Form
Name
frmSplash
Caption
JrmSplash
BorderStyle
0 - None
MDIChJld
True
ShowInTaskBar
False
Image
Name
imgSplash
Picture
]OTBMP
Весь код для этой формы содержится в процедуре Form_Load. При загрузке формы элемент Image смещается в ее левый верхний угол, и размеры формы подгоняются под размеры этого элемента.
Исходный код SPLASH.FRM
Option Explicit.
Private Sub Form_LoadO
перемещаем изображение в левый верхний угол формы imgSplash.Move о, 0
' подгоняем форму под размеры изображения
Me.Width = imgSplash.Width
. Me.Height - imgSplash.Height
End Sub
NOTEFRM
Форма Note создается один раз на этапе разработки и в период выполнения дублируется по мере необходимости. Каждый экземп-
ляр формы хранит одну заметку. Для редактирования заметок я выбрал элемент RichTextBox, но и TextBox вполне подойдет. Если Вы намерены расширить функциональность приложения Jot, элемент RichTextBox предпочтительнее, так каку него больше возможностей.
Рис. 32-16 иллюстрирует форму Note на этапе разработки (на ней всего один элемент управления RichTextBox).
Рис. 32-16 Форма Note на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, приведенные ниже.
Объекты WJItf RM и значения их свойств
СвойствоЗначение
Form
Name
jnnNote
Caption
JvmNote
KcyPrcview
True
MDIChild
TtUe
RichTextBox
Name
rtfNote
ScrollBars
3 - rtfbotb
Исходный код NOTE.FRM
Option Explicit■
Private Sub rorm_KayDown(KeyCode As Integer, Shift As Integer)
' обновляем дату и время в заголовке, если нажаты клавиши Ctrl+T
If KeyCode - 84 And Shift = 2 Then
Me.Caption = Format(Now, "General Date")
End if
End Sub
Private Sub Form_Resize()
rtfNote.Nove 0, 0, ScaleWidth, ScaleHelght
End Sub
продолжение
Private Sub Form_Unload<Cancel As Integer) Dim dbJot As Database Dim recJot As Recordset
Dim tbdJot As TableDef .'
Dim fid Jot As Field
Dim vntErrorNumber As Variant
Dim dtmDateTime As Date Dim strNote As String ' если blnSave равен True, надо сохранить эту заметку If frmJot.blnSave = False Then Exit Sub ' открываем базу данных для сохранения заметки On Error Resume Next
Set dbJot = Workspaces(0).0penDatabas9("Jot.mdb") vntErrorNumber * Err..
On Error GoTo 0.
' создаем базу данных, если ев нет If vntErrorNumber Then
' создаем пустую базу данных
Set dbJot - Workspaces(O). CreateOatabase("Jot. mdb”, _ dbLangGeneral) ' создаем таблицу
Set tbdJot = dbJot. CreateTableDefCJotTable") ' создаем первое поле
Set fldJot = tbdJot. GreateFleld("JotDateTime", dbDate)
' добавляем его в набор Fields таблицы
tbdJot.Fields.Append fldJot ' создаем второе поле Set fldJot = tbdJot. CraateField("JotNote", dbMemo) ' и тоже добавляем в таблицу
toejot.Fields.Append fldJot
' добавляем новую таблицу в набор TableDefs
dbJot.TableDefs.Append tbdJot ' новая база данных готова
End If.
' получаем рабочий набор записей"
Set recJot = dbJot.OpenRecordset("JotTable”, dbOpenTable) ' добавляем новую записи
recJot.AddNew ' готовим данные, включаемые в запись
dtmDateTime = Ne.Caption strNote = Не,rtfNote.Text■
If strNote = "" Then strNote = " ”
' вводим данные sсоответствующие поля новой записи recJotIJotDateTime = dtmDateTime recJot!JotNote = strNote ' обновляем базу данных.
■продолжение'"
recJot.Update
.' закрываем набор записей и базу данных
recJot. Close■
dbJot.Close
End Sub.
Тестирование приложения Jot
Для проверки этого приложения установите в диалоговом окне References ссылку на Microsoft ОАО ? 51 Object Library. Кроме того, установите в диалоговом окне Project Properties свойство Startup Object какfrm/игКоть Jot и простая программа, она послужит Вам точкой опоры в разработке собственных MDI-приложений.
Утилиты
В Visual Basic очень легко создавать небольшие утилиты. Одни полезны при разработке приложений, другие просто помогают осваивать программирование на Visual Basic. В этой главе представлены 3 утилиты: первая позволяет экспериментировать с курсорами мыши, вторая — воспроизводить мультимедийные файлы на компьютере, а третья — вычислять коэффициент резкости погоды (windchill index) (есть такой термин в метеорологии).
Приложение MousePtr
Приложение MousePtr дает возможность быстро просмотреть все 16 стандартных курсоров мыши и проверить любой файл значка: годится ли он на роль курсора.
Рис. 33-1 иллюстрирует форму MousePtr в период выполнения. Когда Вы указываете один из вариантов, курсор мыши соответственно меняет свою форму. Чтобы использовать какой-нибудь значок в качестве курсора, надо выбран, последний вариант, 99 - Custom Icon. При нервом выборе этого варианта появляется диалоговое окно Select An Icon File, а в последующем сразу показывается заданный значок. Для смены значка щелкните кнопку Select Icon и укажите в диалоговом окне Select An Icon File другой значок.
Элемент управления ТехгВох, размещенный на форме, я использую как памятку. При выборе О - Default или 1 -Апхмг появляется один и тот же курсор в виде стрелки. Разницу между ними Вы увидите, проведя курсор над текстовым полем: стандартный* (default) превратится в I-образный (рис. 33-1Y а стрелка (arrow) так и останется стрелкой.
На рис. 33-2 в роли курсора мыши выступает значок EARTH.ICO. Этот и другие значки находятся в папках Visual Basic или на компакт-диске Visual Studio. Если Вы намерены использовать файлы значков в качестве курсоров, поместите их в файл ресурсов, как описано в главе 26 -Разработка проектов».
Как видно из рис. 33-3, в проекте ; файла. Форма About создает стандартное диалоговое окно About. .■ ■
Поим, иерей.-. Стандартный курсор idefauli cursor) всегда принимает форму, определенную в объекте, над которым его перемещают.
< 5 trwSSГ 1 - Алом". ^ 2- Crorr^arr*< 5 trwSSГ 1 - Алом". ^ 2- Crorr^arr*Move mouiB pointer over Ifyi tent bon to сотрете mOUte pointer talbngt 0 - DelauJjand 1 -ArrowMove mouiB pointer over Ifyi tent bon to сотрете mOUte pointer talbngt 0 - DelauJjand 1 -Arrow■ Г 6-$«e1NSEWl r $ eSizelNESWjГ 7-St«£NS];^ fl Sin9tM/BE|<“ 5-Si»fWEJ•^ is- U^Altw■ Г 6-$«e1NSEWl r $ eSizelNESWjГ 7-St«£NS];^ fl Sin9tM/BE|<“ 5-Si»fWEJ•^ is- U^AltwC 11 - Hwq|»hГ 12 No Drop'f 13 - Allow and HtMJKgiw*Г" 14 - Anow end Question Mwk Г tSSkbAlЮ - Cwto* iconC 11 - Hwq|»hГ 12 No Drop'f 13 - Allow and HtMJKgiw*Г" 14 - Anow end Question Mwk Г tSSkbAlЮ - Cwto* iconCudtH* icon twoyw polnteeCudtH* icon twoyw polnteeInoue)Inoue)99
< 5 trwSS
Г 1 - Алом". ^ 2- Crorr^arr*
< 5 trwSS
Г 1 - Алом". ^ 2- Crorr^arr*
Move mouiB pointer over Ifyi tent bon to сотрете mOUte pointer talbngt 0 - DelauJjand 1 -Arrow
Move mouiB pointer over Ifyi tent bon to сотрете mOUte pointer talbngt 0 - DelauJjand 1 -Arrow
■ Г 6-$«e1NSEWl r $ eSizelNESWj
Г 7-St«£NS]
;^ fl Sin9tM/BE|
<“ 5-Si»fWEJ
•^ is- U^Altw
■ Г 6-$«e1NSEWl r $ eSizelNESWj
Г 7-St«£NS]
;^ fl Sin9tM/BE|
<“ 5-Si»fWEJ
•^ is- U^Altw
C 11 - Hwq|»hГ 12 No Drop
'f 13 - Allow and HtMJKgiw*
Г" 14 - Anow end Question Mwk Г tSSkbAl
Ю - Cwto* icon
C 11 - Hwq|»hГ 12 No Drop
'f 13 - Allow and HtMJKgiw*
Г" 14 - Anow end Question Mwk Г tSSkbAl
Ю - Cwto* icon
CudtH* icon twoyw polntee
CudtH* icon twoyw polntee
Inoue)
Inoue)
9
9
Рис. 33-1 Форма MousePtr в действии
Г 0 Sef*A с ! -Arrow ^ ^ • Crmtt#i«rr г a-i-вж f 4 - Icon Г $-5iw(NSEW) г 6 -Ум(НЕЗДГ .l^befliSI <* 8-Sta WW5EJ Г S-Si»fV£} С 18 ■ Up Arrow <* <1 -ll<wgl*r*Г 0 Sef*A с ! -Arrow ^ ^ • Crmtt#i«rr г a-i-вж f 4 - Icon Г $-5iw(NSEW) г 6 -Ум(НЕЗДГ .l^befliSI <* 8-Sta WW5EJ Г S-Si»fV£} С 18 ■ Up Arrow <* <1 -ll<wgl*r*[Mv'e mouie powrtei oV6l this tent Ьок tin compare mputa poihtai selbnga Ш - DeJauWand J Arrow.CtMltMHcon «Ou»» pointer -C:\»*3lWQUSEPTRSEail|!ukrtiSehc/fwi |[Mv'e mouie powrtei oV6l this tent Ьок tin compare mputa poihtai selbnga Ш - DeJauWand J Arrow.CtMltMHcon «Ou»» pointer -C:\»*3lWQUSEPTRSEail|!ukrtiSehc/fwi |Г 12-No DropT1 з - Arrow erof H«irgk*a "' 14 Airow end Qwllon Mo*Г 12-No DropT1 з - Arrow erof H«irgk*a "' 14 Airow end Qwllon Mo*Г IS - $ш> *1^ ^^vNSrlw 1Г IS - $ш> *1^ ^^vNSrlw 1
Г 0 Sef*A с ! -Arrow ^ ^ • Crmtt#i«rr г a-i-вж f 4 - Icon Г $-5iw(NSEW) г 6 -Ум(НЕЗД
Г .l^befliSI <* 8-Sta WW5EJ Г S-Si»fV£} С 18 ■ Up Arrow <* <1 -ll<wgl*r*
Г 0 Sef*A с ! -Arrow ^ ^ • Crmtt#i«rr г a-i-вж f 4 - Icon Г $-5iw(NSEW) г 6 -Ум(НЕЗД
Г .l^befliSI <* 8-Sta WW5EJ Г S-Si»fV£} С 18 ■ Up Arrow <* <1 -ll<wgl*r*
[Mv'e mouie powrtei oV6l this tent Ьок tin compare mputa poihtai selbnga Ш - DeJauWand J Arrow.
CtMltMHcon «Ou»» pointer -
C:\»*3lWQUSEPTRSEail|!ukrti
Sehc/fwi |
[Mv'e mouie powrtei oV6l this tent Ьок tin compare mputa poihtai selbnga Ш - DeJauWand J Arrow.
CtMltMHcon «Ou»» pointer -
C:\»*3lWQUSEPTRSEail|!ukrti
Sehc/fwi |
Г 12-No Drop
T1 з - Arrow erof H«irgk*a "' 14 Airow end Qwllon Mo*
Г 12-No Drop
T1 з - Arrow erof H«irgk*a "' 14 Airow end Qwllon Mo*
Г IS - $ш> *1
^ ^^vNSrlw 1
Г IS - $ш> *1
^ ^^vNSrlw 1
Рис. 33-2 Значок EARTHJCO, используемый как курсор мыши
jltcjaiA) (MouaoPIr.vbp)jltcjaiA) (MouaoPIr.vbp), O About (About rm)'d frmMojseRr(M<XKBptr.fmi), O About (About rm)'d frmMojseRr(M<XKBptr.fmi)
jltcjaiA) (MouaoPIr.vbp)
jltcjaiA) (MouaoPIr.vbp)
, O About (About rm)
'd frmMojseRr(M<XKBptr.fmi)
, O About (About rm)
'd frmMojseRr(M<XKBptr.fmi)
Рис. 33-3 Список файлов в проекте MousePtr
MOUSEPTR.FRM
На форме MousePtr расположена группа переключателей, позволяющих выбрать курсор мыши. Содержимое свойства Index каждого переключателя задает значение свойства Mousepointer формы, что в свою очередь определяет тип курсора. Эти величины варьируются в диапазоне от 0 до 15 и могут принимать особое значение, 99,
для загрузки файла значка как курсора мыши. Если Вы самостоятельно создаете такую форму, не забудьте установить свойство Index последнего переключателя как 99.
Рис. 33-4 иллюстрирует форму MousePtr на этапе разработки.
Е* й*
P В - Default1 Anar» ^ 2-CitHvhnrv Г з, ^Q«m4-ttw ' ll 5№»|N BE WJ6 5r«i|NE SV|7 ■ S^(N SJP В - Default1 Anar» ^ 2-CitHvhnrv Г з, ^Q«m4-ttw ' ll 5№»|N BE WJ6 5r«i|NE SV|7 ■ S^(N SJ. ' [Mow mouse pointer over this lext box .Im compare mouse pointer tettingt ' ' 10 Default and 1 Arrow.. ' [Mow mouse pointer over this lext box .Im compare mouse pointer tettingt ' ' 10 Default and 1 Arrow.г Cution iron mouie pomieiг Cution iron mouie pomieiВ SirtWSfJ . 'В SirtWSfJ . 'fl -fl -E|E|- ttourgleir- Ho Drop . ' . .1Э-A«ow endH«rtgl»tiГ 14 - АтжйггйОим^впН-мк '16 Size A#.J* 39 - CiitlMl^un- ttourgleir- Ho Drop . ' . .1Э-A«ow endH«rtgl»tiГ 14 - АтжйггйОим^впН-мк '16 Size A#.J* 39 - CiitlMl^un(none 1ь(none 1ьSelect^ feon ^Select^ feon ^{тэ]■04=9{тэ]■04=9
P В - Default
1 Anar» ^ 2-CitHvhnrv Г з, ^Q«m
4-ttw ' ll 5№»|N BE WJ
6 5r«i|NE SV|
7 ■ S^(N SJ
P В - Default
1 Anar» ^ 2-CitHvhnrv Г з, ^Q«m
4-ttw ' ll 5№»|N BE WJ
6 5r«i|NE SV|
7 ■ S^(N SJ
. ' [Mow mouse pointer over this lext box .Im compare mouse pointer tettingt ' ' 10 Default and 1 Arrow.
. ' [Mow mouse pointer over this lext box .Im compare mouse pointer tettingt ' ' 10 Default and 1 Arrow.
г Cution iron mouie pomiei
г Cution iron mouie pomiei
В SirtWSfJ . '
В SirtWSfJ . '
fl -
fl -
E|
E|
- ttourgleir
- Ho Drop . ' . .
1Э-A«ow endH«rtgl»ti
Г 14 - АтжйггйОим^впН-мк '16 Size A#.
J* 39 - CiitlMl^un
- ttourgleir
- Ho Drop . ' . .
1Э-A«ow endH«rtgl»ti
Г 14 - АтжйггйОим^впН-мк '16 Size A#.
J* 39 - CiitlMl^un
(none 1ь
(none 1ь
Select^ feon ^
Select^ feon ^
{тэ]
■0
4=9
{тэ]
■0
4=9
Рис. 33-4 Форма MousePtr на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Меню и их элементы в MOUSEPTR.FRM
Название
Имя
Уровень вложения
Параметр Enabled
©File
mnuFile
0
True
©New
mnuNeut
1
false
©Open-
mnuOpen
1
False
©■Save
mnuSave
= 1
False
Save ©As-
mnuSaveAs
1
False
—
mnuFileDasbl
1
True
E©xit
mnuExit
1
True
©Help
mnuHelp
0
True
©Contents
mnuContents
1
TVue
©Searchfor Help on...
mnuSearcb
1
True
—
mnuHelpDash 1
1
True
©About™
mnuAbout
1
True
Объекты MOUSEPTR.FRM и значения их свойств
Тип и номер1
Свойство
Значение
Form
'Name
frmMousePtr
Caption
Mouse Pointers
OptionButton
BorderStyle
3 - Fixed Dialog
1
Name
optMousePtr
Caption
0 - Default
OptionButton
Index
0
2
Name
optMousePtr
Caption
1 -Arrow
OptionButton
Index
1
3
Name
optMousePtr
Caption
2 ~ Crosshairs
OptionButton
Index
2
1
Name
optMousePtr
Caption
3 -I-beam
OptionButton
Index
3
5
Name
optMousePtr
Caption
4 - Icon■
OptionButton
Index
4
6
Name
optMousePtr
-•
Caption
5 - Size (NSE^
OptionButton
Index
5
7
Name
optMousePtr
Caption
6 - Size (NESW)
Index
6
Номер. указанпыП в первой колонке, соответствует порядковому номеру объекта на форме (рис, 33-4).
Объекты MOUSEPTR.FRM и значения их свойств (продолжение)
Тип и номер
Свойство
Значение
OptionButton
8
Name
optMousePtr.
Caption
7 - Size (N S)
Index
—
OptionButton
9
Name
optMousePtr
Caption
8 - Size (NWSg)
Index
8
OptionButton 10
Name
OptMousePtr
Caption
9 - Size (WE)
Index
9
OptionButton 11
Name
optMousePtr
Caption
10 - Up Arrow
Index
10
OptionButton
12
Name
optMousePtr■
Caption
11 - Hourglass
Index
11
OptionButton
13
Name
■ .optMousePtr■
Caption
12 ■ No Drop
Index
12
OptionButton
14
Name
optMousePtr
Caption
13 Arrow and Hourglass
Index
.13
OptionButton
15
Name
.OptMousePtr
Caption
14 - Arrow and Question Mark
Index .
14
OptionButton
16
Name ,
optMousePtr
Caption
15 ■ Size All
Index
15
Объекты MOUSEPTR.FRM И ЗНачеНИЯ ИХ СВОЙСТВ (продолжение)
Тип и номер
СВОЙСТВО'
Значение
OptionButton
17
Name
optMousePir
Caption
99 - Custom Icon
TexlBox
Index
99
18
Name
ixtTestArea
Text
More mouse pointer over ibis text box to compare mouse pointer settings
0 - Default and 1 - Arrow
Frame
Multiline
True
19
Name
fraSelect
Label
Caption
Custom icon mouse pointer
20
Name
Ibllcon
CommandButton
Caption
пусто
21
Name
cmdSelect
CommonDialog
Caption
^■Select Icon
22
Name
dlgOne
Исходный код MOUSEPTR.FRM
Option Explicit •
Private Sub cmdSelect_Click()•
'' запрашиваем имя файла значка
" dlgOne.DlalogTitle - "Select an Icon File" dlgOne. Filter = "Icon(*.ico)|*.lco" dlgOne.CancelError = True
' проверяем, не было ли щелчка кнопки Cancel
On Error Resume Next':■
dlgOne.ShowOpen ■ Il Err <> cdlCancel Then ' загружаем значок как курсор frmMousePtr.Houselcon = LoadPicture(dlgOne.FileName) ' показываем полное имя файла текущего значка Ibllcon.Caption = dlgOne.FileName
продолжение
' выбираем тип курсора - 99
'■ optMousePtr(99).SetFocus
End If
End Sub
Private Sub Forn_Load<)
' центрируем эту форму
left = (Screen.Width - Width) \ 2
Top - (Screen. Height - Height) \ 2
' смещаем фокус с текстового поля на группу переключателей Show
optMousePtr(0),SatFocus.
End Sub
Private Sub optMousePtr_Click(Index As Integer)
' устанавливаем выбранный курсор мыши
frmMousePtr. Mousepointer = Index
' открывав» диалоговое окно, если файл значка еще не выбран
If Index = 99 And Ibllcon = "(none)" Than omdSelect_Click
End If
End Sub ■■
Private Sub mnuAbout„Click()
■' настраиваем свойства
About.Application - "MousePtr”
About.Heading = "Microsoft Visual Basic 6.0 " & _.
"Developer's Workshop"
About.Copyright = "1998 John Clark Craig and Jeff Webb"
About.Display
End Sub'
Private Sub mnuExit_Click()
Unload Me
End Sub
Private Sub ninuContents„Click()
" dlgOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hip" dlgOne.HeipCommand - cdIHelpcontents
:dlgOne.ShowHelp
End Sub
Private Sub mnuSearch_Click()
dlgOne.HelpFile = App.Path & "\. ,\. .\Help\Mvbdw.hlp"
? dlgOne.HelpCommand = cdlHelpPartlalKey
dlgOne.ShowHelp
End Sub
Приложение ShowTell
Интерфейс этого приложения незатейлив, но позволяет быстро просматривать файлы изображений многих типов (BMP, ICO, JPG, GIF и WMF) и видеозаписи (AVI и MPG), а также прослушивать звуковые файлы (WAV).
На стартовой форме/гт51)ою№//содержится по кнопке для каждого типа файлов. Последние разбиты на две большие категории; статические, загружаемые в элемент Image, и динамические, требующие применения API-функции тс1Ехесще. Приложение автоматически обрабатывает все различия в этих типах файлов.
Рис. 33-5 иллюстрирует стартовую форму в действии.
V 1Г~^ ■V *1м
.'We|А,
WIMoil
Рис. 33-5 Стартовая форма приложения Sbou’Tell
При щелчке любой кнопки программа активизирует через элемент управления CommonDialog диалоговое окно File Open, в котором можно выбрать файл указанного типа. Статические изображения показываются на второй форме, jrmlmage, а динамические мультимедийные файлы проигрываются вызовом API-функции. Сразу после закрытия окна с изображением или по окончании аудио- или видеозаписи на экране вновь появляется диалоговое окно для выбора другого файла того же типа. Чтобы закрыть это окно и вернуться в основную форму, щелкните кнопку Cancel.
На рис. 33-6 показан пример отображения JPG-файла, а на рис. 33-7 — AVI-файла.
Рис. 33-6 Приложение Showlell показываетJPG-файл.
Рис. 33-7 Приложение Show lellвоспроизводит видеоклип (AVI)
Как видно из рис. 33-8, в проекте Show l ell две формы.
^ ’irrlwb^tO’TTTiiiiage.fTFf)
- Ck ffmShowTel (ShowTeUrm)
Рис. 33-8 Список файлов в проекте SbowTell
SHOWTELL.FRM
Я уже упоминал, что форма ShowTell выводит диалоговое окно для открытия файла с помощью элемента CommonDialog. Пока это окно присутствует на экране или показывается выбранный файл, форма ShowTell скрывается. При показе статического изображения убирается и форма ShowTell, и диалоговое окно ГТ- Open, но при воспроизведении мультимедийного файла диалоговое окно File Open тут же открывается снова.
Рис. 33-9 иллюстрирует форму ShowTell на этапе разработки. Она содержит 8 кнопок и 1 элемент CommonDialog.
Рис. 33-9 Форма Sbow’fellHa этапе разработки
Создавая форму ShowTell, используйте таблицу и исходный код, приведенные ниже.
Объекты SHOWTELLFRM и значения их свойств
Тип и номер1СвойствоЗначение
Form
CommonDialog
Name Caption Icon
jrmSbowTell
ShowTell
Eye.ico
1
Name
dlgOne
CommandButton
2
Name
cmdSbow
Caption
Bmp
Index
0
CommandButton
3
Name
cmdSbou-
Caption
Ico
Index
1
Номер, указанный в первой колонке, соответствует порядковому номеру объекта па форме (рис. 33-9).
Объекты SHOWTELLFRM и значения их свойств (продолжение)
Тип и номер
Свойство
Значение
CommandButton
4
Name
ondSbou-
сариоп
Wmf
Index
2
CommandButton
5
Name
cmdSbow
Caption
Witr
Index
3
CommandButton
6
Name
cmdSbow
Caption
Gif■
Index
4
CommandButton
7
Name
cmdShow
Caption
JPg
Index
5
CommandButton
8
Name
cmdSbow
Caption
Aui
Index
6
CommandButton
9
Name
cmdSbow
Caption
Mpg
Index
—
Исходный код SHOWTELLFRM
Option Explicit
Private Declare Function mciExecuts _
Lib "winmm.dll" ( _
ByVai IpstrCwand As String _
) As Long
Private Sub Form_Load()
.' центрируем форму
. Me.Move (Screen.Width - Width) \ 2, (Screen.Height Height) \ 2
End Sub
Private Sub cmdShow_Click(intlndex As Integer)
продолжение■"
Select Case Intlndex
Case О
ShowMedia "top"
Case 1
ShowMedia "loo"
Case 2
. ShowMedia "wmf"
Case 3
PlayMedia "wav"
:Case 4
ShowMedia "gif"
Case 5
ShowMedia "jpg"
Case 6
iPlayMedia "avi"?
'Case ?
PlayMedia "mpg"
"End Select
Me,Show
End Sub
Private Sub PlayMedia(strFiIeFxf As String)
' обработка мультимедийных файлов
Dim strFilter As String
Dim strFile String
Dim IngX As Long
strFilter *"*."& strFileExt A "J *." 4 strFileExt Do
strFile = GetFileName( strFilter)
If strFile = "" Then Exit Do
IngX - mclExecute("Play " & strFile)
Loop
End Suh.
Private Sub ShowMedlafstrFileExt As String) ' обработка статических изображений Dis strFilter As String Dim strFile As String Dim IngX As Long strFilter = "*." & strFileExt & " |*." & strFileExt Bo'
strFile = GetFileName(strFilter)
If strFile = "" Then Exit Do frmlmage.Display strFile
Loop
продолжение-
End Sub'
Private Function GetFileName(strFilter As String) As String
для показа диалогового окна File Open используется
элемент управления CommonDialog■
With dIgOne
.DialogTltle = "ShowTell"
.Flags = cdlOFNHldeReadGnly .
.Filter= strFilter
.Filename "",
.CancelError = True
fin Error Resume Next
.ShowOpen
On Error GoTo О
GetFileName = . Filename
Eri With'
End Function
FRMIMAGE.FRM
Фопм.1 /г^г/тл^сзагружает и показывает статическое изображение из файла. Код и модуле формы содержит только открытый метод, Display, обрабатывающий все, что связано с отображением картинки. Единственный параметр метода принимает полное имя файла, который надо загрузить и показать. Это имя запоминается в свойстве Caption фирмы. чтобы впоследствии на него можно было ссылаться. Потом изображение загружается из файла, а позиция и размеры формы подгоняются так, чтобы изображение оказалось в центре экрана.
Добавьте в свой проект ShowTell ;. форму и установите ее
<|«ть i-i«> Мяте как fi-mlmage. Вставьте на нее элемент управления Image с именем imgOne и введите в модуль формы следующий код.
Исходный код FRMIM AGE.FRM
Option Explicit'.
Public Sub ■ splay(strFiiBNameAs String)
Dim IngWedge As Long
Dim IngHedge As Long
Me.Caption я strFileName
With ImgOne
. Picture = LoadPloture(atrFileMame) ■
.Move 0, 0
IngWedge = Width - ScaleWidth
IngHedge = Height - ScaleHeight
продолжение■ '
Ме.МоУб (Screen.Width - (.Width + IngWedge)) \ 2, _ (Screen.Height - (.Height * IngHedge)) \ 2, „ .Width + IngWedge, .Height + IngHedge
End With
He.Show vhModal
End Sub
Приложение WindChil
Приложение WindChil — также, каки Государственная метеорологическая служба (National Weather Service), — вычисляет коэффициент резкости погоды, исходя из температуры воздуха и скорости ветра. (Если Вас интересуют научные и технические аспекты определения такого показателя, проведите поиск в World Wide Web по ключевым словам wind chill. Я нашел несколько узлов со сведениями на этот счет.)
Приложение демонстрирует применение элемента Slider для вводя текущих температуры воздуха и скорости ветра. На рис. 33-Ю форма WindChil показана в период выполнения: на двух ползунках выставлены скорость ветра (17 миль/ч) и температура ( t2=F).
У этого приложения есть определенные преимущества в сравнении со стандартными диаграммами, которые можно найти во «Всемирной паутине* и других местах. Оно позволяет указать любые значения температуры и скорости ветра в диапазоне, заданном для элементов Slider, тогда как на диаграммах значения округляются до ближайших 5 или 10 градусов (миль/ч). Но наибольшее его преимущество в том, что, используя несколько переключателей, Вы можете легко перейти к метрическим величинам: от градусов по Фаренгейту к градусам по Цельсию и от миль/ч к км/ч. При щелчке переключателя текущие позиции ползунков сохраняются, а числа преобразуются в альтернативные единицы. На рис. 33-11 показаны те же значения, что и на рис. 33-Ю, но в метрической системе единиц.
SteSlStieedfautMh-^utD
— Ц —■ -ь
17 МРИ3»Г
Рис. 33-Ю Форма WindChil в действии
Рис. 33-11 Форма WindChilсо значениями в метрической ' системе
WINDCHIL.FRM
Свойства Min и Мах обоих ползунков соответствуют полному диапазону точности уравнения, на основе которого вычисляется коэффициент резкости погоды: 5-50 миль/ч для скорости ветра и от - 50 до ОйТдия температуры. Значения вне этих диапазонов недопустимы. Применение ползунков вместо текстовых полей избавляет от проверки значений, введенных пользователем, поскольку исключается возможность указать недопустимую скорость ветра или температуру. Обратите внимание (см. таблицу «Объекты WINDCHIL.FRM и значения их свойстве), что свойствам LargeChange элементов Slider присваиваются значения Это позволяет при щелчке с любой стороны ползунка изменять его положение на 1 градус или 1 единицу скорости ветра. Для выбранных диапазонов этот прием очень удобен.
Рис. 33-12 иллюстрирует форму WindChil на этапе разработки. Свойству ForeColor некоторых текстовых меток я присвоил красный или синий цвет, просто чтобы украсить форму. Не нравится — возьмите черный цвет. Любые эксперименты с внешним видом этой формы на вычисления не влияют.
ПРИМЕЧАНИЕ Обычно я избегаю типа данных Variant — в основном потому, что явное указание типов позволяет созда- ■ вать более эффективный код и получать более предсказуемые промежуточные результаты, — но в этой программе решил эксперимента ради объявить большинство численных переменных как Variant. Все прошло благополучно. Программа работает достаточно быстро, а показываемые результаты точно соотаетствуюз диаграммам коэффициента резкости погоды, которые я нашел и Интернете. Кстати, некоторые утверждают, что программирование с использованием типа Variant легче, чем с применением явных типов. Думаю, все зависит
от конкретной программы и ее структуры. Ведь автоматическое преобразование строковых значений в числовые (и наоборот) — штук.) очень коварная, я и сам не раз получал весьма странные результаты из-за неожиданного автоматического преобразования чипов. С другой стороны, в некоторых приложениях вроде WindChil тип Variant работает просто
Рис. 33-12 Форма WindChil на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, при- веденныс ниже.
Меню и их элементы в WINDCHILFRM
Название
Имя
Уровень вложения
Параметр Enabled
©File
mnuFile
0
True
©■New
mnuNetc
1
False
©Open...
mnuOpen
1
False
©Sai'e
mnuSane
1 .
False
Save ©As„
mnuSaveAs
1
False
—
mnuFileDashl
1
True
E©xit.
mnuExit
1
True
©Help..
mnuHelp
'0
True
©Contents
mnuContents
1
True
©Searcbfor Help on...
mnuSearcb
1 -
True
—
mnuHelpDashl
1
True
©About..
mnuAbout
1
True
Объекты WINDCHILFRM и значения их свойств
Тип и номер'
Свойство
Значение
Form
Name
frmWindCbill
Caption
Windchill Factor
*
Borderstyle
3 - Fixed Dialog• ,
Icon•
Snowico
CommonDiatog
1
Name
dlgOne.
Label
2
Name
IblWindCbill
Alignment
2 - Center'
Font
MS Sans Serif Bold 12
ForeColor
^HOOFFOOOO^. .
Frame
г •
3
Name
p-aWindSpeed
Caption
Wind Speed■
ForeColor
&H000000FF&
Frame
4
Name
fra Temperature
Caption
Temperature■"
ForeColor
S-HOOOOOOFF<^'
OptionButton
S
Name
optMFH'
Caption
Miles/Hour.
Value
True
OptionButton
6
Name
optKPH1
Caption
Kilometers/Hour
OptionButton
7
Name
optFabrenbeit
Caption.
Fahrenheit
Value
True. -
OptionButton
8
Name
optCelsius
Caption
Celsius
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис 33-12).
Объекты WINDCHILFRM и значения их свойств (продолжение)
Тип и номер
Свойство
Значение
Label
9
Name
IbWindSpeed
Label
Allg! 11 nent
2 - Center
10
Name
IbHbmperature
Slider
Alignment
2 - Center
11
Name
sldWindSpeed
LargeChange
1
Max
50
Slider
Min
5
12
Name
sldTemperature
LargeChange
1
Max
90
Min
-50
Исходный код WINDCHILFRM
. Option Explicit
Private Sub mnuAbout„Click() ' настраиваем свойства With About
'.Application = "WindChill"
’ .Heading = "Microsoft Visual Basic 6.0 " & _ "'Developer's Workshop
.Copyright = "1998 John Clark Craig" .Display
End With
End Sub
Private Sub mnuExit_ClickO
Unload Me
End Sub
Private Sub mnuContants^CliokO
With dlgQne
.HelpFile = App.Path & "\.,\..\Help\Mvbdw.hlp" .HelpCommand = cdlHelpContents
.ShowHelp fcid With
CM. i ivAcmp.
■продолжение
" End Sub' '
Private Sub mnuSearch_Click()■
With dIgOne .HelpFile = App. Path & "V.V .\Help\Mvbdw. hip" . HelpCommand = cdlHelpPartialKey
.. .ShowHalp End With End Sub.....
private Function Oelsius(vntF)
преооразуем градусы по Фаренгейту в градусы по Цельсию
' Celsius = (vntt + 40} * 5 / 9 - 40
End Function
private Sub ChillOut()■■
Dim vntWind,
Dim vntTemp ■
Dim vntChill
. Olis strDisplay As String 1 получаем значений от ползунков
vntWind = sldWindSpeed.Value vntTemp = sldTemperature.Value 1 преобразуем миль/ч, если выбраны км/ч
IE optKPH.Value = True Then vntWind = Hph<vntWind)..
Bad If
..1 преобразуем в градусы no Фаренгейту, если выбраны градусы по
Цельсию
If optCelsius.Value = True Then vntTemp - Fahrenheit (vntTemp)
Bid If.
вычисляем коэффициент резкости погоды
VRtChill = Int(0.0817 * (SqrfvntWind * 3.71 + „
5.81 - 0,25 * vntWind) * (vntTemp - 91.4) ■;■ 91.4).
преобразуем обратно в градусы по Цельсию, есл« они выбраны
If optCelsius.Value - True Then
■ vntChill = Celsius(vntChill) End If
'1 показываем коэффициент резкость погоды
strDisplay = "Windchill index is & atrt(CInt(vntChill)) .
If optFahrenheit. Value • True Then IblWindChill.Caption = strDisplay & " F",
. 'Else
IblWindChill.Caption * strDisplay & " C"
End If,
End Sub.
продолжение
Private Sub cmdCancel_CHck()
' вькодим, если был щелчок кнопки Cancel
Unload frmWindChill
Bad Sub■
Private Function Fahrenheit(vntC)
преобразуем градусы по Цельсию а градусы по Фаренгейту Fahrenheit = (vntC + 40) *9/5-40
End Function
Private Sub Form LoadO
' принудительно обновляем ползунки
sldWindSpeed.Change
sldTemperature_Change
Bad Sub
Private Sub sldTemperature„Change()
Dim vntTmp'
' потучаем температуру
vntTmp = sldTemperature.Value
' показываем ее, используя выбранные единицы
If optCelsius.Value * True Then
IblTemperature.Caption = vntTmp & " C"
Else'
IblTemperature. Caption = vntTmp & " F"
End If
' вычисляем коэффициент резкости погоды
ChillOut:
End Sub■
Private Sub sldTemperature_Scroll()
.' обновляем ползунок при смещении его "ручки"
sldTemperature^Change End Sub'
Private Sub sldWindSpeed_Change()
Dim vntTmp
' получаем скороств ветра
vntTmp = sldWindSpeed,Value
■' показываем ее, исполвзуя выбранные единицы
If optKPH.Value = True Then
IbIWindSpsed. Caption * vntTmp & " KPtT
Else
. IblWlndSpeed.Caption = vntTmp & " MPH" .
Bid If
' вычисляем коэффициент резкости погоды
см. след. стр.
продолжение
ChillOut
End Sub
Private Sub '.idWindSpeed„Scroll()
' обновляем ползунок при смешении его "ручки" sld«ind5peed_Change
End Sub
Private Function Kph(vntMph)
' преобразуем миль/ч в км/ч
Ков = vntMph * 1.609344
End Function■
Private Function Mph(vntKph)
' преобразуем км/ч в миль/ч
I = '.ph / 1,609344
End Function
Private Sub optCelsius_Click()
Dim vntC
' преобразуем текущую температуру в градусы по Цельсию
vntC = vdbiua\=.iuTtieperature. Value)
If vntC < -45 Then vntC = -45
' переустанавливаем ползунок под градусы по Цельсию
sldTemperature.Min = -45
sldTemperature.Мах = 32
sldTemperature. Value = Clnt(vntC)
sldTemperature_Ghange
End Sub
. Private Sub optFahrenheit_Click().
Dim TitF
' преобразуем текущую температуру в градусы по Фаренгейту vntF г Fahrenhelt(sldTemperature.Value) If vntF < -50 Then vntF
" переустанавливав;- ползунок под градусы по Фаренгейту
sldTemperature.Min = -50
sldTemperature.Max = 90
sldTemperature. Value = Clnt(vntF)
c1 и тп(„ре rature_Change
End Sub'
Private Sub optKPH_Click()
Dim vntK
преобразуем текущую скорость ветра в км/ч
vntK - Kph(sldWindSpeed.Value)
продолжен ие
переустанавливаем ползунок' пой км/ч
sldWindSpeed.Min = 8
sldWindSpeed.Мак » 80
sldWindSpeed.Value = vntK sldWindSpeed_€hange
End Sub
Private Sub optMPH_Cllck{)
Dim vntM
' преобразуем текущую скорость ветра в миль/ч vntM я Mph(sldWindSpeed.Value) переустанавливаем ползунок под миль/ч
sldWindSpeed.Min = 5
sldWindSpeed.Max * 50
sldWindSpeed.Value = vntM
sldWindSpeed_Chango
End Sub
Сложные приложения
-N
Т)
О этой главе представлено несколько приложений, демонстрирующих «продвинутое» программирование на Visual Basic. Приложение Messages реализует один из способов управления программой "посредством команд, включаемых в отдельный текстовый файл. Программа шифрования файлов, Secret, использует целый ряд уже описанных форм и приемов. BitPack иллюстрирует применение DLL, созданной на С. И, наконец, приложение Dialogs демонстрирует несколько творческих приемов, расширяющих возможности программ, написанных на Visual Basic.
Приложение Messages
Вы можете создать собственный командный, или интерпретируемый, язык программирования, ориентированный на сугубо специфические задачи. Приложение Messages — простейший тому пример. Оно выводит на экран серию текстовых полей с сообщениями. Для этого объявляются и используются объекты Messages, определенные в модуле класса MSG.CLS и в связанном с ним файле MSG.FRM.
У объекта Message имеется свойство FileName, которому присваивается имя текстового файла особого формата с расширением MES. (Синтаксис этих файлов я опишу чуть позже.) При отображении сообщений, содержащихся MES-файле, модуль MSG.CLS используется совместно с MSG.FRM. Для управления внешним видом и поведением полей с сообщениями я предусмотрел две специальные команды, которые можно задать в MES-файлах. Эти команды просты, но их легко расширить, если Вам понадобится более гибкий контроль за отображением сообщений.
Синтаксис файла сообщений
Каждый MES-файл содержит блоки отображаемого текста — сообщения. Начало каждого блока помечается тремя тильдами ■), и
файл может содержать произвольное число сообщений. Сообщения из выбранного файла выводятся последовательно. Для контроля за отображением конкретного сообщения следует включить команду в ту строку текста, где находится разделитель блока.
Рассмотрим пример файла сообщений, который поможет понять, как все это работает. Возьмем простой файл MESSAGE.MES, содержащийся в том же каталоге, что и приложение Messages."
MESSAGE.MES
Этот файл сообщений - пример возможностей, демонстрируемых приложением Messages.
Обратите внимание, что все строки до заголовка первого блока текста игнорируются.
Это первый блок текста в файле MESSAGE.MES, Заметьте, что окно автоматически подстраивается под размеры сообщения.
Закройте это окно, чтобы перейти к следующему блоку текста в файле. Щелкните кнопку Close в правом верхней углу окна.
р Ю
Это сообщение должно автоматически исчезнуть через 10 секунд.
Если не хотите ждать, закройте его сами.
- F 2
Это сообщение должно появиться в окне, "мигающем" с частотой 2 раза в секунду.
— F 5
Это сообщение должно появиться в окне, "питающем” с частотой 5 раз в секунду.
— р 20 F 1
Это последнее сообщение в файле. Частота мигания
раз в секунду, и сообщение автоматически исчезнет через
секунд, если Вы сами не закроете его раньше.
В этом файле определены 5 сообщений, последовательно отображаемых объектом Message при его активизации из приложения Messages. Первое сообщение появится в немигающем окне и останется на экране до тех пор, пока не будет закрыто. В заголовке второго блока текста присутствует команда Р, означающая, что сообщение исчезнет через заданное время, в данном случае через 10 секунд. Для третьего сообщения задана команда F, контролирующая частоту мигания окна — она определяется числом, указанным за командой, в данном случае 2 раза в секунду. А последнее сообщение отображается в течение 20 секунд с частотой мигания 1 раз в секунду.
Прим, перев.: Здесь представлен файл MESSAGE.MES, содержимое которого переведено на русский, так как в нем . кигсайа концепция MES-файлов. А для иллюстраций использовал исходив™ файл MESSAGE.MES с компакт-диска, прилагаемого к книге.
Я определил лишь команды Р и F но можно добавить и другие — например, команду для управления цветом текста. Изучив обработку существующих команд, Вы легко mtt/iv новые.
Рис. 34-1 иллюстрирует первое сообщение, а рис. 34-2 — третье.
• Pl teWill• Pl teWill
• Pl teWill
• Pl teWill
Рис. 34-1 Сообщение, отображаемое приложением Messages
Рис. 34-2 Окно сообщения, мигающее, чтобы привлечь внимание пользователяРис. 34-2 Окно сообщения, мигающее, чтобы привлечь внимание пользователя
Рис. 34-2 Окно сообщения, мигающее, чтобы привлечь внимание пользователя
Рис. 34-2 Окно сообщения, мигающее, чтобы привлечь внимание пользователя
Для чего нужны файлы сообщений?
Сообщения, конечно, можно встраивать прямо в исходный код приложения. Но применение отдельных текстовых ASCII-файлов имеет свои преимущества. Например, желая изменить какие-то сообщения, Вы просто редактируете файл в Notepad, WordPad или любом другом текстовом редакторе. Кроме того, присутствие многостроковых сообщений в коде потребует конкатенации строк с использованием константы vbCrLfM их модификация может оказаться весьма утомительной. А если Вы создаете приложение, ориентированное на международные рынки, внешний файл сообщений дает те же преимущества, что и файл ресурсов. Вместо редактирования и перекомпиляции исходного кода для каждого иностранного языка Вы просто изменяете содержимое файла сообщений.
Состав проекта приложения Messages показан на рис. 34-3-Чтобы добавить такую поддержку сообщений в свою программу, включите в нее файлы MSG.CLS и MSG.FRM, а также блок кода, аналогичный тому, что содержится в MESSAGES.FRM.
бЕ®!1!!5!ЭВвЯв1||В№ "
l^s^.^cs (Hft^5iigf-s .vbp>|
О Ab»jr .'AbO'jt.rirc^
- Q, fmMessages (Messages.fan)
6 FmMsg (Msg.frm)
Er .^ Class tAodules a Message (Msg ds)
Рис. 34-3 Список файлов в проекте Messages
MESSAGES.FRM
MESSAGES,FRM — стартовая форма приложения Messages. Она использует лишь один экземпляр объекта Message — msgOne. Для последовательного отображения всех сообщений ?щ файла MESSAGE. MES код формы присваивает свойству FileName объекта Message полное имя файла и вызывает метод Display этого объекта. Внешний вид отображаемых сообщений определяется командами, встроенными в файл сообщений.
Я добавил на форму элемент управления CommonDialog и кнопку для выбора любого другого файла, который Вы можете создать для экспериментов. Рис. 34-4 иллюстрирует форму Messages на этапе разработки.
М D । Ipl-ty о M<4 **5t* Нп[3
Рис. 34-4 Форма Messages на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Меню и их элементы в MESSAGES.FRM
Название-
Имя
Уровень вложения
Параметр Enabled
©File
nmuFile
0
True
©New
mnuNew
1
False
©Open™
mnuOpen
1
False
©Save
mnuSave
1
False
Save ^4s_.
mnuSaveAs
1
False
mnuFUeDasbl
1
True
Е@«Й
mnuExit
1
True
©Help
mnuHelp
0
True
©Contents
mnuContents
i
True
©Searcbfor Help on.„
mnu Search
i
True
—
mnuHelpDasbl
i
True
©About...
mnuAboul
i
True
Объекты MESSAGES.FRM и значения их свойств
Свойство
Значение
Form
Name
jrmMessages
BorderStyle
3 - Fixed Dialog
Caprion
CommondButton
Display a Message File
Name
cmdMessages
Caption
Command-Dialog
ODispkiyMessage Fite
Name
cdlOne
ИсходныйкодМЕЗЗАСЕЗГВМ
Option Explicit..'■'"
. Private sub omdMessages_Click()
" объявляем новый объект тжа Message,.
Dim msgOne As New Message ' запрашиваем имя файла cdlOne.DialogTitle = "Message Files”:
cdlOne.Flags = cdlOFNHideReadOnly cdlOne. Filter = "Messages(*.mes)|*,mes" BdlOne.CancelError = True
On Error Resume Next
cdlOne.ShowOpen
' выходим, если пользователь отменил или закрыл диалоговое окНОд If Err Then Exit Sub;."3
On Error Goto 0
' отображаем-файл сообщений';..■
With msgOne
rileName = cdlOne. Filename..
.Display.'
End With1
End Sub
Private Sub Form„Load()
' центрируем эту форму
Me.Left = (Screen.Width - Me.Width) \ 2
Me.Top = (Screen.Height - Me.Height) \ 2
End Sub
Private Sub mnuAbout_Click()
' настраиваем свойства■
About. Application = "Messages"
продолжение
About.Heading = _
"Microsoft Visual Basic 6.0 Developer's Workshop"
About.Copyright = "1398 John Clark Craig and Jeff Webb"
About.Display
End Sub
Private Sub mnuExit„Click().
Unload He
End Sub
Private Sub mnuContents_CIick()
cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne-HelpComand - cdlHelpContents
cdlOne.ShowHelp
End Sub
Private Sub mnuSearch_Click()
cdlOne.HelpFile = App.Path & "\.Д.,\Help\Nvbdw.hlp"
cdlOne.HelpCommand = cdlHelpPartialKey
cdlOne.ShowHelp.
End Sub
MSG.CLS ,
Модуль класса MSG.CLS — «калька» объектов Message. Этому модулю нужен связанный с ним файл MSG.FRM. содержащий видимые элементы объекта Message. Если Вы хотите добавить объекты Message в свои приложения, Вам потребуются оба файла.
У каждого объекта Message одно свойство — FileName, которому вызывающая программа присваивает полное (вместе с путем) имя файла сообщений, и один метод — Display, вызов которого приводит к последовательному отображению сообщений, содержащихся в указанном файле.
Б методе Display довольно много кода, взаимодействующего со свойствами и методами формы/гШ/.^. Метод Display интерпретирует команды, встроенные в заданный файл сообщений, и управляет формой/гтЛАд, реализуя нужные операции.
Исходный код MSG.CLS
Option Explicit
* свойство, указывающее нужный файл сообщений
Public iileha:^ As «...ng
' метод для отображения файла сообщений
продолжение
■ Public Sub DisplayO
' Dire strH As String■
Dim str J As St ring Dim strA As String Dim strB As String ' ' fliffl strG As String Dim IntFilNumAs Integer Dim IngNdx As Long Dim IngFlashRate As Long Dim IngPauseTime As Long '■
Dim IngHeight As Long Dim IngWidth As Long Dim IngMaxTextWldth As Long ' получаем первый же доступный номер файла intFilHum = FrseFile ' перехватываем ошибку, если указано неправильное имя файла On Error Resume Next Open FlleName For Input As ttintFIINum.
If Err Then'
NsgBox "File not found: " A FileName Exit Sub
End If
On Error GoTo 0 ' ищем начало первого блока текста Do Until EOF(intFilNum) Line Input ttntFilNum. strH ' отыскиваем первые три тильды If InStr(strH, * 1 Then strJ = UCaset(strH) Exit Do."
End tf!
Loop ' цикл перебора всех текстовых блоков Do Until EOF(intFllNum) strB * ""'
' StrH * strJ 'IngWidth = 0
IngHeight = 0 ' считываем весь текущий блок текста Do Until EOF(intFilNum) Line Input lifntFUNum, etrA ' конец текущего блока - начало следующего VIf lnStr(strA, - 1 Then
strJ = UCase$(strA) Exit Do ' End tf■
продолжение
’■ ' находим самую длинную строку
IngMaxTaxtWidth = frmMsg.TextWidth(st г А 4 "XX")
'If IngMaxTextWidth > IngWidth Thon
IngWidth = IngMaxTextWidth
End If
' следим за высотой всех строк в блоке. .
IngHeight = IngHeight + 1
' аккумулируем блок текстовых строк
If IngHeight > 1 Then2
strB = strB & vbCrLf & strA
Else
strB = StrA■
End If
loop
■' проверяем, не указана ли в заголовке частота мигания
IngNdx = InStr(strH, "F")
If IngNdx Then
IngFlashRate = Val(Mid$(strH, IngNdx + 1))
Else
IngFlashRate = 0
End If
■' проверяем, не указана ли в заголовка длительность паузы
IngNdx = InStr(strH, "Р")
If IngNdx Then
IngPauseTime = Val(Mid$(strH, IngNdx * 1»
Else
IngPauseTIme = 0
End If
' настраиваем текстовое поле формы frmMsg
With frmMsg.txtMsg
.Text = strB
■.Left - О
•Top = &
.Width = IngWidth
■u-tght = (LngHelght+ 1) * frmMsg.TextHelght("X“) '
End With
настраиваем сам/ форм/ frmMsg■
With frmMsg■
.Width = . txtMsg.Width + (.Width- .Scalewidth)
.Height = .txtMsg.Height + (.Height - .ScaleHeight)
.Left = (Screen.Width - .Width) \ 2
.Top= (Screen.Heiaht - .Height) \ 2
1 устанавливаем мигание и задержку, если они заданы
If InnPaiicaTi mo > / ТЬеП .PaUS6= 1г, л PaosoTI ЩО
If IngFlashRate > 0 Then .Flash = IngFlashRate
End With .
продолжение
' показываем сообщение и ждем его закрытия
' frmMsg.Show vbModal
Loop
End Sub.
MSG.FRM
Форм;frmMsg — неотъемлемая часть модуля класса MSG.CLS. Вместе они образуют основу объектов Message. MSG.FRM взаимодействует только с модулем MSG.CLS. Основная форма Messages не устанавливаем напрямую ни одно свойство формы/гогЛЛ.! .не вызывает ни один из ее методов и вообще никак не взаимодействует с ней.
В MSG.FRM содержатся 4 элемента управления: 2 таймера, текстовое поле для отображения сообщений и кнопка, назначение которой я опишу позже. Рнс. 34-5 иллюстрирует форму MSG.FRM на этапе разработки.
* Manage ' J
1751 [Oj i. ^^^ I
Рис. 34-5 Фордш MSGfRMna этапе разработки
Создавая эту форму, используйте таблицу и исходный код, приведенные ниже.
Объекты MSG.FRM и значения их свойств
Свойство
Значение
Form
Name Caption BorderStyle TextBox
JrmMsg
Message
3 - FixedDialog
Name ForcColor. MultiLine Locked Timer
txtMsg &H00FF00O0&
True True
Name Timer
tmrTermincite •
Name
tmrFlasb
Объекты MSG.FRM и значения их свойств (продолжение)
СвойствоЗначение
CortimandButton
NamecmdDummy
DefaultTrue
CaptionDummy •
Исходный код MSG.FRM
Option Explicit
Private Declare Function FlashWindow _.
Lib "user32” c _.
ByVai hwnd AsLong, _"
ByVai blnvert As Long _.
} As Long:
Private Sub Form_Paint()
' убираем фокус ввода с текстового поля'Т
' cmdDummy.Left = Screen.Width * 2.
cmdDummy.SetFocus'
End Sub
Private Sub tmrTerminate_Timer();
Unload He.
End Sub
Private Sub -ьтгпд^ь Timer() 1 заставляем форму мигнуть'
Flashwindow nwna, CLng(True)-
' End Sub
Property Let Flash(PerSecond AsInteger)f
" указываем частоту мигания и активизируем процесс■ ...
tmrFlash. Interval = 1000 / PerSecond tmrFlash.Enabled - True
End Property
Property Let Pause(Seconds As Double)
* указываем задержку и активизируем автовыгруэку формы tmrTerminate.Interval = Юии * Seconds tmrTerminate.Enabled * True ,
End Property
API-функция FlashWindOw вызывается из процедуры tmrFlash_Ti- mer и заставляет форму мигнуть один раз. Свойство Interval таймера tmrFlash определяет частоту мигания.
Единственное назначение кнопки Dummy — принимать фокус на себя и таким образом убирать мигающий курсор ввода с текстового поля, когда оно отображает сообщение. При каждой отрисов- ке формы свойству Left этой кнопки присваивается значение в 2 раза большее ширины экрана, благодаря чему кнопка удаляется из поля зрения. А вместе с кнопкой уходит и фокус ввода, так как ее свойство Default установлено как True.
Свойства Flash и Pause вызывающим приложением напрямую не задаются; их устанавливает код в модуле MSG.CLS, управляющий поведением формы.
Приложение Secret
Сейчас много говорят о защите и безопасности, особенно по поводу пересылки через Интернет финансовой и другой конфиденциальной информации. Приложение Secret не годится там. где нужна высокая степень защиты, — оно обеспечивает лишь умеренную безопасность почтовых сообщений и файлов, которые Вы хотели бы уберечь от посторонних глаз.
ПРИМЕЧАНИЕ Уровень защиты, реализуемой этим приложением, не дает гарантии от целенаправленного взлома; зашифрованные им сообщения могут быть вскрыты. Но от 99% любопытных сообщения и файлы будут защищены.
Чтобы упростить приложение и чтобы его можно было экспортировать, я использовал технологию шифрования по закрытому, а не открытому ключу. Если Вы собираетесь шифровать с его помощью свою почту, то должны заранее договориться о пароле с другой стороной. В качестве закрытого ключа можно взять любую фразу подходящей длины, но программа хэширует строку пароля и преобразует ее в 24-битный уникальный ключ, что укладывается в пределы, разрешенные властями. В качестве источника псевдослучайных байтов задействован генератор случайных чисел Visual Basic; в мире криптографии его применяют только при невысоких требованиях к безопасности. Если Вас удовлетворяет умеренный уровень защиты, приложение Secret — то, что нужно. А если Вас интересует очень надежный шифр, приобретите один из коммерческих продуктов,
ПРИМЕЧАНИЕ Хэширование строки (string haching) — вычисление однонаправленное, чем-то напоминающее подсчет контрольной суммы: его легко повторить, но трудно обратить. Один и тот же пароль всегда дает один и тот же резуль- т;п. однако по результату очень сложно определить исходный пароль.
Как работает приложение Secret?
А вот как. Вы выбираете любой файл, и программа шифрует (или дешифрует) его, В начало зашифрованных файлов вставляется небольшой заголовок, чтобы программа могла определить, зашифрован ли файл. Прежде чем щелкнуть кнопку Encrypt или Decrypt, введите пароль в соответствующие поля. При шифровании пароль набирается дважды, а при дешифровании — один раз. Это обычная практика, когда пользователя заставляют вручную вводить пароль в двух полях, тем самым уменьшая риск опечаток. Любое несоответствие в паролях прекращает операцию по шифрованию файла. Зашифрованный файл записывается в ASCII-формате, пригодном для отображения, печати и пересылке по электронной почте, даже если исходный файл содержал неподходящий для этого двоичный код.
Чтобы сообщение выглядело при каждом шифровании (даже с одним и тем же паролем) по-разному, я применяю один интересный прием: добавляю в заголовок 8 псевдослучайных символов и хэширую комбинацию этих символов и пароля. Полученный таким образом заголовок (размером в ! б символов) используется для шифрования файла. Заголовок позволяет также быстро проверить правильность пароля перед расшифровкой файла. Пароль и символы хэшируются, а результат сравнивается со строкой заголовка. При неправильном пароле получается несоответствие.
Рис. 34-6 иллюстрирует приложение Secret сразу после того, как выбран текстовый файл TEST.TXT и дважды введен пароль HAARP. Файл еще не зашифрован, и поэтому кнопка Encrypt активна, а кнопка I д । р щ отключена. На рис. 34-7 показан внешний вид приложения после щелчка кнопки Encrypt — файл зашифрован. Заметьте: теперь кнопка Decrypt активна, а кнопка Encrypt и второе поле для ввода пароля отключены.
^»*д j || —^^^J
Г: ' 7-^8^Г: ' 7-^8^Рис. 34-6 В приложении Secret выбран для шифрования файл TEST.TXTРис. 34-6 В приложении Secret выбран для шифрования файл TEST.TXT
Г: ' 7-^8^
Г: ' 7-^8^
Рис. 34-6 В приложении Secret выбран для шифрования файл TEST.TXT
Рис. 34-6 В приложении Secret выбран для шифрования файл TEST.TXT
£& 9i*':<;/•'
Рис. 34-7 Файл ТЕ<ЕТХ!зашифрован
* s«i£rt_j^wimim^• • ■_ _^Sji* s«i£rt_j^wimim^• • ■_ _^SjiВзгляните на содержимое TEST.TXT до шифрования (рис. 34-8) и после (рис. 34-9). Для просмотра содержимого файла щелкните кнопку View. Рис. 34-10 иллюстрирует содержимое того же файла после его дешифрования и повторного шифрования с использованием одинакового пароля. Как видите, результаты шифрования совершенно различны, хотя обе версии получены из одного исходного файла.
* s«i£rt_j^wimim^• • ■_ _^Sji
* s«i£rt_j^wimim^• • ■_ _^Sji
<Xs lequesied. here’s the nut-shell description of the plat la mp pew ki-4 thriller your • agency has shown so much interest in.
the" :"• was i?ed to broadcast ELFthroughthe earth la
• herapand ^ocft^iauiv^eidbytheSpbM
In one letof the rjith's al g« end utheicesc-ucas wets
।mapped
|The jury’s hero made cure he hadALL h» ducks in a row before venturing forb
■with the ЙогтвЬоп provided by Iheright vista?, Ihcsewhiti cakdihemsefve? the lEineigemt.
Рис. 34-8 Содержимое исходного файла TEST.TXT, отображаемое при выборе кнопки View
Сеж-1 I. iWtNMWk&aeh^^ tel
|ber:4'!)jl.4j2'ri[fk'l.ETi-,rMG
i* Ж~- TguJQUT^iMAU^e№ieaW.LA№>x^zg«NI i-VuM
пН'ин^фЬця^.лЮЗтаьНтад^ ^JwJLTRiVkVL ICMV/UV ВЦкВйисО iCdptGh@EbQKh<DM twk^EG? aAficlli^-Г cl irTti «MLZuFkC^SbfiQsRr OePlW -FaduqqyUE W L'F@t»rAWRwin@16,iCF[gCD Wto'.O,^^ i u w*W T Ч^Л H*' t JdWbiwa^^rftdJEyQSlSHp^a^^d^ V^jutfUFUP Yaf
Vtilcnr< f-D\sbH "PvnG inL.Vzhp, wZ> nmVe>| Kribp@>HgGRCB -r2-Zi£ty.''"<ri^D4-LMijg UicEF-tidD |nT; HqCtf^t^HJS’wMuS^ ? V^^tmQpA ^CCw к Ci2D*ine FhOP»>F<^Va Bit DU "cDOMtflYbir< N 7 jJQHG^nN^i EjcP JHbCWH*W4._*lrdVh* i£ CpyDG ol5 rLvSdMe^V4ai4BQ^atfsiiCKea^CLFF),lo46 o@uoG “iw^QflieDWftFfl Ti V* aZ^A. > -»Ywiw*k —Wpfl NPhtM \g | NGD*^M№e\FDj-> чО>П «ne6RcKH@>M-^ r u&JfclSGrv ^Д rmBllvJIftnWrt' ^kjteolj^qwcJ«|'h I'HfrJ^DANVIBHye* bemh< g^bncWlikiWwyiS Hq|v*reptfflaMt]$illQK
Рис. 34-9 Содержимое зашифрованного файла TEST.TXT, отображаемое при выборе кнопки View
. г:., П> с bWD 0№ЧВм^1орХТму>|1 ГЛ
SecteiJr^cSjjJOkV'4:
.iwON? Гj*Y»*BML|VU teeMJ^U.^LIittyHY^^Zi ?
Ptlq. WjWhdM oku'@L lEi^vTjUlDmHdFT QDa4i^P«i "MIN^ W: VlCD uO LDLdAtfW»K.*Wal RlRHrE* jwMl^kK* kODGfJ V J0 We <QSjr l2*l T C 'wU : JA'EX^iv-^^’tVylEis^-fG'EipwiAHMM^rK^YMj^liMitil^i^1
• pf»trt^Qrd^frCHji№iiQjl*(<T^wV^rT>iMJi3J^-'TrJK.K^'
■ .GS HJ > 7iH E T r^n QwG kJ №<3|ШУ.р1 ^Hd nil h^L^M^iA^ck-JirnLciMi ! lYliO ■yj^ELAiP5:^|,wpeiP-ai*^dC''TWJQl>7^E>^S<a>£(raGlHTL^ d
Ь-.:РЪ? 'Ik^ShW^U^H l^CEeeekaR^L »tci<i2T?L5l БцХ^&гё^р■
Pl<-> .<l nhS^n1 the = An^EIFHbetAbdO FuL^tMkU >8Q ifl cfckiZZSwJ w^b^1.
■ .1 ’ -f iJi[c-l.t::-T.M?^jfiHiR\4F-Mak<H^hc^peof|DLl>Fe<^GlEl^?FmH v •'£ ij", л i УI, F i-.Г ’ -v! *,-^Ц2\*/т\т;5 rt<i»^tWE3№?Cd>R T iV/ti^yrrfltmDSECG qh^^ ь i-C,..- ,=l;Cj feVLM'jWPSHjLe'Wa»*,
Рис. 34-10 Файл ТЕЗТ.ТХТповторно зашифрованный с использование того же пароля
Проект Secret состоит из четырех файлов (рис. 34-И). Модуль класса CIPHER.CLS был представлен в главе 18 ««Защита». Модуль формы Secret включает функцию Hash, которая преобразует любую строку в воспроизводимую, но непредсказуемую последовательность из восьми символов. Эта величина используется потом для проверки пароля перед дешифрованием. И. наконец, форма View представляет собой простейшее средство просмотра любого файла в режиме «только для чтения*.
'Ь''Ь'• a fiboui <AbouC.ftri)• a fiboui <AbouC.ftri)‘. . О fnnSeuet (Secret-ftm?• bfrmVI^tvtw.Frm)Й -ffIf Class Modules5 S-dphter^lcherds)‘. . О fnnSeuet (Secret-ftm?• bfrmVI^tvtw.Frm)Й -ffIf Class Modules5 S-dphter^lcherds)
'Ь'
'Ь'
• a fiboui <AbouC.ftri)
• a fiboui <AbouC.ftri)
‘. . О fnnSeuet (Secret-ftm?
• bfrmVI^tvtw.Frm)
Й -ffIf Class Modules
5 S-dphter^lcherds)
‘. . О fnnSeuet (Secret-ftm?
• bfrmVI^tvtw.Frm)
Й -ffIf Class Modules
5 S-dphter^lcherds)
Рис. 34-11 Список файлов в проекте Secret
SECRET.FRM
SECRET.FRM —основная, стартовая форма приложения Secret. Она содержит текстовое поле для ввода имени нужного файла, 2 текстовых поля для набора пароля и 4 кнопки для выполнения всех операций с файлом (рис. 34-12). При выборе файла используется элемент управления Com mon Dia log.
Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Меню и их элементы в SECRET.FRM
Название
Имя
Уровень вложения
Параметр Enabled
ftWfe
тпнРИе
0
True
<&New
mnuNew
1
False
Меню и их элементы в SECRETfRM (продолжение)
Название
Имя
Уровень вложения
Параметр Enabled
©Open...
mnuOpen
1
False
mnuSave
1
False
Save ©As_
mnuSaveAs
1
False
—
mnuFileDashl
1
True
E©xit
mnuExtt
1
True
©Help
mnuHelp
0
True
©Contents
mnuContents
1
True
©Seavcbfor Help on...
mnuSearcb
1
True
—-
mnuHelpDash I
1
True
©About...
mnuAbout
1
True
Рис. 34-12 Форма Secret на этапе разработки
Объекты SECRET.FRM Н значения их свойств
Тип и номер'
Свойство
Значение
Form
Name
frmSecret
Caption
Secret
Label
Icon
SecurO3.ico
1
Name
Wile
TextBox
Caption
File:
2
Name
txtFite
Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 34-12).'
Объекты SECRET.FRM и значения их свойств (продолжение)
Тип и номер1
Свойство
Значение
CommonDialog
3
CommandButton
Name .
cdlOne
4
Name
cmdBrowse
CommandButton
Caption
©Browse.,.
5
Name
стсЛВем-
CommandButton
Caption
©View
6
Name
cmdEncrypt
CommandButton
Caption
©Encrypt
1
Name
cmdDecrypt
Label
Caption
©Decrypt
8
Name
IblPasswordl
Label
Caption
Enter password:
9
Name
lblPassword2
TextBox
Caption
Enter password again:
10
Name
txtPasswordl
PasswordChar
TextBox
Text
пусто
11
Name
txtPassword2
PasswordChar
Text
пусто
'Исходный код SECRET.FRM
Option Explicit
Private Sob CBdBrowse_Click()
* запрашиваем имя файла■
cdlOne.DialogTitle = "Secret"
cdlOne.Flags = cdlOFNHldeRBadOnly cdlOne.Filter = "All files (*.•)!*.»"
продолжение
cdlOne.CancelError = True ■ On Error Resume Next
cdlOne.ShowOpen ' получаем имя файла If Err * 0 Then
txtFile.Text = cdlOne.FileName
End If■
On Error GoTo 0 ' End Sub
Private Sub cmdEncrypt_Click() ' оба пароля должны совпадать
If txtPasswordf .Text <> txtPassword2,Text Then ■ MsgBox "The two passwords are not the same!", гvbExclamation, "Secret"
ExitSub
End If
* шифруем файл
NousePointer = vbHourglass cedEncrypt,Enabled = False cmdDecrypt.Enabled - False cmdView,Enabled = Falsa cmdBrowse.Enabled * False Refresh ■ Encrypt
txtFile_Change•
NousePointer = vbDefault
End Sub
Private sub CBdDecrypt„Cllck()
NousePointer = vbHourglass cmdEncrypt. Enabled = False " cmdDecrypt.Enabled - False cmdView.Enabled = False cmdBrowse,Enabled = False Refresh Decrypt
' txtFile.Change
NousePointer = vbDefault
End Sub
Private Sub cmdView_Click()
Dim strA As String
Dim IngZndx As hong■
. NousePointer = vbHourglass " получаем содержимое файла
продолжение
Open txtFile.Text For Binary As #1■.
strA = Space$(L0F(1))
Cat #1, , strA
Close 81:
So
IngZndx = In8tr(strA, Chr$(0)) If IngZndx = 0 Or IngZndx > 5000 Then Exit Do Mid$(strA, IngZndx, 1) = Chr$(1)
■ Loop
" показываем содержимое файла
HousePolnter * vbDefault f review, rtfview. Text = strA frmView.Caption = "Secret - " & txtFile.Text frniView. Show v&Modal■
End Sub
Private Sub Form„Load()■
■1 центрируем эту форму
Me.Left = (Screen.Width - Me.Width) \ 2
Me.Top = (Screen.Height - Me.Height) \ 2"
1 отключаем большинство кнопок cmdEncrypt.Enabled - False"
CBdDecrypt.Enabled = False cmdView.Enabled = False"'
.1 инициализируем поле с именем файла..
txtFile.Text =
End Sub
Private Sub mnuAbout_CliokO 1 настраиваем свойства
About. Application = "Secret'1
About. Heading = _
"Microsoft Visual Basic 6.0 developer's Workshop" About.Copyright - "1998 John Clark Craig and Jeff Webb" About.Display
End Sub'
-Private Sub mnuExit_Click() Unload Me
End Sub.
Private Sub nnuContents_Click()
cdlOne.HelpFile = App.Path 1 ”\..\..\Help\Mvbdw.hlp" cdlOne.Helpcommand = cdlHelpContents cdlOne.ShowHelp
End Sub
продал женив
Private Sub mnuSearch„Click()
cdlOne.HelpFile = Арр.Path & "\.Д. .XHelpXMvbdvi.hlp" cdlOne.HelpCommand = cdlHelpPartialKey cdlOne.ShowHelp.
End Sub
Private Sub txtFile_Change()
Dim IngFileLen As Long
Dim strHead As String
' проверяем, существует ли файл■
■Gn Error Resume Next
"IngFileLen - Len(Dir(txtFile.Text))■
'' отключаем кнопки, если имя файла наверно
If Err О 0 Or IngFileLen = 0 Or Len(txtFile.Text) = 0 Then "cmdEncrypt.Enabled = False
cmdDecrypt. Enabled = False cmdView.Enabled - False IblPasswo rd1. Enabled = False txtPasswordt. Enabled = False lblPasswcrd2. Enabled = False txtPas$word2.Enabled - False txtPassword2.Text - "" Exit Sub
End If
" получаем первые 8 байтов выбранного файла
Open txtFile.Text For Binary Аз fr1
strHead = Space(8)
Get #1, , strHead
Close #1;
" проверяем, зашифрован ли файл
If strHead = "[Secret]" Then cmdEncrypt. Enabled = False.
cmdDecrypt.Enabled = True 'lblPassword2. Enabled = False.
txtPassword2.Enabled = False txtPassword2.Text = . Else ...■cmdEncrypt. Enabled =True
cmdDecrypt.Enabled =False
■lbLPassword2,Enabled= True
txtPassword2.Enabled= nue
End If
■ lbIPassword1.Enabled = True txtPasswordt,Enabled True GmdBrowse.Enabled = True cmdView.Enabled = True.
продолжение
End Sub'
' SubEncrypt()
. Dim strHead As String.
Dim strT As String
Sin. str A As String
Dim cphX As New Cipher
Dim IngN As Long
Open txtFile.Text For Binary As 81
' загружаем весь файл в strA■
strA = Space$(LOF(D)
■ Set #1, , strA
Close#1 ' формируем строку заголовка strT = Hash(Date &Str(Timer)) strHead = "[Secret]"" & strT & Hash(strT & txtPasswordl.Text) ' шифруем файл.
cphX. KeyStrlng - strHead cphX.Text = strA.
cphX.DoXor cphX.Stretch strA = cphX. Text
' Пишем заголовок
Open txtFile.Text For Output As #1 Print strHead■
'' записываем зашифрованные данные
IngN = 1'
Do.
Print #1, Hid(strA, IngN, 70)
IngN = IngN +70
Loop Until IngN > Len(strA)
Close |f1
End Sub
Sub Decrypt!)
Dim strHead As String
Dim strA As String
Dim strT As String
Dim cphX As New Cipher Dim IngN As long.'
' получаем заголовок (первые IS байтов зашифрованного файла) " Open txtFile.Text For Input As #1 Lire Input #1, strHead Close tl'
' проверяем правильность пароли strT = Min(strHead, 9, 8)
продолжение
. If InStr(strHead, Hash(strT A txtPasswordf.Tgxt)) <> 17 Then
- MsgBox "Sorry, this is not the correct password!", _
■vbExclamation, "Secret"
Exit Sub■
End If
' получаем содержимое файла
Open txtFlle.Text For Input As #1 ' читаем за заголовком
Line Input #1, strHead
' чи1аам и формируем строку содержимого файла
Do Until EOF(1>"'
Line Input #1 strT
strA = strA & strT '
Loop
Close #1
' дешифруем содержимое файла
cphX. KeyString - strHead
cphX.Text = strA
cphX. Shrink
cphX.DoXor
strA = cphX.Text
.' заменяем файл его расшифрованной версией
. Kill txtFlle.Text
Open txtFlle.Text For Binary As #1
Put #1, , strA
Close #1
End Sub
Function Hash(strAAs String) As Stringt’ -.
Dim cphHash As New Cipher
cphHash. KeyString = strA & "123456".’?
cphHash.Text = strA & "123456"Ф
cphHash.DoXor cphHash.St retch“
cphHash.KeyStrina - cchHash.Text
cphHash.Text = "123458"' ”
cphHash.DoXor:S
cphHash. Stretch Hash я cphHash.Text
End Functionif
VIEW.FRM
Эта сравнительно простая форма позволяет просматривать файлы в режиме «только для чтения». Рис. .’>! 1 ' иллюстрирует форму View на этапе разработки. Единственный объект, содержащийся на этой
форме, — элемент управления RichTextBox, в котором и отображается содержимое выбранного файла.■
—„j
Рис. 34-13 Форма View на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, при- веденныениже.
Объекты VIEW.FRM и значения их свойств
Свойство
Значение
Form-
Name
JrmView■
Caption
. Secret - View File
MaxButton
False
MinBution
False■ ‘ '
RichTextBox
Name
rtfView■ ■-
ScrollBars
3 - rtJBotb
Locked
True
Исходный код VIEW.FRM
Option Explicit’
Dim BblnfieenHereDOneThis As Boolean
Private Sub Forffl_Load()
mblnBeenHereDoneThis = False
End Sub
Private Sub Form_Resize()
' центрируем эту форму, но только один раз
If fflblnBeenHereOoneThis = False Then
He. Left = (Screen.Width - Me.Width) \ 2
He.Top = (Screen.Height - Ne.Height) \ 2
продолжение"
ublnBeenHereDoneThis = True
End If
' подгоняем размеры RlchTextBox под форму
rtfView.Mov О, 0, Me.ScaleWidth, Me.ScaleHeight.
End Sub
CIPHER.CLS
Этот модуль класса — «ядро» приложения Secret, обеспечивающее шифрование. Объект Cipher я уже рассматривал в главе 18 -Защита», но привожу здесь для Вашего удобства,
Чтобы создать класс Cipher, добавьте в свой проект новый модуль класса, присвойте ему имя Cipher и введите следующий код.
Исходный код CIPHER.CLS.
■ CIPHER.CLS
Option Explicit'
Private mstrKeyAs String
Private mstrText As String
' Keystring
' строка (кшч), тользуемая в шифровании и дешифровании
Publ. Property Let KeyString(strKey As String, mstrKey strKey Ini 1.1 all^0'
End Property
' "".Text
' принимает шифруемый и дешифруемый текст
Public Property Let TextfstrText As String) rastrText=strText'
End Property
' возвращает зашифрованный или дешифрованный текст
Public Property Get TextO As String
Text = -"rText
End Property
' "".DoXor
' шифрование/дешифрованив на основе побайтовой операции Xor ' Public Sub DoXor()
Dim IngC As Long;
Dim intB As Long■
Dim IngN As Long
_4. For IngN = 1 To Len(mstrText).
продолжение
IngC = AsctMiddstrText, IngN, 1)) intB = Int(Rnd - 256)
Mld(mstrText, IngN, 1) = ChrdngC Xor intB)
Next IngN
End Sub
' Stretch
щеобразует любую строку в пошюстью отображаемую Public Sub StretchO
Шуи As Long
Dim IngN As Long
Dim IngJ As Long
Dim IngK As Long
Dim IngA As Long
Dim strB As String
IngA = Len(mstrText)
strB = SpacedngA + dngA + 2) \ 3)
For IngN = t To IngA
IngC = Asc(Mid(mstrText, IngN, 1))
IngJ - IngJ * 1
Hid(strB, IngJ, 1} = Chr((lngCAnd 63) + 59)
Select Case IngN Hod 3
IngK = IngK Or (dngC\ 64) * 16) Case 2
IngK = IngK Or (dngC \ 64) * 4) Case 0
IngK = IngK Or dngC \ 64)
IngJ = IngJ + 1
Mid(strB, IngJ, 1) = ChrdngK * 59) IngK = 0
End Select Next . i.,-.
Il IngA Mod 3 Then IngJ = IngJ + 1.
Mid(strB, IngJ, 1) = ChrdngK + 59)
End If
mstrText = strB'
End Sub
' .Shrink
' выполняет действия, обратные тому, что делает метод Stretch Public Sub Shrink()
Dim IngC As Long
Dim IngD As Long
Dim IngE As Long
продолжение
Dim IngA As Long
Dim IngB As Long■...
Din IngN As Long"
. Dim IngJ As long
Dim IngK As Long■
01л strB As String'
IngA - Len(mstrText)■
IngB = IngA - 1 - (IngA - 1) \ 4 strB = Space(lngB) For IngN = 1 To IngB
IngJ = IngJ + 1
IngC = Asc(Mid(mstrText, IngJ, 1)) - 59
:Select Case IngN Mod 3
Case T
IngK * IngK + 4
If IngK > IngA Then IngK я IngA
■IngE = Asc(Mld(mstrText. InaK. 1» - 59
. IngD = ((lngE\ 16) And 3) * 64.■
IngD = ((IngE \ 4) And 3) * 64.
1IngD = (IngE And 3) * 64
IngJ = IngJ * 1
End Select
Nid(strB, IngN, 1) = Chr(lngC Or IngD);
Next ingN
mstrText = strB:
End Sub
' инициализация генератора случайных чисел по клочу:
Private Sub Initialize()
Dim IngN As Long
Randomize Rnd(-t)■
For IngN = 1 To Len(mstrKey)
Randomize Rnd(-Rnd * Asc(Nid(instrK0y, IngN, 1)))
Next IngN
End Sub'...
Приложение BitPack
Я разработал приложение BitPack, экспериментируя c DLL, написанными на С. Мне хотелось оценить, какой прирост производительности можно выжать из Visual Basic. Так вот, даже при наличии в Visual Basic компилятора машинного кода внешняя DLL (написанная на С) дает в ряде случаев существенный прирост в скорости выполнения критических блоков программ, и BitPack наглядно демонстрирует его.
BITPACK.DLL предназначена для манипуляций над отдельными битами в байтовом массиве — одной из задач, к которой Visual Basic не слишком хорошо приспособлен. Эта DLL предоставляет 3 функции: BicGet, возвращающую текущее состояние бита; BitSet, устанавливающую бит в заданной позиции: и BitClr, сбрасывающую указанный бит. Вы передаете этим функциям байтовый массив и номер бита — об остальном заботится код, написанный на С. Так, чтобы получить бит номер 542 из байтового массива, функция BitGet находит б-й бит в 67-м байте массива, получает его значение и возвращает 1, если бит установлен, или ", если бит сброшен. В 32-раз- рядном Visual Basic байтовые массивы могут быть очень большими, так что эти функции позволяют реализовать практически неограниченное по размеру хранилище битов.
Генерация таблицы простых чисел (решето Эратосфена)
Область применения этих функций — сбор данных и управление процессами. Состояние тысяч выключателей, перемычек и т. п. можно регистрировать в байтовом массиве, используя всего три щунк- ции из приложения BitPack (BitGet, BitSet и BitClr). Но для книги я подготовил небольшую программу, генерирующую таблицу простых чисел на основе хорошо известного решета Эратосфена. Каждый бит в байтовом массиве представляет нечетное целое число. Задействовав несколько вложенных циклов, можно установить биты, не соответствующие простым, числам. Благодаря тому что DLL работает очень быстро, генерация таблицы простых чисел в диапазоне от 1 до 1 000 000 занимает буквально несколько секунд. А когда Вы узнаете, сколько раз вызываются BitGet и BitSet при формировании этой таблицы, тут-то и поймете, насколько функции DLL, написанной на С, ускоряют некоторые участки кода!
Создание файлов проекта BitPack.DLL
Прежде чем писать на Visual Basic программу, генерирующую таблицу простых чисел, надо создать DLL, выполняющую основные операции.
ПРИМЕЧАНИЕ Последние версии компиляторов Microsoft Visual C++ упрощают создание DLL, как никогда раньше, и я решил дать пример несложного DLL-кода, написанного на С; он позволит понять главные этапы процесса разработки DLL. Если Вы используете версию Microsoft Visual C++, отличную от 6.0, Вам придется модифицировать листинги. Подробные и глубокие объяснения всех аспектов создания DLL Вы найдете в документации на свой компилятор.
Следующие три листинга — это все, что нужно для нашего проекта в Visual C++. Откройте новый проект (выберите тип Win32
Dynamic-Link Library) к 32-разрядной версии Visual C++, введите 5777К<;Лкак имя проекта и щелкните кнопку ОК. Когда появится запрос о том, какой тип DLL Вам нужен, выберите переключатель А DLLThat Exports Some Symbols и щелкните кнопку Finish. Visual C++ создаст новый проект DLL с заготовками исходных файлов. Теперь создайте файл BITPACKDEF как часть этого проекта и введите в него такие строки:■
BitPack. def-
LIBRARY BitPack..' - -
EXPORTS
BitGet.'.
Bitset.
BitClr'.
DEF-файл сообщает ичешним программам имена экспортируемых функций, т, е. перечисляет функции, которые можно вызывать из приложений Visual Basic.
Два файла из числа сгенерированных Visual C++ лтядо модифицировать. Начните с BITPACK.CPP. Это короткий файл, содержащий эффективные одностроковые функции, которые выполняют все необходимые операции над битами, включая адресацию и маскирование. Отредактируйте его следующим образом:-
И BitPack. ерр : определяет входную точку DLL'
' //
#include "stdafx.h".'
#include "BitPack. h"
BOOL APIENTRY DllMainl HANDLE hModule,
DWORD ul_reason_for_call, DEVOID IpReserved )"
switch (ul_reason_for call) ■: ■
{Г
case DLL_PROCESS_ATTACH:.
case DLL-THREAD-ATTACH:'
case DLL_THREAD_DETACH:
■ case DLL.PHOCESSDETACH:"-
break;■
return TRUE;
BITPACK_API int _Stdcall BitGet(LPBYTE bytes, int bitpos)■
return. bytes[bitpos 3] 4 (1 (bitpos % 8)) ? 1; 0);
BITPACK_API int „stdcall BitSet(LPBYTE bytes, int bitpos) ''
return( bytes[bitpos » 3] |= (1 « (bitpos % 8)));
BITPACK API int „stdcall BitClr(LPBYTE bytes, int bitpos) {
return( bytes[bitpos » 3] 4= *(1 « (bitpos X 8)));
}
Второй файл, который надо отредактировать, — ШТРАСК.П. Он должен содержать объявления функций BitPack. Модифицируйте его, как показано ниже. Обратите внимание, что большая часть этого файла, сгенерированного Visual C++, не требует никаких изменений.
// Следующий блок ifdef соответствует стандартному способу создания // макросов, которые упрощают экспортирование функций из DLL.
// Все файлы, относящиеся к этой DLL, компилируются с символом
// BITPACK EXPOR1S. указываемым в командной строке. Этого символа // не должно быть в проектах, использующих данную DLL. Тогда любой // другой проект, в состав которого входит и этот файл, "увидит", // что функции В1ТРАСК_АР1 импортируются из DLL, a DLL - что символы, // определенные с помощью данного макроса, экспортируются.
ffifdef BITPACK_EXPORTS
#deflne Ь11рАСк_аг1 __aeclspec(dllexport) ttelse
#define BITPACK_API declspec(dliimport)
ttendif
BITPACK_API int stdcall BitGet(LPBYTE bytes, int bitpos);
BITPACK.API int .stdcall BitSet(LPBYTE bytes, int bitpos);
BITPACK_API int stdcall BitClr(LPBYTE bytes, int bitpos);
В среде Visual C++ щелкните кнопку Build (собрать), чтобы откомпилировать и скомпоновать эти файлы проекта и создать крошечный 1 >ЕЕ-модульВ1ТРАСК.ВЬЬ. Переместите или скопируйте его в Windows-каталог SYSTEM — тогда операторы Declare в программе на Visual Basic смогут автоматически находить этот DLL-файл. После этого функции из BitPack можно объявлять и вызывать в любой программе, написанной на Visual Basic.
BITPACK.FRM
Эта форма запрашивает наибольшее простое число, нужное пользователю Затем вызывает функции из BitPack.DLL. чтобы сгенерировать таблицу простых чисел, представленных битами в байтовом массиве, и создает выходной файл с результатами. Я добавил на
форму индикатор прогресса, так что Вы можете наблюдать за ходом процесса вычисления простых чисел. На моем компьютере таблица простых чисел генерируется быстрее, чем создается выходной файл! Рис. 34-14 иллюстрирует форму BitPack в процессе поиска всех простых чисел, меньших 1 000 000,
К UilPocL f^M-JW
Г*» Ц*‘
ii t -e jif у ^ я у o/uuai* Г4Ы?- emmiii
Рис, 34-14 Форма BitPack в действии
Найденные простые числа записываются в файл C:\WINDOWS \DESKTOP\FRIMES.TXT, но Вы можете изменить и путь, и имя этого файла — они указаны в константе FileNameK самом начале исходного кода BITPACK.FRM. Если Вы решите сформировать большую таблицу простых чисел, выходной файл окажется весьма велик. Для грубой оценки размера выходного файла поделите наибольшее простое число на 2. Так, таблица простых чисел вплоть до 200 000 .занимает на диске чуть больше и" > ООО байтов.
На рис. 34-15 показаны первые несколько строк файла PRI- MES.TXT после того, как приложение BitPack сгенерировало простые числа, меньшие 1 000 000.
^^iH] 1 №1 I »J_
•■ □£ СП Е.ЛГ'Ии
2 3 5 7 11 13 17 19 23 29 Э1 37 41 43 47 S3 59 61 67 71 73 79 03 89 97 101107 109 113 127 131 137 139 149 151 157 163 167 173 179
131 1911931911992112232272392332392412S1257263£69271
217 2812ЭЭ293307311313317331337347349353359367373379
363 389397401409419421431133439443449457461463467479
467 49149950380952152Э541547557563S6951157758759359Э
601 SC76136176196316416436476536596616736776ВЗ6917С1
709 7197277337397437S1757761769773797797809811321823
827 829339853857859063377861803887907911919929937941
947 953 967 971 977 983 991 997 1009 1013 1019 1D21 1031 103 3 1039 1049 1051 1061 1063 1069 1087 10Э1 1093 1D97 1103 1109 1117 1123 1129 1151 1153 1163 1171 1131 1187 1193 1201 1213 1217 1223 1229 1231 1237 1249 1259 1277 1279 1233 12В9 1291 1297 1301 1303 1307 1319 1321 1327
Рис. 34-15 Файл PRIMES.TXTco списком простых чисел
Форма BitPack демонстрирует элемент управления ProgressBar. Он отражает ход двух процессов; формирования таблицы простых чисел и создания выходного файла PRIMESTXT Я переключаю значения свойств Visible элементов ProgressBarn CommandButton, и Вы всегда видите только один из этих элементов.
Рис. 54-1 о иллюстрирует форму BitPack на этапе разработки.
Рис. 34-16 Форма BitPack на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, при- веденныениже.
Меню и их элементы в BITPACK.FRM
Название
Имя
Уровень вложения
Параметр Enabled
©File
mnuFile
0
True
©New
mnuNew
1
False
©Open...
mnuOpen
1
False
©Sure
mnuSave
1
False
Save ©As...
mnuSaveAs
1
False
mnuFileDash I
1
True
E©xit
mnuExit
1
True
©Help
mnuHelp
0
True
©File
mnuFile
0
True
©Contents
mnuContents
1
True
©Searchfor Help on...
mnuSearch
1
True
mnuHelpDash I
1
True
©About-
mnuAbout
1
True
Объекты BITPACK.FRM и значения их свойств
Тип и номер1
Свойство
Значение
Form
Name
frmBitPack"
Borderstyle
3 - Fixed Dialog
Caption
Bitpack - Prime Numbers
Label
1
Name
IblPrompt
Caption
Enterprime number ceilings
TextBox
Name
txtMaxPrime
CommcmDialog
3
Name
cdlOne,. ...
Label
4
CommandButton
Name
IblStatUS1
5
Name ’
■ cindPrimes'
Caption
Generate PRIMES.TXT
ProgressBar
6
Name
prgOne
1 Номер, указанный в первой колонке, соответствует порядковому номеру объекта на форме (рис. 34-16).'
Исходный код BITPACK.FRM
Option Explicit
Private Declare Function BitGet
Lib 'BitPack,dll" ( _
ByHef bytB As Byte, _
‘ ByVai IngN As Long _
) As Long
Private Declare Function BltSet
Lib "BitPack.dll" ( _
ByBef bytB As Byte, _
ByVai IngN As Long _
) As Long
Private Declare Function BitClr
Lib "BitPack.dll" ( _
, By Ref bytB As Byte, _
продолжение■ -;: ■ - ■■ ■.,
ByVai IngN As Long _
) As Long
' здесь можно изменить путь и/или имя выходного файла Const FileName — v- ^hinJuw.-? lv^ ^r । _ i-u, ext
Private Sub cmdPrlmes_Click()
Dim IngN As Long
Dim Ingl As Long
Dim IngJ As Long
Dim IngK As Long
Dim IngNext As Long.
Dim IngLast As Long
DimbytAry() As Byte
Dim strP As String
' пока занять, показываем песочные часы
MousePointer = vbHourglass cmdPrimes.Visible = False prgOne. Visible = Tre. prgOne. Vale = 0
' получаем наибольшее простое число в указанном диапазоне IngN = Abs(Val(txtMaxPrime,Text))
' битам вбайтов соответствуют только нечетные числа
ReDlfn bytAry(IngN \ 16)
’ информируем полозоват;; “ о ходе процесса'
IblStatus.Caption = "Generating rime numbers table.,," Refresh
' обрабатываем байтовый массив:
' биты с нулевыми значениями представляют простые числа
IngK = (IngN - 3) \ 2
For Ingl = 0 То IngK
1 если следующее число простое...
If BitGet(bytAry(O), Ingl) = О Then
1 ...устанавливаем кратные биты
For IngJ = Э * Ingl + 3 То IngK Step 2 * Ingl + 3 BitSet bytAry(O), IngJ
Next IngJ
' обновляем индикатор прогресса (но не слишком часто) ■IngNext = Int(100 * Ingl / IngK)
If IngNext <> IngLast Then IngLast = IngNext prgOne.Value = IngNext Bid If End If
Next ingi ■ ' по-прежнему держим пользователя в курсе
си. след. стр.
продолжение1‘
' IblStatus.Caption = "Writing prime numbers file...":
, : IngLast = 0'
prgOne.Value = 0 Refresh ' записываем простыв числа в файл на рабочем столе
Open FileHame For Output As #1 ' таблица битов начинается с 3, ' поэтому записываем 2 как простое число ' Print «1, "Prime numbers up to" & Str$(lngN) & vbCrlf : strP = "2"
■ For Ingl * 0 To IngK
'' если это простое ЧИСЛО,, ." Т
■If BitGet(bytAry(O), Ingl) = 0 Then
т <' ... включаем его в строку вывода
■ ■strP = strP & Str$(lngl + Ingl + 3)- T''
'’ если строка уже достаточно длинна,.
If Len(strP) > 65 Then ' .записываем ее в файл Print «1, LTrim$(strP) у ■' готовимся к выводу следующей строки
'StrP =
■:1 обновляем индикатор прогресса (но не слишком часто)
■IngNext = Int(100 - Ingl / и gK>
If IngNext > IngLast Then IngLast = IngNext
LprgOne.Value IngNext
End If Bad If■ '
; . ЕЛО if.
' Next Ingl у. -.:- ' записываем оставшиеся простые числа
Print #1, LTrim$(strP)
Close #1,
* восстанавливаем исходное состояние формы'■
f IblStatus.Caption = '
s cmdPrimes.Visible = True■
prgOne.Visible = False ItausePointer = vbDefault
1"' End Sub.
Private Sub Form_Load() txtMaxPrime.Text = "" ■ IblStatus.Caption = "" prgOne.Visible = False ■ End Sub
продолжение■■ -.
Private Sub mnuAbout_Click()
настраиваем свойства
About. Application = "BitPack"
About.Heading = _
"Microsoft Visual Basic 6.0 Developer's Workshop" ' About.Copyright - "1998 John Clark Craig and Jeff Webb"
About.Display
End Sub: . a
Private Sub mnu£xit_ClickO:-V .
Unload Me
End Sub■
Private Sub mnuCortents._Click()
cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne. Hei pCoirtnand = cdlHelptontents
cdlOne.ShowHelp
. End Sub'
Private Sub mnuSearch_Click()
cdlOne.HelpFile = App.Path 4 "\. ,\.,\Help\Mvbdw. hip"JV
cdlOne.HelpCommand - cdlHelpPartialKeyЙ
cdlOne.ShowHelp;
End Sub.
В модуле формы Bit Pack при каждом вызове функций BitPack. DLL передается первый элемент байтового массива bytAry(). Однако этим функциям можно передавать и отдельную байтовую переменную (не массив) но тогда значение параметра BitPosдолжно укладываться в диапазон от 0 до 7. Для увеличения быстродействия я решил не включать в DLL код проверки диапазона, поэтому Вы должны сами контролировать значения, передаваемые в BitPos. Поскольку в байте содержится 8 битов, диапазон допустимых значений BitPos для массива, например, из 100 байтов составляет 0-807.
Для вычисления простых чисел по алгоритму решета Эратосфена я спроецировал нечетные целые числа 3,. на биты О, 1, 2... в
байтовом массиве. Это позволяет перекрыть каждым 8-битовым элементом массива 16 целых чисел. Поскольку Visual Basic поддерживает байтовые массивы огромных размеров, то теоретически программа позволяет находить очень большие простые числа.
Приложение Dialogs
Приложение Dialogs выводит на экран 5 стандартных диалоговых окон, демонстрируя возможности элемента управления CommonDialog. Я создал панель инструментов с пятью кнопками для откры-
тия диалоговых окон Open, Save As, Color, Font и Print, которые реагируют на все действия, но, так как в моей программе нет соответствующего кода, ни на что не влияют. Например, Вы можете выбрать любой файл в диалоговом окне Save As, но на самом файле это никак не отразится.
Рис. 34-17 иллюстрирует приложение Dialogs в период выполнения, а на рис. 34-18, 34-19, 34-20, 34-21 и 34-22 показаны диало-' говые окна, которые появляются при щелчке соответствующей кнопки панели инструментов.
Рис. 34-17 Приложение Dialogs в действииРис. 34-17 Приложение Dialogs в действии
Рис. 34-17 Приложение Dialogs в действии
Рис. 34-17 Приложение Dialogs в действии
н, £f F32MF04.bmp | ffFEMFClSbmp J gF32MT06bn™н, £f F32MF04.bmp | ffFEMFClSbmp J gF32MT06bn™|< ^"F32vlFa7 bmp Д ^F32MFD8 bmp |j ЙР^РгеЬг^|< ^"F32vlFa7 bmp Д ^F32MFD8 bmp |j ЙР^РгеЬг^. Fh »№♦ |илнк. Fh »№♦ |илнк
н, £f F32MF04.bmp | ffFEMFClSbmp J gF32MT06bn™
н, £f F32MF04.bmp | ffFEMFClSbmp J gF32MT06bn™
|< ^"F32vlFa7 bmp Д ^F32MFD8 bmp |j ЙР^РгеЬг^
|< ^"F32vlFa7 bmp Д ^F32MFD8 bmp |j ЙР^РгеЬг^
. Fh »№♦ |илнк
. Fh »№♦ |илнк
^F32MF1$Xbmp iSF3S4F11.bmp j§F32MF12 w SF32MF1 Э.зп¥ ^Р32МСИ amp ]0Р32М;16зтр
Рис. 34-18 Диалоговое окно Open, доступное через элемент управления CommonDialog
F32MFC1 Ьлр 'F3>HStxnl НЗЧРОЗ ь™
SRZMFCEnmo
jh гумп: tnp JraSHF12bmp ЛГимриьв 5FKMF1 Strop
M—
FF.{«ine :| ^^^«Sefl' ^JII Is
Рис. 34-19 Диалоговое окно Save As, доступное через элемент управления CommonDialog
fr-^MrcSamc F3?MFC6bno ^F32MFC7bmp
пв
yid te-Cutkiiii Cutxkyid te-Cutkiiii CutxkGavz [б а^]Г"Gavz [б а^]Г"
yid te-Cutkiiii Cutxk
yid te-Cutkiiii Cutxk
Gavz [б а^]Г"
Gavz [б а^]Г"
J^eacz<fcaj
i-FrrrrFrr
-вн iTrece еггжжгжж ■Г® «МЖЖЖ жжжжжжжж 1ВЖЖГ" ГГЖГ
Г ГГГГГ 17®
^Ж Ж Ж Ж Ж Ж Я
.]■■ СК I Cinril J
Рис. 34-20 Диалоговое окно Color, доступное через элемент управления CommonDialog
::• ;fb»<t еТд^ле4ж* TH» Ялу 'pi• •" > Ьм-..• --i ^ Гт■ । ";^№«пй inflw pufM
Рис. 34-21 Диалоговое окно Font, доступное через элемент управления CommonDialog
nrnr
nr
nr
|:"puMi^£tinnLiD#!i >!
ч /?Шг 9#w» tnnter, Reef ^wuWwrt^MHTiDil
Г.аптеЩ '
■i w | .aewalh
Рис. 34-22 Диалоговое окно Print, доступное через элемент управления CommonDialog
Работая над новыми программами, я стараюсь держать это приложение под рукой. К примеру, когда мне нужно добавить в новую программу возможность выбора цветов, я просто вставляю элемент CommonDialog, копирую соответствующий код из Dialogs и при необходимости модифицирую его. Зачастую так и быстрее, и удобнее, чем шарить по справочной системе, пытаясь вспомнить, как программируется элемент CommonDialog.
Особенности приложения Dialogs
У приложения Dialogs есть несколько уникальных особенностей, демонстрирующих ряд полезных приемов программирования.
About и About2
Программа может показывать 2 типа диалоговых окон About. В ее меню Help присутствует не только команда About, но и About2. Диалоговое окно About — одно из тех, что я включал в большинство программ для этой книги, a About2 — альтернативное диалоговое окно, которое я уже продемонстрировал в программе VBClock из главы 3 I «Дата и время». Хотя в обоих случаях создаются сходные окна, приемы для этого используются разные. Я привожу здесь оба окна, чтобы Вы могли их сравнить.
Фон с градиентной заливкой типа «заход солнца»
Есть и другие интересные особенности у этого приложения. Простой модификацией алгоритма перехода синего цвета в черный (см. главу 14 -Графические методы») я добился перехода фона на этой форме от красного к желтому — как при красочном закате. Этот код легко переделать под любые другие цвета. Конечно, на большинстве форм лучше смотрится более мягкая цветовая гамма, но для демонстрационного приложения я предпочел цвета поярче.
Скрытое сообщение
Я добавил в приложение скрытое сообщение — «пасхальное яйцо»: этутему мы рассмотрели в главе 18 «Защита». Скрытые сообщения, например, с перечислением авторов, можно активизировать самыми разными способами. В этом приложении я регистрирую координаты курсора мыши при последних четырех щелчках на главной форме. Если Вы последовательно щелкнете около каждого угла картинки, расположенной центре формы, на экране на 5 секунд появится секретное окно. Попробуйте щелкнуть возле левого верхнего, правого верхнего, правого нижнего и левого нижнего углов окна рисунка. При правильных последовательности и позициях этих щелчком Вы увидите сообщение на ярком желтом фоне.
Позиция формы
Практически во всех программах, представленных в книге, я центрировал формы на экране при выполнении их процедур обработки события Load. Небольшая модификация этого алгоритма позволит позиционировать форму в любой точке экрана. Попробуйте щелкнуть какой-нибудь участок уменьшенного изображения рабочего стола (он находится в центре основной формы приложения Dialog). Если Вы щелкнете в его левой нижней четверти, вся форма перепрыгнет в аналогичную позицию на реальном экране и после двухсекундний задержки вернется на прежнее место — так что Вы сможете продолжить эксперименты. Изучите исходный код процедуры picScrecn_Click, и Вы поймете алгоритм перемещения формы.
Рис. 34-23 Щелкните изображение рабочего стола в окне рисунка, чтобы временно переместить окно приложения
На рис. 34-23 курсор мыши находится где-то в левой нижней четверти окна рисунка, и после щелчка форма временно перемещается в соответствующую позицию на экране (рис. 34-24).
РИС. 34-24 Окно приложения временно сместилось в заданную позицию экрана
Состав приложения Dialogs
Проект Dialogs содержит 4 файла. Кроме основной формы Dialogs, присутствуют два типа формы About и специальная форма для скрытого сообщения (рис. 34-25).
а Ahci/ ы№пЫ.п1Гч
ti- dlgEgg{dtfM.M}
Й frrnAboui2 (Abrw^.i'itfi) Cl IrriDiakDOS (Balogs frmi
Рис. 34-25 Список файлов в проекте Dialogs
DIALOGS.FRM
В центре формы Dialogs размещено окно рисунка с уменьшенным изображением рабочего < юла Windows 95. Как я только что говорил, щелчок в любом месте этого изображения заставляет форму временно перепрыгнуть в ауже относительную позицию на реальном рабочем столе.
Рис. 34-26 иллюстрирует форму Dialogs на этапе разработки.
Рис. 34-26 Форма Dialogs на этапе разработки
Создавая эту форму, используйте таблицы и исходный код, приведенные ниже.
Меню и их элементы в DIALOGS. FRMi
Название
ИМЯ
Уровень вложения
Параметр Enabled
©File
mnuFile
0
True
©New
mnuNew
1
False
©Open...
mnuOpen
1
False
©Sate
mnuSave
1
False
Save ©As-
mnuSaveAs
1
False
—
mnuFiteDasbl
1
True
E©xit
mnuExit
1
True
©Help
mnuHelp
0
True
©Contents
mnuContents
1
True
©Searchfor Help on...
mnuSearch
1
True
mnuHelpDasbl
1
True
©About...
nmuAbout
1
True
About©2„
mnuAbout2
1
True
Объекты DIALOGS.FRM и значения их свойств
Тип и номер1
Свойство
Значение
Form
Name
jrmDiatogs■
Caption
Dialogs
BorderStyle
3 - Fixed Dialog. .
PictureBox
Icon
PcOl.ico
1
Name
picTop
CotnmandButton
Align
1 - AlignTop
г
Name
cmdOpen
CommandButton
Caption
WApen
3
Name
cmd.Sare
CommandButton
Caption
e-Save
4
Name
cmdColor
CommandButton
Caption
®Color
5
Name
cmdFont
CommandButton
Caption
&Fonl
6
Name
cmdPrint
PictureBox
Caption
7
Name
picScreen
AutoSize
True
CommonDiaiog
Picture
DESKFOPBMP
Я
Timer
Name
cdlOne
9
Name
tmrClock
Interval
100
1 Номер. указанный в первой колонке, соответствует порядковому номеру объек- ■ тана форме (рис. 34-26).
Объекты D1AL0GSJRM и значения их СВОЙСТВ (продагжение)
Тип и номер
Свойство
Значение
Timer
10
Name
tmrRelocate
Enabled
False
Interval
2000
StatusBar
И
Name
sfbBottom
Align
2 - vbAlignBoltom
Style
1 -sbrSimple
Исходный код DIALOGS.FRM
Option Explicit
Dim tfivntX, fflvntV
Dim mvntLastSec
Dim mvntEggX(1 To 4)
Dim wntEgg¥(1 To 4)
Private Sub FariB_ClickO g)im inti as integer ..
' отслеживаем последние Четыре щелчка на форме
For inti = 1 Io S
mvntEggX(intl) = mvntEggX(intI + 1) mvntEggY(lntl) = mvntEggY(intl + t) Next inti'
mvntEggX(4) = mvntX mvntEggY(4) = mvntY
' проверяем последовательность целиков и их координаты If Abs(mvntEggX(t) - 70} < 30 And „
Abs(mvntEggY(1) - SO) < 30 And _
Abs(mvntEggX(2) - 360} < 30 And _
Abs<mvntEggY(2) - 60} < And _ Abs(mvntEggX(3) - 360) < 30 And Abs(mvntEggY(3) - 290) < 30 And Abs(mvntEggX(4> - 70) < 30 And _ Abs(mvntEggY(4) - 290} < 30 Then ' показываем скрытое сообщение dlgEgg.Show vbModal
End If
End Sub
Private Sub Form_Load()
' центрируем эту форму
продолжение
Me.Left = (Screen.Width - Ne.Width) ч 2'
; Ne.Top = (Screen. Height - Ne, Height) \ 2::
■- подгоняем высоту панели с кнопками
' picTop.Height = cadOpen.Height + _
(picTop.Height - picTop.ScaleHeight)■.
End Sub■
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, . X As Single, Y As Single)
■' сообщаем тай.меру обноаить строку состояния . .. .
mvntLastSeo = -1
' запоминаем позицию курсора мыши
' mvntX = X
' mvntY = Y.
End Sub:'
Private Sub Form_Palnt()
Dim IngN As Long With He;
.ScaleHode * vbPixels
.DrawStyle = 5 'Transparent
S-1".DrawWidth = 1
Г ■ ' End With
■' изображаем фон с градиентной заливкой "заход солнца"
' (переход от красного цвета к желтому)...
For IngN = О То ScaleHeight Step ScaleHeight \ 16
Une (-1. IngN - 1 )-(ScaleWldth, IngN + ScaleHeight \ 16), RGB(255, IngN * 255 \ ScaleHeight, 0), BE
Next IngN■
End Sub'- '
. Private Sub mnuAbout_Click()
" настраиваем свойства для диалогового окна About .
:. About.Application = "Dialogs"'
About. Heading =_
"Microsoft Visual Basic 6.0 Developer's Workshop" About. Copyright = ”1998 John Clark Craig and Jeff Webb" About. Display:
•• End Sub'
Private Sub mnuAbout2_Cl$ck()
' отображаем диалоговое окно About2
TrmAooutS.Display
End Sub•••
■продолжение
Unload МВ„"
End Sub
Private Sub mnuContents„Click()
' uuiune.HelpFile = App.Path & ”\. ,\..\Help\Mvbdw.hlp"
cdlOne.HelpComreand = cdlHelpContents
cdlOne.ShowHelp
End Sub
Private Sub mnuSearch„Click()
cdlOne.HelpFile = App.Path & "V .\, .\Help\Mvbdw.hip"
cdlOne.HelpCommand = cdlHelpPartialKey
cdlOne.ShowHelp
End Sub.
Private Sub pioScreen_Click()
Dim vntXpct
■ Dim vntYpct
‘ определяем относительные координаты курсора мыши
в окна рисунка
vntXpct = 100 * mvntX \ picScreen.ScaleWldth
vntYpct = 100 * mvntY \ picScreen.ScaleHeight
1 перемещаем центр формы в ту же относительную позицию .‘ на экране.
Me.Left = Screen.Width * vntXpct \ 100 - He.width \ 2
Me.Top = Screen.Height * vntYpct \ 100 - Me.Height \ 2
1 включаем таймер для возврата формы на прежнее место tmrRelocate.Enabled = True
End Sub
Private Sub picScreen_MouseMove(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
1 запоминаем координаты мыши
mvntX = X
mvntY = Y
1 обновляем строку состояния анизу формы
stbBottom.SimpleText = "Click to temporarily relocate " 6 . "center of this form on desktop”
.1 указываем таймеру не отображать дату ивремя в строке
■" состояния
с mvntLastSec = -2.
End Sub' .
Private Sub cmdColor_Cliok()
устанавливаем флаги для диалогового окна Color
cdlOne.Flags = cdlCCRGBInit
продолжение
' выводим диалоговое окно Color
cdlOne,ShowColor
' показываем выбранный цвет
■ ' MsgBox "4.Н" & Hex${cdLOne.Color), , _ "Selected color,.."
End Sub
Private Sub cmdFont, CllckO
Dim strlab2 As String
idstrTabS = vbTab & vbTab
' устанавливаем флаги для диалогового окна Font
caivi^ ггауя = lullFINYSIV^c + cdlCFBoth + odLCFScalableCnly
' выводим диалоговое окно Font
CdlOne.ShqwFont
' показываем выбранные параметры шрифта
MsgBox _
"Font Name:" & vbTab & cdlOne,FontName & vbCrLf & _
."Font Size:" & strTab2 4 cdlOne.FontSize 4 vbCrLf 4 _
“Bold:" & strTahS & cdlOne,FontBold & vbCrLf & „ "Italit: & strTabS & cdlOne.Fontltalie, , _ "Selected ■ hl. ..
End Sub'"
Private Sub cmdOpen„Click()
' определяем фильтр для диалогового окна Open
Dim strBat As String
Dim strTxt As String
Dim strAll As String
strBat = "Batch Flies (*.bat)|«.bat"
strTxt = "Text Files <*.txt)|*.txt",
strAll "All Files (*,*)|*.*"-
' caiune.Filter * strBat 4 “ |" & strTht 4& strAll
' указываем, что фильтр по умолчанию - третий перечисленных
. cdlOne.Filterindex = 3
■' убираем флажок "Readonly"■
cdlOne.Flags = cdlOFNHideReadOnly
' отменяем ранее выбранный файл, если такой был
. cdlOne.FileNemv =
' выводим диалоговое окно Open
,. cdlOne.ShowOpen
■' показываем имя выбранного файла
If cdlOne.FileName = "" Then Exit Sub
MsgBox cdlOne.FileName, , "Selected 'ile,.."
End Sub
продолжение
Dim StrPrintToFile As String
' устанавливаем флаги для диалогового окна Print cdlOne. Flags = cdlPDAHPages т cuirunooeiection ' устанавливаем условный диапазон страниц cdlOne-Min = 1 cdlOne.Нах = 100 cdlOne. FromPage - 1 cdlOne.ToPage = 100
' выводим диалоговое окно Print■■■■
cdlOne. ShowPrir,.-.
1 получаем некоторые параметры печати If cdlOne.Flags And cdlPDPrintiurne Then StrPrintToFile = "Yes"
Else
StrPrintToFile - "Mo" End If
' показываем выбранные параметры печати■
MsgBox _
"Begin Page:1' & vbTab & cdlOne.FromPage & vberLf & _ "End Page:" & vbTab & cdlOne.ToPage & vberLf & _
"No. Copies:" 4 vbTab & cdlOne.Copies & vberLf & _ "Print to File:" & vbTab & StrPrintToFile _ , , "Selected print information..."
End Sub
Private Sub TidSave„Click()
' определяем фильтр для диалогового окна Save As Dim strBat As String
Dim strTxt As String■
Dim strAll As String
strBat = "Batch Files (*.bat)|*bat"
strTxt = "Text Files («.txt) | *. txt" strAll = "All Files (*.*)]*.*"
cdlOne.Filter = strBat & "I" & strTxt & T & strAll
’ указываем, что фильтр по умолчанию - третий из перечисленных cdlOne.Filterindex ■ 3
' убираем флажок "Readonly"
cdlOne.Flags = cdlOFNHideReadOnly
' отменяем ранее выбранный файл, если такой был cdlOne,FileName = ""
' выводим диалоговое окно Save As
CdlOne,ShowSave
' показываем имя выбранного файла If cdlOne.FileName = "" Then Exit Sub MsgBox cdlOne.FileName, , ’"SaveAs" file..." End Sub
продолжение
Private Sub tmrflelocate_Timer()' '
■ форма перемещается лишь раз
tmrRelocate. Enabled = False■
' центрируем эту форму
Ne.Left = (Screen.Width - Me.Width) \ 2•'
Me.Top = (Screen, Height - Ne.Height) \ 2.
' End Sub
PrivatsSub tmrClock^TlmerO■
Dim vntSec]
vntSec * Second(Now)'
If vntSec -fflvnt LastSec Then Exit Sub.,
If mvntlastSec = -2 Then Exit Sub mvntlastSec = vntSec ' обновляем дату и время в строке состояния stbBottom.SimpleText = Format(Date, "Long Date") & _
Space$(5) & Format(Time, "hh:mm AMPM")'
End Sub:
DLGEGG.FRM
Эта простая форма показывает секретное сообщение после того, как Вы щелкнете нужные места в правильной последовательности. Таймер выгружасз форму через 5 секунд, но Вы можете модифицировать код так, чтобы она выгружалась при щелчке в любом ее месте. Хотите — усовершенствуйте форму или измените текст сообщения.
Рис. 34-27 иллюстрирует форму гй^^на этапе разработки.
This "Easter egg":'
. (hidden message) will . : ■:
.. disappearin 5 seconds. , ■;
Рис. 34-27 Форлш dlgEgg на этапе разработки
Создавая эту форму, используйте таблицу и исходный код, приведенные ниже.
Объекты DLGEGG.FRM и Значения их свойств
Свойство
Значение
Form
Name Caption BackColor BorderStyle ControlBox MaxButton
MinButton
WindowState
dlgEgg
digEgg &H0O0OFFFF&.
1 - Fixed Single
False
False
False
0 - Normal
Label
Name Alignment Caption
IblEgg
2 - Center
This "Easter egg" (hidden message) will disappear in 5 seconds.
Font BackColor Timer
Arial - Italic - 14 @-H0000FFFF@-
Name1 Enabled
Interval
bnrQuit
True
5000‘'
Исходный KOfiDLGEGG.FRM
Option Explicit
Private Sub ttrQult_11B»rO
Unload He
End Sub
предметный указатель
А
ActiveX 8, 375
DLL-сервер 60
ЕХЕ-сервер 60
компонент Chance 72-75
тестирование 74-75
доступ к функциям Microsoft
Excel 383-384
подсчет слов 382
проверка правописания 375
— средствами Microsoft Excel 379-381
средствами Microsoft Word
375-379
ADO 12
ASP 156
Automation Manager 426-427
C
Code Editor 5
Component Object Model 8
Control Creation Edition (CCE) 8
ID
DAO 368-370,374
DHTML
— конструктор 151'
— приложение
создание 151-156
установка по Интернету 172
реализации 142
страница 138
— пример 143-144
DHTML Page Designer 142-143 DLL
ActiveX 60
компиляция 11 -
создание на Visual Basic 412-415
— тестирование в среде разработки 415-417'
обычная (создание на С)
417-422.658
Document Object Model 142
Е'
Empty 39
G
GDC 250
H
HTML 138
I■
IP-адрес 123
M
Microsoft HTML Help 321
Microsoft Internet Information
Server (IIS) 147
приложение is<>—1<_,-|
Microsoft Windows Scripting Host 439
N
NIST 577-578
Nothing 43-45
Null 39
0
Object Browser 4
P
Platform SDK 173■
iR
Remote Automation Connection Manager 427
s
SDI 4
U.
UDT 12
— выравнивание полей в памяти 28, 50
— передача объекту 449-451
— создание 49-50'
Unicode 32
V
VBScript 447
— особенности 141
Visual Basic 7
— выпуски 3-4
— генератор случайных чисел 499
повторение заданной последовательности 330
— изменения в языке 11—12
— интегрированная среда разработки 4-7
— компилятор 7-8
— новые ActiveX-элементы 9~ I ч
— новые строковые функции 40
новые формы для подключения к базам данных 364
особенности таймеров 486
W
W3C 145
Web-класс 138, 147, 157
включение НТМ-файла 158
— создание 147-148
Windows NT Task Manager 430
A
Автоматизация 375
удаленная 411, 426
— ошибки 429-430
анимация 261-265
аргумент 54
извлечение из командной строки 302
— именованный 54-55
аспект 251
Б
база данных
AREACODE.MDB 585
- BIBL1O.MDB 364, 594
JOT.MDB 599
-PHONES.MDB 370-371
■ STRINGS.MDB 409
генерация отчетов 374
— создание
с помощью Visual Data
Manager 365-367
через DAO 370-371
пользовательский интерфейс 367-368
браузер
FTP (создание) I 31 -134
Internet Explorer (управление) 135-137
В
вызов обратный 176
вычисленияраспределенные
423
Г
графика
— быстрое вычерчивание многоугольников 247-251
— выделение прямоугольных областей «резиновым» контуром 243-246
— вычерчивание эллипса 251-254
— «горячие» участки 246-247, 570
— закраска произвольного замкнутого участка 254-257
— поворачивание
растрового изображения 257-259
текста на произвольный . угол 270-274
— прокрутка графических изображений 259-261
д
деление целочисленное (\) 201
диалоговое окно
-About 197-199
— Add Form 200
Auto Eist Members 6
Auto Quick Info 6
Components 100
Connect Property Pages 104
Data Bindings 113
Display Properties 386
File Download 168
— Font 357, 669
— Metric Conversions 540
New Project 3
Open 668
Password 328-329
Print 670
Procedure Attributes 107,111
Project Properties 575
вкладка Compile 7
вкладка Component 423
— Property Pages 104-108
для элемента Toolbar 604
— — для элемента UpDown 226
Save As 669
TCP/IP Properties 122
Topics Found 315
— Window Properties 314
— стандартное 668-669
- утилиты CLIREG32 425 диапазон действия 21-22 директива #Const 47 документ
-ActiveX 138, 145-146, 168
приложение 164-168
создание 145-146
— — установка по Интернету 168-171
-DHTMP 142-145
- НТМЕ 140-141
— пользовательский 145, 164-165
3
заставка
— экран 206-207
— экранная 385-400
MySaverl 386-388
MySaver2 389-390
MySaver,3 391-392
— - MySaver4 393
MySaverS 394-398
параметры командной
сроки 398-400
— предотвращение одновременного запуска двух экземпляров 389
растворение изображения 398
создание 385-386
И
инкапсуляция 70
Интернет 120-121
разработка компонентов
138-148
распространение IIS-приложений 172
средства защиты 3 37
типы приложений J 19-150
уровни протоколов 120-121
интер фейс 70,431
— IDTExtensibility 431-432
MDI 4-5, 358
SDI 4-5
К
клавиша
Enter (изменение поведения) 344
— <горячая» 346-347
класс 50
-Child 165-
-Cipher 330-335,656-658
— Collection 78,452
— Connect 333-435
— Dice 72-75
-Egg 327-328
-Ellipse 253-254
— Fraction 412-415
-HSL 237-240:
- HSV 232-236
Eoan 62-67
использование 68-70
Midpoint 450
Moon 85
— Moons 84,
-Msg 634, 639-642""
-Number 424-425
— Paint 254-257
— Planet 84
взаимосвязь со строками 285- —динамический 33обмен данными со строками 33передача вместо строк 29-33— как возвращаемое значение 66— передача как параметра 56мастер 306-308—ActiveXControi InterfaceWizard 90Application Wizard 363— uat a Form Wizard■ 363-364Find Setup Wizard . ■.New Project Wizard 322— Package and Deployment Wizard 13,100,169-170— VBCal Wizard 306-308, 552-553— метафора 552меню—динамическая настр ойка 219-220— именование 20метод-AddCode 441-442AddObject 442AsyncRead 109AtEndOfStream 292CheckSpelling 377-Circle 251— Connect 127— ConnectEvents 432File Exists 292GetChunk 134-GetData 126- GetFik- 292-293-GetFolder 290— GetProperty 154-155— GetSpellingSuggesrions 378-379— Line 247-PaintPicture 231, 259, 261— Point 257— PSet 257взаимосвязь со строками 285- —динамический 33обмен данными со строками 33передача вместо строк 29-33— как возвращаемое значение 66— передача как параметра 56мастер 306-308—ActiveXControi InterfaceWizard 90Application Wizard 363— uat a Form Wizard■ 363-364Find Setup Wizard . ■.New Project Wizard 322— Package and Deployment Wizard 13,100,169-170— VBCal Wizard 306-308, 552-553— метафора 552меню—динамическая настр ойка 219-220— именование 20метод-AddCode 441-442AddObject 442AsyncRead 109AtEndOfStream 292CheckSpelling 377-Circle 251— Connect 127— ConnectEvents 432File Exists 292GetChunk 134-GetData 126- GetFik- 292-293-GetFolder 290— GetProperty 154-155— GetSpellingSuggesrions 378-379— Line 247-PaintPicture 231, 259, 261— Point 257— PSet 257
взаимосвязь со строками 285
- —динамический 33
обмен данными со строками 33
передача вместо строк 29-33
— как возвращаемое значение 66
— передача как параметра 56
мастер 306-308
—ActiveXControi Interface
Wizard 90
Application Wizard 363
— uat a Form Wizard■ 363-364
Find Setup Wizard . ■.
New Project Wizard 322
— Package and Deployment Wizard 13,100,169-170
— VBCal Wizard 306-308, 552-553
— метафора 552
меню
—динамическая настр ойка 219-220
— именование 20
метод
-AddCode 441-442
AddObject 442
AsyncRead 109
AtEndOfStream 292
CheckSpelling 377
-Circle 251
— Connect 127
— ConnectEvents 432
File Exists 292
GetChunk 134
-GetData 126
- GetFik- 292-293
-GetFolder 290
— GetProperty 154-155
— GetSpellingSuggesrions 378-379
— Line 247
-PaintPicture 231, 259, 261
— Point 257
— PSet 257
взаимосвязь со строками 285
- —динамический 33
обмен данными со строками 33
передача вместо строк 29-33
— как возвращаемое значение 66
— передача как параметра 56
мастер 306-308
—ActiveXControi Interface
Wizard 90
Application Wizard 363
— uat a Form Wizard■ 363-364
Find Setup Wizard . ■.
New Project Wizard 322
— Package and Deployment Wizard 13,100,169-170
— VBCal Wizard 306-308, 552-553
— метафора 552
меню
—динамическая настр ойка 219-220
— именование 20
метод
-AddCode 441-442
AddObject 442
AsyncRead 109
AtEndOfStream 292
CheckSpelling 377
-Circle 251
— Connect 127
— ConnectEvents 432
File Exists 292
GetChunk 134
-GetData 126
- GetFik- 292-293
-GetFolder 290
— GetProperty 154-155
— GetSpellingSuggesrions 378-379
— Line 247
-PaintPicture 231, 259, 261
— Point 257
— PSet 257
- Planets 81-83 -Polygon 247-251 -Random 499-501
- Registry 294-298
-RGB 231-232
-Rotator 270-274
-Sizer 435-437
-Star 81
— UInt 51-53
-User 76-77
— абстрактный 88
— именование 20-21
— модуль 50 ключевое слово - Event 78 - Friend 89
— Implements 88,431
-Me 259
-New 43-44, 52
— Optional 55
— Private 48
— Public 48
— RaiseEvent 78
— WithEvents 78
комментарий 22 константа
-SWT 204-205
— Visual Basic 46
—для параметра wCommand функции WinHe]p 316
—для свойства MousePointer 338-339
— компилятора 46
— пользовательская 47-48 конструктор
— CONNECT.DSR 525-528
— Web-класса 156 курсор
— изменение 338-339
— нестандартный 340
— с анимацией 341-342
м
маска битовая 48 массив
— байтовый 29-33, 285
PucProperty 154।
— Run 445
ShowHclp 319 — TexiWiclrh 215
WVicTcmplatc 160. 162 — ZOrder 203-204 — дружественный 89 модель потоков 139 модификатор — Alias 525_
— As Any 178 — By Ref 31-32,1^8 — ByVai 31-32, 178-179
H
набор 78
— слипа me dControIs 118 ■Planers 81-82 — Procedures 445 — SelectedControls 106 — SolarSys 79-85 — вложенный 85-S7 — перечисление в цикле For Each 83
— стандартные методы 82-83 надкласс 186 надстройка 431
-API Viewer 174-175, 179 — Resource Editor 403
— Visual Data Manager 365-367 — Web'Ibol 135-137 — Wizard Manager 306-307 — базовые концепции 431 -432 — использование 438-439 — компиляция 533 — первый запуск 437-438 — разработка 432-437
О
объект 51 -Арр 207,575 - Band 229 -Child 152 — Collection 453, 454-455 — DataEnvironnient 374 — DataReport 374,':'1
- DHTMLPageDesigner 13
— Document 155
-Err 461.
— Extender 117
-File 292
FileSystem 13
FileSystemObjecc 141, 287-293
работа с дисками 289-290 — — работа с папками 290-291
работа с файлами 291
Hyperlink 167
Panel 304
— Picture 265-266
применение в анимации 265-266
— Property-Bag 101-102
— Request 161-
— Response 148,157-158
— Session 147
Sheet 380, 383
TexcStreaiu. 292
VB1DE.VBE 432, 435
— VBlnsrance 435
с визуальным интерфейсом 75-78
создание 43-44, 61
сравнение 45
унич1 ожение 4 5
окно
Form Layout 203
Registry Editor 298
— режим 203-205
— динамический 203
— модальный 203
оператор
— AddressOf 177
- ChDir 278
— Declare 173
— DefDbl 484.
-Dim 19'
— DoEvents 290
Enum 48
Event 188
FileCopy 277
-Get 281-283
Is 45
Kill 277
Load 485
- LSet 26-27
MkDir 278
MousePointer 290
Name 277
On Error Resume Next 460
Option Explicit 19
Property Get 65
Property Let 65
Property Set 95
— PropertyChanged 102
-Put 281
— Randomize 330
-RmDir 278
— SavePicture 258
- Scale 519
-SendKt-vs 318, 344
— Set 44-45
-With 86
операция логическая 48-49
описатель контекста устройства
(hDC) 261
определение
версии операционной системы 179
времени работы системы 183-184
—дисковых устройств 184-185
— координат курсора 342-343
— «разрядности» операционной системы 455-456
— системных цветов 180-182
— состояния клавиш-модификаторов . 15-146
— типа процессора 182-183
ошибка
— строчный перехват 459-461
— приложения, выполняемые удаленно 428-429
'П
панель инструментов 603-605
— имитация 217
— создание 216-217
параметр 54-59
— необязательный 55
— передача произвольного типа данных 56-5"'
«пасхальное яйцо» 326-328, 671
перезапуск Windows 457
переменная 25-53
— именование 17 -1 о
венгерская система 19
— объектная 43-44
— объявление 19-20
— TimaDate 35,36-37
выборка компонентов 36
— эмуляция беззнаковых целых 25-28
подпись цифровая 139
подсказка всплывающая 303-304
полиморфизм 88
преобразование единиц измере
ния 241
приложение
— Animate 482-492
-APIAddin 521-532
— AreaCode 585-592
-BitPack 658-667
— ColorBar 518-521
-DataDump 593-597
-Dialogs 667-681
особенности 670-671
— Editor 441.444
— Future Shock 151, 154, 165
- HSVHSI. 465-482
-Jot 597-611
-Lottery 492-501
— Messages 634-644
— Metric 533-542
— MousePtr 612-618
— Music 194-195
— MySaver 501-517
- NISTTime 577-584
— Paint 401-402
- Prime Numbers 423-426, 428
— ScripDem 543-551
-Secret 644-658
— ShowTeli 619-624
-VBCal 552-562
— VBClock 562-577
— WindChiJ 625-633
— выполняемое удаленно
423-429
— локализация 406—ПО
— поддержка сценариев 439-441
— — выполнение макросов 445
- — обработка пит™ и. периода выполнения 445-447
— обработка ошибок периода компиляции 443-444
— — создание макросов 447-449
— с MDI-пнтерфсйсом 358
программа
— Approximate Phase of the Moon 405-406
-Chat 127-128
— Database Language 407
— File System Demo 288
— Fraction Math 51
— Systems Status Monitor 125
■—Tip of the Day 305-306
— компиляциями экранной заставки 385
— набор телефонного номера 457-458
— текстовый редактор 350-352
программирование объектно- ориентированное 60-89
— средства i i
протокол 120
-TCP 123-124
— — связь 127-130
-TCP/IP 121
-UDP 123-124
— пример 124-127
процедура-свойство 199
Р
реестр 294-302
— защита данных 335-336
— импорт раздела 301
— раздел
—Defaulticon 301
— - HKEY_ajRRENT_lTSER\Soft- ware\VB and VBA Program Settings 297
- — Sheil\Open\Command 301
— Shell\Print\Conimand 301
— сохранение состояния приложения 298
— чтение и запись данных 294-298
— экспорт раздела 299
С
свойство
— ActiveControl 359
— Active-Form 359
— Appearance 226,228
— Arrows ■ 226
— AutoRedraw (элемента PictureBox) 563
— Change 352
— Container 212
— CustomFormat 554
- EOFAction. 368
— Flags 356
- HeipContextID 318
—HelpFile 318
— Instancing 437
— Keypreview 327, 346
— Mouseicon 340
— MousePointer 338-339
— Orientation 226
— PjsswordChar 329
— Path (объекта Арр) 278
— Picture 265
— — MDI-формы 360
— Prevlnstance 389
-Tag 359
-ToolTipText 303
— TwipsPerPixelX 241
— TwipsPcrPixeLY 24 I
— используемое по умолчанию 71, 8?
— страница 104
связывание
— позднее 381
— раннее 381
— сданными 110
— текстовых полей с элементом Data 368
— элементов управления с базой данных 585
служба RAS 121
событие
—ApplyChanges 1 Об
— AsyncReadComplete 109
— CommandStateChange 137
— DataArrival 126, 129
— Done 194
— Initialize 167-168
- KeyDown 328, 345-346, 393
— Keypress 344-345
— Key Up 345
- Mouse Down 246, 327, 342
— MouseMove 246, 342, 392-393, 570
— MouseUp 246, 327, 342
— NavigateComplete 137
— onCHck 153, 161
— onReadyStateChanged 155
-Paint 506,519
— ProcessTag 163-164
— Resize 362
— Show 167-168
— StateChanged 134
-WebClassStart l60
сокет 123
список связанный 452-455
справочная система
— в формате HTML Help 321-325 - в формате WinHelp 308-311 компиляция и тестирование
311
макросы 314-315
— —полнотекстовый поиск и оглавление 312-313
связываниеидентифика- торов 3I2
— сноски и элементы форматирования 309-310 создание окон 314 файл проекта 3 * 1
— контекстно-зависимая ,518
— преобразование проектов из формата WinHelp в HTML Help 322-323
— режим WhatsThisHclp 320
строка 283-284
— длина 284
— обмен данными с байтовыми массивами 33
— состояния 304
— хэширование 644
структура SYSTEMJNFO 182
сценарий 439
Т
таблицы стилей, каскадные 142 тип
-BSTR 422
- Byte 25
— Date 33-34
— Error 460
— Integer 25
— Long 2 5
— Object 18
— short 422
-Variant 37-39,484
— автоматическое приведение 29
— Булев 28-29
— контроль 20-21
— перечислимый 48,57-59
— приведение 39
— создание нового 51-52
— целочисленный 2 5
тэг
— <DIV> 155
— <SPAN> 155
- <WC@»162
— подстановки 164
У
утилита
— Application Performance Explorer 7
- CLIREG32 425
— Dial-Up Networking i 2 2
— Microsoft Help Workshop 303-309
— Microsoft HTML Help Workshop 303, 321
-REGOCX32 101
— RoboHelp 541
Ф
файл
— ?.ASP 156,158
- =.AVt 192
- ?.BAT 170
- ?.САВ 170
— переименование 277:
- ?.DDF 170
— перечисление 279-280■
— ?.DEF 418,659
—ресурсов 403-410
- .DSR 153
— сообщений 634-636
- ?.НТМ 170
— сопоставление определенного
- MNP 170
типа с приложением 299-301
- ?.INI 294
— удаление 277
- ?.MMM 192
фильтр 42
- ?.RC 404
флаг 48
— ?.REG 299-300,426
фон с плавным переходом
- ?.RES 404
241-242■
- ?.SCR 385
форма
- ?.TLB 428
— About 585
- ?.VBD 165, 170
—About2 574-575,670
- ?.VBG 415
-AniClock 485
-7VBR 426
— AniC-lobe 488-489
-APIADD1N.DLL 533
— APIAddin 528-530
- APILOAD.EXE 179
— Approximate Phase of the Moon
— B1TPACK.H 660
405
— COMCTL32.OCX 208
— ArcaCode 585-587
-GLOBALASA 172
— BitPack 661-663
- HHCTRLOCX 323
— College Cost Calculator 160
- MACR'Jb.VBS 442
— ColorBat 519-520
-MESSAGE.MES 634-635
— Control 211'
- METRIC.HLP 534
— Convert 535, 539
— MOON.32.RES 404
— Corrections 378
- MSCOMCT2.OCX 33
— DataDump 594
— MSINET, OCX 120
— Dialogs 672
— MSWIN SCK.OCX 119
— DiceDemo 74-
— MYDLL.C 418-419
-DlgEgg 680:
— MYDLLDEF 418
-HSVHSL 466-467C
- NISTTLME.TXT 579
— Image 624
- SCRRUN.DLL 287
-Jot 600-601.
-SHDOCVW.DLL 120■ -
■— Launch 425
- TABCTL32.OCX 208
— Lottery 493
- TIPOFDAY.TXT 3 0 5 - 306
— Master/Detail 365
- TSTPRIME.VBS 440
-MD1 358, 360-362
- VBADDIN.INI 437
■«горячие» клавиши 598
- W32CONS.TXT 524-525
особенности 359-360
— W32DECL.TXT 524-525
центрирование изображения
- W32TYPETXT 524-525
360-362, 600
- WIN 32API.TXT 174,521,
— Messages 637
524-525
-Metric 535,537
— ввод/вывод 277-293.
■— MousePtr 613,615
— — ускорение 281. -• - ■
- Msg 642■ ’
— двоичный 281-287
- MySaver 506
— копирование 277
— MySctup 514-515
— NISTTime 577-578
— Note 598, 608
-Point 450-451
— Registry _Test 296
— ScrlpDem 544-545
— Secret 647
— ShowTell 619-621
SolarSys 81,87
-Splash 206, 600, 607
Strings 409
-Tip of the Day 305
User Object 77
— VBC.nl 555-556
-VBClock 563-564
— VBClock2 569-570
— View 654
-WindChii 626-627
— автоматическое позиционирование на экране 201-202
динамическая модификация 218
— для демонстрации элемента
FlatScrollBar 227
дочерняя 358
— — бтиражированиоэкземпля- ров 598
— мигание 210-211
— удаление строки заголовка 220
функция
-API 173-190
в коде ActiveX-элемента
185-186
вызов 173
модификаторы ByVai, ByRef и As Any 178
объявление 172-175
передача адреса процедуры
176-177
—передача строк 175-176
преобразование единиц перед вызовом 249-250
AppActivate 381
-ATAN2 383
BitBlt 261, 395
— — применение в анимации 261-264
ChildWindowProc 176
ColorSplit 182
Command 302
CurDir 278
— CVErr 460
— DateSerial 35
— DateValue 35
— DeleteSetting 294
— DeleteUrlCaclieEntrv 136
- Dir 278-280’
особенности 280
— Do Events 206
— DrawFocusRect 243, 245
— Ellipse 251
— EnumChildWindows 176
— Filter 40,42
FlashWindow 96, 210, 643 FloodFill 254 F ormat 36
GetAllSc icings 294
— GetDC 395
GetDesktopWindow 395
GetDriveType 184
GetObject 136
— GctPrivateProfilcString 433
- GetSetring 294, 297
— GetSysColor 180
— GetSystemlnfo 182
— GetTempFileName 174
— GetTickCount 183
— GetVersion 456
— GecWindowsDirectory 30, 175
--InStr 42-43
—InStrRev 40,42-43
—Join 40-42
— LenB 32
— LoadCursorFromFile 340
—mciExecute
воспроизведение AVI-файлов 192-193
— воспроизведение WAV-файлов 191
— NewEnum 84
— PlgBlt 257
— Polygon 247, 254
—ReleaseDC 395
— Replace 40
-RGB 182,231
— Rnd 499
-Round 12,65
SaveSetting 294 ■
SendMessage 214
SetWindowPos 205
ShowCursor 390, 395
-Split 40-42
-SrrConv 32, 286-287
SysAllocStringLen 422
— SysFreeString 422
— SystemParametersInfo 389
— TimeSerial 35
— Time Value 35
— TypeName 57,407
— Unlcode-версия 174
— WinExec 537
— WinHelp 178,315-317
— обратного вызова fотладка) 176'
— связанная с типом Variant 39
ц
цвет
— RGB-значение 231
— модель
■HSL 232, 237, 241
HSV232
- -RGB 231
— составной 242
цикл For Each 38
Ш
шаблон
— Addin (для создания надстроек) 431
— формы
—для диалогового окна About 200-201
для экрана-заставки 207-208
шифрование 330-332
шрифт (произвольное масштабирование) 269
э .
элементуправления
ADO Data 12
— Animation 267"
Asynchronous Animation 108-110, 139-140
Calendar 33, 555
применение 33-34
CommonDialog 319, 356-357, 593
стандартноедиалоговое окно Color 669
стандартное диалоговое окно Font 669
стандартное диалоговое окно Open 668■
— стандартное диалоговое окно Print 670
стандартное диалоговое окно Save As 669
-Containr 117-118
-•CoolBar 9,229-250
Data 365, 367-368, 585
-DataGombo 9
DataGrid 9
— DataList 9
DataRepeater 9,114-116.
DTPicker 9, 33-34, 554
применение 33-34
FlatScroJIBar 9, 226-228 .
Hierarchical FlexGrid 9
HTML Help 323-325
ImageCombo 9
ImageList 9, 603-604
• Internet Transfer 120, 131-132
-— ошибка ic Executing 152
Label (два амперсанда подряд) 465-466
Line 485
«ListBox (добавление горизонтальной полосы прокрутки) 214-215
ListView 10
Microsoft Comm 457
■ MonthView 10, 554
— MSChart 10
— Multimedia 192
воспроизведение AVI-файлов 193
— воспроизведение WAV- файлов 192
воспроизведение звуковых компакт-дисков 194-196
PicturcBox 268-269, 275
вывод текста несколькими шрифтами 274-276
точное позиционирование текста 268
-ProgressBar 10,220-221
— —имитация 222-223
RichTextBox 348-353, 532
-Script 10,440-441,543
-Slider 10,223
SSTab 209
— StatusBar 304-305
Syslnfo 179
-TabStrip 10,208
-TextBox 352-353,357
— TickControl 185
— Toolbar 216
-TreeView 10
— UpDown 224-226
— — связывание с другим элементом управления 225
— WebBrowser 120, 135
-Winsock 119, 120-131
— контейнерный 116-117
— на DHTML-страницах 143
- на базе ActiveX 90-118, 138
-Blinker 91-99
Employee 111-114
— асинхронная загрузка 139
для Интернета 1 5* >
- — добавление свойств, методов и событий 93-94
-- —использование с VBScript 140-141
— —компиляция 99-101
отладка 97-99
программирование функциональности 95-96
регистрация 100
свойство, доступное на этапе разработки 101-103
создание 90-92
создание связанного с базой данных 110-114
упаковка перед распространением 139
«облегченный» 213-214
перемещение в новый контейнер 2 12
префиксы 15-17
— расширение созданием надкласса 186-190
свойство ToolTipText 10
Джефф Уэбб
Джефф Уэбб (JeffWebb) — бывший старший сотрудник группы разработчиков Microsoft Visual Basic. В Microsoft принимал участие в работе над OLE Automation и всеми диалектами Бейсика, в том числе Basic PDS, QuickBasic, Visual Basic и Visual Basic for Applications. Живет в Сэнибел (штат Флорида). С ним можно связаться по электронной почте JeffWebb@msn.com — если только он не на рыбалке.
Джон Кларк Крейг ■
Джон Кларк Крейг (John Clark Craig) — автор 16 книг по программированию персональных компьютеров. Большинство его книг выпущено издательством Microsoft Press и охватывает все версии Microsoft Visual Basic for Windows, Microsoft Visual Basic for MS-DOS, Microsoft QuickBasic и Microsoft QuickC. Джон был научным редактором первого и второго изданий Microsoft Mouse Programmer’sReference (Microsoft Press, 1989 и 1991 гг.), а также написал отдельные главы для таких книг, как Microsoft Word Developer's Kit (Microsoft Press, 1993 г.) и Microsoft Windows 3.1 Developer’s Workshop (Microsoft Press, 1994 г.). Живет co своей семьей в Касл Рок (штат Колорадо). Владелец собственной компании Craig Software. Вы можете связаться с ним по электронной почте JohnCraig@msn.com.
ЛИЦЕНЗИОННОЕ СОГЛАШЕНИЕ MICROSOFT
(Microsoft Visual Basic 6.0. Мастерская разработчика, издание пятое -прилагаемый к книге компакт-диск)
ЭТО ВАЖНО ■ ПРОЧИТАЙТЕ ВНИМАТЕЛЬНО. Настоящее лицензионное соглашение (далее «Соглашение») является юридическим документом, оно заключается между Вами (физическим или юридическим лицом) и Microsoft Corporation (далее «корпорация Microsoft") на поименованный выше продукт Microsoft, который включает программное обеспечение и может включать сопутствующие мультимедийные и печатные материалы, а также электронную документацию (далее «Программный Продукт»), Любой компонент, входящий в Программный Продукт, который сопровождается отдельным Соглашением, подпадает под действие именно ...„ Соглашения, а не условий, изложенных ниже. Установка, копирование или иное использование данного Программного Продукта означает принятие Вами данного Соглашения. Если Вы не принимаете его условия, то не имеете права устанавливать, копировать или как-то иначе использовать этот Программный Продукт.
ЛИЦЕНЗИЯ НА ПРОГРАММНЫЙ ПРОДУКТ
Программный Продукт защищен законами Соединенных Штатов по авторскому праву и международными договорами по авторскому праву, а также другими законами и договорами по правам на интеллектуальную собственность.
ОБЪЕМ ЛИЦЕНЗИИ. Настоящее Соглашение дает Вам право:
Программный продукт. Вы можете установить и использовать одну копию Программного Продукта на одном компьютере. Основной пользователь компьютера, на котором установлен данный Программный Продукт, может сделать только для себя вторую копию и использовать ее на портативном компьютере.
Хранение или использование в сети. Вы можете также скопировать или установить экземпляр Программного Продукта на устройстве храпения, например на сетевом сервере, исключительно для установки или запуска данного Программного Продукта на других компьютерах в своей внутренней сети, но тогда Вы должны приобрести лицензии на каждый такой компьютер. Лицензию на данный Программный продукт нельзя использовать совместно или одновременно на других компьютерах.
License Рак. Если Вы купили эту лицензию в составе Microsoft License Рак, можете сделать ряд дополнительных копий программного обеспечения, входящего в данный Программный 1гродукт. и использовать каждую копию так. как было описано выше. Кроме того, Вы получаете право сделать соответствующее число вторичных копий для портативного компьютера в целях, также оговоренных выше.
Примеры кода. Это относится исключительно к отдельным частям Программного Продукта, заявленным как примеры кода (далее «Примеры»), если таковые входят в состав Программного Продукта.
Использование и модификация. Microsoft дает Вам право использовать и модифицировать исходный код Примеров при условии соблюдения пункта (dXiii ) ниже. Вы не имеете права распространять в виде исходного кода ни Примеры, ни их модифицированную версию.
) Распространяемые файлы. При соблюдении пункта (d)(iii) Microsoft дает Вам право на свободное от отчислений копирование и распространение в виде объектного кода Примеров или их модифицированной версии, кроме тех частей (или их модифицированных версий; которые оговорены в файле Readme, относящемся кданно- му Программному Продукту, как не подлежащие распространению.
потребованил к распространению файлов. Вы можете распространять файлы, разрешенные к распространению, при условии, что: а) распространяете их в виде объектного кода только в сочетании со своим приложением и как его часть; б) не используете название, эмблему или товарные знаки Microsoft для продвижения своего приложения; в) включаете имеющуюся в Программном Продукте ссылку на авторские права в состав этикетки и заставки своего приложения; г) согласны освободить от ответственности и взять на себя защиту корпорации Microsoft от любых претензий или преследований по закону, включая судебные издержки, если таковые возникнут в результате использования или распространения Вашего приложения; ид) не допускаете дальнейшего распространения конечным пользователем своего приложения. По поводу отчислений и других условий лицензии применительно к иным видам использования или распространения распространяемых файлов обращайтесь в Microsoft.
ПРОЧИЕ ПРАВА И ОГРАНИЧЕНИЯ
Ограничения на реконструкцию,декомпиляцию и дизассемблирование. Вы не имеете права реконструировать, декомпилировать или дизассемблировать данный Программный Продукт, кроме того случая, когда такая деятельность (только в той мере, которая необходима) явно разрешается соответствующим законом, несмотря на :•■ । - ■■ ограничение.
Разделение компонентов. Данный Программный Продукт лицензируется как единый продукт. Его компоненты нельзя отделять друг от друга для использования более чем на одном компьютере.
Аренда. Данный Программный Продукт нельзя сдавать в прокат, передавать во временное пользование или уступать для использования в иных целях.
Услуги по технической поддержке. Microsoft может (но не обязана) предоставить Вам услуги по технической поддержке данного Программного Продукта (далее «Услуги»), Предоставление Услуг регулируется соответствующими правилами и программами Microsoft, описанными в руководстве пользователя, электронной документации и/или других материалах, публикуемых Microsoft. Любой дополнительный программный код, предоставленный в рамках Услуг, следует считать частью данного Программного Продукта и подпадающим под действие настоящего Соглашения. Что касается технической информации. предоставляемой Вами корпорации Microsoft при использовании ее Услуг, то Microsoft может задействовать эту информацию в деловых целях, в том числе для технической поддержки продукта и разработки. Используя такую техническую информацию, Microsoft не будет ссылаться на Вас.
Передача прав на программное обеспечение. Вы можете безвозвратно уступить все права, регулируемые настоящим Соглашением, при условии, что не оставите себе никаких копий, передадите все составные части данного Программного Продукта (включая компоненты, мультимедийные и печатные материалы, любые обновления.
Соглашение и сертификат подлинности, если таковой имеется) и принимающая сторона согласится с условиями настоящего Соглашения.
Прекращение действия Соглашения. Безущербадля любыхдру- гих прав Microsoft может прекратить.действие настоящего Соглашения, если Вы нарушите его условия. В этом случае Вы должны будете уничтожить все копни данного Программного Продукта вместе со всеми его компонентами;
АВТОРСКОЕ ПРАВО. Все авторские права и право собственности на Программный Продукт (в том числе по.Чн.- тп.Ат и-ппа фотографии, анимации, видео, аудио, музыку, текст, примеры кода, распространяемые файлы и :i'mwn. включенные в состав Программного Продукта) и любые его копии принадлежат корпорации Microsoft или ее поставщикам. Программный Продукт охраняется законодательством об авторских правах и положениями международных договоров. Таким образом, Вы должны обращаться с данным Программным Продуктом, каке любым другим материалом, охраняемым авторскими правами, с тем исключением, что Вы можете гстановц| ; Программный Продукт на один компьютер при условии, что храните оригинал исключительно как резервную или архивную копию. Копирование печатных материалов, поставляемых вместе с Программным Продуктом, запрещается.
ОГРАНИЧЕНИЕ ГАРАНТИИ
ДАННЫЙ ПРОГРАММНЫЙ ПРОДУКТ (ВКЛЮЧАЯ ИНСТРУКЦИИ ПО ЕГО ИСПОЛЬЗОВАНИЮ) ПРЕДОСТАВЛЯЕТСЯ БЕЗ КАКОЙ-ЛИБО ГАРАНТИИ. КОРПОРАЦИЯ MICROSOFT СНИМАЕТ С СЕБЯ ЛЮБУЮ ВОЗМОЖНУЮ ОТВЕТСТВЕННОСТЬ, В ТОМ ЧИСЛЕ ОТВЕТСТВЕННОСТЬ ЗА КОММЕРЧЕСКУЮ ЦЕННОСТЬ ИЛИ СООТВЕТСТВИЕ ОПРЕДЕЛЕННЫМ ЦЕЛЯМ. ВЕСЬ РИСК ПО ИСПОЛЬЗОВАНИЮ ИЛИ РАБОТЕ С ПРОГРАММНЫМ ПРОДУКТОМ ЛОЖИТСЯ НА ВАС.
НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ КОРПОРАЦИЯ MICROSOFT, ЕЕ РАЗРАБОТЧИКИ. А ТАКЖЕ ВСЕ. ЗАНЯТЫЕ В СОЗДАНИИ, ПРОИЗВОДСТВЕ И РАСПРОСТРАНЕНИИ ДАННОГО ПРОГРАММНОГО ПРОДУКТА, НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ЗА КАКОЙ-ЛИБО УЩЕРБ (ВКЛЮЧАЯ ВСЕ. БЕЗ ИСКЛЮЧЕНИЯ, СЛУЧАИ УПУЩЕННОЙ ВЫГОДЫ, НАРУШЕНИЯ ХОЗЯЙСТВЕННОЙ ДЕЯТЕЛЬНОСТИ, ПОТЕРИ ИНФОРМАЦИИ ИЛИ ДРУГИХ УБЫТКОВ) ВСЛЕДСТВИЕ ИСПОЛЬЗОВАНИЯ ИЛИ НЕВОЗМОЖНОСТИ ИСПОЛЬЗОВАНИЯ ДАННОГО ПРОГРАММНОГО ПРОДУКТА ИЛИ ДОКУМЕНТАЦИИ. ДАЖЕ ЕСЛИ КОРПОРАЦИЯ MICROSOFT БЫЛА ИЗВЕЩЕНА О ВОЗМОЖНОСТИ ТАКИХ ПОТЕРЬ, ТАК КАК В НЕКОТОРЫХ СТРАНАХ НЕ РАЗРЕШЕНО ИСКЛЮЧЕНИЕ ИЛИ ОГРАНИЧЕНИЕ - i i.li <. i ш : аю- СГИ ЗА НЕПРЕДНАМЕРЕННЫЙ УЩЕРБ, УКАЗАННОЕ ОГРАНИЧЕНИЕ МОЖЕТ ВАС НЕ КОСНУТЬСЯ.
РАЗНОЕ
Настоящее Соглашение регулируется законодательством штата Вашингтон (США), кроме случаев (и лишь в той мере, насколько это необходимо), исключительной юрисдикции того государства, на территории которого используется Программный Продукт.
Если у Вас возникли какие-либо вопросы, касающиеся настоящего Соглашения, или если Вы желаете связаться с Microsoft по любой другой причине, пожалуйста, обращайтесь в местное представительство Microsoft или пишите по адресу: Microsoft Sales Information Center, One Microsoft Way, Redmond, WA 98052-6399-