例子
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