Archive

Posts Tagged ‘RoutedCommand’

Commanding: Binding Controls to Methods

May 7, 2012 Leave a comment

A Command in WPF encapsulates a method to do something, a check on whether it is possible to do it at the moment , and a notification of when that state changes. It does this in a way so that one can bind a control like a Button or MenuItem to a command, and the control will execute the command when clicked and enable and disable itself according to whether the command is available.

WPF supports this infrastructure through two APIs: CommandBindings, which can be defined in XAML, but can only call methods defined in the XAML’s code-behind file, and the ICommand-Interface, which can be implemented in ViewModel classes, and supports creating properties in the ViewModel which allow to bind a control to a command.

For the first approach, I don’t like that I have to write code in the code-behind file. Defining a command is a routine thing, so I’d like to do it in XAML.

For the second approach, I don’t like that I have to implement an interface, and create kind of standard boilerplate glue-code in order to be able to make the UI call a method in the model. If possible, I want to do all the gluing that is necessary in XAML only.

What would an ideal solution look like? I’d like to have a way to just take any normal method in the ViewModel, bind a command to it (that would be a RoutedCommand object, like with the CommandBinding approach), and assign the RoutedCommand to any Control that supports commands.

In the post about the Sticky Command Design Pattern, I have already demonstrated how one can set up CommandBindings through attached properties. Now, I want to generalize this approach into a commanding framework.

A Reusable Sticky Command

The first element is a reusable component which ties a RoutedCommand and its implementation together. Basically, it contains the same information as a CommandBinding, and in fact it is just a wrapper and adapter which is responsible for creating CommandBindings while overcoming their limitations.

So, on the one hand, the Command class defines the usual Attach and Detach methods for Sticky Components:

Protected Overrides Sub Attach(ByVal sender As Object)
    If SupportsCanExecute Or UseValidation Then
        StickyComponentManager.Attach(sender, Command, AddressOf OnExecuted, AddressOf OnCanExecute, Preview)
    Else
        StickyComponentManager.Attach(sender, Command, AddressOf OnExecuted, Nothing, Preview)
    End If
End Sub

Protected Overrides Sub Detach(ByVal sender As Object)
    StickyComponentManager.Detach(sender, Command)
End Sub

 

Command here is a property which holds a RoutedCommand and which can be configured through XAML. UseValidation and Preview are also properties which can be configured through XAML.

On the other hand, the Command class defines standard implementations for the CommandBinding’s Executed and CanExecute delegates:

Public Overridable Sub Execute(ByVal parameter As Object)

End Sub

Protected Overridable ReadOnly Property SupportsCanExecute() As Boolean
    Get
        Return False
    End Get
End Property

Public Overridable Function CanExecute(ByVal sender As Object, ByVal parameter As Object) As Boolean
    Return CanExecute(parameter)
End Function

Public Overridable Function CanExecute(ByVal parameter) As Boolean
    Return True
End Function

Private Sub OnExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Exception = Nothing
    Status = WorkingMessage
    Dim a As Func(Of Object, Object, Exception) = AddressOf Me.DoExecute
    Dim op As System.Windows.Threading.DispatcherOperation = Me.Dispatcher.BeginInvoke(a, Windows.Threading.DispatcherPriority.Background, sender, e.Parameter)
    op.Wait()
    Dim success As Boolean = (op.Result Is Nothing)
    If success Then
        Status = ReadyMessage
    Else
        Dim ex As Exception = op.Result
        Status = ex.Message
        Exception = ex
    End If
End Sub

Private Function DoExecute(ByVal sender As Object, ByVal parameter As Object) As Exception
    Dim result As Exception = Nothing
    Try
        Execute(sender, parameter)
    Catch ex As Exception
        result = ex
    End Try
    Return result
End Function

Private Sub OnCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    Try
        e.CanExecute = True
        If SupportsCanExecute Then
            e.CanExecute = CanExecute(sender, e.Parameter)
        End If
        If e.CanExecute And UseValidation Then
            e.CanExecute = Not System.Windows.Controls.Validation.GetHasError(sender)
        End If
        e.Handled = Handled
    Catch
        e.Handled = False
    End Try
End Sub

 

This code contains some details which I am going to cover in later posts. The important thing for now is: there are standard event handlers which can be attached to a CommandBinding, and their real work is done in the overridable Execute and CanExecute methods. These methods are supposed to be implemented by derived classes.

Binding to Methods

So far, we have an object which can create a CommandBinding by attaching itself to a Control. The next step is to bind a method of our ViewModel to this object.

For this purpose, I created a subclass of Command, ActionCommand, which defines a property of type Action, i.e. a Delegate for a method that doesn’t take parameters and doesn’t return a result.

Public Shared ReadOnly ExecutedActionProperty As DependencyProperty = DependencyProperty.Register("ExecutedAction", GetType(Action), GetType(ActionCommand))
Public Property ExecutedAction As Action
    Get
        Return GetValue(ExecutedActionProperty)
    End Get
    Set(ByVal value As Action)
        SetValue(ExecutedActionProperty, value)
    End Set
End Property

Public Overrides Sub Execute(ByVal sender As Object, ByVal parameter As Object)
    If ExecutedAction IsNot Nothing Then
        ExecutedAction.Invoke()
    End If
End Sub

 

The class overrides the Execute method with an implementation that invokes the method the ExecutedAction delegate is bound to.

In order to be able to bind a value to the ExecutedAction delegate, I created the property as a DependencyProperty. Also, I derived the base class for my Command from Freezable. Freezables are special objects that are optimized for use as resources. What makes them extremely useful in our case is that they inherit the DataContext of the FrameworkElement in whose Resources section they are defined. The DataContext will be our ViewModel object. So, the only thing lacking is a way to create a Delegate for one of its methods through a Binding.

The solution here is a converter. The special converter we use in this case will accept the name of the method as ConverterParameter. Then it will inspect the type of the bound property and the type of the bound object in order to create a Delegate with a matching signature.  This is its code:

Public Class ActionConverter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If value Is Nothing OrElse parameter Is Nothing OrElse Not TypeOf parameter Is String OrElse Not GetType([Delegate]).IsAssignableFrom(targetType) Then
            Return Nothing
        Else
            Return WPFGlue.Framework.StickyComponentManager.CreateActionDelegate(value, parameter, targetType)
        End If
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New NotSupportedException()
    End Function
End Class

 

And the method which creates the Delegate:

Public Shared Function CreateActionDelegate(ByVal target As Object, ByVal methodName As String, ByVal actionType As Type) As [Delegate]
    Dim result As [Delegate] = Nothing

    If target IsNot Nothing AndAlso Not String.IsNullOrEmpty(methodName) AndAlso GetType([Delegate]).IsAssignableFrom(actionType) Then
        Dim targetType As System.Type = target.GetType
        Dim methodInfo As System.Reflection.MethodInfo
        methodInfo = targetType.GetMethod(methodName, actionType.GetGenericArguments)
        If methodInfo IsNot Nothing Then
            result = [Delegate].CreateDelegate(actionType, target, methodInfo, False)
        End If
    End If

    Return result

End Function

 

Gluing it Together in XAML

While this looks quite complex, the good news is that one needs to create this kind of class only once, and from then on can use it to bind to method in XAML only. This is how one would define a Command which binds to a method in the ViewModel:

<c:ActionCommand Command="Open" ExecutedAction="{Binding Converter={StaticResource actionConverter}, ConverterParameter=Open}"/>

 

In this case, the we use the predefined Open RoutedCommand, and bind it to a method called Open (as specified by the ConverterParameter in the Binding) on the current DataContext. Instead of the predefined Open command, we could use any RoutedCommand, even those defined in our own application.

We connect the command to a Control using a Sticky Property which the commanding framework defines:

<StackPanel c:Commanding.CommandSet="{StaticResource commands}">
    <Button Command="Open">Open</Button>

 

As you can see, the Button which triggers the command is completely unaware of its implementation: it just knows that it should work with the Open predefined command, and the rest works through the WPF RoutedCommand infrastructure. This means that you can also take advantage of command routing, like having a MenuItem triggering the same command on different controls depending on which control has keyboard focus. This is more than one gets using the traditional MVVM RelayCommand approach.

Conclusion

Even though it is a bit of work, it is possible to create reusable components which allow it to bind a RoutedCommand to a method of a ViewModel using XAML only. With this technique, it is possible to take advantage of WPF command routing, while at the same time the amount of glue code in the ViewModel is reduced.

You can find the full code and an example project, CommandingExample, in the WPFGluePublished download through the Downloads page.

In posts to come I will cover more details of the WPFGlue Commanding framework, like executing commands on background threads, displaying status and exception messages, or defining CommandSets in order to attach several commands in one line of XAML.

Advertisements

Commands and InputGestures

November 24, 2009 2 comments

When developing applications with the MVVM design pattern, usually the actions a user can take are expressed through commands, objects that implement the ICommand interface, that are exposed as properties on the ViewModel. WPF supports binding to commands in MenuItems and Buttons, so this seems like a convenient and clean way to connect UI and ViewModel.

However, in WPF, commands can do more than just call a method on a ViewModel. WPF supports command routing, i.e. the ability to redirect a command to a specific implementation that might be connected to the control that has keyboard focus at the moment. Also, WPF supports binding InputGestures like mouse clicks or keyboard shortcuts to commands. Finally, WPF defines a set of standard application commands, which can  be used to define standard behaviour of controls independent of the final application which uses them. E.g. there is a Paste command that is mapped by default to the CTRL+V keyboard shortcut, and can be handled by TextBoxes and RichTextBoxes with built-in behaviour, so that an application using a RichTextBox offers this behaviour by default.

In order to take advantage of these WPF features, the following piece of WPFGlue defines a class that maps an ICommand implementation from a ViewModel to a WPF RoutedUICommand, thus being able to use command routing and default KeyGesture bindings:

Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.Register("Command", GetType(RoutedCommand), GetType(CommandSetCommandBase))
Public Property Command() As RoutedCommand
    Get
        Return GetValue(CommandProperty)
    End Get
    Set(ByVal value As RoutedCommand)
        SetValue(CommandProperty, value)
    End Set
End Property

Public Shared ReadOnly ImplementationProperty As DependencyProperty = DependencyProperty.Register("Implementation", GetType(ICommand), GetType(CommandSetCommand))
Public Property Implementation() As ICommand
    Get
        Return GetValue(ImplementationProperty)
    End Get
    Set(ByVal value As ICommand)
        SetValue(ImplementationProperty, value)
    End Set
End Property

Protected Overrides Sub OnCanExecute(ByVal sender As Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)
    If Implementation Is Nothing Then
        e.Handled = False
    Else
        e.CanExecute = Implementation.CanExecute(e.Parameter)
        e.Handled = True
    End If
End Sub

Protected Overrides Sub OnExecuted(ByVal sender As Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)
    If Implementation Is Nothing Then
        e.Handled = False
    Else
        Implementation.Execute(e.Parameter)
        e.Handled = True
    End If
End Sub

Private Sub OnCanExecuteChanged(ByVal sender As Object, ByVal e As EventArgs)
    If sender Is Implementation Then
        CommandManager.InvalidateRequerySuggested()
    End If
End Sub

Public Overrides Sub Attach(ByVal target As System.Windows.FrameworkElement)
    SetCommandProperties()
    If Implementation IsNot Nothing Then
        MyBase.Attach(target)
    End If
End Sub

 

This code is supposed to be used with the Sticky Command pattern. I will publish the complete class and the ViewModelKit it belongs to at a later stage. For now, it may be enough to see that the handlers for the RoutedCommand’s events delegate to the ICommand’s implementation.

In XAML, the class would be used like this:

<Window.Resources>
    <c:CommandSetCommand x:Key="OpenCommand" Command="Open" Implementation="{Binding Source={StaticResource FileManager}, Path=OpenCommand}"/>
</Window.Resources>

What is the advantage? Now, the ViewModel’s OpenCommand is connected to the semantics of the ApplicationCommands.Open command, it is available through this command’s keyboard shortcut, and it can participate in command routing, which basically means that every input element on the page that can receive keyboard input or has a Command property can trigger this command without more bindings, like the following MenuItem:

