Archive

Archive for December, 2009

The Sticky ViewModel Pattern

December 16, 2009 1 comment

A Sticky ViewModel is a special type of Sticky Component which adds functionality to the ViewModel instead of to the View. By binding to the DataContext of its base element, the Sticky ViewModel is able to provide services which the ViewModel itself doesn’t include. In this respect, it is similar to the Mini-ViewModel pattern, which you might prefer if you want to provide an encapsulated reusable View for the added functionality as well.

An Example

The model or ViewModel might have a property which contains a persons name. In the View, you might want to be able to edit first name and last name separately. I have already discussed this example in the context of the Stateless Sticky ViewModel pattern, so here I want to add some extra: the new implementation should format the name either John Smith or Smith, John, depending on a boolean property LastNameFirst which one can use to configure the behaviour.

The example is contained in the StickyComponentExample project which you will find in the WPFGluePublished solution on the Downloads page.

Using a Sticky ViewModel

Like any other Sticky Component, you would initialize a Sticky ViewModel in the resources of an element that wants to use them:

<Grid.Resources>
  <ex:NameStickyViewModel x:Key="viewModel"
      LastNameFirst="True" FullName="{f:StickyBinding Name}"/>
</Grid.Resources>

Using the LastNameFirst property, we specify that we want the full name displayed in the Smith, John format.

But what is the meaning of the {f:StickyBinding } markup extension? It is understood that the Sticky ViewModel somehow accesses the DataContext of its base element in order to enhance it. In fact, there is a DataContext property in the StickyViewModel base class which is synchronized with the base’s DataContext automatically.

However, if the Sticky ViewModel would get the information it needs directly from the DataContext, it would have to know its type, thus being closely coupled to the ViewModel, which we want to avoid, since we want to be able to re-use the Sticky ViewModel wherever we need its functionality. So, the idea is to get the necessary information from the DataContext through data binding. Unfortunately, since the Sticky ViewModel is not part of the logical WPF tree, normal bindings cannot access the base element’s DataContext implicitly, like child controls. So, we use a special binding class, the StickyBinding, which allows us to bind to the DataContext of the StickyViewModel, instead. By writing FullName=”{f:StickyBinding Name}” we instruct the StickyViewModel to look for the value it should edit in the Name property of the model the element is bound to.

As usual, the Sticky ViewModel is attached to its base through an attached property:

<Grid x:Name="base"
  ex:NameStickyViewModel.ViewModel="{DynamicResource viewModel}"
      HorizontalAlignment="Stretch">

In this case, we have to refer to it with a DynamicResource extension, since it is contained in the resources of the element it is used on and thus is referred to before it is declared. We could also put it into the page’s resources and use a StaticResource extension. (StaticResource extensions are less complicated, so they are a little bit safer and faster, but they can only be specified where the definition comes before the reference in the XAML). However, in the example, the NameStickyViewModel is used in a DataTemplate for a ListView. So, every row of the ListView needs its own instance of the Sticky ViewModel, otherwise the names from different rows will start mixing. We can make sure of this either by putting the declaration of the StickyViewModel into the DataTemplate itself, as I have done here, or by setting x:Shared=”False” where we declare it, which tells WPF to create a new instance of a resource every time it is accessed.

The rest of the DataTemplate defines three TextBoxes for each row:

<TextBox Text="{Binding Name, TargetNullValue='[New Customer]'}"
         Margin="30,0,0,0"/>
<TextBox Grid.Column="1" Text="{Binding ElementName=base,
  Path=(ex:NameStickyViewModel.ViewModel).LastName}"/>
<TextBox Grid.Column="2" Text="{Binding ElementName=base,
  Path=(ex:NameStickyViewModel.ViewModel).FirstName}"/>

The first TextBox is bound directly to the DataContext of the DataTemplate. It refers to the property in the model that contains the full name of a person. The other two TextBoxes are bound to the StickyViewModel that lives on the element named “base”; this element is the Grid from above.

Implementing the Sticky ViewModel Pattern

In order to support binding on their properties, Sticky ViewModels are derived from DependencyObject and expose their working properties as DependencyProperties. Configuration properties don’t need to be data bound, so they are defined as normal properties.

Like in the Stateless Sticky ViewModel, after implementing the logic which does what the model should do, the main point is distributing the change notifications from the different properties so that the logic is called at the right time. This happens in the OnPropertyChanged method of the Sticky ViewModel, which is originally defined in DependencyObject:

Protected Overrides Sub OnPropertyChanged(ByVal e As System.Windows.DependencyPropertyChangedEventArgs)
    MyBase.OnPropertyChanged(e)
    Static changeInitiator As DependencyProperty = Nothing

    If changeInitiator Is Nothing Then
        changeInitiator = e.Property

        Select Case e.Property.Name
            Case "FullName"
                FirstName = DetermineFirstName(FullName)
                LastName = DetermineLastName(FullName)
            Case "FirstName", "LastName"
                FullName = DetermineFullName(FirstName, LastName)
        End Select

        changeInitiator = Nothing
    End If
End Sub

 

Again like in the Stateless Sticky ViewModel, we remember between invocations which property started the change and cancel subsequent invocations of the method. However, this time the code is a little bit shorter and cleaner and not distributed between different methods (I just notice that one could implement it the same way with a Stateless Sticky ViewModel, but alas… never get it perfect the first time.)

Under the Hood

The Sticky Component Framework supports the Sticky ViewModel pattern with two components: the StickyViewModel base class, which can be used to derive one’s own Sticky ViewModels, and the StickyBinding, which one needs to be able to bind to the properties of the Sticky ViewModel’s DataContext.

The StickyViewModel class mainly manages attaching and detaching the ViewModel on its base element:

Public Sub OnAttach(ByVal sender As Object, ByVal e As System.EventArgs) Implements IStickyComponent.OnAttach
    Me.DataContext = StickyComponentManager.GetDataContext(sender)
    StickyComponentManager.AttachDataContextChanged(sender, AddressOf _DataContextChanged)
    Attach(sender, e)
End Sub

Public Sub OnDetach(ByVal sender As Object, ByVal e As System.EventArgs) Implements IStickyComponent.OnDetach
    Me.DataContext = Nothing
    StickyComponentManager.DetachDataContextChanged(sender, AddressOf _DataContextChanged)
    Detach(sender, e)
End Sub

It adds a handler to the base’s DataContextChanged event, so that the DataContext stays synchronised with the base’s DataContext. DataContext is a DependencyProperty, so it provides its own change notifications. Furthermore, the class defines a generic ViewModel attached property which can be used in derived classes in order to attach the model to the element.

The StickyBindingExtension basically is a BindingExtension with a reduced set of properties (everything related to input and validation is not needed on the StickyBinding), and in principle delegates all it does to this binding:

Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object
    Dim target As System.Windows.Markup.IProvideValueTarget = serviceProvider.GetService(GetType(System.Windows.Markup.IProvideValueTarget))
    If target IsNot Nothing Then
        If TypeOf target.TargetProperty Is DependencyProperty Then
            If TypeOf target.TargetObject Is StickyViewModel Then
                Dim dataPath As String = "DataContext"
                If Not String.IsNullOrEmpty(Path) Then
                    If Path.StartsWith("/") Or Path.StartsWith("[") Then
                        dataPath &= Path
                    Else
                        dataPath &= "." & Path
                    End If
                End If
                Dim b As New Binding(dataPath)
                b.Source = target.TargetObject
                b.Mode = Mode
                b.Converter = Converter
                b.ConverterCulture = ConverterCulture
                b.ConverterParameter = ConverterParameter
                Return b.ProvideValue(serviceProvider)
            End If
        End If
    End If
    Return Me
End Function

 

The trick here is to prefix the binding’s path with the DataContext property and always use the target object as source. Thus, following the principles of WPFGlue, I avoid to implement my own version of change notification listeners and instead use what is already there. Since the DataContext is always synchronized with the the base’s DataContext and provides change notification, the StickyBinding will always be able to find the correct property without any explicit dependencies.

Notice that the last line in the block that creates the binding returns Binding.ProvideValue. This is necessary in order to mimic Binding’s behaviour in all contexts.

Comparing Stateless and Stateful Sticky ViewModel

As mentioned before, the main advantage of the Stateful Sticky ViewModel is that it bundles configuration properties in one place and forms a better defined unit than the Stateless version. Its main focus is on the base’s DataContext. Sticky Bindings don’t support binding to properties of elements in the logical tree, as it would be possible with the ElementName or RelativeSource properties of a normal binding. However, if one needs that, one can combine the two patterns and define attached properties on the Sticky ViewModel for no other purpose than to be able to bind them to logical tree elements, and access them through a reference to the base that is acquired during Attach and of course released during Detach.

Coming Soon…

The Sticky Component Framework is still once removed from a real application… the next step will be a Commanding Framework based on Sticky Components which allows to define RoutedCommands, their keyboard shortcuts and display texts in one place, delegating their invocations to methods or ICommand implementations in the ViewModel or model.

Using Validation.ErrorTemplate

December 16, 2009 8 comments

This was originally published as a thread in the MSDN WPF forum before I started this blog. However, today I revisited the thread and decided to maintain it here, now..

There are a couple of known problems and unknown features with Validation.ErrorTemplate that came up frequently, and I’d like to collect the workarounds for these things in one place. This is how far I got:

Problem: The Content of the ErrorTemplate is not displayed correctly

e.g. the red border doesn’t fit around the control, or parts of the error template are not drawn. The reason is that there may not be enough space around the control to display the error template, so the solution is to make the margin of the control big enough that the error template can be drawn.

Unknown Feature: The DataContext of the ErrorTemplate is the Validation.Errors Collection

Haven’t found this in the documentation, but it means that the easiest way to get at an error message in an error template is to use {Binding Path=/ErrorContent}.

Problem: Have to Change the Style of a Control in Order to Set the Error Message as ToolTip

It seems to be impossible to set the ToolTip of a control using Styles or Triggers in the error template. Usually this is worked around by modifying the Style of the control, as in the example from the WPF SDK. However, this has the disadvantage that one has to change the style of the control in order to accommodate Validation, which might interfere with other uses of the Style. There are better workarounds: most elegant by using a ToolTip on some error indicator that is part of the error template, or, if it must be the control itself, using a custom attached property on the AdornedElementPlaceholder like this:

Public Shared ReadOnly AdornedElementToolTipProperty As DependencyProperty = DependencyProperty.RegisterAttached("AdornedElementToolTip", GetType(Object), GetType(Validation), New PropertyMetadata(Nothing, AddressOf OnAdornedElementToolTipChanged))
Public Shared Function GetAdornedElementToolTip(ByVal d As DependencyObject) As Object
    Return d.GetValue(AdornedElementToolTipProperty)
End Function
Public Shared Sub SetAdornedElementToolTip(ByVal d As DependencyObject, ByVal value As Object)
    d.SetValue(AdornedElementToolTipProperty, value)
End Sub
Private Shared Sub OnAdornedElementToolTipChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim placeholder As AdornedElementPlaceholder = TryCast(d, AdornedElementPlaceholder)
    If placeholder IsNot Nothing Then
        Dim control As Control = TryCast(placeholder.AdornedElement, Control)
        If control IsNot Nothing Then
            control.ToolTip = e.NewValue
        End If
    End If
End Sub

One can bind the error message to this property, and this way the error template works without interfering with the control’s style.

Problem: The Error Template is not Displayed when a Page is Loaded

This is by design, since one could assume that the user doesn’t want to see error messages before he/she made any mistakes, but sometimes one needs this functionality. So, the ValidatesOnTargetUpdated property was introduced on the ValidationRule class; by setting it to true, one sees the validation result immediately.

