11.18.4.4 - Настройка шрифтов для текстовых строк пункта меню

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

Здесь меню, заданное в файле определения ресурса. Обратите внимание, что строки для пунктов меню Regular (Обычный), Bold (Полужирный), Italic (Курсив) и Underline (Подчеркнутый) назначены во время выполнения программы, так что их строки пусты в файле определения ресурса.

MainMenu MENU
BEGIN
	POPUP   "&Character"
	BEGIN
		MENUITEM    "",    IDM_REGULAR
		MENUITEM SEPARATOR
		MENUITEM    "",    IDM_BOLD
		MENUITEM    "",    IDM_ITALIC
		MENUITEM    "",    IDM_ULINE
	END
END

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

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

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

Получить дескрипторы шрифтов, использованных для прорисовки этих пунктов.

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

Дескрипторы текстовых строк и шрифта сохраняются в массиве определяемых программой структур MYITEM. Определяемая программой функция GetAFont создает шрифт, который соответствует атрибутам данного шрифта и возвращает значение дескриптора шрифта. Дескрипторы разрушаются в ходе обработки сообщения WM_DESTROY.

В ходе обработки сообщения WM_MEASUREITEM, пример получает ширину и высоту строки пункта меню и копирует эти значения в структуру MEASUREITEMSTRUCT. Windows использует значения ширины и высоты, чтобы вычислить размер меню.

В ходе обработки сообщения WM_DRAWITEM, строка пункта меню рисуется, оставляя место рядом со строкой для значка "галочки ". Если пользователь выбирает пункт, цвет выбранного текста и фона используются, чтобы прорисовать пункт.

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
	
	typedef struct _MYITEM {
		HFONT hfont;
		LPSTR psz;
	} MYITEM;				// структура для шрифта и строки пункта

	MYITEM *pmyitem;			// указатель на шрифт и строку пункта
	static MYITEM myitem[CITEMS];	// массив MYITEMS
	static HMENU hmenu;		// дескриптор главного меню
	static COLORREF crSelText;		// цвет текста выбранного пункта
	static COLORREF crSelBkgnd;		// цвет фона выбранного пункта
	COLORREF crText;			// цвет текста невыбранного пункта
	COLORREF crBkgnd;			// цвет фона невыбранного пункта
	LPMEASUREITEMSTRUCT lpmis;		// указывает на данные пункта
	LPDRAWITEMSTRUCT lpdis;		// указывает на данные для прорисовки пункта
	HDC hdc;				// дескриптор экранного DC
	SIZE size;			// протяженность текста пункта меню
	DWORD dwCheckXY;			// размеры метки «галочка»
	WORD wCheckX;			// ширина метки «галочка»
	int nTextX;			// ширина пункта меню
	int nTextY;			// высота пункта меню
	int i;				// цикл счета
	HFONT hfontOld;			// дескриптор старого шрифта
	BOOL fSelected = FALSE;		// флажок выбора пункта меню

	switch (uMsg) {
		case WM_CREATE:

			// Изменим пункты меню Regular (Обычный), Bold (Полужирный), Italic
			// (Курсив) и Underline (Подчеркнутый), чтобы делать их нарисованными
			// пользователем пунктами. Свяжем структуру MYITEM с каждым пунктом,
			// которые содержат строку и шрифт, чтобы обработать каждый пункт.

			hmenu = GetMenu(hwnd);
			ModifyMenu(hmenu, IDM_REGULAR, MF_BYCOMMAND |
				MF_CHECKED | MF_OWNERDRAW, IDM_REGULAR,
				(LPTSTR) &myitem[REGULAR]);
			ModifyMenu(hmenu, IDM_BOLD, MF_BYCOMMAND |
				MF_OWNERDRAW, IDM_BOLD, (LPTSTR) &myitem[BOLD]);
			ModifyMenu(hmenu, IDM_ITALIC, MF_BYCOMMAND |
				MF_OWNERDRAW, IDM_ITALIC,
				(LPTSTR) &myitem[ITALIC]);
			ModifyMenu(hmenu, IDM_ULINE, MF_BYCOMMAND |
				MF_OWNERDRAW, IDM_ULINE,(LPTSTR)&myitem[ULINE]);

			 // Извлечем дескриптор шрифта каждого пункта и скопируем их в
			// элемент hfont структуры MYITEM каждого пункта.
			// А также, скопируем в структуры строку каждого пункта
			myitem[REGULAR].hfont = GetAFont(REGULAR);
			myitem[REGULAR].psz = "Regular";
			myitem[BOLD].hfont = GetAFont(BOLD);
			myitem[BOLD].psz = "Bold";
			myitem[ITALIC].hfont = GetAFont(ITALIC);
			myitem[ITALIC].psz = "Italic";
			myitem[ULINE].hfont = GetAFont(ULINE);
			myitem[ULINE].psz = "Underline";

			// Извлечем текст и цвет фона выбранного текста меню.

			crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
			crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);

			return 0;

		case WM_MEASUREITEM:

			// Извлечем контекст устройства главного окна.

			hdc = GetDC(hwnd);


			// Извлечем указатели на пункты меню в
			// структурах MEASUREITEMSTRUCT и MYITEM.

			lpmis = (LPMEASUREITEMSTRUCT) lParam;
			pmyitem = (MYITEM *) lpmis->itemData;

			// Выберем шрифт, связанный пунктом в 
			// контексте устройства главного окна.

			hfontOld = SelectObject(hdc, pmyitem->hfont);

			// Извлечем ширину и высоту строки пункта, 
			// а затем скопируем их в элементы itemWidth
			// и itemHeight  структуры MEASUREITEMSTRUCT.

			GetTextExtentPoint32(hdc, pmyitem->psz,
			lstrlen(pmyitem->psz), &size);
			lpmis->itemWidth = size.cx;
			lpmis->itemHeight = size.cy;

			// В контексте устройства обратно выберем старый
			// шрифт, и затем освободим контекст устройства.

			SelectObject(hdc, hfontOld);
			ReleaseDC(hwnd, hdc);

			return TRUE;

			break;

		case WM_DRAWITEM:

			// Получим указатели на пункты меню в структурах 
			// DRAWITEMSTRUCT и MYITEM.

			lpdis = (LPDRAWITEMSTRUCT) lParam;
			pmyitem = (MYITEM *) lpdis->itemData;

			// Если пользователь выбрал пункт, используйте выбранный
			// текст и цвет фона для отображения пункта.

			if (lpdis->itemState & ODS_SELECTED) {
				crText = SetTextColor(lpdis->hDC, crSelText);
				crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd);
				fSelected = TRUE;
			}

			// Не забудьте оставить пространство в пункте 
			// меню для значка "галочки ". Извлеките ширину значка
			// и добавьте ее к ширине пункта меню.

			dwCheckXY = GetMenuCheckMarkDimensions();

			wCheckX = LOWORD(dwCheckXY);
			nTextX = wCheckX + lpdis->rcItem.left;
			nTextY = lpdis->rcItem.top;

			// Выберите шрифт, связанный с пунктом в контексте 
			// устройства пункта, а затем нарисуйте строку.

			hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);
			ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE,
				&lpdis->rcItem, pmyitem->psz,
				lstrlen(pmyitem->psz), NULL);


			// Выберите обратно предыдущий шрифт в контексте устройства.

			SelectObject(lpdis->hDC, hfontOld);

			// Возвратите текст и цвета фона в 
			// их нормальное состояние (не выбранное).

			if (fSelected) {
				SetTextColor(lpdis->hDC, crText);
				SetBkColor(lpdis->hDC, crBkgnd);
			}

			return TRUE;

		.
		. // Обработка других сообщений.
		.

		case WM_DESTROY:

			// Уничтожим дескрипторы шрифта пунктов меню.

			for (i = 0; i < CITEMS; i++)
				DeleteObject(myitem[i].hfont);

			PostQuitMessage(0);
			break;

		default:
			return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	return NULL;
}

HFONT GetAFont(fnFont)
int fnFont;			 // флажки атрибутов шрифта
{
	static LOGFONT lf;  // структура для информации о шрифте


	// Получим дескриптор для моноширинного шрифта ANSI, а информацию о шрифте скопируем
	// в структуру LOGFONT.

	GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT), &lf);

	// Установим соответствующие атрибуты шрифта.

	if (fnFont == BOLD)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_NORMAL;

	lf.lfItalic = (fnFont == ITALIC);
	lf.lfItalic = (fnFont == ULINE);

	// создадим шрифт, а затем возвратим его дескриптор.

	return CreateFont(lf.lfHeight, lf.lfWidth,
		lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
		lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut,
		lf.lfCharSet, lf.lfOutPrecision, lf.lfClipPrecision,
		lf.lfQuality, lf.lfPitchAndFamily, lf.lfFaceName);
}

Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Владимир Соковиков - 07.10.2002