Skip to content

WPF: Dialog Manager

Sukhrob Ilyosbekov edited this page Oct 30, 2021 · 12 revisions

Dialog view

Dialog view is a simple UserControl that can be designed anyway you please. The only requirement it has a ViewModel that implements IDialogAware set as it's DataContext.

The sample view DialogA.xaml as shown below.

<UserControl x:Class="AppDialogs.Views.Dialogs.DialogA"
             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"
             mc:Ignorable="d"
             Height="200"
             Width="400">
    <Grid>
        <TextBlock HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   TextWrapping="Wrap"
                   Text="Modal dialog A content">
        </TextBlock>
    </Grid>
</UserControl>

Dialog ViewModel

Next you need a ViewModel that implements IDialogAware which is defined as follows

public interface IDialogAware
{
   /// <summary>
   /// The title of the dialog that will show in the hostWindow title bar.
   /// </summary>
   string Title { get; }

   /// <summary>
   /// Instructs the IDialogHostWindow to close the dialog.
   /// </summary>
   event Action<IDialogResult> RequestClose;

   /// <summary>
   /// Determines if the dialog can be closed.
   /// </summary>
   /// <returns>If <c>true</c> the dialog can be closed. If <c>false</c> the dialog will not close.</returns>
   bool CanCloseDialog();

   /// <summary>
   /// Called when the dialog is closed.
   /// </summary>
   void OnDialogClosed();

   /// <summary>
   /// Called when the dialog is opened.
   /// </summary>
   /// <param name="parameters">The parameters passed to the dialog.</param>
   void OnDialogOpened(IDialogParameters parameters);
}

The sample ViewModel for the view DialogA.xaml

public class DialogAViewModel : BindableBase, IDialogAware
{
    public DialogAViewModel()
    {
        Title = "Dialog A";
    }
    
    public string Title { get; protected set;}

    public event Action<IDialogResult> RequestClose;
    
    protected virtual void CloseDialog(IDialogResult dialogResult = null)
    {
        RequestClose?.Invoke(dialogResult ?? new DialogResult());
    }

    public virtual bool CanCloseDialog() => true;

    public virtual void OnDialogClosed() { }

    public virtual void OnDialogOpened(IDialogParameters parameters) { }
}

Wire ViewModel to Dialog View

Bind object of the ViewModel to the property DataContext of the view. It is important to keep parameterless constructor because view objects will be instantiated internally using Activator.CreateInstance(viewType).

The sample code demonstrates how to bind ViewModel to dialog view.

// DialogA.xaml.cs file
public partial class DialogA : UserControl
{
    // It is important to keep parameterless constructor of view object, if you use IoC container.
    // This restriction applicable only for view objects.
    public DialogA()
    {
        // Resolve ViewModel object from static instance of the container.
        DataContext = App.Container.Resolve<DialogAViewModel>();
        InitializeComponent();
    }
}

Register dialogs in IDialogManager

Register instance of IDialogManager as a singleton in the container then register all dialog views in the IDialogManager.

The sample code shows how to register IDialogManager and dialog views in application entry point class (App.xaml.cs file) and used DryIoc container as a IoC container.

public partial class App : Application
{
    // Static singleton instance of DryIoc container.
    public static readonly IContainer Container = new Container();

    /// <summary>
    /// Application entry point.
    /// </summary>
    /// <param name="e">Args</param>
    protected override void OnStartup(StartupEventArgs e)
    {
        // Register IDialogManager service as a singleton.
        // Note: It's important to register as a singelton otherwise you will get different instance of managers.
        Container.Register<IDialogManager, DialogManager>(Reuse.Singleton);
        
        // Resolve singleton instance of the dialog manager from container.
        Container.Resolve<IDialogManager>()
                .RegisterDialog<DialogA>() // register all dialog views in the dialog manager.
                .RegisterDialog<DialogB>()
                .RegisterDialog<DialogC>();
    
        // Resolve MainWindow from container then display main window.
        Container.Resolve<MainWindow>().Show();
        base.OnStartup(e);
    }
}

Note

You must provide dialog name when you register dialogs in IDialogManager.Register<>. In above example, explicitly not specified dialog name because by default name of view type will be used implicitly as a dialog name. In case of the above example, view types DialogA, DialogB and DialogC used as a default dialog name such that "DialogA", "DialogB" and "DialogC".

Using Dialog Manager

Inject IDialogManager to your view model constructor.

public MainWindowViewModel(IDialogManager dialogManager)
{
    _dialogManager = dialogManager;
}

Then call either Show or ShowDialog providing the name of the dialog, any parameters your dialogs requires, and then handle the result via a call back.

private void ShowModalDialog()
{
    var dialogParameters = new DialogParameters
    {
        {"paramKey1", "paramValue1"},
        {"paramKey2", "paramValue2"},
        {"paramKey3", "paramValue3"},
    }

   _dialogManager.ShowDialog("DialogA", dialogParameters);
}

Style Dialog host window

You can control the properties of the DialogHostWindow by using a style via an attatched property on the Dialog UserControl.

The sample code demonstrates how to make transparent background of the dialog host window.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="dialogHostStyle" TargetType="{x:Type Window}">
        <Setter Property="WindowStyle" Value="None"></Setter>
        <Setter Property="AllowsTransparency" Value="True"></Setter>
        <Setter Property="Background">
            <Setter.Value>
                <SolidColorBrush Opacity="0.5" Color="Black"/>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Use your styles in dialog view.

<UserControl x:Class="AppDialogs.Views.Dialogs.DialogA"
             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:attached="clr-namespace:MagicMvvm.Dialogs;assembly=MagicMvvm.Wpf"
             attached:DialogHost.WindowStyle="{StaticResource dialogHostStyle}"
             attached:DialogHost.MatchParentSize="True"
             mc:Ignorable="d"
             Height="200"
             Width="400">
    <Grid>
        <TextBlock HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   TextWrapping="Wrap"
                   Text="Modal dialog A content">
        </TextBlock>
    </Grid>
</UserControl>
Clone this wiki locally