Prism 弹出窗口

1、简单实现
①、创建用户控件

跨模块的窗口弹出,只需要创建窗口的内容即可,也就是用户控件,这里是在Views文件夹下,创建DialogContentView用户控件。

需要注意的是,默认情况下,如果需要对弹出窗口进行样式设置的话,需要通过prism:Dialog.WindowStyle来进行设置。

<UserControl x:Class="Zhaoxi.PrismDialog.Views.DialogContentView" 
                         xmlns:prism="http://prismlibrary.com/"
                         ......>
    <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="Width" Value="500"/>
            <Setter Property="Height" Value="400"/>
            <!--设置WindowStyle方式的无边款-->
            <!--<Setter Property="WindowStyle" Value="None"/>
            <Setter Property="AllowsTransparency" Value="True"/>-->
            <!--设置WindowChrome的无边款-->
            <Setter Property="WindowChrome.WindowChrome">
                <Setter.Value>
                    <WindowChrome GlassFrameThickness="-1"/>
                </Setter.Value>
            </Setter>
        </Style>
    </prism:Dialog.WindowStyle>
</UserControl>

②、创建ViewModel

在ViewModels文件夹中创建DialogContentViewModel类并实现IDialogAware接口。

public class DialogContentViewModel : IDialogAware
{
    public string Title => "跨域弹出来的子窗口标题";

    //关闭弹窗的操作
    public event Action<IDialogResult> RequestClose;

    //是否允许关闭窗口
    public bool CanCloseDialog()
    {
        //可以根据情况做一下业务判断
        return true;
    }
    //窗口关闭或主动触发RequestClose事件时调用
    public void OnDialogClosed()
    {
        
    }
    //窗口打开时调用
    public void OnDialogOpened(IDialogParameters parameters)
    {
        
    }
}

③、注册View

在App的RegisterTypes方法中通过调用IContainerRegistry对象的RegisterDialog方法进行DialogContentView的注册

RegisterDialog<TView>(string name = null):注册对话类型,可以传入string作为使用时的key。

需要注意的是,如果没有传入name参数,使用ShowDialog展示时,则根据注册的对话类型名称来查找。如果传入了name,则必须根据name来查找。

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        return Container.Resolve<Views.MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
              //注册一个窗口类型
        containerRegistry.RegisterDialog<Views.DialogContentView>();
    }
}

④、定义弹出窗口命令

通过IOC依赖注入,获得IDialogService属性对象。

定义命令,在命令中调用IDialogService对象的ShowDialog方法来展示串口。

ShowDialog(string name):展示模态对话框,如果注册时,指定了对话名称,则根据名称在IOC的对话容器中查找。否则就根据对话类型的名称进行查找。至于对话的父类窗口,默认情况下会自动使用Prism内置的默认窗口对象。

Show(string name):的用法与ShowDialog(string name)用法一样,只不过展示的是非模态对话框。

public class MainWindowViewModel:BindableBase
{
    private string _btnContent = "跨域弹窗";

    public string BtnContent
    {
        get { return _btnContent; }
        set 
        {
            SetProperty(ref _btnContent, value);
        }
    }

        //IOC依赖注入
    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
                      //展示窗口
            _dialogService.ShowDialog("DialogContentView");
        });
    }
}

⑤、主窗口中绑定命令

<Window ......>
    <Grid>
        <StackPanel>
            <Button Content="{Binding BtnContent}" Command="{Binding BtnCommand}"/>
        </StackPanel>
    </Grid>
</Window>

2、注册父类窗口
在上面的实现过程中,我们只创建了用户控件,而用户控件的父类容器,即弹出来的窗口对象则使用了Prism中默认的Dialog窗口。也因此,默认情况下,如果需要对弹出的窗口进行设置,要在用户控件中通过prism:Dialog.WindowStyle来进行默认窗口的样式设置。

然而通过prism:Dialog.WindowStyle来进行窗口的样式设置的做法本来就不是很符合xaml的设计规范,而且这只能对当前的用户控件起效果,如果有多个控件希望进行统一的窗口样式处理的话,在每个用户控件中都编写一样的代码会显得很冗余。较好的解决方案就是重新创建并设置好一个父类窗体,然后注册到IOC的对话窗口容器中。

成功为对话设置了父类窗口后,父类窗口的样式就可以跟平常的WPF窗口一样直接在XAML中进行设置了。其在XAML中设置的样式的优先级高于prism:Dialog.WindowStyle。

①、创建父类窗体

在程序集中新建DialogBase文件夹(文件夹名称并不重要,甚至可以不用创建)并在其中创建DialogWindowBase窗口。

在后台代码实现IDialogWindow,简化一下Result属性

<Window ......
        WindowStartupLocation="CenterScreen" Background="Transparent"
        ResizeMode="NoResize"
        Title="DialogWindowBase" Height="300" Width="500">
    <WindowChrome.WindowChrome>
        <WindowChrome GlassFrameThickness="-1"/>
    </WindowChrome.WindowChrome>
    <Grid>

    </Grid>
</Window>
public partial class DialogWindowBase : Window, IDialogWindow
{
    public DialogWindowBase()
    {
        InitializeComponent();
    }

    public IDialogResult Result { get; set; }
}

②、注册父类窗体类型

通过RegisterDialogWindow进行对话父类窗口类型的注册后,会优先使用,而不会自动使用默认的对话窗口类型对象。

RegisterDialogWindow<TWindow>([string name]):注册对话的父类窗口类型。

当注册多个父类窗口类型时,可以传入name方便后面展示时通过key来指定窗口类型,需要注意注册时一旦指定了name,展示时就必须要指定name,否则找不到对应的父类窗口对象。
需要注意的是,要进行注册的对话父类窗口的后台代码中都必须实现IDialogWindow接口,否则无法注册。

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        return Container.Resolve<Views.MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        //注册一个对话类型
        containerRegistry.RegisterDialog<DialogContentView>();
        //注册一个对话父类窗口类型
        containerRegistry.RegisterDialogWindow<DialogWindowBase>();
        //还可以注册多个父类窗口类型,然后根据key来指定
    }
}

③、展示窗口

通过IOC依赖注入,获得IDialogService属性对象。

定义命令,在命令中调用IDialogService对象的ShowDialog方法来展示串口。

  • 当注册的对话父类窗口类型只有一个且没有指定名称时,prism会自动去使用,因此只要指定对话类型即可。

public class MainWindowViewModel:BindableBase
{
    ......
    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
            _dialogService.ShowDialog("DialogContentView");
        });
    }
}

3、指定父类窗体

当注册了多个父类窗体时,可以通过名称来区分。

①、注册多个父类窗口

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        return Container.Resolve<Views.MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        //注册一个对话类型
        containerRegistry.RegisterDialog<DialogContentView>("DialogOne");
        containerRegistry.RegisterDialog<DialogContentView>("DialogTwo"); //这里为了方便就不再创建一个新的用户控件了
        //注册一个对话父类窗口类型
        containerRegistry.RegisterDialogWindow<DialogWindowBase>("windowBase");
        //可以注册多个父类窗口类型,然后根据key来指定
        containerRegistry.RegisterDialogWindow<DialogWindowOne>("windowOne");
    }
}

②、指定展示

需要注意的是,如果注册的对话指定了名称,展示时没有使用匹配的名称会导致找不到而报异常;如果注册的窗体类型都指定了名称,

展示时没有指定名称则会使用prism框架中默认的窗体类型。

public class MainWindowViewModel:BindableBase
{
    ......

    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
                        //指定展示的对话及父类窗体
            _dialogService.ShowDialog("DialogOne",null,null, "windowBase");
        });
    }
}

4、数据传递
当需要向弹出窗体的内容(即我们创建的用户控件)传递一些数据的时候,可以通过ShowDialog方法来实现。

①、传递数据

ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName):展示对话窗口。

parameters:向对话窗口传递的数据对象。
DialogParameters:IDialogParameters的子类,用法跟字典是一样的,可以用DialogParameters对象封装数据后传递给对话窗口。

public class MainWindowViewModel:BindableBase
{
    ......

    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
                        //创建数据载体
            var dialogParameters = new DialogParameters();
            dialogParameters.Add("paramKey", "paramValue");
                        //展示窗口并进行数据传递
            _dialogService.ShowDialog("DialogOne", dialogParameters, null, "windowBase");
        });
    }
}

②、数据处理

传递的数据对象会在窗口打开时,传递给对应的IDialogAware子类对象(一般就是弹出窗口的ViewModel)的OnDialogOpened方法。因此,可以在OnDialogOpened方法中对数据进行处理。

public class DialogContentViewModel : BindableBase,IDialogAware
{
    ......

    public void OnDialogOpened(IDialogParameters parameters)
    {
        PassValue = parameters.GetValue<string>("paramKey");
    }

    private string _passValue;

    public string PassValue
    {
        get { return _passValue; }
        set 
        { 
             SetProperty(ref _passValue, value);
        }
    }
}

5、回调函数
当弹出的窗口关闭时需要执行回调函数,也是通过ShowDialog方法来实现的。

①、设置回调函数

ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName):展示对话窗口。

callback:对话窗口的回调函数,为一个接收IDialogResult类型参数的Action对象。设置后会自动挂载到对应的IDialogAware子类对象(一般就是弹出窗口的ViewModel)RequestClose事件上,也就是可以通过RequestClose事件来调用callback回调函数。

需要注意的是RequestClose事件的触发,会先调用窗口的关闭函数,再执行回调函数。对话窗口的正常关闭也会执行回调函数。

public class MainWindowViewModel:BindableBase
{
        ......
    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
            var dialogParameters = new DialogParameters();
            dialogParameters.Add("paramKey", "paramValue");
            _dialogService.ShowDialog("DialogOne", dialogParameters, DialogCallBack, "windowBase");
        });
    }

    //对话窗口的回调函数
    private void DialogCallBack(IDialogResult result)
    {
        //可以根据传过来的result做业务逻辑处理
    }
}

②、调用回调函数
在对应的ViewModel上定义命令,触发RequestClose事件。

 public DelegateCommand CloseCommand { get; set; }
        public DialogContentViewModel()
        {
            CloseCommand = new DelegateCommand(OnClose);
        }
        private void OnClose()
        {
            IDialogResult dialogResult = new DialogResult();
            dialogResult.Parameters.Add("A", true);
            RequestClose?.Invoke(dialogResult);
        }
来源:https://blog.csdn.net/jjailsa/article/details/135972127

 

弹窗中的区域注册

编写三个页面测试

MainView页面打开弹窗

<Window x:Class="Zhaoxi.PrismRegion.NewRegion.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Zhaoxi.PrismRegion.NewRegion.Views"
        xmlns:p="http://prismlibrary.com/"
        mc:Ignorable="d" FontSize="20"
        Title="MainView" Height="450" Width="800">
    <Grid>
        <ContentControl p:RegionManager.RegionName="MainRegion"/>
        <Button Content="打开弹窗" Command="{Binding ShowDialogCommand}"/>
    </Grid>
</Window>

DialogView页面命名一个区域和加载区域的Command

ViewModel继承IDialogAware接口,初始化的时初始化DelegateCommand

<UserControl x:Class="Zhaoxi.PrismRegion.NewRegion.Views.DialogView"
             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:Zhaoxi.PrismRegion.NewRegion.Views"
             xmlns:p="http://prismlibrary.com/"
             mc:Ignorable="d"  FontSize="20"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Button Content="加载页面" Command="{Binding ShowViewCommand}"
                VerticalAlignment="Top"/>
        <ContentControl p:RegionManager.RegionName="DialogRegion"
                        Margin="0,30,0,0"/>
    </Grid>
</UserControl>
public class DialogViewModel : IDialogAware
{
    public string Title => "";

    public event Action<IDialogResult> RequestClose;

    public bool CanCloseDialog()
    {
        return true;
    }

    public void OnDialogClosed()
    {

    }

    public void OnDialogOpened(IDialogParameters parameters)
    {

    }

    public DelegateCommand ShowViewCommand { get; set; }
     
    public DialogViewModel(IRegionManager regionManager)
    {
        ShowViewCommand = new DelegateCommand(() =>
        {     
            // 打开SubView
            regionManager.RequestNavigate("DialogRegion", "SubView");
        });
    }
}

运行后点击加载页面后显示失败

查看regionManager的属性发现只有MainView的MainRegion区域被注册

因为Prism框架会默认扫描CreateShell方法返回的窗体区域进行注册,但弹窗是后续初始化,并不会注册区域所以显示失败

在DialogView初始哈的时候注入IRegionManager

注册区域:RegionManager.SetRegionManager(this, regionManager);

刷新区域:RegionManager.UpdateRegions();

订阅弹窗的Unloaded事件,弹窗关闭时移除注册区域防止下次初始化时报错:区域已注册

/// <summary>
/// DialogView.xaml 的交互逻辑
/// </summary>
public partial class DialogView : UserControl
{
    public DialogView(IRegionManager regionManager)
    {
        InitializeComponent();

        RegionManager.SetRegionManager(this, regionManager);
        RegionManager.UpdateRegions();

        this.Unloaded += DialogView_Unloaded;
    }

    private void DialogView_Unloaded(object sender, RoutedEventArgs e)
    {
        var rm = RegionManager.GetRegionManager(this);
        rm.Regions.Remove("DialogRegion");
    }
}

运行后,regionManager中注册有两个区域

加载成功

来源:https://www.cnblogs.com/ZHIZRL/p/17881822.html

 

posted @ 2024-09-30 09:48  【君莫笑】  阅读(69)  评论(0编辑  收藏  举报