菜单应该算是比较简单的资源了,因为菜单的设计跟VB很像,图形界面的,你只要写菜单按钮的响应程序就行了。每个菜单都有自己的ID。菜单的消息是通过WM_COMMAND传给系统的,通过消息的wParam的低字节,就能获得ID号。需要注意的是,你最好把ID设成宏一样,见到名字就能看懂意思,不要用原配的那种数字。菜单的属性框中,还能选择菜单是否被选中,能否使用,是否变灰等等。
下来看看程序:
/*-----------------------------------------
MENUDEMO.C -- Menu Demonstration
(c) Charles Petzold, 1998
-----------------------------------------*/
#include <windows.h>
#include "resource.h"
#define ID_TIMER 1
#define MAX_COLOR 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName[] = TEXT ("MenuDemo") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
//装载菜单
LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),
hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu ;
HDC hdc;
//定义一个数组,里面存储着背景菜单中的不同颜色消息
static TCHAR idColor [MAX_COLOR] = { IDM_BKGND_WHITE, IDM_BKGND_LTGRAY, IDM_BKGND_GRAY,
IDM_BKGND_DKGRAY, IDM_BKGND_BLACK } ;
switch (message)
{
//菜单消息
case WM_COMMAND:
hMenu = GetMenu (hwnd) ;
hdc = GetDC(hwnd);
//
switch (LOWORD (wParam))
{
//暂时没有其他操作,只发出蜂鸣
case IDM_FILE_NEW:
case IDM_FILE_OPEN:
case IDM_FILE_SAVE:
case IDM_FILE_SAVE_AS:
MessageBeep (0) ;
return 0 ;
//发出关闭窗口消息
case IDM_APP_EXIT:
SendMessage (hwnd, WM_CLOSE, 0, 0) ;
return 0 ;
//暂时没有其他操作
case IDM_EDIT_CUT:
case IDM_EDIT_COPY:
case IDM_EDIT_PASTE:
case IDM_EDIT_CLEAR:
MessageBeep (0) ;
return 0 ;
//白色
case IDM_BKGND_WHITE:
for(int i = 0;i < MAX_COLOR ; i++)
{
if(idColor[i] == IDM_BKGND_WHITE)
CheckMenuItem (hMenu, idColor[i], MF_CHECKED) ;
//
else
{
CheckMenuItem (hMenu, idColor[i], MF_UNCHECKED) ;
}
}
//改变wndclacc类里面的背景
SetClassLong (hwnd, GCL_HBRBACKGROUND,
(LONG) GetStockObject (WHITE_BRUSH)) ;
//刷新
InvalidateRect (hwnd, NULL, TRUE) ;
return 0;
//浅灰
case IDM_BKGND_LTGRAY:
for(int i = 0;i < MAX_COLOR ; i++)
{
if(idColor[i] == IDM_BKGND_LTGRAY)
CheckMenuItem (hMenu, idColor[i], MF_CHECKED) ;
else
{
CheckMenuItem (hMenu, idColor[i], MF_UNCHECKED) ;
}
}
SetClassLong (hwnd, GCL_HBRBACKGROUND,
(LONG) GetStockObject (LTGRAY_BRUSH)) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0;
//灰色
case IDM_BKGND_GRAY:
for(int i = 0;i < MAX_COLOR ; i++)
{
if(idColor[i] == IDM_BKGND_GRAY)
CheckMenuItem (hMenu, idColor[i], MF_CHECKED) ;
else
{
CheckMenuItem (hMenu, idColor[i], MF_UNCHECKED) ;
}
}
SetClassLong (hwnd, GCL_HBRBACKGROUND,
(LONG) GetStockObject (GRAY_BRUSH)) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0;
//深灰
case IDM_BKGND_DKGRAY:
for(int i = 0;i < MAX_COLOR ; i++)
{
if(idColor[i] == IDM_BKGND_DKGRAY)
CheckMenuItem (hMenu, idColor[i], MF_CHECKED) ;
else
{
CheckMenuItem (hMenu, idColor[i], MF_UNCHECKED) ;
}
}
SetClassLong (hwnd, GCL_HBRBACKGROUND,
(LONG) GetStockObject (DKGRAY_BRUSH)) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0;
case IDM_BKGND_BLACK:
for(int i = 0;i < MAX_COLOR ; i++)
{
if(idColor[i] == IDM_BKGND_BLACK)
CheckMenuItem (hMenu, idColor[i], MF_CHECKED) ;
else
{
CheckMenuItem (hMenu, idColor[i], MF_UNCHECKED) ;
}
}
SetClassLong (hwnd, GCL_HBRBACKGROUND,
(LONG) GetStockObject (BLACK_BRUSH)) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
//打开计时器
case IDM_TIMER_START:
if (SetTimer (hwnd, ID_TIMER, 1000, NULL))
{
//不能再次打开
EnableMenuItem (hMenu, IDM_TIMER_START, MF_GRAYED) ;
//可以关闭计时器
EnableMenuItem (hMenu, IDM_TIMER_STOP, MF_ENABLED) ;
}
return 0 ;
//关闭计时器
case IDM_TIMER_STOP:
//关闭计时器
KillTimer (hwnd, ID_TIMER) ;
//可以再次打开
EnableMenuItem (hMenu, IDM_TIMER_START, MF_ENABLED) ;
//不能关闭计时器
EnableMenuItem (hMenu, IDM_TIMER_STOP, MF_GRAYED) ;
return 0 ;
case IDM_APP_HELP:
MessageBox (hwnd, TEXT ("自己百度"),
szAppName, MB_ICONEXCLAMATION | MB_OK) ;
return 0 ;
case IDM_APP_ABOUT:
MessageBox (hwnd, TEXT ("Menu Demonstration Program\n")
TEXT ("(c) Charles Petzold, 1998"),
szAppName, MB_ICONINFORMATION | MB_OK) ;
return 0 ;
ReleaseDC(hwnd,hdc);
}
break ;
case WM_TIMER:
MessageBeep (0) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
在创建窗口时,就要加载菜单。程序中前两个菜单里的内容都没啥作用。只有文件->退出导致一个关闭窗口的消息WM_CLOSE。这个消息会导致WM_DESTROY。
有用的就是改变背景以及计时器,以及帮助。改变背景时,源程序玩了一个很投机取巧的花样,大家可以看Charles Petzold那本书,我在这里使用·了一个更为常见的作法,就是在每个消息下,写他自己的相应。但是,为了保证背景菜单里的内容始终保持单选效果,需要在每个消息下,把背景菜单中的别的选项置为没有选中,而把自己设为选中。而改变背景,也不像我一开始想象的使用画图的方法,而是直接改变wndclass中的背景,然后将客户区设为无效,重新刷新一遍。
计时器的内容比较简单,就是开始时发送计时器消息,结束时发送删除计时器的消息。唯一要注意的是,开始计时以后,计时器的开始就被设为灰色的了,不能再使用了,直到结束为止。同理也这样设计了计时器的结束。