DirectUI的优点及其自定义控件的开发
DirectUI是一款占用资源小、绘图性能高、依赖性小的纯Win32 SDK开发的Windows下普遍适用的可视化界面库。与标准控件换肤类界面库不同的是,DirectUI本身具备了标准控件换肤的功能,但其更强调用户自定义界面的开发,提供可扩展的多种布局控件、几十套可扩展的功能强大的界面控件。它可以构建任何一种类型的2D界面框架。DirectUI吸取了游戏绘图引擎的精髓,并在其基础上创造了脏区域局部更新机制,多核CPU多线程渲染,充分提高了界面库的运行效率,与常见的游戏引擎相比,占用极低的CPU时间。目前支持GDI、DirectX、OpenGL等绘图引擎。
其身影无处不在,MSN、QQ、迅雷、360、网游等等应用案例均是。其具有以下优点:
1、高速绘图引擎,具有较好的性能、极小的内存开销,而且无句柄、无“消息”(事件)
2、无第三方的最小依赖,界面与逻辑彻底分离,且逻辑支持javascript/lua
4、主流界面换肤方式,且皮肤方案可以放在压缩包中
5、不同分辨率、窗口大小下的自适布局(控件)的支持
注1:传统win32/mfc并没有好的布局工具及相应的属性,创建控件实例时必须一旦其位置、大小(CreateWindow/CreateWindowEx),而一旦窗口大小,分辨率改变之后,无法自适,得自己处理。
注2:DirecitUI采用XML描述布局,较传统win32/mfc代码方式更为现代化和便利,用“贴图”点缀应用也因此更加容易。
注3:用过.net/java,会感觉到DirectUI有事件的味道,而c++的消息似乎被包起来了。
DirectUI源于微软的MSN应用,因优秀而很快风靡起来,其商业项目非常成熟但不免费,而开源版的也有不补的,需要根据自身需求进行完善和补充。网上搜索了一下如何其为创建自定义控件,但没有找到,好在自己解决了,方法如下:
UICommonControls.cpp(.h)中包含了八个公共控件类:
class UILIB_API CLabelUI class UILIB_API CButtonUI
class UILIB_API COptionUI
class UILIB_API CTextUI
class UILIB_API CProgressUI
class UILIB_API CSliderUI
class UILIB_API CEditUI
class UILIB_API CScrollBarUI
class UILIB_API CCustomerUI
第一步,照着任何一个类创建一个类似的控件类即可,类名不可重复,上面名为CCustomerUI的控件类是我们创建的,直接继承于DirectUI控件的基类CControlUI 。
第二步,打开UIDlgBuilder.cpp,关注下面代码“case 8”中的代码:
CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent, CPaintManagerUI* pManager) { CDialogLayoutUI* pDialogLayout = NULL; IContainerUI* pContainer = NULL; CControlUI* pReturn = NULL; for ( CMarkupNode node = pRoot->GetChild() ; node.IsValid(); node = node.GetSibling() ) { LPCTSTR pstrClass = node.GetName(); if ( _tcscmp(pstrClass, _T( "Image" )) == 0 || _tcscmp(pstrClass, _T( "Font" )) == 0 \ || _tcscmp(pstrClass, _T( "Default" )) == 0 ) continue ; CControlUI* pControl = NULL; if ( _tcscmp(pstrClass, _T( "Include" )) == 0 ) { if ( !node.HasAttributes() ) continue ; int count = 1; LPTSTR pstr = NULL; TCHAR szValue[500] = { 0 }; SIZE_T cchLen = lengthof(szValue) - 1; if ( node.GetAttributeValue(_T( "count" ), szValue, cchLen) ) count = _tcstol(szValue, &pstr, 10); cchLen = lengthof(szValue) - 1; if ( !node.GetAttributeValue(_T( "source" ), szValue, cchLen) ) continue ; for ( int i = 0; i < count; i++ ) { CDialogBuilder builder; if ( m_pstrtype != NULL ) { // 使用资源dll,从资源中读取 WORD id = (WORD)_tcstol(szValue, &pstr, 10); pControl = builder.Create((UINT)id, m_pstrtype, m_pCallback, pManager, pParent); } else { pControl = builder.Create((LPCTSTR)szValue, (UINT)0, m_pCallback, pManager, pParent); } } continue ; } else { SIZE_T cchLen = _tcslen(pstrClass); switch ( cchLen ) { case 4: if ( _tcscmp(pstrClass, _T( "Edit" )) == 0 ) pControl = new CEditUI; else if ( _tcscmp(pstrClass, _T( "List" )) == 0 ) pControl = new CListUI; else if ( _tcscmp(pstrClass, _T( "Text" )) == 0 ) pControl = new CTextUI; break ; case 5: if ( _tcscmp(pstrClass, _T( "Combo" )) == 0 ) pControl = new CComboUI; else if ( _tcscmp(pstrClass, _T( "Label" )) == 0 ) pControl = new CLabelUI; break ; case 6: if ( _tcscmp(pstrClass, _T( "Button" )) == 0 ) pControl = new CButtonUI; else if ( _tcscmp(pstrClass, _T( "Option" )) == 0 ) pControl = new COptionUI; else if ( _tcscmp(pstrClass, _T( "Slider" )) == 0 ) pControl = new CSliderUI; break ; case 7: if ( _tcscmp(pstrClass, _T( "Control" )) == 0 ) pControl = new CControlUI; else if ( _tcscmp(pstrClass, _T( "ActiveX" )) == 0 ) pControl = new CActiveXUI; break ; case 8: if ( _tcscmp(pstrClass, _T( "Progress" )) == 0 ) pControl = new CProgressUI; else if ( _tcscmp(pstrClass, _T( "RichEdit" )) == 0 ) pControl = new CRichEditUI; else if ( _tcscmp(pstrClass, _T( "Customer" )) == 0 ) pControl = new CCustomerUI; break ; case 9: if ( _tcscmp(pstrClass, _T( "Container" )) == 0 ) pControl = new CContainerUI; else if ( _tcscmp(pstrClass, _T( "TabLayout" )) == 0 ) pControl = new CTabLayoutUI; else if ( _tcscmp(pstrClass, _T( "ScrollBar" )) == 0 ) pControl = new CScrollBarUI; break ; case 10: if ( _tcscmp(pstrClass, _T( "ListHeader" )) == 0 ) pControl = new CListHeaderUI; else if ( _tcscmp(pstrClass, _T( "TileLayout" )) == 0 ) pControl = new CTileLayoutUI; break ; case 12: if ( _tcscmp(pstrClass, _T( "DialogLayout" )) == 0 ) pControl = new CDialogLayoutUI; break ; case 14: if ( _tcscmp(pstrClass, _T( "VerticalLayout" )) == 0 ) pControl = new CVerticalLayoutUI; else if ( _tcscmp(pstrClass, _T( "ListHeaderItem" )) == 0 ) pControl = new CListHeaderItemUI; break ; case 15: if ( _tcscmp(pstrClass, _T( "ListTextElement" )) == 0 ) pControl = new CListTextElementUI; break ; case 16: if ( _tcscmp(pstrClass, _T( "HorizontalLayout" )) == 0 ) pControl = new CHorizontalLayoutUI; else if ( _tcscmp(pstrClass, _T( "ListLabelElement" )) == 0 ) pControl = new CListLabelElementUI; break ; case 20: if ( _tcscmp(pstrClass, _T( "ListContainerElement" )) == 0 ) pControl = new CListContainerElementUI; break ; } // User-supplied control factory if ( pControl == NULL && m_pCallback != NULL ) { pControl = m_pCallback->CreateControl(pstrClass); } } ASSERT(pControl); if ( pControl == NULL ) continue ; // Add children if ( node.HasChildren() ) { _Parse(&node, pControl, pManager); } // Attach to parent // 因为某些属性和父窗口相关,比如selected,必须先Add到父窗口 if ( pParent != NULL ) { if ( pContainer == NULL ) pContainer = static_cast<IContainerUI*>(pParent->GetInterface(_T( "IContainer" ))); ASSERT(pContainer); if ( pContainer == NULL ) return NULL; if ( !pContainer->Add(pControl) ) { delete pControl; continue ; } } // Init default attributes if ( pManager ) { pControl->SetManager(pManager, NULL, false ); LPCTSTR pDefaultAttributes = pManager->GetDefaultAttributeList(pstrClass); if ( pDefaultAttributes ) { pControl->ApplyAttributeList(pDefaultAttributes); } } // Process attributes if ( node.HasAttributes() ) { TCHAR szValue[500] = { 0 }; SIZE_T cchLen = lengthof(szValue) - 1; // Set ordinary attributes int nAttributes = node.GetAttributeCount(); for ( int i = 0; i < nAttributes; i++ ) { pControl->SetAttribute(node.GetAttributeName(i), node.GetAttributeValue(i)); } // Very custom attributes if ( node.GetAttributeValue(_T( "stretch" ), szValue, cchLen) ) { if ( pParent == NULL ) continue ; if ( pDialogLayout == NULL ) pDialogLayout = static_cast<CDialogLayoutUI*>(pParent->GetInterface(_T( "DialogLayout" ))); ASSERT(pDialogLayout); if ( pDialogLayout == NULL ) continue ; UINT uMode = 0; if ( _tcsstr(szValue, _T( "move_x" )) != NULL ) uMode |= UISTRETCH_MOVE_X; if ( _tcsstr(szValue, _T( "move_y" )) != NULL ) uMode |= UISTRETCH_MOVE_Y; if ( _tcsstr(szValue, _T( "move_xy" )) != NULL ) uMode |= UISTRETCH_MOVE_X | UISTRETCH_MOVE_Y; if ( _tcsstr(szValue, _T( "size_x" )) != NULL ) uMode |= UISTRETCH_SIZE_X; if ( _tcsstr(szValue, _T( "size_y" )) != NULL ) uMode |= UISTRETCH_SIZE_Y; if ( _tcsstr(szValue, _T( "size_xy" )) != NULL ) uMode |= UISTRETCH_SIZE_X | UISTRETCH_SIZE_Y; if ( _tcsstr(szValue, _T( "group" )) != NULL ) uMode |= UISTRETCH_NEWGROUP; if ( _tcsstr(szValue, _T( "line" )) != NULL ) uMode |= UISTRETCH_NEWLINE; pDialogLayout->SetStretchMode(pControl, uMode); } } if ( pManager ) { pControl->SetManager(NULL, NULL, false ); } // Return first item if ( pReturn == NULL ) pReturn = pControl; } return pReturn; } |
Customer的长度为8,所以上面case 后面的号是8,关键地方为if( _tcscmp(pstrClass, _T("Customer")) == 0 ) pControl = new CCustomerUI;
自定义控件具体的内容,根据实际需求去写即可。此外,据我所知,商业版的DirectUI的自定义控件是可以做成插件的,即与DirectUI SDK分离开,但开源版的暂不知是否可以。
http://uid.cdc.tencent.com/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端