WTL: CPaneContainerImpl 源代码分析

我用过这个类,尽管对它并不是很清楚。在分割窗口或者TAB窗口的pane部分,添加这个类的对象,会使得窗口出现一个标题栏。标题栏可以是横向的,也可以是纵向的,上面还可以有一个关闭按钮。当然这个pane容器窗口并不是必须的。好了,至少,我们大概知道了这个类能够做什么了。

        分析源代码就我而言,是我一个主要的学习方法。不知道大家是否也是如此,知道别人怎么使用一个函数,怎么达成一个目标。尤其出自那些大师们之手,你基本可以断定哪种做法至少不会是一种糟糕的方法,如果不是最佳的话。

       我先粗粗浏览了一遍这个类,我知道可以学习几个地方。

       1.  如果建立一个通用窗口类,如何使用CCustomDraw 模板类。

       2.  如何在窗口类里面动态添加工具条以及如何自绘按钮,处理工具条上的按钮消息。

       3.  如果维护一个临时的客户窗口。

     windows消息包含窗口消息、命令消息和控件通知消息,控件通知消息,是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。例如,单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。 她类似于命令消息,当用户与控件窗口交互时,那么控件通知消息就会从控件窗口发送到它的主窗口。但是这种消息的存在并不是为了处理用户命令,而是为了让主窗口能够改变控件,例如加载、显示数据。

     控件通知消息种类:

    第一控件通知格式
    第一控件通知格式只是窗口消息的子集。它的特征格式如下:WM_XXXX。它主要来自下面的3种消息类型:
    (1)表示一个控件窗口要么已经被创建或销毁,要么已经被鼠标单击的消息:WM_PARENTNOTIFY;
    (2)发送到父窗口,用来绘制自身窗口的消息,例如: WM_CTLCOLOR、WM_DRAWITEM、WM_MEASUREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKTOITEM、WM_COMMAND和WM_COMPAREITEM
    (3)有滚动调控件发送,通知父窗口滚动窗口的消息:WM_VSCROLL和WM_HSCROLL
    第二控件通知类与命令消息共享,它的特征格式如下:WM_COMMAND。
    在WM_COMMAND中,lParam用来区分是命令消息还是控件通知消息:如果lParam为NULL,则这是个命令消息,否则lParam里面放的必然就是控件的句柄,是一个控件通知消息。对于wParam则是低位放的是控件ID,高位放的是相应的消息事件。
    第三控件通知格式
    这个才是本文要涉及的内容,同时他也是最为灵活的一种格式。它的特征格式如下:WM_NOTIFY。
    在WM_NOTIFY中,lParam中放的是一个称为NMHDR结构的指针。

     有了前面这些铺垫,我们来看看 CPaneContainerImpl 类的定义:

     

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
{
public:
	DECLARE_WND_CLASS_EX(NULL, 0, -1)
    .......

这个类的基类就是一个普通窗口,第二个基类则是 CCustomDraw。我们前面见识过 COwnerDraw,弄这两者功能接近,弄清楚两者的差别也是我们关心的。

template <class T>
class CCustomDraw
{
public:

// Message map and handlers
	BEGIN_MSG_MAP(CCustomDraw< T >)
		NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
	ALT_MSG_MAP(1)
		REFLECTED_NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
	END_MSG_MAP()

// message handler
	LRESULT OnCustomDraw(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		T* pT = static_cast<T*>(this);
		pT->SetMsgHandled(TRUE);
		LPNMCUSTOMDRAW lpNMCustomDraw = (LPNMCUSTOMDRAW)pnmh;
		DWORD dwRet = 0;
		switch(lpNMCustomDraw->dwDrawStage)
		{
		case CDDS_PREPAINT:
			dwRet = pT->OnPrePaint(idCtrl, lpNMCustomDraw);
			break;
		case CDDS_POSTPAINT:
			dwRet = pT->OnPostPaint(idCtrl, lpNMCustomDraw);
			break;
		    。。。。。。。
		default:
			pT->SetMsgHandled(FALSE);
			break;
		}
		bHandled = pT->IsMsgHandled();
		return dwRet;
	}

// Overrideables
	DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
	{
		return CDRF_DODEFAULT;
	}

上面是 CCustomDraw 这个类的主要代码, 根据前面的经验,我想,或者会使用,类似

CHAIN_MSG_MAP_ALT( COwnerDraw<CPicLooker>,1 )

这样的代码,但是,我在消息映射代码段没有看到这样的代码,看来看去,显然它是通过那个通知消息触发了自绘能力。我想上面的方式应该也是可以实现的,有兴趣的朋友可以试一下。

// Message map and handlers
	BEGIN_MSG_MAP(CPaneContainerImpl)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
		MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
		MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
#ifndef _WIN32_WCE
		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
#endif // !_WIN32_WCE
		MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
		MESSAGE_HANDLER(WM_COMMAND, OnCommand)
		FORWARD_NOTIFICATIONS()
	END_MSG_MAP()


       FORWARD_NOTIFICATIONS() 向前通知的意思是本窗口没有处理的通知继续向上返回。 MESSAGE_HANDLER(WM_NOTIFY, OnNotify),就是我们关心的那句了,这就是通知处理。它处理所有的通知。


	LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		if(m_tb.m_hWnd == NULL)
		{
			bHandled = FALSE;
			return 1;
		}

		T* pT = static_cast<T*>(this);
		pT;
		LPNMHDR lpnmh = (LPNMHDR)lParam;
		LRESULT lRet = 0;

		// pass toolbar custom draw notifications to the base class
		if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd)
			lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
#ifndef _WIN32_WCE
		// tooltip notifications come with the tooltip window handle and button ID,
		// pass them to the parent if we don't handle them
		else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID)
			bHandled = pT->GetToolTipText(lpnmh);
