Блоки диалога, или просто диалоги, являются интерфейсными объектами, инкапсулирующими поведение диалоговых блоков. Это дочерние окнам, которые обычно выводятся для выполнения специфических задач (например, конфигурирования принтера или организации ввода текста). Объект TDialog обеспечивает инициализацию, создание и исполнение всех типов блоков диалога. Для каждого типа диалога вашего приложения, как и в случае оконных объектов, вы можете определить производные от TDialog диалоговые блоки.
    ObjectWindows всегда предоставляет два типа диалогов наиболее общего типа, ввод текста и диалог файла. Кроме того, в ObjectWindows имеется тип TDlgWindow, который позволяет вам создавать диалоги, поведение которых более похоже на окно.
    Данная глава охватывает следующие темы:
    Использование объектов диалоговых блоков аналогично
использованию объектов всплывающего окна. Диалоги являются дочерними
окнами своего порождающего окна. Для простых диалоговых блоков,
которые появляются на короткое время, вся обработка диалога может
быть выполнена одним методом объекта порождающего окна. Диалог
может быть сконструирован, выполнен и удален в одном методе, и
нет необходимости хранить диалог в поле объекта. Для более
сложных диалогов может потребоваться записать диалоговый блок в поле
оконного объекта вашего диалогового блока.
    Подобно всплывающим окнам и управляющим элементам,
диалоговые блоки являются дочерними окнами и при конструировании
добавляются к списку ChildList порождающих окон.
    Использование объекта диалогового блока предусматривает
следующие шаги:
    Диалоговые блоки конструируются и специфицируются с помощью
описания ресурса, создаваемого вне программы. Ресурс диалогового
окна описывает внешний вид и размещение управляющих элементов,
таких как кнопок, блоков списка, областей редактирования и
текстовых строк. Он описывает только внешний вид диалогового блока и
не касается его поведения - за это отвечает прикладная программа.
    Каждый ресурс диалогового блока имеет идентификатор, который
может быть номером идентификатора (Word) или строкой (PChar).
Этот идентификатор позволяет объекту диалогового блока задавать,
какой ресурс используется для определения его внешнего вида.
    Чтобы построить объект диалогового блока, вызовите
конструктор Init. Init воспринимает в качестве своих параметров
указатель на порождающее окно и параметр типа PChar, представляющий
имя ресурса диалога:
    Если идентификатор задается номером, его требуется привести
с помощью MakeIntResource к PChar:
    Так как диалоговые блоки обычно строятся внутри метода
оконного объекта, порождающее окно почти всегда задается как Self.
Объекты диалоговых блоков, не создаваемые оконными объектами,
должны иметь в качестве порождающего Applicartion^.MainWindow
(поскольку это единственный оконный объект, всегда присутствующий
в каждой программе ObjectWindows).
    Выполнение диалогов аналогично созданию и отображению окна.
Однако, поскольку диалоги обычно появляются на более короткий
отрезок времени, некоторые этапы могут быть сокращены. Это в
значительной степени зависит от того, будет ли диалоговый блок
отображаться как режимный или безрежимный.
    Режимные диалоговые блоки являются наиболее общими блоками
диалога. Аналогично генерируемым функцией MessageBox блокам
сообщений, режимные диалоги отображаются для специфических целей на
короткий отрезок времени. Слово "режимный" означает, что пока
отображается диалог, пользователь не может выбрать или
использовать его порождающее окно. Пользователь должен воспользоваться
диалогом и выбрать командную кнопку OK или Cancel для прекращения
диалога и возвращению к работе с программой. Режимный диалог как
бы "замораживает" выполнение оставшейся части программы.
    Безрежимный диалоговый блок не приостанавливает выполнения
программы. Как и оконный объект, он может создаваться и
выполняться в одном шаге с помощью MakeWindow:
    В любое момент вы можете считать данные из диалогового окна
(если объект диалогового блока еще существует). Чаще всего это
выполняется в методе OK, который вызывается при активизации
пользователем командной кнопки OK.
    В случае режимных диалоговых блоков лучше всего, вероятно,
строить, выполнять и уничтожать все объекты в одном методе (как
показано в примерах данной главы). Таким образом, при каждом
выводе диалогового блока это будет новый объект.
    Объекты приложения имеют режимный эквивалент MakeWindow,
который называется ExecDialog. Аналогично MakeWindows, ExecDialog
проверяет допустимость передаваемого объекта диалогового блока
(то есть успешность выполнения конструктора объекта и отсутствие
ситуации нехватки памяти), а затем выполняет диалоговый блок,
делая его модальным.
    ExecDialog возвращает целочисленное значение, указывающее,
что пользователь закрывает диалоговое окно. Возвращаемое значение
- это идентификатор задействованного пользователем управляющего
элемента, такой как id_Ok для командной кнопки OK или id_Cancel
для командной кнопки Cancel. После завершения выполнения
диалогового окна ExecDialog уничтожает объект диалогового окна.
    Таким образом, с помощью одного вызова метода ExecDialog вы
можете создать, вывести на экран и завершить диалоговый блок.
    Безрежимные диалоги похожи на всплывающие окна и управляющие
элементы. Основная причина, по которой вы не можете удалять
безрежимными диалогами сразу же после их отработки (в отличие от
режимных), состоит в том, что вы заранее не знаете, когда
пользователь закроет блок диалога. (Помните о том, что в режимных
диалогах метод ExecDialog не возвращает значения до закрытия диалога.)
Следовательно лучше всего конструировать безрежимные диалоги в
конструкторе его порождающего окна и хранить в поле порождающего
объекта.
    В отличие от окон и объектов управления, используемых в
качестве дочерних окон, диалоги автоматически не отображаются при
выводе их порождающих окон.
    Затем, каждый раз, когда вы хотите отобразить диалог,
создайте и выведите его:
    И, наконец, отдельный объект диалога будет автоматически
удаляться при закрытии его порождающего окна.
    Диалоговые блоки отличаются от других дочерних окон, таких
как всплывающие окна и управляющие элементы, тем, что они за
время существования своего порождающего окна создаются и
уничтожаются многократно и редко выводятся на экран и уничтожаются вместе с
порождающим окном. Обычно программа создает диалоговый блок в
ответ на выбор меню, щелчок кнопкой "мыши", ошибку или другое
событие.
    Таким образом, нужно убедиться, что вы не строите объекты
диалоговых блоков снова и снова, не уничтожая их. Помните о том,
что все построенные диалоговые объекты автоматически включаются в
списки дочерних окон их порождающих окон.
    Примечание: К режимным диалоговым блокам это не
относится, так как они автоматически уничтожаются при закрытии.
    Каждый блок диалога должен иметь способ его закрытия
пользователем. Чаще всего это кнопки OK и/или Cancel. Потомки TDialog
автоматически отреагируют на нажатие одной из этих кнопок вызовом
метода EndDlg, который заканчивает диалог. Вы можете разработать
новые средства завершения диалога, если только они приводят к
вызову EndDlg. Для изменения поведения при закрытии вы можете
переопределить методы OK и Cancel.
    Например, вы можете переопределить метод OK таким образом,
что введенные данные будут копироваться в буфер, который
находится вне объекта блока диалога. Если ввод был осуществлен
некорректно, вы можете вывести блок сообщения или сгенерировать
звуковой сигнал. Если ввод был сделан верно, вы можете вызвать EdnDlg.
Переданное в EndDlg значение становится возвращаемым значением
ExecDialog.
    Как и в случае оконных объектов, объект диалога вызывает
CanClose до закрытия блока диалога, как это имело место для
объектов окна. Вы можете переписать CanClose для учета условий
закрытия, как для блока диалога, который проверяет ввод
пользователя. При переписывании CanClose нужно быть уверенным в том, что
вызывается унаследованный от него метод, т.к. он вызывает методы
CanClose дочерних окон.
    Все блоки диалога, кроме самых простейших, имеют (как
дочерние окна) несколько управляющих элементов (например, управления
редактированием, блоки списка и командные кнопки). Обратите
внимание на то, что эти управляющие элементы являются не объектами
управляющих элементов, а только управляющими интерфейсными
элементами, без методов и полей объекта. Эта глава кроме того
показывает альтернативные методы, позволяющие связывать объекты
управления с элементами управления диалоговых блоков с
использованием InitResource.
    Примечание: Использование управляющих объектов в окне
