Глава 4. Работа с диалоговым блоком
Теперь, когда вы оснастили программу Steps созданным из ре-
сурса диалоговым блоком, нужно научиться взаимодействовать с этим
диалоговым блоком. Сначала вы создадите объект, инкапсулирующий
все характеристики окна пера, затем используете диалоговый блок
для установки и изменения этих характеристик.
В данной главе описаны следующие шаги:
* Определение объекта пера.
* Создание сложного диалогового блока.
* Добавление управляющих объектов.
* Создание буфера передачи.
* Выполнение диалогового блока.
* Чтение результатов.
Шаг 6: Изменение атрибутов пера
-----------------------------------------------------------------
+-----------------------+
| Step 1: Basic App |
| Step 2: Text |
| Step 3: Lines |
| Step 4: Menu |
| Step 5: About Box |
|XStepX6:XPensXXXXXXXXXX|
| Step 7: Painting |
| Step 8: Streams |
| Step 9: Printing |
| Step 10: Palette |
| Step 11: BWCC |
| Step 12: Custom ctrls |
+-----------------------+
Данный шаг охватывает ряд тем, касающихся изобразительных
средств Windows, особенно перьевых средств, применяемых для изоб-
ражения линий. Перья Windows имеют три отдельных атрибута: стиль,
ширину и цвет.
Первая часть данного шага - создание объекта для представле-
ния пера не является абсолютно необходимой, но позволяет вашему
окну работать с пером как единой сущностью, а не отслеживать от-
дельно все атрибуты перьев. Инкапсулируя перо, вы можете также
избежать необходимости иметь дело с некоторыми повторяющимися де-
талями использования инструментальных средств GDI, аналогично то-
му, как объекты окон ObjectWindows предохраняют вас от мелких де-
талей, связанных с созданием окна.
Создание объекта пера
-----------------------------------------------------------------
Хотя 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 позволяют
сохранять объекты пера в потоке.
Выбор и удаление объектов пера
Наиболее интересную работу выполняют процедуры 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 объекта пера, поскольку объект пера может управ-
лять цветом и стилем наряду с размером).
Создание сложного диалогового блока
-----------------------------------------------------------------
До сих пор вы использовали достаточно простой диалоговый
блок (см. блок About Box в шаге 5). Особенно полезными становятся
диалоговые блоки, когда вы можете устанавливать и считывать зна-
чения их управляющих элементов.
В модуле Pen определяется более сложный ресурс диалогового
блока с именем 'PenDlg', который дает вам возможность изменения
атрибутов только что определенного объекта пера. Этот диалоговый
блок показан на Рис. 4.1.
+---------------------------------------------------+
|#=#XXXXXXXXXXSet Pen AttributesXXXXXXXXXXXXXXXXXXXX|
+---------------------------------------------------|
| +-Color----------+ +-Style-----------+ |
| | (*) Black | | (*) Solid | |
| | ( ) Purple | | ( ) Dash | |
| | ( ) Blue | | ( ) Dot | |
| | ( ) Cyan | | ( ) DashDot | |
| | ( ) Green | | ( ) DasDotDot | |
| | ( ) Yellow | | ( ) Null | |
| | ( ) Red | +-----------------| |
| | ( ) White | | Width: #1# | |
| +----------------+ +-----------------+ |
+---------------------------------------------------|
| |
| +------------+ +------------+ |
| |####OK######| |##Cancel####| |
| +------------+ +------------+ |
| |
+---------------------------------------------------+
Рис. 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;
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 управляющие объекты поясняются в
следующем разделе.
Управляющие объекты
-----------------------------------------------------------------
Если вашей программе требуется непосредственно взаимодейс-
твовать с управляющими объектами в диалоговом окне (например,
чтобы поместить элементы в блок списка или определить выбор кноп-
ки с независимой фиксацией), с этими управляющими элементами по-
лезно связать объекты. Тогда вы сможете управлять этими элемента-
ми также, как любыми другими объектами в программе.
Использование интерфейсных объектов
-----------------------------------------------------------------
При "обычном" программировании в Windows (то есть без
ObjectWindows), ваша прикладная программа должна взаимодейство-
вать с каждым элементом экрана через функции API Windows. Как вы
уже видели, ObjectWindows облегчает создание и управление диало-
говыми блоками, изолируя вас от Windows путем максимально возмож-
ного использования для представления элементов экрана объектов.
Эти интерфейсные объекты также значительно облегчают взаимодейс-
твие с управляющими элементами в диалоговых блоках.
Примечание: Интерфейсные объекты описываются в Главе
9, а управляющие объекты описываются, в частности, в Главе
12.
Если вам не требуются управляющие объекты, вы все равно смо-
жете взаимодействовать с управляющими элементами, но это приведет
к необходимости частого вызова функций API Windows, передачи уп-
равляющим элементам сообщений и интерпретации результатов.
ObjectWindows значительно облегчает эту задачу, инкапсулируя по-
ведение каждого управляющего элемента в объекте. Передаются и об-
рабатываются те же сообщения, но ObjectWindows заботится обо всех
деталях.
Связь объекта с созданными из ресурса управляющим элементом
достаточно проста: внутри конструктора объекта диалогового блока
вы строите объекты для любых управляющих элементов, которыми хо-
тите манипулировать. Однако вместо использования для построения
управляющих объектов конструктора Init применяется InitResource.
Конструктор InitResource
-----------------------------------------------------------------
Когда вы на этапе выполнения создаете управляющий объект (в
противоположность созданию его из ресурса), вам нужно задать рас-
положение, размер и начальное значение (или состояние) управляю-
щего элемента, а также указать, какой объект является порождаю-
щим. Все эти элементы передаются в качестве параметров конструк-
тору Init объекта.
Связь объекта с управляющим элементом из ресурса намного
проще, так как такая информация как расположение и размер, опре-
деляется ресурсом. Требуется передать конструктору InitResource
только порождающий объект и идентификатор управляющего элемента.
Так как управляющие объекты обычно строятся внутри конструктора
их порождающих диалоговых блоков, указатель порождающего объекта
почти всегда равен @Self.
Как показано в приведенном выше примере, диалог пера модуля
Pen связывает объекты с их управляющими элементами редактирования
(для задания размера пера) и обоими наборами кнопок с зависимой
фиксацией (для задания цвета и стиля).
Заметим, что все управляющие объекты строятся и присваивают-
ся одной и той же локальной переменной AControl. Вашей программе
не придется взаимодействовать ни с одним из этих управляющих эле-
ментов непосредственно, так как пока выполняется режимный диало-
говый блок, остальная часть программы не активна. InitResource
к списку дочерних окон диалогового блока, чтобы обеспечить очист-
ку и уничтожение элементов экрана вместе с диалоговым окном.
В общем случае нет необходимости присваивать дочерним окнам
в режимных диалоговых блоках поля объекта. Однако в шаге 11 вы
увидите, как можно сохранять указатели на управляющие элементы
объектов безрежимного окна, что облегчает работу с ними.
Создание буфера передачи
-----------------------------------------------------------------
Теперь, когда в диалоговом блоке у вас есть объект, связанный
с управляющими элементами диалогового окна, необходим способ для
установки и чтения их значений. Это делается с помощью буфера пе-
редачи. Буфер передачи - это запись, которая содержит одно поле
для каждого управляющего элемента в диалоговом окне, в который
или из которого происходит передача.
Например, диалоговый блок, созданный в шаге 6, имеет поле
редактирования и четырнадцать кнопок с зависимой фиксацией. В уп-
равляющий элемент редактирования требуется передавать строку, а
каждая кнопка с зависимой фиксацией получает значение Word, ука-
зывающее на его выбор. Модуль Pen определяет тип записи, переда-
ваемый TPenDialogs и из него:
type
TPenData = record
XWidth: array[06] of Char;
ColorArray: arra[07] of Word;
StyleArray: array[05] 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;
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;
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.
Назад | Содержание | Вперед