Использование меню
В этом разделе описаны следующие задачи:
- Использование ресурса Menu-Template
- Создание контекстного меню
- Использование Menu-Item растровых изображений
- Создание элементов меню Owner-Drawn
- Использование настраиваемых точечных изображений флажка
Использование ресурса Menu-Template
Обычно меню включается в приложение путем создания ресурса шаблона меню и последующей загрузки меню во время выполнения. В этом разделе описывается формат шаблона меню и объясняется, как загрузить ресурс шаблона меню и использовать его в приложении. Сведения о создании ресурса шаблона меню см. в документации по средствам разработки.
- Расширенный формат Menu-Template
- Старый формат Menu-Template
- Загрузка ресурса Menu-Template
- Создание меню класса
Расширенный формат Menu-Template
Расширенный формат шаблона меню поддерживает дополнительные функции меню. Как и стандартные ресурсы шаблона меню, ресурсы расширенных шаблонов меню имеют RT_MENU тип ресурса. Система различает два формата ресурсов по номеру версии, который является первым элементом заголовка ресурса.
Шаблон расширенного меню состоит из структуры MENUEX_TEMPLATE_HEADER , за которой следует еще одна MENUEX_TEMPLATE_ITEM структур определения элементов.
Старый формат Menu-Template
Старый шаблон меню (Microsoft Windows NT 3.51 и более ранние версии) определяет меню, но не поддерживает новые функции меню. Старый ресурс шаблона меню имеет тип ресурса RT_MENU .
Старый шаблон меню состоит из структуры MENUITEMTEMPLATEHEADER , за которой следует одна или несколько структур MENUITEMTEMPLATE .
Загрузка ресурса Menu-Template
Чтобы загрузить ресурс шаблона меню, используйте функцию LoadMenu , указав дескриптор модуля, который содержит ресурс, и идентификатор шаблона меню. Функция LoadMenu возвращает дескриптор меню, который можно использовать для назначения меню окну. Это окно становится окном владельца меню, получая все сообщения, созданные меню.
Чтобы создать меню на основе шаблона меню, который уже находится в памяти, используйте функцию LoadMenuIndirect . Это полезно, если приложение создает шаблоны меню динамически.
Чтобы назначить меню окну, используйте функцию SetMenu или укажите дескриптор меню в параметре hMenu функции CreateWindowEx при создании окна. Еще один способ назначить меню окну — указать шаблон меню при регистрации класса окна; шаблон определяет указанное меню как меню класса для этого класса окна.
Чтобы система автоматически назначила определенное меню окну, укажите шаблон меню при регистрации класса окна. Шаблон определяет указанное меню как меню класса для этого класса окна. Затем при создании окна указанного класса система автоматически назначает указанное меню окну.
Меню нельзя назначить окну, которое является дочерним.
Чтобы создать меню классов, добавьте идентификатор ресурса menu-template в качестве элемента lpszMenuName структуры WNDCLASS , а затем передайте указатель на структуру функции RegisterClass .
Создание меню класса
В следующем примере показано, как создать меню классов для приложения, создать окно, использующее меню класса, и обработать команды меню в процедуре окна.
Ниже приведена соответствующая часть файла заголовка приложения:
// Menu-template resource identifier
#define IDM_MYMENURESOURCE 3
Ниже приведены соответствующие части самого приложения.
HINSTANCE hinst;
int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { }; // message
WNDCLASS wc; // windowclass data
HWND hwnd; // handle to the main window
// Create the window class for the main window. Specify
// the identifier of the menu-template resource as the
// lpszMenuName member of the WNDCLASS structure to create
// the class menu.
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = MAKEINTRESOURCE(IDM_MYMENURESOURCE);
wc.lpszClassName = "MainWClass";
if (!RegisterClass(&wc))
return FALSE;
hinst = hinstance;
// Create the main window. Set the hmenu parameter to NULL so
// that the system uses the class menu for the window.
hwnd = CreateWindow("MainWClass", "Sample Application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,
NULL);
if (hwnd == NULL)
return FALSE;
// Make the window visible and send a WM_PAINT message to the
// window procedure.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Start the main message loop.
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(hPrevInstance);
}
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// Process other window messages.
case WM_COMMAND:
// Test for the identifier of a command item.
switch(LOWORD(wParam))
{
case IDM_FI_OPEN:
DoFileOpen(); // application-defined
break;
case IDM_FI_CLOSE:
DoFileClose(); // application-defined
break;
// Process other menu commands.
default:
break;
}
return 0;
// Process other window messages.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
Создание контекстного меню
Чтобы использовать контекстное меню в приложении, передайте его дескриптор функции TrackPopupMenuEx . Приложение обычно вызывает TrackPopupMenuEx в процедуре окна в ответ на сообщение, созданное пользователем, например WM_LBUTTONDOWN или WM_KEYDOWN.
Помимо дескриптора всплывающего меню , TrackPopupMenuEx требует указать дескриптор для окна-владельца, положение контекстного меню (в координатах экрана) и кнопку мыши, которую пользователь может использовать для выбора элемента.
Старая функция TrackPopupMenu по-прежнему поддерживается, но новые приложения должны использовать функцию TrackPopupMenuEx . Функция TrackPopupMenuEx требует те же параметры, что и TrackPopupMenu, но также позволяет указать часть экрана, которую меню не должно скрывать. Приложение обычно вызывает эти функции в процедуре окна при обработке сообщения WM_CONTEXTMENU .
Вы можете указать положение контекстного меню, указав координаты x и y вместе с флагом TPM_CENTERALIGN, TPM_LEFTALIGN или TPM_RIGHTALIGN . Флаг указывает положение контекстного меню относительно координат X и Y.
Вы должны разрешить пользователю выбрать элемент из контекстного меню с помощью той же кнопки мыши, которая использовалась для отображения меню. Для этого укажите флаг TPM_LEFTBUTTON или TPM_RIGHTBUTTON , чтобы указать, какую кнопку мыши пользователь может использовать для выбора пункта меню.
- Обработка сообщения WM_CONTEXTMENU
- Создание контекстного меню Font-Attributes
- Отображение контекстного меню
Обработка сообщения WM_CONTEXTMENU
Сообщение WM_CONTEXTMENU создается, когда оконная процедура приложения передает WM_RBUTTONUP или WM_NCRBUTTONUP сообщение функции DefWindowProc . Приложение может обработать это сообщение, чтобы отобразить контекстное меню, соответствующее определенной части экрана. Если приложение не отображает контекстное меню, оно должно передать сообщение в DefWindowProc для обработки по умолчанию.
Ниже приведен пример WM_CONTEXTMENU обработки сообщений, которые могут отображаться в процедуре окна приложения. Слова низкого и высокого порядка параметра lParam указывают экранные координаты мыши при отпускании правой кнопки мыши (обратите внимание, что эти координаты могут принимать отрицательные значения в системах с несколькими мониторами). Определяемая приложением функция OnContextMenu возвращает значение TRUE , если отображает контекстное меню, или FALSE , если это не так.
case WM_CONTEXTMENU:
if (!OnContextMenu(hwnd, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)))
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
Следующая определяемая приложением функция OnContextMenu отображает контекстное меню, если указанная позиция мыши находится в клиентской области окна. Более сложная функция может отображать одно из нескольких различных меню в зависимости от того, какая часть клиентской области указана. Чтобы фактически отобразить контекстное меню, в этом примере вызывается определяемая приложением функция с именем DisplayContextMenu. Описание этой функции см. в разделе Отображение контекстного меню.
BOOL WINAPI OnContextMenu(HWND hwnd, int x, int y)
{
RECT rc; // client area of window
POINT pt = { x, y }; // location of mouse click
// Get the bounding rectangle of the client area.
GetClientRect(hwnd, &rc);
// Convert the mouse position to client coordinates.
ScreenToClient(hwnd, &pt);
// If the position is in the client area, display a
// shortcut menu.
if (PtInRect(&rc, pt))
{
ClientToScreen(hwnd, &pt);
DisplayContextMenu(hwnd, pt);
return TRUE;
}
// Return FALSE if no menu is displayed.
return FALSE;
}
Создание контекстного меню Font-Attributes
Пример в этом разделе содержит фрагменты кода из приложения, которое создает и отображает контекстное меню, позволяющее пользователю задавать шрифты и атрибуты шрифтов. Приложение отображает меню в клиентской области окна main каждый раз, когда пользователь щелкает левую кнопку мыши.
Ниже приведен шаблон меню для контекстного меню, который предоставляется в файле определения ресурсов приложения.
PopupMenu MENU
BEGIN
POPUP "Dummy Popup"
BEGIN
POPUP "Fonts"
BEGIN
MENUITEM "Courier", IDM_FONT_COURIER
MENUITEM "Times Roman", IDM_FONT_TMSRMN
MENUITEM "Swiss", IDM_FONT_SWISS
MENUITEM "Helvetica", IDM_FONT_HELV
MENUITEM "Old English", IDM_FONT_OLDENG
END
POPUP "Sizes"
BEGIN
MENUITEM "7", IDM_SIZE_7
MENUITEM "8", IDM_SIZE_8
MENUITEM "9", IDM_SIZE_9
MENUITEM "10", IDM_SIZE_10
MENUITEM "11", IDM_SIZE_11
MENUITEM "12", IDM_SIZE_12
MENUITEM "14", IDM_SIZE_14
END
POPUP "Styles"
BEGIN
MENUITEM "Bold", IDM_STYLE_BOLD
MENUITEM "Italic", IDM_STYLE_ITALIC
MENUITEM "Strike Out", IDM_STYLE_SO
MENUITEM "Superscript", IDM_STYLE_SUPER
MENUITEM "Subscript", IDM_STYLE_SUB
END
END
END
В следующем примере приведена процедура окна и вспомогательные функции, используемые для создания и отображения контекстного меню.
LRESULT APIENTRY MenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rc; // client area
POINT pt; // location of mouse click
switch (uMsg)
{
case WM_LBUTTONDOWN:
// Get the bounding rectangle of the client area.
GetClientRect(hwnd, (LPRECT) &rc);
// Get the client coordinates for the mouse click.
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
// If the mouse click took place inside the client
// area, execute the application-defined function
// that displays the shortcut menu.
if (PtInRect((LPRECT) &rc, pt))
HandlePopupMenu(hwnd, pt);
break;
// Process other window messages.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
VOID APIENTRY HandlePopupMenu(HWND hwnd, POINT pt)
{
HMENU hmenu; // menu template
HMENU hmenuTrackPopup; // shortcut menu
// Load the menu template containing the shortcut menu from the
// application's resources.
hmenu = LoadMenu(hinst, "PopupMenu");
if (hmenu == NULL)
return;
// Get the first shortcut menu in the menu template. This is the
// menu that TrackPopupMenu displays.
hmenuTrackPopup = GetSubMenu(hmenu, 0);
// TrackPopup uses screen coordinates, so convert the
// coordinates of the mouse click to screen coordinates.
ClientToScreen(hwnd, (LPPOINT) &pt);
// Draw and track the shortcut menu.
TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);
// Destroy the menu.
DestroyMenu(hmenu);
}
Отображение контекстного меню
Функция, показанная в следующем примере, отображает контекстное меню.
Приложение содержит ресурс меню, определенный строкой "ShortcutExample". Строка меню просто содержит имя меню. Приложение использует функцию TrackPopupMenu для отображения меню, связанного с этим пунктом меню. (Сама строка меню не отображается, так как TrackPopupMenu требует дескриптора меню, подменю или контекстного меню.)
VOID APIENTRY DisplayContextMenu(HWND hwnd, POINT pt)
{
HMENU hmenu; // top-level menu
HMENU hmenuTrackPopup; // shortcut menu
// Load the menu resource.
if ((hmenu = LoadMenu(hinst, "ShortcutExample")) == NULL)
return;
// TrackPopupMenu cannot display the menu bar so get
// a handle to the first shortcut menu.
hmenuTrackPopup = GetSubMenu(hmenu, 0);
// Display the shortcut menu. Track the right mouse
// button.
TrackPopupMenu(hmenuTrackPopup,
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);
// Destroy the menu.
DestroyMenu(hmenu);
}
Использование Menu-Item растровых изображений
Система может использовать растровое изображение вместо текстовой строки для отображения пункта меню. Чтобы использовать растровое изображение, необходимо установить флаг MIIM_BITMAP для пункта меню и указать дескриптор растрового изображения, который система должна отображать для элемента меню в элементе hbmpItem структуры MENUITEMINFO . В этом разделе описывается использование растровых изображений для пунктов меню.
- Установка флага типа растрового рисунка
- Создание растрового рисунка
- Добавление линий и графиков в меню
- Пример Menu-Item растровых изображений
Установка флага типа растрового рисунка
Флаг MIIM_BITMAP или MF_BITMAP указывает системе использовать растровое изображение, а не текстовую строку для отображения пункта меню. Флаг MIIM_BITMAP или MF_BITMAP элемента меню должен быть установлен во время выполнения; его нельзя задать в файле определения ресурсов.
Для новых приложений можно использовать функцию SetMenuItemInfo или InsertMenuItem , чтобы задать флаг типа MIIM_BITMAP . Чтобы изменить элемент меню с текстового на точечный, используйте setMenuItemInfo. Чтобы добавить новый элемент растрового рисунка в меню, используйте функцию InsertMenuItem .
Приложения, написанные для более ранних версий системы, могут по-прежнему использовать функции ModifyMenu, InsertMenu или AppendMenu для установки флага MF_BITMAP . Чтобы изменить элемент меню с текстовой строки на точечный, используйте команду ModifyMenu. Чтобы добавить новый элемент растрового рисунка в меню, используйте флаг MF_BITMAP с функцией InsertMenu или AppendMenu .
Создание растрового рисунка
При установке флага типа MIIM_BITMAP или MF_BITMAP для пункта меню необходимо также указать дескриптор для растрового изображения, который система должна отображать для пункта меню. Растровое изображение можно указать как ресурс растрового рисунка или создать точечного рисунка во время выполнения. Если используется ресурс растрового рисунка, можно использовать функцию LoadBitmap , чтобы загрузить растровое изображение и получить его дескриптор.
Чтобы создать растровое изображение во время выполнения, используйте функции интерфейса графических устройств Windows (GDI). GDI предоставляет несколько способов создания растрового изображения во время выполнения, но разработчики обычно используют следующий метод:
- Используйте функцию CreateCompatibleDC, чтобы создать контекст устройства, совместимый с контекстом устройства, используемым окном main приложения.
- Используйте функцию CreateCompatibleBitmap для создания растрового изображения, совместимого с окном main приложения, или используйте функцию CreateBitmap для создания монохромного растрового изображения.
- Используйте функцию SelectObject , чтобы выбрать растровое изображение в контексте совместимого устройства.
- Используйте функции рисования GDI, такие как Ellipse и LineTo, для рисования изображения на растровом рисунке.
Дополнительные сведения см. в разделе Растровые изображения.
Добавление линий и графиков в меню
В следующем примере кода показано, как создать меню, содержащее растровые изображения элементов меню. Он создает два меню. Первый — это меню Диаграмма, которое содержит три точечных изображения пунктов меню: круговая диаграмма, график и линейчатая диаграмма. В примере показано, как загрузить эти растровые изображения из файла ресурсов приложения, а затем использовать функции CreatePopupMenu и AppendMenu для создания меню и пунктов меню.
Второе меню — это меню "Линии". Он содержит точечные изображения, показывающие стили линий, предоставляемые предопределенным пером в системе. Растровые изображения в стиле линий создаются во время выполнения с помощью функций GDI.
Ниже приведены определения ресурсов растровых изображений в файле определения ресурсов приложения.
PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp
Ниже приведены соответствующие части файла заголовка приложения.
// Menu-item identifiers
#define IDM_SOLID PS_SOLID
#define IDM_DASH PS_DASH
#define IDM_DASHDOT PS_DASHDOT
#define IDM_DASHDOTDOT PS_DASHDOTDOT
#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3
// Line-type flags
#define SOLID 0
#define DOT 1
#define DASH 2
#define DASHDOT 3
#define DASHDOTDOT 4
// Count of pens
#define CPENS 5
// Chart-type flags
#define PIE 1
#define LINE 2
#define BAR 3
// Function prototypes
LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
VOID MakeChartMenu(HWND);
VOID MakeLineMenu(HWND, HPEN, HBITMAP);
В следующем примере показано, как в приложении создаются растровые изображения меню и пунктов меню.
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HPEN hpen[CPENS];
static HBITMAP hbmp[CPENS];
int i;
switch (uMsg)
{
case WM_CREATE:
// Create the Chart and Line menus.
MakeChartMenu(hwnd);
MakeLineMenu(hwnd, hpen, hbmp);
return 0;
// Process other window messages.
case WM_DESTROY:
for (i = 0; i < CPENS; i++)
{
DeleteObject(hbmp[i]);
DeleteObject(hpen[i]);
}
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
VOID MakeChartMenu(HWND hwnd)
{
HBITMAP hbmpPie; // handle to pie chart bitmap
HBITMAP hbmpLine; // handle to line chart bitmap
HBITMAP hbmpBar; // handle to bar chart bitmap
HMENU hmenuMain; // handle to main menu
HMENU hmenuChart; // handle to Chart menu
// Load the pie, line, and bar chart bitmaps from the
// resource-definition file.
hbmpPie = LoadBitmap(hinst, MAKEINTRESOURCE(PIE));
hbmpLine = LoadBitmap(hinst, MAKEINTRESOURCE(LINE));
hbmpBar = LoadBitmap(hinst, MAKEINTRESOURCE(BAR));
// Create the Chart menu and add it to the menu bar.
// Append the Pie, Line, and Bar menu items to the Chart
// menu.
hmenuMain = GetMenu(hwnd);
hmenuChart = CreatePopupMenu();
AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (UINT) hmenuChart,
"Chart");
AppendMenu(hmenuChart, MF_BITMAP, IDM_PIE, (LPCTSTR) hbmpPie);
AppendMenu(hmenuChart, MF_BITMAP, IDM_LINE,
(LPCTSTR) hbmpLine);
AppendMenu(hmenuChart, MF_BITMAP, IDM_BAR, (LPCTSTR) hbmpBar);
return;
}
VOID MakeLineMenu(HWND hwnd, HPEN phpen, HBITMAP phbmp)
{
HMENU hmenuLines; // handle to Lines menu
HMENU hmenu; // handle to main menu
COLORREF crMenuClr; // menu-item background color
HBRUSH hbrBackground; // handle to background brush
HBRUSH hbrOld; // handle to previous brush
WORD wLineX; // width of line bitmaps
WORD wLineY; // height of line bitmaps
HDC hdcMain; // handle to main window's DC
HDC hdcLines; // handle to compatible DC
HBITMAP hbmpOld; // handle to previous bitmap
int i; // loop counter
// Create the Lines menu. Add it to the menu bar.
hmenu = GetMenu(hwnd);
hmenuLines = CreatePopupMenu();
AppendMenu(hmenu, MF_STRING | MF_POPUP,
(UINT) hmenuLines, "&Lines");
// Create a brush for the menu-item background color.
crMenuClr = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crMenuClr);
// Create a compatible device context for the line bitmaps,
// and then select the background brush into it.
hdcMain = GetDC(hwnd);
hdcLines = CreateCompatibleDC(hdcMain);
hbrOld = SelectObject(hdcLines, hbrBackground);
// Get the dimensions of the check-mark bitmap. The width of
// the line bitmaps will be five times the width of the
// check-mark bitmap.
wLineX = GetSystemMetrics(SM_CXMENUCHECK) * (WORD) 5;
wLineY = GetSystemMetrics(SM_CYMENUCHECK);
// Create the bitmaps and select them, one at a time, into the
// compatible device context. Initialize each bitmap by
// filling it with the menu-item background color.
for (i = 0; i < CPENS; i++)
{
phbmp[i] = CreateCompatibleBitmap(hdcMain, wLineX, wLineY);
if (i == 0)
hbmpOld = SelectObject(hdcLines, phbmp[i]);
else
SelectObject(hdcLines, phbmp[i]);
ExtFloodFill(hdcLines, 0, 0, crMenuClr, FLOODFILLBORDER);
}
// Create the pens.
phpen[0] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
phpen[1] = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
phpen[2] = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
phpen[3] = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
phpen[4] = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));
// Select a pen and a bitmap into the compatible device
// context, draw a line into the bitmap, and then append
// the bitmap as an item in the Lines menu.
for (i = 0; i < CPENS; i++)
{
SelectObject(hdcLines, phbmp[i]);
SelectObject(hdcLines, phpen[i]);
MoveToEx(hdcLines, 0, wLineY / 2, NULL);
LineTo(hdcLines, wLineX, wLineY / 2);
AppendMenu(hmenuLines, MF_BITMAP, i + 1,
(LPCTSTR) phbmp[i]);
}
// Release the main window's device context and destroy the
// compatible device context. Also, destroy the background
// brush.
ReleaseDC(hwnd, hdcMain);
SelectObject(hdcLines, hbrOld);
DeleteObject(hbrBackground);
SelectObject(hdcLines, hbmpOld);
DeleteDC(hdcLines);
return;
}
Пример Menu-Item растровых изображений
В примере в этом разделе создаются два меню, каждое из которых содержит несколько пунктов точечных меню. Для каждого меню приложение добавляет соответствующее имя меню в строку меню окна main.
Первое меню содержит пункты меню, показывающие каждый из трех типов диаграмм: круговая, линейчатая и линейчатая. Растровые изображения для этих пунктов меню определяются как ресурсы и загружаются с помощью функции LoadBitmap . С этим меню связано имя меню "Диаграмма" в строке меню.
Второе меню содержит пункты меню, показывающие каждый из пяти стилей строк, используемых с функцией CreatePen : PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT и PS_DASHDOTDOT. Приложение создает растровые изображения для этих пунктов меню во время выполнения с помощью функций рисования GDI. С этим меню связано имя меню Строки в строке меню.
В процедуре окна приложения определены два статических массива дескрипторов растровых рисунков. Один массив содержит дескриптора трех растровых изображений, используемых для меню Диаграмма . Другая содержит дескрипторы пяти растровых изображений, используемых для меню "Линии ". При обработке сообщения WM_CREATE оконная процедура загружает растровые изображения диаграммы, создает растровые изображения линий, а затем добавляет соответствующие пункты меню. При обработке сообщения WM_DESTROY процедура окна удаляет все растровые изображения.
Ниже приведены соответствующие части файла заголовка приложения.
// Menu-item identifiers
#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3
#define IDM_SOLID 4
#define IDM_DASH 5
#define IDM_DASHDOT 6
#define IDM_DASHDOTDOT 7
// Number of items on the Chart and Lines menus
#define C_LINES 5
#define C_CHARTS 3
// Bitmap resource identifiers
#define IDB_PIE 1
#define IDB_LINE 2
#define IDB_BAR 3
// Dimensions of the line bitmaps
#define CX_LINEBMP 40
#define CY_LINEBMP 10
Ниже приведены соответствующие части процедуры окна. Процедура окна выполняет большую часть своей инициализации путем вызова определяемых приложением функций LoadChartBitmaps, CreateLineBitmaps и AddBitmapMenu, описанных далее в этом разделе.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
static HBITMAP aHbmLines[C_LINES];
static HBITMAP aHbmChart[C_CHARTS];
int i;
switch (uMsg)
{
case WM_CREATE:
// Call application-defined functions to load the
// bitmaps for the Chart menu and create those for
// the Lines menu.
LoadChartBitmaps(aHbmChart);
CreateLineBitmaps(aHbmLines);
// Call an application-defined function to create
// menus containing the bitmap menu items. The function
// also adds a menu name to the window's menu bar.
AddBitmapMenu(
hwnd, // menu bar's owner window
"&Chart", // text of menu name on menu bar
IDM_PIE, // ID of first item on menu
aHbmChart, // array of bitmap handles
C_CHARTS // number of items on menu
);
AddBitmapMenu(hwnd, "&Lines", IDM_SOLID,
aHbmLines, C_LINES);
break;
case WM_DESTROY:
for (i = 0; i < C_LINES; i++)
DeleteObject(aHbmLines[i]);
for (i = 0; i < C_CHARTS; i++)
DeleteObject(aHbmChart[i]);
PostQuitMessage(0);
break;
// Process additional messages here.
default:
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
}
return 0;
}
Определяемая приложением функция LoadChartBitmaps загружает ресурсы растровых изображений для меню диаграммы, вызывая функцию LoadBitmap , как показано ниже.
VOID WINAPI LoadChartBitmaps(HBITMAP *paHbm)
{
paHbm[0] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_PIE));
paHbm[1] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_LINE));
paHbm[2] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_BAR));
}
Определяемая приложением функция CreateLineBitmaps создает растровые изображения для меню "Линии" с помощью функций рисования GDI. Функция создает контекст устройства памяти (DC) с теми же свойствами, что и dc окна рабочего стола. Для каждого стиля линии функция создает растровое изображение, выбирает его в память контроллера домена и рисует в нем.
VOID WINAPI CreateLineBitmaps(HBITMAP *paHbm)
{
HWND hwndDesktop = GetDesktopWindow();
HDC hdcDesktop = GetDC(hwndDesktop);
HDC hdcMem = CreateCompatibleDC(hdcDesktop);
COLORREF clrMenu = GetSysColor(COLOR_MENU);
HBRUSH hbrOld;
HPEN hpenOld;
HBITMAP hbmOld;
int fnDrawMode;
int i;
// Create a brush using the menu background color,
// and select it into the memory DC.
hbrOld = SelectObject(hdcMem, CreateSolidBrush(clrMenu));
// Create the bitmaps. Select each one into the memory
// DC that was created and draw in it.
for (i = 0; i < C_LINES; i++)
{
// Create the bitmap and select it into the DC.
paHbm[i] = CreateCompatibleBitmap(hdcDesktop,
CX_LINEBMP, CY_LINEBMP);
hbmOld = SelectObject(hdcMem, paHbm[i]);
// Fill the background using the brush.
PatBlt(hdcMem, 0, 0, CX_LINEBMP, CY_LINEBMP, PATCOPY);
// Create the pen and select it into the DC.
hpenOld = SelectObject(hdcMem,
CreatePen(PS_SOLID + i, 1, RGB(0, 0, 0)));
// Draw the line. To preserve the background color where
// the pen is white, use the R2_MASKPEN drawing mode.
fnDrawMode = SetROP2(hdcMem, R2_MASKPEN);
MoveToEx(hdcMem, 0, CY_LINEBMP / 2, NULL);
LineTo(hdcMem, CX_LINEBMP, CY_LINEBMP / 2);
SetROP2(hdcMem, fnDrawMode);
// Delete the pen, and select the old pen and bitmap.
DeleteObject(SelectObject(hdcMem, hpenOld));
SelectObject(hdcMem, hbmOld);
}
// Delete the brush and select the original brush.
DeleteObject(SelectObject(hdcMem, hbrOld));
// Delete the memory DC and release the desktop DC.
DeleteDC(hdcMem);
ReleaseDC(hwndDesktop, hdcDesktop);
}
Определяемая приложением функция AddBitmapMenu создает меню и добавляет в него указанное количество пунктов растрового меню. Затем в строке меню указанного окна добавляется соответствующее имя меню.
VOID WINAPI AddBitmapMenu(
HWND hwnd, // window that owned the menu bar
LPSTR lpszText, // text of menu name on menu bar
UINT uID, // ID of first bitmap menu item
HBITMAP *paHbm, // bitmaps for the menu items
int cItems) // number bitmap menu items
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup = CreatePopupMenu();
MENUITEMINFO mii;
int i;
// Add the bitmap menu items to the menu.
for (i = 0; i < cItems; i++)
{
mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_DATA;
mii.wID = uID + i;
mii.hbmpItem = &paHbm[i];
InsertMenuItem(hmenuPopup, i, TRUE, &mii);
}
// Add a menu name to the menu bar.
mii.fMask = MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
mii.fType = MFT_STRING;
mii.hSubMenu = hmenuPopup;
mii.dwTypeData = lpszText;
InsertMenuItem(hmenuBar,
GetMenuItemCount(hmenuBar), TRUE, &mii);
}
Создание элементов меню Owner-Drawn
Если требуется полный контроль над внешним видом пункта меню, можно использовать в приложении пункт меню, нарисованный владельцем. В этом разделе описываются действия по созданию и использованию элемента меню, нарисованного владельцем.
- Установка флага Owner-Drawn
- Нарисованные владельцем меню и сообщение WM_MEASUREITEM
- Нарисованные владельцем меню и сообщение WM_DRAWITEM
- Нарисованные владельцем меню и сообщение WM_MENUCHAR
- Настройка шрифтов для Menu-Item текстовых строк
- Пример элементов меню Owner-Drawn
Установка флага Owner-Drawn
Вы не можете определить пункт меню, нарисованный владельцем, в файле определения ресурсов приложения. Вместо этого необходимо создать новый пункт меню или изменить существующий с помощью флага меню MFT_OWNERDRAW .
Вы можете использовать функцию InsertMenuItem или SetMenuItemInfo , чтобы указать пункт меню, нарисованный владельцем. Используйте InsertMenuItem , чтобы вставить новый пункт меню в указанной позиции в строке меню или меню. Используйте SetMenuItemInfo для изменения содержимого меню.
При вызове этих двух функций необходимо указать указатель на структуру MENUITEMINFO , которая указывает свойства нового пункта меню или свойства, которые необходимо изменить для существующего элемента меню. Чтобы сделать элемент нарисованным владельцем, укажите значение MIIM_FTYPE для элемента fMask и значение MFT_OWNERDRAW для элемента fType .
Задав соответствующие элементы структуры MENUITEMINFO , можно связать с каждым элементом меню определенное приложением значение, которое называется данными элемента. Для этого укажите значение MIIM_DATA для элемента fMask и определяемое приложением значение для элемента dwItemData .
Данные элементов можно использовать с элементами меню любого типа, но это особенно полезно для элементов, нарисованных владельцем. Например, предположим, что структура содержит сведения, используемые для рисования пункта меню. Приложение может использовать данные элемента для пункта меню для хранения указателя на структуру. Данные элемента отправляются в окно владельца меню с сообщениями WM_MEASUREITEM и WM_DRAWITEM . Чтобы в любое время получить данные об элементах меню, используйте функцию GetMenuItemInfo .
Приложения, написанные для более ранних версий системы, могут продолжать вызывать AppendMenu, InsertMenu или ModifyMenu , чтобы назначить флаг MF_OWNERDRAW элементу меню, нарисованному владельцем.
При вызове любой из этих трех функций можно передать значение в качестве параметра lpNewItem . Это значение может представлять любую информацию, которая имеет смысл для приложения и будет доступна приложению при отображении элемента. Например, значение может содержать указатель на структуру; Структура, в свою очередь, может содержать текстовую строку и дескриптор логического шрифта, который приложение будет использовать для рисования строки.
меню Owner-Drawn и сообщение WM_MEASUREITEM
Прежде чем система впервые отобразит элемент меню, нарисованный владельцем, она отправляет сообщение WM_MEASUREITEM в процедуру окна, которому принадлежит меню элемента. Это сообщение содержит указатель на структуру MEASUREITEMSTRUCT , которая идентифицирует элемент и содержит данные элемента, которые приложение, возможно, назначило ему. Перед возвратом из обработки сообщения процедура окна должна заполнить элементы itemWidth и itemHeight структуры. Система использует сведения из этих элементов при создании ограничивающего прямоугольника, в котором приложение рисует пункт меню. Он также использует эти сведения для определения того, когда пользователь выбирает элемент.
Меню Owner-Drawn и сообщение WM_DRAWITEM
Всякий раз, когда элемент должен быть нарисован (например, при первом отображении или при его выборе пользователем), система отправляет сообщение WM_DRAWITEM в процедуру окна владельца меню. Это сообщение содержит указатель на структуру DRAWITEMSTRUCT , которая содержит сведения об элементе, включая данные элемента, которые приложение могло ему присвоить. Кроме того, DRAWITEMSTRUCT содержит флаги, указывающие состояние элемента (например, является ли он серым или выбранным), а также ограничивающий прямоугольник и контекст устройства, который приложение использует для рисования элемента.
Приложение должно выполнять следующие действия при обработке сообщения WM_DRAWITEM :
- Определите необходимый тип рисунка. Для этого проверка элемент itemAction структуры DRAWITEMSTRUCT.
- Нарисуйте пункт меню соответствующим образом, используя ограничивающий прямоугольник и контекст устройства, полученные из структуры DRAWITEMSTRUCT . Приложение должно рисовать только в ограничивающем прямоугольнике. Из соображений производительности система не обрезает части изображения, нарисованные за пределами прямоугольника.
- Восстановите все объекты GDI, выбранные для контекста устройства элемента меню.
Если пользователь выбирает пункт меню, система присваивает элементу itemAction структуры DRAWITEMSTRUCTзначение ODA_SELECT и задает значение ODS_SELECTED в элементе itemState . Это подсказка приложения для перерисовки пункта меню, чтобы указать, что он выбран.
меню Owner-Drawn и сообщение WM_MENUCHAR
В меню, отличном от меню, нарисованном владельцем, можно указать мнемоническое меню, вставив символ подчеркивания рядом с символом в строке меню. Это позволяет пользователю выбрать меню, нажав клавишу ALT и нажав мнемонический символ меню. Однако в меню, нарисованных владельцем, невозможно указать мнемоническое меню таким образом. Вместо этого приложение должно обработать сообщение WM_MENUCHAR , чтобы предоставить меню, нарисованное владельцем, с помощью мнемоники меню.
Сообщение WM_MENUCHAR отправляется, когда пользователь вводит мнемоническое меню, которое не соответствует ни одной из предопределенных мнемонов текущего меню. Значение, содержащееся в wParam , указывает символ ASCII, соответствующий клавише, которую пользователь нажал с помощью клавиши ALT. Слово низкого порядка wParam указывает тип выбранного меню и может иметь одно из следующих значений:
- MF_POPUP , если текущее меню является подменю.
- MF_SYSMENU , является ли меню системным.
Слово wParam в высоком порядке содержит дескриптор меню для текущего меню. Окно с меню, нарисованным владельцем, может обрабатывать WM_MENUCHAR следующим образом:
case WM_MENUCHAR:
nIndex = Determine index of menu item to be selected from
character that was typed and handle to the current
menu.
return MAKELRESULT(nIndex, 2);
Два слова в слове высокого порядка возвращаемого значения сообщают системе, что слово низкого порядка возвращаемого значения содержит отсчитываемый от нуля индекс выбранного пункта меню.
Следующие константы соответствуют возможным значениям, возвращаемым из сообщения WM_MENUCHAR .
Константа | Значение | Значение |
---|---|---|
MNC_IGNORE | 0 | Система должна отменить нажатый пользователем символ и создать короткий звуковой сигнал на системном динамике. |
MNC_CLOSE | 1 | Система должна закрыть активное меню. |
MNC_EXECUTE | 2 | Система должна выбрать элемент, указанный в слове низкого порядка возвращаемого значения. Окно владельца получает сообщение WM_COMMAND . |
MNC_SELECT | 3 | Система должна выбрать элемент, указанный в слове низкого порядка возвращаемого значения. |
Настройка шрифтов для Menu-Item текстовых строк
В этом разделе содержится пример приложения, использующего пункты меню, нарисованные владельцем, в меню. Меню содержит элементы, которые задают атрибуты текущего шрифта, и элементы отображаются с помощью соответствующего атрибута шрифта.
Вот как определяется меню в файле определения ресурсов. Обратите внимание, что строки для пунктов меню Обычный, Полужирный, Курсив и Подчеркивание назначаются во время выполнения, поэтому их строки пусты в файле определения ресурсов.
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 . Система использует значения ширины и высоты для вычисления размера меню.
Во время обработки сообщения WM_DRAWITEM строка пункта меню рисуется с пространством рядом со строкой для проверка пометки. Если пользователь выбирает элемент, выделенные цвета текста и фона используются для рисования элемента.
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
typedef struct _MYITEM
{
HFONT hfont;
LPSTR psz;
} MYITEM; // structure for item font and string
MYITEM *pmyitem; // pointer to item's font and string
static MYITEM myitem[CITEMS]; // array of MYITEMS
static HMENU hmenu; // handle to main menu
static COLORREF crSelText; // text color of selected item
static COLORREF crSelBkgnd; // background color of selected item
COLORREF crText; // text color of unselected item
COLORREF crBkgnd; // background color unselected item
LPMEASUREITEMSTRUCT lpmis; // pointer to item of data
LPDRAWITEMSTRUCT lpdis; // pointer to item drawing data
HDC hdc; // handle to screen DC
SIZE size; // menu-item text extents
WORD wCheckX; // check-mark width
int nTextX; // width of menu item
int nTextY; // height of menu item
int i; // loop counter
HFONT hfontOld; // handle to old font
BOOL fSelected = FALSE; // menu-item selection flag
size_t * pcch;
HRESULT hResult;
switch (uMsg)
{
case WM_CREATE:
// Modify the Regular, Bold, Italic, and Underline
// menu items to make them owner-drawn items. Associate
// a MYITEM structure with each item to contain the
// string for and font handle to each item.
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]);
// Retrieve each item's font handle and copy it into
// the hfont member of each item's MYITEM structure.
// Also, copy each item's string into the structures.
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";
// Retrieve the text and background colors of the
// selected menu text.
crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);
return 0;
case WM_MEASUREITEM:
// Retrieve a device context for the main window.
hdc = GetDC(hwnd);
// Retrieve pointers to the menu item's
// MEASUREITEMSTRUCT structure and MYITEM structure.
lpmis = (LPMEASUREITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpmis->itemData;
// Select the font associated with the item into
// the main window's device context.
hfontOld = SelectObject(hdc, pmyitem->hfont);
// Retrieve the width and height of the item's string,
// and then copy the width and height into the
// MEASUREITEMSTRUCT structure's itemWidth and
// itemHeight members.
hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// Add code to fail as securely as possible.
return;
}
GetTextExtentPoint32(hdc, pmyitem->psz,
*pcch, &size);
lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;
// Select the old font back into the device context,
// and then release the device context.
SelectObject(hdc, hfontOld);
ReleaseDC(hwnd, hdc);
return TRUE;
break;
case WM_DRAWITEM:
// Get pointers to the menu item's DRAWITEMSTRUCT
// structure and MYITEM structure.
lpdis = (LPDRAWITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpdis->itemData;
// If the user has selected the item, use the selected
// text and background colors to display the item.
if (lpdis->itemState & ODS_SELECTED)
{
crText = SetTextColor(lpdis->hDC, crSelText);
crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd);
fSelected = TRUE;
}
// Remember to leave space in the menu item for the
// check-mark bitmap. Retrieve the width of the bitmap
// and add it to the width of the menu item.
wCheckX = GetSystemMetrics(SM_CXMENUCHECK);
nTextX = wCheckX + lpdis->rcItem.left;
nTextY = lpdis->rcItem.top;
// Select the font associated with the item into the
// item's device context, and then draw the string.
hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);
hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// Add code to fail as securely as possible.
return;
}
ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE,
&lpdis->rcItem, pmyitem->psz,
*pcch, NULL);
// Select the previous font back into the device
// context.
SelectObject(lpdis->hDC, hfontOld);
// Return the text and background colors to their
// normal state (not selected).
if (fSelected)
{
SetTextColor(lpdis->hDC, crText);
SetBkColor(lpdis->hDC, crBkgnd);
}
return TRUE;
// Process other messages.
case WM_DESTROY:
// Destroy the menu items' font handles.
for (i = 0; i < CITEMS; i++)
DeleteObject(myitem[i].hfont);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
HFONT GetAFont(int fnFont)
{
static LOGFONT lf; // structure for font information
// Get a handle to the ANSI fixed-pitch font, and copy
// information about the font to a LOGFONT structure.
GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT),
&lf);
// Set the font attributes, as appropriate.
if (fnFont == BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;
lf.lfItalic = (fnFont == ITALIC);
lf.lfItalic = (fnFont == ULINE);
// Create the font, and then return its handle.
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);
}
Пример элементов меню Owner-Drawn
В примере в этом разделе используются пункты меню, нарисованные владельцем, в меню. В пунктах меню выбираются определенные атрибуты шрифта, и приложение отображает каждый пункт меню с помощью шрифта с соответствующим атрибутом. Например, пункт меню Курсив отображается курсивным шрифтом. Откроется меню Имя меню Символ в строке меню.
Строка меню и раскрывающееся меню изначально определяются ресурсом расширенного шаблона меню. Поскольку шаблон меню не может указать элементы, нарисованные владельцем, изначально меню содержит четыре текстовых пункта меню со следующими строками: "Обычный", "Полужирный", "Курсив" и "Подчеркивание". Процедура окна приложения изменяет их на элементы, нарисованные владельцем, при обработке сообщения WM_CREATE . При получении сообщения WM_CREATE оконная процедура вызывает определяемую приложением функцию OnCreate, которая выполняет следующие действия для каждого пункта меню:
- Выделяет определяемую приложением структуру MYITEM.
- Получает текст пункта меню и сохраняет его в определяемой приложением структуре MYITEM.
- Создает шрифт, используемый для отображения пункта меню, и сохраняет его дескриптор в определяемой приложением структуре MYITEM.
- Изменяет тип элемента меню на MFT_OWNERDRAW и сохраняет указатель на определяемую приложением структуру MYITEM в качестве данных элемента.
Поскольку указатель на каждую определяемую приложением структуру MYITEM сохраняется в виде данных элемента, он передается в процедуру окна вместе с WM_MEASUREITEM и WM_DRAWITEM сообщения для соответствующего пункта меню. Указатель содержится в элементе itemData структур MEASUREITEMSTRUCT и DRAWITEMSTRUCT .
При первом отображении каждого элемента меню, нарисованного владельцем, отправляется сообщение WM_MEASUREITEM. Приложение обрабатывает это сообщение, выбирая шрифт для пункта меню в контексте устройства, а затем определяя пространство, необходимое для отображения текста элемента меню в этом шрифте. Шрифт и текст элемента меню задаются структурой элемента MYITEM
меню (структура, определяемая приложением). Приложение определяет размер текста с помощью функции GetTextExtentPoint32 .
Процедура окна обрабатывает сообщение WM_DRAWITEM , отображая текст пункта меню соответствующим шрифтом. Шрифт и текст элемента меню задаются структурой элемента MYITEM
меню. Приложение выбирает цвета текста и фона, соответствующие состоянию элемента меню.
Процедура окна обрабатывает сообщение WM_DESTROY , чтобы уничтожить шрифты и освободить память. Приложение удаляет шрифт и освобождает определяемую приложением структуру MYITEM для каждого пункта меню.
Ниже приведены соответствующие части файла заголовка приложения.
// Menu-item identifiers for the Character menu
#define IDM_CHARACTER 10
#define IDM_REGULAR 11
#define IDM_BOLD 12
#define IDM_ITALIC 13
#define IDM_UNDERLINE 14
// Structure associated with menu items
typedef struct tagMYITEM
{
HFONT hfont;
int cchItemText;
char szItemText[1];
} MYITEM;
#define CCH_MAXITEMTEXT 256
Ниже приведены соответствующие части процедуры окна приложения и связанные с ней функции.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
case WM_CREATE:
if (!OnCreate(hwnd))
return -1;
break;
case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage(0);
break;
case WM_MEASUREITEM:
OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam);
return TRUE;
case WM_DRAWITEM:
OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam);
return TRUE;
// Additional message processing goes here.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL WINAPI OnCreate(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
MYITEM *pMyItem;
// Get a handle to the pop-up menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Modify each menu item. Assume that the IDs IDM_REGULAR
// through IDM_UNDERLINE are consecutive numbers.
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++)
{
// Allocate an item structure, leaving space for a
// string of up to CCH_MAXITEMTEXT characters.
pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,
sizeof(MYITEM) + CCH_MAXITEMTEXT);
// Save the item text in the item structure.
mii.fMask = MIIM_STRING;
mii.dwTypeData = pMyItem->szItemText;
mii.cch = CCH_MAXITEMTEXT;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem->cchItemText = mii.cch;
// Reallocate the structure to the minimum required size.
pMyItem = (MYITEM *) LocalReAlloc(pMyItem,
sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);
// Create the font used to draw the item.
pMyItem->hfont = CreateMenuItemFont(uID);
// Change the item to an owner-drawn item, and save
// the address of the item structure as item data.
mii.fMask = MIIM_FTYPE | MIIM_DATA;
mii.fType = MFT_OWNERDRAW;
mii.dwItemData = (ULONG_PTR) pMyItem;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
}
return TRUE;
}
HFONT CreateMenuItemFont(UINT uID)
{
LOGFONT lf;
HRESULT hr;
ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = 20;
hr = StringCchCopy(lf.lfFaceName, 32, "Times New Roman");
if (FAILED(hr))
{
// TODO: writer error handler
}
switch (uID)
{
case IDM_BOLD:
lf.lfWeight = FW_HEAVY;
break;
case IDM_ITALIC:
lf.lfItalic = TRUE;
break;
case IDM_UNDERLINE:
lf.lfUnderline = TRUE;
break;
}
return CreateFontIndirect(&lf);
}
VOID WINAPI OnDestroy(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
MYITEM *pMyItem;
// Get a handle to the menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Free resources associated with each menu item.
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++)
{
// Get the item data.
mii.fMask = MIIM_DATA;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem = (MYITEM *) mii.dwItemData;
// Destroy the font and free the item structure.
DeleteObject(pMyItem->hfont);
LocalFree(pMyItem);
}
}
VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)
{
MYITEM *pMyItem = (MYITEM *) lpmis->itemData;
HDC hdc = GetDC(hwnd);
HFONT hfntOld = (HFONT)SelectObject(hdc, pMyItem->hfont);
SIZE size;
GetTextExtentPoint32(hdc, pMyItem->szItemText,
pMyItem->cchItemText, &size);
lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;
SelectObject(hdc, hfntOld);
ReleaseDC(hwnd, hdc);
}
VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis)
{
MYITEM *pMyItem = (MYITEM *) lpdis->itemData;
COLORREF clrPrevText, clrPrevBkgnd;
HFONT hfntPrev;
int x, y;
// Set the appropriate foreground and background colors.
if (lpdis->itemState & ODS_SELECTED)
{
clrPrevText = SetTextColor(lpdis->hDC,
GetSysColor(COLOR_HIGHLIGHTTEXT));
clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_HIGHLIGHT));
}
else
{
clrPrevText = SetTextColor(lpdis->hDC,
GetSysColor(COLOR_MENUTEXT));
clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_MENU));
}
// Determine where to draw and leave space for a check mark.
x = lpdis->rcItem.left;
y = lpdis->rcItem.top;
x += GetSystemMetrics(SM_CXMENUCHECK);
// Select the font and draw the text.
hfntPrev = (HFONT)SelectObject(lpdis->hDC, pMyItem->hfont);
ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE,
&lpdis->rcItem, pMyItem->szItemText,
pMyItem->cchItemText, NULL);
// Restore the original font and colors.
SelectObject(lpdis->hDC, hfntPrev);
SetTextColor(lpdis->hDC, clrPrevText);
SetBkColor(lpdis->hDC, clrPrevBkgnd);
}
Использование настраиваемых точечных изображений флажка
Система предоставляет растровое изображение проверка метки по умолчанию для отображения рядом с выбранным пунктом меню. Вы можете настроить отдельный пункт меню, предоставив пару точечных рисунков для замены растрового изображения по умолчанию проверка метки. Система отображает одно растровое изображение при выборе элемента, а другое , когда оно ясно. В этом разделе описываются действия, связанные с созданием и использованием пользовательских растровых изображений проверка метки.
- Создание настраиваемых точечных изображений флажка
- Связывание растровых изображений с элементом меню
- Установка атрибута check-mark
- Имитация флажков в меню
- Пример использования настраиваемых точечных изображений с галочкой
Создание настраиваемых точечных изображений флажка
Точечный рисунок пользовательской проверка метки должен иметь тот же размер, что и растровое изображение проверка метки по умолчанию. Вы можете получить размер по умолчанию проверка метки растрового изображения, вызвав функцию GetSystemMetrics. Низкоупорядоченное слово возвращаемого значения этой функции указывает ширину; Слово высокого порядка указывает высоту.
Ресурсы точечных изображений можно использовать для предоставления проверка пометок. Однако, поскольку требуемый размер растрового изображения зависит от типа отображения, может потребоваться изменить размер растрового рисунка во время выполнения с помощью функции StretchBlt . В зависимости от растрового изображения искажение, вызванное изменением размера, может привести к неприемлемым результатам.
Вместо использования ресурса растрового изображения можно создать растровое изображение во время выполнения с помощью функций GDI.
Создание растрового изображения во время выполнения
Используйте функцию CreateCompatibleDC, чтобы создать контекст устройства, совместимый с контекстом, используемым окном main приложения.
Параметр hdc функции может указывать значение NULL или возвращаемое из функции значение. CreateCompatibleDC возвращает дескриптор совместимого контекста устройства.
Используйте функцию CreateCompatibleBitmap, чтобы создать растровое изображение, совместимое с окном main приложения.
Параметры nWidth и nHeight этой функции задают размер растрового изображения; они должны указывать сведения о ширине и высоте, возвращаемые функцией GetSystemMetrics .
Примечание
Вы также можете использовать функцию CreateBitmap для создания монохромного растрового изображения.
Используйте функцию SelectObject , чтобы выбрать растровое изображение в контексте совместимого устройства.
Используйте функции рисования GDI, такие как Ellipse и LineTo, чтобы нарисовать изображение в растровом рисунке, или с помощью таких функций, как BitBlt и StretchBlt , чтобы скопировать изображение в растровое изображение.
Дополнительные сведения см. в разделе Растровые изображения.
Связывание растровых изображений с элементом меню
Вы связываете пару растровых рисунков проверка метки с элементом меню, передавая дескрипторы растровых изображений в функцию SetMenuItemBitmaps. Параметр hBitmapUnchecked определяет четкое растровое изображение, а параметр hBitmapChecked определяет выбранное растровое изображение. Если вы хотите удалить одну или обе метки проверка из пункта меню, можно задать для параметра hBitmapUnchecked или hBitmapChecked или обоих меток значение NULL.
Установка атрибута check-mark
Функция CheckMenuItem задает для атрибута проверка метки пункта меню значение selected или cleared. Можно указать значение MF_CHECKED, чтобы задать для атрибута проверка-метки выбранное значение, и значение MF_UNCHECKED, чтобы задать для него значение очистки.
Вы также можете задать состояние проверка пункта меню с помощью функции SetMenuItemInfo.
Иногда группа пунктов меню представляет набор взаимоисключающих параметров. С помощью функции CheckMenuRadioItem можно проверка один пункт меню, одновременно удалив метку проверка из всех остальных пунктов меню в группе.
Имитация флажков в меню
В этом разделе содержится пример, в котором показано, как имитировать проверка прямоугольники в меню. Пример содержит меню Символ, элементы которого позволяют пользователю задать полужирное, курсивное и подчеркивание атрибутов текущего шрифта. При действии атрибута шрифта в поле проверка рядом с соответствующим пунктом меню отображается проверка метка; в противном случае рядом с элементом отображается пустое поле проверка.
В этом примере битовое изображение по умолчанию проверка метки заменяется двумя растровыми рисунками: растровое изображение с выбранным проверка полем и растровое изображение пустым полем. Выбранное проверка точечный рисунок отображается рядом с пунктом меню Полужирный, Курсив или Подчеркивание, если атрибуту проверка-метки элемента присвоено значение MF_CHECKED. Если для атрибута проверка метки задано значение MF_UNCHECKED, отображается пустое или пустое точечный рисунок проверка.
Система предоставляет предопределенное растровое изображение, содержащее изображения, используемые для проверка полей и переключателей. В этом примере выделенные и пустые поля проверка изолируются, копируются в два отдельных растровых рисунка, а затем используются в качестве выделенных и очищенных растровых изображений для элементов меню Символ.
Чтобы получить дескриптор для системного растрового изображения проверка box, в примере вызывается функция LoadBitmap, указывая NULL в качестве параметра hInstance и OBM_CHECKBOXES в качестве параметра lpBitmapName. Так как изображения в растровом рисунке имеют одинаковый размер, в примере их можно изолировать, разделив ширину и высоту растрового рисунка на количество изображений в строках и столбцах.
В следующей части файла определения ресурсов показано, как определяются элементы меню в меню Символ . Обратите внимание, что атрибуты шрифта изначально не действуют, поэтому для атрибута проверка метки для обычного элемента задано значение "Выбрано", а по умолчанию для атрибута проверка метки остальных элементов задано значение очистки.
#include "men3.h"
MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "&Regular", IDM_REGULAR, CHECKED
MENUITEM SEPARATOR
MENUITEM "&Bold", IDM_BOLD
MENUITEM "&Italic", IDM_ITALIC
MENUITEM "&Underline", IDM_ULINE
END
END
Ниже приведено соответствующее содержимое файла заголовка приложения.
// Menu-item identifiers
#define IDM_REGULAR 0x1
#define IDM_BOLD 0x2
#define IDM_ITALIC 0x4
#define IDM_ULINE 0x8
// Check-mark flags
#define CHECK 1
#define UNCHECK 2
// Font-attribute mask
#define ATTRIBMASK 0xe
// Function prototypes
LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP GetMyCheckBitmaps(UINT);
BYTE CheckOrUncheckMenuItem(BYTE, HMENU);
В следующем примере показаны части процедуры окна, которые создают проверка пометки растровых рисунков, задают атрибут проверка метки пунктов меню Полужирный, Курсив и Подчеркивание, а также уничтожают проверка растровые рисунки.
LRESULT APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hbmpCheck; // handle to checked bitmap
static HBITMAP hbmpUncheck; // handle to unchecked bitmap
static HMENU hmenu; // handle to main menu
BYTE fbFontAttrib; // font-attribute flags
switch (uMsg)
{
case WM_CREATE:
// Call the application-defined GetMyCheckBitmaps
// function to get the predefined checked and
// unchecked check box bitmaps.
hbmpCheck = GetMyCheckBitmaps(CHECK);
hbmpUncheck = GetMyCheckBitmaps(UNCHECK);
// Set the checked and unchecked bitmaps for the menu
// items.
hmenu = GetMenu(hwndMain);
SetMenuItemBitmaps(hmenu, IDM_BOLD, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ITALIC, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ULINE, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
// Process the menu commands.
case IDM_REGULAR:
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:
// CheckOrUncheckMenuItem is an application-
// defined function that sets the menu item
// checkmarks and returns the user-selected
// font attributes.
fbFontAttrib = CheckOrUncheckMenuItem(
(BYTE) LOWORD(wParam), hmenu);
// Set the font attributes.
return 0;
// Process other command messages.
default:
break;
}
break;
// Process other window messages.
case WM_DESTROY:
// Destroy the checked and unchecked bitmaps.
DeleteObject(hbmpCheck);
DeleteObject(hbmpUncheck);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}
HBITMAP GetMyCheckBitmaps(UINT fuCheck)
{
COLORREF crBackground; // background color
HBRUSH hbrBackground; // background brush
HBRUSH hbrTargetOld; // original background brush
HDC hdcSource; // source device context
HDC hdcTarget; // target device context
HBITMAP hbmpCheckboxes; // handle to check-box bitmap
BITMAP bmCheckbox; // structure for bitmap data
HBITMAP hbmpSourceOld; // handle to original source bitmap
HBITMAP hbmpTargetOld; // handle to original target bitmap
HBITMAP hbmpCheck; // handle to check-mark bitmap
RECT rc; // rectangle for check-box bitmap
WORD wBitmapX; // width of check-mark bitmap
WORD wBitmapY; // height of check-mark bitmap
// Get the menu background color and create a solid brush
// with that color.
crBackground = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crBackground);
// Create memory device contexts for the source and
// destination bitmaps.
hdcSource = CreateCompatibleDC((HDC) NULL);
hdcTarget = CreateCompatibleDC(hdcSource);
// Get the size of the system default check-mark bitmap and
// create a compatible bitmap of the same size.
wBitmapX = GetSystemMetrics(SM_CXMENUCHECK);
wBitmapY = GetSystemMetrics(SM_CYMENUCHECK);
hbmpCheck = CreateCompatibleBitmap(hdcSource, wBitmapX,
wBitmapY);
// Select the background brush and bitmap into the target DC.
hbrTargetOld = SelectObject(hdcTarget, hbrBackground);
hbmpTargetOld = SelectObject(hdcTarget, hbmpCheck);
// Use the selected brush to initialize the background color
// of the bitmap in the target device context.
PatBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, PATCOPY);
// Load the predefined check box bitmaps and select it
// into the source DC.
hbmpCheckboxes = LoadBitmap((HINSTANCE) NULL,
(LPTSTR) OBM_CHECKBOXES);
hbmpSourceOld = SelectObject(hdcSource, hbmpCheckboxes);
// Fill a BITMAP structure with information about the
// check box bitmaps, and then find the upper-left corner of
// the unchecked check box or the checked check box.
GetObject(hbmpCheckboxes, sizeof(BITMAP), &bmCheckbox);
if (fuCheck == UNCHECK)
{
rc.left = 0;
rc.right = (bmCheckbox.bmWidth / 4);
}
else
{
rc.left = (bmCheckbox.bmWidth / 4);
rc.right = (bmCheckbox.bmWidth / 4) * 2;
}
rc.top = 0;
rc.bottom = (bmCheckbox.bmHeight / 3);
// Copy the appropriate bitmap into the target DC. If the
// check-box bitmap is larger than the default check-mark
// bitmap, use StretchBlt to make it fit; otherwise, just
// copy it.
if (((rc.right - rc.left) > (int) wBitmapX) ||
((rc.bottom - rc.top) > (int) wBitmapY))
{
StretchBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY,
hdcSource, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, SRCCOPY);
}
else
{
BitBlt(hdcTarget, 0, 0, rc.right - rc.left,
rc.bottom - rc.top,
hdcSource, rc.left, rc.top, SRCCOPY);
}
// Select the old source and destination bitmaps into the
// source and destination DCs, and then delete the DCs and
// the background brush.
SelectObject(hdcSource, hbmpSourceOld);
SelectObject(hdcTarget, hbrTargetOld);
hbmpCheck = SelectObject(hdcTarget, hbmpTargetOld);
DeleteObject(hbrBackground);
DeleteObject(hdcSource);
DeleteObject(hdcTarget);
// Return a handle to the new check-mark bitmap.
return hbmpCheck;
}
BYTE CheckOrUncheckMenuItem(BYTE bMenuItemID, HMENU hmenu)
{
DWORD fdwMenu;
static BYTE fbAttributes;
switch (bMenuItemID)
{
case IDM_REGULAR:
// Whenever the Regular menu item is selected, add a
// check mark to it and then remove checkmarks from
// any font-attribute menu items.
CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED);
if (fbAttributes & ATTRIBMASK)
{
CheckMenuItem(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_UNCHECKED);
}
fbAttributes = IDM_REGULAR;
return fbAttributes;
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:
// Toggle the check mark for the selected menu item and
// set the font attribute flags appropriately.
fdwMenu = GetMenuState(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND);
if (!(fdwMenu & MF_CHECKED))
{
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes |= bMenuItemID;
}
else
{
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes ^= bMenuItemID;
}
// If any font attributes are currently selected,
// remove the check mark from the Regular menu item;
// if no attributes are selected, add a check mark
// to the Regular menu item.
if (fbAttributes & ATTRIBMASK)
{
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes &= (BYTE) ~IDM_REGULAR;
}
else
{
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes = IDM_REGULAR;
}
return fbAttributes;
}
}
Пример использования настраиваемых точечных изображений с галочкой
В примере в этом разделе пользовательские растровые изображения проверка пометки назначаются пунктам меню в двух меню. В пунктах меню первого меню указаны атрибуты символов: полужирный, курсив и подчеркивание. Каждый пункт меню можно выбрать или очистить. Для этих пунктов меню в примере используются растровые изображения с проверка метками, похожие на выбранные и очищенные состояния элемента управления проверка box.
Элементы меню во втором меню задают параметры выравнивания абзаца: по левому краю, по центру и по правому краю. В любое время выбирается только один из этих пунктов меню. Для этих пунктов меню в примере используются растровые рисунки проверка метки, похожие на выбранные и понятные состояния элемента управления переключателя.
Процедура window обрабатывает сообщение WM_CREATE путем вызова определяемой приложением функции OnCreate.
OnCreate
создает четыре проверка растровые изображения, а затем назначает их соответствующим пунктам меню с помощью функции SetMenuItemBitmaps.
Чтобы создать каждое растровое изображение, OnCreate вызывает определяемую приложением функцию CreateMenuBitmaps, указывая указатель на функцию рисования для конкретного растрового рисунка. CreateMenuBitmaps создает монохромное растровое изображение требуемого размера, выбирает его в контексте устройства памяти и удаляет фон. Затем он вызывает указанную функцию рисования для заполнения переднего плана.
Четыре определяемые приложением функции рисования: DrawCheck, DrawUncheck, DrawRadioCheck и DrawRadioUncheck. Они рисуют прямоугольник с X, пустой прямоугольник, эллипс, содержащий меньший заполненный эллипс, и пустой эллипс соответственно.
Процедура окна обрабатывает сообщение WM_DESTROY путем удаления растровых рисунков проверка метки. Он извлекает каждый дескриптор растрового изображения с помощью функции GetMenuItemInfo , а затем передает дескриптор функции.
Когда пользователь выбирает пункт меню, в окно владельца отправляется WM_COMMAND сообщение. Для пунктов меню Символ процедура окна вызывает определяемую приложением функцию CheckCharacterItem. Для элементов меню Абзац процедура окна вызывает определяемую приложением функцию CheckParagraphItem.
Каждый элемент в меню Символ можно выбрать и очистить независимо друг от друга. Поэтому CheckCharacterItem просто переключает состояние проверка указанного пункта меню. Сначала функция вызывает функцию GetMenuItemInfo , чтобы получить текущее состояние элемента меню. Затем он переключает флаг состояния MFS_CHECKED и задает новое состояние путем вызова функции SetMenuItemInfo .
В отличие от атрибутов символов, одновременно можно выбрать только одно выравнивание абзаца. Поэтому CheckParagraphItem проверяет указанный пункт меню и удаляет метку проверка из всех остальных элементов меню. Для этого он вызывает функцию CheckMenuRadioItem .
Ниже приведены соответствующие части файла заголовка приложения.
// Menu-item identifiers for the Character menu
#define IDM_CHARACTER 10
#define IDM_BOLD 11
#define IDM_ITALIC 12
#define IDM_UNDERLINE 13
// Menu-item identifiers for the Paragraph menu
#define IDM_PARAGRAPH 20
#define IDM_LEFT 21
#define IDM_CENTER 22
#define IDM_RIGHT 23
// Function-pointer type for drawing functions
typedef VOID (WINAPI * DRAWFUNC)(HDC hdc, SIZE size);
Ниже приведены соответствующие части процедуры окна приложения и связанные функции.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
case WM_CREATE:
if (!OnCreate(hwnd))
return -1;
break;
case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_BOLD:
case IDM_ITALIC:
case IDM_UNDERLINE:
CheckCharacterItem(hwnd, LOWORD(wParam));
break;
case IDM_LEFT:
case IDM_CENTER:
case IDM_RIGHT:
CheckParagraphItem(hwnd, LOWORD(wParam));
break;
// Process other commands here.
}
break;
// Process other messages here.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
VOID WINAPI CheckCharacterItem(HWND hwnd, UINT uID)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
// Get a handle to the Character menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Get the state of the specified menu item.
mii.fMask = MIIM_STATE; // information to get
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
// Toggle the checked state.
mii.fState ^= MFS_CHECKED;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
}
VOID WINAPI CheckParagraphItem(HWND hwnd, UINT uID)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
// Get a handle to the Paragraph menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Check the specified item and uncheck all the others.
CheckMenuRadioItem(
hmenuPopup, // handle to menu
IDM_LEFT, // first item in range
IDM_RIGHT, // last item in range
uID, // item to check
MF_BYCOMMAND // IDs, not positions
);
}
BOOL WINAPI OnCreate(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
HBITMAP hbmChecked;
HBITMAP hbmUnchecked;
// Get a handle to the Character menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Create the checked and unchecked bitmaps.
hbmChecked = CreateMenuBitmap(DrawCheck);
hbmUnchecked = CreateMenuBitmap(DrawUncheck);
// Set the check-mark bitmaps for each menu item.
for (uID = IDM_BOLD; uID <= IDM_UNDERLINE; uID++)
{
SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,
hbmUnchecked, hbmChecked);
}
// Get a handle to the Paragraph pop-up menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Create the checked and unchecked bitmaps.
hbmChecked = CreateMenuBitmap(DrawRadioCheck);
hbmUnchecked = CreateMenuBitmap(DrawRadioUncheck);
// Set the check-mark bitmaps for each menu item.
for (uID = IDM_LEFT; uID <= IDM_RIGHT; uID++)
{
SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,
hbmUnchecked, hbmChecked);
}
// Initially check the IDM_LEFT paragraph item.
CheckMenuRadioItem(hmenuPopup, IDM_LEFT, IDM_RIGHT,
IDM_LEFT, MF_BYCOMMAND);
return TRUE;
}
HBITMAP WINAPI CreateMenuBitmap(DRAWFUNC lpfnDraw)
{
// Create a DC compatible with the desktop window's DC.
HWND hwndDesktop = GetDesktopWindow();
HDC hdcDesktop = GetDC(hwndDesktop);
HDC hdcMem = CreateCompatibleDC(hdcDesktop);
// Determine the required bitmap size.
SIZE size = { GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK) };
// Create a monochrome bitmap and select it.
HBITMAP hbm = CreateBitmap(size.cx, size.cy, 1, 1, NULL);
HBITMAP hbmOld = SelectObject(hdcMem, hbm);
// Erase the background and call the drawing function.
PatBlt(hdcMem, 0, 0, size.cx, size.cy, WHITENESS);
(*lpfnDraw)(hdcMem, size);
// Clean up.
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
ReleaseDC(hwndDesktop, hdcDesktop);
return hbm;
}
VOID WINAPI DrawCheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 0, 0, size.cx, size.cy);
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, size.cx, size.cy);
MoveToEx(hdc, 0, size.cy - 1, NULL);
LineTo(hdc, size.cx - 1, 0);
SelectObject(hdc, hbrOld);
}
VOID WINAPI DrawUncheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, hbrOld);
}
VOID WINAPI DrawRadioCheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Ellipse(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Ellipse(hdc, 2, 2, size.cx - 2, size.cy - 2);
SelectObject(hdc, hbrOld);
}
VOID WINAPI DrawRadioUncheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Ellipse(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, hbrOld);
}
VOID WINAPI OnDestroy(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
// Get a handle to the Character menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Get the check-mark bitmaps and delete them.
mii.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo(hmenuPopup, IDM_BOLD, FALSE, &mii);
DeleteObject(mii.hbmpChecked);
DeleteObject(mii.hbmpUnchecked);
// Get a handle to the Paragraph menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Get the check-mark bitmaps and delete them.
mii.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo(hmenuPopup, IDM_LEFT, FALSE, &mii);
DeleteObject(mii.hbmpChecked);
DeleteObject(mii.hbmpUnchecked);
}