(но не блоков диалога) показано в Главе 12.
    Между объектом диалога и его элементами управления имеется
двухсторонняя связь (можно сказать диалог). С одной стороны
диалогу нужно манипулировать его управляющими элементами, например,
для заполнения блока списка. С другой стороны ему нужно
обрабатывать и реагировать на сгенерированные сообщения управляющих
событий, например, когда пользователь выбирает элемент блока списка.
    Windows определят набор сообщений управляющих элементов,
которые посылаются от приложения к Windows. Например, имеются
следующие сообщения блока списка: lb_GetText, lb_GetCurSel и
lb_AddString. Сообщения управляющих элементов задают
специфическое управление и несут с собой информацию в аргументах wParam и
lParam. Каждый управляющий элемент в ресурсе диалога имеет номер
идентификатора, который вы используете для задания управляющего
элемента, принимающего сообщение. Для посылки сообщения
управляющему элементу нужно вызвать метод TDialg SendDlgItemMsg.
Например, данный метод заполнит блок списка диалога элементами текста
путем посылки сообщения lb_AddString:
где id_LB1 есть константа, равная ID блока списка.
    Если вам потребуется описатель одного из управляющих
элементов диалога, его можно получить методом GetItemHandle:
    Когда пользователь выбирает управляющий элемент, например,
"нажимает" командную кнопку или делает выбор в блоке списка,
диалоговым блоком управляющего элемента порождающего окна
принимается специальное сообщение, основанное на дочернем окне и
называемое управляющим сообщением (сообщение управляющего элемента).
Определим метод реакции на сообщение, основанное на дочернем
идентификаторе, в порождающем типе диалога для каждого дочернего
управляющего элемента:
    В данном примере id_BN1 - это идентификатор кнопки
управляющего элемента, а id_LB1 - это идентификатор блока списка. Щелчок
"мышью" на командной кнопке даст сообщение, посылаемое в
диалоговый блок. Объект диалогового блока реагирует через динамический
метод с индексом, основанным на идентификаторе кнопки IDBN1.
    Примечание: Уведомляющие сообщения подробно поясняются
в Главе 16 "Сообщения Windows"
    Каждое управляющее информационное сообщение поступает с
кодом уведомления - целочисленной константой, которая
идентифицирует произведенное действие. Например, результатом выбора элемента
из блока списка будет сообщение с кодом lbn_SelChange, где lbn_
уведомление блока списка (List Box Notification). Нажатие кнопки
"мыши" дает сообщение bn_Clicked. Ввод в управляющем элементе
редактированием приводит к сообщению с кодом en_Changed. Имеются
коды уведомления для блоков списка, управляющих элементов
редактированием, комбинированных блоков и командных кнопок. Код
уведомления передается в Msg.lParamHi сообщения. Для восприятия
управляющих информационных сообщений напишем метод реакции для типа
диалога, обрабатывающий важные коды уведомления:
    Управляющие элементы, имеющие соответствующие объекты, могут
отвечать на уведомления сами. См. Главу 16.
    В файле с текстом программы DIALTEST.PAS, основное окно
имеет режимный диалог, определенный типом диалога TTestDialog. Эта
программа обеспечивает двухстороннюю связь между объектом диалога
и его управляющими элементами. Два метода - IDBN1 и IDLB1 -
являются методами реакции, основанными на дочерних идентификаторах, и
вызываются при выборе пользователем управляющих элементов
(дочерних окон). Например, при выборе пользователем кнопки диалога BN1
('Fill List Box') вызывается метод IDBN1. Аналогично, когда
пользователь делает выбор в блоке списка, вызывается IDLB1. С другой
стороны, для заполнения блока списка элементами текста код метода
IDBN1 посылает в диалог управляющее сообщение, lb_AddString,
используя метод диалога SendDlgItemMsg.
    Эта программа также показывает как путем создания нового
