1. 程式人生 > >MFC(10)解決回車鍵 ESC 預設關閉視窗的一般方法

MFC(10)解決回車鍵 ESC 預設關閉視窗的一般方法

 在一般情況下編寫的對話方塊程式,使用者在執行的時候,如果不注意按下了ENTER或者ESC鍵,程式就會立刻退出,之所以會這樣,是因為按下Enter鍵時,Windows就會自動去找輸入焦點落在了哪一個按鈕上,當獲得焦點的按鈕的四周將被點線矩形包圍。如果所有按鈕都沒有獲得輸入焦點,Windows 就會自動去尋找程式或資源所指定的預設按鈕(預設按鈕邊框較粗)。如果對話方塊沒有預設按鈕,那麼即使對話方塊中沒有OK按鈕,OnOK函式也會自動被呼叫,對於一個普通的對話方塊程式來說,OnOK函式的呼叫,以為著程式會立刻退出。為了使Enter鍵無效,最簡單的辦法就是將CExDlg的OnOK函式寫成空函式,然後針對OK按鈕寫一個新的函式來響應。ESC鍵的原理也是如此,它是預設和OnCancel函式對映在一起的。對於ESC鍵,需要自己過載 CDialog類的PreTranslateMessage函式,當發現是ESC鍵的時候,過濾掉這個訊息或者是替換掉這個訊息。  


  一下是簡單的程式碼示例:  

  【方法1】  
  可以先過載OnOK函式  
  voidCTestDlg::OnOK()  
  {     //裡面什麼也不寫}  

  然後過載PreTranslateMessage函式  
  把ESC鍵的訊息,用RETURN鍵的訊息替換,這樣,按ESC的時候,也會執行剛才的OnOK函式,這樣問題就可以解決了。  

  BOOL   CxxxDlg::PreTranslateMessage(MSG*   pMsg)    
  {  
    if(pMsg->message==WM_KEYDOWN   &&   pMsg->wParam==VK_ESCAPE)    

    {    
    pMsg->wParam=VK_RETURN;   //將ESC鍵的訊息替換為回車鍵的訊息,這樣,按ESC的時候  
                                                                                      //也會去呼叫OnOK函式,而OnOK什麼也不做,這樣ESC也被遮蔽  
      }    
    return   CDialog::PreTranslateMessage(pMsg);  

  }  

  【方法2】  

  直接在過載的PreTranslateMessage函式中遮蔽回車和ESC的訊息,和以上方法大同小異:  


  BOOL   CxxxDlg::PreTranslateMessage(MSG*   pMsg)    
  {  
    if(pMsg->message==WM_KEYDOWN   &&   pMsg->wParam==VK_ESCAPE)     return   TRUE;  
    if(pMsg->message==WM_KEYDOWN   &&   pMsg->wParam==VK_RETURN)   return   TRUE;    
    else    

          return   CDialog::PreTranslateMessage(pMsg);  

  }

=======================================================================================

我的方法是:找到自己設計的按鈕,在屬性-風格中選擇default button;
如果沒有自己設計的button,按照以下方法:

預設焦點在確定按鈕上,回車就觸發這個按鈕的OnOK事件,預設按回車是觸發OnOK事件,看看有沒有無效斷點; 註釋掉OnOK();或重設tab鍵順序;取消ONOK的defaultbutton屬性;

一、原因 
        當用戶按下Enter鍵時,Windows就會自動去找輸入焦點落在了哪一個按鈕上,當獲得焦點的按鈕的四周將被點線矩形包圍。如果所有按鈕都沒有獲得輸 入焦點,Windows就會自動去尋找程式或資源所指定的預設按鈕(預設按鈕邊框較粗)。如果對話方塊沒有預設按鈕,那麼即使對話方塊中沒有OK按 鈕,OnOK函式也會自動被呼叫。 我把對話方塊上面的二個按鈕全刪掉掉,再編譯執行還是按下回車鍵會自動關閉程式啊。mfc 預設響應enter鍵的啊
二、解決辦法 
        為了使Enter鍵無效,最簡單的辦法就是將CExDlg的OnOK函式寫成空函式,然後針對OK按鈕寫一個新的函式來響應。下面是具體步驟: 
        首先建立一個以Ex為名的基於對話方塊的程式。 
        1.用ClassWizard對映IDOK按鈕到OnOK虛擬函式。 
        在ClassWizard中,從CExDlg的Object   Ids列表中選擇IDOK,然後單擊BN_CLICKED,這樣即產生了OnOK函式的原形和框架。 
        2.利用對話方塊編輯器改變OK按鈕的ID。 
        選擇OK按鈕,將它的ID由IDOK改為IDC_OK,並去掉Default   Button屬性的複選標記。這樣OnOK函式即被分離出來。 
        3.用ClassWizard建立一個OnClickedOK成員函式。 
        在ClassWizard中,從CExDlg的Object   Ids列表中選擇IDC_OK,然後單擊BN_CLICKED,函式名用OnClickedOK,產生了OnClickedOK函式的原形和框架。 
        4.編輯程式碼。 
        void   CExDlg::OnOK() 
        { 
                //   TODO:   Add   extra   validation   here 
                //   CDialog::OnOK(); 
        } 

        void   CExDlg::OnClickedOk() 
        { 
                //   TODO:   Add   your   control   notification   handler   code   here 
                CDialog::OnOK(); 
        } 
        5.編譯並測試程式。 
        重新編譯程式,然後測試一下Enter鍵,你會發現按下Enter鍵後將沒有任何動作,而單擊OK按鈕則仍然會和以前一樣。 
三、   OnCancel的處理 
        正如Enter鍵可以觸發對OnOK函式的呼叫一樣,Esc鍵也可以觸發對OnCancel函式的呼叫。它將導致控制從對話方塊中退出。不過讀者可以像對OK按鈕一樣處理,通過設定一個無效的OnCancel函式來防止這一情況的出現 
第三個方法 
BOOL   CxxxDlg::PreTranslateMessage(MSG*   pMsg) 

        //   修改回車鍵的操作反應 
        if(pMsg-> message==WM_KEYDOWN   &&   pMsg-> wParam==VK_RETURN)
        { 
                UINT   nID   =   GetFocus()-> GetDlgCtrlID(); 
                if(ctButton   !=   GetCtrlType(nID)) 
                { 
                        NextDlgCtrl(); 
                        int   nFocusID   =   GetFocus()-> GetDlgCtrlID(); 
                        if(nFocusID   ==   1001) 
                                ((CComboBox*)(GetFocus()-> GetParent()))-> ShowDropDown(); 
                        else   if(ctComboBox   ==   GetCtrlType(nFocusID)) 
                                ((CComboBox*)GetFocus())-> ShowDropDown(); 

                        return   TRUE; 
                } 
        } 
        return   CDialog::PreTranslateMessage(pMsg); 
}

1. 在VC++中如何在對話方塊函式中獲得edit控制元件的“按下回車鍵”的訊息?

處理WM_KEYDOWN 訊息,其中的wParam 為VK_RETURN 即是回車訊息。

case WM_KEYDWON:
{
  if ( VK_RETURN == msg.wParam )
  {
    // 按下回車
    // 獲取焦點控制代碼
    HWND hFocus = GetFocus();
    if ( hFocus == hEdit )
    {
       // 處理你的邏輯
    }
  }
}
break;

2.我以前的做法是做一個PreTranslateMessage,在裡面裡感應到MSG,是回車就該怎麼樣就怎麼樣,呵呵,現在有個情況,在對整 個Dlg的情況下用PreTranslateMessage判斷回車不太適合,是否有可能從EDIT控制元件接收到回車就如何如何呢?
有什麼做法?

3.我的VS2008對話方塊程式怎麼沒有PreTranslateMessage訊息
用類嚮導生成的基於對話方塊的MFC程式,在屬性的message裡看了。全是WM開頭的。都找了。沒找著。。。汗。。。
怎麼回事?

呵呵,不是在類的message裡面,而是在類的overrides裡面哦…就在classview裡面,不是有兩上類了嗎?點那個對話方塊類,在“屬性”窗口裡,點“重寫”選項卡,裡面列了所有可以過載的函式。

4.在我用MFC生成的對話方塊後,用ESC鍵就可以關閉對話方塊,我的程式不想要這個功能,請教各位,如何去除? 
我嘗試了擷取WM_CHAR和WM_KEYDONWN以及WM_SYSKEYDOWN,但都沒有觸發.

一開始的cancel按扭不要去掉,雙擊它加入命令OnCancel(),把OnCancel中呼叫父類去掉 
直接返回,這就沒問題了 如果你已經把按鈕刪掉,那就得手工加這個命令,但效果一樣

5.你有沒有把文字編輯框屬性裡的want   return選上

//對鍵盤訊息控制 
BOOL   CSetupDlg::PreTranslateMessage(MSG*   pMsg)   

