Archive

Posts Tagged ‘ViewModel’

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…

WPFGlue Patterns

November 5, 2009 4 comments

Let’s recapitulate the design requirements for WPFGlue components:

  • You shall not duplicate functionality that is already in the WPF framework.
  • You shall not mess with WPF’s correct function, like keeping objects from being freed and the like evil practices.
  • You shall not require code behind.
  • You shall not demand sub-classing controls.
  • You shall not demand interface implementations from your business objects (unless the interfaces are part of WPF itself), nor common base classes, nor any other construct that commits the developer to a strong coupling between your glue and his code.

The following design patterns have proved suitable for development under these constraints:

Sticky Properties (a.k.a. Attached Properties)

Attached properties can be used to extend the state of any element in WPF. They can be used in XAML, and neither the framework-supplied controls nor the business object model need to know them. So, they are suitable for WPFGlue.

Built-in examples for attached properties are the Canvas.Left and Canvas.Top properties, which the WPF Canvas uses to store the positions of its child elements.

Sticky Behaviours (a.k.a. Attached Behaviours)

Attached behaviours are a special kind of attached properties that register event handlers on the elements they are attached to. The event handlers provide additional functionality to the elements which is triggered through the elements standard events, while the implementation of the event handlers is contained in the class that defines the attached property.

A built-in example for a sticky behaviour is the SpellCheck.IsEnabled property, which adds the functionality of spell checking to controls based on TextBoxBase.

Josh Smith writes about attached behaviours in this article; Attached behaviours are introduced and defined by Nikhil Kothari here.

Sticky CommandBindings

Work like sticky behaviours, with the difference that they don’t handle events, but standard routed commands. Attaching a sticky CommandBinding to a WPF FrameworkElement means that this element will be able to handle the command, without having to change the original implementation of the element. A sticky CommandBinding should add / remove the CommandBindings when the value of an attached property changes.

Pete O’Hanlon gives an almost perfect example of this pattern here. Almost perfect because he uses a class CommandBinding instead of the CommandBindings collection, but this might be justified on his case since he expects the command to be used by many elements per page.

Sticky ViewModels (a.k.a. Mini-ViewModels)

Sticky ViewModels are ViewModels that don’t wrap a business object, but attach themselves to it and expose a specialized, reusable bit of functionality through their own properties and commands. Typically, a sticky ViewModel wouldn’t be the DataContext of a FrameworkElement, but it would be attached to the element as a sticky property and use the FrameworkElement’s DataContext to find the model it is supposed to work on. Controls inside the scope of the FrameworkElement would then bind to properties of the sticky ViewModel in order to benefit from it.

Quite often jobs like this are done through converters (Converters are classes that implement the IValueConverter interface). However, I view this as converter abuse. A converter is not intended for anything but type conversions, translating data between different data types and string presentations. If the intention of the solution is rather to achieve some more complex interaction between the View and the Model, I’d recommend a sticky ViewModel instead. Usually these cases are discovered by feeling the need to pass more information into the conversion routines than is readily available…

While Mini-ViewModel seems to imply that the functionality of such a class would normally be quite limited, I could imagine whole page ViewModels using this technique.

Colin Eberhardt describes the Mini-ViewModel pattern here and gives a nice example.

ViewModelKits

A ViewModelKit would be a class or set of related classes that can be instantiated and configured as XAML resources, and then be used as ViewModels through referencing the resource.

A built-in example for a ViewModelKit would be the CollectionViewSource class, which can be used to configure views of business object collections.

Sticky Markup (a.k.a. Custom Markup Extensions)

Custom markup extensions are classes derived from System.Windows.Markup.MarkupExtension, which can be used in XAML attributes using the markup extension syntax, like {Binding value}. Writing custom markup extensions is not documented very well in the WPF documentation, and I use them sparingly. They need to be initialized in place, i.e. where they are used in XAML. This limits their “stickiness” to situations where the information they need to do their job is either very simple or can be discovered from the built-in application framework. For example, I found them useful for accessing localized resources, application settings, or the current UI language.