Глава 4.


Классы окон библиотеки MFC

                                                             Труднее всего научиться общему  языку.

                                                                                                         Лешек Кумор

Вспомним, что Windows — это операционная система, которая прежде всего работает с окнами. Со своей стороны, подавляющую часть библиотеки MFC составляют классы окон. Пришло время подробно познакомиться и с теми и с другими, а также с их взаимодействием.

Объекты оконных классов библиотеки MFC, естественно, отличаются от окон Windows, но тем не менее тесно с ними связаны. Полное и правильное понимание взаимосвязей между ними является залогом успеха при создании приложений.

Как и любой другой объект C++, объект "окно" создается и уничтожается конструктором и деструктором класса, соответственно. Это с одной стороны. А с другой, окно Windows — это внутренняя структура данных операционной системы, которая формируется функцией интерфейса .прикладного программирования Win32 API Create, а разрушается функцией Destroy Window. В системе Windows каждое окно идентифицируется своим дескриптором, значение которого хранится в переменной m_hWnd объекта CWnd (рис. 4.1).

Рис. 4.1. Объект класса CWnd связан с окном Windows через дескриптор m_hWnd

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

Окна, определенные в системе Windows

Существует всего три основных типа окон: 

из которых программист может создавать множество самых разнообразных объектов, комбинируя предопределенные биты стиля, имеющие имена с префиксом WS_ (от window style — стиль окна). Рассмотрим более подробно эти биты, их влияние на внешний вид и некоторые свойства окон. Начнем с простого перечисления:

WS_BORDER 

Задает окно, имеющее рамку без заголовка

WS_CAPTION 

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

WS_CHILD 

Устанавливает дочернее окно; не может использоваться совместно со стилем WS_POPUP

WS_CLIPCHILDREN

 Исключает область, занятую дочерним окном, из области рисования родительского окна; используется только для родительских окон

WS_CLIPSIBLINGS 

Исключает все другие дочерние окна из своей области рисования; другими словами, если дочерние окна перекрываются, а этот стиль не указан, то при изменении рабочей области одного из окон могут быть испорчены рабочие области других дочерних окон; этот стиль используется только вместе со стилем WS_CHILD

WS_DISABI_ED 

Задает неактивное окно, т. е. сразу после его создания окно недоступно для ввода с клавиатуры или с помощью мыши

WS_DLGFRAME

 Задает окно, имеющее двойную рамку и не имеющее заголовка

WS_GROUP 

Определяет первый элемент управления группы окон, к которым пользователь может переходить при помощи клавиш со стрелками; все элементы управления, определенные с этим стилем, заканчивают текущую и начинают новую группу (одна группа заканчивается там, где начинается другая)

WS_HSCROLL 

Задает окно, имеющее горизонтальную полосу прокрутки

WS_VSCROLL 

Задает окно, имеющее вертикальную полосу прокрутки

WS_MAXIMIZE 

Задает отображение окна в развернутом виде

WS_MAXIMIZEBOX

Задает окно, имеющее кнопку "Развернуть"; если окно является дочерним окном элемента управления, то этот флаг стиля используется под другим именем — WS_TABSTOP

WS_MINIMIZE 

Задает отображение окна в виде пиктограммы; используется только со стилем WS_OVERLAPPED

WS_MINIMIZEBOX 

Задает окно, имеющее кнопку "Свернуть"; если окно является дочерним окном элемента управления, то этот флаг стиля используется под другим именем — WS_GROUP

WS_OVERLAPPED 

Устанавливает, что окно является перекрывающимся; обычно имеет заголовок и рамку

WS_POPUP

 Задает всплывающее окно; не может использоваться совместно со стилем WS_CHILD

WS_SYSMENU 

Задает окно, имеющее пиктограмму системного меню в полосе , заголовка

WS_TABSTOP 

Назначается одному или нескольким элементам управления для того, чтобы между ними можно было перемещаться с помощью клавиши табуляции <ТаЬ>

WS_THICKFRAME

Задает окно, имеющее утолщенную рамку, при помощи которой можно изменять размер окна

WS_VISIBLE 

Устанавливает режим, когда окно становится видимым сразу после создания; этот стиль в основном используется для окон диалога

Помимо перечисленных стилей окон Windows, в файле <winuser.h> определены стили, являющиеся комбинацией наиболее часто используемых стилей.

WS_OVERLAPPEDWINDOW 

Комбинация стилей WSjDVERLAPPED, WS_CAPTION, WS_SYSMENU, WSJTHICKFRAME, WSJ/IINIMIZEBOX и WS_MAXIMIZEBOX

WS_POPUPWINDOW 

Комбинация стилей WS_POPUP, WS_BORDER и WS_SYSMENU

При работе с окнами часто также используются дополнительные или расширенные биты стиля:

WS_EX_ABSPOSITION 

Определяет, что позиция окна задается в абсолютных единицах относительно левого верхнего угла экрана

WS_EX_ACCEPTFILES 

Определяет, что окно допускает перетаскивание файлов (drag-and-drop)

WS_EX_CUENTEDGE 

Придает "трехмерный эффект" границе окна, клиентская область при этом как бы вдавлена в окно

WS_EX_CONTEXTHELP 

Включает знак вопроса в заголовок окна; когда пользователь нажимает на этот знак, то курсор изменяется на указатель в виде знака вопроса; если теперь выбрать какое-либо дочернее окно — оно получит сообщение WM_HELP

WS_EX_CONTROLPARENT 

Позволяет пользователю перемещаться по дочерним окнам родительского окна, используя для этого клавишу табуляции <ТаЬ>

WS_EX_DLGMODALFRAME 

Задает окно, имеющее удвоенную границу, что при использовании совместно со стилем WS_CAPTION позволяет создавать окно с полосой заголовка

WS_EX_LEFT 

Устанавливает для окна свойство левого выравнивания (по умолчанию)

WS_EX_LEFTSCROLLBAR 

Задает расположение полосы прокрутки (если она имеется) с левой стороны рабочей (клиентской) области окна

WS_EX_LTRREADING

 Используется по умолчанию и определяет естественный порядок (слева направо) для большинства языков ввода и чтения текста

WS_EX_MDICHILD 

Задает дочернее окно MDI

WS_EX_NOPARENTNOTIFY 

Определяет, что дочернее окно, созданное с этим стилем, не посылает сообщения WM_PARENTNOTIFY своему родительскому окну при создании или уничтожении

WS_EX_RIGHT 

Устанавливает в зависимости от класса окна групповое свойство правого выравнивания

WS_EX_RIGHTSCROLLBAR 

Задает расположение полосы прокрутки (если она имеется) с правой стороны рабочей (клиентской) области окна; используется по умолчанию

WS_EX_RTLREADING 

Определяет порядок ввода и чтения текста справа налево

WS_EX_SMCAPTION 

Задает окно, имеющее уменьшенную высоту полосы заголовка; используется при создании "плавающих" панелей инструментов

WS_EX_STATICEDGE 

Определяет окно, имеющее трехмерный стиль границы, заданный для использования в элементах, к которым пользователь не получает доступа (статические элементы управления)

WS_EX_TOOLWINDOW 

Задает окно, которое используется для создания "плавающей" панели инструментов; такое окно имеет укороченную полосу заголовка и не отображается на панели задач

WS_EX_TOPMOST 

Задает окно, которое будет располагаться поверх всех окон (созданных без этого стиля); для установки или сброса этого бита можно использовать функцию SetWindowPos

WS_EX_TRANSPARENT 

Задает прозрачное окно, т. е. не закрывающее другие окна, расположенные "под ним"; сообщение WM_PAINT такое окно получает до того, как это сообщение получают все окна того же уровня, но расположенные "под ним"

WS_EX_WINDOWEDGE 

Задает окно, имеющее приподнятую границу для создания "трехмерного эффекта" 

Так же, как в случае основных стилей, для расширенных определены два комбинированных дополнительных стиля (WINUSER.H);

WS_EX_OVERLAPPEDWINDOW 

Комбинация стилей WS_EX_CLIENTEDGE и WS_EX_WINDOWEDGE

 WS_EX_PALETTEWINDOW 

Комбинация стилей WS_EX_WINDOWEDGE, WS_EX_SMCAPTION и WS_EX_TOPMOST

После того как мы перечислили все возможные стили окон, осталось сказать несколько слов об упомянутых выше основных типах окон Windows. Напомним, что их всего три.

Примечание 

Наиболее полным семантическим эквивалентом английского термина "popup window" является термин "окно-поплавок". Мы будем называть такие окна всплывающими.

Какие же возможности предоставляет нам библиотека MFC в плане создания и работы с окнами? Если обратимся к иерархии классов MFC, то вспомним, что все классы, имеющие какое-либо отношение к окнам, являются производными (прямо или косвенно) от единственного класса CWnd (рис. 4.2).

Рис. 4.2. Иерархия оконных классов

Этот класс, а также специальные производные от него классы, включая CFrameWnd, CMDIFrameWnd, CMDIChildWnd, CView и CDialog, имеющиеся в библиотеке, спроектированы таким образом, чтобы сделать за нас большую часть обязательной, но рутинной работы с различными типами окон Windows.

Класс CWnd — это базовый класс для всех окон, созданных на базе библиотеки MFC (рис. 4.3). Помимо предоставления всем своим производным классам большого числа функций для работы с окнами, он также служит для создания разнообразных дочерних окон.

Рис. 4.3. CWnd— базовый класс всех окон библиотеки MFC

Из имеющихся компонентов класса CWnd для нас на данном этапе интерес представляет один:

HWND CWnd::m_hWhd

m_hWnd является дескриптором окна Windows, присоединенного к объекту класса CWnd. Это достаточно важный параметр, особенно для главного окна приложения, т. к. он позволяет библиотеке взять на себя всю работу для корректного завершения приложения, когда пользователь закрывает его главное окно.

Класс CWnd содержит практически все функции, необходимые для работы с окнами. Таких функций более двухсот и их можно разбить на несколько категорий, каждая из которых включает функции, направленные на решение определенного круга задач. Приведем список категорий с кратком характеристикой входящих в них функций:

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

Класс CWnd служит для выполнения двух основных целей: предоставление интерфейса всем другим оконным классам библиотеки MFC и создание дочерних окон Windows. В связи с этим мы ограничились здесь простым перечислением основных реализованных в классе функций. Многие из них мы рассмотрим при обсуждении конкретных примеров программ.

Рис. 4.4. Основной класс для создания фреймов

В отличие от своего базового класса CWnd класс CFrameWnd (рис. 4.4) служит для создания перекрывающихся или всплывающих окон и поддерживает однодокументный интерфейс Windows (SDI). С этим классом (и производными от него) тесно связано понятие фрейма (frame window). Под ним понимается окно, которое координирует взаимодействие пользователя с приложением. Оно отражается на экране в виде рамки с необязательной строкой состояния и стандартными элементами управления. Это окно отвечает за управление размещением своих дочерних окон и других элементов рабочей области. Кроме того, фрейм переадресует команды своим представлениям (специальным дочерним окнам) и может отвечать на сообщения от элементов управления.

 Примечание 

Понятие "представление" (view) очень тесно связано с архитектурой "документ/представление", которую мы рассмотрим в одной из следующих глав.

Класс CFrameWnd спроектирован таким образом, чтобы взять на себя выполнение всех основных функций приложения Windows на базе интерфейса SDI:

Из определенных в классе CFrameWnd компонентов мы остановимся на одном:

Startic AFX_DATA const CRect CFramewnd: : rectDefault

Компонент определяет константный объект класса CRect, который предписывает Windows самостоятельно установить размеры и положение создаваемого окна и задается следующим образом:

