2009年1月7日 星期三

[C++] 隨時監控使用者在 Edit Control 的輸入

EditControl_WM_KEYDOWN

如果你希望監控使用者在 Edit Control 的輸入行為, 該怎麼做?

例如:

1. 隨時監控使用者在 Edit Control 的輸入 WM_KEYDOWN, 在顯示前先進行處理

     ex: 使用者的輸入剛好是髒話的第一時間, 程式立即反應.

2. 或者是想建立高度互動性的程式, 偵測 [Enter] [上] [下] [ 左] [右] 的輸入

     ex: 使用者輸入關鍵字的同時, 程式的某個相對應的文字會變成紅色等. 

 

本文大綱

           1. 在 MFC 下實做自己的 Edit Control

           2. 直接用 Win32 解決這個問題

           3. 下載完整範例程式
           4. 不用 MFC 照樣可以吃掉 Edit Control 的 Message

 

在 MFC 下實做自己的 Edit Control

<sol>

這個問題在 MFC 很簡單, 只要寫一個自己的 Edit Control 類別, 繼承 CEdit 就可以了, 然後 override PreTranslateMessage method 即可.

作法如下:

----------------------- MFC 的作法 ------------------------------

class MyEditControl:public CEdit{

       public:

     // 關鍵片段:

       BOOL PreTranslateMessage(MSG* pMsg){
               BOOL bDone=FALSE;


               switch(pMsg->message){
                          case WM_KEYDOWN :
                              bDone=KeyDownProcess(pMsg);
                              break;
                          default:
                              ;
                       };

                       return bDone;
          }// end of PreTranslateMessage

 

BOOL KeyDownProcess (MSG* pMsg){

             switch(wParam){
                case 13:  // 輸入 Enter
                                        break;
                case 8: // Backspace 
                                        break;
                case 37: // 左 
                                        break;
                case 39: // 右
                                        break;
                case 38: // 上
                                         break;
                 case 40: // 下
                                         break;

             }

}

} // end of MyEditControl class

-------------------------------------------------------------------

直接用 Win32 解決這個問題

可是呢? 如果不想用體積龐大的 MFC, 那 Win32 API 要怎麼做呢?

<sol>

只要用 SetWindowLongPtr 指令, 將 Edit Control 的 Window Procedure 轉成你的就可以了.

例如:

SetWindowLongPtr(hEdit1,GWLP_WNDPROC,
                                         (LONG_PTR) 你的 Message 處理函式);

 

詳細作法如下:

-------------------------- Win32 的處理方式 -------------------------
A. 設定重新導向 edit control 的 window procedure   

        HWND hEdit1 = GetDlgItem(ghwndMainDlg, IDC_EDIT1);

            // Step 1: 先取得原先的 Edit Control Window Procedure
            oldEditControlProc=GetWindowLongPtr
                                                                 (hEdit1,GWLP_WNDPROC);
            if(oldEditControlProc==0){
                MessageBox(NULL,
                        _T("取得原先的 Edit Control Window Procedure 處理失敗"),
                        _T("GetWindowLongPtr Error"),MB_OK);
                return FALSE;
            }

            // Step 2: 重新導向 Edit Control Window Procedure 到
            //                自己的特別處理

            SetWindowLongPtr(hEdit1,GWLP_WNDPROC,
                          (LONG_PTR) EditControlProc);

 

B. Edit Control Message 特別處理函式

LRESULT CALLBACK EditControlProc(HWND hDlg,UINT message,UINT wParam,LONG lParam){

// 確定 oldEditControlProc 一定有位址
    _ASSERTE(oldEditControlProc!=0);

    switch (message)
    {
        case WM_KEYDOWN:   //  收到 Edit1 WM_KEYDOWN message
              switch(wParam){
                case 13:  // 輸入 Enter
                                     break;
                case 8: // Backspace 
                                        break;
                case 37: // 左 
                                        break;
                case 39: // 右
                                        break;
                case 38: // 上
                                        break;
                 case 40: // 下
                                        break;
            }
            return TRUE;
    }
    // 標明我們不處理這個 message, 交給別人處理
    return CallWindowProc((WNDPROC)
                                     oldEditControlProc,hDlg,message,wParam,lParam);
}

 

下載完整範例程式

  Win32API 處理方法關鍵片段   Win32API 處理方法完整專案

 

 

 

附錄: 

不用 MFC 照樣可以吃掉 Edit Control 的 Message

MFC 的 PreTranslateMessage 很厲害, 可以讓你決定是否要送出指定的 Message, 如果你在 Win32 裡面當然也可以直接在 message-loop 直接處理. 可是如果我們想直接在某個 Dialog 的 Edit Control 裡面, 吃掉某個使用者的按鍵訊息, 不傳出去, 那該怎麼做?

我很懶惰, 不想自己寫 CreateWindowEx 從頭開始建立 Edit Control, 所以我們可以使用 WM_GETDLGCODE 來解決我們的問題.

 

關鍵片段:

switch(message){

     case WM_GETDLGCODE:

             return DLGC_WANTCHARS;  // 表示接下來的 WM_CHAR
                                                                       //   會被吃掉, 不會送到後面處理.

}

 

很簡陋的例子:

int bEat=0; // 是否吃掉目前使用者的輸入

LRESULT CALLBACK EditControlProc(
                     HWND hDlg,UINT message,UINT wParam,LONG lParam){

    switch (message)     {        
        case WM_KEYDOWN:   
              switch(wParam){
                case 13:  // 輸入 Enter

                       if(符合狀態(){

                            bEat=1;  // 符合狀態時, 執行吃掉的動作
                      }
                       break;
            }

        case WM_GETDLGCODE:

           if(bEat==1){
                   bEat=0;

                    return DLGC_WANTCHARS;  // 表示接下來的 WM_CHAR
                                                                              //   會被吃掉, 不會送到後面處理.

           }

    } // end of switch
   

   if(bEat==1){
           return TRUE;

   }else{
               return CallWindowProc(
                (WNDPROC)oldEditControlProc,hDlg,
                                         message,wParam,lParam);

   } 
}

 

Enjoy.   

by Jing.

沒有留言:

張貼留言