Select to view content in your preferred language

Editing Tables in Feature Service with Silverlight 4

10554
38
01-21-2011 08:34 AM
by Anonymous User
Not applicable
I fielded a question this week that may help other Silverlight 4 developers working with Esri products.  The question was how to add records to a table that lives inside a geodatabase.  While there is probably more then one way to skin this cat, here is one way to do this if you've got the Silverlight Toolkit installed.

In short, I used the Feature Service. If other people have a different implementation feel free to share.





<UserControl x:Class="StandaloneTableEditing.MainPage"
    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"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
    xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

   �??- note the SL toolkit package is used here �?� 

    <Grid x:Name="LayoutRoot" Background="White">

        <Grid x:Name="MyForm">
            <Grid.RowDefinitions>
                <RowDefinition Height="40" ></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <TextBlock Text="Silverlight Standalone Table Editing" Margin="10" FontSize="14" >
            </TextBlock>
            <df:DataForm x:Name="myDataForm" AutoEdit="False" CommandButtonsVisibility="All" Grid.Row="1" Width="400" Height="300" Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top" >
            </df:DataForm>
        </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 ESRI.ArcGIS.Client;
using System.ComponentModel;

//Check to aake sure the System.ComponentModel reference has been added

namespace StandaloneTableEditing
{
    public partial class MainPage : UserControl
    {
        public string Owner { get; set; }
        public int Value { get; set; }
        public string Approved { get; set; }
        public int Lastupdate { get; set; }
        public Inspection inspection { get; set; }
        public FeatureLayer featurelayer { get; set; }

        public MainPage()
        {
            InitializeComponent();
            InitializeFeatureService();
            InitializeInspection();
            myDataForm.CurrentItem = inspection;  // Set up data form with data using the set Inspection Class below
        }


        private void InitializeInspection()
        {
            //Set up default values
            inspection = new Inspection()
            {
                Owner = "David Hasselhoff ",
                Value = 100,
                Approved = "Bay Watch",
                Lastupdate = 1111,
                InspectionFeatureLayer = featurelayer
            };
        }

        public void InitializeFeatureService()
        {
            featurelayer = new FeatureLayer();
            featurelayer.Url = "http://serverbox/ArcGIS/rest/services/EditingTables/FeatureServer/1";
            featurelayer.AutoSave = false;
            featurelayer.Mode = FeatureLayer.QueryMode.OnDemand;
            featurelayer.Initialized += Table_IsInitialized;
            featurelayer.Initialize();
            featurelayer.EndSaveEdits += Insert_EndSaveEdits;
            featurelayer.SaveEditsFailed += Insert_SaveEditsFailed;

        }

        public void Table_IsInitialized(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("it's initialized...");
        }

        public void Insert_SaveEditsFailed(object sender, EventArgs e)
        {
            string mes = e.ToString();  // For debugging
            System.Diagnostics.Debug.WriteLine(mes);
        }

        public void Insert_EndSaveEdits(object sender, EventArgs e)
        {
            string mes = e.ToString(); // For debugging
            System.Diagnostics.Debug.WriteLine(mes);
        }

    }

    public class Inspection : IEditableObject
    {
        public string Owner { get; set; }
        public int Value { get; set; }
        public string Approved { get; set; }
        public int Lastupdate { get; set; }
        public Inspection TempInspection { get; set; }
        public FeatureLayer InspectionFeatureLayer;

        public void BeginEdit()
        {
            // Save current Values
            TempInspection = new Inspection()
            {
                Owner = this.Owner,
                Value = this.Value,
                Approved = this.Approved,
                Lastupdate = this.Lastupdate
            };
        }

        public void CancelEdit()
        {
            // Reset Values
            Owner = TempInspection.Owner;
            Value = TempInspection.Value;
            Approved = TempInspection.Approved;
            Lastupdate = TempInspection.Lastupdate;
        }

        public void EndEdit()
        {

            ESRI.ArcGIS.Client.Graphic graphicAttribute = new ESRI.ArcGIS.Client.Graphic();
            graphicAttribute.Attributes.Add("OWNER", this.Owner);
            graphicAttribute.Attributes.Add("VALUE", this.Value);
            graphicAttribute.Attributes.Add("APPROVED", this.Approved);
            graphicAttribute.Attributes.Add("LASTUPDATE", this.Lastupdate);
            InspectionFeatureLayer.Graphics.Add(graphicAttribute);
            InspectionFeatureLayer.SaveEdits();

        }


    }


}





Regards,
Doug Carroll, ESRI Support Services SDK Team
http://support.esri.com/
0 Kudos
38 Replies
JenniferNery
Esri Regular Contributor
When adding a new graphic to your FeatureLayer (Table), you may need to set default values to its attributes before setting FeatureDataForm.GraphicSource property.

You can use LayerInfo.Fields or FeatureTemplate.PrototypeAttributes, the following will be available after the layer has been initialized:
var graphic = new Graphic();
if (l.LayerInfo == null) return;
//use LayerInfo.Fields 
foreach (var field in l.LayerInfo.Fields)
 graphic.Attributes[field.Name] = null;
//use PrototypeAttributes
FeatureTemplate featureTemplate = null;
if (l.LayerInfo.Templates != null && l.LayerInfo.Templates.Count > 0)
 featureTemplate = l.LayerInfo.Templates.FirstOrDefault().Value;
else if (l.LayerInfo.FeatureTypes != null && l.LayerInfo.FeatureTypes.Count > 0)
{
 var featureType = l.LayerInfo.FeatureTypes.FirstOrDefault();
 if (featureType.Value != null && featureType.Value.Templates != null)
  featureTemplate = featureType.Value.Templates.FirstOrDefault().Value;
}
if (featureTemplate != null && featureTemplate.PrototypeAttributes != null)
{
 foreach (var item in featureTemplate.PrototypeAttributes)
  graphic.Attributes[item.Key] = item.Value;
}
0 Kudos
AjaySisodia2
Emerging Contributor
When adding a new graphic to your FeatureLayer (Table), you may need to set default values to its attributes before setting FeatureDataForm.GraphicSource property.

You can use LayerInfo.Fields or FeatureTemplate.PrototypeAttributes, the following will be available after the layer has been initialized:
var graphic = new Graphic();
if (l.LayerInfo == null) return;
//use LayerInfo.Fields 
foreach (var field in l.LayerInfo.Fields)
 graphic.Attributes[field.Name] = null;
//use PrototypeAttributes
FeatureTemplate featureTemplate = null;
if (l.LayerInfo.Templates != null && l.LayerInfo.Templates.Count > 0)
 featureTemplate = l.LayerInfo.Templates.FirstOrDefault().Value;
else if (l.LayerInfo.FeatureTypes != null && l.LayerInfo.FeatureTypes.Count > 0)
{
 var featureType = l.LayerInfo.FeatureTypes.FirstOrDefault();
 if (featureType.Value != null && featureType.Value.Templates != null)
  featureTemplate = featureType.Value.Templates.FirstOrDefault().Value;
}
if (featureTemplate != null && featureTemplate.PrototypeAttributes != null)
{
 foreach (var item in featureTemplate.PrototypeAttributes)
  graphic.Attributes[item.Key] = item.Value;
}


Thanks Jennifer. very helpful!
0 Kudos
RobChouinard
Frequent Contributor
Thanks Jennifer! Very helpful piece of code. This should be in the Samples under "Editing Tables" 😉

When adding a new graphic to your FeatureLayer (Table), you may need to set default values to its attributes before setting FeatureDataForm.GraphicSource property.

You can use LayerInfo.Fields or FeatureTemplate.PrototypeAttributes, the following will be available after the layer has been initialized:
var graphic = new Graphic();
if (l.LayerInfo == null) return;
//use LayerInfo.Fields 
foreach (var field in l.LayerInfo.Fields)
 graphic.Attributes[field.Name] = null;
//use PrototypeAttributes
FeatureTemplate featureTemplate = null;
if (l.LayerInfo.Templates != null && l.LayerInfo.Templates.Count > 0)
 featureTemplate = l.LayerInfo.Templates.FirstOrDefault().Value;
else if (l.LayerInfo.FeatureTypes != null && l.LayerInfo.FeatureTypes.Count > 0)
{
 var featureType = l.LayerInfo.FeatureTypes.FirstOrDefault();
 if (featureType.Value != null && featureType.Value.Templates != null)
  featureTemplate = featureType.Value.Templates.FirstOrDefault().Value;
}
if (featureTemplate != null && featureTemplate.PrototypeAttributes != null)
{
 foreach (var item in featureTemplate.PrototypeAttributes)
  graphic.Attributes[item.Key] = item.Value;
}
0 Kudos
KenCarrier
Deactivated User
Its nice that we can edit standalone tables but in the same breath shouldn't there be an ability to query within the same form or grid.

What I am trying to do is use the Query Only example and wrap the grid in a Feature Data Grid so that the results are editable.

I am having trouble binding the Feature Data Grid with the Query Only sample.

