WPF Commands Part 3: Custom RoutedUICommand


Welcome to Part 3 of WPF Commands. This will be the second last of the Basics for WPF Commands. For this entry, we will be looking at creating a custom RoutedUICommand, and how to make use of CommandTarget and CommandParameter properties.

First of all, you will typically need to create custom commands if the ones you get out of the box from WPF is insufficient. I would not think that this is a common scenario, but it would be good to know how to do it. If you need to do this, I recommend putting your custom controls into a separate class, similar to WPF. Let’s call our custom class CommandLibrary.

public static class CommandLibrary
{
    private static RoutedUICommand add = new RoutedUICommand("Add", "Add", typeof(CommandLibrary));

    private static RoutedUICommand remove = new RoutedUICommand("Remove", "Remove", typeof(CommandLibrary));

    public static RoutedUICommand AddListItem
    {
        get { return add; }
    }

    public static RoutedUICommand RemoveListItem
    {
        get { return remove; }
    }

}

We have created two RoutedUICommands, AddListItem and RemoveListItem. RoutedUICommand constructor has 3 overloads. The one is the snipplet above takes in a (1) text, (2) name and (3) class type that owns this commands. The last overload allows us to specify a Collection of Input Gestures (basically Key Gestures and Mouse Gestures), which is pretty handy. You can find more info here for the overloads. We have exposed these commands via public static properties, to allow for use in Xaml. Now that we have our custom commands, let’s see how we can hook it up. You might be wondering if there’s a need to make them as Dependency Properties…well in my opinion the answer is no. Because these commands are statically created and won’t change, we do not need to register them as DPs. Below is a screen shot of the UI.

customcommand1 You can view a video of the demo here.

We will be hooking up our commands to the Add button and Remove Button. The Add button will be enabled when (1) we input a text in the text box, and also (2) if there’s no item with the same text existing in the ListView. The Remove button will only be enabled if there’s a selected item in the ListView. And the rest is pretty self explantory, clicking the Add and Remove buttons will do what’s it implies. Now let’s look at the Xaml code.

<DockPanel>
 <Grid>
   <Grid.RowDefinitions>
     <RowDefinition Height="*" />
     <RowDefinition Height="*" />
   <RowDefinition Height="3*" />
 </Grid.RowDefinitions>

 <StackPanel Orientation="Vertical" Grid.Row="0" >
   <Label Target="{Binding ElementName=textbox}" Content="Add listbox item" />
   <StackPanel Orientation="Horizontal">
     <TextBox Name="textbox" MinWidth="100" HorizontalAlignment="Left" />
     <Button Command="local:CommandLibrary.AddListItem" 
             CommandTarget="{Binding ElementName=listview}"
             CommandParameter="{Binding ElementName=textbox, Path=Text}" 
             Content="add" HorizontalAlignment="Right" Margin="5,0,0,0" />
   </StackPanel>
 </StackPanel>

 <Button Content="Remove selected listbox item" 
         HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="1"
         Command="local:CommandLibrary.RemoveListItem" 
         CommandTarget="{Binding ElementName=listview}" />

 <ListView Name="listview" Grid.Row="2" SelectionMode="Single" >
   <ListView.CommandBindings>
     <CommandBinding Command="local:CommandLibrary.AddListItem" CanExecute="CanAddListItemExecute" Executed="OnAddListItemExecute" />
     <CommandBinding Command="local:CommandLibrary.RemoveListItem" CanExecute="CanRemoveListItemExecute" Executed="OnRemoveListItemExecute" />
   </ListView.CommandBindings>
 </ListView>
 </Grid>
</DockPanel>

Looking at the Xaml, the CommandBindings are tied to the ListView with our custom commands. Notice the use of local:CommandLibrary.AddListItem, where local is the XML namespace that points to the current project namespace. If you look at the Buttons, you notice these new properties for Command.

CommandTarget="{Binding ElementName=listview}"
CommandParameter="{Binding ElementName=textbox, Path=Text}"

By specifying a CommandTarget, we are telling the command that the target of itself will be a UI Element with name “listview“, and this will be “decoded” via the Databinding syntax at runtime. CommandParameter can be any object and will be passed along with the Command. We can then pull this value out in our event handlers in the code behind for our usage. In this example, using Databinding syntax, we are using the value of Text property (Path) from UI Element “textbox” which is the TextBox on the UI.(ElementName). That was not too hard at all. 🙂

private void OnAddListItemExecute(object sender, ExecutedRoutedEventArgs e)
{
    ListView lv = sender as ListView;
    if (lv != null)
    {
        lv.Items.Add(e.Parameter);
        textbox.Clear();
        e.Handled = true;
    }
}

private void OnRemoveListItemExecute(object sender, ExecutedRoutedEventArgs e)
{
    ListView lv = sender as ListView;
    if (lv != null)
    {
        if (lv.SelectedItem != null)
        {
            lv.Items.Remove(lv.SelectedItem);
            e.Handled = true;
        }
    }
}

private void CanAddListItemExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = !String.IsNullOrEmpty(textbox.Text) && !listview.Items.Contains(textbox.Text);
    e.Handled = true;
}

private void CanRemoveListItemExecute(object sender, CanExecuteRoutedEventArgs e)
{
    ListView lv = sender as ListView;
    if (lv != null)
    {
        e.CanExecute = lv.SelectedItem != null;
        e.Handled = true;
    }
}

Code above is the event handling logic for CanExecute and Execute for the 2 custom commands, as we have seen before in previous posts. One thing of interest would be that we are attempting to cast the sender object to a ListView type. We can safely assume that the sender will be the ListView in this example, because the CommandBindings are specified for the ListView. In the OnAddListItemExecute method, you can see the use of CommandParameter we specified in the Xaml, being retrieved from the ExecutedRoutedEventArgs and invoked like so, e.Parameter. When executed, we will add e.Parameter (value from the textbox) into the ListView‘s items.

That’s sums up this entry. Hopefully you will now appreciate the ease of being able to bind commands to a specific target on the UI, as well as make use of the properties of UI Elements as parameters for these commands. In my next and final post of WPF Commands Basics, I will discuss about how to extend UI controls using the ICommandSource interface to make use of the Routed Command System. Til then…..happy coding! 🙂

Download this sample solution here. (This is same for the Part 1 and 2 of this series, so if you already have it, you don’t need to download this)

Share this post :
Advertisements

4 Responses to “WPF Commands Part 3: Custom RoutedUICommand”

  1. Nico Sap Says:

    Big thanks on helping me understand routedCommands.

    But shouldn’t: private void CanAddListItemExecute(object sender, CanExecuteRoutedEventArgs e):

    ==>

    e.CanExecute = !String.IsNullOrEmpty(textbox.Text) && !listview.Items.Contains(textbox.Text);

    be

    e.CanExecute = !String.IsNullOrEmpty(e.Parameter) && !listview.Items.Contains(e.Parameter);

    Btw.
    Muchos graçias for the source solution 🙂

  2. Using Custom RoutedUICommand in xaml throws exception | BlogoSfera Says:

    […] have followed this article and some others to create a Custom RoutedUICommand. I am using Infragistics Ribbon, but I […]

  3. Using Custom RoutedUICommand in xaml throws exception Says:

    […] have followed this article and some others to create a Custom RoutedUICommand. I am using Infragistics Ribbon, but I […]


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

%d bloggers like this: