If you follow this blog regularly, there is a strong chance that you are familiar with Bing Maps and all the greatness it brings to the web. And, with Windows 8, you can add that same greatness directly into your applications using the Bing Maps SDK for Windows Store Apps. When developing with the Bing Maps SDK for Windows Store Apps you have the option of using JavaScript or Native code (C#, VB or C++). The Native version of the Bing Maps SDK for Windows Store Apps is a new control developed from the ground up for the Windows Store App platform. The native control offers a lot more performance benefits than the JavaScript control. If you have ever used the Bing Maps Silverlight, WPF or Windows Phone controls you will find a lot of similarities between the SDK’s, however, do not assume there is 100% parity between them
In this blog post, we are going to look at how to create a simple application using the C# Bing Maps Windows Store App Native control while using the location services available in Windows 8. If you are looking for more information on the JavaScript control take a look at the Getting Started with Bing Maps Windows Store App (JavaScript) blog post.
Setting up your development environment
To develop Bing Maps Windows Store applications you will need the following installed:
- Windows 8
- Visual Studios 2012 (Get the Express version)
- Bing Maps SDK for Windows Store Apps
You will also need a Bing Maps account and key. If you intend to create a non-trial application, you should create a Basic key for your Windows Store App and read the governing TOU that defines your usage limits.
Setting up the project
To set up the project, open Visual Studios 2012 and create a new project. In the window that opens, select Visual C# -> Windows Store. Select the Blank App template, call the application BingMapsIntro_WinRT_CS and press OK.
A simple project will be created that contains an App.xaml and a MainPage.xaml file along with folders for Assets, CSS, and common style files. The App.xaml.cs file contains the logic for handing the different states of the application. Windows Store applications have three states: not running, running, and suspended. When the state changes, an event is fired, triggering the code in this file. We will not worry about handling the different states in this blog post, but it is good to take note of it as it will become useful later.
Adding Bing Maps to the App
To get started, we will need to add a reference to the Bing Maps SDK. First, right click on the References folder and press Add Reference. Select Windows -> Extensions, and then select Bing Maps for C#, C++ and Visual Basic. If you do not see this option, be sure to verify that you have installed the Bing Maps SDK for Windows Store apps. While you are here, you can also add a reference to the Microsoft Visual C++ Runtime Package as this is required by the Bing Maps SDK when developing using C# or Visual Basic.
You may notice a little yellow indicator on the references that you just added. Bing Maps SDK requires that you set the Active solution platform in Visual Studio to one of the following options:
- C#, Visual Basic: ARM, x86 or x64
- C++: ARM, Win32 or x64
To do this, right click on the Solution folder and select Properties. Then, go to Configuration Properties -> Configuration. Locate your project and under the Platform column and set the target platform. For the purpose of this blog post, I’m going to select x86. Press Ok and the yellow indicator should disappear from your references.
At this point your application should look something like this:
Now we can add a map to our application. To do this, open the MainPage.xaml file and add the Bing Maps SDK as a namespace at the top of the file. Now we can add a Map object to the Grid control and add our Bing Maps key to the credentials properties of the map. We will also give the map a name of MyMap.
<Page x:Class=”BingMapsIntro_WinRT_CS.MainPage” xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″ xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″ xmlns:m=”using:Bing.Maps”>
<Grid Background=”{StaticResource ApplicationPageBackgroundThemeBrush}”> <m:Map Name=”MyMap” Credentials=”YOUR_BING_MAPS_KEY”/> </Grid> </Page> |
If we run the application, we will end up with a Bing map that takes up the whole screen and looks like this.
Using Location Services
We can make use of the Location Services functionality in Window 8 by using the Windows.Devices.Geolocation.Geolocator class. We will use this class to get access the user’s location information. To start, we will open up the MainPage.xaml file and add a checkbox that sits on top of the map to toggle the GPS (or Location services) feature on and off. The XAML for this file should now look like this:
<Page x:Class=”BingMapsIntro_WinRT_CS.MainPage” xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″ xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″ xmlns:m=”using:Bing.Maps”>
<Grid Background=”{StaticResource ApplicationPageBackgroundThemeBrush}”> <m:Map Name=”MyMap” Credentials=”YOUR_BING_MAPS_KEY”/>
<CheckBox Content=”GPS” Click=”GPS_Checked” Width=”60″ Height=”25″ Background=”Gray” Margin=”0,10,100,0″ HorizontalAlignment=”Right” VerticalAlignment=”Top”/> </Grid> </Page> |
We can now begin to add the functionality behind the map. Open the MainPage.xaml.cs file and create two global variables called GpsPushpin and geolocator. In the constructor of the MainPage class we will initialize the GpsPushpin object and add it to the map. To begin, we will set the visibility of the GpsPushpin to collapsed. Your MainPage.xaml.cs file should look like this:
using Bing.Maps; using Windows.Devices.Geolocation; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation;
namespace BingMapsIntro_WinRT_CS { public sealed partial class MainPage : Page { Geolocator geolocator; Pushpin GpsPushpin;
public MainPage() { this.InitializeComponent();
//Create a pushpin for the GPS location and add it to the map GpsPushpin = new Pushpin() { Visibility = Windows.UI.Xaml.Visibility.Collapsed };
MyMap.Children.Add(GpsPushpin); }
protected override void OnNavigatedTo(NavigationEventArgs e) { } } } |
Next, we need to create a method called GPS_Checked which fires when the user clicks on the GPS checkbox. When this method is fired, we will need to verify if the checkbox is actually checked. If checked, we will need to verify that the geolocator object has been initialized. If it hasn’t, then create and initialize it. Once verified, add a position changed event to this object called geolocator_PositionChanged. If the checkbox has been unchecked we will need to remove the position changed event from the geolocator object (if it exists) and hide the GpsPushpin. This method looks like this:
private void GPS_Checked(object sender, RoutedEventArgs e) { CheckBox cb = sender as CheckBox;
if (cb.IsChecked.HasValue && cb.IsChecked.Value) { if (geolocator == null) { //Create an instance of the GeoLocator class. geolocator = new Geolocator(); }
//Add the position changed event geolocator.PositionChanged += geolocator_PositionChanged; } else { if (geolocator != null) { //Remove the position changed event geolocator.PositionChanged -= geolocator_PositionChanged; }
//Hide the GPS pushpin GpsPushpin.Visibility = Windows.UI.Xaml.Visibility.Collapsed; } } |
Now we have to create the geolocator_PositionChanged event handler. This event will be called on a different thread than the map, so we will need to use a Dispatcher to access the same UI thread as the map. Once this is done, we can get the users location from the PositionChangedEventArgs object and use this to update the location of the GpsPushpin.At the same time, we can set the map view so that it is centered over this location. Finally, we need to make the GpsPushpin visible. This event handler will look like this:
private void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args) { // Need to get back onto UI thread before updating location information this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler( () => { //Get the current location Location location = new Location(args.Position.Coordinate.Latitude, args.Position.Coordinate.Longitude);
//Update the position of the GPS pushpin MapLayer.SetPosition(GpsPushpin, location);
//Make GPS pushpin visible GpsPushpin.Visibility = Windows.UI.Xaml.Visibility.Visible;
//Update the map view to the current GPS location MyMap.SetView(location, 17); })); } |
Before we get too far ahead, we need to add the Location services to the capabilities in the package manifest; otherwise, we won’t be able to get any location information from the users device. To do this, simply double click on the Package.appxmanifest file and select the Capabilities tab at the top. Under the Capabilities section, check the Location option.
At this point, we are ready to test the application. If you run the application you will see the check box in the top right hand corner. In the first click, you will be asked to allow the application to share your location. After which, it will zoom and display a pushpin on your location.
Location Accuracy Circle
It is common to display a circle of accuracy in apps that display a user’s location In order to get this,we will need to create the circle. The PositionChangedEventArgs object has an accuracy property which gives an accuracy radius in meters. We can use this radius to make our accuracy circle by creating a User Control called GpsIcon that will mark our user’s location and display our accuracy circle. To do this, right click on the project and select Add -> New Item. Then, select User Control and give it a name of GpsIcon.xaml and press OK.
Start by opening the GpsIcon.xaml file. We are going to use several Ellipse objects to create the icon for the center of the circle as well as our accuracy circle. Since we will need to update our accuracy this point, we will give it a name of AccuracyCircle. Your XAML will look like this:
<UserControl x:Class=”BingMapsIntro_WinRT_CS.GpsIcon” xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″ xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″>
<Canvas> <Ellipse Name=”AccuracyCircle” Fill=”AliceBlue” Opacity=”0.5″ Stroke=”Blue” StrokeThickness=”2″/> <Ellipse Height=”20″ Width=”20″ Fill=”White” Margin=”-10,-10,0,0″/> <Ellipse Height=”14″ Width=”14″ Fill=”Black” Margin=”-7,-7,0,0″/> <Ellipse Height=”10″ Width=”10″ Fill=”Yellow” Margin=”-5,-5,0,0″/> </Canvas> </UserControl> |
Now we can add the logic for this user control to the GpsIcon.xaml.cs file. In the constructor, we will want to take in a Map object. We will need a reference to the map so that we can update the accuracy circle based on the current zoom level. We will also need to create a method for setting the radius of the accuracy circle in meters. Finally, we will need a method to update the radius of the accuracy circle ellipse by converting the radius that is currently in meters to a pixel radius. Putting this all together, the code for the GpsIcon.xaml.cs file will look like this:
using Bing.Maps; using System; using Windows.UI.Xaml.Controls;
namespace BingMapsIntro_WinRT_CS { public sealed partial class GpsIcon : UserControl { private Map _map; private double _radius; private const double EARTH_RADIUS_METERS = 6378137;
public GpsIcon(Map map) { this.InitializeComponent();
_map = map;
//Add a View Changed event to the map to update the accuracy circle _map.ViewChanged += (s, e) => { //Update the accuracy circle UpdateAccuracyCircle(); }; }
public void SetRadius(double radiusInMeters) { //Store the radius value _radius = radiusInMeters;
//Update the accuracy circle UpdateAccuracyCircle(); }
private void UpdateAccuracyCircle() { if (_map != null && _radius >= 0) { //Calculate the ground resolution in meters/pixel //Math based on http://msdn.microsoft.com/en-us/library/bb259689.aspx double groundResolution = Math.Cos(_map.Center.Latitude * Math.PI / 180) * 2 * Math.PI * EARTH_RADIUS_METERS / (256 * Math.Pow(2, _map.ZoomLevel));
//Calculate the radius of the accuracy circle in pixels double pixelRadius = _radius / groundResolution;
//Update the accuracy circle dimensions AccuracyCircle.Width = pixelRadius; AccuracyCircle.Height = pixelRadius;
//Use the margin property to center the accuracy circle AccuracyCircle.Margin = new Windows.UI.Xaml.Thickness(-pixelRadius/2, -pixelRadius/2, 0, 0); } } } } |
It is now time to update our application to make use of this new GpsIcon user control to mark the user’s location. First, we will need to change the type of the GpsPushpin object from Pushpin to GpsIcon, and pass in a reference to the map when initializing this object. Then, we will need to set the radius in the geolocator_PositionChanged event handler. The MainPage.xaml.cs file should now look like this:
using Bing.Maps; using Windows.Devices.Geolocation; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation;
namespace BingMapsIntro_WinRT_CS { public sealed partial class MainPage : Page { Geolocator geolocator; GpsIcon GpsPushpin;
public MainPage() { this.InitializeComponent();
//Create a pushpin for the GPS location and add it to the map GpsPushpin = new GpsIcon(MyMap) { Visibility = Windows.UI.Xaml.Visibility.Collapsed };
MyMap.Children.Add(GpsPushpin); }
private void GPS_Checked(object sender, RoutedEventArgs e) { CheckBox cb = sender as CheckBox;
if (cb.IsChecked.HasValue && cb.IsChecked.Value) { if (geolocator == null) { //Create an instance of the GeoLocator class. geolocator = new Geolocator(); }
//Add the position changed event geolocator.PositionChanged += geolocator_PositionChanged; } else { if (geolocator != null) { //Remove the position changed event geolocator.PositionChanged -= geolocator_PositionChanged; }
//Hide the GPS pushpin GpsPushpin.Visibility = Windows.UI.Xaml.Visibility.Collapsed; } }
private void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args) { // Need to get back onto UI thread before updating location information this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler( () => { //Get the current location Location location = new Location(args.Position.Coordinate.Latitude, args.Position.Coordinate.Longitude);
//Update the position of the GPS pushpin MapLayer.SetPosition(GpsPushpin, location);
//Set the radius of the Accuracy Circle GpsPushpin.SetRadius(args.Position.Coordinate.Accuracy);
//Make GPS pushpin visible GpsPushpin.Visibility = Windows.UI.Xaml.Visibility.Visible;
//Update the map view to the current GPS location MyMap.SetView(location, 17); })); }
protected override void OnNavigatedTo(NavigationEventArgs e) { } } } |
If you run the application and enable the GPS by pressing the GPS checkbox, the map should zoom into the user’s location and display both the location and accuracy circle.
Check out the Developer Code Samples for other interesting examples of how to use the Bing Maps SDK for Windows Store Apps.
-Ricky