메뉴 사용
이 섹션에서는 다음 작업을 설명합니다.
Menu-Template 리소스 사용
일반적으로 메뉴 템플릿 리소스를 만든 다음 런타임에 메뉴를 로드하여 애플리케이션에 메뉴를 포함합니다. 이 섹션에서는 메뉴 템플릿의 형식을 설명하고 메뉴 템플릿 리소스를 로드하고 애플리케이션에서 사용하는 방법을 설명합니다. 메뉴 템플릿 리소스를 만드는 방법에 대한 자세한 내용은 개발 도구에 포함된 설명서를 참조하세요.
확장 Menu-Template 형식
확장 메뉴 템플릿 형식은 추가 메뉴 기능을 지원합니다. 표준 메뉴 템플릿 리소스와 마찬가지로 확장 메뉴 템플릿 리소스에는 RT_MENU 리소스 종류가 있습니다. 시스템은 두 리소스 형식을 리소스 헤더의 첫 번째 멤버인 버전 번호로 구분합니다.
확장 메뉴 템플릿은 MENUEX_TEMPLATE_HEADER 구조와 하나 이상의 MENUEX_TEMPLATE_ITEM 항목 정의 구조로 구성됩니다.
이전 Menu-Template 형식
이전 메뉴 템플릿(Microsoft Windows NT 3.51 이하)은 메뉴를 정의하지만 새 메뉴 기능을 지원하지는 않습니다. 이전 메뉴 템플릿 리소스에는 RT_MENU 리소스 종류가 있습니다.
이전 메뉴 템플릿은 MENUITEMTEMPLATEHEADER 구조와 하나 이상의 MENUITEMTEMPLATE 구조로 구성됩니다.
Menu-Template 리소스 로드
메뉴 템플릿 리소스를 로드하려면 LoadMenu 함수를 사용하여 리소스와 메뉴 템플릿의 식별자가 포함된 모듈에 대한 핸들을 지정합니다. LoadMenu 함수는 창을 할당하는 데 사용할 수 있는 메뉴 핸들을 반환합니다. 이 창은 메뉴에서 생성된 모든 메시지를 수신하는 메뉴의 소유자 창이 됩니다.
이미 메모리에 있는 메뉴 템플릿에서 메뉴를 만들려면 LoadMenuIndirect 함수를 사용합니다. 이는 애플리케이션이 메뉴 템플릿을 동적으로 생성하는 경우에 유용합니다.
창에 메뉴를 할당하려면 SetMenu 함수를 사용하거나 창을 만들 때 CreateWindowEx 함수의 hMenu 매개 변수에서 메뉴 핸들을 지정합니다. 창에 메뉴를 할당할 수 있는 또 다른 방법은 창 클래스를 등록할 때 메뉴 템플릿을 지정하는 것입니다. 템플릿은 지정된 메뉴를 해당 창 클래스의 클래스 메뉴로 식별합니다.
시스템에서 창에 특정 메뉴를 자동으로 할당하도록 하려면 창의 클래스를 등록할 때 메뉴의 템플릿을 지정합니다. 템플릿은 지정된 메뉴를 해당 창 클래스의 클래스 메뉴로 식별합니다. 그런 다음 지정된 클래스의 창을 만들면 시스템에서 지정된 메뉴를 창에 자동으로 할당합니다.
자식 창인 창에는 메뉴를 할당할 수 없습니다.
클래스 메뉴를 만들려면 메뉴 템플릿 리소스의 식별자를 WNDCLASS 구조체의 lpszMenuName 멤버로 포함하고 구조체에 대한 포인터를 RegisterClass 함수에 전달합니다.
클래스 메뉴 만들기
다음 예에서는 애플리케이션에 대한 클래스 메뉴를 만들고, 클래스 메뉴를 사용하는 창을 만들고, 창 프로시저에서 메뉴 명령을 처리하는 방법을 보여 줍니다.
다음은 애플리케이션 헤더 파일의 관련 부분입니다.
// Menu-template resource identifier
#define IDM_MYMENURESOURCE 3
다음은 애플리케이션 자체의 관련 부분입니다.
HINSTANCE hinst;
int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { }; // message
WNDCLASS wc; // windowclass data
HWND hwnd; // handle to the main window
// Create the window class for the main window. Specify
// the identifier of the menu-template resource as the
// lpszMenuName member of the WNDCLASS structure to create
// the class menu.
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = MAKEINTRESOURCE(IDM_MYMENURESOURCE);
wc.lpszClassName = "MainWClass";
if (!RegisterClass(&wc))
return FALSE;
hinst = hinstance;
// Create the main window. Set the hmenu parameter to NULL so
// that the system uses the class menu for the window.
hwnd = CreateWindow("MainWClass", "Sample Application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,
NULL);
if (hwnd == NULL)
return FALSE;
// Make the window visible and send a WM_PAINT message to the
// window procedure.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Start the main message loop.
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(hPrevInstance);
}
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// Process other window messages.
case WM_COMMAND:
// Test for the identifier of a command item.
switch(LOWORD(wParam))
{
case IDM_FI_OPEN:
DoFileOpen(); // application-defined
break;
case IDM_FI_CLOSE:
DoFileClose(); // application-defined
break;
// Process other menu commands.
default:
break;
}
return 0;
// Process other window messages.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
바로 가기 메뉴 만들기
애플리케이션에서 바로 가기 메뉴를 사용하려면 해당 핸들을 TrackPopupMenuEx 함수에 전달합니다. 애플리케이션은 일반적으로 WM_LBUTTONDOWN 또는 WM_KEYDOWN 같은 사용자 생성 메시지에 대한 응답으로 창 프로시저에서 TrackPopupMenuEx를 호출합니다.
팝업 메뉴 핸들 외에도 TrackPopupMenuEx 를 사용하려면 소유자 창에 대한 핸들, 바로 가기 메뉴의 위치(화면 좌표) 및 사용자가 항목을 선택하는 데 사용할 수 있는 마우스 단추를 지정해야 합니다.
이전 TrackPopupMenu 함수는 여전히 지원되지만 새 애플리케이션은 TrackPopupMenuEx 함수를 사용해야 합니다. TrackPopupMenuEx 함수에는 TrackPopupMenu와 동일한 매개 변수가 필요하지만 메뉴가 가려지지 않아야 하는 화면의 일부를 지정할 수도 있습니다. 애플리케이션은 일반적으로 WM_CONTEXTMENU 메시지를 처리할 때 창 프로시저에서 이러한 함수를 호출합니다.
TPM_CENTERALIGN, TPM_LEFTALIGN 또는 TPM_RIGHTALIGN 플래그와 함께 x 및 y 좌표를 제공하여 바로 가기 메뉴의 위치를 지정할 수 있습니다. 플래그는 x 및 y 좌표를 기준으로 바로 가기 메뉴의 위치를 지정합니다.
사용자가 메뉴를 표시하는 데 사용되는 것과 동일한 마우스 단추를 사용하여 바로 가기 메뉴에서 항목을 선택할 수 있도록 허용해야 합니다. 이렇게 하려면 TPM_LEFTBUTTON 또는 TPM_RIGHTBUTTON 플래그를 지정하여 사용자가 메뉴 항목을 선택하는 데 사용할 수 있는 마우스 단추를 나타냅니다.
WM_CONTEXTMENU 메시지 처리
애플리케이션의 창 프로시저가 WM_RBUTTONUP 또는 WM_NCRBUTTONUP 메시지를 DefWindowProc 함수에 전달하면 WM_CONTEXTMENU 메시지가 생성됩니다. 애플리케이션은 이 메시지를 처리하여 화면의 특정 부분에 적합한 바로 가기 메뉴를 표시할 수 있습니다. 애플리케이션에 바로 가기 메뉴가 표시되지 않으면 기본 처리를 위해 메시지를 DefWindowProc 에 전달해야 합니다.
다음은 애플리케이션의 창 프로시저에 나타날 수 있는 WM_CONTEXTMENU 메시지 처리의 예입니다. lParam 매개 변수의 하위 순서 및 상위 단어는 마우스 오른쪽 단추가 놓일 때 마우스의 화면 좌표를 지정합니다(이러한 좌표는 여러 모니터가 있는 시스템에서 음수 값을 사용할 수 있음). 애플리케이션 정의 OnContextMenu 함수는 상황에 맞는 메뉴를 표시하면 TRUE 를 반환하고, 그렇지 않으면 FALSE 를 반환합니다.
case WM_CONTEXTMENU:
if (!OnContextMenu(hwnd, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)))
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
다음 애플리케이션 정의 OnContextMenu 함수는 지정된 마우스 위치가 창의 클라이언트 영역 내에 있는 경우 바로 가기 메뉴를 표시합니다. 보다 정교한 함수는 클라이언트 영역의 어느 부분을 지정했는지에 따라 여러 메뉴 중 하나를 표시할 수 있습니다. 실제로 바로 가기 메뉴를 표시하기 위해 이 예제에서는 DisplayContextMenu라는 애플리케이션 정의 함수를 호출합니다. 이 함수에 대한 설명은 바로 가기 메뉴 표시를 참조하세요.
BOOL WINAPI OnContextMenu(HWND hwnd, int x, int y)
{
RECT rc; // client area of window
POINT pt = { x, y }; // location of mouse click
// Get the bounding rectangle of the client area.
GetClientRect(hwnd, &rc);
// Convert the mouse position to client coordinates.
ScreenToClient(hwnd, &pt);
// If the position is in the client area, display a
// shortcut menu.
if (PtInRect(&rc, pt))
{
ClientToScreen(hwnd, &pt);
DisplayContextMenu(hwnd, pt);
return TRUE;
}
// Return FALSE if no menu is displayed.
return FALSE;
}
바로 가기 Font-Attributes 메뉴 만들기
이 섹션의 예제에는 사용자가 글꼴 및 글꼴 특성을 설정할 수 있는 바로 가기 메뉴를 만들고 표시하는 애플리케이션의 코드 부분이 포함되어 있습니다. 애플리케이션은 사용자가 마우스 왼쪽 단추를 클릭할 때마다 기본 창의 클라이언트 영역에 메뉴를 표시합니다.
다음은 애플리케이션의 리소스 정의 파일에 제공되는 바로 가기 메뉴에 대한 메뉴 템플릿입니다.
PopupMenu MENU
BEGIN
POPUP "Dummy Popup"
BEGIN
POPUP "Fonts"
BEGIN
MENUITEM "Courier", IDM_FONT_COURIER
MENUITEM "Times Roman", IDM_FONT_TMSRMN
MENUITEM "Swiss", IDM_FONT_SWISS
MENUITEM "Helvetica", IDM_FONT_HELV
MENUITEM "Old English", IDM_FONT_OLDENG
END
POPUP "Sizes"
BEGIN
MENUITEM "7", IDM_SIZE_7
MENUITEM "8", IDM_SIZE_8
MENUITEM "9", IDM_SIZE_9
MENUITEM "10", IDM_SIZE_10
MENUITEM "11", IDM_SIZE_11
MENUITEM "12", IDM_SIZE_12
MENUITEM "14", IDM_SIZE_14
END
POPUP "Styles"
BEGIN
MENUITEM "Bold", IDM_STYLE_BOLD
MENUITEM "Italic", IDM_STYLE_ITALIC
MENUITEM "Strike Out", IDM_STYLE_SO
MENUITEM "Superscript", IDM_STYLE_SUPER
MENUITEM "Subscript", IDM_STYLE_SUB
END
END
END
다음 예제에서는 바로 가기 메뉴를 만들고 표시하는 데 사용되는 창 프로시저 및 지원 함수를 제공합니다.
LRESULT APIENTRY MenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rc; // client area
POINT pt; // location of mouse click
switch (uMsg)
{
case WM_LBUTTONDOWN:
// Get the bounding rectangle of the client area.
GetClientRect(hwnd, (LPRECT) &rc);
// Get the client coordinates for the mouse click.
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
// If the mouse click took place inside the client
// area, execute the application-defined function
// that displays the shortcut menu.
if (PtInRect((LPRECT) &rc, pt))
HandlePopupMenu(hwnd, pt);
break;
// Process other window messages.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
VOID APIENTRY HandlePopupMenu(HWND hwnd, POINT pt)
{
HMENU hmenu; // menu template
HMENU hmenuTrackPopup; // shortcut menu
// Load the menu template containing the shortcut menu from the
// application's resources.
hmenu = LoadMenu(hinst, "PopupMenu");
if (hmenu == NULL)
return;
// Get the first shortcut menu in the menu template. This is the
// menu that TrackPopupMenu displays.
hmenuTrackPopup = GetSubMenu(hmenu, 0);
// TrackPopup uses screen coordinates, so convert the
// coordinates of the mouse click to screen coordinates.
ClientToScreen(hwnd, (LPPOINT) &pt);
// Draw and track the shortcut menu.
TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);
// Destroy the menu.
DestroyMenu(hmenu);
}
바로 가기 메뉴 표시
다음 예제에 표시된 함수는 바로 가기 메뉴를 표시합니다.
애플리케이션에는 "ShortcutExample" 문자열로 식별되는 메뉴 리소스가 포함되어 있습니다. 메뉴 모음에는 메뉴 이름이 포함되어 있습니다. 애플리케이션은 TrackPopupMenu 함수를 사용하여 이 메뉴 항목과 연결된 메뉴를 표시합니다. TrackPopupMenu에는 메뉴, 하위 메뉴 또는 바로 가기 메뉴에 대한 핸들이 필요하므로 메뉴 모음 자체가 표시되지 않습니다.
VOID APIENTRY DisplayContextMenu(HWND hwnd, POINT pt)
{
HMENU hmenu; // top-level menu
HMENU hmenuTrackPopup; // shortcut menu
// Load the menu resource.
if ((hmenu = LoadMenu(hinst, "ShortcutExample")) == NULL)
return;
// TrackPopupMenu cannot display the menu bar so get
// a handle to the first shortcut menu.
hmenuTrackPopup = GetSubMenu(hmenu, 0);
// Display the shortcut menu. Track the right mouse
// button.
TrackPopupMenu(hmenuTrackPopup,
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);
// Destroy the menu.
DestroyMenu(hmenu);
}
Menu-Item 비트맵 사용
시스템은 텍스트 문자열 대신 비트맵을 사용하여 메뉴 항목을 표시할 수 있습니다. 비트맵을 사용하려면 메뉴 항목에 대한 MIIM_BITMAP 플래그를 설정하고 MENUITEMINFO 구조체의 hbmpItem 멤버에 있는 메뉴 항목에 대해 시스템이 표시해야 하는 비트맵에 대한 핸들을 지정해야 합니다. 이 섹션에서는 메뉴 항목에 비트맵을 사용하는 방법을 설명합니다.
비트맵 형식 플래그 설정
MIIM_BITMAP 또는 MF_BITMAP 플래그는 시스템에 텍스트 문자열이 아닌 비트맵을 사용하여 메뉴 항목을 표시하도록 지시합니다. 메뉴 항목의 MIIM_BITMAP 또는 MF_BITMAP 플래그는 런타임에 설정해야 합니다. 리소스 정의 파일에서 설정할 수 없습니다.
새 애플리케이션의 경우 SetMenuItemInfo 또는 InsertMenuItem 함수를 사용하여 MIIM_BITMAP 형식 플래그를 설정할 수 있습니다. 메뉴 항목을 텍스트 항목에서 비트맵 항목으로 변경하려면 SetMenuItemInfo를 사용합니다. 메뉴에 새 비트맵 항목을 추가하려면 InsertMenuItem 함수를 사용합니다.
이전 버전의 시스템에 대해 작성된 애플리케이션은 ModifyMenu, InsertMenu 또는 AppendMenu 함수를 계속 사용하여 MF_BITMAP 플래그를 설정할 수 있습니다. 메뉴 항목을 텍스트 문자열 항목에서 비트맵 항목으로 변경하려면 ModifyMenu를 사용합니다. 메뉴에 새 비트맵 항목을 추가하려면 insertMenu 또는 AppendMenu 함수와 함께 MF_BITMAP 플래그를 사용합니다.
비트맵 만들기
메뉴 항목에 대한 MIIM_BITMAP 또는 MF_BITMAP 형식 플래그를 설정하는 경우 시스템이 메뉴 항목에 대해 표시해야 하는 비트맵에 대한 핸들도 지정해야 합니다. 비트맵을 비트맵 리소스로 제공하거나 런타임에 비트맵을 만들 수 있습니다. 비트맵 리소스를 사용하는 경우 LoadBitmap 함수를 사용하여 비트맵을 로드하고 해당 핸들을 가져올 수 있습니다.
런타임에 비트맵을 만들려면 Windows GDI(그래픽 디바이스 인터페이스) 함수를 사용합니다. GDI는 런타임에 비트맵을 만드는 여러 가지 방법을 제공하지만 개발자는 일반적으로 다음 방법을 사용합니다.
- CreateCompatibleDC 함수를 사용하여 애플리케이션의 기본 창에서 사용하는 디바이스 컨텍스트와 호환되는 디바이스 컨텍스트를 만듭니다.
- CreateCompatibleBitmap 함수를 사용하여 애플리케이션의 기본 창과 호환되는 비트맵을 만들거나 CreateBitmap 함수를 사용하여 흑백 비트맵을 만듭니다.
- SelectObject 함수를 사용하여 호환되는 디바이스 컨텍스트로 비트맵을 선택합니다.
- Ellipse 및 LineTo와 같은 GDI 그리기 함수를 사용하여 이미지를 비트맵에 그립니다.
자세한 내용은 비트맵을 참조하세요.
메뉴에 선 및 그래프 추가
다음 코드 샘플에서는 메뉴 항목 비트맵이 포함된 메뉴를 만드는 방법을 보여 줍니다. 두 개의 메뉴를 만듭니다. 첫 번째는 원형 차트, 꺾은선형 차트 및 가로 막대형 차트의 세 가지 메뉴 항목 비트맵이 포함된 차트 메뉴입니다. 이 예제에서는 애플리케이션의 리소스 파일에서 이러한 비트맵을 로드한 다음 CreatePopupMenu 및 AppendMenu 함수를 사용하여 메뉴 및 메뉴 항목을 만드는 방법을 보여 줍니다.
두 번째 메뉴는 선 메뉴입니다. 시스템에서 미리 정의된 펜에서 제공하는 선 스타일을 보여 주는 비트맵이 포함되어 있습니다. 선 스타일 비트맵은 GDI 함수를 사용하여 런타임에 만들어집니다.
다음은 애플리케이션의 리소스 정의 파일에 있는 비트맵 리소스의 정의입니다.
PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp
다음은 애플리케이션 헤더 파일의 관련 부분입니다.
// Menu-item identifiers
#define IDM_SOLID PS_SOLID
#define IDM_DASH PS_DASH
#define IDM_DASHDOT PS_DASHDOT
#define IDM_DASHDOTDOT PS_DASHDOTDOT
#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3
// Line-type flags
#define SOLID 0
#define DOT 1
#define DASH 2
#define DASHDOT 3
#define DASHDOTDOT 4
// Count of pens
#define CPENS 5
// Chart-type flags
#define PIE 1
#define LINE 2
#define BAR 3
// Function prototypes
LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
VOID MakeChartMenu(HWND);
VOID MakeLineMenu(HWND, HPEN, HBITMAP);
다음 예제에서는 애플리케이션에서 메뉴 및 메뉴 항목 비트맵을 만드는 방법을 보여 줍니다.
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HPEN hpen[CPENS];
static HBITMAP hbmp[CPENS];
int i;
switch (uMsg)
{
case WM_CREATE:
// Create the Chart and Line menus.
MakeChartMenu(hwnd);
MakeLineMenu(hwnd, hpen, hbmp);
return 0;
// Process other window messages.
case WM_DESTROY:
for (i = 0; i < CPENS; i++)
{
DeleteObject(hbmp[i]);
DeleteObject(hpen[i]);
}
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
VOID MakeChartMenu(HWND hwnd)
{
HBITMAP hbmpPie; // handle to pie chart bitmap
HBITMAP hbmpLine; // handle to line chart bitmap
HBITMAP hbmpBar; // handle to bar chart bitmap
HMENU hmenuMain; // handle to main menu
HMENU hmenuChart; // handle to Chart menu
// Load the pie, line, and bar chart bitmaps from the
// resource-definition file.
hbmpPie = LoadBitmap(hinst, MAKEINTRESOURCE(PIE));
hbmpLine = LoadBitmap(hinst, MAKEINTRESOURCE(LINE));
hbmpBar = LoadBitmap(hinst, MAKEINTRESOURCE(BAR));
// Create the Chart menu and add it to the menu bar.
// Append the Pie, Line, and Bar menu items to the Chart
// menu.
hmenuMain = GetMenu(hwnd);
hmenuChart = CreatePopupMenu();
AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (UINT) hmenuChart,
"Chart");
AppendMenu(hmenuChart, MF_BITMAP, IDM_PIE, (LPCTSTR) hbmpPie);
AppendMenu(hmenuChart, MF_BITMAP, IDM_LINE,
(LPCTSTR) hbmpLine);
AppendMenu(hmenuChart, MF_BITMAP, IDM_BAR, (LPCTSTR) hbmpBar);
return;
}
VOID MakeLineMenu(HWND hwnd, HPEN phpen, HBITMAP phbmp)
{
HMENU hmenuLines; // handle to Lines menu
HMENU hmenu; // handle to main menu
COLORREF crMenuClr; // menu-item background color
HBRUSH hbrBackground; // handle to background brush
HBRUSH hbrOld; // handle to previous brush
WORD wLineX; // width of line bitmaps
WORD wLineY; // height of line bitmaps
HDC hdcMain; // handle to main window's DC
HDC hdcLines; // handle to compatible DC
HBITMAP hbmpOld; // handle to previous bitmap
int i; // loop counter
// Create the Lines menu. Add it to the menu bar.
hmenu = GetMenu(hwnd);
hmenuLines = CreatePopupMenu();
AppendMenu(hmenu, MF_STRING | MF_POPUP,
(UINT) hmenuLines, "&Lines");
// Create a brush for the menu-item background color.
crMenuClr = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crMenuClr);
// Create a compatible device context for the line bitmaps,
// and then select the background brush into it.
hdcMain = GetDC(hwnd);
hdcLines = CreateCompatibleDC(hdcMain);
hbrOld = SelectObject(hdcLines, hbrBackground);
// Get the dimensions of the check-mark bitmap. The width of
// the line bitmaps will be five times the width of the
// check-mark bitmap.
wLineX = GetSystemMetrics(SM_CXMENUCHECK) * (WORD) 5;
wLineY = GetSystemMetrics(SM_CYMENUCHECK);
// Create the bitmaps and select them, one at a time, into the
// compatible device context. Initialize each bitmap by
// filling it with the menu-item background color.
for (i = 0; i < CPENS; i++)
{
phbmp[i] = CreateCompatibleBitmap(hdcMain, wLineX, wLineY);
if (i == 0)
hbmpOld = SelectObject(hdcLines, phbmp[i]);
else
SelectObject(hdcLines, phbmp[i]);
ExtFloodFill(hdcLines, 0, 0, crMenuClr, FLOODFILLBORDER);
}
// Create the pens.
phpen[0] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
phpen[1] = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
phpen[2] = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
phpen[3] = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
phpen[4] = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));
// Select a pen and a bitmap into the compatible device
// context, draw a line into the bitmap, and then append
// the bitmap as an item in the Lines menu.
for (i = 0; i < CPENS; i++)
{
SelectObject(hdcLines, phbmp[i]);
SelectObject(hdcLines, phpen[i]);
MoveToEx(hdcLines, 0, wLineY / 2, NULL);
LineTo(hdcLines, wLineX, wLineY / 2);
AppendMenu(hmenuLines, MF_BITMAP, i + 1,
(LPCTSTR) phbmp[i]);
}
// Release the main window's device context and destroy the
// compatible device context. Also, destroy the background
// brush.
ReleaseDC(hwnd, hdcMain);
SelectObject(hdcLines, hbrOld);
DeleteObject(hbrBackground);
SelectObject(hdcLines, hbmpOld);
DeleteDC(hdcLines);
return;
}
Menu-Item 비트맵의 예
이 항목의 예제에서는 각각 여러 비트맵 메뉴 항목을 포함하는 두 개의 메뉴를 만듭니다. 각 메뉴에 대해 애플리케이션은 해당 메뉴 이름을 기본 창의 메뉴 모음에 추가합니다.
첫 번째 메뉴에는 원형, 선 및 가로 막대형의 세 가지 차트 유형을 각각 보여 주는 메뉴 항목이 포함되어 있습니다. 이러한 메뉴 항목의 비트맵은 리소스로 정의되며 LoadBitmap 함수를 사용하여 로드됩니다. 이 메뉴와 연결된 메뉴 모음의 "차트" 메뉴 이름입니다.
두 번째 메뉴에는 CreatePen 함수에 사용되는 5가지 줄 스타일( PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT 및 PS_DASHDOTDOT 각각을 보여 주는 메뉴 항목 이 포함되어 있습니다. 애플리케이션은 GDI 그리기 함수를 사용하여 런타임에 이러한 메뉴 항목에 대한 비트맵을 만듭니다. 이 메뉴와 연결된 메뉴 모음의 줄 메뉴 이름입니다.
애플리케이션의 창 프로시저에 정의된 비트맵 핸들의 두 정적 배열입니다. 하나의 배열에는 차트 메뉴에 사용되는 세 개의 비트맵의 핸들이 포함되어 있습니다. 다른 하나는 줄 메뉴에 사용되는 5개의 비트맵의 핸들을 포함합니다. WM_CREATE 메시지를 처리할 때 창 프로시저는 차트 비트맵을 로드하고 줄 비트맵을 만든 다음 해당 메뉴 항목을 추가합니다. WM_DESTROY 메시지를 처리할 때 창 프로시저는 모든 비트맵을 삭제합니다.
다음은 애플리케이션 헤더 파일의 관련 부분입니다.
// Menu-item identifiers
#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3
#define IDM_SOLID 4
#define IDM_DASH 5
#define IDM_DASHDOT 6
#define IDM_DASHDOTDOT 7
// Number of items on the Chart and Lines menus
#define C_LINES 5
#define C_CHARTS 3
// Bitmap resource identifiers
#define IDB_PIE 1
#define IDB_LINE 2
#define IDB_BAR 3
// Dimensions of the line bitmaps
#define CX_LINEBMP 40
#define CY_LINEBMP 10
다음은 창 프로시저의 관련 부분입니다. 창 프로시저는 이 항목의 뒷부분에 설명된 애플리케이션 정의 LoadChartBitmaps, CreateLineBitmaps 및 AddBitmapMenu 함수를 호출하여 대부분의 초기화를 수행합니다.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
static HBITMAP aHbmLines[C_LINES];
static HBITMAP aHbmChart[C_CHARTS];
int i;
switch (uMsg)
{
case WM_CREATE:
// Call application-defined functions to load the
// bitmaps for the Chart menu and create those for
// the Lines menu.
LoadChartBitmaps(aHbmChart);
CreateLineBitmaps(aHbmLines);
// Call an application-defined function to create
// menus containing the bitmap menu items. The function
// also adds a menu name to the window's menu bar.
AddBitmapMenu(
hwnd, // menu bar's owner window
"&Chart", // text of menu name on menu bar
IDM_PIE, // ID of first item on menu
aHbmChart, // array of bitmap handles
C_CHARTS // number of items on menu
);
AddBitmapMenu(hwnd, "&Lines", IDM_SOLID,
aHbmLines, C_LINES);
break;
case WM_DESTROY:
for (i = 0; i < C_LINES; i++)
DeleteObject(aHbmLines[i]);
for (i = 0; i < C_CHARTS; i++)
DeleteObject(aHbmChart[i]);
PostQuitMessage(0);
break;
// Process additional messages here.
default:
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
}
return 0;
}
애플리케이션 정의 LoadChartBitmaps 함수는 다음과 같이 LoadBitmap 함수를 호출하여 차트 메뉴에 대한 비트맵 리소스를 로드합니다.
VOID WINAPI LoadChartBitmaps(HBITMAP *paHbm)
{
paHbm[0] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_PIE));
paHbm[1] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_LINE));
paHbm[2] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_BAR));
}
애플리케이션 정의 CreateLineBitmaps 함수는 GDI 그리기 함수를 사용하여 선 메뉴에 대한 비트맵을 만듭니다. 함수는 데스크톱 창의 DC와 동일한 속성을 사용하여 DC(메모리 디바이스 컨텍스트)를 만듭니다. 각 선 스타일에 대해 함수는 비트맵을 만들고, 메모리 DC로 선택하고, 그 안에 그립니다.
VOID WINAPI CreateLineBitmaps(HBITMAP *paHbm)
{
HWND hwndDesktop = GetDesktopWindow();
HDC hdcDesktop = GetDC(hwndDesktop);
HDC hdcMem = CreateCompatibleDC(hdcDesktop);
COLORREF clrMenu = GetSysColor(COLOR_MENU);
HBRUSH hbrOld;
HPEN hpenOld;
HBITMAP hbmOld;
int fnDrawMode;
int i;
// Create a brush using the menu background color,
// and select it into the memory DC.
hbrOld = SelectObject(hdcMem, CreateSolidBrush(clrMenu));
// Create the bitmaps. Select each one into the memory
// DC that was created and draw in it.
for (i = 0; i < C_LINES; i++)
{
// Create the bitmap and select it into the DC.
paHbm[i] = CreateCompatibleBitmap(hdcDesktop,
CX_LINEBMP, CY_LINEBMP);
hbmOld = SelectObject(hdcMem, paHbm[i]);
// Fill the background using the brush.
PatBlt(hdcMem, 0, 0, CX_LINEBMP, CY_LINEBMP, PATCOPY);
// Create the pen and select it into the DC.
hpenOld = SelectObject(hdcMem,
CreatePen(PS_SOLID + i, 1, RGB(0, 0, 0)));
// Draw the line. To preserve the background color where
// the pen is white, use the R2_MASKPEN drawing mode.
fnDrawMode = SetROP2(hdcMem, R2_MASKPEN);
MoveToEx(hdcMem, 0, CY_LINEBMP / 2, NULL);
LineTo(hdcMem, CX_LINEBMP, CY_LINEBMP / 2);
SetROP2(hdcMem, fnDrawMode);
// Delete the pen, and select the old pen and bitmap.
DeleteObject(SelectObject(hdcMem, hpenOld));
SelectObject(hdcMem, hbmOld);
}
// Delete the brush and select the original brush.
DeleteObject(SelectObject(hdcMem, hbrOld));
// Delete the memory DC and release the desktop DC.
DeleteDC(hdcMem);
ReleaseDC(hwndDesktop, hdcDesktop);
}
애플리케이션 정의 AddBitmapMenu 함수는 메뉴를 만들고 지정된 수의 비트맵 메뉴 항목을 추가합니다. 그런 다음 지정된 창의 메뉴 모음에 해당 메뉴 이름을 추가합니다.
VOID WINAPI AddBitmapMenu(
HWND hwnd, // window that owned the menu bar
LPSTR lpszText, // text of menu name on menu bar
UINT uID, // ID of first bitmap menu item
HBITMAP *paHbm, // bitmaps for the menu items
int cItems) // number bitmap menu items
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup = CreatePopupMenu();
MENUITEMINFO mii;
int i;
// Add the bitmap menu items to the menu.
for (i = 0; i < cItems; i++)
{
mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_DATA;
mii.wID = uID + i;
mii.hbmpItem = &paHbm[i];
InsertMenuItem(hmenuPopup, i, TRUE, &mii);
}
// Add a menu name to the menu bar.
mii.fMask = MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
mii.fType = MFT_STRING;
mii.hSubMenu = hmenuPopup;
mii.dwTypeData = lpszText;
InsertMenuItem(hmenuBar,
GetMenuItemCount(hmenuBar), TRUE, &mii);
}
Owner-Drawn 메뉴 항목 만들기
메뉴 항목의 모양을 완전히 제어해야 하는 경우 애플리케이션에서 소유자가 그린 메뉴 항목을 사용할 수 있습니다. 이 섹션에서는 소유자가 그린 메뉴 항목을 만들고 사용하는 데 관련된 단계를 설명합니다.
- Owner-Drawn 플래그 설정
- 소유자가 그린 메뉴 및 WM_MEASUREITEM 메시지
- 소유자가 그린 메뉴 및 WM_DRAWITEM 메시지
- 소유자가 그린 메뉴 및 WM_MENUCHAR 메시지
- Menu-Item 텍스트 문자열에 대한 글꼴 설정
- Owner-Drawn 메뉴 항목의 예
Owner-Drawn 플래그 설정
애플리케이션의 리소스 정의 파일에서 소유자가 그린 메뉴 항목을 정의할 수 없습니다. 대신 MFT_OWNERDRAW 메뉴 플래그를 사용하여 새 메뉴 항목을 만들거나 기존 메뉴 항목을 수정해야 합니다.
InsertMenuItem 또는 SetMenuItemInfo 함수를 사용하여 소유자가 그린 메뉴 항목을 지정할 수 있습니다. InsertMenuItem을 사용하여 메뉴 모음 또는 메뉴의 지정된 위치에 새 메뉴 항목을 삽입합니다. SetMenuItemInfo를 사용하여 메뉴 내용을 변경합니다.
이러한 두 함수를 호출할 때 새 메뉴 항목의 속성 또는 기존 메뉴 항목에 대해 변경하려는 속성을 지정하는 MENUITEMINFO 구조체에 대한 포인터를 지정해야 합니다. 항목을 소유자가 그린 항목으로 만들려면 fMask 멤버의 MIIM_FTYPE 값과 fType 멤버의 MFT_OWNERDRAW 값을 지정합니다.
MENUITEMINFO 구조체의 적절한 멤버를 설정하여 항목 데이터라고 하는 애플리케이션 정의 값을 각 메뉴 항목과 연결할 수 있습니다. 이렇게 하려면 fMask 멤버의 MIIM_DATA 값과 dwItemData 멤버에 대한 애플리케이션 정의 값을 지정합니다.
모든 유형의 메뉴 항목에서 항목 데이터를 사용할 수 있지만 소유자가 그린 항목에 특히 유용합니다. 예를 들어 구조체에 메뉴 항목을 그리는 데 사용되는 정보가 포함되어 있다고 가정합니다. 애플리케이션은 메뉴 항목에 대한 항목 데이터를 사용하여 구조체에 대한 포인터를 저장할 수 있습니다. 항목 데이터는 WM_MEASUREITEM 및 WM_DRAWITEM 메시지와 함께 메뉴의 소유자 창으로 전송됩니다. 메뉴의 항목 데이터를 언제든지 검색하려면 GetMenuItemInfo 함수를 사용합니다.
이전 버전의 시스템용으로 작성된 애플리케이션은 AppendMenu, InsertMenu 또는 ModifyMenu 를 계속 호출하여 소유자 그리기 메뉴 항목에 MF_OWNERDRAW 플래그를 할당할 수 있습니다.
이러한 세 가지 함수를 호출할 때 값을 lpNewItem 매개 변수로 전달할 수 있습니다. 이 값은 애플리케이션에 의미 있고 항목이 표시될 때 애플리케이션에서 사용할 수 있는 모든 정보를 나타낼 수 있습니다. 예를 들어 값에는 구조체에 대한 포인터가 포함될 수 있습니다. 구조체에는 텍스트 문자열과 애플리케이션이 문자열을 그리는 데 사용할 논리 글꼴에 대한 핸들이 포함될 수 있습니다.
Owner-Drawn 메뉴 및 WM_MEASUREITEM 메시지
시스템이 처음으로 소유자가 그린 메뉴 항목을 표시하기 전에 항목의 메뉴를 소유하는 창의 창 프로시저에 WM_MEASUREITEM 메시지를 보냅니다. 이 메시지는 항목을 식별하고 애플리케이션이 할당했을 수 있는 항목 데이터를 포함하는 MEASUREITEMSTRUCT 구조체에 대한 포인터를 포함합니다. 창 프로시저는 메시지 처리에서 반환하기 전에 구조체의 itemWidth 및 itemHeight 멤버를 채워야 합니다. 시스템은 애플리케이션이 메뉴 항목을 그리는 경계 사각형을 만들 때 이러한 멤버의 정보를 사용합니다. 또한 정보를 사용하여 사용자가 항목을 선택하는 시기를 검색합니다.
Owner-Drawn 메뉴 및 WM_DRAWITEM 메시지
항목을 그려야 할 때마다(예: 처음 표시되거나 사용자가 항목을 선택할 때) 시스템은 메뉴 소유자 창의 창 프로시저에 WM_DRAWITEM 메시지를 보냅니다. 이 메시지에는 애플리케이션이 할당했을 수 있는 항목 데이터를 포함하여 항목에 대한 정보가 포함된 DRAWITEMSTRUCT 구조체에 대한 포인터가 포함되어 있습니다. 또한 DRAWITEMSTRUCT 에는 항목의 상태(예: 회색 또는 선택 여부)를 나타내는 플래그와 경계 사각형 및 애플리케이션이 항목을 그리는 데 사용하는 디바이스 컨텍스트가 포함됩니다.
애플리케이션은 WM_DRAWITEM 메시지를 처리하는 동안 다음을 수행해야 합니다.
- 필요한 그리기 유형을 결정합니다. 이렇게 하려면 DRAWITEMSTRUCT 구조체의 itemAction 멤버를 검사.
- DRAWITEMSTRUCT 구조체에서 가져온 경계 사각형 및 디바이스 컨텍스트를 사용하여 메뉴 항목을 적절하게 그립니다. 애플리케이션은 경계 사각형 내에서만 그려야 합니다. 성능상의 이유로 시스템은 사각형 바깥쪽에 그려진 이미지의 일부를 잘라내지 않습니다.
- 메뉴 항목의 디바이스 컨텍스트에 대해 선택한 모든 GDI 개체를 복원합니다.
사용자가 메뉴 항목을 선택하면 시스템에서 DRAWITEMSTRUCT 구조체의 itemAction 멤버를 ODA_SELECT 값으로 설정하고 itemState 멤버의 ODS_SELECTED 값을 설정합니다. 선택되었음을 나타내기 위해 메뉴 항목을 다시 그리는 애플리케이션의 신호입니다.
Owner-Drawn 메뉴 및 WM_MENUCHAR 메시지
소유자가 그린 메뉴 이외의 메뉴는 메뉴 문자열의 문자 옆에 밑줄을 삽입하여 메뉴 니모닉을 지정할 수 있습니다. 이렇게 하면 사용자가 Alt 키를 누르고 메뉴 니모닉 문자를 눌러 메뉴를 선택할 수 있습니다. 그러나 소유자가 그린 메뉴에서는 이러한 방식으로 메뉴 니모닉을 지정할 수 없습니다. 대신 애플리케이션은 WM_MENUCHAR 메시지를 처리하여 메뉴 니모닉이 있는 소유자 그리기 메뉴를 제공해야 합니다.
WM_MENUCHAR 메시지는 사용자가 현재 메뉴의 미리 정의된 니모닉과 일치하지 않는 메뉴 니모닉을 입력할 때 전송됩니다. wParam에 포함된 값은 사용자가 ALT 키로 누른 키에 해당하는 ASCII 문자를 지정합니다. wParam의 하위 단어는 선택한 메뉴의 형식을 지정하며 다음 값 중 하나일 수 있습니다.
- 현재 메뉴가 하위 메뉴인지 MF_POPUP.
- 메뉴 가 시스템 메뉴인지 MF_SYSMENU.
wParam의 상위 단어에는 현재 메뉴에 대한 메뉴 핸들이 포함되어 있습니다. 소유자가 그린 메뉴가 있는 창은 다음과 같이 WM_MENUCHAR 처리할 수 있습니다.
case WM_MENUCHAR:
nIndex = Determine index of menu item to be selected from
character that was typed and handle to the current
menu.
return MAKELRESULT(nIndex, 2);
반환 값의 상위 단어에 있는 두 단어는 반환 값의 하위 단어에 선택할 메뉴 항목의 인덱스(0부터 시작)가 포함되어 있음을 시스템에 알립니다.
다음 상수는 WM_MENUCHAR 메시지의 가능한 반환 값에 해당합니다.
상수 | 값 | 의미 |
---|---|---|
MNC_IGNORE | 0 | 시스템은 사용자가 누른 문자를 삭제하고 시스템 스피커에 짧은 경고음을 만들어야 합니다. |
MNC_CLOSE | 1 | 시스템에서 활성 메뉴를 닫아야 합니다. |
MNC_EXECUTE | 2 | 시스템에서 반환 값의 하위 단어에 지정된 항목을 선택해야 합니다. 소유자 창에 WM_COMMAND 메시지가 표시됩니다. |
MNC_SELECT | 3 | 시스템에서 반환 값의 하위 단어에 지정된 항목을 선택해야 합니다. |
Menu-Item 텍스트 문자열에 대한 글꼴 설정
이 항목에는 메뉴에서 소유자가 그린 메뉴 항목을 사용하는 애플리케이션의 예제가 포함되어 있습니다. 메뉴에는 현재 글꼴의 특성을 설정하는 항목이 포함되어 있으며 적절한 글꼴 특성을 사용하여 항목이 표시됩니다.
리소스 정의 파일에서 메뉴가 정의되는 방법은 다음과 같습니다. 일반, 굵게, 기울임꼴 및 밑줄 메뉴 항목에 대한 문자열은 런타임에 할당되므로 해당 문자열은 리소스 정의 파일에 비어 있습니다.
MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "", IDM_REGULAR
MENUITEM SEPARATOR
MENUITEM "", IDM_BOLD
MENUITEM "", IDM_ITALIC
MENUITEM "", IDM_ULINE
END
END
애플리케이션의 창 프로시저는 소유자가 그린 메뉴 항목을 사용하는 데 관련된 메시지를 처리합니다. 애플리케이션은 WM_CREATE 메시지를 사용하여 다음을 수행합니다.
- 메뉴 항목에 대한 MF_OWNERDRAW 플래그를 설정합니다.
- 메뉴 항목에 대한 텍스트 문자열을 설정합니다.
- 항목을 그리는 데 사용되는 글꼴의 핸들을 가져옵니다.
- 선택한 메뉴 항목에 대한 텍스트 및 배경색 값을 가져옵니다.
텍스트 문자열 및 글꼴 핸들은 애플리케이션 정의 MYITEM 구조의 배열에 저장됩니다. 애플리케이션 정의 GetAFont 함수는 지정된 글꼴 특성에 해당하는 글꼴을 만들고 글꼴에 대한 핸들을 반환합니다. WM_DESTROY 메시지를 처리하는 동안 핸들이 제거됩니다.
WM_MEASUREITEM 메시지를 처리하는 동안 이 예제에서는 메뉴 항목 문자열의 너비와 높이를 가져오고 이러한 값을 MEASUREITEMSTRUCT 구조체에 복사합니다. 시스템은 너비 및 높이 값을 사용하여 메뉴의 크기를 계산합니다.
WM_DRAWITEM 메시지를 처리하는 동안 메뉴 항목의 문자열은 검사 표시 비트맵의 문자열 옆에 공백이 남아 있는 상태로 그려집니다. 사용자가 항목을 선택하면 선택한 텍스트와 배경색이 항목을 그리는 데 사용됩니다.
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
typedef struct _MYITEM
{
HFONT hfont;
LPSTR psz;
} MYITEM; // structure for item font and string
MYITEM *pmyitem; // pointer to item's font and string
static MYITEM myitem[CITEMS]; // array of MYITEMS
static HMENU hmenu; // handle to main menu
static COLORREF crSelText; // text color of selected item
static COLORREF crSelBkgnd; // background color of selected item
COLORREF crText; // text color of unselected item
COLORREF crBkgnd; // background color unselected item
LPMEASUREITEMSTRUCT lpmis; // pointer to item of data
LPDRAWITEMSTRUCT lpdis; // pointer to item drawing data
HDC hdc; // handle to screen DC
SIZE size; // menu-item text extents
WORD wCheckX; // check-mark width
int nTextX; // width of menu item
int nTextY; // height of menu item
int i; // loop counter
HFONT hfontOld; // handle to old font
BOOL fSelected = FALSE; // menu-item selection flag
size_t * pcch;
HRESULT hResult;
switch (uMsg)
{
case WM_CREATE:
// Modify the Regular, Bold, Italic, and Underline
// menu items to make them owner-drawn items. Associate
// a MYITEM structure with each item to contain the
// string for and font handle to each item.
hmenu = GetMenu(hwnd);
ModifyMenu(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED | MF_OWNERDRAW, IDM_REGULAR,
(LPTSTR) &myitem[REGULAR]);
ModifyMenu(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_BOLD, (LPTSTR) &myitem[BOLD]);
ModifyMenu(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_ITALIC,
(LPTSTR) &myitem[ITALIC]);
ModifyMenu(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_ULINE, (LPTSTR) &myitem[ULINE]);
// Retrieve each item's font handle and copy it into
// the hfont member of each item's MYITEM structure.
// Also, copy each item's string into the structures.
myitem[REGULAR].hfont = GetAFont(REGULAR);
myitem[REGULAR].psz = "Regular";
myitem[BOLD].hfont = GetAFont(BOLD);
myitem[BOLD].psz = "Bold";
myitem[ITALIC].hfont = GetAFont(ITALIC);
myitem[ITALIC].psz = "Italic";
myitem[ULINE].hfont = GetAFont(ULINE);
myitem[ULINE].psz = "Underline";
// Retrieve the text and background colors of the
// selected menu text.
crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);
return 0;
case WM_MEASUREITEM:
// Retrieve a device context for the main window.
hdc = GetDC(hwnd);
// Retrieve pointers to the menu item's
// MEASUREITEMSTRUCT structure and MYITEM structure.
lpmis = (LPMEASUREITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpmis->itemData;
// Select the font associated with the item into
// the main window's device context.
hfontOld = SelectObject(hdc, pmyitem->hfont);
// Retrieve the width and height of the item's string,
// and then copy the width and height into the
// MEASUREITEMSTRUCT structure's itemWidth and
// itemHeight members.
hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// Add code to fail as securely as possible.
return;
}
GetTextExtentPoint32(hdc, pmyitem->psz,
*pcch, &size);
lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;
// Select the old font back into the device context,
// and then release the device context.
SelectObject(hdc, hfontOld);
ReleaseDC(hwnd, hdc);
return TRUE;
break;
case WM_DRAWITEM:
// Get pointers to the menu item's DRAWITEMSTRUCT
// structure and MYITEM structure.
lpdis = (LPDRAWITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpdis->itemData;
// If the user has selected the item, use the selected
// text and background colors to display the item.
if (lpdis->itemState & ODS_SELECTED)
{
crText = SetTextColor(lpdis->hDC, crSelText);
crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd);
fSelected = TRUE;
}
// Remember to leave space in the menu item for the
// check-mark bitmap. Retrieve the width of the bitmap
// and add it to the width of the menu item.
wCheckX = GetSystemMetrics(SM_CXMENUCHECK);
nTextX = wCheckX + lpdis->rcItem.left;
nTextY = lpdis->rcItem.top;
// Select the font associated with the item into the
// item's device context, and then draw the string.
hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);
hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// Add code to fail as securely as possible.
return;
}
ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE,
&lpdis->rcItem, pmyitem->psz,
*pcch, NULL);
// Select the previous font back into the device
// context.
SelectObject(lpdis->hDC, hfontOld);
// Return the text and background colors to their
// normal state (not selected).
if (fSelected)
{
SetTextColor(lpdis->hDC, crText);
SetBkColor(lpdis->hDC, crBkgnd);
}
return TRUE;
// Process other messages.
case WM_DESTROY:
// Destroy the menu items' font handles.
for (i = 0; i < CITEMS; i++)
DeleteObject(myitem[i].hfont);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}
HFONT GetAFont(int fnFont)
{
static LOGFONT lf; // structure for font information
// Get a handle to the ANSI fixed-pitch font, and copy
// information about the font to a LOGFONT structure.
GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT),
&lf);
// Set the font attributes, as appropriate.
if (fnFont == BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;
lf.lfItalic = (fnFont == ITALIC);
lf.lfItalic = (fnFont == ULINE);
// Create the font, and then return its handle.
return CreateFont(lf.lfHeight, lf.lfWidth,
lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,
lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
lf.lfPitchAndFamily, lf.lfFaceName);
}
Owner-Drawn 메뉴 항목의 예
이 항목의 예제에서는 메뉴에서 소유자가 그린 메뉴 항목을 사용합니다. 메뉴 항목은 특정 글꼴 특성을 선택하고 애플리케이션은 해당 특성이 있는 글꼴을 사용하여 각 메뉴 항목을 표시합니다. 예를 들어 기울임꼴 메뉴 항목은 기울임꼴 글꼴로 표시됩니다. 메뉴 모음의 문자 메뉴 이름이 메뉴를 엽니다.
메뉴 모음 및 드롭다운 메뉴는 처음에는 확장된 메뉴 템플릿 리소스에 의해 정의됩니다. 메뉴 템플릿은 소유자 그리기 항목을 지정할 수 없으므로 메뉴에는 처음에는 "일반", "굵게", "기울임꼴" 및 "밑줄" 문자열이 있는 4개의 텍스트 메뉴 항목이 포함됩니다. 애플리케이션의 창 프로시저는 WM_CREATE 메시지를 처리할 때 소유자가 그린 항목으로 변경합니다. WM_CREATE 메시지를 받으면 창 프로시저는 각 메뉴 항목에 대해 다음 단계를 수행하는 애플리케이션 정의 OnCreate 함수를 호출합니다.
- 애플리케이션 정의 MYITEM 구조를 할당합니다.
- 메뉴 항목의 텍스트를 가져오고 애플리케이션 정의 MYITEM 구조에 저장합니다.
- 메뉴 항목을 표시하는 데 사용되는 글꼴을 만들고 애플리케이션 정의 MYITEM 구조에 해당 핸들을 저장합니다.
- 메뉴 항목 유형을 MFT_OWNERDRAW 변경하고 애플리케이션 정의 MYITEM 구조에 대한 포인터를 항목 데이터로 저장합니다.
각 애플리케이션 정의 MYITEM 구조에 대한 포인터는 항목 데이터로 저장되므로 해당 메뉴 항목에 대한 WM_MEASUREITEM 및 WM_DRAWITEM 메시지와 함께 창 프로시저에 전달됩니다. 포인터는 MEASUREITEMSTRUCT 및 DRAWITEMSTRUCT 구조체의 itemData 멤버에 포함됩니다.
처음 표시될 때 소유자가 그린 각 메뉴 항목에 대해 WM_MEASUREITEM 메시지가 전송됩니다. 애플리케이션은 디바이스 컨텍스트에 메뉴 항목의 글꼴을 선택한 다음 해당 글꼴에 메뉴 항목 텍스트를 표시하는 데 필요한 공간을 결정하여 이 메시지를 처리합니다. 글꼴 및 메뉴 항목 텍스트는 모두 메뉴 항목의 MYITEM
구조(애플리케이션에서 정의한 구조)로 지정됩니다. 애플리케이션은 GetTextExtentPoint32 함수를 사용하여 텍스트 크기를 결정합니다.
창 프로시저는 메뉴 항목 텍스트를 적절한 글꼴로 표시하여 WM_DRAWITEM 메시지를 처리합니다. 글꼴 및 메뉴 항목 텍스트는 모두 메뉴 항목의 MYITEM
구조에 의해 지정됩니다. 애플리케이션은 메뉴 항목의 상태에 적합한 텍스트 및 배경색을 선택합니다.
창 프로시저는 WM_DESTROY 메시지를 처리하여 글꼴 및 사용 가능한 메모리를 삭제합니다. 애플리케이션은 글꼴을 삭제하고 각 메뉴 항목에 대해 애플리케이션 정의 MYITEM 구조를 해제합니다.
다음은 애플리케이션 헤더 파일의 관련 부분입니다.
// Menu-item identifiers for the Character menu
#define IDM_CHARACTER 10
#define IDM_REGULAR 11
#define IDM_BOLD 12
#define IDM_ITALIC 13
#define IDM_UNDERLINE 14
// Structure associated with menu items
typedef struct tagMYITEM
{
HFONT hfont;
int cchItemText;
char szItemText[1];
} MYITEM;
#define CCH_MAXITEMTEXT 256
다음은 애플리케이션 창 프로시저 및 관련 함수의 관련 부분입니다.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
case WM_CREATE:
if (!OnCreate(hwnd))
return -1;
break;
case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage(0);
break;
case WM_MEASUREITEM:
OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam);
return TRUE;
case WM_DRAWITEM:
OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam);
return TRUE;
// Additional message processing goes here.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL WINAPI OnCreate(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
MYITEM *pMyItem;
// Get a handle to the pop-up menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Modify each menu item. Assume that the IDs IDM_REGULAR
// through IDM_UNDERLINE are consecutive numbers.
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++)
{
// Allocate an item structure, leaving space for a
// string of up to CCH_MAXITEMTEXT characters.
pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,
sizeof(MYITEM) + CCH_MAXITEMTEXT);
// Save the item text in the item structure.
mii.fMask = MIIM_STRING;
mii.dwTypeData = pMyItem->szItemText;
mii.cch = CCH_MAXITEMTEXT;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem->cchItemText = mii.cch;
// Reallocate the structure to the minimum required size.
pMyItem = (MYITEM *) LocalReAlloc(pMyItem,
sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);
// Create the font used to draw the item.
pMyItem->hfont = CreateMenuItemFont(uID);
// Change the item to an owner-drawn item, and save
// the address of the item structure as item data.
mii.fMask = MIIM_FTYPE | MIIM_DATA;
mii.fType = MFT_OWNERDRAW;
mii.dwItemData = (ULONG_PTR) pMyItem;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
}
return TRUE;
}
HFONT CreateMenuItemFont(UINT uID)
{
LOGFONT lf;
HRESULT hr;
ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = 20;
hr = StringCchCopy(lf.lfFaceName, 32, "Times New Roman");
if (FAILED(hr))
{
// TODO: writer error handler
}
switch (uID)
{
case IDM_BOLD:
lf.lfWeight = FW_HEAVY;
break;
case IDM_ITALIC:
lf.lfItalic = TRUE;
break;
case IDM_UNDERLINE:
lf.lfUnderline = TRUE;
break;
}
return CreateFontIndirect(&lf);
}
VOID WINAPI OnDestroy(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
MYITEM *pMyItem;
// Get a handle to the menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Free resources associated with each menu item.
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++)
{
// Get the item data.
mii.fMask = MIIM_DATA;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem = (MYITEM *) mii.dwItemData;
// Destroy the font and free the item structure.
DeleteObject(pMyItem->hfont);
LocalFree(pMyItem);
}
}
VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)
{
MYITEM *pMyItem = (MYITEM *) lpmis->itemData;
HDC hdc = GetDC(hwnd);
HFONT hfntOld = (HFONT)SelectObject(hdc, pMyItem->hfont);
SIZE size;
GetTextExtentPoint32(hdc, pMyItem->szItemText,
pMyItem->cchItemText, &size);
lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;
SelectObject(hdc, hfntOld);
ReleaseDC(hwnd, hdc);
}
VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis)
{
MYITEM *pMyItem = (MYITEM *) lpdis->itemData;
COLORREF clrPrevText, clrPrevBkgnd;
HFONT hfntPrev;
int x, y;
// Set the appropriate foreground and background colors.
if (lpdis->itemState & ODS_SELECTED)
{
clrPrevText = SetTextColor(lpdis->hDC,
GetSysColor(COLOR_HIGHLIGHTTEXT));
clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_HIGHLIGHT));
}
else
{
clrPrevText = SetTextColor(lpdis->hDC,
GetSysColor(COLOR_MENUTEXT));
clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_MENU));
}
// Determine where to draw and leave space for a check mark.
x = lpdis->rcItem.left;
y = lpdis->rcItem.top;
x += GetSystemMetrics(SM_CXMENUCHECK);
// Select the font and draw the text.
hfntPrev = (HFONT)SelectObject(lpdis->hDC, pMyItem->hfont);
ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE,
&lpdis->rcItem, pMyItem->szItemText,
pMyItem->cchItemText, NULL);
// Restore the original font and colors.
SelectObject(lpdis->hDC, hfntPrev);
SetTextColor(lpdis->hDC, clrPrevText);
SetBkColor(lpdis->hDC, clrPrevBkgnd);
}
사용자 지정 확인 표시 비트맵 사용
시스템은 선택한 메뉴 항목 옆에 표시하기 위한 기본 검사 표시 비트맵을 제공합니다. 기본 검사 표시 비트맵을 대체할 비트맵 쌍을 제공하여 개별 메뉴 항목을 사용자 지정할 수 있습니다. 시스템은 항목이 선택되면 비트맵을 표시하고 다른 하나는 명확할 때 표시합니다. 이 섹션에서는 사용자 지정 검사 표시 비트맵을 만들고 사용하는 데 관련된 단계를 설명합니다.
사용자 지정 확인 표시 비트맵 만들기
사용자 지정 검사 표시 비트맵은 기본 검사 표시 비트맵과 크기가 같아야 합니다. GetSystemMetrics 함수를 호출하여 비트맵의 기본 검사 표시 크기를 검색할 수 있습니다. 이 함수의 반환 값이 낮은 단어는 너비를 지정합니다. 높은 순서의 단어는 높이를 지정합니다.
비트맵 리소스를 사용하여 검사 표시 비트맵을 제공할 수 있습니다. 그러나 필요한 비트맵 크기는 표시 유형에 따라 다르므로 StretchBlt 함수를 사용하여 런타임에 비트맵의 크기를 조정해야 할 수 있습니다. 비트맵에 따라 크기 조정으로 인한 왜곡은 허용할 수 없는 결과를 생성할 수 있습니다.
비트맵 리소스를 사용하는 대신 GDI 함수를 사용하여 런타임에 비트맵을 만들 수 있습니다.
런타임에 비트맵을 만들려면
CreateCompatibleDC 함수를 사용하여 애플리케이션의 기본 창에서 사용하는 것과 호환되는 디바이스 컨텍스트를 만듭니다.
함수의 hdc 매개 변수는 NULL 또는 함수의 반환 값을 지정할 수 있습니다. CreateCompatibleDC 는 호환되는 디바이스 컨텍스트에 대한 핸들을 반환합니다.
CreateCompatibleBitmap 함수를 사용하여 애플리케이션의 기본 창과 호환되는 비트맵을 만듭니다.
이 함수의 nWidth 및 nHeight 매개 변수는 비트맵의 크기를 설정합니다. GetSystemMetrics 함수에서 반환하는 너비 및 높이 정보를 지정해야 합니다.
참고
CreateBitmap 함수를 사용하여 단색 비트맵을 만들 수도 있습니다.
SelectObject 함수를 사용하여 호환되는 디바이스 컨텍스트로 비트맵을 선택합니다.
Ellipse 및 LineTo와 같은 GDI 그리기 함수를 사용하여 이미지를 비트맵에 그리거나 BitBlt 및 StretchBlt 같은 함수를 사용하여 이미지를 비트맵에 복사합니다.
자세한 내용은 비트맵을 참조하세요.
비트맵을 메뉴 항목과 연결
비트맵의 핸들을 SetMenuItemBitmaps 함수에 전달하여 검사 표시 비트맵 쌍을 메뉴 항목과 연결합니다. hBitmapUnchecked 매개 변수는 명확한 비트맵을 식별하고 hBitmapChecked 매개 변수는 선택한 비트맵을 식별합니다. 메뉴 항목에서 하나 또는 둘 다 검사 표시를 제거하려면 hBitmapUnchecked 또는 hBitmapChecked 매개 변수 또는 둘 다를 NULL로 설정할 수 있습니다.
확인 표시 특성 설정
CheckMenuItem 함수는 메뉴 항목의 검사 표시 특성을 선택되거나 지워지도록 설정합니다. MF_CHECKED 값을 지정하여 검사 표시 특성을 선택됨으로 설정하고 MF_UNCHECKED 값을 지정하여 지우도록 설정할 수 있습니다.
SetMenuItemInfo 함수를 사용하여 메뉴 항목의 검사 상태를 설정할 수도 있습니다.
경우에 따라 메뉴 항목 그룹은 상호 배타적인 옵션 집합을 나타냅니다. CheckMenuRadioItem 함수를 사용하면 하나의 메뉴 항목을 검사 동시에 그룹의 다른 모든 메뉴 항목에서 검사 표시를 제거할 수 있습니다.
메뉴에서 확인란 시뮬레이션
이 항목에는 메뉴에서 검사 상자를 시뮬레이션하는 방법을 보여 주는 예제가 포함되어 있습니다. 이 예제에는 사용자가 현재 글꼴의 굵고 기울임꼴 및 밑줄 특성을 설정할 수 있는 항목이 있는 문자 메뉴가 포함되어 있습니다. 글꼴 특성이 적용되면 해당 메뉴 항목 옆의 검사 상자에 검사 표시가 표시되고, 그렇지 않으면 항목 옆에 빈 검사 상자가 표시됩니다.
이 예제에서는 기본 검사 표시 비트맵을 선택한 검사 상자가 있는 비트맵과 빈 상자가 있는 비트맵의 두 비트맵으로 바꿉니다. 선택한 검사 상자 비트맵은 항목의 검사 표시 특성이 MF_CHECKED 설정된 경우 굵게, 기울임꼴 또는 밑줄 메뉴 항목 옆에 표시됩니다. 검사 표시 특성이 MF_UNCHECKED 설정되면 지우기 또는 빈 검사 상자 비트맵이 표시됩니다.
시스템은 검사 상자 및 라디오 단추에 사용되는 이미지를 포함하는 미리 정의된 비트맵을 제공합니다. 이 예제에서는 선택한 빈 검사 상자를 격리하고 두 개의 개별 비트맵에 복사한 다음 문자 메뉴의 항목에 대해 선택한 비트맵 및 지워진 비트맵으로 사용합니다.
시스템 정의 검사 상자 비트맵에 대한 핸들을 검색하기 위해 예제에서는 LoadBitmap 함수를 호출하고 NULL을 hInstance 매개 변수로 지정하고 lpBitmapName 매개 변수로 OBM_CHECKBOXES. 비트맵의 이미지는 모두 크기가 동일하기 때문에 비트맵 너비와 높이를 행과 열의 이미지 수로 나누어 격리할 수 있습니다.
리소스 정의 파일의 다음 부분에서는 문자 메뉴의 메뉴 항목이 정의되는 방법을 보여줍니다. 처음에는 글꼴 특성이 적용되지 않으므로 일반 항목의 검사 표시 특성이 선택됨으로 설정되고, 기본적으로 나머지 항목의 검사 표시 특성은 지우기로 설정됩니다.
#include "men3.h"
MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "&Regular", IDM_REGULAR, CHECKED
MENUITEM SEPARATOR
MENUITEM "&Bold", IDM_BOLD
MENUITEM "&Italic", IDM_ITALIC
MENUITEM "&Underline", IDM_ULINE
END
END
다음은 애플리케이션 헤더 파일의 관련 내용입니다.
// Menu-item identifiers
#define IDM_REGULAR 0x1
#define IDM_BOLD 0x2
#define IDM_ITALIC 0x4
#define IDM_ULINE 0x8
// Check-mark flags
#define CHECK 1
#define UNCHECK 2
// Font-attribute mask
#define ATTRIBMASK 0xe
// Function prototypes
LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP GetMyCheckBitmaps(UINT);
BYTE CheckOrUncheckMenuItem(BYTE, HMENU);
다음 예제에서는 검사 표시 비트맵을 만들고, 굵게, 기울임꼴 및 밑줄 메뉴 항목의 검사 표시 특성을 설정하고, 검사 표시 비트맵을 삭제하는 창 프로시저의 부분을 보여 줍니다.
LRESULT APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hbmpCheck; // handle to checked bitmap
static HBITMAP hbmpUncheck; // handle to unchecked bitmap
static HMENU hmenu; // handle to main menu
BYTE fbFontAttrib; // font-attribute flags
switch (uMsg)
{
case WM_CREATE:
// Call the application-defined GetMyCheckBitmaps
// function to get the predefined checked and
// unchecked check box bitmaps.
hbmpCheck = GetMyCheckBitmaps(CHECK);
hbmpUncheck = GetMyCheckBitmaps(UNCHECK);
// Set the checked and unchecked bitmaps for the menu
// items.
hmenu = GetMenu(hwndMain);
SetMenuItemBitmaps(hmenu, IDM_BOLD, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ITALIC, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ULINE, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
// Process the menu commands.
case IDM_REGULAR:
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:
// CheckOrUncheckMenuItem is an application-
// defined function that sets the menu item
// checkmarks and returns the user-selected
// font attributes.
fbFontAttrib = CheckOrUncheckMenuItem(
(BYTE) LOWORD(wParam), hmenu);
// Set the font attributes.
return 0;
// Process other command messages.
default:
break;
}
break;
// Process other window messages.
case WM_DESTROY:
// Destroy the checked and unchecked bitmaps.
DeleteObject(hbmpCheck);
DeleteObject(hbmpUncheck);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}
HBITMAP GetMyCheckBitmaps(UINT fuCheck)
{
COLORREF crBackground; // background color
HBRUSH hbrBackground; // background brush
HBRUSH hbrTargetOld; // original background brush
HDC hdcSource; // source device context
HDC hdcTarget; // target device context
HBITMAP hbmpCheckboxes; // handle to check-box bitmap
BITMAP bmCheckbox; // structure for bitmap data
HBITMAP hbmpSourceOld; // handle to original source bitmap
HBITMAP hbmpTargetOld; // handle to original target bitmap
HBITMAP hbmpCheck; // handle to check-mark bitmap
RECT rc; // rectangle for check-box bitmap
WORD wBitmapX; // width of check-mark bitmap
WORD wBitmapY; // height of check-mark bitmap
// Get the menu background color and create a solid brush
// with that color.
crBackground = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crBackground);
// Create memory device contexts for the source and
// destination bitmaps.
hdcSource = CreateCompatibleDC((HDC) NULL);
hdcTarget = CreateCompatibleDC(hdcSource);
// Get the size of the system default check-mark bitmap and
// create a compatible bitmap of the same size.
wBitmapX = GetSystemMetrics(SM_CXMENUCHECK);
wBitmapY = GetSystemMetrics(SM_CYMENUCHECK);
hbmpCheck = CreateCompatibleBitmap(hdcSource, wBitmapX,
wBitmapY);
// Select the background brush and bitmap into the target DC.
hbrTargetOld = SelectObject(hdcTarget, hbrBackground);
hbmpTargetOld = SelectObject(hdcTarget, hbmpCheck);
// Use the selected brush to initialize the background color
// of the bitmap in the target device context.
PatBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, PATCOPY);
// Load the predefined check box bitmaps and select it
// into the source DC.
hbmpCheckboxes = LoadBitmap((HINSTANCE) NULL,
(LPTSTR) OBM_CHECKBOXES);
hbmpSourceOld = SelectObject(hdcSource, hbmpCheckboxes);
// Fill a BITMAP structure with information about the
// check box bitmaps, and then find the upper-left corner of
// the unchecked check box or the checked check box.
GetObject(hbmpCheckboxes, sizeof(BITMAP), &bmCheckbox);
if (fuCheck == UNCHECK)
{
rc.left = 0;
rc.right = (bmCheckbox.bmWidth / 4);
}
else
{
rc.left = (bmCheckbox.bmWidth / 4);
rc.right = (bmCheckbox.bmWidth / 4) * 2;
}
rc.top = 0;
rc.bottom = (bmCheckbox.bmHeight / 3);
// Copy the appropriate bitmap into the target DC. If the
// check-box bitmap is larger than the default check-mark
// bitmap, use StretchBlt to make it fit; otherwise, just
// copy it.
if (((rc.right - rc.left) > (int) wBitmapX) ||
((rc.bottom - rc.top) > (int) wBitmapY))
{
StretchBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY,
hdcSource, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, SRCCOPY);
}
else
{
BitBlt(hdcTarget, 0, 0, rc.right - rc.left,
rc.bottom - rc.top,
hdcSource, rc.left, rc.top, SRCCOPY);
}
// Select the old source and destination bitmaps into the
// source and destination DCs, and then delete the DCs and
// the background brush.
SelectObject(hdcSource, hbmpSourceOld);
SelectObject(hdcTarget, hbrTargetOld);
hbmpCheck = SelectObject(hdcTarget, hbmpTargetOld);
DeleteObject(hbrBackground);
DeleteObject(hdcSource);
DeleteObject(hdcTarget);
// Return a handle to the new check-mark bitmap.
return hbmpCheck;
}
BYTE CheckOrUncheckMenuItem(BYTE bMenuItemID, HMENU hmenu)
{
DWORD fdwMenu;
static BYTE fbAttributes;
switch (bMenuItemID)
{
case IDM_REGULAR:
// Whenever the Regular menu item is selected, add a
// check mark to it and then remove checkmarks from
// any font-attribute menu items.
CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED);
if (fbAttributes & ATTRIBMASK)
{
CheckMenuItem(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_UNCHECKED);
}
fbAttributes = IDM_REGULAR;
return fbAttributes;
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:
// Toggle the check mark for the selected menu item and
// set the font attribute flags appropriately.
fdwMenu = GetMenuState(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND);
if (!(fdwMenu & MF_CHECKED))
{
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes |= bMenuItemID;
}
else
{
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes ^= bMenuItemID;
}
// If any font attributes are currently selected,
// remove the check mark from the Regular menu item;
// if no attributes are selected, add a check mark
// to the Regular menu item.
if (fbAttributes & ATTRIBMASK)
{
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes &= (BYTE) ~IDM_REGULAR;
}
else
{
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes = IDM_REGULAR;
}
return fbAttributes;
}
}
사용자 지정 확인 표시 비트맵 사용 예제
이 항목의 예제에서는 두 메뉴의 메뉴 항목에 사용자 지정 검사 표시 비트맵을 할당합니다. 첫 번째 메뉴의 메뉴 항목은 문자 특성(굵게, 기울임꼴 및 밑줄)을 지정합니다. 각 메뉴 항목을 선택하거나 지울 수 있습니다. 이러한 메뉴 항목의 경우 이 예제에서는 검사 상자 컨트롤의 선택되고 지워진 상태와 유사한 검사 표시 비트맵을 사용합니다.
두 번째 메뉴의 메뉴 항목은 단락 맞춤 설정(왼쪽, 가운데 및 오른쪽)을 지정합니다. 이러한 메뉴 항목 중 하나만 언제든지 선택됩니다. 이러한 메뉴 항목의 경우 이 예제에서는 라디오 단추 컨트롤의 선택한 상태와 지우기 상태와 유사한 검사 표시 비트맵을 사용합니다.
창 프로시저는 애플리케이션 정의 OnCreate 함수를 호출하여 WM_CREATE 메시지를 처리합니다.
OnCreate
는 4개의 검사 표시 비트맵을 만든 다음 SetMenuItemBitmaps 함수를 사용하여 적절한 메뉴 항목에 할당합니다.
각 비트맵을 만들기 위해 OnCreate는 애플리케이션 정의 CreateMenuBitmaps 함수를 호출하고 비트맵별 그리기 함수에 대한 포인터를 지정합니다. CreateMenuBitmaps는 필요한 크기의 단색 비트맵을 만들고, 메모리 디바이스 컨텍스트로 선택하고, 배경을 지웁니다. 그런 다음 지정된 그리기 함수를 호출하여 전경을 채웁니다.
네 가지 애플리케이션 정의 그리기 함수는 DrawCheck, DrawUncheck, DrawRadioCheck 및 DrawRadioUncheck입니다. X, 빈 사각형, 채워진 줄임표를 포함하는 타원 및 빈 줄임표를 각각 사용하여 사각형을 그립니다.
창 프로시저는 검사 표시 비트맵을 삭제하여 WM_DESTROY 메시지를 처리합니다. GetMenuItemInfo 함수를 사용하여 각 비트맵 핸들을 검색한 다음 함수에 핸들을 전달합니다.
사용자가 메뉴 항목을 선택하면 WM_COMMAND 메시지가 소유자 창으로 전송됩니다. 문자 메뉴의 메뉴 항목에 대해 창 프로시저는 애플리케이션 정의 CheckCharacterItem 함수를 호출합니다. 단락 메뉴의 항목에 대해 창 프로시저는 애플리케이션 정의 CheckParagraphItem 함수를 호출합니다.
문자 메뉴의 각 항목을 독립적으로 선택하고 지울 수 있습니다. 따라서 CheckCharacterItem은 지정된 메뉴 항목의 검사 상태를 전환하기만 하면 됩니다. 먼저 함수는 GetMenuItemInfo 함수를 호출하여 현재 메뉴 항목 상태를 가져옵니다. 그런 다음 MFS_CHECKED 상태 플래그를 전환하고 SetMenuItemInfo 함수를 호출하여 새 상태를 설정합니다.
문자 특성과 달리 한 번에 하나의 단락 맞춤만 선택할 수 있습니다. 따라서 CheckParagraphItem은 지정된 메뉴 항목을 확인하고 메뉴의 다른 모든 항목에서 검사 표시를 제거합니다. 이렇게 하려면 CheckMenuRadioItem 함수를 호출합니다.
다음은 애플리케이션 헤더 파일의 관련 부분입니다.
// Menu-item identifiers for the Character menu
#define IDM_CHARACTER 10
#define IDM_BOLD 11
#define IDM_ITALIC 12
#define IDM_UNDERLINE 13
// Menu-item identifiers for the Paragraph menu
#define IDM_PARAGRAPH 20
#define IDM_LEFT 21
#define IDM_CENTER 22
#define IDM_RIGHT 23
// Function-pointer type for drawing functions
typedef VOID (WINAPI * DRAWFUNC)(HDC hdc, SIZE size);
다음은 애플리케이션 창 프로시저 및 관련 함수의 관련 부분입니다.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
case WM_CREATE:
if (!OnCreate(hwnd))
return -1;
break;
case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_BOLD:
case IDM_ITALIC:
case IDM_UNDERLINE:
CheckCharacterItem(hwnd, LOWORD(wParam));
break;
case IDM_LEFT:
case IDM_CENTER:
case IDM_RIGHT:
CheckParagraphItem(hwnd, LOWORD(wParam));
break;
// Process other commands here.
}
break;
// Process other messages here.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
VOID WINAPI CheckCharacterItem(HWND hwnd, UINT uID)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
// Get a handle to the Character menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Get the state of the specified menu item.
mii.fMask = MIIM_STATE; // information to get
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
// Toggle the checked state.
mii.fState ^= MFS_CHECKED;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
}
VOID WINAPI CheckParagraphItem(HWND hwnd, UINT uID)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
// Get a handle to the Paragraph menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Check the specified item and uncheck all the others.
CheckMenuRadioItem(
hmenuPopup, // handle to menu
IDM_LEFT, // first item in range
IDM_RIGHT, // last item in range
uID, // item to check
MF_BYCOMMAND // IDs, not positions
);
}
BOOL WINAPI OnCreate(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
HBITMAP hbmChecked;
HBITMAP hbmUnchecked;
// Get a handle to the Character menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Create the checked and unchecked bitmaps.
hbmChecked = CreateMenuBitmap(DrawCheck);
hbmUnchecked = CreateMenuBitmap(DrawUncheck);
// Set the check-mark bitmaps for each menu item.
for (uID = IDM_BOLD; uID <= IDM_UNDERLINE; uID++)
{
SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,
hbmUnchecked, hbmChecked);
}
// Get a handle to the Paragraph pop-up menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Create the checked and unchecked bitmaps.
hbmChecked = CreateMenuBitmap(DrawRadioCheck);
hbmUnchecked = CreateMenuBitmap(DrawRadioUncheck);
// Set the check-mark bitmaps for each menu item.
for (uID = IDM_LEFT; uID <= IDM_RIGHT; uID++)
{
SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,
hbmUnchecked, hbmChecked);
}
// Initially check the IDM_LEFT paragraph item.
CheckMenuRadioItem(hmenuPopup, IDM_LEFT, IDM_RIGHT,
IDM_LEFT, MF_BYCOMMAND);
return TRUE;
}
HBITMAP WINAPI CreateMenuBitmap(DRAWFUNC lpfnDraw)
{
// Create a DC compatible with the desktop window's DC.
HWND hwndDesktop = GetDesktopWindow();
HDC hdcDesktop = GetDC(hwndDesktop);
HDC hdcMem = CreateCompatibleDC(hdcDesktop);
// Determine the required bitmap size.
SIZE size = { GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK) };
// Create a monochrome bitmap and select it.
HBITMAP hbm = CreateBitmap(size.cx, size.cy, 1, 1, NULL);
HBITMAP hbmOld = SelectObject(hdcMem, hbm);
// Erase the background and call the drawing function.
PatBlt(hdcMem, 0, 0, size.cx, size.cy, WHITENESS);
(*lpfnDraw)(hdcMem, size);
// Clean up.
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
ReleaseDC(hwndDesktop, hdcDesktop);
return hbm;
}
VOID WINAPI DrawCheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 0, 0, size.cx, size.cy);
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, size.cx, size.cy);
MoveToEx(hdc, 0, size.cy - 1, NULL);
LineTo(hdc, size.cx - 1, 0);
SelectObject(hdc, hbrOld);
}
VOID WINAPI DrawUncheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, hbrOld);
}
VOID WINAPI DrawRadioCheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Ellipse(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Ellipse(hdc, 2, 2, size.cx - 2, size.cy - 2);
SelectObject(hdc, hbrOld);
}
VOID WINAPI DrawRadioUncheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Ellipse(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, hbrOld);
}
VOID WINAPI OnDestroy(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
// Get a handle to the Character menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Get the check-mark bitmaps and delete them.
mii.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo(hmenuPopup, IDM_BOLD, FALSE, &mii);
DeleteObject(mii.hbmpChecked);
DeleteObject(mii.hbmpUnchecked);
// Get a handle to the Paragraph menu.
mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
// Get the check-mark bitmaps and delete them.
mii.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo(hmenuPopup, IDM_LEFT, FALSE, &mii);
DeleteObject(mii.hbmpChecked);
DeleteObject(mii.hbmpUnchecked);
}