Windows Phone 7: Lists, Page Animation and oData

MiniTutorialLogo2

This is the fourth in a fast paced series on programming Windows Phone 7. In this mini-tutorial I will demonstrate how absurdly easy it is to create a master page with a list of data, and a details page to display more information about the selected item, and to animate the transition from one to the other. To make it more interesting, we’ll get the list and the details from a web service, using oData.

The reason all of this is so easy is that Visual Studio 2010 provides a template for this very case.  While that smells a bit of making a demo that fits the template, the truth is that this is an extremely common scenario, and the template is very flexible,.

Begin by creating a new application. Choose Silverlight For Windows Phone under templates, and Windows Phone List Application, as shown below

new winphone app

When you click OK, Visual Studio will create a new application with the styles in place to make it easy to create an application that looks and feels like a Win 7 Phone.  This time, however, because you selected Windows Phone List Application, MainPaage.xaml has already created a list for you, complete with dummy data and complete with placeholders for data binding!

With my installed version, MainPage.xaml is born with 108 lines of Xaml.  Let’s take a close look at some of that file.

Annotating

MainPage.xaml

The first ten lines or so are what we’d expect in a Silverlight application. line 11 stands out, though,

d:DataContext="{d:DesignData
SampleData/MainViewModelSampleData.xaml}"

The d: prefix sets this data context assignment to sample data (stored as a Xaml file in the  SampleData directory) but only during design time. At run time this will have no effect

This line is followed by a few lines that bind shared attributes of MainPage elements to styles created by Visual Studio, and then begin the definition of the storyboards that manage the animation for moving between pages,

FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}">

<!--Page transition animation-->
<phoneNavigation:PhoneApplicationPage.Resources>
    <Storyboard x:Name="ResetPageTransitionList">

There is nothing special about this animation; it is just what you would create in a web application.

On approximately line 69 I find the end of the storyboard and the declaration of the outermost grid (LayoutRoot). Around line 82 are the declarations of the two titles, using the provided styles,

<TextBlock
   Text="Nerd Dinner"
   x:Name="textBlockPageTitle"
   Style="{StaticResource PhoneTextPageTitle1Style}"/>
<TextBlock
   Text="All Dinners"
   x:Name="textBlockListTitle"
   Style="{StaticResource PhoneTextPageTitle2Style}"/>

Notice that all I needed to do was to change the Text properties, the rest I could leave as is.

The ListBox

Inside the Content Grid is the ListBox control. It starts by setting its ItemsSource property to be bound to a public property Items on whatever will be the data context.  In our case, we’re going to pass it a collection as the dataContext, and so we can change this to bind the ItemsSource to the data context object, rather than to one of its properties.

<ListBox
   x:Name="ListBoxOne"
   ItemsSource="{Binding}"
   MouseLeftButtonUp="ListBoxOne_MouseLeftButtonUp"
   Style="{StaticResource PhoneListBox}">

Having fixed the binding, I’ll also take the opportunity to remove the MouseLeftButtonUp event handler. While it is perfectly legitimate where it is, I find it easier to maintain my programs when all the code (including the declaration of the event handlers) is in code files.

The ListBox ItemTemplate

As you may know from Browser based Silverlight, you can fill the ListBox with items and accept the default display, or you can create an ItemTemplate – a bit of Xaml that designates how one item is to be displayed. The ListBox will use that template for every item it finds to display in the ListBox.

In this case, the ItemTemplate consists of two display elements housed within a StackPanel.  The first is a Border, the second a ListViewItem as defined in the clr-namespace:Microsoft.Phone.Controls namespace (the namespace is given the alias mpc at the top of the file, and the requisite assembly is included by Visual Studio automatically).

Note that while the type ListViewItem is declared in the Phone.Controls namespace, the style applied (PhoneListBoxItemLayout) is in your App.xaml file

Each ListViewItem will display whatever is bound to its Text and Details properties.

<mpc:ListViewItem
   Layout="TextAndDetailsWithIcon"
   Text="{Binding LineOne}"
   Details="{Binding LineTwo}"
   Style="{StaticResource PhoneListBoxItemLayout}" />

