Hands-On Lab
Using Bing Maps
Lab version: 1.0.0
Last updated: 12/8/2010
Push Notifications Hands-on Lab
Page | 2
CONTENTS Overview .......................................................................................................................................... 3
Exercise 1: Introduction to the Bing Map Control ........................................................................... 7
Task 1 – Registering a Bing Maps Account .............................................................................. 7
Task 2 – Working with the Bing Map Control ........................................................................ 10
Exercise 2: Handling and Customizing Pushpins ........................................................................... 30
Task 1 – Creating a Pushpins Layer ........................................................................................ 30
Task 2 – Creating a Pushpins Catalog .................................................................................... 40
Exercise 3: Calculating a Route ...................................................................................................... 50
Task 1 – Adding a Route to the Map ..................................................................................... 50
Task 2 – Displaying Route Itineraries..................................................................................... 61
Summary........................................................................................................................................ 73
Push Notifications Hands-on Lab
Page | 3
Overview
The Bing™ Maps Silverlight® Control for Windows® Phone combines the power of Silverlight and
Bing Maps to provide an enhanced mapping experience. Developers can use the Bing Maps
Silverlight Control to incorporate the latest location and local search features into their
Windows Phone applications.
Using both Bing Maps Silverlight Control and the Bing Maps SOAP (strategy on a page) Services,
you can create Windows Phone applications that include an enhanced mapping experience.
This lab walks you through the steps required for using the Bing Maps Silverlight Control for
Windows Phone, and provides a quick reference for developing Windows Phone applications
integrated with Bing Maps.
Objectives
Upon completion of the lab you will:
Be familiar with Bing Maps for Windows Phone
Understand how to use Bing Map control with Data Binding
Have used Bing Maps services to fetch mapping data
Have created a simple map-based application, complete with pushpins and route layers
Prerequisites
The following is required to complete this hands-on lab:
Microsoft Visual Studio® 2010 Express for Windows Phone or Microsoft Visual Studio
2010
Windows Phone Developer Tools
Active Internet Connection
Note: You can download all Windows Phone development tools in one package from
http://developer.windowsphone.com
Setup
For your convenience, much of the code used in this hands-on lab is available as Visual Studio
code snippets.
Push Notifications Hands-on Lab
Page | 4
To install the code snippets:
1. Run the .vsi installer located in the lab's Source\Setup folder.
Note: If you have issues running the code snippets installer you can install the code
snippets manually by copying all the .snippet files located in the
Source\Setup\CodeSnippets folder of the lab to the following folder:
<your-documents-folder>\Visual Studio 2010\Code Snippets\Visual C#\My Code
Snippets
Using the Code Snippets
With code snippets, you have all the code you need at your fingertips. The lab document will tell
you exactly when you can use them. For example,
Figure 1
Using Visual Studio code snippets to insert code into your project
To add this code snippet in Visual Studio, you simply place the cursor where you would like the
code to be inserted, start typing the snippet name (without spaces or hyphens), watch as
IntelliSense picks up the snippet name, and then press the Tab key twice when the snippet you
want is selected. The code will be inserted at the cursor location.
Push Notifications Hands-on Lab
Page | 5
Figure 2
Start typing the snippet name
Figure 3
Press Tab to select the highlighted snippet
Figure 4
Press Tab again to expand the snippet
To insert a code snippet using the mouse rather than the keyboard, right-click where you want
to insert the code snippet, select Insert Snippet followed by My Code Snippets and then pick
the relevant snippet from the list.
To learn more about Visual Studio IntelliSense Code Snippets, including how to create your own,
see http://msdn.microsoft.com/en-us/library/ms165392.aspx.
Exercises
This hands-on lab comprises the following exercises:
1. Introduction to the Bing Map Control
Push Notifications Hands-on Lab
Page | 6
2. Handling and Customizing Pushpins
3. Calculating and Rendering a Route
Estimated time to complete this lab: 90 minutes.
Push Notifications Hands-on Lab
Page | 7
Exercise 1: Introduction to the Bing
Map Control
In this exercise you will:
Register a Bing Maps account to get a private key
Learn about the Bing Silverlight map control for Windows Phone
Add a map control to the page
Attach the map control with Bing Maps private key
Switch between aerial and road map modes
Center and zoom the map to a geographic location
You will use the Microsoft Visual Studio 2010 Express for Windows Phone development
environment, and will deploy to the Windows Phone Emulator for debugging. The solution we
will be working with is based upon the Silverlight for Windows Phone Application template.
Note: The steps in this hands-on lab illustrate procedures using Microsoft Visual Studio 2010
Express for Windows Phone, but they are equally applicable to Microsoft Visual Studio 2010
with the Windows Phone Developer Tools. Instructions that refer generically to Visual Studio
apply to both products.
Task 1 – Registering a Bing Maps Account
In this task you will create a Bing Maps account over the Internet. The Bing Maps Account
Center allows you to create keys to use the Bing Maps Control, Bing Maps SOAP Services, Bing
Maps REST (representational state transfer) Services and Bing Spatial Data Services. If you
already have a key for using Bing, you may use it for the next tasks. Without a valid key, you
won’t be able to see retrieve Bing Maps content from the web.
1. Open your web browser and go to the following address:
http://www.bingmapsportal.com.
2. Click Create to create a new Bing Maps account using your Windows Live ID.
Push Notifications Hands-on Lab
Page | 8
Figure 5
Bing maps registration
3. On the next page, fill in your details.
Figure 6
Bing maps account details
4. Once you’ve agreed the terms and registered and/or signed in, you may choose to
create a new key for your application. Click the “Create or view keys” link on the left.
Push Notifications Hands-on Lab
Page | 9
Figure 7
Create or view keys
5. On the next page fill in your application details and click Createkey.
Figure 8
Create a new key
Push Notifications Hands-on Lab
Page | 10
6. Below, you’ll see your new private key. Save this key, you will use it later.
Figure 9
Saving the private key
Note: In the next tasks you’ll be asked to use this private key. Keep it handy since
you’ll have to use it for the rest of this lab.
Task 2 – Working with the Bing Map Control
In this task you’ll use the Bing Map Silverlight control for Windows Phone. The Bing Map
Silverlight Control combines the power of Silverlight and Bing Maps to provide an enhanced
mapping experience. Developers can use the Bing Maps Silverlight Control to incorporate the
latest location and local search features into their applications.
You’ll use an existing starter solution for Microsoft Visual Studio 2010 Express for Windows
Phone or Microsoft Visual Studio 2010. This solution contains a partial Windows Phone
application, with startup code and UI behavior for completing this lab.
1. Open Microsoft Visual Studio 2010 Express for Windows Phone from Start | All
Programs | Microsoft Visual Studio 2010 Express | Microsoft Visual Studio 2010 Express
for Windows Phone.
2. In the File menu, choose Open Project.
Visual Studio 2010: In the File menu, point to open and then select Project/Solution.
Push Notifications Hands-on Lab
Page | 11
3. Navigate to the starter project location at the Source\Ex1-TheBingMapControl\Begin
folder of this lab, select Begin.sln and click Open.
Figure 10
Opening starter project
4. Examine the project you’ve just opened. This is a standard Windows Phone application
consists of single page: MainPage:
MainPage.xaml – defines the Bing maps lab UI using XAML; you’ll place UI
controls into this file during the lab
MainPage.cs – this is a partial class that contains startup code for implementing
the Bing maps lab; do not change this file during the lab
MainPage.xaml.cs – contains the map lab logic; you’ll place map application
logic in this file during the lab
o Resources such as icons and styles are located in the Resources folder
o Helper classes are placed in the Helpers folder
Push Notifications Hands-on Lab
Page | 12
5. To run the startup application, press F5. You should see the following:
Figure 11
Running the starter application
Push Notifications Hands-on Lab
Page | 13
6. During the following steps you will use the Bing map control and learn how to use it. To
begin, add a reference to the Microsoft.Phone.Controls.Maps assembly.
Figure 12
Add reference to map controls assembly
Push Notifications Hands-on Lab
Page | 14
7. Open the MainPage.xaml in design mode and drag the Map control, located on the
toolbox, to the center of the page.
This will both add an XAML namespace and create an instance of the Bing map control
for Windows Phone.
Figure 13
Map control design time
8. Open MainPage.xaml with the XAML editor, you should find something like this:
XAML
<!-- Map View -->
<Border x:Name="MapView"
Background="Black"
Height="768" Width="480">
<my:Map Name="map1" Height=”50” Width=”50” />
Push Notifications Hands-on Lab
Page | 15
</Border>
9. Change the map name to Map and remove both Height and Width properties so the
map will stretch across the entire screen.
XAML
<my:Map x:Name="Map" />
10. Press F5 to examine the map.
Figure 14
Bing map control
Push Notifications Hands-on Lab
Page | 16
Note: The first time you zoom in by double clicking the map, the following message
appears: “Invalid Credentials. Sign up for a developer account.” To remove this
message, you should use the Bing Maps private key created earlier with the map.
11. Open the App.xaml.cs file and add a new internal constant string field called “Id” to the
App class.
This field will hold the Bing Maps private key you’ve created. You will reference this key
during the lab to create credentials for both the Bing map control and Bing services.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 11 – Id Const)
C#
internal const string Id = "your Bing maps private key";
12. Open the MainPage.xaml.cs file and add the following using statements above the class
declaration.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 12 – Using statements)
C#
using System;
using System.Device.Location;
using Microsoft.Phone.Controls.Maps;
13. Now that we have the private key, we want to bind the map control with that key. To
do so:
Locate the “Fields” region inside the MainPage class
Create a new private read-only field of type
Microsoft.Phone.Controls.Maps.CredentialsProvider
Initialize it with a new instance using the private key you’ve just added to the
App class
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 13 – CredentialsProvider Field)
C#
private readonly CredentialsProvider _credentialsProvider = new
ApplicationIdCredentialsProvider(App.Id);
Note: If the using statement for the Microsoft.Phone.Controls.Maps were not added,
you should resolve this issue by either using a full qualified class name or adding the
required using statement. Several code snippets in this lab assume you are aware of
how to resolve this kind of inconveniences.
Push Notifications Hands-on Lab
Page | 17
14. Locate the “Properties” region inside the MainPage class and expose the field you’ve
just created as a public property called CredentialsProvider so you can bind it with the
map control.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 14 – CredentialsProvider Property)
C#
public CredentialsProvider CredentialsProvider
{
get { return _credentialsProvider; }
}
15. Open the MainPage.xaml file in XAML editor, and bind the Map.CredentialsProvider
property with the public property you’ve just created.
XAML
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}" />
Note: The MainPage.DataContext is set with the MainPage instance itself in the
MainPage.cs file, so the implicit binding source is the MainPage instance itself.
16. Press F5 again to test the application. Zooming in, you should notice that the message
that appeared previously has gone.
17. Open the MainPage.xaml file in XAML mode, and set the Map.Mode property to
AerialMode with labels. This will change the map mode to be displayed in aerial view
rather than road view, which is the default.
XAML
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
</my:Map.Mode>
</my:Map>
18. We’ve added a button to the application bar for changing the map mode. This button’s
click event is handled by calling the ChangeMapMode method located in the “Tasks”
region. In the MainPage.xaml.cs, locate the “Tasks” region inside the MainPage class.
You’ll find the ChangeMapMode method over there. Place code for switching from
Push Notifications Hands-on Lab
Page | 18
Road to Aerial mode and vice versa, by setting the Map.Mode property with a new
instance of type Microsoft.Phone.Controls.Maps.AerialMode or
Microsoft.Phone.Controls.Maps.RoadMode.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 18 – ChangeMapMode)
C#
private void ChangeMapMode()
{
if (Map.Mode is AerialMode)
{
Map.Mode = new RoadMode();
}
else
{
Map.Mode = new AerialMode(true);
}
}
Push Notifications Hands-on Lab
Page | 19
19. Press F5 to test the results. For switching views, click on the “eye” button on the
application bar. This invokes the ChangeMapMode method.
Figure 15
Aerial map mode
Push Notifications Hands-on Lab
Page | 20
Figure 16
Road map mode
Note: The Silverlight Map control for Windows Phone is designed to support the data
binding model. Changing properties of the Map control using data binding is a good
way for handling data objects within data templates and is a best practice for
decoupling the Map control from the logic behind, especially when using the MVVM
(model view viewmodel) pattern. For the simplicity of this lab, we are not using a data
template for the map view itself; hence the logic is located directly in the MainView
code behind. From now on, we will leverage the map control data binding capabilities,
although properties of the map control can be altered directly from code behind.
Push Notifications Hands-on Lab
Page | 21
20. To remove the Bing logo and Copyright from the bottom, you can set both
Map.CopyrightVisibility and Map.LogoVisibility to Visibility.Collapsed.
XAML
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}"
CopyrightVisibility="Collapsed"
LogoVisibility="Collapsed">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
</my:Map.Mode>
</my:Map>
You may notice that you can zoom in by double-clicking on the map. This option is
implemented internally by the map control. You have an option to display the built-in
zoom bar, by setting the Map.ZoomBarVisibility to Visible, but you can’t change the
buttons style or location. Next you will add custom zoom buttons so you can freely
zoom in and out.
21. In the MainPage.xaml.cs, add a new private field of type double.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 21 – Zoom Field)
C#
private double _zoom;
22. Find the “Consts” region and add three additional constant double fields to constraint
the zoom level:
DefaultZoomLevel = 18.0
MaxZoomLevel = 21.0
MinZoomLevel = 1.0.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 22 – Zoom Level Consts)
C#
private const double DefaultZoomLevel = 18.0;
private const double MaxZoomLevel = 21.0;
private const double MinZoomLevel = 1.0;
Push Notifications Hands-on Lab
Page | 22
23. In MainPage.xaml.cs, find the automatic property called Zoom, and change it to get or
set the value of the _zoom field. In the setter, consider the zoom level constraints
you’ve added and notify on property changed.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 23 – Zoom Property)
C#
public double Zoom
{
get { return _zoom; }
set
{
var coercedZoom = Math.Max(MinZoomLevel, Math.Min(MaxZoomLevel,
value));
if (_zoom != coercedZoom)
{
_zoom = value;
NotifyPropertyChanged("Zoom");
}
}
}
24. Open the MainPage.xaml in XAML editor and bind the Map.ZoomLevel property with
the Zoom property you’ve just changed. The binding mode must be two-way.
XAML
<my:Map Name="Map"
LogoVisibility="Collapsed"
ZoomLevel="{Binding Zoom, Mode=TwoWay}">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
</my:Map.Mode>
</my:Map>
25. Drop two buttons vertically from the toolbox into the middle left side of the map
control, one for zoom-in and the other for zoom-out.
XAML
<my:Map Name="Map"
LogoVisibility="Collapsed"
ZoomLevel="{Binding Zoom, Mode=TwoWay}">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
Push Notifications Hands-on Lab
Page | 23
</my:Map.Mode>
<Button x:Name="ButtonZoomIn"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,180,0,0" />
<Button x:Name="ButtonZoomOut"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,260,0,0" />
</my:Map>
26. Register the respective button Click event with the ButtonZoomIn_Click and
ButtonZoomOut_Click event handlers.
These event handlers are already provided in the MainView.cs file. When invoked, the
ButtonZoomIn_Click event handler increases the Zoom property by one and the
ButtonZoomOut_Click event handler decreases the Zoom property by one.
XAML
<Button x:Name="ButtonZoomIn"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,180,0,0"
Click="ButtonZoomIn_Click" />
<Button x:Name="ButtonZoomOut"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,260,0,0"
Click="ButtonZoomOut_Click" />
27. To change the look and feel of the zoom-in and zoom-out buttons, open the
MainView.xaml in XAML mode and set each Button.Style property with the appropriate
ButtonZoomInStyle or ButtonZoomOutStyle style.
These styles were provided with the starter solution and are located in the
DefaultStyle.xaml resource file.
XAML
<Button x:Name="ButtonZoomIn"
Style="{StaticResource ButtonZoomInStyle}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,180,0,0"
Click="ButtonZoomIn_Click" />
<Button x:Name="ButtonZoomOut"
Style="{StaticResource ButtonZoomOutStyle}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Push Notifications Hands-on Lab
Page | 24
Height="56" Width="56" Margin="8,260,0,0"
Click="ButtonZoomOut_Click" />
28. To test the results, press F5 and click the zoom-in and zoom-out buttons.
Figure 17
Zoom out
Push Notifications Hands-on Lab
Page | 25
Figure 18
Zoom in
In the next step you’ll center the map to your geographic location. Since this lab works
with the emulator, you don’t have a real GPS device. So you’ll have to pre-define your
geographic location using latitude and longitude coordinates.
Note: You can determine your geographic location by browsing to
http://www.bing.com/maps, set your location, and then copy the Latitude and
Longitude coordinates from the address bar.
Push Notifications Hands-on Lab
Page | 26
29. Add a reference to the System.Device assembly.
Figure 19
Reference System.Device
30. In the MainPage.xaml.cs, add a new static, read-only field of type
System.Device.Location.GeoCoordinate, called DefaultLocation, and set it with a new
instance using your latitude and longitude coordinates.
This will be your current location.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 30 – DefaultLocation Field)
C#
private static readonly GeoCoordinate DefaultLocation = new
GeoCoordinate(47.639631, -122.127713);
31. In the MainPage.xaml.cs, add a new field of type
System.Device.Location.GeoCoordinate, called _center.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 31 – Center Field)
C#
private GeoCoordinate _center;
Push Notifications Hands-on Lab
Page | 27
32. Create a property called Center that exposes the _center field. In the setter, call the
notification change method.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 32 – Center Property)
C#
public GeoCoordinate Center
{
get { return _center; }
set
{
if (_center != value)
{
_center = value;
NotifyPropertyChanged("Center");
}
}
}
33. In the MainPage.xaml, bind the Map.Center property with the Center property you’ve
created. The binding mode must be two-way.
XAML
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}"
ZoomLevel="{Binding Zoom, Mode=TwoWay}"
Center="{Binding Center, Mode=TwoWay}">
...
</my:Map>
34. In the MainPage.xaml.cs, find the CenterLocation method. The first application’s bar
left button event handler calls this method. Change both Center and Zoom to defaults.
(Code Snippet – Using Bing Maps – Ex 1 Task 2 Step 34 – CenterLocation Method)
C#
private void CenterLocation()
{
Center = DefaultLocation;
Zoom = DefaultZoomLevel;
}
Push Notifications Hands-on Lab
Page | 28
35. To test the application, press F5 and click the application bar’s target icon on the left.
This should zoom and center the map to the location provided by the DefaultLocation
field.
Figure 20
Center and zoom map to default location
This concludes the exercise.
Note: The complete solution for this exercise is provided at the following location:
Source\Ex1-TheBingMapControl\End.
Push Notifications Hands-on Lab
Page | 29
Push Notifications Hands-on Lab
Page | 30
Exercise 2: Handling and Customizing
Pushpins
In this exercise you will:
Learn how to use map layers with data binding
Create a pushpin layer
Add a pushpin for indicating current location
Handle pushpin clicks
Add pushpins from a catalog to the map
Customize the pushpin look and feel
You will use the Microsoft Visual Studio 2010 Express for Windows Phone development
environment, and will deploy to the Windows Phone Emulator for debugging. The solution we
will be working with is based upon the previous exercise.
Task 1 – Creating a Pushpins Layer
In this task you will learn how to create a map layer and how to populate it with pushpin data
using data binding. A map layer is represented by a custom panel of type
Microsoft.Phone.Controls.Maps.MapLayer. Using the MapLayer panel you can have any kind of
UIElement on top of the map. The MapLayer panel knows how to lay out elements on the map
using geo coordinates. A map control can have zero or more layers.
Although we can add and remove visual items directly to the MapLayer panel from code behind,
we will populate map layers using the data binding mechanism. Instead of using MapLayer
directly, we will use another control of type Microsoft.Phone.Controls.Maps.MapItemsControl.
This custom ItemsControl uses the MapLayer panel to layout items by default.
1. Open the starter solution located in the Source\Ex2-HandlingPushpins\Begin folder.
Note: You should set the Bing Maps application Id in the App class with your private
key created in the previous exercise.
2. To add a new pushpins layer to the map, open the MainPage.xaml in XAML editor and
add a new MapItemsControl element as a child of the map control, just before the
zoom buttons so the buttons will render on top. This will be our pushpins layer.
XAML
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}"
ZoomLevel="{Binding Zoom, Mode=TwoWay}"
Push Notifications Hands-on Lab
Page | 31
Center="{Binding Center, Mode=TwoWay}">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
</my:Map.Mode>
<my:MapItemsControl />
<Button x:Name="ButtonZoomIn"
Style="{StaticResource ButtonZoomInStyle}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,180,0,0"
Click="ButtonZoomIn_Click" />
<Button x:Name="ButtonZoomOut"
Style="{StaticResource ButtonZoomOutStyle}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Height="56" Width="56" Margin="8,260,0,0"
Click="ButtonZoomOut_Click" />
</my:Map>
3. To populate the new pushpins layer with pushpins data, create a new project folder
called Models.
4. Under the Models folder, create a new public class called PushpinModel.
This class represents the pushpin data.
Figure 21
Pushpin model
Push Notifications Hands-on Lab
Page | 32
5. Add a new automatic public property called Location of type
System.Device.Location.GeoCoordinate to the PushpinModel. This property indicates
the pushpin’s geographical location on the map.
(Code Snippet – Using Bing Maps – Ex 2 Task 1 Step 5 – Location Property)
C#
public class PushpinModel
{
public GeoCoordinate Location { get; set; }
}
6. To populate the pushpins layer with pushpins, add a new private read-only field called
_pushpins of type ObservableCollection<PushpinModel> to MainPage.xaml.cs, and
initialize it with one PushpinModel instance using the default location.
(Code Snippet – Using Bing Maps – Ex 2 Task 1 Step 6 – Pushpins Field)
C#
private readonly ObservableCollection<PushpinModel> _pushpins = new
ObservableCollection<PushpinModel>
{
new PushpinModel
{
Location = DefaultLocation
}
};
7. Expose the _pushpins field with public property so you can bind it with the pushpins
layer.
(Code Snippet – Using Bing Maps – Ex 2 Task 1 Step 7 – Pushpins Property)
C#
public ObservableCollection<PushpinModel> Pushpins
{
get { return _pushpins; }
}
8. Open the MainPage.xaml in XAML editor and bind the MapItemsControl you’ve added
with the Pushpins collection.
XAML
Push Notifications Hands-on Lab
Page | 33
<my:MapItemsControl ItemsSource="{Binding Pushpins}" />
9. If you run the application, you’ll notice that the pushpin data is added to the map’s top
left corner as a text block. You can change that by simply creating a data template. To
create a data template, set the MapItemsControl.ItemTemplate property with a
DataTemplate element that contains one Pushpin.
XAML
<my:MapItemsControl ItemsSource="{Binding Pushpins}">
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin />
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
10. In order to place the pushpin at the exact location on the map, bind the
Pushpin.Location property with the Location property of the PushpinModel.
XAML
<my:MapItemsControl ItemsSource="{Binding Pushpins}">
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Location="{Binding Location}" />
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
11. Set the Pushpin.Content property to “My Location.”
XAML
<my:Pushpin Location="{Binding Location}"
Content="My Location" />
Push Notifications Hands-on Lab
Page | 34
12. Press F5 to test the results.
You should see a black pushpin with a text label. This is the pushpin you’ve just added
with a default black style. If you click on the current location icon on the application bar,
the map will center on the default location and you’ll see the pushpin in the center.
Figure 22
Pushpin default style
13. A pushpin can be customized easily. It is a ContentControl; hence we can change its
style and control template. To create a new pushpin style and control template, open
Push Notifications Hands-on Lab
Page | 35
the DefaultStyle.xaml resource dictionary located in the Resources/Styles project
folder and map the Bing maps control namespace.
XAML
<ResourceDictionary
...
xmlns:m="clr-
namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Control
s.Maps">
<!-- Next step, pushpin style comes here -->
</ResourceDictionary>
14. Inside the DefaultStyle.xaml resource dictionary, create a new style for the pushpin as
follows:
XAML
<Style x:Key="PushpinStyle" TargetType="m:Pushpin">
<Setter Property="BorderBrush" Value="#FFF4F4F5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Canvas Height="0"
RenderTransformOrigin="0.5,0.5"
Width="0">
<Canvas RenderTransformOrigin="0.5,0.5"
Height="1"
Width="1">
<Path Data="M22.5,1 C34.374123,1 44,10.625878
44,22.5 C44,31.034525 39.027256,38.407604 31.821138,41.879868
L31.359026,42.095631 L22.702744,60.864998 L13.900847,42.209641
L13.651964,42.100761 C6.1912994,38.727623 0.99999976,31.220058 1,22.5
C0.99999976,10.625878 10.625878,1 22.5,1 z"
Fill="{TemplateBinding BorderBrush}"
Height="61.865"
Stretch="Fill"
Stroke="Black"
StrokeThickness="2"
StrokeLineJoin="Miter"
UseLayoutRounding="False"
Width="45"
RenderTransformOrigin="0.5,0.5"
Canvas.Left="-2.703"
Canvas.Top="-7.187" >
<Path.RenderTransform>
<CompositeTransform TranslateX="-20"
Push Notifications Hands-on Lab
Page | 36
TranslateY="-55"/>
</Path.RenderTransform>
</Path>
<Path Data="M35,17.5 C35,27.164984 27.164984,35
17.5,35 C7.8350167,35 0,27.164984 0,17.5 C0,7.8350167 7.8350167,0 17.5,0
C27.164984,0 35,7.8350167 35,17.5 z"
Fill="{TemplateBinding Background}"
HorizontalAlignment="Left"
Height="35"
Stretch="Fill"
StrokeThickness="2"
VerticalAlignment="Top"
RenderTransformOrigin="0.5,0.5"
Width="35"
UseLayoutRounding="False"
Canvas.Top="-7.4"
Canvas.Left="-2.888" >
<Path.RenderTransform>
<CompositeTransform TranslateX="-15"
TranslateY="-50"/>
</Path.RenderTransform>
</Path>
</Canvas>
<ContentPresenter Width="35"
Height="35"
RenderTransformOrigin="0.5,0.5"
Canvas.Top="-3.5">
<ContentPresenter.RenderTransform>
<CompositeTransform TranslateX="-18"
TranslateY="-54"/>
</ContentPresenter.RenderTransform>
</ContentPresenter>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Note: If you design your own pushpin control template, you should know that the
default relative point of a pushpin when scaling the map (zoom) is the bottom left
corner, not the center or top left. You can easily create your own control template
using Microsoft Expression Blend for Windows Phone, which is part of the Windows
Phone development tools.
15. Add new public property of type Uri, named Icon to the PushpinModel. You will use it
to bind the image of the pushpin.
Push Notifications Hands-on Lab
Page | 37
(Code Snippet – Using Bing Maps – Ex 2 Task 1 Step 15 – Icon Property)
C#
public class PushpinModel
{
public GeoCoordinate Location { get; set; }
public Uri Icon { get; set; }
}
16. Under the Resources\Icons folder, create a new folder called Pushpins, and add all
images from the Source\Assets\Resources\Pushpins folder.
Figure 23
Pushpin images
Push Notifications Hands-on Lab
Page | 38
17. Change each of the images’ build actions from Resource to Content. To do so, select all
pushpin images, right click, select properties, and change the Build Action to Content.
Figure 24
Changing pushpin images build action to Content
18. In the MainPage.xaml, set the style of the pushpin to the one you’ve added.
XAML
<my:MapItemsControl ItemsSource="{Binding Pushpins}">
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Style="{StaticResource PushpinStyle}"
Location="{Binding Location}" />
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
19. Set the content of the pushpin to an Image element and bind the Image.Source
property to the PushpinModel.Icon property.
XAML
<my:MapItemsControl ItemsSource="{Binding Pushpins}">
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Style="{StaticResource PushpinStyle}"
Location="{Binding Location}">
<Image Source="{Binding Icon}" />
</my:Pushpin>
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
Push Notifications Hands-on Lab
Page | 39
</my:MapItemsControl>
20. In the MainPage.xaml.cs, set the PushpinData.Icon property of pushpin instance you’ve
created before with a relative Uri to the PushpinLocation.png icon file located in the
Pushpins project folder.
(Code Snippet – Using Bing Maps – Ex 2 Task 1 Step 20 – Pushpin Icon Value)
C#
private readonly ObservableCollection<PushpinModel> _pushpins = new
ObservableCollection<PushpinModel>
{
new PushpinModel
{
Location = DefaultLocation,
Icon = new Uri("/Resources/Icons/Pushpins/PushpinLocation.png",
UriKind.Relative)
}
};
Push Notifications Hands-on Lab
Page | 40
21. Press F5 to see the results. You should observe that the pushpin’s default black style has
changed, and now it looks like the pushpin in Bing maps over the web.
Figure 25
Custom pushpin style
Task 2 – Creating a Pushpins Catalog
In this task you’ll create a pushpin catalog and display it using a popup window. To build a
catalog, you will bind a list box to a collection of predefined pushpin models. Selecting a pushpin
Push Notifications Hands-on Lab
Page | 41
from the catalog will clone it and add that pushpin to the touched area. You will use the Map
control APIs to translate a view point to geographic location.
1. Add a new public property of type string, named TypeName to the PushpinModel class
you’ve created, located in the Models project folder. You’ll use this type name to pick a
different color for different types of pushpins.
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 1 – TypeName Property)
C#
public class PushpinModel
{
public GeoCoordinate Location { get; set; }
public Uri Icon { get; set; }
public string TypeName { get; set; }
}
2. Add the PushpinCatalog.cs C# file located in the Source\Assets\Code folder to the
Models project folder. This file defines a class which contains one pushpin instance per
pushpin type.
C#
public class PushpinCatalog
{
private List<PushpinModel> _items;
public IEnumerable<PushpinModel> Items
{
get { return _items; }
}
public PushpinCatalog()
{
InitializePuspins();
}
private void InitializePuspins()
{
string[] pushpinIcons =
{
"PushpinBicycle.png",
"PushpinCar.png",
"PushpinDrink.png",
"PushpinFuel.png",
"PushpinHouse.png",
Push Notifications Hands-on Lab
Page | 42
"PushpinRestaurant.png",
"PushpinShop.png"
};
var pushpins = from icon in pushpinIcons
select new PushpinModel
{
Icon = new Uri("/Resources/Icons/Pushpins/"
+ icon, UriKind.Relative),
TypeName =
System.IO.Path.GetFileNameWithoutExtension(icon)
};
_items = pushpins.ToList();
}
}
3. In the MainPage.xaml, find the PushpinPopup popup element and add a ListBox named
ListBoxPushpinCatalog as its only child.
XAML
<Popup x:Name="PushpinPopup" IsOpen="False" Canvas.Top="330"
Canvas.Left="25" Opacity="0">
<ListBox x:Name="ListBoxPushpinCatalog" />
</Popup>
4. Set the following ListBox properties:
XAML
<ListBox x:Name="ListBoxPushpinCatalog"
Width="392" Height="56"
Background="{StaticResource ControlBackgroundBrush}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
5. To create an instance of the catalog and bind the list box with the catalog’s items, map
the UsingBingMaps.Models CLR namespace using xmlns.
XAML
<phone:PhoneApplicationPage x:Class="UsingBingMaps.MainPage"
Push Notifications Hands-on Lab
Page | 43
...
xmlns:models="clr-namespace:UsingBingMaps.Models">
...
</phone:PhoneApplicationPage>
6. Set the ListBox.DataContext property with a fresh instance of type PushpinCatalog and
bind the list box with the PushpinCatalog.Items.
XAML
<ListBox x:Name="ListBoxPushpinCatalog"
Width="392" Height="56"
Background="{StaticResource ControlBackgroundBrush}"
ItemsSource="{Binding Items}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.DataContext>
<models:PushpinCatalog />
</ListBox.DataContext>
</ListBox>
7. Create a list box item template which has a single instance of type Image. Bind the
Image.Source to the PushpinModel.Icon property, and set the image size to 56x56.
XAML
<ListBox.ItemTemplate>
<DataTemplate>
<Image Width="56" Height="56" Source="{Binding Icon}" />
</DataTemplate>
</ListBox.ItemTemplate>
8. To center the popup at the exact touch location, find the CenterPushpinsPopup
method located in the MainPage.xaml.cs file, and add logic to calculate the popup’s
Canvas.Top value based on the size of the list box and the touch point.
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 8 – Canvas SetTop)
C#
private void CenterPushpinsPopup(Point touchPoint)
{
Canvas.SetTop(PushpinPopup, touchPoint.Y -
ListBoxPushpinCatalog.Height / 2);
Push Notifications Hands-on Lab
Page | 44
}
9. To see the results, press F5. When the map loaded, press and hold the left mouse
button on the map.
This should open the popup. This behavior already exists as part of the starter solution.
Figure 26
Pushpin catalog
Push Notifications Hands-on Lab
Page | 45
10. Now that the catalog is ready, let’s add logic to pick a pushpin and place it at the exact
location. Register the ListBox.SelectionChanged event with the
ListBoxPushpinCatalog_SelectionChanged event handler provided with the starter
solution.
11. Add a new method named Clone to the PushpinModel class. This method should return
a clone of the PushpinModel instance with a different geo location provided as
parameter of type GeoCoordinate.
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 11 – Clone Method)
C#
public PushpinModel Clone(GeoCoordinate location)
{
return new PushpinModel
{
Location = location,
TypeName = TypeName,
Icon = Icon
};
}
12. In the MainPage.xaml.cs, find the CreateNewPushpin method. This method is called by
the ListBoxPushpinCatalog_SelectionChanged located in the MainPage.cs. It has two
parameters: the selected pushpin model and the exact touch point in view coordinates.
Translate the touch point to geo coordinates using the
Map.TryViewportPointToLocation method.
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 12 – Translate Location)
C#
private void CreateNewPushpin(object selectedItem, Point point)
{
GeoCoordinate location;
Map.TryViewportPointToLocation(point, out location);
}
13. Now that you’ve got the exact geo coordinate for the pushpin, clone the selected
pushpin with the new coordinate, and add the clone to the Pushpins collection. This will
generate a new pushpin on the correct geo location on the map.
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 13 – Add Pushpin Clone)
C#
private void CreateNewPushpin(object selectedItem, Point point)
Push Notifications Hands-on Lab
Page | 46
{
GeoCoordinate location;
Map.TryViewportPointToLocation(point, out location);
var pushpinPrototype = selectedItem as PushpinModel;
var pushpin = pushpinPrototype.Clone(location);
Pushpins.Add(pushpin);
}
14. To change the pushpin colors, add the following brushes to the DefaultStyle.xaml
located in the Resources\Styles project folder.
XAML
<!-- # Brushes -->
<SolidColorBrush x:Key="PushpinBicycleBrush"
Color="#FF593D00" />
<SolidColorBrush x:Key="PushpinCarBrush"
Color="#FFEC098D" />
<SolidColorBrush x:Key="PushpinDrinkBrush"
Color="#FFD21242" />
<SolidColorBrush x:Key="PushpinFuelBrush"
Color="#FFF47836" />
<SolidColorBrush x:Key="PushpinHouseBrush"
Color="#FF00AFDB" />
<SolidColorBrush x:Key="PushpinLocationBrush"
Color="#FF3784DF" />
<SolidColorBrush x:Key="PushpinRestaurantBrush"
Color="#FF7D4199" />
<SolidColorBrush x:Key="PushpinShopBrush"
Color="#FF00B25A" />
15. Add a new project folder called Converters, and within it create a new value converter
class that converts the pushpin TypeName to the exact brush, and add it as a resource
to the DefaultStyles.xaml resource dictionary.
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 15 – PushpinTypeBrushConverter
Class)
C#
public class PushpinTypeBrushConverter : IValueConverter
Push Notifications Hands-on Lab
Page | 47
{
public object Convert(object value, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
var brushKey = string.Format("{0}Brush", value);
var brush = Application.Current.Resources[brushKey] ??
DefaultBrush;
return brush;
}
public object ConvertBack(object value, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
static PushpinTypeBrushConverter()
{
DefaultBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0x37,
0x84, 0xDF));
}
public static Brush DefaultBrush;
}
XAML
<ResourceDictionary
...
xmlns:converters="clr-namespace:UsingBingMaps.Converters">
<converters:PushpinTypeBrushConverter
x:Key="PushpinTypeBrushConverter" />
</ResourceDictionary>
16. In the MainPage.xaml, search for the Map control. In the pushpin data template, use
the converter instance you created to bind the Pushpin.Background property to the
PushpinModel.TypeName property.
XAML
<my:MapItemsControl ItemsSource="{Binding Pushpins}">
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Style="{StaticResource PushpinStyle}"
Location="{Binding Location}"
Push Notifications Hands-on Lab
Page | 48
Background="{Binding TypeName,
Converter={StaticResource PushpinTypeBrushConverter}}">
<Image Source="{Binding Icon}" />
</my:Pushpin>
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
17. To center the map on a pushpin while touching it, register the
Pushpin.MouseLeftButtonUp event.
Since the Pushpin is defined by a DataTemplate located directly inside MainPage.xaml,
you can simply register the event handler over there. Otherwise you can use an
Attached Behavior to activate a command. In the pushpin DataTemplate, register to the
Pushpin.MouseLeftButtonUp event and handle it by setting the MainPage.Center
property with the pushpin’s Location property. You can extract the pushpin object from
the event handler sender argument.
XAML
<my:Pushpin Style="{StaticResource PushpinStyle}"
MouseLeftButtonUp="Pushpin_MouseLeftButtonUp"
Location="{Binding Location}"
Background="{Binding TypeName, Converter={StaticResource
PushpinTypeBrushConverter}}">
<Image Source="{Binding Icon}" />
</my:Pushpin>
(Code Snippet – Using Bing Maps – Ex 2 Task 2 Step 17 – Pushpin_MouseLeftButtonUp
Event Handler)
C#
private void Pushpin_MouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
{
var pushpin = sender as Pushpin;
Center = pushpin.Location;
}
Push Notifications Hands-on Lab
Page | 49
18. To test the results, press F5, go to the default location, click and hold a position on the
map then select a pushpin from the list. The pushpin you’ve selected should be added
to the map. Repeat this behavior for adding more pushpins. Click on any pushpin to
center the map on it.
Figure 27
Pushpin selection
This concludes the exercise.
Note: The complete solution for this exercise is provided at the following location:
Source\Ex2-HandlingPushpins\End.
Push Notifications Hands-on Lab
Page | 50
Exercise 3: Calculating a Route
In this exercise we will:
Create a map route layer
Use Bing services to calculate to and from routes
Display calculated route itineraries
We will use the Microsoft Visual Studio 2010 Express for Windows Phone development
environment, and will deploy to the Windows Phone Emulator for debugging. The solution we
will be working with is based upon the previous exercise.
Task 1 – Adding a Route to the Map
In this task you’ll work with Bing Maps SOAP services to calculate a route from point A to point
B, and then you’ll create a route layer for rendering the calculated route. As part of the route
rendering, you’ll also render route itineraries provided by the Bing Maps services.
In this task you’ll use the following services:
Geocode Service – to match addresses, places, and geographic entities to latitude and
longitude coordinates on the map, as well as return location information for a specified
latitude and longitude coordinate
Route Service – to generate routes and driving directions based on locations or
waypoints, for example, directions that include traffic warnings and route hints
between multiple locations, and directions from all major roads to a destination (1-click
directions, also referred to as a "party map")
Search Service – to parse a search query that contains a location or keyword (or both)
and return search result
1. Open the starter solution located in the Source\Ex3-RouteCalculation\Begin folder.
Note: You should set the Bing Maps application Id in the App class with your private
key created in the previous exercise.
Push Notifications Hands-on Lab
Page | 51
2. To add a reference to the Bing Maps Geocode service, right click on the project
references, and select “Add Service Reference…”.
Figure 28
Add Service Reference
3. Paste the following service address into the opened dialog, and click Go:
http://dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc
Push Notifications Hands-on Lab
Page | 52
4. In the “Namespace” field, make sure that you enter Bing.Geocode and then click OK.
Figure 29
Bing Service
Note: If you have problems generating the data from the service, please make sure
that you are currently connected to the Internet. Try to paste the service address into
the web browser;, you should get a service test page. If you have a connection, try to
run Visual Studio 2010 again as Administrator (right click on Visual Studio 2010 icon on
start menu, select “Run as administrator”), then try again.
5. To add reference to the Bing Maps Route service, right-click the project references, and
select “Add Service Reference…”
6. Paste the following service address into the opened dialog, and click Go:
http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc
7. In the “Namespace” field, make sure that you enter Bing.Route and then click OK.
8. To add a reference to the Bing Maps Search service, right-click the project references,
and select “Add Service Reference…”
Push Notifications Hands-on Lab
Page | 53
9. Paste the following service address into the opened dialog, and click Go:
http://dev.virtualearth.net/webservices/v1/searchservice/searchservice.svc
10. In the “Namespace” field, make sure that you enter Bing.Search and then click OK.
11. Open the ServiceReferences.ClientConfig configuration file that was automatically
created as a project item and delete all CustomBinding related entries. In this lab we
will use only the basic http bindings. You should have a file that looks like this:
XAML
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IGeocodeService"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
<binding name="BasicHttpBinding_IRouteService"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
<binding name="BasicHttpBinding_ISearchService"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://dev.virtualearth.net/webservices/v1/geocodeservice/Geoco
deService.svc"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IGeocodeService"
contract="Bing.Geocode.IGeocodeService"
name="BasicHttpBinding_IGeocodeService" />
<endpoint
address="http://dev.virtualearth.net/webservices/v1/routeservice/routese
rvice.svc"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IRouteService"
contract="Bing.Route.IRouteService"
name="BasicHttpBinding_IRouteService" />
<endpoint
address="http://dev.virtualearth.net/webservices/v1/searchservice/search
service.svc"
Push Notifications Hands-on Lab
Page | 54
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_ISearchService"
contract="Bing.Search.ISearchService"
name="BasicHttpBinding_ISearchService" />
</client>
</system.serviceModel>
</configuration>
Note: You’ve now added three Bing Maps services. Next you’ll add a helper class that
uses these services to wrap part of the asynchronous calls for calculating a route.
12. Add the following files to the Helpers project folder: RouteCalculator.cs,
RoutingState.cs, RouteCalculationError.cs, located in Source/Assets/Code.
These files will help you calculate a route using the Bing Maps Services you’ve added.
The RouteCalculator class exposes one public method called CalculateAsync and one
public event called Error. To calculate a route all you have to do is to initialize a new
instance of the RouteCalculator type with the following parameter: Map credentials, To
string, From string, UI dispatcher, and a delegate that will be called on the UI thread
when calculation is done. If an error occurs, the Error event is raised on the UI thread.
This is why you must pass the UI dispatcher.
Figure 30
Route Helpers
13. Add a model public class called RouteModel to the Models project folder, for
representing the route data.
Push Notifications Hands-on Lab
Page | 55
14. In the RouteModel class add a new collection of type
Microsoft.Phone.Controls.Maps.LocationCollection to hold the route coordinates, and
expose this as a public property called Locations.
15. Create a constructor with one collection parameter of type
Microsoft.Phone.Controls.Maps.Platform.Location.
Use it to initialize the internal collection. Location has an implicit cast to the
GeoCoordinate type.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 15 – RouteModel Constructor)
C#
public class RouteModel
{
private readonly LocationCollection _locations;
public ICollection<GeoCoordinate> Locations
{
get { return _locations; }
}
public RouteModel(ICollection<Location> locations)
{
_locations = new LocationCollection();
foreach (Location location in locations)
{
_locations.Add(location);
}
}
}
16. In the MainPage.xaml.cs, add a new ObservableCollection<RouteModel> and expose it
with a public property called Routes.
This collection holds the calculated route and will be bound with the route layer.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 16 – Routes Property)
C#
private readonly ObservableCollection<RouteModel> _routes = new
ObservableCollection<RouteModel>();
public ObservableCollection<RouteModel> Routes
{
get { return _routes; }
}
Push Notifications Hands-on Lab
Page | 56
17. Add two public string properties: To and From. You’ll use these properties for the route
calculation.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 17 – To From Properties)
C#
public string To { get; set; }
public string From { get; set; }
18. Find the CalculateRoute method and calculate a route using the route calculator helper
class: Create a try/catch block and use MessageBox to display an error message in case
an exception is caught.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 18 – PushpinTypeBrushConverter
Class)
C#
private void CalculateRoute()
{
try
{
// TODO : Calculate a route.
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
19. To calculate a route asynchronously, create an instance of RouteCalculator inside the
try block. So far you should have all the required parameters as properties of the
MainPage class, except for the last one which will be an empty lambda expression with
a single ‘result’ parameter.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 19 – routeCalculator)
C#
try
{
var routeCalculator = new RouteCalculator(
CredentialsProvider,
To,
From,
Dispatcher,
result =>
Push Notifications Hands-on Lab
Page | 57
{
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
20. Register the RouteCalculator.Error event by displaying an error message to the user
and then call the RouteCalculator.CalculateAsync method to start the route calculation.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 20 – Calculate)
C#
try
{
var routeCalculator = new RouteCalculator(
CredentialsProvider,
To,
From,
Dispatcher,
result =>
{
});
// Display an error message in case of fault.
routeCalculator.Error += r => MessageBox.Show(r.Reason);
// Start the route calculation asynchronously.
routeCalculator.CalculateAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
21. When the route calculation is finished, the lambda expression should be invoked. Inside
the lambda expression, clear the Route collection. For the simplicity, you’ll have only
one route at a time.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 21 – Clear Routes)
C#
result =>
Push Notifications Hands-on Lab
Page | 58
{
Routes.Clear();
};
22. Below, create a new RouteModel instance based on route calculator parameter
(result.Result.RoutePath.Points), and add the new route to the route collection.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 22 – routeModel)
C#
var routeModel = new RouteModel(result.Result.RoutePath.Points);
Routes.Add(routeModel);
23. Then, center the map on the new route, by calling Map.SetView, and passing it a
LocationRect instance which is can be calculated using the
LocationRect.CreateLocationRect static helper method.
(Code Snippet – Using Bing Maps – Ex 3 Task 1 Step 23 – viewRect)
C#
var viewRect = LocationRect.CreateLocationRect(routeModel.Locations);
Map.SetView(viewRect);
24. To pick the route’s to/from text, open the MainPage.xaml with the XAML editor, find
the RouteView border and add a new child composed of a Grid with two TextBlocks,
one for “From” and the other for “To”.
XAML
<Border x:Name="RouteView"
Height="160" Margin="0"
Padding="8" RenderTransformOrigin="0.5,0.5" Width="480"
Background="{StaticResource ControlBackgroundBrush}">
<Border.RenderTransform>
<CompositeTransform TranslateY="-160"/>
</Border.RenderTransform>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="0.8*" />
<ColumnDefinition Width="0.2*"/>
Push Notifications Hands-on Lab
Page | 59
</Grid.ColumnDefinitions>
<TextBlock Text="From" Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center" />
<TextBox Grid.Row="0" Grid.Column="1"
Grid.ColumnSpan="2" />
<TextBlock Text="To" Grid.Row="1" Grid.Column="0"
VerticalAlignment="Center" />
<TextBox Grid.Row="1" Grid.Column="1" />
</Grid>
</Border>
25. Bind each of the TextBlocks as two-way binding with the relevant To/From properties.
XAML
<TextBox Text="{Binding From, Mode=TwoWay}" ... />
<TextBox Text="{Binding To, Mode=TwoWay}" ... />
26. Add a “Go” button to the Grid, set its style with the ButtonGoStyle style and register its
Click event with the ButtonGo_Click event handler.
Both the style and the event handler are provided with the starter project.
XAML
<Button Content="Go" Grid.Column="2" Grid.Row="1"
Click="ButtonGo_Click"
Style="{StaticResource ButtonGoStyle}" />
27. To create a route layer, add another MapItemsControl to the map control just before
the pushpins layer, and bind it to the Routes property.
XAML
<my:MapItemsControl ItemsSource="{Binding Routes}" />
28. To draw the route as a single line on the map, set the MapItemsControl.ItemTemplate
with a data templatethat contains a single MapPolyline instance.
XAML
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:MapPolyline Stroke="#FF2C76B7"
Opacity="0.85"
StrokeThickness="6" />
Push Notifications Hands-on Lab
Page | 60
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
29. Bind the MapPolyline instance with the RouteModel.Locations property.
XAML
<my:MapPolyline Stroke="#FF2C76B7"
Opacity="0.85"
StrokeThickness="6"
Locations="{Binding Locations}" />
30. To test the application:
Press F5 and then click on route icon on the application bar (the upward-facing
arrow).
The route view should slide down from the top.
Write a valid address in the From and To fields.
For example: “Microsoft Redmond Building 1, WA,” “Microsoft Redmond
Building 9, WA.”
Click the search icon.
This should calculate the route and you should see it on the map. If an error
occurs, you should see an error message.
Push Notifications Hands-on Lab
Page | 61
Figure 31
Calculating a route
Task 2 – Displaying Route Itineraries
The Bing route service supplies additional data with the route data, namely, the route itineraries
data. By using the itineraries data you can display the exact directions of how to walk or drive
from point A to point B on the route. In this task you’ll create a directions list based on these
itineraries.
Push Notifications Hands-on Lab
Page | 62
1. In the MainPage.xaml.cs, add a new collection of type ItineraryItem, and expose it as
public property called Itineraries. This type was auto generated as part of the Bing
Route service data contracts. You’ll use this class to encapsulate the itineraries
collection provided with the route.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 1 – ItineraryItem Property)
C#
private readonly ObservableCollection<ItineraryItem> _itineraries = new
ObservableCollection<ItineraryItem>();
public ObservableCollection<ItineraryItem> Itineraries
{
get { return _itineraries; }
}
2. Find the CalculateRoute method and clear both the Routes collection and the
Itineraries collection.
Since you’re displaying only one route at a time you’ll provide the itineraries of that
active route.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 2 – Clear Itineraries)
C#
result =>
{
Routes.Clear();
Itineraries.Clear();
...
}
3. After adding the new route to the collection of routes, add all the itineraries provided
by result.Result.Legs[0] right.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 3 – Add Itineraries)
C#
var routeModel = new RouteModel(result.Result.RoutePath.Points);
Routes.Add(routeModel);
foreach (var itineraryItem in result.Result.Legs[0].Itinerary)
{
Itineraries.Add(itineraryItem);
}
Push Notifications Hands-on Lab
Page | 63
4. In the MainPage.xaml, use the MapItemsControl to add a new layer for the itineraries,
right below the routed layer, and bind it with the Itineraries collection.
XAML
<my:MapItemsControl ItemsSource="{Binding Itineraries}">
</my:MapItemsControl>
5. To display each Itinerary as a custom pushpin, set the MapItemsControl.ItemTemplate
with a new data template that contains a single pushpin.
XAML
<my:MapItemsControl ItemsSource="{Binding Itineraries}">
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin />
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
6. Bind the pushpin with the ItineraryItem.Location property.
XAML
<my:Pushpin Location="{Binding Location}" />
7. To convert the Location property to a GeoCoordinate, create a value converter.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 7 –
LocationGeoCoordinateConverter)
C#
public class LocationGeoCoordinateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object
parameter, CultureInfo culture)
{
var location = (Location)value;
return (GeoCoordinate)location;
}
public object ConvertBack(object value, Type targetType, object
parameter, CultureInfo culture)
Push Notifications Hands-on Lab
Page | 64
{
throw new NotSupportedException();
}
}
8. Create an instance of the value converter inside the DefaultStyles.xaml resource
dictionary located in the Resources\Styles folder.
XAML
<ResourceDictionary
...
xmlns:converters="clr-namespace:UsingBingMaps.Converters"
mc:Ignorable="d">
<converters:LocationGeoCoordinateConverter
x:Key="LocationGeoCoordinateConverter" />
...
</ResourceDictionary>
9. Use the converter inside the binding extension.
XAML
<my:Pushpin Location="{Binding Location,
Converter={StaticResource LocationGeoCoordinateConverter}}"
/>
10. Create a style for displaying the itinerary pushpin as a rounded shape and place it inside
the DefaultStyle.xaml resource dictionary located in the Resources\Styles folder.
XAML
<Style x:Key="MapPoint" TargetType="Ellipse">
<Setter Property="Width" Value="18"/>
<Setter Property="Height" Value="18"/>
<Setter Property="Fill" Value="#FF003664"/>
<Setter Property="Stroke" Value="AliceBlue"/>
</Style>
<Style x:Key="ItineraryPushpinStyle" TargetType="m:Pushpin">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="m:Pushpin">
<Grid Height="20" Width="20">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
Push Notifications Hands-on Lab
Page | 65
<VisualStateGroup.Transitions>
<VisualTransition
GeneratedDuration="0:0:0.1">
<VisualTransition.GeneratedEasingFunction>
<PowerEase
EasingMode="EaseIn"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
<VisualTransition
GeneratedDuration="0:0:0.1" To="Selected">
<VisualTransition.GeneratedEasingFunction>
<PowerEase
EasingMode="EaseIn"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="UnSelected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0"
To="White"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
Storyboard.TargetName="ellipse" d:IsOptimized="True"/>
<DoubleAnimation Duration="0"
To="1.3"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransfo
rm.ScaleX)" Storyboard.TargetName="ellipse" d:IsOptimized="True"/>
<DoubleAnimation Duration="0"
To="1.3"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransfo
rm.ScaleY)" Storyboard.TargetName="ellipse" d:IsOptimized="True"/>
<ColorAnimation Duration="0"
To="#FFF08609"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
Storyboard.TargetName="ellipse_Center" d:IsOptimized="True"/>
<DoubleAnimation Duration="0"
To="1.5"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransfo
rm.ScaleX)" Storyboard.TargetName="ellipse_Center"
d:IsOptimized="True"/>
<DoubleAnimation Duration="0"
To="1.5"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransfo
Push Notifications Hands-on Lab
Page | 66
rm.ScaleY)" Storyboard.TargetName="ellipse_Center"
d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="ellipse" Style="{StaticResource
MapPoint}" Width="20" Height="20" RenderTransformOrigin="0.5,0.5"
Fill="White" Stroke="#FF2C76B7" StrokeThickness="3" >
<Ellipse.RenderTransform>
<CompositeTransform/>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse x:Name="ellipse_Center"
Style="{StaticResource MapPoint}" Width="8" Height="8"
RenderTransformOrigin="0.5,0.5" Fill="Black" Stroke="{x:Null}"
StrokeThickness="2" >
<Ellipse.RenderTransform>
<CompositeTransform/>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
11. Change the pushpin’s style to the new style you’ve just created.
XAML
<my:Pushpin Location="{Binding Location, Converter={StaticResource
LocationGeoCoordinateConverter}}"
Style="{StaticResource ItineraryPushpinStyle}" />
12. To test the results, press F5 and calculate a valid route.
Push Notifications Hands-on Lab
Page | 67
Figure 32
Route with Itineraries
13. To display a list of all itineraries related to the active route, open the MainPage.xaml
and search for a border named DirectionsView. This is a place holder for the itineraries
view. As a child of this border, add a ListBox control and bind it to the Itineraries
collection.
XAML
<ListBox ItemsSource="{Binding Itineraries}" Margin="0,10" />
14. To display the different parts of the itinerary, such as the directions text and distance,
create an item template composed of a horizontal StackPanel, which contains three
TextBlock–one for the direction instructions, one for the distance, and one for the
distance unit.
XAML
Push Notifications Hands-on Lab
Page | 68
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,2.5">
<TextBlock FontFamily="Segoe WP"
FontWeight="Light"
FontSize="20"
Foreground="#FF666666" />
<TextBlock Margin="4,0,0,0"
FontFamily="Segoe WP"
FontWeight="Light"
FontSize="20"
Foreground="#FF1BA1E2" />
<TextBlock Text="mi"
FontFamily="Segoe WP"
FontWeight="Light"
FontSize="20"
Foreground="#FF1BA1E2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
15. Bind the first text block to the Itinerary.Text property.
XAML
<TextBlock FontFamily="Segoe WP"
FontWeight="Light"
FontSize="20"
Foreground="#FF666666"
Text="{Binding Text}" />
Note: Itinerary.Text represents collection of XML elements. Each element represents a
different part of the directions story, such as distance, location name, action (turn left,
right, etc.). To reduce the lab’s complexity, you’ll create a value converter which
converts all elements from the text property into a single line text sentence.
16. Add a new value converter called ItineraryTextConverter to the Converters project
folder.
Note: To implement a value converter class, inherit the class from the IValueConverter
interface and implement its methods by right clicking on the interface name. By doing
this you will implement the Convert and ConvertBack stub methods.
Push Notifications Hands-on Lab
Page | 69
17. In the Convert method, create a StringBuilder used for building the single line text
sentence.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 17 – textBuilder)
C#
var textBuilder = new StringBuilder();
Note: Since Itinerary.Text doesn’t represent a well formatted XML, and instead
provides a collection of elements with no root element, and because the VirtualEarth
prefix is used but is not defined, we're adding a fictitious root element that also maps
the VirtualEarth prefix to a fictitious namespace.
18. Create a string variable with the following content:
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 18 – validXmlText)
C#
string validXmlText = string.Format("<Directives
xmlns:VirtualEarth=\"http://BingMaps\">{0}</Directives>", value);
Note: Since we are dealing with XML and Linq, make sure you add reference to the
following assemblies: System.Xml and System.Xml.Linq. Also make sure that you have
a using statement for System.Linq.
19. Use XDocument to parse the XML, extract all element values, and append each to the
StringBuilder.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 19 – Parse XML)
C#
XDocument.Parse(validXmlText)
.Elements()
.Select(e => e.Value)
.ToList()
.ForEach(v => textBuilder.Append(v));
20. Return the StringBuilder results.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 20 – Return Results)
C#
return textBuilder.ToString();
Push Notifications Hands-on Lab
Page | 70
21. In the DefaultStyles.xaml, add an instance of the converter you’ve created.
XAML
<ResourceDictionary
...
xmlns:converters="clr-namespace:UsingBingMaps.Converters"
...>
<converters:ItineraryTextConverter x:Key="ItineraryTextConverter" />
...
</ResourceDictionary>
22. Go back to the Itinerary item template and use the converter with the binding markup.
XAML
<TextBlock FontFamily="Segoe WP"
FontWeight="Light"
FontSize="20"
Foreground="#FF666666"
Text="{Binding Text,
Converter={StaticResource ItineraryTextConverter}}" />
23. Bind the second TextBlock with the Itinerary.Summary.Distance property.
XAML
<TextBlock Margin="4,0,0,0"
FontFamily="Segoe WP"
FontWeight="Light"
FontSize="20"
Foreground="#FF1BA1E2"
Text="{Binding Summary.Distance}" />
24. To display the itinerary view right after a route was found, in the MainPage.xaml.cs
search the HasDirections property and return true if the Itinerary collection is not
empty.
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 24 – HasDirectories Property)
C#
public bool HasDirections
{
get { return Itineraries.Count > 0; }
}
Push Notifications Hands-on Lab
Page | 71
25. In the CalculateRoute method, call the ShowDirectionsView inside the lambda body,
right after setting the map view.
C#
// Set the map to center on the new route.
var viewRect = LocationRect.CreateLocationRect(routeModel.Locations);
Map.SetView(viewRect);
ShowDirectionsView();
26. To simplify testing, search for the InitializeDefaults, and set the following defaults:
(Code Snippet – Using Bing Maps – Ex 3 Task 2 Step 26 – Set Defaults)
C#
private void InitializeDefaults()
{
Zoom = DefaultZoomLevel;
Center = DefaultLocation;
From = "Microsoft Redmond Building 1, WA";
To = "Microsoft Redmond Building 9, WA";
}
Push Notifications Hands-on Lab
Page | 72
27. To test the final application, press F5 and search for a valid route. The directions view
should pop up automatically.
Figure 33
Bing Maps Final
This concludes the exercise and the lab.
Note: The complete solution for this exercise is provided at the following location:
Source\Ex3-RouteCalculation\End.
Push Notifications Hands-on Lab
Page | 73
Summary
During this lab you’ve learned about the Bing Silverlight Map Control for Windows Phone 7
platform. You’ve created a new account for accessing Bing Maps SOAP Services and created a
private key for your Windows Phone Bing Maps application. You’ve learned about map layers,
pushpins, and route calculations. You’ve created data models related to the map and bound
them to different UI parts. You’ve designed styles and data templates for different parts of the
map and data related to the map.