if   (pMsg-> message==UM_RETURN) 

//讓組合框響應回車獲取文字 
GetComboEditText(); 

//處理回車訊息 
if   (pMsg-> message==WM_KEYDOWN   &&   pMsg-> wParam==VK_RETURN) 

//響應回車為tab鍵 
pMsg-> wParam=VK_TAB; 


return   CDialog::PreTranslateMessage(pMsg); 

ES_WANTRETURN風格只對multiple-line的Edit才有效。還是用wade_vc(我命由我不由天)的方法,在PreTranslateMessage函式中處理

註明:轉自http://blog.csdn.net/bing_bing/archive/2011/03/18/6258636.aspx

===============================================================================

摘要 :本文講述了在指定的編輯框上能響應從鍵盤輸入回車鍵的一種方法,對程序內訊息的解析、動態獲取指定資源ID等技術也作了簡要描述。

關鍵字 :Microsoft Visual C++ 6.0、編輯框、回車鍵、訊息、資源

一、引言

在通常的以CEditView為基類的單文件/多文件檢視程式中,可以很好的響應鍵盤輸入的回車鍵,只需比較最近兩次的輸入的字元,看看最新輸入的字元是否內碼是13(0x0d,回車鍵的內碼)即可識別出來,而要單獨把一個編輯框放入對話方塊中卻根本不響應,這個看似簡單的問題在實際應用中還是解決起來比較困難的。尤其是當一個充當表單錄入的對話方塊上有若干個編輯框,這就要求在一個編輯框添完一項表單後用習慣的回車鍵將該編輯框上的資料讀取到記憶體中去,並自動將游標移動到下一個編輯框中準備填寫下一欄表單。無疑這種介面是十分人機友好的,使錄入人員不必去執行每填一下表單就去按一下執行讀入到快取功能的按鈕的煩瑣操作。但上述功能的實現卻並不象其演示的功能那樣簡單,下面本文就對這項技術的實現及附帶的其他技術作簡要的介紹。

二、不能響應回車鍵的原因分析

之所以在以CEditView作為基類的程式中可以響應回車鍵,是由於該程式的視類本身就是一個Edit控制元件,這就是問題的關鍵所在。CEditView作為CView的派生類能響應從鍵盤輸入的各種訊息,其中有和鍵盤輸入相關的WM_CHAR、WM_KEYDOWN、WM_KEYUP等訊息。我們就可以在這些訊息的響應函式中靈活地設計程式去捕捉到回車鍵的輸入,並執行響應的操作。

當我們將編輯框作為一個普通的控制元件放到對話方塊上時情況就發生了變化。在此我們以CFormView為例,它也是CView的一個派生類,視是一個Form窗體(即對話方塊),當放有編輯框的窗體有回車鍵輸入時,由於只有編輯框可以接受從鍵盤輸入的字元,所以當鍵盤按下時統統把訊息都發給了編輯框(在Windows下每個視窗、按鈕、編輯框都看作一個視窗,都可以接受訊息),可以通過ClassWizard在"Object IDs"選中編輯框所對應的ID號,在右邊的訊息框中可以看出該編輯框並不能響應WM_CHAR等訊息,只能用EN_CHANGE事件來做類似的響應。可當我們加入了對該事件的處理函式時,卻又將回車鍵當作控制字元,當輸入回車鍵並不會激發EN_CHANGE事件,也就是說用這種方法仍舊無法捕獲回車鍵的輸入。

三、攔截回車鍵的思路與方法

Windows作業系統下各個視窗、控制元件歸根結底都是通過系統的各種各樣的訊息來相互協調、相互聯絡的,而我們所遇到的這個問題換到訊息的角度說就是"如何使程式能響應在編輯框上輸入的回車鍵所發出的訊息",只要能響應到這個訊息,剩下的工作都可以在訊息處理函式中完成。所以有必要對Windows系統的訊息機制做些瞭解。

每個Windows應用程式開始執行後,Windows都為該程式建立一個"訊息佇列(message queue)",用來存放郵寄給該程式可能建立的各種不同視窗的訊息。訊息佇列中訊息的結構(MSG)為:

typedef struct tagMSG { /*msg*/ HWND hwnd;//視窗控制代碼,標識接收訊息的視窗。 UINT message;//訊息標識號,如WM_TIMER等。WPARAM wParam;//訊息引數,當為鍵盤訊息時,表示虛擬鍵碼如VK_RETURN等。LPARAM lParam;//訊息引數。 DWORD time;//郵寄訊息的時間。 POINT pt;//郵寄訊息時的游標位置,用螢幕座標表示。 }MSG;在系統下最常用的訊息迴圈是呼叫GetMessage()函式從訊息佇列中取出訊息,然後呼叫DespatchMessage() 函式讓系統把訊息傳送給視窗函式,一般情況下其結果是把視窗的所有訊息都傳送給視窗函式。但特殊情況下可以在GetMessage()函式獲得訊息而又沒傳送出去之前,通過TranslateMessage()函式可以中途對訊息進行解析,可以對指定的訊息進行攔截,攔截後即可以照樣傳送出去,也可以不繼續傳送,完成對該訊息的攔截,下面程式碼是該過程的示例:MSG msg;while(GetMessage(&msg,NULL,NULL,NULL,NULL){TranslateMessage(&msg);…… //對攔截的訊息進行處理DispathchMessage(&msg);}由於按下回車鍵時把產生的訊息加入到訊息佇列中了,也傳給了編輯框,但僅僅是由於編輯框沒有能力處理該訊息而造成了無法對回車鍵的響應,所以可以在訊息迴圈裡在把訊息傳送到編輯框之前就對訊息進行攔截,並對其進行處理。其效果同編輯框響應回車鍵是一樣的,僅在時序上有所提前而已。上述程式碼是在SDK(Software Develope Kits)下使用的,在MFC(Microsoft Foundation Class)下早已對其進行了封裝,可以通過過載虛擬函式PreTranslateMessage()對所關心的訊息進行解析:

BOOL CTestView::PreTranslateMessage(MSG* pMsg) { if (WM_KEYFIRST <= pMsg-> message && pMsg-> message <= WM_KEYLAST) {if(pMsg-> wParam==VK_RETURN ) {UpdateData(TRUE);AfxMessageBox(m_Text); } } return CFormView::PreTranslateMessage(pMsg); }在上面的程式碼中,首先將pMsg-> message所表示的訊息同WM_KEYFIRST 和WM_KEYLAST比較,確定是鍵盤訊息,然後通過訊息引數pMsg-> wParam的值來判斷是否是回車鍵(VK_RETURN,虛擬鍵碼可以從SDK相關資料查到)。如是,則可以將已輸入到編輯框中的字元讀取到m_Text中,並將其顯示出來

四、對編輯框的識別

前面已經可以對回車鍵響應了,可一個表單窗體有若干個編輯框,其各自的處理方式不盡相同,這就有必要對編輯框進行識別、對不同的編輯框做不同的處理。而且當按下回車鍵時必須保證只有當前有焦點的編輯框能完成對回車鍵的響應動作,否則也就失去了實際意義。

在Windows下的程式中,所有的資源都是有唯一標號的,使每個資源物件能唯一的區別於其他資源,所以我們可以通過資源ID來對編輯框做出區別,使之完成各自的響應處理。在Microsoft Visual C++ 6.0下可以通過"View"選單的"ID= Resource Symboles…"查到指定ID的資源標識號的實際數值,如在本例中的兩個編輯框IDC_EDIT1和IDC_EDIT2所對應的數值分別為1000和1001,對前面的解析訊息的程式碼做些改動,主要如下所示:

……if(pMsg-> wParam==VK_RETURN ) { HWND hWnd=::GetFocus(); int iID=::GetDlgCtrlID(hWnd); if(iID==1000)//第一個編輯框的標識為1000{UpdateData(TRUE); AfxMessageBox(m_Text1);//顯示第一個編輯框的內容} if(iID==1001) //第二個編輯框的標識為 1001{UpdateData(TRUE); AfxMessageBox(m_Text2);//顯示第二個編輯框的內容} }……在此通過API函式::GetFocus()(注意前面的"::",標識是全域性API函式,而非某個類中的成員函式)取得當前游標所處的(即有焦點的)編輯框的控制代碼,然後通過API函式::GetDlgCtrlID()根據這個控制代碼返回此視窗資源的ID 號,該ID號是動態獲取的,使之同預先檢視好的編輯框的ID作下比較即可區分出是需要哪個編輯框對回車鍵作出響應。

小結:

本文通過對訊息的解析實現了對特定編輯框的回車鍵的響應,在對訊息機制有了基本的瞭解之後,可以用與本文類似的方法,對程式碼稍作改動,就可以使其他一些不能響應特殊訊息的控制元件能接收、處理特定的訊息。