棱镜在 WPF 中弹出新窗口

本文关键字:窗口 新窗口 WPF 棱镜 | 更新日期: 2023-09-27 18:25:58

如何在不违反 MVVM 模式规则的情况下在 WPF 中打开/关闭新窗口?
我只想模仿ms办公室前景的登录模块。

我已经阅读了这篇文章,但是传递参数时出错confirmation

我目前正在使用棱镜 5.0。

棱镜在 WPF 中弹出新窗口

幸运的是,Prism 5.0(我假设 6.0 也还没有使用它(有一个名为 InteractionRequest<T> 的类,您可以从代码中使用它来引发交互请求。

交互请求基本上是一个窗口,它要求用户执行某个操作,并使用用户的决定/操作调用回调(如有必要或需要(。

public class ShellViewModel : BindableBase
{
    private readonly IRegionManager regionManager;
    public ShellViewModel(IRegionManager regionManager)
    {
        if (regionManager == null)
            throw new ArgumentNullException("regionManager");
        this.regionManager = regionManager;
        this.OptionSettingConfirmationRequest = new InteractionRequest<IConfirmation>();
        openConnectionOptionsCommand = new DelegateCommand(RaiseConnectionOptionsRequest);
    }
    public InteractionRequest<IConfirmation> OptionSettingConfirmationRequest { get; private set; }
    private readonly ICommand openConnectionOptionsCommand;
    public ICommand OpenConnectionOptionsCommand { get { return openConnectionOptionsCommand; } }
    private void RaiseConnectionOptionsRequest()
    {
        this.OptionSettingConfirmationRequest.Raise(new Confirmation { Title = "Options not saved. Do you wish to save?" }, OnConnectionOptionsResponse);
    }
    protected virtual void OnConnectionOptionsResponse(IConfirmation context)
    {
        if(context.Confirmed)
        {
            // save it
        }
        // otherwise do nothing
    }
}

在 XAML 中,你会做类似的事情

<Button Content="Options" Command="{Binding OpenConnectionOptionsCommand}">
    <i:Interaction.Triggers>
        <pit:InteractionRequestTrigger SourceObject="{Binding OptionSettingConfirmationRequest, Mode=OneWay}" >
            <pie:LazyPopupWindowAction RegionName="ConnectionSettings" 
                                NavigationUri="ConnectionSettingsView" IsModal="True" />
        </pit:InteractionRequestTrigger>
    </i:Interaction.Triggers>
</Button>

使用了我自己的PopupWindowAction实现(参见 github 项目页面了解它的实现(,称为 LazyPopupWindowAction ,它将在单击时实例化嵌入式ConnectionSettingsView视图。如果您不在乎视图仅实例化一次,请随意使用 PopupWindowAction ,然后它将与包含该操作的视图同时实例化。

它基本上是复制和粘贴,从我的一个项目中剪切一些无用的线条。我使用了IConfirmationINotification接口,而不是具体的实现。

XAML 与PopupWindowAction

<Button Content="Options" Command="{Binding OpenConnectionOptionsCommand}">
    <i:Interaction.Triggers>
        <pit:InteractionRequestTrigger SourceObject="{Binding OptionSettingConfirmationRequest, Mode=OneWay}" >
            <pi:PopupWindowAction>
                <pi:PopupWindowAction.WindowContent>
                    <views:CustomPopupView />
                </pi:PopupWindowAction.WindowContent>
            </pi:PopupWindowAction>
        </pit:InteractionRequestTrigger>
    </i:Interaction.Triggers>
</Button>

命名空间声明

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:pi="clr-namespace:Microsoft.Practices.Prism.Interactivity;assembly=Microsoft.Practices.Prism.Interactivity"
xmlns:pit="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"
xmlns:pie="clr-namespace:MyProject.UI.Prism.Interactivity;assembly=MyProject.UI"

更新:由于人们一直在询问LazyPopupWindowAction,我已将源代码放在GitHub Gist中。基本上它基于 Prims 5 的PopupWindowAction(对于 Prism,还没有用 Prism 6 对其进行测试,在没有调整的情况下可能无法工作(并做完全相同的事情,但也通过打开的窗口添加了区域和导航支持,这是我在我的应用程序中需要的东西。

我不喜欢默认实现的一件事是,视图及其视图模型将在 Shell 实例化的同时实例化,并且当您关闭它时,视图模型仍处于其状态(它实际上只是隐藏的(。

此答案仅适用于 Prism 7,
如果您使用以前版本的 Prism (6 及更低版本(,
那么此答案不适合您

棱镜7彻底改变了打开新窗口的方式。
如果您想阅读,这里是官方文档。

这里还有一个Youtube视频,解释了Prism库的创建者的想法。

<小时 />

棱镜7引入了DialogService,一种打开新窗口的全新方式。

  1. 创建新的 UserControl.xaml,它将表示窗口的内容。
    最简单的空内容可能是
<UserControl x:Class="YourUserControlName"
             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:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
    </Grid>
</UserControl>
  1. 为此视图创建相应的视图。
    此视图模型必须实现IDialogAware接口。
    这里有一个例子
public class BaseDialogViewModel : IDialogAware
{
    public string Title { get; }
    public event Action<IDialogResult> RequestClose;
    public virtual void RaiseRequestClose(IDialogResult dialogResult)
    {
        RequestClose?.Invoke(dialogResult);
    }
    public virtual bool CanCloseDialog()
    {
        return true;
    }
    public virtual void OnDialogClosed()
    {
    }
    public virtual void OnDialogOpened(IDialogParameters parameters)
    {
    }
}
  1. 您必须像这样注册对话框
public void RegisterTypes(IContainerRegistry containerRegistry)
{
    // replace 'YourUserControlName' with the class of the view which you created in setp 1
    containerRegistry.RegisterDialog<YourUserControlName>();
}
  1. 最后一步是随时
    显示对话框通常,您希望在用户单击按钮或执行操作时显示对话框,因此通常在执行某些命令时执行以下代码,

    但同样取决于您。
_dialogService.ShowDialog(nameof(YourUserControlName), new DialogParameters(), action);
  • _dialogService的类型为 IDialogService,注入到视图模型中,您将在其中使用它,示例
public MainViewModel(IDialogService dialogService) 
{
   this._dialogService = dialogService;
}

所有 前面的步骤是显示窗口所必需的

<小时 />

如果需要,还有其他一些可选步骤(非必需(

  1. 您可以通过添加以下内容来指定窗口的属性 prism:Dialog.WindowStyle XAML 元素
<UserControl x:Class="YourUserControlName"
             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:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" />
            <Setter Property="ResizeMode" Value="NoResize"/>
            <Setter Property="ShowInTaskbar" Value="False"/>
            <Setter Property="WindowState" Value="Maximized"/>
        </Style>
    </prism:Dialog.WindowStyle>
    <Grid>
    </Grid>
</UserControl>
  1. 您可以为显示窗口
    的功能创建扩展方法
public static class DialogServiceExtensions
{
    public static void ShowWindowTest(this IDialogService dialogService, Action<IDialogResult> action)
    {
        dialogService.ShowDialog(nameof(WindowTestView), new DialogParameters(), action);
    }
}

棱镜文档建议这样做,但不是必需的。

<小时 />

如果你想要一个带有.NET Core 3.1的新Prism 7 WPF应用程序的样板设置,那么你可以查看这个Github存储库
。它包含上述设置和许多其他用于启动 WPF 棱镜应用程序的有用功能。

免责声明:我是存储库的作者

你使用棱镜 7 吗?
如果是,那么现在停止阅读并转到下面的
Prism 7 答案如果否,请继续阅读

<小时 />

更新
导致我提出另一个答案的是无法在使用 Prism 6 的项目中应用公认的答案,但是在放置原始答案(见下文(并在评论中讨论后,我发现核心问题是: Prism 6 更改了一些类的命名空间,所有在接受答案中使用的类仍然存在于 Prism6 中,
但在另一个 DLL 和命名空间
因此,如果您使用的是 Prism 6,您可以通过这些修改应用接受的答案

首先替换这些同名

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:pi="clr-namespace:Microsoft.Practices.Prism.Interactivity;assembly=Microsoft.Practices.Prism.Interactivity"
xmlns:pit="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"

使用以下命名空间

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:prism="http://prismlibrary.com/"

第二次更新 XAML,如下所示

<Button Content="Options" Command="{Binding OpenConnectionOptionsCommand}">
    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding OptionSettingConfirmationRequest, Mode=OneWay}" >
            <prism:PopupWindowAction>
                <prism:PopupWindowAction.WindowContent>
                    <views:CustomPopupView />
                </prism:PopupWindowAction.WindowContent>
            </prism:PopupWindowAction>
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
</Button>

注1
确保您使用的视图(在上面的示例中<views:CustomPopupWindow>(不是窗口,否则您将收到异常。

注2
只有在使用 Prism 6 的情况下才需要进行这些修改。因为(正如我在下面的原始答案中所说(接受答案使用的 dll 在 Prism 6 中被弃用

注3
请确保引用Prism.Wpf dll,可以从 Nuget 下载该 dll。


原始答案
接受的答案是针对棱镜 5,它使用这个在棱镜 6不推荐使用的库。

实际上,您在问题中引用的文章非常有帮助(至少对我来说(,并且不会崩溃。

我将尝试总结这篇文章。

视图模型

public class ViewModel : BindableBase
{
    public ViewModel()
    {
        _showWindowCommand = new DelegateCommand(ShowWindow);
        _interactionRequest = new InteractionRequest<Confirmation>();
    }
    private readonly DelegateCommand _showWindowCommand;
    private InteractionRequest<Confirmation> _interactionRequest;
    public ICommand ShowWindowCommand
    {
        get { return _showWindowCommand; }
    }
    public IInteractionRequest InteractionRequest
    {
        get { return _interactionRequest; }
    }
    private void ShowWindow()
    {
        _interactionRequest.Raise(
            new Confirmation(),
            OnWindowClosed);
    }
    private void OnWindowClosed(Confirmation confirmation)
    {
        if (confirmation.Confirmed)
        {
            //perform the confirmed action...
        }
        else
        {
        }
    }
}

XAML

<Button Command="{Binding ShowWindowCommand}" Content="Show Window" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Raised" SourceObject="{Binding InteractionRequest}">
            <i:EventTrigger.Actions>
                <local:ShowWindowAction></local:ShowWindowAction>
            </i:EventTrigger.Actions>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

您将需要使用这些命名空间

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:The namespace which contains the ShowWindowAction">

动作触发器

using System;
using Prism.Interactivity.InteractionRequest;
using System.Windows.Interactivity;
using System.Windows;
public class ShowWindowAction : TriggerAction<FrameworkElement>
{
    protected override void Invoke(object parameter)
    {
        InteractionRequestedEventArgs args = parameter as InteractionRequestedEventArgs;
        if (args != null)
        {
            Confirmation confirmation = args.Context as Confirmation;
            if (confirmation != null)
            {
                // Replace ParametersWindow with your own window.
                ParametersWindow window = new ParametersWindow();
                EventHandler closeHandler = null;
                closeHandler = (sender, e) =>
                {
                    window.Closed -= closeHandler;
                    args.Callback();
                };
                window.Closed += closeHandler;
                window.Show();
            }
        }
    }
}

解释

  1. 您需要 Prism.CorePrism.Wpf 个 dll(至少(才能使此代码正常工作。
  2. ShowWindow方法,将触发ShowWindowActionInvoke方法,这将真正显示窗口。
  3. 您可以在 OnWindowClosed 中处理窗口的关闭,我们将其作为回调传递给 ShowWindowAction 类,当窗口真正关闭时,我们从那里调用它。