wpf WPF中的棱镜弹出新窗口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34125982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:34:28  来源:igfitidea点击:

Prism pop-up new window in WPF

c#wpfmvvmprism

提问by Neil

How can I open/close a new window in WPF without violating rules of the MVVM pattern?
I just want to mimic the login module of ms office outlook.

如何在不违反 MVVM 模式规则的情况下在 WPF 中打开/关闭新窗口?
我只是想模仿一下ms office Outlook的登录模块。

I've already read this article, but there are an error in passing a parameter confirmation

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

I'm currently using prism 5.0.

我目前正在使用棱镜 5.0。

回答by Tseng

Luckily, Prism 5.0 (and I assume 6.0 too, haven't worked with it yet), has a class called InteractionRequest<T>which you can use from code to raise interaction requests.

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

An interaction request is basically a window, that asks the user for a certain action and calls a callback (if necessary or desired) with the users decisions/actions.

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

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
    }
}

In XAML you would do something like

在 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>

I used my own implemetation of PopupWindowAction(see github project page for it's implementation) called LazyPopupWindowAction, which will instantiate the embedded ConnectionSettingsViewView on click. If you don't care that your view is instantiated only once, feel free to use PopupWindowAction, then it will be instantiated at the same time as the View containing the action.

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

It's basically copy & paste with cutting some useless lines from one of my projects. I used IConfirmationand INotificationinterfaces instead of the concrete implementations.

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

XAML with PopupWindowAction

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>

Namespace declarations

命名空间声明

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"

Update:Since people keep asking about the LazyPopupWindowAction, I've put the source in a GitHub Gist. Basically it's based on the PopupWindowActionfrom Prims 5 (and for Prism, haven't test it with Prism 6 yet, probably won't work w/o adjustments) and does the exact same thing, but also adds Region and Navigation support with the opened window, something that I needed in my application.

更新:由于人们不断询问LazyPopupWindowAction,我已将源代码放在GitHub Gist 中。基本上它基于PopupWindowActionPrims 5(对于 Prism,尚未使用 Prism 6 对其进行测试,可能无法在不进行调整的情况下工作)并执行完全相同的操作,但还添加了对打开的区域和导航的支持窗口,我在应用程序中需要的东西。

One thing I disliked about the default implementation was, that the view and it's viewmodel will be instantiated at the same time the Shell gets instantiated and the ViewModel remains in it's state, when you close it (it was actually just hidden).

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

回答by Hakan F?st?k

Do you use Prism 7?
if Yes, then stop reading now and go to this Prism 7 answer below
if No, then continue reading

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



Update
What lead me to put another answer was the inability to apply the accepted answer on my project which using the Prism 6,
but after putting the original answer (see it below) and discussing it in comments, I discovered that the core problem was: The Prism 6changed the namespaces of some classes, all the classes which used in the accepted answer is still exists in Prism 6, but in another dlls and namespaces
So if you are using Prism 6, you can apply the accepted answer with those modifications

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

first replace those namesapces

首先替换那些命名空间

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"

with the following namespaces

具有以下命名空间

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

second update the XAMLas the following

第二次更新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>

NOTE 1
Make sure that the view you are using (in the example above <views:CustomPopupWindow>) is NOT a window, or you will receive an exception.

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

NOTE 2
These modifications are required ONLYin case you are using Prism 6. because (As I said in the Original Answer below) the dlls which used by the accepted answer is deprecatedin Prism 6.

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

NOTE 3
Make sure you are referencing the Prism.Wpfdll, which could be downloaded from Nuget.

注意 3
确保您正在引用Prism.Wpfdll,它可以从 Nuget 下载



Original Answer原来的答案


accepted answer接受的答案是针对Prism 5棱镜 5,它使用this library这个图书馆deprecated过时Prism 6棱镜 6

Actually the article which you referencein your question was very helpful (at least for me), and it does not crash.

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

I will try to summary that article.

我将尝试总结那篇文章。

ViewModel

视图模型

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

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>

and you will need to use those namespaces

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

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

ActionTrigger

动作触发器

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();
            }
        }
    }
}

Explanation

解释

  1. You need Prism.Coreand Prism.Wpfdlls (at least) to make this code work.
  2. ShowWindowmethod, will trigger the Invokemethod of the ShowWindowAction, which will really show the window.
  3. you can handle the closing of the window in the OnWindowClosed, which we passed it as a callback to the ShowWindowActionclass, and we called it from there when the the window really closed.
  1. 您需要Prism.CorePrism.Wpfdll(至少)才能使此代码工作。
  2. ShowWindow方法,将触发 的Invoke方法,该方法ShowWindowAction将真正显示窗口。
  3. 您可以在 中处理窗口的关闭OnWindowClosed,我们将其作为回调传递给ShowWindowAction类,并在窗口真正关闭时从那里调用它。

回答by Hakan F?st?k

This answer is for Prism 7 only,
if you use a previous version of Prism (6 and below)
then this answer is NOT for you

此答案仅适用于 Prism 7,
如果您使用先前版本的 Prism(6 及以下),
则此答案不适合您

Prism 7 changed the way of opening new windows drastically.
Here is the offical documentationif you want to read it.

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

Here is also a Youtube videoexplaining this idea by the creator of the Prism library.

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



Prism 7 introduced DialogService, a completely new way to open new window.

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

  1. Create new UserControl.xaml which will represent the content of the window.
    the simplest, empty content could be
  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. Create the corresponding view-odel for this view.
    This view-model MUSTimplement IDialogAwareinterface.
    Here an example
  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. You have to register the Dialog like this
  1. 你必须像这样注册对话框
public void RegisterTypes(IContainerRegistry containerRegistry)
{
    // replace 'YourUserControlName' with the class of the view which you created in setp 1
    containerRegistry.RegisterDialog<YourUserControlName>();
}
  1. The last step is to show the dialog whenever you want
    Usually, you want to show the dialog when the user clicks on the button or does an action,
    so the following code usually will be executed when some command executed,
    but again it is up to you.
  1. 最后一步是随时显示对话框
    通常,您希望在用户单击按钮或执行操作时显示对话框,
    因此通常会在执行某些命令时执行以下代码,
    但再次取决于你。
_dialogService.ShowDialog(nameof(YourUserControlName), new DialogParameters(), action);
  • the _dialogServiceis of type IDialogServiceand is injected in the view-model, where you will use it, example
  • _dialogService类型是IDialogService在视图模型,在那里你会使用它,例如注入
public MainViewModel(IDialogService dialogService) 
{
   this._dialogService = dialogService;
}

Al the Previous steps are required to show the window.

显示窗口需要前面的步骤



There are some other optional steps If you want them (Not required)

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

  1. You can specify the properties of the Window by adding the following prism:Dialog.WindowStylexaml element
  1. 您可以通过添加以下prism:Dialog.WindowStylexaml 元素来指定 Window 的属性
<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. You can create an extension method for the functionality of showing the window
  1. 您可以为显示窗口的功能创建一个扩展方法
public static class DialogServiceExtensions
{
    public static void ShowWindowTest(this IDialogService dialogService, Action<IDialogResult> action)
    {
        dialogService.ShowDialog(nameof(WindowTestView), new DialogParameters(), action);
    }
}

Prism documentation recommendsthat but does NOT requireit.

Prism 文档建议但不要求它。



if you want a boilerplate setup for new Prism 7 WPF application with .NET Core 3.1, then you can check-out this Github repository
It contains the above-metioned setup and a lot of other useful features for starting a WPF Prism Application.

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

Disclaimer: I am the author of the repository

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