const AFX_DATADEF CRect CFrameWnd::rectDefault(

CW_USEDEFAULT, 

CW_USEDEFAULT,

0, 

0

0;

Функции этого класса можно сгруппировать по следующим категориям:

Помимо своего основного назначения — поддержки SDI-приложений — класс CFrameWnd служит базовым для классов CMDIFrameWnd и CMDlChildWnd, которые созданы специально для организации приложений на базе MDI. Но о них несколько позже.

Еще раз подчеркнем важный момент — на базе этого класса легко и удобно создавать фреймы окон. А мы пока переходим к "развитию" нашего первого приложения. А именно — к созданию и работе с различными типами окон Windows.

 

Окна Windows и библиотека MFC

Пора более подробно рассмотреть процесс создания и работы с окнами. В этом разделе мы познакомимся с тем, как

В результате мы должны получить нечто похожее на представленное на

рис. 4.5.

Рис. 4.5. Различные типы окон приложения Styles

 

Создание главного окна SDI-приложения

В рассмотренной в предыдущей главе программе First мы образовали класс нашего окна из базового CWnd. Это допустимый, но очень неэффективный путь. Пришло время познакомить вас с другим возможным подходом для создания главного окна SDI-приложения.

Примечание

Такой подход стал поддерживаться только в шестой версии Visual C++. До этого все необходимые действия приходилось выполнять вручную. Но даже если вы пользуетесь Visual C++ 6, рекомендую не пропускать эту главу, поскольку здесь вы найдете подробнейшее описание не только того кода, который генерирует AppWizard, но и много другой полезной информации.

Прежде всего обратим внимание на базовый класс нашего главного окна:

class CMainFraime : public CFrameWnd 

{

...

};

Надеюсь, здесь уже нет необходимости объяснять, почему в качестве базового был выбран класс CFrameWncr. Действительно, этот класс спроектирован таким образом, чтобы предоставить максимум удобств для работы с перекрывающимися и всплывающими окнами. А, как вы помните, в качестве главного окна приложения наиболее приемлемым является именно перекрывающееся окно. Теперь нам надо подробно рассмотреть функцию CStylesApp::lnitInsnance. где, как вы помните, и начинается создание главного окна:

BOOL CStylesApp::Triitlnstance()

{

// Создаем объект класса CMain.Frame

CMainFrame ""pMainWnd = new CMainFrame;

r_pMainWnd =- pMainWnd;

// Создаем собственно окно Windows 

pMainWnd->LoadFrame (IDR^MAINFRAME);

// Определяем размеры экрана

int cx = : : GetSystemMetrics  (SM CXSCREEN) /8 ;

int cy = : :GetSystemMetrics (SM CXSCREEN) /4 ;

// В зависимости от них устанавливаем размеры окна 

// и выводим его на экран

pMainWnd->SetWindowPos (NULL,

cx,

cy,

6*cx,

2*cy,

SWP_NOZORDER | SWP_SHOWWINDOW);

return TRUE;

}

Как видите, процесс создания окна состоит из трех шагов. Прежде всего необходимо создать объект "главное окно":

CMainFrame *pMainWnd = new CMainFrame;

Следующий шаг состоит в вызове специальной функции, которая создает окно Windows и присоединяет его к нашему объекту:

pMainWnd->LoadFrame(IDR_MAINFRAME);

И наконец, следует отобразить окно на экране, чтобы пользователь мог с ним взаимодействовать:

pMainWnd->SetWindowPos (NULL,

cx,

 су, 

6*сх, 

2*су, 

SWP_NOZORDER | SWP_SHOWWINDOW);

Создание объекта класса не должно вызвать никаких вопросов, и поэтому рассмотрим процесс создания окна Windows или, используя терминологию MFC, фрейма окна.

Из двух функций, реализованных в классе CFrameWnd, в данном случае мы остановили свой выбор на виртуальной функции CFrameWnd::LoadFrame. которая берет на себя задачу регистрации оконного класса, используя для этого предопределенный шаблон, и извлекает из файла ресурсов, задаваемого идентификатором, остальные необходимые для создания окна Windows параметры:

virtual BOOL CFrameWnd::LoadFrame (

UINT nIDResource,

DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW | FWS ADDTOT1TLE,

CWnd *pParentWnd = NULL,

CCreateContext *pContext = NULL)

Функция возвращает значение TRUE, если создание фрейма прошло успешно, и FALSE — в противном случае. Она имеет следующие параметры: nIDResource — идентификатор ассоциированных с фреймом разделяемых ресурсов, который определяет меню, таблицу командных клавиш (accelerator keys), пиктограмму и строку, помещаемую в заголовок окна; pParentWnd— указатель на родительское окно данного фрейма (необязательный параметр и для главного окна должен быть равен NULL— ведь главное окно не может иметь "родителя"); pContext— указатель на структуру CCreateContex, которая определяет объекты, связанные с данным фреймом; dwDefaultStyle— стиль фрейма (необязательный параметр). Дополнительно к стандартным стилям окна могут быть использованы еще и следующие:

FWS_ADDTOTITLE 

В конец заголовка окна будет добавлена дополнительная текстовая строка

FWS_PREFIXTITLE 

Перед заголовком окна будет помещено имя документа

FWS_SNAPTOBAR 

Размеры окна будут подогнаны под размеры панели элементов управления 

Основное назначение функции LoadFrame — максимальное упрощение процесса создания фрейма окна. Используя идентификатор ресурсов, функция извлекает из них необходимые параметры и... вызывает функцию СFrameWnd::Create, рассмотренную в предыдущей главе. Функция загружает ресурс меню и выполняет функцию CWnd::CreateEx, которая регистрирует имя оконного класса, заполняя структуру WNDCLASS. и вызывает виртуальную функцию PreCreoteWindow.

Здесь мы немного приостановимся, поскольку сначала надо разобраться с ресурсами. Вы обратили внимание, что единственным обязательным параметром функции LoadFrame является идентификатор ресурсов? Говоря точнее, обязательным является только один ресурс, а именно — меню. И если его не определить, после запуска приложения получим сообщение об ошибке (рис. 4.6).

Рис. 4.6. Неопределение меню — единственного обязательного ресурса для работы с функцией LoadFrame — приводит к ошибке во время выполнения приложения

Поэтому следующим нашим шагом будет создание простейшего меню.

 

Создание простейшего меню

Используя мощные возможности Visual C++, создать меню чрезвычайно просто  выполните приведенную ниже последовательность действий, и у вас сразу все заработает.

1. Раскройте вкладку Resource View в окне Workspace, щелкните правой кнопкой мыши на Styles Resource и в появившемся контекстном меню выберите пункт Insert. В результате вы увидите окно Insert Resource (рис. 4.7), в котором необходимо выделить надпись Menu и нажать кнопку New. Результат ваших действий представлен на рис. 4.8.

2. Щелкните правой кнопкой мыши на "пунктирном прямоугольнике" и в появившемся контекстном меню выберите пункт Properties. Введите название пункта меню (я использовал стандартное — Файл) в поле Caption (рис. 4.9) и обратите внимание, что под исходной полосой меню появился новый "прямоугольник". Выделите его и введите название в поле Caption, а также идентификатор в поле ID (можно выбрать один из стандартных, как мы делаем в данном конкретном случае — ID_APP_EXIT).

Рис. 4.7. Диалог для выбора типа создаваемого ресурса

Рис. 4.8. Исходная точка для создания нашего меню

Рис. 4.9. В этом окне можно определить параметры элементов меню (да и других ресурсов тоже)

Примечание 

Для того чтобы каждый раз не приходилось пользоваться правой кнопкой мыши, нажмите кнопку Keep Visible, расположенную в левом верхнем углу окна ... Properties. В результате окно всегда будет доступно.

3. Осталось присвоить соответствующий идентификатор самому созданному ресурсу. Для этого в окне Workspace выделите строку IDR_MENU1 и в поле ID окна Menu Properties введите IDR_MAINFRAME — именно такой идентификатор мы использовали в функции LoadFrame.

4. Вновь скомпилируйте и запустите приложение.

Результат, как видите, совершенно другой — все работает, ничего не "падает", и окно ведет себя аналогично тому, как это было в нашем первом примере. Более того, выбрав пункт меню "Выход", вы увидите, что приложение действительно закрылось. И это несмотря на то, что мы не добавили ни строчки кода. Благодаря выбранному нами идентификатору библиотека сама разобралась в том, чего мы хотим, и все сделала.

Итак, на данный момент мы подошли к тому же результату, что и в первом приложении, только "с другой стороны". Однако вернемся к нашему главному окну.

 

Создание главного окна приложения (продолжение)

Мы остановились на том, что в процессе выполнения функции PreCreateWindow система Windows посылает в окно следующие сообщения: WM_GETMINMAXINFO, WM_NCCREATE, WM_NCCALKSIZE и WM_ CREATE. Если при создании окна был определен стиль WS_VISIBLE, то в окно дополнительно посылаются все сообщения, необходимые для активизации и отображения окна на экране. Таким образом, переопределяя функцию PreCreateWindow и/или обработчик любого из представленных сообщений Windows, можно изменять параметры оконного класса и собственно окна как до, так и после создания окна Windows.

Рассмотрим, как этот механизм реализован в нашей программе. Начнем с переопределения функции CFrameWnd::PreCreateWindow. Для этого:

1. Откройте окно ClassWizard, выбрав пункт меню View ClassWizard или нажав аналогичную кнопку на панели инструментов (это для тех, кто как и я предпочитает работать с кнопками панели инструментов, а не через меню).

2. В раскрывающемся списке Class name выберите класс CMainFrame, a в списке Messages PreCreateWindow (рис. 4.10). Нажмите кнопку Add Function (или дважды щелкните левой кнопкой мыши по имени PreCreateWindow) и затем кнопку Edit Code, чтобы непосредственно перейти на то место проекта, где мы будем выполнять дальнейшие действия.

Рис. 4.10. ClassWizard существенно облегчает процесс создания новых функций

3. Внесите в переопределенную функцию необходимые изменения. В нашем случае она примет вид:

BOOL CMainFrame:: PreCreateWindow (GREATEST/ROOT& cs)

 {

cs.lpszName = "Главное окно приложения \"Стили\"";

 return CFrameWnd::PreCreateWindow(cs); 

}

Единственный параметр, передаваемый в эту функцию, — ссылка на структуру CREATESTRUCT, которая определяет параметры инициализации, посылаемые в оконную процедуру, и содержит следующие поля:

typedef struct tagCREATESTRUCT{

LPVOID IpCreateParams; 

HANDLE hlnctance; 

HMENU hMenu;

HWND hwndParent;

int cy; 

int cx; 

int y;

int x; 

LONG style; 

LPCSTR IpszName;

LPCSTR IpszClass; 

DWORD dwExStyie; 

}CREATESTRUCT;

Назначение полей этой структуры: IpCreateParams — указатель на данные, используемые при создании окна; hlnstance — дескриптор модуля, который владеет окном; hMenu— идентификатор меню, используемого окном; hwndParent — дескриптор окна-родителя, если этот параметр равен NULL, то новое окно является окном верхнего уровня; су, сх, у, х— целые числа, задающие соответственно ширину и высоту окна, а также его положение относительно своего владельца; IpszName— символьная строка, задающая имя окна; IpszClass— символьная строка, содержащая имя класса, которому принадлежит окно; dwExStyie — определяет атрибуты расширенного стиля окна.

Мы использовали функцию PreCreateWindow, чтобы не заниматься процедурой регистрации класса окна, возложив эту задачу на библиотеку, поскольку принятые по умолчанию установки нас вполне устраивают. Единственное, что нам следует заменить, это заголовок окна:

cs . IpszName = "Главное окно приложения \"Стили\ "";

Это один из возможных способов присвоения имени окну. После того как окно Windows создано и присоединено к объекту фрейма окна библиотеки MFC, ему посылается сообщение WM_CREATE. Для этого сообщения в библиотеке (точнее, в классе CWnd) создан специальный обработчик — OnCreate, который можно переопределить под свои потребности:

int CMainFrame: :OnCreate (LPCREATESTRUCT IpCreateStruct)

{

if (CFrameWnd::OnCreateiIpCreateStruct: = -1)

renurn -1;

return 0;

}

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

Для правильного завершения процесса создания окна не забывайте вызывать перед какими-либо своими действиями обработчик базового класса CFrameWndr.OnCreate. Вы, конечно же, понимаете, что последняя фраза относится к случаю самостоятельного создания прототипа этой функции — если воспользоваться услугами ClassWizard, то каркас функции будет вам уже предоставлен.

После того как фрейм создан, его необходимо вывести на экран. Библиотека предоставляет несколько функций для реализации этого процесса. Для отображения нашего главного окна мы остановились на одной из них — функции СWnd::SetWindowPos.

BOOL CWnd::SetWindowPos (

const CWnd *pWndlnsertAfter,

 int x,

 int y, 

int cx,

int cy,

 UINT nFlags)

Вызов этой функции позволяет изменить как размеры, так и расположение дочерних и всплывающих окон относительно экрана и других окон. Она имеет следующие параметры: pWndlnsertAfter — указывает на объект CWnd, после которого будет расположено окно. Вместо этого указателя можно использовать одно из следующих предопределенных значений, задающих расположение окна относительно других окон:

wndBottom 

Разместить окно под другими окнами (внизу стека Z-no-рядка); если окно имело статус wndTopMost то этот статус теряется — удаляется флаг расширенного стиля WS_EX_TOPMOST

wndTop 

Разместить окно над другими окнами (на вершине стека Z-порядка)

wndTopMost 

Разместить окно над всеми другими окнами, имеющими статус wndTopMost; эта позиция сохраняется (флаг расширенного стиля WS_EX_TOPMOST остается установленным), даже когда окно находится в неактивном состоянии

wndNoTopMost

Переместить окно выше всех окон, имеющих статус wndTop, но ниже окон, имеющих статус wndTopMost

х, у, сх, су— задают новые значения, соответственно, горизонтального и вертикального расположения окна, его ширины и высоты. nFlags — определяет параметры размера и позиции окна и может принимать одно из следующих значений:

SWP_DRAWFRAME 

Задает рамку вокруг окна, определенную в классе окна

SWP_HIDEWINDOW

 Делает окно невидимым 

SWP_NOACTIVATE 

Блокирует активизацию окна

SWP_NOMOVE

 Предписывает окну сохранение текущей позиции; параметры х и  у игнорируются

SWP_NOREDRAW

 Предписывает не перерисовывать окно; после перемещения приложение должно перерисовать окно самостоятельно

SWP_NOSIZE 

Задает сохранение текущих размеров; параметры сх и су игнорируются

SWP_NOZORDER

Задает сохранение текущего расположения окна относительно других окон, при этом параметр pWndlnsertAfter игнорируется

SWP_SHOWWINDOW 

Отображает окно

Примечание

Windows располагает окна на экране согласно их положению в так называемом Z-порядке, который определяет позицию окна в стеке перекрывающихся окон. Стек окна ориентирован вдоль воображаемой оси (Z-оси) внутрь экрана. Окно, расположенное на вершине стека, перекрывает все другие окна, а расположенное внизу— перекрывается всеми другими окнами. Приложение не может активизировать окно без того, чтобы не перенести его на самый верх Z-порядка. Кроме того, расположение окна относительно экрана и других окон может изменяться без каких-либо ограничений.

Мы использовали функцию SetWindowPos для указания размеров и расположения окна, а также для того, чтобы сделать его видимым.

 Примечание 

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

Следующий вопрос, на котором мы остановимся— как создаются дочерние окна.

 Примечание 

Поскольку любое порожденное окно программисты зачастую называют "дочерним", то часто возникает некоторая путаница. Вы должны совершенно четко отличать стиль WS_CHILD от более широкого разговорного понятия "дочерний".

 

Создание дочерних окон

Теперь, после создания основного окна приложения, можно переходить к созданию некоторого дополнительного пространства для отображения информации и/или для получения команд от пользователя. Каждому "ребенку", как и основному окну, можно приписать конкретный стиль таким образом, чтобы приспособить его внешний вид и назначение к стоящим перед ним задачам. В приложении Styles мы разбили создаваемые окна на два набора. Окна первого располагаются на экране независимо от положения основного окна и имеют стиль WS_POPUP, а для окон из второго набора установлен стиль WS_CHILD и, следовательно, они не могут покинуть пределы рабочей области основного окна.

Для создания дочерних окон необходимо проделать следующие действия: 

Есть и другие действия, но к ним мы перейдем постепенно.

Первое из перечисленных в списке действий совершим, воспользовавшись мастером ClassWizard.

1. Откроем окно ClassWizard и в нем нажмем кнопку-меню Add Class, где выберем элемент New (рис. 4.11).

Рис. 4.11. Создаем новый класс

2. В результате на экране появится диалоговое окно New Class, в поле Name которого вводим имя вновь создаваемого класса — у меня это CChildWnd, а в раскрывающемся списке Base class — имя базового класса, в данном случае CWnd (рис. 4.12).

3. Нажимаем кнопку ОК и новый класс создан.

4. Повторяем действия, описанные в п. 1. Только теперь не будем создавать новых файлов, а воспользуемся уже существующими. Для этого нажмем кнопку Change и в диалоговом окне Change files выберем файлы <childwnd.h> и <childwnd.cpp> (рис. 4.13).

5. В качестве базового класса в данном случае используем класс CFrameWnd, поскольку CWnd не стоит использовать для создания всплывающих окон (рис. 4.14).

Примечание 

Такое разбиение не продиктовано необходимостью и произведено лишь для наглядности. Дело в том, что, как мы упоминали, на базе класса CWnd можно создавать только дочерние окна (имеющие стиль WS^CHILD). Его мы и решили использовать для этих целей. На базе же класса CFrameWnd могут быть созданы окна любого из трех имеющихся типов. Поэтому для создания всплывающих окон именно он и был использован в качестве базового.

Рис. 4.12. Присваиваем имя и выбираем базовый класс для вновь создаваемого

Рис. 4.13. Не будем создавать новых файлов

Рис. 4.14. Для всплывающих окон в качестве базового используем класс CFrameWnd

Как видите, создать новый класс чрезвычайно просто. К сожалению, это высказывание справедливо не всегда — не из всех классов библиотеки MFC можно создавать свои с помощью мастера ClassWizard.

Следующим шагом является создание объектов новых классов и их привязка к главному окну приложения. Последовательность действий при этом следующая:

1. В определении класса CMainFrame вводим объекты новых классов

class CMainFrame : public CFrameWnd 

{

...

CChildWnd *apChildWnd[5]; 

CPopupWnd *apPopupWnd[5];

...

};

2. В конструкторе класса CMainFrame устанавливаем указатели на эти объекты в NULL, чтобы иметь гарантии корректности создания окон

CMainFrame::CMainFrame() 

{

for(int i = 0; i < 5; i++) 

{

apChildWnd[i] = NULL;

  apPopupWnd[i] = NULL; 

}

}

3. Теперь осталось вернуться к обработчику сообщения WM_CREATE:

extern char szNameWnd[128];

int CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStructi 

{

if (CFrameWnd::OnCreate(IpCreateStruct) == -1)

 return -1;

// Создаем пять дочерних окон for (int i = 0; i < 5; i++)

{

apChildWnd[i] = new CChildWnd; 

apChildWnd[i]->Create(NULL, NULL, WS_VISIBLE,

CRectfO, 0, 0, 0), this, (UINT)i); 

}

// Установка нового курсора для класса дочерних окон

::SetClassLong(apChildWnd[0]->GetSafeHwnd() , GCL_HCURSOR,

(LONG)AfxGetApp()->LoadStandardCursor(IDC_IBEAM));

// Создаем четыре всплывающих окна 

for(i =0; i < 4; i++)

{

apPopupWnd[i] = new CPopupWnd;

apPopupWnd[i]->Create(NULL, NULL, 0,

rectDefault, this);

// Указываем номер окна в поле данных, ассоциированных с окном

::SetWindowLong(apPopupWnd[ij->Get.SafeHwnd() ,

 GWL_USERDATA, i);

// Формируем новый заголовок окна

sprintf(szNameWnd, "Окно %d", i ) ;

apPopupWnd[i]->SetWindowTex~(szNameWndi);

// Создаем перекрывающееся окно

apPopupWnd[4] = new CPopupWnd;

apPopupWnd[4]->Create(NULL, NULL, 0, rectDefaulr) ; ::SetWindowLong(apPopupWnd[i]->GetSafeKwnd(),

GWLJJSERDATA, 4);

sprintf(szNameWnd, "Окно %d", i);

 apPopupWnd[4]->SetWindowText(szNameWnd) ;

// Устанавливаем новый курсор для класса всплывающих окон ::SetClassLong(apPopupWnd[0]->GetSafeHwnd. (), GCL_HCURSOR,

(LONG}AfxGerApp(i->LoadStandardCursor(IDC_UPARROW)); 

// Создаем и устанавливаем новый цвет фона для всплывающих окон

 HBRUSH hNewBrush = ::CreareSolidBrush;(RG3(255, 255, 220)); ::SetClassLong(apPopupWnd[3]->GetSafeHwnd(),

GCL_HBRBACKGROUND, (LONG ) hNewBrush) ; .

// Отображение всплывающих окон

 ford = 0; i < 5; i++)

 {

apPopupWnd[i]->ShowWindow(SW_3HOWNORMAL);

apPopupWnd Гi]->UpdateWindcw () ;

}

}

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

В основном процесс создания окон обоих типов стандартен: создаются объекты классов, после чего вызывается некоторая функция для создания окна Windows. Чтобы продемонстрировать как можно больше предоставляемых библиотекой возможностей, в текст программы включены дополнительные строки. Но обо всем по порядку. Начнем с дочерних окон, имеющих установленный бит стиля WS_CHILD (процесс создания объекта класса ничем не отличается от уже рассмотренных, и мы больше не будем останавливаться на этом вопросе). Итак, вызов функции CWnd::Create;

apChildWnd[i]->Create(NULL, NULL, WS_VISIBLE,

CRect(0, 0, 0, 0}, this, (UINT)i);

Подчеркнем некоторые моменты. Во-первых, не определен класс Windows, к которому относится окно, — эту задачу мы переложили на плечи библиотеки. Во-вторых, установив бит стиля WS_VISIBLE, мы задали режим, когда окно сразу же после создания выводится на экран. В-третьих, окно мы превратили в точку и поместили его в левый верхний угол экрана. В-четвертых, окно имеет "родителя" — пятый параметр определен как this. И. наконец, в качестве последнего параметра мы передаем номер создаваемого окна. Как и при создании главного окна, мы переопределяем функцию

BOOL CChildWnd::PreCreateWindow(CREATESTRUCT& cs)

