Adventures in MVVM – My ViewModel Base

Posted by Brian Genisio's House Of Bilz on Geeks with Blogs See other posts from Geeks with Blogs or by Brian Genisio's House Of Bilz
Published on Sun, 09 May 2010 00:07:02 GMT Indexed on 2010/05/11 2:55 UTC
Read the original article Hit count: 788

Filed under:

More Adventures in MVVM Shout it kick it on DotNetKicks.com

First, I’d like to say: THIS IS NOT A NEW MVVM FRAMEWORK. I tend to believe that MVVM support code should be specific to the system you are building and the developers working on it.  I have yet to find an MVVM framework that does everything I want it to without doing too much.  Don’t get me wrong… there are some good frameworks out there.  I just like to pick and choose things that make sense for me.  I’d also like to add that some of these features only work in WPF.  As of Silveright 4, they don’t support binding to dynamic properties, so some of the capabilities are lost.

That being said, I want to share my ViewModel base class with the world.  I have had several conversations with people about the problems I have solved using this ViewModel base.  A while back, I posted an article about some experiments with a “Rails Inspired ViewModel”.  What followed from those ideas was a ViewModel base class that I take with me and use in my projects.  It has a lot of features, all designed to reduce the friction in writing view models. I have put the code out on Codeplex under the project: ViewModelSupport.

Finally, this article focuses on the ViewModel and only glosses over the View and the Model.  Without all three, you don’t have MVVM.  But this base class is for the ViewModel, so that is what I am focusing on.

Features:

  1. Automatic Command Plumbing
  2. Property Change Notification
  3. Strongly Typed Property Getter/Setters
  4. Dynamic Properties
  5. Default Property values
  6. Derived Properties
  7. Automatic Method Execution
  8. Command CanExecute Change Notification
  9. Design-Time Detection
  10. What about Silverlight?

Automatic Command Plumbing

This feature takes the plumbing out of creating commands.  The common pattern for commands in a ViewModel is to have an Execute method as well as an optional CanExecute method.  To plumb that together, you create an ICommand Property, and set it in the constructor like so:

Before

public class AutomaticCommandViewModel
{
    public AutomaticCommandViewModel()
    {
        MyCommand = new DelegateCommand(Execute_MyCommand, CanExecute_MyCommand);
    }

    public void Execute_MyCommand()
    {
        // Do something
    }

    public bool CanExecute_MyCommand()
    {
        // Are we in a state to do something?
        return true;
    }

    public DelegateCommand MyCommand { get; private set; }
}

With the base class, this plumbing is automatic and the property (MyCommand of type ICommand) is created for you.  The base class uses the convention that methods be prefixed with Execute_ and CanExecute_ in order to be plumbed into commands with the property name after the prefix.  You are left to be expressive with your behavior without the plumbing.  If you are wondering how CanExecuteChanged is raised, see the later section “Command CanExecute Change Notification”.

After

public class AutomaticCommandViewModel : ViewModelBase
{
    public void Execute_MyCommand()
    {
        // Do something
    }

    public bool CanExecute_MyCommand()
    {
        // Are we in a state to do something?
        return true;
    }
}

 

Property Change Notification

One thing that always kills me when implementing ViewModels is how to make properties that notify when they change (via the INotifyPropertyChanged interface).  There have been many attempts to make this more automatic.  My base class includes one option.  There are others, but I feel like this works best for me.

The common pattern (without my base class) is to create a private backing store for the variable and specify a getter that returns the private field.  The setter will set the private field and fire an event that notifies the change, only if the value has changed.

Before

public class PropertyHelpersViewModel : INotifyPropertyChanged
{
    private string text;
    public string Text
    {
        get { return text; }
        set
        {
            if(text != value)
            {
                text = value;
                RaisePropertyChanged("Text");
            }
        }
    }

