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 selectAdd -> New Item. Select theUser Controltemplate and give it a name, such as “.” Open thefile and update the XAML to the following:
 
<UserControl
    x:Class="DraggablePushpin.DraggablePin"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DraggablePushpin"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://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: DragStartDrag, 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
{
    publicsealedpartialclassDraggablePin : UserControl
    {
        privateMap _map;
        privatebool isDragging = false;
        Location _center;
 
        public DraggablePin(Map map)
        {
            this.InitializeComponent();
 
            _map = map;
        }
 
        ///<summary>
        /// A boolean indicating whether the pushpin can be dragged.
        ///</summary>
        publicbool Draggable { get; set; }
 
        ///<summary>
        /// Occurs when the pushpin is being dragged.
        ///</summary>
        publicAction<Location> Drag;
 
        ///<summary>
        /// Occurs when the pushpin starts being dragged.
        ///</summary>
        publicAction<Location> DragStart;
 
        ///<summary>
        /// Occurs when the pushpin stops being dragged.
        ///</summary>
        publicAction<Location> DragEnd;
 
        protectedoverridevoid 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;
            }
        }
 
        privatevoid 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);
                }
            }
        }
 
        privatevoid 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;
        }
 
        privatevoid 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
 
PublicNotInheritableClassDraggablePin
    InheritsUserControl
 
    Private _map AsMap
    Private isDragging AsBoolean = False
    Private _center AsLocation
 
    PublicSubNew(map AsMap)
        Me.InitializeComponent()
 
        _map = map
    EndSub
 
    '''<summary>
    ''' A boolean indicating whether the pushpin can be dragged.
    '''</summary>
    PublicProperty Draggable() AsBoolean
        Get
            Return m_Draggable
        EndGet
        Set(value AsBoolean)
            m_Draggable = Value
        EndSet
    EndProperty
    Private m_Draggable AsBoolean
 
    ''' <summary>
    ''' Occurs when the pushpin is being dragged.
    ''' </summary>
    Public Drag AsAction(OfLocation)
 
    ''' <summary>
    ''' Occurs when the pushpin starts being dragged.
    ''' </summary>
    Public DragStart AsAction(OfLocation)
 
    ''' <summary>
    ''' Occurs when the pushpin stops being dragged.
    ''' </summary>
    Public DragEnd AsAction(OfLocation)
 
    ProtectedOverridesSub OnPointerPressed(e AsPointerRoutedEventArgs)
        MyBase.OnPointerPressed(e)
 
        If Draggable Then
            If _map IsNotNothingThen
                ''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
            EndIf
 
            Dim pointerPosition = e.GetCurrentPoint(_map)
 
            Dim location AsLocation = Nothing
 
            ''Convert the point pixel to a Location coordinate
            If _map.TryPixelToLocation(pointerPosition.Position, location) Then
                MapLayer.SetPosition(Me, location)
            EndIf
 
            If DragStart IsNotNothingThen
                DragStart(location)
            EndIf
 
            ''Enable Dragging
            Me.isDragging = True
        EndIf
    EndSub
 
    PrivateSub Map_PointerMoved(sender AsObject, e AsPointerRoutedEventArgs)
        ''Check if the user is currently dragging the Pushpin
        IfMe.isDraggingThen
            ''If so, move the Pushpin to where the pointer is.
            Dim pointerPosition = e.GetCurrentPoint(_map)
 
            Dim location AsLocation = Nothing
 
            ''Convert the point pixel to a Location coordinate
            If _map.TryPixelToLocation(pointerPosition.Position, location) Then
                MapLayer.SetPosition(Me, location)
            EndIf
 
            If Drag IsNotNothingThen
                Drag(location)
            EndIf
        EndIf
    EndSub
 
    PrivateSub Map_PointerReleased(sender AsObject, e AsPointerRoutedEventArgs)
        ''Pushpin released, remove dragging events
        If _map IsNotNothingThen
            RemoveHandler _map.ViewChanged, AddressOf Map_ViewChanged
            RemoveHandler _map.PointerReleasedOverride, AddressOf Map_PointerReleased
            RemoveHandler _map.PointerMovedOverride, AddressOf Map_PointerMoved
        EndIf
 
        Dim pointerPosition = e.GetCurrentPoint(_map)
 
        Dim location AsLocation = Nothing
 
        ''Convert the point pixel to a Location coordinate
        If _map.TryPixelToLocation(pointerPosition.Position, location) Then
            MapLayer.SetPosition(Me, location)
        EndIf
 
        If DragEnd IsNotNothingThen
            DragEnd(location)
        EndIf
 
        Me.isDragging = False
    EndSub
 
    PrivateSub Map_ViewChanged(sender AsObject, e AsViewChangedEventArgs)
        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
        EndIf
    EndSub
EndClass


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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DraggablePushpin"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://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
{
    publicsealedpartialclassMainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
 
            MyMap.Loaded += MyMap_Loaded;
        }
 
        privatevoid MyMap_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            DraggablePin pin = newDraggablePin(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);
        }
 
        privatevoid 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
 
PublicNotInheritableClassMainPage
    InheritsPage
 
    PublicSubNew()
        InitializeComponent()
 
        AddHandler MyMap.Loaded, AddressOf MyMap_Loaded
    EndSub
 
    PrivateSub MyMap_Loaded(sender AsObject, e As Windows.UI.Xaml.RoutedEventArgs)
        Dim pin AsNewDraggablePin(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)
    EndSub
 
    PrivateSub Pin_Dragged(location As Bing.Maps.Location)
        CoordinatesTbx.Text = String.Format("{0:N5},{1:N5}", location.Latitude,location.Longitude)
    EndSub
EndClass


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.
 
- Ricky Brundritt, EMEA Bing Maps TSP
DraggablePushpins-Thumbnail.png