Часть 1. Изучение ObjectWindows
Глава 1. Знакомство с Windows
В следующих нескольких главах мы построим графическую инте-
рактивную программу Windows, дополненную меню, средствами сохра-
нения и загрузки файлов, графикой и отображением текста, а также
специализированными управляющими элементами. По ходу дела вы поз-
накомитесь с основными принципами проектирования программы
Windows, такими как обработка сообщений, управление дочерним и
порождающим окном и автоматическое повторное отображение графики.
Этот процесс разбит на следующие шаги:
- создание базового приложения;
- вывод текста в окне;
- изображение линий в окне;
- добавление строки меню;
- добавление диалогового окна;
- изменение атрибутов пера;
- повторное отображение графики;
- сохранение изображения в файле;
- печать образа;
- добавление всплывающего окна;
- добавление специализированных управляющих элементов;
- создание специализированного управляющего элемента окна.
Исходный код приложения для различных этапов вы можете найти
на дистрибутивных дисках. Описываемым в руководстве шагам соот-
ветствуют файлы STEP01.PAS, STEP02.PAS и так далее (можно найти
также промежуточные программы).
Шаг 1: Создание базового приложения
-----------------------------------------------------------------
+-----------------------+
|XStepX1:XBasicXAppXXXXX| Базовая программа
| 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 | Специализированные элементы
+-----------------------+
Отправным пунктом для всех программ, которые вы пишете с
применением 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. Он позволяет также задать имя приложения (поле объек-
та) '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;
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.
Объект основного окна
-----------------------------------------------------------------
Пока программа Steps состоит из двух объектов - объекта при-
ложения и объекта окна. Объект приложения (MyApp) является эк-
земпляром TMyApplication - типом, производным от TApplication.
Оконный объект, который содержится в поле MainWindow объекта
MyApp, является экземпляром TWindow (общее окно ObjectWindows).
Во всех программах, кроме простейших, вам нужно определить тип
своего основного окна, соответствующий поведению приложения. В
данном разделе мы выведем на экран основное окно, тип которого
является производным от TWindow.
Приложения: Более подробно об основном окне рассказы-
вается в Главе 8 "Объекты приложения".
Что такое объект окна?
-----------------------------------------------------------------
Объект приложения инкапсулирует стандартное поведение прило-
жения Windows, включая построение основного окна. Тип
TApplication обеспечивает фундаментальное поведение для каждого
создаваемого вами приложения.
Аналогично, объект окна инкапсулирует поведение, реализуемое
приложениями ObjectWindows, включая их основные окна. Это поведе-
ние включает в себя вывод на экран, изменение размера и закрытие;
ответ на пользовательские события, такие как щелчок кнопкой "мы-
ши", буксировку и выбор пунктов меню; вывод управляющих элемен-
тов, таких как блоки списка и командные кнопки. Тип TWindow и его
предок TWindowsObject предусматривают для данного базового пове-
дения методы и поля.
Примечание: Объекты окна подробно описываются в Главе
10 "Объекты окна".
Чтобы сделать ваши программы полезными и интересными, вам
нужно создать новый тип, производный от TWindow. Новый тип насле-
дует поля и методы TWindow и добавляет часть своих. В общем слу-
чае объектно-ориентированный подход позволяет вам не "изобретать"
каждый раз окно.
Описатели
-----------------------------------------------------------------
Объект окна имеет по крайней мере три поля: HWindow. Parent
и ChildList. HWindow содержит описатель окна. Описатель окна -
это уникальное число, которое связывает интерфейсный объект (та-
кой как окно, диалоговый блок или объект управляющего элемента) с
соответствующим элементом экрана.
Примечание: Подробно об описателях окна их использо-
вании рассказывается в Главе 10 "Объекты окна".
Таким образом, HWindow содержит целое значение, идентифици-
рующее соответствующий элемент экрана. Это напоминает бирку на
связке ключей. Аналогично тому как вы выбираете ключ, чтобы дос-
тать из шкафа пальто, вы выбираете описатель для получения окна.
В большинстве случаев вы работаете с объектами окна, и у вас нет
необходимости манипулировать описателем окна непосредственно, но
они используются при вызове функций Windows. Например, на данном
шаге вы вызываете функцию MessageBox. Эта функция требует указа-
ния параметра, идентифицирующего порождающее окно сообщений. Вы
указываете основное окно, описатель которого записан в его поле
HWindow:
MessageBox(MainWindow^.HWindow, 'Хотите сохранить?',
'Файл не изменен', mb_YesNo or mb_IconQuestion);
Порождающие и дочерние окна
-----------------------------------------------------------------
Большинство приложений используют несколько окон, и, чтобы
они взаимодействовали, их требуется связать. Например, когда вы
завершаете приложение, оно должно иметь способ очистки всех окон,
за которые отвечает. В общем случае Windows справляется с этим,
связывая окна как дочернее и порождающее. Порождающее окно отве-
чает за свое дочернее окно. ObjectWindows предусматривает для
каждого объекта окна поля для отслеживания порождающего и дочер-
них окон.
Примечание: Взаимодействие этих окон подробнее описы-
вается в Главе 9.
Поле Parent содержит указатель на порождающий оконный объ-
ект. Это не порождающее окно в смысле предка, а скорее окно-вла-
делец. Взаимосвязь этих окон описывается в шаге 10.
Третье поле оконного объекта - это поле ChildList, содержа-
щее связанный список дочерних окон.
Создание нового типа окна
-----------------------------------------------------------------
Теперь у вас есть некоторое представление о том, что содер-
жит оконный объект, и вы можете создать новый оконный тип, произ-
водный от 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.
Аналогично, когда пользователь щелкает правой кнопкой "мы-
ши", основной оконный объект получает сообщение 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;
Примечание: Программы, которые вызывают MessageBox или
другие функции API Windows, должны использовать модуль
WinProcs.
+-----------------------------------------------------------+-+-+
|#=#XXXXXXXXXXXXXXXXXXXXStepsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|^|v|
+-----------------------------------------------------------+-+-|
| |
| +----------------------------------------------+ |
| |@=@#########Диспетчеризуемое сообщение########| |
| +----------------------------------------------| |
| | | |
| | Вы нажали левую кнопку мыши | |
| | +-----------+ | |
| | |####OK#####| | |
| | +-----------+ | |
| +----------------------------------------------+ |
| |
+---------------------------------------------------------------+
Рис. 1.2 Программа Steps реагирует на пользовательское собы-
тие.
Завершение прикладной программы
-----------------------------------------------------------------
Программа Steps завершает выполнение, когда пользователь
дважды щелкает "мышью" в блоке управляющего меню основного окна -
маленьком квадрате в левом верхнем углу. Окно и приложение немед-
ленно закрываются. Такое поведение годится для простых программ,
но может вызвать затруднения в более сложных случаях.
Перед выходом хорошо написанное приложение всегда спрашива-
ет, хочет ли пользователь сохранить несохраненные результаты ра-
боты. Такой вид поведения вы легко можете добавить в свою прик-
ладную программу ObjectWindows. Начните со Step и добавьте двой-
ную проверку запроса пользователя на выход.
Когда пользователь пытается закрыть приложение
ObjectWindows, Windows посылает основному окну сообщение
wm_Close, которое вызывает метод CanClose приложения. CanClose -
это булевская функция, указывающая, можно ли завершить (OK) при-
ложение (True). По умолчанию метод CanClose наследуется из вызова
TApplication метода CanClose основного оконного объекта. В боль-
шинстве случаев решение о закрытии (OK) принимается объектом ос-
новного окна.
Переопределение 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;
Теперь когда пользователи попытаются закрыть Step, они полу-
чат окно сообщений с запросом "Хотите сохранить". Щелчок "мышью"
на командной кнопке Yes (Да) приводит к тому, что CanClose возв-
ращает значение False и предотвращает закрытие основного окна и
приложения. Щелчок "мышью" на No (Нет) возвращает True, и прило-
жение завершает работу. На шаге 8 это окно сообщений получит не-
который смысл. Модифицированная программа Steps показана на Рис.
1.3.
+-----------------------------------------------------------+-+-+
|#=#XXXXXXXXXXXXXXXXXXXXStepsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|^|v|
+-----------------------------------------------------------+-+-|
| |
| |
| +----------------------------------------------+ |
| |@=@#########Изображение изменилось############| |
| +----------------------------------------------| |
| | @@@ | |
| | @?@ Хотите сохранить? | |
| | @@@ +-----------+ +-----------+ | |
| | |###Yes#####| |####No#####| | |
| | +-----------+ +-----------+ | |
| +----------------------------------------------+ |
| |
+---------------------------------------------------------------+
Рис. 1.3 Программа Steps с переопределенным поведением окна.
Дальнейшее изменение закрытия
-----------------------------------------------------------------
Естественно, сообщение о том, что изображение изменилось,
полезно только в том случае, если программа действительно обнару-
живается изменение изображения. Добавив в TStepWindow поле типа
Boolean, вы можете задать флаг, указывающий на изменение изобра-
жения, и выводить окно сообщения только тогда, когда этот флаг
установлен.
Нужно помнить о том, что когда вы добавляете это поле, поле
нужно также инициализировать, поэтому переопределим конструктор
TStepWindow:
type
PStepWindow = ^TStepWindow;
TStepWindow = object(TWindow)
HasGhanged: Boolean;
constructor Init(AParent: PWindowsObject: ATitle:
PChar);
.
.
.
end;
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);
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.
Назад | Содержание | Вперед