關于WM_NOTIFY與消息反射—耗費我兩天時間才解決的問題
www. 時間 : 2010-06-22 作者:佚名 編輯:壹枝雪糕 點擊: 1306 [ 評論 ] 綜合資源電子書社區(qū) 其實,問題很簡單,我想在listctrl響應NM_SETFOCUS的同時通知其父窗口(其實我這句話說錯了,listctrl只能響應=NM_SETFOCUS,為什么有個“=”呢?稍后解釋),最幼稚的想法是讓在listctrl和父窗口中都添加對此消息的響應,很不幸,我在一開始就是這么想的-_-| 。。。很明顯我失敗了! 后來我又發(fā)現(xiàn),如果在listctrl中添加對=NM_SETFOCUS的響應,父窗口就無法響應NM_SETFOCUS,反之就可以響應。于是很容易想到在listctrl的消息響應中SendMessage給父窗口,控件發(fā)送的消息有兩種:WM_COMMAND和WM_NOTIFY,像button,edit,combobox等控件,主要使用WM_COMMAND消息,而listctrl這類控件主要使用WM_NOTIFY消息,發(fā)送消息時有兩個很重要的參數(shù)wParam和lParam,MSDN說明如下: lResult = SendMessage( // returns LRESULT in lResult (HWND) hWndControl, // handle to destination control (UINT) WM_NOTIFY, // message ID (WPARAM) wParam, // = (WPARAM) (int) idCtrl; (LPARAM) lParam // = (LPARAM) (LPNMHDR) pnmh; ); 結果我還是失敗了!折騰了一天一夜,直到晚上熄燈,我就不信我弄不出來,第二天上CSDN求救,有一個同志的回答提醒了我:消息反射。消息反射是個什么東西,我還是第一次聽到,看來我真是菜鳥。于是查閱了一下消息反射的資料,原來,控件根本無法直接給自己發(fā)送消息,這樣就導致控件無法控制自己,所有的控制必須在父窗口類中實現(xiàn),這樣不符合面向對象的思想,于是出現(xiàn)了消息反射;即子控件發(fā)送給父窗口的消息被父窗口馬上反射回來,如果子控件響應了反射消息,父窗口就不響應,反之則響應。這就和我剛才發(fā)現(xiàn)的現(xiàn)象吻合了。 那么,消息反射是怎樣實現(xiàn)的呢?源代碼說明一切!父窗口在接收到子控件的通知消息時調用虛的消息響應函數(shù)CWnd::OnNotify(),代碼如下: BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult) { ASSERT(pResult != NULL); NMHDR* pNMHDR = (NMHDR*)lParam; HWND hWndCtrl = pNMHDR->hwndFrom; // get the child ID from the window itself UINT nID = _AfxGetDlgCtrlID(hWndCtrl); int nCode = pNMHDR->code; ASSERT(hWndCtrl != NULL); ASSERT(::IsWindow(hWndCtrl)); if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd) return TRUE; // locked out - ignore control notification // reflect notification to child window control if (ReflectLastMsg(hWndCtrl, pResult)) return TRUE; // eaten by child AFX_NOTIFY notify; notify.pResult = pResult; notify.pNMHDR = pNMHDR; return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL); } 接著看ReflectLastMsg()函數(shù): BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult) { // get the map, and if no map, then this message does not need reflection CHandleMap* pMap = afxMapHWND(); if (pMap == NULL) return FALSE; // check if in permanent map, if it is reflect it (could be OLE control) CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild); ASSERT(pWnd == NULL || pWnd->m_hWnd == hWndChild); if (pWnd == NULL) { #ifndef _AFX_NO_OCC_SUPPORT // check if the window is an OLE control CWnd* pWndParent = (CWnd*)pMap->LookupPermanent(::GetParent(hWndChild)); if (pWndParent != NULL && pWndParent->m_pCtrlCont != NULL) { // If a matching control site exists, it''s an OLE control COleControlSite* pSite = (COleControlSite*)pWndParent-> m_pCtrlCont->m_siteMap.GetValueAt(hWndChild); if (pSite != NULL) { CWnd wndTemp(hWndChild); wndTemp.m_pCtrlSite = pSite; LRESULT lResult = wndTemp.SendChildNotifyLastMsg(pResult); wndTemp.m_hWnd = NULL; return lResult; } } #endif //!_AFX_NO_OCC_SUPPORT return FALSE; } // only OLE controls and permanent windows will get reflected msgs ASSERT(pWnd != NULL); return pWnd->SendChildNotifyLastMsg(pResult); } 注意紅色代碼!此時調用的是子控件的SendChildNotifyLastMsg() 。繼續(xù)看SendChildNotifyLastMsg(): BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult) { _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); return OnChildNotify(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult); } 調用子控件的虛函數(shù)OnChildNotify函數(shù),進行處理。 如果沒有處理,則調用ReflectChildNotify(...)函數(shù)進行標準的反射消息的消息映射處理。 如果在ReflectChildNotify(...)中此消息還沒被處理,就返回到CWnd::OnNotify(...)中調用OnCmdMsg(...)處理,這樣,父窗口就可以響應此消息了。 下面回到我最初的問題中來,我想在listctrl和父窗口中都處理此消息,因此我們可以重載OnNotify(...)如下: BOOL CWellListView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) { // TODO: Add your specialized code here and/or call the base class ASSERT(pResult != NULL); NMHDR* pNMHDR = (NMHDR*)lParam; HWND hWndCtrl = pNMHDR->hwndFrom; ReflectLastMsg(hWndCtrl, pResult); UINT nID = _AfxGetDlgCtrlID(hWndCtrl); int nCode = pNMHDR->code; AFX_NOTIFY notify; notify.pResult = pResult; notify.pNMHDR = pNMHDR; return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL); }//CWellListView繼承子CFormView,相當與對話框 還要添加兩個頭文件: #include "AFXPRIV.H" #include "C:\Program Files\Microsoft Visual Studio\VC98\MFC\SRC\AFXIMPL.H" 看到它和CWnd::OnNotify(...)的區(qū)別了沒?恩,就是這樣! 但是,很不幸,我最終沒有采取這種辦法,因為我在一開始就沒有想到最佳方案,我完全可以用指針來實現(xiàn)我的需求,在子控件類中需要的地方添加如下代碼就可以操作父窗口了: CWellListView* listview=(CWellListView*)GetParent(); 因此,在以后的編程學習中,我們應該盡量用指針操作來代替發(fā)送消息來實現(xiàn)我們的需求! |
|
來自: DCW > 《技術開發(fā)》