This article is Day #14 in a series called 31 Days of Mango, and was written by guest author Chris Woodruff. Chris can be reached on Twitter at @cwoodruff. Chris is also a host of the podcast Deep Fried Bytes, so make to check that out as well.
Today, we will be looking at how to consume data from an OData feed and also perform full data duties that Windows Phone 7 apps need to perform.
What is OData?
The official statement for Open Data Protocol (OData) is that is a Web protocol for querying and updating data that provides a way to unlock your data and free it from silos that exist in applications today. Really what that means is that we can select, save, delete and update data from our applications just like we have been against SQL databases for years. The benefit is the ease of setting up and libraries that Microsoft has created for us the developers of Windows Phone 7 Mango apps. The benefit comes from the fact that OData has a standard that allows a clear understanding of the data due to the metadata from the feed.
Behind the scenes, we send OData requests to a web server that has the OData feed through HTTP calls using the protocol for OData. You can read more about OData here.
Setting up OData for your Mango Application
Now with the Windows Phone SDK 7.1, we have a great experience to use OData in our applications. In the previous version of the Windows Phone SDK 7.0, we had to use an external tool to generate our proxy classes based on a given OData feed. Now we have built in Visual Studio 2010 support. To import an OData feed first create a new Silverlight for Windows Phone/Windows Phone Databound Application project.
We will have created a project with the initial project set as shown below.
To import and get a your OData feed accessible from your project you will need to add the feed through the Add Service Reference in visual Studio 2010.
The Add Service Reference will be presented to you and you will enter the URL for the OData feed you wish to work with as shown below. Press the Go button for Visual Studio 2010 to gather the information about the feed and finally press OK to add the OData feed to your project.
We will use for this project an OData feed with historical baseball statistics.
http://www.baseball-stats.info/OData/baseballstats.svc/
You now have your proxy classes generated that will allow you to work with data from the OData feed you have set up within your project. By adding the OData feed to your project, you have also added the reference to the System.Data.Services.Client assembly that will allow you to work with the OData feed and data.
The DataServiceCollection class
The System.Data.Services.Client namespace is a rather magical set of classes that will allow you to do many exciting things with your OData feed. We will not go into this namespace other than the DataServiceCollection but I hope you explore this rich collection of classes at some point.
The DataServiceCollection class is the class that will give your project and you as a developer the opportunity to perform a full array of work using the data and metadata of your OData feed. You will have the full capabilities at your disposal based on the rules that were used to develop the OData feed. At best the OData will allow you to perform full CRUD (Create, Read, Update, Delete) on the data. In this article we will only be looking at the Read functionality of the DataServiceCollection to allow you to retrieve and bind data within your application.
Retrieving Data from an OData feed
We will dive right into the work and get started. You will have a MainPage.xaml file in your project that was created as the starting or home page for your application as shown below. I have updated the ApplicationTitle and PageTitle properties in the XAML to have a better application description.
Add the following using statements to your project. This will allow you to use the DataServiceCollection as well as the service reference we set up earlier.
using _31DaysMangoOData.ServiceReference1;
Create the following private variables in the MainPage.xaml.cs code for the MainPage. This will give you a reference to your OData feed via the _context variable, the collection of Major League Baseball teams (_teams) that exists in the OData feed and finally the selected team (_selectedTeam) that will allow the project to show a list of the years the selected team has played.
private BaseballStatsEntities _context;
We will need to get the data loaded from the OData feed and we will next implement the Loaded event for the Main Page as shown in Figure 6.
{
_context = new BaseballStatsEntities(new Uri("http://www.baseball-stats.info/OData/baseballstats.svc/"));
App.ViewModel.Teams.LoadCompleted += Teams_Loaded;
var query = from t in _context.TeamFranchise
orderby
t.franchName
where t.active == "Y"
select t;
App.ViewModel.Teams.LoadAsync(query);
}
I get asked why we need to again give the URI for the OData feed. Did we not just give that when adding the Service Reference? We did but that reference to the URI of the OData feed was just to allow the generation of the proxy classes based on the metadata from the feed. We need to always give the URI to the OData feed service point when creating our data context.
We initialize the _teams private variable as a DataServiceCollection<T> generic of type TeamFranchise which is an entity type that exists in the OData feed. We associate the _teams’ LoadCompleted event handler with a new method we will look at soon names Teams_Loaded and finally we will get our data using LINQ. One of the great reasons to use the DataServiceCollection<T> class is that it allows you to create LINQ statements to query the data as well as bind data directly to our XAML views (we will look at that in the next section).
The final action we must put into place is to get the data set up in the LINQ statement. The DataServiceCollection class will asynchronously retrieve the data for us also. Remember that just like Silverlight, our Windows Phone 7 Mango projects must perform all actions asynchronously.
We need to have a method that will load the data into the DataContext we have created
{
if (e.Error == null)
{
if (App.ViewModel.Teams.Continuation != null)
{
App.ViewModel.Teams.LoadNextPartialSetAsync();
}
else
{
// Set the data context of the list box control to the team data.
this.LayoutRoot.DataContext = App.ViewModel.Teams;
}
}
else
{
MessageBox.Show(string.Format("We have an error: {0}", e.Error.Message));
}
}
We now have our baseball teams loaded in the _teams variable and ready to bind. Before we show how to bind data to the MainPage of the project, I want to look at how to also work with associated entity classes and data based on the _teams variable.
We need to modify the MainViewModel to contain the data from the OData feed that was queried in MainPage.xaml.cs. Here is the code for the MainViewModel.cs.
using System.ComponentModel;
using System.Data.Services.Client;
using _31DaysMangoOData.ServiceReference1;
namespace _31DaysMangoOData
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Teams = new DataServiceCollection<TeamFranchise>();
}
/// <summary>
/// A collection for ItemViewModel objects.
/// </summary>
public DataServiceCollection<TeamFranchise> Teams { get; private set; }
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a few ItemViewModel objects into the Items collection.
/// </summary>
public void LoadData()
{
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Binding Data to your Views from OData
Now we will dive into the XAML side of the project to show how the data from the OData feed that is located in the MainViewModel is bound the ListBox in the MainPage view and also to the DetailsPage view.
The MainPage.xaml has been associated with the MainViewModel located in the MainViewModel.cs file through the DataContext we set up in the MainPage’s constructor.
Not much has to be done to the XAML code that was created when we started the Windows Phone Databound Application project. We removed the following line since we do not have design-time data.
We also need to change the Binding of the MainListBox to just {Binding} since we set the following line of code in the MainPage.xaml.cs Teams_loaded method.
this.LayoutRoot.DataContext = App.ViewModel.Teams;
This will have the DataServiceCollection containing the baseball teams we retrieved from the OData team ready to use and bind in the XAML of the MainPage.
The following is the XAML source for the MainPage.
x:Class="_31DaysMangoOData.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!–Data context is set to sample data above and LayoutRoot contains the root grid where all other page content is placed–>
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!–TitlePanel contains the name of the application and page title–>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="31 Days of Mango" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="baseball teams" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!–ContentPanel contains ListBox and ListBox ItemTemplate. Place additional content here–>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding}" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="78">
<TextBlock Text="{Binding Path=franchName}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
When we run the application in the Windows Phone Emulator you will see that the baseball teams now show in the application.
Getting the selected baseball team data to show after the application navigates to the DetailPage.xaml is very simple with a few lines of code. We want to assign the team that was selected in the MainPage and then associate the DataContext of the DetailsPage to the selected baseball team’s object in the DataServiceCollection located in the MainViewModel. The code for the DetailsPage.xaml.cs is below.
using Microsoft.Phone.Controls;
namespace _31DaysMangoOData
{
public partial class DetailsPage : PhoneApplicationPage
{
// Constructor
public DetailsPage()
{
InitializeComponent();
}
// When page is navigated to set data context to selected item in list
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
DataContext = App.ViewModel.Teams[index];
}
}
}
}
Again the XAML code in DetailsPage.xaml is very simple also with just the ApplicationTitle being changed along with the PageTitle being data binded to the franchName property of the selected Team. I wanted to show the active property also just for additional information.
x:Class="_31DaysMangoOData.DetailsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!–Data context is set to sample data above and first item in sample data collection below and LayoutRoot contains the root grid where all other page content is placed–>
<Grid x:Name="LayoutRoot" Background="Transparent" d:DataContext="{Binding Items[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!–TitlePanel contains the name of the application and page title–>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="31 Days of Mango" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="ListTitle" Text="{Binding franchName}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!–ContentPanel contains details text. Place additional content here–>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock x:Name="ContentText" Text="{Binding active}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Running the application and selecting a baseball team from the main page will then navigate you to the details page where you will see the following (love my Tigers!!).
Summary
To wrap up, using OData to consume data is very simple. There are many more features that the System.Data.Services.Client and specifically the DataServiceCollection can offer you as a developer. I hope you have seen how you can make your Windows Phone Applications data rich with very little effort.
To download this entire working project for Visual Studio 2010, click the Download Code button below.
Tomorrow, guest author Doug Mair will walk you through using the ProgressBar to both entertain and inform your user about a download or long-running process. See you then!
Leave a Reply