DataBinding a single HubTile
A little while ago I did a series of posts showing you how to build a Metro-styled start page for our app using HubTile controls from the Windows Phone Toolkit. In those examples, we built up the structure of the page in XAML alone. This approach may be fine for a static list of items, such as a page used for navigating to other pages in your app, but it becomes problematic when we want to display dynamic data, such as values created by the user.
To add data binding to the single HubTile control we created in this earlier post, we need to make a few changes to the XAML and C# code-behind. The first step is to create a new class representing the object we want to bind to the HubTile. In this example we'll just create a simple object HubTileItem with the required properties, but this could be any relevant business object you want to display.
public class HubTileItem
{
public string Title { get; set; }
public string Message { get; set; }
public Uri ImageUri { get; set; }
}
If you're familiar with XAML DataBinding in general, the changes to our HubTile control will be pretty straightforward. Basically we want to replace the hardcoded values for Title, Message and Source with their appropriate properties from their DataSource. We'll also give our HubTile a name, so that we can refer to it from our C# code-behind.
<toolkit:HubTile Margin="12,12,0,0" x:Name="MyHubTile"
Title="{Binding Title}"
Message="{Binding Message}"
Source="{Binding ImageUri}">
</toolkit:HubTile>
The last step is to create a new object of type HubTileItem and assign it as the DataContext for the HubTile control. To do this, we'll override the OnNavigatedTo event and include the code to create and assign the HubTileItem.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
HubTileItem item = new HubTileItem
{
Title = "Sam",
Message = "Yosemite Sam",
ImageUri = new Uri("/Assets/Images/Yosemite-Sam.jpg", UriKind.Relative)
};
MyHubTile.DataContext = item;
base.OnNavigatedTo(e);
}
Run your app and your newly data bound HubTile should display exactly the same as it did before.
Adding multiple parameters to JSON-enabled WCF service
One thing you’ll be tempted to do when you first start creating web services is to add multiple parameters to your web methods. Don’t, it will soon trip you up, especially if any of those parameters are complex types.
A better way to handle calling WCF web services that return JSON formatted results is to use Request and Response objects. Your WCF service accepts a single complex Request object that contains all the values it needs to perform its job. The results, along with any exceptions and messages are passed back to the caller in the Response object.
In this post, we’ll modify our GetPlaces method to use Request and Response objects instead of querystring parameters.
First add the following PlaceRequest and PlaceResponse classes.
Code Snippet
- public class PlaceRequest
- {
- public string Country { get; set; }
- public int MaxPopulation { get; set; }
- }
Code Snippet
- public class PlaceResponse
- {
- public bool HasError { get; set; }
- public string Status { get; set; }
- public List<Place> Results { get; set; }
- }
Then modify the GetPlace method as follows.
Code Snippet
- [OperationContract]
- [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
- public PlaceResponse GetPlaces(PlaceRequest Request)
- {
- // instantiate our response object
- PlaceResponse response = new PlaceResponse();
-
- // create a list of places
- List<Place> places = new List<Place>();
-
- places.Add(new Place {
- Name = "London",
- Country = "UK",
- Population = 7825200
- });
-
- places.Add(new Place {
- Name = "New York",
- Country = "USA",
- Population = 8175133
- });
-
- places.Add(new Place
- {
- Name = "Los Angeles",
- Country = "USA",
- Population = 3862839
- });
-
- places.Add(new Place {
- Name = "Sydney",
- Country = "Australia",
- Population = 4391674
- });
-
- var results = from p in places
- where p.Country == Request.Country && p.Population < Request.MaxPopulation
- select p;
-
- response.Results = results.ToList();
-
- // return the response
- return response;
- }
You’ll notice that we’ve changed the WebGet tag to WebInvoke. This declaration enables us to use the HTTP POST verb, instead of GET, since the calling function will be sending a complex object in the request body, not just a parameter in the URL.
To test this web service you’ll need to create a POST HTTP request. The easiest way to do this is use a tool such as Fiddler to create the request.
Add Content-Type:application/json to the request header and specify the body as such:
{"Request":{"Country":"USA", "MaxPopulation":"4000000"}}
You’ll notice the values for Country and MaxPopulation are enclosed in an object called Request. This is because the web method parameter is called Request.
If everything goes well, Fiddler will report a response that includes the following JSON.
{"HasError":false,"Results":[{"Country":"USA","Name":"Los Angeles","Population":3862839}],"Status":null}
Adding Parameters to JSON-enabled WCF service
In the previous post we looked at how you configure a WCF web service to return JSON-formatted results. In this post we’ll look at how you can handle methods that need parameters.
We’re going to add a parameter to our GetPlaces method that will be used to filter the results based on the Country property.
Code Snippet
- [OperationContract]
- [WebGet(ResponseFormat = WebMessageFormat.Json)]
- public List<Place> GetPlaces(string Country)
- {
- // create a list of places
- List<Place> places = new List<Place>();
-
- places.Add(new Place {
- Name = "London",
- Country = "UK",
- Population = 7825200
- });
-
- places.Add(new Place {
- Name = "New York",
- Country = "USA",
- Population = 8175133
- });
-
- places.Add(new Place {
- Name = "Sydney",
- Country = "Australia",
- Population = 4391674
- });
-
- var results = from p in places
- where p.Country == Country
- select p;
-
- // return the list of places
- return results.ToList();
- }
- }
Run the web service in the browser and add the parameter name and value as below:
http://localhost:57128/PlaceService.svc/GetPlaces?Country=USA
The response will be the filtered array of Place objects matching the parameter (New York).
[{"Country":"USA","Name":"New York","Population":8175133}]
Return JSON from WCF Web Service
JSON is a popular, light-weight method of formatting data destined for mobile devices. Many popular APIs default to JSON formatted responses, so it’s a good practice to follow.
If you are retrofitting existing WCF web services to return JSON it easy on paper, but can be painful in practice. In this post, we’ll add a JSON friendly WCF service to an existing web application and point out some of the gotchas that can make your life hell.
Firstly, add an AJAX enabled web service to your ASP.NET web application. Visual Studio will add a default web method and make a number of changes to your web config. We’re going to create a method the will return some information about places around the world, so call the web service PlaceService.
Create a simple class to represent the places, this should be in a separate cs file, but for ease of use, I’m adding it after the closing tag of our PlaceService.
Code Snippet
- public class Place
- {
- public string Name { get; set; }
- public string Country { get; set; }
- public int Population { get; set; }
- }
Delete the existing DoWork method and replace it with this code GetPlaces.
Code Snippet
- [ServiceContract(Namespace = "")]
- [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
- public class PlaceService
- {
- [OperationContract]
- [WebGet(ResponseFormat = WebMessageFormat.Json)]
- public List<Place> GetPlaces()
- {
- // create a list of places
- List<Place> places = new List<Place>();
-
- places.Add(new Place {
- Name = "London",
- Country = "UK",
- Population = 7825200
- });
-
- places.Add(new Place {
- Name = "New York",
- Country = "USA",
- Population = 8175133
- });
-
- places.Add(new Place {
- Name = "Sydney",
- Country = "Australia",
- Population = 4391674
- });
-
-
- // return the list of places
- return places;
- }
-
- }
View the page in the web browser and add the name of the method to the URL like so: http://localhost:57128/PlaceService.svc/GetPlaces
Depending on what browser you use, you’ll either be prompted to download the JSON document, or the browser will display the JSON directly.
{"d":[{"__type":"Place:#FileAccess.Web","Country":"UK","Name":"London","Population":7825200},{"__type":"Place:#FileAccess.Web","Country":"USA","Name":"New York","Population":8175133},{"__type":"Place:#FileAccess.Web","Country":"Australia","Name":"Sydney","Population":4391674}]}
Getting rid of the “d”
Our results are certainly JSON formatted but you’ll notice that they are wrapped in an object called “d”, short for data. Ideally, we want to get rid of the d and simply return an array of Place objects. This is done by modifying the web.config.
Sometimes it seems like configuring your web.config is a dark art, known only to a few souls. Thankfully, this change if pretty easy as all we need to do if modify the endpoint behaviour Visual Studio created for us. Look for the PlaceServiceAspNetAjaxBehavior and insert a tag to enable web HTTP. The modified config will look something like this (your namespace will be different).
Code Snippet
- <endpointBehaviors>
- <behavior name="MyWebApplication.PlaceServiceAspNetAjaxBehavior">
- <enableWebScript />
- <webHttp />
- </behavior>
- </endpointBehaviors>
Run the web service again and this time we’ll have a better response.
[{"Country":"UK","Name":"London","Population":7825200},{"Country":"USA","Name":"New York","Population":8175133},{"Country":"Australia","Name":"Sydney","Population":4391674}]
New size options for HubTiles
One of the main visual changes to the start screen in Windows Phone 7.8 and 8 is that it now supports three sizes of live tiles. The default (medium) tile is now larger and gone is the black strip to the right that used to be home to an arrow that took you to the app screen.
This milestone seemed like a good time to revisit the HubTile control from the Windows Phone Toolkit to see if it could support these new formats. The early versions on the HubTile is was a painful process to choose the size of the tile, having to resort to copying and modifying the XAML elements and animations, as discussed in the post.
Thankfully, since the September 2012 release of the Windows Phone Toolkit, the HubTile control now comes with a Size property that allows you to select from four sizes, Default, Small, Medium and Large. A look at the source code for this release reveals the size of each of these tiles.
switch (hubTile.Size)
{
case TileSize.Default:
hubTile.Width = 173;
hubTile.Height = 173;
break;
case TileSize.Small:
hubTile.Width = 99;
hubTile.Height = 99;
break;
case TileSize.Medium:
hubTile.Width = 210;
hubTile.Height = 210;
break;
case TileSize.Large:
hubTile.Width = 432;
hubTile.Height = 210;
break;
}
Using these new sizes with the data bound HubTile example we used in another post is simple. The easiest option is to specify the Size property in the ItemTemplate.
<ListBox Grid.Row="0" Name="AvatarListView">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:HubTile
Margin="12,12,0,0"
Title="{Binding Title}"
Message="{Binding Message}"
Source="{Binding ImageUri}"
Size="Medium"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Of course, you can also bind the Size property to property on your underlying data source. To do this, add a TileSize property to the HubTileItem class and modify the HubTile control XAML as below.
public class HubTileItem
{
public string Title { get; set; }
public string Message { get; set; }
public Uri ImageUri { get; set; }
public TileSize TileSize {get; set;}
}
<toolkit:HubTile
Margin="12,12,0,0"
Title="{Binding Title}"
Message="{Binding Message}"
Source="{Binding ImageUri}"
Size="{Binding TileSize}"/>