Skin技术实现框架(四)
这样,我们要为每种需要改变外观的控件窗口编写一个类,根据面向对象的思想,我们很自然的想到提取出它们的公共基类,这就是CWidgetHookBase,所有控件窗口处理类的公共基类,实际上是一个C++接口,因为它只包含一个纯虚函数,下面是它的定义:
///////////////////////////////////////////////////////////////////
/// Abstract base class for widget hook
class CWidgetHookBase
{
public:
virtual void Install(HWND hWidget) = 0; //implemented in CWidgetHook
};
这个接口中唯一的Install函数用来实现把对象链接到窗口的功能,也就是SubclassWindow,这会在继承类实现,后面我们再说怎么实现它。今天要讲的实际上是控件类工厂,也就是CWidgetFactory及其继承类。下面是CWidgetFactory的完整声明和实现:
/////////////////////////////////////////////////////////////////////////////////////
/// Abstract factory class for widget hooks. create hook instances
class CWidgetFactory
{
protected:
static CWidgetFactory* m_pInstance;
public:
// initialize the instance
CWidgetFactory()
{
ATLASSERT(m_pInstance==NULL);
m_pInstance = this;
}
// Get the singleton instance
static CWidgetFactory* Instance()
{
return m_pInstance;
}
virtual CWidgetHookBase* CreateWidget(LPCTSTR szClass) = 0;
};
CWidgetFactory* CWidgetFactory::m_pInstance = NULL;
CWidgetFactory使用了两个设计模式,Singleton模式和Abstract Factory模式,实际上还包括Factory Method模式。
首先看抽象工厂模式,我们希望控件工厂根据窗口class的名字创建出不同的控件窗口消息处理类。对于模拟Mac的系统,这些控件窗口消息处理类包括CMacButton, CMacComboBox, CMacTrackBar等;而对于模拟KDE的系统,则是CKDEButton, CKDEComboBox等。这样,我们就可以定义两个CWidgetFactory的继承类,分别叫CMacFactory和CKDEFactory,分别产生这两个系列的对象。CWidgetFactory::CreateWidget就是用来产生这些对象的方法,它是个纯虚函数,必须在继承类中实现。CreateWidget接受窗口class的名字为参数,返回CWidgetHookBase指针,也就是所有控件类的基类。这样,每个对象工厂负责产生一系列对象,但对于一个应用程序来说,应该只有一种风格,也就是说,只能有一个工厂的实例,单件模式来了
这里使用了简化版的Singleton模式,需要声明一个继承类的实例,然后通过CWidgetFactory的静态函数Instance得到这个唯一实例。这里没有控制不能生成第二个实例,不过这不是大问题。
现在来看Factory的一个实现,CMacFactory,完整的代码如下:
class CMacFactory : public CWidgetFactory
{
public:
virtual CWidgetHookBase* CreateWidget(LPCTSTR szClass)
{
if (lstrcmpi(szClass, "Button") == 0 )
return new CMacButton;
else if (lstrcmpi(szClass, "#32770") == 0) //dialog
return new CMacDialog;
else if (lstrcmpi(szClass, "ListBox") == 0)
return new CMacListBox;
else if (lstrcmpi(szClass, WC_TABCONTROL) == 0)
return new CMacTabCtrl;
else if (lstrcmpi(szClass, "#32768") == 0) //menu
return new CMacMenu;
else if (lstrcmpi(szClass, "ComboBox") == 0) //combobox
return new CMacCombo;
else if (lstrcmpi(szClass, TRACKBAR_CLASS) == 0) //trackbar
return new CMacTrackBar;
return NULL;
}
};
再看看消息钩子的代码,都很简单吧
LRESULT CALLBACK CMacSkin::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CWPSTRUCT cwps;
if( nCode == HC_ACTION )
{
CopyMemory(&cwps, (LPVOID)lParam, sizeof(CWPSTRUCT));
switch(cwps.message)
{
case WM_CREATE:
{
CHAR szClass[MAX_PATH];
GetClassName(cwps.hwnd, szClass, MAX_PATH);
CWidgetHookBase* pWidget=NULL;
pWidget = CWidgetFactory::Instance()->CreateWidget(szClass);
if (pWidget)
pWidget->Install(cwps.hwnd);
}
break;
}
}
return CallNextHookEx((HHOOK)CMacSkin::m_hHook, nCode, wParam, lParam);
}
不管对于CMacFactory还是其他的工厂实现,以及不同的控件类系列,钩子函数的实现都是一样的。CWidgetFactory继承类的实现也非常相似,只是替换一些类名而已。
今天把工厂类和钩子都讲完了,可能不是很清楚,那也只能这样了,其实看代码最清楚了。下次讲怎么实现控件吧,包括如何利用ATL/WTL的基础架构,那是我最喜欢的部分了
posted on 2004-05-20 15:25 vibration 阅读(3120) 评论(4) 编辑 收藏 举报