Editing from the code behind

2331
2
12-13-2010 03:08 PM
by Anonymous User
Not applicable
Here's just a quick post sharing my experience web editing.

In the past weeks, I�??ve noticed people looking through support channels for a deeper discussion on web editing with Silverlight.  A few people I talked to wanted more details on how to edit features from the code behind.  So to that end, I did research and came up with some editing code.

In the code, I use the FeatureLayer's graphics property to add features (graphics) to the Feature Service. My no frills customization is that I add an �??Insert Failed!�?� or �??Successfully Saved!�?� message.  This message is triggered using the  SaveEditsFailed and EndSaveEdits events respectively.  I did it because I like success or failed messages.  I also used the Silverlight�??s System.Windows.Threading.DispatcherTimer() object to clear these messages from the page after 5 seconds.  Just to note, I did not use the Editor class for this use case.

When planning for new tools, it�??s important to consider carefully when it�??s worth investing time and effort to develop a tool that performs custom editing on FeatureLayer in the code behind.  This is because the API comes with efficient methods to do editing using the Editor Class.  The Editor Class makes it possible to Add, Delete, and EditVertices straight from the XAML, plus more.  In review, I counted the lines of code necessary to implement an �??Add�?� using the Editor class and then compared it to my code written below that works directly with FeatureLayer.  For those who�??ve done this type of analysis, "Add" (for the most part) only involves adding the Editor resource to the XAML and adding the proper CommandParameter to a button or a hyperlink.  From my research, it�??s clear that custom editing using FeatureLayer members takes a lot more coding.  So before embarking on a custom editing job, be sure to understand the requirements and check to see that the wheel has not already been invented somewhere else in the API. 

If anyone has comments or other implementations, please share. 

-XAML-

<UserControl x:Class="SDK_Custom_Editing.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.Resources>
            <esri:SimpleLineSymbol x:Key="DrawLineSymbol" Color="Green" Width="4" />
            <esri:SimpleFillSymbol x:Key="DrawFillSymbol" Fill="#3300FF00" BorderBrush="Green" BorderThickness="2" />
            <esri:SimpleMarkerSymbol x:Key="DefaultMarkerSymbol" Color="Red" Size="12" Style="Circle" />
            <esri:SimpleLineSymbol x:Key="DefaultLineSymbol" Color="Red" Width="4" />
            <esri:SimpleFillSymbol x:Key="DefaultFillSymbol" Fill="#33FF0000" BorderBrush="Red" BorderThickness="2" />
        </Grid.Resources>

        <esri:Map Extent="-13054165,3850112,-13027133,3863559" MouseMove="SanDeigoMap_MouseMove" x:Name="SanDeigoMap">
            <esri:Map.Layers>
                <esri:ArcGISTiledMapServiceLayer Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" x:Name="Tiled"/>
                <esri:FeatureLayer x:Name="EditLayer" Url="http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/HomelandSecurity/operations/FeatureServer/0" AutoSave="False" Mode="OnDemand"/>
                <esri:GraphicsLayer x:Name="GraphicsLayer" />
            </esri:Map.Layers>
        </esri:Map>

        <Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,0">
            <StackPanel Orientation="Horizontal" Margin="10,10,10,10">
                <esri:Toolbar x:Name="MyToolbar" VerticalAlignment="Top" HorizontalAlignment="Left"
                    ToolbarItemClicked="MyToolbar_ToolbarItemClicked">
                        <esri:Toolbar.Items>
                            <esri:ToolbarItemCollection>
                                <esri:ToolbarItem>
                                    <esri:ToolbarItem.Content>
                                    <Canvas>
                                        <Border BorderBrush="Black" BorderThickness="1" Background="LightGray"  Margin="2" CornerRadius="5">
                                            <TextBlock x:Name="insert" Text="Insert" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="4,4,4,4" FontSize="16" Foreground="Black" FontStyle="Italic"/>  
                                    </Border>
                                    </Canvas>
                                </esri:ToolbarItem.Content>
                                </esri:ToolbarItem>
                            </esri:ToolbarItemCollection>
                        </esri:Toolbar.Items>
                    </esri:Toolbar>
            </StackPanel>
        </Canvas>

        <StackPanel VerticalAlignment="Bottom" Margin="10,10,0,0" >
            <TextBlock Text="{Binding StatusTextMessage}" x:Name="StatusTextMessage" HorizontalAlignment="Left" VerticalAlignment="Center" TextWrapping="Wrap" FontWeight="Bold" Margin="0,0,10,0" />
            <TextBlock x:Name="MapCoordsTextBlock" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Map Coords: " TextWrapping="Wrap" FontWeight="Bold" />
        </StackPanel>
    </Grid>
