Общая информация про MFC. Иерархия классов MFC. Место MFC в среде разработчика Visual C++

Общая информация про MFC. Иерархия классов MFC. Место MFC в среде разработчика Visual C++


Иерархия основных классов MFC. CObject (обеспечивает базовые операции ввода/вывода) →CGDIObject (поддержка графических объектов); CDC (класс, обеспечивающий связь с устройствами); CExeption (обработка исключительных ситуаций в MFC); CFile (базовый класс для обработки файлов); CMenu (поддержка обьектов меню); CCmdTarget (базовый для обработки сообщения)1)→CWnd (базовый класс для окон); СFrameWnd; 2)→CWinThread;CwinApp;

Структура простой MFC программы. Программа на MFC содержит, по крайней мере, 2 класса. И эти классы порождаются от CWnd и CWinThread. Для реализации простой программы сделаем следующую последовательность шагов: 1) Создать класс приложений, порожденный от CWinApp. 2) Создать класс окна, порожденный от CFrameWnd. 3) Для класса приложения объявить функцию InitInstance(). 4) В конструкторе класса окна вызвать функцию Create для создания окна. 5) Объявить глобальный объект приложения. 6) Создать карту сообщения. 7) Подключить заголовочные файлы и определиться с типом объектов.



//App.h

class CApp: public CWinApp

{public: BOOL InitInstance ();};

Class CMainWin:public CFrameWnd

{public: CMainWin (); DECLARE_MESSAGE_MAP ()

};

//App.cpp

#include <afxwin.h>

#include “App.h”

BOOL CApp::InitInstance ()

{m_pMainWnd=newCMainWin;m_pMainWnd→ShouWindow (m_nCmdShow); m_pMainWnd→UpdateWindow (); return TRUE;}

CMainWin::CMainWin ()

{Create (NULL, “Первая MFC-программа”);}

CApp App;

BEGIN_MESSAGE_MAP (CMainWin, CFrameWnd)

END_MESSAGE_MAP ()



MFC - (Microsoft Foundation Class Library) базовая библиотека классов; Иерархия классов MFC. Библиотека MFC содержит большую иерархию классов, написанных на C++. В ее вершине находится класс CObject, который содержит различные функции, используемые во время выполнения программы и предназначенные, в частности, для предоставления информации о текущем типе во время выполнения, для диагностики, и для сериализации. Информация о типе времени выполнения. Если указатель или ссылка ссылается на объект, производный от класса CObject, то в этом случае предусмотрен механизм определения реального типа объекта с помощью макроса RUNTIME _CLASS(). Хотя в C++ имеется механизм RTTI, механизм, реализованный в MFC, намного более эффективен по производительности. 

Диагностика. Каждый класс, производный от CObject, может по запросу проверить свое внутреннее состояние и выдать диагностическую информацию. Это интенсивно используется в MFC при отладке. 

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

Основные классы. Некоторые классы порождаются непосредственно от CObject. Наиболее широко используемыми среди них являются CCmdTarget, CFile, CDC, CGDIObject и CMenu. Класс CCmdTarget предназначен для обработки сообщений. Класс CFile предназначен для работы с файлами. Класс CDC обеспечивает поддержку контекстов устройств. Об контекстах устройств мы будем говорить несколько позднее. В этот класс включены практически все функции графики GDI. CGDIObject является базовым классом для различных DGI-объектов, таких как перья, кисти, шрифты и другие. Класс CMenu предназначен для манипуляций с меню. От класса CCmdTarget порождается очень важный класс CWnd. Он является базовым для создания всех типов окон, включая масштабируемые ("обычные") и диалоговые, а также различные элементы управления. Наиболее широко используемым производным классом является CFrameWnd. Как Вы увидите в дальнейшем, в большинстве программ главное окно создается с помощью именно этого класса. От класса CCmdTarget, через класс CWinThread, порождается, наверное, единственный из наиболее важных классов, обращение к которому в MFC-программах происходит напрямую: С WinApp. Это один из фундаментальных классов, поскольку предназначен для создания самого приложения. В каждой программе имеется один и только один объект этого класса. Как только он будет создан, приложение начнет выполняться. 

Функции-члены в MFC. Большинство функций, вызываемых в MFC-программе, являются членами одного из классов, определенных в библиотеке. Большинство функций API доступны через функции-члены MFC. Тем не менее, всегда можно обращаться к функциям API напрямую. Иногда это бывает необходимым, но все же в большинстве случаев удобнее использовать функции-члены MFC. 

Глобальные функции в MFC. В библиотеке есть ряд глобальных функций. Все они начинаются с префикса Afx. (Когда MFC только разрабатывалась, то проект назывался AFX, Application Framework. После ряда существенных изменений AFX была переработана в MFC, но прежнее название сохранилось во многих идентификаторах библиотеки и в названиях файлов.) Например, очень часто используется функция AfxMessageBox(), отображающая заранее определенное окно сообщения. Но есть и член-функция MessageBox(). Таким образом, часто глобальные функции перекрываются функциями-членами. 	Файл AFXWIN.H. Все MFC-программы включают заголовочный файл AFXWIN.H. В нем, а также в различных вспомогательных файлах, содержатся описания классов, структур, переменных и других объектов MFC. Он автоматически подключает большинство заголовочных файлов, относящихся к MFC, в том числе и WINDOWS.H, в котором определены все функции Windows API и другие объекты, которые используются при традиционном программировании на С и "чистом" APL.

Каркас MFC-программы. В простейшем случае программа, написанная с помощью MFC, содержит два класса, порождаемые от классов иерархии библиотеки: класс, предназначенный для создания приложения, и класс, предназначенный для создания окна. Другими словами, для создания минимальной программы необходимо породить один класс от CWinApp, а другой - от CFrameWnd. Эти два класса обязательны для любой программы. Кроме создания вышеупомянутых классов, в программе также должна быть организована обработка всех сообщений, поступающих от Windows. В данном примере программа еще ничего полезного не делает, поэтому отвечать на каждое сообщение не нужно. MFC обработает все сообщения, которые нас не интересуют. Тем не менее в этом примере присутствует карта откликов на сообщения, или просто карта сообщений. Позже мы рассмотрим ее подробнее. Для создания стандартного окна в приложении должен наследоваться класс от CFrameWnd. В данном примере он называется CMainWin. Он содержит конструктор и макрос DECLARE_MESSAGE_MAP(). Макрос на самом деле разворачивается в декларацию карты сообщений, которая определяет, какая член-функция класса должна вызываться в ответ на сообщение Windows. Этот макрос применяется для любого окна, в котором обрабатываются сообщения. Он должен быть последним в декларации класса. Само окно создается в конструкторе с помощью вызова функции Create(). Эта функция используется почти во всех приложениях. Она выполняет действия по созданию окна. В этом примере приведен самый простой случай ее использования. Пока нам нужно знать, что второй параметр определяет заголовок окна, а первый чаще всего равен NULL. 

Класс САрр приложения порождается от CWinApp. Этот класс отвечает за работу программы. В примере используется член-функция со следующим прототипом: virtual BOOL CWinApp::lnitlnstance(); Это виртуальная функция, которая вызывается каждый раз при запуске программы. В ней должны производиться все действия, связанные с инициализацией приложения. Функция должна возвращать TRUE при успешном завершении и FALSE в противном случае. В нашем случае, в функции сначала создается объект класса CMainWin, и указатель на него запоминается в переменной m_pMainWnd. Эта переменная является членом класса CWinThread. Она имеет тип CWnd* и используется почти во всех MFC-программах, потому что содержит указатель на главное окно. В последующих двух строчках через нее вызываются функции-члены окна. Когда окно создано, вызывается функция с прототипом: BOOL CWnd::ShowWindow(int How);

Обработка сообщений. Windows взаимодействует с приложением, посылая ему сообщения. Поэтому обработка сообщений является ядром всех приложений. В традиционных приложениях Windows (написанных с использованием только API) каждое сообщение передается в качестве аргументов оконной функции. Там обычно с помощью большого оператора switch определяется тип сообщения, извлекается информация и производятся нужные действия. Это громоздкая и чреватая ошибками процедура. С помощью MFC все делается намного проще. Здесь мы рассмотрим обработку в программе некоторых наиболее часто используемых сообщений. 

