菜单应该算是比较简单的资源了,因为菜单的设计跟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中的背景,然后将客户区设为无效,重新刷新一遍。
计时器的内容比较简单,就是开始时发送计时器消息,结束时发送删除计时器的消息。唯一要注意的是,开始计时以后,计时器的开始就被设为灰色的了,不能再使用了,直到结束为止。同理也这样设计了计时器的结束。