以前學習vc的筆記

1  如何讓一個VC的對話框程序響應鍵盤消息?  
   
  我不知道在這樣的基於對話框的程序中,如何使用SetFocus使得對話框窗口獲得輸入焦點,從而能夠響應鍵盤消息.

基於對話框的程序,如果窗體上有控件,由於如果窗體處於活動狀態,那麼將必有一個控件獲得焦點,不能通過在ClassWizard中添加WM_KEYDOWN來響應鍵盤輸入。  
   
  一個簡單的辦法就是重載CWnd::PreTranslateMessage,即  
   
  BOOL   CDlgDlg::PreTranslateMessage(MSG*   pMsg)    
  {  
  //   TODO:   Add   your   specialized   code   here   and/or   call   the   base   class  
  if(pMsg->message   ==   WM_KEYDOWN)  
  {  
  MessageBox("ssd");  
  }  
   
  return   CDialog::PreTranslateMessage(pMsg);  
  }  

2  對話框中如何響應鍵盤消息?

我們首先想到的是響應WM_KEYDOWN消息,但實際運行卻發現沒有任何效果。  
  原因是對話框裏的控件需要首先對按鍵作出響應,比如多行編輯框必須首先  
  處理回車,不至於回車使對話框關閉。  
  我們要想在第一時間對對話框的按鍵做出響應,需要重載PreTranslateMessage,  
  以下的代碼實現了在對話框中顯示虛擬鍵值(virtual-key   code)  
  BOOL   CTestDlg::PreTranslateMessage(MSG*   pMsg)    
  {    
  if(pMsg->message   ==   WM_KEYDOWN)    
  {    
  ///或者直接調用OnKeyDown    
  CString   strwParam;    
  strwParam.Format("%d   ",pMsg->wParam);    
  CDC*   pDC   =   GetDC();    
  pDC->TextOut(10,10,strwParam);    
  ReleaseDC(pDC);    
  }    
  return   CDialog::PreTranslateMessage(pMsg);    
  }  
   
  此方法也適用於FORMVIEW或其它控件中對鍵盤按鍵的響應,以下的代碼來自MSDN,  
  實現了當按下上下左右方向鍵時調用OnKeyDown,我們可以在OnKeyDown中作相應處理。    
   
  BOOL   CSampleControl::PreTranslateMessage(LPMSG   lpmsg)    
  {  
  BOOL   bHandleNow   =   FALSE;  
  switch   (lpmsg->message)  
  {  
  case   WM_KEYDOWN:  
  switch   (lpmsg->wParam)  
  {  
  case   VK_UP:  
  case   VK_DOWN:  
  case   VK_LEFT:  
  case   VK_RIGHT:  
  bHandleNow   =   TRUE;  
  break;  
  }    
  if   (bHandleNow)    
  OnKeyDown(lpmsg->wParam,   LOWORD(lpmsg   ->lParam),   HIWORD(lpmsg->lParam));    
  break;    
  }    
  return   bHandleNow;    
  }    
 
3  對話框窗口只要有子控件就接不到KEY_DOWN,該怎麼寫代碼響應鍵盤呢?我想要響應F10,F11鍵。
  只要對話框上有任何子控件,就不能響應鍵盤消息了(鍵盤消息被派發到了獲取焦點的控件上)  
   方法是在PreTranslateMessage中攔截

4  PreTranslateMessage 是做什麼用的啊????
用來預處理消息的。發給窗口的消息經過預處理以後再發給窗口,這裏你就可以過濾或者處理你感興趣的消息,比如回車等。如果你處理好了不需要窗口繼續處理了,就返回TRUE或者1 。

就是說在你的窗體收到消息前對消息進行處理,舉個很簡單的例子,在對話框裏回車的時候就會直接OnOK,如果你要屏蔽掉回車鍵就在這個函數裏判斷,如果是WM_KEYDOWN並且按鍵爲VK_ENTER就不進行任何處理,這樣的話,回車鍵的消息就不會發給對話框了
5   MFC中PretranslateMessage的實現

在MFC裏面,PretranslateMessage是一個很重要的虛函數。這個函數的作用這裏就不談了,很多地方都有涉及,這裏只談一下其實現的機制。
談到PretranslateMessage的實現,便不得不談到MFC消息循環的實現。MFC通過CWinApp類中的Pumpmessage函數實現消息循環,但是實際的消息循環代碼位於CWinThread中,CWinApp只是從CWinThread繼承過來。其簡化後的代碼大概如下:

