Немодальный
режим работы
Особенность
работы с немодальным диалогом заключается в том, что надо затратить дополнительные
усилия для корректного завершения его работы. Чтобы закрыть немодальный диалог,
документация требует переопределить две виртуальные функции в классе диалога:
обработчик закрытия диалога OnCancel и функцию PostNcDestroy. Существуют подробные
рекомендации для завершения немодального диалога. Внутри вашей версии виртуальной
функции OnCancel следует уничтожить окно диалога (DestroyWindow), а внутри другой
виртуальной функции PostNcDestroy рекомендуется уничтожать объект диалогового
класса (delete this;). С немодальным диалогом принято работать динамически (on
heap frame), а не статически (on stack frame), как с модальным. Когда уничтожается
окно Windows, то последним сообщением, которое ему посылается, является WM_NCDESTROY.
Обработчик по умолчанию, то есть родительская версия cwnd: :OnNcDestroy, открепляет
(detach) описатель окна HWND от объекта класса C++ и вызывает виртуальную функцию
PostNcDestroy.
Некоторые классы
переопределяют ее для того, чтобы произвести освобождение своих объектов в области
heap (динамическая память). При неудаче в процессе создания окна происходит
вызов функции cwnd::PostNcDestroy, которая ничего не делает, но дает возможность
виртуальным двойникам корректно освободить динамическую память. Мы тоже будем
работать с диалогом динамически (on heap frame). В классе документа будет храниться
адрес объекта с Pol у Dig, который должен корректно следить за стадиями создания
и уничтожения диалога. Я намеренно не переопределял PostNCDestroy, чтобы показать
альтернативный способ, допустимый в частных случаях. Так как наш диалог завершается
командой Close с идентификатором IDOK, то, введя обработчик этой команды, мы
можем с помощью родительской версии уничтожить Windows-окно, а затем освободить
память, занимаемую объектом собственного класса:
void
CPolyDlg::OnClickedOk(void)
{
//===
Запоминаем факт отсутствия диалога в документе
m_pDoc->m_pPolyDlg
= 0;
//======
Родительская версия вызовет DestroyWindow
CDialog::OnOK();
//======
Мы освобождаем память
delete
this;
}
Вызов диалога
производится в ответ на команду Edit > Poly Color, которая уже присутствует
в меню IDR_DrawTYPE. Введите в класс CTreeDoc обработчик этой команды и наполните
его кодами, как показано ниже:
void
CTreeDoc::OnEditPolycolor
(void)
{
//======
Если диалог отсутствует
if
(!m_pPolyDlg)
{
//======
Создаем его в две ступени
m_pPolyDlg
= new CPolyDlg(this);
m_pPolyDlg->Create(IDD_POLYCOLOR)
;
}
else
//=====
Иначе делаем активным его окно
m_pPolyDlg->SetActiveWindow();
}
Здесь использован
указатель на объект диалогового класса, который необходимо ввести в число public-данных
класса CTreeDoc (CPolyDlg *m_pPolyDlg;) и обнулить в конструкторе документа
(m_pPolyDlg = 0;). Сделайте это, а также введите в файл реализации класса CTreeDoc
вспомогательную функцию UpdateDrawView:
void
CTreeDoc::UpdateDrawView()
{
//======
Добываем адрес нужного представления
CDrawView
*pView = dynamic_cast<CDrawView*>
(GetView(SCDrawView::classCDrawView));
//======
и просим его перерисоваться с учетом изменений
if
(pView)
pView->Invalidate();
}
Рис. 5.6.
Управление с помощью немодального диалога
Изменения такого
рода, как вы уже догадались, влекут за собой достаточно много ошибок на стадии
компиляции, если не уделить внимания проблеме видимости классов. Так, надо вставить
упреждающее объявление (class CPolyDlg;) в файл с интерфейсом документа и директиву
#include "PolyDlg.h" в файл с его реализацией. Кроме того, при работе с диалогом
в немодалыюм режиме надо помнить о том, что для его окна свойство Visible должно
быть установлено в True. По умолчанию это свойство выключено, так как при запуске
диалога в модальном режиме диалог сначала невидим, но .затем функция DoModal
вызывает showWindow с параметром SW_SHOW, что активизирует окно, делая его видимым.
Мы тоже можем поступить так же, вставив аналогичный вызов после вызова функции
Create, но проще сразу установить для диалога (в категории Behavior окна Properties)
свойство Visible.
В настоящий
момент приложение может быть запущено и при условии отсутствия ошибок протестировано.
Команда запуска диалога должна быть доступна, только когда активно окно CDrawFrame,
или, точнее, фокус ввода принадлежит представлению, управляемому классом CDrawView.
Проверьте все варианты запуска диалога: с помощью команды меню или кнопки на
панели инструментов. Проверьте также возможность перевода фокуса в любое из
представлений документа при наличии окна диалога (рис. 5.6).