Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Порядок использования объекта-приложения каркасом MFC

Поиск

В исходном тексте приложения Hello заметна характерная особенность MFC-приложений – отсутствие исполняемого кода за пределами классов. В приложении Hello нет ни функции main, ни WinMain. Единственный оператор за пределами классов – это оператор создания объекта-приложения в глобальной области видимости. Чтобы понять, где же в самом деле начинается исполнение программы, надо разобраться в структуре каркаса приложения.

В одном из исходных файлов MFC (они поставляются в комплекте Visual C++), в Winmain.cpp, находится функция AfxWinMain. Она является аналогом WinMain в MFC-приложениях. Из AfxWinMain вызываются функции-члены объекта-приложения – отсюда ясно, почему он должен быть глобальным объектом (глобальные переменные и объекты создаются до исполнения какого-либо кода, а объект-приложение должен быть создан до начала исполнения функции AfxWinMain).

После запуска AfxWinMainдля инициализации каркаса приложения вызывает функцию AfxWinInit, которая копирует полученные от Windows значения hInstance, nCmdShow и другие параметры AfxWinMain в переменные-члены объекта-приложения. Затем вызываются функции-члены InitApplication и InitInstance (InitApplication в 32-разрядных приложениях использовать не следует, она нужна для совместимости с Windows 3.x). Если AfxWinInit, InitApplication или InitInstance возвращает FALSE, то AfxWinMain завершает приложение. При условии успешного выполнения всех перечисленных функций AfxWinMain выполняет следующий, крайне важный шаг. У объекта-приложения вызывается функция-член Run и т.о. выполняется вход в цикл обработки сообщений главного окна приложения:

pApp->Run();

Цикл обработки сообщений завершится при получении из очереди сообщения WM_QUIT. Тогда Run вызовет функцию ExitInstanceи вернет управление в AfxWinMain. Она выполнит освобождение служебных ресурсов каркаса и затем оператором return завершит работу приложения.

 

2.5 Класс "окно-рамка" CFrameWnd

В MFC базовым оконным классом является класс CWnd. Этот класс и его потомки предоставляют объектно-ориентированный интерфейс для работы со всеми окнами, создаваемыми приложением. В приложении Hello класс главного окна называется CMainWindow. Он является подклассом CFrameWnd, а тот, в свою очередь, подклассом CWnd. Класс CFrameWnd реализует понятие "окна-рамки". Окна-рамки играют важную роль контейнеров для видов, панелей инструментов, строк состояния и других объектов пользовательского интерфейса в архитектуре "документ/вид". Пока об окне-рамке можно думать как об окне верхнего уровня, которое обеспечивает основной интерфейс приложения с внешним миром.

MFC-приложение для создания окна вызывает его функцию-член Create. Приложение Hello создает объект CMainWindow в функции InitInstance, а в конструкторе CMainWindow как раз и выполняется создание окна Windows, которое потом будет выведено на экран:

Create(NULL, "Приложение Hello");

Функция-член Create, наследуемая в CMainWindow от CFrameWnd, имеет следующий прототип:

BOOL Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName,

DWORD dwStyle = WS_OVERLAPPEDWINDOW,

const RECT& rect = rectDefault,

CWnd* pParentWnd = NULL,

LPCTSTR lpszMenuName = NULL,

DWORD dwExStyle = 0,

CCreateContext* pContext = NULL)

Применение Create упрощается за счет того, что для 6-ти из 8-ми ее параметров определены значения "по умолчанию". Приложение Hello при вызове Create указывает только два первых параметра. Параметр lpszClassNameзадает имя оконного класса (которое хранится в структуре WNDCLASS), на основе которого операционная система будет создавать новое окно. Если этот параметр задать равным NULL, то будет создано окно-рамка на основе оконного класса, зарегистрированного каркасом приложения. Параметр lpszWindowName задает текст строки заголовка окна.

 

Рисование содержимого окна

Приложение Hello выводит текст на экран только по требованию Windows, при обработке сообщения WM_PAINT. Это сообщение генерируется по разным причинам, например, при перекрытии окон или при изменении размеров окна. В любом случае, само приложение ответственно за перерисовку клиентской области окна в ответ на WM_PAINT.

В приложении Hello сообщения WM_PAINT обрабатываются функцией CMainWindow::OnPaint, которая вызывается каркасом приложения при получении каждого сообщения WM_PAINT. Эта функция выводит строку "Hello, MFC" в центре клиентской области окна. Функция начинается с создания объекта класса CPaintDC:

