棱镜交互请求 + MahApps.Metro

Prism InteractionRequest + MahApps.Metro

我想使用 WPF Prism + MahApps.Metro

创建弹出窗口 window 或确认选择(确定,取消)的对话框

我创建了自定义交互:

<i:Interaction.Triggers>
    <interactionRequest:InteractionRequestTrigger SourceObject="{Binding DeleteConfirmationRequest, Mode=OneWay}">
        <interactionRequest:PopupWindowAction>
            <interactionRequest:PopupWindowAction.WindowContent>
                <confirmation:ConfirmationDialog/>
            </interactionRequest:PopupWindowAction.WindowContent>
        </interactionRequest:PopupWindowAction>
    </interactionRequest:InteractionRequestTrigger>
</i:Interaction.Triggers>

但这将创建一个默认的 WPF window,它不是 metro 风格的。怎么改成MahApps.Metro window?

也许,另一种方法是使用 MahApps.Metro 对话框,但我不知道如何将它与 Prism 一起使用。

有什么想法吗?

我猜想 PopupWindowAction 创建了 window(其内容为 WindowContent 的值),因此您必须更改其实现或编写自己的实现。

我不知道你是否可以在 prism 5.0 中做到这一点,但是使用新的 Prism 6.0 (Github) 你有一个虚拟方法 CreateWindow,你可以子类化 PopupWindow 操作并覆盖它创建 Metro Window。我正在使用以下代码:

namespace KPP.Vision.Infrastructure.Interactions
{
    public class MetroPopupWindowAction:PopupWindowAction
    {

        protected override Window CreateWindow()
        {
            return new MetroPopupWindowView();

        }


    }
}

您必须做两件事,创建新的 MetroWindow 对话框,然后覆盖 PopupWindowAction 以使用它们。听起来很啰嗦,但只需要 10 分钟:

因此,首先,创建您自己的继承自 MetroWindow 的确认和通知 windows,就像任何其他 MetroWindow 一样。您可以从 prism 源复制原始确认和通知 windows,然后按照 mahapps quick start 的建议进行更改。 所以确认 window 会是这样的:

<Controls:MetroWindow x:Class="MyApp.DefaultPopupWindows.DefaultConfirmationWindow"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
           MinWidth="300" MinHeight="150"
           Title="{Binding Title}" 
           BorderBrush="{DynamicResource AccentColorBrush}"                      
           BorderThickness="1">

    <Grid x:Name="LayoutRoot" Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <ContentControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Content="{Binding Content}"/>

        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button x:Name="OkButton" Content="OK" Width="75" Height="25" HorizontalAlignment="Right" Margin="0,10,0,0" Click="OkButton_Click" />
            <Button x:Name="CancelButton" Content="Cancel" Width="75" Height="25" HorizontalAlignment="Right" Margin="20,10,0,0" Click="CancelButton_Click" />
        </StackPanel>

    </Grid>
</Controls:MetroWindow>

以及后面的关联代码:

using MahApps.Metro.Controls;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
using System.Windows;

namespace MyApp.DefaultPopupWindows
{
    /// <summary>
    /// Interaction logic for ConfirmationChildWindow.xaml
    /// </summary>
    public partial class DefaultConfirmationWindow : MetroWindow
    {
        /// <summary>
        /// Creates a new instance of ConfirmationChildWindow.
        /// </summary>
        public DefaultConfirmationWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Sets or gets the <see cref="IConfirmation"/> shown by this window./>
        /// </summary>
        public IConfirmation Confirmation
        {
            get
            {
                return this.DataContext as IConfirmation;
            }
            set
            {
                this.DataContext = value;
            }
        }

        private void OkButton_Click(object sender, RoutedEventArgs e)
        {
            this.Confirmation.Confirmed = true;
            this.Close();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.Confirmation.Confirmed = false;
            this.Close();
        }
    }
}

在此基础上,您可以创建自己的 PopupWindowAction 来扩展 prism 中的那个。在那个 class 中,您覆盖了 GetWindow 函数:

protected override Window GetWindow(INotification notification)
{
    MetroWindow wrapperWindow;

    if (this.WindowContent != null)
    {
        wrapperWindow = new MetroWindow();

        // If the WindowContent does not have its own DataContext, it will inherit this one.
        wrapperWindow.DataContext = notification;
        wrapperWindow.Title = notification.Title;

        this.PrepareContentForWindow(notification, wrapperWindow);
    }
    else
    {
        wrapperWindow = this.CreateDefaultWindow(notification);
    }

    return wrapperWindow;
}

您还必须为 "CreateDefaultWindow" 提供您自己的实现,这将创建您新的 MetroWindow 版本的适当的 window:

protected new MetroWindow CreateDefaultWindow(INotification notification)
{
    MetroWindow window = null;

    if (notification is IConfirmation)
    {
        window = new DefaultPopupWindows.DefaultConfirmationWindow() { Confirmation = (IConfirmation)notification };
    }
    else
    {
        window = new DefaultPopupWindows.DefaultNotificationWindow() { Notification = notification };
    }

    return window;
}

最后,在您自己的 view/window 的 InteractionRequest 中,您指定这个新的 PopupWindowAction,而不是棱镜版本.