    protected void RaisePropertyChanged(string propertyName)
    {
        var handlers = PropertyChanged;
        if(handlers != null)
            handlers(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

This way of defining properties is error-prone and tedious.  Too much plumbing.  My base class eliminates much of that plumbing with the same functionality:

After

public class PropertyHelpersViewModel : ViewModelBase
{
    public string Text
    {
        get { return Get<string>("Text"); }
        set { Set("Text", value);}
    }
}

 

Strongly Typed Property Getters/Setters

It turns out that we can do better than that.  We are using a strongly typed language where the use of “Magic Strings” is often frowned upon.  Lets make the names in the getters and setters strongly typed:

A refinement

public class PropertyHelpersViewModel : ViewModelBase
{
    public string Text
    {
        get { return Get(() => Text); }
        set { Set(() => Text, value); }
    }
}

 

Dynamic Properties

In C# 4.0, we have the ability to program statically OR dynamically.  This base class lets us leverage the powerful dynamic capabilities in our ecosystem. (This is how the automatic commands are implemented, BTW)  By calling Set(“Foo”, 1), you have now created a dynamic property called Foo.  It can be bound against like any static property.  The opportunities are endless.  One great way to exploit this behavior is if you have a customizable view engine with templates that bind to properties defined by the user.  The base class just needs to create the dynamic properties at runtime from information in the model, and the custom template can bind even though the static properties do not exist. All dynamic properties still benefit from the notifiable capabilities that static properties do.

For any nay-sayers out there that don’t like using the dynamic features of C#, just remember this: the act of binding the View to a ViewModel is dynamic already.  Why not exploit it?  Get over it :)

Just declare the property dynamically

public class DynamicPropertyViewModel : ViewModelBase
{
    public DynamicPropertyViewModel()
    {
        Set("Foo", "Bar");
    }
}

Then reference it normally

<TextBlock Text="{Binding Foo}" />

 

Default Property Values

The Get() method also allows for default properties to be set.  Don’t set them in the constructor.  Set them in the property and keep the related code together:

public string Text
{
    get { return Get(() => Text, "This is the default value"); }
    set { Set(() => Text, value);}
}

 

Derived Properties

This is something I blogged about a while back in more detail.  This feature came from the chaining of property notifications when one property affects the results of another, like this:

Before

public class DependantPropertiesViewModel : ViewModelBase
{
    public double Score
    {
        get { return Get(() => Score); }
        set
        {
            Set(() => Score, value);
            RaisePropertyChanged("Percentage");
            RaisePropertyChanged("Output");
        }
    }

    public int Percentage
    {
        get { return (int)(100 * Score); }
    }

    public string Output
    {
        get { return "You scored " + Percentage + "%."; }
    }
}

The problem is: The setter for Score has to be responsible for notifying the world that Percentage and Output have also changed.  This, to me, is backwards.    It certainly violates the “Single Responsibility Principle.” I have been bitten in the rear more than once by problems created from code like this.  What we really want to do is invert the dependency.  Let the Percentage property declare that it changes when the Score Property changes.

After

public class DependantPropertiesViewModel : ViewModelBase
{
    public double Score
    {
        get { return Get(() => Score); }
        set { Set(() => Score, value); }
    }

    [DependsUpon("Score")]
    public int Percentage
    {
        get { return (int)(100 * Score); }
    }

    [DependsUpon("Percentage")]
    public string Output
    {
        get { return "You scored " + Percentage + "%."; }
    }
}

 

Automatic Method Execution

This one is extremely similar to the previous, but it deals with method execution as opposed to property.  When you want to execute a method triggered by property changes, let the method declare the dependency instead of the other way around.

Before

public class DependantMethodsViewModel : ViewModelBase
{
    public double Score
    {
        get { return Get(() => Score); }
        set
        {
            Set(() => Score, value);
            WhenScoreChanges();
        }
    }

    public void WhenScoreChanges()
    {
        // Handle this case
    }
}

After

    public class DependantMethodsViewModel : ViewModelBase
    {
        public double Score
        {
            get { return Get(() => Score); }
            set { Set(() => Score, value); }
        }

        [DependsUpon("Score")]
        public void WhenScoreChanges()
        {
            // Handle this case
        }
    }

 

Command CanExecute Change Notification

Back to Commands.  One of the responsibilities of commands that implement ICommand – it must fire an event declaring that CanExecute() needs to be re-evaluated.  I wanted to wait until we got past a few concepts before explaining this behavior.  You can use the same mechanism here to fire off the change.  In the CanExecute_ method, declare the property that it depends upon.  When that property changes, the command will fire a CanExecuteChanged event, telling the View to re-evaluate the state of the command.  The View will make appropriate adjustments, like disabling the button.

DependsUpon works on CanExecute methods as well

public class CanExecuteViewModel : ViewModelBase
{
    public void Execute_MakeLower()
    {
        Output = Input.ToLower();
    }

    [DependsUpon("Input")]
    public bool CanExecute_MakeLower()
    {
        return !string.IsNullOrWhiteSpace(Input);
    }

    public string Input
    {
        get { return Get(() => Input); }
        set { Set(() => Input, value);}
    }

    public string Output
    {
        get { return Get(() => Output); }
        set { Set(() => Output, value); }
    }
}

 

Design-Time Detection

If you want to add design-time data to your ViewModel, the base class has a property that lets you ask if you are in the designer.  You can then set some default values that let your designer see what things might look like in runtime.

Use the IsInDesignMode property

public DependantPropertiesViewModel()
{
    if(IsInDesignMode)
    {
        Score = .5;
    }
}

 

What About Silverlight?

Some of the features in this base class only work in WPF.  As of version 4, Silverlight does not support binding to dynamic properties.  This, in my opinion, is a HUGE limitation.  Not only does it keep you from using many of the features in this ViewModel, it also keeps you from binding to ViewModels designed in IronRuby.  Does this mean that the base class will not work in Silverlight?  No.  Many of the features outlined in this article WILL work.  All of the property abstractions are functional, as long as you refer to them statically in the View.  This, of course, means that the automatic command hook-up doesn’t work in Silverlight.  You need to plumb it to a static property in order for the Silverlight View to bind to it.  Can I has a dynamic property in SL5?

 

 

Good to go?

So, that concludes the feature explanation of my ViewModel base class.  Feel free to take it, fork it, whatever.  It is hosted on CodePlex.  When I find other useful additions, I will add them to the public repository.  I use this base class every day.  It is mature, and well tested.  If, however, you find any problems with it, please let me know!  Also, feel free to suggest patches to me via the CodePlex site.  :)

© Geeks with Blogs or respective owner