MFC中的WM_NOTIFY消息的反射

来源:互联网 发布:千牛mac版怎么安装 编辑:程序博客网 时间:2024/05/17 07:56

在父窗口收到子控件的WM_NOTIFY通知消息后的处理流程,MFC源代码如下:

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 itselfUINT_PTR 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<span style="background-color: rgb(255, 102, 102);">if (ReflectLastMsg(hWndCtrl, pResult))</span>return TRUE;        // eaten by childAFX_NOTIFY notify;notify.pResult = pResult;notify.pNMHDR = pNMHDR;return <span style="background-color: rgb(255, 102, 102);">OnCmdMsg((UINT)nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);</span>}
从上述代码可以看出WM_NOTIFY的默认的流动方向是先反射给产生这个WM_NOTIFY消息的子控件。如果子控件窗口不处理此消息,然后才是本窗口(父窗口)处理。

下面再看看

<span style="background-color: rgb(255, 102, 102);">ReflectLastMsg(hWndCtrl, pResult)</span>
的源代码:

BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult){// get the map, and if no map, then this message does not need reflectionCHandleMap* pMap = afxMapHWND();if (pMap == NULL)return FALSE;// check if in permanent map, if it is reflect it (could be OLE control)<span style="background-color: rgb(255, 102, 102);">CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);</span>ASSERT(pWnd == NULL || pWnd->m_hWnd == hWndChild);if (pWnd == NULL){#ifndef _AFX_NO_OCC_SUPPORT// check if the window is an OLE controlCWnd* pWndParent = (CWnd*)pMap->LookupPermanent(::GetParent(hWndChild));if (pWndParent != NULL && pWndParent->m_pCtrlCont != NULL){// If a matching control site exists, it's an OLE controlCOleControlSite* 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 != 0;}}#endif //!_AFX_NO_OCC_SUPPORTreturn FALSE;}// only OLE controls and permanent windows will get reflected msgsASSERT(pWnd != NULL);return <span style="background-color: rgb(255, 102, 102);">pWnd->SendChildNotifyLastMsg(pResult);</span>}
从上述源代码可以看出来,先是取得产生WM_NOTIFY消息的子控件的对象指针。然后再调用子控件的函数SendChildNotifyLastMsg()。

下面再看看SendChildNotifyLastMsg()函数的源代码:

BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult){_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();return <span style="background-color: rgb(255, 102, 102);">OnChildNotify(pThreadState->m_lastSentMsg.message,pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult);</span>}
从上述代码可以看出子控件调用了自己的OnChildNotify()这个虚函数。这个函数可以被各个控件覆盖(override),这样就可以自己定义对这个反射回来的WM_NOTIFY消息做什么处理了。

如CButton标准控件的OnChildNotify()函数实现如下:

BOOL CButton::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam,LRESULT* pResult){<span style="background-color: rgb(255, 102, 102);">if (message != WM_DRAWITEM)return CWnd::OnChildNotify(message, wParam, lParam, pResult);</span>ASSERT(pResult == NULL);       // no return value expectedUNUSED(pResult); // unused in release builds<span style="background-color: rgb(255, 102, 102);">DrawItem((LPDRAWITEMSTRUCT)lParam);</span>return TRUE;}
从上述代码可以看出父窗口把WM_DRAWITEM消息也给反射回子控件了。CButton标准控件对反射回来的WM_DRAWITEM消息做了特殊的自定义的处理,调用了DrawItem()虚函数,来进行对CButton的控件自身重画。这就是为什么子控件重画可以不需要在父窗口的OnDrawItem()消息函数中处理了,可以直接就在自己的虚函数里来处理了。这样子的话控件就更具有独立性了。我们要是自己做的控件也可以学习上面CButton对某一个反射消息的特殊处理方式。

再看看DrawItem()虚函数的源代码:

// Derived class is responsible for implementing all of these handlers//   for owner/self draw controlsvoid CButton::DrawItem(LPDRAWITEMSTRUCT){<span style="background-color: rgb(255, 102, 102);">ASSERT(FALSE);</span>}
上面的代码说明了,如果子控件设置了自画属性的话,要么1.就在父窗口的OnDrawItem()函数里画完,返回TRUE,就不要再调用CWnd::OnDrawItem()把WM_DRAWITEM消息再反射回子控件了。要么2.不再父窗口里的OnDrawItem()函数里自画,而是覆盖(override)DrawItem()虚函数在自己的DrawItem()函数里自画。否则的话,不覆盖(override)DrawItem()函数,但是又让WM_DRAWITEM消息反射回子控件的话,就会报错(在Debug模式下)。

如果对反射的消息不做特殊的处理,源代码如下:

BOOL CWnd::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult){#ifndef _AFX_NO_OCC_SUPPORTif (m_pCtrlSite != NULL){// first forward raw OCM_ messages to OLE control sourcesLRESULT lResult = SendMessage(OCM__BASE+uMsg, wParam, lParam);if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <= WM_CTLCOLORSTATIC &&(HBRUSH)lResult == NULL){// for WM_CTLCOLOR msgs, returning NULL implies continue routingreturn FALSE;}if (pResult != NULL)*pResult = lResult;return TRUE;}#endifreturn <span style="background-color: rgb(255, 102, 102);">ReflectChildNotify(uMsg, wParam, lParam, pResult);</span>}
ReflectChildNotify()源代码如下:

BOOL CWnd::ReflectChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult){<span style="background-color: rgb(255, 102, 102);">// Note: reflected messages are send directly to CWnd::OnWndMsg//  and CWnd::OnCmdMsg for speed and because these messages are not//  routed by normal OnCmdMsg routing (they are only dispatched)</span>switch (uMsg){// normal messages (just wParam, lParam through OnWndMsg)case WM_HSCROLL:case WM_VSCROLL:case WM_PARENTNOTIFY:case <span style="background-color: rgb(255, 102, 102);">WM_DRAWITEM:</span>case WM_MEASUREITEM:case WM_DELETEITEM:case WM_VKEYTOITEM:case WM_CHARTOITEM:case WM_COMPAREITEM:// reflect the message through the message map as WM_REFLECT_BASE+uMsgreturn <span style="background-color: rgb(255, 102, 102);">CWnd::OnWndMsg(WM_REFLECT_BASE+uMsg, wParam, lParam, pResult);</span>// special case for WM_COMMANDcase WM_COMMAND:{// reflect the message through the message map as OCM_COMMANDint nCode = HIWORD(wParam);if (CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_COMMAND), NULL, NULL)){if (pResult != NULL)*pResult = 1;return TRUE;}}break;// special case for WM_NOTIFYcase <span style="background-color: rgb(255, 102, 102);">WM_NOTIFY:</span>{// reflect the message through the message map as OCM_NOTIFYNMHDR* pNMHDR = (NMHDR*)lParam;int nCode = pNMHDR->code;AFX_NOTIFY notify;notify.pResult = pResult;notify.pNMHDR = pNMHDR;return <span style="background-color: rgb(255, 102, 102);">CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_NOTIFY), ¬ify, NULL);</span>}// other special cases (WM_CTLCOLOR family)default:if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <= WM_CTLCOLORSTATIC){// fill in special struct for compatiblity with 16-bit WM_CTLCOLORAFX_CTLCOLOR ctl;ctl.hDC = (HDC)wParam;ctl.nCtlType = uMsg - WM_CTLCOLORMSGBOX;//ASSERT(ctl.nCtlType >= CTLCOLOR_MSGBOX);ASSERT(ctl.nCtlType <= CTLCOLOR_STATIC);// reflect the message through the message map as OCM_CTLCOLORBOOL bResult = CWnd::OnWndMsg(WM_REFLECT_BASE+WM_CTLCOLOR, 0, (LPARAM)&ctl, pResult);if ((HBRUSH)*pResult == NULL)bResult = FALSE;return bResult;}break;}return FALSE;   // let the parent handle it}
从上述代码可以看出来,默认的反射消息最后是直接交给了CWnd::OnWndMsg()和CWnd::OnCmdMsg()。这样是为了提高消息处理速度,同时也是不需要再对反射消息路由了。因为反射消息的终点就是子控件本身。如果子控件要处理反射消息的话,就要在子控件的消息映射表里添加ON_NOTIFY_REFLECT(wNotifyCode, memberFxn)宏来定义反射消息的消息处理函数(针对WM_NOTIFY+WM_REFLECT_BASE反射消息)。

如果子控件不处理反射消息,那么WM_NOTIFY就在父窗口处理了,(反正是给了子控件机会处理的)。





0 0
原创粉丝点击