Show a tooltip for tapped pushpin on Windows Phone

Show a tooltip for tapped pushpin on Windows Phone

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:

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:

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:

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:

xmlns:sltkit= "clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"  

Define the map control:

<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:

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:

<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:

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:

    <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:

<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:

    <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!

Igor Ralic

igor ralic

View Comments
Microsoft Certified Solutions Developer: Windows Store Apps in C#