#endif // !_WIN32_WCE
		// only let notifications not from the toolbar go to the parent
		else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID)
			bHandled = FALSE;

		return lRet;
	}

          这段代码值得仔细看, 自绘的目标并不是针对 CPaneContainerImpl 的,而是针对他的控件-工具条的。如果没有关闭按钮,就是没有工具条的话,这个函数直接返回。如果是工具条的 NM_CUSTOMDRAW 通知,直接调用 

lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);



如果是对于关闭按钮的 TTN_GETDISPINFO 通知,就将提示文本返回给它。最后一个if语句的意思是, 如果同时不是工具条,同时这个通知与关闭按钮无关的话,就不处理这个通知。也就是说,这个通知会继续向上返回,交由上层窗口处理。
          CPaneContainerImpl 窗口自身的绘制是由OnPaint完成的。CCustomDraw 并不是针对它的,而是针对窗口内部的控件,这大概就是与 COwnerDraw的不同之处。
       至于工具条上的按钮,我们知道,按下工具条上的按钮会触发 COMMAND消息。这个消息会发给上层窗口,那么CPaneContainerImpl就是工具条的父窗口,MESSAGE_HANDLER(WM_COMMAND, OnCommand) 就是处理关闭按钮事件的。

	LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
	// if command comes from the close button, substitute HWND of the pane container instead
		if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd)
			return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd);


		bHandled = FALSE;
		return 1;
	}




     这里对WM_COMMAND消息只是简单发送给父窗口。毕竟我们控件里面不可能写事件处理代码,我们都不知道用户设计的这个事件用来做什么呢。
      至于动态建立的工具条,我并没有找到并关于自绘的风格。我查找了微软关于自绘的文档,大致明白了它的机制。也明白了为什么不直接使用Button,而使用Toolbar的原因。原来Button并不支持自绘。下面是哪个页面的连接。
http://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx#CustomDraw_Prepaint




     至此前面说提出的任务,三件我们完成了两件。最后一件很简单,技术上没有什么难度。自己看看代码吧。


	HWND SetClient(HWND hWndClient)
	{
		HWND hWndOldClient = m_wndClient;
		m_wndClient = hWndClient;
		if(m_hWnd != NULL)
		{
			T* pT = static_cast<T*>(this);
			pT->UpdateLayout();
		}
		return hWndOldClient;
	}



另外需要说明:本文部分内容是从网络拷贝过来的。由于写文章的时候关闭了哪个网页,找不到了。没有办法提供作者的名字,这里想原作者表示歉意和感谢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值