Architecting WP7 - Part 3 of 10: Enough Architecture (or Where is my VM)


Posted by Shawn Wildermuth on Oct 13, 2010 on 10:19AM

Windows Phone 7

Blogging everyday is getting exhausting.  But seriously folks (and don't forget to tip your wait staff)... Here in day three of Architecting Windows Phone 7 applications, I want to talk about locating the view-model. If you've missed the past parts of the series, you you can visit them here:

I will take the assumption that you've read about the MVVM pattern in Silverlight (my MSDN article is here if you want a refresher).

The phone is a small platform (no pun intended) so your Silverlight apps may be smaller than you might imagine. In addition, you are tied to the page navigation story (a good thing IMHO) so you may want to look at handling view-models in a different way.

Luckily there are toolkits that help (e.g. MVVMLight and Caliburn Micro) but before you dive into the toolkits, I think it's important to get your head around the problem. If you're building a single-page app (or more importantly, a single View-Model app), then its easy. You can locate the VM either in the View (which I don't prefer) or have a factory to deliver it (e.g. expose it at the App class or other instantiation method). As you start to look at the nature of the location problem, it starts to look and feel like the routing problem (a la MVC's Routing Framework).

I think it comes down to a matter of taste unfortunately. Some frameworks seem to want to supply a VM to a View; and others want the VM to tie to the View. Having something like a mediator handle 'marrying' the two is still an approach I prefer.  But what does that mean for the phone?  Well, I tend to try to stay pragmatic. Since most (not all) phone applications are going to be small (by the nature of the platform) the more plumbing you add to make this happen, the less helpful it is. Sometimes ceremony of building an IoC container and plumbing up a VM locator (or mediator) is just to much trouble for these smaller applications. Certainly using something like MVVMLight's Locator or CaliburnMicro's Conductor makes sense when you're building a large application, but I don't see it as required in most phone applications. So I am going to let you know a secret...I cheated.

public partial class App : Application
{
  private static UserViewModel viewModel = null;

  public static UserViewModel ViewModel
  {
    get
    {
      // Delay creation of the view model until necessary
      if (viewModel == null)
        viewModel = new UserViewModel();

      return viewModel;
    }
  }
  // ...
}

Instead of building up plumbing, I just made it part of the Application class. Sure, its cheating, but for my application, there was only a single view-model.  Even if there were more than one VM, it was likely to be sub-VM's so the main VM could hand-off the VM. In this pragmatic sense, for a small discrete application, this works just fine and lets me write tests against the different parts of the application. Because its part of the Application (though I could have created it as part of the Application Lifecycle objects), my views can request the data directly like so:

public partial class LandingPage : PhoneApplicationPage
{
  // Constructor
  public LandingPage()
  {
    InitializeComponent();

    // Set the data context of the listbox control to the sample data
    DataContext = App.ViewModel;

  }
  // ...
}

Note that I am not holding onto a hard reference to the type (though I could mitigate this with an interface). The rest of the work on this page is done directly through data binding so the duck-typing experience remains strong.

So what about other views (e.g. Pages)? I continue to use the view-model to give me the data these pages need. I could have created a method that passed in data from a query string, but in my case the application was simple enough that the view-model could expose a property that was the current item to edit (as shown here in a detail page):

public partial class EntryPage : PhoneApplicationPage
{
  Reading _currentReading = null;

  public EntryPage()
  {
    InitializeComponent();
    Loaded += (s, e) =>
      {
        if (_currentReading == null)
        {
          // Ensure that application's ViewModel is tied to this view
          _currentReading = App.ViewModel.CurrentReading.Clone();
          ParseTags();
          DataContext = _currentReading;
        }
      };
  }
  // ...
}

Is this super-clean?  No.  Is it clean enough? I think so. I debated with myself about how much architecture I needed here...and I decided that I had enough (I have a full separation of concerns, but no IoC and no locator service).

This approach works for me as the size of what I've built isn't huge yet. As applications increase in size, the architecture will need to as well. Using techniques like messaging (a la MVVMLight's Messenger) and routing (a la Caliburn Micro's Conductors) could come into play pretty quickly.  But since memory and app size is an issue on the device, I am looking for enough architecture to get the code working and to give me the flexibility to modify the applications going forward. I think that's the important part of the story on the phone to me. I am sure there will be dissenters in the comments, but that's ok...I like that ;)

 




Application Name WilderBlog Environment Name Production
Application Ver 1.0.0.0 Runtime Framework .NETCoreApp,Version=v1.0
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.0.0.0
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86