{

cs.style i= adwStyles[nCount];

 sprintf (szNameWnd, "Окно *d", nCount!; cs.ipszName = szNameWnd;

return CWnd::PreCreateWindow(cs);

}

и функцию-обработчик сообщения WM_CREATE

int CChildWnd::OnCreate(LPCREATESTRUCT IpCreateStruct)

 {

if (CWnd::OnCreate(IpCreateStruct) == -1)

 return -1;

MoveWindow(nCount++ * 118, 24, 116, 96);

 return 0; 

}

Первая заполняет поля style и IpszName структуры CREATESTRUCT соответственно значениями стиля и имени конкретного окна, а вторая — устанавливает размер и положение вновь созданного окна (напомним, что мы задали окну нулевые размеры). Со структурой CREATESTRUCT мы уже познакомились, поэтому рассмотрим изящную функцию CWnd::Move\Window, которая реализована в двух вариантах:

void CWnd::MoveWindow (

int x, 

int. y, 

int nWidth, 

int nHeight, 

BOOL bRepaint = TRUE) 

и

void CWnd::MoveWindow(

 LPCRECT IpRect, 

BOOL bRepaint = TRUE)

С помощью любой из этих реализаций можно одновременно изменить размер и положение окна, задаваемые в параметрах х, у, nWidth, nHeight или IpRect. Последний параметр функции определяет, будет ли окно перерисовываться после перемещения. Если этот параметр равен TRUE (по умолчанию), то объект "окно" получает сообщение Windows WM_PAINT. Кроме того, функция посылает еще и сообщение WM_GETMINMAXINFO, которое вы также можете обработать.

Примечание 

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

Как мы уже отметили, задача регистрации оконного класса была поручена библиотеке. Нам лишь остается "подправить" некоторые поля в соответствии с собственными нуждами. Один из возможных путей состоит в применении функций API SetClassLong. GetClassLong, SetWindowLong и GetWindowLong. Возможно, вы захотите использовать их в своих программах, поэтому рассмотрим эти функции подробнее.

DWORD SetClassLong ( 

HWND hWnd,

 int nlndex, 

LONG dwNewLong)

Эта функция устанавливает в структуре WNDCLASS, описывающей оконный класс для hWnd, новое значение dwNewLong в поле, определяемом параметром nlndex, который может принимать одно из следующих значений:

GCL_CBCLSEXTRA

Устанавливает размер (в байтах) дополнительной памяти, предназначенной для использования всеми окнами, создаваемыми на базе этого класса; размер памяти, уже ассоциированной с классом, при этом не изменяется

GCL_CBWNDEXTRA 

Устанавливает размер (в байтах) дополнительной памяти, предназначенной для использования отдельно каждым окном, создаваемым на базе этого класса; размер памяти, уже ассоциированной с окном, при этом не изменяется

GCL_HBRBACKGROUND 

Устанавливает дескриптор кисти для фона, ассоциированного с этим классом

GCL_HCURSOR 

Устанавливает дескриптор курсора, ассоциированного с этим классом

GCL_HICON

 Устанавливает дескриптор пиктограммы, ассоциированной с этим классом

GCL_HMODULE 

Устанавливает дескриптор модуля, который зарегистрировал этот класс

GCL_MENUNAME 

Устанавливает адрес строки меню, которая идентифицирует ресурс меню, ассоциированного с этим классом

GCL_STYLE 

Устанавливает биты стиля оконного класса GCL_WNDPROC Устанавливает адрес оконной процедуры, ассоциированной с классом

При успешном выполнении функция возвращает значение указанного слова из структуры класса окна или нулевое значение — в случае ошибки. Дополнительную информацию об ошибке можно получить, вызвав функцию API GetLastError.

Мы воспользовались функцией SetClassLong для того, чтобы установить новые значения для курсора и цвета фона.

Обратную задачу — получение информации из структуры класса окна — выполняет функция GetClassLong.

DWORD GetClassLong (

 HWND hWna,

 int nIndex).

Для работы с дополнительной памятью в структуре окна интерфейс API предоставляет также две функции:

DWORD SetWindowLong (

HWND hWnd,

int nlndex,

LONG dwNewLong) 

и

DWORD GetWindowLong (

HWND hWnd,

int nlndex)

По принципу действия эти функции аналогичны только что рассмотренным, но работают с дополнительной памятью в структуре окна. Параметр nIndex при этом может принимать следующие значения:

GWL_EXSTYLE 

Устанавливает новый расширенный стиль окна 

GWL_STYLE 

Устанавливает новый стиль окна

GWL_WNDPROC 

Устанавливает новый адрес оконной процедуры

 GWL_HINSTANCE

 Устанавливает новый дескриптор приложения 

GWL_ID 

Устанавливает новый идентификатор окна 

GWL_USERDATA 

Устанавливает 32-разрядное значение, ассоциированное с конкретным окном и предназначенное для использования приложением, которым это окно создано

Если дескриптор hWnd определяет блок диалога, то доступными являются следующие значения:

DWL_DLGPROC 

Устанавливает новый адрес процедуры блока диалога 

DWL_MSGRESULT 

Устанавливает значение, возвращаемое при обработке сообщения в процедуре блока диалога

DWL_USER 

Устанавливает новую дополнительную информацию для приложения, напр., идентификатор или указатель

Для функции GetWindowLong, кроме перечисленных, определено еще одно доступное значение:

GWL_HWNDPARENT 

Возвращает дескриптор родительского окна или NULL, если его нет

Мы использовали функцию SetWindowLong для записи в дополнительную память номера созданного окна. Другими словами, у первого окна в этом поле будет 0, у второго — 1, у третьего — 2 и т. д. Позже, во время изображения окон на экране, программа воспользуется этими значениями для идентификации окон.

Перед тем как закончить обсуждение вопроса об изменении характеристик оконных классов и собственно окон, осталось отметить способ получения дескриптора окна Windows. Для этой цели в классе CWnd предусмотрена специальная функция

HWND CWnd::GetSafeWnd ()

Эта функция позволяет получить дескриптор окна Windows, хранящийся в компоненте класса CWnd::m_hWnd, или NULL, если к объекту не присоединено никакое окно.

Но вернемся к нашей программе. Нам осталось разобраться в создании всплывающих окон. Обратите внимание на два несколько отличающихся вызова функции CFrameWnd::Create:

apPopupwnd[i]->Create(NULL, NULL, 0, rectDefault, this);

и

apPopupWnd[4]->Create(NULL, NULL, 0, rectDefault);

и связанное с этим отличие в поведении всплывающих окон 3 и 4. Для четвертого окна мы не указали родителя, и поэтому оно ведет себя достаточно независимо от нашего главного окна.

Второй момент, на который хотелось бы обратить ваше внимание, связан с еще одним способом изменения стилей созданных окон. Обратимся к тексту:

int CPopupWnd::OnCreate(LPCREATESTRUCT IpCreateStruct)

{

ModifyStyle((DWORD)-1, adwStyles[nCount] );

...

return 0;

}

Как видите, для установки стиля окна мы воспользовались функцией класса CWnd — ModifyStyle:

BOOL CWnd::MbdifyStyle (

DWORD dwRemove, 

DWORD dwAdd, 

UINT nFlags = 0)

Эта функция позволяет удалить (dwRemove) или установить (dwAdd) определенные атрибуты стиля окна. Если установлен параметр nFlags, то вызывается функция API ::SetWindowPos с этим параметром, являющимся комбинацией одного или нескольких значений: SWP_NOMOVE, SWP_NOACTIVATE, SWP_NOSIZE и SWP_NOZORDER.

Для изменения флагов расширенного стиля можно использовать функцию Modify Style Ex.

BOOL CWnd::MbdifyStyleEx ( 

DWORD dwRemove, 

DWORD dwAdd, 

UINT nFlags = 0)

Попутно упомянем две функции, которые позволяют узнать текущие обычный и расширенный стили окна:

DWORD CWnd::GetStyle ()

и

DWORD CWnd::GetExStyle ()

При рассмотрении вопроса о создании главного окна приложения мы обошли молчанием конструктор и деструктор объекта этого окна. Вернемся теперь к их содержимому:

CMainFrame: : CMainFrame ()

{

forfint 1=0; i < 5; i++)

{

apChildWndfi] = NULL;

apPopupWnd[i] = NULL; 

}

}

CMainFrame::~CMainFrame()

{

for(int i=0; i < 5; i++)

if(apChildWnd[i] != NULL)

 delete apChildWnd[i];

}

}

Как видите, мы явно освобождаем только ту память, которую занимали объекты, образованные из класса, производного от CWnd. При использовании

в качестве базового других классов (в нашем случае CFrameWnd) явно освобождать память не надо, т. к. объекты выполняют это сами.

 

Ограничение размеров окна

Мы создали главное окно приложения Styles таким образом, что пользователь не может изменить его размеры. Более того, окно не изменит своих размеров даже тогда, когда вы попытаетесь максимизировать его. Такое поведение окна достигается введением соответствующей обработки сообщения Windows WM_GETMINMAX1NFO, которая выполняется обработчиком:

void CMainFrame::OnGetMinMaxInfо(MINMAXINFO FAR* IpMMI)

}

int :x = : :GetSystemMetrics (SM CXSCREEN) / 8;

int ey = :: GetSystemMetrics (SM_CYSCREEN)/4;

lpMMI->ptMinTrackSize.x = 6*cx;

lpMMI->ptMinTrackSize.y = 2*cy;

lpMMI->ptMaxTrackSize.x = 6*cx;

lpMMI->ptMaxTrackSize.y = 2*cy;

lpKMI->ptMaxPosition.x = Cx;

lpMMI->ptMaxPosition.y = cy;

CFrameWnd::OnGetMinMaxInfо(IpMMI);

}

Обработчику в качестве параметра передается указатель на структуру типа MINMAXINFO, которая содержит информацию о максимально возможных размерах и позиции окна, а также о минимальных и максимальных изменяемых размерах:

typedef struct tagMINMAXINFO (

POINT ptReserved;

POINT ptMaxSize;

POINT ptMaxPosition;

POINT ptMinTrackSize;

POINT ptMaxTrackSize;

 } MINMAXINFO;

Поля этой структуры имеют следующие назначения: ptReserved— зарезервировано для внутреннего использования; ptMaxSize— устанавливает размеры окна, которые оно может иметь в развернутом виде; ptMaxPosition — устанавливает значение, которое должен иметь левый верхний угол развернутого окна; ptMinTrackSize— устанавливает минимальные размеры, до которых пользователь может сжать границы окна; ptMaxTrackSize— устанавливает максимальные размеры, до которых пользователь может растянуть границы окна.

Как видите, все очень просто. Последнее, что нам осталось рассмотреть — каким образом можно получить информацию о системных параметрах.

 

Функция GetSystemMetrics

Эта функция позволяет получить размеры (высоту и ширину) изображаемы* элементов Windows, а также другую информацию. Все размеры даются в пикселах.

