.
Part I Part II
This is the second installment of a several part series/journey to use social network resources for Windows Phone 7 development and produce a reusable code base.
In Part I the goal simply was to show a couple of reliable sources for WP7 development, and get started building up the baseline phone application solution. The next step is to add some patterns, helpers, and controls to the solution. All of which are reusable for your Silverlight Windows Phone 7 applications.
MVVM Pattern
MVVM Light is one of several options for implementing the Model-View-ViewModel pattern. There are many articles available to explain the benefits of this pattern. If you aren’t using MVVM for WP7 you really need to start. There is a little up front cost in implementing any pattern but the return on investment usually makes it worthwhile. Caliburn.Micro is another great option. Reactive UI is yet another option for the MVVM pattern. For this series we are going to use Laurent Bugnion’s MVVM Light library and if you like, you can refactor the source code at the end of the series to use something else. MVVM Light can be found on Laurent’s blog here. There is also a CodePlex project here where you can get the source code and/or binaries from. There are detailed installation instructions for installing the custom MVVM Light project, item templates, binaries, and code snippets.
Instead of following the installation steps to the letter, since I’ve worked with MVVM Light extensively, we take a modified approach. You may want to install first based on the instructions and then follow my steps after that.
After downloading the latest source code and compiling the binaries, copy compiled assemblies and code snippets for MVVM Light and drop them in the SolutionFiles project. Then reference the binaries located in the SolutionFiles project and add the snippets to Visual Studio. To accomplish this first create two folders within the SolutionFiles project and copy in the binaries and snippets files. Next use the Tools menu in Visual Studio and open up the Code Snippets Manager to import the snippets. There is an Import button to do this. The steps to add the binaries are fairly straightforward but the Code Snippets Manager can be more confusing than necessary sometimes. Here is a screen shot to help.
Now go into the WindowsPhoneApplication1 project and add references to the MVVM Light binaries from the newly added SolutionFiles files.
There is a detailed explanation on how to get started with MVVM Light here. The reason it is mentioned here is that there is a section on Components that is worth a quick review.
It isn’t completely necessary to add folders for different kinds of MVVM code files. It is a good practice to do so. Caliburn.Micro for instance follows a “Convention over Configuration” approach forcing you to structure folders a certain way so that the version of an MVVM framework can locate Views and ViewModels via reflection. Following this folder structure convention doesn’t hurt, it keeps files organized, and might make refactoring a little less tedious later. So, add three folders to the WindowsPhoneApplication1 project. One called “Models”, one called “Views”, and one called “ViewModels”.
To use MVVM something has to “locate” the matching ViewModel for a View. In MVVM Light that is done using a dedicated ViewModelLocator class. Laurent was smart and already created an item template for that. As well as item templates for Views and ViewModels. Go to this link and scroll down to the section on “Installing the templates for Visual Studio 2010”. You can follow the instructions for installing both the Project Template and all of the Item Templates. Because this series is about creating our own custom project template, we skip the step for the MVVM Light project template.
Now, right click on the ViewModels folder to Add a New Item. Choose the ViewModelLocator item template. You can see the other item templates in the screen shot below.
Name this class “ViewModelLocator”. The class file is generated based on the name and there are a couple of interesting points as you look through it. There is a constructor and a cleanup method. The commented out code and summary text has the really interesting stuff. At the top of the file there is a comment section telling you that you need to add some XAML to the App.xaml.cs file. This creates a Static Resource to use throughout the application for the locator class. The second important comment shows how each View references the locator and binds the correct ViewModel to the View. It is a line of XAML that basically says, set this View’s DataContext to the ViewModel located via a public property in the ViewModelLocator. The nice thing about this is that it sets up the ability to have “blendability”, or design-time data. Built into both the WP7 programming API and MVVM Light is the ability to check a value to determine if the application is running in a device or emulator, or not. When the application is not running the term blendability refers to the design surface in Expression Blend. It can also be interpreted as the design surface in Visual Studio.
The Windows Phone 7 Phone Application project template that comes out of the box with the development tools allows for some design-time data if you use Blend to create it. There is an approach for that detailed at KIRUPA.com here.
In a real world application there are going to be ViewModels, web services, and other sources of data. You can mock up data for a simple class very easily. But it is more difficult to mock up data for web services. It requires some code to provide design time data for complex object graphs. It is important to separate that code from production code to improve maintainability and readability. So, how do we get design-time data in the application?
IoC Pattern Plus a Little Trick to Enable Design Time Data
To improve productivity in developing the application we add another pattern. MVVM allows for a clean separation of concerns. Getting design time data in a Silverlight application also allows for the separation of roles between designers working strictly in Blend and developers working strictly in Visual Studio. Design-time data will increase productivity more than enough to justify the work of adding and using the Inversion of Container pattern (IoC).
It is arguable that you don’t need another pattern after using MVVM to get design time data. You could put all the design-time data in the main phone project and switch logic if the DesignerProperties.IsInDesignTool flag is true or false. However, this will clutter code up to some extent and could increase the final size of the application XAP file. Maintainability of code suffers as a result and performance also can be affected. So what you can do is put the data into a separate project that you don’t need to reference directly in the production code.
There are multiple ways of doing what is described here. Essentially, we are going to create a shared project and a design-time data project and set up some references. Then we are going to implement IoC and describe a way to get design-time data from a project that isn’t referenced.
Just like MVVM Light, there are lightweight frameworks available so you don’t have to write your own if you don’t want to. The one we’ll use is called MicroIoC and it can be found on CodePlex here. Download and add just the MicroIoC.Core project into the solution. Again, copy the source code of the MicroIoC.Core project into the root folder of the solution so all the code is organized. Reference the MicroIoC.Core project in the main phone application project.
Create a new project and call it something like WindowsPhoneApplication1.Design. Choose the class library project template for it. Create another project as a class library and call it WindowsPhoneApplication1.Shared. The Design project is going to have the design-time data in the form of ViewModels and Services. The Shared project is going to have interfaces for all of the ViewModel and Service classes. Once those projects are created, add a reference in the main phone application to the Shared project. In the Design project also reference the Shared project. Do not reference the Design project anywhere. In the Design project, do reference the MVVM Light binaries stored in the SolutionFiles project. For the sake of organization, create a folder called “ViewModels” in the Design project.
At this point, you should have 7 projects in the solution. If you press CTRL+W,C the Visual Studio IDE will display the Class View of the solution. It should look something like this.
The default phone application template created a few things that need to change for MVVM. First, create a new View in the Views folder by using the MVVM Light item template. Call it “MainView.xaml”. The code generated will have the DataContext setting described earlier. We want to make this the first phone application page of the application. So in the Properties folder you will find a file WMAppManifest.xml. It has a default startup phone application page. Locate and change the xml as it appears below:
<DefaultTask Name ="_default" NavigationPage="Views\MainView.xaml"/>
The NavigationPage attribute needs the correct relative path to the Main View. Next create a ViewModel in the ViewModels folder and use the MVVM Light item template. Call it “MainViewModel”. You don’t save much time by using the ViewModel template because it is really just a class file. There are some useful instructions. So for all future ViewModels, just use the create new Class.cs template. The convention approach has a naming convention putting the text “ViewModel” at the end of the class file name. Instructions in the new ViewModel tell you how to use the MVVM Light code snippets to create custom dependency properties.
It is worth taking a moment to look at the code generated by one of the snippets. Go ahead and create a custom dependency property now using the code snippet mvvminpc. Make it a string property and call it ApplicationTitle. The backing variable name isn’t as important. After creating the property, set the default text value to something like this:
private string applicationTitle = "This is an awesome phone application template!";
Notice too that in the constructor there is some code to add blendability. It can be ignored or removed. Ok, now we have a MainViewModel, and a MainView. Now we need an interface for the MainViewModel in the Shared project and a copy of the MainViewModel.cs file in the Design project. So right click and select Copy for the MainViewModel.cs file and then paste it into the ViewModels folder of the Design project created earlier. Change the default value of the application title property so we can see the design-time data later on.
private string applicationTitle = "This is Design time data!!!!";
This express version of Visual Studio does not have a Refactor-Extract Interface feature. So, we create an interface manually. It should look something like this and it should be created in the Shared project.
namespace WindowsPhoneApplication1.Shared.ViewModels
{
public interface IMainViewModel
{
string ApplicationTitle { get; set; }
}
}
Again, this interface exists in the Shared project. Which is referenced both by WindowsPhoneApplication1.Design and WindowsPhoneApplication1 projects. Make sure to implement the interface on both of the concrete classes.
Inside the class ViewModelLocator use the MVVM Light snippet for generating a public property that locates MainViewModel for MainView. That snippet is mvvmlocatorproperty. The generated code will be deleted shortly so don’t be too concerned at how much code is there. Take a quick look over it and you see a static MainViewModel is returned from the Main property. If you named everything as intended using the snippet. Ok, now click undo until the snippet code is removed (or just highlight and delete it).
There is a slightly more terse way to accomplish the same thing. That is to use IoC to return the correct MainViewModel based on the value of ViewModelBase.IsInDesignModeStatic. If the application is in Design Mode, we want the locator to return a MainViewModel instance from the Design project. Not from the main phone project. That Design project MainViewModel will have all the design-time data in it. Conversely, if the application is running in an emulator or on a phone, we want the locator to return the MainViewModel instance from the main phone project. Its data will be production quality in some fashion. Either hard coded in the view model or created dynamically from a web service result.
In order to get items from an IoC container, you first have to put things into them. This is done by registering types of objects. These types will be instantiated later when needed. There are variations on where to register objects so by no means is this the only way. For this project, this will be done entirely in the ViewModelLocator class. Ultimately, the MainViewModel needs to be set as the DataContext in the MainView. So, in the constructor of the ViewModelLocator we register a design time instance and a production time instance.
How do you get the Design time instance if it isn’t referenced directly in the project? The trick to loading the Design project assembly dynamically with Assembly.Load. Let’s take a look at this in detail. First in the build output and then how the code ties it together.
If you open the Configuration Manager for the Solution, you will see that the Design project is marked to build in both Debug and Release configurations.
However, if you look at the xap file contents (by changing the xap extension to zip) you will see that in fact the Design assembly is not included.
It seems time to show some code. Note that the source code for this post is available at the bottom.
ViewModelLocator.cs
/*
In App.xaml:
<Application.Resources>
<vm:ViewModelLocator xmlns:vm="clr-namespace:WindowsPhoneApplication1.ViewModels"
x:Key="Locator" />
</Application.Resources>
In the View:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
*/
using System.IO;
using System.Reflection;
using GalaSoft.MvvmLight;
using MicroIoc;
using WindowsPhoneApplication1.Shared.ViewModels;
namespace WindowsPhoneApplication1.ViewModels
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// </summary>
public class ViewModelLocator
{
private readonly IMicroIocContainer container = new MicroIocContainer();
private Assembly designTypes = null;
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
if (ViewModelBase.IsInDesignModeStatic)
{
try
{
//Load the non-referenced assembly containing design time ViewModels and Services
designTypes = Assembly.Load("WindowsPhoneApplication1.Design, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
}
catch (FileNotFoundException ex)
{
designTypes = Assembly.LoadFrom(@"C:\code\WP7\WindowsPhoneApplication1\WindowsPhoneApplication1.Design\BuildFiles\WindowsPhoneApplication1.Design.dll");
}
// Register design time view models and services
container.RegisterInstance(designTypes.GetType("WindowsPhoneApplication1.Design.ViewModels.MainViewModel"), "MainViewModelDesignTime");
}
else
{
// Register run time view models
container.RegisterInstance(typeof(MainViewModel), "MainViewModel");
}
}
public IMainViewModel Main
{
get
{
if (ViewModelBase.IsInDesignModeStatic)
{
return (IMainViewModel)container.Resolve(designTypes.GetType("WindowsPhoneApplication1.Design.ViewModels.MainViewModel"), "MainViewModelDesignTime");
}
else
return GetViewModel<MainViewModel>();
}
}
private T GetViewModel<T>() where T : ViewModelBase
{
// Create a new view model
T vm = container.Resolve<T>();
return vm;
}
}
}
Looking at the code above, the essential “magic” happens in the constructor. If the code is in Design Mode, then Assembly.Load is used to create the Assembly object designTypes. From which view models (or service instances) can be instantiated with design-time data.
There is a try-catch wrapping Assembly.Load. Blend is able to locate the Design assembly but Cider, the Visual Studio designer for XAML, can only load the assembly given an absolute file path. If the designer is not Blend, the Assembly.Load fails to find the assembly and instead uses the method overload Assembly.LoadFrom. The ugly part is that you have to hard code the absolute path to the Design dll. The payoff is that design-time data is available in Visual Studio in addition to Blend.
Unfortunately, when the absolute path is used that points to the output file in the Bin folder, there is another issue. When you open the MainView in Visual Studio, the ViewModelLocator will run the code to load the assembly. Once in use by Visual Studio, the Design project won’t be able to compile. There is a simple workaround to this issue. By using a post build event in the Design project, the output dll from the build can be copied to a different location. The ViewModelLocator can point to that location for the Assembly.LoadFrom code. Then the issue of writing over the dll in the Build folder is fixed. Here is the post build command for the Design project.
start xcopy /y $(ProjectDir)$(OutDir)$(TargetFileName) $(ProjectDir)BuildFiles\
The instance needs to be registered in the container. That is done using the container.RegisterInstance method. Note the use of a string key for the MicroIoC register method. We use a different string key for design verses production time instances. If the application is running and not in Design Mode, the instance registration doesn’t include the Assembly.Load of the Design project at all.
There is a method GetViewModel that uses generics to get (or resolve) the instance of a view model from the container. You can see that for a given type T where T inherits the ViewModelBase type, we can retrieve the instance from the container.
Look at the property described by
public IMainViewModel Main
{
get
{
if (ViewModelBase.IsInDesignModeStatic)
{ ....
The getter includes the test for design mode and returns either the design time instance of an object adhering to the interface IMainViewModel. Or it returns a production time instance MainViewModel.
This is the pattern for all ViewModel instances and it is centralized in this ViewModelLocator class. A small amount of work to have a good scalable, maintainable, and blendable architecture.
When you run this application, you see the production time data for the application title.
While you are writing and designing the application in Visual Studio or Blend you see the design-time data.
Summary
So far, the solution is set up to take advantage of MVVM Light and an IoC framework called MicroIoC. This overall structure could also be applied to Silverlight 4 applications and not just Windows Phone applications.
In the next part we’ll add a few value converters, actions, behaviors, and controls. There is at least one more project to add to the solution. The Silverlight for Windows Phone Toolkit. We’ll also go into a problem and a solution for the WP7 Application Bar control.
Source Code
8f3cfb7b-5663-4b79-92eb-f23ce2b6ee1e|0|.0
Code, Silverlight, WP7