31 Days of Mango | Day #22: App Connect

Day22-AppConnectDay21-SocketsDay20-RingtonesDay19-AddingTiltEffectsDay18-ExpressionBlendSampleData

This article is Day #22 in a series called 31 Days of Mango, and is written by guest author Matt Eland.  Matt can be reached on Twitter at @integerman.

What is App Connect?

Windows Phone Mango introduces a multitude of fantastic improvements to the already great Windows Phone operating system. Among these enhancements are number of improvements to the Bing search engine on the phone. While many of these features such as visual search and turn by turn directions are intended for customers, Microsoft also introduced a few features aimed at allowing developers to integrate apps into the search results either via Instant Answers, a way of highlighting apps relevant to specific searches, or via a new feature in Mango called Quick Cards.

Quick Cards are items that appear in certain searches that allow users to find out more about certain products, locations, or movies within Bing. Pictured below is an example of search results that include Quick Cards for several books matching the user’s search:

clip_image002

Clicking on one of these items navigates you to that product’s Quick Card which allows you to find more information on a product in a centralized location as pictured below:

clip_image004

App Connect is another new feature in Windows Phone Mango that allows apps to be listed on the apps pivot item on Quick Cards that are relevant to an app’s subject matter. From there, the user can click and launch the app and be met with an app launch experience that can be custom tailored to the Quick Card the user just came from.

It’s important to note that presently only the United States supports product and movie Quick Cards though place Quick Cards are more widely available. Despite this present limitation we will also touch on localizing search result text.

See http://msdn.microsoft.com/en-us/library/hh202957(v=VS.92).aspx#BKMK_AppConnect for a more detailed and up-to-date list on the types of Quick Cards that are supported.

Similar to Quick Cards, developers can also have their apps be aware of scenarios where they are launched as by Bing Instant Answers, another Mango improvement to Bing search. Bing Instant Answers allows Bing to feature certain apps that it thinks are highly relevant to a specific search.

For this article we will be creating an app that allows you to quickly search for a book on an online retailer’s site by taking advantage of search extensibility integration for Quick Cards for books and magazines and providing a customized experience if it was launched from Bing Instant Answers.

Getting started with search extensibility

Setting up your app to take advantage of App Connect is relatively easy though there are a number of steps you’ll need to follow in order to get things working.

We’ll start by creating a new Visual Studio Windows Phone Application project targeting Windows Phone 7.1.

Registering search extensions

In order for Windows Phone to know that your app supports App Connect, you need to add an entry to the manifest file telling the phone what search extensions the app supports. This is important and should only include product types that are relevant to your application.

Expand the properties node of the project file and go into WMAppManifest.xml. This is the phone’s application manifest for the marketplace and defines all relevant information for your application. You’ll need to add a new node inside of the App element as illustrated in the following file:

<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.1">
    <App ="" >
        <IconPath IsRelative="true" IsResource="false">ApplicationIcon.jpg</IconPath>
        <Capabilities></Capabilities>
        <Tasks>
            <DefaultTask Name ="_default" NavigationPage="MainPage.xaml"/>
        </Tasks>
        <Tokens></Tokens>
        <Extensions>
            <!– Beta extension, for development and testing only –>
            <Extension ExtensionName="Products"
            ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}"
            TaskID="_default"
            ExtraFile="Extensions/Extras.xml" />
            <!– Production extensions, for submission to Marketplace –>
            <Extension ExtensionName="Bing_Products_Books_and_Magazines"
            ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}"
            TaskID="_default"
            ExtraFile="Extensions/Extras.xml" />
        </Extensions>
    </App>
</Deployment>
 

The important aspect of this is the two Extension elements. ExtensionName identifies which Quick Card the extension supports integration with. See http://msdn.microsoft.com/en-us/library/hh202958(v=VS.92) for a complete list of available Quick Cards.

The emulator handles App Connect differently than the final version Mango that runs on phone. In the emulator all product Quick Cards have a category of “Products” instead of the more specific individual ExtensionNames. That is to say that if we registered our app to support books and magazines by using the “Bing_Products_Books_and_Magazines” ExtensionName, our app would show up as supported for a book Quick Card on the device but not on the emulator and if we registered as only supporting the “Products” category, the app would appear to work fine in the emulator but would not work properly on actual devices. Because of this you’ll want to support both the broad “Products” category as well as any specific categories you may want to handle by defining multiple Extension elements.

The other fields in the Extension element are relatively simple to deal with: ConsumerID identifies search extensibility as the feature the extension maps to and should not be changed. TaskID specifies the task that should be launched when the extension is triggered. ExtraFile tells the app where to look for the extra data needed to support search integration and must point to Extensions/Extras.xml or App Connect will not work.

Configuring extra data needed by App Connect

Since our project doesn’t contain the Extensions/Extras.xml we referenced in the two extensions we registered we’ll have to create it. Right click on the project in the solution explorer and click “Add -> New Folder”, rename the folder to “Extensions”, right click that folder and select “Add -> New Item”. Select XML File from the list and name it Extras.xml. The name and path of this file must be exact or search extensions will not function properly.

clip_image006

You should now have an Extras.xml file in the Extensions folder. From here we’ll need to add an ExtrasInfo node to the xml file that defines how the app behaves with the extensions we registered.

<ExtrasInfo>
    <AppTitle>
        <!– Neutral language application title. –>
        <default>Book Helper</default>
        <!– Localized titles –>
        <es-ES>Libro Helper</es-ES>
        <fr-FR>Livre Helper</fr-FR>
        <zh-CN></zh-CN>
    </AppTitle>
    <Consumer ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}">
        <ExtensionInfo>
            <Extensions>
                <!– Corresponding product extension for books –>
                <ExtensionName>Bing_Products_Books_and_Magazines</ExtensionName>
                <!– Beta extension, for all products. –>
                <ExtensionName>Products</ExtensionName>
            </Extensions>
            <CaptionString>
                <!– Neutral language caption –>
                <default>Search vendors for books and eBooks</default>
                <!– Localized captions –>
                <es-ES>Bsqueda de libros y libros electrnicos de varios proveedores</es-ES>
                <fr-FR>Recherche de livres et eBooks provenant de divers fournisseurs</fr-FR>
                <zh-CN></zh-CN>
            </CaptionString>
        </ExtensionInfo>
    </Consumer>
</ExtrasInfo>
 

The AppTitle element and its children tell Windows Phone what to call our application (and provide some localized strings for different locales). The default node inside of this is used if a locale-specific string cannot be found and should represent the app’s title in the assembly’s neutral language. Additional nodes can be added with names matching the culture code that the localized string is for. For example, es-ES in this example indicates the string used in Spanish language settings.

The Consumer node has a ConsumerID that matches the ConsumerIDs we registered in the WMAppManifest.xml file earlier. Inside of this node is an ExtensionInfo node with an Extensions node that contains ExtensionName elements that define the extensions we said we supported.

The CaptionString element defines the message used next to the search extension’s tile and is localized the same way the AppTitle element is. An example of this app showing up as a search extension is pictured below:

clip_image008

Note that the application’s icon is used for this image and the background could be either light or dark depending on the theme. Consequently, transparency on app icons should not be used when App Connect integration is present. Additionally, the pivot item for App Connect on Quick Cards in the emulator says “extras” while in actual Mango devices this pivot item is labeled “apps”.

If you need to specify different captions for different types of products, you can do so by declaring multiple ExtensionInfo elements and using some ExtensionName elements in one ExtensionInfo element and others in another as long as all ExtensionNames you say your app supports are represented in an ExtensionInfo.

Setting up URI mapping

When a user enters the app using App Connect the runtime will attempt to navigate to the /SearchExtras URI. In order to support this entry point we’ll need to set up some URI mappings to redirect traffic to the appropriate page. To do this, we’ll define a UriMapper resource in the Application’s resources collection by going into App.xaml and adding the following resource to Application.Resources:

<Application.Resources>
    <nav:UriMapper x:Key="UriMapper" xmlns:nav="clr-namespace:System.Windows.Navigation;assembly=Microsoft.Phone">
        <nav:UriMapper.UriMappings>
            <nav:UriMapping Uri="/SearchExtras" MappedUri="/MainPage.xaml" />
        </nav:UriMapper.UriMappings>
    </nav:UriMapper>
</Application.Resources>
 

Defining this resource isn’t enough. We need to tell the app to find and use this UriMapper for resolving requests. We do this by adding the following code to the constructor in App.xaml.cs:

// Enable URI mapping for Search Extras deep link URIs

RootFrame.UriMapper = Resources["UriMapper"] as UriMapper;
 

This will correctly map all incoming App Connect traffic to navigate to the MainPage once our application uses. If we wanted to we could specify a specific item details page in the MappedUri property that is specific to App Connect but for the purposes of this example, this will suffice.

Taking advantage of App Connect integration

At this point if you run your application in the emulator and then go to Bing search on the device using the search key, you should see your application listed on the extras pivot for a product’s Quick Card – provided you registered your app as supporting Products – individual product type mappings do not work for the emulator but will work on your actual device (and vice versa – registering as supporting the broad category of products does not work on actual devices).

Unfortunately, our app doesn’t actually do anything with the data provided by App Connect yet. To fix this, let’s start by defining our user interface. Go into MainPage.xaml and replace the Grid named LayoutRoot and all of its children with the following XAML:

<Grid Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <StackPanel Margin="12,17,0,28">
        <TextBlock Text="BOOK HELPER" Style="{StaticResource PhoneTextNormalStyle}" />
        <TextBlock Text="Welcome" x:Name="lblPageTitle" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" />
    </StackPanel>
    <ScrollViewer Margin="12,0,12,0" Grid.Row="1">
        <StackPanel>
            <TextBlock TextWrapping="Wrap" x:Name="lblBookTitle" Visibility="Collapsed" Style="{StaticResource PhoneTextLargeStyle}" Text="Book title goes here" />
            <TextBlock TextWrapping="Wrap" x:Name="lblNoSearch" Text="To use this product, use your device’s search functions to look for a book and then select this app from the apps section." Style="{StaticResource PhoneTextNormalStyle}" />
            <HyperlinkButton HorizontalContentAlignment="Left" Foreground="{StaticResource PhoneAccentBrush}" Margin="{StaticResource PhoneVerticalMargin}" Content="Search Amazon" Click="HandleSearchAmazonBooks" />
            <HyperlinkButton HorizontalContentAlignment="Left" Foreground="{StaticResource PhoneAccentBrush}" Margin="{StaticResource PhoneVerticalMargin}" Content="Search Barnes and Noble" Click="HandleSearchBarnesAndNoble" />
        </StackPanel>
    </ScrollViewer>
</Grid>
 

This defines a user interface that will be able to function either as a stand-alone app (in case of a normal app launch) or serve as a details page if the application is launched using App Connect. By default, the application will look like the following image:

clip_image010

Handling Query Parameters

Obviously, we’d like to be able to do something special if we launched the app from App Connect on a Quick Card. Thankfully, when entering a page via App Connect, the navigation context of the page contains two query string parameters that can help us identify basic information on the Quick Card that launched the application. The ProductName query string argument will contain the name of the product that is associated with the Quick Card the user just viewed and the Category argument will be the ExtensionName that is associated with that Quick Card.

It is important to wait for the page to load before checking for query string parameters since the NavigationContext will not be available until loading is complete.

While this is fairly basic information, it is still useful for applications that need to pre-populate fields or controls with product names or for pages that need to be flexible enough to handle multiple types of products. Bear in mind that for debugging purposes Category will always be “Products” in the emulator but will have the correct values when run on an actual device.

For our sample app, we’ll have the page display the product name in the page if we came in to the app through App Connect, but leave the existing user interface if this is handling a standard launch. Because the app launch entry point and the search extras entry point use the same page for our demo it is important to check for the existence of the query string arguments before trying to use them.

Here is the modified MainPage.xaml.cs that includes logic for parsing the query string parameters and handling hyperlink clicks:

public partial class MainPage : PhoneApplicationPage
{
    private string _book;
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        // We need to wait until loaded before checking NavigationContext
        Loaded += HandleLoaded;
    }

    private void HandleLoaded(object sender, RoutedEventArgs e)
    {
        // Determine what the context of the app launch is
        if (NavigationContext.QueryString.ContainsKey("ProductName"))
        {
            _book = NavigationContext.QueryString["ProductName"];
            lblPageTitle.Text = "Book Details";
        }
        else
        {
            _book = null;
        }

        // In a "real" application we’d want to use binding and converters
        if (!string.IsNullOrWhiteSpace(_book))
        {
            lblBookTitle.Text = _book;
            lblNoSearch.Visibility = Visibility.Collapsed;
            lblBookTitle.Visibility = Visibility.Visible;
        }
        else
        {
            lblNoSearch.Visibility = Visibility.Visible;
            lblBookTitle.Visibility = Visibility.Collapsed;
        }
    }

    private void HandleSearchBarnesAndNoble(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(_book))
        {
            BrowseTo("http://www.barnesandnoble.com/ebooks/index.asp&quot;);
        }
        else
        {
            BrowseTo("http://productsearch.barnesandnoble.com/search/results.aspx?store=BOOK&keyword="&quot; + _book.Replace(" ", "+") + """);
        }
    }

    private void HandleSearchAmazonBooks(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(_book))
        {
            BrowseTo("http://www.amazon.com/gp/aw/sb.html/sn=Books&quot;);
        }
        else
        {
            BrowseTo("http://www.amazon.com/gp/aw/s/?k=&quot; + _book.Replace(" ", "+"));
        }
    }

    private static void BrowseTo(string address)
    {
        var webTask = new Microsoft.Phone.Tasks.WebBrowserTask { Uri = new Uri(address) };
        webTask.Show();
    }
}
 

Now when we launch our app through App Connect we see the following:

clip_image012

Supporting App Instant Answers

Another related feature introduced in Windows Phone Mango is App Instant Answers. This is an automatic feature of Bing search where apps Bing decides are relevant to a specific search are featured at the top of the search results. You don’t have to do anything in order to support launching from Instant Answers but you can take advantage of information passed in through this functionality if you choose. This information is exposed through a query string parameter called “bing_query” and is accessed similarly to the ProductName and Category query string parameters.

Modifying our sample app to take advantage of Instant Answers is extremely easy as illustrated by the modified top half of the HandleLoaded method:

// Determine what the context of the app launch is
if (NavigationContext.QueryString.ContainsKey("ProductName"))
{
    _book = NavigationContext.QueryString["ProductName"];
    lblPageTitle.Text = "Book Details";
}
else if (NavigationContext.QueryString.ContainsKey("bing_query"))
{
    _book = NavigationContext.QueryString["bing_query"];
    lblPageTitle.Text = "Related Search";
}
 

This modification allows the application to grab either the book title out of the Quick Card’s product name if one is present or from the Bing query that generated the Instant Answers result. The image below illustrates entering the app through an Instant Answers result for the search string “Sphere by Michael Crichton”

clip_image014

Testing App Connect

You might be wondering how to test Instant Answers or attach with a debugger when working with Search Extensions. This is a valid point since there isn’t currently a good way of influencing Bing search results using the emulator and when working with search extensions the Category attribute does not register the same as it would on the device. It also can become time consuming to navigate to Bing, execute a specific search and then launch the app using App Connect. Thankfully, there’s an easy way to simulate launching your app either through search extensions or through instant answers.

Since we can predict the query string parameters that Quick Cards will generate when entering our app, it is possible to hard code these values into the application’s entry point for testing purposes. Windows Phone apps store the default page’s address inside of the WMAppManifest.xml file in the properties node of the project. Open up this file and find the DefaultTask element inside the Tasks collection. It should look something like this:

<DefaultTask Name ="_default" NavigationPage="MainPage.xaml"/>

This element tells the application where to navigate when the application starts and can be modified to point to whatever file you desire. It can also be modified to simulate entering the app via search extensions or instant answers by specifying query string parameters. For example, the following entry was used to test Instant Answers in the section above:

<DefaultTask Name ="_default" NavigationPage="MainPage.xaml?bing_query=Sphere by Michael Crichton"/>

The modifications for simulating a search extension entry point for a similar product are very similar. Note that since we’re specifying multiple query string parameters that using &amp; to escape the ampersand is important as seen in the following snippet:

<DefaultTask Name ="_default" NavigationPage="MainPage.xaml?Category=Bing_Products_Books_and_Magazines&amp;ProductName=Sphere"/>

Of course, once you are done testing and ready to deploy the application, make sure you remove any hardcoded query string parameters from the default task so the app will function as intended.

You may occasionally run into deployment errors when making modifications to the WMAppManifest.xml file and attempting to launch the app in the emulator. If the deployment fails with the message: “Installation of the application failed. Run time error has occurred. Fix the Capabilities in WMAppManifest.xml file.” and your capabilities section looks fine, just rebuild the app and try again.

Summary

This wraps up coverage of App Connect and Instant Answers, two simple, flexible and easy to use ways to allow your app to integrate with extensibility points in the improved Bing search engine for Windows Phone Mango.

To download a complete working Windows Phone application that uses the code that was demonstrated in this article, click the Download Code button below:

download

Tomorrow, Samidip Basu is back to discuss the Execution Model of Windows Phone, including Fast App Switching, and how Tombstoning (which was covered in Day #14 of the 31 Days of Windows Phone) has changed slightly to accommodate the new 7.5 operating system.  See you then!

toolsbutton

31 Days of Mango | Day #21: Sockets

Day21-SocketsDay20-RingtonesDay19-AddingTiltEffectsDay18-ExpressionBlendSampleDataDay17-WindowsAzure

This article is Day #21 in a series called 31 Days of Mango, and was written by guest author Parag Joshi.  Parag can be reached on Twitter at @ilovethexbox.

Today we will take a peek at the new support for sockets announced in Windows Phone 7.1. So, what are sockets? Simply put it is the communication mechanism based on TCP/IP that is used for remote applications like printing, video conferencing and many more.

Before we delve into how to write a socket based windows phone application let us take time to understand what sockets is all about.

Sockets – What is involved?

Applications communicate via sockets based on a socket address. A socket address is a combination of a TCP/IP address and a port. Using this information, applications can communicate with each other over a local network or via the internet.

The article mentioned below gives a very good explanation of the terms and technology behind sockets. As a programmer, all you need to know is how to connect (if using TCP/IP) and how to send / receive data.

Sockets is a low level technology. This means that you need to determine the data that is sent and how it is read. While this can be prone to parsing errors, it gives you tremendous benefit in terms of data transfer speed over web services.

Here is a link to a detailed article on sockets and the support in Windows Phone 7.1: http://msdn.microsoft.com/en-us/library/hh202874(v=vs.92).aspx.

What are we going to do?

We are going to build a simple application for taking orders in a restaurant. There will be a windows phone 7 application for taking orders and there will be a Silverlight out of browser application for receiving/processing the orders that will run on your host desktop or laptop. The concept is that servers will take orders on the Windows Phone 7 devices and the host will receive the orders and send the status of the orders as they are done.

The servers will be notified on each order as it completes.

This application is based on the "Rock, Paper, Scissors" multicast application on msdn (http://msdn.microsoft.com/en-us/library/ff431744(v=vs.92).aspx).

So let’s get started!

Quick overview

At the very minimum, we need to build a server application that receives the data sent by the client. For the demo we chose a Silverlight (Out of Browser) application.

Note: We have to run the Silverlight application in "Out of Browser" mode with elevated permissions so that it can access the socket data received.

We need to agree on what data is sent to the host -> a table number, order information and spice level.

/// <summary>
/// When we receive the Order, we pass on the table number and other parameters
/// </summary>

public class OrderReceivedEventArgs : EventArgs
{
    public int tableNumber { get; set; }
    public int spiceLevel { get; set; }
    public string order { get; set; }
}

We will also display the Windows Phone 7 devices connected to the host. This helps the person managing the host to know how many servers are working at a time. This information comes from the "DeviceInfo" object. Here is a snippet of the declaration:

/// <summary>
/// The information about each device. This class must implement INotifyPropertyChanged as it is
/// bound to a datagrid using an observable collection.
/// </summary>
public class DeviceInfo : INotifyPropertyChanged
{
    private IPEndPoint _DeviceEndPoint;
    private string _DeviceOrServerName;
    public DeviceInfo(string deviceOrServerName, IPEndPoint endPoint)
    {
        _DeviceEndPoint = endPoint;
        _DeviceOrServerName = deviceOrServerName;
    }
}

Here is a screenshot of the host:

clip_image002

We also need the windows phone 7 clients to take the order, a simple application which takes in the above data and submits it.

Here is a screenshot of the windows phone 7 application:

clip_image004

As you can see, we have the name of the server at the top. This is currently application generated but as an extension we can provide the user the ability to sign in and use the sign in data to show the name of the server logged in.

We have two sliders:

1. The first slider is to choose the table number. It ranges from 1 to 10. This is a very simplistic approach for table selection and will work easily for even 20-30 tables as the slider is very precise and allows for easy touch selection.

2. The second slider is to choose the spice level. It comes primarily from my taste of spicy food but is also something widely specified while ordering food.

The range is set from 1 to 10.

The third element is a textbox to put order details. If you wish to extend this demo, you can add a scrolling selection of menu items and a datagrid to add each selected menu item. For this sample we have kept it simple to a text input.

And finally we need a button to send the order. That’s it!

The following key points should be noted:

1. RestaurantCommands class: This class contains the commands allowed in our socket application and must be common to the host and the recipient to understand the message sent and received.

2. UdpAnySourceMulticastChannel.cs and UdpPacketReceivedEventArgs.cs: These are two files we have taken from the sdk multicast application that contain the UDP multicast sockets code for joining the group, sending and receiving data.

3. Order class: This class handles all application communication. Communication for the application is made up of a number of commands that we have defined in the RestaurantCommand.cs class. These commands are the grammar, or set of actions, that we can transmit and receive and interpret.

Understanding the sample

Prerequisites: Install the Mango tools from http://create.msdn.com. This should give you Visual Studio Express 2010 and the Windows Phone SDK that you need to develop applications for Windows Phone. We are also running a Silverlight application on the host. Download Visual Studio c# express edition to get the required templates.

  • Launch Visual Studio and browse to the solution file and open it.
  • Note: The solution file has two startup projects. The "TakeMyOrder" windows phone 7 project and the "PointOfSaleApp" Silverlight application.
  • This is a multicast socket sample. To run this you need to deploy the windows phone 7 sample to the phone and run the Silverlight application on host.
  • Alternatively, if you do not have a phone, deploy the Silverlight application to another machine (say a Virtual Machine) and run the Windows Phone 7 app in an emulator on the host.
  • You cannot run the Silverlight application and the windows phone 7 application on the emulator on the same machine.
  • If you have multiple machines, phones, you can run the windows phone 7 application simultaneously on all of them. This will give you a feel for multiple servers taking orders and submitting them at the same time.
  • Since we are using UDP protocol, there is no guarantee that the messages sent are received. This is due to the fact that there is no peer to peer connection between two devices. Instead devices join a multicast group identified by an ip address and a port. However as you will see when you run the application, the communication is fairly reliable.

How it works:

  • Launch the Silverlight out of browser application.
  • Launch the windows phone 7 application on the phone or on an emulator running on another machine.
  • The host will show the name of the server and the end point address.
  • At this point you are ready to place an order.
  • On the windows phone 7 application, Use the table slider and choose a table from 1 to 10.
  • Enter an order. E.g. Fried rice with chicken and shrimp. Coke no ice etc.
  • Use the spice slider to set a spice level desired. 10 would be an excellent choice!
  • Click Order. At this point the windows phone 7 application sends the "SendOrder" command to all the devices on the group. Alternatively we could have filtered this to just send the order to the host.
  • On the host, the order is received and added to the "Incoming Orders" grid. This shows up with a status of "In Process" and a unique order id (based on number of orders received) is assigned.
  • Let’s say that we have a very fast kitchen and the order is ready as soon as we receive it. Check the checkbox next to the "In Process" order and click "Order Ready" button. The host sends the order status to all the devices on the network and updates the status in the grid. See below regarding INotifyPropertyChanged.
  • The Windows Phone 7 devices receive the order status on all devices. Alternatively we could have filtered it to just send to the device submitting the order.

Here is a screenshot after an order is complete:

clip_image006

Understanding the code

Here are the key pieces to understand about the code.

Joining the group: The devices join the group using the following key components:

/// <summary>
/// All communication takes place using a UdpAnySourceMulticastChannel.
/// A UdpAnySourceMulticastChannel is a wrapper we create around the UdpAnySourceMulticastClient.
/// </summary>
/// <value>The channel.</value>
private UdpAnySourceMulticastChannel Channel { get; set; }

/// <summary>
/// The IP address of the multicast group.
/// </summary>
/// <remarks>
/// A multicast group is defined by a multicast group address, which is an IP address
/// that must be in the range from 224.0.0.0 to 239.255.255.255. Multicast addresses in
/// the range from 224.0.0.0 to 224.0.0.255 inclusive are well-known reserved multicast
/// addresses. For example, 224.0.0.0 is the Base address, 224.0.0.1 is the multicast group
/// address that represents all systems on the same physical network, and 224.0.0.2 represents
/// all routers on the same physical network.The Internet Assigned Numbers Authority (IANA) is
/// responsible for this list of reserved addresses. For more information on the reserved
/// address assignments, please see the IANA website.
/// http://go.microsoft.com/fwlink/?LinkId=221630
/// </remarks>
private const string GROUP_ADDRESS = "224.0.1.11";

/// <summary>
/// This defines the port number through which all communication with the multicast group will take place.
/// </summary>
/// <remarks>
/// The value in this example is arbitrary and you are free to choose your own.
/// </remarks>
private const int GROUP_PORT = 54329;

The following command is issued to join the group:

//Send command to join the multi cast group
App.Order.Join(serverName);

And finally the join method opens the channel for communication.

/// <summary>
/// Join the multicast group.
/// </summary>
/// <param name="serverName">The server name I want to join as.</param>
/// <remarks>The server name is not needed for multicast communication. it is
/// used in this example to identify each member of the multicast group with
/// a friendly name. </remarks>
public void Join(string serverName)
{
    if (IsJoined)
       {
           return;
    }

       // Store my player name
       _serverName = serverName;

       //Open the connection
       this.Channel.Open();
}

We subscribe to the following events for processing:

/// <summary>
/// Register for events on the multicast channel.
/// </summary>
private void RegisterEvents()
{
    // Register for events from the multicast channel
       this.Channel.Joined += new EventHandler(Channel_Joined);
       this.Channel.BeforeClose += new EventHandler(Channel_BeforeClose);
       this.Channel.PacketReceived += new EventHandler<UdpPacketReceivedEventArgs>(Channel_PacketReceived);
}

Of particular note is the “PacketReceived” event. We process all commands received (Device and Host) in this event handler. Simply put, we parse the incoming message and identify the command being sent. Based on the command and the number of arguments we further identify the action to take.

Sending the order: The following command is issued when the user clicks the Order button:

App.Order.SendOrder(txtOrder.Text, TableSlider.Value.ToString(), spiceSlider.Value.ToString());

/// <summary>
/// Send the order
/// </summary>
/// <param name="Order"></param>
/// <param name="tableNumber"></param>
/// <param name="spiceLevel"></param>
public void SendOrder(string Order,string tableNumber, string spiceLevel)
{
    if (this.Channel != null)
       {
           //Send order to all devices. Only the server will process the send order command. Others will simply ignore it.
                this.Channel.Send(RestaurantCommands.SendOrderFormat, _serverName, Order, tableNumber, spiceLevel);
    }
}

Our “SendOrderFormat” looks like this:

public const string SendOrder = "SO";
public const string SendOrderFormat = SendOrder + CommandDelimeter + "{0}" + CommandDelimeter + "{1}" + CommandDelimeter + "{2}" + CommandDelimeter + "{3}";

Here is the code for the Send method:

/// <summary>
/// Sends the specified format. This is a multicast message that is sent to all members of the multicast group.
/// </summary>
/// <param name="format">The format.</param>
/// <param name="args">The args.</param>
public void Send(string format, params object[] args)
{
    try
    {
        if (this.IsJoined)
        {
            byte[] data = Encoding.UTF8.GetBytes(string.Format(format, args));
            this.Client.BeginSendToGroup(data, 0, data.Length, new AsyncCallback(SendToGroupCallback), null);
        }
    }
    catch (SocketException socketEx)
    {

        // See if we can do something when a SocketException occurs.
        HandleSocketException(socketEx);
    }
    catch (InvalidOperationException)
    {
        Debug.WriteLine("BeginSendToGroup IOE");
    }
}

So, as we can see, based on the format the message is formatted and transmitted.

Receiving the order:

On the host the message is received by the handler defined for the “PacketReceived” event: void Channel_PacketReceived(object sender, UdpPacketReceivedEventArgs e)

We parse the message and identify the command.

string message = e.Message.Trim('\0');
string[] messageParts = message.Split(RestaurantCommands.CommandDelimeter.ToCharArray());

else if (messageParts.Length == 5 && messageParts[0]== RestaurantCommands.SendOrder)
{
    //Status of order received
       OrderReceivedEventArgs args = new OrderReceivedEventArgs();
       args.tableNumber = Convert.ToInt32(messageParts[3]);
       args.spiceLevel = Convert.ToInt32( messageParts[4]);
       args.order = messageParts[2];
       if (DataReceivedFromDevice != null)
       {
           DataReceivedFromDevice(this, args);
    }
}

It’s a very simple communication mechanism. Once we have identified, parsed and created our arguments for the data received method, we raise it.

In our host application we handle the event and add the incoming order to the orders collection:

/// <summary>
/// Handles incoming orders
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Order_DataReceivedFromDevice(object sender, OrderReceivedEventArgs e)
{
    int OrderID = App.OrdersCollection.Count +1;
       Orders objOrders = new Orders(OrderID, e.tableNumber, e.spiceLevel, e.order);
       App.OrdersCollection.Add(objOrders);
}

Known Issues

· Once in a while you will receive an error when you stop debugging:

· File or assembly name ‘System.Net.debug.resources, Version=2.0.5.0, Culture=en-US, PublicKeyToken=7cec85d7bea7798e’, or one of its dependencies, was not found.

· This is a known issue as indicated here http://forums.create.msdn.com/forums/p/89666/537141.aspx . Sometimes after this error is raised, you have to close visual studio and re-launch it.

INotifyPropertyChanged

This interface has to be implemented on the objects we use to bind to the datagrids. Even if the object collections are declared like shown below, the user interface does not update unless the properties in the object itself raise the property changed event.

public static ObservableCollection<DeviceInfo> Devices = new ObservableCollection<DeviceInfo>();

Sending Order Status: Similar to the above, a similar mechanism is implemented to send the order status:

/// <summary>
/// Sends the order status
/// </summary>
/// <param name="OrderID"></param>
/// <param name="status"></param>
public void SendOrderStatus(int OrderID, string status)
{
    if (this.Channel != null)
{
           //Send order to all devices. Only the server will process the send order command. Others will simply ignore it.
this.Channel.Send(RestaurantCommands.ReceiveOrderFormat, _serverName, OrderID, status);
}
}

public const string ReceiveOrder = "RO";
        
public const string ReceiveOrderFormat = ReceiveOrder + CommandDelimeter + "{0}" + CommandDelimeter + "{1}" + CommandDelimeter + "{2}";

else if (messageParts.Length == 4 && messageParts[0] == RestaurantCommands.ReceiveOrder)
{
    //Status of order received
       OrderStatusReceivedEventArgs args = new OrderStatusReceivedEventArgs();
       args.orderId = Convert.ToInt32(messageParts[2]);
       args.orderStatus = messageParts[3];
       if (DataReceivedFromDevice != null)
       {
        DataReceivedFromDevice(this, args);
    }
}

/// <summary>
/// Shows a message box with the order status
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Order_DataReceivedFromDevice(object sender, OrderStatusReceivedEventArgs e)
{
    DiagnosticsHelper.SafeShow("Order Status of '" +e.orderStatus + "' Received For Order:" + e.orderId.ToString());
}

Summary

So, using the helpers provided it is fairly simple to implement an application using sockets. This application can be turned into a comprehensive restaurant application by simply extending some features as noted and many others:

  • Add the ability to select from a menu
  • Add the ability to see the pending orders on the devices
  • Add the ability to sign in

The basic steps to building a socket application are:

  • Decide on the type of socket application: Multicast (UDP) or UniCast (TCP).
  • Decide on the commands.
  • Communicate.
  • Process.

This should give you a good overview of sockets support in windows phone 7. Check out the code examples provided at http://msdn.microsoft.com/en-us/library/hh202874(v=vs.92).aspx

To download this entire sample Windows Phone application using sockets, click the Download Code button below:

download

Tomorrow, Matt Eland is going to walk you through using App Connect (also known as search extensibility) to get your application in front of users, even if they haven’t installed it on their device.  See you then!

toolsbutton

31 Days of Mango | Day #20: Creating Ringtones

Day20-RingtonesDay19-AddingTiltEffectsDay18-ExpressionBlendSampleDataDay17-WindowsAzureDay16-IsolatedStorageExplorer

This article is Day #20 in a series called 31 Days of Mango, and was written by guest author Jerrel Blankenship.  (Yes, I realize the remarkable similarities between our names.)  Jerrel can be reached on Twitter at @thejerrel, and you can find his book, Pro Agile .NET Development with Scrum on Amazon.

Today, we are going to talk about ringtones. With the Mango update of Windows Phone, you as a developer have the ability to write applications that can save a user customized ringtone to their device, for later use. This ability is another way to give the user a chance to have a more customized experience on their phone.

Criteria for audio file

Before you can save a ringtone to the phone, there are some stipulations on the audio file that you need to know.

The audio file must meet the following criteria:

  • It can only be in the WMA or MP3 format
  • It can be no larger than 1MB
  • It be unlocked (DRM free)
  • It can be no longer than approximately 40 seconds

SaveRingtoneTask Chooser method

To save a ringtone from your application to the system, you need to use the Chooser API. The Chooser is an API that allows the caller the ability to launch system applications from their own application. Examples of this include the ability to launch the Contacts app to get information about a person, the Bing Maps application to find directions to a location, and launch the Ringtones application. The Ringtones application is an application that allows you to save a ringtone audio file to the ringtones list in the system. The Chooser task you need to call to launch the Ringtones application is called SaveRingtoneTask.

Creating a custom ringtone application

To better understand this and how to use this task, lets build our own ringtone application that takes an audio file from the app and saves it to the system ringtone list.

image_thumb1

To call the SaveRingtoneTask Chooser, you simply need to instantiate a new instance of this chooser and define a delegate that will be ran once the user is complete with the Ringtones application. The sample below displays the application pictured above. To reference this Chooser task, you need to make sure you included the Microsoft.Phone.Task using.

using System;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;

namespace CustomRingtonesExample
{
    public partial class MainPage : PhoneApplicationPage
    {
        private readonly SaveRingtoneTask _customRingtone;
        
        public MainPage()
        {
            InitializeComponent();
            _customRingtone = new SaveRingtoneTask();
            _customRingtone.Completed += customRingtone_Completed;
        }

        private void customRingtone_Completed(object sender, TaskEventArgs e)
        {
            MessageBox.Show(@"You are back from the Ringtones application. Reference e.TaskResult, in your code, 
            if you want to see if the save was successful.");
        }

        private void SaveButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            _customRingtone.Source = new Uri("appdata:/Audio/ExampleRingtone.wma");
            _customRingtone.DisplayName = "Example Custom Ringtone";
            _customRingtone.Show();
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Things to take note of:

  • You need to define a Completed event handler that will run once the user has completed the task. This method is a great place to check to see what the user did in the task and act accordingly.
  • To call the application, you just need to call the Show() method on the chooser.
  • You can access audio files from data that is part of the application (appdata:) as well as from isolated storage (isostore:).

Summary

To wrap up things for today, custom ringtones give the user a chance to have a more personalized experience with their new Window Phone. By calling the SaveRingtonTask, you can provide a gateway for the user to be able to add this level of customization to their phone.

Have fun coding!

To download a working version of the Windows Phone application covered in this article, click the Download Code button below.

download

Tomorrow, Parag Joshi will be covering a new technology available to Windows Phone developers that has been in very high demand: sockets.  See you then!

toolsbutton

31 Days of Mango | Day #19: Tilt Effects

Day19-AddingTiltEffectsDay18-ExpressionBlendSampleDataDay17-WindowsAzureDay16-IsolatedStorageExplorerDay15-ProgressBar

This article is Day #19 in a series called 31 Days of Mango, and was written by guest author Doug Mair. Doug can be reached on Twitter at @doug_mair or at doug.mair@gmail.com.

Applications that look and behave like the native Windows Phone applications will be more attractive to users than applications that don’t behave like the native applications. So, your applications should try to incorporate as much native behavior as possible. A control behavior that is common in many of the native applications is the TiltEffect behavior. Look at the Phone application, which is used for making telephone calls. When you start the Phone app you see a list of users you have called.

Now pay attention as you select an item in the list. When you select that item, you can see a slight tilt to the item that you select. As you release the item, it returns back to its original position. This slight tilt animation gives you a reassurance that the Phone application has received your input. It is a subtle change, but it will increase your users delight.

This article shows you how to add the TiltEffect behavior to controls in your Silver Light applications. A sample application is created which shows the TiltEffect behavior in action and how to turn the TiltEffect behavior on and off at runtime.

Setup for using the TiltEffect Behavior

The TiltEffect behavior is not included in the controls out of the box. We have to get it from a sample that Microsoft released so that third party developers could add this behavior to their applications. The behavior that we are going to use is in a C# file called TiltEffect.cs in the Control Tilt Effect Sample on MSDN.

First of all, download that sample and extract the files to a folder on your computer. The TiltEffect.cs file is in the ControlTiltEffect subdirectory with all of the other source code for the sample. Note the location of the file so that you can add it to your project.

To add it to your Silver Light project, you need to ….

1. Open your project in Visual Studio and right-mouse click on your project.

2. Select the Add menu option, and then select the Existing Item… menu option.

clip_image002_thumb2

3. In the Add Existing Item dialog navigate to and select the TiltEffect.cs file that you downloaded earlier.

4. This will make a copy of the file in your project source directory and will add it to your project.

Namespace

Open the TiltEffect.cs file. Notice that the namespace for this file is ControlTiltEffect. You can either change it to be the same namespace as your project, or leave it as is. For our examples, we will leave it as it is in ControlTiltEffect.

When you want to use this TiltEffect in your application you need to add its namespace to files that use it. To add the namespace to the pages that you want to use the TiltEffect behavior on you controls, add these lines to the top of your XAML file:

mlns:local="clr-namespace:ControlTiltEffect"

local:TiltEffect.IsTiltEnabled="True"

This will create a new alias called local for the ControlTiltEffect namespace. This allows the classes in that namespace to be available on this page.

The second line enables the TiltEffect. With these two lines added, all Buttons and List Boxes on this page will now have the TiltEffect behavior. As you will see later, we can turn on and off the TiltEffect for specific controls on the page.

Now that we know how to add the TiltEffect behavior to our project and XAML pages, lets look at an example of how we would use it an application.

Sample Application

This sample app has many controls which will demonstrate how the TiltEffect behavior works. Most of the buttons and list box items on this page will tilt when you press on them. With TiltEffect.IsTiltEnabled="True" that is the default behavior for the page.

Suppressing TiltEffect on single control: SuppressTilt Property

Suppose that we have a certain button on our page that we don’t want to tilt. We can enable and suppress the TiltEffect for each control or list box item separately. In our sample, the second button will not tilt, this is because the Suppressed property has been set to stop it from tilting. Here’s the XAML code for the second button.

<Button local:TiltEffect.SuppressTilt="True" Content="Tilt Suppressed"

Height="82" HorizontalAlignment="Left" Margin="229,52,0,0"

Name="button2" VerticalAlignment="Top" Width="221" />

Notice that it has the local:TiltEffect.SuppressTilt="True". This suppresses all Tilt effects for this control. The second list box item also has this property set, so it will not tilt either. All of the other controls on this page don’t include this property, so they will tilt.

Stopping TiltEffect for Entire Page: IsTiltEnabled Property

It is also possible to stop the TiltEffect for an entire page. This might be useful for showing a “retro” mode for your application. At the bottom of the page, there is a button labeled “Stop Tilts”. When this button is clicked, all TiltEffects on this page will be stopped. If you press it again, the TiltEffects will be re-enabled.

The button3_Click event handler does this work. The C# code behind is where this takes place. In order to use the TiltEffects in the C# code behind, we add the using ControlTiltEffect; statement.

Then in the button3_Click handler, we get the current IsTiltEnabled property for the entire page. To do this we use the method GetIsTiltEnabled to read the dependency property for the entire page that was given to it by the TiltEffect class. Note the the this object inside the parentheses represents the entire page. This value is saved in the tiltEnabled variable.

Then we set the content for the ToggleTilting button based on the IsTiltEnabled property.

Lastly, we change the IsTiltEnabled property for the entire page, to be the opposite of what it used to be. This time we are modifying the dependency property with the SetIsTiltEnabled method.

At this point, none of the buttons or list box items will tilt. Try it and see. Then press the ToggleTilting button again to let the buttons tilt again.

using Microsoft.Phone.Controls;

using ControlTiltEffect;

namespace Day19_TiltEffects

{

public partial class MainPage : PhoneApplicationPage

{

// Constructor

public MainPage()

{

InitializeComponent();

}

private void button3_Click(object sender, RoutedEventArgs e)

{

bool tiltEnabled = TiltEffect.GetIsTiltEnabled(this);

ToggleTilting.Content = (tiltEnabled) ? "Start Tilts" : "Stop Tilts";

TiltEffect.SetIsTiltEnabled(this, !tiltEnabled);

}

}

}

Complete XAML Source Code

<phone:PhoneApplicationPage

x:Class="Day19_TiltEffects.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;

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&quot;

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;

mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

FontFamily="{StaticResource PhoneFontFamilyNormal}"

FontSize="{StaticResource PhoneFontSizeNormal}"

Foreground="{StaticResource PhoneForegroundBrush}"

SupportedOrientations="Portrait" Orientation="Portrait"

xmlns:local="clr-namespace:ControlTiltEffect"

local:TiltEffect.IsTiltEnabled="True"

shell:SystemTray.IsVisible="True">

<!–LayoutRoot is the root grid where all 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="Day 19"

Style="{StaticResource PhoneTextNormalStyle}"/>

<TextBlock x:Name="PageTitle" Text="Tilt Effects" Margin="9,-7,0,0"

Style="{StaticResource PhoneTextTitle1Style}" Height="75"

FontSize="56" />

</StackPanel>

<!–ContentPanel – place additional content here–>

<Grid x:Name="ContentPanel" Margin="12,118,12,0" Grid.RowSpan="2">

<TextBlock Height="48" HorizontalAlignment="Left" Margin="20,6,0,0"

Name="textBlock1" Text="Buttons" VerticalAlignment="Top"

Width="402" FontSize="32" FontWeight="Bold" />

<Button Content="Tilts" Height="82" HorizontalAlignment="Left"

Margin="0,52,0,0" Name="button1" VerticalAlignment="Top"

Width="202" />

<Button local:TiltEffect.SuppressTilt="True" Content="Tilt Suppressed"

Height="82" HorizontalAlignment="Left" Margin="229,52,0,0"

Name="button2" VerticalAlignment="Top" Width="221" />

<CheckBox Content="CheckBox" Height="72" HorizontalAlignment="Left"

Margin="229,140,0,0" Name="checkBox1" VerticalAlignment="Top" />

<RadioButton Content="RadioButton" Height="72" HorizontalAlignment="Left"

Margin="9,140,0,0" Name="radioButton1" VerticalAlignment="Top" />

<HyperlinkButton Content="HyperlinkButton" Height="30"

HorizontalAlignment="Left" Margin="20,218,0,0"

Name="hyperlinkButton1" VerticalAlignment="Top" Width="200" />

<TextBlock FontSize="32" FontWeight="Bold" Height="48"

HorizontalAlignment="Left" Margin="20,282,0,0" Name="textBlock2"

Text="ListBox" VerticalAlignment="Top" Width="402" />

<ListBox Height="100" HorizontalAlignment="Left" Margin="24,314,0,0"

Name="listBox1" VerticalAlignment="Top" Width="438" Grid.Row="1">

<ListBoxItem Content="Tilts 1" />

<ListBoxItem local:TiltEffect.SuppressTilt="True"

Content="Tilt Suppressed 2" />

<ListBoxItem Content="Tilts 3" />

</ListBox>

<Button Name="ToggleTilting" Content="Stop Tilts" Height="82"

HorizontalAlignment="Left" Margin="14,495,0,0"

VerticalAlignment="Top" Width="438" Click="button3_Click" />

</Grid>

</Grid>

</phone:PhoneApplicationPage>

Summary

Adding Tilt Effects to your controls add a subtle effect that lets your user know their input was received. It is a little more work to add behaviors like this to your applications, but it will pay off with greater user appeal. You have seen how to add the tilt behavior to you app, how to enable it for entire pages, how to disable it for single control and how to disable it for the entire page. Attention to seemingly small details like this will help your application stand out.

BTW, if you are curious, I suggest that you take a look inside the TiltEffect.cs file. It is an excellent example of how to use storyboards and touch manipulations to build effects from the bare bones up. Taking some time to understand how behaviors like this are built will increase your knowledge and confidence with the Windows Phone development. This is a versatile control that you can adapt in other ways if needed.

To download a complete working Windows Phone project that demonstrates Tilt Effects, click the Download Code button below:

download

Tomorrow, we are going to look at creating custom ringtones for your users, and how Windows Phone now supports this awesome new feature.  See you then!

toolsbutton

31 Days of Mango | Day #18: Using Sample Data

Day18-ExpressionBlendSampleDataDay17-WindowsAzureDay16-IsolatedStorageExplorerDay15-ProgressBarDay14-OData

This article is part of a series called 31 Days of Mango.

Today, we are going to explore another great feature of Expression Blend by creating some sample data that our application can use.  Many times, when you start building an application, you want to see what the data will look like in the user interface well before the database (or web service) is actually ready.  To do this, we can use Expression Blend.

Since I have covered Expression Blend and many of its features in the past, I’ll give you some recommended reading here:

For today’s article, we’re just going to dive right in.  If you would like to try this application on your Windows Phone for free, it’s available in the Windows Phone Marketplace:

DownloadIcon

Creating Your Windows Phone Application

As with most of my posts in this series, I have also created a video to demonstrate what I am trying to accomplish with this article.  I will explain everything that is shown in the video, but if you’d prefer to watch it first, here it is:

We will start by creating a new Windows Phone project.  You’ve hopefully done this many times already in Visual Studio 2010, so I will walk you through doing this in Expression Blend this time.

image

Once you have your new project, with your MainPage.xaml page open, we are going to add a ListBox to our page.  To do this, click the arrow in the left-side menu bar, choose controls, and click the ListBox icon.

image

Once you’ve selected the ListBox, you can click and drag one on to your design surface.

image

I’ve expanded my ListBox to take up the entire space inside my ContentPanel Grid that was created by default when I created my project.  (This is all of the space below the “page name” text on the screen.

Creating Some Sample Data

If you look in the top right corner of Expression Blend, you should see a tab with the word “Data” on it.  Open that tab.

image

By clicking the “New Sample Data” button on the top right of this panel, we will be able to get started creating our sample data.

image

This will bring up the “New Sample Data” dialog box.  Give your data a name (I named mine Furniture, to follow the example that I used in the video.)

image

You’ll notice that you have the option to define this data in your project, or on the specific page.  I recommend choosing “Project” because it will create a bunch of useful structures in your application that you can use in a more permanent fashion.

Clicking OK will take you back to the Data tab, with a few new things added.

image

We now have a structure that should look familiar to anyone that has worked with a database before.  “Furniture” represents our database, and “Collection” is a table in that database.  “Property1” and “Property2” are fields in the Collection table.

I am going to add two new properties to the database, and rename the existing two, so that things are a little more descriptive.  Collection will become “Chairs”, and we now have 4 properties: SKU, Price, Description, and Image.

image

At the right end of each of the properties, there is an icon to indicate what type of data it is.  For Description, I want it to be a long string of words.  You can do this by choosing the “Lorem Ipsum” string type.  This will give us random strings of 20 words, with no word being longer than 8 characters.

image

We want the Image property to actually contain images, so you should choose the Image type.  You can specify your own set of images if you’d like, and we’ll do that later in this article.

image

I will quickly set the Price property to a String/Price type, and the SKU to be a Number value, with 6 character values.

imageimage

If you click on the “View Sample Data” button to the right of the “Chairs” title, you will be shown a grid of data that contains randomized data for each of the properties we have specified.  By changing the value of the “Number of records” box at the bottom, you can determine how many data records you would like to use.

SNAGHTML27ad8b6d

So there you have it.  We’ve created some sample data.  At this point, you’re probably staring at the sample data above trying to figure out how it knew to use images of chairs.  Regardless of what you name your data structures, if you use an Image type, and don’t specify a set of images, this set of chair images will always be used.  No magic, just a tricky demo.

To change the images to something more appropriate to your project, you can re-open the definition for the Image property that I created, and specify a folder on your computer.  When you do this, you’ll end up with sample values that look more like this (assuming you used the same images I did.)

SNAGHTML27b15a01

Now it’s time to actually use this data.

Using Sample Data in a Windows Phone App

First, even though this is called “Sample Data"," if you have a small set of static data that you’re intending to use in your application, there’s absolutely nothing wrong with making this a permanent part of your application.  That being said, in order to get this data into our ListBox that we created much earlier in this article, you can simply drag the “Chairs” node from the Data tab directly into your Listbox control, like this (the blue box is my mouse dragging the data to the ListBox…click to enlarge):

image

The moment you let go of your data, it will immediately create two things for you:

  • An DataTemplate for laying out the individual elements in the ListBox.
  • A binding between the SampleData and your ListBox.

In this example, the DataTemplate is stored directly in our MainPage.xaml page, and looks like this:

<DataTemplate x:Key="ChairsItemTemplate">
    <StackPanel>
        <TextBlock Text="{Binding Description}"/>
        <Image Source="{Binding Image}" HorizontalAlignment="Left" Height="64" Width="64"/>
        <TextBlock Text="{Binding Price}"/>
        <TextBlock Text="{Binding SKU}"/>
    </StackPanel>
</DataTemplate>
 

You will see that our ListBox contains an ItemTemplate binding to this DataTemplate, and the ItemsSource is defined as our Chairs data:

<ListBox Margin="8,8,0,8" ItemTemplate="{StaticResource ChairsItemTemplate}" ItemsSource="{Binding Chairs}"/>
 

In order to edit how the DataTemplate appears, we can use Expression Blend to do this too.  Right-click on your ListBox in Expression Blend, and choose

Edit Additional Templates > Edit Generated Items (ItemTemplate) > Edit Current

image

By doing this, you should notice that several things in our interface have changed.  First, at the top of our design pane, it now indicates that we’re working on our ListBox’s ItemTemplate.

image

Second, take a look at your Objects and Timeline panel.  It is now specifically focused on the contents of the ItemTemplate, which contains 3 TextBlocks and an Image control.

image

We can also now manipulate the elements of our ItemTemplate directly on the design surface.  For example, I just made the first image larger.  Since we are editing the template, you’ll notice that all of the images also got larger.

image

In the next image, you’ll notice that I moved the Description TextBlock to the bottom of the template, and right aligned all of the TextBlocks.  The Price has also had its font size increased, and the foreground color is now green.

image

At this point, unless you have a need to make this prettier, you have a working application.  Running it in the emulator will result in an application that has a scrolling ListBox.  This is how it appears, based on the changes I have made:

image

Where Is All Of This Data Stored?

Since we can deploy this application to the emulator, and the data appears, and we can use and manipulate it, you might be wondering just how this is stored in our application, and how it works.

If you take a look at your project structure, you should notice a new folder named SampleData.

image

Inside this folder, you’ve got a Furniture.xaml file which contains your actual generated data. Its contents look like this:

<!–
      *********    DO NOT MODIFY THIS FILE     *********
      This file is regenerated by a design tool. Making
      changes to this file can cause errors.
–>
<SampleData:Furniture xmlns:SampleData="clr-namespace:Expression.Blend.SampleData.Furniture" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"&gt;
  <SampleData:Furniture.Chairs>
    <SampleData:ChairsItem SKU="366413" Price="$100" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day1-EmulatorTools.png" Description="Nam aliquam sed dis cras class duis integer maecenas praesent" />
    <SampleData:ChairsItem SKU="420965" Price="$29.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day10-NetworkInformation.png" Description="Accumsan aenean quisque bibendum mauris curae nullam donec vivamus est aliquam" />
    <SampleData:ChairsItem SKU="727463" Price="$249.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day11-LiveTiles.png" Description="Nunc aliquet blandit aptent auctor hac congue dictumst eleifend amet etiam ante fusce leo commodo facilisi cursus conubia dictum" />
    <SampleData:ChairsItem SKU="549249" Price="$1000" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day12-BetaTesting.png" Description="Lectus cubilia libero arcu dapibus lorem mus faucibus diam habitant egestas" />
    <SampleData:ChairsItem SKU="829031" Price="$2.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day13-MarketplaceTestKit.png" Description="Euismod inceptos nec eget interdum non elit morbi feugiat nulla ligula gravida lobortis iaculis litora" />
    <SampleData:ChairsItem SKU="758558" Price="$300" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day14-OData.png" Description="Proin enim lacinia laoreet augue nascetur natoque erat dolor per posuere" />
    <SampleData:ChairsItem SKU="342996" Price="$10,999" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day15-ProgressBar.png" Description="Fames sed sem felis sit eros vel nam ipsum justo sed nibh" />
    <SampleData:ChairsItem SKU="604077" Price="$49.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day16-IsolatedStorageExplorer.png" Description="Nisi potenti pretium dis pharetra nisl placerat pulvinar luctus lacus est hac sagittis nunc lorem magnis rhoncus leo sodales" />
    <SampleData:ChairsItem SKU="673946" Price="$25.34" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day17-LocalDatabase.png" Description="Viverra odio orci mattis mus magna massa pede quam aliquam senectus metus nec morbi" />
    <SampleData:ChairsItem SKU="177653" Price="$100" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day17-WindowsAzure.png" Description="Non mauris sociosqu integer suscipit quisque torquent mollis quis per ultrices vehicula sed neque" />
    <SampleData:ChairsItem SKU="788040" Price="$100" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day18-ExpressionBlendSampleData.png" Description="Aenean nam cras maecenas duis class nunc aliquam sed amet praesent dis mauris" />
    <SampleData:ChairsItem SKU="547641" Price="$100" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day19-AddingTiltEffects.png" Description="Integer ante arcu diam accumsan bibendum est nullam dictumst eleifend curae aptent eget donec quisque elit facilisi faucibus" />
    <SampleData:ChairsItem SKU="401054" Price="$29.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day2-DeviceStatus.png" Description="Enim habitant vivamus auctor etiam fusce congue aliquam erat inceptos" />
    <SampleData:ChairsItem SKU="256092" Price="$249.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day20-Ringtones.png" Description="Interdum lobortis aliquet blandit commodo hac conubia lorem cubilia leo nascetur eros pharetra cursus" />
    <SampleData:ChairsItem SKU="852495" Price="$1000" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day21-Sockets.png" Description="Dapibus nibh placerat nisi mus nec morbi nulla nisl proin egestas non euismod" />
    <SampleData:ChairsItem SKU="657020" Price="$2.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day22-AppConnect.png" Description="Per pulvinar feugiat dictum sed gravida sem iaculis sagittis sit lectus lacinia nunc laoreet odio" />
    <SampleData:ChairsItem SKU="450567" Price="$300" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day23-ExecutionModel.png" Description="Augue senectus vel nam sed orci sociosqu dis libero dolor natoque pede ligula fames posuere felis est potenti quam" />
    <SampleData:ChairsItem SKU="546952" Price="$10,999" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day24-ProfilerTool.png" Description="Litora luctus hac ipsum suscipit magnis torquent pretium rhoncus leo quis justo sodales urna mus ultrices lacus lorem viverra nec" />
    <SampleData:ChairsItem SKU="481783" Price="$49.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day25-BackgroundAgents.png" Description="Mattis cras aliquam duis nunc amet magna mauris massa ante integer mollis non per sed quisque" />
    <SampleData:ChairsItem SKU="200702" Price="$25.34" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day26-BackgroundFileTransfers.png" Description="Metus morbi sem neque sit netus vel nam arcu vehicula sed volutpat nulla porta diam" />
    <SampleData:ChairsItem SKU="536695" Price="$100" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day27-MicrophoneAPI.png" Description="Dis purus montes vivamus eget maecenas aliquam elit nostra est risus velit" />
    <SampleData:ChairsItem SKU="275101" Price="$29.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day28-MediaLibrary.png" Description="Ornare vitae platea class aliquet hac curae primis praesent enim erat accumsan leo eros blandit bibendum donec nibh rutrum mus" />
    <SampleData:ChairsItem SKU="979319" Price="$249.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day29-Globalization.png" Description="Nec dictumst commodo nisi etiam nisl eleifend conubia cubilia nunc sapien fusce dapibus non semper sociis egestas" />
    <SampleData:ChairsItem SKU="234379" Price="$1000" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day3-Reminders.png" Description="Taciti odio euismod facilisi faucibus lorem habitant per tellus tempor sed orci inceptos feugiat pede morbi quam" />
    <SampleData:ChairsItem SKU="248085" Price="$2.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day30-ConnectionSettings.png" Description="Nulla tempus tortor proin interdum augue quis gravida iaculis lacinia turpis lobortis urna sem varius sit cras laoreet vel" />
    <SampleData:ChairsItem SKU="982403" Price="$300" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day31-PromotingYourApplication.png" Description="Nascetur nam sed aenean pharetra dis natoque posuere potenti mauris pretium dolor fames placerat nullam aptent" />
    <SampleData:ChairsItem SKU="106433" Price="$10,999" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day4-Compass.png" Description="Est pulvinar duis rhoncus sodales felis nunc hac viverra amet ante ipsum auctor congue" />
    <SampleData:ChairsItem SKU="203433" Price="$49.99" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day5-Gyroscope.png" Description="Leo cursus mus arcu dictum nec diam sagittis aliquam eget non integer lectus elit per enim erat senectus" />
    <SampleData:ChairsItem SKU="660918" Price="$25.34" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day6-Motion.png" Description="Libero justo ligula sed sociosqu sem eros lacus suscipit sit torquent litora quisque nibh nisi nisl lorem" />
    <SampleData:ChairsItem SKU="734363" Price="$100" Image="/Day18_BlendSampleData;component/SampleData/Furniture/Furniture_Files/Day7-RawCamera.png" Description="Nunc magna vel massa luctus odio nam orci ultrices vivamus sed" />
  </SampleData:Furniture.Chairs>
</SampleData:Furniture>
 

The actual structure of the data in this file is defined by the Furniture.xaml.cs file, which contains the classes and properties that we defined when we first created our Sample Data.  You can see that each of the properties are shown here, as well as references to the location of our sample images.

//      *********    DO NOT MODIFY THIS FILE     *********
//      This file is regenerated by a design tool. Making
//      changes to this file can cause errors.
namespace Expression.Blend.SampleData.Furniture
{
    using System;

// To significantly reduce the sample data footprint in your production application, you can set
// the DISABLE_SAMPLE_DATA conditional compilation constant and disable sample data at runtime.
#if DISABLE_SAMPLE_DATA
    internal class Furniture { }
#else

    public class Furniture : System.ComponentModel.INotifyPropertyChanged
    {
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }

        public Furniture()
        {
            try
            {
                System.Uri resourceUri = new System.Uri("/Day18_BlendSampleData;component/SampleData/Furniture/Furniture.xaml", System.UriKind.Relative);
                if (System.Windows.Application.GetResourceStream(resourceUri) != null)
                {
                    System.Windows.Application.LoadComponent(this, resourceUri);
                }
            }
            catch (System.Exception)
            {
            }
        }

        private Chairs _Chairs = new Chairs();

        public Chairs Chairs
        {
            get
            {
                return this._Chairs;
            }
        }
    }

    public class ChairsItem : System.ComponentModel.INotifyPropertyChanged
    {
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }

        private double _SKU = 0;

        public double SKU
        {
            get
            {
                return this._SKU;
            }

            set
            {
                if (this._SKU != value)
                {
                    this._SKU = value;
                    this.OnPropertyChanged("SKU");
                }
            }
        }

        private string _Price = string.Empty;

        public string Price
        {
            get
            {
                return this._Price;
            }

            set
            {
                if (this._Price != value)
                {
                    this._Price = value;
                    this.OnPropertyChanged("Price");
                }
            }
        }

        private System.Windows.Media.ImageSource _Image = null;

        public System.Windows.Media.ImageSource Image
        {
            get
            {
                return this._Image;
            }

            set
            {
                if (this._Image != value)
                {
                    this._Image = value;
                    this.OnPropertyChanged("Image");
                }
            }
        }

        private string _Description = string.Empty;

        public string Description
        {
            get
            {
                return this._Description;
            }

            set
            {
                if (this._Description != value)
                {
                    this._Description = value;
                    this.OnPropertyChanged("Description");
                }
            }
        }
    }

    public class Chairs : System.Collections.ObjectModel.ObservableCollection<ChairsItem>
    {
    }
#endif
}

Finally, you have the Furniture_Files folder, which contains all of the actual sample images that we’re using.  Because we used both the default chair images, as well as my own collection, you’ll find that each of the images is inside this folder for you.  As the comments show, if you’re planning on editing this Sample Data from Expression Blend again, don’t do it.  Your changes will be discarded.  These are generated code files, and should be treated as such.

Summary

So, there you have it.  You have an application that uses Sample Data that you created in Expression Blend to populate a ListBox, using a DataTemplate.  There are many places where you might find this technique handy, so keep it in mind for static data sets rather than using something more complicated like a database or web service.

If you would like to download this entire working Windows Phone application that uses this Sample Data, click the Download Code button below:

download

Tomorrow, Doug Mair is back to discuss adding Tilt Effects to your XAML elements, giving some interaction to your user’s touch gestures.  See you then!

toolsbutton

31 Days of Mango | Day #17: Using Windows Azure

Day17-WindowsAzureDay16-IsolatedStorageExplorerDay15-ProgressBarDay14-ODataDay13-MarketplaceTestKit

This article is Day #17 in a series called 31 Days of Mango, and was written by guest author Michael Collier.  Michael can be reached on Twitter at @michaelcollier.

Windows Phone + Windows Azure = Better Together

Some of the most interesting Windows Phone applications will utilize services of some type. Those services could be web services to provide access to business logic, just like we would for traditional ASP.NET applications. Those services could be data services to provide access to our data, both structure and unstructured data. Those services could also be identity management services, providing a way to handle authentication for our application.

Traditional Way

Typically we stand up one or more servers and then deploy our services and data to those servers. Doing so can be an expensive process – in time and money. We need to take the time to build, configure and secure the servers. We also need to maintain those servers (hardware failures, system patches, etc.). Predicting how many and what size servers we need in order to support our application is also a challenge. Buy too much and we’ve wasted somebody’s money. Buy too little and users (and bosses) are going to be upset. All of this forces us as application developers to worry about infrastructure, when all we want to do is build a killer mobile application.

Modern Way

One of the prevailing trends now is to leverage services (notice “services” and not “servers”) provided by a cloud platform. The Windows Azure platform provides compute services that allow for the deployment of our web service applications. We write the application and deploy it – and let Windows Azure handle the rest. Windows Azure also offers access to highly available and scalable storage services in the form of tables, blobs, and queues.

  • Tables are a semi-structured storage mechanism that is capable of storing massive data very efficiently (think NoSQL, not a relational database).
  • Blobs essentially act like a giant file system for storing whatever content you want (pictures, movies, documents, etc.).
  • Queues serve as a lightweight messaging mechanism for passing data between disconnected systems.

Additionally Windows Azure offers an identity management service, Access Control Services (ACS), that provides an easy way to authenticate users via multitude of identity providers. ACS comes preconfigured to support major social networks such as Facebook, Yahoo!, Windows Live ID, and Google. ACS can also tap into an enterprise for identity management via Active Directory Federation Services (ADFSv2). ACS is especially great for mobile applications as users likely already have a social profile of some type. In fact, Windows Phone users already have a Windows Live ID.

There are numerous benefits for leveraging a platform such as Windows Azure when creating your Windows Phone applications. For starters, it is quick! You can use Visual Studio to write both your Windows Phone and Windows Azure powered services. Once the code is written, the service can be available on the internet in a matter of minutes. Using Windows Azure can also be very cheap. You can store as much data as you want starting for just $0.14/GB per month. Since nearly all development can be done locally on your development machine, you don’t even need to start paying until you start running your service in the cloud. All of this allows us to focus on creating a great Windows Phone application and the services that make it light up, and not the underlying infrastructure.

Let’s Do It

Let’s take a quick walkthrough of building a very simple Windows Phone application that is powered by services available on the Windows Azure platform.

Some of the key aspects to this architecture and data flow include:

  1. The need to authenticate users of the Windows Phone application. Major social networks such as Facebook, Windows Live, Yahoo! and Google seem like logic choices. We’ll let Windows Azure Access Control Services (ACS) handle that work (more details below).
  2. A WCF REST service will serve as the primary holder of business logic and provide secure access to our data.

One thing to point out here is how the phone is accessing the table and blob data in Windows Azure. The native API for Windows Azure is a REST API. This includes accessing storage such as tables and blobs. One of the security aspects of Windows Azure storage is that all access is protected by access keys. The access key needs to be sent in each request (part of the REST call) to Windows Azure. These keys should be kept secret. Since we need to keep our secrets a secret, we do not want to put the actual access keys on the phone. Doing so would let our secret information out into the wild, and that could be bad for us. It is possible to change the access keys (e.g. if the keys become compromised), and if changed, we’d need to update the application to have the new key. To be safe, we create a proxy web service by which all requests to storage will be funneled. This keeps our secret information a secret, and allows us to develop a service in a very SOA manner.

There are two basic ways in which we can create this web service proxy. The excellent folks at Microsoft have created the Windows Azure Toolkit for Windows Phone which provides a wealth of resources and samples for writing Windows Phone applications that leverage Windows Azure services. Included in this toolkit are project templates which jump start the process for working with ACS, storage (tables, blobs, and queues), and even recipes for push notifications.

However, for the purposes of this example we’re going to create a simple WCF REST service that we can easily consume from our Windows Phone application. The WCF service will not only serve as a proxy for accessing Windows Azure storage, but will also contain our business logic. If we wanted, we could extend the WCF service for other purposes as well – such as powering an ASP.NET web application or even applications for other mobile platforms. We’ll not dive too much into various aspects of Windows Azure storage here, as that is covered in depth in the Windows Azure Platform Training Kit (see the Exploring Windows Azure Storage module).

Table Storage

To start with, we will need a basic entity that we will storage in Windows Azure table storage.

   1: public sealed class Car : TableServiceEntity

   2:     {

   3:         /*

   4:          * PartitionKey = make

   5:          * RowKey = current date/time in UTC

   6:          */

   7:  

   8:         public Car()

   9:         {}

  10:  

  11:         public Car(string make, string model, int year, string description, string imageUrl)

  12:         {

  13:             PartitionKey = make;

  14:             RowKey = DateTime.UtcNow.Ticks.ToString();

  15:  

  16:             Make = make;

  17:             Model = model;

  18:             Year = year;

  19:             Description = description;

  20:             ImageUrl = imageUrl;

  21:         }

  22:  

  23:         public string Make { get; set; }

  24:         public string Model { get; set; }

  25:         public int Year { get; set; }

  26:         public string Description { get; set; }

  27:         public string ImageUrl { get; set; }

  28:     }

At this point we can create a simple method as part of our WCF service that can handle some business logic (perhaps validating an application key of some sort) and save the entity to table storage. (Note: The CarDataSource class is a simple wrapper class created to simplify some of the code – view the entire class in the solution download at the end of this post.)

   1: public class CarService : ICarService

   2: {

   3:     public void AddCar(Car car)

   4:     {

   5:         try

   6:         {

   7:             var key = ValidateApplicationKey();

   8:             if (key)

   9:             {

  10:                 var carDataSource = new CarDataSource();

  11:                 carDataSource.CreateCar(car);

  12:             }

  13:         }

  14:         catch (Exception)

  15:         {

  16:             throw new WebFaultException<string>("Failed to create a new car.", HttpStatusCode.InternalServerError);

  17:         }

  18:     }

  19: }

With the service in place, we can call the service from our Windows Phone application just like we would call any other RESTful web service. Download the full code package to view that code.

Blob Storage

As mentioned earlier, Windows Azure blob storage provides a means for storing content such as PDF documents, images, movies or really any file you may have. Each file is considered a “blob”, and blobs reside in container. Containers (and thus the residing blobs) are by default accessible only to those who have the appropriate storage access key (the same key used by table storage). With the access key, we can do any operation we want (read, create, delete, etc.). It is possible to change the permissions for a container to allow anonymous read access, which can be great when, for example, we need anybody to view pictures from Windows Phone application.

What are we to do if we want to allow someone to have access to the container for only a specific period of time, and to only have a specific set of permissions during that time? The answer is to use a Shared Access Signature (SAS). A Shared Access Signature is a specially crafted URL query string that contains the granted permissions (only create, only delete, create and delete, etc.) and the timeframe in which those permissions are valid. We can create a SAS and then provide that to whoever we want to allow that access to blob storage. In our example, we can request a Shared Access Signature from the WCF service, and then use the SAS URL to save pictures directly from our phone into Windows Azure blob storage.

The process to create a Shared Access Signature we be as follows:

   1: public Uri CreateCarImageSharedAccessSignature()

   2: {

   3:     Uri signatureUri = null;

   4:  

   5:     var key = ValidateApplicationKey();

   6:     if (key)

   7:     {

   8:         try

   9:         {

  10:             CloudBlobContainer container = CreateContainer("cars");

  11:  

  12:             // Set permissions on the container.

  13:             var sas = container.GetSharedAccessSignature(

  14:                    new SharedAccessPolicy

  15:                        {

  16:                            Permissions =

  17:                                SharedAccessPermissions.Write |

  18:                                SharedAccessPermissions.List,

  19:                            SharedAccessStartTime = DateTime.UtcNow,

  20:                            SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromMinutes(5)

  21:                        });

  22:  

  23:                // Trim the leading '?' to prevent there from being two in the resulting URI.

  24:                var uriBuilder = new UriBuilder(container.Uri) {Query = sas.TrimStart('?')};

  25:                signatureUri = uriBuilder.Uri;

  26:            }

  27:            catch (Exception ex)

  28:            {

  29:                throw new WebFaultException<string>(ex.Message, HttpStatusCode.InternalServerError);

  30:            }

  31:     }

  32:     return signatureUri;

  33: }

Access Control

Now that we have a the basics in place for accessing the storage services of Windows Azure, let’s add in a way to authenticate users of our application. Earlier we briefly discussed Windows Azure’s Access Control Services (ACS). ACS provides a federated, claims-based identity management solution. This allows us to authenticate users against Facebook, Yahoo!, Windows Live ID, or Google without having to write the code for each of those! Once authenticated, we’ll get a series of claims back that contain information about the user (typically their name and email address). We can then use that data to personalize the application or handle some form of user registration (ask the user for more information). The choice on which identity provider to use is entirely ours, and it is all done via configuration.

ACS is a very powerful, yet simple to use, service. To learn more about ACS, please visit http://www.microsoft.com/windowsazure/learn/control-access/#introductory.

It is very easy to add ACS support to an existing Windows Phone application. Microsoft recently released a NuGet package that makes the process quite simple. The NuGet package is great in that it allows for a very flexible solution – we can simply choose to add ACS to our application and do so in a way that is quick and introduces minimal dependencies. You can find the NuGet package by searching for “Access Control Service” in Visual Studio’s Manage NuGet Packages tool.

Or you can install the package from the NuGet package management console.

PM> Install-Package Phone.Identity.AccessControl.BasePage

Either way will set up the necessary controls and placeholders in your Windows Phone application for using ACS. Once configured, you will want to follow the provided instructions for configuring your Windows Phone application to use your ACS configuration. Using the NuGet package to add ACS support to your Windows Phone application is considered a somewhat advanced process, as it still leaves the ACS configuration up to us. There are detailed instructions here which provide an excellent step-by-step walkthrough of creating a new ACS namespace and any necessary configuration. Task 2 in that walkthrough is probably a good starting point, but the entire walkthrough is an excellent read as well.

Push Notifications

If we wanted, it is also very easy to add push notification support to our solution. There is a new NuGet package that makes adding the necessary settings very easy – much like what we saw with adding support for Access Control Services. The process for doing that is nicely demonstrated in a video over on Channel 9.

ShortPushNotifications

The NuGet packages for supporting Push Notifications are pretty straight forward as well.

PM> Install-Package Phone.Notifications.BasePage (client/phone side – sets up registration/registration with the Push Notification cloud service)

PM> Install-Package CloudServices.Notifications (server side –works with Microsoft Push Notification Service)

Microsoft has recently released several NuGet packages that make working with Windows Azure from our Windows Phone applications a breeze. There are packages for ACS, push notifications, and membership (traditional username/password). Be sure to check these packages out as they’ll allow you to easily mix and match the features needed for your application.

Wrap It Up

We should now be complete with our cloud powered Windows Phone application! Here we have seen how easy it can be to leverage several of the services available on the Windows Azure platform. We can easily create a web service that will provide access to our business logic and serve as a proxy to Windows Azure storage services. It is also quite easy to add Facebook, Yahoo!, Windows Live ID, or Google authentication to an existing application thanks to the new NuGet packages. Download the full example below.

Don’t forget to enter the “Go Mango” contest at http://bit.ly/MangoOffer, promo code “MCOLL”.

You can enter to win a new Samsung Series 7 slate and also free advertising for your Windows Phone application! If you use Windows Azure to power your mobile application, you will earn an bonus entry!

To download a full sample application that uses the code from the example above, please click the Download Code button below:

download

To get started with Windows Azure sign up for a free, 90-day trial at http://www.microsoft.com/windowsazure/free-trial/.

Resources

Tomorrow, we are going to discuss using sample data, and how we can use Expression Blend to make this incredibly easy for us.  See you then!

toolsbutton

31 Days of Mango | Day #16: Isolated Storage Explorer

Day16-IsolatedStorageExplorerDay15-ProgressBarDay14-ODataDay13-MarketplaceTestKitDay12-BetaTesting

This article is Day #16 in a series called 31 Days of Mango, and was written by guest author Samidip Basu.  Samidip can be reached on Twitter at @samidip.

Windows Phone SDK 7.1 includes a new utility for Windows Phone Developers, called the Isolated Storage Explorer. In this article, we shall begin with the basics of Isolated Storage usage & see how the new Isolated Storage Explorer could become handy in testing applications that use Isolated Storage for files & directories, targeting both Windows Phone OS 7.0 & Windows Phone OS 7.1 runtimes.

The Basics

For Windows Phone applications, Isolated Storage is the solution to create & maintain sandboxed local storage, for any kind of persistence of data (Jeff covered this in Day #15 of the original 31 Days of Windows Phone). Abstraction of access to the underlying OS file system has the obvious advantage of security & reliability as we run third-party applications. Since our applications perform all their storage I/O against the Isolated Storage, making sure we are keeping it lean & accurate is important; this is where the Isolated Storage Explorer helps.

Isolated Storage usage can be of three types:

  • Persist Name-Value pairs through IsolatedStorageSettings class.
  • Persist Directories/Folders & Files in Isolated Storage.
  • Use SQL CE on top of Isolated Storage to persist relational data.

Of these, storing generic files & folders within Isolated Storage is pretty common usage. The Windows Phone emulator being a VM, it was tricky to test the accuracy of our Directory/File systems prior to the latest Windows Phone Mango SDK 7.1.

Now, with the SDK install, we get the Isolated Storage Explorer utility that helps us see inside/manipulate directories/files stored inside of the Isolated Storage to check for accuracy, both with the Windows Phone emulator & developer-unlocked devices. The command-line tool allows for listing files at the Root of the application’s Isolated Storage, as well as, within Directories/Sub-Directories as created by the application runtime. In addition, we can transfer entire Root/Directory of files out of the emulator/device’s Isolated Storage onto a designated folder in our computer & replace the files/directories after checks/edits back into Isolated Storage. This has distinct advantage of being able to check the file structure of our application’s storage exactly as it is inside the Isolated Storage. Also, as we manipulate file contents & directories outside the Isolated Storage & replace them back in, we get an opportunity to test how our application might respond to changes in storage structures & other edge cases. All talk so far .. shall we see some of this in action?

Our Sample Application

Let us begin with a demo Windows Phone app that creates a few sample files at the Root of Isolated Storage, as well as a few Directories with files in them. So, File > New & we begin with the Pivot template, with first section showing editable files at Root, second showing Directories with files in them & the third is a little tease.

File-New

As the application launches afresh, we want to hit a one-time method to set up the sample files & directories for us, starting at the Root of the application’s sandboxed Isolated Storage. So, here’s what we have in the App.xaml.cs global initialization file to set our one-time flag:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace Day16_IsolatedStorageExplorer
{
    public partial class App : Application
    {
        public PhoneApplicationFrame RootFrame { get; private set; }
        public bool FirstLaunch;

        // Constructor
        public App()
        {
            // All the automatically-added code…

            // Set the flag for fresh App launches.
            this.FirstLaunch = true;
        }

        // Everything else …
    }
}
 

 

Now, our starting application page has a Pivot template to display the files & directories in Isolated Storage; however, as we run our application for the first time, there will be no contents in the Isolated Storage. So, here’s how we start in the code-behind to add our one-time sample files & directories; this is completely optional though if you already have content in the Isolated Storage through other means. Notice the use of Isolated Storage namespace to have access to IsolatedStorageFile & IsolatedStorageFileStream classes, which helps us in writing files to the Root of Isolated Storage or to self-created Directories:

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace Day16_IsolatedStorageExplorer
{
    public partial class MainPage : PhoneApplicationPage
    {
        IsolatedStorageFile fileStorage;

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

            // Create a reference to the App’s Isolated Storage
            fileStorage = IsolatedStorageFile.GetUserStoreForApplication();

            // Create initial sample Files & Directories, for fresh App Launch only.
            if ((App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch)
                this.CreateInitialFilesAndDirectories();

            // Reset the flag.
            (App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch = false;
        }

        private void CreateInitialFilesAndDirectories()
        {
            // Create a new StreamWriter, to write files to the root directory.
            StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile1.txt", FileMode.OpenOrCreate, fileStorage));

            // Write sample data.
            fileWriter.WriteLine("This is test data.");

            // Close the StreamWriter.
            fileWriter.Close();

            // Repeat.
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile2.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();

            // Create 2 new SubDirectories.
            fileStorage.CreateDirectory("SubDirectory1");
            fileStorage.CreateDirectory("SubDirectory2");

            // Put sample files in them.
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\SubDir1File1.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\SubDir1File2.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory2\SubDir2File2.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();
        }
    }
}
 

For more details & the variety of file system operations you can do with the IsolatedStorageFile & IsolatedStorageFileStream classes, please head over to the MSDN documentation here & here.

So, now we have some sample files at the Root of the Isolated Storage, as well as, in some custom Directories. Let’s see how we can scan the Isolated Storage & bring out these details in the UI. This dynamic scanning of the Isolated Storage is also going to help us test any changes made to the Isolated Storage from outside using the Isolated Storage Explorer tool. For the sake of this article’s length, the XAML markup is being skipped here; but available in the downloadable code. The only things we are adding here are a custom class called DirectorySystem to keep track of directory structures & help in binding, the code to scan for files & directories, and the bindings to the two listboxes in the Pivot controls called RootListBox & DirectorySystemListBox. So, here’s our modified code-behind for the main page:

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace Day16_IsolatedStorageExplorer
{
    public partial class MainPage : PhoneApplicationPage
    {
        IsolatedStorageFile fileStorage;
        string[] rootFiles;
        string[] directories;
        string[] directoryFiles;

        List<DirectorySystem> listDirectorySystem = new List<DirectorySystem>();
        
        // Constructor
        public MainPage()
        {
            InitializeComponent();

            // Create a reference to the App’s Isolated Storage
            fileStorage = IsolatedStorageFile.GetUserStoreForApplication();

            // Create initial sample Files & Directories, for fresh App Launch only.
            if ((App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch)
                this.CreateInitialFilesAndDirectories();

            // Reset the flag.
            (App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch = false;

            // Read root files.
            rootFiles = fileStorage.GetFileNames();
            this.RootListBox.ItemsSource = rootFiles;

            // Read Directories & build File system.
            directories = fileStorage.GetDirectoryNames();

            foreach (string dir in directories)
            {
                directoryFiles = fileStorage.GetFileNames(dir + "\*");

                DirectorySystem newDir = new DirectorySystem();
                newDir.DirectoryName = dir;
                newDir.DirectoryFiles = directoryFiles;

                listDirectorySystem.Add(newDir);
            }

            // Bind to UI.
            this.DirectorySystemListBox.ItemsSource = listDirectorySystem;
        }       

        private void CreateInitialFilesAndDirectories()
        {
            // Create a new StreamWriter, to write files to the root directory.
            StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile1.txt", FileMode.OpenOrCreate, fileStorage));

            // Write sample data.
            fileWriter.WriteLine("This is test data.");

            // Close the StreamWriter.
            fileWriter.Close();

            // Repeat.
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile2.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();

            // Create 2 new SubDirectories.
            fileStorage.CreateDirectory("SubDirectory1");
            fileStorage.CreateDirectory("SubDirectory2");

            // Put sample files in them.
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\SubDir1File1.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\SubDir1File2.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();
            fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory2\SubDir2File2.txt", FileMode.OpenOrCreate, fileStorage));
            fileWriter.WriteLine("This is test data.");
            fileWriter.Close();
        }      
    }

    public class DirectorySystem
    {
        public string DirectoryName { get; set; }
        public string[] DirectoryFiles { get; set; }
    }
}
 

Seems like a lot of code? Want to try this yourself? The easiest way to make sense of the code is to download the source code solution through the link at the end of the article & run the application yourself. As we deploy our application fresh to the emulator, the above one-time method kicks in creating the sample files at the Root & designated custom Directories. Next, we have code to scan the Isolated Storage for file & directories and bind to UI. If all is well, you can just hit F5 to run the application, which should look like this:

Demo AppDirectories with FilesRoot Files

Now, we can see the files in Isolated Storage. Wouldn’t it be nice if we could open up each individual file to look into its contents & also be able to do edits on them? So, let’s do it! This way, our Isolated Storage Explorer will be able to check for file content after edits & we shall also be able to test how our application behaves when given random file edits at its Root from outside the emulator. This can definitely be done for files inside a Directory; but you get the point. Now, to pull this off, we’re going to add a simple second XAML page to our project and allow it to open up individual files for content editing. Here’s the simple intended UI:

Root File Edits

Let’s see how we got here. First, we assign a MouseLeftButtonUp event handler to the RootListBox in the main pivot page, so we can identify which file the user tapped on. Next, we add this snippet as an event handler in the code-behind:

private void RootListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    if (this.RootListBox.SelectedItem != null)
    {
        string selectedFileName = this.RootListBox.SelectedItem.ToString();
        this.NavigationService.Navigate(new Uri("/FileContent.xaml?FileName=" + selectedFileName, UriKind.Relative));

        this.RootListBox.SelectedItem = null;
    }
}
 

So, essentially all we are doing here is carrying the File name of the selected file on to our second XAML page. Now, let’s see how we read the File in our new page, open it up for edits & finally save the changes the user might make:

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace Day16_IsolatedStorageExplorer
{
    public partial class FileContent : PhoneApplicationPage
    {      
        string currentFileName;

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.NavigationContext.QueryString.ContainsKey("FileName"))
            {
                currentFileName = this.NavigationContext.QueryString["FileName"];
                this.actualFileName.Text = currentFileName;

                this.ReadFileData(currentFileName);
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            this.EditFileData(currentFileName);
        }

        private void ReadFileData(string filePath)
        {
            using (IsolatedStorageFile appIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (appIsolatedStorage.FileExists(filePath))
                {
                    using (IsolatedStorageFileStream fileStream = appIsolatedStorage.OpenFile(filePath, FileMode.Open, FileAccess.Read))
                    {
                        using (StreamReader reader = new StreamReader(fileStream))
                        {
                            this.fileContent.Text = reader.ReadLine();
                        }
                    }
                }
            }
        }

        private void EditFileData(string filePath)
        {
            using (IsolatedStorageFile appIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (appIsolatedStorage.FileExists(filePath))
                {
                    using (IsolatedStorageFileStream fileStream = appIsolatedStorage.OpenFile(filePath, FileMode.Open, FileAccess.Write))
                    {
                        using (StreamWriter writer = new StreamWriter(fileStream))
                        {
                            string editedText = this.fileContent.Text.Trim();
                            writer.Write(editedText);
                            writer.Close();
                        }
                    }
                }
            }

            this.NavigationService.Navigate(new Uri("/DirectoryListings.xaml", UriKind.Relative));
        }

    }
}
 

Notice how as the page loads up, we read the File name from the query parameter, as was passed in from our Pivot page. Equipped with just the File name & a reference to the application’s own Isolated Storage, the ReadFileData method is able to locate the file and open it read mode for display. What happens when the user makes some edits to the file content & hits the Save button? No worries! We simply call into the EditFileData method which locates & opens the file, this time in Write mode & uses an IsolatedStorageFileStream object to write the updated content back into the file. Voila!

You’ll notice that so far we have been able to create & edit sample files into Isolated Storage, as well as, manage custom Directories. While all this is encouraging, would you not get more confidence if you could actually see the file structure inside the emulator/device to check on the files/directories & their content? Doubt no longer, as the Isolated Storage Explorer is here to help.

Isolated Storage Explorer Usage

The Isolated Storage Explorer is a command-line tool that comes bundled with Windows Phone SDK 7.1. The utility comes as ISETool.exe and can be found in the appropriate Program FilesMSFT SDKsWindows Phonev7.1ToolsIsolatedStorageExplorerTool directory. So, let’s fire up our command prompts & navigate to the above directory so we can invoke the command-line tool.

Let’s get started. The ISE Tool can look inside the Isolated Storage of the emulator or a developer-unlocked device. The application who’s Isolated Storage you want to look into needs to installed on the running emulator or device, but the app does not need to be running since Isolated Storage is persisted. Just like any other command-line tool, the ISE Tool comes with some syntactical options to carry out various commands. The general syntax is the following; we shall look into each of the options separately:

ISETool.exe <ts|rs|dir[:device-folder]> <xd|de> <Product GUID> [<computer-path>]

With our demo Windows Phone application, we had created a one-time sample of files & custom directories. Let’s say our app isn’t running and we just wanted to take a peek inside the sandboxed Isolated Storage. Here’s the command:

ISETool.exe dir xd 04ce6350-faf0-4977-8e5b-fb288fc127c0

The breakdown is as follows:

  • ISETool.exe = Command-line tool.
  • dir = Lists out the files and directories from a specified Isolated Storage directory or the root if nothing is specified.
  • xd = This indicates that we are looking into the Isolated Storage of the emulator.
  • de = This would turn the same command to look into the Isolated Storage of an attached Windows Phone device.
  • <Product GUID> = This is the specific Application ID so that the tool knows which sandboxed environment it’s looking into. The Product GUID can be found in the WMAppManifest.xml file for each Windows Phone application.

That’s it. Our command-line friend lists out the sample files in the Root Isolated Storage directory as well as the custom SubDirectories created. Want to take a peek inside one of our custom Directories in Isolated Storage? Here’s the command:

ISETool.exe dir:"SubDirectory1" xd 04ce6350-faf0-4977-8e5b-fb288fc127c0

You’ll notice that everything remains the same as the last command; but instead of looking at the Root of the Isolated Storage, the tool now lists out all Files/Sub-Directories within the specified Directory.

Now, the really fun part! What if you wanted to rip out the entire Isolated Storage container for a Windows Phone application out of the emulator or device? Well, sure you can .. here’s how:

ISETool.exe ts xd 04ce6350-faf0-4977-8e5b-fb288fc127c0 "C:UsersSamiDesktop"

Two things were new here:

  • ts = Take Snapshot. As the name suggests, this takes a full snapshot of the Root files/Directory structure as specified.
  • <Computer Path> = This simply tells the Isolated Storage Explorer to copy emulator/device Isolated Storage on to the specified local machine folder.

This copies over all contents from the Root of the specified Isolated Storage onto a folder called “IsolatedStore” in the specified computer/desktop path; this folder & its contents will be overwritten if they pre-exist. The same command can copy out only the contents of a SubDirectory from Isolated Storage, if a path is specified. If your application is targeting the Windows Phone 7.1 runtime, the copied out “IsolatedStore” directory will have a folder called “Shared” – this houses the application-specific Background Transfer data & ShellContent for secondary Live Tiles. Here’s how it could look if copied to desktop:

Transfers-out-of-Emulator[1]

Now, how about replacing files/directories back from your computer into Isolated Storage? This is great for testing, as it allows you to inspect how your application handles changes in file content, as well as Directory structure changes. Here’s the command:

ISETool.exe rs xd 04ce6350-faf0-4977-8e5b-fb288fc127c0 "C:UsersSamiDesktopIsolatedStore"

What’s new?

  • rs = Restore Snapshot. As the name suggests, this replaces the files & directories in Isolated Storage on the emulator/device with corresponding directory contents from your computer. Notice that to put back the changed contents from what we pulled out from the Isolated Storage, we had to add the “IsolatedStore” to our computer source path.

Watch us add a random text file to the “IsolatedStore”, which corresponds to the root of the Isolated Storage:

File Additions

After running the command through the Isolated Storage Explorer, the file magically (ok, not really) shows up in the concerned Isolated Storage. And sure enough, our Windows Phone application picks up the changes in directory structure, along with the correct file content on the next run:

File Transfer into EmulatorFile Contents from Desktop

And you can see the output of all the commands we ran so far, just so you know exactly what to expect:

ISE Command Runs

Summary

That’s it! Now, no matter what files/folders you are storing in Isolated Storage for your Windows Phone applications, you can now use the Isolated Storage Explorer to be totally confident in the directory structure & file contents of your persistent storage. Let’s go build some Windows Phone applications now, shall we?

Adios!

To download an entire working Windows Phone application that you can use with the Isolated Storage Explorer, click on the Download Code button below:

download[1]

Tomorrow, we we look at another awesome topic, using Windows Azure to support your Windows Phone application.  See you then!

toolsbutton[1]

31 Days of Mango | Day #15: The Progress Bar

Day15-ProgressBarDay14-ODataDay13-MarketplaceTestKitDay12-BetaTestingDay11-LiveTiles

This article is Day #15 in a series called 31 Days of Mango, and was written by guest author Doug Mair.  Doug can be reached on Twitter at @doug_mair or at doug.mair@gmail.com.

Any time you have a operation that will take more than a couple of seconds to complete, it’s a good idea to let your users know that your application is still making progress and is not just stuck in a loop forever.

How should you let your user know what’s happening? Well you can show them a text message with actual numbers of bytes downloaded or seconds left for an operation. But often the user is not concerned with that level of detail. Progress Bars are a great way to show your users that an operation is making progress, without overloading them with too many details. A progress bar can show this information in two different ways: indeterminate and indeterminate mode.

Indeterminate Mode

Indeterminate mode is when you cannot determine how long the operation will take to complete. For example if you are connecting to a server, you do not know when or if the operation will complete. Indeterminate mode Progress Bars show their status by having dots move from left to right in an animation. This is great for showing that something is happening, but it doesn’t give the user an indication of the amount of progress. To make a progress bar show the indeterminate mode, check the IsIndeterminate checkbox of the progress bar.

image_thumb5

Determinate Mode

If you can determine approximately how long an operation takes to complete, use the Determinate mode to let the user know the percentage that is left in the operation. For example, if you are downloading a file, you know the total file size, and as the download continues, you get updates that tell you the number of bytes downloaded so far.

You can then divide the Total File Size by the Current Bytes Downloaded to get the percentage completion. This code shows an example of how this could be done.

progressBar1.Value = (float)bytesRead / TotalFileSize;

If you want to show your user the percent of progress an operation is taking, you display that information in a Progress Bar. To show the Determinate mode, uncheck the IsIndeterminate checkbox and set the value to correlate to the percentage completion.

image_thumb4

With Windows Phone 7.5 Mango, you have 2 choices for Progress Bars:

1) Progress Bar – A control that you can put anywhere in your Silverlight page.

2) Progress Indicator – A control which lives in the System Tray.

Let’s start off by talking about the ProgressBar.

Progress Bar

The Progress Bar is not in the Visual Studio ToolBox by default, so, you will have to add it to the ToolBox in Visual Studio. To do this, Right-Mouse Click on the ToolBox and select “Choose Items…”. Then find “ProgressBar” and check its checkbox.

clip_image002_thumb2 clip_image004_thumb3

After a short delay, you will see the “ProgressBar” in the toolbox

image_thumb8

Now you can drag the ProgressBar control into your XAML Windows Phone pages. This will add code like the following to your XAML file.

<ProgressBar Name="progressBar1" Value="0"></ProgressBar>

You can then set the properties of the control as needed:

clip_image006_thumb1

To use this new ProgressBar in Determinate mode, make sure the IsIndeterminate checkbox is unchecked. Then you can set the Minimum, Maximum and Value properties of the control.

In the code behind as your operation is making progress, you change the Value property to the desired value between Minimum and Maximum. The Progress Bar will make sure the Value is always between the Minimum and the Maximum. You can fill up the Progress Bar simply by increasing the Value based on the progress of your operation.

Progress Indicator

Another way to show progress is by using a Progress Indicator. This control appears in the SystemTray as shown below. Here are pictures of the SystemTray and the Progress Indicator in the SystemTray.clip_image008_thumb2

clip_image010_thumb4

To use the Progress Indicator you must add this using statement to your code behind.

using Microsoft.Phone.Shell;

You can then create a Progress Indicator and add it to the System Tray like this.

ProgressIndicator progressIndicator = new ProgressIndicator() {

IsVisible = true, IsIndeterminate = false,

Text = "Downloading file …" };

SystemTray.SetProgressIndicator(this, progressIndicator);

Note that the Value property of the Progress Indicator must always be between 0 and 1. Where as for the ProgressBar, the Value property must be between the Minimum and the Maximum.

Sample App

Day15-ProgressBar is an App which tests both of these controls:

clip_image012_thumb2

Here’s an overview of the code:

The XAML code below show all of the controls we will use in our test. There is a ProgressBar to show the progress, a couple of buttons to Start or Stop a timer for the simulation, and a Slider which will be used to change the ProgressIndicator’s Value.

<phone:PhoneApplicationPage

x:Class="Day15_ProgressBar.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;

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&quot;

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;

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">

<!–LayoutRoot is the root grid where all 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="DAY 15" Style="{StaticResource PhoneTextNormalStyle}"/>

<TextBlock x:Name="PageTitle" Text="making progress" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" FontSize="56" />

</StackPanel>

<!–ContentPanel – place additional content here–>

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

<StackPanel>

<TextBlock FontSize="36" Foreground="#FFDBE6FF" Name="textBlock3" Text="Progress Indicator" TextWrapping="Wrap" FontWeight="Bold" />

<TextBlock Name="textBlock1" Text="Drag the Slider to see the Progess In System Tray." FontSize="20" TextWrapping="Wrap" Foreground="#FFDBE6FF" />

<Slider Name="slider1" Width="Auto" Maximum="1" LargeChange="0.1" SmallChange="0.01" ValueChanged="slider1_ValueChanged" Margin="50,0" />

<TextBlock FontSize="36" Name="textBlock2" Text="Progress Bar" TextWrapping="Wrap" Foreground="#FFDBE6FF" Margin="0,50,0,0" FontWeight="Bold" />

<Button Name="StartButton" Content="Start the Timer" Click="Button_Click"></Button>

<Button Name ="StopButton" Content="Reset the Timer" Click="Button_Click_1"></Button>

<ProgressBar Name="progressBar1" Value="0" IsIndeterminate="False" Height="50" SmallChange=".5" LargeChange="10"></ProgressBar>

</StackPanel>

</Grid>

</Grid>

</phone:PhoneApplicationPage>

Here’s the C# Code Behind. Let’s walk through what the code to see what it is doing.

To begin with, we set up the using statements for the controls we are going to use. For the ProgressIndicator, we must use Microsoft.Windows.Shell. Then inside the class that was created for the page, we declare our global variables. A ProgressIndicator is created which we will later put into the SystemTray. A DispatcherTimer called timer is created which will be used to simulate a deterministic operation. In the constructor we put the newly created progressIndicator into the SystemTray and setup the timer to call the timer_Tick method every 5000 ticks.

using System;

using System.Windows;

using System.Windows.Threading;

using Microsoft.Phone.Shell;

namespace Day15_ProgressBar

{

public partial class MainPage

{

ProgressIndicator progressIndicator = new ProgressIndicator() { IsVisible = true, IsIndeterminate = false, Text = "Downloading file …" };

DispatcherTimer timer = new DispatcherTimer();

// Constructor

public MainPage()

{

InitializeComponent();

SystemTray.SetProgressIndicator(this, progressIndicator);

timer = new DispatcherTimer {Interval = new TimeSpan(5000)};

timer.Tick += timer_Tick;

timer.Stop();

}

}

}

When the timer fires and the timer_Tick method is called, we add a value to the ProgressBar’s Value Property to make it seem like the operation is making progress.

Below that method is the Button Click event handlers. They start and stop the timer, so that we can simulate a long operation deterministic operation. Pressing the Start button resets the progressBar1’s Value property to 0 and starts the timer. Pressing the Stop button also resets the progressBar1’s Value property to 0, but this time we Stop the timer from progressing.

The last method in the class is the event handler slider1_ValueChanged that is called when the slider is moved. We will use the slider value to position the ProgressIndicator.

using System;

using System.Windows;

using System.Windows.Threading;

using Microsoft.Phone.Shell;

namespace Day15_ProgressBar

{

public partial class MainPage

{

// Constructor

ProgressIndicator progressIndicator = new ProgressIndicator() { IsVisible = true, IsIndeterminate = false, Text = "Downloading file …" };

DispatcherTimer timer = new DispatcherTimer();

public MainPage()

{

InitializeComponent();

SystemTray.SetProgressIndicator(this, progressIndicator);

timer = new DispatcherTimer {Interval = new TimeSpan(5000)};

timer.Tick += timer_Tick;

timer.Stop();

}

void timer_Tick(object sender, EventArgs e)

{

progressBar1.Value += progressBar1.SmallChange;

}

private void Button_Click(object sender, RoutedEventArgs e)

{

progressBar1.Value = 0;

timer.Start();

}

private void Button_Click_1(object sender, RoutedEventArgs e)

{

timer.Stop();

progressBar1.Value = 0;

}

private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

{

progressIndicator.Value = e.NewValue;

}

}

}

Summary

So that’s how you use Progress Bars in a Windows Phone Mango application. In a few lines of code you can quickly add full featured Progress Bars to any Silver Light Windows Phone application. You can create a ProgressIndicator which you can put into the SystemTray, or you can create a ProgressBar which you can display anywhere on the applications page.

Progress Bars are essential to applications with lengthy operations. It gives the user a feeling that the application is working for them and will be back to them when the operation completes. Your users will feel better about waiting when they are informed on the applications progress.

To download a working version of the application that was built in this article, please click the Download Code button below:

download

Tomorrow, we are going to look at the Isolated Storage Explorer, which allows you to look at and inspect the data you have stored in Isolated Storage in your emulator.  Samidip Basu will show you the hows and whys of this new feature.  See you then!

toolsbutton

31 Days of Mango | Day #14: Using OData

Day14-ODataDay13-MarketplaceTestKitDay12-BetaTestingDay11-LiveTilesDay10-NetworkInformation

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.

clip_image002

We will have created a project with the initial project set as shown below.

clip_image003

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.

clip_image004

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/

clip_image006

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.

clip_image008

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 System.Data.Services.Client;
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 TeamFranchise _selectedTeam;
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.

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    _context = new BaseballStatsEntities(new Uri("http://www.baseball-stats.info/OData/baseballstats.svc/&quot;));

    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

private void Teams_Loaded(object sender, LoadCompletedEventArgs e)
{
    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;
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.

DataContext = App.ViewModel;
 

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.

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

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.

<phone:PhoneApplicationPage
   x:Class="_31DaysMangoOData.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
   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&quot;
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
   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.

clip_image010

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 System.Windows.Navigation;
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.

<phone:PhoneApplicationPage
   x:Class="_31DaysMangoOData.DetailsPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
   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&quot;
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
   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!!).

clip_image012

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.

download

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!

toolsbutton

31 Days of Mango | Day #13: Marketplace Test Kit

Day13-MarketplaceTestKit_thumb1Day12-BetaTesting_thumb1Day11-LiveTiles_thumb1Day10-NetworkInformation_thumb1Day9-CalendarAPI_thumb1

This article is Day #13 in a series called 31 Days of Mango, and was written by guest author Dave Bost. Dave can be reached on Twitter at @davebost.


The final journey for your Windows Phone application development is to publish your app in the Marketplace to share with Windows Phone users across the globe. The Marketplace submission process is pretty straight forward, however there are requirements that each and every app must pass before being certified in the Marketplace. The process for certifying your app could take days if not weeks depending on how many requirements your app may fail during certification testing. The time to certification can be cut down substantially with the new Marketplace Test Kit included in the Windows Phone SDK 7.1.

Note: Every Windows Phone developer needs to understand the Application Submission Requirements.

Running your Windows Phone project through the Marketplace Test Kit helps you determine if your application is ready for submission into the Windows Phone Marketplace. Prior to the availability of the Marketplace Test Kit, developers would upload their app to the Marketplace and they would have to wait for a period of time before they found out if their app passed certification or not. Sometimes these submission errors were as simple as submitting the debug version of your application package or a little more complicated such as not handling Tombstoning properly. With the Marketplace Test Kit, the developer is able to run through a battery of tests to determine if their application is ready for certification prior to going through the Marketplace submission process. This saves the developer a tremendous amount of time and gets your application in the Marketplace faster.

The Marketplace Test Kit is included in the Windows Phone SDK 7.1. You can access the Marketplace Test Kit by opening your application solution in Visual Studio, highlighting the project you’d like to test and opening the Marketplace Test Kit from the Project menu.

OpenTestKit

The Marketplace Test Kit is kept updated by the changing certification requirements of the Windows Phone Marketplace. If an update is available, you will be prompted to apply the update when you open the Marketplace Test Kit. The test kit is applied on a per Project basis. If you have multiple Windows Phone application projects in your solution, you will need to run the test kit for each Project.

TestKit1

Once the Marketplace Test Kit is open, you will see a series of tabs that define application details and multiple test categories. These include: Application Details, Automated Tests, Monitored Tests, and Manual Tests.

The Application Details tab contains details such as the path to your application package (location of your .XAP file) as well as images that represent your application in the Marketplace (Marketplace tile and Screenshots) and on the phone (Large application tile and Small application tile). Note that the path to your application package must be the path to the Release build of your project. There is an MSDN How-To article that outlines the process for creating a proper Release build for your Windows Phone project.

TestKit2

One of the tests in the Automated Tests tab is to verify the required image sizes for your tile images and screenshots. A trivial way to create the appropriately sized screen shots is to use the new screenshot tool found within the Windows Phone SDK 7.1 Emulator.

The Automated Tests tab evaluates application size requirements, capabilities required by your application, and the iconography and screenshot requirements. Press the ‘Run Tests’ button to run the tests.

TestKit3

Once the automated tests are complete, you will see whether or not your tests passed or failed in the Result column. You may also receive additional information regarding the test to help resolve any failures or helpful information such as the capabilities used within your application. You can use the results of the Capability Validation test to define the necessary capabilities required by your application in the WMAppManifest.xml file.

TestKit4

The Monitored Tests tab lists a series of tests to analyze the performance and reliability of your application. When you press the ‘Start Application’ button, your application is deployed to your target device. From there you are to run through your apps functionality while the Monitored Tests analyze the various test cases.

TestKit6

When you are finished running through the functionality, you can press the ‘Close Application’ button. The Result Details column lists various messages based on the test outcome. It is advisable to set the target device to Windows Phone Device and not the Windows Phone Emulator. Performance on the emulator does not represent the actual performance of a physical Windows Phone device. Running the tests on a device will provide the most accurate results for these tests.

TestKit7

The Manual Tests tab list a number of test cases that require you to manually run through on your application. Once again, these tests should be performed on an actual device and not the emulator. These test cases help verify the various scenarios that can’t be tested with automated tools or scripts. These include tests such as:

  • Application Responsiveness during and after various conditions
  • Content and Theme changes – is your app legible in both dark and light themes?
  • Toast Notification Opt-In
  • Volume Control Commands for apps that use Audio
  • Proper handling of Background Transfers

TestKit8

For each test, follow the steps outlined in the Test Description column. Based on your findings, set the Result to either Passed or Failed. Your application may not implement features required by each test condition. For these particular tests you can set the Result to Pass to maintain consistency across your test cases. However, be sure that your app doesn’t implement any of the features of those passed over test cases. You don’t want to risk failing certification because of a shortcut taken in your testing procedures.

Once your Project passes all of the tests in the Marketplace Test Kit, you can be assured that your app is primed and ready to accelerate through the certification process.

Summary

The Marketplace Test Kit saves the developer substantial time in getting their app published. With the thoroughness of the automated, monitored and manual tests, Windows Phone developers can expect an expedient process to getting their apps in the Marketplace and serving Windows Phone customers from across the world.

Tomorrow, we are going to dive into OData, and how we can consume OData feeds in our applications.  It is written by guest author Chris Woodruff.  See you then!

toolsbutton