☰
SukiUI 对话框(Dialog)开发指南:基于 SukiWindow.Hosts 的 MVVM 友好对话框系统
2026/10/6 2:24:18 网站建设 项目流程
  • UI组件
  • 桌面应用

【免费下载链接】SukiUI

UI Theme for AvaloniaUI

项目地址:https://gitcode.com/gh_mirrors/su/SukiUI
点击查看免费下载

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):

  1. .Dismiss()返回一个SukiDialogBuilder.DismissDialog包装对象,开启关闭链;
  2. .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)
InformationIcons.InformationOutlineNotificationColor.InfoIconForeground
SuccessIcons.CheckNotificationColor.SuccessIconForeground
WarningIcons.AlertOutlineNotificationColor.WarningIconForeground
ErrorIcons.AlertOutlineNotificationColor.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)记录最后一次点击坐标,作为开场动画的起始点。

整个对话框的生命周期可归纳为:

  1. CreateDialog()从DialogPool取出一个SukiDialog实例并挂上 Manager;
  2. 链式 API 填充标题、内容、类型、按钮、关闭策略等属性;
  3. .TryShow()→Manager.TryShowDialog,若_activeDialog为空则置为当前活动对话框并触发OnDialogShown,Host 据此翻转IsDialogOpen、淡入背景并播放开场动画;
  4. 关闭时(点击背景 / 按钮 / 代码调用)→TryDismissDialog或DismissDialog,触发OnDialogDismissed与OnDismissed回调,背景延迟 500ms 淡出;
  5. 延迟 500ms 后对话框实例归还DialogPool复用,全程避免重复分配对象。

这套设计使得对话框在 MVVM 下可以完全由 ViewModel 驱动,同时在非 MVVM 场景下也能通过简单的 Code-Behind 赋值快速上手,是 SukiUI 中集成度与可扩展性都比较高的组件之一。更多关于 Hosts 机制与 Toast 的对照说明可继续阅读 hosts.md。

  • UI组件
  • 桌面应用

【免费下载链接】SukiUI

UI Theme for AvaloniaUI

项目地址:https://gitcode.com/gh_mirrors/su/SukiUI
点击查看免费下载

相关推荐

上一篇:Shoelace `<sl-mutation-observer>` 组件完全指南:以声明式方式监听 DOM 变更
下一篇:GB/T 7714参考文献排版终极指南:在Overleaf中快速实现标准格式

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询