Using Commands in BabySmash

For background, you should read the first post by Scott Hanselman on his BabySmash project. I’ve already written one post on how to to do configuration better. In this post, I’ll cover another aspect of WPF that Microsoft got right: commands.

I’ve written about commands before. In that post, I was describing a way to implement Commands in Windows Forms. They are completely missing in Windows Forms, but in WPF, they got first class treatment.

Why Commands?

In my WinForms article, I didn’t really discuss what Commands were. Oh well. I’ve always told myself to proof read, but I never seem to listen.

Consider an operation like opening a text file. Suppose you were writing a notepad clone. That would be an important operation to support. Notepad has a menu with a menu item that shows the system’s OpenFileDialog, lets you pick a file and open it, display it, etc. Now suppose you wanted to do one better than Notepad and you wanted a right-click menu with a menu item that does the same thing. You’d handle the Click event for your new context menu item, and do the same thing as in your notepad clone’s main menu item. A little later in the dev cycle, you decide you’re going to have a Toolbar with a button that lets you open a file too. Then you decide that your notepad clone is so important that you’re going to have a icon in the system tray with a context menu that lets you open a file from one of the menu items. I could go on and on with, say, keyboard shortcuts and the like, but I think I’ve gone far enough.

What does your code look like now? If you were smart, you’d have one method in your Window class that would handle opening a file and call that method from all the click handlers. If you were really smart, you’d hook up all the Click events you were handling to the same method. But you probably handle each one separately and copy the code each time. That’s what I’d do when I was just starting out.

This is probably fine for a notepad clone; it’s fairly straightforward, there’s one window that doesn’t do much. If you started out with the write once/paste anywhere coding style, you could refactor to a more streamlined approach like the other two above. But consider how Visual Studio would look like if it took this approach. A operation can be called from just about anywhere in VS. If it were coded like my theoretical notepad clone, the main window must be millions of lines long: there are tons of operations in VS. Of course, VS is not coded like that. They use commands.

What if we wanted to make our open operation in the notepad clone a little more permanent? We’d probably want to take the code and move it into it’s own class that could be re-used across our whole app, independent of the window. You’ve essentially just made a command: separated the logic of the operation from the UI that invokes that operation. There would likely be more than one operation too, and if we did that to every operation, we’d probably see a lot of duplication in those classes. Duplication that can be removed by formalizing the reoccurring pattern. This idea really takes off when it’s built right in to your UI framework; the controls you use could know about commands, there could be a set of built-in commands that are common enough that they are worth implementing by the framework. Fortunately, Microsoft has done with WPF.

For complete background on commands in WPF, you should check out Jelle Druyts’s article. He explains it really well, and there’s basically nothing I can add that doesn’t repeat what Jelle already says.

Instead what I’ll do is show you what I did in BabySmash to reduce some of the code that Scott wrote, and that you’d have to write in your apps, and let the system to it for us. I’ll explain just enough about how commands work in WPF, but you should really read Jelle’s article. It’s ok, I’ll wait for you.

BabySmash lets a baby, or anyone, repeatedly mash the keyboard which makes fun things happen. That’s the main part of the app, but there are some ancillary things that all apps need, like settings and setting those settings, which I’ve covered already. We also need a way to open the dialog to set the settings. To open the options window, one has to press Ctrl-Shift-Alt-O. Scott sniffs for that in the "main loop" of his app, the KeyUp event handler. Perfectly fine really, there’s only one operation right now, but he may want to add more, then that KeyUp handler method will started getting hairy….er. Also, I’m a firm believer in methods doing one thing. Right now the KeyUp handler is doing many things.

private void Window_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
    //HACK: Why did I find the Windows Forms modifier keys classes to be so much more accurate?
    bool Alt = (System.Windows.Forms.Control.ModifierKeys & WinForms.Keys.Alt) != 0;
    bool Control = (System.Windows.Forms.Control.ModifierKeys & WinForms.Keys.Control) != 0;
    bool Shift = (System.Windows.Forms.Control.ModifierKeys & WinForms.Keys.Shift) != 0;


    if (Alt && Control && Shift && e.Key == Key.O)
    {
        e.Handled = true;
        Options o = new Options();
        o.ShowDialog();
        return;
    }

    numberOfShapes++;
    if (numberOfShapes > Properties.Settings.Default.ClearAfter)
    {
        numberOfShapes = 1;
        MainCanvas.Children.Clear();
    }

    PlayLaughter();
    ...
}

So let’s separate out the Options window opening code. To do that, as Jelle explains, we need to declare a command binding. We could do that in code, but let’s do it in XAML.

<Window x:Class="BabySmash.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Baby Smash by Scott Hanselman - http://www.hanselman.com/babysmash"
        Height="386" Width="601"  WindowStyle="None"
         Topmost="True" Activated="Window_Activated" Closing="Window_Closing"    
        
    AccessKeyManager.AccessKeyPressed="Window_AccessKeyPressed"
       KeyUp="Window_KeyUp" KeyDown="Window_KeyDown"
       Loaded="Window_Loaded"
        >
    <Window.CommandBindings>
        <CommandBinding Command="Properties" Executed="Properties_Executed"/>
    </Window.CommandBindings>
    <Grid>
        <Canvas x:Name="MainCanvas">
            
        </Canvas>
    </Grid>
</Window>

Here’s the Window1.xaml from BabySmash with a CommandBinding declared. I’m using the built-in command, ApplicationCommands.Properties. Technically, not semantically correct, but close enough for our purposes. The CommandBinding is basically saying when someone invokes the Properties command for this window, handle it in the Properties_Executed handler, shown below.

private void Properties_Executed(object sender, ExecutedRoutedEventArgs e)
{
   Options o = new Options();
   o.ShowDialog();
}

We’re almost ready to delete some of Scott’s code. Now we have to hook up a way to invoke the command. If there were buttons or menu items in BabySmash, we could just set the Command property on them and WPF takes care of it all. It doesn’t, so we have to do a little more. We have to create an InputBinding for the Ctrl-Shift+Alt+O shortcut key, known in WPF as an InputGesture.

<Window>
    <Window.CommandBindings>
        <CommandBinding Command="Properties" Executed="Properties_Executed"/>
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Gesture="CTRL+ALT+SHIFT+O" 
                    Command="{x:Static ApplicationCommands.Properties}" />
    </Window.InputBindings>
    <Grid>
        <Canvas x:Name="MainCanvas">
            
        </Canvas>
    </Grid>
</Window>

Note the two ways in XAML to declare the command. The first one, in the CommandBinding element is allowed because it’s a built-in command. The second way will work for your custom commands as well. That’s it.

Now the KeyUp handler can be simplified by deleting code; as Scott pointed out, always a good thing.

private void Window_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
    numberOfShapes++;
    if (numberOfShapes > clearAfter)
    {
        numberOfShapes = 1;
        MainCanvas.Children.Clear();
    }

    PlayLaughter();
    ...
}

I’ll leave it as an exercise for you following at home to add the close command and an InputBinding for ALT-F4. I didn’t test thoroughly but this can handle the problem that Scott noticed with multiple monitors.

2 Replies to “Using Commands in BabySmash”

  1. This may be a stupid question but why is it x:static ApplicationCommands.Properties? Where does the ApplicationCommands bit come from?

    Also, you noted there were two ways to declare the command but I’m not seeing the second way. What am I missing?

    Thanks,
    Kevin

  2. @Kevin Berridge: The two ways:

    <CommandBinding Command=”Properties” />
    <InputBinding Command=”{x:Static ApplicationCommands.Properties}” />

    The first one should be easy to understand.

    The second needs some explaining. The x:Static is a XAML-specific thing for assigning properties to static, as in C# static, values; it can be applied anywhere. ApplicationCommands is one of the classes WPF comes with that provides built-in commands.

    Perhaps the equivalent code will help:

    CommandBinding binding = new CommandBinding();
    binding.Command = ApplicationCommands.Properties;
    binding.Executed += Properties_Executed;
    this.CommandBindings.Add(binding);

    InputBinding inputBinding = new InputBinding();
    inputBinding.Command = ApplicationCommands.Properties;
    inputBinding.Gesture = new KeyGesture(Key.O,
    ModifierKeys.Shift | ModifierKeys.Control | ModifierKeys.Alt);
    this.InputBindings.Add(inputBinding);

    Look on MSDN for ApplicationCommands. There are a couple of other classes like that: NavigationCommands, ComponentCommands and more, but I can’t think of them.

Comments are closed.