Home > Commanding > Commanding: Binding Controls to Methods

Commanding: Binding Controls to Methods


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.

About these ads
  1. No comments yet.
  1. No trackbacks yet.

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: