Archive for Silverlight

Lightswitch: custom ribbon button

Two weeks ago, on July 26th, Visual Studio Lightswitch 2011 was released. I ‘d seen a couple of videos what you can do with Lightswitch so I though I’d put it to the test by developing a replacement for a tool I use at one of my clients. Lightswitch is really easy to use and I believe I can actually teach my wife to create data-centric application using it. Until you reach the limits though, because than it becomes more difficult really fast. The main reason for this is that there’s not much documentation available on the inner workings of Lightswitch and how to hook into some of it. One of the things I ran into was how to add my own button to the ribbon that is available on every screen, like the default Save and Refresh buttons are (unless you explicitly set them not to be visible on a screen).

Extending Lightswitch applications

Like with many things, if you know how to do it, it’s not that hard. Lightswitch makes heavy use of the Managed Extensibility Framework, MEF. To extend the default behavior of Lightswitch you need to implement certain interfaces and export your implementation using the MEF Export attribute. There are multiple places your extension could live:

  • A Lightswitch Extensibility solution
  • A Silverlight class library
  • In the Lightswitch application itself

The Microsoft preferred place if the Lightswitch Extensibility solution, but I believe that’s overkill if you just want a button specific to the application you’re building. The Lightswitch Extensibility solution is a perfect way to build extensions for Lightswitch that you want to reuse for multiple applications, e.g. a certain shell style of theme for the company you work for. To create a Lightswitch Extensibility solution, you need to have the Lightswitch Extensibility Toolkit, available here. Note that you also need Visual Studio 2010 Pro and the Visual Studio 2010 SP1 SDK in order to use the toolkit.

The Silverlight class library is a good option if you have extensions specific to your application. It works the same way as extensions in the Lightswitch application itself, but makes for a better separation of generated and custom code.

The last option is good if you want a quick extension to your application. In this example I use this method because it’s the easiest to demonstrate how it works and allows to focus on the task at hand: adding a button to the ribbon that is available on all screens.

Adding a class file to the Lighswitch application

It may sound easy to add a class file to the Lightswitch application, but if you don’t know how to do it, it can be quite a challenge. The thing is that a Lightswitch application behaves differently in Visual Studio. It isn’t a normal project with class files, folders and custom controls. It’s a logical view of your application that hides all the generated code from you. But there’s a way to switch to the view which does show the various projects and files that make up your application. In the button strip on the top of your solution explorer, there’s a button on the far right that allows you to toggle between the logical and the file views.

image

Once you switched to the file view, you see your application is made up by a Client, Common and Server project, where the Client project contains your Silverlight app, the Server project contains all server related stuff like data access and the Common project contains the things shared between the Client and Server project. You can go one step further by choosing to show all files. This is also a button on the button strip on top of your solution explorer. If you toggle to show all files, you also get to see the ClientGenerated and ServerGenerated projects. This is used for example if you need to change a connection string in the web.config, which is located in the ServerGenerated project.

image

Since the button needs to be added to the Silverlight client, this is where we add the class file. Right click the Client project and choose to add a class. Name it MyGroupProvider.cs. I’ll explain later why we call it a provider.

Steps to add a custom button

Adding a custom button to the ribbon that is available on all screens involves the following steps:

  • Create a class that implements IExecutable for executing code when the button is clicked
  • Create a class that implements IShellCommand which is our button
  • Create a class that implements IShellCommandGroup which is the group for the button
  • Create a class that implements IShellCommandGroupProvider that acts as the provider for MEF so Lightswitch can find our button.

Implement IExecutable

The class that implements IExecutable is responsible for executing code when the button is clicked. This interface could be compare to the Silverlight ICommand interface but with some editions. The interface provides properties through which Lightswitch determines if the executable can be executed and methods for executing the code. In addition to the ICommand interface, the IExecutable interface allows for synchronous and asynchronous execution, allows cancellation of an asynchronous execution and provides a way to handle errors with the execution.

Create a class named MyExecutableObject and implement IExecutable. For the executable to work in the ribbon, you need to change the CanExecuteAsync to return true and implement ExecuteAsync to execute the code you want, e.g. showing a screen. The synchronous CanExecute property and Execute method are not used in this case, although I don’t know where they would be used. Make sure to return null in the ExecutionError property and return Microsoft.LightSwitch.ExecutionState.NotExecuted for the ExecutionState. Of course you can add your own logic to determine whether the executable can be executed or handle the errors the way you like.

public class MyExecutableObject : Microsoft.LightSwitch.IExecutable
{
    public bool CanExecute
    {
        get { return false; }
    }

    public bool CanExecuteAsync
    {
        get { return true; }
    }

    public bool CanExecuteAsyncCancel
    {
        get { return false; }
    }

    public void Execute()
    {
        throw new NotImplementedException();
    }

    public void ExecuteAsync()
    {
        // You code here
    }

    public void ExecuteAsyncCancel()
    {
        throw new NotImplementedException();
    }

    public event EventHandler<microsoft.lightswitch.executecompletedeventargs> ExecuteCompleted;

    public Exception ExecutionError
    {
        get { throw new NotImplementedException(); }
    }

    public Microsoft.LightSwitch.ExecutionState ExecutionState
    {
        get { return Microsoft.LightSwitch.ExecutionState.NotExecuted; }
    }
}

Implement IShellCommand

Now we have a class that implements IExecutable, we need to create a button through which the user can execute it. A button is a class that implements the IShellCommand interface. It allows you to specify a description for the button as well as a name. The ExecutableObject needs to return a new instance of the MyExecutableObject we just created. Furthermore, to be able to use the button, we need to make sure it’s enabled and visible. To create the button, create a new class named ‘MyCommand’ and implement the IShellCommand interface. Make sure to change all property and method bodies to do something and not throw an NotImplementedException.

public class MyCommand : IShellCommand
{
    public string Description
    {
        get { return "This is My Command"; }
    }

    public string DisplayName
    {
        get { return "My Command"; }
    }

    public Microsoft.LightSwitch.IExecutable ExecutableObject
    {
        get { return new MyExecutableObject(); }
    }

    public string Group
    {
        get { return "MyGroup"; }
    }

    public ImageSource Image
    {
        get { return null; }
    }

    public bool IsEnabled
    {
        get { return true; }
    }

    public bool IsVisible
    {
        get { return true; }
    }

    public bool ShowSmallImage
    {
        get { return false; }
    }
}

You can specify an image for the button by returning a BitmapImage using an image resource. For this you add an image to the Resources folder of the Client project. Then you use the following piece of code to return the image.

return new System.Windows.Media.Imaging.BitmapImage(
    new Uri("/DeploymentSettingsManager.Client;component/Resources/import1.png", UriKind.Relative));

Implement IShellCommandGroup

As of the time of writing I don’t know how to add a custom button to the built-in Data button group, so we’ll create our own group to host our button. We create a new button group by creating a new class that implements the IShellCommandGroup interface. This interface lives in the Microsoft.LightSwitch.Runtime.Shell.ViewModels.Commands namespace, so make sure to add a using for it.

Implement the interface by right clicking the interface and choosing ‘Implement Interface’. It will generate three properties for you: Commands, DisplayName and Name. Replace the getters of the DisplayName and Name to return the names you’d like to use for your command. The commands property returns a collection of the commands available in the group. In this case we only return our MyCommand. You can construct a collection or use the ‘yield’ keyword as in the example below.

public class MyGroup : IShellCommandGroup
{
    public System.Collections.Generic.IEnumerable<IShellCommand> Commands
    {
        get { yield return new MyCommand(); }
    }

    public string DisplayName
    {
        get { return "My Group"; }
    }

    public string Name
    {
        get { return "MyGroup"; }
    }
}

Implement IShellCommandGroupProvider

You now created you executable, the button and the group. The last thing to do is make sure Lightswitch know about it, so it can add it to the ribbon. To do this, we use a class that implements the IShellCommandGroupProvider class. Create a new class named MyGroupProvider and imlement the IShellCommandGroupProvider interface. Make sure the GetShellCommandGroups method returns the MyGroup class, since that is the group we want to make available to the Lightswitch application.

To allow the Lightswitch application to discover the group provider, add an Export attribute to MyGroupProvider class with parameter ‘typeof(IShellCommandGroupProvider)’. The Export attribute is a way for MEF to export definitions in your class library or application, which can be imported elsewhere using an Import attribute. MEF scans the application for all Export attributes and makes them available. The parameter for the Export attribute allows MEF to create filters on the import, so that only particular types are imported. The Lightswitch application imports types that implement the IShellCommandGroupProvider and knows what to do with it. This way Lightswitch can discover our command groups and add them to the ribbon.

[Export(typeof(IShellCommandGroupProvider))]
public class MyGroupProvider : IShellCommandGroupProvider
{
    public IEnumerable<IShellCommandGroup> GetShellCommandGroups(Microsoft.LightSwitch.Client.IScreenObject currentScreen)
    {
        yield return new MyGroup();
    }
}

