Draggable Pushpins in Bing Maps (.NET)
When building a Bing Maps application, you may want to give the user the ability to drag a pushpin. In the JavaScript version of Bing Maps this can be done by setting the draggable property of a pushpin to true, but the Pushpin class in the .NET version does not have this property, so what can you do?
In this blog post, I’ll show you how to create a reusable user control that gives you draggable pushpins, plus a lot more flexibility in terms of customization.
Some common uses for draggable pushpins include:
- Allowing the user to edit locations on the map by moving pushpins.
- Using draggable pushpins as handles for data points in drawing tools.
Creating the user control
To get started, open Visual Studio and create a new project in C# or Visual Basic. Select the Blank App (XAML) template, name the application and then press OK.
Next, add a reference to the Bing Maps SDK. Right click on the References folder 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 SDK for Windows Store apps. 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 in Visual Studio by right clicking on the Solution folder and selecting Properties. Select Configuration Properties → Configuration. Find your project and under the Platform column, set the target platform to x86, and press OK.
Right click on the solution folder and select Add → New Item. Select the User Control template and give it a name, such as “DraggablePin.” Open the DraggablePin.xaml file and update the XAML to the following:
<UserControl
x:Class="DraggablePushpin.DraggablePin"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DraggablePushpin"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006">
< Ellipse Height="30" Width="30"Fill="Blue"
Stroke ="White" StrokeThickness="5" Margin="-15,-15,0,0"/>
</UserControl>
Now open DraggablePin.xaml.cs or DraggablePin.xaml.vb.
The code-behind for this user control will need a reference to the map control in its constructor and a local reference to it stored in a private variable. This control will use a number of events on the map to enable the dragging functionality.
Next, override the OnPointerPressed event handler to add the required map events for dragging the user control. When the user control is initially pressed, the coordinate of the center of the map will be stored. Next, fill in the ViewChange event of the map to set the center of the map to the stored center value, which will keep the map from panning when dragging the user control.
Set the PointerMovedOverride event of the map to update the position of the user control as it is dragged. Then set the OnPointerReleasedOverride event of the map to release all the events from the map that were used for dragging the user control.
Now add a Boolean property called Draggable, which can be used to disable the dragging functionality of the pushpin. Lastly, expose three Action events: DragStart, Drag, and DragEnd, for use in future applications.
The following example shows the complete code for the user control.
C#
using Bing.Maps;
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
namespace DraggablePushpin
{
public sealed partial class DraggablePin : UserControl
{
private Map _map;
private bool isDragging = false;
Location _center;
public DraggablePin(Map map)
{
this.InitializeComponent();
_map = map;
}
public sealed partial class DraggablePin : UserControl { this.InitializeComponent();
_map = map;
}
/// <summary>
/// A boolean indicating whether the pushpin can be dragged.
/// </summary>
public bool Draggable { get; set; }
/// <summary>
/// Occurs when the pushpin is being dragged.
/// </summary>
public Action<Location> Drag;
/// <summary>
/// Occurs when the pushpin starts being dragged.
/// </summary>
public Action<Location> DragStart;
/// <summary>
/// Occurs when the pushpin stops being dragged.
/// </summary>
public Action<Location> DragEnd;
protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
base.OnPointerPressed(e);
if (Draggable)
{
if (_map != null)
{
//Store the center of the map
_center = _map.Center;
//Attach events to the map to track touch and movement events
_map.ViewChanged += Map_ViewChanged;
_map.PointerReleasedOverride += Map_PointerReleased;
_map.PointerMovedOverride += Map_PointerMoved;
}
var pointerPosition = e.GetCurrentPoint(_map);
Location location = null;
//Convert the point pixel to a Location coordinate
if (_map.TryPixelToLocation(pointerPosition.Position, out location))
{
MapLayer.SetPosition(this, location);
}
if (DragStart != null)
{
DragStart(location);
}
//Enable Dragging
this.isDragging = true;
}
}
private void Map_PointerMoved(object sender, PointerRoutedEventArgs e)
{
//Check if the user is currently dragging the Pushpin
if (this.isDragging)
{
//If so, move the Pushpin to where the pointer is.
var pointerPosition = e.GetCurrentPoint(_map);
Location location = null;
//Convert the point pixel to a Location coordinate
if (_map.TryPixelToLocation(pointerPosition.Position, out location))
{
MapLayer.SetPosition(this, location);
}
if (Drag != null)
{
Drag(location);
}
}
}
private void Map_PointerReleased(object sender, PointerRoutedEventArgs e)
{
//Pushpin released, remove dragging events
if (_map != null)
{
_map.ViewChanged -= Map_ViewChanged;
_map.PointerReleasedOverride -= Map_PointerReleased;
_map.PointerMovedOverride -= Map_PointerMoved;
}
var pointerPosition = e.GetCurrentPoint(_map);
Location location = null;
//Convert the point pixel to a Location coordinate
if (_map.TryPixelToLocation(pointerPosition.Position, out location))
{
MapLayer.SetPosition(this, location);
}
if (DragEnd != null)
{
DragEnd(location);
}
this.isDragging = false;
}
private void Map_ViewChanged(object sender, ViewChangedEventArgs e)
{
if (isDragging)
{
//Reset the map center to the stored center value.
//This prevents the map from panning when we drag across it.
_map.Center = _center;
}
}
}
}
Visual Basic
Imports Bing.Maps
Public NotInheritable Class DraggablePin
Inherits UserControl
Private _map As Map
Private isDragging As Boolean = False
Private _center As Location
Public Sub New(map As Map)
Me.InitializeComponent()
_map = map
End Sub
'''<summary>
''' A boolean indicating whether the pushpin can be dragged.
'''</summary>
Public Property Draggable() As Boolean
Get
Return m_Draggable
End Get
Set(value As Boolean)
m_Draggable = Value
End Set
End Property
Private m_Draggable As Boolean
''' <summary>
''' Occurs when the pushpin is being dragged.
''' </summary>
Public Drag As Action(Of Location)
''' <summary>
''' Occurs when the pushpin starts being dragged.
''' </summary>
Public DragStart As Action(Of Location)
''' <summary>
''' Occurs when the pushpin stops being dragged.
''' </summary>
Public DragEnd As Action(Of Location)
Protected Overrides Sub OnPointerPressed(e As PointerRoutedEventArgs)
MyBase.OnPointerPressed(e)
If Draggable Then
If _map IsNot Nothing Then
''Store the center of the map
_center = _map.Center
''Attach events to the map to track touch and movement events
AddHandler _map.ViewChanged, AddressOf Map_ViewChanged
AddHandler _map.PointerReleasedOverride, AddressOf Map_PointerReleased
AddHandler _map.PointerMovedOverride, AddressOf Map_PointerMoved
End If
Dim pointerPosition = e.GetCurrentPoint(_map)
Dim location As Location = Nothing
''Convert the point pixel to a Location coordinate
If _map.TryPixelToLocation(pointerPosition.Position, location) Then
MapLayer.SetPosition(Me, location)
End If
If DragStart IsNot Nothing Then
DragStart(location)
End If
''Enable Dragging
Me.isDragging = True
End If
End Sub
Private Sub Map_PointerMoved(sender As Object, e As PointerRoutedEventArgs)
''Check if the user is currently dragging the Pushpin
If Me.isDragging Then
''If so, move the Pushpin to where the pointer is.
Dim pointerPosition = e.GetCurrentPoint(_map)
Dim location As Location = Nothing
''Convert the point pixel to a Location coordinate
If _map.TryPixelToLocation(pointerPosition.Position, location) Then
MapLayer.SetPosition(Me, location)
End If
If Drag IsNot Nothing Then
Drag(location)
End If
End If
End Sub
Private Sub Map_PointerReleased(sender As Object, e As PointerRoutedEventArgs)
''Pushpin released, remove dragging events
If _map IsNot Nothing Then
RemoveHandler _map.ViewChanged, AddressOf Map_ViewChanged
RemoveHandler _map.PointerReleasedOverride, AddressOf Map_PointerReleased
RemoveHandler _map.PointerMovedOverride, AddressOf Map_PointerMoved
End If
Dim pointerPosition = e.GetCurrentPoint(_map)
Dim location As Location = Nothing
''Convert the point pixel to a Location coordinate
If _map.TryPixelToLocation(pointerPosition.Position, location) Then
MapLayer.SetPosition(Me, location)
End If
If DragEnd IsNot Nothing Then
DragEnd(location)
End If
Me.isDragging = False
End Sub
Private Sub Map_ViewChanged(sender As Object, e As ViewChangedEventArgs)
If isDragging Then
''Reset the map center to the stored center value.
''This prevents the map from panning when we drag across it.
_map.Center = _center
End If
End Sub
End Class
Implementing the user control in an application
Now that you have a DraggablePin user control, you can now try using it in an app. Open MainPage.xaml and update the XAML to the following:
<Page
x:Class="DraggablePushpin.MainPage"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DraggablePushpin"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:Bing.Maps">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<m:Map Name="MyMap" Credentials="YOUR_BING_MAPS_KEY"/>
< Border Background="Black" HorizontalAlignment="Center" VerticalAlignment="Bottom">
< TextBlock Name="CoordinatesTbx" FontSize="24" Margin="10"/>
</ Border >
</Grid>
</Page>
Open MainPage.xaml.cs or MainPage.xaml.vb.
In the constructor, add a handler to the MapLoaded event of the map. Set the handler to create a DraggablePin and add it to the center of the map. Then attach to the Drag event of the pin and have it update the coordinates displayed in a TextBlock at the bottom of the map, as shown in the following example.
C#
using Windows.UI.Xaml.Controls;
namespace DraggablePushpin
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
MyMap.Loaded += MyMap_Loaded;
}
private void MyMap_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
DraggablePin pin = new DraggablePin(MyMap);
//Set the location of the pin to the center of the map.
Bing.Maps.MapLayer.SetPosition(pin, MyMap.Center);
//Set the pin as draggable.
pin.Draggable = true;
//Attach to the drag action of the pin.
pin.Drag += Pin_Dragged;
//Add the pin to the map.
MyMap.Children.Add(pin);
}
private void Pin_Dragged(Bing.Maps.Location location)
{
CoordinatesTbx.Text = string.Format("{0:N5},{1:N5}", location.Latitude,
location.Longitude);
}
}
}
Visual Basic
Imports Windows.UI.Xaml.Controls
Public NotInheritable Class MainPage
Inherits Page
Public Sub New()
InitializeComponent()
AddHandler MyMap.Loaded, AddressOf MyMap_Loaded
End Sub
Private Sub MyMap_Loaded(sender As Object, e As Windows.UI.Xaml.RoutedEventArgs)
Dim pin As New DraggablePin(MyMap)
''Set the location of the pin to the center of the map.
Bing.Maps.MapLayer.SetPosition(pin, MyMap.Center)
''Set the pin as draggable.
pin.Draggable = True
''Attach to the drag action of the pin.
pin.Drag = AddressOf Pin_Dragged
''Add the pin to the map.
MyMap.Children.Add(pin)
End Sub
Private Sub Pin_Dragged(location As Bing.Maps.Location)
CoordinatesTbx.Text = String.Format("{0:N5},{1:N5}", location.Latitude,
location.Longitude)
End Sub
End Class
Now if you run the application you will see a blue circle pushpin in the center of the map. Simply press down on it and drag it around on the screen using a mouse or your finger if you have a touch screen device.
You can find the complete source code for this blog post in the MSDN Code Sample Gallery. If you are working with the WPF version of the control, I have a similar code sample available here.
Comments
- Anonymous
March 12, 2014
For fast responses to questions try using the Bing Maps forums: social.msdn.microsoft.com/.../home If you are creating a Windows Store app then take a look at my free eBook: rbrundritt.wordpress.com/my-book