Общая информация про MFC. Иерархияклассов MFC. Место MFCв средеразработчика VisualC++
Иерархия основныхклассов 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
#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— (MicrosoftFoundationClassLibrary) базовая библиотека классов; Иерархияклассов 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) окна программы.
Примеробработчика нажатия клавиш клавиатуры
charstr [80]; // строка символов для вывода
void CMainWin::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{CClientDCdc(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
voidCMainWin::OnPaint()
{CPaintDCdc(this); // получение контекста окна
dc.TextOut(1,1,str, strlen(str)); // отображение символа
}
Впримере приведен обработчик OnPaint(), который обеспечивает вывод наэкран символа, введенного с клавиатуры пользователем соответственно обработчикуOnChar(), записанному в предыдущем примере. Видно, что для полученияконтекста устройства здесь использован объект другого типа, а именно CPaintDC.В отличие от CClientDC, который работает только с клиентской частью окнапрограммы, CPaintDC обеспечивает роботу со всей плоскостью окна.
Впрограмме желательным было бы, чтобы Windows самостоятельно решала, когда ейвызвать сообщение WM_PAINT. Это так и происходит, например, когда пользовательпрограммы минимизирует окно, максимизирует, движет экраном, изменяет размерыокна и т.п… Но иногда необходимо проводить обновление окна принудительно. Длятого, чтобы прислать сообщение WM_PAINT, программа вызывает функцию InvalidateRect()– член класса CWnd, которая имеет следующий прототип:
voidCWnd::InvalidateRect(LPCRECT lpRegion, BOOL Erase=TRUE);
где lpRegion– указатель на область окна, которую необходимо обновить, Erase – флаг,который в значении TRUE устанавливает изъятие предыдущего содержимого окна.Если указать первому параметру значения NULL, произойдет полное обновлениеокна. Вызов функции InvalidateRect() обеспечивает принудительную посылкусообщения WM_PAINT и выполнение обработчика OnPaint().
Полноеобновление окна, как например:
InvalidateRect(NULL);
занимаетмного ресурсов системы, которая визуально может выглядеть мерцанием рабочегоокна программы. Этого можно избегнуть, задав отдельно область обновления,например:
СRectregion(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) и жирное курсивное (bolditalic). Кроме того, шрифты могут быть моноширинные(fixedpitch', пример — Courier New, и пропорциональные (variablepitch), пример — 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по умолчанию текст выводится черным цветом на текущем(обычно белом) фоне. Однако с помощью следующих функций эти параметры можноизменить:
virtualCOLORREF CDC::SetTextColor(COLORREF Color); virtual COLORREFCDC::SetBkColor(COLORREF Color);
Функциивозвращают значение предыдущего цвета. Тип COLORREFпредставляет собой 32-разрядноебеззнаковое целое число — представление цвета в виде красной, зеленой и синейкомпонент, каждая размером в 8 бит. Для формирования этого значения существуетмакрос RGB().
Заданиережима отображения фона
Спомощью функции SetBkMode()можно задать режимотображения фона. Прототип функции такой: int CDC::SetBkMode(int Mode);
Функцияопределяет, что происходит с текущим цветом фона (а также некоторых другихэлементов) при отображении текста. Режим может принимать одно из двух значений:OPAQUEи TRANSPARENT. В первом случае при выводе текста будет выводитсятакже и текущий фон. Во втором случае фон выводится не будет (он будет«прозрачным»). По умолчанию используется режим OPAQUE.
Получениеметрик текста
Большинствотекстовых шрифтов являются пропорциональными, то есть ширина символов у нихразная. Кроме того, расстояние между строками зависит как от шрифта, так и отего размера. Весь вывод текста в Windows осуществляетсяпрограммно, поэтому необходимо учитывать все эти параметры.
Например,при выводе одной текстовой строки после другой предполагается, что известнывысота шрифта и количество пикселей между строками. Получить эту информациюможно с помощью функции:
BOOL CDC::GetTextMetrics(LPTEXTMETRICS TextAtttrib) const; Параметр является указателем на структуру TEXTMETRIC, в которую будут записаны установки текущего шрифтаконтекста устройства. Структура имеет достаточно много полей. Наиболее важныеполя следующие:
LONGtmHeight Полная высота шрифта
LONGtmAscent Высота над базовой линией
LONGtmDescentВысота подстрочных элементов
LONGtmlntemalLeading Пустое пространство над символами
LONGtmExternalLeading Пустой интервал между строками
LONGtmMaxCharWidth Максимальная ширина символов
Дляполучения числа логических единиц по вертикали между строками нужно сложитьзначения 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.