</UserControl>



-Code Behind C#-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client.Symbols;

namespace SDK_Custom_Editing
{
    public partial class MainPage : UserControl
    {
        private TextBlock _textmessage;
        private Symbol _activeSymbol = null;
        private Draw draw { get; set; }
        private FeatureLayer featurelayer { get; set; }
        private System.Windows.Threading.DispatcherTimer timer { get; set; }

        public MainPage()
        {
            InitializeComponent();

            draw = new Draw(SanDeigoMap)
            {
                LineSymbol = LayoutRoot.Resources["DrawLineSymbol"] as LineSymbol,
                FillSymbol = LayoutRoot.Resources["DrawFillSymbol"] as FillSymbol
            };

            draw.DrawComplete += Draw_DrawComplete;
            _textmessage = StatusTextMessage;
        }

        private void SanDeigoMap_MouseMove(object sender, MouseEventArgs e)
        {
            try
            {
                System.Windows.Point screenPoint = e.GetPosition(SanDeigoMap);
                if (SanDeigoMap.Extent != null)
                {
                    MapPoint mapPoint = SanDeigoMap.ScreenToMap(screenPoint);
                    MapCoordsTextBlock.Text = string.Format("Map Coords: X = {0}, Y = {1}",
                    Math.Round(mapPoint.X, 4), Math.Round(mapPoint.Y, 4));
                }
            }
            catch (Exception ex)
            {
                String mes = ex.ToString();
            }
        }

        private void MyToolbar_ToolbarItemClicked(object sender, ESRI.ArcGIS.Client.Toolkit.SelectedToolbarItemArgs e)
        {
            featurelayer = SanDeigoMap.Layers[1] as FeatureLayer;
            switch (e.Index)
            {
                case 0: // First esri toolbar item in toolbar
                    draw.DrawMode = DrawMode.Point;
                    _activeSymbol = LayoutRoot.Resources["DefaultMarkerSymbol"] as Symbol;
                    break;
                case 1:
                    break;
                default:
                    break;
            }
            draw.IsEnabled = (draw.DrawMode != DrawMode.None);
        }


        private void Draw_DrawComplete(object sender, ESRI.ArcGIS.Client.DrawEventArgs args)
        {
            draw.IsEnabled = (draw.DrawMode == DrawMode.None); // Draw one at a time
            featurelayer.EndSaveEdits += Insert_EndSaveEdits;
            featurelayer.SaveEditsFailed += Insert_SaveEditsFailed;

            ESRI.ArcGIS.Client.Graphic graphic = new ESRI.ArcGIS.Client.Graphic()
            {
                Geometry = args.Geometry,
                Symbol = _activeSymbol,
            };

            featurelayer.Graphics.Add(graphic);
            featurelayer.SaveEdits();

            timer = new System.Windows.Threading.DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 0, 5, 0); // 5 seconds 
            timer.Tick += new EventHandler(Insert_TimedMessageEnd);
            timer.Start();
        }


        public void Insert_SaveEditsFailed(object sender, EventArgs e)
        {
            string mes = e.ToString();  // For debugging
            _textmessage.Text = "Insert Failed!";
        }

        public void Insert_EndSaveEdits(object sender, EventArgs e)
        {
            string mes = e.ToString(); // For debugging
            _textmessage.Text = "Successfully Saved!";
        }


        public void Insert_TimedMessageEnd(object o, EventArgs sender)
        {
            _textmessage.Text = "";
            timer.Stop();
        }
    }
}



Regards,
Doug Carroll, ESRI Support Services SDK Team
http://support.esri.com/
0 Kudos
2 Replies
JenniferNery
Esri Regular Contributor
Thank you for your feedback and sample code.

I think that when implementing your own Add, you may also want to consider the Attributes for the feature type you are adding.

Also when dealing with Polygon geometry, you may need to perform AutoComplete, use Freehand draw, correct the orientation of the points (clockwise), and call Simplify to ensure that the geometry is topologically correct.

In your code-snippet, I'm not sure if you want to keep subscribing to the same events (EndSaveEdits and SaveEditsFailed) or create a new DispatcherTimer each time draw is completed.
0 Kudos
by Anonymous User
Not applicable
Thanks Jen.  I went after a different aspect of Editing, specifically updating and deleting features from the code behind.  Even thought it's been awhile, I'd thought I share my progress.  I've used a Linq query to "link" the graphic to the Feature in the Feature Service.

Should anyone try this code, just left mouse click on a feature to get started.  This targets the feature for an update or a delete.  The buttons in the application are responsible for doing the graphic selection and linq query.

Hope this helps someone.





<UserControl x:Class="SDK.StandaloneTables"
    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:esri="http://schemas.esri.com/arcgis/client/2009"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.Resources>
            <esri:Editor x:Key="MyEditor" 
                         Map="{Binding ElementName=MyMap}" 
                         LayerIDs="MyFeatureLayer"   
                         GeometryServiceUrl="http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer"/>
        </Grid.Resources>

        <esri:Map x:Name="MyMap">

            <esri:ArcGISTiledMapServiceLayer 
                Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" />

            <esri:FeatureLayer ID="MyFeatureLayer" x:Name="MyFeatureLayer"
                               Url="http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0"
                               MouseLeftButtonUp="FeatureLayer_MouseLeftButtonUp"
                               AutoSave="False"                  
                               DisableClientCaching="True" 
                               OutFields="*"
                               Mode="OnDemand" />
        </esri:Map>

        <Grid HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,15,15,0" >
            <Rectangle Stroke="Gray"  RadiusX="10" RadiusY="10" Fill="#77919191" Margin="0,0,0,5" >
                <Rectangle.Effect>
                    <DropShadowEffect/>
                </Rectangle.Effect>
            </Rectangle>
            <Rectangle Fill="#FFFFFFFF" Stroke="DarkGray" RadiusX="5" RadiusY="5" Margin="10,10,10,15" />
            
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="30,20,30,30" VerticalAlignment="Top" >
                <sdk:Label Height="27" Content="Target the following ObjectId  : " Width="170" Margin="0,10,0,0" />
                <TextBox Height="23" x:Name="MyTextbox" Width="120"  Margin="10,0,0,0"/>
            </StackPanel>
            
            <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="30,65,30,30" VerticalAlignment="Top">
                <Button Height="23" Content="Update" x:Name="Update" Click="Update_Click" Width="100" Margin="5,5,5,5" HorizontalAlignment="Left" />
                <Button Height="23" Content="Delete" x:Name="Delete" Click="Delete_Click" Width="100" Margin="5,5,5,5" HorizontalAlignment="Left" />
            </StackPanel>
        </Grid>


        <Grid HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,15,15,0" >
            <Rectangle Stroke="Gray"  RadiusX="10" RadiusY="10" Fill="#77919191" Margin="0,0,0,5" >
                <Rectangle.Effect>
                    <DropShadowEffect/>
                </Rectangle.Effect>
            </Rectangle>
            <Rectangle Fill="#FFFFFFFF" Stroke="DarkGray" RadiusX="5" RadiusY="5" Margin="10,10,10,15" />

            <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="15,10,30,30" VerticalAlignment="Top">
                <TextBlock Height="23" Margin="5,5,5,5" Name="textBlock1" Text="Left mouse click on a feature to identify:" />
                <sdk:Label x:Name="objectid" Margin="5,5,5,5" HorizontalAlignment="Left" VerticalAlignment="Top"></sdk:Label>
                <sdk:Label x:Name="reqtype" Margin="5,5,5,5" HorizontalAlignment="Left" VerticalAlignment="Top"></sdk:Label>
                <sdk:Label x:Name="address" Margin="5,5,5,5" HorizontalAlignment="Left" VerticalAlignment="Top"></sdk:Label>
                <sdk:Label x:Name="status" Margin="5,5,5,5" HorizontalAlignment="Left" VerticalAlignment="Top"></sdk:Label>
                
            </StackPanel>
        </Grid>



    </Grid>
