跳到主要内容

8.5 Prism 9 对话框

学习目标​

你将让任务 ViewModel 请求删除确认,而不引用窗口对象;理解 Prism 9 的关闭监听器、生命周期与结果回调。前提:PrismStudy 已使用 Prism.DryIoc 9.0.537 启动,且学习过 Region 导航。

为什么由服务创建对话框​

后端应用服务通过接口访问外部能力;桌面 ViewModel 也可注入 IDialogService 表达交互请求。它按注册名创建视图及 ViewModel、传入参数并把关闭结果送回调用者。ViewModel 不需要自己 new Window,但仍要把“用户同意”与“删除成功”分开:确认只是许可,实际 API 还可能返回冲突或失败。

对话框内容通常是 UserControl,服务把它装进宿主窗口。以下语句放在已有 App.RegisterTypes 中,文件已有 Prism.Ioc、PrismStudy.Views 和 PrismStudy.ViewModels 导入。

App.xaml.cs:注册语句
containerRegistry.RegisterDialog<ConfirmationDialog,
ConfirmationDialogViewModel>("Confirmation");

对话框 ViewModel​

这是完整的 ViewModel 教学文件,与配套项目采用相同契约。Title 和 Message 来自参数;同意与取消分别返回 Yes、No。关键是 RequestClose 为只读的 DialogCloseListener 属性:由服务初始化,业务代码只调用 Invoke,不自行赋值。参数名是调用双方的约定,注册对话框并不会自动验证传入内容。

ViewModels/ConfirmationDialogViewModel.cs
using Prism.Commands;
using Prism.Dialogs;
using Prism.Mvvm;

namespace PrismStudy.ViewModels;

public sealed class ConfirmationDialogViewModel : BindableBase, IDialogAware
{
private string _title = "确认操作";
private string _message = "";

public ConfirmationDialogViewModel()
{
ConfirmCommand = new DelegateCommand(
() => RequestClose.Invoke(ButtonResult.Yes));
CancelCommand = new DelegateCommand(
() => RequestClose.Invoke(ButtonResult.No));
}

public string Title
{
get => _title;
private set => SetProperty(ref _title, value);
}
public string Message
{
get => _message;
private set => SetProperty(ref _message, value);
}

public DelegateCommand ConfirmCommand { get; }
public DelegateCommand CancelCommand { get; }
public DialogCloseListener RequestClose { get; }

public bool CanCloseDialog() => true;

public void OnDialogOpened(IDialogParameters parameters)
{
Title = parameters.ContainsKey("title")
? parameters.GetValue<string>("title")
: "确认操作";
Message = parameters.ContainsKey("message")
? parameters.GetValue<string>("message")
: "确认继续?";
}

public void OnDialogClosed()
{
// 如果持有订阅或临时资源,在这里清理。
}
}

Title 是示例自己的属性,Prism 9 的 IDialogAware 不再要求它。要让 WPF 宿主标题显示这个值,在已有 ConfirmationDialog.xaml 的 UserControl 根内配置下面的样式;根已声明 Prism XML 命名空间。

Views/ConfirmationDialog.xaml:窗口样式与内容片段
<prism:Dialog.WindowStyle>
<Style TargetType="Window">
<Setter Property="Title" Value="{Binding Title}" />
<Setter Property="Width" Value="380" />
<Setter Property="SizeToContent" Value="Height" />
</Style>
</prism:Dialog.WindowStyle>
<StackPanel Margin="24">
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" />
<Button Margin="0,16,0,0" Content="确认"
Command="{Binding ConfirmCommand}" />
<Button Margin="0,8,0,0" Content="取消"
Command="{Binding CancelCommand}" />
</StackPanel>

OnDialogOpened 初始化本次交互;CanCloseDialog 判断当前是否允许关闭;OnDialogClosed 处理清理。正在保存时可以返回 false 阻止关闭,但它是同步判断,不应在其中等待 HTTP。保存工作应由异步命令管理,完成后再请求关闭。

调用者处理结果​

下列是 TasksViewModel 删除方法的精简片段。前提:已注入 _dialogService、_taskStore,已有 Tasks 集合、SelectedTask 和通知属性 Status;任务类型为配套项目的 TodoItem,文件导入 Prism.Dialogs。先捕获本次任务,避免回调中读取后来变化的选择。完整项目还维护操作状态并处理同步异常,不要覆盖其现有方法。

ViewModels/TasksViewModel.cs:删除方法
private void Delete()
{
var task = SelectedTask;
if (task is null) return;

_dialogService.ShowDialog("Confirmation", new DialogParameters
{
{ "title", "删除任务" },
{ "message", $"确认删除“{task.Title}”?" }
}, new DialogCallback()
.OnClose(result =>
{
if (result.Result != ButtonResult.Yes) return;
if (!_taskStore.Delete(task.Id))
{
Status = "任务已不存在";
return;
}
Tasks.Remove(task);
SelectedTask = null;
Status = "已删除本地任务";
})
.OnError(ex => Status = $"对话框失败:{ex.Message}"));
}

IDialogResult.Result 是 ButtonResult,与导航结果的 Success 不同。关闭按钮等路径未必返回 Yes,因此只在明确同意时执行删除。若需要返回输入,可在 RequestClose.Invoke(parameters, ButtonResult.OK) 中携带参数,再从结果的 Parameters 读取。

9.0.537 仍支持接收 Action<IDialogResult> 的 ShowDialog 扩展,旧式回调可以工作;DialogCallback 则能明确配置关闭与异常路径。API 兼容不意味着旧 RequestClose 事件示例仍适用。

常见错误​

把 RequestClose 声明为 event Action<IDialogResult> 属于旧版写法;自己创建监听器也绕过了服务初始化。直接创建这个 ViewModel 后执行确认命令,并不等于由服务打开的对话框。测试调用者时可替换服务或业务确认接口,测试对话框自身则需要正确初始化关闭监听流程。

所有创建窗口的操作应从 UI 线程发起;任务 API 的失败要由删除流程另行处理,不能因为弹窗已关闭就显示“删除成功”。多窗口应用还应明确宿主与所有者策略。

练习与验收​

新增两条任务,选中一条后删除:取消与窗口关闭都应保持集合数量,同意后只移除被确认的那条。把注册名临时改错,验收时应显示对话框失败状态。恢复注册后,用调试器观察 OnDialogOpened、关闭许可与 OnDialogClosed,确认每次交互参数正确且清理执行一次。

官方资料​

下一节:EventAggregator 与消息通信。