CPaintDC dc(this);

В MFC класс CPaintDC является подклассом более абстрактного класса CDC, инкапсулирующего контекст устройства Windows. В CDC есть множество функций-членов для рисования на экране, принтере и других устройствах. Класс CPaintDC является специфической разновидностью CDC, которая используется только в обработчиках сообщения WM_PAINT.

В приложениях на Windows API при обработке сообщения WM_PAINT приложение сначала должно вызвать функцию::BeginPaint для получения контекста устройства, связанного с недействительной областью клиентской области окна. После выполнения в этом контексте всех необходимых операций рисования, приложение должно вызвать::EndPaint для освобождения контекста и информирования Windows о завершении обновления окна. Если приложение при обработке WM_PAINT не будет вызывать функции::BeginPaint и::EndPaint, то Windows не будет удалять сообщение WM_PAINT из очереди и это сообщение будет поступать в окно постоянно.

Объекты класса CPaintDC вызывают::BeginPaint из конструктора, а::EndPaint – из деструктора.

После создания объекта CPaintDC в OnPaint создается объект CRect и вызовом CWnd::GetClientRect в него помещаются координаты клиентской области окна:

CRect rect;

GetClientRect(&rect);

Затем OnPaint вызывает CDC::DrawTextдля вывода строки "Hello, MFC":

dc.DrawText("Hello, MFC", -1, &rect,

DT_SINGLELINE | DT_CENTER | DT_VCENTER);

DrawText – это функция вывода текста в контекст устройства. У нее 4 параметра: указатель на отображаемую строку, количество символов в строке (или -1, если строка заканчивается нулем), указатель на структуру RECT или объект CRect с координатами области вывода, и флаги вывода. В приложении Hello используется комбинация из трех флагов, указывающих, что текст надо выводить в одну строку и центрировать и по горизонтали, и по вертикали внутри области rect.

Заметно, что среди параметров DrawText нет характеристик шрифта и цвета текста. Эти и другие параметры вывода являются атрибутами контекста устройства и управляются специальными функциями-членами CDC, например, SelectObject и SetTextColor. Т.к. приложение Hello не изменяет никаких атрибутов контекста устройства, то используется шрифт и цвет "по умолчанию" (черный). DrawText заполняет прямоугольник, описывающий текст, текущим фоновым цветом контекста устройства (по умолчанию – белый).

 

Карта сообщений

Как сообщение WM_PAINT, полученное от Windows, преобразуется в вызов функции-члена CMainWindow::OnPaint? Это делается с помощью карты сообщений. Карта сообщений – это таблица, связывающая сообщения и функции-члены для их обработки. Когда окно-рамка приложения Hello получает сообщение, то MFC просматривает карту сообщений, ищет в ней обработчик для сообщения WM_PAINT и вызывает OnPaint. Карты сообщений в MFC введены для того, чтобы избежать больших таблиц виртуальных функций, которые были бы необходимы, если в каждом классе завести виртуальную функцию для каждого возможного сообщения. Карту сообщений может содержать любой подкласс класса CCmdTarget.

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

1) Объявить карту сообщений в интерфейсной части класса с помощью макроса DECLARE_MESSAGE_MAP.

2) Создать карту сообщений в файле реализации. Она ограничена макросами BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними размещаются макросы, идентифицирующие конкретные сообщения.

3) Добавить в класс функции-члены для обработки сообщений.

В приложении Hello класс CMainWindow обрабатывает только одно сообщение, WM_PAINT, поэтому карта сообщений выглядит так:

BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)

ON_WM_PAINT()

END_MESSAGE_MAP()

Карта сообщений начинается с макроса BEGIN_MESSAGE_MAP, в котором задается имя класса-владельца карты и имя его базового класса. (Карты сообщений наращиваются путем наследования. Имя базового класса необходимо, чтобы каркас приложений мог продолжить поиск обработчика сообщений и в карте базового класса, если его нет в карте текущего.). Макрос END_MESSAGE_MAP завершает карту сообщений. Между BEGIN_MESSAGE_MAP и END_MESSAGE_MAP располагаются элементы карты сообщений. Макрос ON_WM_PAINT определен в заголовочном файле MFC Afxmsg_.h. Этот макрос добавляет в карту сообщений элемент для обработки сообщения WM_PAINT. У этого макроса нет параметров, в нем жестко задана связь между сообщением WM_PAINT и функцией-членом OnPaint.

