In this article I’ll go through the steps to develop a Windows Phone 7 Series application with Silverlight to browser the Northwind-database exposed through oData at http://services.odata.org/Northwind/Northwind.svc/. The end result will be an application with three pages that allow the user to browse the categories, products and product-details of the database. The following screenshots demonstrates the end result:



If you’d like to skip the article and simply try out the application directly, please download the code here!
The easiest way to get started is to create a new application with Visual Studio 2010 RC (either Express or higher, I’m using Express) and leverage the “Windows Phone List Application”-template which will help with the foundation for the application, choose an appropriate name and let’s go... When the solution have been created we can start by removing stuff we won’t be needing (or actually, stuff we’ll be adding ourselves later on). I will not be using the ViewModel, instead I’ll be leveraging a catalog that I’ll bind UI-elements to, this means we can remove the ViewModels-folder. I also remove the file MainViewModelSampleData.xaml since I’ll be adding custom sample data for the catalog recently mentioned. We should also add a reference to System.Service.Data.Client.dll which if you don’t already have installed can be found here! The image to the right shows the solution explorer with all folders and files expanded when all actions above have been taken.
The first thing we will do is to create the data-types required to communicate with the oData service that exposes the Northwind database. It can be done by opening a command-shell and browse to the following directory or the system-root:
c:\Windows\Microsoft.NET\Framework\v4.0.30128
And in that directory you enter the following command:
DataSvcUtil /uri:http://services.odata.org/Northwind/Northwind.svc/
/DataServiceCollection /Version:2.0 /out:C:\temp\NorthwindClientTypes.cs
I won’t discuss the different parameters here instead, other than the /out: parameter which lets you specify where you want the client-library to be generated. You should feel free to provide your own location and filename, as long as you remember where you generate the content. Then we can include this file in the solution created, I choose to add a folder to the solution which I call Northwind and then add the generated file there. If you try to compile the solution at this time you will most likely get an error complaining about the earlier removed MainViewModel, simply comment out that line of code and recompile. Everything should work just fine, but now you will also notice that the MainPage.xaml file in the visual editor doesn’t display that nice sample data earlier provided, lets fix that for the joy of visual experience while developing.
First we need to create the Northwind-catalog though. This is the component that we will use to fetch the appropriate data from the service, and also expose properties that the differenty UI-elements in our application can databind to. Add a class to the Northwind-folder and call it NorthwindCatalog.cs. I will also choose to change the namespace of this file to be the same as the earlier generated client-types, namely NorthwindModel. I’ve also made it a best practise for myself to leverage interfaces as much as possible while creating data-access layers which we can consider this to be, hence I create the following interface in the NorthwindModel namespace in the NorthwindCatalog.cs file:
public interface ICatalog
{
DataServiceCollection<Product> Products { get; }
void GetProductsByCategoryID(string id);
DataServiceCollection<Category> Categories { get; }
void GetCategories();
}
You will need to add a using-statement to System.Data.Services.Client above but thankfully Visual Studio gladly helps with such actions. Next we implement this interface in our actual NorthwindCatalog by simply inheriting from the ICatalog interface and let Visual Studio once again perform it’s magic, simply by hitting CTRL+. (period). We should now have the following implementation below the interface-definition:
public class NorthwindCatalog : ICatalog
{
public DataServiceCollection<Product> Products
{
get { throw new NotImplementedException(); }
}
public void GetProductsByCategoryID(string id)
{
throw new NotImplementedException();
}
public DataServiceCollection<Category> Categories
{
get { throw new NotImplementedException(); }
}
public void GetCategories()
{
throw new NotImplementedException();
}
}
Before we move away from this file we need to add the basic features of the catalog and that is storing the actual Product and Category-collections. We add the following members to the NorthwindCatalog:
NorthwindEntities _context = null;
DataServiceCollection<Category> _categories;
DataServiceCollection<Product> _products;
Let’s also add a constructor, simply by typing ctor and hitting Enter within the NorthwindCatalog class. Populate the constructor with the instanciation of the _context and DataServiceCollections as below:
_context = new NorthwindEntities(
new Uri("http://services.odata.org/Northwind/Northwind.svc/"));
_categories = new DataServiceCollection<Category>(_context);
_products = new DataServiceCollection<Product>(_context);
The last thing we do is to make sure the Products and Categories properties return the correct collections:
public DataServiceCollection<Product> Products {
get { return _products; }
}
public DataServiceCollection<Category> Categories
{
get { return _categories; }
}
Now we’re done so far with the catalog, we do need to make the implementations of the actual fetching of data from the services, but that will be at a later stage. Instead we will move to fix the sample-data feature that I feel is so appealing to the entire development experience.
Add a text file to the SampleData-folder, call it NorthwindCatalogSampleData.xaml. Visual Studio will complain that there’s no root-element in the xaml-file, that’s easily fixed, paste the code below to get you started:
<northwind:NorthwindCatalog
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:northwind="clr-namespace:NorthwindModel"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<northwind:NorthwindCatalog.Categories>
</northwind:NorthwindCatalog.Categories>
<northwind:NorthwindCatalog.Products>
</northwind:NorthwindCatalog.Products>
</northwind:NorthwindCatalog>
What we’re simply doing is creating an instance of the NorthwindCatalog and making sure that we also can use the northwind-namespace by referencing it with xmlns:northwind=... This namespace should be NorthwindModel if you have chosen the same name as I’ve had earlier.
Now let’s create some instances of the Category-class. You could either use the intellisense feature in Visual Studio and manually add a couple of your own categories with the appropriate properties. The properties that are important to this application is: CategoryID, CategoryName and Description. The Picture property could also create a more visually appealing UI, but Northwind stores the data for Pictures of the different categories in a binary format and that would require us to create a ValueConverter to easily bind the data, that’s out of this articles scope.
Add at least one category before you continue, here is one that you can use:
<northwind:Category
CategoryID="1"
CategoryName="Beverages"
Description="Soft drinks, coffees, teas, beers, and ales"/>
Now let’s hook up the UI-elements to databind to this catalog and the Categories-collection.
Open the MainPage.xaml once again. You should already have a pretty visual guidance where your first action should take place, change the d:DataContext attribute in the root-element to be:
d:DataContext="{d:DesignData SampleData/NorthwindCatalogSampleData.xaml}"
But we still doesn’t see anything in the list, don’t worry, we have some more changes to make first. In the XAML-document you should be able to find the ListBox element with the x:Name=”ListBoxOne”. Make sure that the attribute ItemsSource is wired up to the Categories collection:
<ListBox x:Name="ListBoxOne" ItemsSource="{Binding Items}"...
Now you possibly will notice that at least one icon have appeared in the list, we must be doing something right. Shift your focus to the <mpc:ListViewItem element and wire upp the binding for the attributes Text and Details as below:
<mpc:ListViewItem
Text="{Binding CategoryName}"
Details="{Binding Description}"
...
Voila, you now have a visual represenation of your sample data, and also knows that the data-binding works as expected. The “only” thing we now have to make sure of in the application in runtime is to hook up the DataContext of the page to the actual representation of the data provided by the service. Let’s do that by first opening up the code-behind file for MainPage.xaml, quick way: Hit F7 when examining the XAML-file and Visual Studio will directly take you to the correct file, I love shortcuts, don’t you?
In the constructor, beneath the comment // Set the data context... add the following code which will instanciate the NorthwindCatalog, call the GetCategories method and make sure the catalog is being used as DataContext for the page.
var catalog = new NorthwindCatalog();
catalog.GetCategories();
DataContext = catalog;
Don’t forget to right-click the NorthwindCatalog() and hit CTRL+. (period) to include the using-statement to the NorthwindModel namespace.
Now we also will have to fix the NorthwindCatalog, so let’s open up that file again and focus on the GetCategories() method. The two lines of code that need to go here is the following:
public void GetCategories()
{
_categories.Clear();
_categories.LoadAsync(_context.Categories);
}
We first clear the current collection and then asynchrously load the categories from the oData-service. Let’s try it out, by hitting F5 and if everything compiles and launches correctly you should have the something very similar to the UI provided at the top for the MainPage UI with the actual categories displayed. We should also make sure the top-elements of the page are similar, therefore close the application and open up the App.xaml file, yes that’s right, we’re going to create an application-wide resource for our application name. Directly beneath the <Application.Resources>-tag add the following line:
<system:String x:Key="ApplicationName">Northwind oData</system:String>
Then open up the MainPage.xaml once again and mark the topmost textbox, open up the Properties Windows and click the small icon next to the Text property, this will open up a context menu that you simply choose Apply Resource and the select the ApplicationName resource in the next window. Finally also change the next textbox (currently with the text “list name”) to instead contain the local value “categories”.
There you go, you now have a first page that displays data from the Northwind database exposed with an oData service. Now we could easily go to the DetailsPage, but we want that page to display details for individual products and not categories. That’s why we now need another list-page and we could easily copy the MainPage.xaml file and paste it again in the products as ProductsPage.xaml, please do that and rename the file accordingly! You also need to apply the new name ProductsPage to three more positions in the copied files. Open up the ProductsPage.xaml and rename the x:Class attribute to be ProductsPage instead of MainPage. Then open up the ProductsPage.xaml.cs file and change the class name and the constructor to ProductsPage.
This page will be navigated to from the MainPage, when the user selects a category in the listbox, but first lets hook up the databinding as previous. Start by changing the ItemsSource binding in the ListBox (ListBoxOne) to instead of Categories use Products. The ListViewItem should then have the following attributes:
Layout="Custom"
Text="{Binding ProductName}"
We still don’t see any items in the list though, but that’s because we haven’t added any sample products to the sample data file, let’s add at least one to the file NorthwindCatalogSampleData.xaml in the <northwind:NorthwindCatalog.Products> element...
<northwind:Product
ProductID="1"
ProductName="Chai"
SupplierID="1"
CategoryID="1"
QuantityPerUnit="10 boxes x 20 bags"
UnitPrice="18.0000"
UnitsInStock="39"
UnitsOnOrder="0"
ReorderLevel="10"
Discontinued="True"/>
Ok, so now the databinding appears to be working, when I go back to ProductsPage.xaml I can at least see one products, if you added more, they will appear as well. Now we have to fetch the actual Products for the Category selected in the MainPage. Lets begin with passing the categoryId (and alos the categoryName) from the MainPage to ProductsPage. Back to MainPage.xaml.cs. Change the PageTransitionList_Completed to the following:
private void PageTransitionList_Completed(object sender, EventArgs e)
{
var category = ListBoxOne.SelectedItem as Category;
NavigationService.Navigate(new Uri(
String.Format("/ProductsPage.xaml?categoryId={0}&categoryName={1}",
category.CategoryID, category.CategoryName),
UriKind.Relative));
}
What we’re doing is making sure that when we have transition-animation is completed we get the selected Category from the listbox and pass it’s CategoryID and CategoryName properties to the ProductsPage.xaml page. Now onto the ProductsPage.xaml.cs file. Begin by creating a member of ICatalog in the class:
ICatalog _catalog = null;
Now in the constructor we create an instance of the NorthwindCatalog:
_catalog = new NorthwindCatalog();
But there’s no use fetching any products yet since we don’t have acccess to the parameters yet, instead we need to override another method as follows:
protected override void OnNavigatedTo(
Microsoft.Phone.Navigation.PhoneNavigationEventArgs e)
{
base.OnNavigatedTo(e);
string categoryId = "";
string categoryName = "";
if (NavigationContext.QueryString.TryGetValue("categoryName",
out categoryName))
{
textBlockListTitle.Text = categoryName.ToLower();
}
if (NavigationContext.QueryString.TryGetValue("categoryId",
out categoryId))
{
catalog.GetProductsByCategoryID(categoryId);
DataContext = catalog;
}
}
The code above makes sure we grasp the parameters provided by the querystring to our page, and sets the title of the page to the category-name. It also calls the GetProductsByCategoryID on the catalog with the appropriate id passed to it, after that we connect the DataContext to the catalog. So how do we implement the function to fetch the products from the service? Over to the NorthwindCatalog.cs again. Implement the GetProductsByCategoryID as follows:
public void GetProductsByCategoryID(string id)
{
try
{
//_categoryId = id;
_products.Clear();
Uri uri = new Uri(
String.Format("{1}/Categories({0})/Products",
id,
_context.BaseUri.OriginalString));
_context.BeginExecute<Product>(uri, QueryCallback, null);
}
catch (Exception ex)
{
MessageBox.Show("The query could not be completed:\n"
+ ex.ToString());
}
}
I’ve included some error-handling above cause I did have some issues when trying to get this code to work, naturally it’s also good practice to handle error as efficient as possible. We do need on more method though, since we are using the asynchronus method BeginExecute to fetch the products for the appropriate category. The following is the code for the QueryCallback method:
private void QueryCallback(IAsyncResult asyncResult)
{
Deployment.Current.Dispatcher.BeginInvoke(
() => {
try
{
IEnumerable<Product> results =
_context.EndExecute<Product>(asyncResult)
.ToList() as IEnumerable<Product>;
Products.Clear();
Products.Load(results);
}
catch (Exception ex)
{
MessageBox.Show("The query could not be completed:\n"
+ ex.ToString());
}
}
);
}
If you try to compile now you will get some possibly strange compilation errors, these are easily fixed by making sure you have these two using-statements added:
using System.Collections.Generic;
using System.Linq;
Running the application now should give you the opportunity to browse both categories and products from the service, try navigating back and try different categories, pretty neat. What’s noticeable (or not) is that we don’t have any animation when going back to the MainPage. I’ll leave that as an exercise for later. Now onto the last page, the actual DetailsPage. Let’s open that up and begin with some sample-data. Make sure you’re using the NorthwindCatalogSampleData as the d:DataContext on the page’s root-element. Then update the Grid-element named “LayoutRoot” to have the following dataContext attribute:
d:DataContext=”{Binding Products[0]}”
Then we update the TitleGrid’s textboxes to the following:
<TextBlock Text="{StaticResource ApplicationName}"...
<TextBlock Text="{Binding ProductName}"...
This should cause the title of the page to be the name of the first product in the sample-data added earlier. Then it’s a simple exercise of providing the UI of the rest of the properties. Here’s is my resulting ContentGrid:
<Grid x:Name="ContentGrid" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition Height="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Projection>
<PlaneProjection/>
</Grid.Projection>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="165"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock TextWrapping="Wrap" Text="unit price" VerticalAlignment="Center"
Margin="5,0,0,0" Style="{StaticResource PhoneTextSmallStyle}"/>
<TextBlock Text="{Binding UnitPrice}" VerticalAlignment="Center"
Style="{StaticResource PhoneTextBodyTextStyle}" Grid.Column="1"
Margin="23,0,0,0" HorizontalAlignment="Left"/>
<TextBlock TextWrapping="Wrap" Text="units in stock" Margin="5,12,0,13"
Style="{StaticResource PhoneTextSmallStyle}" Grid.Row="1"
d:LayoutOverrides="Height"/>
<TextBlock Text="{Binding UnitsInStock}" HorizontalAlignment="Left"
Style="{StaticResource PhoneTextBodyTextStyle}" Grid.Column="1"
Margin="23,8,0,10" Grid.Row="1" d:LayoutOverrides="Height"/>
<TextBlock TextWrapping="Wrap" Text="quantity per unit" Margin="5,12,0,13"
Style="{StaticResource PhoneTextSmallStyle}" Grid.Row="2"
d:LayoutOverrides="Height"/>
<TextBlock Text="{Binding QuantityPerUnit}" HorizontalAlignment="Left"
Style="{StaticResource PhoneTextBodyTextStyle}" Grid.Column="1"
Margin="23,8,0,10" Grid.Row="2" d:LayoutOverrides="Height"/>
<TextBlock TextWrapping="Wrap" Text="reorder level" Margin="5,12,0,13"
Style="{StaticResource PhoneTextSmallStyle}" Grid.Row="3"
d:LayoutOverrides="Height"/>
<TextBlock Text="{Binding ReorderLevel}" HorizontalAlignment="Left"
Style="{StaticResource PhoneTextBodyTextStyle}" Grid.Column="1"
Margin="23,8,0,10" Grid.Row="3" d:LayoutOverrides="Height"/>
<TextBlock TextWrapping="Wrap" Text="units on order" Margin="5,12,0,13"
Style="{StaticResource PhoneTextSmallStyle}" Grid.Row="4"
d:LayoutOverrides="Height"/>
<TextBlock Text="{Binding UnitsOnOrder}" HorizontalAlignment="Left"
Style="{StaticResource PhoneTextBodyTextStyle}" Grid.Column="1"
Margin="23,8,0,10" Grid.Row="4" d:LayoutOverrides="Height"/>
<TextBlock TextWrapping="Wrap" Text="discontinued" Margin="5,12,0,13"
Style="{StaticResource PhoneTextSmallStyle}" Grid.Row="5"
d:LayoutOverrides="Height"/>
<CheckBox IsChecked="{Binding Discontinued}" HorizontalAlignment="Left"
Style="{StaticResource PhoneToggleSwitch}" Grid.Column="1" Margin="0"
Grid.Row="5" d:LayoutOverrides="Width" VerticalAlignment="Center"/>
<TextBlock Margin="149,15,67,0" TextWrapping="Wrap" Grid.Column="1"
Text="{Binding Discontinued}" VerticalAlignment="Top" Grid.Row="5"/>
</Grid>
Now you should see a visual representation of the product from the sample data. Let’s hook this up to the actual service now. As a matter of fact, if we run the application now, you might be suprised to see that it already works!
The secret to this amazing behavior is that the solution template that we leverages as a starting point for this application has the beahavior of passing the selectedItem of the listbox to the DataContext of the next page, and since our databinding was only created on the design-surfaces everything works perfectly.
The only thing that I’m slightly discouraged by with the “finished” solution is that when we navigate back to the ProductsPage from the DetailsPage, the current implementation will actually go to the service again and bring down the products. This will cause some load on the network and I’m guessing we don’t want to hog the device-bandwidth with this traffic, unless we are the carrier of the network
. I still need to find some sort of nice caching implementation before I’m completely happy but still this article I hope have provided you with a good foundation of creating oData viewers with Windows Phone 7 Series.
Keep the comments coming, please!