Wednesday, May 19, 2010

Moveable graphics with ESRI’s ArcGIS API for Silverlight

image

By default, graphics added to an ESRI Silverlight map are static.  This post will demonstrate how to add map behavior so that graphics can be repositioned with regular mouse interaction.  In some situations, providing moveable graphics is a more intuitive method of achieving user interaction.  For example, you could have a moveable start and end pushpin in a routing application.

In this sample, the web application will create ten Elvis symbols at random locations.  Whenever a graphic is added to the map, the application will begin for listen for mouse events (on the graphic).  When there is a LeftMouseButtonDown event on a graphic, a DragGraphic object is created.  This class stores a reference to the selected graphic and the original position of the graphic and mouse cursor.  If the mouse moves again the graphic or map, information from the DragGraphic class is used to update the selected graphic’s position.

To view a live sample, please click the link below.
http://maps.esri.com/sldemos/mg/default.html

<UserControl
    x:Class="MoveableGraphicsSample.MainPage"
    xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
    xmlns:esriSymbols="clr-namespace:ESRI.ArcGIS.Client.Symbols;
assembly=ESRI.ArcGIS.Client"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid Background="White"> <esri:Map Name="Map"> <esri:Map.Resources> <esriSymbols:MarkerSymbol x:Key="Elvis"> <esriSymbols:MarkerSymbol.ControlTemplate> <ControlTemplate> <Grid> <Ellipse Height="50" Width="50"
Cursor="Hand" Stroke
="White"
StrokeThickness
="2"> <Ellipse.Fill> <!-- see http://commons.wikimedia.org/wiki/File:Elvis_Presley_1970.jpg –> <ImageBrush Stretch="UniformToFill" ImageSource="http://upload.wikimedia.org/wikipedia/commons/8/82/
Elvis_Presley_1970.jpg" /> </
Ellipse.Fill
> <Ellipse.Effect> <DropShadowEffect /> </Ellipse.Effect> <Ellipse.RenderTransform> <TranslateTransform X="-25"
Y
="-25"/> </Ellipse.RenderTransform> </Ellipse> </Grid> </ControlTemplate> </esriSymbols:MarkerSymbol.ControlTemplate> </esriSymbols:MarkerSymbol> </esri:Map.Resources> <esri:Map.Layers> <esri:ArcGISTiledMapServiceLayer
Url
="http://server.arcgisonline.com/ArcGIS/rest/services/
World_Topo_Map/MapServer"/> <
esri:GraphicsLayer
/> </esri:Map.Layers> </esri:Map> </Grid> </UserControl>

…and the code behind….

using System;
using System.Collections.Specialized;
using System.Windows.Controls;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client.Symbols;

namespace MoveableGraphicsSample {
    public partial class MainPage : UserControl {
        private DragGraphic _dragGraphic = null;
        private Random _random = new Random();

        public MainPage() {
            InitializeComponent();

            // Get layers
            ArcGISTiledMapServiceLayer topoLayer = this.Map.Layers[0]
as ArcGISTiledMapServiceLayer; GraphicsLayer graphicsLayer = this.Map.Layers[1]
as GraphicsLayer;
// Wait until the map service layer has loaded and initialized topoLayer.Initialized += (a, b) => { for (int i = 0; i < 10; i++) { graphicsLayer.Graphics.Add( new Graphic() { // Create a random location Geometry = new MapPoint() { X = topoLayer.InitialExtent.XMin +
this._random.NextDouble() *
(topoLayer.InitialExtent.XMax -
topoLayer.InitialExtent.XMin), Y = topoLayer.InitialExtent.YMin +
this._random.NextDouble() *
(topoLayer.InitialExtent.YMax -
topoLayer.InitialExtent.YMin) },
// Use the markersymbol defined in the resource Symbol = this.Map.Resources["Elvis"]
as
MarkerSymbol } ); } }; graphicsLayer.Graphics.CollectionChanged += (a, b) => { // Exit if not added graphic if (b.Action != NotifyCollectionChangedAction.Add) { return;} // Get added graphic Graphic graphic = b.NewItems[0] as Graphic; // When the graphic is clicked, store the current state graphic.MouseLeftButtonDown += (sender, e) => { this._dragGraphic = new DragGraphic() { Graphic = graphic, GraphicOrigin = graphic.Geometry as MapPoint, MouseOrigin = this.Map.ScreenToMap(e.GetPosition(null)) }; e.Handled = true; }; // When the mouse is moved update the graphic's position graphic.MouseMove += (sender, e) => { if (this._dragGraphic == null) { return; } this._dragGraphic.Graphic.Geometry = new MapPoint() { X = this._dragGraphic.GraphicOrigin.X +
(this.Map.ScreenToMap(e.GetPosition(null)).X -
this._dragGraphic.MouseOrigin.X), Y = this._dragGraphic.GraphicOrigin.Y +
(this.Map.ScreenToMap(e.GetPosition(null)).Y -
this._dragGraphic.MouseOrigin.Y) }; };
// Clear the seleced graphic on mouse up graphic.MouseLeftButtonUp += (sender, e) => { this._dragGraphic = null; }; }; // Adjust the selected graphics position if the mouse moves this.Map.MouseMove += (sender, e) => { if (this._dragGraphic == null) { return; } this._dragGraphic.Graphic.Geometry = new MapPoint() { X = this._dragGraphic.GraphicOrigin.X +
(this.Map.ScreenToMap(e.GetPosition(null)).X -
this._dragGraphic.MouseOrigin.X), Y = this._dragGraphic.GraphicOrigin.Y +
(this.Map.ScreenToMap(e.GetPosition(null)).Y -
this._dragGraphic.MouseOrigin.Y) }; }; } } public class DragGraphic { public Graphic Graphic { get; set; } public MapPoint MouseOrigin { get; set; } public MapPoint GraphicOrigin { get; set; } } }

5 comments:

  1. Hello!
    I've had to implement the same (drag'n'drop functionality) for graphic object using ArcGIS JavaScript API, probably it will be usefull for somebody. It's shown in my blog here - http://kiwigis.blogspot.com/2010/05/moveable-graphics-with-esris-arcgis-api.html

    ReplyDelete
  2. Hello!
    I've had to implement the same (drag'n'drop functionality) for graphic object using ArcGIS JavaScript API, probably it will be usefull for somebody. It's shown in my blog here - http://dotnetfollower.com/wordpress/2011/07/arcgis-javascript-api-how-to-implement-dragndropping-of-pushpin/

    ReplyDelete
  3. Hey Richie,
    the link to the demo is broken :(
    Cheers, Mike

    ReplyDelete
  4. Thank You, Very Nice Implementation

    ReplyDelete
  5. Good one buddy.. i will try to make it more generic.

    ReplyDelete