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

{

    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