1. 程式人生 > >MFC中子控制元件響應鍵盤訊息

MFC中子控制元件響應鍵盤訊息

在MFC的視窗和控制元件程式設計時,經常需要使某個控制元件主動響應使用者的鍵盤訊息,哪怕該控制元件並沒有輸入功能。
為方便說明,假設你的主視窗為A,需要相應鍵盤訊息的子控制元件為B為一個圖片控制元件(Picture control)。
此時,你需要做如下工作:


1. 派生一個自己的類作為B的類
Picture control預設的ID是IDC_STATIC,這種ID是不能定義變數的,因此把該ID改成別的名字,如IDC_TEST。
然後新增一個控制元件變數,先選擇CStatic,確認,讓MFC生成相應程式碼(主要是DDX_Control等程式碼。如果你熟悉MFC,也可以不使用類嚮導自己寫)。
最後新建一個MFC類CMyStatic,繼承自CStatic,將程式碼中的CStatic替換成CMyStatic。


2. 重寫CMyStatic的PreTranslateMessage虛擬函式


PreTranslateMessage顧名思義,是在訊息路由之前,對訊息進行預處理。
注意最重要的是PreTranslateMessage的返回值,預設是FALSE。如果返回TRUE,該訊息將無法再往下傳遞!
按鍵訊息的路由路徑,可由下圖所示:
按鍵訊息路由示意圖
主訊息迴圈執行緒類CWinThread收到按鍵訊息後,由當前焦點視窗(此時為子控制元件)的PreTranslateMessage預處理後再逐個向父視窗傳遞,任何時候返回TRUE,都會終止訊息進一步的處理。
此鏈式處理直到頂級視窗(理解為其父視窗為桌面)的預處理完成後,再由主訊息迴圈執行緒類CWinThread::DispatchMessage分發給焦點視窗(圖中OnWndMsg響應該訊息)。
鍵盤的訊息可以在OnChar中響應,也可以在OnKey*中響應。取決於應用的需求。
可以結合CWinThread中的PumpMessage程式碼加深一下理解:

    GetMessage(...);
    if ( !AfxPreTranslateMessage(...)// 預處理!實現為從子到父鏈式處理
    {
        // 如果預處理返回FALSE,則分發訊息
        ::TranslateMessage(...);
        ::DispatchMessage(...);
    }

按鍵訊息從焦點控制元件到父視窗先預處理一遍再分發,目的就是為了讓整個父子視窗體系都能獲得處理訊息的機會,給程式設計師截獲和處理訊息提供最大的靈活性。掌握和理解這個訊息路徑是非常重要的。


3. 設定視窗焦點
由於在訊息路由中,鍵盤訊息永遠只能傳給當前焦點所在的視窗或空間。因此,需要在A的OnInitDialog中設定控制元件B作為視窗焦點。
注意OnInitDialog的返回值!如果返回TRUE,則表示無論你程式碼設定了誰為焦點,最終都使用A視窗的預設控制元件作為焦點!因此一定要返回FALSE!

    // 設定test控制元件為焦點,以便相應鍵盤事件
    m_test.SetFocus();

    // 返回TRUE則使用預設的焦點(按鈕),因此必須返回FALSE
    return FALSE;  // return TRUE  unless you set the focus to a control

MFC自動生成的程式碼中隱含了大量細節。如各個虛擬函式的功能,每個函式的返回值等等。如果弄錯,都會導致完全不同的結果。這需要編碼者對MFC的低層細節具備較高的掌控能力。
當然,這也是MFC備受詬病的地方。
本文例項程式碼下載連結為:http://download.csdn.net/detail/lsldd/9533217