However, there is one caveat: you must make sure that you set the DataContext after the page is initialized; this would be either in the constructor after the generated comment line that says that initialization code should go there, or in the Loaded event. If you want to set the DataContext in XAML, you find a solution for this problem here (among other things): https://wpfglue.wordpress.com/2009/12/08/navigating-from-object-to-object/

Anyway, I hear this will be fixed in WPF 4.0.

Problem: When Using an Expander, the Error Template Remains Visible even if the Expander is Collapsed

This problem is a known bug, unfortunately as far as I know not yet scheduled for fixing. The workaround (found in the MSDN WPF forum) is binding the Visibility property of the outermost element of the error template to the AdornedElement.IsVisible property of the AdornedElementPlaceholder, using the BooleanToVisibilityConverter that comes with WPF.

Standard Error Template

So, this would be my standard error template:

<ControlTemplate x:Key="errorTemplate">
    <Border BorderBrush="Red" BorderThickness="1">
        <Border.Visibility>
            <Binding ElementName="placeholder" Path="AdornedElement.IsVisible">
                <Binding.Converter>
                    <BooleanToVisibilityConverter/>
                </Binding.Converter>
            </Binding>
        </Border.Visibility>
        <StackPanel>
            <AdornedElementPlaceholder x:Name="placeholder"
             v:Validation.AdornedElementToolTip="{Binding Path=/ErrorContent}"/>
        </StackPanel>
    </Border>
</ControlTemplate>

And this would be how I use it:

<StackPanel>
    <TextBox x:Name="Abox" Margin="3" Validation.ErrorTemplate="{StaticResource errorTemplate}"
      Text="{Binding Path=A, UpdateSourceTrigger=PropertyChanged,
      ValidatesOnDataErrors=True}" />
    <Expander Header="Test" IsExpanded="True" Validation.ErrorTemplate="{x:Null}">
        <Expander.BindingGroup>
            <BindingGroup/>
        </Expander.BindingGroup>
        <TextBox x:Name="Bbox" Margin="3"
         Validation.ErrorTemplate="{StaticResource errorTemplate}"
         Text="{Binding Path=B, UpdateSourceTrigger=PropertyChanged,
          ValidatesOnDataErrors=True}"/>
    </Expander>
</StackPanel>

Categories: Validation Tags: , ,

The Sticky Component Framework

December 11, 2009 3 comments

What is a Sticky Component? A Sticky Component is a component that can be attached to any FrameworkElement or FrameworkContentElement in order to enhance its functionality, without the need to subclass the element or to write code behind for it. Because of this, Sticky Components can be used very effectively to encapsulate specialized behaviours in a reusable way, and to enhance either the View or the ViewModel of an MVVM application without changing its code.

Sticky Component Interaction Patterns

In order to enhance the functionality of an element, the Sticky Component needs to interact with the element. There are several possible ways to connect the two:

  • Through registering event handlers: The Sticky Component can subscribe to events of the element it is attached to, and trigger its own functionality through these events.
  • Through registering CommandBindings: The Sticky Component can add its own CommandBindings to the element’s CommandBindings collection, thus enabling the element to handle the specified command.
  • Through registering InputBindings: the Sticky Component can enable an element to invoke commands by adding InputBindings to its InputBindings collection.
  • Through data binding: the Sticky Component can bind its own properties to the properties of the element. These bindings could in principle have the element as target or as source. But since many properties in the WPF framework are readonly, it is less error prone to always make the Sticky Component the binding target by convention.
  • Through binding to the element’s DataContext: a special class of Sticky Component, the Sticky ViewModel, doesn’t enhance the View, but the ViewModel, by binding to its properties and adding functionality that isn’t present in the ViewModel without the need to change it.

Some of these patterns require references between the Sticky ViewModel and the element it is attached to (its “base”). References are dangerous. They may cause memory leaks and unintended behaviour, e.g. if events are handled even if the view that established the event subscription is already out of scope. Therefore, the core of the Sticky Component Framework is concerned with managing the lifetime of a Sticky Component and its interaction patterns so that all these references are cleaned up when appropriate.

Sticky Component Lifetime

The life cycle of a Sticky Component looks like follows:

  • It is defined and configured in the Resources of the application, a window, or an individual element.
  • It is referenced in an attached property on the base element it is attached to. This is the time when the Sticky Component is actually instantiated.
  • It attaches itself to the base. This can happen either immediately or delayed, depending on whether the base must be in initialized or loaded state before the component can attach to it.

The end of the component’s relationship to the base could come in two ways:

  • Either the attached property which connects the component to the base is reset,
  • or the base itself is unloaded.

In both cases, all references between the Sticky Component and its base should be removed, leaving either object ready for garbage collection. If the component still exists after it has been detached from its base (e.g. because it is kept as a shared instance in a ResourceDictionary), it could be attached again to a different base, or even the same, if that still is available.

This lifetime pattern is expressed in the following interface definition:

Public Enum AttachMode As Integer
    Immediate = 0
    OnInitialized = 1
    OnLoaded = 2
End Enum

Public Interface IStickyComponent

    Sub OnAttach(ByVal base As Object, ByVal e As EventArgs)

    Sub OnDetach(ByVal base As Object, ByVal e As EventArgs)

    ReadOnly Property Mode() As AttachMode

End Interface

OnAttach will be called whenever the Sticky Component will be attached to a base, OnDetach will be called when it is detached. Both methods have EventHandler signatures, so that they can be invoked by the Initialized, Loaded or Unloaded events, respectively. The Mode property defines when the component should attach itself to the element. If possible, the component should attach itself immediately. However, if it needs to modify existing bindings, it will have to do that during the Initialized event, and if it needs to access properties that are data bound, like e.g. the DataContext of the base, it might have to wait until the Loaded event. Since these requirements depend on what the component does, it would be expected that a class that implements IStickyComponent would return a constant value here.

The StickyComponentManager