I think the user should be able to query the table and with the results passed back I should be able to edit as needed.

The only issue with the Feature Data Grid example is the FeatureServer is wrapped inside the esri:Map.

Can Jennifer or someone from Esri provide an example for querying with no map and the results return in a feature data grid that allows editing.

If we are editing a standalone table then there really is no need for a map in my opinion. Great topic and good ideas, hope someone from Esri is watching.
0 Kudos
KenCarrier
Deactivated User
I fielded a question this week that may help other Silverlight 4 developers working with Esri products.  The question was how to add records to a table that lives inside a geodatabase.  While there is probably more then one way to skin this cat, here is one way to do this if you've got the Silverlight Toolkit installed.

In short, I used the Feature Service. If other people have a different implementation feel free to share.





<UserControl x:Class="StandaloneTableEditing.MainPage"
    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"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
    xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

   �??- note the SL toolkit package is used here �?� 

    <Grid x:Name="LayoutRoot" Background="White">

        <Grid x:Name="MyForm">
            <Grid.RowDefinitions>
                <RowDefinition Height="40" ></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <TextBlock Text="Silverlight Standalone Table Editing" Margin="10" FontSize="14" >
            </TextBlock>
            <df:DataForm x:Name="myDataForm" AutoEdit="False" CommandButtonsVisibility="All" Grid.Row="1" Width="400" Height="300" Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top" >
            </df:DataForm>
        </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 ESRI.ArcGIS.Client;
using System.ComponentModel;

//Check to aake sure the System.ComponentModel reference has been added

namespace StandaloneTableEditing
{
    public partial class MainPage : UserControl
    {
        public string Owner { get; set; }
        public int Value { get; set; }
        public string Approved { get; set; }
        public int Lastupdate { get; set; }
        public Inspection inspection { get; set; }
        public FeatureLayer featurelayer { get; set; }

        public MainPage()
        {
            InitializeComponent();
            InitializeFeatureService();
            InitializeInspection();
            myDataForm.CurrentItem = inspection;  // Set up data form with data using the set Inspection Class below
        }


        private void InitializeInspection()
        {
            //Set up default values
            inspection = new Inspection()
            {
                Owner = "David Hasselhoff ",
                Value = 100,
                Approved = "Bay Watch",
                Lastupdate = 1111,
                InspectionFeatureLayer = featurelayer
            };
        }

        public void InitializeFeatureService()
        {
            featurelayer = new FeatureLayer();
            featurelayer.Url = "http://serverbox/ArcGIS/rest/services/EditingTables/FeatureServer/1";
            featurelayer.AutoSave = false;
            featurelayer.Mode = FeatureLayer.QueryMode.OnDemand;
            featurelayer.Initialized += Table_IsInitialized;
            featurelayer.Initialize();
            featurelayer.EndSaveEdits += Insert_EndSaveEdits;
            featurelayer.SaveEditsFailed += Insert_SaveEditsFailed;

        }

        public void Table_IsInitialized(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("it's initialized...");
        }

        public void Insert_SaveEditsFailed(object sender, EventArgs e)
        {
            string mes = e.ToString();  // For debugging
            System.Diagnostics.Debug.WriteLine(mes);
        }

        public void Insert_EndSaveEdits(object sender, EventArgs e)
        {
            string mes = e.ToString(); // For debugging
            System.Diagnostics.Debug.WriteLine(mes);
        }

    }

    public class Inspection : IEditableObject
    {
        public string Owner { get; set; }
        public int Value { get; set; }
        public string Approved { get; set; }
        public int Lastupdate { get; set; }
        public Inspection TempInspection { get; set; }
        public FeatureLayer InspectionFeatureLayer;

        public void BeginEdit()
        {
            // Save current Values
            TempInspection = new Inspection()
            {
                Owner = this.Owner,
                Value = this.Value,
                Approved = this.Approved,
                Lastupdate = this.Lastupdate
            };
        }

        public void CancelEdit()
        {
            // Reset Values
            Owner = TempInspection.Owner;
            Value = TempInspection.Value;
            Approved = TempInspection.Approved;
            Lastupdate = TempInspection.Lastupdate;
        }

        public void EndEdit()
        {

            ESRI.ArcGIS.Client.Graphic graphicAttribute = new ESRI.ArcGIS.Client.Graphic();
            graphicAttribute.Attributes.Add("OWNER", this.Owner);
            graphicAttribute.Attributes.Add("VALUE", this.Value);
            graphicAttribute.Attributes.Add("APPROVED", this.Approved);
            graphicAttribute.Attributes.Add("LASTUPDATE", this.Lastupdate);
            InspectionFeatureLayer.Graphics.Add(graphicAttribute);
            InspectionFeatureLayer.SaveEdits();

        }


    }


}




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


Is there any way to build a query into this form and have the results from the query returned in a grid where the columns are editable?
0 Kudos
JenniferNery
Esri Regular Contributor
Sure, you can bind FeatureDataGrid.GraphicsLayer to a GraphicsLayer that stores results from your QueryTask. FeatureDataForm only works against FeatureLayer, so in that case you need to do the query on FeatureLayer itself. But FeatureLayer does not necessarily have to be tied to a map, as this does not make sense for tables without geometry in the data.

Here's an example:You can update: http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#FeatureDataGrid
After removing binding statements in XAMl for FDG.Map and FDG.GraphicsLayer, they can do something like this.
                 
   var l = new FeatureLayer() { Url = "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/1", Where = "1=1" };
                     l.OutFields.Add("*");
                     MyDataGrid.GraphicsLayer = l;
                     l.Initialized += (s, e) => { l.Update(); };
                     l.Initialize();


You can update l.Where and call l.Update() (similar to running a query). When working with GraphicsLayer, you simply add features from Query_ExecuteCompleted event to your GraphicsLayer and FDG will pickup the Graphics.Attributes. Adding/removing graphics on the layer, should affect FDG. You can also apply FilterSource and do something like FDG.FilterSource = MyGraphicsLayer.Graphics.Where (g => (string) g.Attributes["status"] == "new").ToList(). Using Linq query or QueryTask, you can change the content of FDG.
0 Kudos
KenCarrier
Deactivated User
Sure, you can bind FeatureDataGrid.GraphicsLayer to a GraphicsLayer that stores results from your QueryTask. FeatureDataForm only works against FeatureLayer, so in that case you need to do the query on FeatureLayer itself. But FeatureLayer does not necessarily have to be tied to a map, as this does not make sense for tables without geometry in the data.

Here's an example:You can update: http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#FeatureDataGrid
After removing binding statements in XAMl for FDG.Map and FDG.GraphicsLayer, they can do something like this.
                 
   var l = new FeatureLayer() { Url = "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/1", Where = "1=1" };
                     l.OutFields.Add("*");
                     MyDataGrid.GraphicsLayer = l;
                     l.Initialized += (s, e) => { l.Update(); };
                     l.Initialize();


You can update l.Where and call l.Update() (similar to running a query). When working with GraphicsLayer, you simply add features from Query_ExecuteCompleted event to your GraphicsLayer and FDG will pickup the Graphics.Attributes. Adding/removing graphics on the layer, should affect FDG. You can also apply FilterSource and do something like FDG.FilterSource = MyGraphicsLayer.Graphics.Where (g => (string) g.Attributes["status"] == "new").ToList(). Using Linq query or QueryTask, you can change the content of FDG.


So what function would I put this code in QueryTask_ExecuteCompleted or on the QueryButton_Click Event. Also it looks like you are writing in C# I am having some issue converting your example to VB. Mainly with your initialize statement. Thanks again for the help!
0 Kudos
KenCarrier
Deactivated User
Jennifer I am fairly new to silverlight so please excuse my ignorance but I am not understanding, would you mind including an example with xaml and VB code included for all the functions involved.
0 Kudos
JenniferNery
Esri Regular Contributor
Oh we speak different language 🙂 I usually go to http://www.developerfusion.com/tools/convert/csharp-to-vb/. Here are some Linq Samples in VB: http://msdn.microsoft.com/en-us/vstudio/bb688088. The SDK sample I linked here has a VB tab. Ideally after InitalizeComponent() you can do the VB-version of the code-snippet I sent in previous post. The idea is to create FeatureLayer (from table) in code-behind (not part of Map in XAML). Because it is not part of Map, you are responsible for calling the layer's Initialize() & Update() methods. You will still be setting FeatureDataGrid.GraphicsLayer property but no longer relying on Binding statement as in original SDK sample, instead you set this with your FeatureLayer instance. Try this out first, we can work on using GraphicsLayer+QueryTask later. FeatureLayer is essentially a combination of GraphicsLayer and QueryTask already.
0 Kudos
KenCarrier
Deactivated User
Well if you are a developer then I think we probably speak a similar language, it's called alcohol.

I also use http://www.developerfusion.com/tools/convert/csharp-to-vb/

but when I converted your code I got this;

Dim l = New FeatureLayer() With { _
Key .Url = "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/1", _
Key .Where = "1=1" _
}
l.OutFields.Add("*")
MyDataGrid.GraphicsLayer = l
l.Initialized += Function(s, e) 'this is where the code was showing an error, because I do not think it knew what Function was?
l.Update()

End Function
l.Initialize()

That being said I am following the logic just having trouble with the conversion.

With the latest iteration of the code I can get the FDG to populate but the commit button disappears after executing the query, thoughts?

Here is my VB code:

Imports System.Collections.Generic
Imports System.Windows
Imports System.Windows.Controls
Imports ESRI.ArcGIS.Client
Imports ESRI.ArcGIS.Client.Tasks
Imports ESRI.ArcGIS.Client.Symbols
Imports ESRI.ArcGIS.Client.Toolkit
Imports ESRI.ArcGIS.Client.Geometry

Partial Public Class MainPage
Inherits UserControl
Private Shared _mercator As New ESRI.ArcGIS.Client.Projection.WebMercator()


Private Property featurelayer() As FeatureLayer
Get
Return m_featurelayer
End Get
Set(ByVal value As FeatureLayer)
m_featurelayer = value
End Set
End Property
Private m_featurelayer As FeatureLayer


Public Sub New()
InitializeComponent()
Dim initialExtent As New ESRI.ArcGIS.Client.Geometry.Envelope(TryCast(_mercator.FromGeographic(New ESRI.ArcGIS.Client.Geometry.MapPoint(-122.4545596, 37.783443296)), MapPoint), TryCast(_mercator.FromGeographic(New ESRI.ArcGIS.Client.Geometry.MapPoint(-122.4449924, 37.786447331)), MapPoint))

initialExtent.SpatialReference = New SpatialReference(102100)

MyMap.Extent = initialExtent
initializefeatureservice()

End Sub
Public Sub initializefeatureservice()

Dim featurelayer As New FeatureLayer()
featurelayer.Url = "http://mcesapp/ArcGIS/rest/services/EditRelTbl/FeatureServer/1"
featurelayer.AutoSave = False
featurelayer.Mode = featurelayer.QueryMode.OnDemand

featurelayer.Initialize()

End Sub

Private Sub QueryButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim queryTask As New QueryTask("http://mcesapp/ArcGIS/rest/services/EditRelTbl/FeatureServer/1")
AddHandler queryTask.ExecuteCompleted, AddressOf QueryTask_ExecuteCompleted
AddHandler queryTask.Failed, AddressOf QueryTask_Failed

Dim query As New ESRI.ArcGIS.Client.Tasks.Query()
'query.Text = StateNameTextBox.Text
query.ReturnGeometry = False
Dim s As String
s = StateNameTextBox.Text

'MessageBox.Show(s)
query.Where = "REL_FACILITYID = '" & s & "'"
' query.Where = StateNameTextBox.Text
query.OutFields.Add("*")
queryTask.ExecuteAsync(query)
End Sub

Private Sub QueryTask_ExecuteCompleted(ByVal sender As Object, ByVal args As ESRI.ArcGIS.Client.Tasks.QueryEventArgs)
Dim featureSet As FeatureSet = args.FeatureSet

featureSet.SpatialReference = MyMap.SpatialReference()



If featureSet IsNot Nothing AndAlso featureSet.Features.Count > 0 Then


MyDataGrid.ItemsSource = featureSet.Features
Else
MessageBox.Show("No features returned from query")
End If

Dim graphicsLayer As GraphicsLayer = TryCast(MyMap.Layers("MySelectionGraphicsLayer"), GraphicsLayer)

graphicsLayer.ClearGraphics()

If featureSet IsNot Nothing AndAlso featureSet.Features.Count > 0 Then
For Each feature As Graphic In featureSet.Features
feature.Symbol = TryCast(LayoutRoot.Resources("RedMarkerSymbol"), SimpleMarkerSymbol)
graphicsLayer.Graphics.Add(feature)
Next feature



End If

Dim bind As System.Windows.Data.Binding = New System.Windows.Data.Binding()

bind.ElementName = "MyMap"
bind.Path = New PropertyPath("Layers.[MySelectionGraphicsLayer]")

MyDataGrid.SetBinding(FeatureDataGrid.GraphicsLayerProperty, bind)
MyDataGrid.UpdateLayout()



End Sub

Private Sub QueryTask_Failed(ByVal sender As Object, ByVal args As TaskFailedEventArgs)
MessageBox.Show("Query execute error: " & args.Error.Message)
End Sub


End Class
0 Kudos