Диалогови прозорци на SoftCraft
Използване на фабрика за класове за диалогови прозорци
Диалогов прозорец
Превод на А. И. Легалов
Английският оригинал е на сървъра наНадежден софтуер
От тук можете да изтеглите програма, която демонстрира диалогов прозорец (zip архив 14 kb)
Диалоговият прозорец за програма на Windows е това, което е извикването на функция за програма на C. Първо, програмите на Windows предават някои данни на диалоговия прозорец, за да го инициализират. След това диалоговият прозорец комуникира с потребителя. Когато потребителят реши, че любопитството на програмата е задоволено, той (или тя) натиска бутона OK. Новополучените данни се връщат обратно в програмата.
Диалоговият прозорец обикновено взаимодейства с всякакви контроли (джаджи, устройства, gizmoses - както искате да ги наричате). Кодът на диалоговия прозорец взаимодейства с тези елементи чрез изпращане и получаване на съобщения чрез своята „Процедура за диалог“. Има много общ код в имплементациите на различните диалогови прозорци. Бихме искали да напишем и коригираме този код само веднъж и след това да го използваме многократно.
Какво се променя от диалог към диалог:
- Вид данни, които се предават и връщат от диалог.
- Разнообразие от елементи в диалогов прозорец.
- Взаимодействие между елементите.
Тази променливост може да бъде капсулирана в два специални клиентски класа: клас на списък с опции и клас на диалогов контролер.
Класът на списъка с параметри капсулира параметри в себе си. Той осигурява достъп за тяхното възстановяване и модифициране. Дефинирането и внедряването на този клас е под пълния контрол на клиента (програмист, който използва повторнонашия код).
Класът на диалоговия контролер съдържа контролни обекти за всички диалогови елементи и осъществява взаимодействия между тях. Клиентът трябва да извлече този клас от абстрактния класDlgControllerи да приложи всички негови чисти виртуални методи.
Останалото е просто лепило. Трябва да параметризираме шаблона на класаCtrlFactory, който се използва от нашата обща реализация на класаModalDialog. Фабриката на контролера създава подходящ дефиниран от клиента контролер и връща полиморфен указател към него. Ориз. 1 показва връзките между различните класове, включени в реализацията на диалоговия прозорец.
Ориз. 1. Класове, включени в шаблона за проектиране на диалогов прозорец.
Първо се създава обектEditorDataи се инициализира с низ. След това се генерира шаблонътControllerFactory. Ще го параметризираме с два клиентски класаEditorCtrlиEditorData. Фабричният обект се инициализира с указател към нашите данни. След това се създава обектModalDialog. Той ще получи указател към нашата фабрика като параметър. Това се използва за създаване на обект на контролер и извличане на данни от списъка с параметри. След като взаимодействието с потребителя приключи, ние проверяваме дали потребителят е потвърдил резултатите, като щракнете върху бутона OK, и ако е така, ние предаваме резултатите от редактирането и ги използваме в нашата програма. Този начин за създаване на диалогов прозорец е най-типичният.
КласътEditorDataв нашия пример е изключително прост.
В метода OnInitDialog ние обработваме низа, който беше предаден на EditorData и го използваме за инициализиране на елемента за редактиране.
OnCommand получава командиот различни елементи. Елементите се идентифицират чрез техните идентификатори. Например, ако идентификаторът е IDC_NAME_EDIT, това означава, че нещо се е случило с елемента за редактиране. Има малко функционалност в нашата реализация и ние реагираме на всяка промяна, като копираме целия ред в обекта EditorData. Въпреки че има моменти, когато трябва да потвърдите низ или да го покажете в друга контрола, и също така трябва да отговорите на всяко съобщение за промяна.
Когато потребителят щракне върху бутона OK, получаваме команда с IDOK. Проверяваме низа и ако е правилен, завършваме диалога, предавайки TRUE като код за връщане. Когато идентификаторът е IDCANCEL (от бутона Cancel), завършваме диалога с FALSE код за връщане.
Методът OnNotify не прави нищо, когато използва контроли преди Widnows95, като контроли за редактиране и бутони.
След като вече знаете кода на клиента, нека да разгледаме пълната реализация на примера на диалоговия прозорец. Фабриката на контролера е много прост шаблон за клас. Всичко, което прави, е да вземе списък от общи параметри и да го съхрани чрез void указатели. Само дефинираният от клиента контролер знае какъв всъщност е класът, поставен в списъка с параметри, и само той изпълнява преобразуването (вижте конструктора EditorCtrl).
Кодът, общ за всички фабрики на контролери, е изолиран в класа CtrlFactory, от който действителният шаблон наследява. Шаблонът заменя метода MakeController, за да създаде нов контролер за класа ActualCtrl, дефиниран от клиента. Обърнете внимание, че методът връща ActualCtrl като указател към неговия базов клас DlgController и това е всичко, което вижда останалата част от реализацията.
Следното едефиниция на абстрактния клас DlgController, който се използва като основа за всички класове контролери, дефинирани от клиента. Вече видяхме как тези наследявания работят с клиентския клас EditorCtrl като пример.
Централната част за повторно използване на софтуера е класът ModalDialog. Той върши цялата работа в своя конструктор, като извиква функцията DialogBoxParam API. Параметърът, който предаваме на диалоговия прозорец (всъщност неговата диалогова процедура) е указател към фабриката на контролера. Диалоговата процедура се дефинира като статичен метод (не е необходим указател: диалоговата процедура се извиква от Windows, така че няма достъп до указател).
В заключение отбелязваме, че диалоговата процедура, обща за всички типове диалози, е реализирана, за да отговори на три типа съобщения: WM_INITDIALOG, WM_COMMAND и WM_NOTIFY. Всъщност всички те насочват тези съобщения към обекта на контролера. Той получава указател към обект на полиморфен контролер, като първо извиква фабричния метод MakeController.
Обърнете внимание, че от този момент позволяваме на Windows да проследява указателя към контролера. Ние го съхраняваме като GWL_USERDATA, специален дълъг, който е свързан с всеки прозорец, особено с нашия диалогов прозорец, и е достъпен чрез манипулатора на прозореца.
Трябва поне да внимаваме. На първо място, трябва да освободим контролера, след като сме го използвали. Ние правим това, когато обработваме WM_DESTROY.
Второ, Windows има неприятната идея (навик) да изпраща WM_COMMAND и WM_NOTIFY съобщения преди WM_INITDIALOG и след WM_DESTROY. Какво може да се каже тук? Бих набил мениджъра, който отговаря за тези случаи. Но тъй като е така, трябва да се защитим, като проверим дали ctrl е различно от нула, преди да извикаме OnCommand иOnNotify.
Ето красотата на полиморфизма в действие. Фабричният обект се създава от клиента с помощта на шаблонния клас. Този обект се предава на конструктора ModalDialog. ModalDialog го предава на диалоговата процедура като нулев указател (това е, защото трябва да премине през Windows). Диалоговата процедура го получава в съобщението WM_INITDIALOG като LPARAM. След преминаване през червата на Windows, той трябва да бъде възстановен до оригиналната си форма чрез кастинг обратно към указател към CtrlFactory - основния клас на всички фабрики за контролери.
Когато извикваме неговия виртуален метод MakeController, ние извикваме метода, заместен в шаблонния клас ControllerFactory. Той създава нов обект за класа ActualCtrl, дефиниран от клиента. Но отново, той ни връща този обект, маскиран като общ указател към DlgController. Така че всеки път, когато извикаме някой от виртуалните методи ctrl, ние изпълняваме клиентските замени, дефинирани в класа ActualCtrl. Това е най-добрият полиморфизъм: Вие пишете код, като използвате общи указатели, но когато кодът се изпълнява, той се извиква с много специфични указатели. Когато извиквате методи чрез тези указатели, вие изпълнявате специфичните методи, предоставени от клиента на вашия код.
Ето какво се случва с фабрика за обекти, чийто действителен клас еControllerFactory
Предава се на конструктора ModalDialog като
Преминаване от Windows към ModalDialogProcedure като
Прехвърляне на ModalDialogProcedure към
Ето какво се случва с обектни данни, чийто действителен клас еEditorData.
Предава се на фабричния конструктор като | празен * |
Предаване в метод на клас AcquireControllerControllerFactory към | EditorData* |
Предава се на конструктора EditCtrl като | editdata * |
Обектът на клас EditCtrl, създаден в метода MakeController на класа ControllerFactory, се връща от него като DlgController * и се съхранява в тази форма като статичен член с данни ModalDialog.