Now if you run your application you’ll see your button added to the ribbon. Once this is working, you can continue to modify the properties of your button, group and executable to do what you want.

image

Using the Sync Framework on Windows Phone 7

For a mobile platform, like Windows Phone 7, you may be required to allow an application to work even when there’s no data connection available. Doing so is not that easy. You need to manage state locally, synchronize changed data (one-way or duplex) and maybe even handle conflicts. Luckily Microsoft offers the Sync Framework which makes it easier for you to accomplish this by taking much of the required plumbing out of your hands completely. Currently there’s a community technology preview available of Sync Framework 4 (they skipped version 3 to be in line with other releases), Sync Framework 4 makes it even easier for clients to support synchronization because all synchronization logic has moved to the server.

The figure below shows the Sync Framework 4 architecture where the blue components are the ones provided by Microsoft and the orange components the ones you need to develop yourself. The Sync Logic block is the part that you can extend to allow for more granular control over de synchronization.

syncfx002

For synchronizing data, the Sync Framework makes use of the OData protocol. Since OData is an open standard all kinds of platforms are able to sync data with the Sync Framework. Out of the box the Sync Framework comes with a provider for SQL Server and SQL Azure. On the client the Sync Framework provides components for Isolated Storage so you can use sync without writing a line of code on both Silverlight for the desktop as Silverlight for Windows Phone 7.

In this article I’m going to demonstrate how to use the Sync Framework in your application for Windows Phone 7 by creating an application to manage a shopping list. As I was using the Sync Framework myself I ran into a couple of problems, so I will add my learnings as well.

1. Prerequisites

Before you can use the Microsoft Sync Framework, you need to install the framework itself. To obtain the framework please visit this page and download the file ‘Download Instructions.mht’. Once you open the file you are prompted with two steps. By following the first step you need to answer a few basic feedback questions before you are redirected to the Microsoft Connect site. Once you are transferred to the Microsoft Connect website, click on ‘Downloads’ menu option on the left. You’re now at the download page. Please read the description carefully. It tells you that you need to install the Microsoft Sync Framework 2.1 first before installing the 4.0 CTP. Choose the operating system version that is most appropriate to you. Note that when you’re running the 64-bit version of Windows you need to install the 32-bit redistributables of the Sync Framework 2.1 in order to test in Visual Studio.

Now you have installed the Microsoft Sync Framework, the next step in this sample is setting up the sample database. To do this, download the following SQL file and open and run it in SQL Server Management Studio. This will create a new database named ‘ShoppingList’ and polulate some sample data.

2. Generate Sync Configuration

The sync configuration describes what data you want synchronized. The configuration contains the name and location of the data store, what entities to synchronize and what filters to apply to the entities so you can limit what data is exchanged over the wire. The configuration is used in the next steps to provision the database and generate client and server code.

Since the sync configuration is an XML-file it’s off course possible to craft it by hand, but in this case we’re using the ‘SyncSvcUtilHelper’ tool to do it for us. This tool is limited in what it can do, so if you need more control over the configuration file you can always tweak it afterwards.

The ‘SyncSvcUtilHelper’ tool is a graphical user interface build on top of ‘SyncSvcUtil’ command line tool, which is meant to provision the database and generate code. Generating a sync config isn’t actually a functionality of the ‘SyncSvcUtil’ tool, but for easy of use it’s incorporated in the ‘SyncSvcUtilHelper’ anyway. You find both tools in the ‘bin’ directory of the Microsoft Sync Framework SDK installation directory, which is by default ‘C:Program FilesMicrosoft SDKsMicrosoft Sync Framework4.0bin’ on 32-bit and C:Program Files (x86)Microsoft SDKsMicrosoft Sync Framework4.0bin on 64 bit Windows.

Once you fire up ‘SyncSvcUtilHelper’ you’re presented with the main screen with 3 options:

  1. Generate or Edit Sync Configuration
  2. Provision or Deprovision
  3. Code Generation

syncfx003

By executing all these three steps in this order gives you all you need to be able to sync data between server and a Silverlight-based client. In this case you obviously use the first option to generate the sync configuration. On the first screen specify a file name for the sync configuration file. It’s best to save the configuration file in the same directory as the ‘SyncSvcUtilHelper’ since you’re going to need it in the subsequent steps and the tool defaults to this directory.

syncfx004

On the next screen you need to enter the details of the database that you’ll use to sync data with. Click on the Add button to add a new database. Enter the details of your database and click the Save button to add your database. Click the Next button to go to the next screen.

syncfx005

In this screen you define the synchronization scopes of our application. A synchronization scope defines what data to synchronize for that scope. You can have multiple scopes for different synchronization purposes. In this case you use just one scope and name it ‘DefaultScope’. To add a scope click the Add button and enter the scope name. For schema name, use ‘dbo’ and make sure you check the box in front of ‘Is Template Scope’ since a template scope allows us to apply filters. Click the Save button and then Next to continue.

syncfx006

Here is where you choose what entities (tables) you want to synchronize and on what columns you want to enable filtering. To choose the tables to synchronize, click the ‘Add/Edit’ button. In the new window select the database you’ve configured a few screens back and a list of tables is shown. Select all columns of both tables for syncing and enable filtering on the ‘Id’ column of the ‘User’ table and ‘UserId’ column of the ‘Item’ table. Click the ‘OK’ button to close the window and click the Next button to continue.

On the first screen you see when you chose step 2, you open the sync configuration file you created before. Since you only have one database and sync scope, they are selected by default, so you can just click the ‘Next’ button to continue.

syncfx007

The summary screen shows you the details of the sync configuration that’ll be saved to the file. Once you click the ‘Finish’ button, the file is created and the tool is closed.

3. Provision the database

Open the ‘SyncSvcUtilHelper’ tool again, but this time choose step 2, in which you’ll provision the database. By provisioning the database, additional tables are created for all the tables you configured in the sync configuration file. The additional tables store sync related information so the Sync Framework can calculate diffs and detect conflicts.

On the first screen you get once you chose step 2, you open the sync configuration file you created before. Since you only have a single sync scope and target database they’re selected by default. Click the ‘Next’ button to start provisioning the database.

syncfx008

If provisioning of the database was successful you can close the tool by clicking the ‘Finish’ button.

syncfx009

4. Create the Sync Service

A full Sync Framework solution consists of a Sync Service on the server that speaks OData and an offline context on the client for storing data offline and tracking changes. In this step you’ll create the Sync Service.

To do so you first need a web application to host the Sync Service. Fire up Visual Studio 2010 and create a blank solution (File > New Project > Blank Solution) and name it ‘ShoppingList’. Then add a new ASP.NET Empty Web Application to the ShoppingList solution, named ‘Web’. Add a reference to ‘Microsoft.Synchronization.Services.dll’ which can be found in the ‘Server’ directory of the Microsoft Sync Framework 4.0 installation directory.

Now you have an empty web application to host you need to create the sync service. To do this you use the ‘SyncSvcUtilHelper’ tool again, but this time choose step 3. On the screen you’re presented with open the sync configuration file and click the ‘Next’ button.

syncfx010

On the ‘Select code generation parameters’ screen, select ‘Server’ as the codegen target. Specify the ‘Web’ directory of your ShoppingList solution as the output directory. You can choose whatever language, namespace and output file prefix you like. I used the parameters as in the screenshot below. Click the ‘Next’ button to start the code generation.

syncfx011

If the code generation completed successfully you can close the tool and return to Visual Studio. In the solution explorer, click the button to show all files and include both DefaultScopeEntities.cs and DefaultScopeSyncService.svc.

Before you can use the Sync Service, you need to change a couple of lines of code. Right click DefaultScopeSyncService.svc and choose ‘View code’. Replace the body of the ‘InitializeService’ method with the following lines of code:

config.ServerConnectionString = @"Data Source=.SQL2008;Initial Catalog=ShoppingList;Integrated Security=True;MultipleActiveResultSets=True";
config.SetEnableScope("DefaultScope");
config.AddFilterParameterConfiguration("userId", "User", "@Id", typeof(int));
config.AddFilterParameterConfiguration("userId", "Item", "@UserId", typeof(int));
config.SetSyncObjectSchema("dbo");
config.UseVerboseErrors = true;
config.EnableDiagnosticPage = true;

Lines 1 and 2 define the connection string to the database and which scope to use for synchronization.

Lines 3 and 4 are used to add a mapping for parameter ‘userId’ to the filters you defined when creating the sync configuration file. By using the same parameter name for both filters, the client only has to provide a single value to be able to filter both tables.

The lines 6 and 7 are only used for diagnostics and should be turned off in a production environment. UserVerboseErrors enables full errors to be send back to the client in case of an exception. This comes in handy when trying to find out why you’re service isn’t syncing properly. EnableDiagnosticsPage enables a page that shows you whether the basic server configuration is valid. This page is the first thing you check if you want to know if the Sync Service is working.

