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