Рис. 1. Классы, включаемые в образец проектирования "Диалоговое окно".
Давайте, начнем с клиентского кода. В нашей обобщенной программе мы нуждаемся в диалоговом окне, которое позволяет пользователю редактировать строку. Используя редактор ресурса, мы создаем шаблон диалога, который содержит средства редактирования и две кнопки, OK и CANCEL. Идентификатор ресурса (id) этого диалога – IDD_EDITDIALOG. Затем мы определяем наш собственный класс списка параметров по имени EditorData и специальный класс контроллера по имени EditorCtrl. Когда пользователь выбирает пункт меню Edit, будет выполняться следующий код:
>void Controller::Edit(HWND hwnd) {
> EditorData data(_model.GetText ());
> ControllerFactory factory(&data);
> ModalDialog dialog(_hInst, hwnd, IDD_EDITDIALOG, &factory);
> if (dialog.IsOk()) {
> _model.SetText(data.GetName());
> // Force repaint
> InvalidateRect(hwnd, 0, TRUE);
> }
>}
Сначала создается и инициализируется строкой объект EditorData. Затем, формируется шаблон ControllerFactory. Мы параметризуем его двумя клиентскими классами EditorCtrl и EditorData. Объект фабрики инициализирован указателем на наши данные. Затем создается объект ModalDialog. Он получет указатель на нашу фабрику в качестве параметра. Это используется для того, чтобы создать объект контроллера и восстанавливать данные из списка параметров. После того, как проведено взаимодействие с пользователем, мы проверяем, подтверждал ли пользователь результаты, нажимая кнопку OK, и если так, то мы фиксируем результаты редактирования и используем их в нашей программе. Этот способ создания диалогового окна является наиболее типичным.
Класс EditorData в нашем примере предельно прост.
>class EditorData {
>public:
> enum { maxLen = 128 };
> EditorData(char const* name) {
> SetName(name);
> }
> BOOL IsNameOK() { return (_name[0] != '\0'); }
> void SetName(char const *name) {
> strcpy(_name, name);
> }
> char const* GetName() { return _name; }
>private:
> char _name[maxLen];
>};
Класс контроллера, EditorCtrl, содержит все операции. Прежде всего он встраивает в себя элемент редактирования. Этот объект ответствен за взаимодействие с элементом редактирования, внедренным в диалоговое окно. Элемент имеет идентификатор IDC_NAME_EDIT, заданныйс помощью редактора ресурсов. Во-вторых, контроллер хранит указатель на EditorData. Этот указатель взят из базового класса DlgController. Три виртуальных метода DlgController должны быть переписаны в нашем EditorControl. Это OnInitDialog, который вызывается немедленно после того, как диалог был инициализирован, OnCommand, который вызывается всякий раз, когда любой элемент диалогового окна посылает нам команду и, в заключение, OnNotify, который используется новыми элементами управления Windows95.
>class EditorCtrl : public DlgController {
>public:
> EditorCtrl(HWND hwndDlg, void *argList) : DlgController(argList), _nameEdit(hwndDlg, IDC_NAME_EDIT) {
> _dlgData = (EditorData*)GetArgList();
> }
> void OnInitDialog(HWND hwnd);
> bool OnCommand(HWND hwnd, int ctrlID, int notifyCode);
> bool OnNotify(HWND hwnd, int idCtrl, NMHDR *hdr);
>private:
> Edit _nameEdit;
> EditorData *_dlgData;
>};
В методе OnInitDialog мы обрабатываем строку, которая была передана в EditorData и используем ее, чтобы инициализировать элемент редактирования.
>void EditorCtrl::OnInitDialog(HWND hwnd) {
> char const* name = _dlgData->GetName();
> _nameEdit.SetString(name);
>}
OnCommand получает команды от различных элементов. Элементы идентифицированы их идентификаторами. Например, если идентификатор — IDC_NAME_EDIT, это означает что что-то произошло с элементом редактирования. В нашей реализации мало функциональности, и мы реагируем на каждое изменение, копируя целую строку в объект EditorData. Хотя встречаются случаи, когда Вы должны проверять правильность строки или отображать ее в некотором другом элементе управления, а также Вы должны реагировать на каждое сообщение об изменении.
Когда пользователь нажимает кнопку OK, мы получаем команду с идентификатором IDOK. Мы проверяем строку и, если она правильная, то заканчиваем диалог, передающий TRUE как код возврата. Когда идентификатор — IDCANCEL (от кнопки Cancel) мы заканчиваем диалог с кодом возврата FALSE.