Обработка сообщений в MFC. В MFC включен набор предопределенных функций-обработчиков сообщений, которые можно использовать в программе. Если программа содержит такую функцию, то она будет вызываться всякий раз, когда поступает связанное с ней сообщение. При наличии дополнительной информации в сообщении она передается в качестве аргументов функции. Для организации обработки сообщений нужно выполнить следующие действия: В карту сообщений программы должна быть включена команда соответствующего сообщения. Прототип функции-обработчика должен быть включен в описание класса, ответственного за обработку данного сообщения. В программу должна быть включена реализация функции-обработчика.



	2. Понятие контекста устройства, применение контекстов устройства, обработка сообщений WM_СHAR, WM_PAINT

	

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

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

	В традиционных Windows-программах контекст устройства получают вызовом функции GetDC(), а освобождают с помощью ReleaseDC(). Поскольку Windows может предоставить лишь небольшое количество контекстов, важно, чтобы программа освободила контекст после окончания работы с ним. MFC имеет соответствующие классы, способные руководить этим процессом. В частности, при создании экземпляра объекта типа CClientDC программе предоставляется контекст устройства. Если этот объект необходимо изъять, вызывается функция ReleaseDC() и контекст устройства автоматически освобождается. Конструктор класса СClientDC записывается в виде:

	СClientDC (CWnd *Windows);

	где параметр Windows является указателем на окно, которому предоставляется контекст устройства. Для активного окна можно указать ключевое слово this.

	Вывод в клиентскую область окна программы может обеспечиваться с помощью функции TextOut():

	virtual BOOL CDC::TextOut(int X, int Y, LPCSTR lpszStr, int Length);

	где X, Y – координаты точки начала вывода в пикселях (относительно левого верхнего угла окна программы), lpszStr – указатель на строку, которая выводится, а Length – его длина. Пример иллюстрирует возможный вариант реализации обработчика нажатия клавиш клавиатуры - в точку (1, 1) окна программы.

	Пример обработчика нажатия клавиш клавиатуры

	

	char str [80]; // строка символов для вывода

	void CMainWin::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

	{CClientDC dc(this);			// получение контекста окна

	 dc.TextOut(1,1," ", 3);		// удаление старого текста

	 wsprintf(str,"%с", ch);		// формирование строки с кодом клавиши

	 dc.TextOut(1, 1, str, strlen(str));	// вывод строки в координату (1, 1)

	}

	

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

	Обновление содержимого рабочей области окна программы

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

	Сообщению WM_PAINT отвечает макрокоманда ON_WM_PAINT(), а макрокоманде – обработчик OnPaint(). Этот обработчик может выглядеть следующим образом: 

	
Пример обработчика сообщения WM_PAINT 

	void CMainWin::OnPaint()

	{CPaintDC dc(this);			// получение контекста окна

	 dc.TextOut(1,1, str, strlen(str));	// отображение символа

	}

	

	В примере приведен обработчик OnPaint(), который обеспечивает вывод на экран символа, введенного с клавиатуры пользователем соответственно обработчику OnChar(), записанному в предыдущем примере. Видно, что для получения контекста устройства здесь использован объект другого типа, а именно CPaintDC. В отличие от CClientDC, который работает только с клиентской частью окна программы, CPaintDC обеспечивает роботу со всей плоскостью окна.

	В программе желательным было бы, чтобы Windows самостоятельно решала, когда ей вызвать сообщение WM_PAINT. Это так и происходит, например, когда пользователь программы минимизирует окно, максимизирует, движет экраном, изменяет размеры окна и т.п.. Но иногда необходимо проводить обновление окна принудительно. Для того, чтобы прислать сообщение WM_PAINT, программа вызывает функцию InvalidateRect() – член класса CWnd, которая имеет следующий прототип:

	

	void CWnd::InvalidateRect(LPCRECT lpRegion, BOOL Erase=TRUE);

	

	где lpRegion – указатель на область окна, которую необходимо обновить, Erase – флаг, который в значении TRUE устанавливает изъятие предыдущего содержимого окна. Если указать первому параметру значения NULL, произойдет полное обновление окна. Вызов функции InvalidateRect() обеспечивает принудительную посылку сообщения WM_PAINT и выполнение обработчика OnPaint(). 

	Полное обновление окна, как например:

	InvalidateRect(NULL);

	

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

	

	СRect region(10,10,100,100);

	InvalidateRect(region);

	

	в таком случае будет обновляться лишь область, ограниченная прямоугольником с левым верхним углом (10,10) и нижним правым углом (100,100). Такой вариант обновления становится особенно интересным, если необходимо обеспечить подвижность отдельных элементов окна, а этого можно достичь с одновременным применением обработки сообщения таймера WM_TIMER.



3. Основы работы с текстом в MFC. Функции вывода текста, установки цветов, режимов отображения, получение метрик



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

Небольшое введение

