Post

Handling WPF Window Dialog events, the elegant way

Here are two elegant ways that you can use to handle WPF Dialogs from the ViewModel. I will demonstrate both examples by closing a WPF Window Dialog from the ViewModel depending on some logic.

Using Events

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DialogViewModel
{
    public event EventHandler Saved;

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

    public ICommand SaveCommand { get; }

    public void Save()
    {
        //
        // SOME LOGIC TO SAVE GOES HERE
        //
        Saved?.Invoke(this, EventArgs.Empty);
    }
}

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

1
2
3
4
5
6
7
8
9
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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)
    {
        //
        // SOME SAVE LOGIC GOES HERE
        //

        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:

1
2
3
<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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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:

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

Conclusion

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

This post is licensed under CC BY 4.0 by the author.