Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Esta seção descreve as seguintes tarefas:
- Usando um recurso Menu-Template
- Criando um menu de atalho
- Usando Menu-Item bitmaps
- Criando itens de menu Owner-Drawn
- Usando bitmaps de marca de seleção personalizados
Usando um recurso Menu-Template
Normalmente, você inclui um menu em um aplicativo criando um recurso de modelo de menu e, em seguida, carregando o menu em tempo de execução. Esta seção descreve o formato de um modelo de menu e explica como carregar um recurso de modelo de menu e usá-lo em seu aplicativo. Para obter informações sobre como criar um recurso de modelo de menu, consulte a documentação incluída com suas ferramentas de desenvolvimento.
- Formato Menu-Template Estendido
- Formato Menu-Template antigo
- Carregando um recurso Menu-Template
- Criando um menu de classe
Formato Menu-Template estendido
O formato de modelo de menu estendido suporta funcionalidade de menu adicional. Como os recursos de modelo de menu padrão, os recursos de modelo de menu estendido têm o RT_MENU tipo de recurso. O sistema distingue os dois formatos de recurso pelo número da versão, que é o primeiro membro do cabeçalho do recurso.
Um modelo de menu estendido consiste numa estrutura de MENUEX_TEMPLATE_HEADER seguida de mais uma MENUEX_TEMPLATE_ITEM estruturas de definição de item.
Formato Menu-Template antigo
Um modelo de menu antigo (Microsoft Windows NT 3.51 e anteriores) define um menu, mas não suporta a nova funcionalidade de menu. Um recurso de modelo de menu antigo tem o tipo de recurso RT_MENU.
Um modelo de menu antigo consiste em uma estrutura MENUITEMTEMPLATEHEADER seguida por uma ou mais estruturas MENUITEMTEMPLATE.
Carregando um recurso de Menu-Template
Para carregar um recurso de modelo de menu, use a funçãoLoadMenu, especificando um identificador para o módulo que contém o recurso e o identificador do modelo de menu. A função LoadMenu retorna um identificador de menu que você pode usar para atribuir o menu a uma janela. Esta janela torna-se a janela do proprietário do menu, recebendo todas as mensagens geradas pelo menu.
Para criar um menu a partir de um modelo de menu que já esteja na memória, use a funçãoLoadMenuIndirect. Isso é útil se seu aplicativo gerar modelos de menu dinamicamente.
Para atribuir um menu a uma janela, use a função SetMenu ou especifique o identificador do menu no parâmetro hMenu da funçãoCreateWindowExao criar uma janela. Outra maneira de atribuir um menu a uma janela é especificar um modelo de menu ao registrar uma classe de janela; O modelo identifica o menu especificado como o menu de classe para essa classe de janela.
Para que o sistema atribua automaticamente um menu específico a uma janela, especifique o modelo do menu ao registrar a classe da janela. O modelo identifica o menu especificado como o menu de classe para essa classe de janela. Em seguida, quando você cria uma janela da classe especificada, o sistema atribui automaticamente o menu especificado à janela.
Não é possível atribuir um menu a uma janela que seja uma janela filha.
Para criar um menu de classe, inclua o identificador do recurso de modelo de menu como o lpszMenuName membro de uma estrutura deWNDCLASSe, em seguida, passe um ponteiro para a estrutura para a funçãoRegisterClass.
Criando um menu de classe
O exemplo a seguir mostra como criar um menu de classe para um aplicativo, criar uma janela que usa o menu de classe e processar comandos de menu no procedimento de janela.
A seguir está a parte relevante do arquivo de cabeçalho do aplicativo:
// Menu-template resource identifier
#define IDM_MYMENURESOURCE 3
Seguem-se as partes relevantes do próprio pedido:
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;
}
Criando um menu de atalho
Para usar um menu de atalho em um aplicativo, passe seu identificador para a função TrackPopupMenuEx. Uma aplicação normalmente chama TrackPopupMenuEx num procedimento de janela em resposta a uma mensagem gerada pelo utilizador, como WM_LBUTTONDOWN ou WM_KEYDOWN.
Além da alça do menu pop-up, TrackPopupMenuEx requer que você especifique um identificador para a janela do proprietário, a posição do menu de atalho (nas coordenadas da tela) e o botão do mouse que o usuário pode usar para escolher um item.
A função mais antiga TrackPopupMenu ainda é suportada, mas novas aplicações devem usar a função TrackPopupMenuEx. A função TrackPopupMenuEx requer os mesmos parâmetros que TrackPopupMenu, mas também permite especificar uma parte do ecrã que o menu não deve obscurecer. Um aplicativo normalmente chama essas funções em um procedimento de janela ao processar a mensagem WM_CONTEXTMENU.
Você pode especificar a posição de um menu de atalho fornecendo as coordenadas x e y junto com o sinalizador TPM_CENTERALIGN, TPM_LEFTALIGNou TPM_RIGHTALIGN. O sinalizador especifica a posição do menu de atalho em relação às coordenadas x e y.
Você deve permitir que o usuário escolha um item de um menu de atalho usando o mesmo botão do mouse usado para exibir o menu. Para realizar esta ação, especifique o sinalizador TPM_LEFTBUTTON ou TPM_RIGHTBUTTON para indicar qual botão do rato o utilizador pode usar para escolher um item de menu.
- Processando a mensagem WM_CONTEXTMENU
- Criar um atalho Font-Attributes Menu
- Exibindo um menu de atalho
Processando a mensagem WM_CONTEXTMENU
A mensagem WM_CONTEXTMENU é gerada quando o procedimento de janela de um aplicativo passa a mensagem WM_RBUTTONUP ou WM_NCRBUTTONUP para a função DefWindowProc. O aplicativo pode processar essa mensagem para exibir um menu de atalho apropriado para uma parte específica de sua tela. Se o aplicativo não exibir um menu de atalho, ele deve passar a mensagem para DefWindowProc para manipulação padrão.
A seguir apresenta-se um exemplo de processamento de mensagens WM_CONTEXTMENU, tal como pode surgir no procedimento de janela de uma aplicação. As palavras de ordem baixa e alta do parâmetro lParam especificam as coordenadas de tela do mouse quando o botão direito do mouse é liberado (observe que essas coordenadas podem ter valores negativos em sistemas com vários monitores). A função OnContextMenu definida pelo aplicativo retorna TRUE se exibir um menu de contexto, ou FALSE se não exibir.
case WM_CONTEXTMENU:
if (!OnContextMenu(hwnd, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)))
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
A seguinte função OnContextMenu definida pelo aplicativo exibe um menu de atalho se a posição especificada do mouse estiver dentro da área do cliente da janela. Uma função mais sofisticada pode exibir um dos vários menus diferentes, dependendo de qual parte da área do cliente é especificada. Para realmente exibir o menu de atalho, este exemplo chama uma função definida pelo aplicativo chamada DisplayContextMenu. Para obter uma descrição dessa função, consulte Exibindo um menu de atalho.
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;
}
Criar um menu de atalho Font-Attributes
O exemplo nesta seção contém partes de código de um aplicativo que cria e exibe um menu de atalho que permite ao usuário definir fontes e atributos de fonte. O aplicativo exibe o menu na área do cliente de sua janela principal sempre que o usuário clica no botão esquerdo do mouse.
Aqui está o modelo de menu para o menu de atalho fornecido no arquivo de definição de recursos do aplicativo.
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
O exemplo a seguir fornece o procedimento de janela e as funções de suporte usadas para criar e exibir o menu de atalho.
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);
}
Exibindo um menu de atalho
A função mostrada no exemplo a seguir exibe um menu de atalho.
O aplicativo inclui um recurso de menu identificado pela cadeia de caracteres "ShortcutExample". A barra de menus simplesmente contém um nome de menu. O aplicativo usa a função TrackPopupMenu para exibir o menu associado a este item de menu. (A barra de menus em si não é exibida porque TrackPopupMenu requer um identificador para um menu, submenu ou menu de atalho.)
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);
}
Usando Menu-Item Bitmaps
O sistema pode usar um bitmap em vez de uma cadeia de texto para exibir um item de menu. Para usar um bitmap, deve-se definir o sinalizador de MIIM_BITMAP para o item de menu e especificar um handle para o bitmap que o sistema deve exibir para o item de menu especificado no membro hbmpItem da estrutura MENUITEMINFO. Esta seção descreve como usar bitmaps para itens de menu.
- Definindo o sinalizador de tipo de bitmap
- Criando o Bitmap
- Adicionar linhas e gráficos a um menu
- Exemplo de Menu-Item Bitmaps
Definindo o sinalizador de tipo de bitmap
O sinalizador MIIM_BITMAP ou MF_BITMAP informa ao sistema para usar um bitmap em vez de uma cadeia de caracteres de texto para exibir um item de menu. O sinalizador MIIM_BITMAP ou MF_BITMAP de um item de menu deve ser definido em tempo de execução; Não é possível defini-lo no arquivo de definição de recurso.
Para uma nova aplicação, pode utilizar a função SetMenuItemInfo ou InsertMenuItem para definir o sinalizador de tipo MIIM_BITMAP. Para alterar um item de menu de um item de texto para um item de bitmap, use SetMenuItemInfo. Para adicionar um novo item de bitmap a um menu, use a função InsertMenuItem.
Os aplicativos escritos para versões anteriores do sistema podem continuar a usar as funções ModifyMenu, InsertMenuou AppendMenu para definir o sinalizador MF_BITMAP. Para alterar um item de menu de um item de cadeia de texto para um item de bitmap, use ModifyMenu. Para adicionar um novo item de bitmap a um menu, use o sinalizador MF_BITMAP com a função InsertMenu ou a função AppendMenu.
Criando o bitmap
Ao definir o sinalizador de tipo MIIM_BITMAP ou MF_BITMAP para um item de menu, você também deve especificar um identificador para o bitmap que o sistema deve exibir para o item de menu. Você pode fornecer o bitmap como um recurso de bitmap ou criar o bitmap em tempo de execução. Se você usar um recurso de bitmap, poderá usar a função LoadBitmap para carregar o bitmap e obter seu identificador.
Para criar o bitmap em tempo de execução, use as funções GDI (Windows Graphics Device Interface). GDI fornece várias maneiras de criar um bitmap em tempo de execução, mas os desenvolvedores normalmente usam o seguinte método:
- Use a funçãoCreateCompatibleDC para criar um contexto de dispositivo compatível com o contexto do dispositivo usado pela janela principal do aplicativo.
- Use a funçãoCreateCompatibleBitmap para criar um bitmap compatível com a janela principal do aplicativo ou use a função CreateBitmap para criar um bitmap monocromático.
- Use a função SelectObject para selecionar o bitmap no contexto do dispositivo compatível.
- Use funções de desenho GDI, como Ellipse e LineTo, para desenhar uma imagem no bitmap.
Para obter mais informações, consulte Bitmaps.
Adicionando linhas e gráficos a um menu
O exemplo de código a seguir mostra como criar um menu que contém bitmaps de item de menu. Cria dois menus. O primeiro é um menu de Gráfico que contém três bitmaps de opções do menu: um gráfico circular, um gráfico de linhas e um gráfico de barras. O exemplo demonstra como carregar esses bitmaps a partir do arquivo de recursos da aplicação e, em seguida, utilizar as funções CreatePopupMenu e AppendMenu para criar o menu e os itens de menu.
O segundo menu é um menu de Linhas. Ele contém bitmaps mostrando os estilos de linha fornecidos pela caneta predefinida no sistema. Os bitmaps de estilo de linha são criados em tempo de execução utilizando funções GDI.
Aqui estão as definições dos recursos de bitmap no arquivo de definição de recursos do aplicativo.
PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp
Aqui estão as partes relevantes do arquivo de cabeçalho do aplicativo.
// 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);
O exemplo a seguir mostra como menus e bitmaps de item de menu são criados em um aplicativo.
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;
}
Exemplo de Menu-Item bitmaps
O exemplo neste tópico cria dois menus, cada um contendo vários itens de menu bitmap. Para cada menu, o aplicativo adiciona um nome de menu correspondente à barra de menus da janela principal.
O primeiro menu contém itens de menu mostrando cada um dos três tipos de gráfico: pizza, linha e barra. Os bitmaps para esses itens de menu são definidos como recursos e são carregados usando a função LoadBitmap. Associado a este menu, na barra de menus, está o nome de menu "Gráfico".
O segundo menu contém itens de menu mostrando cada um dos cinco estilos de linha usados com a funçãoCreatePen: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOTe PS_DASHDOTDOT. O aplicativo cria os bitmaps para esses itens de menu em tempo de execução usando funções de desenho GDI. Associado a este menu está na barra de menus um nome de menu Linhas.
No processo de janela da aplicação, estão definidas duas matrizes estáticas de identificadores de bitmap. Uma matriz contém os manípulos dos três bitmaps usados para o menu Gráfico. O outro contém os manipuladores dos cinco bitmaps usados para o menu Linhas. Ao processar a mensagem WM_CREATE, o procedimento de janela carrega os bitmaps do gráfico, cria os bitmaps de linha e adiciona os itens de menu correspondentes. Ao processar a mensagem WM_DESTROY, o procedimento de janela exclui todos os bitmaps.
A seguir estão as partes relevantes do arquivo de cabeçalho do aplicativo.
// 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
Seguem-se as partes relevantes do procedimento da janela. O procedimento de janela executa a maior parte de sua inicialização chamando as funções LoadChartBitmaps, CreateLineBitmaps e AddBitmapMenu definidas pelo aplicativo, descritas posteriormente neste tópico.
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;
}
A função LoadChartBitmaps definida pelo aplicativo carrega os recursos de bitmap para o menu de gráfico chamando a função LoadBitmap, da seguinte maneira.
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));
}
A função CreateLineBitmaps definida pelo aplicativo cria os bitmaps para o menu Linhas usando funções de desenho GDI. A função cria um contexto de dispositivo de memória (DC) com as mesmas propriedades que o DC da janela da área de trabalho. Para cada estilo de linha, a função cria um bitmap, seleciona-o no DC de memória e desenha nele.
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);
}
A função AddBitmapMenu definida pelo aplicativo cria um menu e adiciona o número especificado de itens de menu de bitmap a ele. Em seguida, adiciona um nome de menu correspondente à barra de menus da janela especificada.
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);
}
Criando itens de menu Owner-Drawn
Se você precisar de controle total sobre a aparência de um item de menu, poderá usar um item de menu desenhado pelo proprietário em seu aplicativo. Esta seção descreve as etapas envolvidas na criação e no uso de um item de menu desenhado pelo proprietário.
- Definindo a bandeira Owner-Drawn
- Owner-Drawn Menus e a Mensagem WM_MEASUREITEM
- Owner-Drawn Menus e a Mensagem WM_DRAWITEM
- Owner-Drawn Menus e a Mensagem WM_MENUCHAR
- Definindo fontes para Menu-Item cadeias de texto
- Exemplo de Owner-Drawn itens de menu
Configuração do flag Owner-Drawn
Não é possível definir um item de menu desenhado pelo proprietário no arquivo de definição de recursos do aplicativo. Em vez disso, você deve criar um novo item de menu ou modificar um existente usando o sinalizador de menu MFT_OWNERDRAW.
Você pode usar a função InsertMenuItem ou a função SetMenuItemInfo para especificar um item de menu personalizado. Utilize InsertMenuItem para inserir um novo item de menu na posição especificada numa barra de menus ou num menu. Use SetMenuItemInfo para alterar o conteúdo de um menu.
Ao chamar essas duas funções, você deve especificar um ponteiro para uma estruturaMENUITEMINFO, que especifica as propriedades do novo item de menu ou as propriedades que você deseja alterar para um item de menu existente. Para tornar um item um item desenhado pelo proprietário, especifique o valor MIIM_FTYPE para o membro fMask e o valor MFT_OWNERDRAW para o membro fType.
Ao definir os membros apropriados da estrutura MENUITEMINFO, pode-se associar a cada item de menu um valor definido pelo aplicativo, denominado dados do item. Para fazer isso, especifique o valor MIIM_DATA para o membro fMask e o valor definido pelo aplicativo para o membro dwItemData.
Você pode usar dados de item com qualquer tipo de item de menu, mas é particularmente útil para itens desenhados pelo proprietário. Por exemplo, suponha que uma estrutura contenha informações usadas para desenhar um item de menu. Uma aplicação pode usar os dados de um item de menu para armazenar um ponteiro para a estrutura. Os dados do item são enviados para a janela do proprietário do menu com as mensagens WM_MEASUREITEM e WM_DRAWITEM. Para recuperar os dados do item para um menu a qualquer momento, use a função GetMenuItemInfo.
Os aplicativos escritos para versões anteriores do sistema podem continuar a chamar AppendMenu , InsertMenuou ModifyMenu para atribuir o sinalizador MF_OWNERDRAW a um item de menu desenhado pelo proprietário.
Quando você chama qualquer uma dessas três funções, você pode passar um valor como o lpNewItem parâmetro. Esse valor pode representar qualquer informação que seja significativa para seu aplicativo e que estará disponível para seu aplicativo quando o item for exibido. Por exemplo, o valor pode conter um ponteiro para uma estrutura; A estrutura, por sua vez, pode conter uma cadeia de texto e um identificador para a fonte lógica que seu aplicativo usará para desenhar a cadeia de caracteres.
Owner-Drawn Menus e mensagem WM_MEASUREITEM
Antes de o sistema exibir um item de menu definido pelo proprietário pela primeira vez, ele envia a mensagem WM_MEASUREITEM para o procedimento da janela que contém o menu do item. Esta mensagem contém um ponteiro para uma estrutura de MEASUREITEMSTRUCT que identifica o item e contém os dados do item que uma aplicação pode ter atribuído a ele. O procedimento de janela deve preencher os membros itemWidth e itemHeight da estrutura antes de retornar do processamento da mensagem. O sistema usa as informações nesses membros ao criar o retângulo delimitador no qual um aplicativo desenha o item de menu. Ele também usa as informações para detetar quando o usuário escolhe o item.
Owner-Drawn Menus e a WM_DRAWITEM Message
Sempre que o item deve ser desenhado (por exemplo, quando é exibido pela primeira vez ou quando o usuário o seleciona), o sistema envia a mensagem WM_DRAWITEM para o procedimento da janela do proprietário do menu. Esta mensagem contém um ponteiro para a estrutura DRAWITEMSTRUCT , que contém informações sobre o item, incluindo os dados que uma aplicação pode ter atribuído a ele. Além disso, DRAWITEMSTRUCT contém sinalizadores que indicam o estado do item (como se ele está cinza ou selecionado), bem como um retângulo delimitador e um contexto de dispositivo que o aplicativo usa para desenhar o item.
Um aplicativo deve fazer o seguinte durante o processamento da mensagem WM_DRAWITEM:
- Determine o tipo de desenho necessário. Para fazer isso, verifique o membro itemAction da estrutura DRAWITEMSTRUCT.
- Desenhe o item de menu de forma apropriada, usando o retângulo delimitador e o contexto do dispositivo obtidos da estrutura DRAWITEMSTRUCT. A aplicação deve desenhar apenas dentro do retângulo delimitador. Por razões de desempenho, o sistema não corta partes da imagem que são desenhadas fora do retângulo.
- Restaure todos os objetos GDI associados ao contexto de dispositivo do item de menu.
Se o utilizador selecionar o item de menu, o sistema define o membro itemAction da estrutura DRAWITEMSTRUCT para o valor ODA_SELECT e define o valor ODS_SELECTED no membro itemState. Esta é a sugestão de um aplicativo para redesenhar o item de menu para indicar que ele está selecionado.
Owner-Drawn Menus e a Mensagem WM_MENUCHAR
Menus diferentes dos menus desenhados pelo proprietário podem especificar um mnemônico de menu inserindo um sublinhado ao lado de um caractere na cadeia de caracteres do menu. Isso permite que o usuário selecione o menu pressionando ALT e pressionando o caractere mnemônico do menu. Em menus desenhados pelo utilizador, no entanto, não se pode especificar um menu mnemónico dessa maneira. Em vez disso, o seu aplicativo deve processar a mensagem WM_MENUCHAR para fornecer menus desenhados pelo utilizador com mnemónicos.
A mensagem WM_MENUCHAR é enviada quando o utilizador digita um mnemónico do menu que não corresponde a nenhum dos mnemónicos predefinidos do menu atual. O valor contido em wParam especifica o caractere ASCII que corresponde à tecla que o usuário pressionou com a tecla ALT. A palavra de ordem baixa de wParam especifica o tipo do menu selecionado e pode ser um dos seguintes valores:
- MF_POPUP se o menu atual for um submenu.
- MF_SYSMENU caso o menu seja o menu do sistema.
A palavra de alta ordem de wParam contém o identificador de menu para o menu atual. A janela com os menus desenhados pelo proprietário pode processar WM_MENUCHAR da seguinte forma:
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);
Os dois na palavra de ordem alta do valor de retorno informam ao sistema que a palavra de ordem baixa do valor de retorno contém o índice baseado em zero do item de menu a ser selecionado.
As constantes a seguir correspondem aos possíveis valores de retorno da mensagem WM_MENUCHAR.
| Constante | Valor | Significado |
|---|---|---|
| MNC_IGNORE | 0 | O sistema deve descartar o caractere que o usuário pressionou e criar um pequeno sinal sonoro no alto-falante do sistema. |
| MNC_CLOSE | 1 | O sistema deve fechar o menu ativo. |
| MNC_EXECUTE | 2 | O sistema deve escolher o item especificado na palavra de ordem baixa do valor de retorno. A janela do proprietário recebe uma mensagem WM_COMMAND. |
| MNC_SELECT | 3 | O sistema deve selecionar o item especificado na palavra de ordem inferior do valor de retorno. |
Definindo fontes para cadeias de texto Menu-Item
Este tópico contém um exemplo de um aplicativo que usa itens de menu desenhados pelo proprietário em um menu. O menu contém itens que definem os atributos da fonte atual e os itens são exibidos usando o atributo de fonte apropriado.
Aqui está como o menu é definido no arquivo de definição de recurso. Observe que as cadeias de caracteres para os itens de menu Regular, Negrito, Itálico e Sublinhado são atribuídas em tempo de execução, portanto, suas cadeias de caracteres estão vazias no arquivo de definição de recurso.
MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "", IDM_REGULAR
MENUITEM SEPARATOR
MENUITEM "", IDM_BOLD
MENUITEM "", IDM_ITALIC
MENUITEM "", IDM_ULINE
END
END
O procedimento de janela do aplicativo processa as mensagens envolvidas no uso de itens de menu desenhados pelo proprietário. O aplicativo usa a mensagem WM_CREATE para fazer o seguinte:
- Configure o sinalizador MF_OWNERDRAW para os itens do menu.
- Defina as cadeias de texto para os itens de menu.
- Obtenha identificadores das fontes usadas para renderizar os itens.
- Obtenha os valores de texto e cor de plano de fundo para itens de menu selecionados.
As cadeias de texto e as alças de fonte são armazenadas em uma matriz de estruturas MYITEM definidas pelo aplicativo. A função GetAFont definida pelo aplicativo cria uma fonte que corresponde ao atributo font especificado e retorna um identificador para a fonte. Os identificadores são destruídos durante o processamento da mensagem WM_DESTROY.
Durante o processamento da mensagem WM_MEASUREITEM, o exemplo obtém a largura e a altura de uma string de item de menu e copia esses valores para a estrutura MEASUREITEMSTRUCT. O sistema usa os valores de largura e altura para calcular o tamanho do menu.
Durante o processamento da mensagem WM_DRAWITEM, a cadeia de caracteres do item de menu é desenhada com espaço ao lado da cadeia de caracteres para o bitmap da marca de verificação. Se o usuário selecionar o item, o texto selecionado e as cores do plano de fundo serão usados para desenhar o item.
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);
}
Exemplo de itens de menu Owner-Drawn
O exemplo neste tópico usa itens de menu desenhados pelo proprietário em um menu. Os itens de menu selecionam atributos de fonte específicos e o aplicativo exibe cada item de menu usando uma fonte que tem o atributo correspondente. Por exemplo, o item de menu Itálico é exibido em uma fonte em itálico. O Caractere nome do menu na barra de menus abre o menu.
A barra de menus e o menu suspenso são definidos inicialmente por um recurso de modelo de menu estendido. Como um modelo de menu não pode especificar itens desenhados pelo proprietário, o menu inicialmente contém quatro itens de menu de texto com as seguintes cadeias de caracteres: "Regular", "Negrito", "Itálico" e "Sublinhado". O procedimento de janela do aplicativo os altera para itens desenhados pelo proprietário quando processa a mensagem WM_CREATE. Quando recebe a mensagem WM_CREATE, o procedimento de janela chama a função OnCreate definida pelo aplicativo, que executa as seguintes etapas para cada item de menu:
- Aloca uma estrutura MYITEM definida pelo aplicativo.
- Obtém o texto do item de menu e o salva na estrutura MYITEM definida pelo aplicativo.
- Cria a fonte usada para exibir o item de menu e salva seu identificador na estrutura MYITEM definida pelo aplicativo.
- Altera o tipo de item de menu para MFT_OWNERDRAW e salva um ponteiro para a estrutura MYITEM definida pelo aplicativo como dados do item.
Como um ponteiro para cada estrutura MYITEM definida pelo aplicativo é salvo como dados do item, ele é passado para o procedimento da janela em conjunto com as mensagens WM_MEASUREITEM e WM_DRAWITEM para o item de menu correspondente. O ponteiro está contido no membro itemData de ambas as estruturas MEASUREITEMSTRUCT e DRAWITEMSTRUCT.
Uma mensagem WM_MEASUREITEM é enviada para cada item de menu desenhado pelo proprietário na primeira vez que é exibido. O aplicativo processa essa mensagem selecionando a fonte para o item de menu em um contexto de dispositivo e, em seguida, determinando o espaço necessário para exibir o texto do item de menu nessa fonte. A fonte e o texto do item de menu são especificados pela estrutura MYITEM do item de menu (a estrutura definida pelo aplicativo). O aplicativo determina o tamanho do texto usando a funçãoGetTextExtentPoint32.
O procedimento de janela processa a mensagem WM_DRAWITEM exibindo o texto do item de menu na fonte apropriada. A fonte e o texto do item de menu são especificados pela estrutura MYITEM do item de menu. O aplicativo seleciona texto e cores de plano de fundo apropriadas ao estado do item de menu.
O procedimento de janela processa a mensagem WM_DESTROY para destruir fontes e liberar memória. O aplicativo exclui a fonte e libera a estrutura MYITEM definida pelo aplicativo para cada item de menu.
A seguir estão as partes relevantes do arquivo de cabeçalho do aplicativo.
// 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
A seguir estão as partes relevantes do procedimento de janela do aplicativo e suas funções associadas.
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);
}
Usando bitmaps de marca de seleção personalizados
O sistema fornece um bitmap de marca de verificação padrão para exibição ao lado de um item de menu selecionado. Você pode personalizar um item de menu individual fornecendo um par de bitmaps para substituir o bitmap de marca de verificação padrão. O sistema exibe um bitmap quando o item é selecionado e o outro quando está limpo. Esta seção descreve as etapas envolvidas na criação e no uso de bitmaps de marca de verificação personalizados.
- Criando bitmaps de marca de seleção personalizados
- Associando bitmaps a um item de menu
- Definindo o atributo de marca de seleção
- Simulando caixas de seleção em um menu
- exemplo de uso de bitmaps de marca de seleção personalizados
Criação de bitmaps de marca de verificação personalizados
Um bitmap de marca de verificação personalizado deve ter o mesmo tamanho que o bitmap de marca de verificação padrão. Você pode recuperar o tamanho padrão da marca de verificação de um bitmap chamando a função GetSystemMetrics. A palavra de ordem baixa do valor de retorno desta função especifica a largura; A palavra de ordem alta especifica a altura.
Você pode usar recursos de bitmap para fornecer bitmaps de marca de verificação. No entanto, como o tamanho de bitmap necessário varia dependendo do tipo de exibição, talvez seja necessário redimensionar o bitmap em tempo de execução usando a função StretchBlt. Dependendo do bitmap, a distorção causada pelo dimensionamento pode produzir resultados inaceitáveis.
Em vez de usar um recurso de bitmap, você pode criar um bitmap em tempo de execução usando funções GDI.
Para criar um bitmap em tempo de execução
Use a funçãoCreateCompatibleDC para criar um contexto de dispositivo compatível com o usado pela janela principal do aplicativo.
O parâmetro hdc da função pode especificar NULL ou o valor de retorno da função. CreateCompatibleDC retorna um identificador para o contexto do dispositivo compatível.
Use a funçãoCreateCompatibleBitmap para criar um bitmap compatível com a janela principal do aplicativo.
Os parâmetros nWidth e nHeight definem o tamanho do bitmap; eles devem especificar as informações de largura e altura retornadas pela funçãoGetSystemMetrics.
Observação
Você também pode usar a função CreateBitmap para criar um bitmap monocromático.
Use a função SelectObject para selecionar o bitmap no contexto do dispositivo compatível.
Use funções de desenho GDI, como Ellipse e LineTo, para desenhar uma imagem no bitmap ou use funções como BitBlt e StretchBlt para copiar uma imagem para o bitmap.
Para obter mais informações, consulte Bitmaps.
Associando bitmaps a um item de menu
Você associa um par de bitmaps de marca de verificação a um item de menu passando os identificadores dos bitmaps para a função SetMenuItemBitmaps. O parâmetro hBitmapUnchecked identifica o bitmap claro e o parâmetro hBitmapChecked identifica o bitmap selecionado. Se quiser remover uma ou ambas as marcas de seleção de um item de menu, pode definir o parâmetro hBitmapUnchecked, o parâmetro hBitmapChecked, ou ambos, para NULL.
Definindo o atributo da marca de verificação
A função CheckMenuItem define o atributo de marca de verificação de um item de menu como selecionada ou desmarcada. Você pode especificar o valor MF_CHECKED para definir o atributo de marca de verificação como selecionado e o valor MF_UNCHECKED para defini-lo como limpo.
Você também pode definir o estado de verificação de um item de menu usando a funçãoSetMenuItemInfo.
Às vezes, um grupo de itens de menu representa um conjunto de opções mutuamente exclusivas. Utilizando a função CheckMenuRadioItem, pode-se marcar um item de menu ao mesmo tempo que se remove a marca de seleção de todos os outros itens de menu do grupo.
Simulando caixas de seleção em um menu
Este tópico contém um exemplo que mostra como simular caixas de seleção em um menu. O exemplo contém um menu Caractere cujos itens permitem que o usuário defina os atributos negrito, itálico e sublinhado da fonte atual. Quando um atributo de fonte está em vigor, uma marca de seleção é exibida na caixa de seleção ao lado do item de menu correspondente; caso contrário, uma caixa de seleção vazia será exibida ao lado do item.
O exemplo substitui o bitmap de marca de verificação padrão por dois bitmaps: um bitmap com uma caixa de seleção selecionada e o bitmap com uma caixa vazia. O bitmap da caixa de verificação selecionada é apresentado ao lado do item de menu Negrito, Itálico ou Sublinhado quando o atributo de marca de verificação do item é definido como MF_CHECKED. O bitmap da caixa de seleção limpa ou vazia é exibido quando o atributo de marca de seleção é definido como MF_UNCHECKED.
O sistema fornece um bitmap predefinido que contém as imagens usadas para caixas de seleção e botões de opção. O exemplo isola as caixas de seleção marcadas e vazias, copia-as para dois bitmaps separados e as usa como bitmaps marcados e libertados para itens no menu de caracteres .
Para recuperar um identificador para o bitmap de caixa de seleção definido pelo sistema, o exemplo chama a função LoadBitmap, especificando NULL como o parâmetro hInstance e OBM_CHECKBOXES como o parâmetro lpBitmapName. Como as imagens no bitmap são todas do mesmo tamanho, o exemplo pode isolá-las dividindo a largura e a altura do bitmap pelo número de imagens em suas linhas e colunas.
A parte a seguir de um ficheiro de definição de recursos mostra como os itens de menu no menu de caracteres são definidos. Observe que nenhum atributo de fonte está em vigor inicialmente, portanto, o atributo de marca de verificação para o item Regular é definido como selecionado e, por padrão, o atributo de marca de verificação dos itens restantes é definido como limpo.
#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
Aqui estão os conteúdos relevantes do arquivo de cabeçalho do aplicativo.
// 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);
O exemplo a seguir mostra as partes do procedimento de janela que criam os bitmaps de marca de verificação; definir o atributo de marca de verificação dos itens de menu negrito, itálicoe sublinhar; e destruir bitmaps de marca de verificação.
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;
}
}
Exemplo de uso de bitmaps de marca de verificação personalizados
O exemplo neste tópico atribui bitmaps de marca de verificação personalizados a itens de menu em dois menus. Os itens de menu no primeiro menu especificam atributos de caracteres: negrito, itálico e sublinhado. Cada item de menu pode ser selecionado ou desmarcado. Para esses itens de menu, o exemplo usa bitmaps de marca de verificação que se assemelham aos estados selecionados e deselecionados de um controlo de caixa de seleção.
Os itens do segundo menu especificam as configurações de alinhamento de parágrafo: esquerda, centralizado e direita. Apenas um desses itens de menu é selecionado a qualquer momento. Para esses itens de menu, o exemplo usa bitmaps de marca de verificação que se assemelham aos estados selecionado e limpo de um controle de botão de opção.
O procedimento de janela processa a mensagem WM_CREATE chamando a função OnCreate definida pelo aplicativo.
OnCreate cria os quatro bitmaps de marca de verificação e depois atribui-os aos itens de menu apropriados, utilizando a função SetMenuItemBitmaps.
Para criar cada bitmap, o OnCreate chama a função CreateMenuBitmaps, definida pela aplicação, especificando um ponteiro para uma função de desenho específica para bitmap. CreateMenuBitmaps cria um bitmap monocromático do tamanho necessário, seleciona-o em um contexto de dispositivo de memória e apaga o plano de fundo. Em seguida, ele chama a função de desenho especificada para preencher o primeiro plano.
As quatro funções de desenho definidas pelo aplicativo são DrawCheck, DrawUncheck, DrawRadioChecke DrawRadioUncheck. Eles desenham um retângulo com um X, um retângulo vazio, uma elipse contendo uma elipse cheia menor e uma elipse vazia, respectivamente.
O procedimento de janela processa a mensagem WM_DESTROY eliminando os bitmaps de marca de seleção. Ele recupera cada identificador de bitmap usando a função GetMenuItemInfo e, em seguida, passa um identificador para essa função.
Quando o usuário escolhe um item de menu, uma mensagem de WM_COMMAND é enviada para a janela do proprietário. Para itens de menu no menu Character, o procedimento de janela chama a função CheckCharacterItem definida pelo aplicativo. Para itens no menu Parágrafo, o procedimento de janela chama a função CheckParagraphItem definida pelo aplicativo.
Cada item no menu de caracteres pode ser selecionado e limpo independentemente. Portanto, CheckCharacterItem simplesmente alterna o estado de verificação do item de menu especificado. Primeiro, a função GetMenuItemInfo é chamada para obter o estado atual do item de menu. Em seguida, ele alterna o sinalizador de estado MFS_CHECKED e define o novo estado chamando a funçãoSetMenuItemInfo.
Ao contrário dos atributos de caracteres, apenas um alinhamento de parágrafo pode ser selecionado de cada vez. Portanto, CheckParagraphItem verifica o item de menu especificado e remove a marca de seleção dos restantes itens do menu. Para fazer isso, ele chama a função CheckMenuRadioItem.
A seguir estão as partes relevantes do arquivo de cabeçalho do aplicativo.
// 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);
A seguir estão as partes relevantes do procedimento de janela do aplicativo e funções relacionadas.
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);
}