Любой шрифт, с которым мы имеем дело в Windows, характеризуется несколькими параметрами. Гарнитура (typeface) - это совокупность нескольких начертаний шрифта, объединенных стилевыми и другими признаками. Пример гарнитур: Arial, Times New Roman, MS Sans Serif. Размер шрифта - это высота прямоугольника, в который помещаются все символы шрифта, выражается в специальных единицах - пунктах. Пункт равен 1/72 части дюйма. Эта единица пришла из полиграфии. Начертание - это специфические характеристики шрифта. В Windows доступны четыре начертания: нормальное (normal), курсивное (italic), жирное (bold) и жирное курсивное (bold italic). Кроме того, шрифты могут быть моноширинные (fixed pitch', пример - Courier New, и пропорциональные (variable pitch), пример - Times New Roman.

Сейчас в Windows в основном используются шрифты двух групп: растровые (примеры - MS Sans Serif, Fixedsys) и контурные TrueType (примеры - Arial, Courier New). Первые представляют собой жестко определенные битовые матрицы для каждого символа и предназначены для отображения не очень крупного текста на экране. Вторые представляют собой очень сложные объекты. В них заданы контуры символов, которые закрашиваются по определенным правилам. Каждый шрифт TrueType - это программа на специальном языке, которая выполняется интерпретатором под названием растеризатор. Программа шрифта полностью определяет способ расчета конкретных битовых матриц символов на основе контуров. При использовании символов маленького размера (высотой приблизительно до 30 точек) модель контуров становится некорректной и символы сильно искажаются. Для борьбы с этим в качественных шрифтах используется разметка (хинты). Разметка шрифта -чрезвычайно сложный и долгий процесс, поэтому на рынке встречается немного качественно размеченных шрифтов. Поэтому использовать TrueType шрифты для вывода текста на экран нежелательно, за исключением стандартных шрифтов Windows (Times New Roman, Arial и Courier New), которые очень качественно размечены.

Координаты при выводе текста

За вывод текста в окно в MFC отвечает функция CDC::TextOut(). Ей нужно указать координаты для вывода строки. Эти координаты являются логическими координатами, то есть они измеряются в логических единицах. Это относится и к другим функциям вывода информации в окно. В процессе отображения информации логические координаты преобразуются в пиксели. По умолчанию логическими координатами являются пиксели, что для нас очень удобно.

Задание цвета текста и фона

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

virtual COLORREF CDC::SetTextColor(COLORREF Color); virtual COLORREF CDC::SetBkColor(COLORREF Color);

Функции возвращают значение предыдущего цвета. Тип COLORREF представляет собой 32-разрядное беззнаковое целое число - представление цвета в виде красной, зеленой и синей компонент, каждая размером в 8 бит. Для формирования этого значения существует макрос RGB().

Задание режима отображения фона

С помощью функции SetBkMode() можно задать режим отображения фона. Прототип функции такой: int CDC::SetBkMode(int Mode);

Функция определяет, что происходит с текущим цветом фона (а также некоторых других элементов) при отображении текста. Режим может принимать одно из двух значений: OPAQUE и TRANSPARENT. В первом случае при выводе текста будет выводится также и текущий фон. Во втором случае фон выводится не будет (он будет "прозрачным"). По умолчанию используется режим OPAQUE.

Получение метрик текста

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

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

BOOL CDC::GetTextMetrics(LPTEXTMETRICS TextAtttrib) const; Параметр является указателем на структуру TEXTMETRIC, в которую будут записаны установки текущего шрифта контекста устройства. Структура имеет достаточно много полей. Наиболее важные поля следующие:



	LONG tmHeight	Полная высота шрифта

	LONG tmAscent	Высота над базовой линией

	LONG tmDescent	Высота подстрочных элементов

	LONG tmlntemalLeading	Пустое пространство над символами

	LONG tmExternalLeading	Пустой интервал между строками

	LONG tmMaxCharWidth	Максимальная ширина символов



Для получения числа логических единиц по вертикали между строками нужно сложить значения tmHeight и tmExternalLeading. Это не то же самое, что и высота символов.

Изменение шрифтов

Предположим, что мы имеем готовый инициализированный экземпляр класса CFont, содержащий некоторый шрифт (как это сделать, мы рассмотрим чуть ниже). Для изменения шрифта в контексте устройства используется член-функция SelectObject(), которая в нашем случае принимает один параметр - указатель на объект шрифта. После выполнения этой функции текст будет выводится новым шрифтом. Хотя функция возвращает указатель на объект старого шрифта, сохранять его вовсе не обязательно. 

Инициализация объекта шрифта: выбор шрифта

После того, как объект класса CFont создан, необходимо инициализировать его конкретным шрифтом из установленных в системе, с заданными параметрами. Это может быть как растровый, так и контурный шрифт. Наверное, Вы справедливо ожидаете наличия функции, которая позволяет задать только гарнитуру шрифта, начертание и размер, после чего шрифт будет проинициализирован. К сожалению, ни в MFC ни в Windows такой функции нет. Единственная функция, которая подходит для выполнения этой задачи, имеет такой прототип:



BOOL CFont: :CreateFont(int nHeight, int nWidth,

int nEscapement,

int nOrientation,

int nWeight,

BYTE bltalic,

BYTE bUnderline,

BYTE cStrikeOut,

BYTE nCharSet,

BYTE nOutPrecision,

BYTE nClipPrecision,

BYTE nQuality,

BYTE nPitchAndFamily,

LPCTSTR IpszFacename);



Функция крайне неудобна, и смысл многих ее параметров на сегодняшний день не актуален.

Функция была создана давно, и тогда казалась увлекательной идея подстановки и замены шрифтов, суть которой заключалась в следующем: программист задает такие параметры шрифта, какие он хочет иметь, a Windows сама на основе имеющихся в системе шрифтов произведет необходимые трансформации и синтезирует требуемый шрифт из имеющихся. Впоследствии оказалось, что эта технология не может быть удовлетворительно работоспособной (так как практически это сложная задача из области искусственного интеллекта). Также функция позволяет проводить трансформации шрифта - растягивать и сжимать его, выводить текст под углом. Сейчас это используется редко. Графические пакеты используют свои алгоритмы трансформаций, и часто используют шрифты PostScript. Суть же заключается в том, что качественно могут быть отображены только те шрифты и в тех начертаниях, которые присутствуют в системе, причем без трансформаций (то есть с использованием хинтов, которые отключаются при трансформациях). Для большинства современных программ требуется высокое качество отображения текста, поэтому никакие трансформации и подстановки неуместны. Практически, являются разумными лишь два варианта поведения: либо программа будет использовать точно тот шрифт, который запросила, в таком виде, в каком его создал разработчик, либо откажется работать при отсутствии шрифта.

Рассмотрим пример кода, который позволяет задать размер шрифта, начертание и гарнитуру, минимизировав возможные трансформации шрифта и синтеза при отсутствии заданного. Такие же параметры для функции CreateFontQ Вы можете использовать в своих программах.

Желательно также проверять наличие шрифта, если он не является стандартным. Вот код, который используется в примере программы (указатель на объект шрифта хранится в переменной mjpFoni):



void CMainFrame::SetClientFont(CString Typeface, // Гарнитура

int Size, // размер в пунктах

BOOL Bold, // Признак жирного начертания

BOOL Italic // Признак наклонного начертания

}

{

// Получим контекст окна

CWindowDC winDC(this);

	// Узнаем, сколько пикселей в одном логическом дюйме У

int pixelsPerlnch = winDC.GetDeviceCaps(LOGPIXELSY);

// Узнаем высоту в пикселях шрифта размером Size пунктов

int fontHeight = -MulDiv(Size, pixelsPerlnch, 72);

// Устанавливаем параметр жирности для функции CreateFont()

int Weight = FW_NORMAL;

if(Bold)

Weight = FWBOLD;

// Удаляем предыдущий экземпляр шрифта — нельзя дважды инициализировать шрифт вызовом CreateFont().

delete m pFont;

mjpFont = new CFont;

// Создание шрифта. Большинство параметров не используются.

m_pFont->CreateFont(fontHeight, 0, 0, 0, Weight, Italic, О, О,

DEFAULT_CHARSET,OUT_DEFAULT PRECIS,

CLIP_DEFAULT_PRECIS, PROOF_QUALITY,

DEFAULT_PITCH | FF_DONTCARE, Typeface);

}


Литература



1. Советов Б.Я., Яковлев С.А. “Робототехника”. - М.: Высш. шк., 2007.- 271 с.

2. Методические указания к курсовой работе по дисциплине "Робототехника и мехатроника" для студентов специальности ГКСР ". 2005.

3. Радиоэлектроника. Пичукин Г.В. М. 1999.