6

I would like to create a popup window or dialog with confirmation of choice (OK, Cancel) with WPF Prism + MahApps.Metro

I have created custom Interaction:

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

But this will create a default WPF window, which is not metro-styled. How to change it to MahApps.Metro window?

And maybe, another way is to use MahApps.Metro Dialogs, but I have no idea how to use it with Prism.

Any ideas?

Tomasz
  • 2,051
  • 3
  • 31
  • 64
  • Well, may be now you have to create a custom `confirmation:ConfirmationDialog` element :] – AgentFire May 03 '15 at 20:33
  • 1
    ConfirmationDialog is a UserControl, and it displays content of dialog. But this dialog is enclosed in a `Window`, which is created by Prism probably automatically – Tomasz May 03 '15 at 20:40

5 Answers5

5

You have to do two things, create new MetroWindow dialogs, then override the PopupWindowAction to use them. It sounds long winded but only takes 10 minutes:

So firstly, create your own Confirmation and Notification windows that inherit from MetroWindow, just like any other MetroWindow. You can copy the original Confirmation and Notification windows from the prism source and just change them as advised by the mahapps quick start. So the Confirmation window would be something like:

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

And the associated code behind:

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

Following on from this, you then create your own PopupWindowAction that extends the one in prism. Within that class you override the GetWindow function:

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

You will also have to provide your own implementation for "CreateDefaultWindow" which will create your new MetroWindow version of the appropriate 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;
}

Finally, within the InteractionRequest in your own view/window, you specify this new PopupWindowAction, rather than the prism version.

Jason
  • 617
  • 9
  • 22
  • Looks promising. Thanks – Tomasz Oct 05 '15 at 22:14
  • 1
    This is very good already, but Krzysztof [suggests a solution](http://stuffleftatparty.com/prism-wpf-mahapps-modal-window-the-mvvm-way-part-2) that doesn’t require creating one’s own popup window. It’s sufficient to define a custom PopupWindowAction. I’ve used it and it works marvelously! – Informagic Jan 13 '19 at 22:50
3

I don't know if yo can do it in prism 5.0 but with the new Prism 6.0 (Github) you have a virtual method CreateWindow, you can subclass the PopupWindowAction and override it creating the Metro Window. I'm using the follwing code:

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

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

        }


    }
}
Rui Sebastião
  • 855
  • 1
  • 18
  • 36
2

I had the same problem and with these comments I could get a solution.
I use Prism 6

1) First of all rewrite the 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) Create your MetroWindow, MetroNotificationWindow and your MetroConfirmationWindow based in the default windows

Example: 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) Change all references of PopupWindowAction for MetroPopupWindowAction
Example:

    <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>
ganchito55
  • 3,559
  • 4
  • 25
  • 46
2

Nowadays you don't need to do any custom stuff, nor use InteractionRequests.

You can check out MahApps.Metro.Demo.Samples (on github) and check all preconfigured dialogs for MVVM ("Dialogs>Show InputDialog via VM", in this case).

It's pretty straightforward, you'll need to: 1) Register DialogCoordinator @ your bootstrapper (example with Autofac:)

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

2) Use an attached property in your Window to register your view model with the dialog sub-system. (This needs to be placed in the View.xaml of the ViewModel you want to call your dialog from:

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

3) @ your ViewModel, make sure you have a readonly reference to the DialogCoordinator Interface, for Dependency Injection:

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

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

4) Now, any time you need to call a dialog (InputDialog, ProgressDialog, MessageDialog, or even CustomDialog of your own) from your ViewModel you'll just need to:

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

I guess that PopupWindowAction creates the window (with the value of WindowContent as its content), so you have to either change its implementation or write your own.

Thomas Freudenberg
  • 5,048
  • 1
  • 35
  • 44