Development, WP7

WP7 Part 4: Morphing and Mapping

This entry is part 4 of 8 in the series Windows Phone 7

As stated in previous posts, this series represents my experimentation with the Windows Phone 7 developer tools.  It was suggested that I chronicle those adventures for everyone’s benefit, so here it is. There’s good and bad in that – the good part is that we have the opportunity to all learn together as my dabblings become a real, working application. The down side, is that as I learn new and better ways to do things, the project morphs to go along. That might be off-putting to some, but it’s how my brain works, so I hope you’ll continue to indulge me and follow along as we grow this application.

Migrating to a better MVVM Framework

One of the highlights of the version of the code that accompanies this post is the further migration towards a better MVVM implementation – specifically Laurent Bugnion’s very cool MVVM Light Toolkit. His framework makes it really easy to adopt MMVM in a very declarative way, and I’ve found using it to be quite fun too! So – that means that my home-grown MVVM implementation has now been replaced with MVVM Light. Here’s what I had to do to make that happen:

  • Download and install the bits from http://www.galasoft.ch/mvvm/getstarted/
  • Set references to the MVVM binaries
  • Create a ViewModel locator class (GlobalViewModel) to serve as a repository of all ViewModel classes
  • Register the new ViewModel locator in the App.xaml page:
<application.resources>
        <vm:globalviewmodellocator
                xmlns:vm="clr-namespace:CommunityMegaphone.ViewModels"
                x:key="Locator" />
<application.resources>
  • Create new ViewModel classes to replace the ones I had (minimal changes were needed to these classes to make them work in the new framework – I could almost just copy/paste all the interior code over).
  • Change the XAML documents to reference the ViewModel classes as the page’s DataContext. We do this by adding a new DataContext attribute to each of the Page classes. Here’s an example from the MainView.xaml:
DataContext="{Binding Source={StaticResource Locator},      Path=MainViewModel}"
  • Update the views to make sure they point to the proper location in the ViewModel classes to get the information they need

Please spend some time migrating your application to get a feel for all the steps involved. The MVVM Light Toolkit provides a set of helpers to make adoption go smoothly – one you’ll want to become familiar with is the DispatcherHelper. Make sure you initialize it during application startup, or it won’t function as you’d expect.

This helper makes it really easy to switch execution back to the UI thread to do UI updating. You simply call into the CheckBeginInvokeOnUI method, passing in a delegate for the code you want to run, and that code will be run on the UI thread. Here’s my sample Load method from the MainViewModel class:

public void Load()
{
    var catalog = CMEventsEntities.Instance;

    DateTime maxDate = DateTime.Now.AddMonths(3);
    DateTime minDate = DateTime.Now.AddMonths(-1);

    DataServiceQuery dsq = catalog.ApprovedEvents
                  "year(starttime) le " + maxDate.Year + " and " +
                  "month(starttime) le " + maxDate.Month + " and " +
                  "year(starttime) ge " + minDate.Year + " and " +
                  "month(starttime) ge " + minDate.Month)
         .AddQueryOption("$orderby", "starttime");

    dsq.BeginExecute( (a) =>
    {
        var results = dsq.EndExecute(a);
        DispatcherHelper.CheckBeginInvokeOnUI(() =>
        {
            Items.Clear();
            foreach (ApprovedEvents @event in results)
            {
                Items.Add(@event);
            }
        });
    }, null);
}

Make sure the application is up and running with the new configuration before moving on to the next step, which is to add Mapping capabilities to the details page.

Adding a Bing Map

At the end of episode 3, we left a space at the bottom of the details page for a map control. We want to update our application to display a map of the event location to make it easier for us to find the given event. This should be relatively easy as there are publicly available downloadable bits for Silverlighg 3 and Silverlight 4.

Unfortunately, the publicly available map controls for Silverlight 3 and Silverlight 4 have a dependency on System.Windows.Browser.dll, which is not currently supported by the WP7 framework. I was able to obtain a pre-release build of a WP7-friendly Bing Map control to develop and test against.

Unfortunately, I found out after I wrote this post that the WP7 Bing Maps control referenced in this post has not been made publically available, so I can not release the binaries.  I promise that I will update this post as soon as I am told that the binaries are made available to everyone.

The first step in adding the control is setting a reference to the 2 Bing Maps libraries – Maps and Maps.Common.

Next, we go into our DetailsPage.xaml and add a new XML namespace for the Map control library:

xmlns:map="clr-namespace:Microsoft.Phone.Controls.Maps;  assembly=Microsoft.Phone.Controls.Maps"

We can then add the Map control to our Grid control in the open slot at the bottom:

<map:Map Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"  x:Name="MyMap" ZoomBarVisibility="Visible" ZoomLevel="15"  CredentialsProvider="...mykey..." Margin="5" />

There are number of options we can set. I’ve set the ZoomLevel to 15, so that we can get a close-in map (larger numbers = higher zoom level) set the display to Road and turned off animation so it will run faster on my 3G phone network.

To set up the map location and set the pin, we’re going to make some changes to the DetailsViewModel to raise an event when the loading is complete so that the DetailsPage can create the pushpin and place it on the map.

public void Load(int id)
{
    //TODO: Need to consider a way to cache the initial results and reuse here
    CMEventsEntities context = CMEventsEntities.Instance;
    var query = context.ApprovedEvents
        .AddQueryOption("$filter", "id eq " + id);
    query.BeginExecute((result) =>
    {
        var data = query.EndExecute(result);
        DispatcherHelper.CheckBeginInvokeOnUI(() =>
        {
            foreach (var item in data)
                Item = item;
            if (Item != null)
                LoadItemData();
            OnLoadComplete(new EventArgs());
        });
    }, null);
}

note to self: I need to find a good declarative way to set this up, but for now, the imperative method will work fine. I also need to come up a caching scheme for this data so I don’t have to go back to the database for the details page data. We’ll explore that in future versions of the application.

When this OnLoadComplete event fires, the associated View will respond by running some code to take the selected event from the ViewModel and plot a pushpin according to the associated longitude and latitude. Because the CM database returns the lat/long as one long string, I needed to write some code in the DetailsViewModel to split this out into two separate doubles – that code belongs in the VM and not in the View, so that is where I put it. Here’s the code from inside the event handler that shows how we create the pushpin and center the map on that location:

DetailsViewModel vm = this.DataContext as DetailsViewModel;
Pushpin pushpin = new Pushpin();
Location location = new Location()
{
    Latitude = vm.Latitude,
    Longitude = vm.Longitude
};
pushpin.Location = location;
MyMap.ZoomLevel = 15;
MyMap.Center = location;
MyMap.Children.Add(pushpin);

Now time to test – when we click on an item to bring up the Details page, it should load the map once the data is fetched from the database. The pushpin looks a bit wonky, but I’m not sure what’s going on there. I think there might be a resource issue in the bits I’m running, so I’ll see what I can find out about making that look nicer.

That is it for this post – next time we’re going to look at adding an application bar to control some settings for our application, and investigate integrating with the location sensor to find events near our position.

Series NavigationWP7 Part 3: NavigationCustomizing WP7 Push Notification Tiles

2 thoughts on “WP7 Part 4: Morphing and Mapping

  1. Hey Chris, nice to meet you at the WP7 launch event today. Great article above. Article says “the pushpin looks a bit wonky.” Seeing same issue here (after RTW release.) Anyone find a workaround? Not finding much by googling.

    1. Thank you so much for your email! I am currently out of the office speaking at the Windows Phone 7 Developer Launch in Minneapolis and won’t be back in the office until Thursday, October 7th. Until then, responses to email will be delayed until I can dig out of the mountain of mail that will most certainly pile up. If this is an urgent issue, please call my cell phone at 214-385-5616 and I will help you as soon as I can.

      Cheers!
      Chris

Comments are closed.