Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Commands Everywhere

0.00/5 (No votes)
23 Apr 2009 1  
This article covers techniques for using WPF commands with controls that don't support commands natively

Introduction

In the last few weeks I’ve started to work on a very interesting UI infrastructure project, based on WPF and Prism. I can’t give many details since it’s kind of confidential, but I want to share a great technique regarding WPF commands. Commanding is a very interesting topic in WPF, especially when dealing with WPF composite applications (known as Prism).

If you’ve had the opportunity to work with WPF, you probably wonder why only few controls: ButtonBase, Hyperlink and MenuItem implement the ICommandSource interface.

So the question is "What could be done in order to extend other controls such as Selector to execute both WPF and Prism (custom) commands?"

To answer this question, let's define the problem and provide functional requirements.

Problem

Working with either composite or monolithic UI applications, it is reasonable to treat application domain actions triggered by the user interface as commands, so that they can be handled by the Presentation Model or the Controller and be bound to an availability state. For example, having a ListBox, we want it to execute a command to fetch the rest of the item details from a service on item selection. The usual way to achieve this in WPF is to register the Selector.SelectionChanged routed event via XAML, handle it in the C# behind XAML file and delegate the call to the Presentation Model or Controller. This approach is not only convoluted but also inappropriate by design. But I’ll leave the design for another discussion.

Extracting at least one requirement from the example above, we want the Selector control to be able to execute a command when the Selector.SelectionChanged routed event is being fired.

There are many ways to solve this issue, each has its pros and cons.

Solutions

Custom Control

A straight forward approach is to create a new custom control, for example: CommandListBox, implement the ICommandSource and execute the command on item selection.

Although this solution is fairly reasonable, it requires creation of a custom control for each type of control that doesn’t natively support command.

Attached Properties

So let’s look at another approach. If you’re familiar with the outstanding WPF Attached Property mechanism you could solve this as follows:

<ListBox 
   local:CommandProvider.Command="{x:Static local:CommonCommands.Do}" 
   local:CommandExtender.Handler="{x:Static 
   local:CommandHandlers.SelectorSelect}" 
   local:CommandExtender.Parameter="{Binding Path=/}" />

In this case, the CommandProvider is a static class, it provides commanding services via attached properties, where:

  • Command is the command instance to execute
  • Handler is an instance of a custom type which provides the execute behavior, such as “execute the command on Selector.SelectionChanged event”
  • Parameter is the command parameter

As you can see, this approach is much more flexible and extensible since it can be used on any kind of UIElement, and without the unnecessary creation of a custom control.

Multiple Commands

Since both the custom implementation of ICommandSource and the attached properties approaches support only one command at a time, being executed by only one behavior, I’ve decided to extend the attached properties approach to support more than one command to be executed by more than one behavior.

<ListView x:Name="list" ItemsSource="{Binding Emails}" 
          IsSynchronizedWithCurrentItem="True"> 
   <ts:CommandSource.Trigger> 
      <ts:CommandTriggerGroup> 
         <ts:EventCommandTrigger 
            RoutedEvent="UIElement.PreviewMouseLeftButtonUp" 
            Command="{Binding Path=DownloadEmail}" 
            CustomParameter="{Binding ElementName=list, 
            Path=SelectedValue}" /> 
         <ts:EventCommandTrigger 
            RoutedEvent="UIElement.PreviewMouseRightButtonUp" 
            Command="{Binding Path=MarkAsRead}" 
            CustomParameter="{Binding ElementName=list, 
            Path=SelectedValue}" /> 
         <ts:EventCommandTrigger 
            RoutedEvent="UIElement.PreviewMouseLeftButtonDown" 
            Command="{Binding Path=OpenEmail}" 
            CustomParameter="{Binding ElementName=list, 
            Path=SelectedValue}" /> 
      </ts:CommandTriggerGroup> 
   </ts:CommandSource.Trigger> 
</ListView>

...

<Expander IsExpanded="{Binding Path=DummyProperty}" Header="Contact"> 
   <ts:CommandSource.Trigger> 
      <ts:PropertyCommandTrigger 
         Property="Expander.IsExpanded" 
         Value="True" 
         CustomParameter="{Binding}" 
         Command="{Binding Path=DownloadContact, 
         RelativeSource={RelativeSource 
         Mode=FindAncestor, AncestorType=Window}}" /> 
   </ts:CommandSource.Trigger> 
... 
</Expander>
Image1.pngImage2.pngImage3.png

I’ve replaced the CommandProvider with CommandSource in the markup code above and the three attached properties with only one: Trigger. The real difference here is that the Trigger attached property is of type ICommandTrigger.

  • The ICommandTrigger interface is implemented by three classes: EventCommandTrigger, PropertyCommandTrigger and CommandTriggerGroup.
  • EventCommandTrigger – executes a command when a routed event is being fired.
  • PropertyCommandTrigger – executes a command when a dependency property is being changed, and a specific value is met.
  • CommandTriggerGroup – represents a collection of commands. Using this class as shown above, you can attach more than one command trigger.

Note that both the EventCommandTrigger and PropertyCommandTrigger derive from the WPF Freezable type. This provides an option to be bound to elements in the visual tree. As for the CommandTriggerGroup I’ve used the FreezableCollection as its base class.

Command and Command Parameter

Since CommandTrigger translates routed events and dependency property values into Command, there should be an easy way to have both the routed event and property value, and another user parameter as one parameter of the command. To handle this situation, I’ve created the CommandParameter types.

OpenEmail = new RoutedCommand(); 
CommandBinding cmdBinding3 = new CommandBinding(OpenEmail); 
cmdBinding3.Executed += (s, e) => 
{ 
   var parameter = EventCommandParameter<EmailMessage,
       MouseButtonEventArgs>.Cast(e.Parameter); 
   if (parameter.EventArgs.ClickCount == 2) 
   { 
      parameter.CustomParameter.MarkAsRead(); 
         MessageBox.Show(parameter.CustomParameter.Content, 
            parameter.CustomParameter.Subject); 
   } 
}; 
cmdBinding3.CanExecute += (s, e) => 
{ 
   e.CanExecute = true; 
}; 
CommandBindings.Add(cmdBinding3);

Both the EventCommandParameter and PropertyCommandParameter types derive from the CommandParameter type. You can think of these types as simple wrappers around the Routed Event or Dependency Property and Custom Parameter. From the sample above, you can see that each type of the CommandParameter type has a special Cast<T1, T2> helper method. This simplifies the explicit casting operations of both the custom parameter and routed event argument or dependency property value.

Conclusion

Now that we can use commands anywhere, we can use only Data Templates as views for our Presentation Model. This mechanism comes in handy especially in Composite Applications where presenters are usually laid out in regions and Data Templates generate the view.

You may download the full code from here.

History

  • 23rd April, 2009: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here