Я:
Результат
Архив

МЕТА - Украина. Рейтинг сайтов Webalta Уровень доверия



Союз образовательных сайтов
Главная / Учебники / Учебники на русском языке / Компьютерные науки / Borland Pascal 7.0 & Objects - Руководство по ObjectWindows


Компьютерные науки - Учебники на русском языке - Скачать бесплатно


Автор неизвестен
Borland Pascal 7.0 & Objects - Руководство по ObjectWindows
------------------------------------------------------------------------
B.Pascal 7 & Objects/OW - 1 -

Введение.......................................................25
Что такое ObjectWindows?.......................................25
Для чего предназначена ObjectWindows?..........................26
Что нужно знать................................................26
Как работать с данным руководством.............................27
О чем рассказывается в данном руководстве......................27
Часть 1. Изучение ObjectWindows................................28
Глава 1. Знакомство с Windows..................................28
Шаг 1: Создание базового приложения............................28
Требования к приложению........................................29
Определение типа приложения....................................30
Инициализация основного окна...................................30
Объект основного окна..........................................32
Что такое объект окна?.........................................32
Описатели......................................................33
Порождающие и дочерние окна....................................33
Создание нового типа окна......................................33
Реакция на сообщения...........................................34
Завершение прикладной программы................................36
Переопределение CanClose.......................................37
Дальнейшее изменение закрытия..................................38
Глава 2. Заполнение окна.......................................41
Шаг 2: Отображение текста в окне...............................41
Вывод в контексте дисплея......................................41
Что такое контекст дисплея?....................................42
Получение контекста дисплея....................................42
Использование контекста дисплея................................43
Освобождение контекста дисплея.................................43
Координаты Windows.............................................44
Параметры сообщений............................................44
Очистка окна...................................................45
Шаг 3: Изображение линий в окне................................47
Буксировка линии...............................................48
Сообщения wm_MouseMove.........................................48
Реакция на сообщения буксировки................................49
Изображение точек и линий......................................50
Перехват "мыши"................................................50
Изменение размера пера.........................................51
Отслеживание размера пера......................................51
Получение пера нового размера..................................52
Глава 3. Меню и диалоговые ресурсы.............................56
Шаг 4: Добавление строки меню..................................56
Ресурсы меню...................................................57
Загрузка ресурса меню..........................................60
Перехват сообщений меню........................................61
Определение методов реакции на команду.........................62
Связывание клавиш с командами..................................62
Реакция на команды меню........................................63
Добавление диалогового блока...................................64
Добавление поля объекта........................................66
Шаг 5: Добавление диалогового блока............................68
Создание ресурсов диалогового блока............................69
Идентификаторы управляющих элементов...........................69

B.Pascal 7 & Objects/OW - 2 -

Построение объекта диалогового блока...........................70
Выполнение диалогового блока...................................70
Режимные и безрежимные диалоговые блоки........................72
Глава 4. Работа с диалоговым блоком............................73
Шаг 6: Изменение атрибутов пера................................73
Создание объекта пера..........................................74
Создание сложного диалогового блока............................76
Управляющие объекты............................................77
Использование интерфейсных объектов............................78
Конструктор InitResource.......................................79
Создание буфера передачи.......................................79
Передача данных................................................81
Чтение возвращаемых значений...................................81
Вызов диалогового блока пера...................................82
Глава 5. Повторное отображение графики.........................83
Шаг 7: Вывод на экран графики..................................83
Изображение и рисование........................................83
Сохранение графики в объектах..................................84
Добавление поля объекта........................................85
Определение объекта линии......................................85
Изменение методов работы с "мышью".............................87
Вывод сохраненной графики......................................88
Шаг 8: Сохранение рисунка в файле..............................89
Отслеживание состояния.........................................89
Сохранение и загрузка файлов...................................91
Шаг 9: Печать графического образа..............................94
Построение объекта принтера....................................94
Создание объекта распечатки....................................95
Запись в контекст устройства...................................95
Создание распечатки окна.......................................95
Вывод распечатки...............................................96
Выбор другого принтера.........................................96
Глава 6. Вывод всплывающего окна...............................98
Шаг 10: Добавление всплывающего окна...........................98
Добавление к окну дочернего окна...............................99
Построение окна палитры.......................................100
Назначение порождающего окна..................................100
Создание элементов экрана.....................................102
Вывод и сокрытие палитры......................................103
Шаг 11: добавление специализированных управляющих элементов...103
Добавление к палитре командных кнопок.........................104
Объекты управляющих элементов как поля........................105
Работа с управляющими элементами..............................105
Сокрытие вместо закрытия......................................106
Разрешение специализированных управляющих элементов...........107
Создание для командных кнопок графических изображений.........109
Нумерация ресурсов графических изображений....................109
Шаг 12: Создание специализированного управляющего элемента
окна.........................................................111
Динамическое изменение размеров палитры.......................111
Реакция на события управляющих элементов......................112
Имена методов реакции на сообщения управляющих элементов......112
Добавление "кнопок" палитры...................................114

B.Pascal 7 & Objects/OW - 3 -

Определение объекта палитры...................................114
Создание и уничтожение палитры................................116
Размещение в порождающем окне.................................117
Добавление и удаление перьев..................................118
Отображение содержимого палитры...............................120
Выбор перьев с помощью "мыши".................................121
Что дальше?...................................................121
Многодокументальный интерфейс.................................122
Сглаживание линий.............................................122
Отмена........................................................122
Поведение палитры.............................................123
Прокрутка.....................................................123
Часть 2. Использование ObjectWindows..........................124
Глава 7. Иерархия ObjectWindows...............................124
Соглашения Windows............................................124
Имена объектов................................................124
Имена методов.................................................124
Обзор объектов................................................125
Иерархия объектов.............................................125
Файлы ObjectWindows...........................................130
Файлы ресурсов................................................131
Файлы Windows 3.1.............................................131
Взаимодействие с Windows......................................132
Функции API Windows...........................................132
Вызов в ObjectWindows функций API.............................132
Доступ к функциям API.........................................133
Константы Windows.............................................133
Записи данных Windows.........................................133
Комбинирование констант стилей................................134
Типы функций Windows..........................................134
Функции системного вызова.....................................135
Глава 8. Объекты приложения...................................136
Минимальные требования........................................136
Поиск объекта приложения......................................136
Минимальное приложение........................................137
Методы Init, Run и Done.......................................137
Конструктор Init..............................................137
Метод Run.....................................................138
Деструктор Done...............................................138
Инициализация приложения......................................138
Инициализация основного окна..................................139
Специальный вывод основного окна..............................140
Инициализация первого экземпляра..............................140
Инициализация каждого экземпляра..............................142
Выполнение приложений.........................................143
Закрытие приложений...........................................143
Модификация поведения при закрытии............................143
Глава 9. Интерфейсные объекты.................................145
Для чего нужны интерфейсные объекты?..........................145
Что делают интерфейсные объекты?..............................145
Общий интерфейсный объект.....................................146
Создание интерфейсных объектов................................146
Допустимость описателя окна...................................147

B.Pascal 7 & Objects/OW - 4 -

Видимость на экране...........................................148
Уничтожение интерфейсных объектов.............................148
Связь порождающего и дочернего объектов.......................149
Список дочерних окон..........................................149
Построение дочерних окон......................................150
Создание дочерних элементов экрана............................150
Уничтожение дочерних окон.....................................150
Запрещение автоматического создания...........................151
Итерация дочерних окон........................................151
Поиск определенного дочернего окна............................151
Глава 10. Объекты окон........................................153
Что такое объекты окон?.......................................153
Окна, которые не являются окнами..............................153
Где найти объекты окон........................................154
Инициализация объектов окон...................................154
Установка атрибутов создания..................................155
Используемые по умолчанию атрибуты окна.......................156
Переопределение используемых по умолчанию атрибутов...........157
Атрибуты порожденного окна....................................157
Создание элементов окна.......................................158
Задание атрибутов регистрации.................................159
Классы окон...................................................160
Используемые по умолчанию атрибуты регистрации................162
Регистрация нового класса.....................................162
Изменение имени класса........................................163
Определение новых атрибутов регистрации.......................163
Использование специализированных окон.........................165
Использование окон редактирования.............................165
Использование файловых окон...................................167
Прокрутка содержимого окон....................................168
Что такое объект прокрутки?...................................169
Задание для окна объекта прокрутки............................171
Пример прокрутки..............................................171
Запрещение автоматической прокрутки...........................173
Отслеживание полос прокрутки..................................173
Модификация единиц прокрутки и диапазона......................174
Изменение позиции прокрутки...................................174
Установка размеров страницы...................................175
Оптимизация методов Paint для прокрутки.......................175
Глава 11. Объекты диалоговых блоков...........................177
Использование объектов диалоговых блоков......................177
Построение объекта............................................178
Вызов конструктора............................................178
Выполнение диалоговых блоков..................................178
Режимные и безрежимные диалоговые блоки.......................179
Выполнения режимных диалоговых блоков.........................179
Выполнение безрежимных диалоговых блоков......................180
Работа с безрежимными диалоговыми блоками.....................180
Завершение диалогов...........................................181
Работа с управляющими элементами..............................182
Взаимодействие с управляющим элементом........................182
Реакция на сообщения управляющих элементов....................183
Пример связи..................................................183

B.Pascal 7 & Objects/OW - 5 -

Ассоциирование объектов управляющих элементов.................184
Использование диалоговых окон.................................185
Использование предопределенных диалоговых окон................186
Использование диалоговых блоков ввода.........................186
Файловые диалоговые блоки.....................................188
Инициализация файлового диалогового блока.....................189
Выполнение файловых диалоговых блоков.........................189
Глава 12. Объекты управляющих элементов.......................191
Где можно использовать объекты управляющих элементов?.........191
Что такое объекты управляющих элементов?......................194
Построение и уничтожение объектов управляющих элементов.......194
Построение объекта управляющего элемента......................195
Вызов конструкторов объектов управляющих элементов............195
Присваивание полям объекта....................................195
Изменение атрибутов объекта управляющего элемента.............197
Инициализация управляющего элемента...........................197
Сохранение управляющих элементов..............................198
Уничтожение управляющих элементов.............................198
Связь с управляющими элементами...............................198
Работа с управляющими элементами окна.........................198
Реакция на управляющие элементы...............................199
Действие, аналогичное диалоговому блоку.......................199
Использование конкретных управляющих элементов................199
Использование блока списка....................................199
Построение объектов блока списка..............................200
Модификация блоков списка.....................................200
Запрос в блоках списка........................................201
Реакция на блок списка........................................201
Пример программы: LBoxTest....................................202
Использование статических управляющих элементов...............203
Построение статических управляющих элементов..................204
Пример программы StatTest.....................................205
Использование командных кнопок................................206
Построение командных кнопок...................................207
Реакция на командные кнопки...................................207
Использование блоков выбора...................................208
Построение кнопок с зависимой и независимой фиксацией.........208
Модификация блоков выбора.....................................210
Опрос блоков выбора...........................................210
Использование групповых блоков................................210
Построение групповых блоков...................................211
Группирование управляющих элементов...........................211
Реакция на групповые блоки....................................213
Пример программы: BtnTest.....................................213
Использование полос прокрутки.................................214
Построение полос прокрутки....................................214
Управление диапазоном полосы прокрутки........................215
Управление параметрами полосы прокрутки.......................216
Опрос полосы прокрутки........................................216
Модификация полос прокрутки...................................216
Реакция на полосы прокрутки...................................217
Пример программы: SBarTest....................................218
Использование управляющих элементов редактирования............218

B.Pascal 7 & Objects/OW - 6 -

Построение управляющих элементов редактирования...............220
Использование буфера вырезанного изображения и меню Edit......220
Опрос управляющих элементов редактирования....................222
Модификация управляющих элементов редактирования..............223
Пример программы: EditTest....................................224
Использование комбинированных блоков..........................224
Три типа комбинированных блоков...............................225
Выбор типа комбинированного блока.............................226
Построение комбинированных блоков.............................226
Модификация комбинированных блоков............................227
Пример программы: CBoxTest....................................227
Установка значений управляющих элементов......................227
Для чего используется буфер передачи?.........................228
Определение буфера передачи...................................228
Определение окна..............................................231
Использование буфера передачи с диалоговым блоком.............231
Использование буфера передачи с окном.........................233
Передача данных...............................................233
Передача данных в окно........................................233
Передача данных из диалогового окна...........................233
Передача данных из окна.......................................234
Поддержка передачи для специализированных управляющих
элементов....................................................234
Пример программы: TranTest....................................234
Использование специализированных управляющих элементов........236
Специализированные управляющие элементы Borland для Windows...236
Использование стандартных BWCC................................237
Средства BWCC.................................................237
Расширение BWCC...............................................238
Создание ваших собственных специализированных управляющих
элементов....................................................240
Глава 13. Проверка допустимости данных........................241
Три вида проверки допустимости данных.........................241
Фильтрация ввода..............................................242
Проверка допустимости каждого поля............................242
Проверка допустимости полных экранов..........................242
Использование механизма проверки допустимости данных..........243
Построение объектов проверки допустимости.....................244
Добавление к управляющим элементам............................244
Как работает проверка допустимости............................244
Методы объекта проверки допустимости..........................245
Проверка допустимости данных..................................245
Проверка полной строки........................................246
Проверка допустимости нажатий клавиш..........................246
Сообщение о недопустимых данных...............................246
Стандартные средства проверки допустимости....................248
Абстрактный объект проверки допустимости......................248
Фильтрация....................................................248
Проверка диапазона............................................249
Проверка допустимости с просмотром............................249
Просмотр строк................................................249
Проверка допустимости по шаблону..............................250
Глава 14. Объекты MDI.........................................251

B.Pascal 7 & Objects/OW - 7 -

Меню дочернего окна...........................................252
Дочерние окна MDI.............................................252
Окна MDI в ObjectWindows......................................252
Построение приложения MDI.....................................252
Построение рамки MDI..........................................253
Создание меню дочерних окон...................................253
Создание дочерних окон MDI....................................254
Автоматические дочерние окна..................................254
Управление дочерним окном MDI.................................255
Настройка активизации дочернего окна..........................256
Обработка сообщений в приложении MDI..........................256
Пример приложения MDI.........................................256
Глава 15. Объекты печати......................................257
Почему печать представляет трудности?.........................257
Печать в ObjectWindows........................................257
Построение объекта принтера...................................258
Создание распечатки...........................................259
Печать документа..............................................259
Задание параметров печати.....................................260
Подсчет страниц...............................................260
Печать каждой страницы........................................261
Указание оставшихся страниц...................................263
Другие соглашения по печати...................................263
Печать содержимое окна........................................264
Вывод распечатки на принтер...................................265
Выбор другого принтера........................................265
Выбор принтера пользователем..................................266
Назначение конкретного принтера...............................266
Часть 3. Продвинутое программирование с использование
ObjectWindows................................................267
Глава 16. Сообщения Windows...................................267
Что такое сообщение?..........................................267
Именующие сообщения...........................................268
Откуда поступают сообщения....................................268
Обычная диспетчеризация сообщений.............................269
Способ, предлагаемый ObjectWindows............................269
Динамические виртуальные методы...............................270
Написание методов реакции на сообщение........................270
Что такое сообщение?..........................................271
Поля параметров...............................................271
Поле Result...................................................272
Объектно-ориентированная обработка сообщения..................272
Отмена поведения по умолчанию.................................272
Замена поведения по умолчанию.................................273
Дополнение поведения по умолчанию.............................273
Вызов наследуемых методов.....................................273
Вызов процедур, используемых по умолчанию.....................274
Командные, уведомляющие и управляющие идентификаторы..........275
Командные сообщения...........................................275
Уведомляющие сообщения........................................276
Уведомления управляющих элементов.............................276
Уведомление порождающего объекта..............................277
Уведомления управляющих элементов и порождающих объектов......278

B.Pascal 7 & Objects/OW - 8 -

Определение ваших собственных сообщений.......................278
Передача сообщений............................................279
Передача и отправление сообщений..............................279
Передача сообщения............................................280
Отправление сообщения.........................................280
Передача сообщения управляющему элементу......................280
Диапазоны сообщений...........................................282
Глава 17. Интерфейс с графическими устройствами...............284
Запись на устройство вывода...................................284
Чем отличаются контексты устройства?..........................285
Управление контекстом дисплея.................................285
Работа с контекстом дисплея...................................286
Что содержится в контексте устройства?........................286
Побитовая графика.............................................287
Изобразительные средства......................................287
Цвет..........................................................289
Режимы отображения............................................289
Обрезание областей............................................289
Инструментальные средства рисования...........................290
Основные инструментальные средства............................290
Логические инструментальные средства..........................291
Логические перья..............................................291
Логические кисти..............................................293
Логические шрифты.............................................294
Использование изобразительных инструментальных средств........298
Отображение графики в окнах...................................300
Изображение окон..............................................300
Стратегия графики.............................................301
Рисование в окнах.............................................301
Графические функции GDI.......................................302
Функции изображения текста....................................302
Функции рисования линий.......................................303
Изображение фигур.............................................305
Использование палитр..........................................307
Установка палитры.............................................307
Рисование с палитрами.........................................308
Запрос палитры................................................308
Модификация палитры...........................................309
Реакция на изменения палитры..................................309
Глава 18. Более подробно о ресурсах...........................311
Создание ресурсов.............................................311
Добавление ресурсов к выполняемой программе...................312
Загрузка ресурсов в приложение................................312
Загрузка меню.................................................312
Загрузка оперативных клавиш...................................313
Загрузка блоков диалога.......................................314
Загрузка курсоров и пиктограмм................................315
Загрузка строковых ресурсов...................................315
Загрузка графических изображений..............................317
Использование побитовых отображений для создания кистей.......319
Отображение графических изображений в меню....................320
Глава 19. Наборы..............................................322
Объекты наборов...............................................322

B.Pascal 7 & Objects/OW - 9 -

Динамическая установка размеров наборов.......................322
Полиморфизм наборов...........................................323
Проверка типа и наборы........................................323
Объединение в набор элементов, не являющихся объектами........324
Создание набора...............................................324
Методы итератора..............................................326
Итератор ForEach..............................................326
Итераторы FirstThat и LastThat................................327
Отсортированные наборы........................................328
Наборы строк..................................................329
Пересмотренные итераторы......................................331
Полиморфические наборы........................................331
Наборы и управление памятью...................................335
Глава 20. Потоки..............................................336
Вопрос: объектный ввод-вывод..................................336
Ответ: потоки.................................................337
Полиморфизм потоков...........................................337
Потоки обрабатывают объекты...................................337
Смысл использования потоков...................................338
Чтение из потока и запись в поток.............................339
Закрытие потока...............................................340
Как сделать объекты потоковыми................................340
Методы загрузки и хранения....................................340
Регистрация потока............................................341
Номера идентификаторов объектов...............................342
Автоматические поля...........................................342
Регистрация на месте..........................................343
Регистрация стандартных объектов..............................343
Механизм потока...............................................343
Процесс Put...................................................343
Процесс Get...................................................344
Обработка указателей объектов со значением nil................344
Наборы в потоке: пример.......................................344
Добавление методов Store......................................345
Регистрация записей...........................................346
Регистрация...................................................347
Запись в поток................................................347
Как все хранится?.............................................348
Поля в потоке.................................................348
Родство экземпляров окон......................................349
Копирование потока............................................350
Потоки произвольного доступа..................................350
Необъектные элементы потоков..................................351
Разработка пользователем собственных потоков..................351
Обработка ошибок потока.......................................352
Часть 4. Справочник по ObjectWindows..........................353
Глава 21. Объектные типы ObjectWindows........................353
TSample модуль
TSample......................................................354
Поля..........................................................354
Методы........................................................354
Init..........................................................355
Zilch (иногда переопределяется)...............................355

B.Pascal 7 & Objects/OW - 10 -

Процедура Sample (модуль
Sample)......................................................355
Процедура Abstract модуль
Objects......................................................355
Функция AllocMultiSel модуль
ODialogs.....................................................355
Переменная Application модуль
OWindows.....................................................356
Константы bf_XXXX модуль
ODialogs.....................................................356
Стили кнопок bs_XXXX модуль
WinTypes.....................................................357
Переменная BWCCClassNames модуль
OWindows.....................................................358
Стили комбинированного блока cbs_XXXX модуль
WinTypes.....................................................359
Константы cm_XXXX модуль
OWindows.....................................................361
Константы coXXXX модуль
Objects......................................................363
Стили класса cs_XXXX модуль
WinTypes.....................................................364
Константа cw_UseDefault модуль
WinTypes.....................................................365
Процедура DoneMemory модуль
OMemory......................................................365
Константы em_XXXX модуль
OWindows.....................................................365
Переменная EmsCurHandle модуль
Objects......................................................365
Переменная EmsCurPage модуль
Objects......................................................366
Стили управляющих элементов es_XXXX модуль
WinTypes.....................................................366
Процедура FreeMultiSel модуль
ODialogs.....................................................368
Константа tsFileSpec модуль
OStdDlgs.....................................................368
Константы id_XXXX модуль
OWindows.....................................................369
Процедура InitMemory модуль
OMemory......................................................370
Стили блока списка lbs_XXXX модуль
WinTypes.....................................................370
Функция LongDiv модуль
OWindows.....................................................372
Функция LongMul модуль
OWindows.....................................................373
Тип LongRec модуль
Objects......................................................373
Функция LoMemory модуль
OMemory......................................................373
Тип MakeIntResource модуль

B.Pascal 7 & Objects/OW - 11 -

WinTypes.....................................................373
Переменная MaxCollectionSize модуль
Objects......................................................374
Флаги блоков mb_XXXX модуль
WinTypes.....................................................375
Функция MemAlloc модуль
OMemory......................................................376
Функция MemAllocSeg модуль
OMemory......................................................376
Константы nf_XXXX модуль
OWindows.....................................................377
Константы pf_XXX модуль
OPrinter.....................................................378
Тип PString модуль
Objects......................................................379
Тип PtrRec модуль
Objects......................................................379
Процедура RegisterODialogs модуль
ODialogs.....................................................379
Процедура RegisterOStdWnds модуль
OSrdWnds.....................................................379
Процедура RegisterOWindows модуль
OWindows.....................................................380
Процедура RegisterType модуль
Objects......................................................380
Процедура RegisterValidate модуль
Validate.....................................................380
Процедура RestoreMemory модуль
OMemory......................................................380
Переменная SafetyPoolSize модуль
OMemory......................................................381
Стили полосы прокрутки sbs_XXXX модуль
WinTypes.....................................................382
Константы sd_XXXX модуль
OStdDlgs.....................................................384
Стили управляющего элемента ss_XXXX модуль
WinTypes.....................................................385
Пpедопpеделенные логические объекты модуль
WinTypes.....................................................386
Переменная StreamError модуль
Objects......................................................387
Константы stXXX модуль
Objects......................................................388
Константы отображения окна sw_XXX модуль
WinTypes.....................................................389
TApplication модуль
OWindows.....................................................391
Поля..........................................................391
Методы........................................................392
Init (иногда переопределяется)................................392
Done (иногда переопределяется)................................392
CanClose (переопределяется редко).............................393
Error (часто переопределяется)................................393

B.Pascal 7 & Objects/OW - 12 -

ExecDialog (никогда не переопределяется)......................393
ExecDialog (никогда не переопределяется)......................393
IdleAction....................................................394
InitApplication (иногда переопределяется).....................394
InitInstance (иногда переопределяется)........................394
InitMainWindow (всегда переопределяется)......................395
MakeWindow (никогда не переопределяется)......................395
MessageLooр (никогда не переопределяется).....................395
рrocessAccels (иногда переопределяется).......................396
рrocessAppMsg (иногда переопределяется).......................396
рrocessDlgMsg (иногда переопределяется).......................396
рrocessDMIAccels (иногда переопределяется)....................396
Функция Run (переопределяется редко)..........................398
SetKBHandler (никогда не переопределяется)....................398
TBufStream модуль
Objects......................................................399
Поля..........................................................399
Методы........................................................400
Init..........................................................400
Done (никогда не переопределяется)............................400
Flush (никогда не переопределяется)...........................400
Getрos (никогда не переопределяется)..........................401
GetSize (никогда не переопределяется).........................401
Read (никогда не переопределяется)............................401
Seek (никогда не переопределяется)............................401
Truncate (никогда не переопределяется)........................401
Write (никогда не переопределяется)...........................402
TButton модуль
ODialogs.....................................................403
Методы........................................................405
Init..........................................................405
InitResource..................................................405
GetClassName (никогда не переопределяется)....................405
Тип TByteArray модуль
Objects......................................................405
TCheckBox модуль
ODialogs.....................................................407
Поля..........................................................408
Методы........................................................409
Init (иногда переопределяется)................................409
InitResource..................................................409
Load..........................................................409
BNClicked (иногда переопределяется)...........................410
Check (переопределяется редко)................................410
GetCheck (переопределяется редко).............................410
GetClassName..................................................410
SetCheck (переопределяется редко).............................410
Store.........................................................411
Toggle (переопределяется редко)...............................411
Transfer (иногда переопределяется)............................411
UnСheck (переопределяется редко)..............................411
TCollection модуль
Objects......................................................413

B.Pascal 7 & Objects/OW - 13 -

Поля..........................................................413
Методы........................................................414
Init..........................................................414
Load..........................................................414
Done (часто переопределяется).................................414
At............................................................415
AtDelete......................................................415
AtFree........................................................415
AtInsert......................................................415
Atрut.........................................................416
Delete........................................................416
DeleteAll.....................................................416
Error (иногда переопределяется)...............................416
FirstThat.....................................................416
ForEach.......................................................417
Free..........................................................417
FreeAll.......................................................418
FreeItem (иногда переопределяется)............................418
GetItem (иногда переопределяется).............................418
IndexOf (никогда не переопределяется).........................418
Insert (никогда не переопределяется)..........................419
Insert (никогда не переопределяется)..........................419
рutItem (иногда переопределяется).............................419
SetLimit (переопределяется редко).............................420
Store.........................................................420
TComboBox модуль
ODialogs.....................................................421
Поля..........................................................423
Методы........................................................423
Init (иногда переопределяется)................................423
InitResource..................................................423
Load..........................................................424
Clear.........................................................424
GetClassName (никогда не переопределяется)....................424
GetEditSel....................................................424
GetText.......................................................424
GetTextLen....................................................425
HideList......................................................425
SetEditSel....................................................425
SetText.......................................................425
SetuрWindow...................................................425
ShowList......................................................425
Store.........................................................426
Transfer......................................................426
TControl модуль
ODialogs.....................................................427
Методы........................................................428
Init..........................................................428
InitResource..................................................429
GetGlassName (всегда переопределяется)........................429
Register (никогда не переопределяется)........................429
WMрaint (переопределяется редко)..............................429
TDialog модуль

B.Pascal 7 & Objects/OW - 14 -

ODialog......................................................430
Поля..........................................................431
IsModal.......................................................431
Методы........................................................432
Init (иногда переопеределяется)...............................432
Load..........................................................432
Done (иногда переопределяется)................................432
Cancel (иногда переопределяется)..............................432
Create (никогда не переопределяется)..........................433
DefWndрroc (никогда не переопределяется)......................433
EndDlg (никогда не переопределяется)..........................433
Execute (никогда не переопределяется).........................433
GetItemHandle (никогда не переопределяется)...................434
Ok (иногда переопределяется)..................................434
SendDlgItemMsg (никогда не переопределяется)..................434
Store.........................................................434
WMClose.......................................................434
WMInitDialog (никогда не переопределяется)....................435
WMрostInvalid.................................................435
WMQueryEndSession.............................................435
Тип TDialogAttr модуль
ODialogs.....................................................435
TDlgWindow модуль
ODialogs.....................................................437
Методы........................................................438
Init..........................................................438
Create (никогда не переопределяется)..........................439
GetWindowClass (часто переопределяется).......................439
TDosStream модуль
Objects......................................................440
Поля..........................................................440
Методы........................................................440
Init..........................................................440
Done (никогда не переопределяется)............................441
Getрos (никогда не переопределяется)..........................441
GetSize (никогда не переопределяется).........................441
Read (никогда не переопределяется)............................441
Seek (никогда не переопределяется)............................441
Truncate (никогда не переопределяется)........................442
Write (никогда не переопределяется)...........................442
TEdit модуль
ODialogs.....................................................443
Поля..........................................................445
Методы........................................................445
Init..........................................................445
InitResource..................................................446
Load..........................................................446
Done..........................................................446
CanClose......................................................447
CanUndo (переопределяется редко)..............................447
ClearModify (переопределяется редко)..........................447
CMEditClear (никогда не переопределяется).....................447
CMEditCoрy (никогда не переопределяется)......................447

B.Pascal 7 & Objects/OW - 15 -

CMEditCut (никогда не переопределяется).......................448
CMEditDelete (никогда не переопределяется)....................448
CMEditрaste (никогда не переопределяется).....................448
CMEditUndo (никогда не переопределяется)......................448
Coрy (переопределяется редко).................................449
Cut (переопределяется редко)..................................449
DeleteLine (переопределяется редко)...........................449
DeleteSelection (переопределяется редко)......................449
DeleteSubText (переопределяется редко)........................449
GetClassName (никогда не переопределяется)....................449
GetLine (переопределяется редко)..............................450
GetLineFromрos (переопределяется редко).......................450
GetLineIndex (переопределяется редко).........................450
GetLineLength (переопределяется редко)........................450
GetNumLines (переопределяется редко)..........................451
GetSelection (переопределяется редко).........................451
GetSubText (переопределяется редко)...........................451
Insert (переопределяется редко)...............................451
IsModified (переопределяется редко)...........................452
IsValid.......................................................452
рaste (переопределяется редко)................................452
Scroll (переопределяется редко)...............................452
Search........................................................452
SetSelection (переопределяется редко).........................453
SetuрWindow...................................................453
SetValidator..................................................453
Store.........................................................453
Transfer (иногда переопределяется)............................454
Undo (переопределяется редко).................................454
WMChar........................................................454
WMGetDlgCode..................................................454
WMKeyDown.....................................................455
WMKillFocus...................................................455
TEditPrintout
OPrinter.....................................................456
Поля..........................................................456
Методы........................................................457
Init..........................................................457
BeginDocument.................................................458
GetDialogInfo.................................................458
HasHextPage...................................................458
PrintPage.....................................................458
SetPrintParams................................................459
TEditWindow модуль
OStdWnds.....................................................460
TEmsStream модуль
Objects......................................................462
Поля..........................................................462
Методы........................................................463
Init..........................................................463
Done (никогда не переопределяется)............................463
GetPos (никогда не переопределяется)..........................463
GetSize (никогда не переопределяется).........................463

B.Pascal 7 & Objects/OW - 16 -

Read (никогда не переопределяется)............................463
Seek (никогда не переопределяется)............................464
Truncate (никогда не переопределяется)........................464
Write (никогда не переопределяется)...........................464
Константы tf_XXXX модуль
OWindows.....................................................464
TFileDialog модуль
OStdDlgs.....................................................466
Поля..........................................................467
Методы........................................................468
Init..........................................................468
CanClose......................................................468
SetupWindow...................................................468
HandleFName...................................................469
HandleFList...................................................469
HandleDList...................................................469
TFileWindow
OStdWnds.....................................................470
TFilterValidator модуль
Validate.....................................................472
Поля..........................................................472
Методы........................................................472
Init..........................................................472
Load..........................................................472
Error.........................................................473
IsValid.......................................................473
IsValidInput..................................................473
Store.........................................................473
TGroupBox
ODialogs.....................................................474
Поля..........................................................475
Методы........................................................476
Init (иногда переопределяется)................................476
InitResource..................................................476
Load..........................................................476
GetClassName (иногда переопределяется)........................476
SelectionChanged (иногда переопределяется)....................477
Store.........................................................477
TInputDialog модуль
OStdDlgs.....................................................478
Поля..........................................................479
Методы........................................................480
Init..........................................................480
CanClose......................................................480
SetupWindow...................................................480
Тип TItemList модуль
Objects......................................................480
TListBox модуль
ODialogs.....................................................482
Методы........................................................483
Init..........................................................483
AddString (иногда переопределяется)...........................484
ClearList (иногда переопределяется)...........................484

B.Pascal 7 & Objects/OW - 17 -

DeleteString (иногда переопределяется)........................484
GetClassName (переопределяется редко).........................484
GetCount (никогда не переопределяется)........................484
GetMsgID......................................................485
GetSelIndex (переопределяется редко)..........................485
GetSelString (переопределяется редко).........................485
GetString (переопределяется редко)............................485
GetStringLen (переопределяется редко).........................485
InsertString (иногда переопределяется)........................486
SetSelIndex (переопределяется редко)..........................486
SetSelString (переопределяется редко).........................486
Transfer (иногда переопределяется)............................486
TLookupValidator модуль
Validate.....................................................488
Методы........................................................488
IsValid (переопределяется редко)..............................488
Lookup (часто переопределяется)...............................488
TMDIClient модуль
OWindows.....................................................490
Поля..........................................................491
Методы........................................................492
Init (переопределяется редко).................................492
Load..........................................................492
ArrangeIcons (переопределяется редко).........................492
CascadeChildren (переопределяется редко)......................492
GetClassName (никогда не переопределяется)....................493
Register......................................................493
Store.........................................................493
TileChildren (переопределяется редко).........................493
WMPaint (никогда не переопределяется).........................493
TMDIWindow модуль
OWindows.....................................................494
Поля..........................................................495
Методы........................................................496
Init (часто переопределяется).................................496
Load..........................................................496
Done (иногда переопределяется)................................496
ArrangeIcons (переопределяется редко).........................497
CascadeChildren (переопределяется редко)......................497
CloseChildren (переопределяется редко)........................497
CMArrangeIcons (переопределяется редко).......................497
CMCascadeChildren (переопределяется редко)....................497
CMCreateChild (никогда не переопределяется)...................498
CMTileChildren (переопределяется редко).......................498
CreateChild...................................................498
DefWndProc....................................................498
GetClassName (иногда переопределяется)........................498
GetClient (никогда не переопределяется).......................499
GetWindowClass (иногда переопределяется)......................499
InitChild (часто переопределяется)............................499
InitClientWindow (иногда переопределяется)....................499
SetupWindow (часто переопределяется)..........................499
Store.........................................................500

B.Pascal 7 & Objects/OW - 18 -

TileChildren (переопределяется редко).........................500
Тип TMessage модуль
OWindows.....................................................501
Тип TMultiSelRec модуль
ODialogs.....................................................501
TObject модуль
Objects......................................................502
Методы........................................................502
Init..........................................................502
Free..........................................................502
Done..........................................................502
Тип TPaintStruct модуль
WinTypes.....................................................502
Тип TPicResult модуль
Validate.....................................................503
TPrintDialog модуль
OPrinter.....................................................504
Поля..........................................................505
Методы........................................................507
Init..........................................................507
Тип TPrintDialogRec модуль
OPrinter.....................................................508
TPrinter модуль
OPrinter.....................................................510
Поля..........................................................510
Методы........................................................512
Init..........................................................512
Done (переопределяется редко).................................512
ClearDevice...................................................512
Configure.....................................................512
GetDC (переопределяется редко)................................512
InitAbortDialog (переопределяется редко)......................513
InitPrintDialog (переопределяется редко)......................513
InitSetupDialog (переопределяется редко)......................513
Print.........................................................513
ReportError (иногда переопределяется).........................514
SetDevice.....................................................514
Setup.........................................................514
TPrinterAbortDlg модуль
OPrinter.....................................................515
Методы........................................................516
Init..........................................................516
SetupWindow (переопределяется редко)..........................516
WMCommand (переопределяется редко)............................517
TPrinterSetupDlg модуль
OPrinter.....................................................518
Поля..........................................................519
Методы........................................................519
Init..........................................................519
Done (переопределяется редко).................................520
Cancel (никогда не переопределяется)..........................520
IDSetup (никогда не переопределяется).........................520
TransferData (никогда не переопределяется)....................520

B.Pascal 7 & Objects/OW - 19 -

TPrintout модуль
OPrinter.....................................................521
Поля..........................................................521
Методы........................................................522
Init..........................................................522
Done..........................................................522
BeginPrinting.................................................523
EndDocument...................................................523
EndPrinting...................................................523
GetDialogInfo.................................................523
GetSelection..................................................524
HasNextPage...................................................524
PrintPage.....................................................524
SetPrintParams................................................524
TPXPictureValidator модуль
Validate.....................................................526
Поля..........................................................526
Методы........................................................526
Init..........................................................526
Load..........................................................527
Done..........................................................527
Error.........................................................527
ISValidInput..................................................527
IsInvalid.....................................................527
Picture.......................................................528
Store.........................................................529
TRadioButton модуль
ODialogs.....................................................530
Методы........................................................531
Init (иногда переопределяется)................................531
GetGlassName..................................................532
TRangeValidator модуль
Validate.....................................................533
Поля..........................................................533
Методы........................................................533
Init..........................................................534
Load..........................................................534
Error.........................................................534
IsValid.......................................................534
Store.........................................................535
Transfer......................................................535
TScrollBar модуль
ODialogs.....................................................536
Поля..........................................................538
Методы........................................................538
Init..........................................................538
InitResource..................................................538
Load..........................................................539
DeltaPos (переопределяется редко).............................539
GetClassName (никогда не переопределяется)....................539
GetPosition (переопределяется редко)..........................539
GetRange (переопределяется редко).............................539
SBBottom (переопределяется редко).............................540

B.Pascal 7 & Objects/OW - 20 -

SBLineDown (переопределяется редко)...........................540
SBLineUp (переопределяется редко).............................540
SBPageDown (переопределяется редко)...........................540
SBPageUp (переопределяется редко).............................540
SBThumbPosition (переопределяется редко)......................541
SBThumbTrack (иногда переопределяется)........................541
SBTop (переопределяется редко)................................541
SetPosition (переопределяется редко)..........................541
SetRange (переопределяется редко).............................541
SetupWindow (иногда переопределяется).........................542
Store.........................................................542
Transfer (иногда переопределяется)............................542
TScroller модуль
OWindow......................................................543
Поля..........................................................543
Методы........................................................546
Init..........................................................546
Load..........................................................546
Done..........................................................546
AutoScroll (иногда переопределяется)..........................547
BeginView.....................................................547
EndView (иногда переопределяется).............................547
HScroll (никогда не переопределяется).........................547
IsVisibleRect (переопределяется редко)........................547
ScrollBy (переопределяется редко).............................547
ScrollTo (иногда переопределяется)............................548
SetPageSize (иногда переопределяется).........................548
SetRange (никогда не переопределяется)........................548
SetBarRange (никогда не переопределяется).....................548
SetUnits......................................................548
Store.........................................................548
VScroll (никогда не переопределяется).........................549
TSortedCollection модуль
Objects......................................................550
Поля..........................................................550
Методы........................................................551
Load..........................................................551
Compare (всегда переопределяется).............................551
IndexOf (никогда не переопределяется).........................551
Insert (никогда не переопределяется)..........................552
KeyOf (иногда переопределяется)...............................552
Search (переопределяется редко)...............................552
Store.........................................................553
TStatic модуль
ODialogs.....................................................554
Поля..........................................................555
Методы........................................................555
Init..........................................................556
InitResource..................................................556
Load..........................................................556
Clear (переопределяется редко)................................556
GetClassName (переопределяется редко).........................556
GetText (переопределяется редко)..............................557

B.Pascal 7 & Objects/OW - 21 -

SetText (переопределяется редко)..............................557
Store.........................................................557
Transfer (иногда переопределяется)............................557
TStrCollection модуль
Objects......................................................558
Методы........................................................558
Compare (иногда переопределяется).............................558
FreeItem (переопределяется редко).............................559
GetItem (переопределяется редко)..............................559
PutItem (переопределяется редко)..............................559
TStream метод
Objects......................................................560
Поля..........................................................560
ErrorInfo (чтение/запись).....................................560
Методы........................................................561
CopyFrom......................................................561
Error (иногда переопределяется)...............................561
Flush (иногда переопределяется)...............................561
Get...........................................................562
GetPos (всегда переопределяется)..............................562
GetSize (всегда переопределяется).............................562
Put...........................................................562
Read (всегда переопределяется)................................563
ReadStr.......................................................563
Reset.........................................................563
Seek (всегда переопределяется)................................563
StrRead.......................................................564
Truncate (всегда переопределяется)............................564
Write (всегда переопределяется)...............................564
WriteStr......................................................564
Тип TStreamRec модуль
Objects......................................................565
TStringLookupValidator модуль
Validate.....................................................567
Поля..........................................................567
Методы........................................................567
Init..........................................................567
Load..........................................................568
Done..........................................................568
Error.........................................................568
Lookup........................................................568
NewStringList.................................................568
Store.........................................................569
TValidator модуль
Validate.....................................................570
Поля..........................................................570
Status........................................................570
Методы........................................................570
Init..........................................................570
Load..........................................................571
Error.........................................................571
IsValid.......................................................571
IsValidInput..................................................571

B.Pascal 7 & Objects/OW - 22 -

Store.........................................................572
Transfer......................................................572
Valid.........................................................573
Тип TVTransfer модуль
Validate.....................................................574
Тип TWndClass модуль
WinTypes.....................................................574
TWindow модуль
OWindows.....................................................576
Поля..........................................................577
Методы........................................................578
Init (часто переопределяется).................................578
InitResource..................................................579
Load..........................................................579
Done (часто переопределяется).................................579
Create........................................................579
DefWndProc (никогда не переопределяется)......................580
FocusChild....................................................580
GetID (переопределяется редко)................................580
GetWindowClass (часто переопределяется).......................580
Paint (часто переопределяется)................................580
SetCaption....................................................581
SetupWindow (часто переопределяется)..........................581
Store.........................................................581
UpdateFocusChild..............................................581
WMActivate (иногда переопределяется)..........................582
WMCreate......................................................582
WMHScroll (иногда переопределяется)...........................582
WMLButtonDown (иногда переопределяется).......................582
WMMDIActivate.................................................583
WMMove........................................................583
WMPaint (переопределяется редко)..............................583
WMSize (иногда переопределяется)..............................583
WMSysCommand..................................................583
WMVScroll (иногда переопределяется)...........................584
Тип TWindowAttr модуль
OWindows.....................................................585
TWindowPrintout модуль
OPrinter.....................................................586
Поля..........................................................586
Методы........................................................586
Init..........................................................586
GetDialogInfo.................................................587
PrintPage.....................................................587
TWindowsObject модуль
OWindows.....................................................588
Поля..........................................................588
Методы........................................................590
Init (часто переопределяется).................................590
Load..........................................................590
Done (часто переопределяется).................................590
AddChild......................................................591
At............................................................591

B.Pascal 7 & Objects/OW - 23 -

CanClose (иногда переопределяется)............................591
ChildWithID (никогда не переопределяется).....................591
CloseWindow...................................................591
CMExit........................................................591
Create (никогда не переопределяется)..........................592
CreateChildren................................................592
DefChildProc (иногда переопределяется)........................592
DefCommandProc (иногда переопределяется)......................592
DefNotificationProc (иногда переопределяется).................593
DefWndProc....................................................593
Destroy (никогда не переопределяется).........................593
Disable.......................................................593
DisableAutoCreate.............................................594
DisableTransfer...............................................594
DispatchScroll (никогда не переопределяется)..................594
Enable........................................................594
EnableAutoCreate..............................................594
EnableKBHandler...............................................595
EnableTransfer................................................595
FirstThat.....................................................595
Focus.........................................................596
ForEach.......................................................596
GetChildPtr...................................................596
GetChildren...................................................596
GetClassName (иногда переопределяется)........................597
GetClient (никогда не переопределяется).......................597
GetId (переопределяется редко)................................597
GetSiblingPtr.................................................597
GetWindowClass (иногда переопределяется)......................598
IndexOf.......................................................598
IsFlagSet.....................................................598
Next..........................................................598
Previous......................................................598
PutChildPtr...................................................599
PutChildren...................................................599
PutSiblingPtr.................................................599
Register (никогда не переопределяется)........................599
RemoveChild...................................................600
SetFlags......................................................600
SetupWindow (часто переопределяется)..........................600
Show (никогда не переопределяется)............................600
Store.........................................................601
Transfer (иногда переопределяется)............................601
TransferData (иногда переопределяется)........................601
WMActivate (иногда переопределяется)..........................601
WMClose (иногда переопределяется).............................602
WMCommand (переопределяется редко)............................602
WMDestroy (переопределяется редко)............................602
WMHScroll (переопределяется редко)............................602
WMNCDestroy (никогда не переопределяется).....................602
WMQueryEndSession.............................................603
WMVScroll (переопределяется редко)............................603
Тип TWordArray модуль

B.Pascal 7 & Objects/OW - 24 -

Objects......................................................603
Константы voXXXX модуль
Validate.....................................................604
Константы vsXXXX модуль
Validate.....................................................605
Константы wb_XXXX модуль
OWindows.....................................................606
Константы wm_XXXX модуль
OWindows.....................................................606
Тип WordRec модуль
Objects......................................................606
Стили окна ws_XXXX модуль
WinTypes.....................................................608

B.Pascal 7 & Objects/OW - 25 -

------------------------------------------------------------------
Введение
-----------------------------------------------------------------

В данном руководстве вы найдете полную документацию по
ObjectWindows - объектно-ориентированной прикладной среде для
Microsoft Windows. Здесь описываются не только то, что может де-
лать ObjectWindows и как, но и почему. Вы поймете, что
ObjectWindows - это наиболее быстрый путь построения развитых и
многофункциональных приложений Windows.

Хотя в данном руководстве поясняется, как работает Windows,
и как с ней взаимодействуют ваши прикладные программы, оно не ох-
ватывает все аспекты программирования с использованием приклад-
ного программного интерфейса Windows (API). Эти подробности вы
можете узнать в других книгах о программировании в Windows.

ObjectWindows позволит вам избежать утомительных и трудоем-
ких задач, связанных с разработкой традиционных приложений
Windows. Тем не менее вы будете иметь доступ ко всем имеющимся в
Windows средствам, но иметь дело с Windows вам придется только
когда вы этого захотите. Все остальное делает за вас
ObjectWindows.

Что такое ObjectWindows?
-----------------------------------------------------------------

В иерархию данной версии ObjectWindows включены новые объек-
ты, а также добавлены средства к существующим объектам. Изменения
существующих объектов обладают обратной совместимостью, так что
существующий код ObjectWindows следует компилировать с минималь-
ными изменениями.

В данной версии ObjectWindows вы найдете следующие новые
средства:

* поддержку проверки допустимости данных (Глава 13);

* объекты для печати документов и содержимого окон (Глава
15);

* специализированные управляющие элементы фирмы Borland в
стиле Windows;

* множество модулей.

Кроме того, данное руководство содержит следующие новые ма-
териалы:

* расширенное учебное руководство;

* новую главу по сообщениям Windows (Глава 16);

* реорганизованные главы, посвященные иерархии и объектам;

B.Pascal 7 & Objects/OW - 26 -


* более полную информацию о наследовании в справочной части.

Для чего предназначена ObjectWindows?
-----------------------------------------------------------------

Windows знакомит вас со множеством вещей, которые, возможно,
раньше никогда не приходили вам в голову. Это, например, работа с
текстом и графикой в окнах с изменяемым размером, взаимодействие
с другими программами в многозадачной среде и работа с почти 600
функциями API Windows. Когда вы подумаете, сколько основных шагов
должна выполнять ваша программа при работе в Windows, и что нужно
отследить их все, это может выглядеть обескураживающим.

Чтобы прикладную программу можно было признать приложением
Windows, она должна делать очень многое. Например, она не может
выводить информацию прямо на экран и записывать данные непосредс-
твенно в видеопамять. Кроме того, приложение Windows должно отве-
чать на уведомляющие сообщения, которые посылает своим приложени-
ям Windows в ответ на действия пользователя (события) тип выбора
пункта меню.

Но вам не обязательно делать все это самим. Хорошим началом
для этого послужит ObjectWindows.

ObjectWindows - это объектно-ориентированная библиотека, ин-
капсулирующая большую часть свойств окна и позволяющая вам ис-
пользовать наследование, а не начинать заново с началом каждой
новой программы. Обеспечивая для вас стабильную и жесткую опера-
ционную среду, она позволяет вам сосредоточиться на индивидуаль-
ных фрагментах программы, а не на частях, общих для всех приложе-
ний Windows.

Что нужно знать
-----------------------------------------------------------------

Перед началом программирования для Windows вы должны быть
знакомы с его основами. Во-первых, нужно знать, как использовать
Паскаль и Windows. О программировании на Паскале рассказывается в
"Руководстве пользователя" и "Руководстве по языку", а о работе с
Windows вы можете прочитать в документации, поставляемой с прог-
раммным обеспечением Windows.

Кроме того, для работы с ObjectWindows вы должны владеть
объектно-ориентированным программированием. Приложения, написан-
ные с использованием ObjectWindows, интенсивно используют объект-
но-ориентированные методы, включая наследование и полиморфизм.
Эти темы освещаются в главе по объектно-ориентированному програм-
мированию в "Руководстве пользователя". Кроме объектно-ориентиро-
ванных методов вы должны знать о работе с указателями и динами-
ческими переменными, поскольку почти все экземпляры объектов
ObjectWindows динамически распределяются в динамической области
памяти. Об указателях также рассказывается в "Руководстве пользо-

B.Pascal 7 & Objects/OW - 27 -

вателя".

Как работать с данным руководством
-----------------------------------------------------------------

"Руководство по программированию с использованием
ObjectWindows" расширено, что сделало его более полным и простым
в использовании. Если вы уже знакомы с ObjectWindows, то можете
пропустить главы 7, 13, 15 и 16 и прочитать о новых средствах.
Если вы только начинаете работать с ObjectWindows, то прочитайте
сначала первую часть ("Изучение ObjectWindows"). Эту часть можно
использовать в качестве учебного руководства, в котором описыва-
ется построение полного приложения ObjectWindows и объясняются
принципы ObjectWindows.

О чем рассказывается в данном руководстве
-----------------------------------------------------------------

Так как ObjectWindows представляет собой новый подход в
программировании для Windows и использует некоторые новые методы,
с которыми вы, вероятно, не знакомы, в этом руководстве вы можете
найти поясняющий материал. Полные справочные материалы по
ObjectWindows вы можете найти в Главе 21 "Справочник по
ObjectWindows".

Это руководство разбито на 4 части:

* Часть 1, "Изучение ObjectWindows", знакомит вас с принци-
пами разработки прикладной программы Windows с помощью
ObjectWindows, включая учебные материалы, описывающие про-
цесс написания и дополнения приложения ObjectWindows.

* В Части 2, "Использование ObjectWindows", дается более де-
тальная информация об элементах ObjectWindows, включая об-
зор иерархии и ее взаимодействие с операционной средой
Windows, а также подробно поясняются части иерархии и их
использование.

* В Части 3, "Продвинутое программирование с использование
ObjectWindows" обсуждаются важные темы продвинутого прог-
раммирования в Windows, особенно в части непосредственного
взаимодействия с операционной средой Windows, включая со-
общения, графику и использование ресурсов. Здесь вы найде-
те также главы о наборах и потоковых объектах.

* Часть 4, "Справочник по ObjectWindows", представляет собой
полный справочник по всем объектам и других элементам,
включенным в модули ObjectWindows.



B.Pascal 7 & Objects/OW - 28 -

------------------------------------------------------------------
Часть 1. Изучение ObjectWindows
-----------------------------------------------------------------


Глава 1. Знакомство с Windows
-----------------------------------------------------------------

В следующих нескольких главах мы построим графическую инте-
рактивную программу Windows, дополненную меню, средствами сохра-
нения и загрузки файлов, графикой и отображением текста, а также
специализированными управляющими элементами. По ходу дела вы поз-
накомитесь с основными принципами проектирования программы
Windows, такими как обработка сообщений, управление дочерним и
порождающим окном и автоматическое повторное отображение графики.

Этот процесс разбит на следующие шаги:

- создание базового приложения;
- вывод текста в окне;
- изображение линий в окне;
- добавление строки меню;
- добавление диалогового окна;
- изменение атрибутов пера;
- повторное отображение графики;
- сохранение изображения в файле;
- печать образа;
- добавление всплывающего окна;
- добавление специализированных управляющих элементов;
- создание специализированного управляющего элемента окна.

Исходный код приложения для различных этапов вы можете найти
на дистрибутивных дисках. Описываемым в руководстве шагам соот-
ветствуют файлы STEP01.PAS, STEP02.PAS и так далее (можно найти
также промежуточные программы).

Шаг 1: Создание базового приложения
-----------------------------------------------------------------

------------------------¬
¦-Step-1:-Basic-App-----¦ Базовая программа
¦ Step 2: Text ¦ Текст
¦ Step 3: Lines ¦ Строки
¦ Step 4: Menu ¦ Меню
¦ Step 5: About Box ¦ Об окне
¦ Step 6: Pens ¦ Перья
¦ Step 7: Painting ¦ Рисование
¦ Step 8: Streams ¦ Потоки
¦ Step 9: Printing ¦ Печать
¦ Step 10: Palette ¦ Палитра
¦ Step 11: BWCC ¦ Управляющие элементы окна
¦ Step 12: Custom ctrls ¦ Специализированные элементы
L------------------------


B.Pascal 7 & Objects/OW - 29 -

Отправным пунктом для всех программ, которые вы пишете с
применением ObjectWindows, является программа STEP01A.PAS. Эта
программа, которая называется Steps, создает основное окно прило-
жения.

Все программы ObjectWindows должны использовать модуль
OWindows, которые содержит стандартные объекты, используемые
ObjectWindows для приложений и окон. Большинство приложений вклю-
чают в себя также диалоговые блоки и соответствующие управляющие
элементы. ObjectWindows предусматривает для них объекты в модуле
ODialogs. Объекты, относящиеся к печати, находятся в модуле
OPrinter. Программам, применяющим наборы и потоки, необходим мо-
дуль Objects.

Кроме модулей ObjectWindows большинству программ необходимы
также модули WinTypes и WinProcs. Эти два модуля определяют типы
и константы (WinTypes) и процедуры и функции (WinProcs), образую-
щие прикладной программный интерфейс Windows (API). Приложениям,
использующим продвинутые средства Windows (версий старше 3.0),
кроме данных двух нужны также другие модули.

Примечание: Обзор модулей ObjectWindows вы можете най-
ти в Главе 7 "Иерархия ObjectWindows".

Требования к приложению
-----------------------------------------------------------------

Все приложения Windows имеют основное окно, которое выводит-
ся при запуске программы пользователем. Пользователь выходит из
приложения, закрывая основное окно. В приложении ObjectWindows
основное окно представляет собой объект окна. Этот объект принад-
лежит объекту приложения, который отвечает за создание и вывод на
экран основного окна, обработку сообщений Windows и завершение
программы. Объект приложения действует как объектно-ориентирован-
ная замена самого приложения. Аналогично, чтобы сделать скрытыми
детали программирования в Windows, ObjectWindows предусматривает
окно, диалоговый блок и другие объектные типы.

Каждая программа ObjectWindows должна определять новый тип
приложения, являющегося наследником предоставляемого типа
TApplication. В программе Steps этот тип называется
TMyApplication. Приведем основной блок программы Steps:

var
MyApp: TMyApplication;
begin
MyApp.Init('Steps');
MyApp.Run;
MyApp.Done;
end.

Init - это конструктор TMyApplication, создающий новый объ-
ект MyApp. Он позволяет также задать имя приложения (поле объек-

B.Pascal 7 & Objects/OW - 30 -

та) 'Steps' и создает (и выводит) основное окно приложения. Run
запускает последовательность вызовов методов, составляющих ход
выполнения приложения Windows. Done - это деструктор
TMyApplication.

Определение типа приложения
-----------------------------------------------------------------

Ваша прикладная программа должна создавать новый тип из
стандартного типа ObjectWindows TApplication (или некоторых ти-
пов, производных от TApplication). Этот новый тип должен переоп-
ределять по крайней мере один метод - InitMainWindow.
TApplication.InitMainWindow вызывается ObjectWindows автоматичес-
ки для установки основного окна программы. Каждое приложение
ObjectWindows должно строить свое основное окно.

Примечание: Объекты приложения подробно описываются в
Главе 8.

Определение TMyApplication имеет следующий вид:

type
TMyApplication = object(TApplication)
procedure InitMainWindow; virtual;
end;

Инициализация основного окна
-----------------------------------------------------------------

InitMainWindow отвечает за построение объекта окна, исполь-
зуемого в качестве основного окна программы. Этот объект основно-
го окна хранится в поле объекта приложения MainWindow. Объекту
приложения принадлежит объект основного окна, но эти два объекта
не являются родственными в иерархии наследования.

precedure TMyApplication.InitMainWindow;
begin
MainWindow := New(PWindow, Init(nil, 'Steps'));
end;

Обычно метод InitMainWindow модифицируется для создания но-
вого типа основного окна. Указанный метод использует экземпляр
объекта TWindow - предоставляемый ObjectWindows тип окна, который
определяет наиболее общее окно. На шаге 2 мы заменим его более
интересным оконным типом.

Пока программа Steps просто выводит на экран пустое окно,
которое можно перемещать по экрану, изменять его размер, миними-
зировать, максимизировать и закрывать. Приведем полный листинг
программы Steps, которую мы получили к данному моменту:

program Steps;


B.Pascal 7 & Objects/OW - 31 -

uses OWindows;

type
TMyApplication = object(TApplication)
procedure InitMainWindow; virtual;
end;

procedure TMyApplication.InitMainWindow;
begin
MainWindows := New(PWindow, Init(nil, 'Steps'));
end;

var MyApp: TMyApplication;
begin
MyApp.Init('Steps');
MyApp.Run;
MyApp.Done;
end.



B.Pascal 7 & Objects/OW - 32 -

Объект основного окна
-----------------------------------------------------------------

Пока программа Steps состоит из двух объектов - объекта при-
ложения и объекта окна. Объект приложения (MyApp) является эк-
земпляром TMyApplication - типом, производным от TApplication.
Оконный объект, который содержится в поле MainWindow объекта
MyApp, является экземпляром TWindow (общее окно ObjectWindows).
Во всех программах, кроме простейших, вам нужно определить тип
своего основного окна, соответствующий поведению приложения. В
данном разделе мы выведем на экран основное окно, тип которого
является производным от TWindow.

Приложения: Более подробно об основном окне рассказы-
вается в Главе 8 "Объекты приложения".

Что такое объект окна?
-----------------------------------------------------------------

Объект приложения инкапсулирует стандартное поведение прило-
жения Windows, включая построение основного окна. Тип
TApplication обеспечивает фундаментальное поведение для каждого
создаваемого вами приложения.

Аналогично, объект окна инкапсулирует поведение, реализуемое
приложениями ObjectWindows, включая их основные окна. Это поведе-
ние включает в себя вывод на экран, изменение размера и закрытие;
ответ на пользовательские события, такие как щелчок кнопкой "мы-
ши", буксировку и выбор пунктов меню; вывод управляющих элемен-
тов, таких как блоки списка и командные кнопки. Тип TWindow и его
предок TWindowsObject предусматривают для данного базового пове-
дения методы и поля.

Примечание: Объекты окна подробно описываются в Главе
10 "Объекты окна".

Чтобы сделать ваши программы полезными и интересными, вам
нужно создать новый тип, производный от TWindow. Новый тип насле-
дует поля и методы TWindow и добавляет часть своих. В общем слу-
чае объектно-ориентированный подход позволяет вам не "изобретать"
каждый раз окно.



B.Pascal 7 & Objects/OW - 33 -

Описатели
-----------------------------------------------------------------

Объект окна имеет по крайней мере три поля: HWindow. Parent
и ChildList. HWindow содержит описатель окна. Описатель окна -
это уникальное число, которое связывает интерфейсный объект (та-
кой как окно, диалоговый блок или объект управляющего элемента) с
соответствующим элементом экрана.

Примечание: Подробно об описателях окна их использо-
вании рассказывается в Главе 10 "Объекты окна".

Таким образом, HWindow содержит целое значение, идентифици-
рующее соответствующий элемент экрана. Это напоминает бирку на
связке ключей. Аналогично тому как вы выбираете ключ, чтобы дос-
тать из шкафа пальто, вы выбираете описатель для получения окна.
В большинстве случаев вы работаете с объектами окна, и у вас нет
необходимости манипулировать описателем окна непосредственно, но
они используются при вызове функций Windows. Например, на данном
шаге вы вызываете функцию MessageBox. Эта функция требует указа-
ния параметра, идентифицирующего порождающее окно сообщений. Вы
указываете основное окно, описатель которого записан в его поле
HWindow:

MessageBox(MainWindow^.HWindow, 'Хотите сохранить?',
'Файл не изменен', mb_YesNo or mb_IconQuestion);

Порождающие и дочерние окна
-----------------------------------------------------------------

Большинство приложений используют несколько окон, и, чтобы
они взаимодействовали, их требуется связать. Например, когда вы
завершаете приложение, оно должно иметь способ очистки всех окон,
за которые отвечает. В общем случае Windows справляется с этим,
связывая окна как дочернее и порождающее. Порождающее окно отве-
чает за свое дочернее окно. ObjectWindows предусматривает для
каждого объекта окна поля для отслеживания порождающего и дочер-
них окон.

Примечание: Взаимодействие этих окон подробнее описы-
вается в Главе 9.

Поле Parent содержит указатель на порождающий оконный объ-
ект. Это не порождающее окно в смысле предка, а скорее окно-вла-
делец. Взаимосвязь этих окон описывается в шаге 10.

Третье поле оконного объекта - это поле ChildList, содержа-
щее связанный список дочерних окон.

Создание нового типа окна
-----------------------------------------------------------------

Теперь у вас есть некоторое представление о том, что содер-

B.Pascal 7 & Objects/OW - 34 -

жит оконный объект, и вы можете создать новый оконный тип, произ-
водный от TWindow, используя его как основное окно программы
Step. Сначала измените определения и задайте новый тип
TStepWindow. Не забудьте также определить новый указатель на тип
TStepWindow - PStepWindow, который будет полезен при создании эк-
земпляров объектов TStepWindow.

type
PStepWindow = ^TStepWindow;
TStepWindow = object(TWindow)
end;

Затем измените TMyApplication.InitMainWindow, чтобы создать
в качестве основного окна вместо TWindow TStepWindow.

procedure TMyApplication.InitMainWindow;
begin
Main := New(PStepWindow, Init(nil, 'Step'));
end;

Определение нового типа и создание его экземпляра в
InitMainWindow - это все, что требуется для определения нового
типа основного окна для TMyProgram. Объект приложения вызывает
методы для создания интерфейсного элемента окна (Create) и вывода
его на экран (Show). Вам почти никогда не потребуется использо-
вать эти методы непосредственно. Обычно они вызываются при вызове
метода MakeWindow объекта приложения.

Примечание: MAkeWindow поясняется в Главе 9 "Интер-
фейсные объекты".

Однако TStepWindow не определяет новых видов поведения, от-
личных от тех, которые наследуются от TWindow и TWindowObject.
Другими словами, программа Step не становится более интересной.
Такие виды поведения будут добавлены в следующем разделе.

Реакция на сообщения
-----------------------------------------------------------------

Скорейший способ сделать оконный объект полезным - это зас-
тавить его отвечать на некоторые сообщения Windows. Например,
когда вы щелкаете "мышью" в основном окне программы Step, Windows
посылает окну сообщение wm_LButtonDown, которое перехватывается
ObjectWindows и посылается затем соответствующему оконному объек-
ту. Это указывает оконному объекту, что пользователь щелкнул в
нем кнопкой "мыши". При этом передаются также координаты точки,
где пользователь нажал кнопку. (Эту информацию мы используем в
шаге 2.)

Примечание: Сообщения Windows определены в модуле
WinTypes.

Аналогично, когда пользователь щелкает правой кнопкой "мы-

B.Pascal 7 & Objects/OW - 35 -

ши", основной оконный объект получает сообщение wm_RButtonDown,
переданное Windows. На следующем шаге мы узнаем, как сделать так,
чтобы основное окно (экземпляр TStepWindow) отвечало на эти сооб-
щения и делало что-нибудь полезное.

Чтобы перехватывать сообщения Windows и отвечать на них, для
каждого типа поступающего сообщения, на которое вы хотите реаги-
ровать, вам нужно определить метод оконного объекта. Такие методы
называются методами реакции на сообщение. Чтобы определить заго-
ловок определения метода как метод реакции, нужно добавить к вир-
туальному методу расширение, представляющее собой идентификатор
сообщения, на которое нужно реагировать. Например, определенный
ниже метод реагирует на все сообщения wm_LButtonDown.

type
TStepWindow = object(TWindow)
procedure WMLButtonDown(var Msg: TMessage); virtual
vm_First + wm_LButtonDown;
end;

Примечание: Все программы и модули, переопределяющие
методы ответа на сообщение, должны использовать WinTypes.

Все сообщения в Windows, включая системные сообщения Windows
и команды меню, представляются в виде чисел. Каждый метод реакции
на сообщение должен иметь уникальное число, так что для сообщений
Windows и команд, если они имеют одинаковые номера, вызываются
различные методы.

Чтобы облегчить для вас эту задачу, ObjectWindows определяет
для каждого вида сообщений константы: wm_First для сообщений
окон, cm_First для командных сообщений и nf_First для уведомляю-
щих сообщений. Подробнее об этих константах рассказывается в Гла-
ве 7, но сейчас нужно только помнить, что когда вы пишете метод
реакции на сообщение, начинающееся с wm_, к нему добавляется
wm_First.

Msg - это запись типа TMessage, содержащая такую информацию,
как координаты точки, где была нажата кнопка "мыши". Все методы
реакции на сообщение должны воспринимать один параметр-переменную
типа TMessage. Аргумент Msg мы рассмотрим в программе Step позд-
нее.

В данный момент вы можете просто определить методы реакции,
которые выводят на экран окно сообщения о нажатии кнопки "мыши".
Позднее вы сможете добавить более полезную реакцию. Приведем оп-
ределение метода реакции на нажатие левой кнопки "мыши":

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
begin
MessageBox(HWindow, 'Вы нажали левую кнопку мыши',
'Диспетчеризуемое сообщение', mb_OK);
end;

B.Pascal 7 & Objects/OW - 36 -


Примечание: Программы, которые вызывают MessageBox или
другие функции API Windows, должны использовать модуль
WinProcs.

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ -----------------------------------------------¬ ¦
¦ ¦-=----------Диспетчеризуемое сообщение--------¦ ¦
¦ +----------------------------------------------+ ¦
¦ ¦ ¦ ¦
¦ ¦ Вы нажали левую кнопку мыши ¦ ¦
¦ ¦ ------------¬ ¦ ¦
¦ ¦ ¦----OK-----¦ ¦ ¦
¦ ¦ L------------ ¦ ¦
¦ L----------------------------------------------- ¦
¦ ¦
L----------------------------------------------------------------

Рис. 1.2 Программа Steps реагирует на пользовательское собы-
тие.

Завершение прикладной программы
-----------------------------------------------------------------

Программа Steps завершает выполнение, когда пользователь
дважды щелкает "мышью" в блоке управляющего меню основного окна -
маленьком квадрате в левом верхнем углу. Окно и приложение немед-
ленно закрываются. Такое поведение годится для простых программ,
но может вызвать затруднения в более сложных случаях.

Перед выходом хорошо написанное приложение всегда спрашива-
ет, хочет ли пользователь сохранить несохраненные результаты ра-
боты. Такой вид поведения вы легко можете добавить в свою прик-
ладную программу ObjectWindows. Начните со Step и добавьте двой-
ную проверку запроса пользователя на выход.

Когда пользователь пытается закрыть приложение
ObjectWindows, Windows посылает основному окну сообщение
wm_Close, которое вызывает метод CanClose приложения. CanClose -
это булевская функция, указывающая, можно ли завершить (OK) при-
ложение (True). По умолчанию метод CanClose наследуется из вызова
TApplication метода CanClose основного оконного объекта. В боль-
шинстве случаев решение о закрытии (OK) принимается объектом ос-
новного окна.



B.Pascal 7 & Objects/OW - 37 -

Переопределение CanClose
-----------------------------------------------------------------

Тип основного окна TStepWindow наследует метод CanClose от
TWindowObject, которые вызывает методы CanClose каждого из своих
дочерних окон (если они имеются). Если дочерних окон нет (как в
данном случае), CanClose просто возвращает значение True. Чтобы
модифицировать поведение приложения при закрытии, вы можете пере-
определить метод CanClose для объектного типа своего основного
окна:

function TStepWindow.CanClose: Boolean;
var Reply: Integer;
begin
CanClose := True;
Reply := MessageBox(HWindow, 'Хотите сохранить?',
'Графическое изображение изменено',
mb_YesNo or mb_IconQuestion);
if Reply = id_Yes then CanClose := False;
end;


B.Pascal 7 & Objects/OW - 38 -


Теперь когда пользователи попытаются закрыть Step, они полу-
чат окно сообщений с запросом "Хотите сохранить". Щелчок "мышью"
на командной кнопке Yes (Да) приводит к тому, что CanClose возв-
ращает значение False и предотвращает закрытие основного окна и
приложения. Щелчок "мышью" на No (Нет) возвращает True, и прило-
жение завершает работу. На шаге 8 это окно сообщений получит не-
который смысл. Модифицированная программа Steps показана на Рис.
1.3.

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ ¦
¦ -----------------------------------------------¬ ¦
¦ ¦-=----------Изображение изменилось------------¦ ¦
¦ +----------------------------------------------+ ¦
¦ ¦ --- ¦ ¦
¦ ¦ -?- Хотите сохранить? ¦ ¦
¦ ¦ --- ------------¬ ------------¬ ¦ ¦
¦ ¦ ¦---Yes-----¦ ¦----No-----¦ ¦ ¦
¦ ¦ L------------ L------------ ¦ ¦
¦ L----------------------------------------------- ¦
¦ ¦
L----------------------------------------------------------------

Рис. 1.3 Программа Steps с переопределенным поведением окна.

Дальнейшее изменение закрытия
-----------------------------------------------------------------

Естественно, сообщение о том, что изображение изменилось,
полезно только в том случае, если программа действительно обнару-
живается изменение изображения. Добавив в TStepWindow поле типа
Boolean, вы можете задать флаг, указывающий на изменение изобра-
жения, и выводить окно сообщения только тогда, когда этот флаг
установлен.

Нужно помнить о том, что когда вы добавляете это поле, поле
нужно также инициализировать, поэтому переопределим конструктор
TStepWindow:

type
PStepWindow = ^TStepWindow;
TStepWindow = object(TWindow)
HasGhanged: Boolean;
constructor Init(AParent: PWindowsObject: ATitle:
PChar);
.
.
.
end;

B.Pascal 7 & Objects/OW - 39 -


constructor TStepWindow.Init(AParent: PWindowsObject:
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
HasChanged := False;
end;

Далее измените метод CanClose для проверки перед выводом ок-
на сообщения HasChanged:

function TStepWindow.CanClose: Boolean;
var Reply: Integer;
begin
CanClose := True;
if HasChanged then
begin
Reply := MessageBox(HWindow, 'Хотите сохранить?',
'Изображение изменилось',
mb_YesNo or mb_IconQuestion);
if Reply = id_Yes then CanClose := False;
end;
end;

Позднее, когда вы фактически изменяете изображение,
HasChanged нужно установить в True. Следующий листинг показывает
полный исходный под программы Steps на данном шаге:

program Steps;

uses WinTypes, WinProcs, OWindows;

type
TMyApplication = object(TApplication)
procedure InitMainWindow; virtual;
end;

type
PStepWindow = ^TStepWindow;
TStepWindow = object(TWindow)
Haschanged: Boolean;
constructio Init(AParent: PWindowsObject; ATitle:
PChar);
function CanClose: Boolean; virtual;
procedure CanClose: Boolean; virtual;
procedure WMLButtonDown(var Msg: TMessage);
virtual wm_First + wm_LButtonDown;
procedure WMRButtonDown(var Msg: TMessage);
virtual sm_First +? wm_RButtonDown;
end;

constructor TStepWindow.Init(AParent: PWindowsObject;
ATitle: PChar);

B.Pascal 7 & Objects/OW - 40 -

begin
inherited Init(AParent, ATitle);
HasChanged := False;
end;

function TStepWindow.CanClose: Boolean;
var Reply: Integer;
begin
if HasChanged then
begin
CanClose := True;
Reply := MessageBox(HWindow, 'Хотите сохранить?',
'Изображение изменилось',
mb_YesNo or mb_IconQuestion);
if Reply = id_Yes then CanClose := False;
end;
end;

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
begin
MessageBox(HWindow, 'Вы нажали левую кнопку мыши',
'Диспетчеризуемое сообщение', mb_OK);
end;

procedure TStepWindow.WMRButtonDown(var Msg: TMessage);
begin
MessageBox(HWindow, 'Вы нажали правую кнопку мыши',
'Диспетчеризуемое сообщение', mb_OK);
end;

procedure TMyApplication.InitMainWindow;
begin
MainWindows := New(PStepWindow, Init(nil, 'Steps'));
end;

var MyApp: TMyApplication;
begin
MyApp.Init('Steps');
MyApp.Run;
MyApp.Done;
end.



B.Pascal 7 & Objects/OW - 41 -

------------------------------------------------------------------
Глава 2. Заполнение окна
-----------------------------------------------------------------

Пока ваша программа не делает ничего интересного. В данной
главе мы возьмем программу Step, которая пока представляет собой
просто оболочку программы, и преобразуем ее в полезное интерак-
тивное графическое приложение. Сначала мы выведем в основном окне
текст. Затем преобразуем Step в полное графическое приложение,
позволяющее вам отображать в основном окне линии различной толщи-
ны.

Шаг 2: Отображение текста в окне
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦-Step-2:-Text----------¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

В качестве первого шага в направлении создания отображающей
программы нарисуем в окне некоторые текстовые символы. Текст бу-
дет отображаться в ответ на щелчок левой кнопкой "мыши", который
вы перехватывали на шаге 1. Но вместо вывода окна сообщения на
этот раз реакцией будет вывод текста, показывающего координаты
той точки в окне, где вы щелкнули кнопкой "мыши".

В графической операционной среде типа Windows текст рисуется
как графика, а не выводится в виде символов на стандартное уст-
ройство вывода. В некотором смысле это эквивалентно использованию
позиционирования курсора в программах DOS, где вы задаете распо-
ложение каждого текстового элемента, а не полагаетесь на прокрут-
ку экрана. Конечно, в Windows вы можете также управлять разме-
ром, стилем, гарнитурой и цветом текста.

Вывод в контексте дисплея
-----------------------------------------------------------------

Чтобы вывести текст в основном окне программы Step, вам нуж-
на некоторая отображаемая информация. В Windows имеются специаль-
ные средства, управляющие ее графикой, которые называются кон-
текстом дисплея. Контекст дисплея можно рассматривать как эле-
мент, представляющий поверхность окна, где выводится изображение.
Контекст экрана необходим Windows для отображения в окне любого

B.Pascal 7 & Objects/OW - 42 -

текста или графики.

Примечание: Подробно о контексте дисплея рассказывает-
ся в Главе 17 "Интерфейс с графическими устройствами".

Что такое контекст дисплея?
-----------------------------------------------------------------

Контекст дисплея имеет три основных функции отображения:

* Он обеспечивает, что текст и графика не выводятся вне по-
верхности окна.

* Он управляет выбором и отменой инструментальных средств
отображения: перьев, кистей и шрифтов. В шаге 3 показан
пример выбора нового пера, но мы начнем сначала с вывода
текста.

* Он обеспечивает независимость от устройства. Для вывода в
контексте дисплея ваша программа использует стандартные
функции API Windows. В шаге 9 мы покажем как можно исполь-
зовать одни и те же команды для отображения в окне и на
принтере.

Windows управляет контекстом дисплея в своем собственном
пространстве памяти, но ваша прикладная программа может отслежи-
вать контекст дисплея с помощью описателей. Как и описатель окна,
описатель контекста дисплея - это число, идентифицирующее кор-
ректный контекст дисплея Windows.

Поскольку, чтобы рисовать в окне при буксировке "мыши", вам
необходим контекст дисплея, создайте в объекте основного окна но-
вое поле с именем DragDC, которое будет содержать описатель кон-
текста дисплея. DragDC имеет тип HDC, который эквивалентен типу
Word.

Чтобы использовать контекст дисплея, ваша программа должна:

* получить контекст дисплея;

* нарисовать в нем;

* освободить контекст дисплея.

Получение контекста дисплея
-----------------------------------------------------------------

Чтобы отобразить что-то в окне, вы должны сначала получить
контекст дисплея. Это можно сделать, вызвав в одном из методов
типа непосредственно перед отображением на экране функцию Windows
GetDC:

DragDC := GetDC(HWindow);

B.Pascal 7 & Objects/OW - 43 -

Использование контекста дисплея
-----------------------------------------------------------------

Теперь вы можете использовать DragDC в качестве параметра в
вызовах графических функций Windows, требующих указания контекста
дисплея. Приведем несколько примеров:

TextOut(DragDC, 20, 20, 'Пример текста', 11);
LineTo(DragDC, 30, 45);

Освобождение контекста дисплея
-----------------------------------------------------------------

После отображения текста или графики вы должны освободить
контекст дисплея (как только закончите отображение).

ReleaseDC(HWindow, DragDC);

Если вы не освободите весь полученный контекст дисплея, то
скоро исчерпаете его, что приводит обычно к зависанию компьютера.
Если ваша программа не может что-либо отобразить, проверьте осво-
бождение контекстов дисплея.

Windows выделяет по пять контекстов дисплея на приложение,
которые можно совместно использовать через GetDC. Пока в Windows
зарезервировано достаточно памяти, вы можете с помощью GetDC по-
лучить другие контексты.

Примечание: GDI и вопросы использования памяти освеща-
ются в Главе 17 "Интерфейс с графическим устройством".



B.Pascal 7 & Objects/OW - 44 -

Координаты Windows
-----------------------------------------------------------------

Если вы работали с графикой раньше, то вам уже знакомо поня-
тие системы координат. В Windows координаты потребуются вам для
вывода текста.

При отображении вас касаются только координаты в контексте
дисплея. Windows обеспечивает, чтобы контекст дисплея попадал в
область клиента окна.

Примечание: Область клиента - это часть окна внутри
рамки.

На этом шаге Step отобразит текст, показывающий координаты
той точки в окне, где вы щелкнули кнопкой "мыши". Например, '(20,
30)' - это точка, отстоящая на 20 элементов изображения вправо и
на 30 элементов изображения вниз от верхнего левого угла поверх-
ности отображения. Вы можете отображать прямо в той точке, где
щелкнули "мышью". Это показано на Рис. 2.1.

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦(3,7) (483,7) ¦
¦ (385,31) ¦
¦ ¦
¦ (60,42) ¦
¦ (217,52) ¦
¦ ¦
¦ (302,110) (444,110) ¦
¦ ¦
¦ ¦
¦ (109,141) ¦
¦ ¦
¦ (52,182) ¦
¦ (239,187) (385,181) ¦
¦ ¦
¦(4,288) (474,220)¦
L----------------------------------------------------------------

Рис. 2.1 Отображение текста в точке нажатия кнопки "мыши".

Параметры сообщений
-----------------------------------------------------------------

Щелчок левой кнопкой "мыши" генерирует сообщение
wm_LButtonDown, который вы перехватываете с помощью метода реак-
ции на сообщение WMLButtonDown.

Параметр Msg метода реакции на сообщение несет информацию о
породившем сообщение событии (такую как координаты точки, где
пользователь щелкнул кнопкой "мыши"). Msg - это запись TMessage,

B.Pascal 7 & Objects/OW - 45 -

поля которой содержат параметр lParam типа Longint и параметр
wParam типа Word. Идентификаторы lParam и wParam соответствуют
полям в структуре сообщения Windows TMsg.

TMessage определяют также вариантные поля, содержащие подпо-
ля lParam и wParam. Например, Msg.lParamLo содержит младшее слово
lParam, а Msg.lParamHi - старшее слово. Чаще всего используются
поля wParam, lParamLo и lParamHi.

В случае WMLButtonDown Msg.lParamLo содержит x-координату
точки нажатия кнопки "мыши", а Msg.lParamHi - y-координату этой
точки. Таким образом, чтобы переписать WMLButtonDown для отобра-
жения координат точки нажатия кнопки, нужно преобразовать
Msg.lParamLo и Msg.lParamHi в строки и, чтобы они приняли вид
'(25,21)', конкатенировать их с запятой. В примере для форматиро-
вания строки используется функция Windows WVSPrintF.

Примечание: Слияние параметров зависит от сообщения.
Подробности о каждом сообщении и его параметре вы можете
узнать, воспользовавшись оперативным справочником Help.

После получения итоговой строки ее можно вывести в точке на-
жатия кнопки "мыши" с помощью функции Windows TextOut. Перед
отображением нужно получить контекст дисплея, а после отображения
- освободить его.

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);

var S: array[0..9] of Char;
begin
WVSPrint(S, '(%d,%d)', Msg.LParam);
DragDC := GetDC(HWindow);
TextOut(DragDc, Msg.LParamLo, Msg.LParamHi, S, StrLen(S));
ReleaseDC(HWindow, DragDC);
end;

Примечание: Windows ожидает получения строк с заверша-
ющим нулем (конечным нулевым байтом). Подробнее эти строки
описываются в Главе 18 "Руководства по языку".

Очистка окна
-----------------------------------------------------------------

В приложение, отображающее текст, вы можете также добавить
еще одну функцию - функцию отчистки окна. Заметим, что после из-
менения размера окна, либо когда вы скрываете его и выводите сно-
ва, нарисованный текст стирается. Однако, можно задать принуди-
тельную очистку окна в ответ на команду меню или какое-либо дру-
гое действие пользователя, например, щелчок кнопкой "мыши".

Чтобы очистить окно в ответ на щелчок правой кнопкой "мыши",
переопределите метод WMRButtonDown и вызовите в нем процедуру
InvalidateRect, которая приводит к повторному отображению всего

B.Pascal 7 & Objects/OW - 46 -

окна. Так как ваша программа пока не знает, как повторно вывести
изображение, она просто очистит область клиента:

Procedure TStepWindow.WMRButtonDown(var Msg: TMessage);
begin
InvelidateRect(HWindow, nil, Trut);
end;

Текущий исходный код вы можете найти в файле STEP02.PAS.



B.Pascal 7 & Objects/OW - 47 -

------------------------------------------------------------------
Шаг 3: Изображение линий в окне
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Tex ¦
¦-Step-3:-Lines---------¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

Теперь, когда вы познакомились со схемой отображения (полу-
чение контекста дисплея, отображение, освобождение контекста
дисплея), ее можно использовать в более полном интерактивном гра-
фическом приложении. Следующие несколько шагов посвящены построе-
нию простой графической программы, позволяющей пользователю рисо-
вать в основном окне.

На шаге 3 мы добавим следующее поведение:

* Щелчок левой кнопкой "мыши" и буксировка соединяют началь-
ную и конечную точки отображая в результате линию.

* Щелчок правой кнопкой "мыши" выводит диалоговое окно вво-
да, позволяющее пользователю изменить толщину линии.

Чтобы выполнить эти шаги, изучим сначала схему буксировки
Windows, а затем реализуем простую графическую программу.



B.Pascal 7 & Objects/OW - 48 -

Буксировка линии
-----------------------------------------------------------------

Мы уже видели, что щелчок левой кнопкой "мыши" дает в ре-
зультате сообщение wm_LButtonDown и вызывает метод WMLButtonDown.
В шаге 1 ваша программа отвечала на щелчки левой кнопкой "мыши",
выводя окна сообщений. Вы могли также видеть, что щелчок правой
кнопкой "мыши" давал в результате сообщение wm_RButtonDown и вы-
зывал метод WMRButtonDown. На нажатие правой кнопки "мыши" прог-
рамма отвечала очисткой окна.

Но это предусматривает реакцию только на щелчки кнопкой "мы-
ши". Многие программы Windows требуют от пользователя нажатия
кнопки "мыши" и перемещения ее указателя по экрану (буксировка).
При этом рисуются линии или прямоугольники, либо графическое
изображение помещается в точку с конкретными координатами. Для
программ графического отображения желателен перехват событий бук-
сировки и реакция на них путем изображения линий.

Сообщения wm_MouseMove
-----------------------------------------------------------------

Сделать это можно путем реакции еще на несколько сообщений.
Когда пользователь буксирует "мышь" в новую точку окна, Windows
посылает сообщение wm_MouseMove, а когда пользователь отпускает
левую кнопку "мыши" - сообщение wm_LButtonUp. Обычно окно получа-
ет одно сообщение wm_LButtonDown, за которым следует последова-
тельность сообщений wm_MouseMove (по одному на каждую промежуточ-
ную точку буксировки) и одно сообщение wm_LButtonUp.

Типичная графическая программа Windows реагирует на сообще-
ние wm_LButtonDown инициализацией процесса рисования (получая,
кроме всего прочего, контекст дисплея). На сообщение wm_LButtonUp
она реагирует завершением процесса рисования (освобождая контекст
дисплея).



B.Pascal 7 & Objects/OW - 49 -

Реакция на сообщения буксировки
-----------------------------------------------------------------

Нужно помнить о том, что после wm_LButtonDown всегда следует
сообщение wm_LButtonUp (с промежуточными сообщениями
wm_MouseMove или без них). Таким образом, каждый раз, когда вы
получаете контекст дисплея, вы можете позднее освободить его.

Для правильного функционирования программы Windows очень
важным является освобождение каждого получаемого вами контекста
дисплея. Однако вы можете добавить еще одно более надежное средс-
тво. Определите в TStepWindow новое булевское поле - тип основно-
го окна с именем ButtonDown и обеспечьте его инициализацию в
TStepWindow.Unit значением False. Затем вы можете проверять перед
получением и освобождением контекста дисплея значение ButtonDown.

Приведем три метода обработки буксировки "мыши":

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
begin
InvalidateRect(HWindow, nil, True);
if not ButtonDown then
begin
ButtonDown := True;
SetCapture(HWindow);
DragDC := GetDC(HWindow);
MoveTo(DragDC, Msg.lParamLo, Msg.lParamHi);
end;
end;

procedure TStepWindow.WMMouseMove(var Msg: TMessage);
begin
if ButtonDown then
LineTo(DragDC, Msg.lParamLo, MsglParamHi);
end;

procedure TStepWindow.WMLButtonUp(var Msg: TMessage);
begin
if ButtonDown then
begin
ButtonDown := False;
ReleaseCapture;
ReleaseDC(HWindow, DragDC);
end;
end;



B.Pascal 7 & Objects/OW - 50 -

Изображение точек и линий
-----------------------------------------------------------------

В API Windows имеются графические функции MoveTo и LineTo,
которые, соответственно, перемещают текущую позицию рисования и
рисуют линию до текущей позиции. Для правильной работы функций
требуется указание описателя контекста дисплея DragDC. Нужно пом-
нить о том, что вы рисуете не непосредственно в окне, а в его
контексте дисплея.

Перехват "мыши"
-----------------------------------------------------------------

Передачу Windows соответствующих сообщений wm_MouseMove
обеспечивают функции SetCapture и ReleaseCapture. Например, если
вы буксируете "мышь" за пределы окна, Windows все равно будет по-
сылать сообщения основному, а не смежному с ним окну, в которое
она попала. Перехват "мыши" обеспечивает также поступление в ваше
окно сообщения от "мыши", так что оно будет знать о прекращении
рисования даже если "мышь" перемещается в другом окне.

Нужно изменить определение объекта для TStepWindow с заго-
ловками метода для WMMouseMove и WMLButtonUp:

procedure WMLButtonUp(var Msg: TMessage); virtual wm_First +
wm_LButtonUp;
procedure WMLMouseMove(var Msg: TMessage); virtual wm_First +
wm_LMouseMove;

Пример полученного исходного кода вы найдете в файле
STEP03A.PAS.



B.Pascal 7 & Objects/OW - 51 -

Изменение размера пера
-----------------------------------------------------------------

До сих пор вы могли рисовать только тонкие черные линии. Од-
нако графические программы традиционно позволяют изменять толщину
изображаемых линий. При этом вы изменяете на самом деле не толщи-
ну линии, а размер пера, используемого для ее вычерчивания.
Перья, также как кисти, шрифты и палитры, представляют собой
встроенные в контекст дисплея изобразительные средства. На данном
шаге вы узнаете, как установить в контексте дисплея новые изобра-
зительные средства, что даст программе Step возможность рисовать
линии другой толщины.

Для реализации механизма выбора пользователем размера пера
мы используем диалоговое окно (типа TInputDialog). Это окно вида:

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ Введите новую толщину линии: ¦
¦ -----------------------------------------------¬ ¦
¦ ¦-1- ¦ ¦
¦ L----------------------------------------------- ¦
¦ ------------¬ ------------¬ ¦
¦ ¦----OK-----¦ ¦--Cancel---¦ ¦
¦ L------------ L------------ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 2.2 Задание новой толщины линии с помощью диалогового
окна ввода.

Отслеживание размера пера
-----------------------------------------------------------------

Чтобы изменить толщину изображаемых линий, вам нужно полу-
чить сначала несколько более глубокое представление о графике
Windows и о контексте в частности.

Изобразительные средства

Для получения графики и текста в окне Windows использует
несколько изобразительных средств: перья, кисти и шрифты. Эти
изобразительные средства представляют собой элементы, хранимые в
памяти Windows и не отличающиеся от видимых элементов экрана, та-
ких как окна и управляющие элементы. Ваша программа может обра-
щаться к изобразительным средствам с помощью описателей (как это
имеет место в случае окон). Так как ObjectWindows не использует
для представления изобразительных средств объекты, вашим програм-
мам не нужно создавать их и удалять из памяти Windows при завер-
шении работы с ними.


B.Pascal 7 & Objects/OW - 52 -

Примечание: В шаге 6 мы создадим объект, инкапсулирую-
щий одно инструментальное средство - перо.

Изобразительное средство можно рассматривать как кисть ху-
дожника, а контекст дисплея - как холст. Художник сначала создает
изобразительные средства (кисти) и получает контекст дисплея
(холст). Затем художник выбирает соответствующее изобразительное
средство, используя в каждый момент одну из кистей. Аналогично,
программа Windows должна выбирать изобразительные средства в кон-
тексте дисплея.

Используемые по умолчанию изобразительные средства

Итак, как же сделать, чтобы могли рисовать в своих окнах
текста и линии, не выбирая никаких изобразительных средств? Все
контексты дисплея снабжены набором используемых по умолчанию
средств: тонким черным пером, твердой черной кистью и системным
шрифтом. На данном шаге мы выберем для рисования в окне другое,
более тонкое перо.

Получение пера нового размера
-----------------------------------------------------------------

Сначала нужно обеспечить способ выбора нового размера пера.
В простейшем случае это можно сделать с помощью диалогового окна
ввода модуля OStdDlgs. Добавьте модуль OStdDlgs в оператор uses
программы. Чтобы использовать совместимые с Windows функции рабо-
ты со строками, укажите также модуль Strings. Начало программного
файла должно выглядеть таким образом:

program Steps;

uses Strings, WinTypes, WinProcs, OWindow, OStdDlgs;
.
.
.

Выполнение диалогового окна ввода

Диалоговое окно ввода - это простое диалоговое окно, которое
выводит подсказку и возвращает одну введенную строку текста. Вы
можете использовать его без модификации TInputDialog или других
методов.

Щелчок правой кнопкой "мыши" дает удобный способ вывода па-
раметра для изменения толщины пера. Давайте переопределим метод
WMRButtonDown для вывода нового диалогового окна ввода.

Так как диалоговое окно ввода появляется только на короткое
время, а вся обработка выполняется одним методом, вам нет необхо-
димости определять его как поле TStepWindows. Оно может существо-
вать в виде локальной переменной метода WMRButtonDown. Все пост-
роение и отмену объекта диалогового окна вы можете выполнять в

B.Pascal 7 & Objects/OW - 53 -

рамках метода WMRButtonDowm.

Когда Init построит объект диалогового окна ввода, вы можете
выполнить его как режимное диалоговое окно, вызвав ExecDialog.
ExecDialog проверяет успешность выполнения конструктора Init и
создает объект диалогового окна, соответствующий элементу экрана,
выполняя затем диалоговое окно. Обработка для ExecDialog заверша-
ется только после того как пользователь закрыл диалог, щелкнув
"мышью" на командной кнопке OK (Подтверждение) или Cancel (Отме-
на).

Если пользователь щелкнул "мышью" на командной кнопке OK,
InputText заполняется полученным от пользователя текстом, вызывая
метод GetText из TInputDialog. Так как вы запрашиваете номер тол-
щины, возвращаемый текст нужно преобразовать в число и передать
его в вызове SetPenSize. Таким образом, каждый раз, когда пользо-
ватель выбирает новую толщину линии, старое перо удаляется и соз-
дается новое.

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
var
InputText: array[0..9] of Char;
NewSize, ErrorPos: Integer;
begin
if not ButtonDown then
begin
Str(PenSize, InputText);
if Application^.ExecDialog(New(PInputDialog,
Init(@Self, 'Толщина линии',
'Введите новую толщину:', InputText,
SizeOf(InputText))) = id_Ok then
begin
Val(InputText, NewSize, ErrorPos);
if ErrorPos = 0 then SetPenSize(NewSize);
end;
end;
end.

Добавление полей объекта

Далее добавим в TStepWindow новое поле для хранения описате-
ля пера, которое вы будете использовать для рисования графики. В
данной программе в каждый момент времени вы можете рисовать и вы-
водить на экран линии только одной толщины. Соответствующее этой
толщине перо хранится в новом поле TStepWindow с именем ThePen.
Вы напишете также метод SetPenSize, создающий новое перо и удаля-
ющий старое. Теперь описание объекта TStepWindow должно принять
следующий вид:

type
PStepWindow = ^TStepWindow;
TStepWindow = object(TWindow)
DragDC: HDC;

B.Pascal 7 & Objects/OW - 54 -

ButtonDown, HasChanged: Boolean;
ThePen: HPen;
PenSize: Integer;
constructor Init(AParent: PWindowsObject;
ATitle: PChar);
destructor Done; virtual;
function CanClopse: Boolean: virtual;
procedure WMLButtonDown(var Msg: TMessage); virtual
wm_First + wm_LButtonDown;
procedure WMLButtonUp(var Msg: TMessage); virtual
wm_First + wm_LButtonUp;
procedure WMMouseMove(var Msg: TMessage); virtual
wm_First + wm_LMouseMove;
procedure WMRButtonDown(var Msg: TMessage); virtual
wm_First + wm_RButtonDown;
procedure SetPenSize(NewSize: Integer); virtual;
end;

Инициализация полей

Чтобы инициализировать новые поля, вам нужно модифицировать
конструктор Init для установки пера и переопределить деструктор
Done для его отмены. Не забудьте вызвать в новых методах наследу-
емые методы:

constructor TStepWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
Inherited Init(AParent, ATitle);
ButtonDown := False;
HasChanged := False;
PenSize := 1;
ThePen := CreatePen(ps_Solid, Pensize, 0);
end;

destructor TStepWindow.Done;
begin
DeleteObject(ThePen);
inherited Done;
end;

Изображение линий

Теперь изменим метод WMLButtonDown для выбора текущего пера
(ThePen) во вновь полученном контексте дисплея. Аналогично MoveTo
и MessageBox, SelectObject является функцией API Windows.

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
begin
if not ButtonDown then
begin
ButtonDown := True;
SetCapture"(HWindow);

B.Pascal 7 & Objects/OW - 55 -

DragDC := GetDC(HWindow);
SelectObject(DragDC, ThePen);
MoveTo(DragDC, Msg.lParamLo, Msg.lParamHi);
end;
end;

Указанные методы выбирают в контексте дисплея уже созданное
перо. Однако для создания пера нужно написать следующий вызывае-
мый WMRButtonDown метод SetPenSize:

procedure TStepWindow.SetPenSize(NewSize: Integer);
begin
DeleteObject(ThePen);
ThePen := Create(ps_Solid, NewSize, 0);
PenSize := NewSize;
end;

Вызов функции Windows CreatePen - это один из способов соз-
дания пера Windows заданной толщины. Описатель пера записывается
в ThePen. Очень важным шагом является удаление старого пера. От-
сутствие такого шага приведет к неверному использованию памяти
Windows.

На шаге 5 и 6 вы создадите собственное диалоговое окно и
объект пера и используете их для более эффективного графического
отображения.



B.Pascal 7 & Objects/OW - 56 -

------------------------------------------------------------------
Глава 3. Меню и диалоговые ресурсы
-----------------------------------------------------------------

Большинство приложений Windows имеют в своих основных окнах
меню, которые предоставляют пользователю возможность выбора -
например, команды File¦Save, File¦Open и Help. В шаге 4 мы доба-
вим в программу Steps строку меню. Ввод пользователем данных и
выбор параметров в программах Windows часто происходит в диалого-
вых блоках. В шаге 5 в программу Steps будет добавлен диалоговый
блок.

Меню и диалоговые блоки объединены общим понятием ресурсов.
Ресурсы - это данные, хранимые в выполняемом файле, который опи-
сывает пользовательские интерфейсные элементы приложений Windows.
Ресурс не содержит фактического меню или диалогового блока. Вмес-
то этого он содержит информацию, необходимую программе для созда-
ния конкретного диалогового блока или меню.

Ресурсы для программы проще всего создавать с помощью редак-
торов ресурсов, таких как Resource Workshop (пакет разработчика
ресурсов) фирмы Borland. Подробно о создании и редактировании ре-
сурсов рассказывается в "Руководстве пользователя по пакету раз-
работчика ресурсов".

Шаг 4: Добавление строки меню
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦-Step-4:-Menu----------¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

В оконной среде выбор пункта меню относится к той же катего-
рии, что и щелчок кнопкой "мыши". И то, и другое - это пользова-
тельские события. Ответ на выбор пункта меню аналогичен реакции
на другие пользовательские события. В данном разделе описываются
шаги, необходимые для добавления в приложение меню.

* Проектирование меню как ресурса меню.

* Определение констант меню во включаемом файле.

* Загрузка файла ресурса из программы.

B.Pascal 7 & Objects/OW - 57 -


* Загрузка ресурса меню в объект основного окна.

* Определение реакции на выбор в меню.

Меню прикладной программы - это не отдельный объект, а атри-
бут основного окна. Все оконные объекты имеют набор атрибутов,
записанных в поле записи Attr объекта. В поле Menu записи Attr
хранится не описатель меню, а меню. Чтобы установить атрибут ме-
ню, вы должны переопределить конструктор своего типа окна
TStepWindow.

Ресурсы меню
-----------------------------------------------------------------

Определение ресурсов меню не является частью исходного кода
программы. Вместо этого существует ресурс, содержит текст пунктов
меню и структуру элементов верхнего уровня и их подсистем. Для
проектирования меню и других ресурсов, таких как диалоговые бло-
ки, пиктограммы и битовые массивы, вы можете использовать пакет
разработчика ресурсов Resource Workshop.

Определение идентификаторов ресурса

Приложение обращается к присоединенным к нему ресурсам по
идентификатору ресурса. Этот идентификатор представляет собой це-
лое значение, например, 100, или целочисленную константу, такую
как MyMenu. Кроме того, приложение отличает один выбор меню от
другого по идентификатору, связанному с элементом меню.

Определение констант меню

Чтобы сделать программу более читаемой, замените идентифика-
торы меню константами, определяемыми во включаемом файле. При
создании своего ресурса меню с помощью Resource Workshop или ком-
пилятора ресурсов вы можете включить те же константы и использо-
вать те же идентификаторы, которые вы используете для доступа к
ресурсу к своей программе. Константы меню для программы Steps оп-
ределены в файле STEPS.INC:

const
cm_FilePrint = 105;
cm_FileSetup = 107;
cm_Pen = 200;
cm_About = 201;
cm_PalShow = 301;
cm_PalHide = 302;

Заметим, что число элементов меню в файле STEPS.INC не опре-
делено. Это связано с тем, что ObjectWindows в файле IWINDOWS.INC
определяет для вас некоторые общие команды меню, включая
cm_FileOpen, cm_FileNew, cm_FileSave и cm_FileSaveAs.


B.Pascal 7 & Objects/OW - 58 -

Включение файлов ресурсов

Чтобы продолжить работу с программой Steps, используйте па-
кет разработчика ресурсов или компилятор ресурсов для создания
ресурса меню и сохраните его в файле с расширением .RES -
STEPS.RES. Формат файла ресурса в исходном виде вы можете посмот-
реть в файле STEPS.RC. Вы можете также использовать файл
STEPS.RES, который можно найти на дистрибутивных дисках. Имея
файл STEPS.RES, вы можете включить его с помощью директивы компи-
лятора $R:

{$R STEPS.RES}

Директива компилятора $R в конце компиляции и компоновки ав-
томатически добавляет заданный файл ресурса к выполняемому файлу.
Ресурсы можно добавить или удалить из выполняемых файлов, а су-
ществующие ресурсы можно модифицировать.

Примечание: О модификации ресурсов, уже скомпонованных
с выполняемыми файлами, рассказывается в "Руководстве поль-
зователя по пакету разработчика ресурсов".


B.Pascal 7 & Objects/OW - 59 -


На Рис. 3.1 показан внешний вид этого меню (идентификатор
ресурса 100). Оно включает в себя пункты File (Файл), Options
(Параметры) и Palette (Палитра), а меню File содержит элементы
New (Новый), Open (Открытие), Save (Сохранение), Save As (Сохра-
нение под именем), Print (Печать), Printer Setup (Установка прин-
тера) и Exit (Выход). Элементы верхнего уровня, у которых есть
подэлементы, не имеют идентификаторов меню, а их вывод не вызыва-
ет никаких действий кроме вывода подэлементов.

Примечание: Не путайте идентификатор ресурса меню с
идентификаторами меню отдельных элементов (пунктов) меню.

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦-File- Options Palette ¦
+------------------T--------------------------------------------+
¦-New--------------¦ ¦
¦ Open... ¦ ¦
¦ Save ¦ ¦
¦ Save as... ¦ ¦
+------------------+ ¦
¦ Print... ¦ ¦
¦ Printer Setup... ¦ ¦
+------------------+ ¦
¦ Exit ¦ ¦
+------------------- ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 3.1 Программа Steps с ресурсом меню.



B.Pascal 7 & Objects/OW - 60 -

Загрузка ресурса меню
-----------------------------------------------------------------

Получить ресурс меню можно с помощью вызова функции Windows
LoadMenu:

LoadMenu(HInstance, MakeIntResource(100));

MakeIntResource(100) приводит число 100 к ссылочному типу
PChar, представляющему собой указатель на массив символов. Функ-
ции Windows, воспринимающие в качестве аргументов строки, требу-
ют, чтобы они имели тип PChar. Имея дело с ресурсами, Windows
ожидает, что целые числа должны быть представлены в виде PChar,
поэтому если вы хотите обратиться к ресурсу, имеющему числовой
идентификатор, нужно преобразовать его тип с помощью
MakeIntResource.

Примечание: Для использования типа PChar требуется ус-
тановка $X+ (по умолчанию).

В качестве альтернативы идентификатор меню может иметь сим-
вольный идентификатор, например, 'SAMPLE_MENU'. В этом случае
загрузить ресурс меню можно следующим образом:

LoadMenu(HInstance, 'SAMPLE_MENU');

Вот как это делает TStepWindow.Init (заметим, что первое,
что он делает - это вызов конструктора Init, наследуемого из
TWindow, для выполнения инициализации, необходимой для всех окон-
ных объектов):

constructor TStepWindow(AParent: PWindowObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Menu := LoadMenu(HInstance, MakeIntResource(100));
BottomDown := False;
HasChanged := False;
end;

Теперь при выводе основного окна оно имеет рабочее меню, по-
казанное на Рис. 1.3. Однако, чтобы при выборе элементов меню вы-
полнялись какие-либо действия, вы должны перехватывать сообщения
меню и реагировать на них. Если вы не определили реакцию на ко-
манду меню, то можете выбирать элемент меню, но при этом ничего
не происходит.



B.Pascal 7 & Objects/OW - 61 -

Перехват сообщений меню
-----------------------------------------------------------------

Когда пользователь выбирает элемент меню, окно, к которому
присоединено меню, получает командное сообщение Windows.
ObjectWindows обрабатывает и диспетчеризует эти сообщения
wm_Command аналогично другим сообщениям, но облегчает для вас ра-
боту со специальными командами.

Одним из параметров сообщения wm_Command является сама ко-
манда (номер, соответствующий идентификатору меню выбранного эле-
мента). Вместо вызова метода WMCommand и возложения на вас реше-
ния, что делать с каждой возможной командой, ObjectWindows вызы-
вает основанные на конкретных командах методы. Чтобы обработать
эти сообщения, вы можете определить методы для объектного типа
TStepWindow, используя специальное расширение:

procedure CMFileNew(var Msg: TMessage);
virtual cm_First + cm_FileNew;

где cm_First - это константа ObjectWindows, определяющая начало
диапазона констант для команд, а cm_FileNew - это желаемая коман-
да меню. Это означает, что все элементы меню должны иметь уни-
кальные идентификаторы (если только не предполагается реагировать
на них одинаковым образом).

Примечание: О диапазонах сообщений и смещениях расска-
зывается в Главе 16.

Не путайте основанный на cm_First динамический индекс метода
с индексом, соответствующим поступающему сообщению Windows (осно-
ванному на wm_First). cm_First - это специальное смещение, ис-
пользуемое только для определения методов реакции для команд меню
и командных клавиш.



B.Pascal 7 & Objects/OW - 62 -

Определение методов реакции на команду
-----------------------------------------------------------------

Теперь вы можете определить все методы реакции на команды:

procedure CMFileNew(var Msg: TMessage);
virtual cm_First + cm_FileNew;
procedure CMFileOpen(var Msg: TMessage);
virtual cm_First + cm_FileOpen;
procedure CMFileSave(var Msg: TMessage);
virtual cm_First + cm_FileSave;
procedure CMFileSaveAs(var Msg: TMessage);
virtual cm_First + cm_FileSaveAs;
procedure CMFilePrint(var Msg: TMessage);
virtual cm_First + cm_FilePrint;
procedure CMFileSetup(var Msg: TMessage);
virtual cm_First + cm_FileSetup;

Определять процедуру CMExit не требуется, поскольку
TWindowsObject уже определяет завершающую программу процедуру,
которая вызывается при поступлении в основное окно сообщения
cm_Exit.

Связывание клавиш с командами
-----------------------------------------------------------------

Методы реакции на команды не связываются с конкретным эле-
ментом меню - они привязаны к конкретной команде. Объекту не важ-
но, откуда поступила команда. Он знает только, что что-то вызвало
данное командное сообщение. Таким образом, у вас есть несколько
способов генерации команды. Обычно для этого применяются опера-
тивные клавиши, называемые командными клавишами.

Командные клавиши определяются в ресурсах аналогично меню,
но они намного проще. Ресурс командной клавиши - это таблица на-
жатий клавиш и команд, которые они генерируют. О создании ресур-
сов для командных клавиш рассказывается в "Руководстве пользова-
теля по пакету разработчика ресурсов".

Каждая прикладная программа может иметь только один набор
командных клавиш. Чтобы загрузить в программу ресурс командных
клавиш, переопределите метод InitInstance:

procedure TMyApplication.InitInstance;
begin
inherited InitInstance;
HaccTable := LoadAccelerators(HInstance, 'ShortCuts');
end;

Командные клавиши 'ShortCuts' в STEPS.RES связывают знакомые
вам по IDE функциональные клавиши с аналогичными функциями прог-
раммы Steps. Например, клавиша F3 генерирует команду cm_FileOpen.


B.Pascal 7 & Objects/OW - 63 -

Реакция на команды меню
-----------------------------------------------------------------

Теперь для каждого выбора в меню у вас есть метод, который
будет вызываться в ответ на соответствующую команду. Выбор коман-
ды File¦Print вызывает ваш метод CMFilePrint. Пока вызовем просто
окно сообщения:

procedure TStepWindow.CMFilePrint(var sg: TMessage);
begin
Message(HWindow, 'Средство не реализовано',
'Печать файла', mb_Ok);
end;

На Рис. 3.2 показана реакция программы Steps на выбор коман-
ды File¦Print.

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ File Options Palette ¦
+---------------------------------------------------------------+
¦ ¦
¦ ¦
¦ -----------------------------------------------¬ ¦
¦ ¦-=---------------Печать файла-----------------¦ ¦
¦ +----------------------------------------------+ ¦
¦ ¦ ¦ ¦
¦ ¦ Средство не реализовано ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ------------¬ ¦ ¦
¦ ¦ ¦----OK-----¦ ¦ ¦
¦ ¦ L------------ ¦ ¦
¦ L----------------------------------------------- ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 3.2 Программа Steps реагирует на команду File¦Print.

Для CMFileOpen, CMFileSave, CMFileSaveAs и CMFileSetup напи-
шите фиктивные методы, аналогичные CMFilePrint. Позднее вы пере-
пишете данные методы для выполнения осмысленных действий.

Теперь, очистив окно, вы можете реагировать на выбор команды
меню File¦New более интересным образом. Добавьте следующий метод
CMFileNew:

procedure TStepWindow.CMFileNew(var Msg: TMessage);
begin
InvalidateRect(HWindow, nil, True);

B.Pascal 7 & Objects/OW - 64 -

end;

InvalidateRect выполняет принудительное повторное отображе-
ние окна. Полный исходный код программы Steps для данного этапа
содержится в файле STEP04A.PAS.

Добавление диалогового блока
-----------------------------------------------------------------

Диалоговый блок аналогичен всплывающему окну, но обычно оно
сохраняется на экране в течении короткого периода и выполняет од-
ну конкретную задачу, связанную с вводом-выводом, такую как выбор
принтера или настройка страницы документа. Здесь мы добавим в
программу Steps диалоговое окно для открытия и сохранения файлов.

Файловое диалоговое окно, как одно из диалоговых окон
ObjectWindows, определено с типом TFileDialog. Файловое диалого-
вое окно полезно использовать в любой ситуации, когда вы запраши-
ваете у пользователя для сохранения и загрузки выбор файла на
диске. Например, редактор текстов может использовать диалоговое
окно для открытия и сохранения документов.


B.Pascal 7 & Objects/OW - 65 -


Вы будете выводить файловое диалоговое окно в ответ на выбор
пользователем команды File¦Open или File¦Save As. Файловое диало-
говое окно заменяет окно сообщения "Средство не реализовано". В
шаге 8 оно будет приспособлено для некоторых реальных файлов, а
также сохранения и открытия их для записи и считывания реальных
данных. Пока просто выведем диалоговые окна. Вид файлового диало-
гового окна показан на Рис. 3.3.

------------------------------------------------------------T-T-¬
¦-=---------------------Steps-------------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ File Options Palette ¦
+---------------------------------------------------------------+
¦ -----------------------------------------------¬ ¦
¦ ¦-=---------------Открытие файла---------------¦ ¦
¦ +----------------------------------------------+ ¦
¦ ¦ -------------------¬ -----------¬¦ ¦
¦ ¦ Имя файла: ¦ *.pts ¦ ¦----OK----¦¦ ¦
¦ ¦ L------------------- L-----------¦ ¦
¦ ¦ -----------¬¦ ¦
¦ ¦ Каталог: a:\ ¦--Cancel--¦¦ ¦
¦ ¦ L-----------¦ ¦
¦ ¦ Файлы: Каталоги: ¦ ¦
¦ ¦ -----------------¬ --------------T-¬ ¦ ¦
¦ ¦ ¦ ¦ ¦[-a] ¦^¦ ¦ ¦
¦ ¦ ¦ ¦ ¦[-b] +-+ ¦ ¦
¦ ¦ ¦ ¦ ¦[-c] ¦-¦ ¦ ¦
¦ ¦ ¦ ¦ ¦[-d] ¦-¦ ¦ ¦
¦ ¦ ¦ ¦ ¦[-e] ¦-¦ ¦ ¦
¦ ¦ ¦ ¦ ¦[-f] ¦-¦ ¦ ¦
¦ ¦ ¦ ¦ ¦[-g] ¦-¦ ¦ ¦
¦ ¦ ¦ ¦ ¦[-h] +-+ ¦ ¦
¦ ¦ ¦ ¦ ¦[-i] ¦v¦ ¦ ¦
¦ ¦ L----------------- L-------------+-- ¦ ¦
¦ L----------------------------------------------- ¦
L----------------------------------------------------------------

Рис. 3.3 Программа Steps с диалоговым блоком File Open.

Добавление к программе Steps файлового диалогового блока
требует трех шагов:

* Добавление поля объекта, содержащего имя файла.
* Модификация конструктора объекта для инициализации файла.
* Выполнение диалогового блока.



B.Pascal 7 & Objects/OW - 66 -

Добавление поля объекта
-----------------------------------------------------------------

Вместо хранения всего объекта файлового диалогового окна в
виде поля его порождающего окна, вам следует построить новый объ-
ект файлового диалогового окна, когда он потребуется. Вместо дан-
ных, которые вам следует хранить вместо данных этого диалогового
окна, вы должны сохранять имя и маску файла. На практике хорошо
придерживаться следующего: вместо хранения всех объектов, которые
вам могут не потребоваться, храните просто данные, необходимые
для инициализации объектов, когда они потребуются.

Построение файлового диалогового блока требует трех парамет-
ров: порождающего окна, шаблона ресурса и имя или маску файла (в
зависимости от того, используется файловое окно для открытия или
закрытия файла). Шаблон ресурса определяет, какое из стандартных
файловых диалоговых окон вы хотите использовать. Стандартные фай-
ловые диалоговые ресурсы определяются идентификаторами ресурсов
sd_FileOpen и sd_FileSave. Параметр имени файла используется для
передачи используемой по умолчанию маски файла диалогу открытия
файла (а также для возврата выбранного имени файла) и для переда-
чи используемого по умолчанию имени для сохранения файла.

Параметр шаблона ресурса определяет, будет ли файловый диа-
логовый блок использоваться для открытия или для сохранения фай-
ла. Если диалоговый ресурс имеет блок списка файлов с идентифика-
тором управляющего элемента id_FList, диалоговый блок использует-
ся для открытия файлов; отсутствие такого блока списка указывает
на диалоговое окно для сохранения файлов.

Определение типа TStepsWindow должно теперь выглядеть следу-
ющим образом:

TStepWindow = object(TWindow)
.
.
.
FileName: array[0...fsPathName] of Char;
.
.
.

Примечание: Для работы с константой fsPathName нужно
использовать модуль WinDos.

Модификация конструктора

Для создания экземпляра объекта справочного окна вы можете
использовать конструктор Init типа TStepWindow. Теперь вам потре-
буется добавить к нему код для инициализации FileName:

StrCopy(FileName, '*.PTS');


B.Pascal 7 & Objects/OW - 67 -

Расширение .PTS используется для файлов, содержащих точки
вашего графического изображения.

Выполнение диалогового блока

В зависимости от переданного конструктору диалогового блока
параметра шаблона ресурса диалоговый блок может поддерживать отк-
рытие или сохранение файла. Каждый параметр, при создании диало-
гового блока, аналогичен показанному на Рис. 3.3. Между диалогами
открытия или закрытия файла имеются два различия: диалог открытия
содержит список файлов в текущем каталоге, соответствующий теку-
щей маске файла, а в диалоге сохранения в поле редактирования уп-
равляющего диалогового элемента выводится имя текущего файла, но
список файлов отсутствует.

CMFileOpen и CMFileSaveAs следует переписать следующим обра-
зом:

procedure TStepWindow.CMFileOpen(var Msg: TMessage);
begin
if Application^.ExecDialog(New(PFileDialog,
Init(@Self, PChar(sd_FileOpen), FileName))) = id_Ok
then MessageBox(HWindow, FileName, 'Открыть файл:',
mb_Ok);
end;

procedure TStepWindow.CMFileSaveAs(var Msg: TMessage);
begin
if Application^.ExecDialog(New(PFileDialog,
Init(@Self, PChar(sd_FileSave), FileName))) = id_Ok
then MessageBox(HWindow, FileName, 'Сохранить файл:',
mb_Ok);
end;

Заметим, что при выполнении файлового диалогового окна ис-
пользуется тот же метод ExecDialog, который вы вызывали для вы-
полнения диалогового окна ввода в шаге 3. С помощью метода
ExecDialog выполняются все режимные диалоговые окна в приложении.

Полный исходный код программы Steps для данного шага вы мо-
жете найти в файле STEP04B.PAS.



B.Pascal 7 & Objects/OW - 68 -

------------------------------------------------------------------
Шаг 5: Добавление диалогового блока
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦-Step-5:-About-Box-----¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

До сих пор в программе Steps использовались два очень прос-
тых диалоговых блока: окно сообщений (в методе CanClose) и диало-
говый блок ввода для изменения размера пера. Эти диалоговые блоки
удобно применять для простых задач, но в программах обычно требу-
ются более сложные и ориентированные на задачу взаимодействия с
пользователем. В таких случаях вы можете разработать собственные
диалоговые блоки.

Как и меню, диалоговые блоки обычно создаются из описания,
сохраненного в ресурсе. Для сложных диалоговых блоков это значи-
тельно быстрее, чем индивидуальное создание каждого элемента от-
дельного окна. Однако в отличие от меню, поскольку программы
должны взаимодействовать с диалоговыми окнами более разнообразны-
ми и сложными путями, ObjectWindows использует для представления
диалогового блока объект.

Создание диалогового блока из ресурса требует следующих ша-
гов:

* Создание ресурса диалогового блока.
* Построение объекта диалогового блока.
* Выполнение диалогового блока.



B.Pascal 7 & Objects/OW - 69 -

Создание ресурсов диалогового блока
-----------------------------------------------------------------

Проектировать ресурсы диалогового блока можно несколькими
способами, используя пакет разработчика ресурса Resource Workshop
или компилятор ресурсов. Диалоговый блок (блок диалога) - это
специализированное окно с рамкой, содержащее один или более уп-
равляющих элементов (командные кнопки, блоки списка и пиктограм-
мы). Ваша прикладная программа не знает о том, как выглядят уп-
равляющие элементы и как они позиционированы; она знает только о
типе управляющих элементах и их идентификаторах.

Примечание: Не забывайте, что ресурс - это просто не-
кое описание того, что будет создавать ваша программа.

Идентификаторы управляющих элементов
-----------------------------------------------------------------

Аналогично элементам в ресурсе меню, каждый управляющий эле-
мент в ресурсе диалогового блока имеет идентификатор, который
прикладная программа использует для определения того, с каким уп-
равляющим элементом нужно взаимодействовать. Для статических эле-
ментов, с которыми ваша прикладная программа не взаимодействует
(статический текст или битовые массивы) уникальные идентификаторы
не требуются, поэтому они обычно имеют идентификатор -1.

Обычно, если вашей прикладной программе требуется доступ к
конкретному управляющему элементу, вы присваиваете ему идентифи-
катор-константу, так что и ваша программа, и компилятор ресурсов
могут использовать в качестве идентификатора одно и то же сим-
вольное имя. Такие идентификаторы-константы следует определять во
включаемом файле или в модуле, содержащем только описания-конс-
танты. При создании или редактировании ваших ресурсов пакет раз-
работчика ресурсов автоматически управляет такими файлами.



B.Pascal 7 & Objects/OW - 70 -

Построение объекта диалогового блока
-----------------------------------------------------------------

После того как ресурс диалогового окна будет определен, ваша
программа может использовать его для создания и выполнения диало-
гового окна. Диалоговые блоки выводятся обычно как дочерние окна
основного окна приложения, но они создаются несколько по-другому,
чем обычные окна.

Конструктор объекта диалогового блока выглядит как конструк-
тор оконного объекта, воспринимающий два параметра. В обоих слу-
чаях первый параметр - это указатель на объект порождающего окна.
Второй параметр (PChar) определяет заголовок объекта окна. Однако
для объекта диалогового блока в качестве шаблона диалогового бло-
ка используется имя диалогового ресурса.

Файл ресурса для программы Steps определяет диалоговый блок
с именем 'ABOUTBOX', которое вы можете использовать в качестве
окна About box, показанного на Рис. 3.4. Построение объекта диа-
логового блока из данного ресурса выглядит следующим образом:

New(PDialog, Init(@Self, 'ABOUTBOX'));

--------------------------------------------------¬
¦-=------------------About Steps------------------¦
+-------------------------------------------------+
¦ ¦
¦ -----------------------------------------¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ---- ObjectWindows tutorial program ¦ ¦
¦ ¦ ---- ¦ ¦
¦ ¦ Copiright (C) 1992 ¦ ¦
¦ ¦ Borland International Inc. ¦ ¦
¦ ¦ All Rights Reserved ¦ ¦
¦ L----------------------------------------- ¦
¦ ¦
¦ ------------¬ ¦
¦ ¦----OK-----¦ ¦
¦ L------------ ¦
L--------------------------------------------------

Рис. 3.4 Окно About Box для программы Steps.

Выполнение диалогового блока
-----------------------------------------------------------------

Чтобы выполнить специализированный диалоговый блок, исполь-
зуйте тот же метод ExecDialog, который вы уже использовали для
других диалоговых блоков:

Application^.ExecDialog(New(PDialog,Init(@Self,'ABOUTBOX')));

Естественно, нужно определить команду для вывода диалогового

B.Pascal 7 & Objects/OW - 71 -

блока About box; Steps использует сообщение cm_About, генерируе-
мое выбором меню Optrions¦About. Теперь такой вид реакции на ко-
манду должен быть вам достаточно знаком (см. файл STEP05.PAS):

type
TStepWindow = object(TWindow)
.
.
.
procedure CMAbout(var Msg: TMessage);
virtual cm_First + cm_About;
end;

procedure TStepWindow.CMAbout(var Msg: TMessage);
begin
Application^.ExecDialog(New(PDialog, Init(@Self,
'ABOUTBOX')));
end;

В шаге 6 мы создадим более сложное диалоговое окно с нес-
колькими управляющими элементами.



B.Pascal 7 & Objects/OW - 72 -

Режимные и безрежимные диалоговые блоки
-----------------------------------------------------------------

После выполнения окна с помощью ExecDialog диалоговый блок
становится режимным. Это означает, что программа работает с этим
блоком до его закрытия. Пока активно это окно, все получаемые
программой сообщения поступают в него. Такой диалоговый блок на-
зывается режимным окном приложения, поскольку оно режимное только
по отношению к выполняющему его приложению. Существуют также сис-
темные режимные диалоговые блоки, которые приостанавливают всю
работу приложения, пока блок не будет закрыт. Такие блоки и окна
используются редко, и применять их следует только в том случае,
если при работе других приложений могут возникнуть проблемы.

Иногда желательно получить диалоговый блок, сохраняющийся
при работе других частей программы. Такой диалоговый блок работа-
ет почти как обычное окно, но не является режимным, и потому но-
сит название безрежимного. О создании безрежимных диалоговых бло-
ков рассказывается в Главе 11 "Объекты диалоговых блоков".



B.Pascal 7 & Objects/OW - 73 -

------------------------------------------------------------------
Глава 4. Работа с диалоговым блоком
-----------------------------------------------------------------

Теперь, когда вы оснастили программу Steps созданным из ре-
сурса диалоговым блоком, нужно научиться взаимодействовать с этим
диалоговым блоком. Сначала вы создадите объект, инкапсулирующий
все характеристики окна пера, затем используете диалоговый блок
для установки и изменения этих характеристик.

В данной главе описаны следующие шаги:

* Определение объекта пера.

* Создание сложного диалогового блока.

* Добавление управляющих объектов.

* Создание буфера передачи.

* Выполнение диалогового блока.

* Чтение результатов.

Шаг 6: Изменение атрибутов пера
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦-Step-6:-Pens----------¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

Данный шаг охватывает ряд тем, касающихся изобразительных
средств Windows, особенно перьевых средств, применяемых для изоб-
ражения линий. Перья Windows имеют три отдельных атрибута: стиль,
ширину и цвет.

Первая часть данного шага - создание объекта для представле-
ния пера не является абсолютно необходимой, но позволяет вашему
окну работать с пером как единой сущностью, а не отслеживать от-
дельно все атрибуты перьев. Инкапсулируя перо, вы можете также
избежать необходимости иметь дело с некоторыми повторяющимися де-
талями использования инструментальных средств GDI, аналогично то-
му, как объекты окон ObjectWindows предохраняют вас от мелких де-

B.Pascal 7 & Objects/OW - 74 -

талей, связанных с созданием окна.

Создание объекта пера
-----------------------------------------------------------------

Хотя Windows ссылается на свои изобразительные средства как
на "объекты" (отсюда и имена типа SelectObject и DeleteObject),
они не являются объектами в истинном объектно-ориентированном
смысле, так как не используют наследование и полиморфизм. Перо на
самом деле представляет собой просто группу из трех характеристик
отображения, на которые Windows ссылается при изображении линии.
Эти характеристики являются просто свойствами контекста дисплея,
но полезно рассматривать их, как встроенные в перо.

Характеристики пера

Три характеристики пера - это его стиль, размер и цвет. В
шаге 3 вы изменяли размер пера и отслеживали текущий размер пера
в поле объекта окна. Вместо реализации трех отдельных полей для
отслеживания характеристик пера вы можете инкапсулировать их в
единый объект TPen. Описание TPen имеет следующий вид:

type
PPen = ^TPen;
TPen = object(TObject)
Width, Style: Integer;
Color: Longint;
constructor Init(AStyle, AWidth: Integer;
AColor: Longint);
constructor Load(var S: TStream);
procedure ChangePen;
procedure Delete;
procedure Select(ADC: HDC);
procedure SetAttributes(AStyle, AWidth: Integer;
AColor: Longint);
procedure Store(var S: TStream);
private
PenHandle, OldPen: HPen;
TheDC: HDC;
PenData: TPenData;
end;

Примечание: Большую часть исходного кода из данной
главы вы можете найти в файле PEN.PAS. Для использования
модуля Pen в STEP06A.PAS и STEP06B.PAS нужно внести мини-
мальные изменения.

Примечание: Тип TPen определен в модуле Pen.

Конструктор Init создает новый объект пера с заданным сти-
лем, размером и цветом. SetAttributes изменяет атрибуты уже соз-
данного объекта пера. ChangePen выводит диалоговое окно, позволя-
ющее пользователю задать атрибуты пера. Load и Store позволяют

B.Pascal 7 & Objects/OW - 75 -

сохранять объекты пера в потоке.

Выбор и удаление объектов пера

Наиболее интересную работу выполняют процедуры Select и
Delete. Select создает изобразительное средство Windows на основе
характеристик, записанных в полях атрибутов. Вместо того, чтобы
вызывать в графической программе для создания пера, получения его
описателя, выбора пера в контексте дисплея, использования пера и
его удаления функцию API Windows, вы строите объект пера, а затем
можете его использовать, выделять и удалять.

Метод Delete отменяет описатель пера, освобождая ресурс для
Windows. Select проверяет, имеется ли уже выделенное перо, и пе-
ред созданием и выбором нового отменяет существующее перо. Это
полезно использовать, если это же перо предполагается применять
повторно, так что вам не понадобиться вызывать Delete при каждом
использовании пера. С другой стороны, в шаге 7 вы увидите, как
можно сохранять нарисованные линии, и каждая линия будет иметь
свой собственный объект пера. Если бы каждый объект пера созда-
вался и сохранялся в пере Windows, Windows скоро исчерпала бы ре-
сурсы. Поэтому важно непосредственно после использования пера вы-
зывать для его отмены метод Delete.

Основное достоинство TPen в том, что вам не нужно больше
беспокоиться о получении, сохранении и удалении объекта пера.
TPen имеет два частных поля, в одном их которых записывается опи-
сатель пера. Объект пера отслеживает описатель и взаимодействия с
Windows, а ваша программа просто имеет дело с объектом. Другое
частное поле, PenData, содержит используемый на этом шаге буфер
передачи.

Файл STEP06A.PAS содержит код программы Steps, модифициро-
ванный для использования объекта TPen в модуле Pen. В основном
изменения невелики (например, поле ThePen изменяет тип с HPen на
PPen, а метод SetPenSize заменяется вызовом метода
SetPenAttributes объекта пера, поскольку объект пера может управ-
лять цветом и стилем наряду с размером).



B.Pascal 7 & Objects/OW - 76 -

Создание сложного диалогового блока
-----------------------------------------------------------------

До сих пор вы использовали достаточно простой диалоговый
блок (см. блок About Box в шаге 5). Особенно полезными становятся
диалоговые блоки, когда вы можете устанавливать и считывать зна-
чения их управляющих элементов.

В модуле Pen определяется более сложный ресурс диалогового
блока с именем 'PenDlg', который дает вам возможность изменения
атрибутов только что определенного объекта пера. Этот диалоговый
блок показан на Рис. 4.1.

----------------------------------------------------¬
¦-=-----------Set Pen Attributes--------------------¦
+---------------------------------------------------+
¦ --Color----------¬ --Style-----------¬ ¦
¦ ¦ (*) Black ¦ ¦ (*) Solid ¦ ¦
¦ ¦ ( ) Purple ¦ ¦ ( ) Dash ¦ ¦
¦ ¦ ( ) Blue ¦ ¦ ( ) Dot ¦ ¦
¦ ¦ ( ) Cyan ¦ ¦ ( ) DashDot ¦ ¦
¦ ¦ ( ) Green ¦ ¦ ( ) DasDotDot ¦ ¦
¦ ¦ ( ) Yellow ¦ ¦ ( ) Null ¦ ¦
¦ ¦ ( ) Red ¦ +-----------------+ ¦
¦ ¦ ( ) White ¦ ¦ Width: -1- ¦ ¦
¦ L----------------- L------------------ ¦
+---------------------------------------------------+
¦ ¦
¦ -------------¬ -------------¬ ¦
¦ ¦----OK------¦ ¦--Cancel----¦ ¦
¦ L------------- L------------- ¦
¦ ¦
L----------------------------------------------------

Рис. 4.1 Диалоговый блок с изменением атрибутов пера.

Set Pen Attributes - установка атрибутов пера; Color - цвет;
Black - черный; Purple - фиолетовый; Blue - голубой; Cyan - бирю-
зовый; Green - зеленый; Yellow - желтый; Red - красный; White -
белый; Style - стиль; Solid - непрерывная линия; Dash - пунктир;
Dot - точки; DashDot - точки и тире; DasDotDot - тире и две точ-
ки; Null - пусто; Width - ширина; OK - подтверждение; Cancel -
отмена.

Построение объекта из ресурса 'PenDlg' выполняется также,
как это делается для окна About Box (за исключением порождающего
окна). Поскольку диалоговый блок атрибута пера выполняется из
объекта TPen, а не из оконного объекта, вы не можете в качестве
порождающего окна использовать @Self. Вместо этого TPen присоеди-
няет диалоговый блок к одному из окон, о присутствии которых из-
вестно заранее - основному окну приложения:

procedure TPent.ChangePen;

B.Pascal 7 & Objects/OW - 77 -

var PenDlg: PPenDialog;
begin
.
.
.
PenDlg := New(PPenDialog, Init(Application^.MainWindow,
'PenDlg'));
.
.
.
end;

Другим важным отличием является то, что на этот раз вы имее-
те новый производный объектный тип TPenDialog. Так как окно About
box не использует ничего, кроме назначенного по умолчанию поведе-
ния диалогового окна, инкапсулированного в TDialog, вам не требу-
ется создавать для него новый объектный тип. Однако диалог атри-
бутов пера отличается более сложным поведением и требует настрой-
ки объекта.

Приведем определение TPenDialog из модуля Pen:

type
PPenDialog = ^TPenDialog;
TPenDialog = object(TDialog);
constructor Init(AParent: PWindowsObject; AName;
PChar);
end;

constructor TPenDialog.Init(AParent: PWindowsObject;
AName: PChar;
var
AControl: PRadioButton;
i: Integer;
begin
inherited Init(AParent, AName);
AControl := New(PRadioButton, InitResource(@Self,
1100 + i));
for i := 0 to 5 do
AControl := New(PRadioButton, InitResource(@Self,
1200 + i));
end;

Построенные в TPenDialog управляющие объекты поясняются в
следующем разделе.

Управляющие объекты
-----------------------------------------------------------------

Если вашей программе требуется непосредственно взаимодейс-
твовать с управляющими объектами в диалоговом окне (например,
чтобы поместить элементы в блок списка или определить выбор кноп-
ки с независимой фиксацией), с этими управляющими элементами по-

B.Pascal 7 & Objects/OW - 78 -

лезно связать объекты. Тогда вы сможете управлять этими элемента-
ми также, как любыми другими объектами в программе.

Использование интерфейсных объектов
-----------------------------------------------------------------

При "обычном" программировании в Windows (то есть без
ObjectWindows), ваша прикладная программа должна взаимодейство-
вать с каждым элементом экрана через функции API Windows. Как вы
уже видели, ObjectWindows облегчает создание и управление диало-
говыми блоками, изолируя вас от Windows путем максимально возмож-
ного использования для представления элементов экрана объектов.
Эти интерфейсные объекты также значительно облегчают взаимодейс-
твие с управляющими элементами в диалоговых блоках.

Примечание: Интерфейсные объекты описываются в Главе
9, а управляющие объекты описываются, в частности, в Главе
12.

Если вам не требуются управляющие объекты, вы все равно смо-
жете взаимодействовать с управляющими элементами, но это приведет
к необходимости частого вызова функций API Windows, передачи уп-
равляющим элементам сообщений и интерпретации результатов.
ObjectWindows значительно облегчает эту задачу, инкапсулируя по-
ведение каждого управляющего элемента в объекте. Передаются и об-
рабатываются те же сообщения, но ObjectWindows заботится обо всех
деталях.

Связь объекта с созданными из ресурса управляющим элементом
достаточно проста: внутри конструктора объекта диалогового блока
вы строите объекты для любых управляющих элементов, которыми хо-
тите манипулировать. Однако вместо использования для построения
управляющих объектов конструктора Init применяется InitResource.



B.Pascal 7 & Objects/OW - 79 -

Конструктор InitResource
-----------------------------------------------------------------

Когда вы на этапе выполнения создаете управляющий объект (в
противоположность созданию его из ресурса), вам нужно задать рас-
положение, размер и начальное значение (или состояние) управляю-
щего элемента, а также указать, какой объект является порождаю-
щим. Все эти элементы передаются в качестве параметров конструк-
тору Init объекта.

Связь объекта с управляющим элементом из ресурса намного
проще, так как такая информация как расположение и размер, опре-
деляется ресурсом. Требуется передать конструктору InitResource
только порождающий объект и идентификатор управляющего элемента.
Так как управляющие объекты обычно строятся внутри конструктора
их порождающих диалоговых блоков, указатель порождающего объекта
почти всегда равен @Self.

Как показано в приведенном выше примере, диалог пера модуля
Pen связывает объекты с их управляющими элементами редактирования
(для задания размера пера) и обоими наборами кнопок с зависимой
фиксацией (для задания цвета и стиля).

Заметим, что все управляющие объекты строятся и присваивают-
ся одной и той же локальной переменной AControl. Вашей программе
не придется взаимодействовать ни с одним из этих управляющих эле-
ментов непосредственно, так как пока выполняется режимный диало-
говый блок, остальная часть программы не активна. InitResource
к списку дочерних окон диалогового блока, чтобы обеспечить очист-
ку и уничтожение элементов экрана вместе с диалоговым окном.

В общем случае нет необходимости присваивать дочерним окнам
в режимных диалоговых блоках поля объекта. Однако в шаге 11 вы
увидите, как можно сохранять указатели на управляющие элементы
объектов безрежимного окна, что облегчает работу с ними.

Создание буфера передачи
-----------------------------------------------------------------

Теперь, когда в диалоговом блоке у вас есть объект, связанный
с управляющими элементами диалогового окна, необходим способ для
установки и чтения их значений. Это делается с помощью буфера пе-
редачи. Буфер передачи - это запись, которая содержит одно поле
для каждого управляющего элемента в диалоговом окне, в который
или из которого происходит передача.

Например, диалоговый блок, созданный в шаге 6, имеет поле
редактирования и четырнадцать кнопок с зависимой фиксацией. В уп-
равляющий элемент редактирования требуется передавать строку, а
каждая кнопка с зависимой фиксацией получает значение Word, ука-
зывающее на его выбор. Модуль Pen определяет тип записи, переда-
ваемый TPenDialogs и из него:


B.Pascal 7 & Objects/OW - 80 -

type
TPenData = record
XWidth: array[0..6] of Char;
ColorArray: arra[0..7] of Word;
StyleArray: array[0..5] of Word;
end;

Вы можете также управлять кнопками с независимой фиксацией,
используя 14 отдельных полей или один массив из 14 значений типа
Word; передаваемые данные будут теми же. Однако, так как ваша
прикладная программа будет интерпретировать их как две группы из
8 и 6 кнопок соответственно, удобно задать поле для каждой груп-
пы.

Присваивание буфера

Каждый потомок TWindowsObject имеет поле TransferBuffer.
Когда вы хотите передать данные в диалоговое окно, нужно задать
объект TransferBuffer диалогового блока, указывающий на запись
передачи:

PenDlg := New(PPenDialog, Init(Application^.MainWindow,
'PenDlg'));
PenDlg^.TransferBuffer := @PenData;

Если ваши программы создают объекты диалогового окна динами-
чески, убедитесь, что они каждый раз назначают буфер передачи.
TransferBuffer по умолчанию имеет значение nil. Это означает, что
данные не переданы.

Заполнение буфера

Перед фактической передачей данных в диалоговое окно, вам
нужно установить значение полей в буфере передачи. Перед выводом
диалогового окна пера это делает TPen.ChangePen:

procedure TPen.ChangePen;
var
PenDlg: PPenDialog;
TempWidth, ErrorPos: Integer;
begin
SetColorAttr(PenDate, Color);
SetStyle(PenDate, Style);
wvsprintf(PenDialog, Init(Application^.MainWindows,
'PenDlg'));
PenDlg^.TransferBuffer := @PenData;
if Application^.ExecDialog(PenDlg) <> id_Cancel then
begin
Val(PenData.XWidth, TempWidth, ErrorPos);
if ErrorPos = 0 then
SetAttributes(SetStyle(PenData), TempWidth,
GetColorAttr(PenData));
end;

B.Pascal 7 & Objects/OW - 81 -

end;

SetColorAttr и SetStyle используют то преимущество, что бу-
фер передачи задает кнопки с зависимой фиксацией в виде массива
значений Word. SetStyle, например, выглядит следующим образом:

procedure SetStyle(var ARec: TPenData; AStyle: Integer);
var i: Integer;
begin
for i := 0 to 5 do
if = AStyle then ARec.StyleArray[i] := bf_Checked
else ARec.StyleArray[i] := bf_Unchecked;
end;

Примечание: SetColorAttr выполняет то же назначение,
что и ColorArray. bf_Checked и bf_Unchecled - это константы
ObjectWindows.

Передача данных
-----------------------------------------------------------------

После того как вы создадите буфер передачи и заполняет его
значениями, получение этой информации в диалоговом блоке не
представляет труда, поскольку все за вас делает ObjectWindows.
Когда для выполнения диалогового блока вызывается ExecDialog, он
вызывает TransferDatа для копирования значений из буфера передачи
в отдельные объекты управляющих элементов.

Когда вы завершите диалоговое окно, щелкнув "мышью" на ко-
мандной кнопке OK, ExecDialog перед уничтожением диалогового бло-
ка и его управляющих элементов передает значения из управляющего
элемента обратно в буфер передачи. Отмена диалогового блока или
его закрытие с помощью управляющего меню обходит механизм переда-
чи данных обратно в буфер передачи.

Таким образом, буфер передачи указывает на постоянный набор
данных, не зависящий от диалогового блока. Во многих случаях диа-
логовый блок создается и уничтожается при выполнении программы
многократно, а присваивание каждый раз его поля TransferData од-
ной и той же записи данных позволяет выводить управляющие эле-
менты так, как они выглядели при последнем закрытии диалогового
блока.

Чтение возвращаемых значений
-----------------------------------------------------------------

Считывание значений обратно в буфер передачи - это обратный
процесс по отношению к заполнению буфера перед заполнением диало-
гового окна. В модуле Pen определены некоторые функции, способс-
твующие интерпретации выбора кнопки с зависимой фиксацией в каж-
дой группе.

function GetStyle(ARec: TPenDate): Longint;

B.Pascal 7 & Objects/OW - 82 -

var i: Integer;
begin
for i := 0 to 5 do
if ARec.StyleArray[i] = bf_Cheched then GetStyle := i;
end;

Если пользователь отменяет диалоговый блок, то вас, конечно,
не должно беспокоить считывание значений: они совпадают с пере-
данными значениями. Обычно когда вы выполняете диалоговый блок с
помощью ExecDialog, то чтобы определить, возвратил ли диалоговый
блок какие-либо полезные данные, проверяется возвращаемое значе-
ние (id_Ok, если пользователь щелкнул "мышью" на командной кнопке
OK, в противном случае id_Cancel).

if Application^.ExecDialog(PenDlg) <> id_Cancel then
begin
Val(PenDate.XWith, TempWith, ErrorPos);
SetAttributes(GetStyle(PenData), TempWidth,
GetColorAttr(PenData));
end;

Вызов диалогового блока пера
-----------------------------------------------------------------

Чтобы вывести диалоговый блок пера, вызовите его метод
ChangePen. Программа STEP06B.PAS делает это в ответ на команду
cm_Pen, генерируемую выбором пункта меню Options¦Pen и щелчком
правой кнопкой "мыши".

procedure TStepWindow.CMPen(var Msg: TMessage);
begin
CurrentPen^.ChangePen; { CurrentPen - это объект
блока пера }
end;

procedure TStepWindow.WMRButtonDown(var Msg: TMessage);
begin
if not ButtonDown then CurrentPen^.ChangePen;
end;

Примечание: Данные методы можно найти в файле
STEP06B.PAS.



B.Pascal 7 & Objects/OW - 83 -

------------------------------------------------------------------
Глава 5. Повторное отображение графики
-----------------------------------------------------------------

В следующих трех шагах вы узнаете как

* Отображать по запросу графический образ.

* Сохранять образ файла и загружать его.

* Печатать образ.

Шаг 7: Вывод на экран графики
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦-Step-7:-Painting------¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

Возможно, вы удивитесь, узнав, что графика и текст, которые
вы рисуете в окне с помощью функций Windows типа TextOut или
LineTo, исчезают при изменении размера окна или повторного вывода
его на экран. После того, как графические данные переданы
Windows, через вызовы функций Windows вы никогда не получаете их
обратно для повторного отображения.

Чтобы в окне повторно отображалась графика, вы должны сохра-
нить эту графику (или данные для регенерации графики) в структуре
некоторого типа, такой как объект. С помощью объектов вы можете
полиморфически хранить простую или сложную графику, а также хра-
нить объекты и поля ваших оконных объектов.

Изображение и рисование
-----------------------------------------------------------------

Существует два способа получения в окне графического образа.
С рисованием вы уже знакомы по предыдущим примерам. При рисовании
вы создаете графический образ в реальном времени в ответ на ввод
данных пользователем. Но окно не может требовать от пользователя
воссоздания графики при обновлении экрана.

Окна должны иметь возможность воссоздавать по запросу свои
графические образы. Windows сообщает своим оконным объектам, ког-

B.Pascal 7 & Objects/OW - 84 -

да они требуют изображения или обновления. При этом окно должно
каким-то образом генерировать образ экрана. В ответ на необходи-
мость изображения. ObjectWindows автоматически вызывает метод
Paint вашего окна. Наследуемый и TWindow метод Paint не выполняет
никаких функций. В Paint вы должны поместить код для передачи со-
держимого окна. Фактически Paint вызывается при первом выводе ок-
на. Paint отвечает за обновление (при необходимости) изображения
текущим содержимым.

Существует еще одно важное отличие между отображением графи-
ки в методе Paint и другим ее отображением (например, в ответ на
действия "мышью"). Содержимое экрана, которое должно использо-
ваться для отображения, передается в параметре PaintDC, так что
вашей программе не требуется получать или освобождать его. Однако
вам потребуется вновь выбрать для PaintDC изобразительные средс-
тва.

Чтобы отобразить содержимое окна, вместо повторения тех
действий, которые привели к первоначальному изображению (DragDC),
вы используете PaintDC. Визуальный эффект будет тот же, что и при
первоначальном рисовании пользователем (аналогично проигрыванию
аудиозаписи концерта). Но чтобы "проигрывать" ее в методе Paint,
сначала вам нужно сохранить графику в виде объектов.

Сохранение графики в объектах
-----------------------------------------------------------------

Созданные программой Steps изображения на самом деле просто
представляют наборы различного числа линий. При каждой буксировке
"мыши" вы добавляете другую линию. А каждая линия - это на самом
деле определенный набор точек, соединенных линейными сегментами.
Чтобы сохранить и воспроизвести такие изображения, вам необходим
гибкий и расширяемый тип данных

Для размещения неизвестного числа линий или точек прекрасно
подходит определенный в модуле Objects тип TCollection. Это набор
объектов, который может динамически расширяться, когда вы добав-
ляете другие элементы. Этим наборам не важно, что они включают,
поэтому вы можете использовать один и тот же механизм и для ри-
сунка (набора линий), и для линии (набора точек).

Примечание: О наборах рассказывается в Главе 19 "Набо-
ры".

Концептуально вам нужно просто сделать так, чтобы окно знало
о своем содержимом, так что оно сможет обновить изображение. Окно
содержит рисунок, представляющий собой набор линий. Таким обра-
зом, вам нужно:

* Передать окну объект или поле, содержащее набор линий.

* Определить объект линии, который может отображаться.


B.Pascal 7 & Objects/OW - 85 -

* В ответ на сообщения "мыши" добавлять к сохраненным линиям
точки.

Добавление поля объекта
-----------------------------------------------------------------

Чтобы сохранить рисунок в виде набора линий, добавьте в
TStepWindow поле с именем Drawing. В любой момент Drawing содер-
жит текущий рисунок в виде набора объектов линий. Когда требуется
отобразить окно, оно использует для изображения линии данные, за-
писанные в Drawing.

Определение объекта линии
-----------------------------------------------------------------

Далее нужно ответить на вопрос, что такое линия. В шаге 4 вы
видели, что изображаемая линия представляет собой просто набор
точек, передаваемых из Windows в программу через сообщение
wm_MouseMove. Для представления линий и точек вам необходимы объ-
ектные типы. Поскольку эффективный объект изображения линии дол-
жен быть повторно используемой частью, создайте отдельный модуль,
определяющий объекты линий и точек.

TLine содержит всю информацию, необходимую для изображения
данной линии: перо и набор точек.

type
PLine = ^TLine;
TLine = object(TObject)
Points: PCollection;
LinePen: PPen;
constructor Init(APen: PPen);
constructor Load(var S: TStream);
destructor Done; virtual;
procedure AddPoint(AX, AY: Word);
procedure Draw(ADC: HDC);
procedure Store(var S: TStream);
end;

LinePen просто указывает на объект TPen, а Point - это набор
объектов точек. TLine и TLinePoint содержат методы Load и Store,
преимущества использования которых для записи картинок на диск вы
увидите в шаге 8. В отличие от них объект TLine весьма прост:
конструктор и деструктор создают и уничтожают LinePen, AddPoint
включает объект точки в Points, а Draw рисует линии между точками
Points.

Объект TLinePoint еще проще:

type
PLinePoint = ^TLinePoint;
TLinePoint = object(TObject)
X, Y: Integer;

B.Pascal 7 & Objects/OW - 86 -

constructor Init(AX, AY: Integer);
constructor Load(var S: TStream);
procedure Store(var S: TStream);
end;

constructor TLinePoint.Init(AX, AY: Integer);
begin
X := AX;
Y := AY;
end;

TLinePoint не определяет никакого нового поведения - это
просто объект данных, который должен использоваться в TLine. Но
позднее (в шаге 8) он понадобиться как объект для записи в поток.
Не забудьте построить в TStepWindow.Init Drawing и уничтожить его
в TStepWindow.Done:

constructor TStepWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherites Init(AParent, ATitle);
ButtonDown := False;
HasChanged := False;
CommonPen := New(PPen, Init(ps_Solid, 1, 0));
Drawing := New(PCollection, Init(50, 50));
end;

destructor TStepWindow.Done;
begin
Dispose(CommonPen, Done);
Dispose(Drawing, Done);
inherited Done;
end;

Основное окно программы Steps содержит набор в своем поле
Drawing набор линий. Когда пользователь рисует линии, вы должны
преобразовывать их в объекты и добавлять в Drawing. Затем, когда
потребуется отобразить окно, путем итерации Drawing нужно отобра-
зить каждую его точку.



B.Pascal 7 & Objects/OW - 87 -

Изменение методов работы с "мышью"
-----------------------------------------------------------------

Чтобы сохранять линии в виде объектов, вы должны изменить
EMLButtonDown и WMMouseMove, чтобы не только рисовать линии, но
также сохранять точки в наборе линий. Поскольку текущую линию
придется обновлять не только одному методу, добавьте в
TStepWindow еще одно поле типа PLine с именем CurrentLine:

type
TStepWindow = object(TWindow);
CurrentLine: PLine;
.
.
.
end;

Кроме добавления изображения линии WMLButttonDown создает
при каждом вызове новый объект линии и добавляет его в набор в
Drawing. WMMouseMove просто добавляет новую точку в конец объекта
текущей линии и изображает в окне линейные сегменты. Сохраняя все
точки всех линий, ваше окно будет записывать информацию, необхо-
димую для точного воспроизведения картинки.

procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
begin
if not ButtonDown then
begin
ButtonDown := True;
SetCapture(HWindow);
DragDC := GetDC(HWindow);
CommonPen^.Select(DragDC);
MoveTo(DragDC, Msg.lParamLo, Msg.lParamHi);
CurrentLine := New(PLine, Init(CommonPen));
Drawing^.Insert(CurrentLine);
end;
end.

procedure TStepWindow.WMMouseMove(var Msg: TMessage);
begin
if ButtonDown then
begin
LineTo(DragDC, Msg.lParamLo, Msg.lParamHi);
CurrentLine^.AddPoint(Msg.LParamLo, Msg.LParamHi);
end;
end;

Примечание: Уничтожать устаревшие CurrentLine не тре-
буется, поскольку они записаны в наборе Drawing. Все объек-
ты линий уничтожаются при уничтожении Drawing.

WMLButtonUp модификации не требует. Вам не нужно уничтожать
все объекты линий при очистке отображаемого окна, поэтому добавь-

B.Pascal 7 & Objects/OW - 88 -

те в CMFileNew вызов метода FreeAll:

procedure TStepWindow.CMFileNew(var Msg: TMessage);
begin
Drawing^.FreeAll;
InvalidateRect(HWindow, nil, True);
end;

Вывод сохраненной графики
-----------------------------------------------------------------

Теперь, когда TStepWindow сохраняет свою текущую строку, вы
должны научить его по команде (этой командой является Paint) ри-
совать ее. Давайте напишем для TStepWindow метод Paint, который
повторяет действия WMLButtonDown, WMMouseMove и WMLButtonUp. Пу-
тем итерации по набору линий Paint воссоздает картинку аналогично
тому, как это делаете вы. Метод Paint имеет следующий вид (см.
файл STEP07.PAS):

procedure TStepWindow.Paint(PaintDC: HDC; var PaintInfo:
TPintStruct);

procedure DrawLine(P: PLine); far;
begin
P^.Draw(PaintDC);
end;

begin
Drawing^.ForEach(@DrawLine);
end;

Примечание: Итерация методов описывается в Главе 19
"Наборы".

Метод Draw объекта линии для изображения каждой линии между
точками также использует итератор ForEach:

procedure TLine.Draw(ADC: HDC);
var First: Boolean;

procedure DrawLine(P: PLinePoint); far;
begin
if First then MoveTo(ADC, P^.X, P^.Y)
else LineTo(ADC, P^.X, P^.Y);
First := False;
end;

begin
First := True;
LinePen^.Select(ADC);
Points^.ForEach(@DrawLine);
LinePen^.Delete;
end;

B.Pascal 7 & Objects/OW - 89 -

------------------------------------------------------------------
Шаг 8: Сохранение рисунка в файле
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦-Step 8:-Streams-------¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

Теперь, когда вы сохранили представление данных в виде ри-
сунка как часть оконного объекта, можно легко записать эти данные
в файл (фактически, в буферизованный поток DOS) и считать его об-
ратно.

Данный шаг посвящен добавлению полей объектов для записи
состояния сохраняемой информации, модификации сохраняемой в файле
информации и методам открытия. Используя предусмотренные в
ObjectWindows потоковые объекты, вы убедитесь в удобстве их при-
менения для сохранения данных в файле.

Отслеживание состояния
-----------------------------------------------------------------

Требуется отслеживать две характеристики рисунка. Изменение
файла мы уже отслеживали (в шаге 1 было добавлено поле
HasChanged), но теперь нужно знать, загружен ли файл в данный мо-
мент. Как и HasChanged, IsNewFile - это атрибут TStepWindow типа
Boolean, поэтому его также следует сделать полем:

TStepWindow = object(TWindow)
ButtonDown, HasChanged, IsNewFile: Boolean;
.
.
.
end.

Поле HasChanged принимает значение True, если текущий рису-
нок модифицирован. Модификация означает, что рисунок был изменен
с момента последнего сохранения или не сохранялся вовсе. Вы уже
устанавливаете поле HasChanged в True, когда пользователь начина-
ет рисовать, и в False, когда окно очищается. Когда пользователь
открывает новый файл или сохраняет существующий, HasChanged сле-
дует установить в False.


B.Pascal 7 & Objects/OW - 90 -

IsNewFile указывает, что рисунок не взят из файла, поэтому
сохранение рисунка потребует от пользователя задать имя файла.
IsNeFile имеет значение True только при первоначальном запуске
приложения и после выбора пользователем команды меню File¦New
(Файл¦Новый). Это поле устанавливается в False, когда файл откры-
вается или сохраняется. Фактически, FileSave использует
IsNewFile, чтобы увидеть, можно ли сохранить файл немедленно, или
пользователю требуется выбрать файл из файлового диалога.

Приведем методы сохранения и загрузки файла. На данный мо-
мент они выполняют только сохранение и загрузку файлов. Сохране-
ние файла сконцентрировано в одном новом методе, который называ-
ется WriteFile, а открытие файла выполняет метод ReadFile.

procedure TStepWindow.CMFileNew(var Msg: TMessage);
begin
if CanClose then
begin
Drawing^.FreeAll;
InvalidateRect(HWindow, nil, True);
HasChanged := False;
IsNewFile := True;
end;
end;

procedure TStepWindow.CMFileOpen(var Msg: TMessage);
begin
if CanClose then
if Application^.ExecDialog(New(PFileDialog, Init(@Self,
PChar(sd_FileOpen), StrCopy(FileName, '*.PTS')))) =
id_Ok then ReadFile;
end;

procedure TStepWindow.CMFileSave(var Msg: TMessage);
begin
if IsNewFile then CMFileSaveAs(Msg) else WriteFile;
end;

procedure TStepWindow.CMFileSaceAs(var Msg: TMessage);
begin
if IsNewFile then StrCopy(FileName, '');
if Application^.ExecDialog(New(PFileDialog,
Init(@Self, PChar(sd_FileSave), FileName))) =
id_Ok then WriteFile;
end;

procedure TStepWindow.ReadFile;
begin
MessageBox(HWindow, @FileName, 'Загрузить файл:',
mb_Ok);
HasChanged := False;
IsNewFile := False;
end;

B.Pascal 7 & Objects/OW - 91 -


procedure TStepWindow.WriteFile;
begin
MessageBox(HWindow, @FileName, 'Сохранить файл:',
mb_Ok);
HasChanged := False;
IsNewFile := False;
end;

Примечание: Данный текст программы можно найти в файле
STEP08A.PAS.

Сохранение и загрузка файлов
-----------------------------------------------------------------

Теперь, когда вы создали основную схему для построения и
загрузки файлов, осталось только выполнить фактическую загрузку и
сохранение в файле наборов точек. Для этого можно использовать
потоковый механизм автоматического сохранения объекта. Сначала вы
научитесь сохранять и загружать сами объекты точек и линий (как
это сделать для наборов вы уже знаете). Затем методы WriteFile и
FileOpen будут модифицированы для использования потоков.

Примечание: Подробнее об использовании потоков с объ-
ектами рассказывается в Главе 20 "Потоки".

Ниже приведен исходный код, показывающий как сохранять и
загружать сами объекты TLine и TLinePoint:

const
RLinePoint: TStreamRec = (
ObjType: 200;
VmtLink: Ofs(TypeOf(TLinePoint)^);
Load: @TLinePoint.Load;
Store: @TLinePoint.Store);

RLine: TStreamRec = (
ObjType: 201;
VmtLink: Ofs(TypeOf(TLine)^);
Load: @TLine.Load;
Store: @TLine.Store);

constructor TLinePoint.Load(var S: TStream);
begin
S.Read(X, SizeOf(X));
S.Read(Y, SizeOf(Y));
end;

procedure TLinePoint.Store(var S: TStream);
begin
S.Write(X, SizeOf(X));
S.Write(Y, SizeOf(Y));
end;

B.Pascal 7 & Objects/OW - 92 -


constructor TLine.Load(var S: TStream);
begin
Points := PCollection(S.Get);
LinePen := PPen(S.Get);
end;

procedure TLine.Store(var S: TStream);
begin
S.Put(Points);
S.Put(LinePen);
end;

procedure StreamRegistration;
begin
RegisterType(RCollection);
end;

Для регистрации TCollection при запуске прикладной программы
вы должны вызывать StreamRegistration (который находится в
Steps). Вы можете поместить это вызов в метод TStepWindow.Init.
Модуль DrawLine регистрирует в своем коде инициализации
TLinePoint и TLine, поэтому линии и точки регистрируются простым
включением DrawLine в оператор uses.

Заключительным шагом изменения методов WriteFile и ReadFile
будет фактическая запись в потоки и чтение из них (см.
STEP08B.PAS):

procedure TStepWindow.ReadFile;
var
TempColl: PCollection;
TheFile: TDosStream;
begin
TheFile.Init(FileName, stOpen);
TempColl: := PCollection(TheFile.Get);
TheFile.Done;
if TempColl <> nil then
begin
Dispose(Drawing, Done);
Drawing := TempColl;
InvalidateRect(HWindow, nil, True);
end;
HasChanged := False;
IsNewFile := False;
end;

procedure TStepWindow.WriteFile;
var
TheFile: TDosStream;
begin
TheFile.Init(FileName, stCreate);
TheFile.Put(Drawng);

B.Pascal 7 & Objects/OW - 93 -

TheFile.Done;
IsNewFile := False;
HasChanged := False;
end;



B.Pascal 7 & Objects/OW - 94 -

------------------------------------------------------------------
Шаг 9: Печать графического образа
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦-Step-9:-Printing------¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

Печать из Windows может представлять собой сложную задачу,
но ObjectWindows предоставляет простой механизм добавления
средств печати в вашу прикладную программу.

Добавление этих средств предусматривает следующие три шага:

* Построение объекта принтера.

* Создание объекта распечатки.

* Печать объекта распечатки.


Построение объекта принтера
-----------------------------------------------------------------

Любая программа ObjectWindows может получить доступ к прин-
теру с помощью объекта типа TPrinter. В этом случае основное окно
вашего приложения должно построить объект принтера и сохранить
его в объектном поле с именем Printer:

constructor TStepWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
.
.
.
Printer := New(PPrinter, Init);
end;

Примечание: Тип TPrinter определен в модуле OPrinter,
поэтому не забудьте добавить OPrinter в свой оператор uses.

Это все, что обычно приходится делать для инициализации объ-

B.Pascal 7 & Objects/OW - 95 -

екта принтера. По умолчанию TPrinter использует назначенный по
умолчанию принтер, заданный в файле WIN.INI. TPrinter предусмат-
ривает также механизм для выбора альтернативных принтеров.


Создание объекта распечатки
-----------------------------------------------------------------

ObjectWindows управляет выводимыми на печать данными точно
также, как выводом на экран. То есть вместо записи непосредствен-
но на устройство вывода (или даже непосредственно в Windows) вы
направляете свой вывод на объект, который знает, как представлять
информацию на устройстве вывода. Для вывода на печать используйте
объекты, полученные из абстрактного типа TPrintout и метода с
именем PrintPage.

Существует два основных случая, с которыми вы будете иметь
дело при генерации из приложения Windows данных для печати: пе-
чать документов и печать содержимого окна. Печать содержимого ок-
на проще, поскольку окна уже "знают" о своем представлении. Как
оказывается на самом деле, это просто особый случай печати доку-
мента.

Примечание: О печати документов рассказывается в Главе 15.

Запись в контекст устройства
-----------------------------------------------------------------

До настоящего момента вы всегда записывали текст и графику в
контекст дисплея (что является способом представления в Windows
пользовательской области окон). Контекст дисплея - это просто
специализированная версия контекста устройства - механизма, через
который приложения Windows работают с различными устройствами,
такими как экраны, принтеры или коммуникационные устройства.

Запись в контекст одного устройства мало отличается от запи-
си в контекст другого, поэтому, например, достаточно просто сооб-
щить, например, оконному объекту, что для записи на принтер нужно
использовать механизм Paint.

Создание распечатки окна
-----------------------------------------------------------------

TWindowPrint - это специальный потомок TPrintout, используе-
мый для печати содержимого окна. Печать содержимого окна не
представляет сложности по двум причинам: вы имеете дело только с
одной страницей, и объект окна уже знает, как отображать свой об-
раз.

Обычно при печати документа вашей прикладной программе нужно
выполнять итерации процесса печати для каждой страницы документа.
Так как окно имеет только один образ, при печати окна это не яв-
ляется необходимым.

B.Pascal 7 & Objects/OW - 96 -


Таким образом, печать также проста, как ориентация метода
Paint объекта окна на вывод вместо окна в контекст устройства,
подходящий для принтера:

procedure TWindowPrint.PrintPage(DC: HDC; Page: Word;
Size: TPoint; var Rect: TRect; Flags: Word);
var PS: TPaintStruct;
begin
Window^.Paint(DC, PS);
end;

Поскольку переданный PrintPage параметр DC уже указывает на
подходящий для принтера контекст устройства, в простейшем случае
PrintPage должен только сообщить объекту окна на вывод его содер-
жимого в этот контекст устройства. Теперь ваш объект распечатки
также знает о своем представлении.

Вывод распечатки
-----------------------------------------------------------------

Наличие объекта распечатки, которому известно о своем предс-
тавлении, это все, что нужно для передачи распечатки на принтер.
Программа Steps делает это в ответ на команду cm_FilePrint, гене-
рируемую командой Print меню File:

procedure TStepWindow.CMFilePrint(var Msg: TMessage);
var P: PPrintout;
begin
if IsNewFile then StrCopy(FileName, 'Untitled');
P := New(PWindowPrint, Init(FileName, @Self));
Printer^.Print(@Self, P);
Dispose(P, Done);
end;

CMFilePrint очень просто строит объект распечатки, озаглав-
ленный заданным именем (имя файла точек или 'Untitled') и запол-
няет его своим содержимым (так как это единственное окно в при-
ложении).

При наличии объекта распечатки CMFilePrint сообщает объекту
принтера, что его нужно напечатать, добавив какие-либо сообщения
об ошибках или диалоговые окна (отсюда параметр @Self). Когда пе-
чать закончится, CMFilePrint уничтожает объект распечатки.

Выбор другого принтера
-----------------------------------------------------------------

Одним из средств TPrinter является возможность изменения ус-
тановки принтера. TPrinter определяет метод Setup, который выво-
дит диалоговое окно установки принтера, позволяющее пользователю
выбрать принтер из числа установленных в Windows и обеспечивает
доступ к диалоговому окну конфигурации устройства.

B.Pascal 7 & Objects/OW - 97 -


Чтобы вывести диалоговое окно установки принтера, ваша прик-
ладная программа вызывает метод Setup объекта принтера. Steps де-
лает это в ответ на команду cm_FileSetup (см. STEP09.PAS):

procedure TStepWindow.CMFileSetup(var Msg: TMessage);
begin
Printer^.Setup(@Self);
end;

Диалоговое окно установки принтера является экземпляром типа
TPrinterSetupDlg (см. Рис. 5.1).

----------------------------------------------------------------¬
¦-=---------------------Select Printer--------------------------¦
+---------------------------------------------------------------+
¦ ¦
¦ Принтер и порт ¦
¦ ---------------------------------------------T-¬ ¦
¦ ¦PostScript Printer on LPT1:-----------------¦v¦ ¦
¦ L--------------------------------------------+-- ¦
¦ ------------¬ ------------¬ ------------¬ ¦
¦ ¦----OK-----¦ ¦--Setup----¦ ¦--Cancel---¦ ¦
¦ L------------ L------------ L------------ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 5.1 Диалоговое окно установки принтера.

В комбинированном блоке диалогового окна выводятся принтеры,
заданные в WIN.INI. Это дает пользователю возможность доступа ко
всем установленным принтерам.



B.Pascal 7 & Objects/OW - 98 -

------------------------------------------------------------------
Глава 6. Вывод всплывающего окна
-----------------------------------------------------------------

Итак, вы создали два типа окон - основное окно (объект
TStepWindow) и режимные дочерние окна, которые создаются и унич-
тожаются каждый раз, когда они необходимы (например, блоки сооб-
щений). Однако, в полноценной программе Windows дочерние окна
часто требуется сохранять активными в течении неопределенного пе-
риода времени (в качестве примера можно привести окно оперативной
полосы SpeedBar в работающей под Windows интегрированной среде
IDE).

До сих пор все дочерние окна в Steps имели фиксированный
размер и создавались из шаблонов ресурсов. В шагах 10 - 12 вы бу-
дете делать следующее:

* Создавать окна с динамическим размером, сохраняющиеся на
все время работы программы.

* Добавлять в окно специализированные управляющие элементы.

* Создавать собственное интерактивное окно.

Наконец, мы дадим предложения по дальнейшему расширению
программы Steps.

Шаг 10: Добавление всплывающего окна
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦-Step-10:-Palette------¦
¦ Step 11: BWCC ¦
¦ Step 12: Custom ctrls ¦
L------------------------

Создание и уничтожение окон и диалоговых блоков прекрасно
подходит, когда они используются не часто. Но в некоторых случаях
желательно иметь дочернее окно, доступное большую часть времени.
Примером такого окна является инструментальная палитра.

В этом шаге вы будете делать следующее:

* Добавите к основному окну поле.


B.Pascal 7 & Objects/OW - 99 -

* Построите плавающую палитру пера.

* Выведете и скроете палитру пера.

Палитра пера, которая выводится при выборе пользователем ко-
манды Palette¦Show (Палитра¦Вывод) показана на Рис. 6.1.

----------------------------------------¬
¦-=--------------PenPalette-------------¦
+------------------T--------------------+
¦ -- Add ¦ Delete ¦
¦ ------ Pen ¦ ------ Pen ¦
¦ -- ¦ ¦
+------------------+--------------------+
¦ ¦
¦ ----------------------------- ¦
¦ ¦
+---------------------------------------+
¦ ¦
¦ ----------------------------- ¦
¦ ¦
+---------------------------------------+
¦ ¦
¦ ----------------------------- ¦
¦ ¦
L----------------------------------------

Рис. 6.1 Палитра пера программы Steps с тремя перьями.

Поскольку это первое "новое" окно, которое вы создаете и ко-
торое будет создаваться автоматически, неплохо рассмотреть, как
создаются и выводятся на экран объекты и элементы окна.

Добавление к окну дочернего окна
-----------------------------------------------------------------

Режимными дочерними окнами, которые вы до сих пор использо-
вали, управлять легко. Вы можете их создать, использовать и отме-
нить. Но здесь нам нужно работать с диалоговым окном, которое не
является режимным. Например, оно должно закрываться, если закры-
вается приложение, и становиться скрытым, если основное окно ми-
нимизируется.

В основном поведение диалоговых окон (такое как закрытие или
когда окно временно становится скрытым) автоматическое. Единс-
твенный случай, когда вам нужно делать что-то особенное - это
когда вы хотите сделать дочернее окно независимым от его порожда-
ющего окна. Например, окно палитры, которое вы собираетесь выво-
дить, сможет скрываться и выводиться, пока основное окно остается
видимым. Окна, которые могут перемещаться или выводиться незави-
симо от своих порождающих окон, называются независимыми дочерними
окнами. На следующем шаге вы будете создавать зависимые окна,
присоединенные к независимому окну палитры.

B.Pascal 7 & Objects/OW - 100 -


Так как основное окно должно посылать команды окну палитры,
потребуется указатель на это окно, поэтому добавьте в TStepWindow
его палитры пера. TStepWindow содержит теперь следующие поля:

TStepWindow = object(TWindow)
DragDC: DHC;
ButtonDown: Boolean;
FileName: array[0..fsPathName] of Char;
HasChanged, IsNewFile: Boolean;
Drawing: PCollection;
Printer: PPrinter;
PenPalette: PPenPalette; { окно палитры }
.
.
.
end;

Осталось только построить объект дочернего окна и присвоить
его PenPalette. Этому посвящен следующий раздел.

Построение окна палитры
-----------------------------------------------------------------

Объекты дочернего окна строятся обычно в конструкторах своих
порождающих окон. Аналогично тому, как это происходит при инициа-
лизации любого другого поля, вы присваиваете значения любому ука-
зателю дочернего окна. В данном случае:

constructor TStepWindows.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
.
.
.
PenPalette := New(PPenPalette, Init(@Self, 'Pan Palette');
end;

Назначение порождающего окна
-----------------------------------------------------------------

Порождающие окна автоматически управляют своими дочерними
окнами, поэтому при создании дочернего окна ему передается указа-
тель на объект порождающего окна. Поскольку порождающее окно
обычно строит свои дочерние окна, указателем порождающего окна
обычно является @Self.

Важным исключением является основное окно приложения. Так
как оно не имеет порождающего окна, конструктору основного окна
передается nil.

Каждый оконный объект имеет список своих дочерних окон,

B.Pascal 7 & Objects/OW - 101 -

обеспечивающий создание, вывод, сокрытие и закрытие окон в нужные
моменты. Построение и уничтожение дочерних окон автоматически об-
новляет список дочерних окон: построение дочернего окна добавляет
его в список своего порождающего окна; уничтожение окна удаляет
его из списка.



B.Pascal 7 & Objects/OW - 102 -

Создание элементов экрана
-----------------------------------------------------------------

При построении объекта дочернего окна, ObjectWindows берет
на себя функции по работе с соответствующими объекту элементами
экрана. Это обратно тому, что вы делали в шаге 6 с помощью
InitResource. Тогда вы имели созданный из ресурса элемент экрана
и связывали с ним объект, благодаря чему могли манипулировать
элементом экрана. Теперь вы создали собственный объект, и вам
нужно сообщить Windows о необходимости создания соответствующего
экранного элемента.

Когда вы в шаге 3 делали это для диалогового окна, то вызы-
вали ExecDialog. Метод TApplication создает элемент экрана и вы-
полняет режимное диалоговое окно. Соответствующим методом для не-
режимных (или безрежимных) диалоговых окон является
TApplication.MakeWindow. Основным отличием является то, что
MakeWindow не выводит автоматически создаваемый элемент экрана и
не переходит в режимное состояние.

Примечание: MakeWindow и создание элементов экрана
подробно описываются в Главе 9 "Интерфейсные объекты".

Тогда процесс построения и вывода окна состоит из следующих
трех этапов:

* Построение оконного объекта с помощью Init.

* Создание элемента экрана с помощью MakeWindow.

* Вывод окна с помощью Show.

К счастью, второй и третий шаги для основного окна приложе-
ния выполняются автоматически. Кроме того, вызов для порождающего
окна MakeWindow автоматически вызывает для любого окна в его
списке дочерних окон MakeWindow, так что дочерние окна основного
окна (такие как палитра пера) автоматически получают элементы эк-
рана.

В следующем разделе мы выведем дочернее окно.



B.Pascal 7 & Objects/OW - 103 -

Вывод и сокрытие палитры
-----------------------------------------------------------------

Дочерние окна, отличные от тех, которые были созданы и выве-
дены по умолчанию их порождающими окнами (этот процесс управляет-
ся с помощью EnableAutoCreate и DisableAutoCreate) в каждом ин-
терфейсном объекте. Но вы можете скрыть или вывести дочернее окно
по команде. Обе функции метода Show наследуются из
TWindowsObject.

В зависимости от передаваемых параметров метод Show выводит
либо скрывает окно. Параметр - это одна из констант sw_, опреде-
ленная в Windows. В ответ на команды меню Palette¦Show (Палит-
ра¦Вывод) или Palette¦Hide (Палитра¦Сокрытие), которые генериру-
ют, соответственно, команды cm_PalShow и cm_PalHide, TStepWindow
вызывает метод Show палитры пера (это дополняет STEP10.PAS):

procedure TStepWindow.CMPalShow(var Msg: TMessage);
begin
PenPalette^.Show(sw_ShowNA);
end;

procedure TStepWindow.CMPalHide(var Msg: TMessage);
begin
PenPalette^.Show(sw_Hide);
end;

Если у вас есть поле порождающего окна, указывающее на до-
чернее окно, с которым необходимо работать, вы легко можете за-
дать дочернее окно.

------------------------------------------------------------------
Шаг 11: добавление специализированных управляющих элементов
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦-Step-11:-BWCC---------¦
¦ Step 12: Custom ctrls ¦
L------------------------

В шаге 10 вы добавили к основному окну независимое дочернее
окно. Теперь вы добавите зависимые дочерние окна, которые называ-
ются управляющими элементами. Порождающим окном этих управляющих
элементов является окно палитры пера. Нужно помнить о том, что

B.Pascal 7 & Objects/OW - 104 -

окно палитры пера является независимым дочерним окном, для кото-
рого порождающим окном является основное окно. Таким образом, па-
литра пера является одновременно дочерним окном основного окна и
порождающим окном для управляющих элементов.

В шаге 6 вы уже имели дело с управляющими элементами и объ-
ектами управляющих элементов, но тогда вы просто связывали объек-
ты с управляющими элементами, определенными в ресурсе. Построение
управляющего элемента на этапе выполнения несколько сложнее, так
как наряду с типом как вам требуется задать позицию и размер уп-
равляющих элементов.

Палитра пера, показанная на Рис. 6.1, использует две специа-
лизированные кнопки кисти и последовательность графических изоб-
ражений, каждое из которых представляет перо, которое можно ис-
пользовать для рисования. Эти перьевые "кнопки" фактически не яв-
ляются управляющими элементами, а скорее представляют собой обра-
зы в единственным дочернем окне, которое может их идентифициро-
вать при щелчке "мышью".

В данном шаге вы добавите графические кнопки с помощью:

* добавления простых управляющих кнопок;

* реализации в программе специализированных управляющих эле-
ментов;

* определения графических изображений для кнопок.

Для всех управляющих элементов, в целом, поведение задается
типом ObjectWindows TControl и его типом-потомком, позволяющим
работать с каждым типом управляющего элемента. Например, TListBox
определяет объекты блока списка, а TEdit определяет каждый управ-
ляющий объект редактирования. Вы должны также понимать, что
TControl - это потомок TWindow.


Добавление к палитре командных кнопок
-----------------------------------------------------------------

Хотя они ведут себя идентично, между управляющими кнопками
диалоговых блоков (таких как файловое диалоговое окно) и управля-
ющими элементами окон (таких как окно палитры) имеется существен-
ное различие. Управляющие элементы диалогового блока вы можете
задать в ресурсе диалогового блока. Они не являются объектами, и
диалоговый блок, которому они принадлежат, полностью отвечает за
управление этими элементами. В Главе 11 показано, как создать из
диалоговых ресурсов свои собственные диалоговые блоки и работать
с их управляющими элементами.

Управляющие элементы окон задаются определением объекта. По-
рождающее окно управляет их поведением через методы, определенные
объектами управляющих элементов ObjectWindows. Например, чтобы

B.Pascal 7 & Objects/OW - 105 -

получить следующий элемент, который пользователь выбрал в блоке
списка, вызовите метод GetSelString объекта блока. Аналогично
оконному объекту или объекту диалогового блока, объект управляю-
щего элемента имеет соответствующий визуальный элемент.

Объект управляющего элемента и его управляющий элемент свя-
заны через поле идентификатора объекта управляющего элемента.
Каждый управляющий элемент имеет уникальный идентификатор, кото-
рый используется его порождающим окном для идентификации управля-
ющего элемента при маршрутизации управляющих событий (таких как
щелчок на элементе "мышью"). Для ясности для каждого идентифика-
тора управляющего элемента следует определить следующие констан-
ты:

const
id_Add = 101;
id_Del = 102;
MaxPens = 9;

MaxPens задает максимальное число перьев, которые будет со-
держать палитра. Значение 9 хорошо подходит для стандартного эк-
рана VGA.

Объекты управляющих элементов как поля
-----------------------------------------------------------------

Как и в случае других дочерних окон, часто удобно хранить
указатель объекта управляющего элемента в виде поля. Это необхо-
димо только для дочерних окон, с которыми вы позднее сможете ра-
ботать непосредственно, вызывая методы их объектов. TPenPalette
записывает каждый из этих объектов управляющих элементов в от-
дельном поле. Приведем часть описания объекта TPenPalette:

TPenPalette = object(TWindow)
AddBtn, DelBtn: PButton;
.
.
.
end;

После создания экземпляра этих дочерних объектов управляющих
элементов вы можете манипулировать ими с помощью вызовов методов.
Например, в соответствующие моменты можно разрешать или запрещать
командные кнопки, вызывая их методы Enable и Disable. Используя
метод ChildList порождающего окна, можно получить доступ к объек-
там управляющих элементов дочерних окон, не записанных в полях,
но гораздо удобнее делать это с помощью полей.


Работа с управляющими элементами
-----------------------------------------------------------------

Любой тип окна, который имеет объекты управляющих элементов

B.Pascal 7 & Objects/OW - 106 -

(или другое дочернее окно) должен определять для построения своих
объектов управляющих элементов конструктор Init. Кроме того, для
задания управляющих элементов перед выводом вы можете переопреде-
лить SetupWindow. Порождающее окно (TPenPalette) автоматически
создает и выводит все свои дочерние окна.

Приведем в качестве примера метод Init палитры пера. Первое,
что он делает - это установка собственного расположения и атрибу-
тов размера. Так как метод Init окна отвечает за задание его ат-
рибутов создания, и поскольку вместе с ним создаются управляющие
элементы окна, вы должны также в методе Init окна построить уп-
равляющие элементы. В каждом вызове конструктора первый параметр
- это @Self (порождающее окно). За ним следует идентификатор уп-
равляющего элемента.

constructor TPenPalette.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
with Attr do
begin
Style := Style or ws_Tiled or ws_SysMenu or
ws_Visible;
W := 133;
H := GetSystemMetrics(sm_CYCaction) + 42;
AddBtn := New(PButton, Init(@Self, id_Add,
'Добавить перо', 0, 0, 65, 40, True);
DelBtn := New(PButton, Init(@Self, id_Del,
'Удалить перо', 0, 0, 65, 40, False);
end;

После создания окна, чтобы задать управляющие элементы окна,
вызывается виртуальный метод TPenPalette.SetupWindow. Поскольку
здесь вы имеете дело только с командными кнопками, инициализация
не требуется, но TPenPalette.SetupWindow первоначально запрещает
одну из командных кнопок. Если бы вы использовали другой управля-
ющий элемент (например, блок списка), то для инициализации объек-
та управляющего элемента потребовалось бы вызывать SetupWindow.

Примечание: Когда вы переопределяете метод SetupWindow
окна, не забудьте сначала вызывать наследуемый метод
SetupWindow, так как он создает все дочерние управляющие
элементы.

Вызов методов Init и SetupWindow вполне достаточен для пра-
вильного вывода в окне палитры всех управляющих элементов. Коман-
дные кнопки можно будет активизировать ("нажимать"), но без ка-
ких-либо действий. В шаге 12 мы определим реакцию на события уп-
равляющего элемента.


Сокрытие вместо закрытия
-----------------------------------------------------------------

B.Pascal 7 & Objects/OW - 107 -


Если вы дважды щелкните "мышью" в блоке системного меню па-
литры пера, оно исчезнет. Выбор команды Palette¦Show не может
больше выводить палитру, так как объект и его экранные элементы
уничтожены. Выводить нечего. Вы можете переопределить это, доба-
вив метод CanClose, который скрывает окно, а затем запрещает его
закрытие (см. STEP11A.PAS):

function TPenPalette.CanClose: Boolean;
begin
Show(sw_Hide);
CanClose := False;
end;

Теперь двойной щелчок "мышью" в блоке системного меню скры-
вает окно, но не закрывает его, так что позднее вы можете вывести
его снова.

Обычно наличие дочернего окна, которое всегда возвращает из
CanClose False, может предотвратить закрытие всего приложения. Но
TStepWindow перед закрытием не проверяет своих дочерних окон, так
как в шаге 1 вы переопределили его метод CanClose.


Разрешение специализированных управляющих элементов
-----------------------------------------------------------------

Как вы уже знаете, объекты управляющих элементов создают
стандартные управляющие элементы Windows. Например, только что
созданный вами объект TButton дает в результате в палитре окна
стандартную серую кнопку. Однако IDE и диалоговые блоки, создан-
ные вами из ресурсов, используют кнопки другого типа с располо-
женными на них графическими изображениями. ObjectWindows предос-
тавляет вам простой способ использования в программах командных
кнопок такого вида.

Использовать специализированные управляющие элементы Borland
для Windows (BWCC) также просто, как использование модуля. Для
этого нужно просто добавить BWCC в оператор uses основной прог-
раммы. Это немедленно дает два эффекта. Первый состоит в том, что
все стандартные диалоговые блоки (такое как файловое диалоговое
окно, которое вы уже добавили в программу Steps) используют для
таких общих элементов как кнопки OK или Cancel, а также кнопки с
зависимой и независимой фиксацией, вместо стандартных управляющих
элементов специализированные.

Примечание: Об использовании и проектировании специа-
лизированных управляющих элементов Borland рассказывается в
Главе 12.

Фактически, после добавления в оператор uses программы Steps
BWCC вы можете перекомпилировать программу и получить доступ к
диалоговым блокам. Без каких-либо других усилий вы существенно

B.Pascal 7 & Objects/OW - 108 -

улучшите внешний вид программы и ее интерфейса.

Но как насчет кнопок в палитре пера? Они были созданы из уп-
равляющих объектов с BWCC, используемых в программе, но выглядят
как обычные командные кнопки. Ответ, конечно, состоит в том, что
вы еще не определили для кнопок, графические изображения, так что
по умолчанию они просто используют метки, переданные в конструк-
торе Init. В следующем разделе вы увидите, как добавлять к специ-
ализированным управляющим элементам графические изображения.



B.Pascal 7 & Objects/OW - 109 -

Создание для командных кнопок графических изображений
-----------------------------------------------------------------

Хотя вы можете создавать графические изображения (битовые
массивы) для командных кнопок на этапе выполнения, эту задачу
следует решать с помощью ресурсов. Используя пакет разработчика
ресурсов, вам нужно создать для каждой командной кнопки три раз-
личных битовых массива: один для позиционирования сверху, один
для позиционирования снизу и один для позиционирования в фокусе
ввода.

С этими графическими изображениями вы можете делать что
угодно. Здесь ограничений нет. Вы можете изменять цвет образа в
зависимости от состояния кнопки или перемещать образы (что обычно
используется) при нажатии, а также добавлять вокруг текста кнопки
линию из точек, когда она находится в фокусе (активна). На Рис.
6.2 показано три графических изображения для командной кнопки Add
Pen палитры пера.

-------------------¬ -------------------¬ -------------------¬
¦ ¦- ¦ ..... ¦ ¦ ..... ¦-
¦ -- Add ¦- ¦ -- : Add : ¦ ¦ -- : Add : ¦-
¦ ------ Pen ¦- ¦ ------ : Pen : ¦ ¦ ------ : Pen : ¦-
¦ -- ¦- ¦ -- ..... ¦ ¦ -- ..... ¦-
¦ ¦- ¦ ¦ ¦ ¦-
L-------------------- L------------------- L--------------------
------------------- -------------------

Рис. 6.2 Графические изображения для специализированной
кнопки.

Нумерация ресурсов графических изображений
-----------------------------------------------------------------

B.Pascal 7 & Objects/OW - 110 -


Единственная сложная часть в определении графических изобра-
жений для командных кнопок - это присваивание идентификаторов ре-
сурсов. Управляющие элементы BWCC знают о том, какое графическое
изображение использовать, основываясь на идентификаторе конкрет-
ного управляющего элемента. Для командных кнопок в системах с VGA
для ресурсов используется 1000 + идентификатор для "верхнего" об-
раза, 3000 + идентификатор для "нижнего" образа и 5000 + иденти-
фикатор для образа в фокусе.

Примечание: В системах с EGA используются, соответс-
твенно, ресурсы 2000 + идентификатор, 4000 + идентификатор
и 6000 + идентификатор.

Так как командная кнопка Add Pen имеет идентификатор 101
(id_Add), разрешение использования BWCC принимает вид ресурсов
1101, 3101 и 5101. В программе STEP11B.PAS, для доступа к специа-
лизированными графическим изображениям, для командных кнопок Add
Pen и Del Pen, используется директива:

{$R PENTAL.RES}



B.Pascal 7 & Objects/OW - 111 -

------------------------------------------------------------------
Шаг 12: Создание специализированного управляющего элемента окна
-----------------------------------------------------------------

------------------------¬
¦ Step 1: Basic App ¦
¦ Step 2: Text ¦
¦ Step 3: Lines ¦
¦ Step 4: Menu ¦
¦ Step 5: About Box ¦
¦ Step 6: Pens ¦
¦ Step 7: Painting ¦
¦ Step 8: Streams ¦
¦ Step 9: Printing ¦
¦ Step 10: Palette ¦
¦ Step 11: BWCC ¦
¦-Step-12:-Custom-ctrls-¦
L------------------------

Наиболее интересная часть создания палитры пера - это созда-
ние вашего собственного специализированного окна палитры. Здесь
вы можете, наконец, использовать возможности, предусмотренные
стандартными инструментальными средствами окон, и что-то создать.

На этом шаге вы сделаете следующее:

* реализуете динамическое изменение размера окна палитры;

* зададите реакцию на уведомляющие сообщения от управляющих
элементов;

* создадите объект палитры с несколькими областями.

Динамическое изменение размеров палитры
-----------------------------------------------------------------

Так как каждое перо, которое вы сохраняете в палитре, имеет
один и тот же размер (40 элементов изображения высотой и 128 ши-
риной), вам нужно убедиться, что окно палитры может увеличиваться
и сжиматься на этот размер каждый раз, когда вы удаляете перо.
Объект TPenPalette определяет два метода, которые позволяют это
делать: Grow и Shrink.

procedure TPenPalette.Grow
var WindowRect: TRect;
begin
GetWindowRect(HWindow, WindowRect);
with WindowRect do
MoveWindow(HWindow, left, top, right - left,
bottom - top + 40, True);
end;

procedure TPenPalette.Shrink;
var WindowRect: TRect;

B.Pascal 7 & Objects/OW - 112 -

begin
GetWindowRect(HWindow, WindowRect);
with WindowRect do
MoveWindow(HWindow, left, top, right - left,
bottom - top - 40, True);
end;

Оба метода находят координаты границ окна, модифицируют их и
сообщают окну, что нужно использовать новые координаты границ.
Функция API GetWindowRect возвращает структуру TRect, содержащую
верхнюю, нижнюю, левую и правую координату. Grow добавляет в ниж-
нюю область окна 40 элементов изображения, а Shink вычитает тот
же объем.

В следующем разделе вы узнаете, как вызывать методы Grow и
Shrink в ответ на нажатие командных кнопок Add Pen и Del Pen.

Реакция на события управляющих элементов
-----------------------------------------------------------------

Основное различие между окном палитры и режимными диалоговы-
ми окнами, которые вы использовали ранее, состоит в том, что ре-
жимное диалоговое окно манипулирует управляющими элементами, а
затем считываете результаты, если пользователь щелкает "мышью" на
командной кнопке OK. В данном безрежимном окне палитры вы имеете
дело с активной, динамической частью программы и можете активно
отвечать на каждый используемый управляющий элемент.

Пока командные кнопки выводятся в окне палитры, но щелчок
кнопкой "мыши" не дает никакого эффекта. Щелчок и выбор "мышью"
являются событиями управляющего элемента. Они аналогичны событиям
меню, на которые вы отвечали в шаге 4.

Вы отвечали на события меню, определяя методы реакции на ко-
манды. Что-то аналогичное нужно делать с сообщениями управляющих
элементов. События управляющих элементов создают сообщения (на
основе дочернего идентификатора), аналогичные командным сообщени-
ям, но вместо идентификатора меню содержащие идентификатор управ-
ляющего элемента. Для идентификации заголовка метода на основе
дочернего идентификатора используйте сумму идентификаторов уп-
равляющего элемента и констант id_First.

Примечание: Подробнее о командных сообщениях и уведом-
ляющих сообщениях управляющих элементов рассказывается в
Главе 16 "Сообщения окон".

Имена методов реакции на сообщения управляющих элементов
-----------------------------------------------------------------

Как и в случае методов реакции на сообщения, имена которым
присваиваются по сообщениям, методы, основанные на дочерних иден-
тификаторах, также должны именоваться по идентификаторам сообще-
ний. Так как две командные кнопки, на которые вы хотите реагиро-

B.Pascal 7 & Objects/OW - 113 -

вать, имеют идентификаторы id_Add и id_Del, TPenPalette нужны ме-
тоды с именами IDAdd и IDDel.

TPenPalette = object(TWindow)
AddBtn, DelBtn: PBitButton;
constructor Init(AParent: PWindowsObject; ATitle: PChar);
procedure Grow;
procedure SetupWindow; virtual;
procedure Shrink;
procedure IDAdd(var Msg: TMessage); virtual
id_First + id_Add;
procedure IDDel(var Msg: TMessage); virtual
id_First + id_Del;
end;

Теперь для выполнения соответствующих действий в ответ на
командные кнопки осталось только определить методы IDAdd и IDDel.
Пока что IDAdd должен просто вызывать увеличение окна, а IDDel -
его сжатие

procedure TPenPalette.IDAdd(var Msg: TMessage);
begin
Grow;
end;

procedure TPenPalette.IDDel(var Msg: TMessage);
begin
Shrink;
end;

Примечание: Это дополняет содержимое файла
STEP12A.PAS.



B.Pascal 7 & Objects/OW - 114 -

Добавление "кнопок" палитры
-----------------------------------------------------------------

Теперь, когда у вас есть окно палитры, вам необходим просто
способ вывода на экран и выбора перьев в палитре. Для этого вы
можете использовать относительно простой потомок TWindow и набор
объектов пера.

В данном разделе вы сделаете следующее:

* определите объект палитры пера;

* выберете перья по щелчку кнопкой "мыши".


Определение объекта палитры
-----------------------------------------------------------------

Так как окно палитры пера может изменять свой размер, палит-
ра в окне может фактически оставаться фиксированной. Чтобы пока-
зать только часть палитры, в которой отображаются перья, вы може-
те использовать возможности отсечения Windows.

Самой палитре необходимы только несколько полей данных: на-
бор перьев, указание того, какое перо в данный момент выбрано, и
описатели представляющих перья графических образов. Описатели
графических изображений представляют собой частные поля не пото-
му, что они должны быть секретными, а для предотвращения их неп-
реднамеренного изменения другим кодом.

Приведем описание объекта палитры:

TPenPic = object(TWindow)
PenSet: PCollection;
CurrentPen: Integer;
constructor Init(AParent: PWindowsObject);
destructor Done: virtual;
procedure Paint(PaintDC: HDC; var PaintInfo:
TPaintStruct); virtual;
procedure AddPen(APen: PPen);
procedure DeletePen;
procedure SetupWindow; virtual;
procedure WMLButtonDown(var Msg: TMessage);
virtual wm_First + wm_LButtonDown;
private
UpPic, DownPic: HBitMap;
end;

Объекту TPenPic не требуется очень много методов. Он имеет
простой конструктор для создания набора перьев и деструктор для
их уничтожения. Метод SetupWindow просто перемещает палитру внут-
ри ее порождающего окна. AddPen и DeletePen включают перо в набор
и удаляют перо из набора, а WMLButtonDown интерпретирует щелчки

B.Pascal 7 & Objects/OW - 115 -

"мышью" для выбора перьев из палитры. Наконец, Paint рисует
"кнопки", представляющие перья в наборе.

Отметим также, что TPenPic является потомком TWindow, а не
TControl. Хотя поведение вашего нового объекта во многом напоми-
нает поведение управляющего элемента окна, он должен быть произ-
водным от TWindow, так как TControl работает только со стандарт-
ными управляющими элементами, такими как "нажимаемые" командные
кнопки и полосы прокрутки. При создании собственных управляющих
элементов нужно начинать с TWindow.



B.Pascal 7 & Objects/OW - 116 -

Создание и уничтожение палитры
-----------------------------------------------------------------

Построение и уничтожение объекта палитры выполняется доста-
точно просто. Конструктор Init вызывает TWindow.Init, затем изме-
няет стиль окна (чтобы оно стало видимым дочерним окном). PenSet
инициализируется как набор фиксированного размера, достаточно
большой, чтобы содержать максимальное число заданных константой
MaxPens перьев, и не возрастающий. Для текущего выбранного пера
CurrentPen устанавливается в -1. Это означает, что выбранного пе-
ра нет.

Наконец, Init загружает в UpPic и DownPic два графических
образа. Они используются в качестве фона для каждого пера палит-
ры. DownPic рисуется за выбранным пером, а UpPic - в качестве фо-
на других перьев.

-------------------
--------------------¬ -------------------¬
-¦ ¦ ¦ ¦-
-¦ ¦ ¦ ¦-
-¦ ¦ ¦ ¦-
L------------------- L--------------------
-------------------

Рис. 6.3 Фоновые образы палитры пера.

Деструктор Done перед вызовом наследуемого деструктора Done
отменяет графические образы. Вызов DeleteObject для уничтожения
графических образов имеет важное значение. Подобно контексту
дисплея, графические образы являются ресурсами Windows, поддержи-
ваемыми в ограниченной памяти Windows. Если размещаете их в памя-
ти Windows и не удаляете, то ваша программа (и другие работающие
параллельно с ней программы) потеряют доступ к этой памяти.

Init и Done объекта палитры выглядят следующим образом:

constructor TPenPic.Init(AParent: PWindowsObject);
begin
inherited Init(AParent, nil);
AttrStyle := ws_Child or ws_Visible;
PenSet := New(PCollection, Init(MaxPens, 0));
CurrentPen := -1;
UpPic := LoadBitMap(HInstance, 'PAL_UP');
DownPic := LoadBitmap(HInstance, 'PAL_DOWN');
end;

destructor TPenPic.Done;
begin
DeleteObject(UpPic);
DeleteObject(DownPic);
Dispose(PenSet, Down);
inherites Done;

B.Pascal 7 & Objects/OW - 117 -

end;

Размещение в порождающем окне
-----------------------------------------------------------------

Как вы могли заметить, TPenPic не задает свою позицию в
конструкторе так, как это делают большинство управляющих элемен-
тов. Причина здесь в том, что вы не можете полагаться на коорди-
наты окна, пока оно не будет реально существовать. TPenPalette
обходит эту проблему, создавая свои объекты кнопок путем вызова
для определения высоты заголовка окна функции API
GetSystemMetrics. Хотя это будет работать, существует и другой
подход.

Вместо позиционирования палитры в конкретное место порождаю-
щего окна вы можете поместить его в определенную позицию в порож-
дающей области клиента. Таким образом, если вы добавляете меню
или изменяете рамку, либо выполняете вне окна какое-либо другое
изменение, ваш объект палитры все равно будет правильно позицио-
нирован.

Изменение позиции окна выполняется в SetupWindow, так как
окну палитры требуется использовать описатель своего порождающего
окна, который до вызова собственного метода SetupWindow недосту-
пен. При достижении SetupWindow дочернее окно может рассчитывать
на допустимость описателя порождающего окна.

procedure TPenPic.SetupWindow;
var ClientRect: TRect;
begin
inherited SetupWindow;
GetClientRect(Parent^.HWindow, ClientRect);
with ClientRect do
MoveWindow(HWindow, 1, bottom - top + 1, 128,
40 * MaxPens, False;
end;

Для возврата координат области клиента окна палитры метод
TPicPen использует функцию API Windows GwetClientRect. Затем он
перепозиционируется с помощью MoveWindow непосредственно под объ-
екты кнопок, задавая высоту, достаточную для размещения всех
перьев в наборе. Заметим, что последний параметр MoveWindow - это
значение типа Boolean, указывающее, следует ли выполнять повтор-
ное отображение окна после перемещения. Так как палитра на экран
пока не выводилась, то заново отображать ее не имеет смысла, поэ-
тому TPenPic.SetupWindow передает значение False.



B.Pascal 7 & Objects/OW - 118 -

Добавление и удаление перьев
-----------------------------------------------------------------

В последнем разделе вы отвечали на сообщения от Add Pen (До-
бавить перо) и Del Pen (Удалить перо) изменением размера окна па-
литры. Теперь настало время изменить эту реакцию и фактически до-
бавлять или удалять перья из палитры, что в свою очередь указыва-
ет окну палитры на необходимость изменения размера. Вместо вызова
собственных методов Grow и Shrink методы объекта палитры AddPen и
DeletePen должны вызывать соответственно, методы IDAdd и IDDel
в TPenPalette.

procedure TPenPalette.IDAdd(var Msg: TMessage);
begin
Pens^.AddPen(CommonPen);
end;

procedure TPenPalette.IDDel(var Msg: TMessage);
begin
Pens^.DeletePen;
end;

Метод AddPen воспринимает передаваемое перо, копирует его в
набор и отмечает перо, как текущее выбранное. Затем он разрешает
кнопку Del Pen в окне палитры пера, запрещает кнопку Add Pen, ес-
ли набор полон, и сообщает порождающему окну о необходимости уве-
личения размера, чтобы поместить новое перо.

procedure TPenPic.AddPen(APen: PPen);
begin
CurrentPen := PenSet^.Count;
with APen^ do PenSet^.Insert(New(PPen, Init(Style, With,
Color)));
with PPenPalette(Parent)^ do
begin
DelBtn^.Enable;
if PenSet^.Count >= MaxPens tnen
AddBtn^.Disable;
Grow;
end;
end;

Примечание: Чтобы использовать преимущества средств,
специфических для TPenPAlette, TPenPic может выполнять при-
ведение типа поля Parent. Большинство оконных объектов не
связаны так жестко с конкретным порождающим типом, и поэто-
му не должны делать никаких предположений относительно типа
порождающих их окон.

Метод DeletePen по существу изменяет действия AddPen на об-
ратные. При наличии выбранного в палитре пера оно удаляется из
набора, а набор уплотняется таким образом, чтобы перья размеща-
лись непрерывно. Затем он указывает, что в данный момент выбран-

B.Pascal 7 & Objects/OW - 119 -

ных перьев нет (поскольку выбранное перо только что удалено) и
запрещает командную кнопку Del Pen, так как Del Pen работает с
выбранным пером. Далее он разрешает кнопку Add Pen, поскольку
удаление пера автоматически освобождает место для по крайней мере
еще одного пера. Наконец, он сообщает порождающему окну на необ-
ходимость уменьшения его размера (так выводить теперь нужно мень-
ше перьев).

procedure TPenPic.DeletePen;
begin
if CurrentPen > -1 then
begin
PenSet^.AtFree(CurrentPen);
PenSet^.Pack;
CurrentPen := -1;
with PPenPelette(Parent)^ do
begin
AddBtn^.Enable;
DelBtn^.Disable;
Shrink;
end;
end;
end;

Заметим, что AddPen и DeletePen используют преимущества того
факта, что окна палитры пера для упрощения связи с методами имеет
указатели на свои командные кнопки. Если бы TPenPalette не имел
полей AddBtn и DelBtn, то объекту палитры пришлось бы искать их
по идентификаторам и посылать им сообщения, либо нужно было бы
послать сообщение порождающему окну, которое в свою очередь долж-
но каким-то образом связываться с командными кнопками.



B.Pascal 7 & Objects/OW - 120 -

Отображение содержимого палитры
-----------------------------------------------------------------

За все, что мы насоздавали в объекте палитры, приходится
расплачиваться, когда приходит момент отображать ее на экране.
Поскольку перья хранятся в наборе, для отображения каждого пера
вы легко можете выполнять итерацию. В самом деле, метод Paint
состоит только из инициализации локальной переменной-счетчика, а
затем выполняет итерацию по набору с помощью ForEach:

procedure TPenPic.Paint(PaintDC: HDC;
var PaintInfo: TPaintStruct);
var PenCount: Integer;

procedure ShowPen(P: PPen); far;
var
MemDC: HDC;
TBitmap: HBitmap;
begin
MemDC := CreateCompatibleDC(PaintDC);
Inc(PenCount);
if PenCount = CurrentPen then
TheBitmap = DownPic;
else TheBitmap := UpPic;
SelectObject(MemDC, TheBitmap);
BitBlt(PaintDC, 0, PenCount * 40, 128, 40, MemDC, 0,
0, SrcCopy);
P^.Select(PaintDC);
MoveTo(PaintDC, 15, PenCount * 40 + 20);
LineTo(PaintDC, 115, PenCount * 40 + 20);
P^.Delete;
DeleteDC(MemDC);
end;

begin
PenCount := -1;
PenSet^.ForEach(@ShowPen);
end;

Наиболее интересная часть содержится не в Paint, а во вло-
женной процедуре ShowPen, которая вызывается для каждого пера в
палитре. На самом деле ShowPen состоит из двух различных частей.
Первая рисует графическое изображение фона, а вторая (которая уже
должна быть вам достаточно знакома) использует объект пера для
изображения по этому фону образца линии.

Изображение графических образов предусматривает три шага:
создание контекста устройства памяти, выбор в контексте устройс-
тва графического образа и копирование образа в контекст экрана.

Как вы видели в шаге 8, для различных видов устройств су-
ществует различные контексты устройства. Для работы с графически-
ми образами (битовыми массивами) Windows позволяет создавать кон-

B.Pascal 7 & Objects/OW - 121 -

текст устройства памяти. Фактически, вы можете только выбирать
битовые массивы в контексте устройства памяти, хотя они могут ко-
пироваться в другие контексты устройства.

Метод CreateMemoryDC создает пустой контекст устройства па-
мяти, совместимый с текущим PaintDC. Затем, в зависимости от то-
го, является ли данное конкретное перо выбранным, ShowPen выбира-
ет в контексте устройства графические образы UpPic или DownPic.
Заметим, что в контексте устройства памяти графический образ
интерпретируется аналогично любому другому изобразительному
средству. Наконец, функция BitBlt копирует заданную часть кон-
текста устройства памяти в PaintDC. Заметим, что контекст уст-
ройства памяти требуется уничтожать.

После того как фон будет на месте, ShowPen использует ShowTo
и LineTo аналогично тому, как это делается при рисовании непос-
редственно в окне.

Выбор перьев с помощью "мыши"
-----------------------------------------------------------------

Наконец. TPenPic обеспечивает реакцию на щелчок в нарисован-
ной области кнопкой "мыши". Заметим, что хотя размер объекта па-
литры никогда не изменяется, он получает сообщение от щелчков
"мышью" в области, фактически показываемой на экране. Палитра от-
секается рамкой окна палитры. Это означает, что вы можете щелкать
кнопкой "мыши" только непосредственно в палитре.

Поскольку каждый элемент в палитре имеет один и тот же раз-
мер, WMLButton для определения того, в каком графическом образе
была нажата кнопка "мыши", может просто разделить y-координату
щелчка "мыши" (которая поступает в LParamHi) на 40 (размер каждо-
го графического изображения). Затем он делает перо, на котором
была нажата кнопка "мыши" текущим и задает в качестве пера для
рисования копию выбранного пера палитры. Поскольку теперь есть
выбранное перо, он разрешает кнопку Del Pen в окне палитры, затем
запрещает палитру для обеспечения ее повторного отображения для
того, чтобы показать новый выбор.

Код для WMLButtonDown имеет следующий вид (это дополняет
текст STEP12B.PAS):

procedure TPenPic.WMLButtonDwon(var Msg: TMessage);
begin
CurrentPen := Msg.LParamHi div 40;
if CurrentPen <> nil then Dispose(CurrentPen, Done);
with PPen(PenSet^.At(CurrentPen))^ do
CurrentPen := New(PPen, Init(Style, With, Color));
PPenPalette(Parent)^.DelBlt^.Enable;
InvalidateRect(HWindow, nil, False);
end;

Что дальше?

B.Pascal 7 & Objects/OW - 122 -

-----------------------------------------------------------------

Есть много дополнений и изменений, которые вы можете внести
в программу Steps, чтобы сделать ее более полезной. Версию
программы Steps, которая включает в себя эти изменения, вы можете
найти в файле GRAFFITI.PAS.

Программа Graffiti содержит следующие изменения:

* Многодокументальный интерфейс (MDI).

* Сглаживание линий.

* Отмена.

* Улучшает поведение окна палитры.

* Прокрутка.

Многодокументальный интерфейс
-----------------------------------------------------------------

TStepWindow может очень легко работать в качестве дочернего
окна MDI. Фактически с небольшими изменениями программа Graffiti
использует в качестве своих дочерних окон те же окна, что Steps
использует для основного окна. Так как владельцем окна палитры
пера является объект TStepWindow, каждый отдельный рисунок имеет
свой собственный отличный набор перьев. О многодокументальном ин-
терфейсе рассказывается в Главе 14 "Объекты MDI").

Сглаживание линий
-----------------------------------------------------------------

Для сложных рисунков со многими длинными линиями повторное
отображение рисунка может потребовать много времени. Graffiti ис-
пользует одно из преимуществ графический функций GDI - функцию
PolyLine, которая рисует сегменты каждой линии сразу, а не по од-
ному. Эффектом будет более быстрое и плавное отображение окна.

Отмена
-----------------------------------------------------------------

Поскольку рисунок состоит из наборов линий, для стирания
последней нарисованной линии легко использовать методы наборов.
Graffiti связывает элемент меню Edit¦Undo (Редактирование¦Отмена)
с набором линий метода AtDelete, удаляя последнюю линию в наборе,
которая является также последней нарисованной линией. Повторяя
удаление последней линии в наборе, вы можно эффективно отменять
все изображение.

Хотя программа Graffiti этого не делает, вы можете добавить
также функцию Redo (Возобновление), сохраняя удаленные линии в
другом наборе, и перемещая их по одной в набор отображаемых ли-

B.Pascal 7 & Objects/OW - 123 -

ний, либо даже в другой рисунок.


Поведение палитры
-----------------------------------------------------------------

Вы можете также отметить, что при щелчке "мышью" между па-
литрой и основным окном, окно, где вы нажимаете кнопку, становит-
ся активным (получает активную рамку), а другие окна становятся
неактивными. Если вы часто перемещаетесь между двумя окнами (что
может иметь место при работе с палитрой), это может показаться
весьма раздражающим. Чтобы предотвратить это явление, вам нужно
перехватывать передачу в окна сообщений sm_NCActivate, и когда
параметр WParam сообщений равен 0 (попытка деактивизации рамки),
вы можете изменить его на 1 (активизация рамки):

procedure TPenPalette.WVNCActivate(var Msg: TMessage);
begin
if Msg.WParam = 0 then Msg.WParam := 1;
DefWndProc(Msg);
end;

Вызов DefWndProc обеспечивает, что сообщение обрабатывается
как обычно, но теперь рамка палитры деактивизироваться не будет.
Аналогичный перехват вы можете добавить в TStepWindow.

Прокрутка
-----------------------------------------------------------------

Наконец, так как каждое дочернее окно MDI обычно невелико по
размеру, Graffiti добавляет возможность автоматической прокрутки
изображения при рисовании. В программе Steps, когда перо выходит
за границы окна, линия продолжает рисоваться, хотя видеть ее вы
больше не можете. Graffiti прокручивает изображение, так что вы
будете видеть другие части рисунка.



B.Pascal 7 & Objects/OW - 124 -

------------------------------------------------------------------
Часть 2. Использование ObjectWindows
-----------------------------------------------------------------


Глава 7. Иерархия ObjectWindows
-----------------------------------------------------------------

ObjectWindows представляет собой исчерпывающий набор объек-
тов, облегчающий разработку программ для Microsoft Windows на
Паскале. В данной главе вы найдете обзор иерархии объектов
ObjectWindows. В остальных главах данной части дается детальное
описание различных частей иерархии.

Кроме описания иерархии объектов, в данной главе описываются
основные принципы программирования для операционной среды
Windows, включая вызов API Windows.

Соглашения Windows
-----------------------------------------------------------------

Соглашения по именам ObjectWindows обеспечивают ясность и
содержательность имен.

Имена объектов
-----------------------------------------------------------------

Имена всех объектный типов, предусмотренных в ObjectWindows,
начинаются с буквы T. Например, объекты диалоговых окон имеют тип
TDialog. Для каждого определения объектного типа имеется соот-
ветствующий ссылочный тип, начинающийся с P. Например, указатель
на TDialogh имеет тип PDialog. В примерах данного руководства бу-
дут создаваться динамические экземпляры объектов, например, с по-
мощью PDialog позволяет разместить объекты TDialog в динамически
распределяемой области памяти.

Имена методов
-----------------------------------------------------------------

Методы реакции на сообщения называются по именам сообщений,
на которые они отвечают, но без подчеркиваний. Например, метод,
отвечающий на сообщение wm_KeyDown будет называться WMKeyDown.



B.Pascal 7 & Objects/OW - 125 -

Обзор объектов
-----------------------------------------------------------------

Иерархия объектов
-----------------------------------------------------------------

ObjectWindows - это иерархия объектных типов, которые вы мо-
жете использовать для работы с большинством обычных задач в при-
ложении ObjectWindows. Схема объектов пользовательского интерфей-
са библиотеки показана на Рис. 7.1. На Рис. 7.2 представлены объ-
екты иерархии, используемые для управления данными и проверки их
допустимости.

-----------------¬
¦ TObject ¦
L-------T---------
-----+--------------T----------------------T------------¬
--------+--------¬ ---------+-------¬ --------+--------¬ ¦
¦ TPrintout ¦ ¦ TPrinter ¦ ¦ TWindowsObject ¦ ¦
L-------T--------- L----------------- L-------T--------- ¦
+-------------------¬ ¦ ¦
--------+--------¬ ---------+-------¬ ------------- ¦
¦ TEditPrintout ¦ ¦ TWindowPrintout¦ ¦ -------------+
L----------------- L----------------- ¦ ¦ ¦
¦ --------+--------¬ ¦
---------------------T----------- ¦ TApplication ¦ ¦
¦ ¦ L----------------- ¦
-------+---------¬ ---------+-------¬ --------------
¦ TDialog ¦ ¦ TWindow ¦ ¦
L------T---------- L--------T-------- --------+--------¬
¦ ¦ ¦ TScroller ¦
¦ ¦ L-----------------
¦ L----------------------------¬
+-------------------T-----------¬ ¦
-------+---------¬ --------+--------¬ ¦ ¦
¦ TFileDialog ¦ ¦ TInputDialog ¦ ¦ ¦
L----------------- L----------------- ¦ ¦
-----------------------T--------- ¦
-------+------------¬ ¦ ¦
¦TPrinterAbortDialog¦ +------------¬ ¦
L-------------------- ¦ ¦ ¦
¦ ---------+-------¬ ¦
----------------------+ ¦ TPrintDialog ¦ ¦
--------+--------¬ ¦ L----------------- ¦
¦TPrinterSetupDlg¦ L------------¬ ¦
L----------------- ¦ ¦
---------+-------¬ ¦
¦ TDlgWindow ¦ ¦
L----------------- ¦


B.Pascal 7 & Objects/OW - 126 -


¦
-------------------T------------------T----------+
--------+--------¬ --------+--------¬ --------+--------¬ ¦
¦ TControl ¦ ¦ TMDIClient ¦ ¦ TMDIWindow ¦ ¦
L-------T--------- L----------------- L----------------- ¦
+------------------T-----------¬ --------
--------+--------¬ --------+--------¬ ¦ --------+--------¬
¦ TButton ¦ ¦ TScrollBar ¦ ¦ ¦ TEditWindow ¦
L-------T--------- L----------------- ¦ L-------T---------
--------+--------¬ ¦ --------+--------¬
¦ TCheckBox ¦ ------------+ ¦ TFileWindow ¦
L-------T--------- ¦ ¦ L-----------------
--------+--------¬ --------+--------¬ +----------¬
¦ TRadioButton ¦ ¦ TStatic ¦ ¦ ¦
L----------------- L-------T--------- ¦ ¦
¦ ¦ --------+--------¬
--------+--------¬ ¦ ¦ TListBox ¦
¦ TEdit ¦ ¦ L-------T---------
L----------------- ¦ ¦
¦ --------+--------¬
------------- ¦ TComboBox ¦
¦ L-----------------
--------+--------¬
¦ TGroupBox ¦
L-----------------

Модуль OPRINTER: Модуль OWINDOWS: Модуль OSTDDLGS:

TPrinout TWindow TInputDialog
TPtinter TAppication TInputDialog
TEditPrintout TScroller
TWindowPrintout
TPrinterAbortDlg
TPrintDialog
TPrinterSetup

Модуль ODUIALOGS: Модуль OSTDWNDS:

TDialog TEditWindow
TDlgWindow TFileWindow
TButton
TScrollBar
TCheckBox
TRadioButton
TControl
TStatic
TEdit
TListBox
TComboBox
TGroupBox
Рис. 7.1. Иерархия объектных типов ObjectWindows.


B.Pascal 7 & Objects/OW - 127 -


-----------------¬
¦ TObject ¦
L-------T---------
-----+--------------T--------------------¬
--------+--------¬ --------+--------¬ --------+--------¬
¦ TValidator ¦ ¦ TCollection ¦ ¦ TStream ¦
L-------T--------- L----------------- L---------T-------
+---------------------T------------------¬ L--------¬
--------+-----------¬ --------+--------¬ --------+--------¬ ¦
¦TPXPictureValidator¦ ¦TFilterValidator¦ ¦TLookupValidator¦ ¦
L-------------------- L-------T--------- L-------T--------- ¦
--------+---------¬ ¦ ¦
¦ TRangeValidator ¦ ---- ¦
L------------------ ¦ ¦
-----------+-----------¬ ¦
¦TStringLookupValidator¦ ¦
L----------------------- ¦
--------------------T-------------------T------------
--------+--------¬ --------+--------¬ --------+--------¬
¦ TMemoryStream ¦ ¦ TDosStream ¦ ¦ TEmsStream ¦
L----------------- L-------T--------- L-----------------
--------+--------¬
¦ TBufStream ¦
L-----------------

Рис. 7.2 Иерархия наборов, потоков и проверки допустимости.

Модуль OBJECTS: Модуль VALIDATE:

TObject TValidator
TCollection TPXPictureValidator
TSortedCollection TFilterValidator
TStrCollection TRangeValidator
TStringCollection TLookupValidator
TStream TStrongLookupValidator
TMemoryStream
TEmsStream
TDosStream
TBufStream

Базовый объект

TObject - это базовый объектный тип, общий предок всех объ-
ектов ObjectWindows. Он определяет рудиментарный конструктор и
деструктор. Потоки ObjectWindows требует, чтобы записанные объек-
ты были потомками TObject.

TApplication

Этот тип определяет поведение, необходимое для всех приложе-
ний ObjectWindows. Каждое приложение ObjectWindows, которое вы
пишете, будет определять объектный тип приложения, производный от

B.Pascal 7 & Objects/OW - 128 -

TApplication. Объекты приложения подробно описываются в Главе 8
"Объекты приложения".

Интерфейсные объекты

Остальные объекты в иерархии ObjectWindows классифицируются
в общем случае как интерфейсные объекты. Они являются интерфейс-
ными в том смысле, что представляют элементы и пользовательском
интерфейсе Windows и служат интерфейсом между кодом вашей прик-
ладной программы и операционной средой Windows. Интерфейсные объ-
екты описываются в Главе 9.

Объекты Windows

Объекты Windows представляются не только знакомыми окнами
среды Windows, но и большинством визуальных инструментов операци-
онной среды, такими как управляющие элементы.

Объекты диалоговых блоков

Объекты диалоговых блоков (окон) обеспечивают временные ок-
на для обслуживания специальных функций ввода и вывода. В общем
случае они включат в себя текст и управляющие элемента, такие как
командные кнопки, блоки списка и полосы прокрутки. Об объектах
диалоговых блоков подробно рассказывается в Главе 11.

Объекты управляющих элементов

В диалоговых блоках и некоторых окнах управляющие элементы
позволяют пользователям вводить данные и выбирать параметры. Объ-
екты управляющих элементов обеспечивают согласованные и простые
средства для работы со всеми типами управляющих элементов, опре-
деленных в Windows. Объекты управляющих элементов подробно описы-
ваются в Главе 12.

Объекты MDI

Windows реализует стандарт для работы с несколькими докумен-
тами в рамках одного окна, которое называется множественным доку-
ментальным интерфейсом (MDI). ObjectWindows обеспечивает средства
для установки окон MDI и работы с ними. Объекты MDI подробно опи-
сываются в Главе 14.

Объекты проверки допустимости

ObjectWindows содержит полный набор объектов проверки допус-
тимости данных, которые позволяют проверять допустимость данных,
введенных пользователем, при выходе пользователя из поля или ког-
да пользователь завершает работу с окном. Проверка допустимости
данных описывается в Главе 13.

Объекты принтера


B.Pascal 7 & Objects/OW - 129 -

Для работы с печатью документов или печати содержимого окна
ObjectWindows предусматривает соответствующие объекты. О том, как
использовать эти объекты, рассказывается в Главе 15.

Объекты наборов и потоков

Модуль Object включает в себя многочисленные объекты, реали-
зующие гибкие структуры данных и потоки, позволяющие считывать и
записывать объекты. Наборы описываются в Главе 19, а потоки - в
Главе 20.



B.Pascal 7 & Objects/OW - 130 -

Файлы ObjectWindows
-----------------------------------------------------------------

Перечисленные выше типы ObjectWindows реализует в скомпили-
рованных модулях. В данном разделе кратко описывается содержимое
поставляемых модулей. В каждом приложении ObjectWindows требуется
только модуль OWindows.

В данной версии ObjectWindows различные части иерархии объ-
ектов разбивают различные части иерархии объектов по отдельным
моделям. Чтобы перекомпилировать код ObjectWindows более ранних
версий, в общем случае нужно изменить все ссылки на модуль
WObjects на OWindows и добавить к программам и модулям, использу-
ющим наборы и потоки или диалоговые блоки, соответственно Objects
и ODialogs.

В данную версию ObjectWindows добавлены также новые модули
для печати, проверки данных, специализированных управляющих эле-
ментов Borland и поддержки Windows 3.1.

В Таблице 7.1 перечислены модули, составляющие интерфейс
ObjectWindows и AOPI Windows 3.0. Модули, поддерживающие расшире-
ния Windows 3.1, представлены в Таблице 7.2.

Модули для ObjectWindows и API Windows Таблицы 7.1
-----------------T----------------------------------------------¬
¦ Модуль ¦ Содержимое ¦
+----------------+----------------------------------------------+
¦ Objects ¦ Базовый объект TObject, наборы, потоки. ¦
¦ OWindows ¦ Приложения, окна, полосы прокрутки, окна MDI.¦
¦ ODialogs ¦ Диалоговые блоки, диалоговые окна, управляю-¦
¦ ¦ щие элементы. ¦
¦ OPrinter ¦ Печать, специальные распечатки. ¦
¦ Validate ¦ Проверка допустимости данных. ¦
¦ BWCC ¦ Специализированные управляющие элементы фирмы¦
¦ ¦ Borland. ¦
¦ OStdDlgs ¦ Диалоговые блоки имен файлов, однострочный¦
¦ ¦ ввод. ¦
¦ OStdWnds ¦ Окна текстового редактора, окна редактора¦
¦ ¦ файлов. ¦
¦ WinTypes ¦ Все типы, используемые подпрограммами API¦
¦ ¦ Windows 3.0, включая записи, стили, сообщений¦
¦ ¦ и флаги. ¦
¦ WinProcs ¦ Описания процедур и функций для API Windows¦
¦ ¦ 3.0. ¦
L----------------+-----------------------------------------------



B.Pascal 7 & Objects/OW - 131 -

Файлы ресурсов
-----------------------------------------------------------------

Модули OStdDlgs, OStdWnds и OPrinter имеют связанные с ними
файлы ресурсов. Ресурс модуля находится в файле с именем, эквива-
лентным имени модуля, и расширением .RES. Ресурсы автоматически
включаются при использовании соответствующих модулей, так что
программа, которая использует модуль OstdDlgs, будет автоматичес-
ки иметь доступ к ресурсам в OSTDDLGS.RES.

Файлы Windows 3.1
-----------------------------------------------------------------

Кроме стандартных модулей API Windows 3.0, вы можете писать
программы, использующие преимущества средств, добавленных в вер-
сию 3.1 Windows. Каждая из 11 DLL Windows 3.1 имеет соответствую-
щий модуль:

Модули для доступа к средствам Windows 3.1 Таблица 7.2
-----------------T----------------------------------------------¬
¦ Модуль ¦ Средство ¦
+----------------+----------------------------------------------+
¦ ComDlg ¦ Общие диалоговые блоки. ¦
¦ DDTML ¦ Сообщения динамического обмена данными. ¦
¦ Dlgs ¦ Константы диалогового блока. ¦
¦ LZExpand ¦ Расширения файла LZ. ¦
¦ MMSystem ¦ Расширения мультимедиа. ¦
¦ OLE ¦ Компоновка и встраивание объектов (OLE). ¦
¦ ShellAPI ¦ Оболочка API Windows. ¦
¦ Stress ¦ Строгая проверка типов. ¦
¦ ToolHelp ¦ Отладка и другие инструментальные средства. ¦
¦ Ver ¦ Версии. ¦
¦ Win31 ¦ Расширения Windows 3.1. ¦
L----------------+-----------------------------------------------



B.Pascal 7 & Objects/OW - 132 -

Взаимодействие с Windows
-----------------------------------------------------------------

ObjectWindows избавляет вас от многих утомительных и запу-
танных частей Windows, но иногда возникает необходимость в непос-
редственном взаимодействии с Windows (например, когда вы хотите
переопределить некоторое заданное в Windows поведение или выйти
за рамки того, что инкапсулировано в ObjectWindows).

Существует два способа, с помощью которых вы можете взаимо-
действовать с ObjectWindows: вызов ее функций API и получение со-
общений. В данном разделе описываются функции API. Об обработке
сообщений рассказывается в Главе 16 "Сообщения Windows".

Функции API Windows
-----------------------------------------------------------------

Функциональные возможности Windows заключены в ее около 600
функций. Каждая функция имеет имя. Взаимодействовать с операцион-
ной средой Windows, модифицировать ее отображение или действие в
ответ на ввод пользователя можно с помощью вызова функций API.
Однако с помощью ObjectWindows вы можете создавать окна, выводить
диалоговые блоки и манипулировать управляющими элементами, не вы-
зывая функций Windows. Как все это работает?

Вызов в ObjectWindows функций API
-----------------------------------------------------------------

Методы ObjectWindows вызывают функции API. Но ObjectWindows
- это не дублируемые функциональные возможности; она предоставля-
ет в новом пакете объектно-ориентированной библиотеки функции
Windows, ее прикладной программный интерфейс (API). Кроме того,
ObjectWindows значительно упрощает задачи спецификации многочис-
ленных параметров, требуемых в функциях Windows. Часто ObjectWin-
dows автоматически подставляет параметры, такие как описатели
окон и идентификаторы дочерних окон, которые хранятся в интер-
фейсных объектах в виде полей.

Например, многие функции Windows для задания окна, с которым
они должны работать, используют описатели окна, и эти функции вы-
зываются обычно из методов оконного объекта. Объекты содержат в
поле HWindow описатель соответствующего окна и может передавать
его, освобождая вас от необходимости каждый раз задавать описа-
тель. Таким образом, объектные типы ObjectWindows служат объект-
но-ориентированным слоем, реализованным с помощью вызовов необъ-
ектно-ориентированных функций API.



B.Pascal 7 & Objects/OW - 133 -

Доступ к функциям API
-----------------------------------------------------------------

Чтобы из приложения ObjectWindows обратиться непосредствен-
но к функциям API, вы должны использовать модуль WinProcs.
WinProcs определяет для каждой функции Windows заголовок процеду-
ры или функции Паскаля, что позволяет вам вызывать функции
Windows как любую подпрограмму на Паскале. Перечень заголовков
этих функций вы можете найти в файле WINPROCS.PAS или в оператив-
ном справочнике Help.

Константы Windows
-----------------------------------------------------------------

Функции Windows требуют от вас передачи в качестве аргумен-
тов разнообразных констант типа Word или Longint. Эти константы
представляют стили окна, диалогового блока или управляющего эле-
мента, а также возвращаемые значение и др. Если в программе ис-
пользуются данные константы, она становится более читаемой, обс-
луживаемой и будет более независимой от изменений в последующих
версиях Windows, чем программы, использующие числа. Определенные
в модуле WinTypes константы описываются в Главе 21 "Справочник по
ObjectWindows".

Записи данных Windows
-----------------------------------------------------------------

Некоторые функции Windows требуют более сложных структур
данных, например, шрифтов (TLongFont) или классов окон
(TWndClass). Windows и ObjectWindows определяют эти и другие
структуры данных. Перечень доступных структур вы можете найти в
оперативном справочнике или в файле WINTYPES.PAS. Структуры, не-
посредственно используемые в ObjectWindows, вы можете найти в
Главе 21 "Справочник по ObjectWindows".

При использовании ObjectWindows все функции Windows доступны
также непосредственно и могут вызываться в вашей программе (если
в ее операторе uses указывается модуль WinProcs). Например, сле-
дующий код для получения окна сообщений вызывает функцию Windows
MessageBox:

Reply := MessageBox(HWindow, 'Хотите сохранить?',
'Файл изменен', mb_YesNo or mb_IconQuestion);

MessageBox возвращает целочисленное значение, указывающее,
какое действие выбрал пользователь для закрытия окна сообщения.
Если пользователь щелкнул "мышью" на командной кнопке Yes (Да),
то результат равен определенной в Windows целочисленной константе
id_Yes. Если пользователь щелкнул "мышью" на командной кнопке No
(Нет), то результат равен id_No.



B.Pascal 7 & Objects/OW - 134 -

Комбинирование констант стилей
-----------------------------------------------------------------

Функции Windows, позволяющие получить интерфейсные элементы,
требуют обычно некоторого параметра типа Word или Longint. Иден-
тификаторы констант стилей состоят из двухбуквенного мнемоничес-
кого префикса, за которым следует подчеркивание и описательное
имя. Например, ws_Popup - это константа стиля окна (ws_ означает
стиль окна - window style").

Примечание: В Windows определены сотни констант сти-
лей, которые перечислены в Главе 21 "Справочник по
ObjectWindows".

Часто эти стили комбинируются для получения другого сти-
ля. Например, в случае функции MessageBox вы можете передать в
качестве параметра стиля mb_YesNo или mb_IconQuestion. Этот стиль
дает окно сообщений с двумя командными кнопками Yes и No и пик-
торгаммой вопросительного знака. Поразрядная операция or факти-
чески комбинирует две константы бит за битом. Полученный в ре-
зультате стиль представляет собой комбинацию обоих стилей.

Имейте в виду, что некоторые стили взаимноисключающие. Их
комбинирование может дать непредвиденные или нежелательные ре-
зультаты.

Типы функций Windows
-----------------------------------------------------------------

Ниже перечислены виды функций Windows, которые вы можете ис-
пользовать в своих программах ObjectWindows.

Функции интерфейса с администратором Windows

Эти функции выполняют обработку сообщений, манипулируют ок-
нами и диалоговыми блоками и создают системный вывод. Данная ка-
тегория включает в себя функции для меню, курсоров и буфера выре-
занного изображения.

Функции интерфейса
с графическими устройствами (GDI)

Эти функции выводят на разнообразные устройства (включая эк-
ран и принтер) текст, графику и битовые массивы. Они не привязаны
к конкретному устройству и являются независимыми от устройства.

Функции интерфейса со служебными функциями системы

Эти функции работают с широким диапазоном системных уст-
ройств, включая управление памятью, взаимодействие с операционной
системой, администрирование ресурсов и коммуникации.



B.Pascal 7 & Objects/OW - 135 -

Функции системного вызова
-----------------------------------------------------------------

Windows содержит ряд функций, которые позволяют вам прохо-
дить в цикле (или перечислять) определенные типы элементов в сис-
теме Windows. Например, перечислить шрифты системы и напечатать
образец текста, используя каждый из них. Чтобы использовать такую
функцию, вы должны передать указатель на нее (указатель системной
функции), и Windows будет использовать его в процессе перечисле-
ния. Данная функция при итерации по списку характеристик окна,
окон, дочерних окон, шрифтов и т.д. вызывается непосредственно
Windows.

Функции Windows, требующие функций системного вызова (или
повторного вызова), включают в себя: EnimChildWindows,
EnumClipboardFormats, EnumFonts, EnumMetaFile, EnumObjects,
EnumPops, EnumTaskWindows и EnumWindows.

Функция системного вызова должна быть обычной функцией, а не
методов объекта. Указатель на эту функцию передается в качестве
первого параметра (типа TFarProc) данных методов. Например, если
вы определили в Паскале функцию системного вызова ActOnWindow
следующим образом:

function ActOnWindow(TheHandle: HWnd; The Value: Longint):
Integer; far; export;

то можете передать ее в качестве функции системного вызова при
вызове функции Windows EnumWindows:

ReturnValue := EnumWindows(TFarProc(ActOnWindow), ALongint);

Функция системного вызова должна иметь тот же тип возвращае-
мого значения, что и вызывающая ее функция Windows. Функция
ActOnWindows будет выполнять некоторое действие с окном, заданным
переданным указателем. Параметр TheValue - это любое значение,
выбранное для передачи в вызывающей программе.

Директива компилятора {$K+} позволяет автоматически управ-
лять функциями системного вызова. Если вы не выбираете {$K+}, то
для возврата адреса, по которому будет выполнять вызов Windows,
должны передавать свои функции системного вызова через функцию
API MakeProcInstance.



B.Pascal 7 & Objects/OW - 136 -

------------------------------------------------------------------
Глава 8. Объекты приложения
-----------------------------------------------------------------

При разработке приложения ObjectWindows вам нужно сначала
определить объект приложения, производный от типа ObjectWindows
TApplication. Этот объект инкапсулирует следующее поведение при-
ложения ObjectWindows:

* Создание и вывод основного окна приложения.

* Инициализацию первого экземпляра приложения для задач, вы-
полняемых всеми экземплярами приложения, таких как созда-
ние файлов, совместно используемых всеми экземплярами.

* Инициализацию каждого экземпляра приложения, например,
загрузку таблицы оперативных клавиш.

* Обработку сообщений Windows.

* Закрытие приложения.

Кроме определения объекта приложения вы должны добавить к
нему возможность построения объекта основного окна. Вы имеете
также возможность переопределения используемого по умолчанию по-
ведения инициализированных экземпляров, закрытия приложения и об-
работки сообщений Windows.

Минимальные требования
-----------------------------------------------------------------

Чтобы ваше программа ObjectWindows стала рабочим приложением
Windows, она должна в своем основном блоке begin..end делать сле-
дующее:

- выполнять инициализацию;

- обрабатывать сообщения;

- по требованию завершать работу.

Используемый по умолчанию объект выполняет эти задачи путем
вызова трех его методов: Init, Run и Done. Основной блок любой
программы ObjectWindows содержит обычно эти три метода. Чтобы из-
менить поведение по умолчанию, методы нужно переопределить.

Поиск объекта приложения
-----------------------------------------------------------------

Когда программа выполняется, ObjectWindows поддерживает гло-
бальную переменную Application - указатель на объект приложения.
Этот указатель позволяет подпрограммам вне объекта приложения об-
ращаться к его полям и методам. По умолчанию Application устанав-
ливается в @Self конструктором объекта приложения и в nil дест-

B.Pascal 7 & Objects/OW - 137 -

руктором объекта.

Минимальное приложение
-----------------------------------------------------------------

Приведем пример минимального приложения ObjectWindows:

program MinApp;
uses OWindows;
var
MyApp: TApplication;
begin
MyApp.Init('TtstApp');
MyApp.Run;
MyApp.Done;
end;

MinApp - это абсолютный минимум приложения ObjectWindows.
Эта программа не требует определения других объектов. Обычно вы
определяете новый объектные типы как минимум для приложения и ос-
новного окна.

Методы Init, Run и Done
-----------------------------------------------------------------

Так как в основном блоке программы ObjectWindows каждый раз
используются одни и те же операторы, вы должны понимать, что де-
лает каждый из них.

Конструктор Init
-----------------------------------------------------------------

Первый оператор - это вызов конструктора Init приложения.
Этот вызов делает следующее:

* Строит объект.

* Инициализирует поля данных объекта.

* Устанавливает глобальную переменную Application на объект
(@Self).

* Выполняет два вида инициализации:

- Вызывает InitApplication при отсутствии других выполняе-
мых экземпляров данного приложения.

- Всегда вызывает InitInstance, устанавливая вызовом
InitMainWindow основное окно.

Когда Init завершает работу, основное окно вашего приложения
находится на экране. В большинстве случаев вам нужно только пере-
определить InitMainWindow.

B.Pascal 7 & Objects/OW - 138 -

Метод Run
-----------------------------------------------------------------

Второй оператор вызывает метод Run приложения, который реа-
лизует работу приложения, вызывая MessageLoop. MessageLoop обра-
батывает сообщения от Windows и выполняет инструкции, которые уп-
равляют работой приложения. MessageLoop - это цикл, который про-
должает работу до закрытия приложения.

MessageLoop вызывает несколько методов, обрабатывающих конк-
ретные поступающие сообщения (см. далее).

Деструктор Done
-----------------------------------------------------------------

Done - это деструктор объекта приложения. Перед завершением
работы приложения он освобождает память объекта приложения.

-----------------¬ ------------------¬
¦ Init +--T->¦ InitApplication ¦
L----------------- ¦ L------------------
¦
¦ ------------------¬ -----------------¬
L->¦ InitInstance +--->¦ InitMainWindow ¦
L------------------ L-----------------

-----------------¬ ------------------¬
¦ Run +---->¦ MessageLoop ¦
L----------------- L------------------

-----------------¬
¦ Done ¦
L-----------------

Рис. 8.1 Вызовы методов, управляющие работой приложения.

Инициализация приложения
-----------------------------------------------------------------

Выполнение методов, инициализирующих объект приложения, поз-
воляет вам настроить части процесса путем переопределения отдель-
ных методов. Один из методов, требующий переопределения, чтобы
приложения получило конкретный смысл - это InitMainWindow. Вы мо-
ете также переопределить InitInstance и InitApplication.



B.Pascal 7 & Objects/OW - 139 -

Инициализация основного окна
-----------------------------------------------------------------

Вы должны определить метод InitMainWindow, который строит и
инициализирует объект основного окна и сохраняет его в поле
MainWindow объекта приложения. Ниже показан пример описания объ-
екта приложения и метода InitMainWindow.

Данный метод создает новый экземпляр типа TWindow
ObjectWindows (PWindow - это указатель на тип TWindow). Обычно
ваша программа будет определять для своего основного окна новый
оконный тип, а InuitMainWindow будет использовать этот тип вмес-
то TWindow.

Примечание: Объекты окон подробно описываются в Главе
10.

Следующее простое приложение ObjectWindows объединяет в себе
новый тип TMyApplication и старое приложение MinApp. Оно отлича-
ется от MinApp только тем, что основное окно имеет заголовок:

program TestApp;
uses OWindows;

type
TMyApplication = object(TApplication)
procedure InitMainWindow; virtual;
end;

procedure TMyApplication.InitMainWindow;
begin
MainWindow := New(PWindow, Init(nil, 'Основное окно'));
end;


var
MyApp: TApplication;
begin
MyApp.Init('TestApp');
MyApp.Run;
MyApp.Done;
end;

Программа TestApp выводит окно с заголовком 'Основное окно'.
Вы можете легко перемещать это окно и изменять его размер, мини-
мизировать его, восстанавливать или максимизировать. Закрытие ок-
на завершает приложение. Короче, TestApp - это полнофункциональ-
ный "скелет" приложения, оснащенный только простейшим основным
окном.



B.Pascal 7 & Objects/OW - 140 -

Специальный вывод основного окна
-----------------------------------------------------------------

Начальный вывод основного окна управляется переменной
CmdShow модуля System. CmdShow содержит одну из констант sw_ и
передается в качестве параметра функции API Windows ShowWindow,
когда приложение создает свое основное окно.

С помощью CmdShow вы легко можете вывести основное окно в
нормальном виде (по умолчанию), распахнутым до размеров полного
экрана, минимизированным в пиктограмму или скрытым. Лучшим местом
присваивания значения CmdShow является конструктор объекта основ-
ного окна. Это обеспечивает передачу выбранного значения при соз-
дании элемента экрана.

Инициализация первого экземпляра
-----------------------------------------------------------------

Если пользователь запускает приложение в отсутствии уже ра-
ботающих других экземпляров этого приложения, вы можете опреде-
лить для такого первоначального запуска некоторую обработку. Эта
обработка называется инициализацией первого экземпляра. Если
пользователь начинает и завершает ваше приложение, затем запуска-
ет его снова и т.д., каждый экземпляр будет первым экземпляром
(поскольку другие экземпляры в этот момент не выполняются).

Если текущим экземпляром является первый экземпляр, то конс-
труктор Init вызывает InitApplication. TApplication определяет
заместитель метода InitApplication, который вы можете для выпол-
нения специальной инициализации первого экземпляра переопреде-
лить.


B.Pascal 7 & Objects/OW - 141 -


Например, вы можете модифицировать TestApp таким образом,
чтобы о первом экземпляре сообщалось в заголовке основного окна.
Для этого добавьте в тип приложения TMyApplication булевское поле
с именем FirstApp, затем переопределите метод InitApplication,
чтобы он устанавливал FirstApp в True. Наконец, модифицируйте
InitMainWindow для проверки FirstApp и вывода в основном окне
приложения соответствующего заголовка (см. Рис. 8.2).

-----------------------------------------------------------------
------------------------------------------------------T-T-¬------
-¦-=---------------Первый экземпляр-------------------¦^¦v¦------
-+----------------------------------------------------+-+-+------
-¦ ¦------
-¦ -----------------------------------------------------T-T-¬---
-¦ ¦-=---------------Дополнительный экземпляр-----------¦^¦v¦---
-¦ +----------------------------------------------------+-+-+---
-¦ ¦ ¦---
-¦ ¦ -----------------------------------------------------T-T-¬
-¦ ¦ ¦-=-------------Дополнительный экземпляр-------------¦^¦v¦
-¦ ¦ +----------------------------------------------------+-+-+
-¦ ¦ ¦ ¦
-¦ ¦ ¦ ¦
-L--¦ ¦ ¦
----¦ ¦ ¦
----L--¦ ¦
-------¦ ¦
-------L---------------------------------------------------------

Рис. 8.2 Новая инициализация приложения.

program TestApp;
uses OWindows;

type
TMyApplication = object(TApplication)
FirstApp: Boolean;
procedure InitMainWindow; virtual;
procedure InitApplication; virtual;
end;

procedure TMyApplication.InitMainWindow;
begin
if FirstApp then
MainWindow := New(PWindow, Init(nil,
'Первый экземпляр'))
else MainWindow := New(PWindow, Init(nil,
'Дополнительный экземпляр'));
end;

procedure TMyApplication.InitApplication;
begin
FirstApp := True;

B.Pascal 7 & Objects/OW - 142 -

end;

var MyApp; TMyApplication;
begin
MyApp.Init('TestApp');
MyApp.Run;
MyApp.Done;
end.

Инициализация каждого экземпляра
-----------------------------------------------------------------

Пользователь может одновременно выполнять несколько экземп-
ляров ObjectWindows. Метод InitInstance инициализирует каждый эк-
земпляр приложения. Он должен инициализировать только само прило-
жение, а не его основное окно. Основное окно инициализируйте в
InitMaionWindow.

InitInstance вызывает InitMainWindow, а затем создает и вы-
водит основное окно. Для модификации стандартной инициализации
приложения (например, для загрузки таблицы оперативных клавиш)
вам нужно только переопределить InitInstance. Если вы переопреде-
ляете для своего приложения InitInstance, убедитесь сначала, что
он вызывает метод InitInstance, наследуемый из TApplication.

Приведем метод InitInstance, который перед выполнением при-
ложения загружает метод InitInstance. 'MeHotKeys' - это идентифи-
катор ресурса таблицы оперативных клавиш, определенный в файле
ресурса:

procedure TEditApplication.InitInstance;
begin
inherited InitInstance;
HAccTable := LoadAccelerators(HInstance, 'MyHotKeys');
end;

Вы можете также использовать InitInstance для регистрации
экземпляра приложения с внешней DLL (типа Paradox Engine).



B.Pascal 7 & Objects/OW - 143 -

Выполнение приложений
-----------------------------------------------------------------

Метод Run вашего приложения вызывает метод MessageLoop, ко-
торый вызывает цикл обработки сообщений вашей программы. Во время
выполнения вашей программы в цикле обработки сообщений обрабаты-
ваются поступающие от Windows сообщения. Программы ObjectWindows
наследуют цикл MessageLoop, который работает автоматически. До-
полняйте цикл обработки сообщений только специальными диалогами,
оперативными клавишами или обработкой MDI.

Метод MessageLoop для обработки сообщений Windows вызывает
три метода. ProcessDlgMsg работает с безрежимными диалоговыми ок-
нами, ProcessAccels - обрабатывает оперативные клавиши, а
ProcessMDIAccels - оперативные клавиши для приложений MDI. Для
приложений, не использующих командные клавиши или безрежимные ди-
алоговые окна или не являющихся приложениями MDI MessageLoop мож-
но несколько упростить. См. методы TApplication в Главе 21.

Закрытие приложений
-----------------------------------------------------------------

Вызов деструктора Done в основной программе вашего приложе-
ния уничтожает объект приложения. Однако, перед тем как это про-
исходит, программа должна прервать цикл обработки сообщения. Это
может произойти, когда пользователь пытается закрыть основное ок-
но приложения. Мы говорим, что это может произойти, так как
ObjectWindows предусматривает механизм изменения поведения прило-
жения при закрытии: вы можете задавать условия закрытия.

Модификация поведения при закрытии
-----------------------------------------------------------------

Все оконные объекты, наследуют булевский метод CanClose,
возвращающий по умолчанию True (что указывает на подтверждение
закрытия, то есть TestApp закрывается немедленно). Для изменения
поведения при закрытии вы можете переопределить методы CanClose
приложения или основного типа окна. Если какие-либо объекты возв-
ращают из CanClose значение False, то приложение завершиться не
может. Обычно вы можете изменить поведение при закрытии объектно-
го типа основного окна. Например, перед завершением можно убе-
диться, что приложение сохранило файлы.

Механизм CanClose

Механизм CanClose дает основному окну, объекту приложения и
другим окнам возможность подготовиться с закрытию или предотвра-
тить его. В итоге объект приложения должен разрешить закрытие
приложения. По умолчанию он проверяет основное окно. Обычная пос-
ледовательность закрытия выглядит следующим образом:

1. Windows посылает основному окну приложения сообщение
wm_Close.

B.Pascal 7 & Objects/OW - 144 -


2. Объект основного окна вызывает метод CanClose объекта
приложения.

3. Объект приложения вызывает метод CanClose.

4. Объект основного окна вызывает метод CanClose для каждого
из дочерних окон и возвращает True только в том случае,
если методы CanClose дочерних окон возвращают True.

Модификация CanClose

Методы CanClose редко возвращают значение False. Вместо это-
го они должны выполнять некоторые действия, позволяющие им возв-
ращать True. Метод CanClose должен возвращать False только в том
случае, если он не может выполнить действий, необходимых для нор-
мального завершения, или пользователь показывает, что он хочет
продолжать выполнение программы.

Например, метод CanClose окна редактора может проверять из-
менение редактируемого текста, а затем выводить диалоговое окно с
запросом, нужно ли сохранить текст перед закрытием, и восприни-
мать ответ Yes (Да), No (Нет) или Cancel (Отмена). Cancel будет
указывать, что пользователь пока не хочет закрывать приложение,
так что CanClose должен возвращать False. CanClose следует также
возвращать False при обнаружении ошибки в сохранении текста, пре-
доставляя пользователю другую возможность сохранения данных перед
закрытием.

Если метод CanClose не переопределяется, тип основного окна
наследует его от TWindowsObject, где CanClose возвращает True
после вызовов методов CanClose дочерних окон. Чтобы модифициро-
вать поведение при закрытии основного окна, переопределите метод
CanClose. Например, для проверки того, что пользователь собирает-
ся закрыть окно, он может проверять открытые файлы.



B.Pascal 7 & Objects/OW - 145 -

------------------------------------------------------------------
Глава 9. Интерфейсные объекты
-----------------------------------------------------------------

Объекты, представляющие окна, диалоговые окна и управляющие
элементы, называются интерфейсным объектами. В данной главе об-
суждаются общие требования к интерфейсным объектам и их поведе-
ние, а также взаимодействие с реальными окнами, диалоговыми бло-
ками и выводимыми на экран управляющими элементами.

В этой главе поясняется также взаимосвязь между различными
интерфейсными объектами приложения, а также механизм передачи со-
общений Windows.

Примечание: Приводимый здесь материал относится к ок-
нам, диалоговым блокам и управляющим элементам.

Для чего нужны интерфейсные объекты?
-----------------------------------------------------------------

Для чего нужны интерфейсные объекты, если в Windows уже есть
окна, диалоговые блоки и управляющие элементы?

Одна из основных трудностей программирования в Windows - это
достаточно сложная работа с визуальными элементами. Иногда вам
нужно послать в окно сообщение, в другой раз - вызвать функцию
API Windows. Для разных типов элементов экрана соглашения будут
различными.

ObjectWindows уменьшает большую часть этих сложностей, пре-
доставляя объекты, инкапсулирующие элементы экрана и избавляющие
вас от необходимости иметь дело непосредственно с Windows. К тому
же они обеспечивают более удобный интерфейс.

Что делают интерфейсные объекты?
-----------------------------------------------------------------

Интерфейсные объекты предоставляют методы для создания, ини-
циализации, управления и уничтожения соответствующих элементов
экрана, и имеют содержащие данные поля, включая описатель интер-
фейсного элемента и его порождающего и дочернего окна. Методы
объекта берут на себя многие детали программирования в Windows.

Взаимосвязь объект/элемент во многом аналогична связи файла
DOS с переменной Паскаля. Имея файл, вы можете присвоить файловую
переменную, представляющую физическую структуру фактического фай-
ла на диске, а затем работать с этой файловой переменной. С по-
мощью ObjectWindows вы можете определить объект, представляющий
физическое окно, управляющий элемент или диалоговый блок, который
фактически обслуживается администратором окон Windows. Вы работа-
ете с объектом, а он берет на себя функции по обслуживанию эле-
мента экрана.



B.Pascal 7 & Objects/OW - 146 -

Общий интерфейсный объект
-----------------------------------------------------------------

Все интерфейсные объекты ObjectWindows наследуют из единс-
твенного абстрактного объектного типа TWindowsObject, который
определяет поведение, общее для окна, диалога и объектов управля-
ющих элементов, видоизменяемых и специализируемых в производных
объектных типах TDialog, TWindow и TControl.

В качестве общего предка всех интерфейсных объектов методы
TWindowsObject обеспечивают единообразный способ поддержки взаи-
мосвязи между объектами и элементами экрана (включая создание и
уничтожение объектов), обслуживают соотношения "родитель-потомок"
между интерфейсными объектами и регистрируют новый классы Windows
(см. Главу 10).

Новые типы, производные от TWindowsObject, определяются ред-
ко, но он служит основой объектно-ориентированной модели окон. Он
определяет большинство наследуемых объектами функциональных воз-
можностей, когда вы получаете из TWindow и TDialog новые типы.

Создание интерфейсных объектов
-----------------------------------------------------------------

Задание полного интерфейсного объекта с соответствующими ин-
терфейсными элементами требует двух шагов:

* Построения объекта.

* Создания элемента экрана.

Первым шагом является вызов конструктора Init, который стро-
ит интерфейсный объект и устанавливает его атрибуты, такие как
стиль и меню.

Второй шаг заключается в вызове метода создания окна объекта
приложения, MakeWindow, связывающего интерфейсный объект с новым
элементом экрана. Эта связь поддерживается полем HWindow экрана
(описателем окна).

Примечание: Об описателях окон рассказывается в Главе 7
"Иерархия ObjectWindows".

MakeWindow вызывает метод Create объекта, который всегда со-
общает Windows о необходимости создания элемента на экране.
Create создает также метод SetupWindow, который инициализирует
интерфейсный объект путем создания, например, дочерних окон.



B.Pascal 7 & Objects/OW - 147 -

Допустимость описателя окна
-----------------------------------------------------------------

Обычно в Windows вновь созданный интерфейсный элемент полу-
чает (от Windows) сообщение wm_Create, на которое требуется отве-
тить инициализацией. Интерфейсный объект ObjectWindows не будет
получать сообщений wm_Create, поэтому не забудьте определить для
инициализации метод SetupWindow.

Если инициализация интерфейсного объекта требует описателя
элемента экрана (например, для вызова функции API Windows), то
она не должна вызываться раньше SetupWindow. То есть, перед вызо-
вом SetupWindow поле HWindow интерфейсного объекта не является
допустимым и использоваться не должно. Если вы хотите вызывать
функцию API или нечто требующее описателя окна, не вызывайте их
в конструкторе Init. Поместите такие вызовы в метод SetupWindow.

¦<----HWindow допустим--->¦
¦ ¦
¦<-------------------интерфейсный объект допустим---------->¦
--+-----------------------------------------------------------+-->
^ ^ ^ ^ ^
¦ ¦ ¦ ¦ ¦
Init вызывает ¦ ¦ ¦ ¦
наследуемый Init ¦ ¦ ¦ ¦
¦ ¦ Done ¦
SetupWindow вызывает наследуемый ¦
SetupWindow ¦ Done вызывает наследуемый
¦ метод Done
¦
Наследуемый SetupWindow вызывает Create

Рис. 9.1 Когда окно имеет допустимый описатель.



B.Pascal 7 & Objects/OW - 148 -

Видимость на экране
-----------------------------------------------------------------

Создание интерфейсного объекта и соответствующего визуально-
го элемента не обязательно означает, что вы что-то видите на эк-
ране. Когда метод Create указывает Windows на создание элемента
экрана, Windows проверяет, включает ли стиль окна ws_Visible. Ес-
ли да, то интерфейсный элемент будет выводиться. В противном слу-
чае он будет скрытым.

ws_Visible и другие стили окна обычно устанавливаются или
сбрасываются конструктором Init в поле Attr.Style объекта.

В любой момент после создания элемента экрана вы можете вы-
вести или скрыть его, вызвав метод Show интерфейсного объекта.

Уничтожение интерфейсных объектов
-----------------------------------------------------------------

Как и в случае создания интерфейсный объектов, их уничтоже-
ние предполагает выполнение двух шагов:

* Уничтожение визуального интерфейсного элемента (Destroy).

* Уничтожение интерфейсного объекта (Dispose).

Уничтожением экранного элемента занимается метод Destroy ин-
терфейсного объекта, который делает следующее: он вызывает функ-
цию Windows DestroyWindow, чтобы избавиться от элемента экрана, и
устанавливает поле HWindow объекта в 0. Таким образом, проверив
указатель, вы можете сообщить, связан ли еще объект с элементом
экрана.

Уничтожить элемент экрана вы можете без уничтожения объекта
(если хотите создавать и выводить его снова).

Примечание: Уничтожение самого окна обычно не требует-
ся. Это делается автоматически при закрытии окна.

Когда пользователь закрывает на экране окно, ObjectWindows
обнаруживает, что данный элемент экрана уничтожен, устанавливает
поле HWindow соответствующего объекта в 0 и вызывает деструктор
объекта Done.



B.Pascal 7 & Objects/OW - 149 -

Связь порождающего и дочернего объектов
-----------------------------------------------------------------

В приложении Windows совместная работа элементов экрана
(окон, диалоговых блоков и управляющих элементов) обеспечивается
с помощью связей "родитель-потомок". Порождающие окна управляют
своими дочерними окнами, а Windows отслеживает эти связи.
ObjectWindows поддерживает параллельный набор связей между соот-
ветствующими интерфейсными объектами.

Дочернее окно - это элемент экрана (оно не обязано быть ок-
ном), который управляется другим элементом экрана. Например, бло-
ки списка обслуживаются окном или диалоговым блоком, в котором
они выводятся. Они выводятся на экран только при выводе их порож-
дающих окон. Диалоговые блоки, в свою очередь, являются дочерними
окнами, управляемыми порождающими их окнами.

Когда вы перемещаете или закрываете порождающее окно, дочер-
ние окна автоматически закрываются, и в некоторых случаях переме-
щаются в нем. Конечным предком всех дочерних интерфейсных элемен-
тов является основное окно, хотя вы можете иметь окна и диалоги
без порождающих окон.

Порождающими окнами могут быть только диалоговые блоки и ок-
на, но не порождающие элементы. Дочерним окном может быть любой
интерфейсный элемент.

Список дочерних окон
-----------------------------------------------------------------

Когда вы строите объект дочернего окна, то можете в качестве
параметра конструктора Init можете задать порождающее окно (при-
мер вы можете найти в Главе 10). Объект дочернего окна отслежива-
ет свое порождающее окно через указатель на его поле Parent. Он
отслеживает также объекты его дочерних окон, сохраненные в поле
ChildList. Дочернее окно, на которое в данный момент установлен
ChildList, является последним созданным дочерним окном.



B.Pascal 7 & Objects/OW - 150 -

Построение дочерних окон
-----------------------------------------------------------------

Как и в случае интерфейсных объектов, объекты дочерних окон
создаются в два этапа (построение объекта и создание элемента эк-
рана). Объекты порожденного окна следует строить с помощью конс-
труктора Init порождающего окна. Например, объект окна, наследую-
щий из TWindow и содержащий командную кнопку должен иметь пример-
но следующий вид:

constructor TMyWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
TheButton := New(PButton, Init(@Self, id_TheButton,
'Текст кнопки', 20, 10, 100, 25, True));
end;

Обратите внимание на использование указателя Self для связи
дочернего объекта (TheButton) с порождающим (экземпляром
TMyWindow). Конструктор интерфейсного объекта автоматически до-
бавляет к своему списку дочерних окон новые объекты.

Создание дочерних элементов экрана
-----------------------------------------------------------------

Когда построен список дочерних элементов интерфейсного объ-
екта, создание элементов экрана для дочерних окон выполняется ав-
томатически. Создание родительского окна (через вызов MakeWindow)
включает в себя вызов метода SetupWindow порождающего окна. Одним
из наследуемых действий SetupWindow является вызов для каждого из
окон в списке дочерних окон методов SetupWindow.

Примечание: Автоматическое создание можно запретить.
См. ниже раздел "Запрещение автоматического создания".

При создании нового производного объектного типа нужно пом-
нить об инициализации объекта в SetupWindow после вызова наследу-
емого метода SetupWindow, например:

procedure TMyCheckBox.SetupWindow;
begin
inherited SetupWindow; { сначала по умолчанию }
.
.
. { выполнить инициализацию объекта }
end;

Уничтожение дочерних окон
-----------------------------------------------------------------

Вызов деструктора порождающего окна приводит к вызову дест-
рукторов всех его дочерних окон, так что вашей программе не нужно

B.Pascal 7 & Objects/OW - 151 -

явно вызывать деструкторы дочернего окна. Это же справедливо для
метода CanClose, который возвращает True только после вызова
CanClose для всех его дочерних окон.

Запрещение автоматического создания
-----------------------------------------------------------------

Чтобы явно исключить дочернее окно из механизма автоматичес-
кого создания и вывода, вызовите после его создания метод
DisableAutoCreate. Чтобы явно добавить в механизм создания и вы-
вода дочернее окно (такое как диалоговый блок, который при нор-
мальном выводе будет исключен), вызовите после построения его ме-
тод EnableAutoCreate.

Итерация дочерних окон
-----------------------------------------------------------------

Иногда желательно написать методы, для реализации функции
выполняющие итерации по каждому дочернему окну данного окна. Нап-
ример, можно проверить в окне все кнопки с независимой фиксацией.
В этом случае используйте метод TWindowsObject.ForEach:

procedure TMyWindow.CheckAllBoxes;

procedure CheckTheBox(ABox: PWindowsObject); far;
begin
PCheckBox(ABox)^.Check;
end;

begin
ForEach(@CheckTheBox);
end;

Использование метода ForEach (и аналогичных методов
FirstThat и LastThat) похоже на применение методов с аналогичными
названиями в TCollection. Хотя ObjectWindows не использует наборы
для обслуживания дочерних окон, методы итерации работают анало-
гично.

Поиск определенного дочернего окна
-----------------------------------------------------------------

Иногда желательно иметь методы, выполняющие итерацию по
списку окон в поиске конкретного окна. Например, в окне с нес-
колькими кнопками вам может потребоваться найти первую установ-
ленную кнопку с независимой фиксацией. В этом случае метод
TWindowsObject.FirstThat можно записать так:

function TMyWindow.GetFirstChecked: PWindowsObject;

function IsThisOneChecked(ABox: PWindowsObject): Boolean;
far;
begin

B.Pascal 7 & Objects/OW - 152 -

IsThisOneChecked := (ABox^.GetCheck = bf_Checked);
end;

begin
GetFirstChecked := FirstThat(@IsThisOneChecked);
end;



B.Pascal 7 & Objects/OW - 153 -

------------------------------------------------------------------
Глава 10. Объекты окон
-----------------------------------------------------------------

Объекты окон (или оконные объекты) - это интерфейсные объек-
ты, предназначенные для облегчения работы с окнами. В данной гла-
ве поясняется, как создавать и заполнять окна приложения. Это
предусматривает следующие задачи:

* Инициализацию оконных объектов.

* Установку атрибутов создания.

* Создание экранных элементов окна.

* Установку атрибутов регистрации.

* Использование специализированных окон.

* Прокрутку окон.

Что такое объекты окон?
-----------------------------------------------------------------

Термин "объект окна" относится к любому интерфейсному объек-
ту, представляющему окно, а в Windows это почти все, что выводит-
ся на экран. В качестве шаблона определения большей части фунда-
ментального поведения основного окна и любого всплывающего окна
приложения ObjectWindows использует тип TWindow.


Окна, которые не являются окнами
-----------------------------------------------------------------

TWindow имеет три типа-потомка: TMDIWindow, TControl и
TEditWindow, так что все они также являются оконными объектами,
хотя на самом деле это не окна в полном смысле слова. Типы MDI
используются в приложениях ObjectWindows, которые соответствуют
стандарту многодокументального интерфейса Windows. Об MDI и этих
типах рассказывается в Главе 14. TControl определяет управляющие
элементы, такие как командные кнопки и блоки списков (см. Главу
12). Чаще всего новые оконные типы являются производными от
TWindow.

Эта глава охватывает типы TWindow и TEditWindow и содержит
примеры регистрации новых классов окон.



B.Pascal 7 & Objects/OW - 154 -

Где найти объекты окон
-----------------------------------------------------------------

Каждое приложение Windows имеет основное окно. Это окно мо-
жет выводиться в виде пиктограммы или не выводиться снова (скры-
тое окно), но существует всегда. Приложения ObjectWindows не яв-
ляются исключением: они должны иметь основное окно, представлен-
ное оконным объектом.

Примером минимальной программы ObjectWindows ("скелета"
программы) является TestApp в Главе 8. Основное окно программы
ObjectWindows является обычно экземпляром TWindow или определяе-
мого в программе наследующего типа. Многие приложения имеют дру-
гие окна, которые обычно являются дочерними окнами основного ок-
на. Эти дополнительные окна также являются экземплярами TWindow
или одного из его потомков.

Например, графическая программа может определять для своего
основного окна тип TPaintWindow, а для окна, показывающего графи-
ческий рисунок - тип TZoomWindow. В этом случае TPaintWindow и
TZoomWindow являются наследниками TWindow.

Инициализация объектов окон
-----------------------------------------------------------------

Оконные объекты представляют элементы окна, связанные через
описатели, сохраненные в наследуемом из TWindowsObject поле
HWindow. Так как объект окна имеет две части, его создание требу-
ет двух шагов: инициализации объекта и создания визуального эле-
мента.

Инициализация окна - это процесс создания оконного объекта
ObjectWindows путем вызова конструктора Init:

Window1 := New(PWindow,Init(nil, 'Заголовок окна 1'));
Window2 := New(PNewWindowType,Init(nil,'Заголовок окна 2'));

Init создает новый оконный объект и устанавливает поле Title
в Attr в передаваемый аргумент PChar. Первый аргумент вызова Init
- это оконный объект порождающего окна. Если окно является основ-
ным окном (не имеющим порождающего окна), то это nil.



B.Pascal 7 & Objects/OW - 155 -

Установка атрибутов создания
-----------------------------------------------------------------

Типичное приложение Windows имеет много различных типов
окон: перекрывающиеся или всплывающие, окна с рамкой, прокручива-
емые окна, окна с заголовком и др. Эти атрибуты стилей, а также
заголовок и меню окна задаются при инициализации оконного объекта
и используются при создании элементов окна.

Атрибуты создания оконного объекта, такие как стиль, заголо-
вок и меню, записываются в поле Attr объекта - записи типа
TWindowAttr. TWindowAttr содержит следующие поля:

Атрибуты создания окна Таблица 10.1
-------------T--------------T-----------------------------------¬
¦ Поле ¦ Тип ¦ Использование ¦
+------------+--------------+-----------------------------------+
¦ Title ¦ PChar ¦ Строка заголовка. ¦
¦ ¦ ¦ ¦
¦ Style ¦ Longint ¦ Комбинированная константа стиля. ¦
¦ ¦ ¦ ¦
¦ Menu ¦ HMenu ¦ Описатель ресурса меню. ¦
¦ ¦ ¦ ¦
¦ X ¦ Integer ¦ Горизонтальная координата экрана¦
¦ ¦ ¦ верхнего левого угла окна. ¦
¦ ¦ ¦ ¦
¦ Y ¦ Integer ¦ Вертикальная координата экрана¦
¦ ¦ ¦ верхнего левого угла окна. ¦
¦ ¦ ¦ ¦
¦ W ¦ Integer ¦ Начальная ширина окна в координа-¦
¦ ¦ ¦ тах экрана. ¦
¦ ¦ ¦ ¦
¦ H ¦ Integer ¦ Начальная высота окна в координа-¦
¦ ¦ ¦ тах экрана. ¦
¦ ¦ ¦ ¦
L------------+--------------+------------------------------------


B.Pascal 7 & Objects/OW - 156 -


^----------------------------------------------------------------
-(0,0)-----------------------------------------------------------
-----(X,Y)-------------------------------------------------------
-----v-----------------------------------------------------------
---------------------------------------------T--T--¬-------------
-----¦ ---------------- Title --------------¦ ¦ ¦ ^-----------
-----+---------------------------------------+--+--+ ¦-----------
-----¦ Menu ¦ ¦-----------
-----+---------------------------------------------+ ¦-----------
-----¦ ¦ ¦-----------
-----¦ ¦ ¦-----------
-----¦ ¦ H-----------
-----¦ ¦ ¦-----------
-----¦ ¦ ¦-----------
-----¦ ¦ ¦-----------
-----¦ ¦ ¦-----------
-----¦ ¦ ¦-----------
-----¦ ¦ v-----------
-----L-----------------------------------------------------------
-----¦<------------------W------------------------>¦-------------
-----------------------------------------------------------------
-----------------------------------------------------------------

Рис. 10.1 Атрибуты окна.

Используемые по умолчанию атрибуты окна
-----------------------------------------------------------------

По умолчанию TWindow.Init устанавливает Attr.Style в
ws_Visible. Если окно является основным окном приложения, то
Style равно ws_OverlappedWindow or ws_Visible.

Menu по умолчанию устанавливается в 0. Это означает, что ме-
ню не определено.

X, Y, W и H устанавливаются в cw_UseDefault, что дает в ре-
зультате перекрывающееся окно удовлетворительного размера. Когда
создается окно, не являющееся основным, значения X, Y, W и H вы
обычно устанавливаете сами.




B.Pascal 7 & Objects/OW - 157 -

Переопределение используемых по умолчанию атрибутов
-----------------------------------------------------------------

При создании новых оконных типов, производных от TWindow, вы
обычно определяете новый конструктор Init (особенно если хотите
получить атрибут создания, отличных от используемого по умолча-
нию). Если вы хотите переопределить Init, то можете заново задать
атрибуты объекта, непосредственно изменяя поле Attr после вызова
Init.

Если вы переопределили Init, убедитесь, что первое, что он
делает - это вызов наследуемого метода TWindow.Init, устанавлива-
ющего используемые по умолчанию атрибуты. Затем вы можете изме-
нить по своему выбору любой из атрибутов. Например, типичное окно
может определять конструктор Init, который устанавливает атрибут
Menu:

constructor TWindowType.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Menu := LoadMenu(HInstance, 'TheMenu');
AChildWindow := New(PChildWindowType, Init(@Self,
'Заголовок дочернего окна'));
List1 := New(PListBox, Init(@Self, id_ListBox,
201, 20, 20, 180, 80));
.
.
.
end;

Атрибуты порожденного окна
-----------------------------------------------------------------

Конструктор TWindowType отвечает за построение его дочерних
объектов, таких как всплывающие окна и блоки списка. Тип порож-
денного окна, в свою очередь, может устанавливать атрибуты в сво-
ем собственном конструкторе Init:

constructor TChilwWindowType.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
with Attr do
begin
Style := Style or ws_PopupWindow or ws_Caption;
X := 100; Y := 100; W := 300; H := 300;
end;
end;

В качестве альтернативы вы можете не определять потомка
типа окна, а сначала построить объект окна, а затем переустано-
вить его атрибуты (все это в конструкторе Init порождающего ок-

B.Pascal 7 & Objects/OW - 158 -

на):

constructor TWindowType.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Menu := LoadMenu(HInstance, 'TheMenu');
AChildWindow := New(PChildWindowType, Init(@Self,
'Заголовок дочернего окна'));
with Attr do
begin
Style := Style or ws_PopupWindow or ws_Caption;
X := 100; Y := 100; W := 300; H := 300;
end;
.
.
.
end;

Создание элементов окна
-----------------------------------------------------------------

После построения оконного объекта вам нужно сообщить
Windows, что требуется создать связанные с объектом элементы эк-
рана. Это делается с помощью вызова MakeWindow объекта приложения
и передачи ему в качестве параметра указателя на объект окна.

if Application^.MakeWindow(AWindow) <> nil then
{ успешное создание }
else { неуспешное создание }

MakeWindow вызывает два важных метода: ValidWindow и Create.
ValidWindow проверяет успешность построение объекта окна, прове-
ряя поле Status. Если по каким-либо причинам конструктор завер-
шился неуспешно, то MakeWindow возвращает nil. При успешном вы-
полнении конструктора MakeWindow переходит на метод Create окон-
ного объекта.

Create - это метод, который фактически сообщает Windows о
создании элемента экрана. Если Create завершается неудачно,
MakeWindow возвращает nil. В противном случае возвращается указа-
тель на оконный объект. Для работы с элементом экрана Create так-
же устанавливает поле HWindow.

Хотя этот метод фактически создает элемент экрана, вы обычно
не можете вызывать Create явно. Основное окно приложения автома-
тически создается при запуске программы методом
TApplication.InitInstance.

Все прочие окна приложения являются дочерними окнами, прямо
или косвенно порождаемыми основным окном, а дочерние окна созда-
ются обычно в методе SetupWindow или в его порождающих оконных
объектах, либо с помощью MakeWindow динамически на этапе выполне-

B.Pascal 7 & Objects/OW - 159 -

ния.

Примечание: Дочерние окна и SetupWindow описываются в
Главе 9 "Интерфейсный объекты".

В общем случае порождающие окна обычно вызывают для своих
дочерних окон методы Init и MakeWindow. Атрибуты оконного объекта
обычно устанавливаются их методами объекта порождающего окна.
Поскольку основное окно приложения не имеет порождающего окна,
объект приложения строит и создает его при запуске приложения.

Задание атрибутов регистрации
-----------------------------------------------------------------

В ходе инициализации оконного объекта путем заполнения поля
объекта Attr вы можете установить несколько атрибутов окна, такие
как его стиль, расположение и меню. Эти атрибуты используются для
создания соответствующего оконного элемента, поэтому они называ-
ются атрибутами создания.

Другие атрибуты, включая фоновый цвет, пиктограмму представ-
ления и курсора "мыши", более тесно связаны с данным типом окон-
ного объекта и не могут изменяться в ходе работы программы. Эти
присущие окну атрибуты называются атрибутами регистрации, так как
они устанавливаются при регистрации класса окна в Windows.



B.Pascal 7 & Objects/OW - 160 -

Классы окон
-----------------------------------------------------------------

С каждым типом оконного объекта связан список атрибутов ре-
гистрации, которые называются классом окна. Список атрибутов ре-
гистрации во многом напоминает список атрибутов создания, запи-
санных в поле записи Attr объекта окна. Однако, атрибуты регист-
рации сохраняются в записи с именем TWndClass, который определя-
ется и поддерживается Windows.

Процесс связи класса окна с типом оконного объекта называет-
ся регистрацией класса окна. ObjectWindows автоматизирует процесс
регистрации. Таким образом, если вы хотите изменить какую-либо из
используемых по умолчанию характеристик объекта, то можете не
беспокоиться о классе регистрации окна.

Поля записи TWndClass и их типы перечислены в следующей таб-
лице:

Атрибуты регистрации окна Таблица 10.2
--------------------------T-------------------T-----------------¬
¦ Характеристика ¦ Поле ¦ Тип ¦
+-------------------------+-------------------+-----------------+
¦ стиль класса ¦ style ¦ Word ¦
¦ пиктограмма ¦ hIcon ¦ HIcon ¦
¦ курсор ¦ hCursor ¦ HCursor ¦
¦ фоновый цвет ¦ hbrBackground ¦ HBrush ¦
¦ меню по умолчанию ¦ lpszMenuName ¦ PChar ¦
L-------------------------+-------------------+------------------

Поля стиля класса

Это поле стиля отличается от атрибута стиля окна (ws_), за-
даваемого при инициализации окна, поскольку задает поведение,
присущее операциям окна (в отличие от их визуального представле-
ния). Это поле может заполняться комбинацией констант стиля
(cs_).

Например, cs_HRedraw приводит к повторному отображению окна
при изменении его размера по горизонтали; cs_DoubleClk позволяет
окну получать сообщения о двойном нажатии кнопки "мыши";
cs_NoClose предотвращает выбор параметра Close меню Control, а
cs_ParentDC дает окну контекст дисплея порождающего окна.

Поле пиктограммы

Это поле содержит описатель пиктограммы, которое использует-
ся для представления окна в его минимизированном состоянии. Обыч-
но для представления основного окна программы выбирается ресурс
пиктограммы.

Поле курсора


B.Pascal 7 & Objects/OW - 161 -

Поле hCursor содержит описатель курсора, который использует-
ся для представления указателя "мыши" при позиционировании его в
окне.

Поле фонового цвета

Это поле задает фоновый цвет окна. Для большинства приложе-
ний используется стандартный назначаемый по умолчанию цвет окна,
который может устанавливаться пользователем в управляющей панели.
Однако вы можете путем установки этого поля в описатель физичес-
кой кисти подставить конкретный цвет. Либо вы можете установить
любое из значений цветов Windows, такие как color_ActiveCaption.
К любому значению цвета всегда добавляйте 1.

Поле используемого по умолчанию меню

Это поле указывает на имя ресурса меню, которое служит ис-
пользуемым по умолчанию меню для данного класса. Например, если
вы определите тип EditWindow, который всегда имеет стандартное
меню редактирования, то можете задать здесь это меню. Это устра-
нит необходимость задания меню в методе Init. Если данный ресурс
меню имеет идентификатор 'MyMenu', вы можете установить это поле
следующим образом:

AWndClass.IpszMenuName := 'MyMenu';



B.Pascal 7 & Objects/OW - 162 -

Используемые по умолчанию атрибуты регистрации
-----------------------------------------------------------------

Тип TWindow определяет класс окна 'TurboWindow' с пустой
пиктограммой, курсором-стрелкой и стандартным цветом окна. Ис-
пользуемый по умолчанию класс ObjectWindows (TurboWindow) имеет
следующие атрибуты:

* стиль: cs_HRedraw or cs_VRedraw (повторное отображение
после каждого изменения размера);

* пиктограмма: idi_Application (пустой прямоугольник);

* курсор: idc_Arrow (стандартная стрелка Windows);

* фоновый цвет: HBrush(color_Window + 1);

* меню по умолчанию: nil.

Регистрация нового класса
-----------------------------------------------------------------

Чтобы изменить атрибут регистрации, такой как курсор или
пиктограмму, вам нужно написать два метода - GetClassName и
GetWindowClass - и определить новый класс окна. Каждый раз, когда
вы изменяете атрибуты регистрации, вам нужно изменить имя класса.
Если класс регистрации с данным именем уже зарегистрирован в
Windows, другие классы с тем же именем класса регистрироваться не
будут - они получат атрибуты уже зарегистрированного класса.



B.Pascal 7 & Objects/OW - 163 -

Изменение имени класса
-----------------------------------------------------------------

GetClassName - это функция, которая возвращает имя (PChar)
класса окна. TWindow.GetClassName возвращает 'TurboWindow', имя
используемого по умолчанию класса окна. TWindow.GetClassName
возвращает 'TurboWindow' - имя используемого по умолчанию класса
окна:

function TWindow.GetClassName: PChar;
begin
GetClassName := 'TurboWindow';
end;

Чтобы определить тип объекта окна с именем IBeamWindow, ко-
торый использует вместо стандартной стрелки I-образный курсор,
переопределите наследуемый метод следующим образом:

function TBeamWindow.GetClassName: PChar;
begin
GetClassName := 'IBeamWindow';
end;

Примечание: Имя класса не обязательно должно соответс-
твовать имени объектного типа.

Имя класса должно быть уникальным.

Определение новых атрибутов регистрации
-----------------------------------------------------------------

Чтобы отклониться от стандартных характеристик, вы должны
заполнить поля записи TWndClass с различными данными в методе
GetWindowClass.

GetWindowClass воспринимает в качестве аргумента-переменной
запись TWndClass и заполняет ее поля новыми атрибутами регистра-
ции. Когда вы определяете новый метод GetWindowClass, вам следует
всегда сначала для установки значений по умолчанию вызывать нас-
ледуемый метод TWindow.GetWindowClass, а затем устанавливать по-
ля, которые вы хотите изменить.

Например, в поле hCursor хранится описатель ресурса курсора.
Для IBeamWindow определяется метод GetWindowClass:

procedure IBeamWindow.GetWindowClass(var AWndClass:
TWndClass);
begin
inherited GetWindowClass(AWndClass);
AWndClass.hCursor := LoadCursor(0, idc_IBeam);
end;

Примечание: idc_Beam - это константа, представляющая

B.Pascal 7 & Objects/OW - 164 -

один из курсоров Windows.

Кроме окон, диалоговым окнам (не диалоговым блокам) необхо-
димо регистрировать классы окна (см. Главу 11). Диалоговым блокам
и управляющим элементам классы окон не требуются.



B.Pascal 7 & Objects/OW - 165 -

Использование специализированных окон
-----------------------------------------------------------------

ObjectWindows предусматривает два потомка TWindow, являющих-
ся специализированными окнами для редактирования текста. Объект-
ный тип TEditWindow обеспечивает простой текстовый редактор, не
обладающий возможностями чтения из файла или записи в него. Тип
TFileWindow, наследующий из TEditWindow, обеспечивает текстовый
редактор с возможностями чтения/записи файлов.

Эти объекты можно использовать непосредственно как стандарт-
ные компоненты ваших приложений. Вы можете также построить произ-
водные от них типы и создать свои собственные специализированные
редакторы. Программы или модули, использующие окна редактирования
или файловые окна, должны включать в свой оператор uses модуль
OStdWnds.

Использование окон редактирования
-----------------------------------------------------------------

Окно редактирования - это окно с управляющим элементом ре-
дактирования, заполняющим его область клиента. TEditWindow.Init
инициализирует поле Editor окна редактирования, чтобы оно указы-
вало на управляющий элемент объекта редактирования.
TEditWindow.SetupWindow устанавливает размеры управляющего эле-
мента редактирования в соответствии с областью клиента окна и
создает экранный управляющий элемент редактирования.

Метод WMSize обеспечивает изменение размера управляющего
элемента редактирования при изменении размера его окна. Метод
WMSetFocus обеспечивает, что управляющий элемент редактирования
получает фокус ввода при получении окном сообщения wm_SetFocus.


B.Pascal 7 & Objects/OW - 166 -


Показанная ниже программа EditWindowTester использует окно
редактирования, чтобы пользователь мог редактировать текст для
простой (нефункциональной) электронной почты.

------------------------------------------------------------T-T-¬
¦-=---------------------Edit Window Tester------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ Edit Text ¦
+---------------------------------------------------------------+
¦ Кого это может касаться: ¦
¦ ¦
¦ Я хотел бы зарегистрировать жалобу по поводу попугая, которого¦
¦ я купил в вашем магазине полгода назад. Он умер. ¦
¦ ------------------------------------¬ ¦
¦ Брюс ¦-=-------Передано сообщение--------¦ ¦
¦ +-----------------------------------+ ¦
¦ ¦ ¦ ¦
¦ ¦ 6 строк послано ¦ ¦
¦ ¦ -------------¬ ¦ ¦
¦ ¦ ¦----OK------¦ ¦ ¦
¦ ¦ L------------- ¦ ¦
¦ L------------------------------------ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 10.2 Окно редактирования.

program EditWindowTester;
{$R EWNDTEST.RES}
uses ODialogs, WinTypes, WinProcs, Strings, OStdWnds;
const cm_sendText = 399;
type
TestApplication = object(TApplication)
procedure InitMainWindow; virtual;
end;

PMyEditWindow = ^MyEditWindow;
MyEditWindow = object(TEditWindow)
constructor Init(AParent: PWindowsObject;
ATitle: PChar);
procedure CMSendText(var Msg: TMessage); virtual
cm_First + cm_SendText;
end;

constructor MyEditWindow.Init(AParent: PWindowsObject;
Atitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Menu := LoadMenu(HInstance, MakeIntResource(102));
end

procedure MyEditWindows.CMSendText(var Msg: TMessage);

B.Pascal 7 & Objects/OW - 167 -

var
Lines: Integer;
TestString: string[3];
Text: array[0..20] of Char;
begin
Lines := Editor^.GetNumLines;
Str(Lines, TextString);
StrCat(Text, ' строк послано');
MessageBox(HWindow, @Text, 'Передано сообщение', mb_Ok);
end;

procedure TestApplication.InitMainWindow;
begin
MainWindow := New(PMyEditWindow, Init(nil,
'Окно редактирования - попробуйте набор и редактирование'));
end;

var TestApp: TestApplication;
begin
TestApp.Init('EditWindowTester');
TestApp.Run;
TestApp.Done;
end.

Использование файловых окон
-----------------------------------------------------------------

Файловое окно - это окно редактирования с дополнительными
возможностями, позволяющими считывать и записывать данные в файл.
TFileWindow.Init воспринимает в качестве аргумента заголовок окна
и устанавливает поле FileDialog таким образом, чтобы оно указыва-
ло на файловый диалоговый объект.


B.Pascal 7 & Objects/OW - 168 -


Для работы с файлами TFileWindow имеет четыре метода. Методы
Open, Save и SaveAs для вывода пользователю подсказки с именем
файла используют поле TFileWindow.FileDialog (см. Главу 11). Ме-
тод New дает пользователю возможность отмены, если редактирование
нового файла приведет к потере изменений текущего текста. Чтобы
дать пользователю возможность доступа к этим методам, создайте
свое меню со следующими идентификаторами меню:

Методы и идентификаторы меню файлового окна Таблица 10.3
----------------------T-----------------------------------------¬
¦ Метод ¦ Идентификатор меню для вызова ¦
+---------------------+-----------------------------------------+
¦ New ¦ cm_FileNew ¦
¦ Open ¦ cm_FileOpen ¦
¦ Save ¦ cm_FileSave ¦
¦ SaveAs ¦ cm_FileSaveAs ¦
L---------------------+------------------------------------------

Вы можете использовать файловые окна без модификации как
простые автономные текстовые редакторы. Однако, иногда желательно
создать производные от TFileWindow типы и обеспечить дополнитель-
ные функциональные возможности. Например, можно предусмотреть
средство поиска. Помните, что вы все равно будете иметь доступ к
управляющему элементу редактирования TFileWindow.Editor.

Прокрутка содержимого окон
-----------------------------------------------------------------

В большинстве случаев для прокрутки текущей области просмот-
ра пользователи используют полосы прокрутки вдоль края окна. В
отличие от стандартных управляющих элементов типа полос прокрутки
полосы прокрутки окна являются частью самого окна.

ObjectWindows управляет прокруткой окна, предоставляя каждо-
му оконному объекту поле Scroller, которое может указывать на
объект TScroller. Объект прокрутки TScroller обеспечивает автома-
тизированный способ прокрутки в окнах текста и графики. Кроме то-
го, TScroller может прокручивать окна, когда пользователь переме-
щает "мышь" за область клиента окна (это называется автоматичес-
кой прокруткой и действует даже для окон, которые не имеют полос
прокрутки).



B.Pascal 7 & Objects/OW - 169 -

Что такое объект прокрутки?
-----------------------------------------------------------------

TScroller содержит значения, определяющие, насколько должно
прокручивается окно. Эти значения записываются в полях XUnit,
YUnit, XLine, YLine, XRange, YRange, XPage и YPage объекта
TScroller. Поля, начинающиеся с буквы X, представляют горизон-
тальные значения, а начинающиеся с буквы Y - вертикальные.

Единицы прокрутки

Единица прокрутки определяет минимальную величину прокрутки.
Она задается в наименьших единицах устройства (обычно в элементах
изображения, но может зависеть от текущего режима отображения),
на которые вы можете выполнять прокрутку в горизонтальном или
вертикальном направлении. Это значение обычно основывается на ви-
де выводимой на экран информации.

Например, если вы выводите текст с шириной символа 8 элемен-
тов изображения и высотой 15, то в качестве значений XUnit и
YUnit полезно задать, соответственно, 8 и 15.

Строки, страницы и диапазон

Другие атрибуты прокрутки - строка, страница и диапазон -
выражаются в единицах прокрутки. Значения Line (строка) и Page
(страница) - это число единиц, на которые выполняется прокрутка в
ответ на запрос пользователя. Запрос может иметь форму щелчка
кнопкой "мыши" на концах полосы прокрутки (построчная прокрутка).
Щелчок "мышью" в самой полосе прокрутки (но не на маркере полосы
прокрутки) позволяет выполнять постраничную прокрутку. Атрибуты
диапазона (XRange, YRange) представляют общее число единиц, на
которое можно выполнять прокрутку. Обычно этот диапазон определя-
ется на основе размера редактируемого документа.


B.Pascal 7 & Objects/OW - 170 -


Типичный объект прокрутки

В качестве примера рассмотрим текстовое окно редактирования.
Если вы хотите вывести на экран текстовый файл, имеющий 400 строк
текста с границей 80 символов и 50 строками на странице, то можно
выбрать следующие значения:

Типичные значения для окна редактирования Таблица 10.4
------------------T-------------T-------------------------------¬
¦ Поле ¦ Значение ¦ Смысл ¦
+-----------------+-------------+-------------------------------+
¦ XUnit ¦ 8 ¦ ширина символа ¦
¦ YUnit ¦ 15 ¦ высота символа ¦
¦ XLine, YLine ¦ 1 ¦ 1 единица на строку ¦
¦ XPage ¦ 40 ¦ 40 символов по горизонтали на¦
¦ ¦ ¦ страницу ¦
¦ YPage ¦ 50 ¦ 50 символов по вертикали на¦
¦ ¦ ¦ страницу ¦
¦ XRange ¦ 80 ¦ максимальный горизонтальный¦
¦ ¦ ¦ диапазон ¦
¦ YRange ¦ 400 ¦ максимальный вертикальный ди-¦
¦ ¦ ¦ апазон ¦
L-----------------+-------------+--------------------------------

Объект TScroller с данными значениями позволяет выполнять
построчную или постраничную прокрутку. С помощью полос прокрутки
или автоматической прокрутки выполняется просмотр всего файла.

Значения по умолчанию

По умолчанию XLine и YLine имеют значение 1, так что без яв-
ной необходимости устанавливать их в другие значения не нужно.
Для установки значений прокрутки на страницу также существует ис-
пользуемая по умолчанию схема, согласно которой страница прокрут-
ки будет соответствовать текущей высоте или ширине области клиен-
та окна (в зависимости от направлений прокрутки). Если вы не хо-
тите переопределить данный механизм, переустанавливать эти значе-
ния не требуется.



B.Pascal 7 & Objects/OW - 171 -

Задание для окна объекта прокрутки
-----------------------------------------------------------------

Чтобы задать для окна объект прокрутки, постройте в конс-
трукторе своего оконного объекта объект TScroller и присвойте его
полю Scroller. Вам нужно установить начальный размер единицы и
диапазона, но позднее вы можете их изменить.

При использовании объекта прокрутки для автоматической прок-
рутки полосы прокрутки не требуются, но многие прокручиваемые
окна их имеют. Чтобы добавить в окно полосы прокрутки, добавьте
в поле Attr.Style ws_VScroll, ws_HScroll (или то и другое).

Приведем пример конструктора для текстового окна редактиро-
вания:

constructor TTextWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Style := Attr.Style or ws_VScroll or ws_YScroll;
Scroller := New(PScroller, Init(@Self, 8, 15, 80, 400));
end;

В качестве аргументов TScroller воспринимает прокручиваемое
окно и начальные значения для полей XUnit, YUnit, XRange и YRange
соответственно. Атрибуты строки и страницы получают значения по
умолчанию.

После вывода окна на экран содержимое его области клиента
можно прокручивать вертикально и горизонтально, используя для
этого полосу прокрутки или автоматическую прокрутку. Метод Pant
окна просто рисует на экране графическую информацию, необходимую
для уведомления о прокрутке. Как описывается в конце этого разде-
ла, метод Paint можно оптимизировать для вывода только части ри-
сунка.

Пример прокрутки
-----------------------------------------------------------------

Scroll - это полное приложение с графическим выводом, допус-
кающим прокрутку. Показанная ниже программа рисует последователь-
ность прямоугольников, затем увеличивает их размер, так что вся
картинка не умещается в область клиента окна, отображенного на
обычном экране VGA. С помощью полос прокрутки вы можете просмат-
ривать различные части рисунка или автоматически прокручивать
картинку, удерживая нажатой левую кнопку "мыши" и перемещая ее из
области клиента.

program Scroll;

uses Strings, WinTypes, WinProcs, OWindows;


B.Pascal 7 & Objects/OW - 172 -

type
TScrollApp = object(TApplication)
procedure InitMainWindow; virtual;
end;

PScrollWindow = ^TScrollWindow;
TScrollWindow = object(TWindow)
constructor Init(ATitle: PChar);
procedure Paint(PaintDC: HDC;
var PaintInfo: TPaintStruct); virtual;
end;

procedure TScrollApp.InitMainWindow;
begin
MainWindow := New(PScrollWindow, Init('Boxes'));
end;

constructor TScrollWindow.Init(ATitle: PChar);
begin
inherited Init(nil, ATitle);
Attr.Style := Attr.Style or ws_VScroll or ws_HScroll;
Scroller := New(PScroller, Init(@Self, 8, 15, 80, 60));
end;

procedure TScrollWindow.PAint(PaintDC: HDC;
var PaintInfo: TPaintStruct);
var X1, Y1, I: Integer;
begin
for I := 0 to 49 do
begin
X1 := 10 + I*8;
Y1 := 30 + I*5;
Rectangle(PaintDC, X1, Y1, X1 + X1, X1 + Y1 * 2);
end;
end;

var ScrollApp: TScrollApp;

begin
ScrollApp.Init('ScrollApp');
ScrollApp.Run;
ScrollApp.Done:
end.



B.Pascal 7 & Objects/OW - 173 -

Запрещение автоматической прокрутки
-----------------------------------------------------------------

Объект TScroller может по умолчанию выполнять автоматическую
прокрутку, но установка поля AutoMode TScroller в значение False
отключает это средство. Окно-владелец может сделать это в конс-
трукторе после построения объекта TScroller:

Scroller := New(PScroller, Init(@Self, 8, 15, 80, 60));
Scroller^.AutoMode :=False;

Если AutoMode равно False, то прокрутка может выполняться
только с помощью полос прокрутки. Полезная особенность автомати-
ческой прокрутки состоит в том, что чем дальше вы сдвинете "мышь"
от области клиента окна, тем быстрее будет происходить прокрутка
окна. В зависимости от удаления мыши приращение прокрутки будет
обратно пропорционально значению параметра строк и прямо пропор-
ционально значению параметра страницы.

Отслеживание полос прокрутки
-----------------------------------------------------------------

В дополнение к автоматической прокрутке, приведенный выше
пример программы будет отслеживать запросы на прокрутку, сдвигая
при нажатой кнопке "мыши" маркер полосы прокрутки. Другими слова-
ми картинка сдвигается уже при нажатой кнопке. Эта особенность
дает действительную обратную связь, и пользователь может сдвигать
нужную часть изображения не отпуская кнопку "мыши".

Однако, в некоторых случаях этот эффект нежелателен. Напри-
мер, если вы просматриваете большой текстовый файл, такое отсле-
живание может замедлить работу, поскольку возникает необходимость
постоянно считывать информацию с диска и отображать порцию текста
для каждого движения "мыши". В такой ситуации лучше отменить этот
эффект:

Scroller^.TrackMode:=False;

Теперь никакой прокрутки не происходит до момента отпускания
кнопки на мыши, и в области клиента будет лишь однократно показа-
на нужная часть картинки.



B.Pascal 7 & Objects/OW - 174 -

Модификация единиц прокрутки и диапазона
-----------------------------------------------------------------

В приведенных выше примерах мы предполагали, что к моменту
построения TScroller известны значения единиц и диапазонов. Во
многих случаях эта информация неизвестна или может меняться при
изменении размеров отображаемой информации. В этом случае может
потребоваться установить или изменить значения диапазона (а может
быть и единиц) позднее. Если значения заранее неизвестны, то их
можно задать как 0 в конструкторе TScroller.

Изменение диапазона

Метод SetRange воспринимает два целочисленных аргумента -
число горизонтальных и вертикальных единиц, которые определяют
общий диапазон прокрутки. Метод SetRange должен использоваться
при изменении размеров картинки. Например, при подготовке изобра-
жения картинки шириной 1 0 единиц и высотой 300, данная команда
установит диапазон прокрутки надлежащим образом:

Scroller^.setRange(100, 300);

Изменение единиц прокрутки

Если при инициализации объекта TScroller единицы неизвестны,
то их значения могут быть установлены непосредственно перед прок-
руткой. Например, они могут быть установлены методом окна
SetupWindow:

procedure ScrollWindow.SetupWindow;
begin
TWindow.SetupWindow;
Scroller^.XUnit:=10;
Scroller^.YUnit:=20;
end;

Изменение позиции прокрутки
-----------------------------------------------------------------

Windows с помощью методов ScrollTo и ScrollBy может выпол-
нять принудительную прокрутку. Каждый из них воспринимает два це-
лочисленных аргумента в терминах горизонтальных и вертикальных
единиц прокрутки. Например, если нужно переместиться к левому
верхнему углу картинки, то используется ScrollTo:

Scroller^.ScrollTo(0, 0);

Приведем другой пример. Если картинка имеет длину 400 единиц
в вертикальном направлении, то позицию прокрутки можно перемес-
тить к середине картинки следующим образом:

Scroller^.ScrollTo(0, 200);


B.Pascal 7 & Objects/OW - 175 -

Метод ScrollBy может перемещать позицию просмотра на задан-
ное число единиц вверх, вниз, влево или вправо. Отрицательные
значения осуществляют сдвиг к левому верхнему углу, а положитель-
ные - к правому нижнему. Если нужно сместиться на 10 единиц впра-
во и на 20 единиц вниз, то это можно сделать командой:

Scroller^.ScrollBy(10, 20);

Установка размеров страницы
-----------------------------------------------------------------

По умолчанию размер страницы (XPage и YPage) устанавливается
в соответствии с размером области клиента окна. При изменении
размеров окна механизм прокрутки учитывает эту информацию. Метод
окна WMSize вызывает метод прокрутки SetPageSize, который уста-
навливает поля объекта XPage и YPage на основании текущих разме-
ров области клиента окна и значений XUnit и YUnit. Для отмены
этого механизма и непосредственной установки размеров страницы вы
должны переписать унаследованный метод объекта окна WMSize и не
вызывать SetPageSize:

procedure TTestWindow.WMSize(var Msg: TMessage);
begin
DefWndProc(Msg);
end;

Затем вы можете непосредственно установить XPage и YPage в
конструкторе окна (или в производном конструкторе TScroller):

constructor ScrollWindow.Init(AParent:PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Attr.Style:=Attr.Style or ws_VScroll or ws_HScroll;
Scroller:=New(PScroller, Init(@Self, 8, 15, 80, 400));
Scroller^.XPage:=40;
Scroller^.YPage:=100;
end;

Оптимизация методов Paint для прокрутки
-----------------------------------------------------------------

В приведенном выше примере рисуется 50 прямоугольников, но
не делается даже попытки определить, все ли прямоугольники видны
в области клиента окна. Это может привести к излишним усилиям на
дорисовку невидимых изображений. Для оптимизации рисования в окне
методом Paint можно использовать метод TScroller.IsVisibleRect.

Приведенный ниже метод ScrollWindow.Paint использует
IsVisibleRect для определения, нужно ли вызывать функцию Windows
Rectange. Rectange воспринимает аргументы в единицах устройства,
а VisibleRect в единицах прокрутки. С этой целью вершина прямоу-
гольника X1 Y1 и ширина прямоугольника (X2-X1) и его высота

B.Pascal 7 & Objects/OW - 176 -

(Y2-Y1) должны быть разделены на соответствующее число единиц до
вызова IsVisibleRect:

procedure TScrollWindow.Paint(PaintDC: HDC; var PaintInfo:
TPaintStruct);
var
X1, Y1, X2, Y2, I: Integer;
begin
for I:=0 to 49 do
begin
X1 := 10 + I * 8;
Y1 := 30 + I * 5;
X2 := X1 + X1;
Y2 := X1 + Y1 * 2;
if Scroller^.IsVisibleRect(X1 div 8,
Y1 div 15, (X2-X1) div 8,
(Y2-Y1) div 15) then
Rectangle(PaintDC, X1, Y1, X2, Y2);
end;
end;



B.Pascal 7 & Objects/OW - 177 -

------------------------------------------------------------------
Глава 11. Объекты диалоговых блоков
-----------------------------------------------------------------

Блоки диалога, или просто диалоги, являются интерфейсными
объектами, инкапсулирующими поведение диалоговых блоков. Это до-
черние окнам, которые обычно выводятся для выполнения специфичес-
ких задач (например, конфигурирования принтера или организации
ввода текста). Объект TDialog обеспечивает инициализацию, созда-
ние и исполнение всех типов блоков диалога. Для каждого типа диа-
лога вашего приложения, как и в случае оконных объектов, вы може-
те определить производные от TDialog диалоговые блоки.

ObjectWindows всегда предоставляет два типа диалогов наибо-
лее общего типа, ввод текста и диалог файла. Кроме того, в
ObjectWindows имеется тип TDlgWindow, который позволяет вам соз-
давать диалоги, поведение которых более похоже на окно.

Данная глава охватывает следующие темы:

* Использование объектов диалоговых блоков.
* Работа с управляющими элементами в диалоговых блоках.
* Связь объектов с управляющими элементами.
* Связь окон с ресурсами.

Использование объектов диалоговых блоков
-----------------------------------------------------------------

Использование объектов диалоговых блоков аналогично исполь-
зованию объектов всплывающего окна. Диалоги являются дочерними
окнами своего порождающего окна. Для простых диалоговых блоков,
которые появляются на короткое время, вся обработка диалога может
быть выполнена одним методом объекта порождающего окна. Диалог
может быть сконструирован, выполнен и удален в одном методе, и
нет необходимости хранить диалог в поле объекта. Для более слож-
ных диалогов может потребоваться записать диалоговый блок в поле
оконного объекта вашего диалогового блока.

Подобно всплывающим окнам и управляющим элементам, диалого-
вые блоки являются дочерними окнами и при конструировании добав-
ляются к списку ChildList порождающих окон.

Использование объекта диалогового блока предусматривает сле-
дующие шаги:

* Построение объекта.

* Выполнение диалогового окна.

* Закрытие диалогового окна.



B.Pascal 7 & Objects/OW - 178 -

Построение объекта
-----------------------------------------------------------------

Диалоговые блоки конструируются и специфицируются с помощью
описания ресурса, создаваемого вне программы. Ресурс диалогового
окна описывает внешний вид и размещение управляющих элементов,
таких как кнопок, блоков списка, областей редактирования и текс-
товых строк. Он описывает только внешний вид диалогового блока и
не касается его поведения - за это отвечает прикладная программа.

Каждый ресурс диалогового блока имеет идентификатор, который
может быть номером идентификатора (Word) или строкой (PChar).
Этот идентификатор позволяет объекту диалогового блока задавать,
какой ресурс используется для определения его внешнего вида.

Вызов конструктора
-----------------------------------------------------------------

Чтобы построить объект диалогового блока, вызовите конструк-
тор Init. Init воспринимает в качестве своих параметров указа-
тель на порождающее окно и параметр типа PChar, представляющий
имя ресурса диалога:

ADlg:=New(PSampleDialog, Init(@Self, 'EMPLOYEEINFO'));

Если идентификатор задается номером, его требуется привести
с помощью MakeIntResource к PChar:

Dlg := New(PSampleDialog, Init(@Self, PChar(120)));

Так как диалоговые блоки обычно строятся внутри метода окон-
ного объекта, порождающее окно почти всегда задается как Self.
Объекты диалоговых блоков, не создаваемые оконными объектами,
должны иметь в качестве порождающего Applicartion^.MainWindow
(поскольку это единственный оконный объект, всегда присутствующий
в каждой программе ObjectWindows).

Выполнение диалоговых блоков
-----------------------------------------------------------------

Выполнение диалогов аналогично созданию и отображению окна.
Однако, поскольку диалоги обычно появляются на более короткий от-
резок времени, некоторые этапы могут быть сокращены. Это в значи-
тельной степени зависит от того, будет ли диалоговый блок отобра-
жаться как режимный или безрежимный.



B.Pascal 7 & Objects/OW - 179 -

Режимные и безрежимные диалоговые блоки
-----------------------------------------------------------------

Режимные диалоговые блоки являются наиболее общими блоками
диалога. Аналогично генерируемым функцией MessageBox блокам сооб-
щений, режимные диалоги отображаются для специфических целей на
короткий отрезок времени. Слово "режимный" означает, что пока
отображается диалог, пользователь не может выбрать или использо-
вать его порождающее окно. Пользователь должен воспользоваться
диалогом и выбрать командную кнопку OK или Cancel для прекращения
диалога и возвращению к работе с программой. Режимный диалог как
бы "замораживает" выполнение оставшейся части программы.

Безрежимный диалоговый блок не приостанавливает выполнения
программы. Как и оконный объект, он может создаваться и выпол-
няться в одном шаге с помощью MakeWindow:

Application^.MakeWindow(ADlg);

В любое момент вы можете считать данные из диалогового окна
(если объект диалогового блока еще существует). Чаще всего это
выполняется в методе OK, который вызывается при активизации поль-
зователем командной кнопки OK.

Выполнения режимных диалоговых блоков
-----------------------------------------------------------------

В случае режимных диалоговых блоков лучше всего, вероятно,
строить, выполнять и уничтожать все объекты в одном методе (как
показано в примерах данной главы). Таким образом, при каждом вы-
воде диалогового блока это будет новый объект.

Объекты приложения имеют режимный эквивалент MakeWindow, ко-
торый называется ExecDialog. Аналогично MakeWindows, ExecDialog
проверяет допустимость передаваемого объекта диалогового блока
(то есть успешность выполнения конструктора объекта и отсутствие
ситуации нехватки памяти), а затем выполняет диалоговый блок, де-
лая его модальным.

ExecDialog возвращает целочисленное значение, указывающее,
что пользователь закрывает диалоговое окно. Возвращаемое значение
- это идентификатор задействованного пользователем управляющего
элемента, такой как id_Ok для командной кнопки OK или id_Cancel
для командной кнопки Cancel. После завершения выполнения диалого-
вого окна ExecDialog уничтожает объект диалогового окна.

Таким образом, с помощью одного вызова метода ExecDialog вы
можете создать, вывести на экран и завершить диалоговый блок.

ADlg := New(PSampleDialog, Init(@Self, 'RESOURCEID'));
ReturnValue := Application^.ExecDialog(ADlg);
if ReturnValue = id_OK then
{ кодирование для выборки данных и обработки диалога }

B.Pascal 7 & Objects/OW - 180 -

else
if ReturnValue = id_Cancel then { нажата Cancel }

Выполнение безрежимных диалоговых блоков
-----------------------------------------------------------------

Безрежимные диалоги похожи на всплывающие окна и управляющие
элементы. Основная причина, по которой вы не можете удалять без-
режимными диалогами сразу же после их отработки (в отличие от ре-
жимных), состоит в том, что вы заранее не знаете, когда пользова-
тель закроет блок диалога. (Помните о том, что в режимных диало-
гах метод ExecDialog не возвращает значения до закрытия диалога.)
Следовательно лучше всего конструировать безрежимные диалоги в
конструкторе его порождающего окна и хранить в поле порождающего
объекта.

В отличие от окон и объектов управления, используемых в ка-
честве дочерних окон, диалоги автоматически не отображаются при
выводе их порождающих окон.

constructor ParentWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
TWindow.Init(AParent, ATitle);
ADlg := New(PSampleDialog, Init(@Self, 'EMPLOYEEINFO'));
end;

Затем, каждый раз, когда вы хотите отобразить диалог, соз-
дайте и выведите его:

begin
Application^.MakeWindow(ADlg)
end;

И, наконец, отдельный объект диалога будет автоматически
удаляться при закрытии его порождающего окна.

Работа с безрежимными диалоговыми блоками
-----------------------------------------------------------------

Диалоговые блоки отличаются от других дочерних окон, таких
как всплывающие окна и управляющие элементы, тем, что они за вре-
мя существования своего порождающего окна создаются и уничтожают-
ся многократно и редко выводятся на экран и уничтожаются вместе с
порождающим окном. Обычно программа создает диалоговый блок в от-
вет на выбор меню, щелчок кнопкой "мыши", ошибку или другое собы-
тие.

Таким образом, нужно убедиться, что вы не строите объекты
диалоговых блоков снова и снова, не уничтожая их. Помните о том,
что все построенные диалоговые объекты автоматически включаются в
списки дочерних окон их порождающих окон.


B.Pascal 7 & Objects/OW - 181 -

Примечание: К режимным диалоговым блокам это не отно-
сится, так как они автоматически уничтожаются при закрытии.

Завершение диалогов
-----------------------------------------------------------------

Каждый блок диалога должен иметь способ его закрытия пользо-
вателем. Чаще всего это кнопки OK и/или Cancel. Потомки TDialog
автоматически отреагируют на нажатие одной из этих кнопок вызовом
метода EndDlg, который заканчивает диалог. Вы можете разработать
новые средства завершения диалога, если только они приводят к вы-
зову EndDlg. Для изменения поведения при закрытии вы можете пере-
определить методы OK и Cancel.

Например, вы можете переопределить метод OK таким образом,
что введенные данные будут копироваться в буфер, который находит-
ся вне объекта блока диалога. Если ввод был осуществлен некор-
ректно, вы можете вывести блок сообщения или сгенерировать звуко-
вой сигнал. Если ввод был сделан верно, вы можете вызвать EdnDlg.
Переданное в EndDlg значение становится возвращаемым значением
ExecDialog.

Как и в случае оконных объектов, объект диалога вызывает
CanClose до закрытия блока диалога, как это имело место для объ-
ектов окна. Вы можете переписать CanClose для учета условий зак-
рытия, как для блока диалога, который проверяет ввод пользовате-
ля. При переписывании CanClose нужно быть уверенным в том, что
вызывается унаследованный от него метод, т.к. он вызывает методы
CanClose дочерних окон.



B.Pascal 7 & Objects/OW - 182 -

Работа с управляющими элементами
-----------------------------------------------------------------

Все блоки диалога, кроме самых простейших, имеют (как дочер-
ние окна) несколько управляющих элементов (например, управления
редактированием, блоки списка и командные кнопки). Обратите вни-
мание на то, что эти управляющие элементы являются не объектами
управляющих элементов, а только управляющими интерфейсными эле-
ментами, без методов и полей объекта. Эта глава кроме того пока-
зывает альтернативные методы, позволяющие связывать объекты уп-
равления с элементами управления диалоговых блоков с использова-
нием InitResource.

Примечание: Использование управляющих объектов в окне
(но не блоков диалога) показано в Главе 12.

Между объектом диалога и его элементами управления имеется
двухсторонняя связь (можно сказать диалог). С одной стороны диа-
логу нужно манипулировать его управляющими элементами, например,
для заполнения блока списка. С другой стороны ему нужно обрабаты-
вать и реагировать на сгенерированные сообщения управляющих собы-
тий, например, когда пользователь выбирает элемент блока списка.

Взаимодействие с управляющим элементом
-----------------------------------------------------------------

Windows определят набор сообщений управляющих элементов, ко-
торые посылаются от приложения к Windows. Например, имеются сле-
дующие сообщения блока списка: lb_GetText, lb_GetCurSel и
lb_AddString. Сообщения управляющих элементов задают специфичес-
кое управление и несут с собой информацию в аргументах wParam и
lParam. Каждый управляющий элемент в ресурсе диалога имеет номер
идентификатора, который вы используете для задания управляющего
элемента, принимающего сообщение. Для посылки сообщения управляю-
щему элементу нужно вызвать метод TDialg SendDlgItemMsg. Напри-
мер, данный метод заполнит блок списка диалога элементами текста
путем посылки сообщения lb_AddString:

procedure TestDialog.FillListBox(var Msg: TMessage);
var
TextItem: PChar;
begin
TextItem := 'Item 1';
SendDlgItemMsg(id_LB1, lb_AddString, 0, Longint(TextItem));
end;

где id_LB1 есть константа, равная ID блока списка.

Если вам потребуется описатель одного из управляющих элемен-
тов диалога, его можно получить методом GetItemHandle:

GetItemHandle(id_LB1);


Реакция на сообщения управляющих элементов
-----------------------------------------------------------------

Когда пользователь выбирает управляющий элемент, например,
"нажимает" командную кнопку или делает выбор в блоке списка, диа-
логовым блоком управляющего элемента порождающего окна принимает-
ся специальное сообщение, основанное на дочернем окне и называе-
мое управляющим сообщением (сообщение управляющего элемента). Оп-
ределим метод реакции на сообщение, основанное на дочернем иден-
тификаторе, в порождающем типе диалога для каждого дочернего уп-
равляющего элемента:

TTestDialog = object(TDialog)
procedure HandleBN1Msg(var Msg: TMessage); virtual
id_First + id_BN1;
procedure HandleListBox(var Msg: TMessage); virtual
id_First + id_LB1;
end;

В данном примере id_BN1 - это идентификатор кнопки управляю-
щего элемента, а id_LB1 - это идентификатор блока списка. Щелчок
"мышью" на командной кнопке даст сообщение, посылаемое в диалого-
вый блок. Объект диалогового блока реагирует через динамический
метод с индексом, основанным на идентификаторе кнопки IDBN1.

Примечание: Уведомляющие сообщения подробно поясняются
в Главе 16 "Сообщения Windows"

Каждое управляющее информационное сообщение поступает с ко-
дом уведомления - целочисленной константой, которая идентифициру-
ет произведенное действие. Например, результатом выбора элемента
из блока списка будет сообщение с кодом lbn_SelChange, где lbn_ -
уведомление блока списка (List Box Notification). Нажатие кнопки
"мыши" дает сообщение bn_Clicked. Ввод в управляющем элементе ре-
дактированием приводит к сообщению с кодом en_Changed. Имеются
коды уведомления для блоков списка, управляющих элементов редак-
тированием, комбинированных блоков и командных кнопок. Код уве-
домления передается в Msg.lParamHi сообщения. Для восприятия уп-
равляющих информационных сообщений напишем метод реакции для типа
диалога, обрабатывающий важные коды уведомления:

procedure TestDialog.HandleLB1Msg(var Msg: TMesage);
begin
case Msg.lParamHi of
lbn_SelChange: { изменение порядка выбора };
lbn_DblClk: { выбор с помощью двойного щелчка };
end;
end;
Управляющие элементы, имеющие соответствующие объекты, могут
отвечать на уведомления сами. См. Главу 16.

Пример связи
-----------------------------------------------------------------
В файле с текстом программы DIALTEST.PAS, основное окно име-

B.Pascal 7 & Objects/OW - 184 -

ет режимный диалог, определенный типом диалога TTestDialog. Эта
программа обеспечивает двухстороннюю связь между объектом диалога
и его управляющими элементами. Два метода - IDBN1 и IDLB1 - явля-
ются методами реакции, основанными на дочерних идентификаторах, и
вызываются при выборе пользователем управляющих элементов (дочер-
них окон). Например, при выборе пользователем кнопки диалога BN1
('Fill List Box') вызывается метод IDBN1. Аналогично, когда поль-
зователь делает выбор в блоке списка, вызывается IDLB1. С другой
стороны, для заполнения блока списка элементами текста код метода
IDBN1 посылает в диалог управляющее сообщение, lb_AddString, ис-
пользуя метод диалога SendDlgItemMsg,

Эта программа также показывает как путем создания нового ти-
па диалога и связывания его с ресурсом диалога в вызове конструк-
тора Init метода TestWindow.RunDialog создаются новые диалоги.
Полный текст программы вы можете найти на дистрибутивных дисках.

Ассоциирование объектов управляющих элементов
-----------------------------------------------------------------

До этого момента мы имели дело с реакцией блоков диалога на
управляющие информационные сообщения, которая использовала методы
реакции, основанные на дочерних идентификаторах. Однако, иногда
более предпочтительно, чтобы управляющий элемент сам реагировал
на сообщение. Например, вам может потребоваться управляющий эле-
мент редактирования, который позволяет вводить только цифры, или
командная кнопка, которая меняет стиль при своем "нажатии". Это
можно реализовать с помощью объектов управляющих элементов в ок-
нах (см. Главу 12). Однако, чтобы это имело место для управляющих
элементов диалога, созданного с файлом ресурса, вам нужно исполь-
зовать для конструирования объекта другой конструктор.

При организации связей вы создаете объект управляющего эле-
мента для представления управляющего объекта диалога. Этот объект
управления дает вам гибкость в реакции на управляющие сообщения.
Он дает вам возможность использования набор методов объектов уп-
равляющих элементов, описанных в Главе 12.

Для связи объекта с управляющим элементом определите сначала
объект управляющего элемента. Он должен быть создан в конструкто-
ре диалога. Однако, вместо того, чтобы использовать конструктор
Init, как это показано в Главе 12, следует использовать
InitResource, который берет в качестве параметров порождающее ок-
но и идентификатор управляющего элемента (из ресурса диалога).
Это приводит к вызову методов реакции на сообщения объектов уп-
равляющих элементов вместо обработки элементов по умолчанию. Для
этого нужно определить новый тип объекта, производный от предус-
мотренного типа управляющего элемента.

Обратите внимание, что в отличие от задания оконного объек-

B.Pascal 7 & Objects/OW - 185 -

та, которое предполагает два шага (Init и MakeWindow), поскольку
управляющий элемент уже существует, связь объекта с управляющим
элементов выполняется за один шаг: он загружается из диалогового
ресурса. Вам нужно только сообщить InitResource, какой управляю-
щий элемент из ресурса вы хотите связать с объектом, используя
идентификатор управляющего элемента.

Использование диалоговых окон
-----------------------------------------------------------------

Основная разница между диалоговыми блоками и окнами состоит
в том, что диалог имеет соответствующий ресурс и задает тип и
расположение своих управляющих элементов. Но окно также может
иметь управляющие элементы.

Одним из подходов размещения управляющих элементов в окне
является использование объектов управляющих элементов (как пока-
зано в Главе 12). Другой подход - это слияние возможностей диало-
говых блоков и окон, как это делается в объектном типе
TDlgWindow, что позволяет получить гибридный объект, называемый
диалоговым окном. Второй подход предусматривает более удобный
способ построения и управления многими управляющими элементами в
окне. Кроме того, он предлагает для диалоговых блоков более гиб-
кие средства окон.

TDglWindow является потомком TDialog и наследует его методы,
такие как Execute, Create, Ok и EndDlg. Как и диалоговые блоки,
диалоговые окна имеют соответствующий ресурс диалогового блока. С
другой стороны, как и окна, диалоговые окна имеют соответствующий
класс окон, определяющий среди всего прочего пиктограмму, курсор
и меню. Из-за связи с оконным классом в потомке TDlgWindow следу-
ет переопределять методы GetClassName и GetWindowClass. Этот
класс должен быть тем же, что и перечисленный в диалоговом ресур-
се.

В большинстве случаев вы будете выполнять диалоговые окна
как и другие окна или безрежимные диалоговые окна с помощью мето-
дов Create и Show, а не метода Execute.

В тех случаях, когда основное окно должно содержать много
сложных управляющих элементов, хорошим использованием диалоговых
окон является основное окно приложения. Например, программа-каль-
кулятор может иметь в качестве основного окна диалоговое окно,
где кнопки калькулятора заданы как управляющие элементы диалого-
вого ресурса. Это позволило бы вывести в основном окне также ме-
ню, пиктограмму и курсор.



B.Pascal 7 & Objects/OW - 186 -

Использование предопределенных диалоговых окон
-----------------------------------------------------------------

Для выполнения двух типов общих функций ObjectWindows пре-
дусматривает стандартные диалоговые блоки. Одно их них, окно диа-
логового блока, выводит пользователю однострочную подсказку. Дру-
гое, файловое диалоговое окно, позволяет пользователю задать имя
файла и каталог для открытия или сохранения файла.

Использование диалоговых блоков ввода
-----------------------------------------------------------------

Диалоговые блоки ввода - это простые объекты диалоговых бло-
ков, определяемых типом TInputDialog, которые выводят пользовате-
лю подсказку со строкой текста.

Вы можете запускать диалоги ввода как режимные или безрежим-
ные диалоговые блоки, но обычно вы будете выполнять их как режим-
ные. С объектом диалога ввода связан ресурс диалога ввода. Он на-
ходится в файле ObjectWindows OSTDDLGS.RES.

Примечание: Использование модуля StdDlgs автоматически
включает ресурсы в OSTDDLGS.RES.

Каждый раз при конструировании диалога ввода с использовани-
ем метода Init, вы задаете для диалога заголовок, подсказку и
текст по умолчанию. Покажем вызов конструктора Init объекта диа-
лога ввода:

var SomeText: array[0..79] of Char;
begin
AnInputDlg.Init(@Self, 'Caption', 'Prompt', SomeText,
SizeOf(SomeText))
.
.
.
end;

B.Pascal 7 & Objects/OW - 187 -


В данном примере EditText - это текстовый буфер, который за-
полняется вводом пользователя, когда он "нажимает" кнопку OK.
Когда пользователь "нажимает" кнопку OK или клавишу Enter, строка
введенного в диалоге ввода текста автоматически передается в мас-
сив символов, который хранит текст по умолчанию. В данном примере
конструируется и отображается блок диалога и считывается текст:

procedure TSampleWindow.Test(var Msg: TMessage);
var
EditText: array[0..255] of Char;
begin
EditText:='Frank Borland';
if ExecDialog(New(PInputDialog, Init(@Self, 'Data Entry',
'Введите имя:', EditText, SizeOf(EditText)))) = id_OK then
MessageBox(HWindow, EditText, 'Имя =', mb_OK);
else MessageBox(HWindow, EditText,
'Имя пока имеет значение:',mb_OK);
end;



B.Pascal 7 & Objects/OW - 188 -

Файловые диалоговые блоки
-----------------------------------------------------------------

Файловые диалоговые блоки являются другим типом диалогов,
поставляемых с ObjectWindows в типе TFileDialog. Файловый диало-
говый блок следует использовать каждый раз, когда вы желаете по-
будить пользователя ввести имя файла, например в функциях File
Open и File Save во всех приложениях. См. Рис. 11.1.

------------------------------------------------------¬
¦-=---------------------File Open---------------------¦
+-----------------------------------------------------+
¦ ¦
¦ -------------------¬ -----------¬ ¦
¦ Имя файла: ¦ *.pas ¦ ¦----OK----¦ ¦
¦ L------------------- L----------- ¦
¦ -----------¬ ¦
¦ Каталог: a:\ ¦--Cancel--¦ ¦
¦ L----------- ¦
¦ Файлы: Каталоги: ¦
¦ ---------------T-¬ ---------------T-¬ ¦
¦ ¦collect3.pas ¦^¦ ¦[-a-] ¦^¦ ¦
¦ ¦collect4.pas +-+ ¦[-c-] +-+ ¦
¦ ¦diatest.pas ¦-¦ ¦[-f-] ¦-¦ ¦
¦ ¦edittest.pas ¦-¦ ¦[-g-] ¦-¦ ¦
¦ ¦ewndtest.pas ¦-¦ ¦[-h-] ¦-¦ ¦
¦ ¦helpwind.pas ¦-¦ ¦[-i-] ¦-¦ ¦
¦ ¦lboxtest.pas ¦-¦ ¦[-j-] ¦-¦ ¦
¦ ¦mditest.pas +-+ ¦[-k-] +-+ ¦
¦ ¦paltest.pas ¦v¦ ¦[-w-] ¦v¦ ¦
¦ L--------------+-- L--------------+-- ¦
¦ ¦
L------------------------------------------------------

Рис. 11.1 Файловый диалоговый блок.

В большинстве случаев файловые диалоговые блоки выполняются
как режимные. С объектом файлового диалога связан ресурс файлово-
го диалогового блока, имеющийся в ObjectWindows в файле
OSTDDLGS.RES. Использование модуля OStdDlgs автоматически включа-
ет файл ресурса.

B.Pascal 7 & Objects/OW - 189 -

Инициализация файлового диалогового блока
-----------------------------------------------------------------

TFileDialog определяет конструктор Init, который позволяет
задать маску файла и буфер для считывания имени файла. Маска фай-
ла (такая как '*.TXT') ограничивает файлы, перечисляемые в комби-
нированном блока (аналогично тому, как это делается в команде DOS
DIR *.TXT). Имя файла и маска передаются в записи типа
TFileDlgRec. Приведем пример вызова файлового диалогового блока
Init:

var
FileRec: TFileDlgRec;
IsOpen: Boolean;
begin
StrCopy(FileRec.Name, 'TEST1.TXT');
StrCopy(FileRec.Mask, 'C:\*.TXT');
IsOpen := True;
AFileDlg.Init(@Self, FileRec, IsOpen);
.
.
.
end;

Последний параметр указывает, будет ли диалог диалогом отк-
рытия или сохранения (как описывается в следующем разделе).

Выполнение файловых диалоговых блоков
-----------------------------------------------------------------

Существует два вида файловых диалоговых блоков: диалоговый
блок открытия файла и диалоговый блок сохранения файла. Они раз-
личаются текстом кнопки в правом верхнем углу диалогового окна. В
зависимости от того, запрашивает пользователь открытие или сохра-
нение файла, на командной кнопке будет написано Open или Save.
Когда вы вызовите ExecDialog, то получите тип диалогового блока,
заданных в конструкторе IsOpen параметром типа Boolean. Если фай-
ловый диалоговый блок строится с IsOpen, установленным в True, то
диалоговый блок будет работать как диалоговый блок открытия фай-
ла. Если он строится с IsOpen, установленным в False, то файловый
диалоговый блок будет блоком сохранения файла.


B.Pascal 7 & Objects/OW - 190 -


Дополнительным средством файлового диалогового блока
ObjectWindows является то, что он выводит пользователю подсказку,
хочет ли пользователь сохранить файл с именем уже существующего
файла (см. Рис. 11.2). В другой раз вы можете запросить пользова-
теля, хочет ли он открыть новый файл или очистить текущий текст
без сохранения. Поскольку это должно происходить перед выводом
файлового диалогового блока, то не является частью поведения это-
го блока. В примере программы Steps в первой части данного руко-
водства перед загрузкой рисунка из файла проверяется метод
CanClose его основного окна.

--------------------------------------------¬
¦-=-------File exists! Overwrite it?--------¦
+-------------------------------------------+
¦ ¦
¦ C:\TEMP\NICELINE.PTS ¦
¦ ¦
¦ -----------¬ -----------¬ ¦
¦ ¦---Yes----¦ ¦---No-----¦ ¦
¦ L----------- L----------- ¦
¦ ¦
L--------------------------------------------

Рис. 11.2 Предупреждение пользователя о перезаписи существу-
ющих файлов.

File exists! Overwrite it? - файл существует, затереть его?

Приведем пример типичного использования диалогового окна:

procedure TMyWindow.OpenSelectedFile;
var FileRec: TFileDlgRec;
begin
StrCopy(FileRec.Name, 'HEDGEHOG.PAS');
StrCopy(FileRec.Mask, '*.PAS');
if ExecDialog(New(PFileDialog,
Init(@Self, FileRec, True))) = id_Ok then
begin
Assign(AFile, StrPas(FileRec.Name));
.
.
.
end;
end;



B.Pascal 7 & Objects/OW - 191 -

------------------------------------------------------------------
Глава 12. Объекты управляющих элементов
-----------------------------------------------------------------

Windows предусматривает ряд стандартных интерфейсных элемен-
тов, называемых управляющими элементами. Управляющие элементы -
это специальные окна с некоторым предопределенным поведением.
ObjectWindows обеспечивает интерфейсные объекты для стандартных
управляющих элементов Windows, так что вы можете использовать эти
интерфейсные элементы в своих приложениях. Интерфейсные объекты
для управляющих элементов называются объектами управляющих эле-
ментов или просто управляющими объектами.

Примечание: Об интерфейсных объектах рассказывается в
Главе 9.

Данная глава охватывает следующие темы:

* Задачи, общие для всех управляющих объектов:

- построение и уничтожение объектов управляющих элементов;

- взаимодействие с другими управляющими объектами.

* Установка и чтение значений управляющих элементов.

* Использование специализированных управляющих элементов.

Кроме того, в данной главе подробно описывается использова-
ние каждого интерфейсного объекта для стандартных управляющих
элементов Windows.

Где можно использовать объекты управляющих элементов?
-----------------------------------------------------------------

Управляющие элементы - это специализированные окна, которые
позволяют пользователю предопределенным образом задавать или вы-
бирать данные. Чаще всего управляющие элементы содержатся в диа-
логовом блоке. Диалоговый блок управляет их определением с по-
мощью ресурса диалогового блока, так что вам не потребуется часто
использовать в них объекты. Режимные диалоговые блоки не распола-
гают способами взаимодействия с управляющим объектом, поэтому в
диалоговых блоках объекты управляющих элементов используются
обычно для установки и считывания значений управляющих элементов
с помощью передачи. Передача для объектов управляющих элементов в
диалоговых блоках и в окнах выполняется одинаково (как описывает-
ся ниже в этой главе).

Примечание: Диалоговые блоки и их управляющие элементы
описываются в Главе 11 "Объекты диалоговых блоков".

Возможно вы захотите использовать управляющие элементы в ок-
нах, поэтому в данной главе описывается использование управляющих
элементов вне диалоговых блоков. Следующая таблица описывает уп-

B.Pascal 7 & Objects/OW - 192 -

равляющие элементы Windows, поддерживаемые типами объектов
ObjectWindows:

Управляющие элементы Windows,
поддерживаемые в ObjectWindows Таблица 12.1
----------------T------------T----------------------------------¬
¦ Управляющий ¦ Тип объекта¦ Использование ¦
¦ элемент ¦ ¦ ¦
+---------------+------------+----------------------------------+
¦ блок списка ¦TListBox ¦Прокручиваемый список элементов,¦
¦ ¦ ¦из которых можно сделать выбор. ¦
+---------------+------------+----------------------------------+
¦ полоса ¦TScrollBar ¦Полоса прокрутки, аналогичная¦
¦ прокрутки ¦ ¦тем, которые выводятся в прокру-¦
¦ ¦ ¦чиваемых окнах и блоках списка. ¦
+---------------+------------+----------------------------------+
¦ "нажимаемая" ¦TButton ¦Кнопка для "нажатия" со связанным¦
¦ кнопка ¦ ¦с ней текстом. ¦
+---------------+------------+----------------------------------+
¦ кнопка с ¦TCheckBox ¦Состоящая из блока кнопка, которая¦
¦ независимой ¦ ¦может выбираться или нет, со свя-¦
¦ фиксацией ¦ ¦занным текстом. ¦
+---------------+------------+----------------------------------+
¦ кнопка с ¦TRadioButton¦Кнопка, которая может выбираться¦
¦ зависимой ¦ ¦или нет. Обычно используется¦
¦ фиксацией ¦ ¦во взаимоисключающих группах. ¦
+---------------+------------+----------------------------------+
¦ блок группы ¦TGroupBox ¦Статический прямоугольник с текс-¦
¦ ¦ ¦том в левом верхнем углу. ¦
+---------------+------------+----------------------------------+
¦ управляющий ¦TEdit ¦Поле для ввода текста пользовате-¦
¦ элемент ¦ ¦лем. ¦
¦ редактирования¦ ¦ ¦
+---------------+------------+----------------------------------+
¦ статический ¦TStatic ¦Фрагмент отображаемого текста ¦
¦ управляющий ¦ ¦который не может быть изменен ¦
¦ элемент ¦ ¦пользователем. ¦
+---------------+------------+----------------------------------+
¦ Комбиниро- ¦TComboBox ¦Комбинация блока списка и управля-¦
¦ ванный блок ¦ ¦ющего элемента редактирования. ¦
L---------------+------------+-----------------------------------


B.Pascal 7 & Objects/OW - 193 -


-----------------------------------¬
Командная строка: ¦ ¦
L-----------------------------------
^
L редактируемый упрвляющий элемент

-----------¬ -----------¬
¦---OK-----¦ ¦--Cancel--¦ <- командные кнопки
L----------- L-----------

----T-----------------------------------------------T---¬
¦ < ¦-----------------------------------------------¦ > ¦
L---+-----------------------------------------------+----
^
полоса прокрутки --


---------------T-¬
¦collect3.pas ¦^¦ <- блок списка
¦collect4.pas +-+
¦diatest.pas ¦-¦
¦edittest.pas ¦-¦ -----------------¬
¦ewndtest.pas ¦-¦ ¦ *.txt ¦
¦helpwind.pas ¦-¦ L-T--------------+-¬
¦lboxtest.pas ¦-¦ ¦netlect3.txt ¦^¦
¦mditest.pas +-+ ¦netlect4.txt +-+
¦paltest.pas ¦v¦ ¦diatext.txt ¦-¦
L--------------+-- ¦readtxt.txt ¦-¦
¦vwndtext.txt ¦-¦
комбинированный блок -> ¦whelpwnd.txt ¦-¦
¦wboxtext.txt ¦-¦
¦ydrtest.txt +-+
¦xaltesx.txt ¦v¦
L--------------+--

Рис. 12.1 Стандартные управляющие элементы Windows.

B.Pascal 7 & Objects/OW - 194 -

Что такое объекты управляющих элементов?
-----------------------------------------------------------------

Для Windows управляющие элементы - это просто специализиро-
ванные виды окон. В ObjectWindows тип TControl является потомком
типа TWindow, так что большинство объектов управляющих элементов
можно использовать как все другие оконные объекты. Объекты управ-
ляющих элементов по способу их создания и уничтожения и способу
их поведения (как дочерние окна) аналогичны оконным объектам. Од-
нако они отличаются от других окон способом реакции на сообщения.
Например, методы Paint объектов управляющих элементов запрещены.
Windows берет на себя функции по отображению своих стандартных
управляющих элементов.

Может оказаться, что перечисленные в приведенной выше табли-
це управляющие элементы отвечают всем потребностям вашего прило-
жения. Однако могут возникать случаи, когда требуется определить
наследующие типы управляющих элементов. Например, вы можете соз-
дать специализированный блок списка TFontListBox, производный от
TListBox, содержащий имена всех доступных вашему приложению шриф-
тов и автоматически выводящих их при создании нового экземпляра
объекта.

Тип TControl, как и TWindowsObject, является абстрактным
объектным типом. Вы можете создать экземпляры его потомков -
TListBox, TButton и другие - но не можете создать экземпляр
TControl.

Заметим, что вам, возможно, никогда не потребуется создавать
новый объектный тип, наследующий непосредственно из TControl.
TControl инкапсулирует свойства и стандартные управляющие элемен-
ты, о которых уже знает Windows. Создание специализированных уп-
равляющих элементов описывается в данной главе ниже.

Построение и уничтожение объектов управляющих элементов
-----------------------------------------------------------------

Обычно в объекта порождающего окна для каждого из дочерних
окон определяется поле данных. Независимо от того, какой вид объ-
ектов вы используете, для каждого из них вы можете выполнить нес-
колько задач:

* Построение объекта управляющего элемента.

* Вывод управляющего элемента.

* Уничтожение управляющего элемента.



B.Pascal 7 & Objects/OW - 195 -

Построение объекта управляющего элемента
-----------------------------------------------------------------

Построение управляющих элементов отличается от построения
любых других дочерних окон. Обычно конструктор порождающего окна
вызывает конструкторы всех его дочерних окон. Однако в случае уп-
равляющих элементов не просто создается связь "родитель - пото-
мок", но устанавливается также связь "окно - управляющий эле-
мент". Это очень важно, поскольку кроме обычных связей между
предком и потомком управляющие элементы взаимодействуют с порож-
дающими окнами особыми способами (через уведомления).

Примечание: Уведомления описываются в Главе 16 "Сооб-
щения Windows".
Чтобы построить и инициализировать объект управляющего эле-
мента, нужно сделать следующее:

* добавить в объект порождающего окна поле (не обязательно);
* вызвать конструктор объекта управляющего элемента;
* изменить атрибуты управляющего элемента;
* инициализировать управляющий элемент в SetupWindows.

Вызов конструкторов объектов управляющих элементов
-----------------------------------------------------------------

В то время как обычный конструктор объекта дочернего окна
имеет только два параметра (порождающее окно и строку заголовка),
конструктор управляющего объекта имеет их не менее шести. Объект
блока списка имеет наиболее простой из всех управляющих элементов
конструктор, который требует задания только шести параметров:

- объекта порождающего окна;
- идентификатора управляющего элемента;
- координату x верхнего левого угла;
- координату y верхнего левого угла;
- ширину;
- высоту.

TListBox.Init описывается следующим образом:

constructor TListBox.Init(AParent: PWindowsObject;
AnID: Integer; X, Y, W, H: Integer);

Все объекты управляющих элементов ObjectWindows (кроме
TMDIClient) требуют не менее 6 параметров. Большинство из них
воспринимают также параметр, задающий текст управляющего элемен-
та.

Присваивание полям объекта
-----------------------------------------------------------------
Часто при построении управляющего элемента в окне желательно

B.Pascal 7 & Objects/OW - 196 -

сохранять указатель на управляющий элемент в поле оконного объек-
та. Например, чтобы добавить в окно, определенное типом
TSampleWindow блок списка, вы можете задать в поле TSampleWindow
поле TheList и присвоить ему блок списка:

constructor SampleWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
TheList := New(PListBox,
Init(@Self, id_LB1, 20, 20, 100, 80));
end;

Порождающие окна автоматически поддерживают список своих до-
черних окон, включая управляющие элементы. Однако удобнее манипу-
лировать управляющими объектами, когда имеются соответствующие
поля объекта. Управляющие элементы, с которыми часто работать не
требуется (такие как статический текст или групповые блоки) могут
не иметь соответствующих полей.

При наличии поля объекта построение объекта управляющего
элемента не представляет труда. Например, чтобы добавить в
TSampleWindow групповой блок, нужно сделать следующее:

constructor SampleWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
TempGroupBox := New(PListBox,
Init(@Self, id_LB1, 'Group name',
140, 20, 100, 80));
end;



B.Pascal 7 & Objects/OW - 197 -

Изменение атрибутов объекта управляющего элемента
-----------------------------------------------------------------

Все управляющие объекты, кроме TMDIClient, получает от вызо-
ва TControl.Init используемые по умолчанию стили ws_Child и
ws_Visible. Если вы хотите изменить стиль управляющего элемента,
то можно изменить поле Attr.Style (как это описывается для окон в
Главе 10). Каждый тип управляющего элемента имеет также другие
стили, определяющие его конкретные характеристики.

Инициализация управляющего элемента
-----------------------------------------------------------------

Экранный элемент управляющего объекта автоматически создает-
ся методом SetupWindow, наследуемым объектом порождающего окна.
Убедитесь, что при создании новых производных типов окон вы вызы-
ваете наследуемый метод SetupWindow перед любой другой инициали-
зацией окна.

При необходимости методом SetupWindow порождающего окна так-
же устанавливаются и заполняются управляющие элементы. Приведем
пример типичного метода SetupWindow:

procedure TSampleWindows.SetupWindow;
begin
inherited SetupWindow; { создает дочерние управляющие
элементы }
{ добавляет элементы в список }
TheList^.AddString('Элемент 1');
TheList^.AddString('Элемент 2');
end;

Заметим, что инициализация управляющего элемента, такая как
добавление строк в блок списка, должна выполняться в SetupWindow,
а не в конструкторе. Вызов такого метода как TListBox.AddString
приводит к передаче сообщений экранному управляющему элементу.
Так как экранный элемент до вызова наследуемого метода
SetupWindow не создается, попытки инициализации управляющих эле-
ментов до этого момента завершится неудачно.



B.Pascal 7 & Objects/OW - 198 -

Сохранение управляющих элементов
-----------------------------------------------------------------

Для вывода на экран управляющих элементов нет необходимости
вызывать метод Show. Как дочерние окна, они автоматически выво-
дятся на экран и повторно отображаются вместе с порождающим ок-
ном. Однако, вы можете использовать Show для сокрытия или вывода
управляющих элементов по запросу.

Уничтожение управляющих элементов
-----------------------------------------------------------------

Порождающее окно отвечает за уничтожение управляющих элемен-
тов. Экранный управляющий элемент автоматически уничтожается
вместе с элементом порожденного окна, когда пользователь закрыва-
ет окно или приложение. Деструктор порождающего окна автоматичес-
ки уничтожает все дочерние объекты.

Связь с управляющими элементами
-----------------------------------------------------------------

Связь между оконным объектом и его управляющими объектами в
некотором смысле аналогичны взаимодействию между объектом диало-
гового блока и его управляющими элементами. Как и диалоговому
блоку, окну требуется механизм для работы с его управляющими эле-
ментами и для ответа на управляющие события, такие как выбор в
блоке списка.

Работа с управляющими элементами окна
-----------------------------------------------------------------

Диалоговые окна работают с их управляющими элементами путем
передачи им сообщений с помощью метода SendDlgItemMsg с констан-
той управляющего сообщения (такой как lb_AddString) в качестве
параметра (см. Главу 11). Объекты управляющих элементов сильно
упрощают этот процесс путем использования методов (таких как
TListBox.AddString) для непосредственной работы с управляющими
элементами на экране.

Когда объекты управляющих элементов окна имеют соответствую-
щие поля объекта, то вызвать управляющие методы достаточно прос-
то:

TheListBox^.AddString('Scotts Valley');



B.Pascal 7 & Objects/OW - 199 -

Реакция на управляющие элементы
-----------------------------------------------------------------

Реакция на взаимодействие с пользователем с помощью управля-
ющих элементов несколько более сложна, чем просто вызов методов
объектов управляющих элементов. Чтобы узнать, как отвечать на со-
общения управляющих элементов, см. раздел "Команды, уведомления и
идентификаторы управляющих элементов" в Главе 16.

Действие, аналогичное диалоговому блоку
-----------------------------------------------------------------

Диалоговый блок с управляющими элементами позволяет пользо-
вателю с помощью клавиши Tab циклически перемещаться по всем эле-
ментам диалогового блока. Он может также использовать клавиши
стрелок для выбора в групповом блоке кнопок с зависимой фиксаци-
ей. Чтобы эмулировать этот клавиатурный интерфейс для окон с уп-
равляющими элементами, вызовите для оконного объекта в его конс-
трукторе метод EnableKBHandler объекта TWindowsObject.

Использование конкретных управляющих элементов
-----------------------------------------------------------------

Каждый вид управляющих элементов работает в чем-то отлично
от других. В данном разделе вы можете найти конкретную информацию
о том, как использовать объекты для каждого из стандартных управ-
ляющих элементов Windows.

Использование блока списка
-----------------------------------------------------------------

Использование блока списка - это простейший способ запросить
пользователя программы Windows выбрать что-либо из списка. Блоки
списка инкапсулируются объектным типом TListBox. TListBox опреде-
ляет методы для создания блоков списка, модификации списка эле-
ментов, запроса состояния списка элементов и поиска выбранного
пользователем элемента.



B.Pascal 7 & Objects/OW - 200 -

Построение объектов блока списка
-----------------------------------------------------------------

Конструктор Init в TListBox воспринимает только шесть пара-
метров, которые необходимы всем объектам управляющих элементов.
Этими параметрами являются порождающее окно, идентификатор и раз-
меры управляющего элемента X, Y, W и H:

LB1 := New(PListBox, Init(@Self, id_LB1, 20, 20, 340, 100));

TListBox получает используемый по умолчанию стиль управляю-
щего элемента ws_Child or ws_Visible, затем прибавляется
lbs_Standard. lbs_Standard - это комбинация lbs_Notify (для полу-
чения уведомляющих сообщений), ws_VScroll (для получения верти-
кально полосы прокрутки), lbs_Sort (для сортировки списка элемен-
тов в алфавитном порядке) и ws_Border (для вывода рамки). Если вы
хотите получить другой стиль блока списка, то можете модифициро-
вать поле Attr.Style в TListBox. Например, для блока списка, не
сортирующего свои элементы, можно использовать следующее:

LB1 := New(PListBox, Init(@Self, id_LB1, 20, 20, 340, 100));
LB1^.Attr.Style := LB1^.Attr.Style and not lbs_Sort;

Модификация блоков списка
-----------------------------------------------------------------

После создания блока списка вам нужно заполнить его элемен-
тами списка, который должны представлять собой строки. Позднее вы
можете добавить, вставить или удалить элементы, либо полностью
очистить список. Указанные действия вы можете выполнить с помощью
методов, приведенный в следующей таблице:

Методы модификации блоков списка Таблица 12.2
---------------------------------T------------------------------¬
¦ Выполняемое действие ¦ Метод ¦
+--------------------------------+------------------------------+
¦ Добавление элемента ¦ AddString ¦
¦ Вставка нового элемента ¦ InsertString ¦
¦ Добавление элемента ¦ InsertString ¦
¦ Удаление элемента ¦ DeleteString ¦
¦ Удаление каждого элемента ¦ ClearList ¦
¦ Выбор элемента ¦ SetSellIndex или SetSelString¦
L--------------------------------+-------------------------------



Запрос в блоках списка
-----------------------------------------------------------------

Существует пять методов, которые вы можете вызвать для полу-
чения информации о списке, содержащейся в объекте блока списка.
Эти методы перечислены в следующей таблице:

Методы запроса блока списка Таблица 12.3
--------------------------------------------T-------------------¬
¦ Получаемая информация ¦ Вызываемый метод ¦
+-------------------------------------------+-------------------+
¦ Число элементов в списке ¦ GetCount ¦
¦ Элемент с конкретным индексом ¦ GetString ¦
¦ Длина конкретного элемента ¦ GetStringLen ¦
¦ Выбранный элемент ¦ GetSelString ¦
¦ Индекс выбранного элемента ¦ SEgSelIndex ¦
L-------------------------------------------+--------------------

Реакция на блок списка
-----------------------------------------------------------------

Методы модификации и запроса блока списка позволяют вам ус-
тановить значения или определять в каждый конкретный момент сос-
тояние блока списка. Однако, чтобы знать, что делает пользователь
в данный момент с блоком списка, вам нужно реагировать на уведом-
ляющие сообщения управляющего элемента.

Пользователь может выполнять с блоком списка только следую-
щие действия: прокрутку списка, щелчок кнопкой "мыши" на элементе
списка, двойной щелчок кнопкой "мыши" на элементе. Когда выполня-
ется одно из этих действий, Windows посылает порождающему окну
блока списка уведомляющее сообщение блока списка. Обычно метод
реакции на уведомление для обработки сообщений для каждого управ-
ляющего элемента порождающего объекта определяется в порождающем
оконном объекте.

Каждое уведомляющее сообщение блока списка содержит в поле
lParamHi параметра Msg код уведомления (константу lbn_), который
специфицирует характер действия. Наиболее общие коды lbn перечис-
лены в следующей таблице:

Информационные сообщения блока списка Таблица 12.2
--------------T-------------------------------------------------¬
¦ wParam ¦ Действие ¦
+-------------+-------------------------------------------------+
¦lbn_SelChange¦Отдельным нажатием кнопки "мыши" был выбран¦
¦ ¦элемент. ¦
+-------------+-------------------------------------------------+
¦lbn_DblClk ¦Элемент был выбран двойным щелчком кнопки "мыши".¦
+-------------+-------------------------------------------------+
¦lbn_SetFocus ¦Пользователь переместил фокус на блок списка¦
¦ ¦простым или двойным нажатием кнопки "мыши", либо¦
¦ ¦клавишей Tab. Предшествует lbn_SelChange. ¦
L-------------+--------------------------------------------------

B.Pascal 7 & Objects/OW - 202 -


Приведем пример метода порождающего окна по обработке сооб-
щений блока списка:

procedure TLBoxWindow.HandleLB1Msg(var Msg: TMessage);
var
Idx: Integer;
ItemText: string[10]
begin
if Msg.lParamHi=lbn_SelChange then
begin
Idx:=LB1^.GetSelIndex;
if LB1^.GetStringLenIdx)<11 then
begin
LB1^.GetSelString(@ItemText, 10);
MessageBox(HWindow, @ItemText, 'Вы выбрали:', mb_OK);
end;
end;
else DefWndProc(Msg);
end;

Пользователь делает выбор, если Msg.lParamHi совпадает с
константой lbn_SelChange. Если это так, то берется длина выбран-
ной строки, проверяется, что она помещается в строку из 10 симво-
лов, и выбранная строка показывается в блоке сообщения.

Пример программы: LBoxTest
-----------------------------------------------------------------

Программа LBoxTest - это полная программа, которая создает
окно с блоком списка. После запуска приложения появляется основ-
ное окно с блоком списка. Когда пользователь выбирает элемент
блока списка, появляется диалог с выбранным элементом. Обратите
внимание на взаимоотношения между объектом окна и объектом блока
списка. Блок списка - это не просто дочернее окно основного окна,
основное окно владеет им как полем объекта. LB1 - это одно из по-
лей объекта основного окна, и оно содержит объект блока списка.
Полный текст программы содержится в файле LBOXTEST.PAS на ваших
дистрибутивный дискетах.


B.Pascal 7 & Objects/OW - 203 -

Использование статических управляющих элементов
-----------------------------------------------------------------

Статические управляющие элементы - это обычно неизменяемые
модули текста или простые изображения, которые могут появляться
на экране в окне или блоке диалога. Пользователь не взаимодейс-
твует со статическими управляющими элементами, хотя программа и
может изменять их текст. Рис. 12.2 показывает множество стилей
статического управления и соответствующих констант стиля Windows.

------------------------------------------------------------T-T-¬
¦-=------------------Static Control Tester------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ Default Static Sample Text ¦
¦ ¦
¦ ss_Simple Sample Text ¦
¦ ¦
¦ ss_Left Sample Text ¦
¦ ¦
¦ ss_Center Sample Text ¦
¦ ¦
¦ ss_Right Sample Text ¦
¦ -------------------------------¬ ¦
¦ ss_BlackFrame ¦ ¦ ¦
¦ L------------------------------- ¦
¦ -------------------------------¬ ¦
¦ ss_BlackRect ¦------------------------------¦ ¦
¦ L------------------------------- ¦
¦ -------------------------------¬ ¦
¦ ss_GrayFrame ¦ ¦ ¦
¦ L------------------------------- ¦
¦ -------------------------------¬ ¦
¦ ss_GrayRect ¦------------------------------¦ ¦
¦ L------------------------------- ¦
¦ ¦
¦ ss_NoPrefix Sample & Text ¦
¦ ¦
L----------------------------------------------------------------

Рис. 12.2 Стили статических управляющих элементов.


B.Pascal 7 & Objects/OW - 204 -

Построение статических управляющих элементов
-----------------------------------------------------------------

Пользователь никогда не взаимодействует непосредственно со
статическими управляющими элементами, поэтому приложение будет
очень редко, если вообще будет, принимать уведомляющие сообщения
управляющих элементов относительно статического управляющего эле-
мента. Следовательно, большинcтво статических управляющих элемен-
тов можно сконструировать с идентификатором ресурса -1 или неко-
торым другим неиспользуемым числом.

Конструктор Init в TStatic конструирует новый объект стати-
ческого управления и описывается следующим образом:

constructor TStatic.Init(AParent: PWindowsObject;
AnID: Integer; ATitle: PChar;
X, Y, W, H: Integer; ATextLen: Word);

Кроме обычных параметров Init управляющего объекта,
TStatic.Init имеет два дополнительных параметра - текстовую стро-
ку ATitle и максимальную длину текста ATextLen. Текст должен за-
канчиваться нулевым символом, поэтому в действительности число
отображаемых символов на единицу меньше заданной в конструкторе
длины текста. Типичный вызов для построения статического управля-
ющего элемента может выглядеть так:

Stat1 := New(Static, Init(@Self, id_ST1, '&Text', 20, 50,
200, 24, 6));

После его создания обращаться к статическому управляющему
объекту или манипулировать им требуется редко, поэтому поле, со-
держащее статический управляющий объект, в общем случае присваи-
вать не нужно.

Используемым по умолчанию стилем статического управляющего
элемента является назначенный по умолчанию стиль управляющего
элемента, то есть ws_Child or ws_Visible (выравнивание влево).
Чтобы изменить стиль, модифицируйте поле Attr.Style. Например,
чтобы центрировать текст управляющего элемента, сделайте следую-
щее:

Stat1^.Attr.Style := Stat1^.Attr.Style and (not ss_Left) or
ss_Center;

В статическом управляющем элементе есть возможность подчер-
кивания одного или нескольких символов в строке текста. Реализа-
ция и действие этого эффекта аналогичны подчеркиванию первого
символа в выборе меню: Insert и & должны непосредственно пред-
шествовать символу в строке, который будет подчеркиваться. Напри-
мер, для подчеркивания T в слове 'Text' нужно в вызове Init стро-
ку '&Text'. Если в строке вам нужно использовать &, применяйте
статический стиль Windows ss_NoPrefix (см. Рис. 12.2). Для уточ-
нения текущего текста, который хранится в статическом управляющем

B.Pascal 7 & Objects/OW - 205 -

элементе, используется метод GetText.

Модификация статического управляющего элемента

Для изменения текста статического управляющего элемента
TStatic имеет два метода. SetText устанавливает статический
текст, передаваемый аргументом PChar. Clear удаляет статический
текст. Однако, вы не можете сменить текст статического управляю-
щего элемента, созданный со стилем ss_Simple.

Опрос статических управляющих элементов

Чтобы считать текст, содержащийся в статическом управляющем
элементе, используйте метод GetText.

Пример программы StatTest
-----------------------------------------------------------------

Программа StatTest создает статическое тестовое приложение,
показанное на Рис.12.2. Обратите внимание на то, что метки
('Default Static' и 'ss_Simple') представляют собой статистичес-
кие управляющие элементы, также как и 'Sample Text', черные и се-
рые прямоугольники. Полный текст программы содержится в файле
STATTEST.PAS на ваших дистрибутивных дискетах.



B.Pascal 7 & Objects/OW - 206 -

Использование командных кнопок
-----------------------------------------------------------------

Управляющие элементы типа командных кнопок (которые иногда
называют "нажимаемыми" кнопками) выполняют некоторое действие при
"нажатии" такой кнопки. Есть два стиля командных кнопок, и оба
они имеют тип TButton. Эти два стиля - bs_PushButton и
DefPushButton. Используемые по умолчанию командные кнопки анало-
гичны командным другим кнопкам, но имеют жирную рамку и обычно
используются для указания реакции пользователя по умолчанию. На
Рис. 12.3 показан пример программы Windows, в которой используют-
ся обычные кнопки нажатия и командные кнопки по умолчанию.

----------------------------------T-¬
¦-=----------Hex Calculator-------¦v¦
+---------------------------------+-+
¦ ------------------¬ ¦
¦ ¦ 0 ¦ ¦
¦ L------------------ ¦
¦ ¦
¦ ----¬ ----¬ ----¬ ----¬ ----¬ ¦
¦ ¦¦D¦¦ ¦ E ¦ ¦ F ¦ ¦ + ¦ ¦ & ¦ ¦
¦ L---- L---- L---- L---- L---- ¦
¦ ----¬ ----¬ ----¬ ----¬ ----¬ ¦
¦ ¦ A ¦ ¦ B ¦ ¦ C ¦ ¦ - ¦ ¦ ¦ ¦ ¦
¦ L---- L---- L---- L---- L---- ¦
¦ ----¬ ----¬ ----¬ ----¬ ----¬ ¦
¦ ¦ 7 ¦ ¦ 8 ¦ ¦ 9 ¦ ¦ * ¦ ¦ ^ ¦ ¦
¦ L---- L---- L---- L---- L---- ¦
¦ ----¬ ----¬ ----¬ ----¬ ----¬ ¦
¦ ¦ 4 ¦ ¦ 5 ¦ ¦ 6 ¦ ¦ / ¦ ¦ < ¦ ¦
¦ L---- L---- L---- L---- L---- ¦
¦ ----¬ ----¬ ----¬ ----¬ ----¬ ¦
¦ ¦ 1 ¦ ¦ 2 ¦ ¦ 3 ¦ ¦ % ¦ ¦ > ¦ ¦
¦ L---- L---- L---- L---- L---- ¦
¦ ----¬ ----------¬ -----------¬ ¦
¦ ¦ 0 ¦ ¦ Back ¦ ¦ Equals ¦ ¦
¦ L---- L---------- L----------- ¦
L------------------------------------

Рис. 12.3 Программа Windows, использующая командные кнопки.



B.Pascal 7 & Objects/OW - 207 -

Построение командных кнопок
-----------------------------------------------------------------

Кроме обычных 6 параметров, конструктор Init объекта TButton
воспринимает текстовую строку типа PChar, AText и флаг типа
Boolean IsDefaultButton, указывающий, должна ли кнопка быть ис-
пользуемой по умолчанию или обычной командной кнопкой. Конструк-
тор Init объекта TButton описывается следующим образом:

constructor TButton.Init(AParent: PWindowsObject;
AnID: Integer; AText: PChar;
X, Y, W, H: Integer; IsDefault: Boolean);

Типичный конструктор для обычной кнопки выглядит так:

Push1 := New(PButton, Init(@Self, id_Push1, 'Test Button',
38, 48, 316, 24, False));

Реакция на командные кнопки
-----------------------------------------------------------------

Когда пользователь щелкает на командной кнопке "мышью", по-
рождающее окно кнопки принимает уведомляющее сообщение. Если объ-
ект порождающего окна перехватывает сообщение, он может отреаги-
ровать на эти события выводом блока диалога, записью файла или
другим контролируемым программой действием.

Для организации реакции на сообщения кнопок нужно определить
основанный на дочернем идентификаторе метод для обработки каждой
кнопки. Например, следующий метод IDBut1 обрабатывает реакцию на
"нажатие" пользователем кнопки. Единственный код уведомления, оп-
ределенный в Windows для командных кнопок - это bn_Clicked, поэ-
тому код уведомления не нужно проверять.

type
TTestWindow = object(TWindow)
But1: PButton;
procedure IDBut1(var Msg: TMessage);
virtual id_First + idBut1;
.
.
.
end;

procedure TestWindow.IDBut1(var Msg: TMessage);
begin
MessageBox(HWindow, 'Clicked', 'The Button was:' mb_OK)
end;

Примечание: Пример использования командных кнопок по-
казывает программа BtnTest, которую вы можете найти на
дистрибутивных дисках.


B.Pascal 7 & Objects/OW - 208 -

Использование блоков выбора
-----------------------------------------------------------------

Тип TCheckBox наследует от TButton, а тип TRadioButton - от
TCheckBox. Кнопки с зависимой и независимой фиксацией мы будем
иногда кратко называть блоками выбора.

Кнопки с независимой фиксацией обычно используются для пре-
доставления пользователю выбора одного из двух возможных вариан-
тов. Пользователь может установить или не установить управляющий
элемент, или оставить его так, как он установлен. Если имеется
группа кнопок с независимой фиксацией, то может устанавливаться
не одна кнопка, а несколько. Например, вы можете использовать
кнопку с независимой фиксацией для выбора трех шрифтов для их
загрузки в приложение.

Кнопки с зависимой фиксацией используются для выбора одного
из нескольких взаимоисключающих вариантов. Например, кнопка с за-
висимой фиксацией может использоваться для выбора шрифта для
конкретного символа. Когда пользователь щелкает "мышью" на кнопке
с зависимой фиксацией, это событие приводит к генерации сообщения
Windows. Как и в случае других управляющих элементов, порождающее
окно кнопки с независимой фиксацией обычно перехватывает эти со-
общения и выполняет по ним действия.

Однако, вы можете для выполнения ими некоторых действий при
нажатии создать типы, производные от TCheckBox и TRadioButton.
Если ваш тип определяет метод для nf_First + bn_Clicked, то он
сначала должен вызывать метод реакции BNClicked, а уже затем вы-
полнять любые дополнительные действия.

Построение кнопок с зависимой и независимой фиксацией
-----------------------------------------------------------------

Кроме обычных 6 параметров, конструктор Init для кнопок с
зависимой и независимой фиксацией воспринимает текстовую строку и
указатель на объект группового блока (см. "Групповые блоки"), ко-
торый логически и визуально выделяет кнопки. AGroup - это указа-
тель на объект группового блока. Если AGroup имеет значение nil,
то блок выбора не является частью какой-либо логической группы.
Конструкторы описываются следующим образом:

constructor Init(AParent: PWindowsObject; AnID: Integer;
ATitle: PChar; X, Y, W, H: Integer;
AGroup: PGroupBox);

Для обоих видов блоков выбора синтаксис идентичен. Конструк-
торы различаются только присваиваемым стилем, используемым по
умолчанию. Типичное использование конструкторов блока выбора име-
ет вид:

GroupBox1 := New(PGroupBox, Init(@Self, id_GB1,
'A Group Box', 38, 102, 176, 108));

B.Pascal 7 & Objects/OW - 209 -

ChBox1 := New(PCheckBox, Init(@Self, id_Check1,
'Check Box Text', 235, 12, 150, 26, GroupBox1));

Кнопки с независимой фиксацией по умолчанию инициализируются
со стилем bs_AutoCheckBox, а кнопки с независимой фиксацией име-
ют стиль bs_AutoRadioButton. В соответствии с этими стилями в
каждый момент времени может выбираться только одна клавиша выбора
в группе. Если одна выбрана, то другие автоматически остаются не-
выбранными.

Если вы переопределяете стили объектов кнопок с зависимой
или независимой фиксацией как "неавтоматические", то тогда уже вы
отвечаете за их выбор или не выбор в ответ на произведенные поль-
зователем нажатия.



B.Pascal 7 & Objects/OW - 210 -

Модификация блоков выбора
-----------------------------------------------------------------

Модификация (выбор или отмена выбора) блоков выбора выглядит
задачей пользователя программы, а не вашей. Но в некоторых случа-
ях программе требуется явно управлять состоянием блоков выбора.
Одним из таких случаев является вывод на экран параметров, кото-
рые были выбраны и сохранены ранее. Для модификации состояния
кнопки с независимой фиксацией TCheckBox определяет 4 метода:

Методы модификации кнопок с независимой фиксацией Таблица 12.5
----------------------------T-----------------------------------¬
¦ Выполняемое действие ¦ Вызов метода ¦
+---------------------------+-----------------------------------+
¦ Выбор кнопки ¦ Check или SetCheck(bf_Chacked) ¦
¦ Отмена выбора кнопки ¦ Uncheck или SetCheck(bf_Unchecked)¦
¦ Переключение кнопки ¦ Toggle ¦
L---------------------------+------------------------------------

Когда вы используете данные методы с кнопками с зависимой
фиксацией, ObjectWindows обеспечивает выбор в группе только одной
кнопки с зависимой фиксацией.

Опрос блоков выбора
-----------------------------------------------------------------

Опрос блока выбора - это один из способов выяснения его сос-
тояния и организации реакции на него. Кнопки с зависимой и неза-
висимой фиксацией имеют два состояния: выбранные и невыбранные.
Для получения состояния блока выбора используется метод GetCheck
типа TheckBox:

MyState:=Check1^.GetCheck;

Для определения состояния блока возвращаемое GetCheck значе-
ние можно сравнить с заданными константами bf_Unchecked,
bf_Checked и bf_Grayed.

Примечание: Использование кнопок обоих видов показано
в примере программы BtnTest на ваших дистрибутивных дисках.

Использование групповых блоков
-----------------------------------------------------------------

В своей простейшей форме блок группы представляет собой ста-
тический прямоугольник с меткой, который обычно объединяет другие
управляющие элементы.



B.Pascal 7 & Objects/OW - 211 -

Построение групповых блоков
-----------------------------------------------------------------

Конструктор Init группового блока кроме обычных 6 параметров
воспринимает текстовую строку метки группы.

constructor TGroupBoxInit(AParent: PWindowsObject;
AnID: Integer;
AText: PChar; X, Y, W, H: Integer);

Типичное использование конструктора группового блока может
быть следующим:

GroupBox1 := New(PGroupBox, Init(@Self, id_GB1, 'A Group Box',
38, 102, 176, 108));

Группирование управляющих элементов
-----------------------------------------------------------------

Поскольку блок группы визуально связывает группу других уп-
равляющих элементов, он может логически связывать группу блоков
выбора (кнопок с зависимой и независимой фиксацией). Логическая
группа автоматически отменяет выбор характеристик блоков выбора
"автоматического" стиля.

Для добавления в группу, нужно при конструировании блока вы-
бора указать указатель на блок группы. Например, чтобы добавить в
окно группу кнопок с независимой фиксацией, в оконный объект и
его конструктор можно включить следующее:

type
TSomeWindow = object(TWindow)
Group: PGroupBox;
FirstCheck, SecondCheck: PCheckBox:
constructor Init(AParent: PWindowsObject,
ATitle: PChar);
end;

constructor TSomeWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Group := New(PCheckBox, Init(@Self, id_TheGroup,
'Various boxes', 10, 01, 100, 50));
FirstCheck := New(PCheckBox, Init(@Self, id_FirstCheck,
'One', 15, 20, 90, 10, Group));
SecondCheck := New(PCheckBox, Init(@Self, id_SecondCheck,
'Two', 15, 20, 90, 10, Group));
end;


B.Pascal 7 & Objects/OW - 212 -

Заметим, что передаваемый блоку выбора параметр группы - это
указатель на объект блока группы, а не идентификатор группового
управляющего элемента (как в API Windows). Использование указате-
ля позволяет вам строить объекты перед созданием методом
SetupWindows порождающего окна экранных элементов.



B.Pascal 7 & Objects/OW - 213 -

Реакция на групповые блоки
-----------------------------------------------------------------

Когда происходит событие, которое может изменить выбор блока
группы (например, "нажатие" пользователем кнопки или вызов прог-
раммой метода Check), порождающее окно блока группы принимает со-
общение, основанное на дочернем идентификаторе. Порождающий объ-
ект воспринимает сообщение, используя сумму id_First и идентифи-
катора группового блока. Это позволяет вам определить методы для
каждой группы вместо их задания для каждого блока выбора в груп-
пе.

Для определения управляющего элемента в группе, на который
было оказано воздействие, вы можете прочитать текущее состояние
каждого управляющего элемента.

Пример программы: BtnTest
-----------------------------------------------------------------

BtnTest - это полная программа, которая создает окно с ко-
мандной кнопкой, кнопками с зависимой и независимой фиксацией и
блоком группы управляющих элементов. После запуска приложения по-
является основное окно с управляющими элементами. Когда пользова-
тель "нажимает" на управляющий элемент, приложение реагирует на
это различными способами. См. Рис. 12.4.

Примечание: Полный текст программы содержится в файле
BTNTEST.PAS на ваших дистрибутивных дискетах.

------------------------------------------------------------T-T-¬
¦-=--------------------Button Tester------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ ----¬ ¦
¦ ¦ X ¦ Текст кнопки с независимой фиксацией ¦
¦ L---- ¦
¦ -----------------------------------------------------------¬ ¦
¦ ¦-----------Состояние кнопки с независимой фиксацией-------¦ ¦
¦ L----------------------------------------------------------- ¦
¦ ¦
¦ --Групповой блок-------------------------------¬ ¦
¦ ¦ ( ) Кнопка с зависимой фиксацией 1 ¦ ¦
¦ ¦ (*) Кнопка с зависимой фиксацией 2 ¦ ¦
¦ L----------------------------------------------- ¦
L----------------------------------------------------------------

Рис. 12.4 Окно с различными видами кнопок.


B.Pascal 7 & Objects/OW - 214 -

Использование полос прокрутки
-----------------------------------------------------------------

Полосы прокрутки являются важнейшим механизмом изменения об-
зора пользователем окна приложения, блока списка или комбиниро-
ванного блока. Однако, может возникнуть ситуация, когда нужна от-
дельная полоса прокрутки для выполнения некоторой специализиро-
ванной задачи (например, управление температурой в программе тер-
мостата или цветом в программе рисования). Когда нужна отдельная
специализированная полоса прокрутки, используются объекты
TScrollBar. Рис. 12.5 показывает типичное использование объекта
TSсrollBar.

------------------------------------------------------------T-T-¬
¦-=-----------------------Thermostat------------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ 68 градусов ¦
¦ ¦
¦ ¦
¦ ¦
¦ ---T----------------------------------------------------T--¬ ¦
¦ ¦<-¦----------------------------------------------------¦->¦ ¦
¦ L--+----------------------------------------------------+--- ¦
¦ ¦
L----------------------------------------------------------------

Рис. 12.5 Объект полосы прокрутки.

Построение полос прокрутки
-----------------------------------------------------------------

Кроме обычных 6 параметров управляющего объекта, конструктор
Init полосы прокрутки воспринимает флаг типа Boolean, указываю-
щий, является ли полоса прокрутки горизонтальной. Приведем описа-
ние конструктора полосы прокрутки:

constructor TScrollBarInit(AParent: PWindowsObject;
AnID: Integer; X, Y, W, H: Integer;
IsHScrollBar: Boolean);

Если вы зададите нулевую ширину вертикальной полосы прокрут-
ки, Windows присвоит ей стандартную ширину (аналогичную полосе
прокрутки блока списка). То же самое касается задания нулевой вы-
соты горизонтальной полосы прокрутки. Вызов:

ThermScroll := New(PScrollBar, Init(@Self, id_ThermScroll,
20, 170, 340, 0, True));

создает горизонтальную полосу прокрутки стандартной высоты, как
это показано на Рис. 12.5. Init конструирует полосы прокрутки со
стилями ws_Child, ws_Visible и sbs_Horz или sbs_Vert для горизон-
тальной или вертикальной полосы прокрутки соответственно. Разно-

B.Pascal 7 & Objects/OW - 215 -

образные полосы прокрутки показаны на Рис. 12.6.

------------------------------------------------------------T-T-¬
¦-=--------------------Scroll Bar Tester--------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ---¬ --¬ ¦
¦ ---T--¦/\¦---------------------------T--¬ ¦^¦ ¦
¦ ¦/-¦--+--+---------------------------¦-\¦ +-+ ¦
¦ ¦\-¦--¦--¦---------------------------¦-/¦ ¦-¦ ¦
¦ L--+--¦--¦---------------------------+--- ¦-¦ ¦
¦ ¦--¦ ¦-¦ ¦
¦ ¦--¦ ¦-¦ ¦
¦ ¦--¦ +-+ ¦
¦ +--+ ¦v¦ ¦
¦ ¦\/¦ L-- ¦
¦ L--- ¦
¦ ---T----------------------------------------------------T--¬ ¦
¦ ¦<-¦----------------------------------------------------¦->¦ ¦
¦ L--+----------------------------------------------------+--- ¦
¦ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 12.6 Окно с разнообразными полосами прокрутки.

Управление диапазоном полосы прокрутки
-----------------------------------------------------------------

Один из атрибутов полосы прокрутки, инициализируемый при ее
конструировании, это диапазон. Диапазон полосы прокрутки - это
набор всевозможных положений указателя (маркера полосы прокрут-
ки). Маркер полосы прокрутки - это подвижный прямоугольник, кото-
рый пользователь может перемещать по ней. Каждой позиции соот-
ветствует целое число. Порождающее окно использует эту целую ве-
личину, позицию, для установки и запроса по полосе прокрутки.
После конструирования объекта полосы прокрутки его диапазон уста-
навливается от 1 до 100.

Положению маркера в "самой верхней" позиции (вершина верти-
кальной полосы прокрутки или крайнее левое положение горизонталь-
ной полосы прокрутки) соответствует позиция 1. "Самой нижней" по-
зиции маркера соответствует позиция 100. Для установки иного диа-
пазона нужно использовать метод SetRange, описанный в разделе
"Модификация полосы прокрутки".



B.Pascal 7 & Objects/OW - 216 -

Управление параметрами полосы прокрутки
-----------------------------------------------------------------

Два других атрибута объекта полосы прокрутки - это его при-
ращение по строкам и страницам. Приращение по строкам, установ-
ленное в 1, это расстояние в единицах диапазона, на которое пере-
местится указатель при нажатии пользователем стрелок на полосе
прокрутки. Приращение по страницам, установленное в 10, это расс-
тояние в единицах диапазона, на которое переместится указатель
при нажатии пользователем в области прокрутки. Эти значения можно
изменить непосредственной модификацией полей объекта TScrollBar,
LineSize и PageSize.

Опрос полосы прокрутки
-----------------------------------------------------------------

TScrollBar определяет два метода опроса полосы прокрутки:
GetRange и GetPosition. Метод GetRange - это процедура, использу-
ющая два целочисленных переменных аргумента. Процедура заносит в
эти целые значения верхнюю и нижнюю позиции из диапазона полосы
прокрутки. Этот метод очень удобен, когда нужно, чтобы ваша прог-
рамма переместила указатель в его верхнюю или нижнюю позицию.

GetPosition - это функция, которая возвращает в виде целой
величины позицию указателя. Ваша программа очень часто будет зап-
рашивать диапазон и позицию и сравнивать их.

Модификация полос прокрутки
-----------------------------------------------------------------

Модификация полос прокрутки - это скорее работа для пользо-
вателя вашей программы, и часто это действительно так. Однако,
ваша программа также может модифицировать полосу прокрутки. Ис-
пользуемые для этого методы перечислены в следующей таблице.

Методы модификации полос прокрутки Таблица 12.6
---------------------------------------T------------------------¬
¦ Выполняемое действие ¦ Вызываемый метод ¦
+--------------------------------------+------------------------+
¦ Задание диапазона прокрутки ¦ SetRange ¦
¦ Установка позиции маркера ¦ SetPosition ¦
¦ Перемещение позиции маркера ¦ DeltaPos ¦
L--------------------------------------+-------------------------

SetRange - это процедура, которая воспринимает два целочис-
ленных аргумента, наименьшую и наибольшую позицию диапазона. По
умолчанию новая полоса прокрутки имеет диапазон от 1 до 100. Вы
можете изменить этот диапазон для наилучшего расположения управ-
ляющих элементов полос прокрутки. Например, полоса прокрутки в
приложении для термостата может иметь диапазон от 32 до 120 гра-
дусов Фаренгейта:

ThermScroll^.SetRange(32, 120);

B.Pascal 7 & Objects/OW - 217 -



SetPosition - это процедура, которая воспринимает один цело-
численый аргумент - позицию, в которую нужно переместить указа-
тель полосы прокрутки. В рассмотренном ранее приложении для тер-
мостата, ваша программа может непосредственно установить темпера-
туру 78 градусов:

ThermScroll^.SetPosition(78);

Третий метод DeltaPos передвигает позицию указателя полосы
прокрутки вверх (налево) или вниз (направо) на величину, заданную
целым аргументом. Положительная целая величина перемещает указа-
тель вниз (направо). Отрицательная целая величина перемещает его
вверх (налево). Например, для уменьшения температуры термостата
на 5 градусов используется:

ThermScroll^.DeltaPos(-5);

Реакция на полосы прокрутки
-----------------------------------------------------------------

При работе пользователя с полосой прокрутки ее порождающее
окно получает от нее уведомляющие сообщения Windows. Если нужно,
чтобы ваше окно реагировало на сообщения прокрутки, реакция на
информационные сообщения должна быть обычной, путем определения
методов реакции, основанных на дочерних идентификаторах.

Однако, уведомляющие сообщения полосы прокрутки несколько
отличаются от других уведомляющих сообщений элемента управления.
Они основаны на сообщениях Windows wm_HScroll и wm_VScroll, а не
wm_Command. Единственное отличие, на которое нужно обратить вни-
мание состоит в том, что уведомляющие коды полосы прокрутки запи-
саны в Msg.wParam, а не в Msg.lParamHi.

Чаще всего встречаются коды sb_LineUp, sb_LineDown,
sb_PageUp, sb_PageDown, sb_ThumbPosition и sb_ThumbTrack. Наибо-
лее часто вы будете реагировать на каждое событие проверкой новой
позиции полосы прокрутки и организацией соответствующего дейс-
твия. В данном случае вы можете игнорировать уведомляющий код.
Например:

procedure TestWindow.HandleThermScrollMsg(var Msg:
TMessage);
var
NewPos: Integer;
begin
NewPos:=ThermScroll^.GetPosition;
{ обработка с помощью NewPos }
end;

Часто альтернатива состоит в том, чтобы не реагировать на
перемещение указателя до тех пор, пока пользователь не выберет

B.Pascal 7 & Objects/OW - 218 -

его нового местоположения. В этом случае нужно реагировать на со-
общение с кодом sb_ThumbTrack.

procedure TestWindow.HandleThermScrollMsg(var Msg:
TMessage);
var
NewPos: Integer;
begin
if Msg.wParam <> sb_ThumbTrack
then begin
NewPos:=ThermScroll^.GetPosition;
{ некоторая обработка на основе NewPos. }
end;
end;

Иногда может потребоваться, чтобы объекты полосы прокрутки
сами реагировали на уведомляющие сообщения полосы прокрутки. При
этом конкретная реакция поведения должна быть встроена в объект
полосы прокрутки. Для программирования объекта полосы прокрутки,
который непосредственно реагировал бы на его информационные сооб-
щения, нужно определить для его типа метод реакции, основанный на
информации. В качестве идентификатора заголовка метода нужно ис-
пользовать сумму nf_First и информационного кода полосы прокрут-
ки. Этот процесс описан в разделе "Уведомляющие сообщения управ-
ляющих элементов" Главы 16.

Пример программы: SBarTest
-----------------------------------------------------------------

Программа SBarTest создает приложение для термостата, пока-
занное на Рис. 12.5. Полный текст программы содержится в файле
SBARTEST.PAS на ваших дистрибутивных дискетах.

Использование управляющих элементов редактирования
-----------------------------------------------------------------

Управляющие элементы редактирования могут быть описаны, как
интерактивные статические управляющие элементы. Это прямоугольные
области (с рамкой или без) на экране, которые пользователь прило-
жения может заполнять текстом, изменять или удалять. Управляющий
элемент редактирования наиболее удобен в качестве поля для ввода
данных на экране. Они обеспечивают следующие операции:

- Ввод текста пользователем.
- Динамическое отображение текста приложением.
- Вырезание, копирование и вставка в буфер вырезанного изоб-
ражения.
- Многострочное редактирование (удобно для текстовых редак-
торов).


B.Pascal 7 & Objects/OW - 219 -

На Рис. 12.7 показано окно с двумя управляющими элементами
редактирования.

------------------------------------------------------------T-T-¬
¦-=------------------Edit Control Tester--------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ Оригинал: Копия: ¦
¦ -----------------------¬ ----¬ -----------------------¬ ¦
¦ ¦Default Text ¦ ¦->-¦ ¦DEFAULT.TEXT ¦ ¦
¦ L----------------------- L---- L----------------------- ¦
¦ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 12.7 Окно с управляющими элементами редактирования.



B.Pascal 7 & Objects/OW - 220 -

Построение управляющих элементов редактирования
-----------------------------------------------------------------

Конструктор Init управляющего элемента редактирования анало-
гичен конструктору статического управляющего элемента и восприни-
мает 6 обычных параметров, плюс начальная текстовая строка, мак-
симальная длина строки и флаг Multiline типа Boolean. Конструктор
TEdit описывается следующим образом:

constructor TEdit.Init(AParent: PWindowsObject;
AnID: Integer; ATitle: PChar;
X, Y, W, H, ATextLen: Integer; Multiline: Boolean);

По умолчанию управляющий элемент редактирования имеет стили
ws_Child, ws_Visible, es_TabStop, es_Left и es_AutoHScroll. Так
как управляющий элемент должен включать в себя завершающий нуле-
вой символ, параметр длины текста на самом деле на 1 превышает
максимальное число символов, допустимых в строке редактирования.

Если Multiline имеет значение True, то управление редактиро-
ванием имеет стиль es_MultiLine, es_AutoVScroll, ws_VScroll и
ws_HScroll. Приведем типичные конструкторы управляющих элементов
редактирования (один для однострочного элемента, другой - для
многострочного):

EC1 := New(PEdit, Init(@Self, id_EC1, 'Default Text', 20, 50,
150, 30, 40, False));
EC2 := New(PEdit, Init(@Self, id_EC2, '', 20, 20, 200, 150,
40, True));

Использование буфера вырезанного изображения и меню Edit
-----------------------------------------------------------------

Вы можете передавать текст непосредственно между объектом
управляющего элемента редактирования и буфером вырезанного изоб-
ражения Windows, используя для этого вызовы методов. Часто вам
бывает нужно предоставить пользователю доступ к этим методам че-
рез меню редактирования. Объект управляющего элемента редактиро-
вания автоматически отреагирует на выбор из меню таких вариантов,
как Edit¦Copy и Edit¦Undo. TEdit определяет основанные на коман-
дах методы (например, CMEditCopy и CMEditUndo), которые вызывают-
ся в ответ на конкретный выбор (команду) меню в порождающем окне
управляющего элемента редактирования. CMEditCopy вызывает Copy, а
CMEditUndo вызывает Undo.


B.Pascal 7 & Objects/OW - 221 -


Следующая таблица содержит список методов, которые вызывают-
ся в ответ на выбор пункта меню:

Управляющие элементы редактирования и меню Edit Таблица 12.7
------------------------T-------------------T-------------------¬
¦ Операция ¦ Метод TEdt ¦ Команда меню ¦
+-----------------------+-------------------+-------------------+
¦ Копирование текста в ¦ Cut ¦ cm_EditCut ¦
¦ буфер вырезанного ¦ ¦ ¦
¦ изображения. ¦ ¦ ¦
+-----------------------+-------------------+-------------------+
¦ Вырезание текста в ¦ Copy ¦ cm_EditCopy ¦
¦ буфер вырезанного ¦ ¦ ¦
¦ изображения. ¦ ¦ ¦
+-----------------------+-------------------+-------------------+
¦ Вставка текста из ¦ Paste ¦ cm_EditPaste ¦
¦ буфера вырезанного ¦ ¦ ¦
¦ изображения. ¦ ¦ ¦
+-----------------------+-------------------+-------------------+
¦ Очистка всего элемента¦ Clear ¦ cm_EditClear ¦
¦ редактирования. ¦ ¦ ¦
+-----------------------+-------------------+-------------------+
¦ Удаление выделенного ¦ DeleteSelection ¦ cm_EditDelete ¦
¦ текста. ¦ ¦ ¦
+-----------------------+-------------------+-------------------+
¦ Отмена последнего ¦ Undo ¦ cm_EditUndo ¦
¦ редактирования. ¦ ¦ ¦
L-----------------------+-------------------+--------------------

Чтобы добавить в окно меню редактирования, содержащее управ-
ляющий элемент редактирования, определите для окна с помощью ко-
манд, перечисленных в Таблице 12.7, ресурс меню. Никаких новых
методов вам писать не нужно.

Имеется также один дополнительный метод в виде булевской
функции CanUndo, который определяет, можно ли отменить последнюю
операцию редактирования.



B.Pascal 7 & Objects/OW - 222 -

Опрос управляющих элементов редактирования
-----------------------------------------------------------------

Иногда нужно организовать опрос управляющих элементов редак-
тирования для проверки допустимости введенного текста, записи
ввода для его последующего использования или копирования ввода в
другой управляющий элемент. TEdit поддерживает несколько методов
опроса. Многие из опросов управляющих элементов редактирования и
методов модификации возвращают или требуют от вас указать номер
строки или позицию символа в строке. Все эти индексы начинаются с
нуля. Другими словами, первая строка - это нулевая строка, а пер-
вый символ в любой строке это нулевой символ. Самыми важными ме-
тодами запроса являются GetText, GetLine, NumLines и LineLength.

Методы опроса управляющих элементов редактирования Таблица12.8
-------------------------------------------T--------------------¬
¦ Выполняемое действие ¦ Вызываемый метод ¦
+------------------------------------------+--------------------+
¦ Определение изменения текста ¦ IsModified ¦
¦ Считывание всего текста ¦ GetText ¦
¦ Считывание строки ¦ GetLine ¦
¦ Получение числа строк ¦ GetNumLines ¦
¦ Получение длины данной строки ¦ GetLineLength ¦
¦ Получение индекса выделенного текста ¦ GetSelection ¦
¦ Получение диапазона символов ¦ GetSubText ¦
¦ Подсчет символов перед строкой ¦ LineIndex ¦
¦ Поиск строки, содержащей индекс ¦ GetLineFromProc ¦
L------------------------------------------+---------------------

Вы можете заметить, что методы запросов TEdit, которые возв-
ращают текст из управляющего элемента редактирования, сохраняют
форматирование текста. Это важно только для многострочных управ-
ляющих элементов редактирования, которые допускают появление нес-
кольких строк текста. В этом случае возвращаемый текст, который
занимает несколько строк в управляющем элемента редактирования
содержит в конце каждой строки два дополнительных символа: возв-
рат каретки (#13) и смена строки (#10). Если этот текст снова по-
мещается в управляющий элемент редактирования, вставляется из бу-
фера вырезанного изображения, записывается в файл или выводится
на принтер, то строки разбиваются так, как это было в управляющем
элемента редактирования.

Следовательно, при использовании метода запроса для получе-
ния определенного числа символов, нужно учитывать эти два симво-
ла, которые заканчивают строку. GetText ищет текст в управляющем
элементе редактирования. Он заполняет строку, на которую указыва-
ет переданный аргумент PChar, содержимым управляющего элемента
редактирования, включая перевод строки. Общее число символов за-
дается вторым параметром. Он возвращает значение False, если уп-
равляющий элемент редактирования пуст, или содержит текста боль-
ше, чем помещается в предоставленную строку. Следующая процедура
считывает из управляющего элемента редактирования строку и выде-
ленный текст:

B.Pascal 7 & Objects/OW - 223 -


procedure TTestWindow.ReturnText(RetText: PChar);
var TheText: array[0..20] of Char;
begin
if EC1^.GetText(@TheText, 20) then
RetText:=@TheText
else RetText:=nil;
end;

procedure TTestWindow.ReturnText(RetText: PChar);
var TheText: array[0..20] of Char;
begin
RetText:=nil;
with EC^ do
if NumLines >= LineNum then
if LineLength(LineNum) < 11 then
if GetLine(@TheText, 20, LineNum) then
RetText := @TheText;
end;

procedure TestWindow.ReturnLineText(RetText: PChar;
LineNum: Integer);
var
TheText: array[0..20] of Char;
begin
with EC1^ do
begin
GetSelection(SelStart, SelEnd);
GetSubText(TheText, SelStart, SelEnd);
end;
RetText := TheText;
end;

Модификация управляющих элементов редактирования
-----------------------------------------------------------------

Для традиционной программы ввода текста вам может не потре-
боваться непосредственно модифицировать управляющий элемент ре-
дактирования. Пользователь модифицирует текст, а программа считы-
вает этот текст методом опроса. Однако, во многих других случаях
использование управляющего элемента редактирования требует, чтобы
ваше приложение явно заменяло, вставляло, удаляло и выбирало
текст. ObjectWindows обеспечивает подобное поведение и кроме того
предоставляет возможность использовать прокрутку управляющего
элемента редактирования.

Методы модификации
управляющих элементов редактирования Таблица 12.9
-----------------------------------------T----------------------¬
¦ Выполняемое действие ¦ Вызываемый метод ¦
+----------------------------------------+----------------------+
¦ Удаление всего текста ¦ Clear ¦
¦ Удаление выделенного текста ¦ DeleteSelection ¦

B.Pascal 7 & Objects/OW - 224 -

¦ Удаление диапазона символов ¦ DeleteSubText ¦
¦ Удаление строки текста ¦ DeleteLine ¦
¦ Вставка текста ¦ Insert ¦
¦ Вставка текста из буфера ¦ Paste ¦
¦ вырезанного изображения ¦ ¦
¦ Замена всего текста ¦ SetText ¦
¦ Выделение диапазона текста ¦ SelectRange ¦
¦ Прокрутка текста ¦ Scroll ¦
L----------------------------------------+-----------------------

Пример программы: EditTest
-----------------------------------------------------------------

EditTest - это программа, которая помещает на экран основное
окно, которое будет порождающим для двух управляющих элементов
редактирования, двух статических управляющих элементов и кнопки.
Данное окно показано на Рис. 12.7.

Когда пользователь щелкает на командной кнопке кнопкой "мы-
ши", текст из левого управляющего элемента редактирования (EC1)
копируется в правый управляющий элемент редактирования (EC2). В
EC2 текст преобразуется в буквы верхнего регистра, поскольку оно
было построено со стилем es_UpperCase. Если в C1 никакой текст не
выбран, то в EC2 копируется весь текст. Если в EC1 выбран некото-
рый текст, то будет скопирован именно он. Меню редактирования
обеспечивает функции редактирования независимо от того, с каким
управляющим элементов редактирования идет работа. Полный файл
EDITTEST.PAS и файл ресурса EDITTEST содержатся на ваших дистри-
бутивных дискетах.

Использование комбинированных блоков
-----------------------------------------------------------------

Управляющий элемент типа комбинированного блока является со-
четанием двух других управляющих элементов: блока списка и управ-
ляющего элемента редактирования. Он служит тем же целям, что и
блок списка - позволяет пользователю выбрать один элемент списка
из прокручиваемого списка элементов текста, нажимая на кнопку
"мыши". Управление редактированием, вынесенное в верхнюю часть
блока списка предоставляет иной механизм выбора, позволяя пользо-
вателю ввести текст нужного элемента. Если отображается область
списка комбинированного блока, то автоматически выбирается нужный
элемент. Тип TComboBox является производным от типа TListBox и
наследует его методы модификации, опроса и выбора элементов спис-
ка. Кроме того, TComboBox предоставляет методы по манипулированию
списком, находящемся в комбинированном блоке, который в некоторых
случаях может раскрываться по запросу.



B.Pascal 7 & Objects/OW - 225 -

Три типа комбинированных блоков
-----------------------------------------------------------------

Имеются три типа комбинированных блоков: простые, раскрываю-
щиеся и раскрывающиеся со списком. На Рис. 12.8 показан вывод
трех типов комбинированных блоков с блоком списка.

------------------------------------------------------------T-T-¬
¦-=------------------Static Control Tester------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ ¦
¦ Блок списка Простой комбинированный блок ¦
¦ -----------------¬ ---------------------------¬ ¦
¦ ¦a ¦ ¦ ¦ ¦
¦ ¦b ¦ LT-------------------------+ ¦
¦ ¦c ¦ ¦a ¦ ¦
¦ ¦d ¦ ¦b ¦ ¦
¦ ¦e ¦ ¦c ¦ ¦
¦ ¦f ¦ ¦d ¦ ¦
¦ L----------------- L-------------------------- ¦
¦ ¦
¦ Раскрывающийся комбинированный Комбинированный блок с ¦
¦ блок раскрывающимся списком ¦
¦ ------------------------¬----¬ -----------------------T---¬ ¦
¦ ¦ ¦¦ v ¦ ¦c---------------------¦ v ¦ ¦
¦ L------------------------L---- L----------------------+---- ¦
¦ ¦
¦ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 12.8 Три типа комбинированных блоков и блок списка.

Перечень стилей комбинированного блока Таблица 12.10
----------------------T---------------------T-------------------¬
¦ Стиль ¦Возможность скрытого ¦Соответствие текста¦
¦ ¦ списка ¦ списку ¦
+---------------------+---------------------+-------------------+
¦ Простой ¦ нет ¦ нет ¦
¦ Раскрывающийся ¦ есть ¦ нет ¦
¦ Раскрывающийся со ¦ есть ¦ да ¦
¦ списком ¦ ¦ ¦
L---------------------+---------------------+--------------------

С точки зрения пользователя между различными стилями комби-
нированных блоков существуют следующие различия:

* Простые комбинированные блоки.

Простой комбинированный блок не может делать область спис-
ка скрытой. Его область редактирования ведет себя анало-
гично управляющему элементу редактирования. Пользователь
может вводить и редактировать текст, и текст не обязан

B.Pascal 7 & Objects/OW - 226 -

совпадать ни с одним из элементов в списке. При совпадении
выбирается соответствующий элемент списка.

* Раскрывающиеся комбинированные блоки.

Раскрывающиеся комбинированные блоки ведут себя аналогично
простым комбинированным блокам, но с одним исключением. В
начальной стадии работы их область списка не отображается.
Она появляется, когда пользователь нажимает стрелку вниз,
расположенную справа от области редактирования. Раскрываю-
щиеся комбинированные блоки и раскрывающиеся комбинирован-
ные блоки списков очень удобны, когда нужно поместить
большое число управляющих элементов в маленькую область.
Когда они не используются, то занимают значительно меньшую
площадь, чем простой комбинированный блок или блок списка.

* Раскрывающиеся комбинированные блоки списка.

Область списка в раскрывающемся комбинированном блоке
списка ведет себя подобно области списка в спускающемся
комбинированном блоке - появляется при необходимости и ис-
чезает, когда не нужна. Эти два типа комбинированных бло-
ков отличаются поведением их областей редактирования.
Раскрывающиеся области редактирования ведут себя подобно
обычным управляющим элементам редактирования. Раскрывающи-
еся области редактирования списка ограничиваются только
отображением одного элемента списка. Если редактируемый
текст соответcтвует элементу списка, то никаких дополни-
тельных символов ввести нельзя.

Выбор типа комбинированного блока
-----------------------------------------------------------------

Раскрывающиеся комбинированные блоки списка удобно использо-
вать тогда, когда не допускаются никакие другие варианты, кроме
перечисленных в области списка. Например, при выборе принтера для
печати можно выбрать только принтер, к которому есть доступ в ва-
шей системе.

С другой стороны, раскрывающиеся комбинированные блоки могут
воспринимать выбор, который отличается от приведенных в списке
элементов. Раскрывающийся комбинированный блок можно использовать
для выбора файлов на диске при их открытии или записи. Пользова-
тель может либо просматривать каталоги в поисках нужного файла,
либо ввести полный маршрут и имя файла в области редактирования,
независимо от того, присутствует ли это имя файла в области спис-
ка.

Построение комбинированных блоков
-----------------------------------------------------------------

Кроме обычных 6 параметров объектов управляющих элементов
конструктор Init для TComboBox воспринимает в качестве аргументов

B.Pascal 7 & Objects/OW - 227 -

стиль и максимальную длину текста. Конструктор TComboBox описыва-
ется следующим образом:

constructor TComboBox.Init(AParent: PWindowsObject;
AnID: Integer: X, Y, W, H: Integer;
AStyle, ATextLen: Word);

Все комбинированные блоки, построенные с помощью Init, име-
ют стили ws_Child, ws_Visible, cbs_AutoHScroll, cbs_Sort (отсор-
тированный список), и VScroll (вертикальная полоса прокрутки).
Параметр стиля - это один из стандартных стилей комбинированных
блоков Windows: cbs_Simple, cbs_DropDown или cbs_DropDownList.
Параметр длины текста работает подобно соответствующему параметру
управляющего элемента редактирования, ограничивая число символов,
которые можно ввести в область редактирования комбинированного
блока.

Следующие строки приведут к созданию спускающегося комбини-
рованного блока списка с неотсортированным списком:

CB3: = New(PComboBox, Init(@Self, id_CB3, 190, 160,
150, 100, cbs_DropDownList, 40));
CB3^.Attr.Style:=CB3^.Attr.Style and (not cbs_Sort);

Модификация комбинированных блоков
-----------------------------------------------------------------

TComboBox определяет два метода для демонстрации и сокрытия
области списка в раскрывающихся комбинированных блоках и раскры-
вающихся комбинированных блоках списка: ShowList и HideList. Обе
эти процедуры не нуждаются в аргументах. Вызывать эти методы для
демонстрации или сокрытия списка, когда пользователь нажимает
стрелку вниз справа от области редактирования, не нужно. В этом
случае работает автоматический механизм комбинированных блоков.
Эти методы полезны только для принудительного вывода или сокрытия
списка.

Пример программы: CBoxTest
-----------------------------------------------------------------

Программа CBoxTest реализует приложение, показанное на Рис.
12.8. В нем использованы все три типа комбинированных блоков. CB1
- это простой комбинированный блок, CB2 это раскрывающийся комби-
нированный блок, а CB3 - это раскрывающийся комбинированный блок
списка. Нажатие кнопок Show и Hide выполняет принудительный вывод
и сокрытие правого верхнего комбинированного блока, CB3, путем
вызова методов ShowList и HideList.

Примечание: Полный текст файла CBOXTEST.PAS содержится
на ваших дистрибутивных дискетах.

Установка значений управляющих элементов
-----------------------------------------------------------------

B.Pascal 7 & Objects/OW - 228 -


Для управления сложными блоками диалога или окнами с мно-
жеством дочерних окон управляющих элементов вы обычно можете для
хранения и выяснения состояния его управляющих элементов создать
производный тип объекта. Состояние управляющего элемента включает
в себя текст управляющего элемента редактирования, положение по-
лосы прокрутки и установку кнопки с зависимой фиксацией.

Для чего используется буфер передачи?
-----------------------------------------------------------------

В качестве альтернативы вы можете не определять производный
объект, а определить соответствующую запись, представляющую сос-
тояние управляющих элементов окна или диалога. Эта запись называ-
ется буфером передачи, поскольку легко передает информацию о сос-
тоянии между буфером и набором управляющих элементов.

Например, ваша программа может иметь режимный блок диалога
и после его закрытия, выделить информацию из буфера передачи от-
носительно состояния каждого из его управляющих элементов. Следо-
вательно, при повторном вызове пользователем блока диалога, его
управляющие элементы будут выведены в соответствии с их состояни-
ем перед последним закрытием диалога. Кроме того, вы можете уста-
новить начальное состояние каждого из управляющих элементов и на
основании данных буфера передачи. Вы можете явно передавать дан-
ные в любом направлении в любой момент времени, например, устано-
вить значения управлений равными их предыдущим значениям. Окно
или безрежимный блок диалога с управляющими элементами также мо-
гут использовать механизм передачи для установки или выяснения
информации о состоянии в любой момент времени.

Механизм передачи требует для представления управляющих
элементов, для которых вы будете передавать данные, использования
объектов ObjectWindows. Это означает, что вы должны использовать
InitResource для связывания объектов с управляющими элементами в
блоках и окнах диалога.

Примечания: Связь управляющих элементов с управляющими
объектами описывается в Главе 11 "Объекты диалоговых бло-
ков".

Чтобы использовать механизм передачи, вы можете сделать сле-
дующее:

* Определить буфер передачи.
* Определить соответствующее окно.
* Передать данные.

Определение буфера передачи
-----------------------------------------------------------------

Буфер передачи - это запись с одним полем для каждого управ-

B.Pascal 7 & Objects/OW - 229 -

ляющего элемента, участвующего в передаче. Окно или диалог могут
также иметь управляющие элементы, значения которых не устанавли-
ваются механизмом передачи. Например, командные кнопки, у которых
нет состояния, не участвуют в передаче. Это же справедливо для
групповых блоков.

Для определения буфера передачи нужно определить поле для
каждого участвующего управляющего элемента диалога или окна. Оп-
ределять поля для каждого управления диалога или окна не требует-
ся - нужно лишь определить поля для тех из них, которые будут по-
лучать и принимать значения по вашему желанию. Этот буфер переда-
чи хранит один из каждых типов управляющих элементов, кроме ко-
мандной кнопки и блока группы:

type
TSampleTransferRecord = record
Stat1: array[0..TextLen-1] of Char; { статический текст }
Edit1: array[0..TextLen-1] of Char; { текст управляющего
элемента редактирования }
List1Strings: PStrCollection; { строки блока списка }
List1Selection: Integer; { индекс выбранных строк }
ComboStrings: PStrCollection; { строки комбинированного
блока }
ComboSelection: array[0..TextLen-1] of Char; { выбранные
строки }
Check1: Word; { проверка состояния блока}
Radio1: Word; { состояние кнопки с независимой фиксацией }
Scroll1: ScrollBarTransferRec; { диапазон полосы
прокрутки и т.д. }
end;


B.Pascal 7 & Objects/OW - 230 -


В каждом типе управляющего элемента хранится различная ин-
формация. Буфер передачи для каждого стандартного управляющего
элемента поясняется в следующей таблице:

Поля буфера передачи
для каждого типа управляющего элемента Таблица 12.11
------------------------T---------------------------------------¬
¦ Тип управляющего ¦ Буфер передачи ¦
¦ элемента ¦ ¦
+-----------------------+---------------------------------------+
¦ Статический ¦ Символьный массив размером до макси-¦
¦ ¦ мальной длины текста, плюс завершающий¦
¦ ¦ нулевой символ. ¦
¦ ¦ ¦
+-----------------------+---------------------------------------+
¦ Редактирование ¦ Текстовый буфер управляющего элемента¦
¦ ¦ редактирования размером до длины, оп-¦
¦ ¦ ределенной в текстовом поле TextLen. ¦
¦ ¦ ¦
+-----------------------+---------------------------------------+
¦ Блок списка ¦ ¦
¦ одиночный выбор ¦ Набор строк в списке, плюс целочислен-¦
¦ ¦ ный индекс выделенной строки. ¦
¦ ¦ ¦
¦ множественный выбор ¦ Набор строк в списке, плюс запись, со-¦
¦ ¦ держащая индексы всех выделенных эле-¦
¦ ¦ ментов. ¦
¦ ¦ ¦
+-----------------------+---------------------------------------+
¦ Комбинированный блок ¦ Набор строк в списке, плюс выбранная¦
¦ ¦ строка. ¦
¦ ¦ ¦
+-----------------------+---------------------------------------+
¦ Кнопка с независимой ¦ Значения Word с указывающими состояния¦
¦ фиксацией ¦ bf_Unchecked, bf_Checked и bf_Grayed. ¦
¦ ¦ ¦
+-----------------------+---------------------------------------+
¦ Кнопка с зависимой ¦ Значения Word с указывающими состояния¦
¦ фиксацией ¦ bf_Unchecked, bf_Checked и bf_Grayed. ¦
¦ ¦ ¦
+-----------------------+---------------------------------------+
¦ Полоса прокрутки ¦ Запись типа TScrollBarTransferRec, со-¦
¦ ¦ храняющая диапазон полосы прокрутки и¦
¦ ¦ позицию в ней. ¦
¦ ¦ ¦
L-----------------------+----------------------------------------

Тип TScrollBarTransferRec имеет вид:

TScrollBarTransferRec := record
LowValue : Integer;
HighValue: Integer;

B.Pascal 7 & Objects/OW - 231 -

Position : Integer;
end;

Определение окна
-----------------------------------------------------------------

Окно или диалоговый блок, которые используют буфер передачи,
должны создавать объекты участвующих управляющих элементов в той
последовательности, в которой определяются их соответствующие по-
ля буфера передачи. Для подключения механизма передачи к объекту
окна или диалога нужно просто установить значение его поля
TransferBuffer в указатель на определенный вами буфер передачи.

Использование буфера передачи с диалоговым блоком
-----------------------------------------------------------------

Для случая окон с управляющими элементами объекты управляю-
щих элементов конструируются с использованием Init. Для диалогов
и окон диалогов нужно использовать конструктор InitResource. Нап-
ример (используется определенный ранее тип TSampleRecord):

type
TSampleTransferRecord = record
.
.
.
PParentWindow = ^TParentWindow;
TParentWindow = object(TWindow)
TheDialog: PDialog;
TheBuffer: SampleTransferRecord;
.
.

B.Pascal 7 & Objects/OW - 232 -

.
.
constructor TParentWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
var
Stat1: PStatic;
Edit1: PEdit;
List1: PListBox;
Combo1: PComboBox;
Check1: PCheckBox;
Radio1: PRadioButton;
Scroll1: PScrollBar;
begin
TWindow.Init(AParent, ATitle);
TheDialog^.Init(@Self, PChar(101));
New(Stat1, InitResource(TheDialog, id_Stat1));
New(Edit1, InitResource(TheDialog, id_Edit1));
New(List1, InitResource(TheDialog, id_List1));
New(Combo1, InitResource(TheDialog, id_Combo1));
New(Check1, InitResource(TheDialog, id_Check1));
New(Radio1, InitResource(TheDialog, id_Radio1));
New(Scroll1, InitResource(TheDialog, id_Scroll1));
TheDialog^.TranssferBuffer:=@TheBuffer;
end;

Для управляющих элементов, построенных с помощью
InitResource, механизм передачи разрешается автоматически.


B.Pascal 7 & Objects/OW - 233 -

Использование буфера передачи с окном
-----------------------------------------------------------------

Для случая окна с управляющими элементами используйте для
конструирования объектов управления в надлежащей последователь-
ности Init, а не InitResource. Другое отличие между диалогами и
окнами состоит в том, что механизм передачи по умолчанию для уп-
равляющих элементов окна запрещен. Для разрешения использования
механизма вызывается EnableTransfer:

constructor TSampleWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
inherited Init(AParent, ATitle);
Edit1 := New(PEdit, Init(@Self, id_Edit1, '', 10, 10,
100, 30, 40, False));
Edit1^.EnableTransfer;
end;

Чтобы явно исключить управляющий элемент из механизма пере-
дачи, вызовите после его создания метод DisableTransfer.

Передача данных
-----------------------------------------------------------------

В большинстве случаев передача данных в окно выполняется
автоматически, но в любой момент вы можете явно передать данные.

Передача данных в окно
-----------------------------------------------------------------

После создания окна или диалогового блока данные автомати-
чески. Для создания экранного элемента, представляющего оконный
объект, конструктор вызывает SetupWindow. Затем для загрузки дан-
ных из буфера передачи вызывается TransferData. Окно SetupWindow
интерактивно вызывает SetupWindow для каждого из дочерних окон,
так что дочерние окна имеют возможность передать свои данные.
Поскольку порождающее окно устанавливает свои дочерние окна в по-
рядке их построения, данные в буфере передачи должны следовать в
том же порядке.

Если объект управляющего элемента был построен с помощью
InitResource или если порождающее окно явным образом разрешило
межанизм передачи путем вызова для управляющего элемента
EnableTransfer, то методы управляющего объекта SetupWindow вызы-
вают TransferData.

Передача данных из диалогового окна
-----------------------------------------------------------------

Когда режимное диалоговое окно получает командной сообщение
с идентификатором управляющего элемента id_OK, оно автоматически
передает данные из управляющего элемента в буфер передачи. Обычно

это сообщение указывает, что пользователь для завершения диалога
щелкнул "мышью" на кнопке OK, так что диалог автоматически обнов-
ляет свой буфер передачи. Затем, если вы снова выполняете диалог,
диалоговый блок передает текущие данные в управляющие элементы.

Передача данных из окна
-----------------------------------------------------------------

Однако, вы можете явно передавать данные в любом направлении
в любой момент времени. Например, вы можете передать данные из
управляющих элементов окна или безрежимного диалога. Вы также мо-
жете сбросить состояния управляющих элементов, используя данные
буфера передачи, в ответ на щелчок "мышью" на кнопке Reset
(сброс). В обоих случаях используется метод TransferData. Конс-
танта tf_SetData обозначает передачу данных из буфера в управляю-
щий элемент, а константа tf_GetData - передачу в другом направле-
нии. Например, вы можете вызвать TransferData в методе Destroy
объекта окна:

procedure TSampleWindow.Destroy;
begin
TransferData(tf_GetData);
TWindow.Destroy;
end;

Поддержка передачи для специализированных управляющих элементов
-----------------------------------------------------------------

Вы можете изменить способ передачи данных для конкретного
управляющего элемента или включить новый управляющий элемент, оп-
ределенный вами в механизме передачи. В обоих случаях вам просто
нужно написать метод Transfer для вашего управляющего объекта,
который если установлен флаг tf_GetData копирует данные из управ-
ляющего элемента в место, задаваемое указателем. Если установлен
флаг tf_SetData, то просто скопируйте данные по заданному указа-
телю в управляющий элемент. Рассмотрим в качестве примера
TStatic.Transfer:

function TStatic.Transfer(DataPrt: Pointer;
TransferFlag: Word): Word;
begin
if TransferFlag = tf_GetData then
GetText(DataPrt, TextLen)
else if TransferFlag = tf_SetData then
SetText(DataPtr);
Transfer:=TextLen;
end;

Метод Transfer должен всегда возвращать число переданных
байт информации.

Пример программы: TranTest
-----------------------------------------------------------------

Основное окно программы TranTest воспроизводит режимный диа-

B.Pascal 7 & Objects/OW - 235 -

лог с полями, в которые пользователь вводит данные об имени и ад-
ресе. Буфер передачи используется для хранения этой информации и
отображения ее в управляющих элементах диалога при повторном его
выполнении. Обратите внимание на то, что нам не нужно определять
новый тип объекта диалога для установки и поиска данных диалога.
Также обратите ваше внимание на то, что мы непосредственно мани-
пулируем данными буфера передачи, поэтому статическое управление
при первом выводе диалога гласит "First Mailing Label" (первая
почтовая этикетка), а при всех остальных появлениях "Subsequent
Mailing Label" (следующая почтовая этикетка).

Примечание: Полный текст программы содержится в файле
TRANTEST.PAS на ваших дистрибутивных дискетах.



B.Pascal 7 & Objects/OW - 236 -

Использование специализированных управляющих элементов
-----------------------------------------------------------------

Windows обеспечивает механизм, позволяющий вам создавать
свои собственные виды управляющих элементов, а ObjectWindows об-
легчает создание объектов, использующих преимущества управляющих
элементов. В данном разделе обсуждается использование специализи-
рованных управляющих элементов Borland, которые придают своеоб-
разный вид работающим в Windows приложениям Borland, а затем опи-
сывается, как создавать свои собственные уникальные управляющие
элементы.

Специализированные управляющие элементы Borland для Windows
-----------------------------------------------------------------

Специализированные управляющие элементы Borland для Windows
(BWCC) обеспечивают выразительный внешний вид приложений Borland
для Windows. Основными средствами BWCC являются:

* командные кнопки с графическими изображениями;

* серый "рельефный" фон диалоговый блоков;

* трехмерные кнопки с зависимой и независимой фиксацией.

ObjectWindows дает вам возможность простого доступа к BWCC,
так что вы можете придать своим приложениями стандартный для
Borland вид.



B.Pascal 7 & Objects/OW - 237 -

Использование стандартных BWCC
-----------------------------------------------------------------

ObjectWindows позволяет легко добавлять BWCC в ваши приложе-
ния Windows. Нужно просто добавить модуль BWCC в оператор uses
программы:

uses BWCC;

Использование BWCC автоматически позволяет вам делать следу-
ющее:

* использовать загружаемые из ресурсов управляющие элементы
BWCC;

* создавать в вашей программе BWCC.

Например, с помощью пакета разработчика ресурсов Resource
WorkShop вы можете создать ресурсы диалоговых блоков, использую-
щие специализированные управляющие элементы Borland. Включение в
оператор uses программы модуля BWCC обеспечивает для вашей прог-
раммы информацию о том, где искать динамически компонуемую библи-
отеку (BWCC.DLL), содержащую код, который обеспечивает работу
BWCC.

Кроме того, после добавления модуля BWCC любые создаваемые в
программе объекты управляющих элементов будут иметь вид и харак-
теристики управляющих элементов Borland.

Средства BWCC
-----------------------------------------------------------------

BWCC добавляет к стандартным управляющим элементам в стиле
Windows некоторые новые стили, но подчиняется также новым согла-
шения и предположениям. Если вы создаете все свои новые управляю-
щие элементы из ресурсов, то беспокоиться об этом вам не нужно.
Однако при построении управляющих элементов в программном коде
вам может потребоваться использовать некоторые новые стили и сле-
довать соглашениям.



B.Pascal 7 & Objects/OW - 238 -

Расширение BWCC
-----------------------------------------------------------------

BWCC обеспечивает кнопки с графическими изображениями для
всех стандартных командных кнопок Windows. То есть, имеются гра-
фические изображения, предусмотренные для командных кнопок, для
которых Windows обеспечивает стандартный идентификатор: id_Abort,
id_Cancel, id_Ignore, id_No, id_Ok, id_Retry и id_Yes.

Создание кнопок с графическими изображениями

В своих приложениях вы можете обеспечить для командных кно-
пок собственные графические образы. Все что нужно предусмотреть -
это шесть ресурсов графических изображений (битовых массивов),
пронумерованных относительно идентификатора управляющего элемента
вашей командной кнопки. Например, если вы хотите создать графи-
ческую командную кнопку с идентификатором id_MyButton, то создае-
те ресурсы битовых массивов с идентификаторами ресурса 1000 +
id_MyButton, 2000 + id_MyButton, 3000 + id_MyButton, 4000 +
id_MyButton, 5000 + id_MyButton и 6000 + id_MyButton. Каждый
представляемый битовый массив показан в следующей таблице:

Ресурсы битовых массивов для командных кнопок BWCC Таблица 12.12
-------------------------T-----------------T--------------------¬
¦ Образ ¦ Идентификатор ¦ Идентификатор ¦
¦ ¦ ресурса VGA ¦ ресурса VGA ¦
+------------------------+-----------------+--------------------+
¦ Командная кнопка ¦ 1000 + идент. ¦ 2000 + идент. ¦
¦ в фокусе ¦ ¦ ¦
¦ ¦ ¦ ¦
+------------------------+-----------------+--------------------+
¦ Нажатая командная ¦ 3000 + идент. ¦ 4000 + идент. ¦
¦ кнопка ¦ ¦ ¦
¦ ¦ ¦ ¦
+------------------------+-----------------+--------------------+
¦ Командная кнопка ¦ 5000 + идент. ¦ 6000 + идент. ¦
¦ не в фокусе ¦ ¦ ¦
¦ ¦ ¦ ¦
L------------------------+-----------------+---------------------

Графические образы командных кнопок VGA должны иметь ширину
63 и высоту 39 элементов изображения. Графические образы команд-
ных кнопок EGA должны иметь ширину 63 и высоту 39 элементов
изображения.

B.Pascal 7 & Objects/OW - 239 -


Для текста следует использовать шрифт Helvetica размером 8
пунктов, а вокруг образа кнопки, находящейся в фокусе, следует
выводить рамку из точек. Набор графических изображений для ко-
мандных кнопок с идентификатором 201 показан на следующем рисун-
ке:

-------------------¬ -------------------¬ -------------------¬
¦------------------¦ ¦------------------¦ ¦------------------¦
¦-----------Add----¦ ¦-----------Add----¦ ¦-----------Add----¦
¦-----------Pen----¦ ¦-----------Pen----¦ ¦-----------Pen----¦
¦------------------¦ ¦------------------¦ ¦------------------¦
¦------------------¦ ¦------------------¦ ¦------------------¦
L------------------- L------------------- L-------------------
1201 3201 5201

-------------------¬ -------------------¬ -------------------¬
¦ ¦- ¦ ..... ¦ ¦ ..... -¦
¦ -- Add ¦- ¦ -- : Add : ¦ ¦ -- : Add : -¦
¦ ------ Pen ¦- ¦ ------ : Pen : ¦ ¦ ------ : Pen : -¦
¦ -- ¦- ¦ -- ..... ¦ ¦ -- ..... -¦
¦ ¦- ¦ ¦ ¦ -----------------¦
L-------------------- L------------------- L-------------------
-------------------
2201 4201 6201

Рис. 12.9 Графические ресурсы для командной кнопки BWCC с
идентификатором 201.

B.Pascal 7 & Objects/OW - 240 -

Создание ваших собственных
специализированных управляющих элементов
-----------------------------------------------------------------

Простейший способ создания специализированного управляющего
элемента состоит в фактическом создании окна, которое действует
как управляющий элемент, но вовсе не является окном. Этот подход
используется в программе Steps в Части 1 данного руководства. Тот
же используемый в программе Steps метод применяется для ее объек-
та палитры, который можно использовать, например, для создания
объекта инструментальной полосы. Таким "управляющие элементы" яв-
ляются наследниками TWindow, а не TControl, поскольку TControl
имеет дело только со стандартными управляющими элементами
Windows.

Другим стандартным способом создания специализированного уп-
равляющего элемента является построение в динамически компонуемой
библиотеке нового класса окон. После этого вы можете создавать
объекты ObjectWindows, использующие этот новый класс. Пакет раз-
работчика ресурсов также может использовать специализированные
управляющие элементы, созданные в DLL. Информацию об использова-
нии специализированных управляющих элементов в ресурсах диалого-
вых блоках вы можете найти в "Руководстве пользователя по пакету
разработчика ресурсов".

Примечание: О классах окон рассказывается в Главе 10.



B.Pascal 7 & Objects/OW - 241 -

------------------------------------------------------------------
Глава 13. Проверка допустимости данных
-----------------------------------------------------------------

ObjectWindows представляет вам несколько гибких способов
проверки допустимости информации, набираемой пользователем в уп-
равляющем элементе редактирования, путем связи объектов проверки
допустимости с объектами управляющих элементов редактирования.
Использование объектов проверки допустимости облегчает добавление
механизма проверки допустимости к существующим приложениям
ObjectWindows или для изменения способа проверки в поле его дан-
ных.

Данная глава охватывает следующие темы, относящиеся к про-
верке допустимости:

* Три вид проверки допустимости данных.

* Использование объектов проверки допустимости.

* Как работает проверка допустимости.

Проверка допустимости обрабатывается методом CanClose интер-
фейсных объектов. В любой момент вы можете проверить содержимое
любого конкретного управляющего элемента редактирования или экра-
на данных, вызвав метод CanClose объекта, но ObjectWindows пре-
дусматривает также механизм автоматизации проверки допустимости
данных. В большинстве случаев проверка допустимости данных прак-
тически не требует от программиста никаких усилий.

Три вида проверки допустимости данных
-----------------------------------------------------------------

Существует три различных типа проверки допустимости данных,
и ObjectWindows поддерживает их по-разному. Этими тремя видами
являются:

* Фильтрация ввода.

* Проверка допустимости каждого элемента.

* Проверка допустимости полных экранов.

Заметим, что эти методы не являются взаимно-исключающими.
Ряд стандартных средств проверки допустимости могут комбинировать
в одном механизме проверки допустимости различные методы.

Важно запомнить, что проверка допустимости выполняется объ-
ектом проверки допустимости, а не объектом управляющего элемента
редактирования. Если вы уже создали для особого назначения специ-
ализированный управляющий элемент редактирования, то возможно
сдублировали возможность, встроенную в управляющие элементы ре-
дактирования и их средства проверки допустимости.


B.Pascal 7 & Objects/OW - 242 -

В разделе данной главы "Как работают средства проверки до-
пустимости" описываются различные способы, с помощью которых объ-
екты управляющего элемента редактирования автоматически вызывают
объекты проверки допустимости.

Фильтрация ввода
-----------------------------------------------------------------

Простейший способ обеспечения включения в поле только допус-
тимых данных состоит в обеспечении ввода пользователем только до-
пустимых данных. Например, числовое поле ввода может быть ограни-
чено вводом пользователем только цифровых данных.

Объект фильтра проверки допустимости ObjectWindows представ-
ляет общий механизм, ограничивающий вид символов, которые пользо-
ватель может вводить в данном управляющем элементе редактирова-
ния. Объекты проверки допустимости рисунков могут также контроли-
ровать форматирование и типы символов, которые может набирать
пользователь.

Проверка допустимости каждого поля
-----------------------------------------------------------------

Иногда удобно гарантировать, чтобы пользователь обеспечивал
для конкретного поля допустимый ввод перед переходом к следующему
полю. Этот подход часто называют "проверкой допустимости по табу-
ляции", поскольку переход в новое поле обычно выполняется по кла-
више Tab.

В качестве примера можно привести приложение, которое выпол-
няет поиск в базе данных, где пользователь вводит в поле некото-
рые виды ключевой информации, а приложение отвечает на это считы-
ванием соответствующей записи и фильтрацией остальных полей. В
таком случае вашему приложению перед действием по клавише требу-
ется проверка, что пользователь набрал в этом ключевом поле пра-
вильную информацию.

Проверка допустимости полных экранов
-----------------------------------------------------------------

Проверить допустимость полных экранов вы можете тремя раз-
личными способами:

* Проверкой допустимости режимных окон.
* Проверкой допустимости при изменении фокуса.
* Проверкой допустимости по запросу.

Проверка допустимости режимных окон

Когда пользователь закрывает режимное окно, оно перед закры-
тием автоматически проверяет допустимость всех своих подобластей
просмотра (если закрывающей командой не была cmCancel). Для про-
верки допустимости всех подобластей окно вызывает метод CanClose

B.Pascal 7 & Objects/OW - 243 -

каждой подобласти, и если каждый из них возвращает True, то окно
можно закрыть. Если любая из подобластей возвращает значение
False, то окно закрыть нельзя.

Пока пользователь не обеспечит допустимые данные, режимное
окно с недопустимыми данными можно только отменить.

Проверка допустимости по запросу

В любой момент вы можете указать окну на необходимость про-
верки всех его подокон путем вызова метода CanClose. CanClose по
существу спрашивает окно "Если сейчас будет дана команда закры-
тия, являются ли все поля допустимыми?" Окно вызывает методы
CanClose всех своих дочерних окон в порядке включения и возвраща-
ет True, если все они возвращают значение True.

Вызов CanClose не обязывает вас фактически закрывать окно.
Например, вы можете вызвать CanClose, когда пользователь "нажима-
ет" командную кнопку Save (Сохранение), обеспечивая проверку до-
пустимости данных перед их сохранением.

Вы можете проверить любое окно (режимное или безрежимное) и
в любое время. Однако автоматическую проверку допустимости при
закрытии имеют только режимные окна. Если вы используете безре-
жимные окна ввода данных, то нужно обеспечить, чтобы приложение
перед выполнением действий с введенными данными вызывало метод
CanClose окна.

Использование механизма проверки допустимости данных
-----------------------------------------------------------------

Использование объекта проверки допустимости данных с управ-
ляющим элементом редактирования требует двух шагов:

* Построение объекта проверки допустимости.
* Присваивание объекта проверки допустимости управляющему
элементу редактирования.

После того, как вы построите объект проверки допустимости и
свяжите его с управляющим элементом редактирования, вам не потре-
буется взаимодействовать с ним непосредственно. Управляющий эле-
мент редактирования знает, когда вызывать методы проверки допус-
тимости и в какие моменты.



B.Pascal 7 & Objects/OW - 244 -

Построение объектов проверки допустимости
-----------------------------------------------------------------

Так как объекты проверки допустимости не являются интерфейс-
ными объектами, их конструкторам требуется только информация,
достаточная для установки критерия проверки допустимости. Напри-
мер, объект проверки допустимости числового диапазона воспринима-
ет два параметра - минимальное и максимальное значения в допусти-
мом диапазоне:

constructor TRangeValidator.Init(AMin, AMax: Integer);

Добавление к управляющим элементам
редактирования средств проверки допустимости
-----------------------------------------------------------------

Каждый управляющий элемент редактирования имеет поле с име-
нем Validator, установленное по умолчанию в nil, которое может
указывать на объект проверки допустимости. Если вы не присваивае-
те объекта полю Validator, то управляющий элемент редактирования
ведет себя так, как описано в Главе 12. После присваивания с по-
мощью вызова SetValidator объекта проверки допустимости управляю-
щий элемент редактирования автоматически проверяется им при обра-
ботке основных событий и при самом вызове для проверки допусти-
мости.

Обычно, как показано ниже, объект проверки допустимости
строится и присваивается в одном операторе:

.
. { создание трехсимвольного управляющего элемента редакти-
. рования }
Ed := New(PEdit, Init(@Self, id_Me, '', 10, 10, 50, 30, 3,
False));
Ed^.SetValidator(New(PRangeValidator, Init(100, 999)));
.
.
.

Как работает проверка допустимости
-----------------------------------------------------------------

В ObjectWindows предусмотрено несколько видов объектов про-
верки допустимости, которые должны охватывать большинство ваших
потребностей по проверке данных. Из абстрактных типов проверки
допустимости вы можете также построить свои собственные произ-
водные типы.

В данном разделе освещаются следующие темы:

* Виртуальные методы объекта проверки допустимости.
* Стандартные типы объекта проверки допустимости.


B.Pascal 7 & Objects/OW - 245 -

Методы объекта проверки допустимости
-----------------------------------------------------------------

Каждый объект проверки допустимости наследует от абстрактно-
го объектного типа TValidator четыре важных метода. Различным
образом переопределяя эти методы, наследующие объекты проверки
допустимости выполняют свои конкретные задачи по проверке. Если
вы собираетесь модифицировать стандартные объекты проверки допус-
тимости или написать собственные объекты проверки допустимости,
то нужно понимать, что делает каждый из этих методов и как их ис-
пользуют управляющие элементы редактирования.

Этими четырьмя методами являются следующие:

* Valid

* IsValid

* IsValidInput

* Error

Единственными методами, вызываемыми вне объекта, являются
Valid и IsValidInput. Error и IsValid - единственные методы, вы-
зываемые другими методами объекта проверки допустимости.

Проверка допустимости данных
-----------------------------------------------------------------

Основным внешним интерфейсом с объектами проверки допусти-
мости данных является метод Valid. Аналогично методу CanClose ин-
терфейсных объектов, Valid представляет собой булевскую функцию,
которая возвращает значение True, если переданная ей строка со-
держит допустимые данные. Один из компонентов метода CanClose уп-
равляющего элемента редактирования является вызов метода Valid с
переданным ему текущим текстом управляющего элемента редактирова-
ния.

При использовании средств проверки допустимости с управляю-
щими элементами редактирования вам никогда не требуется вызывать
или переопределять метод Valid объекта проверки допустимости. По
умолчанию Valid возвращает True, если возвращает True метод
IsValid. В противном случае для уведомления пользователя об ошиб-
ке и возврата значения False вызывается Error.

B.Pascal 7 & Objects/OW - 246 -

Проверка полной строки
-----------------------------------------------------------------

Объекты проверки допустимости содержат виртуальный метод
IsValid, который воспринимает в качестве единственного аргумента
строку и возвращает True, если строка представляет допустимые
данные. IsValid - это метод, который выполняет фактическую про-
верку допустимости, так что если вы создаете собственные объекты
проверки допустимости, то почти всегда переопределяете IsValid.

Заметим, что метод IsValid не вызывается вами явно. Исполь-
зуйте для вызова IsValid метод Valid, так как для уведомления
пользователя в случае возврата методом IsValid значения False
Valid вызывает метод Error. Не путайте также проверку допустимос-
ти сообщением об ошибке.

Проверка допустимости нажатий клавиш
-----------------------------------------------------------------

Когда объект управляющего элемента редактирования получает
имеющее для него значение событие нажатия клавиши, он вызывает
метод IsValidInput объекта проверки допустимости. По умолчанию
методы IsValid всегда возвращают True. Это означает, что воспри-
нимаются все нажатия клавиш. Однако, наследующие объекты проверки
допустимости могут переопределять метод IsValidInput, чтобы от-
фильтровывать нежелательные нажатия клавиш.

Например, средства проверки допустимости диапазона, которые
используются для числового ввода, возвращают из IsValidInput True
только для цифр и символов '+' и '-'.

IsValidInput воспринимает два параметра. Первый параметр -
это параметр-переменная, содержащая текущий текст ввода. Второй
параметр - это булевское значение, указывающее, следует ли перед
попыткой проверки допустимости применять к строке ввода дополне-
ние или заполнение. TPictureValidator - это единственный из стан-
дартных объектов проверки допустимости, использующий второй пара-
метр.

Сообщение о недопустимых данных
-----------------------------------------------------------------

Виртуальный метод Error уведомляет пользователя, что содер-
жимое управляющего элемента редактирования не прошло проверку до-
пустимости. Стандартные объекты проверки допустимости в общем
случае представляет простой блок сообщения, уведомляющий пользо-
вателя, что содержимое ввода недопустимо, и описывающее, каким
должен быть правильный ввод.

Например, метод Error для проверки допустимости диапазона
создает блок сообщения, указывающий, что значение в управляющем
элементе редактирования не находится между указанными минимальным
и максимальным значениями.

B.Pascal 7 & Objects/OW - 247 -


Хотя большинство объектов проверки допустимости переопреде-
ляют Error, вам не следует вызывать его непосредственно. Метод
Error вызывается методом Valid, если IsValid возвращает False
(что является единственным моментом, когда необходимо вызывать
Error).



B.Pascal 7 & Objects/OW - 248 -

Стандартные средства проверки допустимости
-----------------------------------------------------------------

ObjectWindows включает в себя шесть стандартных типов объек-
тов проверки допустимости, включая абстрактный объект проверки
допустимости и следующие пять специальных типов таких объектов:

* Фильтрация.
* Проверка диапазона.
* Проверка допустимости с просмотром.
* Проверка допустимости с просмотром строк.
* Проверка допустимости с просмотром шаблонов.

Абстрактный объект проверки допустимости
-----------------------------------------------------------------

Абстрактный тип TValidator служит базовым типом для всех
объектов проверки допустимости, но сам по себе он не делает ниче-
го полезного. По существу, TValidator - это объект проверки до-
пустимости, для которого всегда допустим любой ввод: IsValid и
IsValidInput возвращают True, а Error не выполняет никаких функ-
ций. Наследующие типы переопределяют IsValid и/или IsValidInput
для фактического определения того, какие значения являются допус-
тимыми.

Если никакие из других объектных типов проверки допустимости
не годятся в качестве исходных, вы можете использовать TValidator
в качестве отправной точки собственных объектов проверки допусти-
мости.

Фильтрация
-----------------------------------------------------------------

Фильтрующие объекты проверки допустимости - это простая реа-
лизация средств проверки допустимости, при которой проверяется
только набираемый пользователем ввод. Конструктор фильтрующего
объекта проверки допустимости воспринимает один параметр - набор
допустимых символов:

constructor TFilterValidator.Init(AValidChars: TCharSet);

TFilterValidator переопределяет IsValidInput для возврата
True только в том случае, если все символы в текущей строке ввода
содержатся в наборе символов, переданных конструктору. Управляю-
щие элементы редактирования включают символы только в том случае,
если IsValidInput возвращает True, так что нет необходимости пе-
реопределять IsValid. Поскольку символы проходят через фильтр
ввода, полная строка допустима по определению.

Потомки TFilterValidator, такие как TRAngeValidator, могут
сочетать фильтрацию ввода с другими проверками завершенной стро-
ки.


B.Pascal 7 & Objects/OW - 249 -

Проверка диапазона
-----------------------------------------------------------------

Объект проверки допустимости диапазона TRangeVaidator - это
потомок TFilterValidator, которые воспринимают только числа и до-
бавляют к итоговым результатам проверку диапазона. Конструктор
воспринимает два параметра, определяющим минимальное и максималь-
ное допустимое значение:

constructor TRangeValidator.Init(AMin, AMax: Integer);

Объект проверки допустимости диапазона сам строит числовое
средство проверки-фильтрации, воспринимающее только цифры
'0'..'9' и символы плюса и минуса. Таким образом, наследуемый
IsValidInput обеспечивает отфильтрацию только цифр. Затем
TRangeValidator переопределяет IsValid, чтобы он возвращал True
только если введенные числа находятся в допустимом диапазоне, оп-
ределяемом в конструкторе. Метод Error выводит блок сообщения,
указывающий, что введенное значение находится вне диапазона.

Проверка допустимости с просмотром
-----------------------------------------------------------------

Абстрактный объект проверки допустимости с просмотром
TLookupValidator обеспечивает основу для общего типа объекта про-
верки допустимости, который для определения допустимости сравни-
вает введенное значение со списком воспринимаемый элементов.

TLookupValidator - это абстрактный тип, который никогда не
используется сам по себе, но служит важным изменением и дополне-
нием к стандартному объекту проверки допустимости.

Примечание: Пример работы такого объектного типа вы
можете найти в разделе по преобразованию строк.

Новый метод, вводимый объектом TLookupValidator называется
Lookup. По умолчанию Lookup возвращает значение False, но при об-
разовании производного абстрактного объекта проверки допустимости
c просмотром вы можете переопределить Lookup для сравнения пере-
данной строки со списком и возвращать True, если строка содержит
допустимую запись.

TLookupValidator переопределяет IsValid для возврата True
только если Lookup также возвращает True. В наследующих типах
проверки допустимости с просмотром вам следует переопределять не
IsValid, а Lookup.

Просмотр строк
-----------------------------------------------------------------

Рабочий пример объекта проверки допустимости с преобразова-
нием представляет TStringLookupValidator, сравнивающий переданную
из управляющего элемента редактирования строку с элементами в

B.Pascal 7 & Objects/OW - 250 -

списке строк. Если переданная строка содержится в списке, метод
объекта проверки допустимости с просмотром строки возвращает
True. Конструктор воспринимает только один параметр - список до-
пустимых строк:

constructor TStringLookupValidator.Init(AString:
PStringCollection);

Чтобы после построения объекта проверки допустимости с прос-
мотром использовать другой список строк, передайте новый список
методу NewStringList объекта проверки допустимости (который унич-
тожает старый список и задает новый).

TStringLookupValidator переопределяет методы Lookup и Error,
так что Lookup возвращает True, если переданная строка содержится
в наборе строк, а Error выводит на экран блок сообщения, указыва-
ющий, что строка отсутствует в списке.

Проверка допустимости по шаблону
-----------------------------------------------------------------

Объекты проверки допустимости с шаблоном сравнивают строки,
набранные пользователем, с шаблоном, описывающим формат допусти-
мого ввода. Применяемые шаблоны совместимы с теми, которые ис-
пользуются для контроля ввода в реляционной базе данных Paradox
фирмы Borland. При построении объекта проверки допустимости по
шаблону используется два параметра: строка, содержащая образ шаб-
лона, и булевское значение, указывающее, нужно ли заполнять по
шаблону литеральные строки.

Примечание: Синтаксис шаблонов описывается в справоч-
нике. См. TPXPictureValidator.Picture.

constuctor TPictureValidator.Init(const APic: String;
AAutoFill: Boolean);

TPictureValidator переопределяет Error, IsValidInput и
IsValid и добавляет новый метод Picture. Изменения в Error и
IsValid просты: Error выводит на экран блок сообщения, указываю-
щий, какой формат должна иметь строка, а IsValid возвращает True
только если True возвращается функцией Picture, позволяя получать
новые производные типы проверки допустимости по шаблону путем пе-
реопределения только метода Picture. IsValidInput проверяет сим-
волы по мере набора их пользователем, допуская только те символы,
которые разрешены в шаблоне формата, и возможно дополняя лите-
ральные символы из шаблона.

Метод Picture пытается сформатировать заданную строку ввода
в соответствии с шаблоном формата и возвращает значение, указы-
вающее степень успеха: полный, неполный или ошибка.


B.Pascal 7 & Objects/OW - 251 -

------------------------------------------------------------------
Глава 14. Объекты MDI
-----------------------------------------------------------------
Многодокументальный интерфейс (MDI) - это стандарт интерфей-
са для приложений Windows, которые позволяют пользователю однов-
ременно работать с несколькими открытыми документами. Документ, в
этом смысле, это обычно связанная с файлом задача, например, ре-
дактирование текстового файла или работа с файлом электронной
таблицы. В приложениях MDI пользователь может, например, иметь
несколько открытых файлов в одном приложении. Возможно, что вы
уже использовали приложения MDI: Microsoft Exel, администратор
программ Windows, администратор файлов Windows. Стандарт MDI яв-
ляется также частью спецификации общего доступа пользователя
(CUA) фирмы IBM.
ObjectWindows предусматривает объекты, позволяющие легко пи-
сать приложения MDI.

Что такое приложение MDI?
-----------------------------------------------------------------
Имеются определенные компоненты, которые присутствуют в каж-
дом приложении MDI. Чаще всего основное окно вызывает окно с рам-
кой. В области клиента окна-рамки есть невидимое окно - окно кли-
ента MDI - которое содержит дочернее окно, вызывающее дочерние
окна MDI. Это очень важно, т.к. обработка дочерних окон MDI про-
исходит скрытно от пользователя.
------------------------------------------------------------T-T-¬
¦-=---------------------MDI Conformist----------------------¦^¦v¦
+-----------------------------------------------------------+-+-+
¦ MDI Children ¦
+---------------------------------------------------------------+
¦----------------------------------------------------¬ ¦
¦¦------------------Child #1-------------------------¦ ¦
¦+---------------------------------------------------+ ¦
¦¦ -------------------------------------------------------¬ ¦
¦¦ ¦ X¦--------------------Child #2-----------------------¦ ¦
¦¦ L--+---------------------------------------------------+ ¦
¦¦ ¦ ---------------------------------------------------T-T-¬¦
¦¦ ¦ ¦ X¦-=-----------------Child #3--------------------¦^¦v¦¦
¦L----¦ L--+-----------------------------------------------+-+-+¦
¦ ¦ ¦ ----¬ ^ ¦¦
¦ ¦ ¦ ¦ X ¦ CanClose блоки минимизации и ---- ¦¦
¦ L----¦ L---- максимизации ¦¦
¦ ¦ ^ ¦¦
¦ L-----------------------------+----------------------¦
¦ дочернее окно MDI -- ¦
¦ ----¬ ¦
¦ ¦ <+--- пиктограмма ¦
¦ L---- ¦
¦ Child #4 ^ ¦
L----------------------------+-----------------------------------
¦ ^
окно клиента MDI -- ¦
окно-рамка MDI --
Рис. 14.1 Компоненты приложения MDI.

B.Pascal 7 & Objects/OW - 252 -

Меню дочернего окна
-----------------------------------------------------------------

Строка меню окна-рамки содержит меню, управляющее дочерними
окнами MDI. Меню дочернего окна содержит такие элементы как Tile
(Вывод без перекрытия), Cascade (Вывод с перекрытием), Arrange
(Упорядочить) и Close All (Закрыть все). Имя каждого открытого
окна MDI автоматически добавляется к концу этого меню с выбором
текущего окна.

Дочерние окна MDI
-----------------------------------------------------------------

Каждое дочернее окно MDI имеет некоторые характеристики пе-
рекрывающего окна. Его можно максимизировать до полного размера
окна клиента MDI или минимизировать в пиктограмму, которая поме-
щается к нижней границе окна-рамки. Дочернее окно MDI никогда не
выходит за границы его окна-рамки (обрамляющего окна). Дочернее
окно MDI не может иметь меню, поэтому все его функции реализуются
меню окна-рамки. Заголовок каждого дочернего окна MDI часто
представляет собой имя открытого файла, связанного с этим окном,
хотя его поведение заранее неизвестно и определяется программой.
Можно рассматривать приложение MDI как мини-сеанс Windows, когда
несколько приложений представлены окнами или пиктограммами.

Окна MDI в ObjectWindows
-----------------------------------------------------------------

ObjectWindows определяет типы для представления рамок MDI и
клиентов MDI. Это соответственно TMDIWindow и TMDIClient.
TMDIWindow является производным от TWindow, но TMDIClient на са-
мом деле представляет собой управляющий элемент и является произ-
водным от TControl. В приложении MDI ObjectWindows, окно-рамки
владеет своим окном клиента MDI и хранит его в поле ClientWnd.
Окно-рамка также содержит каждое из дочерних окон MDI в связанном
списке ChildList. Дочерние окна MDI являются экземплярами типа
объекта, производного от написанного вами TWindow.

Методы TMDIWindow занимаются в основном конструированием и
управлением дочерними окнами MDI, окном клиента MDI и обработкой
выбора в меню. Главная работа TMDIClient происходит скрытно от
пользователя и состоит в управлении дочерними окнами MDI. При
разработке приложений MDI вы в общем случае будете создавать но-
вые производные типы для своих рамок и дочерних окон соответс-
твенно от TMDIWindow и TWindow.

Построение приложения MDI
-----------------------------------------------------------------

Построение приложения MDI в ObjectWindows представляет собой
относительно простую задачу:

* Построение основного окна MDI.

B.Pascal 7 & Objects/OW - 253 -


* Установка меню дочернего окна.

* Предоставление основному окну возможности создания дочер-
них MDI.

Окно MDI обрабатывает для вас все специфические функции, а
ваши функции, специфические для приложения, могут перейти в до-
черние окна.

Построение рамки MDI
-----------------------------------------------------------------

Окно-рамка MDI всегда является основным окном приложения,
поэтому оно конструируется в методе InitMainWindow его объекта
приложения. Однако, существует два аспекта рамки MDI, которые от-
личают его от других основных окон:

* Рамка MDI всегда является основным окном, поэтому оно ни-
когда не имеет порождающего окна. Таким образом,
TMDIWindow.Init нет необходимости воспринимать в качестве
параметра указатель порождающего окна.

* Окно-рамка MDI всегда должно иметь меню, так что вторым
параметром Init является описатель меню. Для основных
окон, отличных от MDI и производных от TWindows, вы опре-
деляете Init для установки Attr.Menu в допустимый описа-
тель меню. TMDIWindow.Init устанавливает для вас AttrMenu.

Типичный метод InitMainWindow для приложения MDI может выг-
лядеть следующим образом:

procedure TMDIApplication.InitMainWindow;
begin
MainWindow := New(PMyFrame, Init('Заголовок рамки',
LoadMenu(HInstance, 'MenuName'));

Если предположить, что TMyFrame - это потомок TMDIWindow,
при этом будет создаваться окно-рамка MDI с заголовком "Заголовок
рамки" и строкой меню, заданной ресурсом "MenuName".

Создание меню дочерних окон
-----------------------------------------------------------------

Меню окна-рамки должно включать в себя меню дочернего окна в
стиле MDI. Открытие дочернего окна MDI добавляет его заголовок к
меню дочернего окна, а закрытие дочернего окна удаляет его из
списка. Это позволяет пользователю активизировать любое дочернее
окно, даже если оно не является видимым.

Окно-рамка должно знать, каким элементом меню верхнего уров-
ня является меню его дочернего окна. Объект TMDIWindow хранит це-
лое значение позиции в поле объекта ChildMenuPos. TMDIWindow.Init

B.Pascal 7 & Objects/OW - 254 -

первоначально устанавливает ChildMenuPos в ноль, указывая край-
ний левый элемент меню верхнего уровня. Однако, для установки по-
зиции ChildMenuPos вы можете переопределить Init для своего про-
изводного от TMDIWindow типа:

constructor TMyMDIWindow.Init(ATitle: PChar; AMenu: HMenu);
begin
inherited Init(ATitle, AMenu);
ChildMenuPos := 1;
end;

TMDIWindow.Init также вызывает InitClientWindow для констру-
ирования объекта TMDIClient, который будет служит его окном кли-
ента MDI. TMDIWindow.SetupWindow создает окно клиента MDI.

Создание дочерних окон MDI
-----------------------------------------------------------------

TMDIWindow определяет автоматический метод реакции
CreateChild, который вызывается при выборе из меню варианта, ре-
зультатом которого будет команда с идентификатором Create_Child.
Обычно этот вариант меню называется New или Create. Как это опре-
делено в TMDIWindow, CreateChild конструирует и создает дочернее
окно MDI типа TWindow вызовом TMDIWindow.InitChild. Для задания
корректного типа дочернего окна (производного от TWindow), пере-
определим InitChild для вашего типа окна-рамки MDI:

function MyMDIWindow.InitChild: PWindowsObject;
begin
InitChild:=New(PMyChild, Init(@Self,
'Новое дочернее окно'));
end;

Автоматические дочерние окна
-----------------------------------------------------------------

Может потребоваться, чтобы ваша окно-рамка воспроизводило
только одно дочернее окно MDI при своем первом появлении. Для
этого первого дочернего окна вы можете явно задать его размер. В
отличие от других дочерних окон, дочерние окна MDI должны быть
сконструированы и созданы в методе SetupWindow окна-рамки MDI, а
не в Init. Вы также должны явно создать экранный элемент дочерне-
го окна с помощью вызова MakeWindow:

procedure MyMDIWindow.SetupWindow;
var
ARect: TRect;
NewChild: PMyChild;
begin
TMDIWindow.SetupWindow;
NewChild:=PMyChild(InitChild);
GetClientRect(HWindow, ARect);
with NewChild^.Attr, ARect do

B.Pascal 7 & Objects/OW - 255 -

begin
W:=(right*4) div 5;
H:=(bottom*3) div 5;
Title:='Child #1';
end;
Application^.MakeWindow(NewChild);
end;

В некоторых приложениях вам может потребоваться создать до-
чернее окно MDI в ответ на более чем один выбор в меню. Например,
пункты меню New и Open в редакторе файла могут приводить к воз-
никновению нового дочернего окна с заголовком в виде имени файла.
В этом случае определите для построения дочернего окна методы ав-
томатической реакции. ObjectWindows определяет команды
cm_MDIFileOpen и cm_MDIFileNew, что облегчает дифференциацию от
стандартных cm_FileOpen и cm_FileNew.

Управление дочерним окном MDI
-----------------------------------------------------------------

Тип окна MDI в ObjectWindows содержит методы манипулирования
дочерними окнами MDI приложения MDI. Хотя большая часть скрытой
работы делается в TMDIClient, доступ к данным и функциям происхо-
дит через метод TMDIWindow.

TMDIWindow определяет методы реакции на сообщения Windows,
которые автоматически реагируют на выбор команды стандартного ме-
ню MDI: Title, Cascade, Arrange Icon и Close All. Эти методы ожи-
дают основанных на командах сообщений с заранее определенными
константами идентификаторов меню. Обязательно используйте эти
идентификаторы при построении ресурса меню дочернего окна:

Стандартные методы, команды и действия MDI Таблица 14.1
-----------------T------------------------T---------------------¬
¦ Действие ¦ Константа ID меню ¦ Метод TMDIWindow ¦
+----------------+------------------------+---------------------+
¦ Tile ¦ cm_TileChildren ¦ CM_TileChildren ¦
¦ Cascade ¦ cm_CascadeChildren ¦ CM_CascadeChildren ¦
¦ Arrange Icons¦ cm_ArrangeChildIcons ¦ CM_ArrangeChildIcons¦
¦ Close All ¦ cm_CloseChildren ¦ CM_CloseChildren ¦
L----------------+------------------------+----------------------

Методы реакции TMDIWindows, подобные CMTileChildren, вызыва-
ют другие методы TMDIWindows, такие как CMChildren. Эти методы
вызывают методы TMDIClient с тем же именем, например,
TMDIClient^.TileChildren. Для переопределения такого автоматичес-
кого поведения нужно переопределить TMDIWindow.TileChildren или
другой метод TMDIWindow. Для дочерних окон MDI не подходит реаги-
рование на основанные на командах сообщения, генерируемые меню
дочернего окна.



B.Pascal 7 & Objects/OW - 256 -

Настройка активизации дочернего окна
-----------------------------------------------------------------

Пользователь приложения MDI может свободно активизировать
любое открытое или минимизировать дочернее окно MDI. Однако, вам
может потребоваться предпринять некоторые действия, когда пользо-
ватель дезактивирует одно дочернее окно активизацией другого.
Например, меню окна-рамки может отражать текущее состояние актив-
ного дочернего окна, выделяя его цветом. Каждый раз, когда дочер-
нее окно становится активным или неактивным, оно получает сообще-
ние Windows wm_MDIActivate. Определив метод реакции на это с об-
щение для дочернего окна, вы можете отслеживать, какое дочернее
окно активно и соответственно реагировать.

Обработка сообщений в приложении MDI
-----------------------------------------------------------------

Как и для обычных порождающих и дочерних окон, основанные на
командах и дочерних идентификаторах сообщения Windows сначала
поступают в дочерние окна для их восприятия и обработки. Затем
сообщения поступают в порождающее окно. Однако, в случае приложе-
ния MDI сообщения поступают к текущему дочернему окну MDI, затем
к окну клиента MDI, и, наконец, к окну-рамке MDI (которое являет-
ся порождающим окном для всех дочерних окон MDI). Следовательно,
меню окна-рамки можно использовать для управления работой в теку-
щем активном дочернем окне MDI. Затем шанс отреагировать получают
окно клиента и окно-рамка.

Пример приложения MDI
-----------------------------------------------------------------

Программа MDITest создает приложение MDI, показанное на
Рис. 14.1. Полный текст файла MDITEST.PAS содержится на ваших
дистрибутивных дискетах.



B.Pascal 7 & Objects/OW - 257 -

------------------------------------------------------------------
Глава 15. Объекты печати
-----------------------------------------------------------------

Один из наиболее трудных аспектов программирования в Windows
- это вывод на принтер. ObjectWindows путем использования ряда
модулей и инкапсуляции поведения устройства печати и самой распе-
чатки делает печать более простой.

Данная глава описывает основы процесса печати, после чего
описываются следующие задачи:

* Построение объекта принтера.

* Создание распечатки.

- Печать документа.

- Печать содержимого окна.

* Передача распечатки на принтер.

* Выбор другого принтера.

* Настройка конфигурации принтера.

Почему печать представляет трудности?
-----------------------------------------------------------------

С одной стороны печать в Windows достаточно проста. Вы може-
те использовать для генерации распечатки те же функции GDI, что
используются для вывода образов на экран. Для вывода текста ис-
пользуется функция TextOut, а для вычерчивания прямоугольника -
Rectangle.

С другой стороны, процесс осложняется, так как Windows тре-
бует непосредственного "общения" с драйверами принтера через вы-
зовы Escape или получения адреса DeviceMode или ExtDeviceMode.
Это еще более осложняется требованием Windows, чтобы приложение
считывало имя драйвера устройства из файла WIN.INI. Кроме того,
устройства печати обладают большими возможностями проверки допус-
тимости и возможностями разрешения, чем видеоустройства.

ObjectWindows не может преодолеть все препятствия на этом
пути, но делает процесс печати более простым и легким для понима-
ния.

Печать в ObjectWindows
-----------------------------------------------------------------

Модуль ObjectWindows OPrinter предусматривает для упрощения
печати два объекта - TPrinter и TPrintout. TPrinter инкапсулирует
доступ к устройствам печати. Он предоставляет возможность конфи-
гурирования принтера, выводя диалог, в котором пользователь может

B.Pascal 7 & Objects/OW - 258 -

выбрать нужный принтер, а также установить параметры печати, та-
кие как графическое разрешение или ориентация (горизонтальная и
вертикальная) печати.

TPtintout инкапсулирует задачу печати документа. К принтеру
этот объект имеет такое же отношение, как TWindow - к экрану. Ри-
сование на экране выполняется методом Paint объекта TWindow, а
печать на принтере - методом PrintPage объекта TPrintout. Чтобы
напечатать что-то на принтере, приложение должно передать методу
Print объекта TPrinter экземпляр TPrintout.

Построение объекта принтера
-----------------------------------------------------------------

В большинстве случаев приложению требуется в каждый момент
времени доступ только к одному принтеру. Простейшим способом реа-
лизации этого является задание в объекте основного окна поля с
именем Printer (типа PPrinter), которые другие объекты в програм-
ме вызывают для целей печати. Чтобы сделать принтер доступным,
поле Printer должно указывать на экземпляр TPrinter.

В большинстве приложений это просто. Основное окно приложе-
ния инициализирует объект принтера, который использует заданный
по умолчанию принтер, указанный в WIN.INI:

constructor TSomeWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
Inherited Init(AParent, ATitle);
.
.
.
Printer := New(PPrinter, Init);
end;

В некоторых случаях может возникать необходимость использо-
вать в приложении различные принтеры одновременно из различных
окон. В этом случае постройте объект принтера в конструкторах
каждого такого окна, затем смените устройство принтера на один
или более таких принтеров. Если программа использует различные
принтеры, но не одновременно, то возможно лучше использовать один
и тот же объект принтера и при необходимости выбирать различные
принтеры.

Хотя вы можете сомневаться насчет переопределения конструк-
тора TPrinter для использования принтера, отличного от заданного
в системе по умолчанию, рекомендуемой процедурой является исполь-
зование конструктора по умолчанию, а затем смена связанного с
объектом устройства. См. раздел "Выбор другого принтера".



B.Pascal 7 & Objects/OW - 259 -

Создание распечатки
-----------------------------------------------------------------

Единственной "хитрой" частью процесса печати в ObjectWindows
являются создание распечатки. Этот процесс аналогичен написанию
метода Paint для объекта окна: вы используете графические функции
Windows для генерации в контексте устройства нужного графического
образа. Контекст устройства оконного объекта обрабатывает ваши
взаимодействия с устройством экрана; аналогичным образом контекст
устройства распечатки изолирует вас от устройства печати.

Примечание: Графические функции Windows поясняются в
Главе 17.

Чтобы создать объект распечатки, постройте новый тип, произ-
водный от TPtintout, который переопределяет PrintPage. В очень
простых случаях это все, что требуется сделать. Если документ
имеет размер более одной страницы, то вам нужно также переопреде-
лить HasNextPage для возврата True. Текущий номер страницы пере-
дается в качестве параметра PrintPage.

Объект распечатки имеет поля, содержащие размер страницы и
контекст устройства, который уже инициализирован для принтера.
Объект принтера устанавливает значения, вызывая метод объекта
распечатки SetPrintParams. Используйте контекст устройства объек-
та распечатки в любом вызове графических функций Windows.

Модуль OPrinter включает в себя два специализированных объ-
екта распечатки, которые показывают диапазон сложности распеча-
ток. Объект TWindowPrintout, печатающий содержимое окна, очень
прост. TEditPrintout, который печатает содержимое управляющего
элемента редактирования, очень сложен, так как имеет множество
возможностей.

Печать документа
-----------------------------------------------------------------

Windows рассматривает распечатку как последовательность
страниц, поэтому задачей вашего объекта распечатки является прев-
ращение документа в последовательность станичных образов для пе-
чати Windows. Аналогично тому как оконные объекты используются
для отображения образов на экране дисплея, объекты распечатки ис-
пользуются для печати образов на принтере.

ObjectWindows предусматривает абстрактный объект распечатки
TPrintout, из которого вы можете создать производные объекты рас-
печатки. Вам нужно переопределить в TPrintout только несколько
методов.

Ваши объекты распечатки должны делать следующее:

* Устанавливать параметры принтера.


B.Pascal 7 & Objects/OW - 260 -

* Подсчитывать страницы.

* Отображать каждую страницу в контексте устройства.

* Указывать, есть ли еще страницы.

Остальная часть этой главы ссылается на пример программы
PrnTest, записанной на ваших дистрибутивных дискетах под именем
PRNTEST.PAS. PrnTest считывает текстовый файл в набор строк, а
затем по команде печатает документ. Объект PrnTest описывается
следующим образом:

type
PTextPrint = ^TTextPrint;
TTextPrint = object(TPrintout);
TextHeight, LinesPerPage, FirstOnPage, LastOnPage:
Integer;
TheLines; PCollection;
constructor Init(ATitle: PChar;
TheText: PPCharCollection);
function GetDialogInfo(var Pages: Intger): Boolean;
virtual;
function HasNextPage(Page: Word): Boolean; virtual;
procedure SetPrintParams(ADC: HDC; ASize: TPoint);
virtual;
procedure PrintPage(Page: Word; var Rect: TRect;
Flags: Word); virtual;
end;

Задание параметров печати
-----------------------------------------------------------------

Перед запросом распечатки документа объект принтера предос-
тавляет вашему документу возможность разбивки на страницы. Для
этого вызываются два метода объекта распечатки - SetPrintParams и
GetDialogInfo.

Функция SetPrintParams предназначена для инициализации
структур данных, которые могут потребоваться для получения эффек-
тивной распечатки отдельных страниц. SetPrintParams - это первая
возможность вашей распечатки обратиться к контексту устройства и
задать для принтера размер страницы. Приводимый ниже фрагмент
программы показывает пример переопределенного метода
SetPrintParams.

Если вы переопределяете SetPrintParams, убедитесь в вызове
наследуемого метода, устанавливающего значения полей распечатки
объекта.

Подсчет страниц
-----------------------------------------------------------------

После вызова SetPrintParams объект печати вызывает булевскую

B.Pascal 7 & Objects/OW - 261 -

функцию GetDialogInfo. GetDialogInfo задает информацию, необходи-
мую для вывода на экран диалогового блока, позволяющего пользова-
телю выбрать перед печатью диапазон страниц. В подсчете страниц в
GetDialogInfo и индикации вывода диалогового блока имеются два
аспекта.

Функция GetDialogInfo воспринимает единственный параметр-пе-
ременную Pages, которую она должна устанавливать в число страниц
в документе или в 0, если она не может подсчитать страницы. Возв-
ращаемое значение равно True, если вы хотите вывести диалоговый
блок, и False для подавления его вывода.

По умолчанию GetDialogInfo устанавливает Pages в 0 и возвра-
щает True, что означает, что она не знает, сколько может полу-
читься страниц, и что перед печатью будет выводиться диалоговый
блок. GetDialogInfo обычно переопределяется для установки Pages в
число страниц в документе и возвращает True.

Например, PrnTest подсчитывает, сколько строк текста выбран-
ного шрифта может поместиться в области печати в SetPrintParams,
а затем использует это число для подсчета количества страниц, ко-
торые нужно напечатать в GetDialogInfo:

procedure TTextPrint.SetPrintParams(ADC: HDC;
ASize: TPoint);
var TextMetrics: TTextMetric;
begin
inherited SetPrintParams(ADC, ASize); { установить DC и
размер Size }
GetTextMetrics(DC, TextMetrics); { получить информацию о
размере текста }
TextHeigh := TextMetrics.tmHeight; { вычислить высоту
строки }
LinesPerPages := Size.Y div TextHeight; { и число строк
на странице }
end;

function TTextPtint.GetDialogInfo(var Pages: Integer):
Boolean);
begin
Pages:= TheLines^.Count div LinesPerPage + 1;
GetDialogInfo := True { вывод перед печатью диалогового
блоки }
end;

Печать каждой страницы
-----------------------------------------------------------------

Когда объект принтера предоставляет возможность разбивки до-
кумента на страницы, то для печати каждой страницы он вызывает
метод PrintPage объекта распечатки. Процесс распечатки только
части документа, которому принадлежит данная страница, аналогичен
определению того, какую часть прокручиваемого окна нужно отобра-

B.Pascal 7 & Objects/OW - 262 -

жать на экране. Например, можно отметить подобие методов отобра-
жения окна и отображения страницы:

procedure TTextWindow.Paint(PaintDC: HDC;
var PaintInfo: TPaintStruct);
var
Line: Integer;
TextMetrics: TTextMetric;
TheText: PChar;

function TextVisible(ALine: Integer): Boolean;
begin
with Scroller^ do
TextVisible := IsVisible(0, (ALine div YUnit) +
YPos, 1, Attr.W div YUnit);
end;

begin
GetTextMetrics(PaintDC, TextMetrics);
Scroller^.SetUnits(TextMetrics.tmAveCharWidth,
TextMetrics.tmHeight);
Line := 0;
while (Line < FileLines^.Count) and TextVisible(Line) do
begin
TheText := PChar(FileLines^.At(Line));
if TheText <> nil then
TextOut(PaintDC, 0, Line * Scroller^.YUnit, TheText,
StrLen(TheText));
Inc(Line);
end;
end;

procedure TTextPrint.PrintPage(Page: Word; var Rect: TRect;
Flags: Word);
var
Line: Integer;
TheText: PChar;
begin
FirstOnPage := (Page - 1) * LinesPerPage;
LastOnPage := (Page * LinesPerPage) - 1;
if LastOnPage >= TheLines^.Count then
LastOnPage := TheLines^.Count - 1;
for Line := FirstOnPage to LastOnPage do
begin
TheText := Theines^.At(Line);
if TheText <> nil then
TextOut(DC, 0, (Line - FirstOnPage) * TextHeight,
TheText, StrLen(TheText));
end;
end;

При написании методов PrintPage следует иметь в виду следую-
щее:

B.Pascal 7 & Objects/OW - 263 -


* Независимость от устройств. Убедитесь, что в вашем коде не
делается предположений относительно масштаба, коэффициента
относительного удлинения или цветах. Для различных уст-
ройств видеоотображения и печати эти характеристики могут
отличаться, так что в программе следует избегать такой за-
висимости.

* Возможности устройств. Хотя большинство видеоустройств
поддерживают все операции GDI, некоторые принтеры этого не
делают. Например, многие устройства печати (такие как гра-
фопостроители) совсем не воспринимают графических изобра-
жений. При выполнении сложных задач вывода в программе
следует вызывать функцию API Windows GetDeviceCaps, кото-
рая возвращает важную информацию о данном устройстве выво-
да.

Указание оставшихся страниц
-----------------------------------------------------------------

У объектов распечатки имеется также последняя обязанность -
указать объекту принтера, имеются ли после данной страницы еще
печатаемые страницы. Метод HasNextPage воспринимает в качестве
параметра номер строки и возвращает значение Boolean, указываю-
щее, существуют ли еще страницы. По умолчанию HasNextPage всегда
возвращает значение False. Чтобы напечатать несколько страниц,
ваши объекты распечатки должны переопределять HasNextPage для
возврата True, если документ имеет больше страниц для печати, и
False, если переданным параметром является последняя страница.

Например, PrnTest сравнивает номер последней напечатанной
строки с последней строкой в файле и определяет, нужно ли печа-
тать еще страницы:

function TTextPrint.HasNextPage(Page: Word): Boolean;
begin
HasNextPage := LastOnPage < TheLines^.Count - 1;
end;

Убедитесь, что HasNextPage возвращает в некоторой точке зна-
чение False. Если HasNextPage всегда возвращает True, то процесс
печати попадет в бесконечный цикл.

Другие соглашения по печати
-----------------------------------------------------------------

Объекты распечатки содержат также несколько других методов,
которые вы можете при необходимости переопределить. Методы
BeginPrintint и EndPrinting вызываются, соответственно, перед пе-
чатью и после печати любого документа. Если вам требуется специ-
альная установка, вы можете выполнить ее в BeginPrinting и отме-
нить в EndPrinting.


B.Pascal 7 & Objects/OW - 264 -

Печать страниц выполняется последовательно. То есть, для
каждой страницы в последовательности принтер вызывает метод
PrintPage. Однако, перед первым вызовом PrintPage объект принтера
вызывает BeginDocument, передавая номер первой и последней стра-
ницы, которые будут печататься. Если для вашего документа при пе-
чати страниц, отличных от первой, требуется специальная подготов-
ка, переопределите метод BeginDocument. После распечатки послед-
ней страницы вызывается соответствующий метод EndDocument.

Может потребоваться также переопределение метода
GetSelection. GetSelection указывает в своем возвращаемом булевс-
ком значении, имеет ли документ выделенную часть. Если это так,
диалоговое окно печати предоставляет вам возможность распечатать
только эту выделенную часть. Позицию выделения указывают два па-
раметра-переменных Start и Stop. Например, TEditPrintout интерп-
ретирует Start и Stop как позиции символов, но может представлять
также строки текста, страницы и т.д.

Печать содержимое окна
-----------------------------------------------------------------

Поскольку окно не разбивается на несколько страниц, и окон-
ные объекты уже знают, как отображаться в контексте устройства,
простейшим видом генерируемой распечатки является копия содержи-
мого окна.

Чтобы еще более облегчить эту общую операцию, ObjectWindows
обеспечивает дополнительный вид объекта распечатки -
TWindowPrint. Любой оконный объект ObjectWindows может без моди-
фикации печатать свое содержимое в объект TWindowPrintout. Объек-
ты распечатки масштабируют образ для заполнения нужного числа
страниц и поддерживают коэффициент относительного удлинения.

Создание объекта распечатки окна требует только одного шага.
Все, что требуется сделать - это построить объект печати окна,
передавая ему строку заголовка и указатель на окно, которое тре-
буется напечатать:

PImage := New(PWindowPrintout, Init('Заголовок',
PSomeWindow));

Часто возникает необходимость в том, чтобы окно само созда-
вало свою распечатку, возможно в ответ на команды меню:

procedure TSomeWindow.CMPrint(var Msg: TMessage);
var P: PPrintout;
begin
P := New(PWindowPrintout, Init('Дамп экрана', @Self));
{ передать образ на экран }
Dispose(P, One);
end;

TWindowPrintout не предусматривает разбивки на страницы. При

B.Pascal 7 & Objects/OW - 265 -

печати документа вам нужно печатать каждую страницу отдельно, но
так как окна не имеют страниц, вам нужно напечатать только один
образ. Окно уже знает, как создать этот образ - оно имеет метод
Paint. TWindowsPrintout печатается путем вызова метода Paint окна
объекта с контекстом устройства печати вместо контекста дисплея.

Вывод распечатки на принтер
-----------------------------------------------------------------

При наличии объекта принтера и объекта распечатки фактичес-
кая печать выполняется достаточно просто, независимо от того,
происходит это из документа или из окна. Все, что вам нужно сде-
лать - это вызов метода Paint объекта принтера, передача указате-
ля на порождающее окно и указателя на объект распечатки:

Printer^.Print(PParentWindow, PPrintoutObject);

В этом случае порождающее окно - это окно, к которому будут
присоединены все всплывающие диалоговые блоки (например, состоя-
ния принтера или сообщений об ошибках). Обычно это будет окно,
генерирует команду печати, такое как основное окно со строкой ме-
ню. В процессе печати для предотвращения передачи множественных
команд печати это окно запрещается.

Предположим, у вас есть приложение, основное окно которого
является экземпляром TWidGetWindow. В меню этого окна вы можете
выбрать команду меню для печати содержимого окна, генерирующую
команду cm_Print. Метод реакции на сообщения может иметь следую-
щий вид:

procedure TWidgetWindow.CMPrint(var Msg: TMessage);
var P: PPrintout;
begin
P := New(PWindowPrint, Init('Widgets', @Self));
Printer^.Print(@Self, P);
Dispose(P, Done);
end;

Выбор другого принтера
-----------------------------------------------------------------

Когда у вас в приложении есть объект принтера, вы можете
связать его с любым установленным в Windows устройстве печати. По
умолчанию TPrinter использует заданный по умолчанию принтер
Windows (как это определено в разделе устройств файла WIN.INI).

Существует два способа задания альтернативного принтера: не-
посредственно в программе и через диалоговое окно пользователя.



B.Pascal 7 & Objects/OW - 266 -

Выбор принтера пользователем
-----------------------------------------------------------------

Наиболее общим способом назначения другого принтера является
вывод диалогового окна, предоставляющего пользователю возможность
выбора из списка установленных устройств печати. TPtinter делает
это автоматически при вызове его метода Setup. Как показано на
Рис. 15.1, Setup использует для этого диалогового окна объект
TPrinterSetupDlg.

----------------------------------------------------------------¬
¦-=--------------------SelectPrinter----------------------------¦
+---------------------------------------------------------------+
¦ ¦
¦ Printer and port: ¦
¦ ---------------------------------------------T-¬ ¦
¦ ¦PostScript Printer on LPT1: ¦v¦ ¦
¦ L--------------------------------------------+-- ¦
¦ ------------¬ ------------¬ ------------¬ ¦
¦ ¦----OK-----¦ ¦--Setup----¦ ¦--Cancel---¦ ¦
¦ L------------ L------------ L------------ ¦
¦ ¦
L----------------------------------------------------------------

Рис. 15.1 Диалоговое окно задания принтера.

Настройка конфигурации принтера

Одна из командных кнопок в диалоге выбора принтера позволяет
пользователям изменить конфигурацию конкретного принтера. Кнопка
Setup выводит диалоговый блок конфигурации, определенной в драй-
вере принтера. Ваше приложение не может управлять внешним видом
или функциями диалогового блока конфигурации драйвера.

Назначение конкретного принтера
-----------------------------------------------------------------

В некоторых случаях вам может потребоваться назначить для
своего объекта принтера специфическое устройство печати. TPrinter
имеет метод SetDevice, который именно это и делает.

SetDevice воспринимает в качестве параметров три строки: имя
устройства, имя драйвера и имя порта.



B.Pascal 7 & Objects/OW - 267 -

------------------------------------------------------------------
Часть 3. Продвинутое
программирование с использование ObjectWindows
-----------------------------------------------------------------


Глава 16. Сообщения Windows
-----------------------------------------------------------------

Приложения Windows управляются событиями. То есть вместо не-
посредственной обработки оператор за оператором управление прог-
раммой определяется внешними событиями, такие как взаимодействия
с пользователем и системное оповещение. Приложения узнают о собы-
тиях, на которые они должны реагировать, получая от Windows сооб-
щения.

Данная глава охватывает ряд тем, связанных с передачей, по-
лучением и обработкой сообщений, включая следующие вопросы:

* что такое сообщение?

* как выполняется диспетчеризация сообщений?

* обработка сообщений Windows;

* определение ваших собственных сообщений;

* передача и адресация сообщений;

* диапазоны сообщений.

Что такое сообщение?
-----------------------------------------------------------------

Если вы не используете программирование, управляемое событи-
ями, Windows может выглядеть достаточно странной операционной
средой. Возможно, вам придется писать программы, которые основную
часть своего времени просто ждут ввода от пользователя (например,
в операторе Readln).

Программирование, управляемое событиями, обходит эту ситуа-
цию, возлагая обработку ввода от пользователя на центральную
подпрограмму, которую вам даже не нужно вызывать. В этом случае
Microsoft Windows сама взаимодействует с пользователем и опраши-
вает список взаимодействий для каждого работающего приложения.
Эти информационные пакеты называются сообщениями и представляют
собой просто структуры записей типа TMsg:

type
TMsg = record
hwnd: HWnd;
message: Word;
wParam: Word;
lParam: Longint;

B.Pascal 7 & Objects/OW - 268 -

time: Longint;
pt: TPoint;
end;

Поля записи сообщения дают приложению информацию о том, ка-
кой вид событий сгенерировал сообщение, где оно произошло и какое
окно должно на него реагировать.

Относительно сообщений следует иметь в виду, что ваше сооб-
щение в общем случае получает их после того, что произошло. Нап-
ример, если вы изменяете размер окна на экране, то объект окна
получает сообщение wm_Size, когда вы закончите изменение размера.
Некоторые сообщения запрашивают выполнение каких-то действий. Од-
нако в большинстве случаев это уведомления о том, что пользова-
тель или система выполнили некоторые действия, на которые следует
реагировать вашей программе.

Именующие сообщения
-----------------------------------------------------------------

Наиболее важным полем сообщений является поле message, кото-
рое содержит одну из констант сообщений Windows, начинающихся с
wm_. Каждое сообщение Windows уникальным образом идентифицируется
16-битовым числом с соответствующим мнемоническим идентификато-
ром. Например, сообщение, являющееся результатом нажатия клавиши,
содержит в поле сообщения wm_KeyDown ($0100). На сообщения обычно
ссылаются по их мнемоническим именам.

Откуда поступают сообщения
-----------------------------------------------------------------

Генерировать сообщения позволяют несколько различных событий:

* взаимодействия с пользователем, такие как нажатия клавиш,
щелчок кнопкой "мыши" или ее буксировка;
* вызовы функций Windows, которым нужно информировать об из-
менениях другие окна;
* ваша программа явно посылает сообщение;
* другое приложение посылает сообщение через DDE (динамичес-
кий обмен данными);
* сообщение генерируется самой Windows (например, сообщение
об останове системы).

Вашему приложению в общем случае не важно, как генерируются
сообщения. Основным в программировании, управляемом событиями,
является генерация сообщений и реакция на них. Поскольку Windows
и ObjectWindows берут на себя функции по доставке сообщений из
одного места в другое, вы можете сосредоточиться на генерации со-
общений с соответствующими параметрами и реакции на сообщения, а
не на механизме их доставки из одного места в другое.



B.Pascal 7 & Objects/OW - 269 -

Обычная диспетчеризация сообщений
-----------------------------------------------------------------

Обычные приложения Windows (то есть не использующие
ObjectWindows) имеют цикл сообщения, в котором выполняется выбор-
ка и диспетчеризация сообщения. По существу, в цикле сообщения
вызывается связанная с окном функция, заданная описателем окна в
поле hwnd записи сообщения.

Каждое окно имеет функцию окна, заданную при его создании.
Когда Windows находит сообщение для конкретного окна, она переда-
ет сообщение функции данного окна. Функция окна отсортировывает
сообщения на основе типа сообщения, а затем вызывает подпрограмму
реакции на конкретное сообщение.

Обычный способ обработки сообщений в приложении и его окнах
показывает программа GENERIC.PAS. Вы можете видеть, что оконная
функция каждого окна для сортировки сообщение содержит большой
оператор case. Все это может выглядеть не так плохо, пока вы не
осознаете тот факт, что в окне может потребоваться обрабатывать
более 100 различных сообщений Windows. После этого идея написания
и обслуживания такого оператора case будет выглядеть менее впе-
чатляющей.

Даже если у вас имеется несколько аналогичных окон, каждое
из них будет, очевидно, иметь свою оконную функцию с аналогичным
большим оператором case.

Способ, предлагаемый ObjectWindows
-----------------------------------------------------------------

ObjectWindows вносит в это обычный способ диспетчеризации
сообщений два основных улучшения. Первое состоит в том, что цикл
сообщений скрыт в ядре вашего объекта приложения. Все, что вам
нужно сделать - это привести свое приложение в действие, вызвав
метод Run объекта приложения. После этого оно будет получать со-
общения Windows.

Второе основное улучшение - это автоматическая диспетчериза-
ция сообщений. Вместо необходимости иметь для каждого окна окон-
ную функцию, вы просто определяете в оконных объектах методы, ко-
торые реагируют на конкретные сообщения. Эти методы называются
методами реакции на сообщения.



B.Pascal 7 & Objects/OW - 270 -

Динамические виртуальные методы
-----------------------------------------------------------------

Ключем к автоматической диспетчеризации сообщений является
расширение описаний в объектах виртуальных методов, называемых
динамическими виртуальными методами. По существу вы связываете с
методом целочисленный номер (такой как константа сообщения). Ваша
программа ObjectWindows (в данном случае цикл сообщения объекта
приложения) может затем вызвать этот метод на основе номера сооб-
щения.

Например, сообщение, генерируемое при нажатии в окне левой
кнопки "мыши", содержит в своем поле message wm_LButtonDown
($0201). Когда цикл сообщения ObjectWindows считывает для одного
из своих окон такое сообщение, то выполняется поиск в таблице
виртуальных методов данного оконного объекта и определяется дина-
мический метод, описанный для данного значения. Если такой метод
найден, то он вызывается, и ему в качестве параметра передается
распакованная запись сообщения типа TMessage. Если оконный объект
не описывает метод с данным индексом динамического метода, то
цикл сообщения вызывает используемую по умолчанию оконную проце-
дуру.

Написание методов реакции на сообщение
-----------------------------------------------------------------

Чтобы описать методы реакции на сообщение, нужно задать в
оконном объекте процедуру, названную по имени константы сообще-
ния. Например, чтобы ответить на сообщение wm_LButtonDown, вам
нужно описать методы следующим образом:

type
TMyWindow = object(TWindow)
.
.
.
procedure WMLButtonDown(var Msg: TMessage);
virtual wm_First + wm_LButtonDown;
.
.
.
end;

На самом деле метод может называться как угодно, но наимено-
вание методов по сообщениям, на которые они реагируют, делают
программу понятней. Единственным реальным ограничением является
то, что этот метод должен быть процедурой с единственным парамет-
ром типа TMessage.

Поскольку для методов реакции ObjectWindows использует нес-
колько диапазонов, а все индексы методы отсчитываются с 0, в ка-
честве смещения используется константа wm_First. Добавив для каж-
дого метода это смещение, вы создадите уникальный индекс динами-

B.Pascal 7 & Objects/OW - 271 -

ческого метода. Подробнее о диапазонах сообщений рассказывается в
разделе "Диапазоны сообщений" данной главы.

Что такое сообщение?
-----------------------------------------------------------------

Теперь, когда вы знаете, как написать метод реакции на сооб-
щение, можно рассмотреть, какая информация содержится в сообще-
нии. Запись TMessage, передаваемая методу реакции на сообщение,
выглядит следующим образом:

type
TMessage = record
Receiver: HWnd;
Message: Word;
case Integer of
0: (
WParam: Word;
LParam: Longint;
Result: Longint);
1: (
WParamLo: Byte;
WParamHi: Byte;
LParamLo: Word;
LParamHi: Word;
ResultLo: Word;
ResultHi: Word);
end;

Поля Receiver и Message для объектов ObjectWindows не осо-
бенно полезны, поскольку описатель Receiver обычно представляет
собой тоже самое, что и поле HWindow оконного объекта, а Message
уже отсортировано в цикле сообщения ObjectWindows.

Однако другие три поля очень важны. WParam и LParam - это
16- и 32-битовые параметры, передаваемые в сообщениях от Windows.
Result содержит код результата, который может потребоваться пере-
дать обратно. Заметим, что TMessage - вариантная запись, так что
вы можете обращаться к старшему и младшему байту слов параметров.

Поля параметров
-----------------------------------------------------------------

Поля параметров записи сообщения имеют для каждого сообщения
свой смысл. Однако, можно сделать некоторые обобщения.

WParam

Параметр WParam типа Word обычно содержит описатель, иден-
тификатор (например, идентификатор управляющего элемента) или бу-
левское значение. Например, параметр WParam сообщения
wm_SetCursor содержит описатель окна, в котором находится курсор.
Уведомляющие сообщения управляющего элемента, такие как

B.Pascal 7 & Objects/OW - 272 -

bn_Clicked, содержат в WParam идентификатор соответствующего уп-
равляющего элемента. wm_Enable использует WParam для булевского
значения, указывающего, разрешено или запрещено соответствующее
окно.

LParam

Параметр LParam типа Longint обычно содержит значение-указа-
тель двух переменных размером в слово, таких как координаты x и
y. Например, параметр LParam сообщения wm_SetText указывает на
строку с завершающим нулем, содержащую устанавливаемый текст. Со-
общения "мыши", такие как wm_LButtonDown, используют LParam для
записи координат события "мыши". Благодаря вариантным частям за-
писи сообщения, LParamLo содержит x-координату, а LParamHi -
y-координату.

Поле Result
-----------------------------------------------------------------

Поле Result сообщения TMessage управляет возвращаемым значе-
нием сообщения. Иногда программа, посылающая сообщение, ожидает
возврата конкретного значения, такого как булевское значение,
указывающее успешное или неуспешное выполнение или код ошибки. Вы
можете задать возвращаемое значение, присвоив значение полю
Result.

Например, когда пользователь пытается восстановить окно из
состояния пиктограммы, ему посылается сообщение wm_QueryOpen. По
умолчанию wm_QueryOpen возвращает булевское значение True (не
ноль). Если вы хотите иметь окно, которое всегда выводится в виде
пиктограммы, то вы можете ответить на сообщение wm_QueryOpen и
установить Result в 0. Это означает, что окно не может быть отк-
рыто:

procedure TIconWindow.WMQueryOpen(var Msg: TMessage);
begin
Msg.Result := 0;
end;

Объектно-ориентированная обработка сообщения
-----------------------------------------------------------------

Одним из огромных преимуществ обработки сообщения в
ObjectWindows (кроме того, что можно избавиться от больших опера-
торов case) является обработка сообщений объектно-ориентированным
способом. То есть, ваши оконные объекты наследуют возможность оп-
ределенной реакции на сообщения, и вам нужно только изменить ре-
акцию на конкретные сообщения, которые ваш объект обрабатывает
по-другому.

Отмена поведения по умолчанию
-----------------------------------------------------------------


B.Pascal 7 & Objects/OW - 273 -

Иногда, когда вы переопределяете используемую по умолчанию
реакцию на сообщение, это делается потому что данное поведение
просто нежелательно. Простейшим случаем является ситуация, когда
объект должен игнорировать сообщение, а не отвечать на него. Для
этого вы можете просто написать пустой метод реакции на сообще-
ние. Например, следующий метод сообщает управляющему элементу ре-
дактирования, что нужно игнорировать передаваемые в сообщении
wm_Char символы:

procedure TNonEdit.WMChar(var Msg: TMessage);
begin
end;

Замена поведения по умолчанию
-----------------------------------------------------------------

Более полезным подходом, чем простое игнорирование сообще-
ния, является замена поведения по умолчанию чем-то совершенно
другим. Например, следующий метод сообщает управляющему элементу
редактирования, что вместо вставки символа при нажатии любой кла-
виши нужно давать звуковой сигнал:

procedure TBeepEdit.WMChar(var Msg: TMessage);
begin
MessageBeep(0);
end;

Звуковой сигнал по нажатию клавиш сам по себе не особенно
полезен, но дает хорошую иллюстрацию. В общем случае вы можете
заменить используемое по умолчанию поведение некоторым другим.
Определяемая вами реакция будет единственной.

Дополнение поведения по умолчанию
-----------------------------------------------------------------

Иногда может возникнуть потребность в комбинировании некото-
рых действий с используемой по умолчанию реакцией на сообщение.
ObjectWindows предоставляет для этого объектно-ориентированный
способ. Обычно, когда вы хотите, чтобы объект выполнял некоторые
действия на основе используемого по умолчанию поведения, вы може-
те встроить в свой переопределенный метод наследуемый метод.
ObjectWindows позволяет вам делать это также для реакции на сооб-
щения.

Вызов наследуемых методов
-----------------------------------------------------------------

Предположим, например, что вы создали новый оконный объект и
хотите, чтобы он в дополнение к другим обычно выполняемым дейс-
твиям он давал звуковой сигнал при щелчке в окне левой кнопкой
"мыши". Все, что вам нужно сделать - это вызов в вашем новом ме-
тоде наследуемого метода TWindow.WMLButtonDown:


B.Pascal 7 & Objects/OW - 274 -

procedure TBeepWindow.WMLButtonDown(var Msg: TMessage);
begin
inherited WMLButtonDown(Msg);
MessageBeep(0);
end;

В данном случае неважно, размещаете ли вы вызов наследуемого
метода WMLButtonDown перед или после вызова MessageBeep. Вам нуж-
но решить, должен ли ваш метод вызывать наследуемые действия пе-
ред специальной обработкой или после нее (на основе того, нужны
ли вам параметры сообщения, или требуется их изменить).

Следует иметь в виду, что вы можете изменять параметры Msg
перед вызовом наследуемого метода. Однако делать это следует ак-
куратно, так как передача параметров вне диапазона может привес-
ти к тому, что ваша программа вызовет сбой Windows, особенно если
эти параметры содержат описатели или указатели. Если вы будете
аккуратны, изменение Msg может оказаться полезным, но нужно учи-
тывать, что это может быть также опасным.

Вызов процедур, используемых по умолчанию
-----------------------------------------------------------------

Как вы могли заметить, в данном разделе при вызове наследу-
емых методов используется не такой пример, как в предыдущих раз-
делах. Вы, возможно, ожидали, что новый управляющий элемент ре-
дактирования для обработки действия по умолчанию будет вызывать
свой наследуемый метод WMChar. Это имеет смысл, но не будет рабо-
тать, так как TEdit не имеет метода WMChar, поэтому производный
из TEdit объект не может вызывать WMChar в качестве наследуемого
метода.

К счастью, ваши объекты все равно могут наследовать реакцию
на сообщения. Когда ObjectWindows диспетчеризует сообщение объек-
ту, и этот объект не определяет конкретного метода реакции,
ObjectWindows передает запись TMessage методу с именем DefWndProc
- используемой по умолчанию оконной процедуре. DefWndProc знает,
как применять действия по умолчанию для всех сообщений. Таким об-
разом, если компилятор дает ошибку, когда вы пытаетесь наследо-
вать метод реакции на сообщения, поскольку для данного сообщения
нет наследуемого метода, вызовите вместо этого DefWndProc.

Например, чтобы в дополнение к вставке набираемых символов
добавить к управляющему элементу редактирования звуковой сигнал,
вам нужно вызвать MessageBeep и DefWndProc:

procedure TBeepEdit.WMChar(var Msg: TMessage);
begin
MessageBeep(0);
DefWndProc(Msg);
end;

Вызов DefWndProc вы можете рассматривать как используемый

B.Pascal 7 & Objects/OW - 275 -

вместо всех не определенных конкретно наследуемых методов реакции
на сообщения. Отметим, однако, что это применяется только к сооб-
щениям Windows. Командные сообщения, уведомляющие сообщения и уп-
равляющие сообщения имеют свои собственные используемые по умол-
чанию обработчики сообщений. Эти сообщения и их используемые по
умолчанию процедуры описываются в следующем разделе.

Командные, уведомляющие и управляющие идентификаторы
-----------------------------------------------------------------

Сообщение - это просто запись данных, идентифицируемая конк-
ретным значением в поле message, а ObjectWindows экономит вам
массу времени и усилий путем сортировки этих сообщений и диспет-
черизации их методам реакции на сообщения в ваших объектах. Одна-
ко, сообщение Windows wm_Command имеет много вариантов, которые
нужно отсортировывать с помощью большого оператора case.
ObjectWindows также выполняет это для вас.

Например, сообщение wm_Command посылается при выборе элемен-
та меню или нажатии оперативной клавиши. В обычном приложении
сообщение Windows передавалось бы вашей функции окна, где в боль-
шом операторе case нужно было бы отсортировать различные сообще-
ния, вызывая другие подпрограммы при обнаружении wm_Command. Эта
подпрограмма должна в свою очередь определить, какая была переда-
на команда (с помощью другого большого оператора case).

Командные сообщения
-----------------------------------------------------------------

ObjectWindows обрабатывает команды меню и оперативных клавиш
путем отдельной диспетчеризации командных сообщений в основном
аналогично другим сообщениям Windows. Реально обработка выполня-
ется внутри метода WMCommand ваших оконных объектов, наследуемого
из TWindowsObject. Но вместо обработки самих команд WMCommand вы-
полняет диспетчеризацию командных сообщений на основе генерируе-
мого командой идентификатора меню или оперативной клавиши.

Например, если вы определяете элемент меню с идентификатором
cm_DoSomething, в ваших объектах следует на основе этого иденти-
фикатора определить методы реакции:

type
TSomeWindow = object(TWindow)
.
.
.
procedure CMDoSomething(var Msg: TMessage);
virtual cm_First + cm_DoSomething;
end;

procedure TSomeWindow.CMDoSomething(var Msg: TMessage);
begin
{ реакция на команду }

B.Pascal 7 & Objects/OW - 276 -

end;

Аналогично wm_First, cm_First - это константа ObjectWindows,
определяющая начало диапазона сообщений. Ваши командные константы
должны лежать в диапазоне 0..24319.

Обработка команд по умолчанию

Чтобы вызвать используемую по умолчанию реакцию на команду,
для нее обычно вызывается наследуемый метод реакции. Если в объ-
екте-предке не определяется метод реакции на конкретную команду,
по умолчанию обработка выполняется с помощью DefCommandProc.
DefCommandProc работает во многом аналогично методу DefWndProc
для сообщений Windows, но обрабатывает команды.

Уведомляющие сообщения
-----------------------------------------------------------------

Команды не обязательно должны поступать от меню или команд-
ных клавиш. Управляющие элементы в окнах посылают сообщения своим
порождающим окнам, когда вы щелкаете на них "мышью" или что-либо
набираете. Эти сообщения называются уведомляющими сообщениями, и
ObjectWindows обрабатывает их двумя различными путями.

В основном уведомления могут передаваться объекту управляю-
щего элемента или его порождающему окну. Если управляющий элемент
имеет связанный с ним объект ObjectWindows, ObjectWindows дает
объекту возможность сначала ответить на команду. Это называется
уведомлением управляющего элемента. Если управляющий элемент не
имеет соответствующего объекта, или объект управляющего элемента
не определяет реакцию на команду, то ответить имеет возможность
порождающее окно. Это называется уведомлением порождающего объек-
та.

Уведомления управляющих элементов
-----------------------------------------------------------------

Обычно управляющим элементам не требуется в ответ на дейс-
твия пользователя делать ничего особенного; ожидаемым поведением
является поведение, используемое по умолчанию. Но если вы хотите,
чтобы управляющий элемент делал что-то дополнительно или что-то
другое, то уведомляющие сообщения позволяют вам осуществить это.

Предположим, например, что вы хотите, чтобы при каждом щелч-
ке кнопкой "мыши" раздавался звуковой сигнал. Вы можете просто
задать для объекта кнопки метод реакции на уведомление:

type
TBeepButton = object(TButton)
procedure BNClicked(var Msg: TMessage);
virtual nf_First + bn_Clicked;
end;


B.Pascal 7 & Objects/OW - 277 -

procedure TBeepButton.BNClicked(var Msg: TMessage);
begin
MessageBeep(0);
end;

ObjectWindows определяет в качестве смещения, задающего диа-
пазон используемых в уведомлениях управляющих элементов сообще-
ний, константу nf_First.

Уведомление порождающего объекта
-----------------------------------------------------------------

Если управляющий элемент не имеет связанного с ним интер-
фейсного объекта, или управляющий объект не определяет реакции на
конкретную команду, уведомляющие сообщения посылаются вместо объ-
екта управляющего элемента порождающему окну управляющего элемен-
та. Так как порождающее окно должно знать, какой из его управляю-
щих элементов вызвал уведомление, уведомляющее сообщение порожда-
ющему окну основывается на идентификаторе управляющего элемента.

Например, чтобы ответить на уведомление о взаимодействиях с
управляющим элементом с идентификатором id_MyControl, вы можете
определить метод следующим образом:

type
TMyWindow = object(TWindow)
.
.
.
procedure IDMyControl(var Msg: TMessage);
virtual id_First + id_MyControl;
end;

procedure TMyWindow.IDMyControl(var Msg: TMessage);
begin
{ реакция на сообщение }
end;

В качестве смещения в диапазоне сообщений, используемых для
реакции на уведомления управляющих элементов, ObjectWindows опре-
деляет константу id_First.

Windows редко определяет реакцию на конкретные управляющие
элементы, заданную по умолчанию. Однако, если вы хотите позабо-
титься о заданном по умолчанию поведении, то можете вызвать
DefChildProc. DefChildProc работает аналогично DefWndProc, но об-
рабатывает вместо сообщений Windows уведомляющее сообщение по-
рождающему объекту.



B.Pascal 7 & Objects/OW - 278 -

Уведомления управляющих
элементов и порождающих объектов
-----------------------------------------------------------------

Возможно, иногда вам потребуется, чтобы одновременно на не-
которое взаимодействие пользователя с управляющим элементом реа-
гировали и управляющий элемент, и порождающее окно. ObjectWindows
также обеспечивает способ реализовать это. Поскольку уведомление
порождающего объекта предусматривает реакцию по умолчанию, когда
объект управляющего элемента не определяет реакции на уведомле-
ние, все, что нужно сделать - это вызов реакции по умолчанию в
дополнение реакции управляющего элемента.

Например, с учетом приведенного выше объекта TBeepButton вы
можете также уведомлять порождающее окно с помощью добавления вы-
зова DefNotificationProc:

procedure TBeepButton.BNClicked(var Msg: TMessage);
begin
MessageBeep(0);
DefNotificationProc(Msg);
end;

Вызов DefNotificationProc обеспечивает получение уведомляю-
щего сообщения на основе идентификатора порождающего окна управ-
ляющего элемента, как если бы управляющий элемент вовсе не опре-
делял реакции.

Определение ваших собственных сообщений
-----------------------------------------------------------------

Windows резервирует для собственного использования 1024 со-
общения. В этот диапазон попадают все стандартные сообщения. На-
чало диапазона сообщений определяется константой wm_User. Чтобы
определить сообщение, используемое окнами вашей программы, опре-
делите идентификатор сообщения, попадающий в диапазон
wm_User..wm_User+31744.

Чтобы избежать конфликта со стандартными сообщениями, вам
следует определить идентификаторы своих сообщений как константы
на основе wn_User. Например, в приложении, где требуется три оп-
ределенных пользователем сообщения, вы можете описать их следую-
щим образом:

const
wm_MyFirstMessage = wm_User;
wm_MySecondMessage = wm_User + 1;
wm_MyThirdMessage = wm_User + 2;

Реакция на ваши сообщения аналогично реакции на любое другое
сообщение:

TCustomWindow = object(TWindow)

B.Pascal 7 & Objects/OW - 279 -

.
.
.
procedure WMMyFirstMessage(var Msg: TMessage);
virtual wm_First + wm_MyFirstMessage

Согласно общему правилу, определенные пользователем сообще-
ния следует использовать для внутреннего обмена сообщения. Если
вы посылаете определенное пользователем сообщение другому прило-
жению, нужно убедиться, что это другое приложение определяет со-
общение также, как это делается в коде вашего приложения. Внешние
коммуникации лучше обрабатываются с помощью динамического обмена
данными (DDE).

Передача сообщений
-----------------------------------------------------------------

До сих пор мы определяли реакцию на сообщения, но не говори-
ли пока о том, как генерировать сообщения. Большинство сообщений
Windows генерируются самой Windows в ответ на действия пользова-
теля или системные события. Но ваша программа можете генериро-
вать сообщения либо моделируя действия пользователя, либо манипу-
лирую элементами на экране.

ObjectWindows уже обеспечивает для вас способы передачи мно-
гих сообщений, которые в противном случае пришлось бы передавать
вручную. Например, общим случаем для генерации сообщений является
работа с управляющим элементами. Чтобы добавить строку в блок
списка, Windows определяет такие сообщения как lb_AddString, а
чтобы отменить выбор кнопки с зависимой фиксацией или выбрать ее
- bm_SetCheck. ObjectWindows определяет методы для объектов уп-
равляющих элементов (TListBox.AddString и TCheckBox.SetCheck),
посылающие для вас эти сообщения, так что вам даже не нужно ду-
мать об их использовании.

Возможно вы найдете, что для большинства функций (которые в
противном случае пришлось бы выполнять передачей сообщений) уже
существуют объектные методы.

Передача и отправление сообщений
-----------------------------------------------------------------

Тем не менее, иногда возникает необходимость передачи сооб-
щений в окна вашего приложения. Windows предусматривает две функ-
ции, позволяющие вам генерировать сообщения - SendMessage и
PostMessage. Основное отличие этих двух функций состоит в том,
что SendMessage ожидает обработки сообщения перед возвратом.
PostMessage просто добавляет сообщение в очередь сообщений и
возвращает управление.

Имейте в виду, что SendMessage, особенно при вызове в методе
реакции на сообщение, может вызвать бесконечный цикл или клинч,
приводящие к сбою программ. Вам следует не только избегать оче-

B.Pascal 7 & Objects/OW - 280 -

видных циклов, таких как методы реакции на сообщения, генерирую-
щих то же сообщение, которое его вызывает, но избегать также пе-
редачи сообщений с побочными эффектами. Например, метод Paint
объекта, который вызывается в ответ на сообщения wm_Paint, оче-
видно должен явным образом посылать самому себе другое сообщение
wm_Paint. Но он должен также избегать других действий, дающих в
результате другое сообщение wm_Paint, посылаемое, пока метод еще
активен, таких как изменение размеров окна, запрещение окна или
создание/уничтожение перекрывающихся окон.

Передача сообщения
-----------------------------------------------------------------

Для передачи сообщения требуется следующее: описатель ок-
на-получателя, номер сообщения и параметры Word и Longint. В при-
ложении ObjectWindows описателем получателя является обычно поле
HWindow интерфейсного объекта. Идентификатор сообщения - это
просто константа, идентифицирующая конкретное сообщение, которое
вы хотите передать (такая как wm_More или em_SetTabStops). Пара-
метры в зависимости от сообщения могут быть различными.

Значение, возвращаемое SendMessage - это значение поля
Result в записи сообщения при завершении обработки. Имейте в ви-
ду, что если вы вызываете наследуемый или используемый по умолча-
нию метод реакции на сообщение, ваше значение может быть переза-
писано.

Windows с помощью SendMessage обеспечивает ограниченное
средство циркулярной рассылки сообщений. Если вы в качестве опи-
сателя окна, в которое нужно передать сообщение, зададите $FFFF,
Windows посылает сообщения всем всплывающим и перекрывающимся ок-
нам в системе (не только в вашем приложении). Таким образом, вы
не должны использовать циркулярную рассылку сообщений, опреде-
ляемых пользователем, так как не может быть уверены, что другое
приложение не определило данное сообщение каким-то другим спосо-
бом.

Отправление сообщения
-----------------------------------------------------------------

Отправление сообщения полезно использовать, если вас не осо-
бенно волнует, когда должна последовать реакция на сообщение, или
вам нужно избежать циклов, которые может вызвать SendMessage. Ес-
ли все, что вам нужно сделать - это убедиться в передаче сообще-
ния, и вы можете продолжать работу, не ожидая реакции, то можете
вызывать функцию PostMessage. Ее параметры идентичны параметрам
функции SendMessage. PostMessage не следует использовать для пе-
редачи сообщений управляющим элементам.

Передача сообщения управляющему элементу
-----------------------------------------------------------------

Возможно, единственным случаем, когда вам потребуется пере-

B.Pascal 7 & Objects/OW - 281 -

дать сообщение элементу на экране, не имеющему соответствующего
объекта, является случай, когда вам нужно взаимодействовать с уп-
равляющим элементом в диалоговом окне. Проще всего для этого ис-
пользовать объекты (управляющего элемента, диалогового блока или
обоих).

Если управляющий элемент имеет связанный с ним объект, вы
можете просто использовать для получения идентификатора управляю-
щего элемента поле HWindow объекта. Если диалоговый блок имеет
связанный с ним объект, вы можете вызвать метод SendDlgItemMsg
объекта диалогового блока, для которого задаются идентификатор
управляющего элемента, идентификатор сообщения и два параметра
сообщения. В большинстве приложений ObjectWindows для вас досту-
пен любой их этих подходов.

Если по каким-то причинам у вас нет ни объекта диалогового
блока, ни доступного объекта управляющего элемента, вы можете
послать сообщение управляющему элементу в диалоговом окне с по-
мощью функции API Windows SendDlgItemMessage, которая воспринима-
ет в качестве параметров описатель диалогового блока, идентифика-
тор управляющего элемента, идентификатор сообщения и два парамет-
ра сообщения.



B.Pascal 7 & Objects/OW - 282 -

Диапазоны сообщений
-----------------------------------------------------------------

Сообщение определяется полем message записи сообщения
(16-битовое значение). Windows резервирует для своих собственных
стандартных сообщений 0..$03FF, а остальные сообщения до $7FFF
резервируются для сообщений, определенных пользователем. Диапа-
зоны остальных сообщений в ObjectWindows подразделяются на диа-
пазоны команд и уведомлений (как показано в следующей таблице):

Диапазоны сообщений Таблица 16.1
--------------------------------------T----------------¬
¦ Диапазон ¦ Значения ¦
+-------------------------------------+----------------+
¦ Сообщения, зарезервированные ¦ $0000-$7FFF ¦
¦ для Windows. ¦ ¦
+-------------------------------------+----------------+
¦ Сообщения, определяемые ¦ $0400-$8FFF ¦
¦ пользователем. ¦ ¦
+-------------------------------------+----------------+
¦ Уведомляющие сообщения ¦ $8000-$8FFF ¦
¦ управляющих элементов. ¦ ¦
+-------------------------------------+----------------+
¦ Зарезервированные в Windows ¦ $8F00-$8FFF ¦
¦ уведомляющие сообщения ¦ ¦
¦ управляющих элементов. ¦ ¦
+-------------------------------------+----------------+
¦ Уведомляющие сообщения ¦ $9000-$9FFF ¦
¦ порождающего объекта. ¦ ¦
+-------------------------------------+----------------+
¦ Зарезервированные в Windows ¦ $9F00-$9FFF ¦
¦ уведомляющие сообщения ¦ ¦
¦ порождающего объекта. ¦ ¦
+-------------------------------------+----------------+
¦ Командные сообщения. ¦ $A000-$FFFF ¦
+-------------------------------------+----------------+
¦ Команды, зарезервированные ¦ $FF00-$FFFF ¦
¦ в ObjectWindows. ¦ ¦
L-------------------------------------+-----------------


B.Pascal 7 & Objects/OW - 283 -


-------------------------¬
cm_Internal ($FF00) +------------------------+
¦ ¦
¦ ¦
¦ Команды ¦