Select to view content in your preferred language

Editing Tables in Feature Service with Silverlight 4

8585
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
To add a row to your table, you simply add Graphic to your FeatureLayer.

This is an example for adding to this table: http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/1
  
private void Button_Click(object sender, RoutedEventArgs e)
{
 var g = new Graphic();
 g.Attributes["sf_311_serviceoid"] = 21467;
 g.Attributes["agree_with_incident"] = (Int16) 1;
 g.Attributes["DateTime"] = DateTime.UtcNow;
 g.Attributes["cient_ip"] = "unknown";
 g.Attributes["notes"] = string.Format("Added - {0} - local time", DateTime.Now);
 l.Graphics.Add(g);
}


Another way is to check for Field Type/Length/Domain etc, to decide default value for the field.
private void Button_Click(object sender, RoutedEventArgs e)
{
   if (l.LayerInfo != null && l.LayerInfo.Fields != null)
   {
    var g= new Graphic();    
    foreach (var f in l.LayerInfo.Fields)
    {
     //check f.Type, f.Length to decide default value.
     g.Attributes[f.Name] = f.Nullable ? null : (f.Type == Field.FieldType.Double ? (object)new double() : string.Empty);
    }
   }
}
0 Kudos
KenCarrier
Deactivated User
Jennifer,

So if I am understanding correctly, I could just create a new button next to Do Query in XAML and use code behind to add a blank record to the FDG, then the user could edit and click commit thereby creating a new row in the table?

When I tried converting your C# to VB I receive an error on this line

For Each f As var In l.LayerInfo.Fields

What is var supposed to represent?

VB:
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
 If l.LayerInfo IsNot Nothing AndAlso l.LayerInfo.Fields IsNot Nothing Then
  Dim g = New Graphic()
  For Each f As var In l.LayerInfo.Fields
   'check f.Type, f.Length to decide default value.
   g.Attributes(f.Name) = If(f.Nullable, Nothing, (If(f.Type = Field.FieldType.[Double], DirectCast(New Double(), Object), String.Empty)))
  Next
 End If
End Sub
0 Kudos
PaulLeedham
Deactivated User
Ken,
You may want to use a Feature Data Form to add new records, or create a child window that will appear when you click a button.  If you choose to use the ESRI Feature Data Form, you would need to bind your Feature Data Service, much the same as you did with the Feature Data Grid.  Essentially, all you will be doing after you establish your binding to your Feature Data Service will be adding a graphic or item to your collection.  If you choose to do a child window you can use the text boxes to enter the attributes into your new record. 

Thanks,
0 Kudos
KenCarrier
Deactivated User
Jennifer,

I see what you are trying to do in your code, I figured out var in C# is Object in VB.

Now the issue is I set a breakpoint at the "If l.LayerInfo IsNot Nothing AndAlso l.LayerInfo.Fields IsNot Nothing Then" statement, but it goes straight to the End If statement. So for some reason I am not getting this property. Therefore, it never goes into the If statement.

I have googled around to see if I could find out how to explicitly set this property but I cannot find anything. I am using the same logic as in the other code for initializing the FDG.

Can you tell me why l.LayerInfo would not have a value, this is why I assuming the code never gets into the If Statement becase l.LayerInfo is in fact nothing, I also noticed this through debugging.

Here is my code in VB:
Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim l As ESRI.ArcGIS.Client.FeatureLayer
        l = New ESRI.ArcGIS.Client.FeatureLayer
        l.Url = "http://mcesapp/ArcGIS/rest/services/EditRelTbl/FeatureServer/1"
        'Dim s As String
        's = StateNameTextBox.Text
        'l.Where = "REL_FACILITYID = '" & s & "'"
        l.OutFields.Add("*")
        l.AutoSave = False

        AddHandler l.Initialized, AddressOf layerInit

        l.Initialize()

        If l.LayerInfo IsNot Nothing AndAlso l.LayerInfo.Fields IsNot Nothing Then
            Dim g = New Graphic()
            For Each f As Object In l.LayerInfo.Fields
                'check f.Type, f.Length to decide default value.
                g.Attributes(f.Name) = If(f.Nullable, Nothing, (If(f.Type = Field.FieldType.[Double], DirectCast(New Double(), Object), String.Empty)))
            Next
            l.Graphics.Add(g)
            MyDataGrid.GraphicsLayer = l

        End If

    End Sub
0 Kudos
JenniferNery
Esri Regular Contributor
When FeatureLayer.IsInitialized=True and FeatureLayer.InitializationFailure==null, FeatureLayer.LayerInfo will have a value. The code from post# 16 (I've attached C# code: XAML and code-behind) should come to your Initialized event handler. Can you check that it does? This is the case where FeatureLayer is not part of Map and is created in code-behind.
0 Kudos
KenCarrier
Deactivated User
Jennifer,

I think the conversion is the problem, in VB this line does not make sense "l.Initialized += Function(s, e) "

Has anyone successfully Initialized a FeatureDataGrid in VB? Are there any examples anywhere in VB?

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

Namespace ForumTest
 Public Partial Class MainPage
  Inherits UserControl
  Public Sub New()
   InitializeComponent()
   l = New FeatureLayer() With { _
    Key .ID = "MyLayer", _
    Key .Url = "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/1", _
    Key .Mode = FeatureLayer.QueryMode.Snapshot, _
    Key .Where = "1=1" _
   }
   l.OutFields.Add("*")
   l.Initialized += Function(s, e) 
   If l.InitializationFailure IsNot Nothing Then
    MessageBox.Show(l.InitializationFailure.Message)
   End If

End Function
   l.InitializationFailed += Function(s, e) 
   If l.InitializationFailure IsNot Nothing Then
    MessageBox.Show(l.InitializationFailure.Message)
   End If

End Function
   l.Initialized += New EventHandler(Of EventArgs)(AddressOf l_Initialized)
   l.Initialize()
   MyFDG.GraphicsLayer = l
  End Sub

  Private Sub l_Initialized(sender As Object, e As EventArgs)
   Dim l = TryCast(sender, FeatureLayer)
   l.Update()
  End Sub
  Private l As FeatureLayer

  Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
   Dim g = New Graphic()
   If l IsNot Nothing AndAlso l.LayerInfo IsNot Nothing AndAlso l.LayerInfo.Fields IsNot Nothing Then
    For Each f As var In l.LayerInfo.Fields
     g.Attributes(f.Name) = Nothing
    Next
   Else
    g.Attributes("sf_311_serviceoid") = 21467
    g.Attributes("agree_with_incident") = CType(1, Int16)
    g.Attributes("DateTime") = DateTime.UtcNow
    g.Attributes("cient_ip") = "unknown"
    g.Attributes("notes") = String.Format("Added - {0} - local time", DateTime.Now)
   End If
   l.Graphics.Add(g)
  End Sub
 End Class
End Namespace
0 Kudos
DonKemlage
Esri Contributor
Translating lambda expressions from C# to VB.NET can be a little tricky. Here are few code fragment conversions that should give you a starting point for translating the C# code to VB.NET:

EXAMPLE #1:

C#
l.InitializationFailed += (s, e) =>
{
  if (l.InitializationFailure != null)
    MessageBox.Show(l.InitializationFailure.Message);
};

VB.NET
AddHandler l.InitializationFailed, Sub(s, e)
                                      If l.InitializationFailure IsNot Nothing Then
                                          MessageBox.Show(l.InitializationFailure.Message)
                                      End If
                                   End Sub

EXAMPLE #2:

C#
QueryTask queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5");
queryTask.ExecuteCompleted += (evtsender, args) =>
{
  if (args.FeatureSet == null)
    return;
  _featureSet = args.FeatureSet;
  SetRangeValues();
  RenderButton.IsEnabled = true;
};
queryTask.ExecuteAsync(query);


