WPF – Bring Closing Event to MVVM

The Issue

I’m jumping around a bit, but I don’t get to work on a single project at a time, so I am going to switch gears to talk about some WPF work that I put together. In this project, I want to make sure that I can control when the user closes the window, regardless of whether they click on the “X” in the top-right or if they click the “Exit” button in the UI.

The problem that we have is that the logic that controls whether the user can close the window or not is in the view model. The exit button is controlled by the view model. But the “X” in the top-left corner can only be controlled by the Closing event in the code behind. You can use the event parameters to cancel the close. How can we leverage the logic in the view model to determine if the Window can close or not?

Hack Method

One thing we can do, is inside of the Closing event, we could query the view model to see if it is ok to exit or not. It’s not the greatest, it will be writing a lot of custom code in every window code behind.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Closing += MainWindow_Closing;
    }

    private void MainWindowClosing(object sender, CancelEventArgs a)
    {
        // make sure we have the view model class
        if (DataContext is MainWindowViewModel vm)
        {
            // check with the view model if should be canceled
            e.Cancel = vm.ShouldCancel();
        }
    }
}

Not pretty but it works.

Using Attached Properties

The way I chose to do it is to use DependencyProperty’s that participate in the XAML binding system and can attach to the view model directly and not have to wire up each and every window explicitly. To do this, we need to setup a few new properties. One for enabling or disabling this functionality; a second for a parameter to send back and forth from the view model; and finally, a third that will hold an object that contains an action that gets called when the window is attempted to be closed.

Let’s get started with the two dependency properties that are used for holding the can close implementation and the can close parameter that is passed back and forth. These properties have the following shapes.

public interface ICanCloseResult
{
    bool Cancel { get; set; }
}

public interface ICanCloseCheck
{
    void CheckCanClose(ICanCloseResult canCloseResult);
}

And here are the properties that hold these values.

public class CanCloseBinding
{
        public static readonly DependencyProperty CanCloseCheckProperty =
            DependencyProperty.RegisterAttached(
                "CanCloseCheck",
                typeof(ICanCloseCheck),
                typeof(CanCloseBinding),
                new PropertyMetadata(null));

        public static ICanCloseCheck GetCanCloseCheck(DependencyObject sender) => 
            (ICanCloseCheck) sender.GetValue(CanCloseCheckProperty);
        public static void SetCanCloseCheck(DependencyObject sender, ICanCloseCheck value) => 
            sender.SetValue(CanCloseCheckProperty, value);


        public static readonly DependencyProperty CanCloseCheckParameterProperty =
            DependencyProperty.RegisterAttached(
                "CanCloseCheckParameter",
                typeof(ICanCloseResult),
                typeof(CanCloseBinding),
                new PropertyMetadata(null));

        public static ICanCloseResult GetCanCloseCheckParameter(DependencyObject sender) => 
            (ICanCloseResult) sender.GetValue(CanCloseCheckParameterProperty);
        public static void SetCanCloseCheckParameter(DependencyObject sender, ICanCloseResult value) => 
            sender.SetValue(CanCloseCheckParameterProperty, value);
}

Next we have to build up the dependency property that enables or disables the functionality. The important thing this property does is, when it is set to true, it attaches an event handler to the Closing event of the window. Then, when that event is executed, it tries to get the instances of the other two parameters and execute them. So it looks like the following.

namespace CanClose.Views
{
public class CanCloseBinding
{
    // ... other properties here

    public static readonly DependencyProperty CheckCanCloseEnabledProperty =
        DependencyProperty.RegisterAttached(
            "CheckCanCloseEnabled",
            typeof(bool),
            typeof(CanCloseBinding),
            new PropertyMetadata(false, new PropertyChangedCallback(OnCheckCanClosePropertyChanged)));

    private static void OnCheckCanClosePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is Window w)
        {
            bool newEnabled = (bool) e.NewValue;
            bool oldEnabled = (bool) e.OldValue;

            if (oldEnabled && !newEnabled)
                w.Closing -= MyOwnWindowClosing; // remove the event handler for Closing from the Window
            else if (!oldEnabled && newEnabled)
                w.Closing += MyOwnWindowClosing; // add the event handler for Closing to the window
        }
    }

    public static bool GetCheckCanCloseEnabled(DependencyObject sender) => 
        (bool) sender.GetValue(CheckCanCloseEnabledProperty);
    public static void SetCheckCanCloseEnabled(DependencyObject sender, bool value) => 
        sender.SetValue(CheckCanCloseEnabledProperty, value);

    public static void MyOwnWindowClosing(object sender, CancelEventArgs e)
    {
        /// get the values from the dependency properties
        ICanCloseCheck q = GetCanCloseCheck((Window) sender);
        ICanCloseResult p = GetCanCloseCheckParameter((Window) sender);
        if (q == null || p == null)
            return;

        /// execute and save the value back to the CancelEventArgs object
        q.CheckCanClose(p);
        e.Cancel = p.Cancel;
    }
}
}

Finally, all we need to do is attach these properties to the window that we want to control from our view model. And that is done like this:

<Window x:Class="CanClose.Views.MainWindow"
    xmlns:local="clr-namespace:CanClose.Views"
    local:CanCloseBinding.CheckCanCloseEnabled="True"
    local:CanCloseBinding.CanCloseCheck="{Binding CCQuery}"
    local:CanCloseBinding.CanCloseCheckParameter="{Binding CCResult}"
    >
    <!-- stuff here -->
</Window>

And that is about it. If you want to see a sample, you can find it here.

XF: ListView ICommand Attached Property

Introduction

In this article we are going to look at how we can attach an ICommand object to a listview and have it execute when the user taps a listview item.

So how are we going to do this? We are going to use an attached property. The attached property is a way to move capability from the code behind to your view model where it is accessible to the business logic.

namespace XamlHelpers {
public sealed class ItemTappedCommandListView
{
    /// Define the attached property with a name and a type for the value.
    /// make sure you add in the property changed event handler so that you
    /// can react to changes to the value of the property
    public static readonly BindableProperty ItemTappedCommandProperty =
        BindableProperty.CreateAttached(
            "ItemTappedCommand",
            typeof(ICommand),
            typeof(ItemTappedCommandListView),
            default(ICommand),
            BindingMode.OneWay,
            null,
            PropertyChanged);

    /// This is the event handler that is invoked when the value of the
    /// attached property is changed. All we do is subscribe to the 
    /// ItemTapped property.
    private static void PropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is ListView listView)
        {
            listView.ItemTapped -= ListViewOnItemTapped;
            listView.ItemTapped += ListViewOnItemTapped;
        }
    }

    /// This is the event handler for when an item is tapped. We make sure
    /// that the sender is a list view, its enabled and that the list isn't in
    /// the middle of refreshing.
    /// If all that is ok, retrieve the ICommand object from the property.
    /// if there, we execute the CanExecute method, and if that is True
    /// we can execute the command.
    private static void ListViewOnItemTapped(object sender, ItemTappedEventArgs e)
    {
        if (sender is ListView list && list.IsEnabled && !list.IsRefreshing)
        {
            list.SelectedItem = null;
            var command = GetItemTappedCommand(list);
            if (command != null && command.CanExecute(e.Item))
            {
                command.Execute(e.Item);
            }
        }
    }

    public static ICommand GetItemTappedCommand(BindableObject bindableObject)
    {
        return (ICommand) bindableObject.GetValue(ItemTappedCommandProperty);
    }

    public static void SetItemTappedCommand(BindableObject bindableObject, object value)
    {
        bindableObject.SetValue(ItemTappedCommandProperty, value);
    }
}
}

Now this is how we use it in our XAML.

<ContentPage
    xmlns:u="clr-namespace:XamlHelpers;">

    <ListView ItemsSource="{Binding MenuItems}"
        u:ItemTappedCommandListView.ItemTappedCommand="{Binding CommandNavigate}">
    </ListView>
</ContentPage>

JSON to C#

Do you ever get tired of translating JSON to the equivalent C# shape for your app? I do. One of the tools that I like to use is quicktype. It is a web app that will allow you to paste in some JSON and it will generate the C# classes for you. That’s a pretty good start.

But it gets better than that. If you are like most, and you use NewtonSoft Json.NET (if you don’t, you should check it out), it will decorate the properties it generates with the [JsonProperty] attributes with the property name from JSON while the C# property has the standard casing.

It also has features for handling things like enums, maps, lists or arrays among others.

https://app.quicktype.io/#l=cs&r=json2csharp

Check it out, I love it.