提示:
1、因為加入快捷鍵, 本例的基礎代碼和以前不同了; 因為窗體初始化時要先載入快捷鍵資源, 還要攔截快捷鍵消息另作處理.
2、關於快捷鍵的話題, 以前接觸過不少了, 但那些都不是本質:
注冊系統級熱鍵
所有可選的快捷鍵列表
快捷鍵設置控件
快捷鍵的記錄方式
3、我曾經以為快捷鍵不過就是攔截鍵盤消息, 現在看來那是想得太簡單了; 很容易就可以說明: 鍵盤消息是需要焦點的, 而快捷鍵是只對主窗口.
4、使用快捷鍵前應該先建立一個快捷鍵表或建立快捷鍵資源, 本例使用了後者.
快捷鍵一般是執行菜單的功能, 那它是怎麼和菜單掛接的呢? 很簡單: 給每一項快捷鍵和每一個菜單項指定相同的標識即可!
5、在本例中, 窗口建立後, 先用 LoadAccelerators 載入快捷鍵資源並返回快捷鍵資源句柄 hAcc;
GetMessage 從消息隊列中取出消息後, 先用 TranslateAccelerator 檢查是否是快捷鍵消息. 如果是, TranslateAccelerator 會把此快捷鍵消息轉換成菜單消息(WM_COMMAND, 也可能是系統菜單消息: WM_SYSCOMMAND), 然後直接發送給窗口; 如果不是, 再繼續以前的消息傳遞.
其實快捷鍵消息的本質也是鍵盤消息, 不過 TranslateAccelerator 會檢查鍵盤消息的組合是否是我們定義的快捷鍵. 但我們在執行快捷鍵時, 焦點未必就在主窗口, 它是怎麼吧其他窗口的鍵盤消息轉給主窗口的呢?
如果 GetMessage 函數的第二個參數為 0 時, 它會檢索程序中所有窗口的消息, TranslateAccelerator 接過來後, 不管消息來自那個窗口, 都把消息包中的窗口句柄換成主窗口句柄再傳遞.
關於快捷鍵資源文件還是解釋在資源文件下面吧...
本例效果圖:
本例使用的資源文件(TestRes.rc):
MyMenu1 MENUEX
BEGIN
POPUP "菜單"
BEGIN
MENUITEM "菜單_1t Shift+1" ,101
MENUITEM "菜單_2t Ctrl+2" ,102
MENUITEM "菜單_3t Alt+3" ,103
MENUITEM "菜單_4t Shift+Ctrl+Alt+4" ,104
MENUITEM "菜單_5t 5" ,105
MENUITEM "菜單_6t F5" ,106
MENUITEM "菜單_7t Ctrl+A" ,107
MENUITEM "菜單_8t Alt+A" ,108
END
END
MyAccel ACCELERATORS
BEGIN
"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.