Universal Apps are a pretty hot topic in the world of Windows app development. Universal apps allow you to build an app for Windows 8.1 and Windows Phone 8.1 while, at the same time allowing you to share code, user controls, styles, strings, and other assets between the two projects in Visual Studio. This saves time having to develop code for each platform. There are a few caveats to be aware of. The main caveat is that not all controls available in Windows Store apps are available to Windows Phone apps and vice-versa. Also, at the time of writing this blog the Universal app template in Visual Studio is not available for Visual Basic apps. You can find some useful documentation on creating universal apps on MSDN here.
Recently on the Bing Maps forums there has been a lot of questions around maps and universal apps. The primary concern being that many users want to create a XAML control in the shared part of the universal App and have it include a map. The main road block developers are running into is that the Bing Maps Windows Store SDK uses the namespace Bing.Maps, while the maps in the Windows Phone SDK use the namespace Windows.UI.Xaml.Controls.Maps. To make things a bit more complicated the map control in the Bing Maps Windows Store SDK is called Map while in the Windows Phone SDK it’s called MapControl. This makes it pretty much impossible to reference these map controls in a single shared XAML control. But not all is lost, there is a way to get around this limitation. In this blog post we are going to take a look at how to make use of maps inside the shared project of a universal app.
If the mapping functionality you are adding to your app is fairly basic you may want to simply launch the built in maps app on the device rather than creating a custom mapping solution. If this is a case take a look this blog post on how to launch the maps app from a universal app.
To get started open Visual Studio and create a new Universal App project in C#. Select the Blank App template, call the application SharedMapSample, and then press OK.
When the solution loads you will see three projects. The first two will be Windows and Windows Phone projects. The third project is a shared project used by the first two projects.
To keep things simple we are going to share the MainPage control. To do this drag and drop the MainPage.xaml and MainPage.xaml.cs files from either of the first two projects into the shared project. Next delete the references to these files in the first two projects. By doing this both apps will use the same MainPage control. You solution should now look like this:
Next, add a reference to the Bing Maps SDK to the Windows Store app. Right click on the References folder in the Windows Store app 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 Windows Store SDK. While you are here, 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. Press OK.
In Solution Explorer, set the Active solution platform of the Windows Store app in Visual Studio by right clicking on the Solution folder and selecting Properties. Select Configuration Properties → Configuration. Find the Windows Store project and under the Platform column, set the target platform to x86, and press OK.
Using Conditional Compilation Symbols
One really powerful feature we have in the shared project of a universal app is being able to access libraries that are referenced by the Windows Store and Windows Phone app. If both projects reference libraries that have the same named class in the same namespace, you will be able to use this in the shared app without having to do anything special. However, if the libraries are different, as is the case when using the Bing Maps SDK in the Windows Store app and the built in maps in the Windows Phone SDK, then the classes will not work in both projects. To get around this we can use conditional compilation symbols in our code to separate and handle the differences between the libraries. Conditional compilation symbols are “if” statements that are interpreted by the compiler in Visual Studios at compile time. These symbols allow us to specify which blocks of code to compile based on which symbols are defined in the project. One of the most common conditional compilation symbols used is DEBUG, which allows us to specify a block of code that will be compiled when Visual Studios is in debug mode. Here is an example of how these symbols can be used.
In universal apps there are two conditional compilation symbols already defined for us to make things easy, WINDOWS_APP and WINDOWS_PHONE_APP. If we wanted we could define additional compilation symbols in the Build section of the project properties, but that’s needed for this app.
Before we jump into creating the map functionality we are going to create some extensions to help make things easier. In the Bing Maps Windows Store SDK coordinates are represented using a class called Location. In the Windows Phone maps SDK coordinates are represented using the BasicGeoposition and Geopoint classes and are in the Windows.Devices.Geolocation namespace which is available to both Windows and Windows Phone. As such it makes sense to create a few extensions to easily convert between these classes in our Windows app. There are a couple of ways to do this, we could add the extensions to the Windows app, or we could add it to the shared project and use conditional compilation symbols. Since we may want to add other extensions to the app in the future we will add an extension class to the shared project. To do this right click on the shared project and select Add -> New Item and create a new class file called Extensions.cs. Update this file with the following code:
Wrapping the Map Controls
Conditional compilation symbols are great for working around platform specific dependencies in blocks of code, but this doesn’t address our main issue of using the map control in XAML. Since the map controls for Windows and Windows Phone have different names and namespaces we simply can’t use them in a shared XAML file. To get around this we can create a XAML control that wraps both map controls. If we create a XAML control class that inherits from a common control that is available Windows and Windows Phone, such as the Grid control, we can then use conditional compilation symbols in our code to add the respective map control as a child of our XAML control. To do this create a new class in the shared project called MapView. When this class is created we will use conditional compilation symbols to create an instance of the appropriate map control and add it as a child of our class. Update the MapView.cs file with the following code.
To make use of this class open the MainPage.xaml file and update it with the following XAML.
If you run either app you should see a full screen map should load up. This is a good starting point but we want to do more than just load a map. We need to be able do things like set the center and zoom level of the map, turn on the traffic layer, and credentials. There are a lot of little differences between the two map controls when it comes to doing these things. We can wrap these functionalities and use conditional compilation symbols to help us out. While we are at it we will make the MapView class inherit from the INotifyPropertyChanged class so we can make some of the properties bindable via XAML. We will also create a method for setting the map view as well. To do all this update the MapView class with the following code.
We can now set the zoom level and show or hide the traffic layer of the map right in XAML like so.
Adding Pushpins and Shapes to the Map
A map without content can be pretty boring. There are several different types of content that can be added to the map, the most common are pushpins, polylines, and polygons. In the Bing Maps Windows Store SDK there is a Pushpin class which we can use to create our pushpins, but for the maps in the Windows Phone SDK we have to create a UIElement to use as a pushpin. To keep things simple we will create a method for adding pushpins to the map that will take in a location and some text. The text will be overlaid on top of the pushpin. This is useful for numbering pushpins so that you can visually link them to content that may be displayed outside of the map. Add the following method to the MapView class.
Polylines and Polygons are very similar in both map controls, however there are some slight differences to be aware of. The Bing Maps SDK uses the Color class to define the Fill and Stroke properties of shapes, whereas the maps in Windows Phone SDK use brushes. Also, the Bing Maps SDK does not provide a stroke property for polygons. One of the biggest differences when it comes to polylines and polygons in the maps is how they are added to the map. In Bing Maps we have to add a MapShapeLayer to the ShapeLayers property of the map and then add our shapes to the new MapShapeLayer. For the maps in the Windows Phone SDK we add our shapes to the MapElements property of the map. In the MapView class add a private MapShapeLayer property called _shapeLayer and add it to the map in the constructor of the MapView class like so.
Next add the following methods to the MapView class for adding and polylines and polygons to the map.
To help us out we will also create a simple method for clearing all the pushpins and shapes on the map. Add the following method to the MapView class.
At this point our MapView class has lots of useful functionality, now it’s just a matter of implementing it. In our app we will create an AppBar that has a bunch of buttons for testing out the different functionalities we created. Open the MainPage.xaml file and update it with the following XAML.
Next we need to add the event handlers for all the buttons in the app bar. Open up the MainPage.xaml.cs file and update it with the following code.
At this point our application is complete. If you run the Windows app and press the buttons to “Go to London”, show traffic and add a polygon you will end up with a map that looks something like this.
If you run the Windows Phone app and press the same buttons you will end up with a map that looks something like this.
Wrapping Things Up
In this blog post we have seen how we can make use of maps in the shared project of a universal app. The full source code for this blog with some extended functionalities can be found on the MSDN Code Sample Gallery here. In this blog we have taken a look at one approach on how to use maps in universal apps. For a similar but somewhat different approach take a look at this video on Channel 9.
If you want to take things further you may want to make the map view as reusable as possible. One way to go about this is to create two class libraries, one for Windows and the other for Windows Phone. Add the MapView class to one of the libraries, then from the other project add it as a linked file. This will give you one file to maintain between the two projects that has the same class name and namespace. If you then reference these projects from the Windows and Windows Phone projects in the Universal app respectively you will be able to use the MapView class inside the shared project. We could even go further and create a wrapper for the Bing Maps WPF control as well if we wanted to have even more cross platform support. In fact this has been done in the Microsoft Maps Spatial Toolbox library on CodePlex. This library exposes a bunch of functionalities such as heat maps, pushpin clustering and support for importing various spatial data formats. This library can be used in Windows 8.1, Windows Phone 8.x and WPF apps.