DM-V-VM part 5: Commands

In the previous posts, I've covered data models and how to make data easily consumable by Avalon. Now, I want to start getting into the view model side of things because I don't think it's clear what I mean by them and I'm working up to an example.

I want to start by talking about handling behavior in the UI. Let's take a simple example. Say you have some UI that has a TextBox and a Button that does some operation on that text. The standard way you might do something like this with Xaml and code behind is something like:

<Window x:Class="CommandDemo.Window1"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    Title="CommandDemo" Height="300" Width="300"

    >

    <StackPanel>

      <TextBox Name="_textBox"/>

      <Button Name="_button" Click="OnButtonClick">Do something</Button>

    </StackPanel>

</Window>

and the code behind:

namespace CommandDemo

{

    public partial class Window1 : Window

    {

 

        public Window1()

        {

            InitializeComponent();

        }

 

        private void OnButtonClick(object sender, RoutedEventArgs e)

        {

            string text = _textBox.Text;

 

            // Do something with text

        }

 

    }

}

The Xaml specifies the function that should be called on click and the code behind goes directly to the text box to get the text. So, there's a fair amount of coupling between the Xaml and the code behind. Now let's say you want the button to be disabled when the TextBox is empty. You might have code behind that watches for the text changing and enables/disables the button. That leads to more coupling. Or, you could write a ValueConverter and bind the enabled state to the text box text passed through the value converter.

But, I want to get the behavior side of things out of the code behind to clearly separate the behavior aspects from the styling. This will make the behavior more unit testable and makes it easier for designers to own the Xaml and developers to own the behavior.

Let's start with a quick example of how to do this with RoutedCommands. A button can be bound to an ICommand. ICommand lets the button know when to be enabled and what to do when it's clicked. RoutedCommand is an ICommand implementation that fires a routed event looking for a CommandBinding definied on an element that handles that command. One other thing that commands support is a command parameter. Here's an example of how we can use a RoutedCommand to handle the scenario above.

First, the code behind:

    public partial class Window1 : Window

    {

        public Window1()

        {

            InitializeComponent();

 

            this.CommandBindings.Add(new CommandBinding(this.MyCommand, OnExecute,

                   OnQueryCommandEnabled));

        }

 

        public RoutedCommand MyCommand

        {

            get { return _myCommand; }

        }

 

        private void OnQueryCommandEnabled(object sender, CanExecuteRoutedEventArgs e)

        {

            e.CanExecute = !string.IsNullOrEmpty(e.Parameter as string);

            e.Handled = true;

        }

 

        private void OnExecute(object sender, ExecutedRoutedEventArgs e)

        {

            string text = e.Parameter as string;

 

            // Do something with text

        }

 

        RoutedCommand _myCommand = new RoutedCommand();

    }

Note: We've removed any reference to specific UI elements from the code behind! We set up a RoutedCommand and make it available through a property. And, we've set up a binding pointing to query enabled and execute functions. These functions get the string to operate on through the command parameter.

Now, here's the Xaml:

<Window x:Class="CommandDemo.Window1"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    Title="CommandDemo" Height="300" Width="300"

    DataContext="{Binding RelativeSource={RelativeSource Self}}"

    >

    <StackPanel>

      <TextBox Name="_textBox"/>

      <Button Name="_button"

              Command="{Binding MyCommand}"

              CommandParameter="{Binding Text, ElementName=_textBox}">

        Do something

      </Button>

    </StackPanel>

</Window>

One key thing I've done here is to set the DataContext of the window to itself. That's what lets Command="{Binding MyCommand}" work in the button. That binding is relative to the DataContext. It sets up the command parameter by binding to the text in the text box.

With this version, the button's enabled state will change when the text field changes. How does that work exactly? Well, ICommand has a CanExecuteChanged event that the button registers for and requeries the enabled state each time it fires. But, when does it fire? With a RoutedCommand, it will fire on any input event. You can also suggest that commands update by calling CommandManager.InvalidateRequerySuggested. So, after each key press or other input event, WPF will call OnQueryCommandEnabled.

So, where are we? We've really cleaned things up from the original version. The code behind no longer knows about any of the UI elements in the Xaml. But, we can go further and completely remove the command behavior from the code behind. That'll be my next post!