dotMobimobiThinkingmobiForgemobiReadyDeviceAtlasgoMobi
background image

Web Access in Windows Phone 7 Apps

Window with web
Posted by weimenglee - 07 Jan 2011
Twitter share icon Facebook share icon Google Plus share icon

In previous articles, I have shown how easy it is to get started in Windows Phone 7 programming using Visual Studio 2010 and the Windows Phone Developer Tools. In this third installment of the series, I will continue the exploration of Windows Phone development. This time I will focus on one key development topic: Web access. Unless you are writing a Hello World application, chances are that your application will need to connect to the outside world. And so in this article, I will show you the various ways in which your Windows Phone application can connect to the outside world to access Web resources. In addition, I will also show you how to consume XML and JSON Web services.

Using the WebClient Class to Download Web Resources

To get started, create a new Windows Phone Application project using Visual Studio 2010 and name it WebServices.

The first method to connect to the outside world is to use the WebClient class. The WebClient class provides an easy programmatic interface for sending and receiving data from a resource identified by URL.

In the MainPage.xaml file of the project, add in the following block of code to populate the main page:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 
<Button Content="Download" Height="72"
HorizontalAlignment="Left" Margin="6,6,0,0" 
                Name="btnDownload" VerticalAlignment="Top" Width="444" 
                Click="btnDownload_Click" />
<TextBlock Height="30" HorizontalAlignment="Left" 
                Margin="21,84,0,0" Name="txtStatus" Text="" 
VerticalAlignment="Top" Width="328" />
 
</Grid>

This creates the UI as shown in Figure 1.


Figure 1: Creating the UI for the application

In the MainPage.xaml.cs file, first create an instance of the WebClient class:

public partial class MainPage : PhoneApplicationPage
{
  WebClient client = new WebClient();

In the constructor for the page, wire up the event handlers for the two events associated with the WebClientobject - DownloadProgressChanged and DownloadStringCompleted:

public partial class MainPage : PhoneApplicationPage
{
  WebClient client = new WebClient();
 
  // Constructor
 
  publicMainPage()
  {
    InitializeComponent();
 
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
  }
}

When the WebClientobject is downloading resources, it will continuously fire the DownloadProgressChanged event, and so this is a good place to display the progress of the download. The second argument for the DownloadProgressChangedevent handler contains a UserState property, which allows you to identify the origin of the download (useful to identify the source of the download as you may be downloading several items at once). The BytesReceived property tells you the number of bytes downloaded so far. Code the DownloadProgressChanged event handler as follows:

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
  if (e.UserState as string == "mobiforge")
  {
    txtStatus.Text = e.BytesReceived.ToString() + " bytes received.";
  }
}

When the download is completed, the DownloadStringCompleted event will be fired. Here, the resource downloaded will be stored in the Result property:

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
  if (e.Error == null && !e.Cancelled) MessageBox.Show(e.Result);
}

You should check the Error and Cancelled property to ensure that there is no error and that the download has not been cancelled half-way through before using the result stored in the Result property.
Finally, double-click on the Download button and code the following:

private void btnDownload_Click(object sender, RoutedEventArgs e)
{
  client.DownloadStringAsync(new Uri("http://mobiforge.com/rssfeed"), "mobiforge");
}

The above downloads an RSS document from mobiForge's Web server. Press F5 to test the application on the Windows Phone 7 Emulator. When the application loads, click the Download button and observe the progress of the download. When the document is finally loaded, it is displayed in a message box (see Figure 2).


Figure 2: Displaying the progress of the download and the document downloaded

Note that in this example we are using the WebClient class to download a text resource using the DownloadStringAsync() method, which asynchronously downloads the text resource and fires the DownloadStringCompleted event when the download completes. Readers familiar with the .NET framework should note that the DownloadDataAsync() method (which downloads resources as a byte array) is missing from the WebClient class on the Windows Phone. So if you are trying to download binary data using the WebClientclass, you may run into problems. For that, you have to use the more versatile HttpWebRequest class.

Using the HttpWebRequestClass

