引言:
现在的应用软件都讲究个性化,因此提供一套漂亮的皮肤就必不可少。这就需要用到一些控件肤化技术。常规是重载MFC的消息列表或虚函数来实现的,但引来的问题是肤化库和应用程序的耦合性太强。经常会由于肤化代码的一处小改动而引起逻辑上并没有关联的整个应用程序大规模的编译和链接,并带来开发效率的急剧下降,这在开发大型软件时是很难忍受的。
本文在参考了一些常用肤化技术后提供了自己的解决方案:采用替换窗体过程,皮肤库高度独立,内部完善与修改毫不影响应用程序。
常见肤化方法:
第一种:利用继承关系直接子类化
第二种:在一个对话框的 OnInitDialog 中逐控件子类化
第三种:在 Hook 中拦截窗口创建消息,并进行子类化
第四种 SetWindowLong替换窗口过程
第五种 在Hook中替换窗口过程
各种肤化技术的原理:
第一类方法采用的是MFC子类化方法来实现(前三种)
1. MFC 通过维护内部一张ChandleMap来记录Window控件与控件子类对象之间的关系。在MFC的全局窗口过程AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)中,通过调用CWnd::FromHandlePermanent(HWND hWnd)得到参数句柄所依附的C+控件子类对象,并调用虚函数CWnd::WindowProc将相应的消息送给相应的控件子类对象的窗口过程
2. 控件子类对象调用CWnd::SubclassWindow登记控件与控件子类对象之间的关系
第二类方法采用替换窗口过程方法来实现(第四、五种)
肤化技术分析:
MFC采用具有层次结构的控件子类设计:通过一张Map表建立控件句柄与C++对象(控件子类对象)的联系,通过控件子类对象的变量来记录和维护控件的状态信息
“肤化”的本质:
1)响应发送给该控件的Windows消息,
2)用恰当的数据表现出恰当的行为。
其中:
1)可以用替换窗体过程来实现,
2)可以用维护自定义控件子类对象和控件句柄的关系来实现。
本文独立肤化库采用的方法:
1. 目标:
肤化目标窗口,接口简单,并且被肤化的窗口影响小。
2. 采用方法:
替换窗口过程
3. 难点:
窗口过程的设计,是否需要为每个类都设计单独的窗口过程?工作量是否过大?
维护状态变量,当有多个控件共享同一窗口过程时,如何维护它们各自的状态?
实现方法:
1. 仿照MFC,建立统一的窗体过程SkinWndProc
2. 建立一张Map表,用来登记hWnd和自定义控件子类对象之间的联系。
3. 创建一个皮肤父类,所有的肤化控件均从它派生(类似与MFC的CWnd),可以在统一的窗体过程SkinWndProc中通过Map表找到相应的自定义控件子类对象,并调用其虚函数实现对Windows消息的响应
4. 重载:SubclassWindow函数,用来完成窗体过程的替换。
外部接口:
1. 提供SkinDlg(HWND hWnd)完成对对话框及其子控件的肤化
2. 提供SkinBtn(HWND hWnd, LPCTSTR lpszSection)指定采用配制文件的哪一节属性肤化按钮
3. 如果需要,提供DonotSkinCtrl(HWND hWnd)用来指定不需要肤化的控件
4. 提供ChangeSkin(LPCTSTR lpszSkinName)更换皮肤
框架原理图:
时序图: