MVVM for non MVVM people : Part 1

In my recent explosion of Silverlight development I have seen the term Model View ViewModel being thrown around a lot. I really enjoy writing Silverlight apps so I am interested in technologies that make that experience better/easier.

The MVVM pattern is easy to pick up for an ASP.NET MVC developer because both are working towards the same separation goals. While my use with MVVM has been relatively limited so far, I have found some great applications for it and I also recognize that it may not be the right tool for every Silverlight application.

I recently created a Silverlight Video Player from scratch for a presentation that follows the MVVM pattern and I thought it worked out great. I want to take you through that process now.

It is easy to visualize the result. I just want a way to wire up a video player. When the user presses play on the video player I want it to invoke some command and execute some code. Knowing that, I want to make a command for each button or each action that might normally have code behind in Silverlight.

VideoPlayerMockup

Why Would You Do This?

Because MVVM is a buzz word and employers like it when you know buzz words! That may be true, but some people are turned off of by buzz word topics. If you are one of those people, just try and think of MVVM as a separation of concerns between a UI artist and a developer. You don't want the artist messing around with your code, but if your code is in the code behind you do not have a choice. Your UI artist will be editing the xaml directly which will affect how the code behind file is generated. Alternatively, the UI developer does not want the untrained eye of the software engineer to alter the artist's work.

There are also benefits with testing. Some may not find value in that benefit but you do if you regularly test code. MVC developers should appreciate and completely understand.

Getting Started

There are a few general thoughts on how to get started. Some say to start with your view and create your model off of the components you can see that you need to wire up. For example, if you drop on a play, stop and pause button on your UI, you know that your model needs to support a play, stop, and pause command that the UI will call.

Others prefer to create the model before creating the UI. This way you can build the UI off of the public exposure of the model.

I actually prefer creating the view before the model, but in this case I will be creating the model before the UI.

Creating the Model

To create the model you need to remember that the model is just a collection of commands that the view will be calling. Literally, they are commands! The command object descends from the ICommand interface. That means each of our commands will be implementing CanExecute and Execute. You'll need to create a class that implements these (which is not difficult) or use a third party command. Either way, you need a way to create an ICommand descendant. I will be using the Mvvm Light Toolkit

As you have probably figured out by now, your model will be doing the direct two way communication with the view. Because of this, the model class will need to descend from the INotifyPropertyChanged interface. From there you can just go nuts and define your public interface. Just think of whatever you might need to do in your player. If you're new to MVVM you might think it will only take a few simple video navigation commands to get this working, but you need to remember that the goal is to not have code behind. That means, you should not be writing code for loading or initializing either. If your model needs to grow to accommodate, that is fine.

public ICommand PlayCommand { get; set; }
public ICommand PauseCommand { get; set; }
public ICommand StopCommand { get; set; }
public ICommand MediaOpenedCommand { get; set; }
public ICommand SetVideoSourceCommand { get; set; }

Then your constructor can create the concrete implementation or you can use dependency injection or an Inversion of Control container if you prefer.

PlayCommand = new RelayCommand(PlayVideo, CanPlayVideo);
PauseCommand = new RelayCommand(PauseVideo, CanPauseVideo);
StopCommand = new RelayCommand(StopVideo, CanStopVideo);
MediaOpenedCommand = new RelayCommand<MediaElement>(MediaOpened, CanMediaOpened);
SetVideoSourceCommand = new RelayCommand<string>(SetVideoSource, CanSetVideoSource);

Now we can get down to writing some code for those fancy commands. When conceptually thinking about how to tell the video how to play, you have to wonder, what is the video? Video in Silverlight is displayed in a MediaElement. We will need one of those in our class.

private MediaElement _mediaElement;

The PlayVideo() method has a really easy implementation!

public void PlayVideo()
{
    _mediaElement.Play();
}

If you want to make it more complicated you can. I like this implementation. From that I am guessing you can figure out what the implementation of the pause and stop buttons will be. One last thing to do for the play button, you will want to implement the CanPlayVideo() method. For me, I only want to play the video if it is not already playing.

private bool CanPlayVideo()
{
    return _mediaElement != null && _mediaElement.CurrentState != MediaElementState.Playing;
}

MediaOpenedCommand is intended to be invoked when the MediaElement has been loaded with a video. This code will need to remember what MediaElement has been loaded so the other commands can work properly. You can assume that the view will be passing in the MediaElement as the parameter. We set this requirement in the view models constructor when we passed the MediaElement type to the RelayCommand.

private void MediaOpened(MediaElement mediaElement)
{
    _mediaElement = mediaElement;
}

One command that may not be intuitive is the SetVideoSource() method. It is still simple though.

public void SetVideoSource(string source)
{
    MediaSource = new Uri(source, UriKind.Relative);
}

When the view calls the SetVideoSource command it will be passing a video source location in the form of string. This code will convert that to a Uri and set it to a public property on our model that we have not defined yet. Here it is:

private Uri _mediaSource;
public Uri MediaSource
{ get { return _mediaSource; } set { _mediaSource = value; NotifyPropertyChanged("MediaSource"); } }

This code makes me angry. It is so close to being an automatic property using get; set; but the NotifyPropertyChanged messes everything up! Not only that, but I do not like having to pass in MediaSource as a string to the changed method. I think there is a lambda equivalent but I have a short attention span and I lost interest when Googling it.

Now for the confession: you do not really need to bind all UI elements to a command on a view model. You can bind the UI to anything on your model. It can be a string, Uri, a custom Silverlight type, or whatever you want as long as your view knows how to interpret the result. The commands are used for more of a method call where you are expecting an action to be performed (like the play button). You can use other types of binding to determine:

  • The duration of the video source
  • The current location in the video timeline
  • Does the video currently require buffering
  • If the video is buffering, how much longer will it be buffering

The list goes on, but really any information you want to show or bind to on your player is what you can define in your model

As a developer you can now tell the UI artist to slap on their squiglies, rainbows, and unicorns or whatever it is they do. The cool part is that they can use your model and it is simple enough that they can even wire it up without writing code. That is sweet.