Getting started with Bing Maps Windows Store Apps (Native)

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:

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.

image-1.png

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.

image_thumb1-3.png

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.

image_thumb2-1.png

At this point your application should look something like this:

image_thumb3-1.png

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.

NativeMap

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.

image_thumb4-1.png

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.

gpslocation_thumb-1.jpg

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.

image_thumb5-1.png

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.

image_thumb6-1.png

Check out the Developer Code Samples for other interesting examples of how to use the Bing Maps SDK for Windows Store Apps.

-Ricky