MFC 定制控件(Customize Control) 及 MFC CWnd和WIN32 HWND关联方法
<参考资料 MSDN MFC TNO 15>
文档将概述MFC中定制自定义控件的3种方法:
拥有者绘制控件(Owner Drawing Control and Menu) 自绘制控件(self drawing control and menu) 和子集化(subclass)
1 使用MFC方法定制控件必备的几个基本概念:
控制定制风格的控件的几个标准Windows消息:
WM_MEASUREITEM
WM_COMPAREITEM
WM_DELETEITEM
WM_DRAWITEM
消息的详细说明:
1.1 WM_MEASUREITEM
当自绘制风格(owner draw)的控件(owner-drawn button, combo box, list box, list view control, or menu item)创建时控件的父窗体将受到这个消息用于定制控件的大小
WM_MEASUREITE
MidCtl = (UINT) wParam;//控件的标识
lpmis = (LPMEASUREITEMSTRUCT) lParam;//控件的大小信息
消息处理的返回值:
当函数处理了这个消息必须返回TRUE
1.2 WM_COMPAREITEM
系统发送此消息用于设定一个需要排序的控件(如具有CBS_SORT风格的COMBOBOX,有LBS_SORT风格的LISTBOX)新插入项的位置信息
WM_COMPAREITEM
idCtl = wParam; // 控件标识
lpcis = (LPCOMPAREITEMSTRUCT) lParam; // 2个进行比较的子项信息
消息处理的返回值:
-1 子项1在子项2之前的位置
0 子项1,2 具有相等的排序位置
1 子项1在子项2之后的位置
1.3 WM_DELETEITEM
当ListBox或是combo box被销毁时或它们的某一子项将被除去的时候(如消息 LB_DELETESTRING TCONTENT, CB_DELETESTRING CB_RESETCONTENT)系统会对应每一个被删除的控件子项发送消息给控件的父窗体
WM_DELETEITEM
idCtl = wParam; //控件标识
lpdis = (LPDELETEITEMSTRUCT) lParam; //删除子项信息
消息返回值
当函数处理了这个消息必须返回TRUE
1.4 WM_DRAWITEM
当自绘制风格的button, combo box, list box, or menu 的视觉方式需要改变时将发送WM_DRAWITEM消息给所有者窗体
WM_DRAWITEM
idCtl = (UINT) wParam; // 控件标识
lpdis = (LPDRAWITEMSTRUCT) lParam; // 绘制控件的信息
函数返回值
当函数处理了这个消息必须返回TRUE
2 以下内容为定制自定义控件的3种常见方法:
2.1 拥有者绘制控件或菜单(owner draw controls/menu)
windows支持拥有WS_OWNERDRAW风格的控件发送指定消息给控件的父窗体(控件或是菜单的拥有者)使得父窗体可以定制这些控件的视觉风格或行为
MFC在其消息路由中直接支持4种消息消息的处理:
CWnd::OnDrawItem
CWnd::OnMeasureItem
CWnd::OnCompareItem
CWnd::OnDeleteItem
可以在CWnd的派生类(通常是CMainFrame或是CDialog)中实现这些方法来定制控件
注意:
这种方法实现的又很大的弊端,代码重用率低,这样定制的控件如在另一个地方重用时只能把代码从一个地方拷贝到另一个地方
2.2 自绘制的控件或是菜单
MFC默认的实现owner draw标准消息的方法,将这些本来由父窗体实现的绘制的工作消息解码发送到指定控件,由这些控件来处理这些消息,这种优雅的处理方式使得很容易实现重用率很高的自定义风格的控件
在这些封装控件的MFC类(CMenu, CButton, CListBox, CListCtrl etc)中 ,只需要派生一个新类并重写对应的虚函数就可以轻松定制自定义风格的控件
如CMenu的DrawItem(), MesureItem()函数
2.3 动态子集化(dynamic subclassing)
2.3.1 subclass的概念:
子集化在windows编程中指用一个新的窗口过程(subclass winproc)取代旧的窗口过程,而是用旧的窗体过程(superclass winproc)作为默认的窗口处理,来使得窗体呈现新的特性;
superclass 和 subclass在windows编程中的概念可以用C++中的基类和派生类的关系来理解
在WIN32这个过程可以用API: SetWindowLong来实现
3.4.2 MFC CWnd和WIN32 HWND关联的3种方法
方法一 创建一个CWnd时 CWnd对象创建一个HWND 此时的HWND的风格是可以更改的,如使用Create()
方法二 创建一个CWnd与一个已经存在的HWND关联 此时HWND的风格是不可以被更改的 如使用Attach()
方法三 创建一个CWnd与一个已存在的HWND关联,此时HWND的风格是可以更改的 这就是所谓的动态子集化(dynamic subclassing) 这样就可以实现运行时态的改变一个窗体的行为
对于动态子集化MFC提供了两个函数:
CWnd::SubclassWindow
CWnd::SubclassDlgItem.
使用动态子集化是控制界面风格最优雅的风格和方法,代码重用率高和运行时态动态变化
一般一些比较优良的MFC第三方界面库都是基于这种技术实现的