關(guān)于WM_NOTIFY與消息反射—耗費(fèi)我兩天時間才解決的問題
www.firnow.com 時間 : 2010-06-22 作者:佚名 編輯:壹枝雪糕 點(diǎn)擊: 1306 [ 評論 ]
綜合資源電子書社區(qū) 其實(shí),問題很簡單,我想在listctrl響應(yīng)NM_SETFOCUS的同時通知其父窗口(其實(shí)我這句話說錯了,listctrl只能響應(yīng)=NM_SETFOCUS,為什么有個“=”呢?稍后解釋),最幼稚的想法是讓在listctrl和父窗口中都添加對此消息的響應(yīng),很不幸,我在一開始就是這么想的-_-| 。。。很明顯我失敗了!
后來我又發(fā)現(xiàn),如果在listctrl中添加對=NM_SETFOCUS的響應(yīng),父窗口就無法響應(yīng)NM_SETFOCUS,反之就可以響應(yīng)。于是很容易想到在listctrl的消息響應(yīng)中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;
);
結(jié)果我還是失敗了!折騰了一天一夜,直到晚上熄燈,我就不信我弄不出來,第二天上CSDN求救,有一個同志的回答提醒了我:消息反射。消息反射是個什么東西,我還是第一次聽到,看來我真是菜鳥。于是查閱了一下消息反射的資料,原來,控件根本無法直接給自己發(fā)送消息,這樣就導(dǎo)致控件無法控制自己,所有的控制必須在父窗口類中實(shí)現(xiàn),這樣不符合面向?qū)ο蟮乃枷耄谑浅霈F(xiàn)了消息反射;即子控件發(fā)送給父窗口的消息被父窗口馬上反射回來,如果子控件響應(yīng)了反射消息,父窗口就不響應(yīng),反之則響應(yīng)。這就和我剛才發(fā)現(xiàn)的現(xiàn)象吻合了。
那么,消息反射是怎樣實(shí)現(xiàn)的呢?源代碼說明一切!父窗口在接收到子控件的通知消息時調(diào)用虛的消息響應(yīng)函數(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);
}
注意紅色代碼!此時調(diào)用的是子控件的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);
}
調(diào)用子控件的虛函數(shù)OnChildNotify函數(shù),進(jìn)行處理。 如果沒有處理,則調(diào)用ReflectChildNotify(...)函數(shù)進(jìn)行標(biāo)準(zhǔn)的反射消息的消息映射處理。 如果在ReflectChildNotify(...)中此消息還沒被處理,就返回到CWnd::OnNotify(...)中調(diào)用OnCmdMsg(...)處理,這樣,父窗口就可以響應(yīng)此消息了。
下面回到我最初的問題中來,我想在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,相當(dāng)與對話框
還要添加兩個頭文件:
#include "AFXPRIV.H"
#include "C:\Program Files\Microsoft Visual Studio\VC98\MFC\SRC\AFXIMPL.H"
看到它和CWnd::OnNotify(...)的區(qū)別了沒?恩,就是這樣!
但是,很不幸,我最終沒有采取這種辦法,因?yàn)槲以谝婚_始就沒有想到最佳方案,我完全可以用指針來實(shí)現(xiàn)我的需求,在子控件類中需要的地方添加如下代碼就可以操作父窗口了:
CWellListView* listview=(CWellListView*)GetParent();
因此,在以后的編程學(xué)習(xí)中,我們應(yīng)該盡量用指針操作來代替發(fā)送消息來實(shí)現(xiàn)我們的需求!