我遇到了同样的问题,通过这些评论我可以获得解决方案。
我用棱镜6

1) 首先重写PopupWindowAction

/// <summary>
        ///     it creates a new metro window instead of a window
        /// </summary>
        /// <returns></returns>
        protected override Window CreateWindow()
        {
            return new MetroPopupWindow();
        }

        /// <summary>
        /// Creates a window with the notification type
        /// </summary>
        /// <param name="notification"></param>
        /// <returns></returns>
        private new Window CreateDefaultWindow(INotification notification)
        {
            Window window = null;

            if (notification is IConfirmation)
            {
                window = new MetroConfirmationWindow {Confirmation = (IConfirmation) notification};
            }
            else
            {
                window = new MetroNotificationWindow {Notification = notification};
            }

            return window;
        }

        /// <summary>
        ///     Returns the window to display as part of the trigger action.
        /// </summary>
        /// <param name="notification">The notification to be set as a DataContext in the window.</param>
        /// <returns></returns>
        protected override Window GetWindow(INotification notification)
        {
            Window wrapperWindow;

            if (WindowContent != null)
            {
                wrapperWindow = CreateWindow();

                if (wrapperWindow == null)
                    throw new NullReferenceException("CreateWindow cannot return null");

                // If the WindowContent does not have its own DataContext, it will inherit this one.
                wrapperWindow.DataContext = notification;
                wrapperWindow.Title = notification.Title;

                PrepareContentForWindow(notification, wrapperWindow);
            }
            else
            {
                wrapperWindow = CreateDefaultWindow(notification);
            }

            // If the user provided a Style for a Window we set it as the window's style.
            if (WindowStyle != null)
                wrapperWindow.Style = WindowStyle;

            return wrapperWindow;
        }

2) 创建您的 MetroWindow、MetroNotificationWindow 和 MetroConfirmationWindow based in the default windows

示例: MetroPopupWindow.xaml

<controls:MetroWindow x:Class="MetroPopupWindow"
                      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:controls="http://metro.mahapps.com/winfx/xaml/controls"
                      mc:Ignorable="d"
                      Title="MetroPopupWindow" TitleCaps="False" SizeToContent="WidthAndHeight">
    <Grid>
        <ContentControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0"
                        Content="{Binding Content}" />
    </Grid>
</controls:MetroWindow>

MetroNotificationWindow.xaml

<controls:MetroWindow x:Class="MetroNotificationWindow"
                      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:controls="http://metro.mahapps.com/winfx/xaml/controls"                          
                      Title="Web Studio" TitleCaps="False" SizeToContent="WidthAndHeight">
    <Grid x:Name="LayoutRoot" Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <ContentControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0"
                        Content="{Binding Content}" />
        <Button x:Name="OKButton" Content="{lex:Loc Ok}" Width="75" Height="25" HorizontalAlignment="Right"
                Margin="0,10,0,0" Grid.Row="1" Click="OKButton_Click" IsDefault="True" />

    </Grid>
</controls:MetroWindow>

3) 将 PopupWindowAction 的所有引用更改为 MetroPopupWindowAction
示例:

    <i:Interaction.Triggers>
    <!-- Trigger listening for the "Raised" event on the source object (of type IInteractionRequest) -->
    <interactionRequest:InteractionRequestTrigger
        SourceObject="{Binding SaveChangesConfirmationRequest, Mode=OneWay}">
        <!-- That window will be show as a modal dialog and centered over this window -->
        <windowAction:MetroPopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
    </interactionRequest:InteractionRequestTrigger>
</i:Interaction.Triggers>

现在您不需要做任何自定义的事情,也不需要使用 InteractionRequests。

您可以检查 MahApps.Metro.Demo.Samples(在 github 上)并检查 MVVM 的所有预配置对话框(在本例中为 "Dialogs>Show InputDialog via VM")。

这很简单,您需要: 1) 注册 DialogCoordinator @你的引导程序(Autofac 的例子:)

builder.RegisterType<DialogCoordinator>().As<IDialogCoordinator>().SingleInstance();

2) 在您的 Window 中使用附加的 属性 将您的视图模型注册到对话子系统。 (这需要放在要从中调用对话框的 ViewModel 的 View.xaml 中:

<UserControl x:Class="MyNamespace.Views.MyView"
             xmlns:dialogs="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
             dialogs:DialogParticipation.Register="{Binding}"
             .../>

3) @您的 ViewModel,确保您具有对 DialogCoordinator 接口的只读引用,用于依赖注入:

namespace MyNamespace
{
    class MyViewModel : BindableBase
    {
        readonly IDialogCoordinator _dialogCoordinator;

        public MyViewModel (IDialogCoordinator dcFromDependencyInjection)
        {
            _dialogCoordinator = dcFromDependencyInjection;
            [rest of your constructor code here]
        }
    }
}

4) 现在,任何时候您需要从您的 ViewModel 调用对话框(InputDialog、ProgressDialog、MessageDialog,甚至是您自己的 CustomDialog)时,您只需要:

async void MyInputDialog()
{
    await _dialogCoordinator.ShowInputAsync(this, "Dialog Title", "Dialog Message")
                            .ContinueWith(t => Console.WriteLine(t.Result));
}