"Применение Windows API" - читать интересную книгу автора (Легалов А. И.)Windows и «Модель-Вид-Контроллер»Англоязычный оригинал находится на сервере компании Reliable Software Эта программа использует набор базовых классов, которые инкапсулируют Windows API • Controller (Контроллер) — Мост между оконной процедурой и объектно-ориентированным миром. • View (Вид) — Инкапсулирует вывод Windows программы. • Canvas (Холст) — Инкапсулирует различные контексты устройств и действия, которые Вы можете сделать с их использованием. • Model (Модель) — Работник и мозг вашей программы. Вообще не имеет дело с Windows. Обратите внимание: это Win32 программа — она м.б. запущена под Windows 95, 98, NT, 2000, Me. Обратите внимание: _set_new_handler — это специфика Microsoft. Если вы используете другой компилятор, то скорее удалите эту строку из кода. Согласно текущему стандарту C++, оператор new должен выбрасывать исключения в любом случае ( Обратите внимание: Старые компиляторы могут иметь проблемы с шаблонами (
вы можете записать
Загрузка упакованных исходных текстов Generic (11 кб). При запуске WinMain, создается класс окна и главное окно нашего приложения. Я инкапсулировал эти действия внутри двух классов: WinClass и WinMaker. WinClass может также сообщать нам о том, что уже выполняются другие экземпляры нашей программы. Когда подобное случается в нашем примере, мы просто активизируем уже выполняющийся экземпляр программы и выходим из запускаемого приложения. Так необходимо поступать тогда, когда Вы хотите, чтобы в один момент времени выполнялся только один экземпляр вашей программы. При успешном создании главного окна, мы входим в цикл обработки сообщений. Обратите внимание, что в этот раз функцией TranslateMessage обрабатываются клавиатурные сообщения. Дело в том, что наша программа имеет пункты меню, к которым можно обращаться, используя комбинации Alt+key. Другим интересным моментом этой программы является то, что мы больше не используем строки для обозначения наших ресурсов. Мы используем числовые идентификаторы (ids). Более того, мы используем их даже тогда, когда осуществляются API вызовы таких строк, как имя класса окна или заголовок окна. Мы сохраняем все строки в строковых ресурсах и обращаемся к ним через идентификаторы (ids). Ваша среда разработки для Windows скорее всего имеет редактор ресурсов, который позволяет Вам создавать иконки, меню, строковые ресурсы и назначать им соответствующие числовые идентификаторы. Символические имена этих ids сохранены в файле заголовка, сгенерированном таким редактором. В нашем случае он назван "resource.h". Константа, ID_MAIN, например, ссылается на иконки основной программы (большую и малую в том же самом ресурсе), главное меню, и строку с именем оконного класса. ID_CAPTION ссылается на строку заголовка окна. Такая организация данных поддерживает возможность многократного использования кода, не говоря уже о легкости локализации.
Давайте, рассмотрим WinClass. Он инкапсулирует предопределенную в Windows структуру WNDCLASSEX и обеспечивает приемлемые значения по умолчанию для всех ее полей. Этот класс получен из более простого класса WinSimpleClass, который Вы могли бы использовать, чтобы инкапсулировать некоторые встроенные в Windows классы (такие как кнопки, списки просмотров, и т.д.). Я обеспечил примеры методами, которые могут использоваться, чтобы перестроить значения, устанавливаемые по умолчанию. Например, SetBgSysColor изменяет заданный по умолчанию цвет заднего фона окна к одному из предопределенных цветов системы. Метод SetResIcons загружает соответствующие иконки из ресурсов и присоединяетих к оконному классу. Эти иконки затем появятся в верхнем левом углу основного окна и на панели задач Windows. TopWinClass наследует от WinClass и использует этот метод. Он также подцепляет меню в вершине оконного класса.
После того, как оконный класс зарегистрирован системой, Вы можете создать столько окон этого класса, сколько пожелаете. Они, конечно, совместно используют ту же самую оконную процедуру, которая была зарегистрирована классом. Как будет показано дальше, мы можем различать между собой разные экземпляры окна внутри этой процедуры. Класс WinMaker организован аналогично WinClass. Его конструктор устанавливает значения по умолчанию, которые могут быть переустановлены вызовом специфических методов. После завершения всех установок, Вы вызываете метод Create, чтобы создать окно, и метод Show, чтобы отобразить его. Обратите внимание, что в тот момент, когда Вы вызываете Create, ваша оконная процедура вызывается с сообщением WM_CREATE. Верхнее окно создано с использованием класса TopWinMaker, который обеспечивает соответствующий стиль и заголовок.
Прежде, чем идти дальше, рассмотрим некоторые простые классы общего назначения. WinException — нечто, что мы хотим использовать для исключений во время сбоев Windows API. Он заботится о восстановлении кода ошибки Windows. (Между прочим, имеется простой способ преобразовать код ошибки в строку функцией API FormatMessage.) Класс ResString просто инкапсулирует строку, хранимую в строковых ресурсах вашего приложения.
Контроллер — нервная система отдельного экземпляра окна. Он создается с этим окном, хранится с ним и, в заключение, разрушается вместе с ним. Вы можете помещать любую информацию о состоянии, имеющую отношение к специфическому экземпляру окна в его контроллер. Вообще же, контроллер содержит "Вид", который имеет дело с рисованием на поверхности окна, и он имеет доступ к "Модели", которая является мозгом вашего приложения (все это называется MVC, или образцом "Модель-Вид-Контроллер" ("Model-View-Controller"), изобретенным Smalltalk-программистами. Если, как это часто бывает, ваше приложение имеет только одно окно верхнего уровня, Вы можете непосредственно включать модель в ее контроллер. Это упрощает управление ресурсами, но ценой усиления связи контроллера с моделью. В больших проектах нужно избегать таких связей. Предпочтительнее использовать внутри контроллера "интеллектуальный" указатель на модель. Большинство методов контроллера требует дескриптора окна, с которым они взаимодействуют. Этот дескриптор передается с каждым сообщением Windows, но проще сохранить его один раз внутри контроллера и использовать всякий раз, когда он необходим. Помните — имеется взаимно однозначное соответствие между контроллерами и экземплярами окон (а следовательно, и их дескрипторами).
Оконная процедура — основной коммутационный узел Windows приложения. Вы не вызываете ее из вашей программы — ее вызывает Windows! Каждый раз когда случается что-то интересное, Windows посылает вашей программе сообщение. Это сообщение передается оконной процедуре. Вы можете обработать его, или передать оконной процедуре, заданной по умолчанию. Оконная процедура вызывается с указанием дескриптора к окна, к которому направлено данное сообщение. Этот дескриптор однозначно идентифицирует внутреннюю структуру данных Windows, которая соответствует экземпляру окна. Это так часто случОконная процедура вызывается с указанием дескриптора к окна, к которому направлено данное сообщение. Этот дескриптор однозначно идентифицирует внутреннюю структуру данных Windows, которая соответствует экземпляру окна. Это так часто случается, что мы можем обращаться к этой структуре данных и использовать ее, чтобы сохранить некоторые специфические для экземпляра данные. Имеется типовой безопасный способ доступа к этой структуре. Между прочим, элемент GWL_USERDATA этой структуры гарантированно присутствует во всех окнах, включая окна сообщения, диалоговые окна и даже кнопки.
Каждый раз, когда Windows вызывает нашу оконную процедуру, мы хотим сначала восстановить ее контроллер. Вспомните, что может быть несколько окон, совместно использующих ту же самую оконную процедуру, и мы хотим иметь отдельный контроллер для каждого окна. Как мы узнаем, какой из контроллеров используетсять, когда произходит обратный вызов оконной процедуры? Мы можем выяснить это, рассмотрев дескриптор окна. В этом дескрипторе мы сохраняем указатель на контроллер данного окна, используя функцию Win[Set/Get]Long. Оконная процедура сначала вызывается с сообщением WM_CREATE. В этот момент мы создаем экземпляр контроллера, нициализируем его дескриптором окна и специальной структурой данных по имени CREATESTRUCT, которая передана нам от Windows. Если же мы уже имеем контроллер, то сохраняем указатель на его в соответствующей внутренней Windows-структуре данных помеченной текущим hwnd. В следующий раз оконная процедура вызывается с сообщением, отличным от WM_CREATE, и мы просто восстанавливаем (отыскиваем) указатель на наш контроллер, используя hwnd. Остальное просто. Оконная процедура интерпретирует параметры сообщения и вызывает соответствующие методы контроллера.
Ниже представлены примеры простых реализаций нескольких методов построения контроллеров. Конструктор должен помнить дескриптор окна для более позднего использования, деструктор должен посылать сообщение выхода (quit), метод Size передает его параметр Просмотру (Экрану), и т.д. Мы будем говорить о рисовании в окне немного позже. Теперь, обратите внимание, что контроллер готовит поверхность "Холста" для работы "Вида".
Когда пользователь выбирает один из пунктов меню, оконная процедура вызывается с сообщением WM_COMMAND. Соответствующий метод контроллера посылает команду, основанную на id команды. Когда Вы создаете меню, используя редактор ресурса, Вы выбираете эти идентификаторы команд для каждого пункта меню. Они сохранены в соответствующем заголовочном файле ("resource.h" в нашем случае), который должен быть включен в исходный файл контроллера. Наше меню содержит только три пункта с идентификаторами IDM_EXIT, IDM_HELP, и IDM_ABOUT. Диалоговое окно, которое отображается в ответ на IDM_ABOUT, также создано с использованием редактора ресурсов и имеет id IDD_ABOUT. Его процедура диалога — AboutDlgProc. И, наконец, чтобы отобразить диалоговое окно, нам нужен дескриптор экземпляра приложения. Стандартный способ восстанавить (отыскать) его состоит в том, чтобы обратиться к внутренней структуре данных Windows, используя соответствующий hwnd.
Объект "Вид" (Экранный объект) обычно хранит размеры клиентской области. Они обновляются всякий раз, когда контроллер обрабатывает сообщение WM_SIZE. Первое сообщение WM_SIZE посылается во время создания окна и до посылки WM_PAINT, поэтому мы можем безопасно принимать, его. Когда вызывается Paint, размерности клиентской области уже известны. Графический вывод к окну осуществляется, вызывом соответствующих методов объекта Canvas. В нашем случае, мы печатаем текст, полученный из модели и рисуем вертикальную строку в десяти пикселах от левого края клиентской области.
Объект "Холст" инкапсулирует то, что, на языке Windows, называется Контекстом устройства. Наш Холст очень прост, он знает только, как печатать текст и рисовать линии, но ваш Холст может иметь много больше методов, которые выполняют творческие функции. Мы больше расскажем о Холсте при описании одной из следующих обучающих программ.
Холсты, который Вы создаете, в ответ на сообщение WM_PAINT, имеет специальный вид. Они получают контекст устройства, вызывая BeginPaint и отдают его, вызывая EndPaint. PAINTSTRUCT содержит дополнительную информацию, о некоторой части рабочей области, которая должна быть перерисована, и т.д. Пока мы игнорируем некоторые подробности, но если вы серьезно относитесь к эффективности, Вы должны изучить это более детально.
Далее: Разве можно программировать в Windows без использования элементов управления? |
|
|