В MFC есть макросы для более чем 100 сообщений Windows, начиная от WM_ACTIVATE до WM_WININICHANGE. Узнать имя функции-обработчика сообщения для некоторого макроса ON_WM можно из документации по MFC, но правила обозначений прозрачны и можно просто заменить в имени сообщения префикс WM_ на On и преобразовать остальные символы имени сообщения, кроме первых символов отдельных слов, в нижний регистр. Например, WM_PAINT преобразуется в имя обработчика OnPaint, WM_LBUTTONDOWN в OnLButtonDown и т.п.

Типы параметров функции-обработчика сообщения можно узнать в справочной системе по MFC. В обработчик OnPaint не передается никаких параметров и у него нет возвращаемого значения. Но может быть и иначе, например, прототип обработчика OnLButtonDown выглядит так:

afx_msg void OnLButtonDown(UINT nFlags, CPoint point)

Параметр nFlags является набором битовых флагов, отражающих состояние кнопок мыши, клавиш Ctrl и Shift. В объекте point хранятся координаты указателя мыши в момент щелчка левой кнопкой. Параметры, передаваемые в обработчик сообщений, первоначально приходят в приложение в виде параметров сообщения wParam и lParam. В Windows API параметры wParam и lParam служат общим способом передачи информации о сообщении и не учитывают его специфику. Поэтому с обработчиками сообщений MFC работать гораздо удобнее, т.к. каркас приложения передает в них параметры в виде, наиболее удобном для конкретного сообщения.

Что будет, если вы хотите обработать сообщение, для которого в MFC нет макроса карты сообщений? Вы можете создать элемент карты для такого сообщения с помощью макроса ON_MESSAGE. У него два параметра: идентификатор сообщения и адрес соответствующей функции-члена класса. Например, для обработки сообщения WM_SETTEXT с помощью функции-члена OnSetText надо создать следующую запись в карте сообщений:

ON_MESSAGE(WM_SETTEXT, OnSetText)

Функция-член OnSetText должна быть объявлена так:

afx_msg LRESULT OnSetText(WPARAM wParam, LPARAM lParam);

В MFC есть еще ряд служебных макросов карты сообщений. Например, ON_COMMAND связывает с функциями-членами класса команды меню и события других элементов пользовательского интерфейса. Макрос ON_UPDATE_COMMAND_UI связывает элементы меню и другие объекты интерфейса с обработчиками обновления, которые синхронизируют состояние объектов интерфейса с внутренним состоянием приложения. Эти и другие макросы карты сообщений будут рассматриваться позже.

Еще раз вернемся к приложению Hello. В классе CMainWindow функция-член OnPaint и карта сообщений описываются в Hello.h:

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

Макрос afx_msg применяется для удобочитаемости исходного текста, он напоминает о том, что OnPaint является обработчиком сообщений. Этот макрос указывать не обязательно, т.к. при компиляции он заменяется пробелом.

Макрос DECLARE_MESSAGE_MAP обычно идет последним оператором в объявлении класса, т.к. в него встроены модификаторы доступа Си++. Вы можете объявлять компоненты класса и после DECLARE_MESSAGE_MAP, но обязательно указывайте для них соответствующий модификатор доступа public, protected или private.

 

Резюме

Перечислим наиболее важные особенности устройства MFC-приложения Hello. Сразу после запуска приложения создается глобальный объект-приложение подкласса CWinApp. Функция каркаса MFC AfxWinMain вызывает функцию объекта-приложения InitInstance. Эта функция создает объект-главное окно приложения, а его конструктор создает и выводит на экран окно Windows. После создания окна, InitInstance делает окно видимым с помощью функции-члена ShowWindow и затем посылает этому окну сообщение WM_PAINT с помощью функции UpdateWindow. Затем InitInstance возвращает управление и AfxWinMain вызывает у объекта-приложения функцию-член Run, внутри которой реализован цикл обработки сообщений. MFC с помощью карты сообщений преобразует поступающие сообщения WM_PAINT в вызовы функции-члена CMainWindow::OnPaint, а OnPaint выводит в клиентскую область окна символьную строку "Hello, MFC". Вывод текста выполняется с помощью функции-члена DrawText объекта-контекста устройства CPaintDC.

