提示:
1、因為加入快速鍵, 本例的基礎代碼和以前不同了; 因為表單初始化時要先載入快速鍵資源, 還要攔截快速鍵訊息另作處理.
2、關於快速鍵的話題, 以前接觸過不少了, 但那些都不是本質:
註冊系統級熱鍵
所有可選的快速鍵列表
快速鍵設定控制項
快速鍵的記錄方式
3、我曾經以為快速鍵不過就是攔截鍵盤訊息, 現在看來那是想得太簡單了; 很容易就可以說明: 鍵盤訊息是需要焦點的, 而快速鍵是只對主視窗.
4、使用快速鍵前應該先建立一個快速鍵表或建立快速鍵資源, 本例使用了後者.
快速鍵一般是執行菜單的功能, 那它是怎麼和菜單掛接的呢? 很簡單: 給每一項快速鍵和每一個功能表項目指定相同的標識即可!
5、在本例中, 視窗建立後, 先用 LoadAccelerators 載入快速鍵資源並返回快速鍵資源控制代碼 hAcc;
GetMessage 從訊息佇列中取出訊息後, 先用 TranslateAccelerator 檢查是否是快速鍵訊息. 如果是, TranslateAccelerator 會把此快速鍵訊息轉換成菜單訊息(WM_COMMAND, 也可能是系統功能表訊息: WM_SYSCOMMAND), 然後直接發送給視窗; 如果不是, 再繼續以前的訊息傳遞.
其實快速鍵訊息的本質也是鍵盤訊息, 不過 TranslateAccelerator 會檢查鍵盤訊息的組合是否是我們定義的快速鍵. 但我們在執行快速鍵時, 焦點未必就在主視窗, 它是怎麼吧其他視窗的鍵盤訊息轉給主視窗的呢?
如果 GetMessage 函數的第二個參數為 0 時, 它會檢索程式中所有視窗的訊息, TranslateAccelerator 接過來後, 不管訊息來自那個視窗, 都把訊息包中的視窗控制代碼換成主視窗控制代碼再傳遞.
關於快速鍵資源檔還是解釋在資源檔下面吧...
本例:
本例使用的資源檔(TestRes.rc):
MyMenu1 MENUEXBEGIN POPUP "菜單" BEGIN MENUITEM "菜單_1\t Shift+1" ,101 MENUITEM "菜單_2\t Ctrl+2" ,102 MENUITEM "菜單_3\t Alt+3" ,103 MENUITEM "菜單_4\t Shift+Ctrl+Alt+4" ,104 MENUITEM "菜單_5\t 5" ,105 MENUITEM "菜單_6\t F5" ,106 MENUITEM "菜單_7\t Ctrl+A" ,107 MENUITEM "菜單_8\t Alt+A" ,108 ENDENDMyAccel ACCELERATORSBEGIN "1", 101, VIRTKEY, SHIFT, NOINVERT "2", 102, VIRTKEY, CONTROL, NOINVERT "3", 103, VIRTKEY, ALT, NOINVERT "4", 104, VIRTKEY, SHIFT, CONTROL, ALT, NOINVERT "5", 105, VIRTKEY, NOINVERT, NOINVERT VK_F5, 106, VIRTKEY, NOINVERT, NOINVERT "A", 107, VIRTKEY, CONTROL "a", 108, ASCII, ALT END
注釋:
1、快速鍵資源檔關鍵字: ACCELERATORS, 需要取個名給程式中的 LoadAccelerators 使用, 這裡是: MyAccel.
2、快速鍵資源檔可以有五組參數, 從後面說: NOINVERT 是讓被啟用的菜單的頂層菜單不閃爍(但我去掉也沒看出來).
它前面是修飾鍵: SHIFT、CONTROL、ALT.
再前面有兩個選擇: VIRTKEY、ASCII, VIRTKEY 指示快速鍵要用 虛擬鍵碼 說明; ASCII 指示用 ASCII 碼說明快速鍵. 譬如例子中的 VK_F5 就是典型的虛擬鍵碼. 但常規數字鍵和字母鍵沒有對應的虛擬鍵碼, 對數字(非小鍵盤數字)來講, 這兩者沒有區別; 特別注意: VIRTKEY 時指示的字母須大寫、ASCII 指示的字母須小寫.
3、這其中最重要的就是第二個參數了, 它必須和菜單的標識相對應.
4、另外, 菜單資源也和以前有點區別: 在 \t (Tab 的意思) 後面用文本說明了對應的快速鍵, 不過這隻是顯示效果而已, 有沒有都不影響功能.
本例代碼檔案:
program Project1;{$R 'TestRes.res' 'TestRes.rc'}uses Windows, Messages;{收到 WM_COMMAND 訊息時需要做的工作}procedure OnCommand(h: HWND; wParam: Integer);var W: Word; arr: array[Byte] of Char;begin W := LoWord(wParam); case W of 101..108: begin GetMenuString(GetMenu(h), W, arr, Length(arr), MF_BYCOMMAND); SetWindowText(h, arr); end; end;end;function WndProc(wnd: HWND; msg: UINT; wParam: Integer; lParam: Integer): Integer; stdcall;begin Result := 0; case msg of WM_COMMAND : OnCommand(wnd, wParam); {收到 WM_COMMAND 訊息後調用 OnCommand 過程} WM_DESTROY : PostQuitMessage(0); else Result := DefWindowProc(wnd, msg, wParam, lParam); end;end;function RegMyWndClass: Boolean;var cls: TWndClass;begin cls.style := CS_HREDRAW or CS_VREDRAW; cls.lpfnWndProc := @WndProc; cls.cbClsExtra := 0; cls.cbWndExtra := 0; cls.hInstance := HInstance; cls.hIcon := 0; cls.hCursor := LoadCursor(0, IDC_ARROW); cls.hbrBackground := HBRUSH(COLOR_WINDOW + 1); cls.lpszMenuName := 'MyMenu1'; cls.lpszClassName := 'MyWnd'; Result := RegisterClass(cls) 0;end;{程式入口}const tit = 'New Form'; ws = WS_OVERLAPPEDWINDOW; x = 100; y = 100; w = 300; h = 180;var hWnd: THandle; Msg : TMsg; hAcc: HACCEL; {定義快速鍵資源控制代碼}begin RegMyWndClass; hWnd := CreateWindow('MyWnd', tit, ws, x, y, w, h, 0, 0, HInstance, nil); ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); hAcc := LoadAccelerators(HInstance, 'MyAccel'); {載入快速鍵資源} while(GetMessage(Msg, 0, 0, 0)) do begin if (TranslateAccelerator(hwnd, hAcc, Msg) = 0) then {攔截並處理快速鍵訊息} begin TranslateMessage(Msg); DispatchMessage(Msg); end; end;end.