Using MVVMLight, ItemsControl, Blend and behaviors to make a ‘heads up compass’


When I talk of the MVVM pattern, people usually think of business objects that get wrapped by ViewModels which get data bound to a user interface. Usually this is something like a list of people, news, items that can be purchased, whatever – and usually this data is displayed in a list box, with a bit of templating if it isn’t too much trouble. That’s fine in itself and good way to use my favorite pattern but there are more things possible using MVVM data binding than most people imagine. The most fun way I have been able to discover is to combine ItemsControl and behaviors. This is what drives my game Catch’em Birds. And this article shows how to use this technique make a kind of heads up compass. I’ll sprinkle some ‘how to do things in Blend’ (like adding and configuring behaviors) throughout the article as well.
For the hasty readers: “Setting the stage”, “Building the models” and “Building the ViewModel” is the ground work. The real stuff starts at “Initial user interface”.
Setting the stage
- Create a new Windows Phone 7 application. Let’s call it “HeadsUpCompass”. Select Windows Phone 7.1 - duh ;).
- Install my wp7nl library from codeplex via NuGet. This will get you some of my stuff and MVVMLight and some more stuff as well in one go.
- Add references to Microsoft.Device.Sensors and Microsoft.Xna.Framework.
Building the models
The application has two models: CompassDirectionModel – holding stuff that wants to be displayed, and CompassModel, that checks the compass direction using the motion API. The CompassModel is implemented below. I’ve explained using the Motion API to check where the camera is looking in an earlier post so I’ll skip the details here. It’s basically the same functionality, wrapped in a model, with an event firing at the end:
using System;
using System.Windows;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;
namespace HeadsUpCompass.Models
{
public class CompassModel
{
Motion motion;
/// <summary>
/// Inits this instance.
/// </summary>
public void Init()
{
// Check to see if the Motion API is supported on the device.
if (!Motion.IsSupported)
{
MessageBox.Show("the Motion API is not supported on this device.");
return;
}
// If the Motion object is null, initialize it and add a CurrentValueChanged
// event handler.
if (motion == null)
{
motion = new Motion {TimeBetweenUpdates = TimeSpan.FromMilliseconds(250)};
motion.CurrentValueChanged += MotionCurrentValueChanged;
}
// Try to start the Motion API.
try
{
motion.Start();
}
catch (Exception)
{
MessageBox.Show("unable to start the Motion API.");
}
}
/// <summary>
/// Stops this instance.
/// </summary>
public void Stop()
{
motion.Stop();
motion.CurrentValueChanged -= MotionCurrentValueChanged;
}
/// <summar