I’ve been working on a Windows Phone project which includes a lot of pushpins on Bing maps which carry information important to the user. Now, I wanted some form of interaction in the form of a tooltip – user can request more information about particular pushpin by tapping on it. Of course, the word tooltip might be misleading because it usually involves mouse pointer placed above a control for a short period of time before the tooltip gets shown. There is no such thing on Windows Phone like placing a mouse pointer over a control, but the effect can easily be accomplished on tap event. I’ll show you how to do it in this article.
Now, let’s assume that we have a model called PushpinModel which holds the Pushpin object and some information about it, such as Name, Description and Date when it was added to some imaginary database. This is how we could describe such a model:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
using System; using System.ComponentModel; using System.Device.Location; namespace PushpinTooltipSample.Models { public class PushpinModel : INotifyPropertyChanged { private GeoCoordinate _location; private DateTime _datetimeAdded; private String _description; private String _name; public GeoCoordinate Location { get { return _location; } set { if (_location != value) { _location = value; NotifyPropertyChanged("Location"); } } } public DateTime DatetimeAdded { get { return _datetimeAdded; } set { if (_datetimeAdded != value) { _datetimeAdded = value; NotifyPropertyChanged("DatetimeAdded"); } } } public String Description { get { return _description; } set { if (_description != value) { _description = value; NotifyPropertyChanged("Description"); } } } public String Name { get { return _name; } set { if (_name != value) { _name = value; NotifyPropertyChanged("Name"); } } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } } |
Next we could create a ViewModel in which we’ll populate ObservableCollection with some PushpinModels:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
using System; using System.Collections.ObjectModel; using System.Device.Location; using PushpinTooltipSample.Models; namespace PushpinTooltipSample.ViewModels { public class PushpinViewModel { private ObservableCollection<PushpinModel> _pushpins = new ObservableCollection<PushpinModel>(); public ObservableCollection<PushpinModel> Pushpins { get { return _pushpins; } } public PushpinViewModel() { InitializePushpins(); } private void InitializePushpins() { _pushpins.Add(new PushpinModel { Description = "Popular tourist destination in Croatia", Name = "Dubrovnik", Location = new GeoCoordinate(42.642749786377, 18.1106491088867), DatetimeAdded = DateTime.Now }); _pushpins.Add(new PushpinModel { Description = "Diocletian's Palace is in this city", Name = "Split", Location = new GeoCoordinate(43.5069808959961, 16.4417095184326), DatetimeAdded = DateTime.Now.AddMonths(-10) }); _pushpins.Add(new PushpinModel { Description = "Capital of Croatia", Name = "Zagreb", Location = new GeoCoordinate(45.807258605957, 15.9675998687744), DatetimeAdded = DateTime.Now.AddYears(-1) }); _pushpins.Add(new PushpinModel { Description = "Popular for Tvrda, Old Town of the city", Name = "Osijek", Location = new GeoCoordinate(45.5584487915039, 18.6764907836914), DatetimeAdded = DateTime.Now.AddDays(-10) }); } } } |
You’ll notice that you’re missing some references when you try to build the app, so add the following to the project:
|
1 2 |
Microsoft.Phone.Control.Maps System.Device |
We’ll only have the Bing map in MainPage. But first, add the Silverlight for Windows Phone Toolkit to your project. I used NuGet for it. Reference it in the MainPage.xaml:
|
1 |
xmlns:sltkit= "clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" |
Define the map control:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <my:Map CredentialsProvider="CREDENTIALS" Loaded="Map_Loaded"> <my:MapItemsControl Name="mapPins"> <my:MapItemsControl.ItemTemplate> <DataTemplate> <my:Pushpin Background="Blue" Location="{Binding Location}"> </my:Pushpin> </DataTemplate> </my:MapItemsControl.ItemTemplate> </my:MapItemsControl> </my:Map> </Grid> |
In the code behind, in MainPage.xaml.cs, add the following code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class MainPage : PhoneApplicationPage { // Constructor PushpinViewModel _viewModel; public MainPage() { _viewModel = new PushpinViewModel(); InitializeComponent(); } private void Map_Loaded(object sender, RoutedEventArgs e) { mapPins.ItemsSource = _viewModel.Pushpins; } } |
And the result we get is:
Show tooltip on pushpin tap even
In order to show the tooltip on pushpin tap event, we’ll used the ContextMenu available in Silverlight for Windows Phone Toolkit. Add it to pushpin like this:
|
1 2 3 4 5 6 7 8 9 |
<my:Pushpin Background="Blue" Location="{Binding Location}" Tap="Pushpin_Tap"> <sltkit:ContextMenuService.ContextMenu> <sltkit:ContextMenu IsZoomEnabled="False"> <sltkit:MenuItem Style="{StaticResource MenuItemStyle}"/> </sltkit:ContextMenu> </sltkit:ContextMenuService.ContextMenu> </my:Pushpin> |
To show the ContextMenu on Tap event, we’ll use the Pushpin_Tap event handler:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private void Pushpin_Tap(object sender, System.Windows.Input.GestureEventArgs e) { var _ppmodel = sender as Pushpin; ContextMenu contextMenu = ContextMenuService.GetContextMenu(_ppmodel); contextMenu.DataContext = _viewModel.Pushpins.Where (c => (c.Location == _ppmodel.Location)).FirstOrDefault(); if (contextMenu.Parent == null) { contextMenu.IsOpen = true; } } |
The MenuItemStyle should be defined in the App.xaml, and look (for example), like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<Application.Resources> <Style x:Key="MenuItemStyle" TargetType="sltkit:MenuItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="sltkit:MenuItem"> <StackPanel> <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Margin="24,0" FontSize="26"/> <TextBlock Text="{Binding Description}" TextTrimming="WordEllipsis" Margin="24,0" FontSize="22"/> <TextBlock Text="{Binding DatetimeAdded}" TextTrimming="WordEllipsis" Margin="24,0" FontSize="22"/> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </Application.Resources> |
The app now looks like this:
You could even apply style the whole contextmenu to make it look more like a tooltip window. The whole map would then be defined like:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<my:Map CredentialsProvider="CREDENTIALS" Loaded="Map_Loaded"> <my:MapItemsControl Name="mapPins"> <my:MapItemsControl.ItemTemplate> <DataTemplate> <my:Pushpin Background="Blue" Location="{Binding Location}" Tap="Pushpin_Tap"> <sltkit:ContextMenuService.ContextMenu> <sltkit:ContextMenu IsZoomEnabled="False" Style="{StaticResource MenuStyle}"> <sltkit:MenuItem Style="{StaticResource MenuItemStyle}"/> </sltkit:ContextMenu> </sltkit:ContextMenuService.ContextMenu> </my:Pushpin> </DataTemplate> </my:MapItemsControl.ItemTemplate> </my:MapItemsControl> </my:Map> |
And the styles in App.xaml would be:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<Application.Resources> <Style x:Key="MenuItemStyle" TargetType="sltkit:MenuItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="sltkit:MenuItem"> <StackPanel> <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Margin="12,0" FontSize="26" FontWeight="Bold"/> <TextBlock Text="{Binding Description}" TextTrimming="WordEllipsis" Margin="12,0" FontSize="22"/> <TextBlock Text="{Binding DatetimeAdded}" TextTrimming="WordEllipsis" Margin="12,0" FontSize="22"/> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="MenuStyle" TargetType="sltkit:ContextMenu"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Border CornerRadius="8" Margin="24" BorderBrush="Blue" BorderThickness="2"> <Border.Background> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="White" Offset="0.0"/> <GradientStop Color="LightBlue" Offset="0.5"/> </LinearGradientBrush> </Border.Background> <ItemsPresenter /> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </Application.Resources> |
The final result:
This really looks good to me. What do you think? Let me know in comments or on Twitter!



Well performed technically. But you should read up on the style guidelines for metro design. The gradient and rounded corners are chrome that do not add or support the information. Design it more clear and simplistic. Start by reading this: http://ux.artu.tv/?p=179
ANd don’t give up on coding :)
I wanted to show the possibilities, but I agree that it could’ve been more simplistic.
Thanks for your comment! :)
Great tutorial! This was exactly what I was looking for!
Glad to hear it! :)
Your Pushpin_Tap method is way to complex. This will do the job too:
private void OpenContextMenu(object sender, EventArgs e)
{
ContextMenuService.GetContextMenu(sender as DependencyObject).IsOpen = true;
}
I’ve a got a small issue with your code. When I use the MenuStyle for the contextmenu, I get a NullReferenceException when it loads. I have no problem with the MenuItemStyle, but i can’t use the other one without the exception being thrown. Any idea on what I’m doing wrong? hahaha
Are you sure you’re using the right style key?
Using your code as-is, only change I made was remove the Style=”{StaticResource MenuStyle}” from the context menu so it wouldn’t throw the exception, when i add it again, i get the exception when the “tap” event completes, i.e, when the contextmenu should display.
I should’ve mentioned that. It’s not that I get an exception on loading, it’s only when I tap on the pushpin.
I must ask these questions, even though they may seem a long shot:
1. Is the _ppmodel maybe null?
2. Are you sure you instantiated the viewmodel, so it isn’t null?
3. Is the FirstOrDefault giving you a null for the DataContext?
After all, if you don’t find the error, I could upload the solution/project somewhere if you wanted because I tested it (and I’m also using a similar concept in my application), and it works like a charm.
I’d love it if you could upload the project somewhere, it would help me a lot. Thank you
See here:
http://igrali.wordpress.com/2012/02/04/bing-maps-data-bound-pushpins-fade-in-animation/
I want to run something by you, maybe you can help me figure it out…if it’s not too much to ask of course.
In both your app and mine, I’ve noticed that if you click on the pushpin while the map’s still loading, you get a nullreferenceexception. Do you know of any way to prevent that? From what I’ve researched, the error is tied to the contextmenu and its UpdateVisualStates method, something having to do with onApplyTemplate() not being executed at the moment so a few variables are null.
The problem lies in not having access to those parameters so I can’t check is _outerPanel is null, for example.
Do you know any way around this? What I’d like to happen is that unless everything’s “fine” the tooltip is either unresponsive or maybe get a toast saying “map view not loaded” or anything else…
I’ve racked my brain looking for an answer as well as scouring through search engines and still have come up with, well, zilch. So now I look to the experts, such as you hahaha
You seem to know too much to not have tried this, but I’ll still ask – have you tried putting the code inside the tap event handler in a try-catch block?
That should catch the exception, and from there you can display a messagebox or whatever…
To be honest, I hadn’t tried it, and the moment I read your reply, I had a veritable Homer Simpson moment, and by that I mean, “D’OH!!!”.
But after putting the try-catch in place and see the exception somehow skipping it, I realized what was happening. It’s my original concern coming back. If I use the context menu’s style, I get a nullreferenceexception when the menu launches. I’d left the style in place when I was looking for a solution and forgot to remove it.
Now I understand why your project kept throwing me exceptions. I still haven’t found what’s wrong, but considering both your tried and tested project throws the same error as mine, we can safely assume the problem’s on my end. Just don’t know what it is.
Looking through it now, it really is related to the contextmenu styling, if I add any property, such as border, background, margin, anything really, when it opens it throws a NullReferenceException. Need to do some research on this, ’cause it doesn’t make any sense.
No matter where I put the try/catch block for the contextmenu launch, if the nullreferenceexception is launched (like i explained, when you click on the pushpin when the view still hasn’t fully loaded) it’s not captured by the block and goes straight into the app’s unhandled exception event, making it sort of an uncapturable exception.
I’ve used the try/catch in these 2 ways: having the entire IF inside the try, or having just the contextmenu.isopen inside the try (with the catch following the try as normal)
if (contextMenu.Parent == null)
{
contextMenu.IsOpen = true;
}
Any idea on what can be done?
A friend of mine had an issue with weird exceptions on pushpin tap.
He fixed it by updating all the referenced toolkits.
Do you have the latest toolkit version?
I think so, but I might need to double check…I think something related to this issue presents itself when you use Tap insteado Hold, and I’ve read the solution lies in adding a nice little IF to check for nulls in a class in the tooltip source code and recompiling. Since both issues lie in the visual state not being updated, doing that might solve the problem for me…
I’ll have to see what works, updating or editing.
Hi,
How can I put a link inside the tooltip? On click it should open the default browser on the phone. Any solution for this?
Thanks
Using this example, on the MenuItem Style’s control template, add a hyperlinkbutton and set the URL using its NavigateUri property.
Hope this helps,
Kevin Kutlesa
Thanks Kevin. Worked really well.
Ricardo
No problem, glad I could help. With the amount of questions I’ve had, it’s high time I start giving back hahaha
I finally found the solution to my problem and wish to share it here.
My problem was that when the view wasn’t set or changed, interacting with the pushpins threw an exception. After trying anything I could fin I decided to go back to basics and check the documentation, article by article, and decided to start with the map events. There I found my solution, the ViewChangeStart and ViewChangeEnd events, which are triggered when the map view changes.
In the start event I disabled my mapitemscontrol, using its IsEnabled property, so the user couldn’t interact with it. In the end event I re-enabled them.
Hope this helps anyone who’s ever been in the same position as I’ve been.
And igrali, I want to thank you for all the help and patience, really appreciate it.
Well, it seems from your solution that I haven’t been very helpful, but I’m glad you shared your solution with others who may have the same issue. :)
Hi,
Is it possible to share this solution somehow? I still have the same problem.
I think it is a bug in the toolkit 7.1
Regards,
Dawid
There is a problem with using the ContextMenu approach. Sometimes when tapping the pushpin, you get a NulReferenceException. It’s the same problem as in here : http://silverlight.codeplex.com/workitem/9347
Your approach is nice, but I can’t use it because of the random exception. Have you been able to workaround it?
Actually I cheated a bit there, instead of using Tap or Hold events on the tooltip, i used the Click event, which works the same and really didn’t cause any error once I figured out the viewchange start & end event solution.
Give it a try.
The Only thing I had to remove, because it was raising an exception (NullReference) no matter what I did, was the context menu style (MenuStyle in igrali’s code). I haven’t yet figured out why it raised the exception…
As Kevin described you need to extend the ViewChangeStart and ViewChangeEnd events with your own functions which disable/enable the map.
Firstly, define the handlers:
private void disableMapHandler(object sender, MapEventArgs e)
{
Map map = (Map)sender;
map.IsEnabled = false;
}
private void enableMapHandler(object sender, MapEventArgs e)
{
Map map = (Map)sender;
map.IsEnabled = true;
}
Secondly, attach them to your Control, for example, in the Page constructor (assuming you named your control MapControl):
MapControl.ViewChangeStart += new EventHandler(disableMapHandler);
MapControl.ViewChangeEnd += new EventHandler(enableMapHandler);
Many thanks for the fix Kevin.
Hey, no problem, glad I could be of help…
Someone should repost the example adding the fix as part of it hahahahaha
I could repost it, and give you credits for the fix… :)
That works hahahahaha
Hi Kenvin. Can you post your demo. I have a error same you but i can’t fix it. Thank you very much.
With your code I get Error:
XamlParseException occured
Failed to create a ‘System.Type’ from the text ‘sltkit:MenuItem’.
Thats in App.g.i.cs.
It’s something with TargetType in App.xaml.
How to fix this?
Your making a mistake in XAML. It should be working, the code is tested and the whole code is in the post…
I made new project just with the code here and it is still the same XamlParseException pointing at .
Could you upload your project of that one more time ?
Here’s the project https://skydrive.live.com/redir.aspx?cid=c8b1f62c5b2dc515&resid=C8B1F62C5B2DC515!260&parid=C8B1F62C5B2DC515!259&authkey=!AA0equ7I9MM6bi0
The problem was i didn’t have:
xmlns:sltkit=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit”
in App.xaml
easy mistake ;P
glad you got it working ;)
I am having the same prob as Kevin Kutlesa. I cannot understand where to put mapPins.ViewChangeStart += new EventHandler(disableMapHandler);
mapPins.ViewChangeEnd += new EventHandler(enableMapHandler);
can you further expalined. Thank you!
the sample is published on skydrive.
See here for details http://igrali.wordpress.com/2012/02/04/bing-maps-data-bound-pushpins-fade-in-animation/
I’m Running the downloaded pushpinsample1 form skydrive and i got the same NullReferenceExeption.
Have you tried implementing solutions from Kevin Kutlesa and darkesthour ?
How can i Apply http://igrali.com/2011/12/06/making-a-custom-windows-phone-bing-pushpin-from-an-image/ to this page ? cause i want my push pin to be an image rather than a standard default window push pin.
Set the control template of the pushpin in XAML, that should work.
I already did nothing happen same deafult push pin appears
i got it igrali thanks ! :) Yeah
Great! :)
Hi Igrali, I can’t run your demo. when i click to pushpin, it was stoped. i’m a newbie in wp programming. So i hope you can help me. Thank you so much!
error is here
No symbols are loaded for any call stack frame. The source code can not be displayed.
Microsoft.Phone.Controls.Toolkit.dll!Microsoft.Phone.Controls.ContextMenu.UpdateVisualStates(bool useTransitions) + 0x4f bytes
What’s up i am kavin, its my first occasion to commenting anywhere, when i read this article i thought i could also make comment due to this good piece of writing.
Pretty section of content. I just stumbled upon your website
and in accession capital to assert that I get actually enjoyed
account your blog posts. Any way I’ll be subscribing to your feeds and even I achievement you access consistently quickly.
Making sure that supplement provides herbal ingredients extends those benefits.
Research studies on the health benefits of turmeric both on
humans as well as animals are numerous. John’s Medical College, black pepper increases the bioavailability of turmeric by 2000%.