Implementing a DataTemplateSelector for BingMap Pushpins (of course while using MVVMLight)

You’ll find a lot of examples for DataTemplateSelectors with Listboxes, but you hardly find any with Pushpins for the Bing Maps Control.
After searching for hours but not finding any, I decided to figure it out myself and let “the world” know how I did it.
In fact setting it all up wasn’t difficult so difficult, in fact to get most stuff done I followed this “guide” at Windows Phone Geek.
ScreenshotAfter an hour or so I had all basic ingredients to make it work, but then came the hardest part : finding the correct XAML syntax to display the PushPins correctly.
At first I had pushpins in pushpins, to get rid of those it took about a complete day of trail and error, until I found the missing piece.

Ok, here we go:

For this example I want my map to display a yellow pin with a little man on, which represents my location, a blue pin which displays the name of the location, and a red pin which also displays a name of the location.

First, like the “guide” instructs us, we need the abstract class “DataTemplateSelector”, of which we will derrive our “PushPinDataTemplateSelector” class, this class will then override the “SelectTemplate” function.

public abstract class DataTemplateSelector : ContentControl
{
	public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
	{
		return null;
	}

	protected override void OnContentChanged(object oldContent, object newContent)
	{
		base.OnContentChanged(oldContent, newContent);
		ContentTemplate = SelectTemplate(newContent, this);
	}
}

With this class in place we can now make our PushPinDataTemplateSelector. This class will derrive from the abstract class DataTemplateSelector and we will override the SelectTemplate function. This function in fact just returns a type, which can be found in the Bing Map Control’s available templates. That’s how the magic happens in fact.

public class PushPinDataTemplateSelector : DataTemplateSelector
{
	public DataTemplate RedPin     { get; set; }
	public DataTemplate BluePin    { get; set; }
	public DataTemplate MyLocation { get; set; }

	public override DataTemplate SelectTemplate(object item, DependencyObject container)
	{
		switch (item.GetType().ToString())
		{
			case "PushPinDataTemplateSelectorExample.ViewModel.MyLocation":
				return MyLocation;
			case "PushPinDataTemplateSelectorExample.ViewModel.BluePin":
				return BluePin;
			case "PushPinDataTemplateSelectorExample.ViewModel.RedPin":
				return RedPin;
			default:
				break;
		}

		return base.SelectTemplate(item, container);
	}
}

So, this DataTemplateSelector derrivate is now ready to be used in our XAML.
We still need some other classes to make it work, first a base class of which we will derrive our pushpin types. The structure of this base class depends largly of what you want to display on/as your pushpins. Here’s an example of a “PinLocationBase” class, important is of course the “Location” property which is a GeoCoordinate that holds the position on the map. I added an extra property called Name because I want to display a name in the PushPin.

public class PinLocationBase
{
	public GeoCoordinate Location { get; set; }
	public string        Name     { get; set; }
}

I put this class always in the ViewModel, because most of the time this will be only used for that one page that contains a Bing Map Control, if you have more than one page with a map on, you probably want to move it to your Models.
Now you need some derrived classes of your PinLocationBase class so you can distinguish between your location, a blue pin location or a red pin location. This is very easy since it are actually just aliasses of the PinLocationBase class.

public class MyLocation : PinLocationBase { }
public class BluePin : PinLocationBase { }
public class RedPin : PinLocationBase { }

You can put these 3 classes in the same file as the base class.
Your ViewModel, finally, should contain a property that returns a List, you can use the “mvvminpc” snippit for that, like so:

///
<summary> /// The property's name.
/// </summary>
public const string ListOfPinLocationsPropertyName = "ListOfPinLocations";

private List _listOfPinLocations = null;

///
<summary> /// Gets the ListOfPinLocations property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public List ListOfPinLocations
{
	get
	{
		return _listOfPinLocations;
	}

	set
	{
		if (_listOfPinLocations == value)
		{
			return;
		}

		var oldValue = _listOfPinLocations;
		_listOfPinLocations = value;

		// Update bindings, no broadcast
		RaisePropertyChanged(ListOfPinLocationsPropertyName);
	}
}

You’ll need to fill the List with either MyLocation classes, BluePin classes and/or RedPin classes. I noticed that the best was is to populate a temporary List and then assigning this temporary list to the ListOfPinLocations property.

And now comes what was for me the hardest part : getting the XAML right so it works as wanted, and it doesn’t display a pin in a pin or just nothing. Anyway, here’s some copy+paste XAML for you… you can change the templates to whatever you like.

<BingMap:Map x:Name="MyMap" LogoVisibility="Visible" CopyrightVisibility="Collapsed" ScaleVisibility="Visible" CredentialsProvider="{StaticResource BingMapsAPIKey}" ZoomLevel="10" Culture="{Binding System.Threading.Thread.CurrentThread.CurrentCulture.Parent}" Margin="12,0,12,0">
	<BingMap:MapItemsControl ItemsSource="{Binding ListOfPinLocations}">
		<BingMap:MapItemsControl.ItemTemplate>
			<DataTemplate>
				<BingMap:Pushpin Location="{Binding Location}">
					<BingMap:Pushpin.Template>
						<ControlTemplate>
							<local:PushPinDataTemplateSelector Content="{Binding}">
								<local:PushPinDataTemplateSelector.MyLocation>
									<DataTemplate>
										<Grid>
											<StackPanel Orientation="Vertical">
												<Grid Background="#FFDD2C"
										  		      HorizontalAlignment="Center"
										  		      MinHeight="31"
										  		      MinWidth="17">
													<Image Height="29"
													       Source="pushpins/man.png"
													       Stretch="None"
													       Width="15"
													       HorizontalAlignment="Center"/>
												</Grid>
												<Polygon Fill="#FFDD2C"
												         Points="0,0 17,0 0,15"
												         Width="17"
												         Height="15"
												         HorizontalAlignment="Left"/>
											</StackPanel>
										</Grid>
									</DataTemplate>
								</local:PushPinDataTemplateSelector.MyLocation>
								<local:PushPinDataTemplateSelector.BluePin>
									<DataTemplate>
										<Grid>
											<StackPanel Orientation="Vertical">
												<Grid Background="#003974"
												      HorizontalAlignment="Left"
												      MinHeight="31"
												      MinWidth="29">
													<TextBlock Text="{Binding Name}"
														 Margin="4,4,4,4"
														 HorizontalAlignment="Left"
														 Foreground="White"/>
												</Grid>
												<Polygon Fill="#003974"
												         Points="0,0 29,0 0,29"
												         Width="29"
												         Height="29"
												         HorizontalAlignment="Left"/>
											</StackPanel>
										</Grid>
									</DataTemplate>
								</local:PushPinDataTemplateSelector.BluePin>
								<local:PushPinDataTemplateSelector.RedPin>
									<DataTemplate>
										<Grid>
											<StackPanel Orientation="Vertical">
												<Grid Background="#743900"
												      HorizontalAlignment="Left"
												      MinHeight="31"
												      MinWidth="29">
													<TextBlock Text="{Binding Name}"
														 Margin="4,4,4,4"
														 HorizontalAlignment="Left"
														 Foreground="White"/>
												</Grid>
												<Polygon Fill="#743900"
												         Points="0,0 29,0 0,29"
												         Width="29"
												         Height="29"
												         HorizontalAlignment="Left"/>
											</StackPanel>
										</Grid>
									</DataTemplate>
								</local:PushPinDataTemplateSelector.RedPin>
							</local:PushPinDataTemplateSelector>
						</ControlTemplate>
					</BingMap:Pushpin.Template>
				</BingMap:Pushpin>
			</DataTemplate>
		</BingMap:MapItemsControl.ItemTemplate>
	</BingMap:MapItemsControl>
	<BingMap:Map.Center>
		<System_Device_Location:GeoCoordinate Altitude="NaN" Course="NaN" HorizontalAccuracy="NaN" Latitude="51.0634402162154" Longitude="4.34346499605191" Speed="NaN" VerticalAccuracy="NaN" />
	</BingMap:Map.Center>
</BingMap:Map>

Basically these are all the ingredients you need to make it work… not that difficult, and a lot like the “guide” instructs, like I said, too bad that you can find a lot of ListBox examples, but I needed a Bing Maps Control example which I just couldn’t find.

I’ve added a sample project, so you can see it in action.

Advertisements

8 thoughts on “Implementing a DataTemplateSelector for BingMap Pushpins (of course while using MVVMLight)

  1. Yep, add some sceenshots… and maybe abstract this example in one small demo project! So you can attach this demo project to this post 🙂

  2. I don’t even know how I ended up here, but I thought this post was good. I don’t know who you are but definitely you are going to a famous blogger if you are not already 😉 Cheers!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s