Closing WPF Dialogs from the ViewModel

Posted by Vinicius de Melo Rocha on July 26th, 2019


Here are two elegant ways that you can use to close a WPF window dialog from the ViewModel.

Using Events

The first solution is by using an event that notifies the window dialog to close. Consider the following example:

public class DialogViewModel
{
  public event EventHandler Saved;

  public DialogViewModel()
  {
    SaveCommand = new DelegateCommand(OnSave);
  }

  public ICommand SaveCommand { get; }

  public void Save()
  {
    Saved?.Invoke(this, EventArgs.Empty);
  }
}

The dialog window can listen to that event and close itself accordingly.

public class SaveDialog : Window
{
  public SaveDialog(DialogViewModel viewModel)
  {
    InitializeComponent();
    DataContext = viewModel;
    viewModel.Saved += (s, e) => Close();
  }
}

Using the DialogResult property

Another way to close a dialog in WPF is by setting the DialogResult property. Everytime we set the DialogResult to true or false, WPF closes the dialog automatilly for us. We can use that in our advantage by binding that property to the Result property in our ViewModel.

public class DialogViewModel : BaseViewModel
{
  private bool? _result;

  public DialogViewModel()
  {
    SaveCommand = new DelegateCommand(OnSave);
  }

  public bool? Result
  {
    get => _result;
    set
    {
      if (_result != value)
      {
        _result = value;
        RaisePropertyChanged();
      }
    }
  }

  public ICommand SaveCommand { get; }

  public void OnSave(object parameter)
  {
    Result = true;
  }
}

In our dialog we COULD just bind the Result property to DialogResult. That would automatically close the dialog when we set the result to true or false. Like that:

<Window
  ...
  DialogResult={Binding Result} />

Unfortunately, we CANNOT. The DialogResult property is not a Dependency Property and we cannot be something to it. To by pass that we can use an Attached Property.

public static class DialogExtension
{
  public static readonly DependencyProperty DialogResultProperty =
      DependencyProperty.RegisterAttached(
      "DialogResult",
      typeof(bool?),
      typeof(DialogExtension),
      new PropertyMetadata(DialogResultChanged));

  private static void DialogResultChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
  {
    var window = d as Window;
    if (window != null && window.IsVisible)
    {
      window.DialogResult = e.NewValue as bool?;
    }
  }

  public static object GetDialogResult(Window target)
  {
    bool? bReturn = null;

    try
    {
      if (target != null)
      {
        bReturn = target.GetValue(DialogResultProperty) as bool?;
      }
    }
    catch { }

    return bReturn;
  }

  public static void SetDialogResult(Window target, bool? value)
  {
    if (target != null)
    {
      target.SetValue(DialogResultProperty, value);
    }
  }
}

And now:

<Window
  ...
  local:DialogExtension.DialogResult={Binding Result} />

Conclusing

There are probably many ways of doing that; those two are the ones that I find more elegant.