VB.NET
Dim queryTask As New QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5")
AddHandler queryTask.ExecuteCompleted, Sub(evtsender, args)
                                           If args.FeatureSet Is Nothing Then
                                               Return
                                           End If
                                           _featureSet = args.FeatureSet
                                           SetRangeValues()
                                           RenderButton.IsEnabled = True
                                       End Sub
queryTask.ExecuteAsync(query)

EXAMPLE #3:

C#
geometryService.ProjectCompleted += (evt, a) => 
{
  for (int i=0;i<a.Results.Count; i++)                   
    _geoRssLayer.Graphics.Geometry = a.Results.Geometry;                    
  _geoRssLayer.Visible = true;
};

VB.NET
AddHandler geometryService.ProjectCompleted, Sub(evt, a)
                                                 For i As Integer = 0 To a.Results.Count - 1
                                                    _geoRssLayer.Graphics(i).Geometry = a.Results(i).Geometry
                                                 Next i
                                                 _geoRssLayer.Visible = True
                                             End Sub



The 'Rendering with code' Silverlight interactive sample has a full code example that shows using a lambda expression in both C# and VB.NET. It is located at: http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#Thematic_Interactive


I hope this helps.
-Don
0 Kudos
KenCarrier
Deactivated User
I tried using this example
        AddHandler l.InitializationFailed, Sub(s, e)
                                               If l.InitializationFailure IsNot Nothing Then
                                                   MessageBox.Show(l.InitializationFailure.Message)
                                               End If
                                           End Sub


But I get error;
Lambda parameter 's' hides a variable in an enclosing block, a previously defined range variable, or an implicitly declared variable in a query expression.


Basically I am trying to ensure the layer is initialized properly, so that in order to requery all I would need to do is call l.Update()

But when I call l.Update() within the QueryButton_Click event nothing is returned. To me this means the FDG is not getting initialized properly. Thoughts?

Thank you for the help!
0 Kudos
michaelrosen
Deactivated User
Amplifying on Jennifer's example in #16.  I'm attaching a revised version of ESRI's FeatureDataGrid example which shows
- The ability to add a feature without a Geometry and do this  on a background worker thread.
- The ability to alternate between FDG showing rows associated with the map extent (QueryMode.OnDemand) and showing rows associated with an arbitrary attribute query (QueryMode.SnapShot).  In this case, I find the feature added above.
  - Maintains the ability to edit via the FDG.

I worked with ESRI Tech Support on this. I hope it helps others here.

For the impatient, the FDG hook up is like this:

In the XAML, hook up the FDG to the Map and the appropriate FeatureLayer (just like before, nothing special here):

<esri:FeatureDataGrid Grid.Row="2" x:Name="MyDataGrid"
                Map="{Binding ElementName=MyMap}"
                GraphicsLayer="{Binding ElementName=MyMap, Path=Layers.[1]}" Foreground="Black" Initialized="MyDataGrid_Initialized">
</esri:FeatureDataGrid>



Make a UI (Context menu here) that allows you to set the QueryMode on the FeatureLayer and hook it up to set the FeatureLayer's Mode and Where members and then call Update().

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            ArcGISLocalFeatureLayer featureLayer = MyMap.Layers["States"] as ArcGISLocalFeatureLayer;

            MenuItem mi = sender as MenuItem;
            if (mi.Header.ToString().Contains("Import On BackgroundWorker"))
                ImportTest();
            else if (mi.Header.ToString().Contains("Show Rows in Map Extent (QueryMode.OnDemand)"))
            {
                featureLayer.Mode = FeatureLayer.QueryMode.OnDemand;
                featureLayer.Where = null;
                featureLayer.Update();
            }
            else if (mi.Header.ToString().Contains("Show Rows in for Custom Query (QueryMode.Snapshot)"))
            {
                featureLayer.Mode = FeatureLayer.QueryMode.Snapshot;
                featureLayer.Where = "STATE_NAME like 'gra%'";
                featureLayer.Update();
            }
        }
0 Kudos