【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() 里面。

posted @   宋桓公  阅读(1314)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示