Now check if your Sync Service is working by right clicking ‘DefaultScopeSyncService.svc’ and choosing ‘View in browser’. The page should load correctly. Then change the ‘$syncscopes’ parameter in the url to ‘$diag’ and press enter. If all tests passed, except for the ClientAccessPolicy/CrossDomain files, you’re ready to continue to create the Windows Phone 7 client.

5. Create the Windows Phone 7 client

Add an empty Windows Phone 7 application to the solution. In this example I named it ‘ShoppingList’. Add a reference to ‘Microsoft.Synchronization.ClientServices.dll’ which can be found in the ‘ClientWP7′ directory of the Microsoft Sync Framework 4.0 installation directory. Also add a reference to the Silverlight 3 version of ‘System.ComponentModel.DataAnnotations.dll’. This assembly van be found in C:Program FilesMicrosoft SDKsSilverlightv3.0LibrariesClient (32 bit) or C:Program Files (x86)Microsoft SDKsSilverlightv3.0LibrariesClient (64 bit).

In order to access the sync service and use the entities, you need to generate the client code. Start the ‘SyncSvcUtilHelper’ tool and follow the same steps as when generating code for the server. This time choose ‘IsolatedStore Client’ as the codegen target and specify the Windows Phone 7 application directory, ‘ShoppingList’. The ‘IsolatedStore Client’ generates code that uses IsolatedStorage for storing the synced entities and can be used for both Silverlight for the desktop as for Windows Phone 7. Click the ‘Next’ button to start the code generation.

syncfx012

If the code generation completed successfully you can close the tool and return to Visual Studio. Make sure the Windows Phone 7 project is selected and in the solution explorer, click the button to show all files and include both DefaultScopeEntities.cs and DefaultScopeOfflineContext.cs.

Now open up MainPage.xaml.cs and replace all the code with the following:

using System;
using Microsoft.Phone.Controls;
using Microsoft.Synchronization.ClientServices.IsolatedStorage;

namespace ShoppingList
{
    public partial class MainPage : PhoneApplicationPage
    {
        private DefaultScope.DefaultScopeOfflineContext context;

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            context = new DefaultScope.DefaultScopeOfflineContext(
                    "ShoppingList",
                    new Uri("http://localhost/ShoppingList/DefaultScopeSyncService.svc")
                );
            context.CacheController.ControllerBehavior.AddScopeParameters("userId", "1");
            DataContext = context;

            context.LoadCompleted += context_LoadCompleted;
            context.LoadAsync();
        }

        void context_LoadCompleted(object sender, LoadCompletedEventArgs e)
        {
            context.CacheController.RefreshAsync();
        }

        private void ApplicationBarIconButton_Click(object sender, EventArgs e)
        {
            context.CacheController.RefreshAsync();
        }
    }
}

In lines 16 to 19 the DefaultScopeOfflineContext is initialized with the path in IsolatedStorage to store the offline data and the Uri to the sync service. In line 20 a parameter is added for the userId. For each client parameter name on the server you need to add a scope parameter on the client. In this case there’s just a single client parameter ‘userId’, which is mapped on the server to two table fields. In the sample data in the database there are two users you can try: IDs 1 and 2. In line 21 the DefaultScopeOfflineContext is set as DataContext so you can use it in the XAML bindings. In lines 23 and 24 an event handler for LoadComplete is assigned and the context is asked to load the offline stored data from IsolatedStorage. Once the offline data is loaded, the CacheController is asked to refresh, which calls the sync service to synchronize the offline data with the live data on the server. Once the sync completes, the data in the offline context is in sync with the server data, which is done in line 34.

Open up MainPage.xaml and add a ListBox to the ContentPanel Grid using the following piece of XAML. This creates a ListBox that binds to the ItemCollection propery of the DefaultScopeOfflineContext which contains the items in your shopping list. A CheckBox is used so you can mark the item as bought.

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Sync" Click="ApplicationBarIconButton_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Return to the MainPage.xaml.cs and add the event handler for the ApplicationBar button using the following piece of code. This makes sure any changes made by the user are persisted to the IsolatedStorage first and then start the synchronization.

private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
    context.SaveChanges();
    context.CacheController.RefreshAsync();
}

Now your application is ready to test! Make sure the Windows Phone 7 project is marked as startup project and then press ‘F5′ to start debugging. Once the application is started you should soon see a list of shopping list items. Try making changes on the client by marking an item as bought and click the sync button and notice that the IsBought value changed in the database. Also try adding additional items to the Item table in the database and notice the items appear in the list in the Windows Phone 7 client as soon as you click the sync button again.

