XUI XUI

X,不被定义,无限可能。

前身DUIThink。

XUI是带可视化设计器的跨平台界面开发框架。

它可以像 MFC 一样自动生成窗体类、控件变量和消息映射,并带上线程池、文件、网络、监听等常用开发部件,用来直接做应用。

它可根据设计图直接生成ui。

开源 · C++ · 跨平台

XUI

框架特性

XUI 更接近一套开发框架。线程池、文件操作、网络模型、文件监听、注册表监听、文件拖拽、命令行解析、磁吸算法、内存池、分辨率转换、字符串操作这些部件和界面库在同一套框架里,可以直接用来做完整应用。 设计器负责把界面做成看得见、改得动的结果,避免手写界面时记属性、来回预览。

布局与绘制

  • 控件布局:靠上、靠下、靠左、靠右、居中、相对、绝对
  • 尺寸:固定宽高、最大最小宽高、按文本或子控件自动调整
  • 背景:无锯齿颜色、图片、圆角、渐变,以及自定义 Bitmap
  • SVG图片:覆盖比常见看图工具和部分开源库更全的格式
  • DPI:切换一行代码完成,设计器里可预览各 DPI 效果
  • RichText:同一段文字分字体、分颜色,支持多行与六向对齐
  • Clone:复制出相同控件;Attach / Detach 随意挂到父节点

控件与特效

  • 自绘 Edit:可插入图片、Gif 和带透明通道的文字,适合聊天输入框
  • 自绘 IP 控件、日历控件、Clock 时钟、3D 旋转菜单
  • ComboBox 支持梯度子项滑动
  • Animate 支持 Gif 与序列帧
  • ListView 改一个属性即可在 List、Grid、TileH、TileV 之间切换,子项可拖动换位
  • 树控件:选中按钮、无选中按钮、表头树、单选树,改属性即可切换
  • 窗体特效、控件水波纹,以及一行代码换肤

可视化设计器

设计器是所见即所得的。绑定 Visual Studio 的 sln 后,可以像 MFC 一样生成窗体类、控件变量和控件事件代码。 运行时库开源,设计器单独提供。

根据设计图直接生成ui

  • 任意拖放、对齐控件,修改属性立即看到结果
  • 图片、字体、颜色资源集中查看
  • 控件树里用 Shift + Up / Down 实时调整 Z 序
  • 菜单可按一级、二级、三级实时编辑
  • 支持 Ctrl + C / V / Z / Y,也能把界面复制到另一个视图
  • 支持开发者自定义控件,并实时预览各 DPI
XUI 设计器界面
设计器主界面
项目与工具视图
项目 / 工具视图
控件与属性视图
控件 / 属性视图
图片与事件视图
图片 / 事件视图
颜色资源视图
颜色资源
字体资源视图
字体资源
添加类
添加类
添加变量
添加变量
添加事件
添加事件

效果示例

控件演示和已经用这套界面做出来的应用。

功能控件

基础控件
设计器操作
列表控件
表头树
选中树
QQChat
水波纹

实际应用

FlowSheet · 少儿编程
Scratch · 少儿编程

视频教程

从设计界面到生成代码的操作演示。

根据设计图生成ui
从 0 到 1,几分钟设计一个弹窗
自动生成窗体类、控件变量、控件事件代码
多级菜单:可视化制作迅雷看看菜单

更多教程: bilibili 空间

各平台 Demo

Windows

C++ Demo · Release

已发布

DemoWindows.zip · 5.4 MB

下载 Windows Demo

打包说明

Windows Demo 用 Visual Studio 直接编 Win32 版,不需要 SDL3。开关在 MMHelper/DuiPlatformSDL.props,DuiPlatformSDL 保持默认的 false,也就是不定义 DuiPlatform_SDL。只有改成 true 才会走 SDL3。

用 Visual Studio 打开 XUI.2017.sln,配置选 Release_Unicode | Win32,先编出库。DLL 会拷到仓库的 bin 目录(XUI.2017.dll、MMHelper.2017.dll)。

再打开 Demo\XUIDemo_C++\XUIDemo_C++.sln,配置选 Release | Win32。工程会把这两个 DLL 拷到 Demo\XUIDemo_C++\Release,并生成 XUIDemo_C++.exe。

程序从可执行文件的上一级打开 ../XUIDemo_C++.DuiProj/XUIDemo_C++.DuiProj。把 Release 目录(XUIDemo_C++.exe、XUI.2017.dll、MMHelper.2017.dll)和它旁边的 XUIDemo_C++.DuiProj 按这个相对位置打进 zip,就是 DemoWindows.zip。

Linux

C++ Demo · WSL

已发布

DemoLinux.tgz · 8.2 MB

下载 Linux Demo

打包说明

Linux 包是在 WSL 里编译的,源码直接用 Windows 盘上的仓库,不另拷一份。G: 在 WSL 里是 /mnt/g。先安装编译器、CMake、Ninja 和 SDL3 开发包。

sudo apt install cmake ninja-build g++ libsdl3-dev
cmake -S /mnt/g/Project/DirectUI/DUIThink-Develop -B ~/build-xui -G Ninja \
  -DCMAKE_BUILD_TYPE=Release -DXUI_PLATFORM_SDL=ON
cmake --build ~/build-xui --target XUI XUIDemo_C++ -j

产物在 ~/build-xui/Debug:XUIDemo_C++、libXUI.so。再把 libSDL3.so 和中文字体 wqy-microhei.ttc 放到同一目录。工程目录 Demo/XUIDemo_C++/XUIDemo_C++.DuiProj 要作为 Debug 的上一级,因为程序读的是 ../XUIDemo_C++.DuiProj/XUIDemo_C++.DuiProj。把这一层打成 tar.gz,就是 DemoLinux.tgz。仓库里的 pack-vm.sh 是同一套打包,路径和目标名要以当前 CMake 为准。

macOS

C++ Demo · Release

已发布

DemoMac.zip · 5.5 MB

下载 macOS Demo

打包说明

在 Mac 上安装 SDL3 和 CMake,然后在仓库根目录编译。CMake 会生成 XUIDemo_C++.app,并把 libXUI.dylib、SDL3 拷进 App,同时把 DuiProj 链到 Debug 的上一级。

brew install sdl3 cmake ninja
cmake -S . -B build-macos -G Ninja -DCMAKE_BUILD_TYPE=Release -DXUI_PLATFORM_SDL=ON
cmake --build build-macos --target XUI XUIDemo_C++

把 build-macos/Debug 里的 XUIDemo_C++.app,连同它上一级的 XUIDemo_C++.DuiProj 目录一起打成 zip,就是 DemoMac.zip。App 的 rpath 是 @executable_path,库必须留在 App 里面。

Android

arm64-v8a · APK

已发布

DemoAndroid.apk · 10.5 MB

下载 Android Demo

打包说明

Android 也在 WSL 里打,工程就是 G:\Project\DirectUI\DUIThink-Develop(WSL 路径 /mnt/g/Project/DirectUI/DUIThink-Develop)。需要 JDK 17、Android SDK(platform android-35、build-tools 35.0.0、NDK 27.2.12479018、CMake 3.22.1),以及 app/libs 里的 SDL3-3.4.16.aar。

cd /mnt/g/Project/DirectUI/DUIThink-Develop/Android/DUIThinkDemo
echo "sdk.dir=$ANDROID_HOME" > local.properties
chmod +x ./gradlew
./gradlew --no-daemon assembleRelease

编译前 Gradle 会把 Demo/XUIDemo_C++/XUIDemo_C++.DuiProj 拷进 assets。native 由仓库根目录的 CMakeLists 编出 arm64-v8a 的 libmain.so。APK 在 app/build/outputs/apk/release/app-release.apk,拷出来就是 DemoAndroid.apk。调试包可以把最后一行换成 assembleDebug,或按同样路径跑仓库里的 pack-android.sh。

认识 XUI

XUI 是带可视化设计器的跨平台界面框架,前身是 DUIThink。X 的意思是不被定义,无限可能。许可证是 LGPL-2.1。它把界面和业务拆开:界面放在 .DuiProj 工程里,行为用接近 MFC 的窗体类、控件变量和消息映射来写。

控件没有自己的窗口句柄。整棵控件树挂在一个窗口上,由框架做命中测试、布局和绘制。Windows 默认走 Win32;Linux、macOS、Android 走 SDL3。

源码仓库是 github.com/miaomiaoyule/XUI。下面按这棵目录读,点左侧条目进入对应说明。

  • 认识
  • 仓库与架构
  • 使用
  • 界面
  • 工程
  • 上手

它解决什么

MFC 做复杂皮肤时,很多地方要自己画,改样式成本高。纯手写 XML 又没有预览,属性名也不好记。XUI 把这两件事分开:

  • 结构、布局、颜色、图片、字体放在 .DuiProj 里,由设计器维护。
  • 窗口生命周期、控件查找、点击和定时器放在 C++ 类里,写法接近 MFC 的 DDX 和消息映射。
  • 控件本身没有 HWND。绘制和输入都由所属窗口代理。

所以它既是界面库,也带一套可以直接拿来写程序的部件:线程池、文件、网络、监听、拖拽、命令行、磁吸、DPI、字符串。这些在 里,不依赖控件树。

目录

仓库按库、工具、示例和预编译库分开。应用工程通常同时依赖 XUI 和 MMHelper。头文件入口是 XUI/XUIHead.h,它会继续包含 MMHelper/MMHelperHead.h。

路径作用
XUI/界面框架。导出窗口、控件、资源、工厂
XUI/DUIControl/具体控件
XUI/DUICore/窗口、绘制、消息泵、动画
XUI/DUIManager/CDUIGlobal、CDUIFactory
XUI/DUIXmlPack/读写 DuiProj
MMHelper/与界面解耦的基础库
ThirdDepend/tinyxml2、libwebp、SVG
lib/按 VS 版本、Win32/x64、Debug/Release、MD/MT 区分的预编译库
Demo/XUIDemo_C++/主示例:控件、DPI、换肤、SVG、聊天界面
Demo/FlowSheet/、Demo/Scratch/较小示例
XUI.2017.sln库工程
XUI.2017.Design.sln带设计器宏的库工程

分层

上层只使用下层暴露的类和宏。XML 和绘制细节留在框架内部。

应用窗体(继承 CDUIWnd / CDUINotifyPump)
        |
消息映射、控件查找、业务通知
        |
CDUIWnd:窗口、焦点、捕获、绘制表面、定时器
        |
控件树:CDUIControlBase 及其子类
        |
属性 CDUIPropertyObject + CDUIAttribute*
资源:字体 / 颜色 / 图片
        |
CDUIGlobal + CDUIFactory + CDUIXmlPack
        |
MMHelper + 绘制后端(Win32 为 GDI/GDI+,SDL 平台为 SDL)
层关键类型职责
应用CDlgDemo创建窗口、绑定控件、响应点击和定时器
窗口CDUIWnd持有窗口,分发输入,管理根控件
控件CDUIControlBase位置、可见性、背景、命中、克隆
属性CDUIAttribute*设计器和 XML 都能读写的字段
全局CDUIGlobal加载工程、资源表、DPI、换肤
工厂CDUIFactory按 XML 类名创建控件、资源和属性
序列化CDUIXmlPack读写工程、皮肤、字体、颜色、界面 XML

窗口实现按平台分开:DUIWndWin32 和 DUIWndSDL,对外仍是 CDUIWnd。

编译开关

导出宏在 XUIHead.h:

宏谁定义效果
XUISDK编译 XUI DLL 的工程符号 dllexport
XUILIB静态链接 XUI 的工程不使用 dllimport,并让 MMHelper 也按静态库处理
两者都不定义普通应用按 DLL 导入使用

MMHelper 使用同一套约定:MMHELPER_DLL / MMHELPLIB。

Win32 与 SDL

开关在 MMHelper/DuiPlatformSDL.props。属性 DuiPlatformSDL 默认是 false,也就是不定义 DuiPlatform_SDL,走 Win32,不需要 SDL3。改成 true 才会定义 DuiPlatform_SDL 并链接 SDL3。

Linux、macOS、Android 用 CMake,选项 XUI_PLATFORM_SDL 打开后同样定义 DuiPlatform_SDL。这三端需要 SDL3。Windows 上发布 Demo 用的是 Visual Studio 的 Win32 配置,见 。

最小流程

主示例 Demo/XUIDemo_C++/XUIDemo_C++/XUIDemo_C++.cpp 就是最小闭环。桌面端入口如下:

CDUIGlobal::GetInstance()->Init(hInstance);
CDUIGlobal::GetInstance()->LoadProjectFromFile(
    _T("../XUIDemo_C++.DuiProj/XUIDemo_C++.DuiProj"));
CMMDpi::SetProcessDPIAwareness(
    enMMPROCESS_DPI_AWARENESS::MMPROCESS_PER_MONITOR_DPI_AWARE);

CDlgDemo DlgDemo;
DlgDemo.Create(NULL, _T("XUIDemo"), DUI_WNDSTYLE_FRAME, DUI_WNDSTYLE_EX_FRAME);
DlgDemo.DoModal();

CDUIGlobal::GetInstance()->UnInit();
  1. Init 保存实例,准备绘制、线程池和 DPI。
  2. LoadProjectFromFile 读入整个 UI 工程:皮肤、字体、颜色、属性表、控件 ID、各个界面 XML。
  3. CDlgDemo 构造时把界面名设为 "DlgMain"。这个名字对应工程里的一条界面记录。
  4. Create 创建窗口,按界面名把 XML 实例化成控件树。
  5. DoModal 进入模态消息循环。DoBlock 是另一种阻塞循环,Demo 的 DPI 窗口用的就是它。
  6. UnInit 关闭工程并释放全局资源。

工程也可以不放在磁盘目录里:

接口场景
LoadProjectFromFile开发期,直接读 .DuiProj 目录
LoadProjectFromZip从 zip 文件或内存加载,可带密码
LoadProjectFromResZip把 zip 打进程序资源,类型默认 "ZIP"

加载失败时用 GetDuiLastError() 取最后一条错误文本。Android 上 Demo 会先把 assets 里的 DuiProj 拷到内部存储,再调用 LoadProjectFromFile。程序从可执行文件的上一级打开 ../XUIDemo_C++.DuiProj/XUIDemo_C++.DuiProj。

窗口

CDUIWnd 同时是可设计对象、动画窗口和通知泵。它上面维护根控件、捕获、焦点、悬停、按控件 ID 索引的表、单选分组、绘制表面、Tooltip、插入符和定时器。

方法用途
Create创建顶层或子窗口
DoModal / DoBlock阻塞消息循环
ShowWindow / Close / CenterWindow显示、关闭、居中
FindControl按 ID 或回调在整棵树上找控件
SetDpi / SetScale当前窗口的 DPI 或百分比缩放
AddINotify / RemoveINotify让外部对象接收本窗口通知
SetCapture / SetFocusControl捕获与焦点

样式宏在 DUIExport/DefineBase.h:

  • DUI_WNDSTYLE_FRAME:重叠窗口
  • DUI_WNDSTYLE_DIALOG:弹出对话框
  • DUI_WNDSTYLE_CHILD:子窗口
  • DUI_WNDSTYLE_EX_FRAME / DUI_WNDSTYLE_EX_DIALOG:对应扩展样式

标题栏、关闭按钮、圆角背景由 XML 里的控件自己画。系统样式只决定窗口种类。未进入消息映射的窗口消息可以重写 OnWndMessage。Demo 用它接收托盘消息和 DPI 窗口句柄。

消息映射

输入先进入 CDUIWnd。框架做命中测试后,把鼠标、键盘、定时器转换成 DuiNotify,再交给 CDUINotifyPump。映射宏在 DUIExport/DefineMessage.h。

DuiBegin_Message_Map(CDlgDemo, CDUIWnd)
    Dui_On_Notify_Ctrl(DuiNotify_Timer, IDC_TabViewControls, OnTimerTabViewControls)
DuiEnd_Message_Map()
宏含义
DuiDeclare_Message_Map()在类里声明映射表
DuiBegin_Message_Map(类, 基类)开始映射,并链接基类表
Dui_On_Click_Ctrl(控件ID, 函数)指定控件的点击
Dui_On_SelectChanged_Ctrl选中状态变化
Dui_On_Notify_Ctrl(通知, 控件ID, 函数)任意通知加控件 ID
Dui_On_Notify(通知, 函数)不限控件 ID
Dui_On_Timer()定时器,处理函数固定为 OnDuiTimer
Dui_On_KillFocus_Ctrl失去焦点

处理函数签名统一为 void Member(const DuiNotify &Notify)。Notify.wParam、Notify.lParam、Notify.pNotifyCtrl 携带上下文。定时器 ID 在 wParam 里。

常见通知:DuiNotify_WndInited、DuiNotify_WndLayout、DuiNotify_DpiChanged、DuiNotify_Click、DuiNotify_TextChanged、DuiNotify_SelectChanged、DuiNotify_ItemClick、DuiNotify_WndManagerAttach、DuiNotify_WndManagerDetach。

控件绑定

设计器为每个控件生成数字 ID,写在工程的 ResourceCtrlID.h。窗体类在 OnFindControl 里用 Dui_DDX_Control 绑定成员。这个宏会 FindControl 并做接口查询。

void CDlgDemo::OnFindControl()
{
    __super::OnFindControl();
    Dui_DDX_Control(CDUIContainerCtrl, m_pTabViewControls, IDC_TabViewControls)
    Dui_DDX_Control(CDUIRadioBoxCtrl, m_pRadioViewHomePageCtrl, IDC_RadioBoxHomePage)
}

OnInitDialog 适合做创建后的一次性逻辑:把子页面挂到逻辑类、创建托盘、居中、设置默认选中项。

一块窗口,多块逻辑

大窗口不要把所有逻辑堆在一个对话框类里。Demo 的做法是:

  1. XML 里用 TabLayout 或 VerticalLayout 划出一块容器。
  2. 另写一个继承 CDUINotifyPump 的视图类。
  3. 在 OnInitDialog 里 FindSubControl 找到这块容器,调用视图类的 Attach。
  4. Attach 内部 GetWndOwner()->AddINotify(this),同一套消息映射就可以写在视图类里。

析构时要 RemoveINotify,避免窗口还在派发通知。

控件

所有可视对象都从 CDUIControlBase 派生,而它又是 CDUIPropertyObject。类名同时是 XML 节点名,工厂按这个字符串创建对象。

CDUIPropertyObject
    └── CDUIControlBase
            ├── CDUIStaticCtrl / CDUIButtonCtrl / CDUIEditCtrl / ...
            └── CDUIContainerCtrl
                    ├── CDUIHorizontalLayoutCtrl / CDUIVerticalLayoutCtrl
                    ├── CDUITabLayoutCtrl
                    ├── CDUIListViewCtrl / CDUITreeViewCtrl
                    └── CDUIGroupCtrl / ...
类名说明
CDUIStaticCtrl文本、富文本,可按文本自动算宽高
CDUIButtonCtrl按钮,普通、悬停、按下、禁用图
CDUICheckBoxCtrl / CDUIRadioBoxCtrl复选、单选。单选按组互斥
CDUIEditCtrl自绘编辑框,可插入图片和动图
CDUIRichEditCtrl富文本编辑
CDUIProgressCtrl / CDUISliderCtrl进度、滑块
CDUIHorizontalLayoutCtrl / CDUIVerticalLayoutCtrl水平、垂直布局
CDUITabLayoutCtrl分页容器
CDUIListViewCtrl列表。List、TileH、TileV 之间切换,子项可拖动换位
CDUITreeViewCtrl树。选中按钮、表头、单选靠属性切换
CDUIComboxCtrl下拉框,支持子项梯度滑动
CDUIMenuCtrl / CDUIRotateMenuCtrl多级菜单、3D 旋转菜单
CDUIAnimateCtrl / CDUIWaterCtrlGIF、序列帧、水波纹
CDUICalendarCtrl / CDUIClockCtrl / CDUIDateTimeCtrl日历、时钟、日期时间
CDUIIPAddressCtrl / CDUIHotKeyCtrlIP 地址、热键

每个控件都有背景色、渐变、背景图、圆角、边框、自定义 Bitmap、Clone 和 SetParent。静态文本用 SetAutoCalcWidth / SetAutoCalcHeight 按文字收缩。容器用同名接口按子控件收缩。

布局

位置由 CDUIAttributePosition 描述。每个控件相对父容器声明水平和垂直对齐,再加偏移、固定宽高和最小最大尺寸。

方向取值含义
水平HorizAlign_Left / Right / Center靠左、靠右、水平居中
垂直VertAlign_Top / Bottom / Middle靠上、靠下、垂直居中

SetFloat 让控件脱离自动流,再用 SetAbsoluteRect 按绝对矩形摆放。父容器排布时调用 GetAbsoluteRect 得到子控件矩形。DPI 变化后,内边距可以选择是否跟随缩放。

列表视图类型是:

enum enDuiListViewType
{
    ListView_List,
    ListView_TileH,
    ListView_TileV,
};

调用 CDUIListViewCtrl::SwitchListViewType 切换。网格效果由平铺模式和项尺寸组合出来。

属性

设计器和 XML 能编辑的字段,都是 CDUIAttributeObject 的子类,挂在 CDUIPropertyObject 的属性组里。

属性类存什么
CDUIAttributeBool / Long / Float标量
CDUIAttributeText / TextStyle / RichText文本、对齐、分段富文本
CDUIAttributeColor / ColorSwitch单色,或一组可切换的颜色资源名
CDUIAttriImageSection图片、九宫格、源矩形、平铺
CDUIAttributePosition / Size / Rect布局与矩形
CDUIAttributeDuiSelect / TabSelect / CtrlID引用另一份界面、当前页、控件 ID

属性值多数不直接写死在控件 XML 里,而是存一个 ValueID。真正的值在工程级 Attribute.xml 中按哈希去重。多个控件可以共享同一套圆角、字体或颜色,改一处即可作用于全部引用。

富文本允许一段文字拆成不同字体和颜色,并支持多行以及左、右、上、下、居中对齐。

资源、换肤、DPI

三类资源

CDUIGlobal 启动后维护三张表:字体 CDUIFontBase、颜色 CDUIColorBase、图片 CDUIImageBase。图片包括 PNG、JPEG、GIF、WebP、SVG。绘制支持九宫格、圆角和平铺。

换肤

颜色和图片属性可以保存一组资源名。PerformSwitchRes(nIndex) 切换当前皮肤序号,GetSwitchResIndex 读取当前序号。ColorSwitch 按这个序号取对应颜色。Demo 里的写法:

int nSkin = (CDUIGlobal::GetInstance()->GetSwitchResIndex() + 1) % 2;
CDUIGlobal::GetInstance()->PerformSwitchRes(nSkin);

换肤索引是全局的。一个进程里已加载的界面共享这个序号。资源增删改可以通过 IDuiResourceCallBack 观察。

DPI

进程级用 CMMDpi::SetProcessDPIAwareness,Demo 使用 Per-Monitor。界面级用 CDUIGlobal::SetDpi / SetScale,以及窗口自己的同名方法。SetScale(100) 表示 100%,内部按 96 DPI 换算。全局 SetDpi 会同步已经打开的窗口。只想缩放某一个窗口时,调用该 CDUIWnd 自己的 SetDpi / SetScale。

绘制与动画

CDUIWnd 维护一张后台位图。控件的 OnPaint 在裁剪区内把背景、边框、文字和前景画上去,再贴到窗口。分层透明由窗口属性 WndLayered 和 WndAlpha 控制。Win32 下 GdiplusRenderText、GdiplusRenderImage 决定文字和图片走 GDI 还是 GDI+。

动画分三处:

  • 窗口属性里的入场动画:类型、帧数、帧间隔。
  • CDUIAnimation::StartAnimation:通用帧动画,回调开始、步进和结束。
  • 控件级拖动动画:水平、垂直或双向,用于列表换位、组合框展开。

水波纹数据挂在控件的涟漪位图上,由 CDUIWaterCtrl 和基类配合完成。

DuiProj

一个界面工程是一个目录,入口文件扩展名是 .DuiProj。Demo 的入口是 XUIDemo_C++.DuiProj,内容是资源索引:

<Version Version="5"/>
<SkinRes SkinFile="SkinRes.xml"/>
<FontRes FontFile="FontRes.xml" Default="微软雅黑-字号:12-..."/>
<ColorRes ColorFile="ColorRes.xml"/>
<Attribute AttributeFile="Attribute.xml"/>
<ResourceCtrlID CtrlIDFile="ResourceCtrlID.h"/>
<DirectUI DuiName="DlgMain" DuiType="0" DuiFile="Dlg/DlgMain.xml"/>
DuiType用途
0 对话框独立窗口。构造 CDUIWnd 时使用这个名字
1 视图可嵌入的视图片段
2 列表项模板运行时 Clone 成每一行
3 树节点模板树节点
4 菜单 / 5 三维菜单 / 6 日历弹出层

界面 XML 的根是窗口类名,子节点是控件类名。属性节点带 NameID、ValueID 或内联 Value。日常用设计器改界面,它会同时维护 XML、Attribute.xml 和 ResourceCtrlID.h。

运行时动态插入一块界面:

CDUIControlBase *pCtrl = CDUIGlobal::GetInstance()->LoadDui(_T("ComboxView1"), pWnd);

列表项模板应 Clone 后再插入,这样每一行是独立实例。Clone 默认会刷新控件 ID。若要保持原 ID,把刷新参数设为 false。

自定义控件

CDUIFactory 按类名字符串创建控件、资源和属性。XML 解析到 <CDUIButtonCtrl> 时,工厂调用对应的创建函数。

  1. 继承最接近的基类,一般是 CDUIControlBase 或某个布局控件。
  2. 头文件写 DuiDeclare_CreateControl(CMyCtrl)。
  3. 源文件写 DuiImplement_CreateControl(CMyCtrl),并在 InitProperty 里注册自己的属性组。
  4. 加载 XML 之前调用 DuiRegisterCreateControl(CMyCtrl)。

设计器要识别自定义控件,需要在带设计器宏的库工程里提供控件描述。普通应用工程不必定义这个宏。XML 节点名必须和注册的类名一致,否则创建失败,整段子树加载不出来。

IDuiControlCallBack 用来在不改控件类的情况下监听绘制、尺寸、可见性和释放。

MMHelper

MMHelper 不依赖控件树。XUI 和普通 Win32 程序都能用。CDUIGlobal 自己持有线程池,界面线程和后台任务可以通过服务消息通信。

模块类型用途
字符串CMMString统一字符串
文件CMMFile路径和读写
线程CMMThreadPool、CMMService线程池和服务消息
网络MMTCPSocketClient、MMTCPSocketServerTCP
监听MMFileMonitor、MMRegMonitor目录变化、注册表变化
拖放CMMDragDropOLE 拖放
DPICMMDpi感知级别和缩放换算
其他CMMCommandLine、CMMTrayIcon、MMMagnetBox、MMSvg命令行、托盘、磁吸停靠、SVG

Demo 里的托盘使用 CMMTrayIcon,鼠标消息以自定义窗口消息回到 OnWndMessage。命令行使用 CMMCommandLine,用来区分主 Demo 和 DPI 预览进程。

新建应用

Windows

用 Visual Studio 打开 XUI.2017.sln,配置选 Release_Unicode | Win32,先编出库。DLL 会拷到仓库的 bin(XUI.2017.dll、MMHelper.2017.dll)。保持 DuiPlatformSDL 为 false,这一端不需要 SDL3。

再打开 Demo/XUIDemo_C++/XUIDemo_C++.sln,配置选 Release | Win32,确认示例能跑起来。新工程照它的包含目录和库目录来:包含 XUIHead.h,运行目录能找到对应 DLL。字符集使用 Unicode。头文件里已经写了公共控件 6.0 的 manifest。

Linux、macOS、Android

这三端打开 XUI_PLATFORM_SDL,需要 SDL3。Linux 在 WSL 里直接编译本机仓库,macOS 用 CMake 生成 App,Android 用 Gradle 打 APK。各端命令和打包步骤在 平台 Demo。

最小窗体

class CMainWnd : public CDUIWnd
{
    DuiDeclare_Message_Map()
public:
    CMainWnd() : CDUIWnd(_T("DlgMain")) {}
protected:
    CDUIButtonCtrl *m_pClose = NULL;
    void OnFindControl() override
    {
        __super::OnFindControl();
        Dui_DDX_Control(CDUIButtonCtrl, m_pClose, IDC_BtnClose)
    }
    void OnClickClose(const DuiNotify &)
    {
        Close(IDOK);
    }
};

DuiBegin_Message_Map(CMainWnd, CDUIWnd)
    Dui_On_Click_Ctrl(IDC_BtnClose, OnClickClose)
DuiEnd_Message_Map()

IDC_BtnClose 必须来自设计器生成的 ResourceCtrlID.h,并且 XML 里该按钮的 ID 与宏一致。入口顺序与 相同。

发布与设计器

界面资源怎么带上

开发时用目录形式的 .DuiProj,方便设计器来回保存。发布可以:

  1. 把整个工程目录放在可执行文件旁边,继续 LoadProjectFromFile。可执行文件在 Release/ 里时,DuiProj 目录要放在它的上一级。
  2. 打成 zip,LoadProjectFromZip。
  3. 把 zip 放进程序资源,LoadProjectFromResZip。

图片文件在 SkinRes/。SkinRes.xml 只记录资源名到文件名的映射。

设计器

设计器单独提供,绑定 Visual Studio 的解决方案后可以:

  • 像 MFC 一样生成窗体类、控件变量和消息映射。
  • 拖放、对齐,改属性立即看到结果。
  • 集中查看图片、字体、颜色。
  • 控件树里用 Shift + Up / Down 调整 Z 序。
  • 按一级、二级、三级编辑菜单。
  • 支持 Ctrl + C / V / Z / Y,也能把界面复制到另一个视图。
  • 注册开发者自定义控件,并预览各 DPI。

手写 XML 可以跑通框架,但属性哈希和控件 ID 很容易和 Attribute.xml、ResourceCtrlID.h 对不上。新界面在设计器里做,C++ 只处理绑定之后的行为。设计器安装包在页面顶部的「下载设计器」。

示例与注意

读 Demo/XUIDemo_C++ 时,先看 .DuiProj 里的 DuiName,再在 C++ 里搜同名构造,就能把 XML 和类对上。

路径说明
XUIDemo_C++.cpp初始化、加载工程、命令行分流、模态主窗口
DlgDemo.cpp主窗口绑定、子视图 Attach、托盘定时器
BaseControl/静态文本、复选、单选、进度、组合框
Usage/DpiDlg拉起 DPI 预览,滑块改变缩放
Usage/SwitchSkin换肤入口
Usage/SvgImageSVG 资源
QQ/聊天窗、表情、列表项模板

Demo/FlowSheet 是流程类小示例。Demo/Scratch 是更小的启动样例。

写代码时注意

  • 控件由框架创建,延迟删除走窗口的删除队列。不要在通知回调里直接 delete 正在派发事件的控件。
  • FindControl 依赖 InitControls 建立的 ID 表。动态插入后要让控件进入当前窗口。
  • 消息映射函数必须是 CDUINotifyPump 派生类的成员,控件 ID 用编译期常量。
  • 换肤索引是进程全局的。只缩放一个窗口时,调用该窗口自己的 SetScale。
熟悉的概念在 XUI 里
MFC CDialogCDUIWnd 子类 + 一个对话框界面
DDXOnFindControl + Dui_DDX_Control
消息映射DuiBegin_Message_Map / Dui_On_Click_Ctrl
对话框资源.DuiProj + Dlg/*.xml
控件 IDResourceCtrlID.h 里的 IDC_*