Home > Patterns > The Sticky ViewModel Pattern

The Sticky ViewModel Pattern


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.

Advertisements
  1. No comments yet.
  1. April 1, 2010 at 14:15

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: