- UI组件
- 桌面应用
【免费下载链接】SukiUI
UI Theme for AvaloniaUI
SukiUI 内置了一套现代、流畅的对话框(Dialog)系统,它作为可选窗口控件挂在SukiWindow.Hosts之上,以覆盖层形式展示在窗口所有内容(包括标题栏)之上。本指南围绕 官方文档 展开,完整讲解如何在 MVVM 与非 MVVM 场景下接入SukiDialogHost、如何用链式 Builder 构建并显示/关闭对话框、如何配置操作按钮与消息框样式,并结合 SukiUI 仓库 的源码剖析其背后的 Manager 单例调度、对话框池与动画机制,帮助你写出可直接复制运行的对话框代码。
一、对话框系统与 Hosts 架构
SukiUI 在SukiWindow上提供Hosts属性,可以往其中添加任意控件,这些控件将显示在所有其他子控件(包括标题栏)的上层。其基础用法如下(详见 hosts.md):
<!-- XMLNS 定义已略去 --> <suki:SukiWindow> <suki:SukiWindow.Hosts> <!-- 你的控件代码 --> </suki:SukiWindow.Hosts> </suki:SukiWindow>SukiUI 本身提供了两个可选窗口控件:SukiDialogHost(对话框)与SukiToastHost(Toast 通知),均通过Hosts注入。需要注意一个关键约束:
suki:SukiWindow.Hosts仅在SukiWindow上有效,不要在页面(Views)中声明,那是不生效的。
从源码看,SukiDialogHost是一个TemplatedControl(见 SukiDialogHost.cs),其公开的Manager属性是一个StyledProperty<ISukiDialogManager>,负责接收对话框管理器实例;模板由 SukiDialogHost.axaml 定义,包含PART_DialogBackground(半透明背景遮罩)与PART_DialogContent(居中的对话框内容容器),当IsDialogOpen=True时背景透明度为 0.4 并拦截命中测试,关闭后背景延迟 500ms 淡出再隐藏,避免零透明度遮罩继续吞掉 Tooltip 等交互(见 SukiDialogHost.cs)。
二、快速接入:MVVM 方式
对话框对 MVVM 设计模式友好,这是官方最推荐、能达到最佳效果的使用方式。你只需在 ViewModel 中暴露一个ISukiDialogManager,然后在 XAML 中将其绑定到SukiDialogHost的Manager属性即可。
View
<!-- XMLNS 定义已略去 --> <suki:SukiWindow> <suki:SukiWindow.Hosts> <suki:SukiDialogHost Manager="{Binding DialogManager}"/> </suki:SukiWindow.Hosts> <suki:SukiWindow>ViewModel
public class ExampleViewModel { public ISukiDialogManager DialogManager { get; } = new SukiDialogManager(); }这里的ISukiDialogManager是对话框系统的核心抽象(见 ISukiDialogManager.cs),它定义了:
TryShowDialog(ISukiDialog dialog):尝试显示对话框,若当前已有对话框正在显示则返回false且不显示;TryDismissDialog(ISukiDialog dialog):尝试关闭指定对话框,若该对话框已关闭则返回false;DismissDialog():直接关闭当前活动对话框(若有);OnDialogShown/OnDialogDismissed事件:对话框显示/关闭时的回调,参数为SukiDialogManagerEventArgs(内含Dialog属性,见 SukiDialogManagerEventArgs.cs)。
由于绑定基于ISukiDialogManager接口,ViewModel 层可以保持对 UI 的零依赖,方便单元测试与跨平台复用。
三、快速接入:非 MVVM 方式
如果你没有使用 MVVM 模式,只想做简单实现,可以在 XAML 中给SukiDialogHost起一个名字,然后在 Code-Behind 中赋值Manager。
AXAML
<!-- XMLNS 定义已略去 --> <suki:SukiWindow> <suki:SukiWindow.Hosts> <suki:SukiDialogHost Name="DialogHost"/> </suki:SukiWindow.Hosts> <suki:SukiWindow>Code-Behind
public class MainWindow : SukiWindow { public static ISukiDialogManager DialogManager = new SukiDialogManager(); public MainWindow() { InitializeComponent(); DialogHost.Manager = DialogManager; } }用法
MainWindow.DialogManager.CreateDialog() .TryShow();注意SukiDialogHost.Manager的赋值时机:由于Manager是 StyledProperty,上述示例在构造函数中手动赋值即可;MVVM 场景下则通过绑定自动完成。两种方式殊途同归,最终都让 Host 订阅 Manager 的OnDialogShown事件来驱动遮罩与内容的显示(见 SukiDialogHost.cs 对IsDialogOpen的监听逻辑)。
四、显示对话框:CreateDialog 链式构建
SukiUI 提供了一种现代的构建方式来创建和显示对话框。在ISukiDialogManager实例上调用.CreateDialog()即可开始构建,随后通过链式调用轻松设置标题、内容等属性。所有方法都有对应的 XML 注释说明(见 FluentSukiDialogBuilder.cs)。
构建完成后,调用.TryShow()显示对话框——若当前没有其他对话框正在显示,则显示成功;否则静默失败(返回false)。
下面是一个最简单的对话框示例:
public void DisplayDialog() { DialogManager.CreateDialog() .WithTitle("示例对话框") .WithContent("这里是示例对话框的内容。") .TryShow(); }从源码看,CreateDialog()实际是ISukiDialogManager的扩展方法,内部构造SukiDialogBuilder(见 FluentSukiDialogBuilder.cs)。SukiDialogBuilder(见 SukiDialogBuilder.cs)持有Manager与Dialog两个核心成员:Dialog从DialogPool对象池中取出(Dialog = DialogPool.Get()),这样频繁创建/关闭对话框时无需反复分配对象,降低 GC 压力。
除WithTitle/WithContent外,FluentSukiDialogBuilder还提供以下内容类方法:
| 方法 | 作用 |
|---|---|
WithViewModel(Func<ISukiDialog, object> viewModel, bool isViewModelOnly = true) | 给对话框指定 ViewModel,此时 Title/Content 被忽略,仅渲染 ViewModel 对应的 View(由常规的 View 定位策略解析),适合自定义复杂对话框 |
ShowCardBackground(bool show) | 控制是否显示卡片背景 |
五、关闭对话框:点击背景与关闭链
默认情况下,对话框没有自动关闭机制。要添加关闭方式,可以使用.Dismiss()方法。目前最常见的方式是.ByClickingBackground(),即用户点击对话框外部(背景遮罩)时关闭对话框。
例如,下面的代码展示了一个点击背景即可关闭的空对话框:
public void DisplayDialog() { DialogManager.CreateDialog() .Dismiss().ByClickingBackground() .TryShow(); }这一链式调用的内部实现分为两步(见 FluentSukiDialogBuilder.cs):
.Dismiss()返回一个SukiDialogBuilder.DismissDialog包装对象,开启关闭链;.ByClickingBackground()调用SetCanDismissWithBackgroundClick(true),将ISukiDialog.CanDismissWithBackgroundClick置为true(对应属性见 ISukiDialog.cs),此后 Host 检测到背景点击即触发关闭。
关闭动作最终由SukiDialogManager完成(见 SukiDialogManager.cs):
TryDismissDialog只允许关闭当前活动对话框(_activeDialog),随后触发OnDialogDismissed事件、调用对话框的OnDismissed回调;- 关闭后对话框并不会立即销毁,而是通过
DispatcherTimer.RunOnce延迟 500ms 后由DialogPool.Return归还对象池,且若在延迟期间该对话框被重新TryShowDialog,会取消这次归池(CancelPendingPoolReturn),避免复用已被归还的实例。
SukiDialogManager在任何时刻只维护一个_activeDialog,这正是.TryShow()中"若当前没有其他对话框正在显示"这一行为的源码依据。
六、交互操作:添加动作按钮
通过.WithActionButton()方法可以为对话框添加按钮。该方法可以设置按钮的文字、点击后的回调操作,并通过可选参数dismissOnClick控制点击后是否关闭对话框。你可以添加任意多个按钮,为每个按钮设置不同的操作。
以下是一个包含两个按钮的对话框示例,其中一个按钮会关闭对话框:
public void DisplayDialog() { DialogManager.CreateDialog() .WithActionButton("保持打开", _ => { }) .WithActionButton("关闭", _ => { }, true) // 点击后关闭对话框 .TryShow(); }其完整签名(见 FluentSukiDialogBuilder.cs):
public static SukiDialogBuilder WithActionButton( this SukiDialogBuilder builder, object? content, // 按钮文字或任意内容(object) Action<ISukiDialog> onClicked, // 点击回调,参数为当前对话框 bool dismissOnClick = false, // 点击后是否自动关闭对话框,默认 false params string[] classes) // 可选的按钮样式类列表按钮也可以通过最后一个可选参数classes指定样式类,默认使用Flat样式;也可以使用任意一种标准按钮样式。从 SukiDialogBuilder.cs 的实现看,classes为空数组时自动回退为["Flat"],每个类都会被添加到按钮的Classes集合,从而命中 SukiUI 主题中对应的按钮样式;点击时先执行onClicked回调,若dismissOnClick为true则调用Manager.TryDismissDialog(Dialog)关闭对话框。
以下示例代码创建了带有Flat样式按钮并使用强调色的对话框:
public void DisplayDialog() { dialogManager.CreateDialog() .WithActionButton("Styled Button ", _ => { }, true, "Flat", "Accent") .TryShow(); }其中"Flat"、"Accent"等样式类来自 SukiUI 的按钮样式体系(见 SukiButtonStyles.cs 及 Button.axaml),开发者也可以传入自己定义的样式类以实现完全自定义的按钮外观。
七、消息框样式:OfType
你还可以通过.OfType()方法为对话框应用内置的消息框样式。目前支持的样式类型有Information、Success、Warning和Error,它们来自 Avalonia 的NotificationType枚举。
DialogManager.CreateDialog() .OfType(NotificationType.Information) .WithTitle("提示") .WithContent("操作已完成。") .Dismiss().ByClickingBackground() .TryShow();从源码看,OfType委托给SukiDialogBuilder.SetType(见 SukiDialogBuilder.cs),它会为对话框设置图标与图标颜色:
| 类型 | 图标(来自 Icons.cs) | 图标颜色(来自 NotificationColor.cs) |
|---|---|---|
Information | Icons.InformationOutline | NotificationColor.InfoIconForeground |
Success | Icons.Check | NotificationColor.SuccessIconForeground |
Warning | Icons.AlertOutline | NotificationColor.WarningIconForeground |
Error | Icons.AlertOutline | NotificationColor.ErrorIconForeground |
这意味着OfType不仅改变了视觉样式,也把图标与语义色直接绑定到对话框上,配合WithActionButton即可快速拼出"确定/取消"式的消息框:
DialogManager.CreateDialog() .OfType(NotificationType.Success) .WithTitle("保存成功") .WithContent("文件已保存到本地。") .WithActionButton("好的", _ => { }, true) .TryShow();八、进阶能力:异步等待、结果回调与自定义对话框
在官方文档基础之上,从 FluentSukiDialogBuilder.cs 的源码可以看到更多进阶用法,适合需要与用户交互结果联动的场景。
8.1 异步等待对话框关闭:TryShowAsync
SukiDialogBuilder提供TryShowAsync(CancellationToken cancellationToken = default)方法(见 SukiDialogBuilder.cs),可以await对话框直到其被关闭,返回bool作为结果:
var result = await DialogManager.CreateDialog() .WithTitle("确认删除?") .WithYesNoResult("删除", "取消") .TryShowAsync();- 若当前已有对话框打开,
TryShowAsync会抛出InvalidOperationException(Debug 构建下还会触发Debugger.Break()便于排查); - 它支持
CancellationToken,取消时会以TrySetCanceled结束等待。
8.2 WithYesNoResult / WithOkResult:内置结果按钮
配合TryShowAsync,Builder 提供了两种预置结果按钮(见 FluentSukiDialogBuilder.cs):
WithYesNoResult(yesButtonContent, noButtonContent, params classes):添加"是/否"两个按钮,点击"是"返回true,点击"否"返回false,两者都会关闭对话框;WithOkResult(okButtonContent, params classes):添加一个"确定"按钮,点击返回true并关闭对话框。
8.3 OnDismissed:关闭回调
通过.OnDismissed(Action<ISukiDialog>)可以注册一个回调,无论对话框因何种原因(点击背景、点击按钮、程序调用)被关闭都会触发,适合做清理工作或状态同步:
DialogManager.CreateDialog() .WithTitle("提示") .WithContent("即将关闭。") .Dismiss().ByClickingBackground() .OnDismissed(dialog => Console.WriteLine("Dialog dismissed.")) .TryShow();8.4 WithViewModel:自定义复杂对话框
当默认的"标题 + 内容"结构无法满足需求时,可使用WithViewModel渲染任意 ViewModel(通过 SukiUI 的ViewLocator定位对应的 View,见 ViewLocator.cs):
DialogManager.CreateDialog() .WithViewModel(dialog => new MyCustomDialogViewModel()) .TryShow();当isViewModelOnly = true时,对话框只渲染 ViewModel 对应的视图(忽略 Title/Content);设为false时 ViewModel 会作为Content内容渲染。
九、底层动画与生命周期
从 SukiDialogHost.cs 的类注释与实现可以了解到,对话框的开场/退场动画并非写死在 XAML 模板中,而是由SukiDialogMotion(位于 ControlsAnimation/DialogAnimation)统一编排:对话框可以从触发点击的指针位置"浮现"出来,其弹簧动画会依据对话框实测尺寸校准,并支持打开、关闭、固定时抖动的完整编排。Host 通过全局指针位置追踪(_lastPointerPosition)记录最后一次点击坐标,作为开场动画的起始点。
整个对话框的生命周期可归纳为:
CreateDialog()从DialogPool取出一个SukiDialog实例并挂上 Manager;- 链式 API 填充标题、内容、类型、按钮、关闭策略等属性;
.TryShow()→Manager.TryShowDialog,若_activeDialog为空则置为当前活动对话框并触发OnDialogShown,Host 据此翻转IsDialogOpen、淡入背景并播放开场动画;- 关闭时(点击背景 / 按钮 / 代码调用)→
TryDismissDialog或DismissDialog,触发OnDialogDismissed与OnDismissed回调,背景延迟 500ms 淡出; - 延迟 500ms 后对话框实例归还
DialogPool复用,全程避免重复分配对象。
这套设计使得对话框在 MVVM 下可以完全由 ViewModel 驱动,同时在非 MVVM 场景下也能通过简单的 Code-Behind 赋值快速上手,是 SukiUI 中集成度与可扩展性都比较高的组件之一。更多关于 Hosts 机制与 Toast 的对照说明可继续阅读 hosts.md。
- UI组件
- 桌面应用
【免费下载链接】SukiUI
UI Theme for AvaloniaUI
相关推荐
Electron.NET 原生系统对话框(Dialog)API 实战指南:文件选择、消息框与证书信任对话框
Electron.NET 原生系统对话框(Dialog)API 实战指南:文件选择、消息框与证书信任对话框 Electron.Dialog 是 Electron
桌面应用跨平台PrimeVue对话框系统终极指南:模态框、确认框、动态对话框
PrimeVue对话框系统终极指南:模态框、确认框、动态对话框 PrimeVue对话框系统 是Vue.js应用中处理用户交互的核心组件库,提供了一套完整且专业的
前端UI组件设计系统SukiUI 对话框宿主(SukiDialogHost)实战指南:MVVM 接入、流式构建与异步确认框
SukiUI 对话框宿主(SukiDialogHost)实战指南:MVVM 接入、流式构建与异步确认框 SukiUI 为 AvaloniaUI 提供了基于宿主(
UI组件桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考