1. Problem
You want to build an application that interacts with a service by using Windows Communication Foundation (WCF).
2. Solution
You need to create your
service (or get the address of the service that you want to query), and
add a reference to that service so that you can query your methods and
access the exposed DataContract.
3. How It Works
Windows Phone was created to use
services, but you should always remember one thing when you work with
it: service access is always asynchronous, because (as with the use of
services in Silverlight for the Web) you must not freeze the user
interface.
The implementation of
Silverlight for Windows Phone provides a limited set of features to work
on a network, as compared to its "big brother." Only Windows
Communication Foundation, HttpWebRequest, and WebClient can be used with Silverlight for Windows Phone.
We will use web services,
connecting your application to the Microsoft Research Maps service,
which enables us to take pictures of the world (via satellite), based on
GeoCoordinate.
4. The Code
Unfortunately, you cannot dynamically create a proxy to a service by using the ChannelFactory<TChannel>
class, because it is not supported by this Windows Phone version.
Therefore, to create a reference, you must use the command-line tool slsvcutil.exe to create a proxy for use at compile time.
To complete the recipe, you need to see how to add a reference to the service (so that Visual Studio is running only the slsvcutil.exe utility to create a proxy), as in Figure 1.
As you learned in Recipe 6-3,
to use Bing Maps, you must be registered as a Windows Phone developer.
Therefore, you might prefer to use the map service offered by Microsoft
Research.
Then you need to add a reference, calling it TerraService, to the service available at http://msrmaps.com/TerraService2.asmx, as shown in Figure 2,. As you can see in the picture, there are many methods that you can call, but we are interested in GetAreaFromPt and GetTile.
At this point, you have a
reference to your service and you can call the method that interests
you. It's a best practice to instantiate the server proxy in the Loaded
event handler, thereby ensuring that the application starts as soon as
possible and that the user maintains control of the phone:
using Microsoft.Phone.Net.NetworkInformation;
...
TerraService.TerraServiceSoapClient proxy = null;
GeoCoordinateWatcher geoWatcher = null;
// Constructor
public MainPage()
{
InitializeComponent();
InizializeServices();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
...
private void InizializeServices()
{
proxy = new TerraService.TerraServiceSoapClient();
geoWatcher = new GeoCoordinateWatcher();
}
...
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
if (NetworkInterface.GetIsNetworkAvailable())
MessageBox.Show("No Network available. The application will not work fine");
proxy.GetAreaFromPtCompleted +=
new EventHandler<TerraService.GetAreaFromPtCompletedEventArgs>(
proxy_GetAreaFromPtCompleted);
proxy.GetTileCompleted +=
new EventHandler<TerraService.GetTileCompletedEventArgs>(
proxy_GetTileCompleted);
geoWatcher.PositionChanged +=
new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(
geoWatcher_PositionChanged);
geoWatcher.StatusChanged +=
new EventHandler<GeoPositionStatusChangedEventArgs>(
geoWatcher_StatusChanged);
geoWatcher.Start();
}
As you can see in this code, you subscribe an event handler to two events (GetAreaFromPtCompleted and GetTileCompleted)
because service calls are made asynchronously, because you are working
with Silverlight (and this is the default way of working with
Silverlight) and because it would make no sense to lock the phone
interface for too long.
Then you look at the code to
see whether a network connection is available. If you need access to a
web service, it's a good idea to indicate that users will have access to
a limited version of your software if they don't activate a network
interface.
void geoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (!e.Position.Location.IsUnknown)
{
proxy.GetAreaFromPtAsync(
//the ActualPosition
new TerraService.LonLatPt()
{
Lat = e.Position.Location.Latitude,
Lon = e.Position.Location.Longitude
},
1,
TerraService.Scale.Scale2km,
(int)ContentPanel.ActualWidth,
(int)ContentPanel.ActualHeight);
}
}
In Figure 3, you can see all the classes you need to prepare a GetAreaFromPt request, for whose response you must analyze the implementation of the event handler that handles the response.
void proxy_GetAreaFromPtCompleted(object sender,
TerraService.GetAreaFromPtCompletedEventArgs e)
{
if (e.Error == null)
{
int startX = e.Result.NorthWest.TileMeta.Id.X;
int endX = e.Result.NorthEast.TileMeta.Id.X;
int startY = e.Result.NorthWest.TileMeta.Id.Y;
int endY = e.Result.SouthWest.TileMeta.Id.Y;
for (int x = startX; x < endX; x++)
for (int y = startY; y >= endY; y--)
{
Image image = new Image();
image.Stretch = Stretch.None;
image.Margin = new Thickness(
(x - startX) * 200 - e.Result.NorthWest.Offset.XOffset,
(startY - y) * 200 - e.Result.NorthWest.Offset.YOffset,
0,
0);
ContentPanel.Children.Insert(0, image);
TileId tileId = e.Result.NorthWest.TileMeta.Id;
tileId.X = x;
tileId.Y = y;
proxy.GetTileAsync(tileId, image);
}
}
else
MessageBox.Show(e.Error.Message);
}
In this way, you will call the GetTile
method as many times as needed to display each image according to the
level of detail chosen.
void proxy_GetTileCompleted(object sender, TerraService.GetTileCompletedEventArgs e)
{
if (e.Error == null)
{
Image img = e.UserState as Image;
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(new MemoryStream(e.Result));
img.Source = bitmap;
}
}
To further clarify, the use of this service presents some pros and cons:
Pros:
The service allows you to retrieve maps for educational purposes
without worrying about record in any place like happens when you use
bing maps, but simply making requests. The service responses are rapid,
and the service can be interrogated via different protocols.
Cons: The service provides no support for maps outside the United States.
5. Usage
Normally this application
needs to run on a physical device, so change your output target to
Windows Phone 7 Device and start the application pressing F5 than wait
that your application become available and look that the map charges.