Sunday, December 26, 2010

Extend ViewModelLocator to be a bit more dynamic

I've been using MVVM Light in my Windows Phone development. It's lightweight, has all the base functionality I need and seems pretty solid.

One pattern that MVVM Light uses is a static ViewModelLocator class that holds all of the main/root view models in the application. You declare it as a data source in the app.xaml:
<vm:ViewModelLocator d:isdatasource="True" x:key="Locator"/>

Then in the page Xaml you can do:
DataContext="{Binding Main, Source={StaticResource Locator}}"

This is great when there is basically a 1:1 mapping between the page and the contents of each view model. What it's not so good at is dealing with the situation where you want the same page to bind to multiple view models are structurally equivalent but have different data contents.

Take for instance an RSS viewer (which coincidentally enough I'm working on at the moment). You might have a feed that represents articles form a website and another feed that is discussion posts from the same website. Each feed is further broken down into topic channels. So if we wanted to display each feed as a page, with each topic as a Pivot Item on that page we could distill the page xaml down to:
<controls:Pivot Title="{Binding Name}" 
   ItemsSource="{Binding Topics}" 
   ItemTemplate="{StaticResource RssTopicTemplate}"/>
Now if the ViewModel is statically linked to the page (as above) we need to parametrize the ViewModel as the user navigates from articles to discussions and back. It can be made to work but it violates the Single Responsibility Principle and I just don't like it.

So rather than parameterizing the ViewModel how about we parametrize the page? Let's declare the linkage between ViewModel in page on the ViewModel. We'll declare an attribute that specifies the linkage and allows a parameter to be passed the page in the form of a query string.
[Page("/RssPage.xaml?vm=ArticlesStatic")]
public class ArticlesViewModel : ViewModelBase

...

[Page("/RssPage.xaml?vm=ForumsStatic")]
public class ForumsViewModel : ViewModelBase

In this app the set of navigable items are collections of viewmodels that are displayed in ListBoxes wherein each ViewModel can be selected and navigated to:
public class ContentsViewModel : ViewModelBase
{
   public ObservableCollection<ViewModelBase> Contents 
             { get; private set; }
   public RelayCommand<object> SelectViewModel 
             { get; private set; }

   private void Select(object vm)
   {
      if (vm != null)
      {
         var page = vm.GetType().GetAttribute<PageAttribute>();
         Navigate(page.Page);
      }
   }
}
Then we need a wee bit of code in the page codebehind:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (NavigationContext.QueryString.ContainsKey("vm"))
    {
        string key = NavigationContext.QueryString["vm"];
        DataContext = ViewModelLocator.FindViewModel(key);
    }
    base.OnNavigatedTo(e);
}
where FindViewModel is a method added to the ViewModelLocator that returns the correct ViewModel using reflection:
public static object FindViewModel(string key)
{
    var prop = typeof(ViewModelLocator).GetProperty(key, 
              BindingFlags.Public | BindingFlags.Static);

    return prop.GetValue(null, null);
}

I find that moving the linkage between View and ViewModel onto the ViewModel gives us the flexibility to reuse the same UI to display the contents of multiple, structurally equivalent ViewModels, while still maintaining a loose coupling between those two layers.

Friday, December 17, 2010

CPVanity for Windows Phone

I always kind of liked Luc Pattyn's CPVanity app that lets you check out your code project reputation and keep an eye on article ranking.

So here's a WP7 port of it as a small homage. It's up on the marketplace too.

Wednesday, December 15, 2010

Handy Windows Phone 7 resources

Now that I've gotten past the "Hello World" stage of WP7 development here are some code and resources that I've found quite helpful:

Wednesday, December 8, 2010

A helper class to get the current location on a Windows Phone just once

Getting the location on Windows Phone is done via the GeoCoordinateWatcher. The coordinate watcher has a some best practices associated with it one of which is to minimize power consumption. Once you turn this thing on its going to be using the GPS to stream location event back to your app which will hit the battery.

There are some instance where you may not a running notification of the current potion but just want the current position (for instance you may want to initialize the position on a map but not track changes to position).

So this is a small helper class that starts up the coordinate watcher asynchronously, returns the first coordinate found and then shuts it down. One could use TryStart like so:
using (var watcher =  new 
                GeoCoordinateWatcher(GeoPositionAccuracy.Default))
{
    var location = watcher.TryStart
                (false, TimeSpan.FromMilliseconds(1000));
}
but since that will block your UI thread (assuming you call it from the UI) you'd need to wrap it in some asynchronous stuff anyways.
So I use this class:
public class ImmediateLocation : IDisposable
{
    private GeoCoordinateWatcher _watcher;
    private Action<GeoCoordinate> _action;

    public ImmediateLocation(Action<GeoCoordinate> a)
    {
        Debug.Assert(a != null);

        _action = a;
    }

    public void GetLocation()
    {
        if (_watcher == null)
        {
            _watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
            _watcher.MovementThreshold = 1000;

            _watcher.PositionChanged += new 
                EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>
                (_watcher_PositionChanged);
            _watcher.StatusChanged += new 
                EventHandler<GeoPositionStatusChangedEventArgs>
                (_watcher_StatusChanged);

            _watcher.Start(false);

            if (_watcher.Status == GeoPositionStatus.Disabled
                || _watcher.Permission == GeoPositionPermission.Denied)
                Dispose();
        }
    }

    void _watcher_StatusChanged(object sender, 
        GeoPositionStatusChangedEventArgs e)
    {
        if (e.Status == GeoPositionStatus.Disabled 
            || _watcher.Permission == GeoPositionPermission.Denied)
            Dispose();
    }

    void _watcher_PositionChanged(object sender, 
        GeoPositionChangedEventArgs<GeoCoordinate> e)
    {
        _action(e.Position.Location);
        Dispose();
    }

    public void Dispose()
    {
        if (_watcher != null)
        {
            _watcher.Stop();
            _watcher.PositionChanged -= new 
                EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>
                (_watcher_PositionChanged);
            _watcher.StatusChanged -= new 
                EventHandler<GeoPositionStatusChangedEventArgs>
                (_watcher_StatusChanged);
            _watcher.Dispose();
        }
        _watcher = null;
        _action = null;
    }
}

Then you can just do this:
void InitLocation()
{
     var immediate = new ImmediateLocation(x => location = x);
     immediate.GetLocation();
}

GeoCoordinate location;
It acts like a one time use, fire and forget location setter that cleans itself up when done. It does have Dispose if you want to mange the lifetime more closely.

Sunday, December 5, 2010

New CodeProject article

I just posted an article on codeproject that goes into detail on my experience building a Windows Phone 7 app. Source code included. Check it out.

Microsoft posts a nice improvement to the WP7 progress bar

Showing that your WP7 app is doing some work is as simple as adding a progress bar and setting its IsIndeterminate property to true. This will show the animating dots that glide from left to right in all the WP7 apps.

<ProgressBar IsIndeterminate="{Binding Working}"/>

The problem with the current version of the control is that it animates on the UI thread. So if your UI is doing something as well it can be kind of jerky.

MSDN has a code snippet that moves the animation to the compositor thread. I'd recommend using this approach as it has a noticeable improvement, animating nice and smoothly.

Thursday, December 2, 2010

Got a WP7 app published

There's already a Microsoft last.fm browser/player but since I like last.fm and have some experience with their API, I thought it would be a good WP7 learning tool. Plus it goes a little more in dpeth into the content.

It's published now on the app hub. So that's pretty cool.