Let’s Get Some Real Data

Before we go any further, it will be helpful to have “real” data from a proper data source. For this we will use the Nerd Dinner websource, obtaining what we need in the increasingly ubiquitous oData format. (Grammar police alert!)

The very easiest way to do this is to invoke the Datasvcutil program, passing in the URI of an oData service and the name of a file that we’ll then add to this project.

Follow along with me as we learn three new words in Turkish:  Towel… Bath… Border.   “May I see your papers please?”  ‘Excuse me sir, but is this your bar of soap?’ “Uh, yes, I think it is.” ‘So do we.’

Invoking Datasvcutil

  1. Click on Start –> Visual Studio 2010 –> Visual Studio Tools –> Visual Studio Command Prompt (2010)
  2. Change directories to you source code (NerdDinner/NerdDinner)
  3. Invoke the following command (feel free to cut and paste:)
Datasvcutil.exe
   /uri:http://www.nerddinner.com/Services/OData.svc
   /out:NerdDinnerSvc.cs

This will produce a file NerdDinnerSvc.cs that you can then add to your project (Click on the project and choose Add->Existing item).

oDataOnce added, right click on the newly added file and choose View Class Diagram.  Three classes are added, as shown in the (cropped) image to the left.

We’re only displaying the two classes that we’ll be using for Binding: the NerdDinnerEntitis (which serves as the holder of the Dinners collection) and the Dinner class.

EventDate and HostedBy will provide the details when a Dinner is selected.

Fixing Up the DataBinding

Now that we know what we want to bind to, let’s return to MainPage.xaml and find the ListViewItem we considered above. You’ll remember that it was set to bind its Text to LineOne and its Details to LineTwo. We can just swap in the correct names of the properties, EventDate and HostedBy respectively.

<mpc:ListViewItem
   Layout="TextAndDetailsWithIcon"
   Text="{Binding HostedBy}"
   Details="{Binding EventDate}"
   Style="{StaticResource PhoneListBoxItemLayout}" />

Creating the Details Page

The next step is to create the details page to which we’ll transfer control when the user selects an item in the ListBox.

Right click on the project, choose Add –>New Item and select Windows Phone Portrait Page.  Name it Details.xaml and let Visual Studio add it to the project.

You can go ahead and change MY APPLICATION to Nerd Dinner in the ApplicationName TextBlock (line 19) but we want to bind the “ListName” to the Tile of the item in the list clicked on by the user

<TextBlock
   Text="Nerd Dinner"
   x:Name="ApplicationName"
   Style="{StaticResource PhoneTextPageTitle1Style}" />

<TextBlock
   Text="{Binding Title}"
   x:Name="ListName"
   Style="{StaticResource PhoneTextPageTitle2Style}" />

Visual Studio creates a basic content grid for the details page, we’ll add a few rows for the data we’ll be binding to

<Grid.RowDefinitions>
   <RowDefinition
      Height="Auto" />
   <RowDefinition
      Height="Auto" />
   <RowDefinition
      Height="Auto" />
   <RowDefinition
      Height="Auto" />
   <RowDefinition />
</Grid.RowDefinitions>

Remove the TextBlock placed by VisualStudio and replace it with this TextBlock,

<TextBlock
  Grid.Row="0"
  x:Name="EventDateTime"
  FontSize="25.333"
  Text="{Binding EventDate}" />

That is the first property we want to bind to. Next we need a prompt and a value for the HostedBy property:

<StackPanel
   Orientation="Horizontal"
   Grid.Row="2">
   <TextBlock
      Text="Hosted By: "
      Margin="0,0,5,0"
      FontSize="25.333" />
   <TextBlock
      Grid.Row="2"
      x:Name="HostedBy"
      FontSize="25.333"
      Text="{Binding HostedBy}" />
</StackPanel>

The Xaml is complete. Let’s turn to the code.

The Logic in MainPage.xaml.cs

You will remember that the objects that we found in the oData were of type NerdDinnerEntities, Dinner and RSVP and that we marked the Xaml to allow binding to the first two of these. In MainPage.xaml.cs we’ll declare objects of these respective types and an object (literally) to hold the selected dinner,

public partial class MainPage : PhoneApplicationPage
{
    DataServiceCollection<NerdDinnerModel.Dinner> allDinners;
    NerdDinnerModel.NerdDinnerEntities nerddinner;
    object selectedDinner;

The constructor begins with two lines supplied by Visual Studio. Immediately afterwards we instantiate the two members (nerddinner and allDinners) that we just declared.

The former is assigned to a new instance of NerdDinnerEntities whose constructor takes the URI of the OData web service for Nerd Dinner.

The latter holds a new DataServiceCollection of type Dinner objects. A DataServiceCollection is an ObservableCollection from Web Services that supports both dynamic data and data binding.

public MainPage()
{
    InitializeComponent();
    SupportedOrientations = SupportedPageOrientation.Portrait;

    nerddinner = new NerdDinnerModel.NerdDinnerEntities(
       new System.Uri("http://www.nerddinner.com/Services/OData.svc/"));

    allDinners =
       new DataServiceCollection<NerdDinnerModel.Dinner>();
    allDinners.LoadCompleted +=
        new EventHandler<LoadCompletedEventArgs>(DinnersLoaded);

    Loaded += new RoutedEventHandler( MainPage_Loaded );
}

The constructor ends by creating two event handlers, one to handle the DinnersLoaded event (discussed shortly) and the second for when the page is loaded:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    AllDinnerList.DataContext = allDinners;
    PageTransitionList.Completed +=
         new EventHandler(PageTransitionList_Completed);
    LoadAllDinners();
    ResetPageTransitionList.Begin();
}

The first line in this event handler sets the DataContext for the list box to the DataServiceCollection created above. It still has no members, however.

The second line sets up an event handler for when the PageTransitionList animation completes. It has not yet been started, and won’t until all the data is loaded.

The third line calls LoadAllDinners() which runs synchronously and thus blocks until its work is done. Interestingly, its work is to kick off an asynchronous query, so the wait won’t be long.

ResetPageTransitionList.Begin() is called to run a short storyboard that resets everything we need for a nice page transition once the Dinners are loaded and the user picks a dinner.

As noted, the LoadAllDinners method sets off an asynchronous query; specifically a Linq query to obtain the dinners in date order.

private void LoadAllDinners()
{
    var query = from d in nerddinner.Dinners
                orderby d.EventDate descending
                select d;

    allDinners.LoadAsync(query);
}

When the asynchronous call completes we hide the “Loading…” text and display the list of dinners.

void DinnersLoaded(object sender, LoadCompletedEventArgs e)
{
    LoadingText.Visibility = System.Windows.Visibility.Collapsed;
    AllDinnerList.Visibility = System.Windows.Visibility.Visible;
}

When the user clicks on a dinner, we save the selected dinner in the member variable and kick off the story board that will transition to the new page,

private void DinnerSelected(object sender, MouseButtonEventArgs e)
{
    selectedDinner = (sender as ListBox).SelectedItem;
    PageTransitionList.Begin();
}

Finally, when the transition completes we call the static Navigate method passing in the URI of the page we want to transition to. We then get the new RootVisual from the application and set its DataContext to the selected Dinner (so that databinding will work on the details page).

void PageTransitionList_Completed(object sender, EventArgs e)
{
    NavigationService.Navigate(
        new Uri("/DinnerDetails.xaml", UriKind.Relative));
    FrameworkElement root =
        Application.Current.RootVisual as FrameworkElement;
    root.DataContext = selectedDinner;
}

Plus ça change, plus c’est la même chose

While the logic above can be daunting when you are reading through, there really is nothing very new here. We’re just obtaining data asynchronously from a web service, and once obtained, changing pages.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Mini-Tutorial, WindowsPhone and tagged , . Bookmark the permalink.

11 Responses to Windows Phone 7: Lists, Page Animation and oData

Comments are closed.