Написание приложений Win32 с помощью одних классов C++ (часть 4) - Как заставить все работать

ОГЛАВЛЕНИЕ

Как заставить все работать

Нелегко продемонстрировать все вышеназванное в простом приложении, не делающем почти ничего, но позволяющем проверить почти все. С этой целью проект W3 использует Nlib и NGUI. Была создана рамка, обернута EMenuUpdator и EMauImagerIDE, и было добавлено дочернее окно при WM_CREATE.

Команды направлялись потомку, и обрабатывался ID_FILE_EXIT в главном окне и ID_HELP_ABOUT в потомке (это необычно, но нормально для демонстрации маршрутизации команд). Чтобы реагировать на ID_HELP_ABOUT, был создан экземпляр модального DialogBox.

Использование Commoncontrols

Надо подключить библиотеку импорта ComCtrl32.lib и вызвать InitCommonControlsEx. Вся эта неприятная, но всегда нужная начинка была помещена в класс (NUtil::SInitCommonControlsEx). Создается экземпляр временного объекта, вызывающий конструктор с передачей требуемого значения (по умолчанию принято ICC_WIN95_CLASSES) –  и всё тут. Библиотека подключается посредством #pragma comment(lib ...) в заголовке NGUI/CommCtrl.h.

Примечание: Эта инициализация не сделана неявной (т.е. через созданный статический экземпляр объекта), потому что не всем приложениям нужны точно такие же общие управляющие элементы.

Диалоговое окно «О» обернуто CAboutBox, и его экземпляр создается при создании таймера, осуществляющего обратный отсчет с помощью индикатора выполнения. При достижении нуля диалоговое окно автоматически закрывается. (Нажатие OK ускоряет его закрытие). Это показывает корректную работу карты сообщений с DLGPROC.

Обработка более сложных компоновок

Цель – позволить рамке манипулировать клиентским окном и набором состыкованных панелей по алгоритму, схожему с IDE. DockMan.h и DockMan.cpp содержат нужную начинку. В частности, два интерфейса определяют взаимодействия между стыкуемыми объектами и рамками.

Диспетчер стыковки

ILayoutManager определяет прототип для функции RedoLayout, тогда как IAutoLayout создает прототипы для функций DoLayout и GetSideAlignment. Как правило, реализация ILayoutManager должна содержать или ссылаться на несколько элементов IAutolayout, чтобы размещать их при перемещении или изменении размера. RedoLayout вызывается с передачей запрашивающего HWND (в случае если его пропустит алгоритм компоновки: обычно этот параметр равен NULL). Он, в свою очередь, должен вызвать DoLayout вложенного IAutoLayout, передав прямоугольник. Вложенный IAutoLayout должен перестроиться на базе конца того прямоугольника, изменив его до части прямоугольника, остающейся открытой.

ILayoutManager определяет порядок компоновки. IAutolayout – размещаемые компоненты. Представленная реализация – во избежание запутывания классов – делит реализацию ILauoutManager на две части.

Внутренний класс (XLayoutProvider) реализует в куче ILayoutManager и получается путем вызова статической функции ILayoutManager::Get(HWND): она извлекает (через DynamicFind) ILayoutManager, связанный с переданным HWND или (если ничего не прикреплено) создает XLayoutProvider и прикрепляет его, сделав его автоматически удаляемым наблюдателем.

XLayoutProvider обрабатывает сообщение WM_SIZE, получая прямоугольник клиента и передавая его в типизированное уведомительное сообщение, чьей структурой данных является ILayoutManager::Autonotify.

Здесь можно прикрепить произвольное число оберток, реализующих описатель этого сообщения, который, получив прямоугольник, переносимый структурой данных, и некоторые принадлежащие данные, решает, что делать с вложенными или указанными элементами IAutoLayout.

В частности, EDockBarManager реализует ILayoutManager::Autonotify, вмещая один EClientWnd и четыре (один на каждую сторону) EDockBar. Каждый EDockBar может принять любое число HWND для встраивания, и при осуществлении этого прикрепляет EDockBar::XElement к переданному HWND. Указанный второй внутренний класс предназначен для работы с EDockBar и является наблюдателем, автоматически удаляющим EWnd. Он существует в куче и уничтожается при уничтожении окна, к которому он прикреплен, и хранит состояние стыковки (размещение), управляя стыковкой и отстыковкой обернутого HWND. Возможность стыковки не встроена в переданное окно, а подключается при прикреплении окна.

Не нужны особенные HWND: всего лишь обычные всплывающие окна, созданные принадлежащими родителю EDockBar (как правило, EDockBarManager). Они становятся потомками панелей при стыковке и снова становятся всплывающими при свободном перемещении.

Конечно, те окна могут быть управляющими элементами, или панелями инструментов, или другими более сложными окнами (это рассмотрено в части 4 статьи).

Команды «О», обе - EDockBar и EdockBar::XElem пересылают принятые команды родителю (или владельцу), тогда как EDockBarManager пересылает их клиентскому окну. Это создает маршрутизацию команд в духе MFC.

Пример приложения

В примере приложения создается CMainWnd, создающий, в свою очередь, 8 по-разному расположенных панелей (смотрите CMainWnd::OnCreate). Некоторые из них перемещаемы, у других можно менять размер.

Некоторые панели снабжены обычной кнопкой закрытия. При закрытии панели она уничтожится (и так как она обернута внутренней автоматически удаляющей наблюдающей оберткой, обертка тоже уничтожается). Отсутствует интерфейс для управления созданием или скрытием панелей, так как это не вошло в функционал классов.

Обратите внимание на NUtil::STrace::_Filter() = 2; в WinMain: это не дает классу STrace отображать в отладочном выводе много сообщений, связанных с отправкой сообщений и с операциями обертывания и распаковки описателя GDI(интерфейс графического устройства).