syncfx013

Debugging a sync service

Having trouble with your sync service, but don’t have a clue what’s wrong? Debugging a sync service is a bit tricky. As you probably noticed during your own investigation, you can handle the RefreshCompleted event of the CacheController, but all you find when inspecting the Error property of the RefreshCompletedEventArgs is a ‘The remote server returned an error: NotFound.’. Before explaining how to do it, here are a few questions I compiled after stepping into a couple of common errors myself:

  • Open the Diagnostics page. Do all tests pass?
  • Did you provision the database? Re-provisioning doesn’t hurt, so just try it.
  • Did you add lines of code to the sync service code behind setting the connection string and sync scope name? When regenerating the server code, this file could be overwritten.
  • Did you add filter parameter configurations for all the filter parameters in your sync service config on the server?
  • Did you add parameters on the client for each parameter mapping on the server?
  • Are the names and types of the filter parameters on the server equal to the GlobalName values in your sync service configuration?
  • Does the account under which the sync service runs have access to the database in the connection string?
  • Did you specify the correct Uri on the client? Try the Uri in your browser to make sure it is correct.

If all of the above didn’t help it’s time to start debugging the sync service. For debugging you use a common HTTP-traffic monitor tool called Fiddler2. Download Fiddler2 and install it. Next start Fiddler2 and configure it to allow remote connections, as described in this blog post. You should be able to capture the internet traffic of the Windows Phone 7 emulator by now. Make sure you start the emulator after you started Fiddler2, so if it is running, restart it first. Open internet explorer on the emulator and browse to a particular website. Fiddler2 should show you the internet traffic.

Since the ASP.NET Development Service doesn’t allow remote connections and the emulator is regarded as a remote device, you need to configure the web application to run in IIS. Note: I assume you’re running Vista or Windows 7 and you already installed IIS 7. Open the properties of the web project and go to the ‘Web’ tab. Select to use the local IIS web server and specify a Url, see example below. Click ‘Create Virtual Directory’ to create the virtual directory if it doesn’t exist yet.

syncfx001

Fiddler2 doesn’t capture traffic going to localhost, so in order to capture your sync service traffic go to MainPage.xaml.cs in your Windows Phone 7 application and replace localhost with the computer name in the Uri to the sync service. Make sure the Uri points to the IIS hosted sync service.

In the DefaultScopeSyncService.svc.cs file in your web project, make sure you enabled UserVerbodeLogging by adding the following line of code:

config.UseVerboseErrors = true;

Now, if you encounter a synchonization error, go into Fiddler2 and lookup the faulted web session, which is indicated with status 500. Click the ‘TextView’ button of the response area (bottom right part of the screen) in order to see the plain error message that was transmitted over the wire.

syncfx014

Building a location aware Windows Phone 7 application

The Windows Phone 7 SDK contains a couple of APIs for interacting with different parts of the phone hardware, like accessing the camera or initiating a phone call. One of the cool APIs of Windows Phone 7 is the Location API. Just as in the desktop version of .NET Framework 4.0, the Location API enables you to get the approximate location of the user. To find the location of the user, the API uses GPS, WiFi or cell tower triangulation, where the first is the most and the latter least accurate. The API hides the method of determining the location for you as a developer and will attempt to use the most accurate available method. By default it uses cell tower triangulation, but if you tell it you need high accuracy it will try WiFi or GPS when available. For you as a developer you need to carefully determine the level of accuracy, since the more accurate method the slower and more power consuming it gets.

In this example I’ll show you how to use the Location API. First off I created a new Windows Phone application using the ‘Windows Phone Databound Application’ project template. You can use any of the project templates, but I chose this one because I like to use MVVM all the time. The next thing I do is changing the UI. Because I use the Bing maps control, make sure you reference the Microsoft.Phone.Controls.Maps.dll assembly. Also add a reference to the namespace to the MainPage.xaml, like this:

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

We will use a TextBlock to show the current estimated address and a Maps control to show your current estimated location on a map. Replace the complete contents of the ContentPanel Grid with the following XAML snippet. You may need to remove some code from the codebehind of MainPage.xaml because it references the ListBox we remove.

  <StackPanel>
    <TextBlock Text="{Binding Status}" />
    <TextBlock Text="{Binding Address}" />
    <maps:Map Center="{Binding Location, Mode=TwoWay}" ZoomLevel="13">
      <maps:Map.CredentialsProvider>
        <maps:ApplicationIdCredentialsProvider ApplicationId="&lt;YourKeyHere>"/>
      </maps:Map.CredentialsProvider>
      <maps:Pushpin Content="You are here" Location="{Binding Location}" />
    </maps:Map>
  </StackPanel>

In order to use the Bing maps control, you need your own key. Request a key on the Bing maps portal.

In the XAML I’ve used bindings to Status, Address and Location properties. These properties do not yet exist, so the next thing is to add these properties to the MainViewModel class in MainViewModel.cs. The Status property is of type GeoPositionStatus and contains the status of the GeoCoordinateWatcher, the Address property is a string containing the current estimated address and the Location property is of type GeoCoordinate, which is a type used to represent a lat/long position. The following code snippet shows you the three properties to be added to the view model:

private GeoPositionStatus _status;
/// <summary>
/// Contains the status of the GeoCoordinateWatcher to see if it is initializing, ready or not getting any data.
/// </summary>
/// <returns></returns>
public GeoPositionStatus Status
{
    get
    {
        return _status;
    }
    set
    {
        if (value != _status)
        {
            _status = value;
            NotifyPropertyChanged("Status");
        }
    }
}

private string _address;
/// <summary>
/// Contains the current estimated address as returned by the reverse geolocation web service.
/// </summary>
/// <returns></returns>
public string Address
{
    get
    {
        return _address;
    }
    set
    {
        if (value != _address)
        {
            _address = value;
            NotifyPropertyChanged("Address");
        }
    }
}

private GeoCoordinate _location;
/// <summary>
/// Contains the current estimated lat/long location as returned by the GeoCoordinateWatcher.
/// </summary>
/// <returns></returns>
public GeoCoordinate Location
{
    get
    {
        return _location;
    }
    set
    {
        if (value != _location)
        {
            _location = value;
            NotifyPropertyChanged("Location");
        }
    }
}

Now that we’ve set up our UI and bindings, it’s time to start using the Location API. The Location API consists of two classes: GeoCoordinateWatcher and CivicAddressResolver, just like in the desktop framework. The GeoCoordinateWatcher does the heavy lifting of calculating the approximate lat/long of the phone, while the CivicAddressResolver translates the lat/long to a human readable address using reverse geolocation. Unlike the desktop version of the .NET Framework, the CivicAddressResolver isn’t implemented on the phone. Instead of throwing an exeption it will always tell you the address is unknown. This means we can’t use this class yet to resolve the lat/long to an address. Instead we will use the Bing web service for that, but more on that later.

In order to get the lat/long of our current position, we need to create an instance of the GeoCoordinateWatcher, subscribe to the StatusChanged and PositionChanged event and start listening for position changes. The following lines of code will do just that:

GeoCoordinateWatcher gcw;

public MainViewModel()
{
    Items = new ObservableCollection<ItemViewModel>();
    gcw = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
    gcw.StatusChanged += gcw_StatusChanged;
    gcw.PositionChanged += gcw_PositionChanged;
    gcw.Start();
}

void gcw_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
    Status = e.Status;
}

void gcw_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
    Location = e.Position.Location;
    Address = String.Format("{0}, {1}", Location.Latitude, Location.Longitude);
}

In other examples on the web you’ll find that most of the time also the StatusChanged event of the GeoCoordinateWatcher is handled to see if it is ready before listening for position changes. Because you could get a PositionChanged event before you’ve even received a StatusChanged event, it’s not very useful to do so and may cause you to miss the PositionChanged event. So be sure just to listen for the PositionChanged event and only use the StatusChanged event to display the status, like done in this example.

In this case I used the default constructor of the GeoCoordinateWatcher, which uses the default accuracy (which only uses cell tower triangulation). If I wanted, I could pass in GeoPositionAccuracy.High as a parameter, which tells the GeoCoordinateWatcher to use WiFi triangulation or GPS when available. The PositionChangedEvent gives you a property ‘HorizontalAccuracy’ which tells you, in meters, how accurate the detection of the position was and allows you to filter for specific accuracy. Note that when done indoors and without a WiFi connection, it won’t provide a better accuracy then the default, since only cell tower triangulation is available.

The event handler for the StatusChanged event updates the Status property and the PositionChanged event handler updates the Location property and takes the latitude and longitude and concatenates them into the Address property.

Now if you run the application in the emulator by pressing F5 (which is the default), you’ll notice that nothing happens and the status is ‘NoData’. This is because the emulator doesn’t emulate location data. There are two things you can do:

1) Simulate location data using this technique described by Tim Heuer.

2) Test on your Windows Phone. This requires your phone being developer unlocked.
clip_image001
If you have the possibility to test on a physical Windows Phone, I’d highly recommend it. During my tests it appeared the location data on the phone wasn’t what I expected. For instance, the GeoLocationWatcher was unable to find my location using cell tower triangulation as soon as I have HSDPA connectivity. This probably has something to do with the way HSDPA works. It also shows you that the GeoCoordinateWatcher may jump between NoData and Ready every now and then when e.g. connection is lost or the phone switches to a different type of connection.

As soon as you run the application on an actual phone you’ll see something similar to this:
clip_image002
Now that we’re able to obtain the lat/long of our estimated position, I’d like to translate the lat/long into an address I can understand. As mentioned earlier, we can’t use the the CivicAddressResolver class since it’s not implemented for Windows Phone 7. Instead we’ll use the Bing web service. In order to do this, add a service reference to the following URL:

http://dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc
clip_image003
Adding the service reference also creates a new file ‘ServiceReferences.ClientConfig’ to your project. This file contains the WCF bindings required for calling the web service. By default it will add two end points: a SOAP endpoint (the first) and a Binary endpoint (the second). Because WCF can’t decide by itself which of the two to use, we remove the SOAP binding from the file.

Now we want to modify our code to call the ReverseGeocode service method once we receive a position update. In order to do so we modify the MainViewModel class. First we add a field of type GeocodeServiceClient. In the constructor we initialize the field and handle the ReverseGeocodeCompleted event. In the event handler we check if any addresses are returned and display the first found address. In the PositionChanged event hander of the GeoCoordinateWatcher, instead of setting the Address property using the lat/long values, we construct a ReverseGeocodeRequest using the Bind key we used earlier and the lat/long values and call the ReverseGeocodeAsync method. The changes to the MainViewModel are shown in the following snippet (changes are in bold):

GeoCoordinateWatcher gcw;
GeocodeServiceClient gsc;

public MainViewModel()
{
    Items = new ObservableCollection<ItemViewModel>();
    gsc = new GeocodeServiceClient();
    gsc.ReverseGeocodeCompleted += gsc_ReverseGeocodeCompleted;
    gcw = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
    gcw.StatusChanged += gcw_StatusChanged;
    gcw.PositionChanged += gcw_PositionChanged;
    gcw.Start();
}

void gcw_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
    Status = e.Status;
}

void gcw_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
    Location = e.Position.Location;
    var request = new ReverseGeocodeRequest();
    request.Credentials = new Credentials { ApplicationId = @"MyApplicationId" };
    request.Location = new Location { Latitude = e.Position.Location.Latitude, Longitude = e.Position.Location.Longitude };
    gsc.ReverseGeocodeAsync(request);
}

void gsc_ReverseGeocodeCompleted(object sender, GeoCodeService.ReverseGeocodeCompletedEventArgs e)
{
    if (e.Result.Results.Count == 0)
        Address = "No address found";
    else
        Address = e.Result.Results[0].DisplayName;
}

Now if we run the application, we don’t see the lat/long values, but instead see a readable address of our estimated location.
clip_image004
There are a couple of things wrong with this implementation. First, each time the position is changed a new web request is done to get the address, even if the location didn’t change (much). Second, if a web request is underway, it’s not cancelled, but instead a new web request is done. This could lead to a racing condition where the second request would complete before the first, resulting in the address of the first location being displayed. Third, the PositionChanged event keeps firing, even if the position doesn’t change. This costs unnecessary resources on the phone.

The third flaw could be overcome by using a MovementThreshold. MovementThreshold is a property on the GeoCoordinateWatcher and specifies the amount of movement is necessary before a new PositionChanged event is fired. A common MovementThreshold is one of 20.0, e.g.:

gcw = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
gcw.MovementThreshold = 20.0;
gcw.StatusChanged += gcw_StatusChanged;
gcw.PositionChanged += gcw_PositionChanged;
gcw.Start();

The first two flaws can be overcome by writing more boilerplate code, like starting timers and cancelling web requests. This could easily lead to hard to read code since it is all scattered around. A way to simplify things is to use Reactive Extensions, but that’s food for another blog post.

WCF RIA Services OData endpoint does NOT support update or LINQ

After struggling with WCF RIA Services v1 (Silverlight 4 RTM) to enable insert/update for the OData endpoint for use with Windows Phone 7, I finally figured it out. The current version of WCF RIA Services does NOT support update or LINQ! I think I searched for a couple of hours until I found the single line of information on this in this following Silverlight TV post:

NOTE: The ODATA endpoint has very limited support in V1. There is no Update or LINQ query support in this release.