</UserControl>




using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Linq;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client.Tasks;

namespace SDK
{
    public partial class StandaloneTables : UserControl
    {
        private static ESRI.ArcGIS.Client.Projection.WebMercator _mercator =
            new ESRI.ArcGIS.Client.Projection.WebMercator();

        public StandaloneTables()
        {
            InitializeComponent();

                    ESRI.ArcGIS.Client.Geometry.Envelope initialExtent =
                    new ESRI.ArcGIS.Client.Geometry.Envelope(
                _mercator.FromGeographic(
                    new ESRI.ArcGIS.Client.Geometry.MapPoint(-122.4306073721, 37.7666097907)) as MapPoint,
                _mercator.FromGeographic(
                    new ESRI.ArcGIS.Client.Geometry.MapPoint(-122.4230971868, 37.77197420877)) as MapPoint);

            initialExtent.SpatialReference = new SpatialReference(102100);

            MyMap.Extent = initialExtent;
        }

        private void FeatureLayer_MouseLeftButtonUp(object sender, GraphicMouseButtonEventArgs e)
        {
            FeatureLayer featureLayer = sender as FeatureLayer;

            for (int i = 0; i < featureLayer.SelectionCount; i++)
                featureLayer.SelectedGraphics.ToList().UnSelect();

            MyTextbox.Text = e.Graphic.Attributes["objectid"].ToString();
            objectid.Content = "ObjectId:  " + e.Graphic.Attributes["objectid"].ToString();
            reqtype.Content = "Required Type:  " + e.Graphic.Attributes["req_type"].ToString();
            address.Content = "Address:  " + e.Graphic.Attributes["address"].ToString();
            status.Content = "Status:  " + e.Graphic.Attributes["status"].ToString();
        }

        private void Update_Click(object sender, RoutedEventArgs e)
        {
            FeatureLayer featureLayer = MyMap.Layers["MyFeatureLayer"] as FeatureLayer;

            IEnumerable<Graphic> graphicEnum;
            graphicEnum = (from g in featureLayer.Graphics
                           where g.Attributes["objectid"].ToString() == MyTextbox.Text
                           select g);

            List<Graphic> graphicList = graphicEnum.ToList<Graphic>();
            foreach (ESRI.ArcGIS.Client.Graphic linqedGraphic in graphicList)
            {
                linqedGraphic.Select();
                System.Diagnostics.Debug.WriteLine
                (string.Format("{0}{1}{2}{3}", "Preparing to update objectid ", linqedGraphic.Attributes["objectid"], " that has a current address field set to ", linqedGraphic.Attributes["address"]));

                //Change the address to be 114 South Buena Vista Street
                linqedGraphic.Attributes["address"] = "114 South Buena Vista Street";
                featureLayer.SaveEdits();
                
            }
        }

        private void Delete_Click(object sender, RoutedEventArgs e)
        {
            FeatureLayer featureLayer = MyMap.Layers["MyFeatureLayer"] as FeatureLayer;
            Editor editor = LayoutRoot.Resources["MyEditor"] as Editor;
            
            IEnumerable<Graphic> graphicEnum;
            graphicEnum = (from g in featureLayer.Graphics
                           where g.Attributes["objectid"].ToString() == MyTextbox.Text
                           select g);

            List<Graphic> graphicList = graphicEnum.ToList<Graphic>();
            foreach (ESRI.ArcGIS.Client.Graphic linqedGraphic in graphicList)
            {
                linqedGraphic.Select();
                System.Diagnostics.Debug.WriteLine
                (string.Format("{0}{1}", "Preparing to delete objectid ", linqedGraphic.Attributes["objectid"]));

                if (editor.DeleteSelected.CanExecute(null))
                    editor.DeleteSelected.Execute(null);
            }

        }

    }
}



Regards,
Doug Carroll, ESRI Support Services SDK Team
http://support.esri.com/
0 Kudos