金山界面库分析(1)

来源:互联网 发布:燕十八mysql视频全套 编辑:程序博客网 时间:2024/05/01 20:09

通过XML创建界面---对象的动态创建以及属性的设置


为了界面可配置化和换肤,需要界面元素可以根据XML动态创建和设置属性。

在BKLib中,CBkObject类就完成了这样的功能,主要负责类的创建和属性的设置。因为对象都是从XML动态创建的,动态的创建是一个类最基本的属性,所以其他类都从CBkObject派生。

来看看这个类的四个方法:

BOOL IsClass(LPCSTR lpszName):判断是不是这个类的对象。纯虚方法,也就是从CBkObject继承来的类都要实现这个方法才行,同时,这个方法在不同的类上面会有不同的表现。所以上层定义接口,下层提供实现。这个方法可以在运行时检测类实例的实际类型。

LPCSTR GetObjectClass():同上一个方法,用于获取类的名字。

BOOL Load(TiXmlElement* pXmlElem):从XML中获取属性并将属性设置到对象中,在基类中仅仅是将一个XML元素的属性设置到对象中。当然,如果子类对象有更复杂的实现,比如一个对象对应的XML元素还有子节点,就需要Load其子节点,这些都可以在子类中通过覆盖父类方法来实现。

HRESULT SetAttribute(CStringAstrAttribName, CStringA strValue, BOOL bLoading):设置属性的方法,CBkObject是纯虚类,在XML中不会有对应的节点,自然也没有相应的属性,所以其实现仅仅放回了一个E_FAIL,没有其他操作。

 

接着我们就看到了在bkobject.h里面一大堆的宏定义:

宏定义一般是为了简洁,而这些宏的用途多为子类使用。

BKOBJ_DECLARE_CLASS_NAME:获取类名,判断是否是某个类的对象,还有CheckAndNew,用于动态创建

以下的宏定义主要用于属性的设置和映射(XML节点属性和对象属性的对应)

BKWIN_DECLARE_ATTRIBUTES_BEGIN

BKWIN_DECLARE_ATTRIBUTES_END

BKWIN_CHAIN_ATTRIBUTE

BKWIN_CUSTOM_ATTRIBUTE

BKWIN_INT_ATTRIBUTE

BKWIN_UINT_ATTRIBUTE

BKWIN_DWORD_ATTRIBUTE

BKWIN_STRING_ATTRIBUTE

BKWIN_TSTRING_ATTRIBUTE

BKWIN_HEX_ATTRIBUTE

BKWIN_COLOR_ATTRIBUTE

BKWIN_FONT_ATTRIBUTE

BKWIN_ENUM_ATTRIBUTE

BKWIN_ENUM_VALUE

BKWIN_ENUM_END

BKWIN_STYLE_ATTRIBUTE

BKWIN_SKIN_ATTRIBUTE

 

现在我们看一个例子,继承自CBkObject的控件对象CBkProgress是如何完成从XML动态创建的。

首先,在类的定义中包含宏BKOBJ_DECLARE_CLASS_NAME(CBkProgress,"progress"),将宏展开如下:

public:                                                   static CBkProgress* CheckAndNew(LPCSTR lpszName)         {                                                         if (strcmp(GetClassName(), lpszName) == 0)               return new CBkProgress;                              else                                                      return NULL;                                   }      //通过传入名称创建对应的类,在解析XML中按照节点名字创建对应类的实例                                                          static LPCSTR GetClassName()                          {                                                          return “progress”;                                 }                                                                                                            virtual LPCSTR GetObjectClass()                       {                                                          return “progress”;                                 }       //覆盖父类方法,返回类实例对应的XML节点名字                                                          virtual BOOL IsClass(LPCSTR lpszName)                 {                                                         return strcmp(GetClassName(), lpszName) == 0;    }      //覆盖父类方法,根据XML节点名字检查类实例是否是此节点

另外,包含设置节点属性的宏,如下

        BKWIN_DECLARE_ATTRIBUTES_BEGIN()

        BKWIN_SKIN_ATTRIBUTE("bgskin",m_pSkinBg,TRUE)

        BKWIN_DWORD_ATTRIBUTE("min",m_dwMinValue,FALSE)

        BKWIN_UINT_ATTRIBUTE("showpercent",m_bShowPercent,FALSE)

        BKWIN_DECLARE_ATTRIBUTES_END()

将宏展开如下:

public:                                                               virtual HRESULT SetAttribute(                                         CStringA strAttribName,                                           CStringA strValue,                                                BOOL     bLoading)  //添加SetAttribute方法,在Load中循环调用设置属性    {                                                                     HRESULT hRet = __super::SetAttribute(               //首先设置父类定义的属性           strAttribName,                                                    strValue,                                                         bLoading                                                          );                                                            if (SUCCEEDED(hRet))                                                  return hRet;                                                        if ("bgskin" == strAttribName)                       //属性名称是strAttribName       {                                                                     m_pSkinBg = BkSkin::GetSkin(strValue);           //属性的值是strValue           hRet = TRUE ? S_OK : S_FALSE;                  //是否全部重绘       }                                                                 else                                                                    if ("min" == strAttribName)                                  {                                                                     m_dwMinValue = (DWORD)::StrToIntA(strValue);                           hRet = FALSE ? S_OK : S_FALSE;                             }                                                                 else             if ("showpercent" == strAttribName)                                  {                                                                     m_bShowPercent = (UINT)::StrToIntA(strValue);                            hRet = FALSE ? S_OK : S_FALSE;                            }                                                                 else                                                                   return E_FAIL;                                                                                                                   return hRet;                                                   }

现在我们来看看CheckAndNew和SetAttribute这两个方法是如何被调用的,看调用栈:


1.在实窗口的Create方法中(DoModal中调用Create)调用实窗口的Load和SetXml方法装载XML,在SetXML方法中查找XML中存在的"header"、"body"、"footer"节点调用各自的Load方法并设置相应属性。这三个节点开始就是虚窗口了,调用其Load方法就进入了虚窗口的创建体系。

2.在这三者的调用中会调用CBkPanel::Load方法

   virtual BOOLLoad(TiXmlElement*pTiXmlElem)   {       if (!CBkWindow::Load(pTiXmlElem))  //调用父类load方法,主要进行自身属性设置            returnFALSE;        return LoadChilds(pTiXmlElem->FirstChildElement());  //load子节点}

在CBkPanel::LoadChilds方法中,顺序调用每个子节点的创建方法并调用Load方法。

3. 在CBkPanel::LoadChilds方法中进行了如下调用:

CBkWindow*pNewChildWindow =_CreateBkWindowByName(pXmlChild->Value());

_CreateBkWindowByName函数根据从XML解析出的节点名称调用

pNewWindow= CBkDialog::CheckAndNew(lpszName); // CBkDialog为需要动态创建类的名称

创建出对应节点对象。安装我们展开的CheckAndNew方法,如果节点名称相同,创建类对象并返回,否则返回空。

 

至此,按照XML节点名称动态创建类对象的过程就完成了。

原创粉丝点击