типа диалога и связывания его с ресурсом диалога в вызове
конструктора Init метода TestWindow.RunDialog создаются новые диалоги.
Полный текст программы вы можете найти на дистрибутивных дисках.
    До этого момента мы имели дело с реакцией блоков диалога на
управляющие информационные сообщения, которая использовала методы
реакции, основанные на дочерних идентификаторах. Однако, иногда
более предпочтительно, чтобы управляющий элемент сам реагировал
на сообщение. Например, вам может потребоваться управляющий
элемент редактирования, который позволяет вводить только цифры, или
командная кнопка, которая меняет стиль при своем "нажатии". Это
можно реализовать с помощью объектов управляющих элементов в
окнах (см. Главу 12). Однако, чтобы это имело место для управляющих
элементов диалога, созданного с файлом ресурса, вам нужно
использовать для конструирования объекта другой конструктор.
    При организации связей вы создаете объект управляющего
элемента для представления управляющего объекта диалога. Этот объект
управления дает вам гибкость в реакции на управляющие сообщения.
Он дает вам возможность использования набор методов объектов
управляющих элементов, описанных в Главе 12.
    Для связи объекта с управляющим элементом определите сначала
объект управляющего элемента. Он должен быть создан в
конструкторе диалога. Однако, вместо того, чтобы использовать конструктор
Init, как это показано в Главе 12, следует использовать
InitResource, который берет в качестве параметров порождающее
окно и идентификатор управляющего элемента (из ресурса диалога).
Это приводит к вызову методов реакции на сообщения объектов
управляющих элементов вместо обработки элементов по умолчанию. Для
этого нужно определить новый тип объекта, производный от
предусмотренного типа управляющего элемента.
    Обратите внимание, что в отличие от задания оконного
объекта, которое предполагает два шага (Init и MakeWindow), поскольку
управляющий элемент уже существует, связь объекта с управляющим
элементов выполняется за один шаг: он загружается из диалогового
ресурса. Вам нужно только сообщить InitResource, какой
управляющий элемент из ресурса вы хотите связать с объектом, используя
идентификатор управляющего элемента.
    Основная разница между диалоговыми блоками и окнами состоит
в том, что диалог имеет соответствующий ресурс и задает тип и
расположение своих управляющих элементов. Но окно также может
иметь управляющие элементы.
    Одним из подходов размещения управляющих элементов в окне
является использование объектов управляющих элементов (как
показано в Главе 12). Другой подход - это слияние возможностей
диалоговых блоков и окон, как это делается в объектном типе
TDlgWindow, что позволяет получить гибридный объект, называемый
диалоговым окном. Второй подход предусматривает более удобный
способ построения и управления многими управляющими элементами в
окне. Кроме того, он предлагает для диалоговых блоков более
гибкие средства окон.
    TDglWindow является потомком TDialog и наследует его методы,
такие как Execute, Create, Ok и EndDlg. Как и диалоговые блоки,
диалоговые окна имеют соответствующий ресурс диалогового блока. С
другой стороны, как и окна, диалоговые окна имеют соответствующий
класс окон, определяющий среди всего прочего пиктограмму, курсор
и меню. Из-за связи с оконным классом в потомке TDlgWindow
следует переопределять методы GetClassName и GetWindowClass. Этот
класс должен быть тем же, что и перечисленный в диалоговом
ресурсе.
    В большинстве случаев вы будете выполнять диалоговые окна
как и другие окна или безрежимные диалоговые окна с помощью
методов Create и Show, а не метода Execute.
    В тех случаях, когда основное окно должно содержать много
сложных управляющих элементов, хорошим использованием диалоговых
окон является основное окно приложения. Например,
программа-калькулятор может иметь в качестве основного окна диалоговое окно,
где кнопки калькулятора заданы как управляющие элементы
диалогового ресурса. Это позволило бы вывести в основном окне также
меню, пиктограмму и курсор.
    Для выполнения двух типов общих функций ObjectWindows
предусматривает стандартные диалоговые блоки. Одно их них, окно
диалогового блока, выводит пользователю однострочную подсказку.
Другое, файловое диалоговое окно, позволяет пользователю задать имя
файла и каталог для открытия или сохранения файла.
    Диалоговые блоки ввода - это простые объекты диалоговых
блоков, определяемых типом TInputDialog, которые выводят
пользователю подсказку со строкой текста.
    Вы можете запускать диалоги ввода как режимные или
безрежимные диалоговые блоки, но обычно вы будете выполнять их как
режимные. С объектом диалога ввода связан ресурс диалога ввода. Он
находится в файле ObjectWindows OSTDDLGS.RES.
    Примечание: Использование модуля StdDlgs автоматически
включает ресурсы в OSTDDLGS.RES.
    Каждый раз при конструировании диалога ввода с
использованием метода Init, вы задаете для диалога заголовок, подсказку и
текст по умолчанию. Покажем вызов конструктора Init объекта
диалога ввода:
    В данном примере EditText - это текстовый буфер, который
заполняется вводом пользователя, когда он "нажимает" кнопку OK.
Когда пользователь "нажимает" кнопку OK или клавишу Enter, строка
введенного в диалоге ввода текста автоматически передается в
массив символов, который хранит текст по умолчанию. В данном примере
конструируется и отображается блок диалога и считывается текст:
    Файловые диалоговые блоки являются другим типом диалогов,
поставляемых с ObjectWindows в типе TFileDialog. Файловый
диалоговый блок следует использовать каждый раз, когда вы желаете
побудить пользователя ввести имя файла, например в функциях File
Open и File Save во всех приложениях. См. Рис. 11.1.
    В большинстве случаев файловые диалоговые блоки выполняются
как режимные. С объектом файлового диалога связан ресурс
файлового диалогового блока, имеющийся в ObjectWindows в файле
OSTDDLGS.RES. Использование модуля OStdDlgs автоматически
включает файл ресурса.
Использование объектов диалоговых блоков
Построение объекта
Вызов конструктора
ADlg:=New(PSampleDialog, Init(@Self, 'EMPLOYEEINFO'));
Dlg := New(PSampleDialog, Init(@Self, PChar(120)));
Выполнение диалоговых блоков
Режимные и безрежимные диалоговые блоки
Application^.MakeWindow(ADlg);
Выполнения режимных диалоговых блоков
ADlg := New(PSampleDialog, Init(@Self, 'RESOURCEID'));
ReturnValue := Application^.ExecDialog(ADlg);
if ReturnValue = id_OK then
{ кодирование для выборки данных и обработки диалога }
else
if ReturnValue = id_Cancel then { нажата Cancel }
Выполнение безрежимных диалоговых блоков
constructor ParentWindow.Init(AParent: PWindowsObject;
ATitle: PChar);
begin
TWindow.Init(AParent, ATitle);
ADlg := New(PSampleDialog, Init(@Self, 'EMPLOYEEINFO'));
end;
begin
Application^.MakeWindow(ADlg)
end;
Работа с безрежимными диалоговыми блоками
Завершение диалогов
Работа с управляющими элементами
Взаимодействие с управляющим элементом
procedure TestDialog.FillListBox(var Msg: TMessage);
var
TextItem: PChar;
begin
TextItem := 'Item 1';
SendDlgItemMsg(id_LB1, lb_AddString, 0, Longint(TextItem));
end;
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;
procedure TestDialog.HandleLB1Msg(var Msg: TMesage);
begin
case Msg.lParamHi of
lbn_SelChange: { изменение порядка выбора };
lbn_DblClk: { выбор с помощью двойного щелчка };
end;
end;
Пример связи
Ассоциирование объектов управляющих элементов
Использование диалоговых окон
Использование предопределенных диалоговых окон
Использование диалоговых блоков ввода
var SomeText: array[0..79] of Char;
begin
AnInputDlg.Init(@Self, 'Caption', 'Prompt', SomeText,
SizeOf(SomeText))
.
.
.
end;
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;
Файловые диалоговые блоки
Рис. 11.1 Файловый диалоговый блок.