BOOL CWinThread::PumpMessage()
...{
    _AFX_THREAD_STATE *pState = AfxGetThreadState();

    ::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
 
    if (!AfxPreTranslateMessage(&(pState->m_msgCur)))
    ...{
        ::TranslateMessage(&(pState->m_msgCur));
        ::DispatchMessage(&(pState->m_msgCur));
    }
    return TRUE;
}
可以看到,PumpMessage在實際的TranslateMessage和DispatchMessage發生之前會調用AfxPreTranslateMessage,AfxPreTranslateMessage又會調用CWnd::WalkPreTranslateTree(雖然也會調用其他函數,但是這個最爲關鍵),其代碼如下:

BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
...{
    ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));
    ASSERT(pMsg != NULL);

    // walk from the target window up to the hWndStop window checking
    //  if any window wants to translate this message

    for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
    ...{
        CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
        if (pWnd != NULL)
        ...{
            // target window is a C++ window
            if (pWnd->PreTranslateMessage(pMsg))
                return TRUE; // trapped by target window (eg: accelerators)
        }
   
        // got to hWndStop window without interest
        if (hWnd == hWndStop)
            break;
    }
    return FALSE;       // no special processing
}

可以看到,代碼還是很直接的。從接受到消息的窗口層層往上遍歷,並調用PretranslateMessage看是否返回TRUE,是則結束,否則繼續。
這裏有一個地方非常關鍵:CWnd *pWnd = CWnd::FromHandlePermanent(hWnd) 這一句代碼從當前AfxModuleThreadState拿到Permanent句柄表,從而找到hWnd對應的CWnd對象。關於PreTranslateMessage有一個常見的問題就是與此有關:如果編寫了一個MFC DLL並從另外的一個MFC主工程之中調用這個MFC DLL中的Modeless Dialog的話,Modeless Dialog的PreTranslateMessage不會被調。因爲MFC DLL和這個MFC工程擁有不同的AfxModuleThreadState,因此在MFC DLL中創建的modeless CDialog對象不在MFC工程的句柄表中(CWnd::FromhandlePermanent返回NULL),因此雖然MFC主工程中的CWinApp的Pretranslatemessage會被調(注意此時Dialog的消息循環在MFC主工程裏面),但是不會調用MFC DLL中創建的那個modeless CDialog的PreTranslateMessage函數。因此需要特殊處理。一般有兩種方法:
一種是直接在MFC主工程中的CWinApp::PreTranslatemessage裏面調用MFC DLL的CWinApp::PreTranslateMessage(可以專門在MFC DLL中export一個專門的函數來做這件事情)。另外的方法是使用鉤子,在鉤子消息處理函數之中,判斷目標窗口是否是當前具有焦點的窗口,如果是,則直接調用目標窗口的PreTranslateMessage函數(前提是你有要保存這個對象的指針)。

6. 可以通過如下獲得控件的HWND
CWnd* pWnd;
pWnd = GetDlgItem(IDC_...);
HWND hWnd;
hWnd = pWnd->GetSafeHandle();
 
下面的回調函數要調用該ITEM裏的數據
SetTimer(this->GetSafeHwnd(), MY_TIMER, 1000,(TIMERPROC)MyTimerProc);

7.  通過HWND來處理控件數據
 
static void CALLBACK MyTimerProc(
            HWND hwnd, // handle to window
            UINT uMsg, // WM_TIMER message
            UINT_PTR idEvent, // timer identifier
            DWORD dwTime // current system time
            )
{
     CWnd* pWnd;
     pWnd = CWnd::FromHandle(hwnd);
 
     Page1 *pPage = (Page1 *)pWnd;   //Page1爲類名稱
     CString strTmp = pPage->m_NameList.GetItemText(0,0);
 
}

8    我想請問一下,GetDlgItem 的第二個函數void CWnd::GetDlgItem( int nID, HWND* phWnd ) const;有什麼用處呀?

兩個函數分別按如下方法使用,效果等價
1.
CWnd* pWnd;
pWnd = GetDlgItem(IDC_...);
HWND hWnd;
hWnd = pWnd->GetSafeHandle();

2.
HWND hWnd;
GetDlgItem(IDC_..., &hWnd);
CWnd* pWnd;
pWnd = FromHandle(hWnd);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章