MFC中的WM_NOTIFY消息的反射

在父窗口收到子控件的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 itself
	UINT_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 child

	AFX_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 reflection
	CHandleMap* 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 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 != 0;
			}
		}
#endif //!_AFX_NO_OCC_SUPPORT
		return FALSE;
	}

	// only OLE controls and permanent windows will get reflected msgs
	ASSERT(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 expected
	UNUSED(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 controls
void 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_SUPPORT
	if (m_pCtrlSite != NULL)
	{
		// first forward raw OCM_ messages to OLE control sources
		LRESULT 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 routing
			return FALSE;
		}
		if (pResult != NULL)
			*pResult = lResult;
		return TRUE;
	}
#endif

	return <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+uMsg
		return <span style="background-color: rgb(255, 102, 102);">CWnd::OnWndMsg(WM_REFLECT_BASE+uMsg, wParam, lParam, pResult);</span>

	// special case for WM_COMMAND
	case WM_COMMAND:
		{
			// reflect the message through the message map as OCM_COMMAND
			int 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_NOTIFY
	case <span style="background-color: rgb(255, 102, 102);">WM_NOTIFY:</span>
		{
			// reflect the message through the message map as OCM_NOTIFY
			NMHDR* 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_CTLCOLOR
			AFX_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_CTLCOLOR
			BOOL 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就在父窗口处理了,(反正是给了子控件机会处理的)。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值