Welcome to EMC Consulting Blogs Sign in | Join | Help

Richard Griffin's Blog

WPF Commands a scenic tour part I

WPF Commands provides a new method for decoupling UI events, the handling of these events and also how you can register multiple UI elements for these defined events.

When I first came across commands they immediately stood out from the crowd as one of the new baked in features that developers need to use and be aware of, what I found was not a concise and easy to understand guide or examples on how to use them but a frustratingly over complex message. So in this post I am going to attempt to clear the water and bring your attention to the different ways that you can utilise the power of Commands with less pain. I am not going to discuss what they are, but you can read Jelle Druyts blog who has a great post and of course you can dig out your copy of the Gang of Four et al or Head First Design Patterns to remind yourself of the Command Pattern.

For many years I have used such patterns as MVC and more recently MVP for building applications to provide the levels of separation to help with unit testing and make applications more manageable to build. Personally I have always split the View into what I call a Skin and a Process layer. The Skin contains all code to do with the elements to be presented on the form or page and the Process holds all the UI processing for actions that can be performed by the user.

Why ? Well I wanted to be able to unit test the UI and this technique provided the right levels of isolation enabling unit tests to be written against the Processing sub layer. At the time I was working for a company that wanted to do white labeling of their product so the UI Skin needed to be flexible, this was achieved using the described methods. However, due to limitations of C# it was not possible to build the cleanest eventing mechanism, and testing the event handling was always tricky to the point of un-testable at times.

WPF Commands means that you don't need to put this amount of effort in to get the benefits. Commands can be separated and therefore isolated, so that Commands are testable, Commands are localisable (baked in Commands do this for free), baked in Commands provide default key gestures (standard keyboard shortcuts).

When it comes to implementation then there are really two options, does your implementation require UI interaction, or do you need a non UI Command. If your implementation needs a visual representation then RoutedUICommands are your friend, if you need a non visual commands then you can implement a more structured design by creating custom classes that implement ICommand.

So we can start the tour off by having a look into the implementation of those RoutedUICommands that are baked into WPF. WPF provides a library of common commands, which include; ApplicationCommands; ComponentCommands; NaviagationCommands; MediaCommands and EditingCommands. These baked in commands make life a lot easier for designers and developers, with a couple of lines of XAML you can associate UI Elements with the baked in command that you want. For example:-

    <UserControl.CommandBindings>
        <CommandBinding Command="MediaCommands.Play" CanExecute="OnQueryExecute" Executed="Execute"/>
        <CommandBinding Command="MediaCommands.Pause" CanExecute="OnQueryExecute" Executed="Execute"/>
        <CommandBinding Command="MediaCommands.Stop" CanExecute="OnQueryExecute" Executed="Execute"/>
    </UserControl.CommandBindings>

....

    <StackPanel Grid.Row="2" Orientation="Horizontal">
        <Button Command="MediaCommands.Play" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />
        <Button Command="MediaCommands.Pause" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />
        <Button Command="MediaCommands.Stop" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}" />
        <Slider Name="Volume" Minimum="0" Maximum="100" Value="{Binding ElementName=Player, Path=Volume}" Width="100" />
    </StackPanel>

So the baked in commands provide some cool functionality but this is rather specific, what happens when we want to create your own RoutedUICommands. So we come to the most important part of the tour; design your commands before you implement them. The first part is to identify the commands that you are going to need, next categorise these into functional areas of the application that you are building. Chat with your UE person and I am sure that they will be more than willing to help you out.

I will clear this up with a simple example of an application that has a list view control with add and remove functionality, items selected in the list view are added to another control or removed from the list view control. The user can interactive with this functionality either from a file menu or they can invoke the same two actions from add and remove buttons, or I can use the context menu by right clicking and select the same options. These options can be viewed as library commands in a WPF world allowing the developer to isolate the functionality and provide a consistent look and feel throughout the application. Now that we have identified a simple mechanism for helping group commands, we need to briefly look over how we would have usually implemented these in an application that was not using WPF. Using either a MVC or MVP pattern these events would live in the processing sub layer of the view and this is still the case for WPF, where the difference lies is in the way we implement the event handlers around the application. Due to limitations in C# we would have had to provide a handler that wires up to the event and provide a delegate method that would call the code in the processing sub layer. There are other ways that we could achieve the same result but this would require using multicast delegates and these can be difficult to debug. However, WPF provides the developer/designer with the flexibility to hook up to these commands directly in the XAML or execute them directly in the code behind and this is where you can harness the real power of commands.

Now let's start cutting some code. Goto your UI project and create a folder called commands, this will be the repository for all your UI commands that we will create whilst building the application. In this folder create yourself a static class called LibraryCommands ensuring that you have a static constructor. For those Library commands that were identified earlier we are now going to create these as private members of our class and provide some public properties to allow outsiders to access the commands. You should have something that looks like this :-

    namespace Conchango.WPF.BrowserApp.UI.Commands
    {
        public static class LibraryCommands
        {
            private static Dispatcher _dispatcher;
            private static RoutedUICommand addCommand = new RoutedUICommand("Add", "add", typeof(LibraryCommands));
            private static RoutedUICommand removeCommand = new RoutedUICommand("Remove", "remove", typeof(LibraryCommands));

       

            public static RoutedUICommands Add
            {
                return addCommand;
            }


            public static RoutedUICommands Remove
            {
                return removeCommand;
            }

 

            static LibraryCommands()
            {
                _dispatcher = Dispatcher.CurrentDispatcher;
            }
        }
    }    

So far we have created a static class in our UI project, defining a set of library commands for a simple application. We need to take a closer look at the RoutedUICommand constructor; the first parameter is a string description of the command and will be used in the UI and provides the visual representation of the command; the second parameter is the name of the command itself which is used by the framework for serialisation; and the third parameter is ownertype, essentially the owing class for this defined command. There is a certain amount of refactoring that can happen here in regards to the text parameter that we are passing in. I will come back to this later on in the post.

The other important implementation detail that you need to come to terms with is that the majority of your classes in WPF will be instantiated on the UI thread and therefore have the UI Dispatcher associated with the UI thread. When you do Dispatcher.CurrentDispatcher call you will retrieve an instance of the Dispatcher object for the context of the current thread your code is being run in, which may not be the UI thread. Therefore, a simple and effective work around is to always keep a local copy of the Dispatcher and wire this up in your constructor.

So the commands that we have defined are looking good however, they are pretty useless at the moment as they are not registered with the Command Manager, who is a rather important when it comes to dealing with commands, it is also rather particular about what parameters that you pass in. So for the moment we are going to register the commands with the application, by adding the line of code to the constructor :-

             Application.Current.MainWindow.CommandBindings.Add(
             new CommandBinding(_addCommand, ExecuteAddCommand, CanExecuteAddCommand));

             Application.Current.MainWindow.CommandBindings.Add(
             new CommandBinding(_removeCommand, ExecuteRemoveCommand, CanExecuteRemoveCommand));

The RegisterClassCommandBinding call wires all our hard work together and registers this with the input element in this case I have used a ListView but this could by any element that supports the IInputElement interface. There is a second option that you can use depending on the design that you need, the second option is to register the commands at an application wide level.

CommandManager.RegisterClassCommandBinding(typeof(ListView), new CommandBinding(_addCommand, ExecuteNavgiateForwardCommand,  CanExecuteNavigateForwardCommand));

CommandManager.RegisterClassCommandBinding(typeof(ListView), new CommandBinding(_backwardCommand, ExecuteNavgiateBackwardCommand, CanExecuteNavigateBackwardCommand));

 

Next we need to provide the Execute and CanExcute methods.

     public static void CanExecuteAddCommand(object sender, CanExecuteRoutedEventArgs e)
     {
        // Handler to provide mechanism for determining when the add command can be executed.
        e.Handled = true;
        ......
     }

    public static void CanExecuteRemoveCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        // Handler to provide mechanism for determining when the remove command can be executed.
        e.Handled = true;
        ......
    }

 

    public static void ExecuteAddCommand(object sender, ExecutedRoutedEventArgs e)
    {
        // Handler to actually execute the code to perform the add command
        //add your code here to handle
    }

 
    public static void ExecuteRemoveCommand(object sender, ExecutedRoutedEventArgs e)
    {
        // Handler to actually execute the code to perform the remove command
        //add your code here to handle
    }

Unfortunately we are coming to the end of the tour and building up to the grand finale. So, just looking back for a moment at the great view; what have we implemented, we have added all the plumbing for the Commands; created the navigational commands that have been identified that are required in the application; and we have also gone a step further and isolated these to a single class ready to be unit tested. The final stage is to add these commands into the XMAL so that they can be used.

In the XMAL file that will be using the command I have declared the namespace that I want to include:-

xmlns:conchangoCommand="clr-namespace: Conchango.WPF.BrowserApp.UI.Commands "

Next add the list view control and define the context menu that we want to use:-

    <ListView Name="AList" Style='{StaticResource MyListStyle}'
         ItemsSource="{Binding Source={StaticResource TestData}}"
        VirtualizingStackPanel.IsVirtualizing="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
        MouseDoubleClick="ListDblClick" >
        <ListView.ContextMenu>
            <ContextMenu>
            <MenuItem
                Command=" conchangoCommand: LibraryCommands.Add"
                CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"/>


            <MenuItem
                Command=" conchangoCommand: LibraryCommands.Remove"
                CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"/>
            </ContextMenu>

        </ListView.ContextMenu>

You may have noticed that there is a MouseDoubleClick event handled that I have added to the List View. The user experience that we want to implement allows the user to double click on an item in the List View and the result of the action is that this item is added to another control in the window. The implementation is rather straight forward, WPF provide the flexibility to allow designer or developer to implement commands not only to be used in the XAML but also in the code behind.

        private void ListDblClick(object sender, MouseEventArgs e)
        {
            ListView list = sender as ListView;
            if (list != null)
            {
                LibraryViewCommands.Add.Execute(list.SelectedItems, list);
            }
        }

And finally we can also wire up the commands to buttons :-

         <Button Content="Add" Command="{x:Static conchangoCommand: LibraryCommands.Add}" />
         <Button Content="Remove" Command="{x:Static conchangoCommand: LibraryCommands.Remove}" />

So this brings us to the end of the scenic tour, looking back on the journey I hope that this has helped cleared up the usage of RoutedUICommands and provides the information that you require to implement them in your own applications. Earlier on in the article I mentioned that the implementation of the string description associated with the creation of the command, this can be refactored so that it retrieves this from the Resources of you application.

In part II I will investigate how to utilise ICommand in your WPF applications.

Published 23 February 2007 15:47 by Anonymous
New Comments to this post are disabled
Powered by Community Server (Personal Edition), by Telligent Systems