The View Part 1 - Definition and Bindings

Audience
In (finally) continuing this series, it is important to define the audience, as follows:
  • You know WPF pretty well, at least enough to know there is a DataContext property on a UserControl or the like.
  • You know C# pretty well, at least to the point of knowing what reflection is even if you've never used it in your code.
Source Code
Much of the source code used in this series will be derived from my project.  There is a clickonce deployment setup for it, but please download the code and peruse it.  Become comfortable with it and tear it apart. It is not meant to be an ideal development project for all things in software, just an exercise in applying MVVM to a very specific application.  

Download it. Break it, fix it, break it, fix it...do that enough and you will learn MVVM. :)

What It Is and Isn't
Going forward, when I say 'View', you can just think of a UserControl in WPF/Silverlight.  And when I say 'ViewModel', just think of some class that implements INotifyPropertyChanged that is set to the view.DataContext property of the UserControl.  Got it?  If you do, you begin to understand the 'magic' of MVVM bindings between a view and viewmodel.  The rest isn't magic of course; it's my favorite piece of .NET: reflection.

The View in MVVM is nothing more than a definition of the UI in code/XAML.  Not what it does or how it does it, just a code representation of different UI components.  In XAML, the view also includes any details about how UI pieces in that particular control interact with each other, like:
  • How selecting a drop down item enables a checkbox somewhere else.  
  • How a certain animation is begun when a view is loaded.
  • How a data trigger from a binding shows one part of the view and hides another.
Some MVVM purists actually just have XAML files in the project with absolutely no code behind to ensure that the View can never know about anything else.  I do not take this purist approach because sometimes I find it handy to do some things in code behind that would be difficult or hard to debug/maintain if I do them in XAML.  An example is hiding a button group in a ribbon bar if all of its children have been disabled, but I'm sure there are others.  I would recommend doing as little as possible in code behind, as a general rule.

The View's main responsibilities:
  • Ensuring a UI workflow that meets the expectations of the user in an ergonomic fashion and adheres to the UI requirements and workflow of the feature.
...and that's it.

The View does not care about:
  • Back end data
  • Middle layers
  • Business logic
  • Communication with other views (generally) or other layers in the code - this is a Controller's job (discussed later in the series)
  • What DataContext object is actually bound to it

It only has a very shallow knowledge of its data context, ideally through an interface so that you can swap out data context's at will for runtime or unit tests.

The View is something of a megalomaniac - it only cares and knows about itself.  If there are nested UserControls in the view, I prefer View Injection (discussed later) to simplify both the XAML and, later, the logic in the ViewModel.  View injection is like IoC applied to XAML...and is really cool (and immensely beneficial)...and will be discussed in a subsequent post, so hold on. :)

Bindings in a View
When the DataContext property of the View is set at runtime (or even design time in a designer), the View has certain definitions set inside of it to get its values for explicitly bound properties on pieces within it from the DataContext property's value (this is the default behavior unless a different source is set in the binding).  Does that sound cryptic?  Well here's a simple example of what I mean.

If we have something like this in our XAML:
...then at runtime, the View will look for the following (and this is the important part):
A public property that it finds via reflection on the DataContext named CoolText that has both a public getter and setter.  
  - If you want it readonly, set the 'Mode' in the binding
"CoolText" cannot be a field or a method.  It must be a public property.

More on all of this when we talk about ViewModels.

But first, what about a more complex binding?  Like this:
That would be an instance of a class that has a property Name that is an instance of a class that has a property First (the return type let's say is a string), like this:
public class Person
{
    public Name Name { get; set; }
}

public class Name
{
    public string First { get; set; }
    public string Last { get; set; }
}

This type of complex binding is totally fine, and even encouraged to encapsulate logic where it makes sense in your code (avoid creating a Manager class at all times in code - I should write a post on that).  Keep in mind what I said about the DataContext referencing an instance of some type and then just make the logical leap that your binding definition in XAML can access any public property anywhere in the DataContext's logical tree, i.e. your ViewModel's logical tree.

Give it some practice by creating a simple app that displays a person's data.  Set the datacontext at runtime (like in the code behind's ctor) to an instance of a Person class and play around with the bindings to see what's possible.  Soon, you'll start asking a bunch of questions that will lead you to the concepts of a Controller (discussed next) and a ViewModel (discussed after that).

Hopefully this simple explanation helps in applying a high-level pattern like MVVM to your code in WPF/Silverlight.  There are some big hurdles in doing so, but once you do...it's just awesome. :)  

Comments

Popular posts from this blog

35x Improved T-SQL LevenShtein Distance Algorithm...at a cost

The hidden workings of __doPostBack - Full or Partial Postback?

Facing Death...dum de dum dum