WinUI 参考: 完整的属性面与设计指导,参见 Window Features。
高级窗口特性¶
有些外壳窗口的构想,在 Windows 上只有脱离 Reactor 的安全契约才可能实现。我们把这些做法记录下来,是为了让专家级应用能在充分知情的前提下自行取舍;但 Reactor 不会把这些原语作为一等公民的 WindowSpec 字段提供。
FancyZones 风格的鼠标穿透叠加层¶
类似 FancyZones 的叠加层需要逐像素或颜色键透明,以及鼠标点击穿透。WinUI XAML 合成器无法与经典的 WS_EX_LAYERED 渲染干净地组合,因此 Reactor 只暴露更安全的构建块(Opacity、IgnorePointerInput、WindowStyle.None),把真正的分层叠加层留给 HWND 互操作。
const int GWL_EXSTYLE = -20;
const nint WS_EX_TRANSPARENT = 0x00000020;
const nint WS_EX_TOOLWINDOW = 0x00000080;
const nint WS_EX_LAYERED = 0x00080000;
const uint LWA_ALPHA = 0x00000002;
public static void MakeClickThrough(ReactorWindow window)
{
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window.NativeWindow);
var ex = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
ex | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW);
SetLayeredWindowAttributes(hwnd, 0, 160, LWA_ALPHA);
}
注意事项:
WS_EX_LAYERED可能绕过 WinUI 的合成路径;每次构建都要实测。- 点击穿透意味着该窗口无法接收正常的指针输入。
- 请使用独立的叠加窗口;不要给主应用外壳加分层。
HUD 外观范例¶
当一个 HUD 风格的表面只需要外观效果、而不需要真正的透明时,它可以留在 Reactor 受支持的契约之内:
static class HudWindow
{
public static WindowSpec Spec { get; } = new()
{
Title = "HUD",
Style = WindowStyle.None,
IsMovableByBackground = true,
Level = WindowLevel.Floating,
CornerStyle = WindowCornerStyle.Rounded,
Backdrop = BackdropChoice.Of(BackdropKind.DesktopAcrylic),
};
}
渲染一个深色调的根 Border,加上自定义的 TitleBar(...),并用 WindowLevel.Floating 让该表面保持在应用本地。随仓库发布的 samples/apps/window-styles 应用就是它的活体对照:在一个窗口上切换全部 WindowStyle、WindowLevel、WindowCornerStyle 与 BackdropKind,让你在定稿前先看到组合效果;而 samples/apps/tool-palette 则以真实调色板窗口的形式演示了同样的形态。
注意事项:
BackdropKind.DesktopAcrylic不是 vibrancy(活力材质);它是 Windows 的背景材质。WindowStyle.None会移除系统菜单和标题栏拖拽的交互区域。- 无边框窗口请搭配
IsMovableByBackground或显式调用BeginDragMove()。
通过 SetWindowRgn 实现任意圆角¶
DWM 只暴露离散的圆角偏好(Default、Square、Rounded、RoundedSmall)。你可以用 SetWindowRgn 强制指定任意区域,但这不是平台品质的默认做法。
public static void ApplyRoundedRegion(nint hwnd, int widthPx, int heightPx, int radiusPx)
{
// CreateRoundRectRgn's last two args are the width/height of the corner
// ellipse, not the radius — pass the diameter to get `radiusPx` corners.
var region = CreateRoundRectRgn(0, 0, widthPx, heightPx, radiusPx * 2, radiusPx * 2);
if (region == 0) return;
// SetWindowRgn transfers ownership of the HRGN to the system on
// success — the region must NOT be deleted afterwards. Delete it
// only when the call fails, or the next repaint reads freed GDI
// memory.
if (SetWindowRgn(hwnd, region, bRedraw: true) == 0)
DeleteObject(region);
}
取舍:
- DWM 阴影通常会丢失或被裁剪。
- 在 100% DPI 下边缘可能有锯齿,因为区域是二值掩码。
- 调整大小时必须重新计算该区域,这可能引发重绘级联。
SetWindowRgn成功后,HRGN的所有权归系统所有。 之后再删除它 —— 或用using/SafeHandle包装而间接删除 —— 会释放合成器仍在读取的 GDI 内存。只在SetWindowRgn调用失败时才删除该区域。
仅在你愿意接受上述代价的高度专用外壳中使用。
无法作为 Reactor 原语提供的能力¶
| 场景 | 为何不是 Reactor 的一等 API |
|---|---|
| 真正透明的 XAML 窗口 | WinUI XAML 合成与经典分层窗口渲染相冲突。 |
| NSWindow 式的层级栈 | Windows 只有「普通」和「置顶」两档,没有任意的 z 轴阶梯。 |
SystemBackdrop 之外的 vibrancy/HUD 材质 |
需要平台合成器支持,而不是框架层面的垫片。 |
顶层 HWND 上的连续 CornerRadius |
DWM 只暴露离散的圆角样式;用区域会损失画质。 |
当你确实需要其中某项时,请把不受支持的互操作隔离在一个小型托管辅助类中,让其余 UI 仍是声明式的 Reactor 组件。
后续阅读¶
- 窗口 —— 受支持的顶层窗口 API
- 停靠窗口 —— 可停靠窗格与浮动文档
- WinForms 互操作 —— 在现有桌面外壳中托管 Reactor
- WPF 互操作 —— 把 Reactor 孤岛与 WPF 宿主组合起来