ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Часть 12 Глава 11. Объекты диалога Блоки диалога, или просто диалоги, являются дочерними окнами, которые обычно появляются для выполнения специфических задач (например, конфигурирования принтера или организации ввода текста. Тип объекта TDialog обеспечивает инициализацию, создание и испол нение всех типов блоков диалога. Для каждого типа диалога вашего приложения, как и в случае с TWindow, вы можете определить производные от TDialog типы диалога. ObjectWindows всегда предоставляет два типа диалогов наиболее общего типа, ввод текста и диал ог файла. Кроме того, в ObjectWindows имеется тип TDlgWindow, который позволяет вам создавать диалоги, поведение которых более похоже на окно. Использование ресурсов диалога Подобно меню, диалоги разрабатываются и специфицируются с помощью описания ресурса, который создается вне кода программы. Ресурс диалога описывает появление и размещения управлений (например, кнопок), блоков списка, областей редактирования и текстовых ст рок. Он описывает только появление диалога и не описывает его поведения. За это отвечает приложение. С ресурсом диалога связан идентификатор, который может быть номером ID (Word) или строкой (PChar). В теле приложения объект диалога связывается с ресурсо м диалога через ID ресурса. Использование объектов диалога Использование объектов диалога подобно использованию объектов всплывающего окна. Диалоги являются дочерними окнами своего родительского окна. Для простых диалогов, которые появляются на короткое время, вся обработка диалога может быть сделана одним метод ом объекта родительского окна. Диалог может быть сконструирован, выполнен и удален в одном методе, и нет необходимости хранить диалог в поле объекта. Для более сложных диалогов может потребоваться записать диалог в поле объекта вашего типа диалога. Подобно всплывающим окнам и управлениям, диалоги являются дочерними окнами и добавляются к ChildList их родительских окон при конструировании. Конструирование и инициализация диалогов Объекты диалога конструируются и инициализируются конструктором Init. Init берет в качестве своих параметров указатель на родительское окно и PChar, представляющий имя ресурса диалога: ADlg:=New(PSampleDialog, Init(@Self, 'EMPLOYEEINFO')); или Dlg:=New(PSampleDialog, Init(@Self, PChar(120))); Выполнение диалогов Выполнение диалогов аналогично созданию и отображению окна. Однако, поскольку диалоги обычно появляются на более короткий отрезок времени, некоторые этапы могут быть сокращены. Это в значительной степени зависит от того, будет ли диалог отображаться как модальный или лишенный модели. Модальные и немодальные диалоги Модальные блоки диалога являются наиболее общими блоками диалога. Аналогично генерируемым функцией MessageBox блокам сообщений, модальные диалоги отображаются для специфических целей на короткий отрезок времени. Слово "модальный" означает, что пока отобр ажается диалог, пользователь не может выбрать или использовать его родительское окно. Пользователь должен воспользоваться диалогом и выбрать кнопку OK или Cancel для прекращения диалога и возвращению к работе с программой. Модальный диалог как бы "замора живает" выполнение оставшейся части программы. Т.о. вызовом одного метода ExecDialog вы можете создавать, отображать и заканчивать диалог: ReturnValue:=Application^.ExecDialog(ADlg); if ReturnValue=id_OK then {Кодирование для выборки данных и обработки диалога} else if ReturnValue=id_Cancel then {Cancel was pressed} Если пользователь выберет в блоке диалога кнопку Cancel, то ExecDialog возвратит id_Cancel. Немодальные диалоги не замораживают выполнение программы. Они могут быть созданы и отображены за один шаг с помощью MakeWindow, как объект окна: Application^.MakeWindow(ADlg); Вы можете найти данные в блоке диалога в любой момент времени, пока существует объект диалога. Это чаще всего делается методом OK, который вызывается при выборе пользователем кнопки OK. Завершающие диалоги Каждый блок диалога должен иметь способ его закрытия пользователем. Чаще всего это кнопки OK и/или Cancel. Производные TDialog автоматически отреагируют на нажатие одной из этих кнопок вызовом метода EndDlg, который заканчивает диалог. Вы свободны в том, что можете разработать новые и другие пути заканчивания диалога, если только они приводят к вызову EndDlg. Для изменения поведения при закрытии вы можете переопределить методы OK и Cancel. Например, вы можете переопределить метод OK таким образом, что введенные данные будут копироваться в буфер, который находится вне объекта блока диалога. Если ввод был осуществлен некорректно, вы можете вывести блок сообщения или сгенерировать звуковой си гнал. Если ввод был сделан верно, вы можете вызвать EdnDlg. Переданное в EndDlg значение становится возвращаемым значением ExecDialog. Объект диалога вызывает CanClose до закрытия блока диалога, как это имело место для объектов окна. Вы можете переписать CanClose для учета условий закрытия, как для блока диалога, который проверяет ввод пользователя. При переписывании CanClose нужно быть уверенным в том, что вызывается унаследованный от него метод, т.к. он вызывает методы CanClose дочерних окон. Управление объектами диалога Диалоги отличаются от других дочерних окон (например, всплывающих окон и управлений) тем, что они отображаются и выводятся много раз за время жизни их родительских окон. Диалоги редко отображаются и уничтожаются одновременно с их родителями. Обычно прогр амма воспроизводит блок диалога в ответ на выбор из меню, нажатие кнопки на мыши, возникновение ошибочной ситуации или некоторые другие события. Следовательно, не нужно конструировать все новые и новые объекты блока диалога без их удаления. Следует также помнить о том, что все сконструированные блоки диалога автоматически вставляются в списки дочерних окон их родителей. В случае модальных диалогов вероятно лучше всего конструировать, выполнять и удалять все в одном методе объекта родительского окна, как это показано в примере данной главы. Если идти по этому пути, то каждый раз при появлении диалога это будет новый объ ект. Немодальные диалоги более похожи в этом аспекте на всплывающие окна и управления. Основная причина, по которой вы не можете удалять немодальные диалоги сражу же после их отработки (в отличие от модальных) , состоит в том, что вы заранее не знаете, когда пользователь закроет блок диалога. (Помните о том, что в модальных диалогах метод 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; И, наконец, отдельный объект диалога будет автоматически удаляться при удалении его родительского окна. Управляющие манипуляции и обработка сообщений Все блоки диалога, кроме самых простейших, имеют (как дочерние окна) несколько управлений (например, управления редактированием, блоки списка и кнопки). Обратите внимание на то, что эти управления не являются объектами управления, а только элементами инт ерфейса управления, без методов и полей объекта. Использование объектов управления в окне (но не блоков диалога) показано в Главе 12. Эта глава кроме того показывает альтернативные методы, позволяющие связывать объекты управления с элементами управления диалога с использованием InitResource. Имеется двухсторонняя связь (можно сказать диалог) между объектом управления и его элементами управления. С одной стороны диалогу нужно манипулировать его управлениями, например, для заполнения блока списка. С другой стороны ему нужно обрабатывать и реаг ировать на сгенерированные сообщения управляющих событий, например, когда пользователь выбирает элемент блока списка. Манипуляция управлениями диалога Windows определят набор сообщений управления, которые посылаются от приложения к Windows. Например, имеются следующие сообщения блока списка: lb_GetText, lb_GetCurSel и lb_AddString. Сообщения управления задают специфическое управление и и несут с собой информацию в аргументах wParam и lParam. Каждое управление в ресурсе диалога имеет номер ID, который вы используете для задания управления, которое будет принимать сообщение. Для посылки сообщения управления нужно вызвать метод 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); Реакция на управляющие информационные сообщения Когда пользователь выбирает управление, например, нажимает кнопку или делает выбор в блоке списка, специальное сообщение, основанное на дочернем окне, называемое управляющим сообщением, принимается диалогом управления. Определим метод реакции на сообщени е, основанное на дочернем ID, в родительском типе диалога для каждого дочернего управления: TestDialog=object(TDialog) procedure HandleBN1Msg(var Msg: TMessage); virtual id_First + id_BN1; procedure HandleListBox(var Msg: TMessage); virtual id_First + id_LB1; end; Каждое управляющее информационное сообщение поступает с кодом извещения, целой константой, которая идентифицирует произошедшее действие. Например, результатом выбора элемента из блока списка будет сообщение с кодом 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; Пример связи диалог/управление В приведенном тексте программы, головное окно имеет модальный диалог, определенный типом диалога TestDialog. Эта программа обеспечивает двухстороннюю связь между объектом диалога и его управлениями. Два метода TestDialog, HandleBN1Msg и HandleLB1Msg, явл яются методами реакции, основанными на дочерних ID, и вызываются при выборе пользователем управлений (дочерних окон). Например, при выборе пользователем кнопки диалога BN1 ('Fill List Box') вызывается метод HandleBN1Msg. Аналогично, когда пользователь де лает выбор в блоке списка, вызывается HandleLB1Msg. С другой стороны, код метода HandleBN1Msg посылает в диалог управляющее сообщение, lb_AddString, используя метод диалога SendDlgItemMsg, для заполнения блока списка элементами текста. Эта программа также показывает как создаются новые диалоги путем создания нового типа диалога и связывания его с ресурсом диалога в вызове конструктора Init метода TestWindow.RunDialog. Рис.11.1 показывает результат работы программы. Рис.11.1. Приложения диалога Полный текст программы DIALTEST.PAS содержится на ваших дистрибутивных дисках. Ассоциирование объектов управления До этого момента мы имели дело с реакцией блоков диалога на управляющие информационные сообщения, которая использовала методы реакции, основанные на дочерних ID. Однако, иногда более предпочтительно, чтобы управление само реагировало на сообщение. Наприм ер, вам нужно управление редактированием, которое позволяет вводить только цифры, или кнопку, которая меняет стиль при своем нажатии. Это можно реализовать с помощью объектов управления в окнах (см. Главу 12). Однако, чтобы это имело место для управлений диалога, созданного с файлом ресурса, вам нужно использовать для конструирования объекта другой конструктор. При организации связей вы создаете объект управления для представления объекта управления диалогом. Этот объект управления дает вам гибкость в реакции на управляющие сообщения. Он дает вам возможность использования набор методов объектов управления, опис анных в Главе 12. Для организации управления сначала определяется объект управления. Он должен быть создан в конструкторе диалога. Однако, вместо того, чтобы использовать конструктор Init, как это показано в Главе 12, следует использовать InitResource, который берет в кач естве параметров родительское окно и ID управления (из ресурса диалога). Это приводит к вызову методов реакции на сообщения управляющих объектов вместо обработки элементов по умолчанию. Для этого нужно определить новый тип объекта, производный от поставл яемого типа управления. Обратите внимание на то, что используя InitResource вы можете теперь манипулировать управлением, используя поля и методы объекта (стиль OOP), вместо посылки сообщений элементам интерфейса через Windows API. Также обратите внимание на то, что в отличие от объекта окна, которая происходит в два этапа (Init и MakeWindow), связывание объекта с элементом управления происходит в один этап. т.к. элемент управления уже существует: Он загружается из ресурса диалога. Вы должны лишь сообщить InitResource, какое из управлений ресурса вы хотите связать с объектом, используя ID управления. Вызов DefWndProc В каждом из методов объекта управления последним оператором в общем случае является вызов DefWndProc(Msg), чтобы имела место обработка стандартных сообщений Windows. Если вы решили не вызывать DefWndProc, тогда ваш метод будет единственной реакцией на ко нкретное сообщение. Ва ш метод будет отвечать за обработку (или игнорирование) таких незавидных задач, как демонстрация нажатия кнопки или передача сообщения родителю управления. Если вы вызываете DefWndProc, то ваш метод наследует обычную обработку, есл и вы не вызываете DefWndProc, то ваш метод заменяет обычную обработку. Единственный случай, когда вам может потребоваться отменить обработку сообщений по умолчанию, это когда он возвращает конкретное значение, а вам нужно, чтобы ваш метод возвращал друго е значение. Приведем пример приложения с блоком диалога с кнопкой, которое имеет связанный объект для модификации его реакции на сообщение. Сама кнопка генерирует звуковой сигнал при своем нажатии и затем вызывает DefWndProc. Это позволяет диалогу получить сообщение и вывести число произведенных нажатий кнопки. На Рис.11.2 показан результат работы программы. Рис.11.2. Диалог со специализированной кнопкой Полный текст программы INIRTEST.PAS содержится на ваших дистрибутивных дисках. Расширенный пример использования диалогов Приведем более сложный пример, который показывает, как определить развитый диалог, который побуждает пользователя проделать ввод текста и проверить его. Отображается блок диалога с полями редактирования для имени сотрудника, номера его страховки и ID. Ко гда пользователь нажмет OK, программа проверит отсутствие цифр в имени, корректность длины номера страховки и то, что ID есть целая величина. Новый тип диалога EmployeeDlg переопределяет метод CanClose для проверки корректности ввода. На Рис.11.3 показан результат работы программы. Рис.11.3. Диалог с проверкой корректности ввода Полный текст программы VDLGTEST.PAS содержится на ваших дистрибутивных дисках. Окна диалога После работы с диалогами и окнами у вас может возникнуть вопрос: "Чем же они отличаются друг от друга ?" Их главное отличие состоит в том, что диалог имеет связанный с ним ресурс, который задает тип и местоположение его управлений. Но окно также может им еть управления. Один из методов помещения управлений в окно состоит в использовании объектов управления, как это показано в Главе 12. Другой подход состоит в совмещении возможностей диалогов и окон, как это сделано в типе объекта TDlgWindow, который реал изует гибрид объекта с именем dialog window. Второй подход дает более удобный способ разработки и обслуживания нескольких управлений в окне. Кроме того, он добавляет диалогам некоторые наиболее гибкие черты окон. TDlgWindow происходит от TDialog и наследует его методы, такие как Execute, Create, Ok и EndDlg. Подобно диалогам, окна диалога имеют соответствующие ресурсы диалога. С другой стороны, подобно окнам, окна диалогов имеют соответствующий класс окна, которы й может задавать наряду с другими параметрами, пиктограмму, курсор и меню. Поскольку он связан с классом окна, его последователь TDlgWindow должен переопределять методы GetClassName и GetWindowClass. Это имя класса должно быть указано в ресурсе диалога. В большинстве случаев вы будете запускать окна диалога как окна или немодальные диалоги, используя методы Create и Show, а не метод Execute. Хорошо использовать окна диалога в качестве головного окна приложения, поскольку головное окно должно содержать много сложных управлений. Например, программа-калькулятор может иметь в качестве головного окна окно диалога, где клавиши калькулятора заданы в ресурсе диалога как клавиши управления. Это позволит головному окну иметь меню, пиктограмму и курсор. Диалоги ввода Диалоги ввода поставляются с ObjectWindows. Это простые объекты диалога, определяемые типом TInputDialog, и побуждают пользователя ввести отдельную строку текста (см. Рис.11.4). Рис.11.4. Диалог ввода Вы можете запускать диалоги ввода как модальные или немодальные, но обычно вы будете запускать их как модальные. С объектом диалога ввода связан ресурс диалога ввода. Он находится в ObjectWindows в файле STDDLGS.RES. Обязательно включите этот ресурс в ва ше приложение. Использование модуля StdDlgs автоматически включает ресурсы в STDDLGS.RES. Каждый раз при конструировании диалога ввода с использованием метода Init, вы задаете для диалога заголовок, подсказку текст по умолчанию. Покажем вызов конструктора Init объекта диалога ввода: var SomeText: array[0..79] of Char; begin AnInputDlg.Init(@Self, 'Caption', 'Prompt', SomeText, SizeOf(SomeText)) ... end; В данном примере 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', 'Enter name:', EditText, SizeOf(EditText))))=id_OK then MessageBox(HWindow, EditText, 'Name is:', mb_OK); else MessageBox(HWindow, EditText, 'Name is still:', mb_OK); end; Диалоги файлов Диалоги файлов являются другим типом диалогов, поставляемых с ObjectWindows в типе TFileDialog. Следует использовать диалог файла каждый раз, когда вы желаете побудить пользователя ввести имя файла, например в функциях File Open и File Save во всех прило жениях. См. Рис.11.5. Рис.11.5. Диалог файла В большинстве случаев диалоги файлов запускаются как модальные. С объектом диалога файла связан ресурс диалога файла, имеющийся в ObjectWindows в файле STDDLGS.RES. Использование модуля StdDlgs автоматически включает файл ресурса. Инициализация диалогов файлов TFileDialog.Init позволяет задать родительское окно, используемый ресурс (который определяет, будет ли это диалог Open или Save As), и законченную пустым символом строку, которая передается по умолчанию в редактируемый текст диалога. В случае закрытия ди алога эта строка будет содержать выбранное имя файла. Если диалог отменен, то строка остается без изменений. Приведем пример типичного использования диалога фала для открытия текстового файла: var AFile: array[0..fsPathName] of Char; begin StrCopy(AFile, '*.TXT'); if Application^.ExecDialog(New(PFileDialog, Init(@Self, PChar(sd_FileOPen), AFile)))=id_OK then ... {Файл содержит имя файла} end; Работа с диалогами файлов В действительности существуют два вида диалога файла: диалог открытия файла и диалог записи файла. Они отличаются друг от друга наличием (или отсутствием) блока списка с именами файлов. Когда вы запускаете диалог с ExecDialog, вы получите тип диалога, заданный шаблоном ресурса (второй параметр Init). Если ресурс содержит блок списка файлов (ID управления id_FList), то это диалог открытия файла (Open File). Если ID управления id_FList, т о будет работать диалог записи файла (File Save As). Вы можете создать собственный ресурс диалога файла. Файл ресурса STDDLGS.RES содержит два шаблона, один для File Open (sd_FileOpen) и другой для File Save As (sd_FileSave). Приведем типичный пример использования диалога файла: procedure TMyWindow.SaveAs(var AFile: array[0..fsPathName] of Char); begin if Application^.ExecDialog(New(PFileDialog, Init(@Self, PChar(sd_FileSave), AFile)))=id_OK then begin {Файл теперь содержит полный маршрут поиска} SaveFile(AFile); {запись файла на диск} end; end; |