When Google pulled the plug for it’s maps for all Windows Phone users two weeks ago, a loud fuss went trough the loyal Windows Phone user and developer base. A couple of days later Google found the plug again and since then we’re able to use the web version of Google Maps from our Windows Phones again.
The discussion around the whole map provider thing reminded me about a an implementation I did a while back for Windows Phone 7. I developed an app that displayed various map provider instead of the provided Bing Maps. It changed the base layer and allowed to display basically all base layer that support a standard URL scheme. Google Maps, Bing Maps and OpenStreetMaps are just a few examples. In this post I would like to show you how to use the Maps control in Windows Phone 8 and change the underlying base layer from Bing Maps to a different one.
Unfortunately this explanation has to start with a disclaimer. In Windows Phone 7 the shipped map control was using Bing Maps as a Base Layer and supported the change of the base layer. In Windows Phone 8 the story is a bit different as the Maps Control that ships with WP8 is a complete rewrite of the control from WP7 and does not support the change of the base layer at this point – although Microsoft said in a forum it’ll support it in a future version. That said, you can still use the “old” map control that has Bing Maps as a base layer in your Windows Phone 8 application. Keep in mind though, that the control is deprecated.
Create a new project
First of all create a new Windows Phone 8 project and add the old Maps control as a reference to your project. You can find the assembly in the WP SDK folder here C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Libraries.
Add maps control to view
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
xmlns:Microsoft_Phone_Controls_Maps="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps" | |
xmlns:MSPCMCore="clr-namespace:Microsoft.Phone.Controls.Maps.Core;assembly=Microsoft.Phone.Controls.Maps" |
The next step is to add the actual map control to the view, by adding the following xaml into your view.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Microsoft_Phone_Controls_Maps:Map | |
Name="MyMap" | |
Grid.Column="1" | |
LogoVisibility="Collapsed" | |
d:LayoutOverrides="GridBox" | |
Margin="0,0,0,2"> | |
<Microsoft_Phone_Controls_Maps:Map.Mode> | |
<MSPCMCore:MercatorMode/> | |
</Microsoft_Phone_Controls_Maps:Map.Mode> | |
</Microsoft_Phone_Controls_Maps:Map> |
By default the map control supports all the known features of a map control like navigating the map, zooming and pinching, and displays the Bing Map as a base layer.
Implement custom base layer as TileSource
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Windows.Input; | |
using System.Windows.Media; | |
using System.Windows.Media.Animation; | |
using System.Windows.Shapes; | |
namespace MapApp | |
{ | |
public enum GoogleType | |
{ | |
Street = 'm', | |
Hybrid = 'y', | |
Satellite = 's', | |
Physical = 't', | |
PhysicalHybrid = 'p', | |
StreetOverlay = 'h', | |
WaterOverlay = 'r' | |
} | |
public class Google : TileSource | |
{ | |
public Google() | |
{ | |
MapType = GoogleType.Street; | |
UriFormat = @"http://mt{0}.google.com/vt/lyrs={1}&z={2}&x={3}&y={4}"; | |
} | |
public GoogleType MapType { get; set; } | |
public override Uri GetUri(int x, int y, int zoomLevel) | |
{ | |
return new Uri( | |
string.Format(UriFormat, (x % 2) + (2 * (y % 2)), | |
(char)MapType, zoomLevel, x, y)); | |
} | |
} | |
} |
Special binding for base layer
The Binding Helper is listed below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Phone.Controls.Maps; | |
using System.Windows; | |
namespace MapApp | |
{ | |
public static class BindingHelpers | |
{ | |
//Used for binding a single TileSource object to a Bing Maps control | |
#region TileSourceProperty | |
// Name, Property type, type of object that hosts the property, method to call when anything changes | |
public static readonly DependencyProperty TileSourceProperty = | |
DependencyProperty.RegisterAttached("TileSource", typeof(TileSource), | |
typeof(BindingHelpers), new PropertyMetadata(SetTileSourceCallback)); | |
// Called when TileSource is retrieved | |
public static TileSource GetTileSource(DependencyObject obj) | |
{ | |
return obj.GetValue(TileSourceProperty) as TileSource; | |
} | |
// Called when TileSource is set | |
public static void SetTileSource(DependencyObject obj, TileSource value) | |
{ | |
obj.SetValue(TileSourceProperty, value); | |
} | |
//Called when TileSource is set | |
private static void SetTileSourceCallback(object sender, DependencyPropertyChangedEventArgs args) | |
{ | |
var map = sender as Map; | |
var newSource = args.NewValue as TileSource; | |
if (newSource == null || map == null) return; | |
// Remove existing layer(s) | |
for (var i = map.Children.Count - 1; i >= 0; i--) | |
{ | |
var tileLayer = map.Children[i] as MapTileLayer; | |
if (tileLayer != null) | |
{ | |
map.Children.RemoveAt(i); | |
} | |
} | |
var newLayer = new MapTileLayer(); | |
newLayer.TileSources.Add(newSource); | |
map.Children.Add(newLayer); | |
} | |
#endregion | |
} | |
} |
To bind to the map, please change the xaml in the view to the following.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Microsoft_Phone_Controls_Maps:Map | |
Name="MyMap" | |
Grid.Column="1" | |
LogoVisibility="Collapsed" | |
d:LayoutOverrides="GridBox" | |
MapAppScope:BindingHelpers.TileSource="{Binding GoogleMap}" | |
Margin="0,0,0,2"> | |
<Microsoft_Phone_Controls_Maps:Map.Mode> | |
<MSPCMCore:MercatorMode/> | |
</Microsoft_Phone_Controls_Maps:Map.Mode> | |
</Microsoft_Phone_Controls_Maps:Map> |
Make sure you create a ViewModel, bind that to your View and have a property called GoogleMap in your ViewModel that is of type Google – the type of the custom TileSource implementation.
In our implementation we choose Google Street as map type. Google offers Satellite, Hybrid and others as well. You can change those by swapping the map type in the constructor of the Google Map TileSource implementation.
Adding another base layer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Phone.Controls.Maps; | |
using System; | |
using System.Net; | |
using System.Windows; | |
using System.Windows.Controls; | |
using System.Windows.Documents; | |
using System.Windows.Ink; | |
using System.Windows.Input; | |
using System.Windows.Media; | |
using System.Windows.Media.Animation; | |
using System.Windows.Shapes; | |
namespace MapApp | |
{ | |
public class Mapnik : TileSource | |
{ | |
public Mapnik() | |
{ | |
UriFormat = "http://{0}.tile.openstreetmap.org/{1}/{2}/{3}.png"; | |
} | |
private readonly static string[] TilePathPrefixes = new[] { "a", "b", "c" }; | |
public override Uri GetUri(int x, int y, int zoomLevel) | |
{ | |
if (zoomLevel > 0) | |
{ | |
var url = string.Format(UriFormat, TilePathPrefixes[y % 3], zoomLevel, x, y); | |
return new Uri(url); | |
} | |
return null; | |
} | |
} | |
} |
The map control now displays openstreetmap tiles.
Do you know if it is possible to use your own images from isostorage within Windows 8 map control TileSource? It was not possible on WP7.
AntwortenLöschenThe idea is to provide offline functionalities. Who knows... maybe in-app map purchasing ;)
Hi Pablo, since the control I am using in this post is the 'old' control from Bing Maps Control from 7.1 you can't load images from isolated storage.
LöschenThe new maps control that ships with WP8 does not allow to use a custom TileSource unfortunately.
I would be interested to hear about loading from isostore as well.
AntwortenLöschenHi Steve, since the control I am using in this post is the 'old' control from Bing Maps Control from 7.1 you can't load images from isolated storage as this control doesn't allow to load from isostore.
LöschenThe new maps control that ships with WP8 does not allow to use a custom TileSource unfortunately.
This article may be of interest:
AntwortenLöschenhttp://kodira.de/2013/01/offline-maps-with-windows-phone-8/
Hi, can you provide the full source code?
AntwortenLöschen