<MenuItem Command="Open"/>

 

Because ApplicationCommands.Open is a RoutedUICommand and has default values for its Text and InputGestures properties, this will display like this (on a computer with German Windows):

OpenMenuItemScreenShot

But this is not all.

Changing Default Keyboard Shortcuts

In the Attach method of the CommandSetCommand, a SetCommandProperties method is called. It is the job of this method to change properties on the RoutedCommand which can be configured through more properties of the CommandSetCommand:

Protected Overridable Sub SetCommandProperties()
    If Command IsNot Nothing Then
        If KeyGesture IsNot Nothing Then
            Command.InputGestures.Clear()
            Command.InputGestures.Add(KeyGesture)
        End If
        If TypeOf Command Is RoutedUICommand And Not String.IsNullOrEmpty(Text) Then
            DirectCast(Command, RoutedUICommand).Text = Text
        End If
    End If
End Sub

 

This is enough to enable overriding of the default keyboard shortcuts of the commands defined in ApplicationCommands at load time, when the command is bound to the Window or other Framework element. If you want to be able to change the keyboard shortcut at runtime, you need to do something when KeyGesture is changed:

Public Shared ReadOnly KeyGestureProperty As DependencyProperty = DependencyProperty.Register("KeyGesture", GetType(KeyGesture), GetType(CommandSetCommandBase), New PropertyMetadata(AddressOf onkeygesturechanged))
Public Property KeyGesture() As KeyGesture
    Get
        Return GetValue(KeyGestureProperty)
    End Get
    Set(ByVal value As KeyGesture)
        SetValue(KeyGestureProperty, value)
    End Set
End Property
Private Shared Sub OnKeyGestureChanged(ByVal d As CommandSetCommandBase, ByVal e As DependencyPropertyChangedEventArgs)
    Dim c As RoutedCommand = TryCast(d.Command, RoutedCommand)
    If c IsNot Nothing Then
        c.InputGestures.Clear()
        If e.NewValue IsNot Nothing Then
            c.InputGestures.Add(e.NewValue)
        End If
        d.SetValue(CommandProperty, Nothing)
        d.SetValue(CommandProperty, c)
    End If
End Sub

This code will update the RoutedCommand’s InputGestures and will connect it to the new keyboard shortcut whenever KeyGesture is changed, which allows it to redefine keyboard shortcuts at runtime, both for standard application commands and for custom RoutedCommands.

This would be the right place to say that explicit KeyBindings on elements within a page should be avoided, because their scope is always local to the element on which they are defined. If this is not explicitly intended, it is easier to maintain consistent keyboard bindings throughout the application if the RoutedCommands InputGestures are used.

However, there is one problem: MenuItems use their command’s InputGestures collection to display a keyboard shortcut when they first load, but they don’t monitor it for changes. Therefore, the OnKeyGestureChanged method sets the Command property to nothing and then back to its old value. This causes MenuItems which are binding their Command property to the CommandSetCommand’s Command property to update both their Header and their InputGestureText properties. The MenuItem would look like this to support this functionality:

<MenuItem Command="{Binding Source={StaticResource
    PasteCommand}, Path=Command}"/>

 

Overriding Class CommandBindings

Have you ever tried to override the default paste command in a RichTextBox? It’s not so easy to get around this behaviour if you want to use the keyboard shortcut CTRL+V for something else, or want to do checks on the clipboard contents before you paste them. Clearing the InputGestures collection of ApplicationCommands.Paste does the trick, without resorting to dirty tricks like intercepting keystrokes in the PreviewKeyDown event. Once you have got the class CommandBinding out of the way, you can define a custom RoutedCommand, its CommandBindings, and InputBindings with the original Ctrl+V keystroke wherever you need them.

To Be Continued…

In posts to come, I will introduce the CommandSet ViewModelKit, which will allow it to define the complete set of commands for an application in one place, including the functionality introduced here, and other things like connecting RoutedCommands directly to method calls in the ViewModel.