惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Webroot Blog
Webroot Blog
U
Unit 42
A
About on SuperTechFans
宝玉的分享
宝玉的分享
月光博客
月光博客
C
CERT Recently Published Vulnerability Notes
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
P
Privacy & Cybersecurity Law Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Securelist
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
Apple Machine Learning Research
Apple Machine Learning Research
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
B
Blog
I
Intezer
Last Week in AI
Last Week in AI
T
Threat Research - Cisco Blogs
V
V2EX
L
LangChain Blog
AI
AI
G
GRAHAM CLULEY
T
Tor Project blog
人人都是产品经理
人人都是产品经理
D
Docker
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
I
InfoQ
Y
Y Combinator Blog
C
Comments on: Blog
GbyAI
GbyAI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
Vercel News
Vercel News
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿

博客园 - zhaotianff

一文带你搞懂医疗器械设计开发全生命周期 Windows如何清除本机记录的git用户名和密码 HL7协议详解 医疗行业 GDT 数据格式详解 一文带你搞懂C# 异步编程(async/await)底层原理 如何在C#中使用Chromium headless(无头模式)浏览器 不同.NET版本中的WPF新增功能 如何在WPF中使用 Fluent 主题 Windows平台下的各种原生UI框架介绍 C#如何Hook托管函数 WinDbg 用户层调试进阶教程 Windows编程的一些基础理论 推荐一款优秀的Windows经典文件管理器项目-WinFile Windows如何阻止应用程序联网 如何在WPF中捕获窗口外的事件 如何查看Windows进程的启动来源 Visual Studio 2026新解决方案格式slnx详解 我的微软MVP申请通过了 WPF MVVM实战系列教程(七、Prism模块化) WPF 性能优化实战指南 WPF MVVM实战系列教程(六、Prism区域导航) WPF MVVM实战系列教程(五、Prism中的MVVM) WPF MVVM实战系列教程(四、Prism中的依赖注入) 微软 IDE 新纪元:Visual Studio 2026 初体验 Windows 7无法安装VMWare Tools的解决办法 WPF MVVM实战系列教程(三、创建Bootstrapper/启动器) WPF MVVM实战系列教程(二、使用Visual Studio 创建Prism项目) WPF MVVM实战系列教程(一、Prism框架介绍)
WPF MVVM实战系列教程(八、Prism DialogService, 对话框服务)
zhaotianff · 2026-01-27 · via 博客园 - zhaotianff

DialogService

DialogService(对话框服务)是一种使用抽象层来显示对话框的方法。

ViewModel将显示对话框的责任委托给DialogService,只需向服务提供显示所需的数据即可。

DialogService拥有显示对话框的职责,因此我们可以将ViewModel与 System.Windows解耦,避免从ViewViewModel之间的引用。

在单元测试中,我们可以注入一个虚假的对话框服务实例,而不是显示实际的对话框,并使用我们的虚假对象进行预留和模拟。

在前面介绍MVVM进阶内容时,介绍过为什么需要使用对话框服务

可以参考下面的链接

https://www.cnblogs.com/zhaotianff/p/18816257

在Prism中,框架提供了DialogService功能,所以我们只需要关注View和ViewModel的功能就可以了,不用再关心对话框的创建、返回值如何获取及生命周期等细节问题。

IDialogService

在Prism中,提供了一个IDialogService接口,可以控制对话框的显示

IDialogService.cs

 1 public interface IDialogService
 2 {
 3     /// <summary>
 4     /// 显示对话框
 5     /// </summary>
 6     /// <param name="name">注入到容器中的View名称(要求唯一).</param>
 7     /// <param name="parameters">传递到对话框的参数</param>
 8     /// <param name="callback">对话框显示完成后的回调</param>
 9     /// <example>
10     /// 示例(传递2个参数)
11     /// <code>
12     /// var parameters = new DialogParameters
13     /// {
14     ///     { "title", "Connection Lost!" },
15     ///     { "message", "We seem to have lost network connectivity" }
16     /// };
17     /// _dialogService.ShowDialog("DemoDialog", parameters, <paramref name="callback"/>: null);
18     /// </code>
19     /// </example>
20     void ShowDialog(string name, IDialogParameters parameters, DialogCallback callback);
21 }

除了IDialogService中定义的ShowDialog函数外,Prism框架还在一个IDialogServiceCompatExtensions扩展类中定义了一些扩展函数

如下所示:

IDialogServiceCompatExtensions.cs

 1     /// <summary>
 2     /// IDialogService 的扩展方法
 3     /// </summary>
 4     public static class IDialogServiceCompatExtensions
 5     {
 6 #if !UNO_WINUI
 7         /// <summary>
 8         /// 显示一个非模态对话框。
 9         /// </summary>
10         /// <param name="dialogService">对话框服务实例</param>
11         /// <param name="name">要显示的对话框名称。</param>
12         /// <param name="parameters">要传递给对话框的参数。</param>
13         /// <param name="callback">对话框关闭时要执行的操作。</param>
14         public static void Show(this IDialogService dialogService, string name, IDialogParameters parameters, Action<IDialogResult> callback)
15         {
16             parameters = EnsureShowNonModalParameter(parameters);
17             dialogService.ShowDialog(name, parameters, new DialogCallback().OnClose(callback));
18         }
19 
20         /// <summary>
21         /// 显示一个非模态对话框。
22         /// </summary>
23         /// <param name="dialogService">对话框服务实例</param>
24         /// <param name="name">要显示的对话框名称。</param>
25         /// <param name="parameters">要传递给对话框的参数。</param>
26         /// <param name="callback">对话框关闭时要执行的操作。</param>
27         /// <param name="windowName">在 IContainerRegistry 中注册的宿主窗口名称。</param>
28         public static void Show(this IDialogService dialogService, string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName)
29         {
30             parameters = EnsureShowNonModalParameter(parameters);
31 
32             if(!string.IsNullOrEmpty(windowName))
33                 parameters.Add(KnownDialogParameters.WindowName, windowName);
34 
35             dialogService.ShowDialog(name, parameters, new DialogCallback().OnClose(callback));
36         }
37 
38         /// <summary>
39         /// 显示一个非模态对话框。
40         /// </summary>
41         /// <param name="dialogService">对话框服务实例</param>
42         /// <param name="name">要显示的对话框名称。</param>
43         public static void Show(this IDialogService dialogService, string name)
44         {
45             var parameters = EnsureShowNonModalParameter(null);
46             dialogService.Show(name, parameters, null);
47         }
48 
49         /// <summary>
50         /// 显示一个非模态对话框。
51         /// </summary>
52         /// <param name="dialogService">对话框服务实例</param>
53         /// <param name="name">要显示的对话框名称。</param>
54         /// <param name="callback">对话框关闭时要执行的操作。</param>
55         public static void Show(this IDialogService dialogService, string name, Action<IDialogResult> callback)
56         {
57             var parameters = EnsureShowNonModalParameter(null);
58             dialogService.Show(name, parameters, callback);
59         }
60 
61         private static IDialogParameters EnsureShowNonModalParameter(IDialogParameters parameters)
62         {
63             parameters ??= new DialogParameters();
64 
65             if (!parameters.ContainsKey(KnownDialogParameters.ShowNonModal))
66                 parameters.Add(KnownDialogParameters.ShowNonModal, true);
67 
68             return parameters;
69         }
70 #endif
71     }

使用DialogService实现对话框显示的步骤

1、创建对话框View

这一步我们创建一个用户控件,根据自己想要显示的对话框效果进行布局即可

DialogView.xaml

 1  <Grid x:Name="LayoutRoot" Margin="5">
 2      <Grid.RowDefinitions>
 3          <RowDefinition />
 4          <RowDefinition Height="Auto" />
 5      </Grid.RowDefinitions>
 6 
 7      <TextBlock Text="{Binding Message}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" TextWrapping="Wrap" />
 8      <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0" Grid.Row="1" >
 9          <Button Command="{Binding CloseDialogCommand}" CommandParameter="true" Content="确认" Width="75" Height="25" IsDefault="True" />
10          <Button Command="{Binding CloseDialogCommand}" CommandParameter="false" Content="取消" Width="75" Height="25" Margin="10,0,0,0" IsCancel="True" />
11      </StackPanel>
12  </Grid>

布局效果如下所示

image

2、创建ViewModel

接下来我们创建对话框View绑定的ViewModel

在此之前我们先了解一下IDialogAware接口

IDialogAware接口是弹窗视图模型(ViewModel)必须实现的接口,用于处理弹窗的参数接收、结果返回、关闭确认等逻辑。

定义如下

IDialogAware.cs

 1 //
 2 // 摘要:
 3 //     为视图模型(ViewModels)提供对话框功能和事件的接口。
 4 public interface IDialogAware
 5 {
 6     //
 7     // 摘要:
 8     //     将显示在窗口标题栏中的对话框标题。
 9     string Title { get; }
10 
11     //
12     // 摘要:
13     //     指示 Prism.Services.Dialogs.IDialogWindow 关闭该对话框。
14     event Action<IDialogResult> RequestClose;
15 
16     //
17     // 摘要:
18     //     确定对话框是否可以关闭。
19     //
20     // 返回结果:
21     //     如果为 true,则对话框可以关闭。如果为 false,则对话框将不会关闭。
22     bool CanCloseDialog();
23 
24     //
25     // 摘要:
26     //     当对话框关闭时调用。
27     void OnDialogClosed();
28 
29     //
30     // 摘要:
31     //     当对话框打开时调用。
32     //
33     // 参数:
34     //   parameters:
35     //     传递给对话框的参数。
36     void OnDialogOpened(IDialogParameters parameters);
37 }

其中RequestClose事件包含了一个IDialogResult类型,它用于返回对话框的结果。

该类型定义如下:

IDialogResult.cs

 1 /// <summary>
 2 /// 一个 <see cref="IDialogResult"/> 类型,包含来自对话框的 <see cref="IDialogParameters"/> 参数
 3 /// 以及对话框的 <see cref="ButtonResult"/> 结果。
 4 /// </summary>
 5 public class DialogResult : IDialogResult
 6 {
 7     /// <summary>
 8     /// 来自对话框的参数。
 9     /// </summary>
10     public IDialogParameters Parameters { get; private set; } = new DialogParameters();
11 
12     /// <summary>
13     /// 对话框的结果。()
14     /// </summary>
15     public ButtonResult Result { get; private set; } = ButtonResult.None;
16 
17     /// <summary>
18     /// 初始化 <see cref="DialogResult"/> 类的新实例。
19     /// </summary>
20     public DialogResult() { }
21 
22     /// <summary>
23     /// 初始化 <see cref="DialogResult"/> 类的新实例。
24     /// </summary>
25     /// <param name="result">对话框的结果。</param>
26     public DialogResult(ButtonResult result)
27     {
28         Result = result;
29     }
30 
31     /// <summary>
32     /// 初始化 <see cref="DialogResult"/> 类的新实例。
33     /// </summary>
34     /// <param name="result">对话框的结果。</param>
35     /// <param name="parameters">来自对话框的参数。</param>
36     public DialogResult(ButtonResult result, IDialogParameters parameters)
37     {
38         Result = result;
39         Parameters = parameters;
40     }
41 }

现在我们可以创建ViewModel

DialogViewModel.cs

 1   public class DialogViewModel : BindableBase, IDialogAware
 2   {
 3       /// <summary>
 4       /// 确认/取消 绑定的命令,通过参数来判断true/false
 5       /// </summary>
 6       public DelegateCommand<string> CloseDialogCommand { get; private set; }
 7 
 8       public DialogViewModel()
 9       {
10           CloseDialogCommand = new DelegateCommand<string>(CloseDialog);
11       }
12 
13       private string _message;
14 
15       public string Message
16       {
17           get { return _message; }
18           set { SetProperty(ref _message, value); }
19       }
20 
21       private string _title = "通知消息";
22 
23       public string Title
24       {
25           get { return _title; }
26           set { SetProperty(ref _title, value); }
27       }
28 
29       public event Action<IDialogResult> RequestClose;
30 
31       protected virtual void CloseDialog(string parameter)
32       {
33           ButtonResult result = ButtonResult.None;
34 
35           //判断选择了“确认”还是“取消”
36           if (parameter?.ToLower() == "true")
37               result = ButtonResult.OK;
38           else if (parameter?.ToLower() == "false")
39               result = ButtonResult.Cancel;
40 
41           RaiseRequestClose(new DialogResult(result));
42       }
43 
44       public virtual void RaiseRequestClose(IDialogResult dialogResult)
45       {
46           RequestClose.Invoke(dialogResult);
47       }
48 
49       public virtual bool CanCloseDialog()
50       {
51           return true;
52       }
53 
54       public virtual void OnDialogClosed()
55       {
56           //对话框关闭时调用
57       }
58 
59       public virtual void OnDialogOpened(IDialogParameters parameters)
60       {
61           //从参数中获取消息
62           Message = parameters.GetValue<string>("message");
63       }
64   }

