第10章 菜单及其它资源_10.3 键盘加速键
①对于单个窗口应用程序可以直接捕获WM_KEYDOWN或WM_SYSKEYDOWN消息。
②对于多窗口的应用程序,因为键盘消息只能发给具有输入焦点的窗口。但使用键盘加速键却可以让Windows通过TranslateAccelerator函数将WM_COMMAND\WM_SYSCOMMAND消息发送给特定的窗口过程。
③如果多窗口定义了同一个键盘加速键,可以在主窗口处理这个逻辑,可以不必复制到每个子窗口过程中去。
10.3.2 加速键表
(1)加速键
ID |
击键组合 |
1、可自定义ID 2、与菜单绑定——输入菜单ID |
①虚拟键+Shift\Ctrl\Alt组合 ②ASCII字符+Shift\Ctrl\Alt组合如^C与Ctrl组合。 |
(2)加载加速键表
①HANDLE hAccel =LoadAccelerators(hInstance,TEXT(“MyAccelerators”));
②HANDLE hAccel =LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));
③HANDLE hAccel = LoadAccelerators(hInstance,TEXT(“#40001”));
(3)翻译加速键
标准消息循环 |
使用键盘加速键表 |
while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } |
while (GetMessage (&msg, NULL, 0, 0)) { if(!TranslateAccelerator(hwnd,hAccel,&msg) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } |
①TranslateAccelerator检查msg是否是键盘消息,如果是在hAccel中去进行加速键的匹配。找到匹配后会向窗口hwnd发送WM_COMMAND或WM_SYSCOMMAND消息。
②为什么消息结构中有个hwnd字段,TranslateAccelerator的参数中也有hwnd?理由如下:
A、msg是由GetMessage填充的,当第2个参数为NULL时,检索所有窗口的消息,GetMessage返回时,msg.hwnd是得到该消息的窗口句柄(焦点窗口)。
B、经过TranslateAccelerator将键盘消息翻译成WM_COMMAND\WM_SYSCOMMAND消息后,消息并不一定要在焦点窗口(msg.hwnd)中处理。如在多窗口程序中,加速键的处理可能会放在同一个主窗口过程处理。所以可以将消息重新发送到参数hwnd指定的窗口进行处理。
③在模态对话框或消息框拥有焦点时,TranslateAccelerator不翻译键盘消息,因为这些窗口的消息不通过程序的消息循环。
(4)接收加速键消息:WM_COMMAND、WM_SYSCOMMAND(加速键对应系统菜单项时发送)
参数 |
加速键 |
菜单 |
控件 |
LOWORD(wParam) |
加速键ID |
菜单ID |
控件ID |
HIWORD(wParam) |
1 |
0 |
通知码 |
lParam |
0 |
0 |
子窗口句柄 |
说明:
①如果键盘加速键对应某菜单项时,会发送WM_INITMENU、WM_INITMENUPOPUP、WM_MENUSELECT消息。可以处理启用或禁用弹出菜单的菜单项逻辑。同时,如果该菜单项禁用或变灰,则不会发送WM_COMMAND或WM_SYSCOMMAND消息
②对没有映射到任何菜单项的加速键,TranslateAccelerator也会发送WM_COMMAND消息。
10.3.3 带有菜单和加速键的POPPAD程序
(1)启用菜单项——处理WM_INITMENUPOPUP消息
参数 |
说明 |
wParam |
下拉菜单或弹出菜单句柄 |
lParam |
LOWORD(lParam):菜单位置相对索引值 |
HIWORD(lParam):系统菜单标记 |
(2)菜单属性设置
①撤消(UNDO)菜单设置——向编控件查询EM_CANUNDO是否可以撤消
EnableMenuItem(wParam,IDM_UNDO,
SendMessage(hwndEdit,EM_CANUNDO,0,0)?MF_ENABLED:MF_GRAYED);
②粘贴(Paste)设置——当剪贴板包含文本,才被启用。检测剪贴板是否CF_TEXT格式
EnableMenuItem(wParam,IDM_PASTE,
IsClipboardFormatAvailable(CF_TEXT)?MF_ENABLED:MF_GRAYED);
③Cut\Copy\Delete只有在文本被选中时,才启用
iSelect = SendMessage(hwndEdit,EM_GETSEL,0,0); //向编辑框发送EM_GETSEL查询
LOWORD(iSelect):第1个被选中的字符
HIWORD(iSelect):结束位置,选中文本后面的第一个字符。
if(LOWORD(iSelect) == HIWORD(iSelect)) //说明文本没被选中
(2)处理菜单项——WM_CLOSE和WM_QUERYENDSESSION
①WM_CLOSE:用户选择关闭窗口里,发送的
②WM_QUERYENDSESSION消息
Windows在关机的时候会想(向)所有顶层窗口广播一个消息WM_QUERYENDSESSION,其lParam参数可以区分是关机还是注销用户(注销用 户时lParam是ENDSESSION_LOGOFF)。然后Windows会等到所有的应用程序都对这个消息返回TRUE才会关机,因此,只要我们的应用程序对这个消息的处理返回FALSE,Windows就不会关机了。
【PopPad2程序】
/*------------------------------------------------------------ POPPAD2.C -- Popup Editor Version2 (includes menu) (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #include "resource.h" #define ID_EDIT 1 static TCHAR szAppName[] = TEXT("PopPad2"); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HACCEL hAccel; 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, szAppName); 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, // window class name TEXT("edit"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); hAccel = LoadAccelerators(hInstance, szAppName); while (GetMessage(&msg, NULL, 0, 0)) { //处理键盘加速键 if (!TranslateAccelerator(hwnd, hAccel, &msg)) { //非键盘加速键消息的处理 TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } int AskConfirmation(HWND hwnd) { return MessageBox(hwnd, TEXT("Really Want to close PopPad2?"), szAppName, MB_YESNO | MB_ICONQUESTION); } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; int iSelect, iEnable; switch (message) { case WM_CREATE: hwndEdit = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd, (HMENU)ID_EDIT, ((LPCREATESTRUCT)lParam)->hInstance, NULL); return 0; case WM_SETFOCUS: SetFocus(hwndEdit); return 0; case WM_INITMENUPOPUP: //lParam:item position and indicator if (lParam == 1) //Edit菜单 { //Undo菜单项 EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO, SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED); //Paste菜单项 EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE, IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED); iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0); if (HIWORD(iSelect) == LOWORD(iSelect)) iEnable = MF_GRAYED; else iEnable = MF_ENABLED; EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable); return 0; } break; case WM_COMMAND: if (lParam) //控件消息 { if (LOWORD(wParam) == ID_EDIT) { if (HIWORD(wParam) == EN_ERRSPACE || HIWORD(wParam) == EN_MAXTEXT) { MessageBox(hwnd, TEXT("Edit Control out of space."), szAppName, MB_OK | MB_ICONSTOP); } } } else //加速键或菜单消息 { switch (LOWORD(wParam))//加速键ID或菜单ID,这里两个ID相等 { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: case IDM_FILE_PRINT: MessageBeep(0); return 0; case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0, 0); return 0; case IDM_EDIT_UNDO: SendMessage(hwndEdit, WM_UNDO, 0, 0); return 0; case IDM_EDIT_CUT: SendMessage(hwndEdit, WM_CUT, 0, 0); return 0; case IDM_EDIT_COPY: SendMessage(hwndEdit, WM_COPY, 0, 0); return 0; case IDM_EDIT_PASTE: SendMessage(hwndEdit, WM_PASTE, 0, 0); return 0; case IDM_EDIT_CLEAR: SendMessage(hwndEdit, WM_CLEAR, 0, 0); return 0; case IDM_EDIT_SELECT_ALL: SendMessage(hwndEdit, EM_SETSEL, 0, -1); return 0; case IDM_HELP_HELP: MessageBox(hwnd, TEXT("Help not yet implement!"), szAppName, MB_OK | MB_ICONEXCLAMATION); return 0; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("POPPAD2(c) Charles Petzold 1998!"), szAppName, MB_OK | MB_ICONINFORMATION); return 0; } } break; case WM_SIZE: MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); return 0; case WM_CLOSE: //选择窗口关闭按钮时收到该消息 if (IDYES == AskConfirmation(hwnd)) DestroyWindow(hwnd); return 0; case WM_QUERYENDSESSION: //系统关机或注销时收到该消息 if (IDYES == AskConfirmation(hwnd)) return 1; else return 0; return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 PopPad2.rc 使用 // #define IDM_FILE_NEW 40001 #define IDM_FILE_OPEN 40002 #define IDM_FILE_SAVE 40003 #define IDM_FILE_SAVE_AS 40004 #define IDM_FILE_PRINT 40005 #define IDM_APP_EXIT 40006 #define IDM_EDIT_UNDO 40007 #define IDM_EDIT_CUT 40008 #define IDM_EDIT_COPY 40009 #define IDM_EDIT_PASTE 40010 #define IDM_EDIT_CLEAR 40011 #define IDM_EDIT_SELECT_ALL 40012 #define IDM_HELP_HELP 40013 #define IDM_APP_ABOUT 40014 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40043 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//PopPad2.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Menu // POPPAD2 MENU BEGIN POPUP "&File" BEGIN MENUITEM "&New", IDM_FILE_NEW MENUITEM "&Open", IDM_FILE_OPEN MENUITEM "&Save", IDM_FILE_SAVE MENUITEM "Save &As...", IDM_FILE_SAVE_AS MENUITEM SEPARATOR MENUITEM "&Print", IDM_FILE_PRINT MENUITEM SEPARATOR MENUITEM "E&xit", IDM_APP_EXIT END POPUP "&Edit" BEGIN MENUITEM "&Undo\tCtrl+Z", IDM_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE MENUITEM "De&lete\tDel", IDM_EDIT_CLEAR MENUITEM SEPARATOR MENUITEM "&Select All", IDM_EDIT_SELECT_ALL END POPUP "&Help" BEGIN MENUITEM "&Help...", IDM_HELP_HELP MENUITEM "&About PopPad2...", IDM_APP_ABOUT END END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. POPPAD2 ICON "POPPAD2.ICO" ///////////////////////////////////////////////////////////////////////////// // // Accelerator // POPPAD2 ACCELERATORS BEGIN VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT VK_INSERT, IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT VK_DELETE, IDM_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT VK_INSERT, IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT VK_BACK, IDM_EDIT_UNDO, VIRTKEY, ALT, NOINVERT VK_F1, IDM_HELP_HELP, VIRTKEY, NOINVERT "^C", IDM_EDIT_COPY, ASCII, NOINVERT "^V", IDM_EDIT_PASTE, ASCII, NOINVERT "^X", IDM_EDIT_CUT, ASCII, NOINVERT "^Z", IDM_EDIT_UNDO, ASCII, NOINVERT END #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED