Using .Net Localized String Resources in WPF

December 5, 2009 hbarck Leave a comment

When localizing an application, the most obvious feature is that labels, texts and pictures in the UI are displayed in another language. This is done through localized string resources, and .Net has a whole framework for handling these. In Windows Forms, this framework is quite well supported by Visual Studio. However, in WPF this support is considerably lacking. This is about how you can use them anyway.

Creating Localized Resources

You can edit the localized resources of your application if you choose the Resources tab in the My Project editor. However, these are the resources for the neutral or invariant culture, the .Net culture description that is not connected to any natural language. These resources are embedded into the executable of your application. If you want culture specific resources which are compiled into .Net satellite DLLs which you can distribute with your application in order to let it support multiple languages, you have to create them manually. Unfortunately, the documentation on this is not very clear. In my tests, I did the following:

  • Close Visual Studio
  • Go to the My Project / MyExtensions folder in the project directory
  • Copy the file Resources.resx and rename the copy to the correct name for the culture, e.g. Resources.de-DE.resx for German or Resources.en-US.resx for US English
  • Open the Project again in Visual Studio
  • Select Add Existing Item and add the file to the project.
    After you have done that, the Resource designer will display if you open the file from Solution Explorer. Just make sure that the Access Modifier combo box on top says “No Code Generation”, otherwise you might get error messages about conflicting class names on compilation.

Since you copied the original resource file, all resources will already be there in the original language; so, if you do this towards the end of the project, you will already have the correct resource keys and their original translations to help you…

The Visual Studio Resource designer was originally intended for Windows Forms. As a result, the image resources it produces (at least at the time of writing, Visual Studio 2008 SP1) are tied to the Windows Forms image types, which are not working with WPF. For this reason, I decided to not support image resources. Instead, one can create bitmap images from URIs which are stored as resource strings, so that they can point to different sources for different languages. These URIs should be pack URIs if you provide the images with your program; pack URIs are explained very nicely here.

Using Localized Resources in Your WPF Pages

WPFGlue offers four different MarkupExtensions for integrating localized resources in XAML. Their names are StringExtension, URIExtension, BitmapImageExtension, and FlowDirectionExtension. StringExtension is the base class. Its ProvideValue method, which is called whenever the XAML that contains the extension is instantiated, looks like this:

Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object
    Dim result As Object = "[" & Key & "]"
    If _ResourceManager Is Nothing Then
        Try
            If Assembly IsNot Nothing AndAlso Not Assembly.FullName.StartsWith("PresentationFramework,") Then
                _ResourceManager = New System.Resources.ResourceManager(BaseName, Assembly)
            End If
        Catch x As Exception
            result = Assembly.FullName
        Finally
        End Try
    End If
    If _ResourceManager IsNot Nothing Then
        Try
            result = _ResourceManager.GetString(Key)
        Finally
        End Try
        If result Is Nothing Then
            result = "[" & Key & "]"
        End If
    End If
    Return result

End Function

It reads strings from the application’s resources; URIExtension converts them into URIs on top of that, and BitmapImageExtension creates a BitmapImage from the URI it looked up in the resource. FlowDirectionExtension checks whether the current UI language is written from right to left, and returns the correct FlowDirection value. All these markup extensions behave like StaticResource. This means that they return their value at the time the XAML is instantiated, and don’t create a binding to the properties they are applied to. It also means that you can use them everywhere in your XAML, not only with DependencyProperties.

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
    <TextBlock Text="{l:String TestString}" />
    <TextBlock Text="{Binding Source={StaticResource Value}}"/>
    <TextBlock Text="{l:Binding Source={StaticResource Value}}"/>
    <Image Stretch="None" Source="{l:BitmapImage FlagURI}"/>
</StackPanel>

So far so good. Using these extensions, you make sure that the users see the application in their system’s language, if you provided the correct resources. But what if you want to change the language at runtime?

This is where it gets sticky. The UILanguage Sticky Property is used to read and set the current UI language, which is the same language the resource manager uses in order to find the resources it wants to display. However, changing this property will not automatically change all the labels. I didn’t think this necessary; in this solution, changes of the UI language will take effect only after you navigate to a different page.

Public Shared ReadOnly UILanguageProperty As DependencyProperty = DependencyProperty.RegisterAttached("UILanguage", GetType(String), GetType(Localization), New PropertyMetadata(Nothing, AddressOf OnUILanguageChanged, AddressOf CoerceUILanguage))
Public Shared Function GetUILanguage(ByVal d As DependencyObject) As String
    Return d.GetValue(UILanguageProperty)
End Function
Public Shared Sub SetUILanguage(ByVal d As DependencyObject, ByVal value As String)
    d.SetValue(UILanguageProperty, value)
End Sub
Private Shared Sub OnUILanguageChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    If e.NewValue IsNot Nothing Then
        System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(CStr(e.NewValue))
    End If
End Sub
Private Shared Function CoerceUILanguage(ByVal d As DependencyObject, ByVal baseValue As Object) As Object
    Dim result As String = baseValue
    If baseValue Is Nothing Then
        result = System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag
    End If
    Return result
End Function

Notice that setting this property to {x:Null} explicitly will coerce its value to the current UI language. You can use this to initialize a language selection box to the correct start value.

Setting UILanguage on any element in XAML will change the UI language for the whole application. However, if you want a common and consistent access point for this, consider setting it on the topmost XAML element in your application, i.e. the main Window or NavigationWindow.

Using Resources from Different Assemblies

You can use l:String etc. just out of the box, without having to tell the system where it would find the resources. This is possible because the MarkupExtension uses the type of the current Application object in order to find the assembly of the main program.

In the designer, obviously the main program is Visual Studio, so the markup extension doesn’t know where to look. If it isn’t able to discover the ResourceManager it needs to provide the string it is looking for, or if the key it receives does not have a corresponding resource, it just returns the value of the key itself enclosed in brackets.

However, there is a way to tell it where to look. This has to be there in order to be able to read resources from assemblies other than the main program’s assembly. For example, you could have put all your resources into a special resource assembly, or you could use a library which brings along its own resources.

The most convenient way to make a different assembly the resource lookup source is to assign a type from that assembly to the ResourceReferenceType sticky property. This assumes that the basename for resources in this type’s assembly is “Resources”, as is the default with VB.Net. You can also have finer control setting the ResourceAssembly and ResourceBasename properties, respectively. (n.b. I’m not sure whether the automatic detection of the assembly and basename works the same way in VB and C#).

These settings influence the whole application. This means that as long as they point to a certain place, all resource strings in the application are taken from there. That looks like quite a serious limitation; however, if using a Navigation application, every page would be independent and could easily have its own setting, and if you’d divide your page into regions using containing elements, the behaviour would be consistent as long as every containing element and every data template sets the source for its localized resources explicitly. This is so because the markup extensions access the resource DLL only while the XAML is instantiated, and the set properties retain their values even if the resource assembly changes later on (*).

If you want to preview your resource strings in the designer, you can just set the ResourceReferenceType and UILanguage on the window you are working on to your program’s application class and language, respectively.

<Page x:Class="LanguageSelectionPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="http://wpfglue.wordpress.com/localization"
    xmlns:ex="clr-namespace:LocalizedResourcesExample"
    xmlns:r="clr-namespace:LocalizedResourceAssembly;assembly=LocalizedResourceAssembly"
    Title="LanguageSelectionPage" FlowDirection="{l:FlowDirection}"
      l:Localization.ResourceReferenceType="{x:Type ex:Application}"
      l:Localization.UILanguage="de-DE">

     However, if you want to be able to change the resource assembly and language at runtime, don’t forget to remove these attributes before you build the application.

The LocalizedResourceExample

The example solution you can download from the Downloads page demonstrates how to select the UI language at runtime:

<ComboBox SelectedValue="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
    AncestorType={x:Type NavigationWindow}}, Path=(l:Localization.UILanguage)}"
    ItemsSource="{StaticResource languageList}" DisplayMemberPath="Key"
    SelectedValuePath="Value"/>
</StackPanel>

 

Notice that the ComboBox sets the value of the UILanguage attached property directly on the NavigationWindow that hosts the page. However, you have to navigate away from the page in order to see the changes applied. If you go back to the language selection page, you will see that this page also re-reads its resources and displays differently dependent on what is selected in the language selection box.

The second combobox will select the assembly LocalizedResourceAssembly in order to access its resources. Apart from the resources themselves, this assembly contains only an empty class called “Dummy” which serves as reference class to find the resources. Since the LocalizedResourceAssembly doesn’t contain all the resources defined in the main assembly, you can watch the fallback behaviour for the resource lookup: If the resource isn’t there in the specified language, it is taken from the main culture of the application (which is de-DE German in the example), and if it is not found there, the key of the resource will be displayed. Since this fallback mechanism only works within the same resource assembly, you will see different fallback behaviour if you choose the external resources.

Look out for..

The methods described here help in localizing the descriptive elements of your user interface, and given the resources are already there, everything is controlled completely through XAML. In order to localize value formats, you need some other techniques. They are already contained in the example, but I will explain them in a later post.

Credits

I learned how to write a MarkupExtension from this article by Christian Moser. Even though I changed his approach considerably, I found it extremely helpful, and especially if you want to take changes in the UI Language to take effect immediately, you should look at his example.

 

 

(*) Footnote: In order to make this fool proof, one could make the ResourceReferenceType property inheritable, so that it is copied to all child elements. However, I try to avoid FrameworkPropertyMetadataOptions.Inherit if possible, even if I don’t think that looking around for the correct ResourceManager on every localized string is very expensive in terms of performance.

Localization in WPF

December 5, 2009 hbarck Leave a comment

There are lots of good articles on localizing WPF applications out there, so here it is more a matter of commenting and choosing different approaches than of creating new value. But before I start, I’d like to discuss the jobs of localization a bit, and explain my choices in how to handle them.

Providing a Localized User Interface

The most obvious effect of localization is to provide labels, descriptions and pictures which contain texts in the current user’s language. So, the two bits of information one needs in order to provide this are the user’s language and the source of the texts. In .Net, localized resources are contained in resource DLLs, which are provided together with the main program executable, and are accessed through the ResourceManager class internally, already taking into account the language of the current thread.

Visual Studio supports two ways of editing localized resources: the one is directly through the My Project / Resources editor, the second is indirectly as part of the Windows Forms designer. Unfortunately, there is no such support for WPF. There is a command line tool named LocBAML which is supposed to extract all localizable strings from compiled XAML and to allow to create the localized resource DLLs from it, but this is not very integrated into the development process and requires changes to the XAML itself using UI ids (which are a good thing to have but a nightmare to maintain). So, most examples I found ignore this approach and instead make for a way to make the usual localized resources from the Resource editor available to WPF.

The obvious solution is a MarkupExtension that accepts the name of the resource as a parameter and returns its value. Some of the published components allow to change the language at runtime; I didn’t think this necessary, so my implementation just sets the value, which means you have to close and reopen forms in order to make a language change visible. However, it has the neat little feature of being able to find the ResourceManager of the main program’s assembly by itself, so you don’t have to configure anything in order to be able to use it. Find it explained here.

Formatting and Parsing Values According to Localized Formats

Formatting and parsing values that are displayed to or edited by the user needs to take into account local variations in formatting conventions. 12.456,70 and 12,456.70 are actually the same number, but if you’d ask an American and a German they’d probably disagree on what number that is.

Recognizing these formats has been a job for localization components ever since computers have been invented, so it’s quite well supported in .Net. However, for reasons unknown WPF’s default language for all these purposes is US English, regardless of the system’s settings, let alone the culture of the current thread. In order to change the language for formatting, you have to set the Language attribute. There are some ways to do it; one way one frequently sees is to override its default value to be the system’s culture on application startup, but I find this a little bit harsh, and I think you can’t undo it if you want to change the language at runtime. So I prefer to set the Language attribute on top of the page or window, so that it is inherited by all the controls. In order to get at the language more conveniently, I implemented MarkupExtensions that return the current language or the current UI language, respectively.

There are cases where this behaviour isn’t enough. As I learned recently, even setting the Language property to the CurrentCulture doesn’t make WPF acknowledge changes in date and number formats users might have made in the Regional Settings of their computer. In order to work around this, one would have to use a custom Binding extension, which might be a good idea anyway, since it provides a way to hook all sorts of useful little helpers to bindings without changing the XAML code too much. But actually, I don’t like this solution and hope there will be a fix soon…

A special case of localized formatting is providing translations for Enum values. .Net can parse Enum values from their string equivalents, but unfortunately those string equivalents are hardcoded with the definition of the Enum. Therefore, WPFGlue contains a LocalizedList, which fills two jobs: It works as a Converter, converting the Enum values into localized strings and vice versa, and it can be used as ItemsSource for a ComboBox or other Selector in order to let the user pick from the available values. Its intended way of usage is defining an instance of it in a ResourceDictionary, using an initialization string which maps the invariant string representations to localized counterparts. As an extra, it can produce and parse strings that are combinations of multiple Enum values.

You can find descriptions and examples for these components here.

Switching the Keyboard to Different Languages

Handling different languages with different keyboard layouts within the same document has only recently become a common feature in text processing applications. However, since I frequently have to work with data in different languages, I wouldn’t like to miss this feature.

In WPF, the class responsible for managing different keyboard languages is called InputLanguageManager. It exposes an attached property, InputLanguage, that accepts a culture identifier and switches the keyboard to the defined layout for this language, if one is installed. Unfortunately, this only works when the control receives focus. So, if you are typing something in a TextBox and change the InputLanguage while you are typing, e.g. through a keyboard shortcut, this change doesn’t become active unless the keyboard focus leaves the TextBox and returns again.

Also, in principle you can define different languages for paragraphs in a RichTextBox by setting the InputLanguageManager.InputLanguage on the Paragraph elements in the RichTextBoxes FlowDocument, but the keyboard doesn’t automatically change to the appropriate layout when the selection enters a section in a different language. This feature is extremely useful, so I decided to create a Sticky Property which allows to change the keyboard language effective immediately, and a Sticky Behaviour that sets the current input language according to what is set in this attached property on the FrameworkContentElements inside a RichTextBox’s FlowDocument. This property is explained here. Recently I found a post on Connect which suggests that there might be a native solution for this problem in WPF 4.

Links

Christian Moser describes a good example for localization by MarkupExtension here.

Categories: Localization Tags: ,

Forwarding the Result of WPF Validation in MVVM

December 3, 2009 hbarck Leave a comment

A question that is frequently raised in connection with MVVM design is how one can access the result of WPF Validation (Validation.HasError) from the ViewModel. There is a way, but before I start explaining it, I’d like to ask an important question:

Why Would You Want to Do it?

Normally you would say: actually the ViewModel is responsible for validating the user input. So, if the input is invalid, the ViewModel should already know about it, isn’t it? Well, partially. There are some cases were the user input never reaches the ViewModel: when what the user entered cannot be converted to the correct data type, e.g. with date values or numbers. Catching these conversion errors in the ViewModel requires that you wrap each and every property of the data model that has one of these data types into a ViewModel property of type string, which bloats the ViewModel unnecessarily, and creates exactly the kind of glue code one tries to avoid by using the MVVM pattern in the first place.

One possibility to work around this is to create general, reusable ValidationRules for type checking and assign them to the Bindings. But then the ViewModel would have to know about the results of these checks in order to disable or enable commands or make other decisions based on the validity of its data. This would be the use case for the following technique:

How to Do it

The trick is to create a Sticky Property (which is a special case of an attached property) which, different from Validation.HasError, allows data bindings with direction to the source:

Public Shared ReadOnly MVVMHasErrorProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("MVVMHasError", GetType(Boolean), GetType(Window1), _
    New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, _
    Nothing, AddressOf CoerceMVVMHasError))
Public Shared Function GetMVVMHasError(ByVal d As DependencyObject) As Boolean
    Return d.GetValue(MVVMHasErrorProperty)
End Function
Public Shared Sub SetMVVMHasError(ByVal d As DependencyObject, ByVal value As Boolean)
    d.SetValue(MVVMHasErrorProperty, value)
End Sub

 

In the metadata of this property, I set a flag that tells WPF that this property expects to forward its value to the source, so we don’t have to set the Binding Mode on the bindings in XAML.

In order to reflect the value of Validation.HasError, we need to set up an event that fires whenever the value of this property changes. In order to find out that we need this event, the CoerceValue callback of the property checks whether the property is data bound:

Private Shared Function CoerceMVVMHasError(ByVal d As DependencyObject, ByVal baseValue As Object) As Object
    Dim result As Boolean = baseValue
    If BindingOperations.IsDataBound(d, MVVMHasErrorProperty) Then
        If GetHasErrorDescriptor(d) Is Nothing Then
            Dim desc As System.ComponentModel.DependencyPropertyDescriptor = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(System.Windows.Controls.Validation.HasErrorProperty, d.GetType)
            desc.AddValueChanged(d, AddressOf OnHasErrorChanged)
            SetHasErrorDescriptor(d, desc)
            result = System.Windows.Controls.Validation.GetHasError(d)
        End If
    Else
        If GetHasErrorDescriptor(d) IsNot Nothing Then
            Dim desc As System.ComponentModel.DependencyPropertyDescriptor = GetHasErrorDescriptor(d)
            desc.RemoveValueChanged(d, AddressOf OnHasErrorChanged)
            SetHasErrorDescriptor(d, Nothing)
        End If
    End If
    Return result
End Function
Private Shared Sub OnHasErrorChanged(ByVal sender As Object, ByVal e As EventArgs)
    Dim d As DependencyObject = TryCast(sender, DependencyObject)
    If d IsNot Nothing Then
        d.SetValue(MVVMHasErrorProperty, d.GetValue(Validation.HasErrorProperty))
    End If
End Sub

Private Shared ReadOnly HasErrorDescriptorProperty As DependencyProperty = DependencyProperty.RegisterAttached("HasErrorDescriptor", GetType(System.ComponentModel.DependencyPropertyDescriptor), GetType(Window1))
Private Shared Function GetHasErrorDescriptor(ByVal d As DependencyObject) As System.ComponentModel.DependencyPropertyDescriptor
    Return d.GetValue(HasErrorDescriptorProperty)
End Function
Private Shared Sub SetHasErrorDescriptor(ByVal d As DependencyObject, ByVal value As System.ComponentModel.DependencyPropertyDescriptor)
    d.SetValue(HasErrorDescriptorProperty, value)
End Sub

And that’s it… Notice that the CoerceValue callback uses a private DependencyProperty in order to store the DependencyPropertyDescriptor that is used to set up the change event. This has two purposes: it allows to remove the notification conveniently, and more important, it serves as a flag in order to see whether the notification is already in place or still needs to be set up.

You can download an example for this technique here. (You will have to change the extension to .zip in order to extract the contents).

Credits

I found the information about how to monitor changes in any object’s DependencyProperties in this article by Charles Petzold.

Categories: Validation Tags: ,

No More Sticky Stars!

November 30, 2009 wpfglue Leave a comment

Oh, bother… good idea shattered on the rocks of the big Language Barrier Reef… Seems that folks feel a little ambiguous about being awarded Sticky Stars. Probably “sticky” has some connotations I wasn’t aware of, since English isn’t my mother tongue. So, I’ll have to find another way to express my respect… maybe “Self-Adhesive Stars” or “Attached Stars”?

Anyway, no innuendos intended (at least I was able to look that up in a dictionary…) and “Honi soit qui mal y pense”.

Categories: Uncategorized

CSS Class Equivalent in WPF

November 30, 2009 hbarck Leave a comment

In the small hours of this morning, I had an idea about how to create a stylesheet for FlowDocuments that behaves similar to CSS. What’s the difference between CSS classes and WPF styles? Well, CSS classes are not necessarily specific to a certain element type, and they can be combined on the styled element, while WPF Styles are specific to the element type, and each element can have exactly one Style (in addition to the default style, which fills in wherever the element style doesn’t provide a value). Also, if you want to combine Styles in WPF, you have to do it in the Style definition using BasedOn, which I find quite inflexible, and which drives one crazy if one has to support a lot of independent permutations.

So, what was the idea? The first one was that CSS classes should not be implemented as Styles, but as Triggers inside styles, which are selected whenever a certain attached property has the fitting value. So, in order to support different classes of TextBlocks, one would do this:

<Style TargetType="TextBlock" >
    <Style.Triggers>
        <Trigger Property="t:MultiStyle.Selector" Value="italic">
            <Setter Property="FontStyle" Value="Italic"/>
        </Trigger>
        <Trigger Property="t:MultiStyle.Selector" Value="red">
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
        <Trigger Property="t:MultiStyle.Selector" Value="bold">
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="FontStyle" Value="Normal"/>
        </Trigger>
    </Style.Triggers>
</Style>

 

The second idea was to use a special type for the attached property. The StyleSelector class tokenizes its String value, and returns “equal” on a comparison with another string or StyleSelector if the other object contains one of the tokens that are defined:

Public Class MultiStyle
    Inherits DependencyObject

    Public Shared ReadOnly SelectorProperty As DependencyProperty = DependencyProperty.RegisterAttached("Selector", GetType(StyleSelector), GetType(MultiStyle))
    Public Shared Function GetSelector(ByVal d As DependencyObject) As StyleSelector
        Return d.GetValue(SelectorProperty)
    End Function
    Public Shared Sub SetSelector(ByVal d As DependencyObject, ByVal value As StyleSelector)
        d.SetValue(SelectorProperty, value)
    End Sub

End Class

<System.ComponentModel.TypeConverter(GetType(StyleSelectorConverter))> _
Public Class StyleSelector
    Implements IEquatable(Of StyleSelector), IEquatable(Of String)

    Public Sub New(ByVal value As String)
        _Value = value
    End Sub

    Private _Value As String
    Public Property Value() As String
        Get
            Return _Value
        End Get
        Set(ByVal value As String)
            _Value = value
        End Set
    End Property

    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        If TypeOf obj Is StyleSelector Then
            Return Equals1(obj)
        End If
        If TypeOf obj Is String Then
            Return Equals2(obj)
        End If
        Return MyBase.Equals(obj)
    End Function

    Public Function Equals1(ByVal other As StyleSelector) As Boolean Implements System.IEquatable(Of StyleSelector).Equals
        If other IsNot Nothing And _Value IsNot Nothing Then
            Dim myValue() As String = _Value.Split(" "c)
            Dim otherValue() As String = other.Value.Split(" "c)
            Dim result As Boolean = False
            Dim i As Integer = 0
            Do While i < otherValue.Length And Not result
                result = myValue.Contains(otherValue(i))
                i += 1
            Loop
            Return result
        Else
            Return MyBase.Equals(other)
        End If
    End Function

    Public Function Equals2(ByVal other As String) As Boolean Implements System.IEquatable(Of String).Equals
        If other IsNot Nothing And _Value IsNot Nothing Then
            Dim myValue() As String = _Value.Split(" "c)
            Return myValue.Contains(other)
        Else
            Return MyBase.Equals(other)
        End If
    End Function
End Class

Public Class StyleSelectorConverter
    Inherits System.ComponentModel.TypeConverter

    Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
        Return (sourceType Is GetType(String))
    End Function

    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        Return (destinationType Is GetType(String))
    End Function

    Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
        Dim result As StyleSelector = Nothing
        If TypeOf value Is String Then
            result = New StyleSelector(value)
        End If
        Return result
    End Function

    Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
        Dim result As Object = Nothing
        If TypeOf value Is StyleSelector Then
            result = DirectCast(value, StyleSelector).Value
        End If
        Return result
    End Function
End Class

The only thing that remains to do is to implement the TypeConverter that allows to use StyleSelector as an attribute value in XAML, and to define the attached property.

Applications

Since I couldn’t go back to sleep, I tried it out immediately. You can download the VB solution here. (you will have to remove the .doc extension from the zip file; wordpress.com doesn’t allow zip files directly.)

However, when I was done, the idea didn’t seem so brilliant anymore as when I woke up. There are limitations of course. The trick works only with property setters. If you want to use triggers, they will be the same for all classes. What can you do with this what you can’t do with styles? The last thing that I came up with is that one could write a style sheet editor that creates styles with identical class definitions for different types of controls, so that you can use the same set of property values for different elements, as in CSS. However, you’d have to have this editor, otherwise it would be to much work to keep the class definitions equal to each other. And, of course, one can combine multiple classes on the same element, for what it’s worth…

PS: What happens if two classes provide different values for the same property and are both set on the same element? Because triggers are executed in the order they appear in the Style definition, the class that is defined last wins and overwrites the value of its sibling…

Categories: Uncategorized Tags: , , ,

Commands and InputGestures

November 24, 2009 hbarck Leave a comment

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.

Submit Sticky Command Example

November 21, 2009 hbarck Leave a comment

WPFGluePublished.zip on the Downloads page contains an example project called SubmitCommandExample which demonstrates how to use a Sticky Command in order to submit validated user input from a group of data bound controls to the object they bind to. The command will only be enabled if the input passes validation, and invalid data will not be forwarded to the data object.

Switching the Keyboard to Different Languages

November 21, 2009 hbarck 1 comment

Handling different languages with different keyboard layouts within the same document has only recently become a common feature in text processing applications. However, since I frequently have to work with data in different languages, I wouldn’t like to miss this ability.

In WPF, the class responsible for managing different keyboard languages is called InputLanguageManager. It exposes an attached property, InputLanguage, that accepts a culture identifier and switches the keyboard to the defined layout for this language, if one is installed. Unfortunately, this only works when the control receives focus. So, if you are typing something in a TextBox and change the InputLanguage while you are typing, e.g. through a keyboard shortcut, this change doesn’t become active unless the keyboard focus leaves the TextBox and returns again.

Also, in principle you can define different languages for paragraphs in a RichTextBox by setting the InputLanguageManager.InputLanguage on the Paragraph elements in the RichTextBoxes FlowDocument, but the keyboard doesn’t automatically change to the appropriate layout when the selection enters a section in a different language. This feature is extremely useful, so I decided to create a Sticky Property which allows to change the keyboard language effective immediately, and a Sticky Behaviour that sets the current input language according to what is set in this attached property on the FrameworkContentElements inside a RichTextBox’s FlowDocument.

Changing the Keyboard Language at Runtime

The way the KeyboardLanguage property is implemented is a typical example for the Sticky Property design pattern. A Sticky Property is an attached property that has an OnPropertyChanged handler which does some useful work, so that the property has an effect aside from storing data with the object it is set on. In the case of the KeyboardLanguage property, this work would be looking up the available keyboard languages, checking whether one of them fits the value, and activating this keyboard layout.

Public Shared ReadOnly KeyboardLanguageProperty As DependencyProperty = DependencyProperty.RegisterAttached("KeyboardLanguage", GetType(String), GetType(Localization), New FrameworkPropertyMetadata(Nothing, System.Windows.FrameworkPropertyMetadataOptions.Inherits, AddressOf OnKeyboardLanguageChanged))
Public Shared Function GetKeyboardLanguage(ByVal d As DependencyObject) As String
    Return d.GetValue(KeyboardLanguageProperty)
End Function
Public Shared Sub SetKeyboardLanguage(ByVal d As DependencyObject, ByVal value As String)
    d.SetValue(KeyboardLanguageProperty, value)
End Sub
Public Shared Sub OnKeyboardLanguageChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    If TypeOf d Is FrameworkElement Then
        Dim l As System.Globalization.CultureInfo = FindKeyboardLanguage(e.NewValue)
        If String.IsNullOrEmpty(e.NewValue) Or l Is System.Globalization.CultureInfo.InvariantCulture Then
            d.ClearValue(InputLanguageManager.InputLanguageProperty)
            d.ClearValue(InputLanguageManager.RestoreInputLanguageProperty)
        Else
            d.SetValue(InputLanguageManager.InputLanguageProperty, l)
            d.SetValue(InputLanguageManager.RestoreInputLanguageProperty, True)
            InputLanguageManager.Current.CurrentInputLanguage = l
        End If
    End If
End Sub

Public Shared Function FindKeyboardLanguage(ByVal tag As String) As System.Globalization.CultureInfo
    Dim result As System.Globalization.CultureInfo = System.Globalization.CultureInfo.InvariantCulture
    If Not String.IsNullOrEmpty(tag) Then
        For Each c As System.Globalization.CultureInfo In InputLanguageManager.Current.AvailableInputLanguages
            If c.IetfLanguageTag.ToUpper.StartsWith(tag.ToUpper) Then
                result = c
            End If
            If c.IetfLanguageTag.ToUpper = tag.ToUpper Then
                Exit For
            End If
        Next
    End If
    Return result
End Function

 

This gives the added flexibility of supporting language-only IETF tags, so that you don’t have to know exactly which flavour of the language the target system supports.

Keeping Track of Different Languages in a FlowDocument

This is done through a sticky (or attached) behaviour. A Sticky Behaviour is an attached property that registers event handlers on the control it is attached to. In this case, the behaviour will be specialized for RichTextBoxes, because their ability to edit FlowDocuments is the most convenient way to edit structured documents in WPF.

The Sticky Behaviour is activated by setting the TracksKeyboardLanguage property to true. Whenever the selection changes, this will cause the RichTextBox to check whether there are KeyboardLanguage attributes on the parent element of the current selection, and to activate the appropriate keyboard layout:

Public Shared ReadOnly TracksKeyboardLanguageProperty As DependencyProperty = DependencyProperty.RegisterAttached("TracksKeyboardLanguage", GetType(Boolean), GetType(Localization), New PropertyMetadata(AddressOf OnTracksKeyboardLanguageChanged))
Public Shared Function GetTracksKeyboardLanguage(ByVal d As DependencyObject) As Boolean
    Return d.GetValue(TracksKeyboardLanguageProperty)
End Function
Public Shared Sub SetTracksKeyboardLanguage(ByVal d As DependencyObject, ByVal value As Boolean)
    d.SetValue(TracksKeyboardLanguageProperty, value)
End Sub
Private Shared Sub OnTracksKeyboardLanguageChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    If TypeOf d Is RichTextBox Then
        Dim b As RichTextBox = d
        If CBool(e.OldValue) Then
            RemoveHandler b.SelectionChanged, AddressOf OnSelectionChanged
        End If
        If CBool(e.NewValue) Then
            AddHandler b.SelectionChanged, AddressOf OnSelectionChanged
        End If
    End If
End Sub

Private Shared Sub OnSelectionChanged(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim b As RichTextBox = sender
    Dim f As FrameworkContentElement = TryCast(b.Selection.Start.Parent, FrameworkContentElement)
    If f IsNot Nothing Then
        Dim tag As String = GetKeyboardLanguage(f)
        If Not String.IsNullOrEmpty(tag) Then
            If Not InputLanguageManager.Current.CurrentInputLanguage.IetfLanguageTag.ToUpper.StartsWith(tag.ToUpper) Then
                InputLanguageManager.Current.CurrentInputLanguage = FindKeyboardLanguage(tag)
            End If
        End If
    End If
    e.Handled = False
End Sub

 

It is always wise to make these event handlers static, so that they don’t create any references which could keep the control from being freed. Setting e.Handled to false makes sure that the RichTextBox still can accept other handlers for the SelectionChanged event.

Downloading the Example

The WPFGluePublished solution on the Downloads page contains a project called KeyboardLanguageExample which demonstrates the use of the KeyboardLanguage and TracksKeyboardLanguage properties. This example assumes that you have German, Arabic and English installed as Input languages on your computer. If you don’t have any additional keyboards installed, you can do so using the Regional and Language Options control panel in Windows.

New Sticky Star Award: Selectors and attached behaviours

November 14, 2009 wpfglue Leave a comment

I just awarded 5 Sticky Stars to Zoodazzle, a user in the MSDN WPF forum who published a way of making one item only selected across a group of selector controls in a WPF form. He uses an attached (or sticky) behaviour for this and can have several groups of selectors on the same page… really elegant solution.

Look at this thread: Selectors and attached behaviours.5StickyStars

 

See the Sticky Star Awardees page for more links to great solutions out there.

Categories: Uncategorized Tags:

Implementing the Sticky Command Design Pattern

November 12, 2009 hbarck Leave a comment

The Sticky Command design pattern is one of the patterns in WPFGlue which allow to add custom functionality to out-of-the-box WPF controls. Adding a Sticky Command to a FrameworkElement gives it a certain new behaviour, and makes this behaviour available to the element and all its child elements through a routed command, while the implementation of the element remains separate and independent of the implementation of the behaviour.

Let’s take an example. A very common need in data oriented applications is a submit command which sends the values of a group of controls to the underlying data store. This command should be disabled when the data is invalid, and it should be possible to connect it to a KeyGesture which allows to call the command by pressing a given key when entering data.

<GroupBox x:Name="CustomerGroupBox" Header="Customer"
          v:Submit.Command="{StaticResource SubmitCommand}"
          v:Submit.KeyGesture="Enter"> <!—- Content left out for clarity -->
</GroupBox>

The Sticky Command is stuck to the GroupBox by setting the Submit.Command Sticky Property. Optionally, one can add a KeyGesture which invokes the command, in this case pressing the Enter key. The SubmitCommand is just a standard RoutedCommand:

<Window.Resources>
    <RoutedCommand x:Key="SubmitCommand"/>
</Window.Resources>

By itself it doesn’t provide any functionality; it’s just kind of an identifier for the behaviour we want to add to the GroupBox.

Creating a Sticky Property

A Sticky Property is an attached property that, in addition to storing a value on the target element, does some work in its PropertyChanged handler. In our case, this work consists of creating a CommandBinding and setting it on the target element, or removing the created CommandBinding, respectively.

Public Class Submit
    Inherits DependencyObject

    Public Shared ReadOnly CommandProperty As DependencyProperty = _
            DependencyProperty.RegisterAttached("Command", _
            GetType(RoutedCommand), GetType(Submit), _
            New PropertyMetadata(AddressOf OnCommandChanged))
    Public Shared Function GetCommand(ByVal d As DependencyObject) _
            As RoutedCommand
        Return d.GetValue(CommandProperty)
    End Function
    Public Shared Sub SetCommand(ByVal d As DependencyObject, _
            ByVal value As RoutedCommand)
        d.SetValue(CommandProperty, value)
    End Sub
    Private Shared Sub OnCommandChanged(ByVal d As DependencyObject, _
        ByVal e As DependencyPropertyChangedEventArgs)
        Dim f As FrameworkElement = TryCast(d, FrameworkElement)
        If f IsNot Nothing Then
            If e.OldValue IsNot Nothing Then
                Detach(f, e.OldValue)
            End If
            If e.NewValue IsNot Nothing Then
                Attach(f, e.NewValue)
            End If
        End If
    End Sub
'...
End Class

The implementation of OnCommandChanged is quite simple: if the property already has a value, the command should be detached from the target, and if a new value is provided, this value should be attached to it. One might ask why it is necessary to provide a Detach procedure as well. Why not just leave the CommandBinding in place? If the command can be removed by changing setting the value of Submit.Command to Nothing, then the command can be attached and detached through triggers, which opens up a whole new line of uses…

Attaching and Detaching the CommandBinding

Private Shared Sub Attach(ByVal d As FrameworkElement, _
                          ByVal command As RoutedCommand)
    d.CommandBindings.Add(New SubmitCommandBinding(command))
    Dim gesture As KeyGesture = GetKeyGesture(d)
    AttachKeyGesture(d, command, gesture)
    If d.BindingGroup Is Nothing Then
        d.BindingGroup = New BindingGroup
    End If
End Sub
Private Shared Sub Detach(ByVal d As FrameworkElement, _
                          ByVal command As RoutedCommand)
    For Each b As SubmitCommandBinding In _
            d.CommandBindings.OfType(Of SubmitCommandBinding)()
        d.CommandBindings.Remove(b)
    Next
    DetachKeyGesture(d)
End Sub

The Attach procedure creates a new CommandBinding that connects the provided RoutedCommand to its implementation, and adds it to the targets CommandBindings collection.

The Detach procedure removes the CommandBinding from the collection. In order to be able to find the correct CommandBinding, Attach uses a class derived from CommandBinding, in order to use its type as marker. This way, one doesn’t have to store a reference to the created CommandBinding in order to be able to remove it later. The dummy class is a private nested class inside the Submit class:

Private Class SubmitCommandBinding
    Inherits CommandBinding
    Public Sub New(ByVal command As RoutedCommand)
        MyBase.New(command, AddressOf OnExecutedSubmit, _
                   AddressOf OnCanExecuteSubmit)
    End Sub
End Class

The Command’s Implementation

The procedures OnExecutedSubmit and OnCanExecuteSubmit handle the RoutedCommand.CanExecute and RoutedCommand.Executed events that are sent out by the CommandBinding. It is useful to separate the code specific to event handling from the code that does the actual work:

Private Shared Sub OnCanExecuteSubmit(ByVal sender As Object, _
        ByVal e As CanExecuteRoutedEventArgs)

    Dim result As Boolean = False
    Dim target As FrameworkElement = TryCast(sender, FrameworkElement)
    If target IsNot Nothing Then
        ' Separate event specific code from domain specific code...
        result = CanExecuteSubmit(target,e.Parameter)
    End If
    e.CanExecute = result
    e.Handled = True
End Sub

Public Shared Function CanExecuteSubmit(ByVal target As FrameworkElement, _
        ByVal parameter As Object) As Boolean
    Dim result As Boolean = False
    If target IsNot Nothing Then
        result = (target.BindingGroup IsNot Nothing AndAlso Not _
                  System.Windows.Controls.Validation.GetHasError(target))
    End If
    Return result
End Function

Private Shared Sub OnExecutedSubmit(ByVal sender As Object, _
                                    ByVal e As ExecutedRoutedEventArgs)
    Dim target As FrameworkElement = TryCast(sender, FrameworkElement)
    If target IsNot Nothing Then
        If CanExecuteSubmit(target, e.Parameter) Then
            ExecuteSubmit(target, e.Parameter)
        End If
        e.Handled = True
    End If
End Sub

Public Shared Sub ExecuteSubmit(ByVal target As FrameworkElement, _
        ByVal parameter As Object)
    target.BindingGroup.UpdateSources()
End Sub

Providing a KeyGesture

In order to be able to specify a KeyGesture for the command on the target, we need to create a second Sticky Property, which is able to add the KeyGesture to the target’s InputBindings collection. The method is similar to the one used with the CommandBinding itself. However, there is one point of note: We cannot be sure of the order in which the attached properties are set on the target. So, we have to make sure that CommandBinding and InputBinding are set up correctly no matter which is specified first. Since the InputBinding depends on the CommandBinding, one could say that Submit.Command is the main Sticky Property, while Submit.KeyGesture is a supporting Sticky property. The rule would then be that the Attach and Detach procedures for the main Sticky property would have to call the Attach procedures for all supporting properties in turn, while the Attach procedures of the supporting properties only attach something if the main Sticky Property has already been set.

The implementation of the KeyGesture functionality looks like this:

Public Shared ReadOnly KeyGestureProperty As DependencyProperty = _
        DependencyProperty.RegisterAttached("KeyGesture", _
                    GetType(KeyGesture), GetType(Submit), _
                    New PropertyMetadata(AddressOf OnKeyGestureChanged))
Public Shared Function GetKeyGesture(ByVal d As DependencyObject) _
        As KeyGesture
    Return d.GetValue(KeyGestureProperty)
End Function
Public Shared Sub SetKeyGesture(ByVal d As DependencyObject, _
                                ByVal value As KeyGesture)
    d.SetValue(KeyGestureProperty, value)
End Sub
Private Shared Sub OnKeyGestureChanged(ByVal d As DependencyObject, _
        ByVal e As DependencyPropertyChangedEventArgs)
    Dim f As FrameworkElement = TryCast(d, FrameworkElement)
    If f IsNot Nothing Then
        If e.OldValue IsNot Nothing Then
            DetachKeyGesture(f)
        End If
        If e.NewValue IsNot Nothing Then
            AttachKeyGesture(f, GetCommand(f), e.NewValue)
        End If
    End If
End Sub
Private Shared Sub AttachKeyGesture(ByVal d As FrameworkElement, _
                                    ByVal command As RoutedCommand, _
                                    ByVal gesture As KeyGesture)
    If command IsNot Nothing And gesture IsNot Nothing Then
        d.InputBindings.Add(New SubmitKeyBinding(command, gesture))
    End If
End Sub
Private Shared Sub DetachKeyGesture(ByVal d As FrameworkElement)
    For Each i As SubmitKeyBinding In _
            d.InputBindings.OfType(Of SubmitKeyBinding)()
        d.InputBindings.Remove(i)
    Next
End Sub

Private Class SubmitKeyBinding
    Inherits InputBinding
    Public Sub New(ByVal command As RoutedCommand, _
            ByVal gesture As KeyGesture)
        MyBase.New(command, gesture)
    End Sub
End Class

Reusability

Once we have this submit command, we can reuse it as it is, in any project, with any kind of control or data. But what about reusability of the pattern implementation itself?

The whole Submit class consists of static members, only. This has the advantage of not creating dependent object references with the command bindings, which could interfere with the garbage collection of the page on which the command is used. Unfortunately, shared members cannot be overridden, which means that it is not straight forward to create a common base class for Sticky Commands and derive new implementations from that class. However, the domain specific code and the pattern specific code are quite easy to separate into different procedures, so that it might be an option to use Item Templates or Code Snippets in order to reuse the pattern specific code.

Conclusion

The Sticky Command pattern offers a powerful and flexible way of modifying the behaviour of standard controls without changing their implementation. Because it is used through declarative syntax, it can leverage the full power of WPF style, trigger and template mechanisms. In addition to that, a new implementation of the pattern can be created from an existing one with minimal changes.

Downloads

Download a complete example project for the Submit StickyCommand here.

Download an Item Template for Sticky Commands here.