Теперь, когда вы оснастили программу Steps созданным из ресурса диалоговым блоком, нужно научиться взаимодействовать с этим диалоговым блоком. Сначала вы создадите объект, инкапсулирующий все характеристики окна пера, затем используете диалоговый блок для установки и изменения этих характеристик.     В данной главе описаны следующие шаги:
    Данный шаг охватывает ряд тем, касающихся изобразительных
средств Windows, особенно перьевых средств, применяемых для
изображения линий. Перья Windows имеют три отдельных атрибута: стиль,
ширину и цвет.
    Первая часть данного шага - создание объекта для
представления пера не является абсолютно необходимой, но позволяет вашему
окну работать с пером как единой сущностью, а не отслеживать
отдельно все атрибуты перьев. Инкапсулируя перо, вы можете также
избежать необходимости иметь дело с некоторыми повторяющимися
деталями использования инструментальных средств GDI, аналогично
тому, как объекты окон ObjectWindows предохраняют вас от мелких
деталей, связанных с созданием окна.
    Хотя Windows ссылается на свои изобразительные средства как
на "объекты" (отсюда и имена типа SelectObject и DeleteObject),
они не являются объектами в истинном объектно-ориентированном
смысле, так как не используют наследование и полиморфизм. Перо на
самом деле представляет собой просто группу из трех характеристик
отображения, на которые Windows ссылается при изображении линии.
Эти характеристики являются просто свойствами контекста дисплея,
но полезно рассматривать их, как встроенные в перо.
    Три характеристики пера - это его стиль, размер и цвет. В
шаге 3 вы изменяли размер пера и отслеживали текущий размер пера
в поле объекта окна. Вместо реализации трех отдельных полей для
отслеживания характеристик пера вы можете инкапсулировать их в
единый объект TPen. Описание TPen имеет следующий вид:
    Примечание: Большую часть исходного кода из данной
главы вы можете найти в файле 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.
    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
присоединяет диалоговый блок к одному из окон, о присутствии которых
известно заранее - основному окну приложения:
    Другим важным отличием является то, что на этот раз вы
имеете новый производный объектный тип TPenDialog. Так как окно About
box не использует ничего, кроме назначенного по умолчанию
поведения диалогового окна, инкапсулированного в TDialog, вам не
требуется создавать для него новый объектный тип. Однако диалог
атрибутов пера отличается более сложным поведением и требует
настройки объекта.
    Приведем определение TPenDialog из модуля Pen:
    Построенные в TPenDialog управляющие объекты поясняются в
следующем разделе.
    Если вашей программе требуется непосредственно
взаимодействовать с управляющими объектами в диалоговом окне (например,
чтобы поместить элементы в блок списка или определить выбор
кнопки с независимой фиксацией), с этими управляющими элементами
полезно связать объекты. Тогда вы сможете управлять этими
элементами также, как любыми другими объектами в программе.
    При "обычном" программировании в Windows (то есть без
ObjectWindows), ваша прикладная программа должна
взаимодействовать с каждым элементом экрана через функции API Windows. Как вы
уже видели, ObjectWindows облегчает создание и управление
диалоговыми блоками, изолируя вас от Windows путем максимально
возможного использования для представления элементов экрана объектов.
Эти интерфейсные объекты также значительно облегчают
взаимодействие с управляющими элементами в диалоговых блоках.
    Примечание: Интерфейсные объекты описываются в Главе
9, а управляющие объекты описываются, в частности, в Главе 12.
Если вам не требуются управляющие объекты, вы все равно
сможете взаимодействовать с управляющими элементами, но это приведет
к необходимости частого вызова функций API Windows, передачи
управляющим элементам сообщений и интерпретации результатов.
ObjectWindows значительно облегчает эту задачу, инкапсулируя
поведение каждого управляющего элемента в объекте. Передаются и
обрабатываются те же сообщения, но ObjectWindows заботится обо всех
деталях.
    Связь объекта с созданными из ресурса управляющим элементом
достаточно проста: внутри конструктора объекта диалогового блока
вы строите объекты для любых управляющих элементов, которыми
хотите манипулировать. Однако вместо использования для построения
управляющих объектов конструктора Init применяется InitResource.
    Когда вы на этапе выполнения создаете управляющий объект (в
противоположность созданию его из ресурса), вам нужно задать
расположение, размер и начальное значение (или состояние)
управляющего элемента, а также указать, какой объект является
порождающим. Все эти элементы передаются в качестве параметров
конструктору Init объекта.
    Связь объекта с управляющим элементом из ресурса намного
