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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM