激烈振动

Visit My MSN Space

导航

Skin技术实现框架(四)

今天有点空了,继续写。上次我们已经得出了基本的设计,由此确定了每种窗口必须有一个类来与之对应,这里所说的窗口种类是按照窗口的windows class名称来区分的,class名称相同的就认为是一种窗口。这种分类方法和我们看到的窗口种类可能有一些差异,例如,普通按钮,单选按钮和复选框的类名都是“Button”,对于这种情况,我们仍然用一个类来对应这些窗口,而在类内部区分对待这些不同的窗口。
这样,我们要为每种需要改变外观的控件窗口编写一个类,根据面向对象的思想,我们很自然的想到提取出它们的公共基类,这就是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  阅读(3114)  评论(4编辑  收藏  举报