Single Responsibility Misuse

The Inquisitive Coder recently made a post about MVVM being overrated. He states a preference for MVP (a great pattern, and a great implementation of it for WPF is Caliburn). Whilst patterns are there to be abused used as the needs of a project dictate. I think the arguments are subject to a common flaw in peoples thinking, especially around front-end patterns.

The primary attack on MVVM is that it violates the Single Responsibility Pattern (SRP). In short SRP states that a class should have one and only one reason to change. David suggests that ViewModels have 2 reasons to change (paraphrased)

  1. If, the Model changes
  2. If, the View requirements change ( i.e. needs more model data displayed/edited)

This is not what I take issue with, it’s true. But it’s true of all bridge classes and interfaces; DTO’s, Data Layers and UI’s. A bridge depends on both sides, but that is it’s responsibility. Sometimes the SRP is looked at from a much to granular view point and it leads to anaemic domains. People want to separate data from behaviour it seems to be human nature. I regularly hear SRP as the reason why this is good.

From Daniels post the 2 responsibilities a ViewModel has are:

  • communication/interaction with the Model
  • making data (from the Model) available so it can be displayed

Rephrased

  • Behaviour
  • Data

The SOLID principles (of which the S stands for SRP) are a great way to evaluate code and patterns, but they take a back seat to the tenets of OO Design

  • encapsulation
  • inheritance (I think this should be renamed Inheritance and Composition or removed as being implicit in polymorphism)
  •  polymorphism

Encapsulation states thou shall not whore out your data to other classes. Whilst UI is a grey area for encapsulation as we all need to display our data. MVVM takes a step closer to encapsulation by keeping the behaviour and data together.

In my opinion ViewModels contain their data and the behaviour that relies on it in one place with the single responsibility of Modelling a View.  If your ViewModels are two big or unwieldy then look at decomposing them into self contained Commands (with both data and behaviour) and/or smaller ViewModels with clearer responsibilities (an upcoming post).

Side Note
After 18 months of MVVM I too am starting to wonder if the extra work required to maintain two-way synchronization (View-Model) is worth it for most scenarios. But that’s another topic and to do with choosing the right tool for the job. I still believe MVVM is the most elegant pattern for solving the problem it solves.

MVVM Validation and Type Checking

This post is based on Present.Validation a part of the Present project at codeplex.

ScreenShot

Present.Validation is an implementation of IDataErrorInfo for MVVM which uses a pluggable contributor pattern to provide validation. It currently has a single contributor for use with System.ComponentModel.DataAnnotations. Validation can then be acheived by decorating the ViewModel properties with attributes

 [Range(0, 120, ErrorMessage = "Age must be between 0 and 120")]
public int Age 
 <StackPanel>
        <v:BrokenBindingNotifier PropertiesWithBrokenBindings="{Binding Validator.BrokenBindings}" />
        <Controls:Form Padding="20">
            <TextBox Controls:FormItem.LabelContent="_Firstname" Text="{v:ValidatedBinding FirstName}" />
            <TextBox Controls:FormItem.LabelContent="_Lastname" Text="{v:ValidatedBinding LastName}" />
            <TextBox Controls:FormItem.LabelContent="_Age" Text="{v:ValidatedBinding Age, TargetNullValue={x:Static System:String.Empty}}" />
        </Controls:Form>
        <Button Content="Save" Command="{Binding SaveCommand}" Width="50"/>
    </StackPanel>

There are plenty examples of implementing IDataErrorInfo on the web, but they tend to ignore broken bindings. Broken Bindings occur for a number of reasons such as the binding source can’t be found or the Path is invalid. They also occur when the input value can’t be coerced back to the data type of the source.

For example if someone tries to enter Twelve into a text box bound to the int Age property the binding will break; leaving the value of Age unset and the ViewModel unaware of the error.

Present.Validation tackles this via a binding notifier which binds broken bindings to the a validator. (Line 2 of the Xaml). It also uses a custom binding ValidatedBinding which just sets all the needed error checking properties to true.

Gotcha: Nullable Types

Watch out when using nullable types that you set the TargetNullValue as for the Age binding. An empty textbox is set to String.Empty which isn’t compatible with int?. So we need to tell WPF that String.Empty is equal to Null.

Window.Close() from XAML

It’s been a long time since I last posted. Since then I have been working on my first WPF contract. I have definately drank the cool aid, I love WPF (when MVVM is being used anyway).

One thing I haven’t been able to find any info on is how to close a window or dialog from XAML. I tried and tried and then gave up. Until now!

Having discovered the power of Attached Behaviours, I decided to write one that would close a window. All you need do then is create a data trigger that watches a CloseSignal on the ViewModel and sets the Close property to true.

public static class WindowCloseBehaviour
{
public static void SetClose(DependencyObject target, bool value)
{
target.SetValue(CloseProperty, value);
}
public static readonly DependencyProperty CloseProperty =
DependencyProperty.RegisterAttached(
"Close",
typeof(bool),
typeof(WindowCloseBehaviour),
new UIPropertyMetadata(false, OnClose));
private static void OnClose(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool && ((bool)e.NewValue))
{
Window window = GetWindow(sender);
if (window != null)
window.Close();
}
}
private static Window GetWindow(DependencyObject sender)
{
Window window = null;
if (sender is Window)
window = (Window)sender;
if (window == null)
window = Window.GetWindow(sender);
return window;
}
}

and then in your XAML

<Style.Triggers>
   <DataTrigger Binding="{Binding CloseSignal}" Value="true">
        <Setter Property="Behaviours:WindowCloseBehaviour.Close" Value="true" />
   </DataTrigger>
</Style>