int GetSystemMetrics (int nlndex)

Значение nIndex определяет параметр, который вы хотите получить (возвращаемые функцией значения могут изменяться в зависимости от типа используемого дисплея и установок пользователя). Приведем небольшой список наиболее часто используемых параметров:

SM_CMOUSEBUTTONS 

Число кнопок мыши; если мышь не установлена, то возвращается 0

SM_CXDORDERSM_CYDORDER                 

 Ширина и высота рамки окна 

SM_CXFULLSCREEN, SM_CYFULLSCREEN

 Ширина и высота рабочей области максимизированного окна

SM_CXICON, SM_CYICON

Ширина и высота большой пиктограммы, заданные по умолчанию; обычно они равны 32, но могут изменяться в зависимости от установок пользователя

SM_CXMIN, SM_CYMIN 

Минимальные допустимые ширина и высота окна

 SM_CXMINTRACK, SM_CYMINTRACK

 Минимальные величины, до которых пользователь может уменьшить ширину и высоту окна; для их изменения необходимо обработать сообщение Windows WM_GETMINMAXINFO

SM_CXSCREEN, SM_CYSCREEN

Ширина и высота экрана 

SM_CYCAPTION

 Высота области заголовка окон 

SM_CYMENU 

Высота полосы меню

SM_SWAPBUTTON 

He равен 0, если левая и правая кнопки мыши обменялись своими функциями

SM_CXEDGE, SM_CYEDGE

Горизонтальный и вертикальный размеры трехмерной рамки

SM_CXFIXEDFRAME, SM_CYFIXEDFRAME

Толщина (по горизонтали и вертикали) рамки окна, которое имеет заголовок, но не может изменить свои размеры

SM_CXMAXIMIZED, SM_CYMAXIMIZED

Предельно допустимые размеры развернутого окна 

SM_CXMAXTRACK, SM_CYMAXTRACK 

Максимальные величины, до которых пользователь может увеличить ширину и высоту окна; для их изменения необходимо обработать сообщение Windows WM_GETMINMAXINFO

SM_CXMINIMIZED, SM_CYMINIMIZED

Предельно допустимые размеры свернутого окна 

SM_CXSIZEFRAME,  SM_CYSIZEFRAME

Толщина (по горизонтали и вертикали) рамки окна, которое может изменять свои размеры

SM_CXSMICON, SM_CYSMICON

Рекомендуемые размеры маленькой пиктограммы, которая обычно используется в заголовке окна

SM_CYSMCAPTION 

Высота области уменьшенного заголовка

SM_NETWORK 

В возвращаемом значении установлен самый младший бит, если сеть присутствует; остальные биты зарезервированы для будущего использования

SM_SECURE 

Функция возвращает ненулевое значение, если установлена защита, и 0 — в противном случае

 

Ограничение доступа к окну

Большинство из стилей окна, использованных в приложении Styles, достаточно ясны. Поэтому мы остановимся только на одном из них — WS_DISABLED. Окна, имеющие такой стиль, абсолютно неподвижны, и ими нельзя манипулировать ни с помощью мыши, ни с помощью клавиатуры. Обратите также внимание, что у такого окна и курсор не соответствует заданному в оконном классе. Для доступа к этому свойству окон в классе CWnd реализована специальная функция

BGOL CWnd::EnableWindow (BOOL bEnable = TRUE)

Разрешает (bEnable = TRUE) или запрещает (bEnable = FALSE) доступ к окну для мыши и клавиатуры. Если при вызове функции изменилось состояние окна, то до возврата из нее Windows посылает окну сообщение WM_ENABLE.

Для того чтобы узнать текущее состояние окна в смысле его доступности, можно использовать функцию

BOOL CWnd::IsWindowEnable ()

Возвращает значение TRUE, если окно доступно, и FALSE — в противном случае.

Основная цель этой главы и рассмотренного в ней приложения — знакомство с основными классами библиотеки MFC, отвечающими за создание и работу практически всех типов окон Windows. Исключение составляют окна так называемых MDI-приложений — приложений многодокументного (или многооконного) интерфейса, которые мы рассмотрим несколько позже.

Здесь я попытался максимально подробно рассмотреть основные аспекты программирования для Windows с использованием библиотеки классов MFC

и показать, что слухи о сложности этого процесса "несколько преувеличены". Разобравшись в основах, изложенных в данной главе, вы уже прошли большую часть пути: программирование для Windows — это прежде всего работа с окнами.

Единственный механизм, которым мы достаточно активно пользовались, но о котором мало что сказали — работа с сообщениями. Поэтому, на мой взгляд, будет разумно отвлечься на этот чрезвычайно важный вопрос и рассмотреть его более глубоко и "теоретизированно".