Consider a Command-Centric View Model – A Simple WPF Command/View interaction model for Windows 8 Metro Applications

Like most of you I’ve written lots of MVVM code in WPF and Silverlight applications, and maybe it’s just me, but I eventually grew pretty tired of the explosion of command execution code in my View Models. I not only came to the conclusion (as did many others) that commands don’t belong in view models, I also began to get a feeling that view models don’t belong in view models.

Enter Windows 8 and with it that lovely little base class generated when you create a Grid XAML application, the LayoutAwarePage class. Yeah, yeah, it handles lots of issues with layout (thus the name) but it also does something else that’s pretty interesting. It provides a nice implementation of IObservableMap and binds it to the DefaultViewModel property. So now you can do stuff like this:


protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    this.DefaultViewModel["MyString"] = "my string";
}

And in your XAML you bind to it thusly:


<TextBox Text="{Binding MyString, Mode=TwoWay}"/>

And when the user types something, say “Hello” into the box”, the member in DefaultViewModel[“MyString”] changes to “Hello”.

Cool, yeah?

So throw that concept into the stew with the issues I’ve been having with Command patterns in general:

  • Commands have typically called into execution methods in view models. That makes for a LOT of command execution code in view models if you have a moderately complex app. Just scrolling through all that code makes programming a lot less fun.
  • I hate the creation and wiring up of commands and their delegates. I’ve seen some nice patterns that can mitigate this, but still.
  • A lot of the solutions I’ve seen to wrestling the command/view model issues to the ground seemed really complex. I hate complexity, even when it’s necessary. I have a simple mind and I like simple solutions that I can readily grasp when I come back to code after a couple of years of working on something else.
  • Many commands require some interaction with views. A command will start doing some stuff, then have the view do some things like collect user input, then the command will continue on with its work. It seems like every time I had to do something like this things got just a little messier.

That last point has become important to me with Metro apps. Imagine I have an app with a screen that looks like this:

The user selects the item, and the Edit button lights up. When the user hits the edit button, this view is shown:

The user hits OK or Cancel, and they’re taken back to the previous screen with the operation completed (or cancelled.)

All of this busy stuff is part of a single Edit command, and I’d like a simple way to standardize this kind of thing without the ridiculously tight coupling I’m always stuck with when it’s all embedded in my command’s Execute method.

So I decided to tackle all of these issues and implement something I’d been thinking of as the Command-Centric View Model. I don’t think it’s really a formal pattern because it’s not complex and hard to understand J but it does kind of feel like a pattern to me. And it does have elements of other patterns I’ve looked at, with a twist.

And here’s the basic interaction model:

It starts, like several other solutions, with a base class ICommand implementation. If you’ve seen other patterns, this will look pretty familiar, with a couple of twists. You can get the source code for this sample application at this link.

    public class BaseCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;

        // used to access page's DefaultViewModel for executing commands
        private LayoutAwarePage _hostPage;
        public LayoutAwarePage HostPage
        {
            get { return _hostPage; }
            set { _hostPage = value; }
        }

        // a nice accessor to the default view model in the host page
        public IObservableMap<String, Object> DefaultViewModel
        {
            get
            {
                if(HostPage != null)
                    return (IObservableMap<String, Object>)HostPage.GetValue(LayoutAwarePage.DefaultViewModelProperty);
                return null;
            }
        }

        // ctor
        public BaseCommand()
        {

        }

        // Dispatch the command to the view. The derived class has
        // probably done some fix up before invoking this method
        protected void BeginCommand(string commandName)
        {
            var hostPage = HostPage as ICommandBinder;
            if (hostPage != null)
                hostPage.BeginCommand(commandName);

        }

        // Dispatch a command completed to the view to allow
        // it to refresh or redraw or whatever
        protected async void CommandCompleted()
        {
            var hostPage = HostPage as ICommandBinder;
            if (hostPage != null)
                await RunOnUIThread(hostPage.CommandCompleted);
        }

        // a public method that allows the view to trigger a re-evaluation
        // of the command invoking UI (like a button) enabled state
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, new EventArgs());
        }

        // the public ICommand.CanExecute method, simply dispatches to a virtual method
        // for derived classes to handle
        public bool CanExecute(object parameter)
        {
            return OnCanExecute(parameter);
        }

        // the public ICommand.Execute method, also dispatches to a virtual method
        // for derived classes to handle
        public void Execute(object parameter)
        {
            OnExecute(parameter);
        }

        // the virtual method for testing if execution can be done
        public virtual bool OnCanExecute(object parameter)
        {
            return true;
        }

        // the virtual method to execute the command
        public virtual void OnExecute(object parameter)
        {
        }

        // a method for the view to notify the command
        // that it's done with UI stuff
        public virtual void ContinueExecuting(string flag)
        {
        }

        // Some helper methods I use a lot in commands

        // Shows a yes/no message box and executes callbacks
        public static async Task ShowYesNoMessageBoxAsync(string title, string content, UICommandInvokedHandler yesCallback, UICommandInvokedHandler noCallback)
        {
            var mb = new MessageDialog(content, title);
            var yesCommand = new UICommand("Yes", yesCallback, 0);
            var noCommand = new UICommand("No", noCallback, 1);

            mb.Commands.Add(yesCommand);
            mb.Commands.Add(noCommand);
            mb.CancelCommandIndex = 1;

            await mb.ShowAsync();
        }

        // A simple method to dispatch calls to the UI thread,
        // because I'm always forgetting the syntax to this
        protected async Task RunOnUIThread(Windows.UI.Core.DispatchedHandler agileCallback)
        {
            // notify the UI that it's ok to update the display
            await HostPage.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, agileCallback);
        }
    }

Aside from the OnExecute/OnCanExecute virtual methods that allow derived types to do their thing, there are some things to note:

  • It stores the page in the property HostPage. This gives the command access to the Page’s DefaultViewModel
  • Since DefaultViewModel is implemented as a DepedencyProperty in LayoutAwarePage, I created a get method that snags it and makes it available to the command as an internal property.
  • There are three additional methods that bear explaining:
    • BeginCommand – this is used by the Command to let the view do some view stuff prior to executing the command. In this case we’ll show an edit dialog, but you could turn on a progress indicator in the view, or the command could simply elect to not call this method at all. In my sample, the view shows a dialog and returns.
    • ContinueExecuting – The view calls this method when it’s done doing whatever it was doing. In the sample, it’s called when the user clicks on the OK or cancel buttons, and those results are passed back to the command. The command can then finish it’s work.
    • CommandCompleted – When the command is done, it calls this method on the view (in the UI thread) to allow the view to set whatever view state it needs, like turning off progress indicators.

Here’s my DoSomthingCommand implementation that leverages BaseCommand. It’s pretty simple:

    public class DoSomethingCommand : BaseCommand
    {
        public DoSomethingCommand()
            : base()
        {
        }

        // a test to see if the command can run
        public override bool OnCanExecute(object parameter)
        {
            // see if the value is there. I want to allow the
            // user to edit this item in a dialog, then save it
            // to the database

            if (HostPage == null)
                return false;

            if (!DefaultViewModel.ContainsKey("SelectedItem"))
                return false;
            if (DefaultViewModel["SelectedItem"] == null)
                return false;

            return true;
        }

        // ICommand.Execute, called by the framework when the Edit button is pushed
        public async override void OnExecute(object parameter)
        {
            var item = DefaultViewModel["SelectedItem"] as FakeItem;
            if (item == null)
            {
                await ShowYesNoMessageBoxAsync("No Item!", "Error", (cmd) => { }, (cmd) => { });
                return;
            }

            // Set up the properties I want the user to edit. I use separate
            // properties because I want to be able to cancel
            DefaultViewModel["ItemName"] = item.Name;
            DefaultViewModel["ItemDescription"] = item.Description;

            // tell the view to do it's stuff, and tell it which command this
            // is. After all, there's probably more than one command here.
            BeginCommand("DoSomethingCommand");

        }

        // when the view is done displaying or doing whatever,
        // it will call this method.
        public async override void ContinueExecuting(string flag)
        {
            var item = DefaultViewModel["SelectedItem"] as FakeItem;
            if (item == null)
            {
                await ShowYesNoMessageBoxAsync("No Item!", "Error", (cmd) => { }, (cmd) => { });
                return;
            }

            item.Name = (string)DefaultViewModel["ItemName"];
            item.Description = (string)DefaultViewModel["ItemDescription"];
            item.SaveItem();

            CommandCompleted();
        }
    }

Like the pages generated by the Windows Store Grid project template, the Page derives from LayoutAware page, and implements the ICommandBinder interface. ICommandBinder allows the page to orchestrate its own view semantics with the execution of a given command. Here’s the sequence of events:

  • Implement a command object and bind it to a button.
  • In the page, set up the conditions for the command to be active, like selecting an item in a list
  • When the Execute method of the command is invoked (by a button click, for example) by the framework, the command invokes the BeginCommand method, passing a string identifying itself to the Page. The page can use this to differentiate between multiple commands on the page.
  • The page in turn displays its DataEntryDialog for the user to input information. I’ve implemented a nice little user control to support generic dialog semantics that the page can set up via markup, and the Page calls its BeginEdit method.
  • The dialog in turn calls the page’s ICommandBinder.ViewOperationCompleted method with the indication of a result, in this case, an OK or Cancel enum.

The Page markup declares the instance of the command and binds it to a button like so:


<Page.Resources>
    <common:DoSomethingCommand x:Name="doSomethingCommand"/>
</Page.Resources>

And…


<Button HorizontalAlignment="Center" Content="Edit the selected item" Command="{Binding Source={StaticResource doSomethingCommand}}"/>

The page also displays a list of FakeItems:

<ListView x:Name="itemListView" 
          HorizontalAlignment="Center" 
          Height="400"
          Width="300"
          Margin="6"
          ItemsSource="{Binding ItemList}" 
          SelectionMode="Single" 
          SelectionChanged="itemListViewSelectionChanged">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Background="White" Width="300" Height="80" Margin="4">
                <TextBlock Margin="6" Foreground="Black" Text="{Binding Name}"/>
                <TextBlock Margin="6" Foreground="Black" Text="{Binding Description}"/>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

I also declare the edit dialog and set it to collapsed:

[/sourcecode]

I also declare the edit dialog and set it to collapsed:


<Grid x:Name="dialogHost" HorizontalAlignment="Center"  Background="Transparent" Visibility="Collapsed">
    <local:DataEntryDialog 
        HorizontalAlignment="Center"  
        x:Name="dataEntryDialog" 
        Title="Edit Me">
        <local:DataEntryDialog.DialogContent>
            <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="260"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="48"/>
                    <RowDefinition Height="48"/>
                </Grid.RowDefinitions>
                <TextBlock FontSize="16" Margin="4" VerticalAlignment="Center" Grid.Column="0" Grid.Row="0" Text="Name: "/>
                <TextBlock FontSize="16" Margin="4" VerticalAlignment="Center" Grid.Column="0" Grid.Row="1" Text="Description:"/>
                <TextBox  Margin="4" VerticalAlignment="Center" Grid.Column="1" Grid.Row="0" Text="{Binding Item.Name, Mode=TwoWay}"/>
                <TextBox  Margin="4" VerticalAlignment="Center" Grid.Column="1" Grid.Row="1" Text="{Binding Item.Description, Mode=TwoWay}"/> 
            </Grid>
        </local:DataEntryDialog.DialogContent>
    </local:DataEntryDialog>
</Grid>

The sample project has the full implementation of the DataEntryDialog control, but it simply provides layout for the content you declare in the DialogContent property and provides OK/Cancel buttons. You can make this as rich or as simple as you wish, depending on your needs.

The page does some minimal housekeeping at startup. Here’s the first few lines declaring it. Note that it inherits LayoutAwarePage and implements ICommandBinder, and declares a private member variable for the current command string. In it’s loaded event, it sets the doSomethingCommand’s HostPage property to this
and when LoadState is triggered, it sets up its view model with our item list.


<summary>
    /// A page that displays an overview of a single group, including a preview of the items
    /// within the group.
    /// </summary>

    public sealed partial class ItemPage : App2.Common.LayoutAwarePage, ICommandBinder
    {
        private string _currentCommand;

        public ItemPage()
        {
            this.InitializeComponent();
            Loaded += ItemPage_Loaded;
        }

        void ItemPage_Loaded(object sender, RoutedEventArgs e)
        {
            // set up the command's host member
            doSomethingCommand.HostPage = this;
        }
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            // set up my "list". You would probably use real data here.
            this.DefaultViewModel["ItemList"] = App.ItemList;
        }

At page initialization time, the BaseCommand’s CanExecute method is hit by the framework to determine if the Button should be enabled. BaseCommand dispatches it out DoSomethingCommand’s OnCanExecute method, which looks like this:


        // a test to see if the command can run
        public override bool OnCanExecute(object parameter)
        {
            // see if the value is there. I want to allow the
            // user to edit this item in a dialog, then save it
            // to the database

            if (HostPage == null)
                return false;

            if (!DefaultViewModel.ContainsKey("SelectedItem"))
                return false;
            if (DefaultViewModel["SelectedItem"] == null)
                return false;

            return true;
        }

It pokes around in the page’s DefaultViewModel looking for the objects it needs to execute. Since the page hasn’t put them there yet, it returns false and the button is disabled. When it does, the button becomes active. This happens in the SelectionChanged method of the ListView in our Page codebehind:


        private void itemListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // set the view model with the selected item
            DefaultViewModel["SelectedItem"] = itemListView.SelectedItem;

            // trigger a CanExecute call on our command by the framework.
            doSomethingCommand.RaiseCanExecuteChanged();
        }

The page sets the expected property and hits the command’s RaiseCanExecute method, implemented in BaseComman. This causes BaseCommand to raise it’s CanExecuteChanged event, and the framework will respond by re-invoking the CanExecute method. And this time, our OnCanExecute override will return true, the button will be enabled and we’re ready to go. Once the user hits the button, the OnExecute method of the command is called.


        // ICommand.Execute, called by the framework when the Edit button is pushed
        public async override void OnExecute(object parameter)
        {
            var item = DefaultViewModel["SelectedItem"] as FakeItem;
            if (item == null)
            {
                await ShowYesNoMessageBoxAsync("No Item!", "Error", (cmd) => { }, (cmd) => { });
                return;
            }

            // make a temp binding object so we can revert
            // if the user cancels.
            var tempItem = new FakeItem()
            {
                Name=item.Name,
                Description = item.Description
            };

            DefaultViewModel["Item"] = tempItem;

            // tell the view to do it's stuff, and tell it which command this
            // is. After all, there's probably more than one command here.
            BeginCommand("DoSomethingCommand");
        }

Our command again pokes around looking for the SelectedItem property. This time it’s there, so it creates a clone of the selected item that can be discarded if the user hits Cancel, and it places it in the DefaultViewModel as “Item”. It then invokes the ICommandBinder.BeginCommand method on the Page.

The page receives the method and displays the dialog:


        // called by the command when it's time for the view
        // to do something during command processing
        public void BeginCommand(string Command)
        {
            // save the command
            _currentCommand = Command;

            // if we had a progress indicator, this is a good place to turn it on.
            // since we're going to show a dialog for data entry, we're not doing that.
            dataEntryDialog.BeginEdit(this, this.dialogHost);
        }

Our dialog has a BeginEdit method that actually manages the displaying of the page, and how it does all that isn’t too relevant, suffice it to say that it’s pretty simple. The implementation is included in the sample code download.

What is important is that when the user clicks on the DataEntryDialog’s OK or Cancel buttons, it calls back through the ICommandBinder.ViewOperationCompleted method. Here’s the code from the relevant portion of DataEntryDialog:


        // OK button click handler
        private void buttonOKClick(object sender, RoutedEventArgs e)
        {
            EndEdit(ViewOperationResults.OK);
        }

        // Cancel button click handler
        private void buttonCancelClick(object sender, RoutedEventArgs e)
        {
            EndEdit(ViewOperationResults.Cancel);
        }

        // Called when the OK or Cancel button is pressed, dispatches the result to
        // the hosting page
        public void EndEdit(ViewOperationResults results)
        {
            _dialogHost.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            _page.ViewOperationCompleted(results);
        }


And the page processes it like so:

        
        public void ViewOperationCompleted(ViewOperationResults result)
        {
            if (_currentCommand == "DoSomethingCommand")
                doSomethingCommand.ContinueExecuting(result.ToString());
        }

Once the command receives control, it finishes out the operation:

        
        // when the view is done displaying or doing whatever,
        // it will call this method.
        public async override void ContinueExecuting(string flag)
        {
            if (flag == "OK")
            {
                var tempItem = DefaultViewModel["Item"] as FakeItem;
                if (tempItem == null)
                {
                    await ShowYesNoMessageBoxAsync("No Item!", "Error", (cmd) => { }, (cmd) => { });
                    return;
                }

                var item = DefaultViewModel["SelectedItem"] as FakeItem;
                if (item == null)
                {
                    await ShowYesNoMessageBoxAsync("No Item!", "Error", (cmd) => { }, (cmd) => { });
                    return;
                }

                item.Name = tempItem.Name;
                item.Description = tempItem.Description;
                item.SaveItem();
            }

            CommandCompleted();
        }

The CommandCompleted implementation in my sample doesn’t do anything, but it’s likely that a real app would have some additional work to do. One thing to remember is that when the page is calling into the command, but if you do any async calls, any statements after the await will be on the thread pool, and not on the UI thread, so if you start setting databound properties, you’ll need to do that on the UI thread. I’ve provided method called RunOnUIThread in BaseCommand that’s a thin shim around the dispatcher for that purpose. It’s a trivial method but it keeps me from having to remember the syntax for dispatching, which I’m always forgetting.

So that’s the basics of it. Some additional work could be done to make these elements more loosely coupled, but this is plenty to get started. The advantages of the pattern are:

  • I don’t really worry about how commands relate to complex models, the DefaultViewModel dictionary in the page provides a simple abstraction for the command to work with.
  • I keep the command classes in separate files, so they’re really easy to find.
  • Commands are instantiated in the XAML markup in the page that needs them, so we leverage the consistency of XAML to achieve repeatability and re-use. In Windows 8 Metro apps, it’s common to have a command on the AppBar and to surface it on the UI for ease of access. In this case both buttons bind to the same command instance in the same way.
  • I have a repeatable and pretty nice model for doing UI interaction within the scope of a command’s execution. This has really cleaned up my view model code.

Enjoy!

About these ads

Leave a comment

Filed under Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s