- Add a new Services folder.
- Add a new interface,
IGroupService, within the Services folder. The following is the code forIGroupService: - Create a subfolder named Implementation within the Services folder.
- Add a new class,
GroupService, within Services\Implementation. The following code forGroupService, that implements the previously created interface:
namespace DrDobbsApp.Services
{
using System.Collections.Generic;
using System.Threading.Tasks;
//// DrDobbsApp.Entities (Portable Class Library)
using Entities;
public interface IGroupService
{
Task<IEnumerable<Group>> GetAll();
}
}
namespace DrDobbsApp.Services.Implementation
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
//// DrDobbsApp.Entities (Portable Class Library)
using Entities;
public class GroupService : IGroupService
{
private const string RestServiceUrl = "http://localhost:46037/api/";
public async Task<IEnumerable<Group>> GetAll()
{
var client = new HttpClient
{
BaseAddress = new Uri(RestServiceUrl)
};
//// I want to add and accept a header for JSON
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue(
"application/json"));
//// Retrieve all the groups with their items
var response = await client.GetAsync("groups");
//// Throw an exception if something went wrong
response.EnsureSuccessStatusCode();
//// In case you need date and time properties
const string dateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
var jsonSerializerSettings = new DataContractJsonSerializerSettings
{
DateTimeFormat = new DateTimeFormat(dateTimeFormat)
};
var jsonSerializer = new DataContractJsonSerializer(
typeof(Group[]),
jsonSerializerSettings);
var stream = await response.Content.ReadAsStreamAsync();
return (Group[]) jsonSerializer.ReadObject(stream);
}
}
}
The GroupService class has a GetAll method that uses the async modifier and returns a Task<IEnumerable<Group>>. As you already know, you can call this method with the await keyword and you will be able to retrieve an IEnumerable<Group> without having to worry about the Task wrapper.
The GetAll method performs the following steps:
- Creates a new instance of the System.Net.Http.HttpClient class and sets its base address to the ASP.NET Web API rest service URL.
- Adds and accepts a header for JSON for the HttpClient instance.
- Makes the REST call to
GET api/groupswith an asynchronous execution by using theHttpClient.GetAsyncmethod. The method returns aTask<HttpResponseMessage>that theawaitkeyword unwraps into anHttpResponseMessage. - Calls the
EnsureSuccessStatusCodefor theHttpResponseMessageinstance. This method throws an exception if something goes wrong. Of course, more exception handling code should be added. However, I'm trying to keep things simple. - Creates a new instance of
DataContractJsonSerializerSettingsand sets a value for theDateTimeFormatproperty. In this case, I haven't addedDateTimevalues. However, I've added the code so that you have a baseline to deserialize JSON with REST calls in Windows Store apps. - Creates a new
DataContractJsonSerializerinstance specifiying and array of Group as a type and the previously explained settings. - Calls the
ReadAsStreamAsyncmethod for theContentproperty of the response that is an instance ofHttpContent. The method returnsTask<System.IO.Stream>. The code makes the call with an asynchronous execution by using theawaitkeyword that unwraps the response into aSystem.IO.Streaminstance. - Finally, the code returns the results of the call to the
ReadObjectfor theDataContractJSonSerializerinstance that receives the previously readSystem.IO.Streamas a parameter. The code returns an array ofGroup(Group[]).
var client = new HttpClient
{
BaseAddress = new Uri(RestServiceUrl)
};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync("groups");
response.EnsureSuccessStatusCode();
const string dateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
var jsonSerializerSettings = new DataContractJsonSerializerSettings
{
DateTimeFormat = new DateTimeFormat(dateTimeFormat)
};
var jsonSerializer = new DataContractJsonSerializer(
typeof(Group[]),
jsonSerializerSettings);
var stream = await response.Content.ReadAsStreamAsync();
return (Group[]) jsonSerializer.ReadObject(stream);</p>
It is necessary to make sure that the App manifest provides outbond access to the Internet to consume the REST service with HttpClient. If you double click on Package.appxmanifest and then select the Capabilities page, Visual Studio displays the different capabilities that determine the features and devices that your app can use. In this case, it is necessary to make sure Internet (Client) is checked (see Figure 10).
Figure 10: The system features or devices that the app can use with Internet (Client) as the only capability enabled for the app.
Making Asynchronous Calls to Replace the Sample Data Source
Now, I just need to replace the sample data source included in the Grid app template with the results of calling the previously created GetAll method that retrieves all the groups and their items from the REST service. The code isn't going to represent best practices because I'm reusing many existing pieces from the Grid app template and keeping most of the code of the SampleDataSource class, which is not the perfect implementation of a data source. However, you will be able to understand the idea and use them in your better designed Model-View-ViewModel Windows Store app. The following steps enable you to display the results of the GetAll method in the different views (also known as pages) of the Grid app template:
- Remove all the code in the
SampleDataSourceconstructor. The constructor was adding the sample groups and items. - Add a new static asynchronous method to the
SampleDataSourceclass (RefreshDataSource): - Go to
Appclass (App.xaml.cs) and change the code for theOnLaunchedevent handler so that it calls the previously createdSampleDataSource.RefreshDataSourcewith an asynchronous execution when the app's previous state isApplicationExecutionState.NotRunning.
public static async Task RefreshDataSource()
{
var groupService = new GroupService();
var allGroups = await groupService.GetAll();
foreach (var group in allGroups)
{
var sampleDataGroup = new SampleDataGroup(
group.Id,
group.Title,
group.Subtitle,
"Assets/LightGray.png",
group.Description);
_sampleDataSource.AllGroups.Add(sampleDataGroup);
if (group.Items != null)
{
foreach (var item in group.Items)
{
sampleDataGroup.Items.Add(new SampleDataItem(
item.Id,
item.Title,
item.Subtitle,
"Assets/LightGray.png",
item.Description,
item.Content,
sampleDataGroup));
}
}
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context, and navigate to first page
rootFrame = new Frame();
//Associate the frame with a SuspensionManager key
SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
// Restore the saved session state only when appropriate
try
{
await SuspensionManager.RestoreAsync();
}
catch (SuspensionManagerException)
{
//Something went wrong restoring state.
//Assume there is no state and continue
}
}
else if (args.PreviousExecutionState == ApplicationExecutionState.NotRunning)
{
//// Retrieve all the groups by making a call to the REST service
try
{
await Data.SampleDataSource.RefreshDataSource();
}
catch (Exception ex)
{
////Something went wrong refreshing the data source
////It is obviously necessary to add more code here :)
}
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
{
throw new Exception("Failed to create initial page");
}
}
// Ensure the current window is active
Window.Current.Activate();
}
The SampleDataSource.RefreshDataSource method calls the GroupService.GetAll method with an asynchronous execution by using the await keyword. When GetAll returns all the groups with their items, the code creates the SampleDataGroup and SampleDataItem instances with the same images used in the original app template. I've reused SampleDataGroup and SampleDataItem to take advantage of the entire infrastructure provided the Grid app template and focus on just a few specific topics while providing a complete example. Of course, there is a clear need for a better exception handling in this method and in the rest of the app.
In addition, it would be necessary to take advantage of caching mechanisms and querying mechanisms to avoid retrieving the entire content each time the app is launched. In this case, I'm using the images provided by the Grid template to keep the example simple. However, in an app like this one, you might like to cache some images that won't change all the time, such as the image used for Dr. Dobb's Cloud section (a group in the app).
As explained in the previous article, the OnLaunched event handler method uses the async modifier and therefore is ready to make calls with the await keyword. The new code executes SampleDataSource.RefreshDataSource with an asynchronous execution when the previous state for the app is ApplicationExecutionState.NotRunning and leaves the data source ready to be displayed by the different pages.
If you run the application step-by-step, you will be able to understand the flow and the different asynchronous executions that allow the app to display the data retrieved from the REST service. The first view displays the groups (Figure 11).
Figure 11: The app displaying the groups (GroupedItemsPage).
If you click on Cloud, the app will display the details for the group with the two items, which are the two articles (see Figure 12).
Figure 12: The app displaying the details for the Cloud group (GroupDetailsPage).
If you click on an item, the app will display its details (see Figure 13). The app uses all the navigation features of the Grid app template but retrieving the groups and items from a REST service.
Figure 13: The app displaying the details for one of the items (ItemDetailsPage).
You can also test the app with the simulator included in Visual Studio 2012. You just need to select Simulator in the drop down menu located in the Standard toolbar (see Figure 14).
Figure 14: Selecting the Simulator as the target for the app in Visual Studio 2012.
The simulator allows you to see how the app behaves in a device. You can rotate the simulator (see Figure 15), set different resolutions, configure a desired location and use some touch emulation with the mouse, among other features.

Figure 15: The Grid app template redefines the layout when the device is rotated.
Conclusion
I've provided a full example of how to develop and consume a REST service that provides data to a Windows Store app that uses the Grid app, developed with C# and XAML. This solution uses a Portable Class Library to share code between the REST service, developed with the new ASP.NET Web API, and the Windows Store app. The app makes asynchronous calls to follow the best practices for Windows Store apps. Now, you have what you need to start working with Windows 8 apps that require a data source.
Related Reading
Writing Your First Windows 8 App: The Lay of the Land
Customizing the Appearance of Windows 8 Apps
Gaston Hillar is a frequent contributor to Dr. Dobb's on the topic of Microsoft development.


