Duilib 源码分析(三)界面解析

例子

CPaintManagerUI m_PaintManager;
CDialogBuilder builder;
CControlUI* pRoot;
pRoot = builder.Create(_T("duilib.xml"), (UINT)0, NULL, &m_PaintMana);
m_PaintManager.AttachDialog(pRoot);

CDialogBuilder头文件

// 创建界面的回调函数
class IDialogBuilderCallback
{
public:
    // 自定义控件
    virtual CControlUI* CreateControl(LPCTSTR pstrClass) = 0;
};

class DUILIB_API CDialogBuilder
{
public:
    CDialogBuilder();
    // 加载xml界面
    CControlUI* Create(STRINGorID xml, LPCTSTR type = NULL, IDialogBuilderCallback* pCallback = NULL,
        CPaintManagerUI* pManager = NULL, CControlUI* pParent = NULL);

    // 根据xml界面生成界面信息
    CControlUI* Create(IDialogBuilderCallback* pCallback = NULL, CPaintManagerUI* pManager = NULL,
        CControlUI* pParent = NULL);

    // 获取xml解析器
    CMarkup* GetMarkup();
    void GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const;
    void GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const;
private:

    // 解析界面信息,生成界面节点树,并且填充属性
    CControlUI* _Parse(CMarkupNode* parent, CControlUI* pParent = NULL, CPaintManagerUI* pManager = NULL);

    // xml解析器,用以读取并解析xml配置文件;
    CMarkup m_xml;

    // 创建界面的回调,支持定义控件的接口
    IDialogBuilderCallback* m_pCallback;

    // 资源类型,指定要加载什么资源
    LPCTSTR m_pstrtype;
}

CDialogBuilder源文件

CControlUI* CDialogBuilder::Create(STRINGorID xml, LPCTSTR type, IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent)
{
    // 1、从字符串加载xml
    m_xml.Load(xml.m_lpstr);
    
    // 2、从文件加载xml
    m_xml.LoadFromFile(xml.m_lpstr);
    
    // 3、从资源加载xml
    HGLOBAL hGlobal = ::LoadResource(CPaintManagerUI::GetResourceDll(), hResource);
    m_xml.LoadFromMem((BYTE*)::LockResource(hGlobal), ::SizeofResource(CPaintManagerUI::GetResourceDll(), hResource);
    
  return Create(pCallback, pManager, pParent);
}

CControlUI* CDialogBuilder::Create(IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent)
{
    // 界面根节点
    CMarkupNode root = m_xml.GetRoot();
    
    // 遍历界面节点,获得常规属性
    for( CMarkupNode node = root.GetChild() ; node.IsValid(); node = node.GetSibling() ) 
    {
         if( _tcsicmp(pstrClass, _T("Image")) == 0 )
         else if( _tcsicmp(pstrClass, _T("Font")) == 0 )
         else if( _tcsicmp(pstrClass, _T("Default")) == 0 ) 
         else if( _tcsicmp(pstrClass, _T("MultiLanguage")) == 0 )  
    }
    
    // 最后获取属性的默认值
    if( _tcsicmp(pstrClass, _T("Window")) == 0 ) ...
    
    // 解析节点
    return _Parse(&root, pParent, pManager);
}

CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent, CPaintManagerUI* pManager)
{
    // 遍历所有节点
    for( CMarkupNode node = pRoot->GetChild() ; node.IsValid(); node = node.GetSibling() ) 
    {
        // 过滤已处理的常规属性
        if( _tcsicmp(pstrClass, _T("Image")) == 0 
            || _tcsicmp(pstrClass, _T("Font")) == 0 
            || _tcsicmp(pstrClass, _T("Default")) == 0 
			|| _tcsicmp(pstrClass, _T("MultiLanguage")) == 0 ) 
            continue;
            
        // include 表示引入另一个xml
        if( _tcsicmp(pstrClass, _T("Include")) == 0 ) 
        {
            CDialogBuilder builder;
            CControlUI* pControl = NULL;
            pControl = builder.Create(m_pCallback, pManager, pParent);
        }
        // 处理 树节点
        else if( _tcsicmp(pstrClass, _T("TreeNode")) == 0 ) 
        {
            CTreeNodeUI* pParentNode	= static_cast<CTreeNodeUI*>(pParent->GetInterface(_T("TreeNode")));
			CTreeNodeUI* pNode			= new CTreeNodeUI();
            pParentNode->Add(pNode)
        }
        // 处理 控件
        else
        {
            if( _tcsicmp(pstrClass, DUI_CTR_EDIT) == 0 )
                pControl = new CEditUI;
            else if( _tcsicmp(pstrClass, DUI_CTR_LIST) == 0 )
                pControl = new CListUI;
            ...
        }
        
        // 不是标准控件,则在插件中生成节点
        if( pControl == NULL ) 
        {
            CDuiPtrArray* pPlugins = CPaintManagerUI::GetPlugins();
            for( int i = 0; i < pPlugins->GetSize(); ++i ) 
            {
                lpCreateControl = (LPCREATECONTROL)pPlugins->GetAt(i);
                pControl = lpCreateControl(pstrClass);
            }
        }
        // 插件里面也没有,就在回调中生成节点,自定义控件
        if( pControl == NULL && m_pCallback != NULL ) 
        {
            pControl = m_pCallback->CreateControl(pstrClass);
        }
    }
    
    // 解析子节点
    if( node.HasChildren() ) {
        _Parse(&node, pControl, pManager);
    }
    // 先设置默认属性
    pControl->SetAttributeList(pDefaultAttributes);
    
    // 再设置节点属性
    pControl->SetAttribute(node.GetAttributeName(i), node.GetAttributeValue(i));
}

插件

CDuiPtrArray* CPaintManagerUI::GetPlugins()
{
    return &m_aPlugins;
}

bool CPaintManagerUI::LoadPlugin(LPCTSTR pstrModuleName)
{
    ASSERT( !::IsBadStringPtr(pstrModuleName,-1) || pstrModuleName == NULL );
    if( pstrModuleName == NULL ) return false;
    HMODULE hModule = ::LoadLibrary(pstrModuleName);
    if( hModule != NULL ) {
        LPCREATECONTROL lpCreateControl = (LPCREATECONTROL)::GetProcAddress(hModule, "CreateControl");
        if( lpCreateControl != NULL ) {
            if( m_aPlugins.Find(lpCreateControl) >= 0 ) return true;
            m_aPlugins.Add(lpCreateControl);
            return true;
        }
    }
    return false;
}

Duilib通过LoadLibrary加载插件dll,
在插件dll中,只需要实现一个接口CreateControl,如
extern"C" __declspec(dllexport) CControlUI* CreateControl(LPCTSTR pstrType)
{
    if( _tcscmp(pstrType,_T("ButtonEx")) == 0 ) return new CButtonExUI;
    return NULL;
}
在使用程序中,需要在WinMain函数把插件dll使用CPaintManagerUI::LoadPlugin加载进来,然后就可以和内置控件一样使用了。

自定义控件

// MenuUI
const TCHAR* const kMenuUIClassName = _T("MenuUI");
const TCHAR* const kMenuUIInterfaceName = _T("Menu");
class CMenuBuilderCallback: public IDialogBuilderCallback
{
	CControlUI* CreateControl(LPCTSTR pstrClass)
	{
		if (_tcsicmp(pstrClass, kMenuUIInterfaceName) == 0)
		{
			return new CMenuUI();
		}
		else if (_tcsicmp(pstrClass, kMenuElementUIInterfaceName) == 0)
		{
			return new CMenuElementUI();
		}
		return NULL;
	}
};

  
小结
  Duilib加载界面信息的三个来源:字符串,文件,内存。Duilib控件的三个来源:标准控件、插件、自定义控件。

  
Duilib技术交流群:799142530
源码地址:https://github.com/KongKong20/DuilibTutor

posted @ 2021-01-29 14:19  万物归空  阅读(1243)  评论(0编辑  收藏  举报