Archive

Posts Tagged ‘Commanding’

Commanding II: Beyond Calling Methods

June 30, 2012 Leave a comment

In the last post about Commanding I described a basic technique for binding RoutedCommands to method calls in the ViewModel. Today I want to introduce some refinements which make the user experience more complete: Status messages for running commands, localizing exception messages, and how to have one status bar display the status messages of multiple commands.

The complete example code for this post is contained in the CommandingExample project which is part of the WPFGluePublished download available through the downloads page.

Status Messages

If users click a button or a menu item in order to execute a command, they expect instant feedback on whether the command is executing. If the result is not visible immediately, they will appreciate a status message somewhere in the UI which tells them that the command has been executed, and also a message that tells them when it has finished.

In order to accommodate this scenario, we will add three more properties to our Command base class:

Public Shared ReadOnly ReadyMessageProperty As DependencyProperty = DependencyProperty.Register("ReadyMessage", GetType(String), GetType(Command))
Public Property ReadyMessage As String
    Get
        Return GetValue(ReadyMessageProperty)
    End Get
    Set(ByVal value As String)
        SetValue(ReadyMessageProperty, value)
    End Set
End Property

Public Shared ReadOnly WorkingMessageProperty As DependencyProperty = DependencyProperty.Register("WorkingMessage", GetType(String), GetType(Command))
Public Property WorkingMessage As String
    Get
        Return GetValue(WorkingMessageProperty)
    End Get
    Set(ByVal value As String)
        SetValue(WorkingMessageProperty, value)
    End Set
End Property

Private Shared ReadOnly StatusPropertyKey As DependencyPropertyKey = DependencyProperty.RegisterReadOnly("Status", GetType(String), GetType(Command), New PropertyMetadata())
Public Shared ReadOnly StatusProperty As DependencyProperty = StatusPropertyKey.DependencyProperty
Public Property Status As String
    Get
        Return GetValue(StatusProperty)
    End Get
    Private Set(ByVal value As String)
        SetValue(StatusPropertyKey, value)
    End Set
End Property

 

WorkingMessage and ReadyMessage are supposed to be configured by the developer. They are the messages which are shown to the user when the command starts executing and when it is finished. Status is a readonly property. This is where the messages arrive. The idea is to set Status to the WorkingMessage before one calls the ViewModel, and to the ReadyMessage when the ViewModel method has returned.

However, simply changing the property won’t get the message displayed: data binding, notifying of changes that require a new rendering, and executing the ViewModel method all happen on the same thread, so the changed status messages will not be processed until when the ViewModel method has returned. In order to give the UI time to render the WorkingMessage, we need to use an asynchronous pattern including the Dispatcher:

Protected Overridable Sub OnExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Exception = Nothing
    IsRunning = True
    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
    IsRunning = False
End Sub

Protected Overridable 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

 

The OnExecuted method begins with setting some properties that indicate that the command is about to execute. The status message is one of them. Then, it calls Dispatcher.Invoke with the address of a wrapper function, DoExecute, which calls the ViewModel method. The Dispatcher.Invoke call includes a DispatcherPriority.Background parameter. This tells the Dispatcher that it should finish all its usual housekeeping chores, among others displaying our status message, before it calls the DoExecute method. Dispatcher.Invoke returns a DispatcherOperation object. Using this object, we first call Wait in order to yield control to the Dispatcher. The Dispatcher will then take care of everything that is more important (again including displaying our status message), and finally call our method. When the method has returned, the Wait method will return, and the DispatcherOperation’s Result property will contain the return value of DoExecute.

Localizing Exception Messages

The main purpose of DoExecute is to catch any exceptions that might be thrown by the ViewModel method before they reach the Dispatcher, and to expose them through the Command’s Exception property. By default, in case of an exception, the Status property will return the Exception’s Message.

However, this message might not be suitable for display to the users: it might be in a language different form the UI language, or it might be formulated in a way that makes sense to developers, but not to end users who lack the necessary technical background.

So, the UI should be able to receive Exceptions and to translate them into localized messages. Since there might be important information embedded in the original exception message, there should also be a way to extract this information and to insert it into the translated message.

The idea here is to create a converter which receives an Exception, and can be configured in order to create a String message.

The converter looks like this:

Public Class ErrorMessageConverter
    Inherits Freezable
    Implements IValueConverter

    Public Sub New()
        MyBase.New()
        ErrorMessages = New ErrorMessageCollection
    End Sub

    Protected Overrides Function CreateInstanceCore() As System.Windows.Freezable
        Return New ErrorMessageConverter
    End Function

    Private Shared ReadOnly ErrorMessagesPropertyKey As DependencyPropertyKey = DependencyProperty.RegisterReadOnly("ErrorMessages", GetType(ErrorMessageCollection), GetType(ErrorMessageConverter), New PropertyMetadata())
    Public Shared ReadOnly ErrorMessagesProperty As DependencyProperty = ErrorMessagesPropertyKey.DependencyProperty
    Public Property ErrorMessages As ErrorMessageCollection
        Get
            Return GetValue(ErrorMessagesProperty)
        End Get
        Private Set(ByVal value As ErrorMessageCollection)
            SetValue(ErrorMessagesPropertyKey, value)
        End Set
    End Property

    Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        Dim result As String = Nothing
        If value IsNot Nothing Then
            For Each em As ErrorMessage In ErrorMessages
                result = em.GetMessage(value)
                If result IsNot Nothing Then
                    Exit For
                End If
            Next
        End If

        Return result
    End Function

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

Public Class ErrorMessage
    Inherits Freezable

    Protected Overrides Function CreateInstanceCore() As System.Windows.Freezable
        Return New ErrorMessage
    End Function

    Public Shared ReadOnly ExceptionProperty As DependencyProperty = DependencyProperty.Register("Exception", GetType(String), GetType(ErrorMessage), New PropertyMetadata("Exception"))
    Public Property Exception As String
        Get
            Return GetValue(ExceptionProperty)
        End Get
        Set(ByVal value As String)
            SetValue(ExceptionProperty, value)
        End Set
    End Property

    Private Shared ReadOnly MessageParserProperty As DependencyProperty = DependencyProperty.Register("MessageParser", GetType(System.Text.RegularExpressions.Regex), GetType(ErrorMessage))
    Private Property MessageParser As System.Text.RegularExpressions.Regex
        Get
            Return GetValue(MessageParserProperty)
        End Get
        Set(ByVal value As System.Text.RegularExpressions.Regex)
            SetValue(MessageParserProperty, value)
        End Set
    End Property

    Public Shared ReadOnly MessageRegExProperty As DependencyProperty = DependencyProperty.Register("MessageRegEx", GetType(String), GetType(ErrorMessage), New PropertyMetadata(AddressOf OnMessageRegExChanged))
    Public Property MessageRegEx As String
        Get
            Return GetValue(MessageRegExProperty)
        End Get
        Set(ByVal value As String)
            SetValue(MessageRegExProperty, value)
        End Set
    End Property
    Private Shared Sub OnMessageRegExChanged(ByVal d As ErrorMessage, ByVal e As DependencyPropertyChangedEventArgs)
        Try
            d.MessageParser = New System.Text.RegularExpressions.Regex(e.NewValue)
        Catch ex As Exception
            d.MessageParser = Nothing
        End Try
    End Sub

    Public Shared ReadOnly FormatProperty As DependencyProperty = DependencyProperty.Register("Format", GetType(String), GetType(ErrorMessage))
    Public Property Format As String
        Get
            Return GetValue(FormatProperty)
        End Get
        Set(ByVal value As String)
            SetValue(FormatProperty, value)
        End Set
    End Property

    Public Function IsMatch(ByVal ex As Exception) As Boolean
        Dim result As Boolean = ex IsNot Nothing
        If result Then
            result = ex.GetType.FullName.EndsWith(Exception)
        End If
        If result And MessageParser IsNot Nothing Then
            result = MessageParser.IsMatch(ex.Message)
        End If
        Return result
    End Function

    Public Function GetMessage(ByVal ex As Exception) As String
        Dim result As String = Nothing
        If IsMatch(ex) Then
            If MessageParser Is Nothing Then
                result = Format
            Else
                Dim m As System.Text.RegularExpressions.Match = MessageParser.Match(ex.Message)
                Dim values() As String = (From g As System.Text.RegularExpressions.Group In m.Groups
                                          Select g.Value).ToArray
                result = String.Format(Format, values)
            End If
        End If
        Return result
    End Function
End Class

Public Class ErrorMessageCollection
    Inherits FreezableCollection(Of ErrorMessage)
End Class

 

The most important element here is the ErrorMessage class. The ErrorMessage class has the following properties:

  • Exception: The type name of the Exception class, or at least the last part of it.
  • MessageRegEx: optionally, a standard .Net regular expression which must match the original exception message. It can contain grouping constructs (i.e. parentheses) in order to extract information from the exception message.
  • Format: the String into which the exception message should be translated. This string can contain format placeholders (like {1}) for the information extracted from the original exception message.
    If the converter receives an exception, it iterates the configured ErrorMessage objects in order to find the first one that matches the exception. Then it calls GetMessage on this object in order to return a message, and stops the iteration. Since the Exception property only matches the last part of the exception’s type name, can configure a catchall using Exception=”Exception” on the last ErrorMessage object in your ErrorMessageConverter.

Here an example for how to configure an ErrorMessageConverter:

<l:ErrorMessageConverter x:Key="errorMessageTranslator">
    <l:ErrorMessage Exception="InvalidOperationException"
                    MessageRegEx="This operation is ([^!]*)!" Format="Don't try {1} operations!"/>
</l:ErrorMessageConverter>

 

Notice that the regular expression’s first capture group has index 1, while index 0 will return the complete match.

Creating a Status Line

While we can now display status messages for one command, normally one doesn’t want one status control for each command that might be executed through the UI. Instead, normally there is a status line that displays status of any command that might be executing at the moment.

So, what we need is kind of a funnel which collects status messages from all commands and always returns the most recent message. The component which does this is called FunnelConverter:

Public Class FunnelConverter
    Implements IMultiValueConverter

    Private _Values() As Object
    Private _Result As Object = Nothing

    Public Function Convert(values() As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
        Dim result As Object = Nothing

        If values Is Nothing OrElse values.Length = 0 Then
            _Values = Nothing
        Else
            Dim i As Integer = 0
            Do While _Values IsNot Nothing AndAlso (i < _Values.Length And i < values.Length And result Is Nothing)
                If _Values(i) Is Nothing OrElse Not _Values(i).Equals(values(i)) Then
                    result = values(i)
                End If
                i += 1
            Loop
            Do While result Is Nothing And i < values.Length
                result = values(i)
                i += 1
            Loop
            _Values = values.ToArray
        End If

        If result IsNot Nothing Then
            _Result = result
        End If
        Return _Result

    End Function

    Public Function ConvertBack(value As Object, targetTypes() As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
        Throw New NotSupportedException
    End Function

 

This class is a MultiBindingConverter. It is supposed to be used with a MultiBinding, which accepts an array of bindings, and returns a new value whenever one of the bound values changes. The FunnelConverter stores a copy of the list of values it receives, and displays the first value that has changed, or the first value which is not nothing if there are no values, yet. Since all command status changes are executed on the UI thread, only one value will change at any given time, and no changes will be lost. Since this converter stores the results of the last invocation, one has to make sure that each converter instance is only used in one binding; so, the most natural solution is to not declare the converter as a resource, but to instantiate it inline in the MultiBinding tag:

<StatusBarItem>
    <StatusBarItem.Content>
        <MultiBinding>
            <MultiBinding.Converter>
                <c:FunnelConverter/>
            </MultiBinding.Converter>
            <Binding Source="{StaticResource commands}" Path="Commands[Open].Status"/>
            <Binding Source="{StaticResource commands}" Path="Commands[Find].Status"/>
            <Binding Source="{StaticResource commands}" Path="Commands[Print].Status"/>
            <Binding Source="{StaticResource commands}" Path="Commands[Stop].Status"/>
            <Binding Source="{StaticResource commands}" Path="Commands[Print].Exception" Converter="{StaticResource errorMessageTranslator}"/>
        </MultiBinding>
    </StatusBarItem.Content>
</StatusBarItem>

Conclusion

Using the techniques described in this post, one can keep the users informed about what the application is currently doing, and about any exceptions that might occur in ViewModel code. Since all messages which are configured in XAML can be localized, using the classes described here is also a step towards globalizing the application.

Another topic I want to cover in a later post is how to have commands executed asynchronously in the background, either on a background thread or by executing a number of short operations on the Dispatcher.

Advertisements

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.

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.

WPFGlue Categories

November 7, 2009 Leave a comment

I’d like to classify WPFGlue components according to what they do in the application. So far, the following categories came out:

Navigation

The WPF navigation framework is just great. Especially the journal: you get the complete forwards and backwards navigation for free, the journal remembers the user input on the page, and this works even when pages are freed in order to save memory.

However, WPF navigation stops just short of being really sticky. Selecting an object on one page and passing it forward to another page is supposed to be done through code behind, and the journal serializes objects instead of hold references to them, which is in itself a wise thing to do in order to not keep these objects from being freed, but makes it difficult to return to the same object instance if one goes back to a page. Also, I don’t like the suggestion in the WPF documentation that the navigation topology should be defined through pages referencing each other through hyperlinks. In my opinion, the pages should work stand-alone as much as possible, and there should be a central definition for the navigation topology, defining the structure of the application and mapping pages to the appropriate places in the object model.

WPFGlue supports navigation through a sticky GoToPage CommandBinding which takes an object parameter and passes it to the DataContext of the new page, a sticky Session property which can hold WeakReferences to business objects while the journal stores pointers to these references, and a ViewModelKit for defining a navigation menu.

Localization

The need for localization is immediately obvious for every programmer whose mother tongue is not English. Surprisingly, in WPF localization seems to have been added more or less as an afterthought. While Visual Studio supports localization very nicely for Windows Forms applications, getting at resources contained in satellite assemblies is not straight forward in WPF, and even something as basic as detecting the system language and making it the default culture for display and formatting in the application requires extra work. I could imagine that in the beginning the creators of WPF thought that people would just write different XAML pages for different languages, as is done in HTML, and that the need for localizing XAML only arose after the power of XAML as a programming language became more apparent, and programming elements became more predominant on XAML pages than clear text.

In the Localization category, WPFGlue comes with some sticky MarkupExtensions which make it easier to localize an application. These MarkupExtensions get their values from standard .Net localized resources, so no need for LocBAML or third party tools there. Other MarkupExtensions can be used to set WPF elements’ Language property to the CurrentCulture or CurrentUICulture.

Commanding

The commanding support in WPF is ingenious. It allows to implement some functionality once and to attach it to all the places where it is applicable, through RoutedCommands and CommandBindings. At the same time, commands decouple the traditional menu and toolbar from the application’s object model, making for great reusable code. Fabulously sticky stuff!

Commands are also the way ViewModels in the MVVM pattern expose their functionality to the user interface. The only unsticky thing about them is that CommandBindings support adding handler routines only through code, either code behind or explicitly. Also, there is no native general way of wrapping a method call into a command.

In the Commanding category, WPFGlue should support mapping commands to method calls, and also direct commands (ICommand implementations that are exposed by a ViewModel) to routed commands which can be handled centrally. There should be a way to set up a CommandSet and to connect it to a page, thus being able to define the available commands and their handlers in a central location. Also, the Commanding category contains a CommandGroup, which executes a series of commands in one go.

Validation

MVVM developers seem to avoid the validation features of WPF in favour of validation through the ViewModel or business model, exposed through the IDataErrorInfo interface. I don’t really understand why.

First of all, providing error messages to a user is not the job of a business object. The business object shouldn’t know whether it is used by an interactive user, a background service, or a batch job. In addition to that, in order to be able to provide meaningful error messages, the business object model would have to localize its error messages, and I wouldn’t want to burden my business objects with a job like that.

Second, while the business model knows what itself can do, it has no idea about what the developer of an application that reuses it may want to do. It would be perfectly justified to forbid certain legal property values because they are outside the scope of the current application. In this case, the ViewModel would have to constrain the values it allows for a certain property before it passes them on to the business object model.

So why not let the ViewModel do the job? I agree, the ViewModel is responsible. However, I wouldn’t use IDataErrorInfo, because that forces me to re-implement a lot of functionality that is already present in the WPF validation model. For example, resetting the values of an object to their pre-edit state before they are committed while still being able to provide instant validation feedback is really easy with ValidationRules and a BindingGroup, but requires a lot of effort, including ongoing maintenance effort, when done in a ViewModel specialized for a certain business model.

So, what I’d do instead is create a ViewModelKit that allows one to define a group of ValidationRules, which may or may not be dependent on validation functions in the business object model, and to allow setting these ValidationRules to a BindingGroup using a sticky property.

In addition to that, one would find workarounds for some minor problems with Validation.ErrorTemplate and ways to inform the ViewModel of validation results in the Validation category of WPFGlue.

Application Services

Application services would be things like forwarding application lifetime events to the ViewModel or providing services that are available in the operating system environment without creating a dependency from the ViewModel to the View.

Examples for this are a MessageBox that is invoked through sticky properties, a sticky StartupCommand property, a markup extension that allows to include application settings in the user interface, or context sensitive help through sticky properties.

Common Tasks

Common tasks would be jobs that recur in user interfaces, so that it makes sense to implement them in sticky ViewModels.

Examples would be a FileManager that provides support for the usual File menu commands for any type of document, a ListEditor that has commands for moving, adding and deleting items in collections implementing the standard collection interfaces, a ListPager that divides a collection into pages of a given length, and the like.

Conclusion

This is only a tentative list. The only category that will definitely survive is Common Tasks, since the others hopefully will be replaced by native support through WPF as the framework develops and the gaps between business object model and user interface vanish, so that there is no more need to fill them with glue.