Like the WebClient class, the HttpWebRequest class allows you to download resources using an URL. Unlike WebClient, it supports downloading of binary data, not just strings.

Using the same MainPage.xaml file, add the following element:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="Download" Height="72"
HorizontalAlignment="Left" Margin="6,6,0,0" 
                Name="btnDownload" VerticalAlignment="Top" Width="444" 
                Click="btnDownload_Click" />
<TextBlock Height="30" HorizontalAlignment="Left" 
                Margin="21,84,0,0" Name="txtStatus" Text="" 
VerticalAlignment="Top" Width="328" />
 
<phone:WebBrowserHorizontalAlignment="Left" Margin="7,133,0,0" 
                Name="webBrowser1" VerticalAlignment="Top" Height="478" 
                Width="443" />
 
</Grid>

This will add a Web browser control to the page (see Figure 3).


Figure 3: Adding a Web browser control to the page

In the MainPage.xaml.cs file, import the namespace for System.IO:

using System;
...
usingMicrosoft.Phone.Controls;
 
using System.IO;

Insert the following delegate and method so that you can update the Web browser's content in a thread-safe manner:

public partial class MainPage : PhoneApplicationPage
{
  WebClient client = new WebClient();
 
  //---delegate for updating the Web Broswer---
  delegate void delegateUpdateWebBrowser(string content);
  private void updateWebBrowser(string content)
  {
    webBrowser1.NavigateToString(content);
  }

Modify the btnDownload_Click event handler so that you will now use an HttpWebRequest object to asynchronously download the RSS feed document from MobiForge.com:

private void btnDownload_Click(object sender, RoutedEventArgs e)
{
  HttpWebRequesthttpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://mobiforge.com/rssfeed"));
  httpReq.BeginGetResponse(HTTPWebRequestCallBack, httpReq);
}

When the document is downloaded, it will fire the HTTPWebRequestCallBack method:

private void HTTPWebRequestCallBack(IAsyncResult result)
{
  HttpWebRequesthttpRequest = (HttpWebRequest)result.AsyncState;
  WebResponse response = httpRequest.EndGetResponse(result);
  Streamstream = response.GetResponseStream();
  StreamReader reader = new StreamReader(stream);
  this.Dispatcher.BeginInvoke(
    newdelegateUpdateWebBrowser(updateWebBrowser),
    new Object[] { reader.ReadToEnd() });
  }

Here, you will use a Stream object to download the document and then call the delegate to display the content using the Web browser control. Press F5 to test the application. Click the Download button and you will be able to see the content of the RSS feed displaying in the Web browser control after a while (see Figure 4).


Figure 4: Viewing the download content in the Web browser control

Using the HttpWebRequest class, you can also download binary data and access it through a Stream object.

Consuming ASMXWeb Services

The previous two sections showed how you can download resources from the Web. In this section, you will learn how to consume Web services in your Windows Phone application.
Using the same project, modify the MainPage.xaml file as follows:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="Download" Height="72"
HorizontalAlignment="Left" Margin="6,6,0,0" 
                Name="btnDownload" VerticalAlignment="Top" Width="444" 
                Click="btnDownload_Click" />
<TextBlock Height="30" HorizontalAlignment="Left" 
                Margin="21,84,0,0" Name="txtStatus" Text="" 
VerticalAlignment="Top" Width="328" />
 
<phone:WebBrowserHorizontalAlignment="Left" Margin="7,133,0,0" 
                Name="webBrowser1" VerticalAlignment="Top" Height="405" 
                Width="443" />
<Button Content="Dictionary Web Service" Height="72" 
HorizontalAlignment="Left" Margin="0,535,0,0" 
                Name="btnDictionary" VerticalAlignment="Top" Width="450" />
 
</Grid>

The above addition basically adjusted the height of the Web browser control as well as added a button (see Figure 5).


Figure 5: Adding a Button to the bottom of the page

For this example, we shall consume the Web service located at: http://services.aonaware.com/DictService/DictService.asmx. To consume a Web service, you first need to add a service reference in your project. Right-click on the References item in Solution Explorer and select Add Service Referenceā€¦ (see Figure 6).


Figure 6: Adding a service reference to the project

Enter the URL for the Web service (see Figure 7) and click Go. When the Web service is found, click OK. You shall use the default name of ServiceReference1 (or you can change it to a more intuitive name).


Figure 7: Entering the service name

Double-click on the Dictionary Web Service button and code the following:

private void btnDictionary_Click(object sender, RoutedEventArgs e)
{
  ServiceReference1.DictServiceSoapClient ws = new ServiceReference1.DictServiceSoapClient();
  ws.DefineCompleted += new EventHandler<ServiceReference1.DefineCompletedEventArgs>(ws_DefineCompleted);
  ws.DefineAsync("cool");
}

In the above, you will asynchronously connect to the Web service and call its DefineAsync() method to get the definition for the word "cool". When the result is returned back to your application, the ws_DefineCompleted() method is called:

void ws_DefineCompleted(object sender, ServiceReference1.DefineCompletedEventArgs e)
{
  for (int i = 0; i <e.Result.Definitions.Length; i++) MessageBox.Show(e.Result.Definitions[i].WordDefinition);
}

Here, you will display each of the definitions of the word that was returned (see Figure 8).


Figure 8: Displaying the result returned by the Web service

Accessing JSON Web Services

In the era of mobile applications, traditional XML Web services poses some serious problems - the heavy payload and the associated cost in transporting them over the network and parsing them on the client side make them clunky to work with. A much better approach is to develop JSON (Java Script Object Notation) Web services, which returns the result in a lightweight format. In this section, you will learn how to consume a JSON Web service in your Windows Phone application.
For the example, you shall use the JSON Web service located at http://www.geonames.org/export/JSON-webservices.html. An example call would look like this: http://ws.geonames.org/postalCodeLookupJSON?postalcode=6600&country=AT. This call returns a list of cities and placenames in the bounding box, ordered by relevancy (capital/population). The resultant JSON result looks like this (formatted for clarity):

{
    "postalcodes": [
        { 
            "adminCode3":"70828", 
            "adminName2":"Reutte",
            "adminName3":"Reutte",
            "adminCode2":"708",
            "postalcode":"6600",
            "adminCode1":"T",
            "countryCode":"AT",
            "lng":10.724044444,
            "placeName":"Ammerwald",
            "lat":47.490988888,
            "adminName1":"Tirol"
        },
        {
            "adminCode3":"70805",
            "adminName2":"Reutte", 
            "adminName3":"Breitenwang", 
            "adminCode2":"708",
            "postalcode":"6600",
            "adminCode1":"T",
            "countryCode":"AT",
            "lng":10.7050916669573,
            "placeName":"Bad Kreckelmoos",
            "lat":47.4900855904715,
            "adminName1":"Tirol"
        },
        ...
        ...
    ]
}

To consume this JSON Web service in your Windows Phone 7 application, you need to add a reference to the System.Servicemodel.Web.dll to your project (see Figure 9).


Figure 9: Adding a reference to the System.Servicemodel.Web.dll

Then, import the following namespaces in MainPage.xaml.cs:
usingSystem.Runtime.Serialization;
usingSystem.Runtime.Serialization.Json;

Add the following class definitions to the MainPage.xaml.cs file:

namespaceWebServices
{
    [DataContract]
    public class detailedAddress
    {
        [DataMember]
        public string adminCode1 { get; set; }
 
        [DataMember]
        public string adminCode2 { get; set; }
 
        [DataMember]
        public string adminCode3 { get; set; }
 
        [DataMember]
        public string adminName1 { get; set; }
 
        [DataMember]
        public string adminName2 { get; set; }
 
        [DataMember]
        public string adminName3 { get; set; }
 
        [DataMember]
        public string countryCode { get; set; }
 
        [DataMember]
        public string placeName { get; set; }
 
        [DataMember]
        public double lat { get; set; }
 
        [DataMember]
        public double lng { get; set; }
    }
 
    [DataContract]
    public class Postal_Result
    {
        [DataMember(Name = "postalcodes")]
        publicdetailedAddress[] addresses;
    }

The two classes - detailedAddress and Postal_Result will be used to map to the fields in the JSON result, by applying the DataMemberAttribute and DataContractAttribute attributes.
In the btnDownload_Click event handler, code the following:

private void btnDownload_Click(object sender, RoutedEventArgs e)
{
  WebClient client = new WebClient();
  client.OpenReadCompleted += new
  OpenReadCompletedEventHandler(client_OpenReadCompleted);
  client.OpenReadAsync(new
  Uri("http://ws.geonames.org/postalCodeLookupJSON?
  postalcode=6600&country=AT"), UriKind.Absolute);            
}

The above uses the WebClient class to asynchronously call the JSON Web service, passing the arguments of the Web service call through the URL. When the response is read from the Web service, the client_OpenReadCompleted method is fired:

void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
  varserializer = new DataContractJsonSerializer(typeof(Postal_Result));
  Postal_ResultipResult = (Postal_Result)serializer.ReadObject(e.Result);
 
  for (int i = 0; i <= ipResult.addresses.Length - 1; i++)
  {
    //---print out the place name---
    System.Diagnostics.Debug.WriteLine(ipResult.addresses[i].placeName.ToString());
 
    //---print out the latitude---
    System.Diagnostics.Debug.WriteLine(ipResult.addresses[i].lat.ToString());
 
    //---print out the longitude---
    System.Diagnostics.Debug.WriteLine(ipResult.addresses[i].lng.ToString());
    System.Diagnostics.Debug.WriteLine("----------");
  }
}

Here, you use the DataContractJsonSerializer() class's ReadObject() method to de-serialize the JSON string into an object of type Postal_Result. Once the result is de-serialized, you will loop through the result and print out the place name, latitude, and longitude.
Press F5 to test the application. Click the Download button to access the Web service. Observe the output printed in the Output window:

Ammerwald
47.490988888
10.724044444
----------
Ammerwaldalpe
47.490988888
10.724044444
----------
Bad Kreckelmoos
47.4900855904715
10.7050916669573
----------
Brandstatt
47.4900855904715
10.7050916669573
----------
Breitenwang
47.4833333
10.7333333
----------
Doktor Schwarzkopf-Siedlung
47.5002778
10.73
...
...

Detecting the Availability of Network

So far, all the previous sections talked about accessing Web resources, but all these would not be possible if the device has no Internet connection. As a developer, it is important that you programmatically check if the device has Internet access before you go ahead and access the Web resources.
To obtain the current network status, you can use the NetworkInterface class, which is defined in the Microsoft.Phone.Net.NetworkInformation namespace. Hence, you need to import it if you want to use it in your application:

using Microsoft.Phone.Net.NetworkInformation;

The following code snippet shows how you can detect if you have a network available using the GetIsNetworkAvailable() method and then print out the network type:

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
  varni = NetworkInterface.NetworkInterfaceType;
 
  System.Diagnostics.Debug.WriteLine("Network Available: " + 
  NetworkInterface.GetIsNetworkAvailable());
 
  if (ni == NetworkInterfaceType.Wireless80211) System.Diagnostics.Debug.WriteLine("Wireless");
  else if (ni == NetworkInterfaceType.None)
  System.Diagnostics.Debug.WriteLine("None");
}
 
<h2>Summary</h2>
So now you have the means to connect to the outside world and access the vast resources available. In general, when developing solutions for mobile devices, it is recommended that you use JSON as the data exchange medium. In the next article, I will talk about one very important control in Windows Phone 7 - the ListBox. Stay tuned!

Posted by weimenglee - 07 Jan 2011

weimenglee's picture

Wei-Meng Lee is a technologist and the founder of Developer Learning Solutions (http://www.learn2develop.net), a company focusing on hands-on training on the latest technology. Wei-Meng specializes in mobile technologies and has written several books on .NET, VB, C#, and .NET Compact Framework and is currently working on an Android book for Wrox. Contact Wei-Meng Lee at .

comments powered by Disqus