【Prism系列】Prism子窗口实现
前言
上节介绍的事件聚合器其实就可以实现MVVM模式下的弹窗过程,不过Prism非常贴心的有封装了专门实现子窗口的全套逻辑。
IDialogAware
既然要打开子窗口,那我们需要准备内容,但是窗口本身是prism准备好的,我们需要的准备是窗口内容,所以这里我新建一个用户控件,最终这个用户控件会被安排在一个prism为我们准备好的窗口里。我们为这个用户控件(view), 准备一个ViewModel,注意这个ViewModel需要实现接口IDialogAware。
1 新建用户控件
新建一个用户控件,命名一个类名就叫 “MyDialog” 吧,记住这个名字后续注册时用到:
<UserControl x:Class="WpfTest.Control.MyDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfTest.Control"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<TextBlock Text="我是个子窗口" FontSize="30"/>
<Button Command="{Binding BtnCloseCommand}" Content="Close" Width="200"/>
</StackPanel>
</UserControl>
2 新建一个ViewModel
给 “MyDialog”配套一个 “MyDialogViewModel.cs” ,注意该类需要实现接口IDialogAware。
internal class MyDialogViewModel: BindableBase, IDialogAware
{
#region IDialogAware的接口实现
// 窗口标题
public string Title => "弹窗";
// 关闭弹窗操作
public event Action<IDialogResult> RequestClose;
// 是否允许关闭弹窗
public bool CanCloseDialog()
{
return true;
}
// 当窗口关闭的调用
public void OnDialogClosed()
{
MessageBox.Show("窗口关闭了!");
}
// 当窗口打开的时候调
public void OnDialogOpened(IDialogParameters parameters)
{
MessageBox.Show($"窗口打开了!" + "\n" +
$"{parameters.GetValue<MainWindowViewModel>("123")}" + "\n" +
$"{parameters.GetValue<string>("456")}"
);
}
#endregion
private string name = "bean";
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
private string key = "321mima";
public string Key
{
get { return key; }
set { SetProperty(ref key, value); }
}
public DelegateCommand BtnCloseCommand { get => new DelegateCommand(() =>
{
// 关闭当前窗口
RequestClose?.Invoke(new DialogResult(ButtonResult.OK));
});
}
}
3 窗口内容注册
将我们刚刚新建的用户控件,在App的回调函数RegisterTypes中通过RegisterDialog方法注册到Prism的容器中,确保后期容器知道打开什么内容。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// 注册一个Dialog的内容
containerRegistry.RegisterDialog<MyDialog>();
}
准备工作差不多了,接下来就看看,如何打开这个窗口了。
4 确保MyDialog和MyDialogViewModel是关联上
我们可以通过Prism的默认匹配规则(Views => ViewModels)。
也可以通过手动进行匹配:
ViewModelLocationProvider.Register<MyDialog, MyDialogViewModel>();
如果没匹配,则会报错如下:告诉你Dialog的ViewModel没有实现接口IDialogAware
IDialogService
Prism框架在初始化的时候,就会向容器中注册17项内容,事件聚合器是其一,IDialogService就是其二。它规定了窗口的打开方式。我们之前准备好的窗口内容就由它打开。
1 注入IDialogService
由于IDialogService是prism框架预先注册好的,所以,我们直接在MainWindowViewModel注入(MainWindowViewModel,对应的是主窗口):
[Dependency]
public IDialogService dialogService { get; set; }
2 ShowDialog打开窗口
这里ShowDialog和之前的实现了IDialogAware接口的MyDialogViewModel息息相关。
public DelegateCommand BtnCommand { get; }
[Dependency]
public IDialogService dialogService { get; set; }
public Prism子窗口实现ViewModel()
{
BtnCommand = new DelegateCommand(() => {
DialogParameters dialogParameters = new DialogParameters();
dialogParameters.Add("123", this);
dialogParameters.Add("456", "asdf");
dialogService?.ShowDialog("MyDialog", dialogParameters, DoDialogResult);
});
}
private void DoDialogResult(IDialogResult dialogResult)
{
// 委托 被动执行 被子窗口执行
// dialogResult 第一方面可以拿到任意参数 第二方面 可判断关闭状态
MessageBox.Show("返回值:" + dialogResult.Result.ToString());
}
ShowDialog的时候,我们传入的三个参数。
第一个参数,“MyDialog” ,这个就是我们之前新建的用户控件的类名称,也是我们往容器注册过的类:
containerRegistry.RegisterDialog<MyDialog>();
这里传入字符串“MyDialog” ,Prism就找到之前通过RegisterDialog注册的MyDialog。
第二个参数,dialogParameters 打开窗口可以传入的参数,以键值对的方式,键必须是字符串,值可以是任何内容。
DialogParameters dialogParameters = new DialogParameters();
dialogParameters.Add("123", this);
dialogParameters.Add("456", "asdf");
之前和MyDialog关联实现了IDialogAware的的MyDialogViewModel,就有个实现了一个OnDialogOpened,ShowDialog传入的参数就会在窗口打开时,传入这个函数。
第三个参数,DoDialogResult,是一个回调函数,当窗口关闭之后被调用
private void DoDialogResult(IDialogResult dialogResult)
{
// 委托 被动执行 被子窗口执行
// dialogResult 第一方面可以拿到任意参数 第二方面 可判断关闭状态
MessageBox.Show("返回参数:" + dialogResult.Parameters.ToString());
MessageBox.Show("返回值:" + dialogResult.Result.ToString());
}
这里函数里还有个参数,是对话框返回的结果,如果直接点击关闭按钮关闭的窗口,dialogResult参数里面就没啥内容。
那如何,在关闭之后得到对话框的一些信息呢?
这又要看:实现了IDialogAware的的MyDialogViewModel里面的事件:
public event Action<IDialogResult> RequestClose;
通过触发该事件,就可以关闭窗口并传入参数:
public DelegateCommand BtnCloseCommand { get => new DelegateCommand(() =>
{
// 关闭当前窗口
//RequestClose?.Invoke(new DialogResult(ButtonResult.OK));
DialogParameters dialogParameters = new DialogParameters();
dialogParameters.Add("123", 123);
dialogParameters.Add("456", "456");
RequestClose?.Invoke(new DialogResult(ButtonResult.Retry, dialogParameters));
});
}
这里有个地方,需注意:RequestClose?.Invoke 不能写到 接口方法 OnDialogClosed() 里面。
程序执行到这里时,你会发现 RequestClose 已经为 NULL 了, RequestClose 是Prsim框架赋值的
当事件触发时,窗口会关闭,此时DoDialogResult就会被框架调用,此时dialogResult得到了,触发事件时传入的参数ButtonResult.Retry,以及相应的dialogParameters 。
总结
为了实现MvvM整个实现过程,有亿点绕,这里从头梳理一遍。
1 需要建立一个用户控件(MyDialog)充当窗口内容,Prism框架提供默认窗口包含此用户控件。
2 需要为用户控件,配置一个ViewModel(MyDialogViewModel),此 ViewModel必须实现接口IDialogAware
3 MyDialog与MyDialogViewModel需要关联起来
4 需要通过RegisterDialog注册MyDialog
5 在主窗口中注入IDialogService对象,dialogService 。主窗口中dialogService调用ShowDialog
打开子窗口,通过ShowDialog参数以及实现了IDialogAware的MyDialogViewModel实现窗口调用前的参数输入,以及窗口关闭后的结果输出。
内容扩充
窗口样式设计
方式一:
因为,这里我们使用的是用户控件,而窗口其实是Prism提供的,那如何设置窗口的样式呢?
我们,可以添加如下代码,进行窗口设置。
<prism:Dialog.WindowStyle>
<Style TargetType="Window">
<Setter Property="Width" Value="500"/>
<Setter Property="Height" Value="400"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome GlassFrameThickness="-1"/>
</Setter.Value>
</Setter>
</Style>
</prism:Dialog.WindowStyle>
方式二:
不使用prism提供的窗口,而是自己窗口:
首先我们新建一个窗口DialogWindowBase,然后让该窗口实现接口IDialogWindow:
public partial class DialogWindowBase : Window, IDialogWindow
{
public IDialogResult Result
{
get; set;
}
public DialogWindowBase()
{
InitializeComponent();
}
}
最后在APP类中注册,该窗口:
containerRegistry.RegisterDialogWindow<DialogWindowBase>();
那么prism就会使用这个窗口而不是默认的,那么我们设置这个窗口就OK了。
2022年12月7日 更新内容:
RequestClose?.Invoke 不能写到 接口方法 OnDialogClosed() 里面。
作者:宋桓公
出处:http://www.cnblogs.com/douzi2/
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!