標籤:
一直想整理鍵盤事件的調用順序,剛好看見園子裡的這篇文章,寫的不錯,就轉載了:http://www.cnblogs.com/tedzhao/archive/2010/09/07/1820557.html
在WinForm控制項上我們可以看到很多關於鍵盤訊息處理的方法,比如OnKeyDown, OnKeyPress, ProcessCmdKey, ProcessDialogKey,IsInputKey等等,那麼這些方法是如何被組織的,每一個方法的具體含義又是什麼哪?Win32的鍵盤訊息又是如何到達控制項上的這些方法的,本文將著重闡述這些問題,對.Net WinForm控制項的鍵盤訊息處理過程進行剖析。
1. WinForm訊息迴圈
大家都知道WinForm也是依賴於底層的訊息機制的,通常我們的WinForm應用程式會以如下方式啟動:
Application.Run(new Form());
上面的代碼將會在當前線程啟動一個訊息迴圈,並且顯示指定表單。
反編譯Application類的Run方法,我們可以看到這一點:
public static void Run(Form mainForm)
{
ThreadContext.FromCurrent().RunMessageLoop(-1,new ApplicationContext(mainForm));
}
啟動訊息迴圈之後,作業系統就會將使用者對於當前應用程式的UI輸入轉換為Windows訊息發給當前線程進行處理。本文的重點不在於講述Windows訊息機制,而在於底層訊息到達.Net這一層後,WinForm控制項是如何處理的。
2. 訊息處理
從上面可以看到通過ThreadContext類型的RunMessageLoop方法,構建了訊息迴圈。那麼對於一個特定的Windows訊息,ThreadContext又是如何處理的哪?
分析ThreadContext的代碼可以發現其調用關係如下:
在LocalModalMessageLoop方法中我們就可以看到對於Windows訊息的處理了:
private bool LocalModalMessageLoop()
{
// ...
if(!PreTranslateMessage(ref msg))
{
// ...
UnsafeNativeMethods.TranslateMessage(ref msg);
UnsafeNativeMethods.DispatchMessage(ref msg);
}
// ...
}
可以發現對於一個特定的Windows訊息,分為兩個階段進行處理:
- PreTranslateMessage
- DispatchMessage
WinForm控制項訊息的處理將從這兩個地方開始。
2.1 PreTranslateMessage
PreTranslateMessage提供了一個時機,來決定是否應該Dispatch這個訊息,如果傳回值為False,這個訊息才會派發給WinForm控制項。
PreTranslateMessage分為兩個層次,第一優先調用當前應用程式的IMessageFilter來進行處理,使用者可以在這一層進行訊息預先處理或者訊息過濾。如果沒有被過濾掉,調用當前控制項的PreProcessMessage方法進行訊息預先處理。
Control類型的PreProcessMessage處理流程如下:
對於WM_KeyDown訊息,其預先處理Control類上有三個時機:ProcessCmdKey,IsInputKey以及ProcessDialogKey;對於WM_KeyChar訊息,其預先處理有兩個時機:IsInputChar和ProcessDialogChar。
ProcessCmdKey預設用來處理快速鍵以及菜單快速鍵,此方法會遞迴調用父控制項。如果傳回值為False,繼續調用IsInputKey,決定是否引發KeyDown事件。如果不是InputKey,調用ProcessDialogKey來檢查該鍵是否為導航鍵,或者進行一些特別的處理,此方法會遞迴調用父控制項的處理。
IsInputChar決定輸入字元是否為一般字元,如果傳回值為True會引發KeyPress事件。傳回值為False會調用ProcessDialogChar,ProcessDialogChar預設用來處理Mnemonic鍵,例如控制項的文本為“&OK”, 對於Char“O”的處理。
2.2 DispatchMessage
如果PreTranslateMessage沒有過濾掉該Windows訊息的話,該訊息將會派發到控制項,交由控制項的WndProc函數進行處理。
是控制項的處理流程:
訊息到達WnProc之後,會交由ProcessKeyMessage,ProcessKeyPreview以及ProcessKeyEventArgs處理。每一個方法都會返回一個Boolean值,表明控制項是否已經處理了該訊息。
ProcessKeyMessage會處理所有由WndProc過來的所有鍵訊息,首先會調用父控制項的ProcessKeyPreview函數,如果返回True,表明父控制項已經處理。否則調用ProcessKeyEventArgs來觸發控制項的KeyDown,KeyPress,KeyUp事件。
3. 結語
本文著重講述了WinForm控制項對於鍵盤訊息的處理,分析了訊息預先處理以及處理兩個階段的各個函數。在進行三方控制項的開發中可以根據需要重載這些函數,另外也可從其設計以及實現思路中獲得更多啟發。
WinForm 控制項鍵盤訊息處理剖析(轉)