The StickyComponentManager is a static class that encapsulates the different ways to establish and remove interaction connections between a Sticky Component and its base. It offers overloaded Attach and Detach procedures in pairs with corresponding parameters. So, one would attach an event to a base using the Attach(base,event,handler) overload, and remove it again using the Detach(base, event,handler) overload. There are overloads for all cases discussed here; some of these overloads offer additional services; e.g. there are overloads for attaching event handlers which automatically remove the event handler after it has fired once (using the One Shot Event pattern), or if another specified event occurs before the event that the handler is supposed to handle.

In your Sticky Component, you would set up the connection to the base by calling the appropriate Attach methods of the StickyComponentManager in your OnAttach implementation, and remove the connection by calling the corresponding Detach methods in the OnDetach implementation.

The most important overloads of these methods are the ones that attach / detach Sticky Components. There is a special implementation of a DependencyPropertyChangedHandler that you can use with your attached properties that attach Sticky Components which calls these overloads:

Public Shared Sub OnStickyComponentChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim c As IStickyComponent
    If e.OldValue IsNot e.NewValue Then
        c = TryCast(e.OldValue, IStickyComponent)
        If c IsNot Nothing Then
            StickyComponentManager.Detach(d, c)
        End If
        c = TryCast(e.NewValue, IStickyComponent)
        If c IsNot Nothing Then
            StickyComponentManager.Attach(d, c)
        End If
    End If
End Sub

This will ensure that the lifetime of the sticky component is managed according to the life cycle described above.

In addition to the Attach and Detach methods, the StickyComponentManager contains several utility methods which hide the difference between FrameworkElement and FrameworkContentElement when attaching Sticky Components. So, elements derived from both of these classes could be the base for Sticky Components; on other elements, the Attach / Detach methods would simply do nothing.

Using Sticky Components

In XAML, you would normally declare and configure a Sticky Component in a Resources section, like this:

    <ex:TestStickyPatterns x:Key="UseEvents"
        Name="UseEvents" UseCommand="False"/>
</Page.Resources>

This would define a Sticky Component of class TestStickyPatterns and configure it by setting its Name and UseCommand properties.

You would then use it on a base element like this:

    <Button x:Name="TestEventPatternsButton"
          ex:TestStickyPatterns.Component="{StaticResource UseEvents}">
    Test Event Patterns</Button>

 

If you need the Sticky Component more than once on your page, consider setting the x:Shared attribute to false on its declaration. Thus you make sure that each time the component is used, you start with a fresh instance. Otherwise it could happen that the component is attached to multiple elements on your page at the same time, with unwelcome results.

Implementing Sticky Components

The TestStickyPatterns.Component attached property would be defined like this:

Public Shared ReadOnly ComponentProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("Component", _
    GetType(IStickyComponent), GetType(StickyTestComponent), _
    New PropertyMetadata(AddressOf StickyComponentManager.OnStickyComponentChanged))
Public Shared Function GetComponent(ByVal d As DependencyObject) As IStickyComponent
    Return d.GetValue(ComponentProperty)
End Function
Public Shared Sub SetComponent(ByVal d As DependencyObject, _
                               ByVal value As IStickyComponent)
    d.SetValue(ComponentProperty, value)
End Sub

Notice that the property metadata refers to the shared (static in C#) method StickyComponentManager.OnStickyComponentChanged in order to enable lifecycle management for the component.

Its OnAttach and OnDetach implementations look as follows:

Public Sub OnAttach(ByVal sender As Object, ByVal e As EventArgs) Implements IStickyComponent.OnAttach
    MyBase.OnAttach(sender, e)
    If _UseCommand Then
        StickyComponentManager.Attach(sender, MyCommand, AddressOf OnExecuted, Nothing)
        StickyComponentManager.Attach(sender, New KeyGesture(Key.Space, ModifierKeys.Control), MyCommand)
    Else
        StickyComponentManager.Attach(sender, ButtonBase.ClickEvent, AddressOf OnClick)
        StickyComponentManager.Attach(sender, ButtonBase.ClickEvent, AddressOf OnClickOneShot, True)
        StickyComponentManager.Attach(sender, ButtonBase.ClickEvent, AddressOf OnClickCancel, FocusManager.LostFocusEvent)
    End If
End Sub

Public Sub OnDetach(ByVal sender As Object, ByVal e As EventArgs) Implements IStickyComponent.OnDetach
    MyBase.OnDetach(sender, e)
    If _UseCommand Then
        StickyComponentManager.Detach(sender, New KeyGesture(Key.Space, ModifierKeys.Control))
        StickyComponentManager.Detach(sender, MyCommand)
    Else
        StickyComponentManager.Detach(sender, ButtonBase.ClickEvent, AddressOf OnClick)
        StickyComponentManager.Detach(sender, ButtonBase.ClickEvent, AddressOf OnClickOneShot)
        StickyComponentManager.Detach(sender, ButtonBase.ClickEvent, AddressOf OnClickCancel)
    End If
End Sub

 

Grouping Sticky Components

Since the idea in using Sticky Components is that their behaviour is very narrow and specialized, so that they are suitable for many different contexts, there is a need to be able to group Sticky Components and attach a whole group at once to the same base element. The Sticky Component Framework contains a generic collection to help with that: StickyComponentCollection(Of T as IStickyComponent). This collection attaches and detaches its items at the right times, provides change notification, and if you implement the INamedStickyComponent interface on the collection’s items, you even can refer to the components inside the collection using string indexers in XAML. Here is an example of the use of this collection:

<Page.Resources>
    <ex:StickyTestComponentSet x:Key="test2">
        <ex:StickyTestComponent Name="Page Immediate"/>
        <ex:StickyTestComponent Name="Page OnInitialized" AttachMode="OnInitialized"/>
        <ex:StickyTestComponent Name="Page OnLoaded" AttachMode="OnLoaded"/>
    </ex:StickyTestComponentSet>

 

StickyTestComponentSet is defined by inheriting a concrete type instance of the generic collection:

Public Class StickyTestComponentSet
    Inherits StickyComponentCollection(Of StickyTestComponent)
    Implements IStickyComponent, IList

 

Notice that for some reason the Visual Studio XAML designer (Cider) will only let you add direct content to the class if you specify “Implements IList” again, even though this interface is already implemented by the base class.

The StickyComponent Example

On the Downloads page you will find the source code for the framework and an example project called StickyComponentExample. This project mainly focuses on demonstrating the different interaction techniques between StickyComponents, their usage patterns, and their life cycle. In order to trace the life cycle, a lot of debug output is written to Debug.Print, so you should run the project in the Visual Studio Debugger and follow the output in the Immediate window.

Coming Soon…

There is one special case of a Sticky Component: the Sticky ViewModel. It deserves special attention, and even though the example is already in the example solution, I’d like to cover it in a separate post.

The Last Word on Localized Resources in WPF

December 8, 2009 1 comment

After finishing my last post on using localized resources in WPF, I had the following idea, which I should have included from the start:

If you want to maintain your resources in a more WPF-ish and less WinForms-ish way, just do this:

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:l="https://wpfglue.wordpress.com/localization&quot;
    Title="Window1" Height="300" Width="300">
  <Window.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="{l:URI MyUriToLocalizedDictionaries}"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Window.Resources>

Set up your localized resource satellite assemblies like described in the other post, but don’t put anything into them except the pack URI’s of your localized dictionary in the respective language. This way, you can include every type of resource that’s available in WPF, don’t have any WinForms dependencies, and still have the standard .Net way of selecting the right localized resources. You can maintain the consistency of the dictionaries using a normal diff tool, and are no longer annoyed by the Visual Studio resource designer’s bad habit of interrupting your typing while auto-saving and swallowing what you just typed if you are not lucky.

On the downside, however, all localized resources will be part of your main assembly, either directly or by reference, and it is less convenient to add support for new languages, except if you decide to store the resource dictionaries as uncompiled Content files.

Categories: Localization Tags:

Navigating from Object to Object

December 8, 2009 1 comment

WPF offers a whole application paradigm based on navigation: The Navigation Application, hosted in a NavigationWindow, Frame or WebBrowser; consisting of XAML pages which are independent of each other; keeping track of the navigation history and restoring pages and their values while going forwards and backwards are supported out of the box.

However, some things are lacking. Suppose you have a data object model. In your application, you want to be able to select an object from a list, then click on a link to open a new page and edit the details of the object. When you are done, you want to select another object, and  when you have repeated this a couple of times, you want to be able to go backwards using the back button, returning to the objects you edited on each page.

What is the problem here? WPF offers no built-in way to pass an object from one page to the other using XAML only. This has consequences. If you use the approach suggested in the WPF documentation, you will have to instantiate a new page object, pass the object you want to edit to its constructor, and navigate to it using the NavigationService.Navigate(Object) overload. However, pages which are navigated to this way are not disposed when they are no longer displayed, but retained in memory as a whole, and this has some side effects which I already talked about in this post.

So, what can we do about it?

The GoToPage Sticky Command

A sticky command is a command binding that is attachable to any XAML element and adds a certain functionality to this element without changing its code.

The GoToPage navigation command was one of my first experiments with the WPFGlue programming style. Since then, I have learned a few things and changed it considerably. One of the changes being that now the command which is the navigation command is no longer hardcoded, but could be any routed command which is configured to use the Navigation command implementation. This is how you use it:

<NavigationWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:n="https://wpfglue.wordpress.com/navigation&quot;       
    Title="NavigationExample" Height="300" Width="300"
    n:Navigation.Command="GoToPage" Source="ListPage.xaml"/>

 

By setting the Navigation.Command attached property on the NavigationWindow to the WPF GoToPage command, you enable all pages that the NavigationWindow hosts to use this command in order to navigate to other pages and pass along objects as DataContext for the new page.

In the page, you would use the command like this:

<Button x:Name="EditButton"
   DataContext="{Binding ElementName=ItemListView, Path=SelectedItem}"
   Command="GoToPage" CommandParameter="{Binding}"
   n:Navigation.Uri="DetailPage.xaml">

This button uses the selected item of a ListView as its DataContext. If clicked, it invokes the GoToPage command (set to its Command property) The selected item is bound to the CommandParameter property, which means that it will be passed as parameter to the GoToPage command. Finally, the URL of the page that should be navigated to is configured through the Navigation.Uri attached property.

How Does It Work?

The Navigation.Command property is a sticky property: in its change handler, it attaches a CommandBinding with the code that calls the Navigation command to the element it is set on:

Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.RegisterAttached("Command", GetType(RoutedCommand), GetType(Navigation), 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)
    If e.OldValue IsNot Nothing Then
        Detach(d, e.OldValue)
    End If
    If e.NewValue IsNot Nothing Then
        Attach(d, e.NewValue)
    End If
End Sub
Private Shared Sub OnUnloaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    SetCommand(sender, Nothing)
End Sub

This code follows a pattern that is typical for Sticky Components; I want to introduce them in more detail later, but we are going to encounter their patterns continuously here, so I want to point it out:

A Sticky Component needs to be able to clean up after itself. So, there are two procedures, Attach and Detach. The Attach method connects the sticky component by setting up CommandBindings or event handlers, the Detach method removes all these references between the Sticky Component and its hosting element. So, the Detach procedure needs to be called in two cases: either if the Sticky Component is removed from the element, or if the element is unloaded. So, the common pattern for Sticky Components is to call Detach when a Sticky Component is replaced on an element, and to reset the attached property that controls the Sticky Component in the element’s Unloaded event.

Navigation Flow Control

This is the method that finally gets called when the GoToPage command is invoked:

Public Shared Function Navigate(ByVal sender As Object, ByVal target As Uri, ByVal data As Object) As Boolean
    Dim result As Boolean = False
    Dim service As NavigationService = GetNavigationService(sender)
    If service IsNot Nothing Then
        AddHandler service.LoadCompleted, AddressOf SetDataContextHandler
        result = service.Navigate(target, data)
    End If
    Return result
End Function

It uses the NavigationService.Navigate(Uri,Object) overloaded method, which allows to pass additional data into the navigation process. This additional data is the object that the user selected, and that was passed to the GoToPage command as parameter. The NavigationService will hold on to this object while the pages are changed. When the new page is loaded, we want to set the pages DataContext to the object, so we register a handler for the NavigationService’s LoadCompleted event.

The handler looks like this:

Private Shared Sub SetDataContextHandler(ByVal sender As Object, ByVal e As System.Windows.Navigation.NavigationEventArgs)
    Dim service As NavigationService = GetNavigationService(e.Navigator)
    If service IsNot Nothing Then
        RemoveHandler service.LoadCompleted, AddressOf SetDataContextHandler
        Dim data As Object = e.ExtraData
        If data IsNot Nothing Then
            If TypeOf e.Content Is DependencyObject Then
                SetDataContext(e.Content, data)
                AddHandler service.Navigating, AddressOf SaveDataContextHandler
            End If
        End If
    End If
End Sub

 

Notice that the first thing this procedure does is to unregister itself from the LoadCompleted event. This is because there is no guarantee that all navigation in the application will use our command. This handler makes sense only if the ExtraData of the NavigationEventArgs really contains an object which should be set to the DataContext of the new page. Thus, we register it specifically for this case, and unregister it immediately after use. I call this design pattern a “One Shot Event”.

Then, we set the DataContext of the new page. By the time the event occurs, this page can be found in the Content property of the NavigationEventArgs. However, we cannot set its DataContext directly: WPF is quite particular about when exactly during the lifetime of a page the DataContext is set; some validation features will not work properly if it is set too early, so we define a special attached behaviour that waits until the new page’s Loaded event occurs and then sets the DataContext:

Public Shared ReadOnly DataContextProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("DataContext", GetType(Object), _
    GetType(Navigation), New PropertyMetadata(Nothing, AddressOf OnDataContextChanged))
Public Shared Function GetDataContext(ByVal d As DependencyObject) As Object
    Return d.GetValue(DataContextProperty)
End Function
Public Shared Sub SetDataContext(ByVal d As DependencyObject, ByVal value As Object)
    d.SetValue(DataContextProperty, value)
End Sub
Private Shared Sub OnDataContextChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    If e.NewValue IsNot Nothing Then
        If StickyComponentManager.GetIsLoaded(d) Then
            d.Dispatcher.Invoke(System.Delegate.CreateDelegate(GetType(Navigation), Nothing, "OnDataContextLoaded"), d, Nothing)
        Else
            StickyComponentManager.AttachEvent(d, FrameworkElement.LoadedEvent, AddressOf OnDataContextLoaded)
        End If
    End If
End Sub
Private Shared Sub OnDataContextLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim d As DependencyObject = TryCast(sender, DependencyObject)
    If d IsNot Nothing Then
        StickyComponentManager.DetachEvent(d, FrameworkElement.LoadedEvent, AddressOf OnDataContextLoaded)
        Dim data As Object = GetDataContext(d)
        d.SetValue(FrameworkElement.DataContextProperty, data)
        d.ClearValue(DataContextProperty)
    End If
End Sub

Notice that the handler for the Loaded event not only removes itself from the page, but also resets the attached property that held on to the DataContext so as to leave no references to this object in places where they are not expected.

We want to be able to return to the page and still see the same object as DataContext. So, we need to save the DataContext to the Journal when the page is about to be left. This is where it gets tricky (again). The method to add custom information to the Journal is to implement a class that inherits from CustomContentState and set it to the CustomContentState property of the NavigatingCancelEventArgs that are passed into the NavigationService.Navigating event when the user tries to navigate away from the current page. However, CustomContentState objects are not stored as object references, but in serialized format, being deserialized as they are needed. This means that our DataContext would be serialized as well, making it impossible to return to the same instance. In order to work around this, we save the DataContext to a shared Session object in the background, and retain only an integer index, which can be serialized easily, while allowing us to retrieve the object later:

Private Shared Sub SaveDataContextHandler(ByVal sender As Object, ByVal e As System.Windows.Navigation.NavigatingCancelEventArgs)
    Dim service As NavigationService = GetNavigationService(e.Navigator)
    If service IsNot Nothing Then
        RemoveHandler service.Navigating, AddressOf SaveDataContextHandler
        If Not e.Cancel AndAlso TypeOf service.Content Is DependencyObject Then
            Dim content As DependencyObject = service.Content
            Dim data As Object = content.GetValue(FrameworkElement.DataContextProperty)
            If data IsNot Nothing Then
                Dim id As Integer = GetDataContextId(content)
                id = Session.StoreReference(id, data)
                Dim state As NavigationContentState = New NavigationContentState(id, e.ContentStateToSave)
                e.ContentStateToSave = state
            End If
        End If
    End If
End Sub

 

Since we don’t want to keep the DataContext object alive longer than its object model supposes it is alive, the Session object uses WeakReferences for storing the DataContext.

When the user navigates back to the page, the CustomContentState’s Replay method is called. In this method, the DataContext is retrieved from the Session object and put into the new page’s DataContext property, using the same method as before. Since we cannot use a CustomContentState more than once, we also have to set up the handling of the Navigating event again, so that the DataContext is saved again when the page is left.

Public Overrides Sub Replay(ByVal navigationService As System.Windows.Navigation.NavigationService, ByVal mode As System.Windows.Navigation.NavigationMode)
    If _OriginalState IsNot Nothing Then
        _OriginalState.Replay(navigationService, mode)
    End If
    If _DataContextId <> -1 Then
        Dim content As DependencyObject = TryCast(navigationService.Content, DependencyObject)
        If content IsNot Nothing Then
            Dim data As Object = Session.RetrieveReference(_DataContextId)
            If data Is Nothing Then
                SetIsExpired(content, True)
                WPFGlue.Validation.Validation.SetSuppressErrorTemplate(content, True)
            Else
                SetDataContext(content, data)
                SetDataContextId(content, _DataContextId)
                AddHandler navigationService.Navigating, AddressOf SaveDataContextHandler
            End If
        End If
    End If
End Sub

 

Handling Expired Content

But what if the object that was the DataContext has be freed since the user last visited the page?

Handling this case gracefully almost drove me crazy. What I wanted to do was to navigate backwards one step and to clear the forward navigation stack so that the user couldn’t navigate to the page again. But I found no way of doing this that would work with all cases I wanted to cover: the API of the Journal is just too narrow. So, what I ended up doing was simply disabling the page and displaying a big fat Adorner on top of it, telling the user to go away and not come back… The Adorner can be styled using a ControlTemplate, a little bit like the Validation.ErrorTemplate, so you can make it less obnoxious; if anyone can tell me how to achieve what I originally wanted to do, I’ll be forever grateful.

Disabling the GoToPage Command

In the example, it makes no sense to try to go to the details page if no object is selected in the list. In cases like this, it is possible to disable the GoToPage command by setting the Navigation.CanNavigate property on the element that invokes the command. This can be done through a trigger, like in the example:

<Style TargetType="Button">
    <Style.Triggers>
        <Trigger Property="DataContext" Value="{x:Null}">
            <Setter Property="n:Navigation.CanNavigate" Value="False"/>
        </Trigger>
    </Style.Triggers>
</Style>

Or it could be done by binding this property to a property in a ViewModel.

Blocking Navigation Completely

Sometimes, it might be necessary to block navigation completely. On the details page, there is a ValidationRule that demands that the Name property contains a value. By binding the Navigation.BlockNavigation property to Validation.HasError on the Page element, one can disable all navigation away from the page until this property is filled. In order to support this behaviour, the following event handler is attached to the NavigationService.Navigating event:

Private Shared Sub BlockNavigationHandler(ByVal sender As Object, ByVal e As System.Windows.Navigation.NavigatingCancelEventArgs)
    Dim service As NavigationService = GetNavigationService(sender)
    If service IsNot Nothing Then
        If service.Content IsNot Nothing Then
            e.Cancel = GetBlockNavigation(service.Content)
            'Allow Fragment navigation
            If e.Cancel And Uri.Compare(service.CurrentSource, service.Source, UriComponents.PathAndQuery, UriFormat.UriEscaped, StringComparison.InvariantCulture) = 0 Then
                e.Cancel = False
            End If
        End If
    End If
End Sub

 

Testing the Component

You will find the example application NavigationExample in the WPFGluePublished solution on the Downloads page. In this example, each page contains a “GC” button which forces the .Net garbage collection. By running the example in the debugger and following the debug output, you can see the lifetime events of the pages, and by forcing the garbage collection you can verify that the page objects get finalized when they are no longer needed, thus proving that the Navigation.Command didn’t leave behind any dangling references and doesn’t cause any memory leaks.

By adding some customers, editing their details, removing them, and going back using the NavigationWindow’s Back button, you can test the behaviour for pages whose DataContext has expired.

Conclusion

This example shows how it is possible to pass objects from page to page using XAML only, and how one can handle forwards and backwards navigation using object references. However, there are still some limitations: I don’t really like displaying expired pages, and since the Session object uses weak references, it might not be suitable for partial trust scenarios where there is no permission to execute unmanaged code.

In posts to come, I want to explore the possibilities of defining a navigation topology as a central resource. However, for this I will need the complete Sticky Component Framework, about which I will write as soon as possible…

The Stateless Sticky ViewModel Pattern

December 6, 2009 1 comment

What do you do when you want some functionality in your application that is too small to justify creating a full ViewModel, or that you want to reuse in different places and applications? You create a Sticky ViewModel. A Sticky ViewModel is a ViewModel that doesn’t replace the DataContext, but attaches itself to it and provides some functionality that closes a gap between what the model can do and what WPF needs.

While I want to introduce Sticky ViewModels in general in a later post, today I had the idea for a special case which is easy to implement and demonstrate: the Stateless Sticky ViewModel.

What is a Stateless Sticky ViewModel?

A Stateless Sticky ViewModel is a ViewModel that provides all its functionality in shared (static in C#) code, and stores all its state in attached properties. This pattern is suitable for cases where you have a simple dependency between a small group of values, all of which could be data bound.

For example, you might have a data model which exposes the name of a person as one property. In the UI, you’d like to edit first name and last name separately.

Implementing the Pattern

The first step in implementing the pattern would be to create a class that provides the code and attached properties. Since the class needs to define attached properties, it has to be based on DependencyObject. Then it would have to provide code in order to determine first name, last name and full name depending on what other information is already known.

Public Class NameViewModel
    Inherits DependencyObject

    Public Shared Function DetermineLastName(ByVal fullname As String) As String
        Dim result As String = fullname
        If result Is Nothing Then
            result = String.Empty
        Else
            result = result.Trim
            Dim pos As Integer = result.LastIndexOf(" "c)
            If pos >= 0 Then
                result = result.Substring(pos).Trim
            Else
                result = String.Empty
            End If
        End If
        Return result

    End Function

    Public Shared Function DetermineFirstName(ByVal fullname As String) As String
        Dim result As String = fullname
        If result Is Nothing Then
            result = String.Empty
        Else
            result = result.Trim
            Dim pos As Integer = result.LastIndexOf(" "c)
            If pos >= 0 Then
                result = fullname.Substring(0, pos).Trim
            End If
        End If
        Return result
    End Function

    Public Shared Function DetermineFullName(ByVal firstname As String, ByVal lastname As String) As String
        If firstname Is Nothing Then
            firstname = String.Empty
        Else
            firstname = firstname.Trim
        End If
        If lastname Is Nothing Then
            lastname = String.Empty
        Else
            lastname = lastname.Trim
        End If
        Dim result As String = String.Empty
        If String.IsNullOrEmpty(firstname) Or String.IsNullOrEmpty(lastname) Then
            result = firstname & lastname
        Else
            result = firstname & " " & lastname
        End If
        Return result
    End Function

 

Notice that all these functions are declared “Shared”. This means that we actually never have to create an instance of our ViewModel.

The next bit would be to declare the attached properties which contain the data and are bound to model and UI. Let’s take the FirstName property as an example:

Public Shared ReadOnly FirstNameProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("FirstName", GetType(String), GetType(NameViewModel), _
    New FrameworkPropertyMetadata(String.Empty, _
    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, _
    AddressOf OnFirstNameChanged))
Public Shared Function GetFirstName(ByVal d As DependencyObject) As String
    Return d.GetValue(FirstNameProperty)
End Function
Public Shared Sub SetFirstName(ByVal d As DependencyObject, ByVal value As String)
    d.SetValue(FirstNameProperty, value)
End Sub
Private Shared Sub OnFirstNameChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    If CStr(e.NewValue) <> CStr(e.OldValue) And GetChangeInitiator(d) Is Nothing Then
        SetChangeInitiator(d, FirstNameProperty)
        SetFullName(d, DetermineFullName(e.NewValue, GetLastName(d)))
        SetChangeInitiator(d, Nothing)
    End If
End Sub

 

The first three declarations are the standard declaration of an attached property: shared get and set methods, and the registration of the property with the property system. It contains a flag that causes the property to update any property to which it is bound by default, because this is what is probably supposed to happen if someone binds this property to their data model. But the most important point in this declaration is that it registers the OnFirstNameChanged method to be called every time the value of the property changes.

In the OnFirstNamedChanged method, we need to make the FullName property reflect the new value of the FirstName property. So, we need to call DetermineFullName and set the FullName property to the new value. In order to do that, we need the last name; since NameViewModel itself is stateless, we take this information from another attached property on the same object.

If we change the FullName property, of course its change handler will try to update the FirstName property. In order to avoid endless circular calls, we only set the FullName property if the value of the FirstName has really changed.

Tracking Dependencies Between the ViewModel’s Properties

But what is the meaning of the bit about the ChangeInitiator? Well, what would be the difference if we set “John A.” to the FirstName or to the FullName? In the first case, we’d expect the LastName to remain unchanged, in the second case we’d expect it to be set to “A.”. Since changing the FirstName changes the FullName and changing the FullName in turn changes the LastName, we need a way to determine that the LastName shouldn’t be changed in this special case.

In general, if we have multiple interdependent properties which all could be changed by the user, we need to make sure that the changes are propagated in the right direction. One way which helps in simple cases like this is storing a reference to the property which originated the change. Since we cannot store state in the ViewModel, we need another attached property:

Private Shared ReadOnly ChangeInitiatorProperty As DependencyProperty = DependencyProperty.RegisterAttached("ChangeInitiator", GetType(DependencyProperty), GetType(NameViewModel))
Private Shared Function GetChangeInitiator(ByVal d As DependencyObject) As DependencyProperty
    Return d.GetValue(ChangeInitiatorProperty)
End Function
Private Shared Sub SetChangeInitiator(ByVal d As DependencyObject, ByVal value As DependencyProperty)
    d.SetValue(ChangeInitiatorProperty, value)
End Sub

This property is declared private, since it is intended for internal use, only. Using the ChangeInitiator property, we can make sure that property changes trigger other properties to change only if it is necessary.

Using the NameViewModel

We use the NameViewModel by binding the model’s Name property to the FullName attached property, and the user controls to the other properties.

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:ex="clr-namespace:SharedStickyViewModelExample"
    Title="Shared Sticky ViewModel Example" Height="300" Width="300" x:Name="Window">
    <Grid x:Name="SVMHost" DataContext="{Binding ElementName=Window}"
          ex:NameViewModel.FullName="{Binding Path=MyName}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0">First Name:</Label>
        <TextBox Grid.Row="0" Grid.Column="1"
          Text="{Binding ElementName=SVMHost,
          Path=(ex:NameViewModel.FirstName)}"/>
        <Label Grid.Row="1" Grid.Column="0">Last Name:</Label>
        <TextBox Grid.Row="1" Grid.Column="1"
          Text="{Binding ElementName=SVMHost,
          Path=(ex:NameViewModel.LastName)}"/>
        <Label Grid.Row="2" Grid.Column="0">Full Name:</Label>
        <TextBox Grid.Row="2" Grid.Column="1"
          Text="{Binding ElementName=SVMHost,
          Path=(ex:NameViewModel.FullName)}"/>
        <Label Grid.Row="3" Grid.Column="0">Model Value:</Label>
        <Label Grid.Row="3" Grid.Column="1" Content="{Binding Path=MyName}"/>
    </Grid>
</Window>

 

Notice that since the ViewModel needs an object to which it can attach its properties, we name the Grid that hosts the other controls “SVMHost”, and refer to when binding to the Stateless Sticky ViewModel’s properties.

Conclusion

The approach introduced here has the advantage that it is very lightweight and has no dependencies whatsoever except those imposed by WPF itself. However, because the Stateless Sticky ViewModel is kind of scattered on the XAML elements that make up the view, it is prone to become confusing when the properties become many and their relationships become complicated. If you need a more structured approach, you could try a full Sticky Component or a Mini-ViewModel.

Coming Soon…

Shortly, I want to introduce Sticky Components and the WPFGlue Sticky Component Framework. However, before I do that, I want to get the Navigation Command off my desk, which I promised in one of the first posts…

Using .Net Localized String Resources in WPF

December 5, 2009 1 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&quot;
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:l="https://wpfglue.wordpress.com/localization&quot;
    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.