Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In deze sectie worden de volgende taken beschreven:
- Een Menu-Template-resource gebruiken
- Een snelmenu maken
- Het gebruik van Menu-Item Bitmaps
- Owner-Drawn menu-items maken
- Aangepaste vinkjes gebruiken
Een Menu-Template-resource gebruiken
Meestal neemt u een menu op in een toepassing door een menusjabloonresource te maken en vervolgens het menu tijdens runtime te laden. In deze sectie wordt de indeling van een menusjabloon beschreven en wordt uitgelegd hoe u een menusjabloonresource laadt en gebruikt in uw toepassing. Zie de documentatie die is opgenomen in uw ontwikkelhulpprogramma's voor informatie over het maken van een menusjabloonresource.
- uitgebreide Menu-Template-indeling
- oude Menu-Template Formaat
- een Menu-Template bron laden
- Een klasmenu maken
Uitgebreide Menu-Template-indeling
De uitgebreide indeling van de menusjabloon ondersteunt extra menufunctionaliteit. Net als standaardmenusjabloonresources hebben uitgebreide menusjabloonresources het RT_MENU resourcetype. Het systeem onderscheidt de twee resourceindelingen op basis van het versienummer. Dit is het eerste lid van de resourceheader.
Een uitgebreide menusjabloon bestaat uit een MENUEX_TEMPLATE_HEADER structuur, gevolgd door nog een MENUEX_TEMPLATE_ITEM itemdefinitiestructuren.
Oude Menu-Template-indeling
Een oude menusjabloon (Microsoft Windows NT 3.51 en eerder) definieert een menu, maar biedt geen ondersteuning voor de nieuwe menufunctionaliteit. Een oude menusjabloonresource heeft het RT_MENU resourcetype.
Een oude menusjabloon bestaat uit een MENUITEMTEMPLATEHEADER structuur gevolgd door een of meer MENUITEMTEMPLATE structuren.
Een Menu-Template-resource laden
Als u een menusjabloonresource wilt laden, gebruikt u de functie LoadMenu, waarbij u een ingang opgeeft aan de module die de resource en de id van de menusjabloon bevat. De functie LoadMenu retourneert een menugreep die u kunt gebruiken om het menu toe te wijzen aan een venster. Dit venster wordt het venster eigenaar van het menu en ontvangt alle berichten die door het menu worden gegenereerd.
Gebruik de functie LoadMenuIndirect om een menu te maken op basis van een menusjabloon die al in het geheugen aanwezig is. Dit is handig als uw toepassing dynamisch menusjablonen genereert.
Als u een menu aan een venster wilt toewijzen, gebruikt u de functie SetMenu of geeft u de greep van het menu op in de parameter hMenu van de functie CreateWindowEx bij het maken van een venster. Een andere manier waarop u een menu aan een venster kunt toewijzen, is door een menusjabloon op te geven wanneer u een vensterklasse registreert; de sjabloon identificeert het opgegeven menu als het klasmenu voor die vensterklasse.
Als u wilt dat het systeem automatisch een specifiek menu aan een venster toewijst, geeft u de sjabloon van het menu op wanneer u de klasse van het venster registreert. De sjabloon identificeert het opgegeven menu als het klasmenu voor die vensterklasse. Wanneer u vervolgens een venster van de opgegeven klasse maakt, wijst het systeem het opgegeven menu automatisch toe aan het venster.
U kunt geen menu toewijzen aan een venster dat een onderliggend venster is.
Als u een klassemenu wilt maken, neemt u de id van de menusjabloonresource op als de lpszMenuName lid van een WNDCLASS- structuur en geeft u vervolgens een aanwijzer door aan de structuur aan de RegisterClass-functie.
Een klasmenu maken
In het volgende voorbeeld ziet u hoe u een klassenaam menu voor een applicatie maakt, een venster maakt dat gebruikmaakt van het klassenaam menu, en de menuopdrachten verwerkt in de vensterprocedure.
Hier volgt het relevante gedeelte van het headerbestand van de toepassing:
// Menu-template resource identifier
#define IDM_MYMENURESOURCE 3
Hieronder volgen de relevante gedeelten van de toepassing zelf:
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;
}
Een snelmenu maken
Als u een snelmenu in een toepassing wilt gebruiken, geeft u de greep door aan de functie TrackPopupMenuEx. Een toepassing roept doorgaans TrackPopupMenuEx- aan in een vensterprocedure als reactie op een door de gebruiker gegenereerd bericht, zoals WM_LBUTTONDOWN of WM_KEYDOWN.
Naast de pop-upmenuhandle vereist TrackPopupMenuEx dat u een handle aan het eigenaarsvenster specificeert, de positie van het contextmenu (in schermcoördinaten) en de muisknop die de gebruiker gebruikt om een item te kiezen.
De oudere TrackPopupMenu functie wordt nog steeds ondersteund, maar nieuwe toepassingen moeten de functie TrackPopupMenuEx gebruiken. De functie TrackPopupMenuEx vereist dezelfde parameters als TrackPopupMenu, maar u kunt ook een gedeelte van het scherm opgeven dat het menu niet moet verbergen. Een toepassing roept deze functies doorgaans aan in een vensterprocedure bij het verwerken van het WM_CONTEXTMENU bericht.
U kunt de positie van een snelmenu opgeven door x- en y-coördinaten op te geven, samen met de TPM_CENTERALIGN, TPM_LEFTALIGNof TPM_RIGHTALIGN vlag. De vlag geeft de positie van het snelmenu op ten opzichte van de x- en y-coördinaten.
U moet toestaan dat de gebruiker een item in een snelmenu kiest met behulp van dezelfde muisknop die wordt gebruikt om het menu weer te geven. Geef hiervoor TPM_LEFTBUTTON of TPM_RIGHTBUTTON vlag op om aan te geven welke muisknop de gebruiker kan gebruiken om een menu-item te kiezen.
Het WM_CONTEXTMENU-bericht verwerken
Het WM_CONTEXTMENU bericht wordt gegenereerd wanneer de vensterprocedure van een toepassing het WM_RBUTTONUP of WM_NCRBUTTONUP bericht doorgeeft aan de de functie DefWindowProc. De toepassing kan dit bericht verwerken om een snelmenu weer te geven dat geschikt is voor een specifiek gedeelte van het scherm. Als de toepassing geen snelmenu weergeeft, moet het bericht worden doorgegeven aan DefWindowProc- voor standaardafhandeling.
Hieronder volgt een voorbeeld van WM_CONTEXTMENU berichtverwerking, zoals deze kan worden weergegeven in de vensterprocedure van een toepassing. De woorden met lage volgorde en hoge volgorde van de lParam parameter geven de schermcoördinaten van de muis op wanneer de rechtermuisknop wordt vrijgegeven (houd er rekening mee dat deze coördinaten negatieve waarden kunnen aannemen op systemen met meerdere monitors). De toepassingsgedefinieerde OnContextMenu functie retourneert TRUE als er een contextmenu wordt weergegeven of ONWAAR als dat niet het geval is.
case WM_CONTEXTMENU:
if (!OnContextMenu(hwnd, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)))
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
De volgende toepassingsgedefinieerde OnContextMenu-functie geeft een snelmenu weer als de opgegeven muispositie zich in het clientgebied van het venster bevindt. Een meer geavanceerde functie kan een van de verschillende menu’s weergeven, afhankelijk van welk deel van de clientomgeving wordt gespecificeerd. Als u het snelmenu daadwerkelijk wilt weergeven, roept dit voorbeeld een toepassingsgedefinieerde functie aan met de naam DisplayContextMenu. Zie Een snelmenu weergevenvoor een beschrijving van deze functie.
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;
}
Een snelmenu Font-Attributes maken
Het voorbeeld in deze sectie bevat delen van code van een toepassing die een snelmenu maakt en weergeeft waarmee de gebruiker lettertypen en lettertypekenmerken kan instellen. De toepassing geeft het menu weer in het clientgebied van het hoofdvenster wanneer de gebruiker op de linkermuisknop klikt.
Hier volgt de menusjabloon voor het snelmenu dat is opgegeven in het resourcedefinitiebestand van de toepassing.
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
In het volgende voorbeeld ziet u de vensterprocedure en ondersteunende functies die worden gebruikt om het snelmenu te maken en weer te geven.
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);
}
Een snelmenu weergeven
De functie die in het volgende voorbeeld wordt weergegeven, geeft een snelmenu weer.
De toepassing bevat een menuresource die wordt geïdentificeerd door de tekenreeks 'ShortcutExample'. De menubalk bevat gewoon een menunaam. De toepassing gebruikt de functie TrackPopupMenu om het menu weer te geven dat is gekoppeld aan dit menu-item. (De menubalk zelf wordt niet weergegeven omdat TrackPopupMenu een greep naar een menu, submenu of snelmenu vereist.)
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);
}
Het gebruik van Bitmaps Menu-Item
Het systeem kan een bitmap gebruiken in plaats van een tekenreeks om een menu-item weer te geven. Als u een bitmap wilt gebruiken, moet u de MIIM_BITMAP vlag instellen voor het menu-item en een ingang opgeven voor de bitmap die het systeem moet weergeven voor het menu-item in de hbmpItem lid van de MENUITEMINFO structuur. In deze sectie wordt beschreven hoe u bitmaps gebruikt voor menu-items.
- De bitmaptypevlag instellen
- De bitmap maken
- Lijnen en grafieken toevoegen aan een menu
- Voorbeeld van Menu-Item Bitmaps
De Bitmaptype-vlag instellen
De vlag MIIM_BITMAP of MF_BITMAP geeft aan dat het systeem een bitmap moet gebruiken in plaats van een tekenreeks om een menu-item weer te geven. De MIIM_BITMAP of MF_BITMAP vlag van een menu-item moet worden ingesteld tijdens de uitvoering; u kunt deze niet instellen in het resourcedefinitiebestand.
Voor nieuwe toepassingen kunt u de functie SetMenuItemInfo of InsertMenuItem gebruiken om het type vlag MIIM_BITMAP in te stellen. Als u een menu-item van een tekstitem wilt wijzigen in een bitmapitem, gebruikt u SetMenuItemInfo. Als u een nieuw bitmapitem aan een menu wilt toevoegen, gebruikt u de functie InsertMenuItem.
Toepassingen die zijn geschreven voor eerdere versies van het systeem, kunnen de functies ModifyMenu, InsertMenuof AppendMenu blijven gebruiken om de vlag MF_BITMAP in te stellen. Als u een menu-item van een tekenreeksitem wilt wijzigen in een bitmapitem, gebruikt u Menu Wijzigen. Als u een nieuw bitmapitem wilt toevoegen aan een menu, gebruikt u de vlag MF_BITMAP met de functie InsertMenu of AppendMenu.
De bitmap maken
Wanneer u de vlag voor het type MIIM_BITMAP of MF_BITMAP voor een menu-item instelt, moet u ook een ingang opgeven voor de bitmap die het systeem moet weergeven voor het menu-item. U kunt de bitmap opgeven als bitmapresource of de bitmap tijdens runtime maken. Als u een bitmapresource gebruikt, kunt u de functie LoadBitmap gebruiken om de bitmap te laden en het handvat ervan op te halen.
Als u de bitmap tijdens runtime wilt maken, gebruikt u GDI-functies (Windows Graphics Device Interface). GDI biedt verschillende manieren om een bitmap tijdens runtime te maken, maar ontwikkelaars gebruiken doorgaans de volgende methode:
- Gebruik de functie CreateCompatibleDC om een apparaatcontext te maken die compatibel is met de apparaatcontext die wordt gebruikt door het hoofdvenster van de toepassing.
- Gebruik de functie CreateCompatibleBitmap om een bitmap te maken die compatibel is met het hoofdvenster van de toepassing of gebruik de functie CreateBitmap om een monochrome bitmap te maken.
- Gebruik de functie SelectObject om de bitmap te selecteren in de context van het compatibele apparaat.
- Gebruik GDI-tekenfuncties, zoals Ellips en LineTo, om een afbeelding in de bitmap te tekenen.
Zie Bitmapsvoor meer informatie.
Lijnen en grafieken toevoegen aan een menu
In het volgende codevoorbeeld ziet u hoe u een menu maakt dat bitmaps voor menu-items bevat. Er worden twee menu's gemaakt. De eerste is een Grafiekenmenu dat drie menu-item bitmaps bevat: een cirkeldiagram, een lijndiagram en een staafdiagram. In het voorbeeld ziet u hoe u deze bitmaps uit het resourcebestand van de toepassing laadt en vervolgens de functies CreatePopupMenu en AppendMenu gebruikt om de menu- en menu-items te maken.
Het tweede menu is een menu Lijnen. Het bevat bitmaps met de lijnstijlen die worden geleverd door de vooraf gedefinieerde pen in het systeem. De bitmaps in lijnstijl worden tijdens runtime gemaakt met behulp van GDI-functies.
Hier volgen de definities van de bitmapresources in het resourcedefinitiebestand van de toepassing.
PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp
Dit zijn de relevante gedeelten van het headerbestand van de toepassing.
// 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);
In het volgende voorbeeld ziet u hoe menu's en menu-item bitmaps worden gemaakt in een toepassing.
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;
}
Voorbeeld van Menu-Item bitmaps
In het voorbeeld in dit onderwerp worden twee menu's gemaakt, elk met verschillende bitmapmenu-items. Voor elk menu voegt de toepassing een bijbehorende menunaam toe aan de menubalk van het hoofdvenster.
Het eerste menu bevat menu-items met elk van de drie grafiektypen: cirkel, lijn en staaf. De bitmaps voor deze menu-items worden gedefinieerd als resources en worden geladen met behulp van de functie LoadBitmap. Gekoppeld aan dit menu is een menunaam 'Grafiek' op de menubalk.
Het tweede menu bevat menu-items met elk van de vijf regelstijlen die worden gebruikt met de functie CreatePen: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOTen PS_DASHDOTDOT. De toepassing maakt de bitmaps voor deze menu-items tijdens runtime met behulp van GDI-tekenfuncties. Gekoppeld aan dit menu is een Regels naam van het menu op de menubalk.
Gedefinieerd in de vensterprocedure van de toepassing zijn twee statische arrays van bitmap-handles. Eén matrix bevat de grepen van de drie bitmaps die worden gebruikt voor het menu Grafiek. De andere bevat de verwijzingen van de vijf bitmaps die gebruikt worden voor het menu Regels. Bij het verwerken van het WM_CREATE bericht, laadt de vensterprocedure de diagrambitmaps, maakt de lijnbitmaps en voegt vervolgens de bijbehorende menu-items toe. Bij het verwerken van het WM_DESTROY bericht worden alle bitmaps verwijderd door de vensterprocedure.
Hieronder vindt u de relevante gedeelten van het headerbestand van de toepassing.
// 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
Hieronder volgen de relevante gedeelten van de vensterprocedure. De vensterprocedure voert de meeste initialisatie uit door de door de toepassing gedefinieerde LoadChartBitmaps, CreateLineBitmaps en AddBitmapMenu-functies aan te roepen, die verderop in dit onderwerp worden beschreven.
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;
}
Met de door de toepassing gedefinieerde functie LoadChartBitmaps worden de bitmapbronnen voor het grafiekmenu geladen door de LoadBitmap functie als volgt aan te roepen.
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));
}
Met de toepassingsgedefinieerde functie CreateLineBitmaps worden de bitmaps voor het menu Lijnen gemaakt met behulp van GDI-tekenfuncties. De functie maakt een geheugenapparaatcontext (DC) met dezelfde eigenschappen als de DC van het bureaubladvenster. Voor elke lijnstijl maakt de functie een bitmap, selecteert deze in het geheugen-basis DC en tekent daarin.
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);
}
De toepassingsgedefinieerde functie AddBitmapMenu maakt een menu en voegt het opgegeven aantal bitmapmenu-items eraan toe. Vervolgens wordt een bijbehorende menunaam toegevoegd aan de menubalk van het opgegeven venster.
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);
}
Menu-items voor Owner-Drawn maken
Als u volledige controle nodig hebt over het uiterlijk van een menu-item, kunt u een eigen vormgegeven menu-item in uw applicatie gebruiken. In deze sectie worden de stappen beschreven die betrokken zijn bij het maken en gebruiken van een menu-item dat door de eigenaar is getekend.
- De vlag Owner-Drawn instellen
- Owner-Drawn Menus en het WM_MEASUREITEM Message
- Owner-Drawn Menu's en het WM_DRAWITEM Bericht
- Owner-Drawn Menu's en het WM_MENUCHAR Bericht
- lettertypen instellen voor Menu-Item tekenreeksen
- voorbeeld van Owner-Drawn menu-items
De Owner-Drawn vlag instellen
U kunt geen door de eigenaar getekend menu-item definiëren in het resourcedefinitiebestand van uw toepassing. In plaats daarvan moet u een nieuw menu-item maken of een bestaande item wijzigen met behulp van de MFT_OWNERDRAW menuvlag.
U kunt de functie InsertMenuItem of SetMenuItemInfo gebruiken om een eigenaar-getekend menu-item op te geven. Gebruik InsertMenuItem om een nieuw menu-item in te voegen op de opgegeven positie in een menubalk of menu. Gebruik SetMenuItemInfo om de inhoud van een menu te wijzigen.
Wanneer u deze twee functies aanroept, moet u een aanwijzer opgeven naar een MENUITEMINFO structuur, waarmee de eigenschappen van het nieuwe menu-item of de eigenschappen die u wilt wijzigen voor een bestaand menu-item worden opgegeven. Als u een item een door de eigenaar getekend item wilt maken, geeft u de MIIM_FTYPE waarde op voor het fMask lid en de MFT_OWNERDRAW waarde voor het fType lid.
Door de juiste leden van de MENUITEMINFO structuur in te stellen, kunt u een door de toepassing gedefinieerde waarde, die itemgegevenswordt genoemd, koppelen aan elk menu-item. Hiertoe geeft u de MIIM_DATA waarde op voor het fMask-lid lid en de door de toepassing gedefinieerde waarde voor het dwItemData lid.
U kunt itemgegevens gebruiken met elk type menu-item, maar dit is met name handig voor door de eigenaar getekende items. Stel dat een structuur informatie bevat die wordt gebruikt om een menu-item te tekenen. Een toepassing kan de itemgegevens voor een menu-item gebruiken om een aanwijzer op te slaan in de structuur. De itemgegevens worden met de WM_MEASUREITEM en WM_DRAWITEM berichten verzonden naar het venster eigenaar van het menu. Als u de itemgegevens voor een menu op elk gewenst moment wilt ophalen, gebruikt u de functie GetMenuItemInfo.
Toepassingen die zijn geschreven voor eerdere versies van het systeem, kunnen AppendMenu, InsertMenuof Modify Menu blijven aanroepen om de vlag MF_OWNERDRAW toe te wijzen aan een menu-item dat door de eigenaar is getekend.
Wanneer u een van deze drie functies aanroept, kunt u een waarde doorgeven als de parameter lpNewItem. Deze waarde kan alle informatie vertegenwoordigen die zinvol is voor uw toepassing en die beschikbaar is voor uw toepassing wanneer het item wordt weergegeven. De waarde kan bijvoorbeeld een aanwijzer naar een structuur bevatten; de structuur kan op zijn beurt een tekenreeks bevatten en een ingang voor het logische lettertype dat uw toepassing gebruikt om de tekenreeks te tekenen.
Owner-Drawn menu's en het WM_MEASUREITEM bericht
Voordat het systeem voor het eerst een door de eigenaar getekend menu-item weergeeft, wordt het WM_MEASUREITEM bericht verzonden naar de vensterprocedure van het venster dat eigenaar is van het menu van het item. Dit bericht bevat een aanwijzer naar een MEASUREITEMSTRUCT structuur die het item identificeert en de itemgegevens bevat die een toepassing eraan kan hebben toegewezen. De vensterprocedure moet de itemWidth en itemHeight leden van de structuur invullen voordat het terugkeert van het verwerken van het bericht. Het systeem gebruikt de informatie in deze leden bij het maken van de begrenzingsrechthoek waarin een toepassing het menu-item tekent. Ook wordt de informatie gebruikt om te detecteren wanneer de gebruiker het item kiest.
Owner-Drawn Menu's en het WM_DRAWITEM-bericht
Wanneer het item moet worden getekend (bijvoorbeeld wanneer het voor het eerst wordt weergegeven of wanneer de gebruiker het selecteert), verzendt het systeem het WM_DRAWITEM bericht naar de vensterprocedure van het menu eigenaarvenster. Dit bericht bevat een aanwijzer naar een DRAWITEMSTRUCT structuur, die informatie over het item bevat, inclusief de itemgegevens die een toepassing eraan kan hebben toegewezen. Bovendien bevat DRAWITEMSTRUCT- vlaggen die de status van het item aangeven (zoals of het grijs of geselecteerd is) evenals een begrenzingsrechthoek en een apparaatcontext die door de toepassing wordt gebruikt om het item te tekenen.
Een toepassing moet het volgende doen tijdens het verwerken van het WM_DRAWITEM bericht:
- Bepaal het type tekening dat nodig is. Hiervoor controleert u het itemAction lid van de DRAWITEMSTRUCT structuur.
- Teken het menu-item op de juiste manier met behulp van de begrenzingsrechthoek en apparaatcontext die is verkregen uit de DRAWITEMSTRUCT structuur. De toepassing mag alleen binnen de begrenzingsrechthoek tekenen. Vanwege prestatieredenen knipt het systeem niet de delen van de afbeelding die buiten de rechthoek worden getekend.
- Alle GDI-objecten herstellen die zijn geselecteerd voor de apparaatcontext van het menu-item.
Als de gebruiker het menu-item selecteert, stelt het systeem het itemAction lid van de DRAWITEMSTRUCT structuur in op de ODA_SELECT-waarde en stelt de ODS_SELECTED waarde in het itemState lid. Dit is de aanwijzing van een toepassing om het menu-item opnieuw te tekenen om aan te geven dat het is geselecteerd.
Owner-Drawn menu's en het WM_MENUCHAR bericht
Andere menu's dan door de eigenaar getekende menu's kunnen een menumnemonic opgeven door een onderstrepingsteken in te voegen naast een teken in de menutekenreeks. Hierdoor kan de gebruiker het menu selecteren door op Alt te drukken en op het menu mnemonic-teken te drukken. In door de eigenaar getekende menu's kunt u op deze manier echter geen menu-nemonic opgeven. In plaats daarvan moet uw toepassing het WM_MENUCHAR bericht verwerken om door de eigenaar getekende menu's met menumnemonics te bieden.
Het WM_MENUCHAR bericht wordt verzonden wanneer de gebruiker een menumnemonic typt die niet overeenkomt met een van de vooraf gedefinieerde nemonics van het huidige menu. De waarde in wParam geeft het ASCII-teken op dat overeenkomt met de sleutel die de gebruiker heeft ingedrukt met de ALT-toets. Het woord met een lage volgorde van wParam geeft het type van het geselecteerde menu aan en kan een van de volgende waarden zijn:
- MF_POPUP als het huidige menu een submenu is.
- MF_SYSMENU als het menu het systeemmenu is.
Het hoogste orde woord van wParam bevat de menuhandle voor het huidige menu. Het venster met de door de eigenaar getekende menu's kan als volgt WM_MENUCHAR verwerken:
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);
De twee in het woord met hoge volgorde van de retourwaarde informeert het systeem dat het woord met lage volgorde van de retourwaarde de op nul gebaseerde index bevat van het menu-item dat moet worden geselecteerd.
De volgende constanten komen overeen met de mogelijke retourwaarden uit het WM_MENUCHAR bericht.
| Constante | Waarde | Betekenis |
|---|---|---|
| MNC_IGNORE | 0 | Het systeem moet het teken negeren dat de gebruiker heeft ingedrukt en een korte pieptoon op de systeemspreker maken. |
| MNC_CLOSE | 1 | Het systeem moet het actieve menu sluiten. |
| MNC_EXECUTE | 2 | Het systeem moet het item kiezen dat is opgegeven in het laag-orde woord van de terugkeerwaarde. Het eigenaarsvenster ontvangt een WM_COMMAND bericht. |
| MNC_SELECT | 3 | Het systeem moet het item selecteren dat is opgegeven in het laag-orde woord van de retourwaarde. |
Lettertypen instellen voor Menu-Item tekenreeksen
Dit onderwerp bevat een voorbeeld van een toepassing die gebruikmaakt van door de eigenaar getekende menu-items in een menu. Het menu bevat items die de kenmerken van het huidige lettertype instellen en de items worden weergegeven met het juiste lettertypekenmerk.
Hier ziet u hoe het menu wordt gedefinieerd in het resourcedefinitiebestand. Houd er rekening mee dat de tekenreeksen voor de menu-items Regular, Bold, Cursief en Onderstrepen tijdens runtime worden toegewezen, zodat de tekenreeksen leeg zijn in het resourcedefinitiebestand.
MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "", IDM_REGULAR
MENUITEM SEPARATOR
MENUITEM "", IDM_BOLD
MENUITEM "", IDM_ITALIC
MENUITEM "", IDM_ULINE
END
END
De vensterprocedure van de toepassing verwerkt de berichten die betrokken zijn bij het gebruik van door de eigenaar getekende menu-items. De toepassing gebruikt het WM_CREATE bericht om het volgende te doen:
- Stel de MF_OWNERDRAW vlag in voor de menu-items.
- Stel de tekenreeksen voor de menu-items in.
- Haal handvatten op van de lettertypen die worden gebruikt om de items te tekenen.
- Haal de tekst- en achtergrondkleurwaarden op voor geselecteerde menu-items.
De tekenreeksen en tekengrepen worden opgeslagen in een matrix met door de toepassing gedefinieerde MYITEM-structuren. De toepassingsgedefinieerde GetAFont-functie maakt een lettertype dat overeenkomt met het opgegeven lettertypekenmerk en retourneert een ingang naar het lettertype. De handles worden verwijderd tijdens de verwerking van het WM_DESTROY bericht.
Tijdens de verwerking van het WM_MEASUREITEM bericht verkrijgt het voorbeeld de breedte en hoogte van een tekenreeks voor een menu-item en kopieert deze waarden naar de MEASUREITEMSTRUCT structuur. Het systeem gebruikt de waarden voor breedte en hoogte om de grootte van het menu te berekenen.
Tijdens de verwerking van het WM_DRAWITEM bericht wordt de tekenreeks van het menu-item getekend met ruimte naast de tekenreeks voor de vinkjesbitmap. Als de gebruiker het item selecteert, worden de geselecteerde tekst- en achtergrondkleuren gebruikt om het item te tekenen.
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);
}
Voorbeeld van Owner-Drawn menu-items
In het voorbeeld in dit onderwerp worden door de eigenaar getekende menu-items in een menu gebruikt. De menu-items selecteren specifieke lettertypekenmerken en de toepassing geeft elke menu-item weer met behulp van een lettertype met het bijbehorende kenmerk. De menuopdracht Cursief wordt bijvoorbeeld weergegeven in een cursief lettertype. De menunaam met teken op de menubalk opent het menu.
De menubalk en de vervolgkeuzelijst worden in eerste instantie gedefinieerd door een uitgebreide resource voor menusjablonen. Omdat een menusjabloon geen items met eigen tekenstijl kan opgeven, bevat het menu in eerste instantie vier tekstmenu-items met de volgende tekenreeksen: 'Normaal', 'Vet', 'Cursief' en 'Onderstreept'. De vensterprocedure van de toepassing wijzigt deze in items met eigen tekenstijl wanneer het WM_CREATE bericht wordt verwerkt. Wanneer het bericht WM_CREATE wordt ontvangen, roept de vensterprocedure de toepassingsgedefinieerde OnCreate-functie aan, waarmee de volgende stappen worden uitgevoerd voor elk menu-item:
- Wijst een door de toepassing gedefinieerde MYITEM-structuur toe.
- Haalt de tekst van het menu-item op en slaat deze op in de door de toepassing gedefinieerde MYITEM-structuur.
- Hiermee maakt u het lettertype dat wordt gebruikt om het menu-item weer te geven en slaat u de greep op in de door de toepassing gedefinieerde MYITEM-structuur.
- Hiermee wijzigt u het menu-itemtype in MFT_OWNERDRAW en slaat u een aanwijzer op in de door de toepassing gedefinieerde MYITEM-structuur als itemgegevens.
Omdat een aanwijzer naar elke door de toepassing gedefinieerde MYITEM-structuur wordt opgeslagen als itemgegevens, wordt deze doorgegeven aan de vensterprocedure in combinatie met de WM_MEASUREITEM en WM_DRAWITEM berichten voor het bijbehorende menu-item. De aanwijzer zit in het itemData--veld binnen zowel de MEASUREITEMSTRUCT als de DRAWITEMSTRUCT structuren.
Er wordt een WM_MEASUREITEM bericht verzonden voor elke door de eigenaar getekend menu-item wanneer het voor het eerst wordt weergegeven. De toepassing verwerkt dit bericht door het lettertype voor het menu-item in een apparaatcontext te selecteren en vervolgens de ruimte te bepalen die nodig is om de tekst van het menu-item in dat lettertype weer te geven. Het lettertype en de tekst van het menu-item worden beide opgegeven door de MYITEM structuur van het menu-item (de structuur die door de toepassing is gedefinieerd). De toepassing bepaalt de grootte van de tekst met behulp van de functie GetTextExtentPoint32.
De vensterprocedure verwerkt het WM_DRAWITEM bericht door de tekst van het menu-item in het juiste lettertype weer te geven. Het lettertype en de tekst van het menu-item worden beide opgegeven door de MYITEM structuur van het menu-item. De toepassing selecteert tekst- en achtergrondkleuren die geschikt zijn voor de status van het menu-item.
De vensterprocedure verwerkt het WM_DESTROY bericht om lettertypen en geheugen te vernietigen. In de toepassing wordt het lettertype verwijderd en wordt de door de toepassing gedefinieerde MYITEM-structuur voor elk menu-item vrijgemaakt.
Hier volgen de relevante gedeelten van het headerbestand van de toepassing.
// 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
Hier volgen de relevante gedeelten van de vensterprocedure van de toepassing en de bijbehorende functies.
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);
}
Aangepaste bitmaps voor controlemarkeringen gebruiken
Het systeem biedt een standaard bitmap met vinkje voor weergave naast een menu-item dat is geselecteerd. U kunt een afzonderlijk menu-item aanpassen door een paar bitmaps op te geven om de standaard vinkje-afbeelding te vervangen. In het systeem wordt één bitmap weergegeven wanneer het item is geselecteerd en de andere wanneer het wordt gewist. In deze sectie worden de stappen beschreven voor het maken en gebruiken van aangepaste bitmaps voor vinkjes.
- Aangepaste vinkafbeeldingen maken
- Bitmaps koppelen aan een menu-item
- het kenmerk Vinkje instellen
- selectievakjes simuleren in een menu
- Voorbeeld van het gebruik van aangepaste vinkjes-bitmaps
Aangepaste bitmaps voor vinkjes maken
Een aangepaste bitmap met vinkje moet dezelfde grootte hebben als de standaard bitmap voor vinkje. U kunt de standaardgrootte van het vinkje van de bitmap ophalen door de functie GetSystemMetrics aan te roepen. Het woord met lage volgorde van de retourwaarde van deze functie geeft de breedte aan; het woord met hoge volgorde geeft de hoogte aan.
U kunt bitmapbronnen gebruiken om bitmaps met vinkjes te voorzien. Omdat de vereiste bitmapgrootte echter afhankelijk is van het weergavetype, moet u mogelijk het formaat van de bitmap tijdens runtime wijzigen met behulp van de functie StretchBlt. Afhankelijk van de bitmap kan de vervorming die wordt veroorzaakt door de grootte, onacceptabele resultaten opleveren.
In plaats van een bitmapresource te gebruiken, kunt u tijdens runtime een bitmap maken met behulp van GDI-functies.
Een bitmap maken tijdens runtime
Gebruik de functie CreateCompatibleDC om een apparaatcontext te maken die compatibel is met de context die door het hoofdvenster van de toepassing wordt gebruikt.
De parameter hdc van de functie kan NULL- of de retourwaarde van de functie opgeven. CreateCompatibleDC retourneert een handle naar de compatibele apparaatcontext.
Gebruik de functie CreateCompatibleBitmap om een bitmap te maken die compatibel is met het hoofdvenster van de toepassing.
De nWidth en nHeight parameters stellen de grootte van de bitmap in; ze moeten de breedte- en hoogtegegevens opgeven die worden geretourneerd door de functie GetSystemMetrics.
Notitie
U kunt ook de functie CreateBitmap gebruiken om een monochrome bitmap te maken.
Gebruik de functie SelectObject om de bitmap te selecteren in de context van het compatibele apparaat.
Gebruik GDI-tekenfuncties, zoals Ellipse en LineTo, om een afbeelding in de bitmap te tekenen of gebruik functies zoals BitBlt- en StretchBlt- om een afbeelding naar de bitmap te kopiëren.
Zie Bitmapsvoor meer informatie.
Bitmaps koppelen aan een menu-item
U koppelt een paar bitmaps met vinkjes aan een menu-item door de grepen van de bitmaps door te geven aan de SetMenuItemBitmaps functie. De parameter hBitmapUnchecked identificeert de duidelijke bitmap en de parameter hBitmapChecked identificeert de geselecteerde bitmap. Als u een of beide vinkjes uit een menu-item wilt verwijderen, kunt u de hBitmapUnchecked of hBitmapChecked parameter of beide instellen op NULL-.
Het kenmerk Vinkje instellen
Met de functie CheckMenuItem wordt het vinkje van een menu-item ingesteld op geselecteerd of uitgeschakeld. U kunt de MF_CHECKED waarde opgeven om de vinkje-eigenschap in te stellen op geselecteerd en de MF_UNCHECKED waarde om deze in te stellen op ongedaan maken.
U kunt ook de controlestatus van een menu-item instellen met behulp van de functie SetMenuItemInfo.
Soms vertegenwoordigt een groep menu-items een reeks wederzijds exclusieve opties. Met behulp van de functie CheckMenuRadioItem kunt u één menu-item controleren en tegelijkertijd het vinkje uit alle andere menu-items in de groep verwijderen.
Selectievakjes simuleren in een menu
Dit onderwerp bevat een voorbeeld waarin wordt getoond hoe u selectievakjes in een menu simuleert. Het voorbeeld bevat een Tekenmenu waarvan de items de gebruiker in staat stellen om de vetgedrukte, cursieve en onderstreepte attributen van het huidige lettertype in te stellen. Wanneer een lettertypekenmerk van kracht is, wordt er een vinkje weergegeven in het selectievakje naast het bijbehorende menu-item; anders wordt er een leeg selectievakje naast het item weergegeven.
In het voorbeeld wordt de standaard vink bitmap vervangen door twee bitmaps: een bitmap met een geselecteerd selectievakje en een bitmap met een leeg selectievakje. De geselecteerde bitmap van het selectievakje wordt weergegeven naast het menu-item Vet, Cursief of Onderstrepen wanneer het vinkje van het item is ingesteld op MF_CHECKED. De bitmap van het lege of onbevinkte selectievakje wordt getoond wanneer de vinkje-eigenschap is ingesteld op MF_UNCHECKED.
Het systeem biedt een vooraf gedefinieerde bitmap die afbeeldingen bevat die gebruikt worden voor selectievakjes en radioknoppen. Het voorbeeld isoleert de geselecteerde en lege selectievakjes, kopieert ze naar twee afzonderlijke bitmaps en gebruikt ze vervolgens als de geselecteerde en gewiste bitmaps voor items in het menu Teken.
Om een handle op te halen naar de door het systeem gedefinieerde selectievakbitmap, roept het voorbeeld de functie LoadBitmap aan, waarbij NULL wordt opgegeven als de hInstance-parameter en OBM_CHECKBOXES als de lpBitmapName-parameter. Omdat de afbeeldingen in de bitmap allemaal dezelfde grootte hebben, kan het voorbeeld deze isoleren door de bitmapbreedte en hoogte te delen door het aantal afbeeldingen in de rijen en kolommen ervan te verdelen.
In het volgende gedeelte van een resourcedefinitiebestand ziet u hoe de menu-items in het menu Teken zijn gedefinieerd. Houd er rekening mee dat in eerste instantie geen lettertypekenmerken van kracht zijn, dus het vinkje voor het Normaal item is ingesteld op geselecteerd en standaard is het vinkje van de resterende items ingesteld op wissen.
#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
Hier volgt de relevante inhoud van het headerbestand van de toepassing.
// 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);
In het volgende voorbeeld ziet u de gedeelten van de vensterprocedure waarmee de bitmaps van het selectievinkje worden gemaakt; het selectievinkje van de Vet, Cursiefen Onderstrepen menu-items instellen; en de bitmaps van het selectievinkje vernietigen.
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;
}
}
Voorbeeld van het gebruik van aangepaste aanvinktekens bitmaps
In het voorbeeld in dit onderwerp worden aangepaste bitmaps voor vinkjes toegewezen aan menu-items in twee menu's. In de menu-items in het eerste menu worden tekenkenmerken opgegeven: vet, cursief en onderstrepen. Elk menu-item kan worden geselecteerd of gewist. Voor deze menu-items gebruikt het voorbeeld bitmapafbeeldingen van vinkjes die lijken op de geselecteerde en niet-geselecteerde statussen van een selectievakje.
De menu-items in het tweede menu geven instellingen voor alinea-uitlijning op: links, gecentreerd en rechts. Slechts één van deze menu-items is op elk gewenst moment geselecteerd. Voor deze menu-items gebruikt het voorbeeld bitmaps met vinkjes die lijken op de geselecteerde en gewiste staten van een radioknop.
De vensterprocedure verwerkt het WM_CREATE bericht door de door de toepassing gedefinieerde functie OnCreate aan te roepen.
OnCreate maakt de vier vinkjes bitmaps en wijst deze vervolgens toe aan de juiste menu-items met behulp van de SetMenuItemBitmaps functie.
Als u elke bitmap wilt maken, roept OnCreate de toepassingsgedefinieerde functie CreateMenuBitmaps aan, waarbij een aanwijzer naar een bitmapspecifieke tekenfunctie wordt opgegeven. Met CreateMenuBitmaps maakt u een monochrome bitmap van de vereiste grootte, selecteert hij in een geheugencontext en wist de achtergrond. Vervolgens wordt de opgegeven tekenfunctie aangeroepen om de voorgrond in te vullen.
De vier toepassingsgedefinieerde tekenfuncties zijn DrawCheck, DrawUncheck, DrawRadioChecken DrawRadioUncheck. Ze tekenen een rechthoek met een X, een lege rechthoek, een ellips met een kleinere gevulde ellips en een lege ellips.
De vensterprocedure verwerkt het WM_DESTROY bericht door de bitmaps van het vinkje te verwijderen. Hiermee wordt elk bitmaphandle opgehaald met behulp van de functie GetMenuItemInfo en vervolgens wordt een handle doorgegeven aan de functie.
Wanneer de gebruiker een menu-item kiest, wordt er een WM_COMMAND bericht verzonden naar het venster eigenaar. Voor menu-items in het menu Character roept de vensterprocedure de functie CheckCharacterItem aan. Voor items in het menu Alinea roept de vensterprocedure de door de toepassing gedefinieerde functie CheckParagraphItem aan.
Elk item in het menu Teken kan onafhankelijk worden geselecteerd en gewist. Daarom schakelt CheckCharacterItem simpelweg de controlestatus van het opgegeven menu-item in. Eerst roept de functie de GetMenuItemInfo- functie aan om de status van het huidige menu-item op te halen. Vervolgens wordt de MFS_CHECKED statusvlag gewijzigd en wordt de nieuwe status ingesteld door de functie SetMenuItemInfo aan te roepen.
In tegenstelling tot tekenkenmerken kan slechts één alinea-uitlijning tegelijk worden geselecteerd. Daarom vinkt CheckParagraphItem het opgegeven menu-item aan en verwijdert het het vinkje van alle andere items in het menu. Hiervoor wordt de functie CheckMenuRadioItem aangeroepen.
Hieronder vindt u de relevante gedeelten van het headerbestand van de toepassing.
// 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);
Hier volgen de relevante gedeelten van de vensterprocedure van de toepassing en gerelateerde functies.
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);
}