金山界面库分析(1)

通过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节点名称动态创建类对象的过程就完成了。

你可能感兴趣的:(DUI界面库)