☰
在传统 Win32 窗口中使用 UI Composition 技术
2026/10/14 18:50:34 网站建设 项目流程

本文使用 DraftDepot(稿仓 https://github.com/xland/DraftDepot)编辑并发布,DraftDepot 可以把一篇文章一键发布到多个平台。

建立 WinRT 消息分发队列

应用启动时,首先要创建 WinRT 消息分发队列:

DispatcherQueueOptions options{sizeof(DispatcherQueueOptions),DQTYPE_THREAD_CURRENT,DQTAT_COM_NONE};staticwinrt::Windows::System::DispatcherQueueController controller{nullptr};autohr=CreateDispatcherQueueController(options,reinterpret_cast<ABI::Windows::System::IDispatcherQueueController**>(winrt::put_abi(controller)));if(FAILED(hr)){MessageBox(NULL,L"无法创建DispatcherQueueController",L"系统提示",MB_OK);ExitProcess(-1);}

这段代码在当前线程创建 DispatcherQueueController(变量名 controller),用于建立 WinRT 消息分发队列并管理其生命周期。

它是使用 Windows UI Composition 等异步 API 的前提,为后续创建合成器(Compositor)及构建视觉树提供必要的消息循环基础。

桥接原生窗口与 WinRT 合成器

这个任务完成后,就可以创建窗口了,如下代码所示

// winrt::Windows::UI::Composition::Compositor compositor{ nullptr };compositor=Composition::Compositor();autohIns=GetModuleHandle(nullptr);autocls=getWinClsName(hIns);hwnd=CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP|exStyle,cls.data(),title.data(),style,x,y,(float)w,(float)h,NULL,NULL,hIns,NULL);SetWindowLongPtr(hwnd,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(this));autointerop=compositor.as<ABI::Windows::UI::Composition::Desktop::ICompositorDesktopInterop>();// winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget winTarget{ nullptr };autor=reinterpret_cast<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget**>(winrt::put_abi(winTarget));interop->CreateDesktopWindowTarget(hwnd,false,r);// winrt::Windows::UI::Composition::SpriteVisual visual{ nullptr };visual=compositor.CreateSpriteVisual();winTarget.Root(visual);

这段代码在 DispatcherQueue 就绪后,负责创建原生窗口并将其与 WinRT 合成器桥接:

  1. 创建合成器:
    首先实例化 Compositor 对象,它是整个 Windows UI Composition 视觉系统的工厂和入口。
  2. 创建透明窗口:
    通过 CreateWindowEx 创建窗口,其中 WS_EX_NOREDIRECTIONBITMAP 标志至关重要,
    它禁用了传统的 GDI/DWM 重定向表面,使窗口成为仅由 DirectComposition 驱动的纯合成器窗口。
    这种窗口天生支持背景半透明效果。
  3. 关联上下文:
    使用 SetWindowLongPtr 将当前对象指针存入窗口的用户数据区,以便在窗口过程(WndProc)中访问类实例。
    这部分代码不是本文的重点,我没有给出。
  4. 桥接 HWND 与合成器:
    利用 ICompositorDesktopInterop 接口调用 CreateDesktopWindowTarget,
    将原生窗口句柄注册为合成器的桌面渲染目标(winTarget),
    这一步打通了 Win32 窗口与 WinRT Composition 视觉树之间的通道,使后续创建的 Visual 能够正确呈现到该窗口上。
  5. 建立视觉树根节点:
    创建一个 SpriteVisual 作为根视觉对象,并将其设置为 winTarget 的 Root。
    此后所有子 Visual 都挂载在该根节点下,最终通过合成器呈现到窗口上。

绘制元素

如果你想调整窗口的背景色,可以使用如下方法修改 visual 的颜色

winrt::Windows::UI::Color color=winrt::Windows::UI::ColorHelper::FromArgb(a,r,g,b);visual.Brush(win->compositor.CreateColorBrush(color));

如你所见,颜色支持透明通道,可以设置透明颜色。

如果你想增加子元素,可以用如下代码:

auto childVisual=compositor.CreateSpriteVisual();visual.Children().InsertAtTop(childVisual);//移除子元素的代码//visual.Children().Remove(borderVisual);

如果你想设置元素的位置和尺寸,可以用如下方式:

visual.Offset({x,y,0.0f});visual.Size({w,h});

可以通过如下方式控制元素的显隐

visual.IsVisible(false);visual.IsVisible(true);

所有绘图操作都是直接完成的,你不需要通过类似这样的方法刷新窗口

InvalidateRect(hwnd,nullptr,FALSE);

通过这种方式编译的程序可以单exe执行(不需要分发一大堆dll),单exe的体积小于1M

如果大家感兴趣的话,可以点赞、关注。

点赞的人多的话,后面我再介绍如何做布局、圆角、滚动条、渲染文本、画布、字体图标等一系列的知识。

甚至可以介绍如何让这个技术与FaceBook的Yoga布局引擎整合,开发GUI框架(我做过验证了)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询