3、使用DialogService显示对话框

现在我们可以在Shell中使用DialogService来显示对话框了。

在此之前,还需要了解DialogParameters类型,它用于传递参数到对话框。本质上来说,它是一个键值对列表。使用方法和Dictionary<string,object>基本类似 ,这里不做详细介绍。

ShellViewModel.cs

 1  public class ShellViewModel : BindableBase
 2  {
 3      private string dialogResultContent;
 4 
 5      public string DialogResultContent
 6      {
 7          get => dialogResultContent;
 8          set => SetProperty(ref dialogResultContent, value);
 9      }
10 
11      public DelegateCommand ShowDialogCommand { get; private set; }
12 
13      private IDialogService dialogService;
14 
15      public ShellViewModel(IDialogService dialogService)
16      {
17          this.dialogService = dialogService;
18 
19          ShowDialogCommand = new DelegateCommand(ShowDialog);
20      }
21 
22      /// <summary>
23      /// 显示对话框
24      /// </summary>
25      private void ShowDialog()
26      {
27          DialogParameters keyValuePairs = new DialogParameters();
28          keyValuePairs.Add("message", "这是用于显示在对话框上的消息");
29          this.dialogService.Show(nameof(DialogView), keyValuePairs, HandleDialogResult);
30      }
31 
32      /// <summary>
33      /// 处理对话框返回结果
34      /// </summary>
35      /// <param name="dialogResult"></param>
36      private void HandleDialogResult(IDialogResult dialogResult)
37      {
38          //显示对话框结果
39          DialogResultContent = dialogResult.Result.ToString();
40      }
41  }

4、注册对话框View

万事具备,只欠东风。

此时我们只需要注册View类型,就可以正常显示这个对话框

 1  public partial class App : PrismApplication
 2  {
 3      protected override Window CreateShell()
 4      {
 5          return Container.Resolve<Shell>();
 6      }
 7 
 8      protected override void RegisterTypes(IContainerRegistry containerRegistry)
 9      {
10          //注册对话框View类型
11          containerRegistry.RegisterDialog<DialogView>();
12      }
13  }

5、运行效果

demo

自定义对话框样式

通过前面的示例我们可以发现,以前在WPF中创建对话框都是直接创建一个Window类型,但是现在创建的是一个UserControl类型。

我们都知道UserControl是无法直接被显示的,这里实际上是Prism帮我们创建了一个宿主窗口。

在前面介绍Bootstrapper在初始化时,会初始化Prism提供的服务

其中就包含了这样一行代码

1  containerRegistry.Register<IDialogWindow, DialogWindow>(); //default dialog host

这个DialogWindow就是Prism用于显示对话框的宿主窗口,它的定义如下

src\Wpf\Prism.Wpf\Services\Dialogs\DialogWindow.xaml

 1 <Window x:Class="Prism.Services.Dialogs.DialogWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="{Binding Title}"
 5         WindowStartupLocation="CenterOwner">
 6     <Window.Style>
 7         <Style TargetType="{x:Type Window}" >
 8             <Setter Property="SizeToContent" Value="WidthAndHeight" />
 9         </Style>
10     </Window.Style>
11 
12 </Window>

如果我们需要自定义宿主窗口的样式,可以通过下面两种方式

方式一,在对话框View中定义样式

 1 <UserControl ...>
 2      <!--定义宿主窗口样式-->
 3     <prism:Dialog.WindowStyle>
 4         <Style TargetType="Window">
 5             <Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" />
 6             <Setter Property="ResizeMode" Value="NoResize"/>
 7             <Setter Property="ShowInTaskbar" Value="False"/>
 8             <Setter Property="SizeToContent" Value="WidthAndHeight"/>
 9         </Style>
10     </prism:Dialog.WindowStyle>
11      <Grid x:Name="LayoutRoot" Margin="5">
12          ...
13      </Grid>
14 </UserControl>

运行效果

image

方式二,自定义宿主窗口对象

这种方式是借助IDialogService的扩展函数来实现的,也就是下面这种形式

1 public static void Show(this IDialogService dialogService, string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName)

实现步骤如下

1、创建自定义宿主窗口

CustomDialog.xaml

 1 <tianxia:BlurWindow x:Class="CustomDialogWindow.Views.CustomDialog"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:CustomDialogWindow.Views"
 7         xmlns:tianxia="clr-namespace:TianXiaTech;assembly=BlurWindow"
 8         mc:Ignorable="d"
 9         Title="CustomDialog" Height="450" Width="800">
10     <tianxia:BlurWindow.Background>
11         <SolidColorBrush Color="White" Opacity=".4"></SolidColorBrush>
12     </tianxia:BlurWindow.Background>
13     <Grid>
14         
15     </Grid>
16 </tianxia:BlurWindow>

说明:这里我使用了blurwindow包,用于窗口美化

2、在窗口后台代码中实现IDialogWindow接口

在此之前我们了解一下IDialogWindow接口,在Prism 框架中,IDialogWindow 是用于定义对话框窗口的核心接口,它是 Prism 对话框服务(IDialogService)的基础组件,目的是标准化对话框的行为。

它可以灵活自定义对话框窗口的样式和逻辑,同时兼容 Prism 的对话框生命周期管理。

IDialogWindow定义如下:

IDialogWindow.cs

 1 public interface IDialogWindow
 2 {
 3     //
 4     // 摘要:
 5     //     对话框内容。
 6     object Content { get; set; }
 7 
 8     //
 9     // 摘要:
10     //     窗口的所有者。
11     Window Owner { get; set; }
12 
13     //
14     // 摘要:
15     //     窗口的数据上下文。
16     //
17     // 备注:
18     //     数据上下文必须实现 Prism.Services.Dialogs.IDialogAware 接口。
19     object DataContext { get; set; }
20 
21     //
22     // 摘要:
23     //     对话框的结果。
24     IDialogResult Result { get; set; }
25 
26     //
27     // 摘要:
28     //     窗口样式。
29     Style Style { get; set; }
30 
31     //
32     // 摘要:
33     //     窗口加载完成时触发。
34     event RoutedEventHandler Loaded;
35 
36     //
37     // 摘要:
38     //     窗口关闭后触发。
39     event EventHandler Closed;
40 
41     //
42     // 摘要:
43     //     窗口正在关闭时触发。
44     event CancelEventHandler Closing;
45 
46     //
47     // 摘要:
48     //     关闭窗口。
49     void Close();
50 
51     //
52     // 摘要:
53     //     显示非模态对话框。
54     void Show();
55 
56     //
57     // 摘要:
58     //     显示模态对话框。
59     bool? ShowDialog();
60 }

CustomDialog.xaml.cs

1   public partial class CustomDialog : TianXiaTech.BlurWindow, IDialogWindow
2   {
3       public CustomDialog()
4       {
5           InitializeComponent();
6       }
7 
8       public IDialogResult Result { get; set; }
9   }

3、注册自定义宿主窗口

1  protected override void RegisterTypes(IContainerRegistry containerRegistry)
2  {
3      //注册自定义宿主窗口
4      containerRegistry.RegisterDialogWindow<CustomDialog>();
5 
6      //注册对话框View类型
7      containerRegistry.RegisterDialog<DialogView>();
8  }

说明:注册宿主窗口之后,显示对话框的过程参考文章前面的内容即可

运行结果

demo

示例代码

https://github.com/zhaotianff/WPF-MVVM-Beginner/tree/main/16_Prism_DialogService