проще, так как такая информация как расположение и размер,
определяется ресурсом. Требуется передать конструктору InitResource
только порождающий объект и идентификатор управляющего элемента.
Так как управляющие объекты обычно строятся внутри конструктора
их порождающих диалоговых блоков, указатель порождающего объекта
почти всегда равен @Self.
    Как показано в приведенном выше примере, диалог пера модуля
Pen связывает объекты с их управляющими элементами редактирования
(для задания размера пера) и обоими наборами кнопок с зависимой
фиксацией (для задания цвета и стиля).
    Заметим, что все управляющие объекты строятся и
присваиваются одной и той же локальной переменной AControl. Вашей программе
не придется взаимодействовать ни с одним из этих управляющих
элементов непосредственно, так как пока выполняется режимный
диалоговый блок, остальная часть программы не активна. InitResource
к списку дочерних окон диалогового блока, чтобы обеспечить
очистку и уничтожение элементов экрана вместе с диалоговым окном.
    В общем случае нет необходимости присваивать дочерним окнам
в режимных диалоговых блоках поля объекта. Однако в шаге 11 вы
увидите, как можно сохранять указатели на управляющие элементы
объектов безрежимного окна, что облегчает работу с ними.
    Теперь, когда в диалоговом блоке у вас есть объект, связанный
с управляющими элементами диалогового окна, необходим способ для
установки и чтения их значений. Это делается с помощью буфера
передачи. Буфер передачи - это запись, которая содержит одно поле
для каждого управляющего элемента в диалоговом окне, в который
или из которого происходит передача.
    Например, диалоговый блок, созданный в шаге 6, имеет поле
редактирования и четырнадцать кнопок с зависимой фиксацией. В
управляющий элемент редактирования требуется передавать строку, а
каждая кнопка с зависимой фиксацией получает значение Word,
указывающее на его выбор. Модуль Pen определяет тип записи,
передаваемый TPenDialogs и из него:
    Каждый потомок TWindowsObject имеет поле TransferBuffer.
Когда вы хотите передать данные в диалоговое окно, нужно задать
объект TransferBuffer диалогового блока, указывающий на запись
передачи:
    Если ваши программы создают объекты диалогового окна
динамически, убедитесь, что они каждый раз назначают буфер передачи.
TransferBuffer по умолчанию имеет значение nil. Это означает, что
данные не переданы.
    Перед фактической передачей данных в диалоговое окно, вам
нужно установить значение полей в буфере передачи. Перед выводом
диалогового окна пера это делает TPen.ChangePen:
    SetColorAttr и SetStyle используют то преимущество, что
буфер передачи задает кнопки с зависимой фиксацией в виде массива
значений Word. SetStyle, например, выглядит следующим образом:
    Примечание: SetColorAttr выполняет то же назначение,
что и ColorArray. bf_Checked и bf_Unchecled - это константы
ObjectWindows.
    После того как вы создадите буфер передачи и заполняет его
значениями, получение этой информации в диалоговом блоке не
представляет труда, поскольку все за вас делает ObjectWindows.
Когда для выполнения диалогового блока вызывается ExecDialog, он
вызывает TransferDatа для копирования значений из буфера передачи
в отдельные объекты управляющих элементов.
    Когда вы завершите диалоговое окно, щелкнув "мышью" на
командной кнопке OK, ExecDialog перед уничтожением диалогового
блока и его управляющих элементов передает значения из управляющего
элемента обратно в буфер передачи. Отмена диалогового блока или
его закрытие с помощью управляющего меню обходит механизм
передачи данных обратно в буфер передачи.
    Таким образом, буфер передачи указывает на постоянный набор
данных, не зависящий от диалогового блока. Во многих случаях
диалоговый блок создается и уничтожается при выполнении программы
многократно, а присваивание каждый раз его поля TransferData
одной и той же записи данных позволяет выводить управляющие
элементы так, как они выглядели при последнем закрытии диалогового
блока.Шаг 6: Изменение атрибутов пера
Создание объекта пера
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;
Создание сложного диалогового блока
Рис. 4.1 Диалоговый блок с изменением атрибутов пера.
procedure TPent.ChangePen;
var PenDlg: PPenDialog;
begin
.
.
.
PenDlg := New(PPenDialog, Init(Application^.MainWindow,
'PenDlg'));
.
.
.
end;
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;
Управляющие объекты
Использование интерфейсных объектов
Конструктор InitResource
Создание буфера передачи
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 кнопок соответственно, удобно задать поле для каждой
группы.
PenDlg := New(PPenDialog, Init(Application^.MainWindow,
'PenDlg'));
PenDlg^.TransferBuffer := @PenData;
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;
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;
Передача данных