В MFC, по сравнению с программированием в Windows API, заметны новые сложности. Окно создается в два этапа. Нужен объект-приложение. Отсутствует функция WinMain. Все это отличается от программирования для Windows на уровне API. Но, если сравнить исходный текст MFC-приложения Hello и текст аналогичной программы, пользующейся API, станет заметно бесспорное преимущество MFC. MFC уменьшает размер исходного текста, и он становится проще для понимания, т.к. значительная часть исходного текста располагается внутри библиотеки классов. Поведение классов MFC можно изменять путем наследования от них своих собственных подклассов. В результате MFC оказывается очень эффективным средством программирования для Windows. Преимущества MFC становятся особенно очевидны при использовании сложных возможностей Windows, например, элементов управления ActiveX или при связи с другими приложениями через интерфейс OLE.

 

Упражнения

1) Ознакомьтесь с иерархией классов, приведенной в документе "Иерархия классов MFC" или в справочной системе Visual C++ по теме " Hierarchy Chart ".

2) На основе приведенных в лекции исходных файлов соберите приложение Hello. Для этого в Visual C++ надо выполнить следующие действия:

1. Выберите команду FileÞNew и затем перейдите на закладку Projects.

2. Выберите Win32 Application и в строке ввода Project Name укажите имя проекта При необходимости измените путь к папке проекта. Затем нажмите кнопку OK.

3. Добавьте в проект файлы с исходным текстом: заголовочный файл Hello.h и файл реализации Hello.cpp. Для добавления каждого файла выбирайте команду FileÞNew, затем указывайте тип и имя файла. Убедитесь, что флажок Add To Project включен, так что этот файл будет добавлен в проект. Затем нажмите OK, и введите содержимое файла.

4. Выберите команду ProjectÞSettings и перейдите на закладку General. В списке Microsoft Foundation Classes выберите вариант компоновки Use MFC In A Shared DLL и затем нажмите OK.

Параметр связи с MFC типа Use MFC In A Shared DLL приводит к уменьшению исполняемого файла, т.к. позволяет приложению обращаться к MFC посредством DLL. Если вы выберете вариант компоновки Use MFC In A Static Library, то Visual C++ присоединит к исполняемому EXE-файлу вашего приложения двоичный код MFC, что приведет к значительному увеличению объема EXE-файла. С другой стороны, приложение, статически скомпонованное с MFC, можно запустить на любом компьютере, независимо от того, есть на нем MFC DLL или нет.

3) Прочитайте документ "Венгерская форма записи имен переменных и типы данных Windows" (см. CD-ROM). Какой тип имеют переменные с именами bRepaint, szMsg, nAge, cxLength, clrBtn? Запишите операторы описания этих переменных.

4) Откройте файл WinMain.cpp (хранится в папке \DevStudio\Vc\Mfc\Src) и разберитесь с функцией AfxWinMain по описанию из п.2.4 лекции. Зачем в ней нужен оператор goto?

Посмотрите исходный текст функций CWinApp::Run (файл AppCore.cpp) и CWinThread::Run (файл ThrdCore.cpp) и найдите, где именно вызываются OnIdle и ExitInstance.

5) В приложении Hello обеспечьте вывод символьной строки красным цветом внутри зеленого описывающего прямоугольника. В контексте устройства для задания цвета текста и фонового цвета предназначены функции-члены SetTextColor и SetBkColor. Значение цвета имеет тип COLORREF. Это значение можно сформировать с помощью макроса RGB(r, g, b), например, красный цвет записывается так: RGB(255, 0, 0).

6) На основе приложения Hello разработайте приложение, которое будет реагировать на следующие сообщения Windows:

WM_LBUTTONDOWN щелчок левой кнопкой мыши
WM_RBUTTONDOWN щелчок правой кнопкой мыши
WM_KEYDOWN нажатие клавиши на клавиатуре
WM_MOVE перемещение окна
WM_SIZE изменение размеров окна
WM_NCRBUTTONDOWN щелчок правой кнопкой мыши в неклиентской области окна
WM_CLOSE закрытие окна

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

Прототипы функций-членов CMainWnd для обработки указанных сообщений узнайте в справочной системе, выполняя поиск по именам соответствующих макросов карты сообщений (ON_WM_LBUTTONDOWN и т.п.)

7) Изучите англо-русский словарь терминов по теме 4-й лекции (см. CD-ROM).




Поделиться:


Последнее изменение этой страницы: 2016-08-01; просмотров: 616; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 52.14.88.137 (0.01 с.)