Something that we regularly do at work is add additional properties to business objects that are only for use on the client (isDirty
, HasChanged
, …etc.). We have always just added these to the business object directly and thought nothing of it, but after listening to a Hanselminutes interview with Laurent Bugnion, I discovered the ‘other’ use for a ViewModel
– wrapping client side business objects to expose new properties. This is also useful in removing the overhead of viewing objects via a converter.
I have recently started looking at ASP.NET MVC as well and the way to pass things between controller and view is by using a ViewModel
class. This overload of the term had not translated into being useable in MVVM as well so what follows is a short, simple example of using a ViewModel
to better model some data to fit a view.
Let's say we have a meeting object that we have extracted from a service and it is now ready to display on our client. This business object has no real relation to the ORM version of the same object if we are correctly differentiating between them and client facing business objects so it has no in-built tracking to determine if it has changed and we don’t want to send it back to the service for updating if nothing has changed. Our class looks like this:
public class Meeting
{
public Meeting()
{
}
public string Subject { get; set; }
public string Location { get; set; }
public DateTime StartTime { get; set; }
public List<Person> Attendees { get; set; }
}
This is a very simple class to define our meeting but it has no way of knowing if it has changed and also the StartTime
property is stored on the server as UTC but the client could be anywhere.
Taking the latter problem first, you may be tempted to bind directly to the StartTime
property of this object and use a converter to convert to the appropriate time zone based on the user culture (as I have done many times). As for the problem with a meeting change, there has to be some way of telling an object that it has changed. Both of these problems can be solved with a MeetingViewModel
. This is a VM that follows the decorator pattern and could be implemented as follows:
public class MeetingViewModel
{
private Meeting inner;
public MeetingViewModel(Meeting m)
{
inner = m;
}
public Meeting Inner
{
get
{
return inner;
}
}
public string Location
{
get
{
return inner.Location;
}
set
{
inner.Location = value;
}
}
public DateTime StartTime
{
get
{
return inner.StartTime;
}
set
{
inner.StartTime = value;
}
}
public List<Person> Attendees
{
get
{
return inner.Attendees;
}
set
{
inner.Attendees = value;
}
}
public DateTime LocalStartTime
{
get
{
return inner.StartTime.ToLocalTime();
}
set
{
inner.StartTime = value.ToUniversalTime();
}
}
public bool IsDirty { get; set; }
}
Here, we now have a nice property that sorts out the StartTime
into a local time for us and a property that determines if this object has changed. In your VM that controls your view, you now operate on an object of type MeetingViewModel
and all bindings are to this object. When it comes time to save the object, you simply need to check if you have set the dirty flag and if so, extract the internal Meeting
object using the get-only property and send it back to the service.
This is also a nice way of working with business objects that you have no control over such as from an external web service or third party library. Once you start to understand the power of this approach over simply using converters, you will find yourself writing VMs for all of your business objects that you need to modify slightly on the client side when you need properties like the above or other bits of information like calculated properties.