Select to view content in your preferred language

Another relate editing post:

4211
17
Jump to solution
05-29-2012 04:11 PM
NathalieNeagle
Regular Contributor
Like a few others I'm trying to set up a relate and I want to be able to add records (rows) to my related table and change attribute infomation...basic editing.  There are a few post and Jennifer and Chris (ESRI) give suggestion(s) to start with the relate example and make the related table a feature services.  I've done both of these tasks but I've been having problems with figurin out how to move forward from this point.  I'm thinking I might have to convert the DataGrid that the example uses to a featuredatagrid.  Would this be the right move? I've tired thi with no success and was hoping for a push forward with some more insight or code.

Thanks
Nathalie


http://forums.arcgis.com/threads/47597-editing-related-table?highlight=related+records
http://forums.arcgis.com/threads/35452-Web-Editing-FeatureClass-with-Relatio
0 Kudos
1 Solution

Accepted Solutions
JoeHershman
MVP Alum
Sorry, when I looked at that later I see it would not work but we don't want to be using ItemsSource with the FeatureDataGrid, that is there because it inherits from DataGrid but really should be working with the GraphicsLayer property as the binding property.

You said you had it working to load the entire FeatureLayer data using code the Jenifer provided.  If you go back to that setup than the only part missing is filtering the list after the relationship query.  In that setup you have a class level variable l which is the FeatiureLayer.  I am going to call that _featureLayer because I hate having a variable named l.

Right now you have

        private void QueryTask_ExecuteRelationshipQueryCompleted(object sender, RelationshipEventArgs e)         {               IEnumerable<Graphic> graphics = e.Result.RelatedRecordsGroup.First().Value;                RelationshipResult pr = e.Result;              if (pr.RelatedRecordsGroup.Count == 0)              {                    RelatedRowsDataGrid.ItemsSource = null;              }              else              {                  foreach (var pair in pr.RelatedRecordsGroup)                  {                        RelatedRowsDataGrid.ItemsSource = pair.Value;                      MyFDG.ItemsSource = pair.Value;                                          }              }         }


What needs to happen in the RelationshipComplete handler is that the FeatureLayer (_featureLayer) gets filtered to only show the correct records.

        private void QueryTask_ExecuteRelationshipQueryCompleted(object sender, RelationshipEventArgs e)         {             //This line gets you the all features returned from the Relationship Query             IEnumerable<Graphic> graphics = e.Result.RelatedRecordsGroup.First().Value;                          // There are two ways to filter either using Where clause or using the ObjectIDs property             // before you set the Where clause to return all records 1=1 I think             // Here instead lets use the ObjectIDs approach, first get rid of the old Where                    _featureLayer.Where = null //remember _featureLayer is l                                      //ObjectIDs takes an int[] and with a little Linq we can get that array             // I am assumning you have included OBJECTID in your outfields when you defined the query                          int[] objectIds = graphics.Select(g => (int)g.Attributes["OBJECTID"]).ToArray();               //now set the ObjectIDs on our FeatureLayer                          _featureLayer.ObjectIDs = objectIds;               //Update the FeatureLayer to 'redraw' the new filtered set of Features                     _featureLayer.Update();               //And I hope this worked.  you could rebind the layer to the grid to make sure all is good             MyFDG.GraphicsLayer = _featureLayer;           }



Hope that helps
-Joe
Thanks,
-Joe

View solution in original post

0 Kudos
17 Replies
JoeHershman
MVP Alum
You don't need to use the FeatureDataGrid or FeatureDataForm, they do add some nice features to the standard Silverlight toolkit tools.  There would be slightly different approaches because with the FeatureDataGid you are bound to the FeatureLayer directly.

Here is how I have done it.

  1. Following the sample here http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#QueryRelatedRecords I get my related records when the user selects the related point

  2. This next steps would depend on which grid you use

    1. With the SL Grid:

      1. Create a collection of some sort of objects that show my attributes (like the Inspection object shown here http://forums.arcgis.com/threads/21714-Editing-Tables-in-Feature-Service-with-Silverlight-4 from my returned set of Graphics

      2. Bind this collection to my data grid

      3. To update an existing feature when the user  completes edits to the record I query the graphics collection from the FeatureLayer to find the Graphic with the same OID.

      4. Then make changes to the attributes in this Graphic and call save on the FeatureLayer

      5. So very similar to what he shows but instead of adding to the Graphics collection I query for the Graphic and update its attributes.  Also I have found the only way to get the Graphics to load properly in my FeatureLayer is to add the FeatureLayer in Xaml, creating it like in the sample will not load the Graphics collection on the FeatureLayer

    2. With the FeatureDataGrid

      1. Bind a FeatureLayer (not the actual FeatureLayer in my map, though) to the grid but initially have the FeatureLayer as null

      2. Do the related records query as before but when I get the results back I set the ObjectId property on my bound FeatureLayer and call Update() on the FeatureLayer

      3. To add a record I need to create the Graphics object and first initialize all my Attributes to their default values for the data type

      4. Then can add the record to the FeatureLayer and it will show up in the Grid

      5. Then you just call Save() on the FeatureLayer

      6. For updates the user is editing the graphic so just need a call to save no searching like before



      7. I will try and post a code sample for one of the approaches (probably #2) later when I get everything finished

        Hope that helps
        -Joe
        Thanks,
        -Joe
        0 Kudos
        NathalieNeagle
        Regular Contributor
        Joe,

        Thanks for your reply,

        I've been struggling with this all morning...your post did a good job of outlining the steps but my lack of skills has produced much from it. 


        I'm more interested in getting example 2 (FeatureDataGrid) going or maybe the featuredataform but that is giving me problems also.

        1.With the FeatureDataGrid1.Bind a FeatureLayer (not the actual FeatureLayer in my map, though) to the grid but initially have the FeatureLayer as null
        2.Do the related records query as before but when I get the results back I set the ObjectId property on my bound FeatureLayer and call Update() on the FeatureLayer
        3.To add a record I need to create the Graphics object and first initialize all my Attributes to their default values for the data type
        4.Then can add the record to the FeatureLayer and it will show up in the Grid
        5.Then you just call Save() on the FeatureLayer
        6.For updates the user is editing the graphic so just need a call to save no searching like before


        So you first stated to Bind a featurelayer but not the actually feature layer in my map (so not the parent feature layer or the related table featrue layer) so I created a feature layer in my code behind (C#)

        FeatureLayer RelateFeatureLayer = new FeatureLayer();
                     RelateFeatureLayer = null;
        


        2. Step is where I fall short -
        Do the relate records query as before
        (check I had that working earlier)

        THis is where I lose you:
        when I get the results back I set the ObjectId property on my bound FeatureLayer and call Update() on the FeatureLayer
        WHere should I do this and how?


        If I just try to bind my featuredatagrid to my RelatedTable featurelayer it seems to work except my table is filled with feature geometry attributes like (Geometry, Symbol, Attributes, MapTip, TimeExtent, Selected, etc. ) instead of the attributes of the feature (see attachment)


        Thanks again any more help or code would be much appreciated.

        Nat
        0 Kudos
        JenniferNery
        Esri Regular Contributor
        I think this thread might also be related: http://forums.arcgis.com/threads/21714-Editing-Tables-in-Feature-Service-with-Silverlight-4.

        If you happen to have GraphicsLayer, this is also supported in FeatureDataGrid but if you already have FeatureLayer, that is better since metadata information on fields help FDG build its columns.
        0 Kudos
        NathalieNeagle
        Regular Contributor
        Jennifer,

        Thanks for the post/suggestions.  Yea I've looked at that post several times and I got the example up and running that Doug Carroll posted.  I couldn't really make the transition to a related table and a featuredatagrid.  I see you posted some code but once again I'm kind of lost with that also.  I do have a feature layer and I have it running with the related table example I just can't make the jump to a featuredatagrid or a featuredataform.

        Nathalie
        0 Kudos
        JenniferNery
        Esri Regular Contributor
        I think Post# 35 has XAML and Code-behind (see attachment), so the FeatureLayer there that is a table is not added to the map but is just associated to FeatureDataGrid by setting FDG.GraphicsLayer property.
        0 Kudos
        JoeHershman
        MVP Alum
        Attached is the code I have for doing Updates and Adds of related records.  The way it works is that the user clicks on a point and the FeatureDataGrid will load with the related data.  Then when the user clicks on a record the FeatureDataForm is loaded with that record.  There is an Add button on the UI and when the user clicks it a new record is added to the FeatureLayer and the FeatureDataForm is populated with that record (containing default values for the particular data types of the field).

        A few notes about how the code is written.  The application architecture is Modular (each application feature is in a Module) and use a MVVM design pattern.  PRISM is used for modularity and to support commanding and event aggregation.  The application also uses MEF for dependency injection.  There is an object of type IMapViewUIService that is injected into the ViewModel which is used primarily to share the Map across Modules.  Hence, the code would not be usable as as (unless you have a PRISM/MEF application that happens to have an IMapViewUIService :)), but I hope shows the code that takes care of everything.  I tried to put enough comments in there to explain what is going on.



        This is the Xaml from my main page that has the FeatureLayer.  As I have said in some other posts I have never got it to work for editing records when adding the FeatureLayer in code, don't know why but the Graphics never get loaded

                             <esri:Map x:Name="_map" Background="Transparent" WrapAround="true" IsLogoVisible="False">
                                <esri:ArcGISDynamicMapServiceLayer ID="Ad Soft" 
                                        Url="http://sdm7/ArcGIS/rest/services/Ad_Soft/MapServer" />
                                <esri:ArcGISDynamicMapServiceLayer ID="BaseLayer" 
                                        Url="http://sdm7/arcgis/rest/services/MapOverview/MapServer" />
                                 <!-- FeatureLayer with Url of Related Table -->
                                <esri:FeatureLayer ID="Feature_Layer" Url="http://sdm7/ArcGIS/rest/services/Demos/Demo_RelatedDataEntry/FeatureServer/1" />
                            </esri:Map> 
        


        Hope it helps
        -Joe
        Thanks,
        -Joe
        0 Kudos
        NathalieNeagle
        Regular Contributor
        Jennifer and Joe,

        Thank you both for all the replies and the code.  Jennifer I missed your post (number 35), before that it was all about converting VB to C# so I stopped reading. 

        Jennifer,

        I now have the query relate example up and running (with my services) http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#QueryRelatedRecords - and the code you posted (with my same feature layer related table).

        But I'm still missing the piece and struggling with how I drop the original data grid, which is used in the relate example and replace it with the feature grid from your example.

        I'm giving both of you points for all your help.

        xaml
        
          <!-- RELATED LINK RESOURCE-->
                    <common:HierarchicalDataTemplate x:Key="TreeViewItemTemplate">
                        <StackPanel Orientation="Horizontal">
                            <Ellipse Fill="Transparent" Height="6" Width="6" StrokeThickness="2" Stroke="Black" Margin="0,0,10,0"/>
                            <TextBlock Text="{Binding Attributes[APN]}" FontWeight="Bold" HorizontalAlignment="Left" FontSize="10"/>
                        </StackPanel>
                    </common:HierarchicalDataTemplate>
                    <!--END RELATED LINK RESOURCE-->
        
        
        <Border x:Name="RelateResultsDisplay" Background="#77919191" BorderThickness="1" CornerRadius="5"
                        HorizontalAlignment="Right"  VerticalAlignment="Center" Visibility="Collapsed" 
                        Margin="5" Padding="5" BorderBrush="Black">
                    <Border.Effect>
                        <DropShadowEffect/>
                    </Border.Effect>
                    <Grid x:Name="RelatedRowsGrid" HorizontalAlignment="Right" Background="White" 
                      VerticalAlignment="Top" MinHeight="200" Margin="10" Width="Auto">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition MaxHeight="170" Height="*" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="140" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
        
                        <TextBlock Text="Wells: " Margin="4,10,10,6" Foreground="Black" TextWrapping="Wrap" 
                               FontWeight="Bold" FontSize="10" />
        
                        <basics:TreeView x:Name="SelectedWellsTreeView" Grid.Row="1" Grid.Column="0" Margin="2"
                                         BorderBrush="Black" BorderThickness="1" ItemsSource="{Binding}"   
                          SelectedItemChanged="SelectedWellsTreeView_SelectedItemChanged" 
                                         ItemTemplate="{StaticResource TreeViewItemTemplate}" />
        
                        <TextBlock Margin="4,10,60,6" TextWrapping="Wrap" Grid.Column="1"
                                   Text="Tops related to the selected well: " 
                                   FontSize="10" FontWeight="Bold" />
        
                        <slData:DataGrid x:Name="RelatedRowsDataGrid" AutoGenerateColumns="False" HeadersVisibility="All" 
                                     BorderThickness="1" HorizontalScrollBarVisibility="Hidden" Grid.Row="1" Grid.Column="1"
                         IsReadOnly="False" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
                                     Margin="2">
                            <slData:DataGrid.Columns>
                                <slData:DataGridTextColumn CanUserSort="False" Binding="{Binding Attributes[PARCELID]}" Header="Parcel Number"/>
                                <slData:DataGridTextColumn CanUserSort="False" Binding="{Binding Attributes[ADDRNMBR]}" Header="Number"/>
                                <slData:DataGridTextColumn CanUserSort="False" Binding="{Binding Attributes[ADDRNAME]}" Header="Name"/>
                                <slData:DataGridTextColumn CanUserSort="False" Binding="{Binding Attributes[GEN_PLAN]}" Header="General Plan"/>
                            </slData:DataGrid.Columns>
                        </slData:DataGrid>
        
                        <!--<esri:FeatureDataForm x:Name="MyFeatureDataForm"   
                                                 FeatureLayer="{Binding Path=Layers[AP], ElementName=Map}" 
                                                 IsReadOnly="False" LabelPosition="Left" />-->
        
        
                        <!--<esriWidgets:FeatureDataGrid Grid.Row="1" x:Name="MyFDG" Height="170" 
                                              Map="{Binding ElementName=Map}" 
                                              GraphicsLayer="{Binding Layers[RelateFeatureLayer], 
                            ElementName=Map}" Style="{StaticResource FeatureDataGridStyle1}"  />-->
        
        
                        <!--<Border x:Name="FeatureDataFormBorder" Visibility="Collapsed" 
                        HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,10,10,0" Width="300" Height="300" >
                            <Border.Effect>
                                <DropShadowEffect Color="Black" Direction="-45" BlurRadius="20" Opacity=".75" />
                            </Border.Effect>
                            <esri:FeatureDataForm x:Name="RelatedRowsDataGrid"   
                                                 FeatureLayer="{Binding Path=Layers[RelateFeatureLayer], ElementName=Map}" 
                                                 IsReadOnly="False" LabelPosition="Left" />
                        </Border>-->
        
                    </Grid>
                </Border>
        
                <!--END GRID FOR RELATED (AP) -->
        
                <StackPanel VerticalAlignment="Bottom">
                    <Button Content="AddRow" Click="Button_Click"/>
                    <esri:FeatureDataGrid x:Name="MyFDG"  Height="250"/>
                </StackPanel>
        
        


        code behind C#
        
        
         //For FDG -- Jennifer's code
        
                     l = new FeatureLayer()
                    {
                        ID = "MyLayer",
                        Url = "http://myserver/ArcGIS/rest/services/Relate/FeatureServer/1",
                        Mode = FeatureLayer.QueryMode.Snapshot,
                        Where = "1=1"
                    };
                     l.OutFields.Add("*");
                     l.Initialized += (s, e) =>
                     {
                         if (l.InitializationFailure != null)
                             MessageBox.Show(l.InitializationFailure.Message);
                     };
                     l.InitializationFailed += (s, e) =>
                     {
                         if (l.InitializationFailure != null)
                             MessageBox.Show(l.InitializationFailure.Message);
                     };
                     l.Initialized += new EventHandler<EventArgs>(l_Initialized);
                     l.Initialize();
                     MyFDG.GraphicsLayer = l;
        
                     //End for FDG Jennifer's code-
        
        
                     //for Relate
        
                     queryTask = new QueryTask("http://myserver/ArcGIS/rest/services/Relate/MapServer/0");
                     queryTask.ExecuteCompleted += QueryTask_ExecuteCompleted;
                     queryTask.ExecuteRelationshipQueryCompleted += QueryTask_ExecuteRelationshipQueryCompleted;
                     queryTask.Failed += QueryTask_Failed;
        
                    //End for relate
        
        
        //Jennifer's Code to update the feature data grid with a where 1=1 all records
        
         void l_Initialized(object sender, EventArgs e)
                {
                    var l = sender as FeatureLayer;
                    l.Update();
                }
                FeatureLayer l;
        
                private void Button_Click(object sender, RoutedEventArgs e)
                {
                    var g = new Graphic();
                    if (l != null && l.LayerInfo != null && l.LayerInfo.Fields != null)
                    {
                        foreach (var f in l.LayerInfo.Fields)
                        {
                            g.Attributes[f.Name] = null;
                        }
                    }
                    else
                    {
                        //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);
                        g.Attributes["APN"] = 2146700000;
                        g.Attributes["GEN_PLAN"] = "GPk";
        
                    }
                    l.Graphics.Add(g);
                }
        
        //End Jennifer's code
        
        
        //Relate Code
        
                private void SelectedWellsTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
                {
                    if (e.OldValue != null)
                    {
                        Graphic g = e.OldValue as Graphic;
                        g.UnSelect();
                        g.SetZIndex(0);
                    }
        
                    if (e.NewValue != null)
                    {
                        Graphic g = e.NewValue as Graphic;
                        g.Select();
                        g.SetZIndex(1);
        
                       
                       
                        //Relationship query
                        RelationshipParameter relationshipParameters = new RelationshipParameter()
                        {
                            ObjectIds = new int[] { Convert.ToInt32(g.Attributes[SelectedWellsTreeView.Tag as string]) },
                            OutFields = new string[] { "PARCELID, APN, ADDRNMBR, ADDRNAME, GEN_PLAN" },
                            RelationshipId = 0,
                            OutSpatialReference = Map.SpatialReference
                        };
                        
                        queryTask.ExecuteRelationshipQueryAsync(relationshipParameters);
                    }
                }
        
                void QueryTask_ExecuteRelationshipQueryCompleted(object sender, RelationshipEventArgs e)
                {
        
        
                  
                     RelationshipResult pr = e.Result;
                     if (pr.RelatedRecordsGroup.Count == 0)
                     {
        
                         RelatedRowsDataGrid.ItemsSource = null;
                     }
                     else
                     {
                         foreach (var pair in pr.RelatedRecordsGroup)
                         {
        
                             RelatedRowsDataGrid.ItemsSource = pair.Value;
        
                         }
                     }
                }
        //End relate
        
        0 Kudos
        JoeHershman
        MVP Alum
        I would try to go back to the original setup you have for the MyFDG (the commented out version).

        Where you instantiate a new FeatureLayer in code
        
                     l = new FeatureLayer()
                    {
                        ID = "MyLayer",
                        Url = "http://myserver/ArcGIS/rest/services/Relate/FeatureServer/1",
                        Mode = FeatureLayer.QueryMode.Snapshot,
                        Where = "1=1"
                    };


        Instead use (you don't need any of the Initialization handlers because it will already be initialized):
        
            l = Map.Layers["RelatedFeatureLayer"];


        I am assuming you have the 'RelatedFeatureLayer' as a FeatureLayer in your map (if not add it). 

        
        <esri:FeatureLayer ID="RelatedFeatureLayer" Url="http://myserver/ArcGIS/rest/services/Relate/FeatureServer/1" />


        I would also be very interested to know (because it is something strange I have seen) what happens if you change your existing code (i.e., does update complete successfully):

        
                    //Modified initialzed method
                    l.Initialized += (s, e) =>
                                         {
                                             l.UpdateCompleted += UpdateCompleted;
                                             l.UpdateFailed += UpdateFailed;
                                             l.Update();
                                         };
        
        
                private void UpdateFailed(object sender, TaskFailedEventArgs e)
                {
                    MessageBox.Show(e.Error.Message);
                }
        
                private void UpdateCompleted(object sender, EventArgs e)
                {
                    MessageBox.Show("WooHoo!");
                }


        Hope that helps
        -Joe
        Thanks,
        -Joe
        0 Kudos
        NathalieNeagle
        Regular Contributor
        Joe,

        Thanks again for all the posts and help.  I followed what you said and now my feature datagrid isn't populated with all the features(records) on startup like before (it is blank) but when I do a selection nothing happens it doesn't get populated.

        I did have to change this variable

         l.Initialized += (s, e) =>
                                         {
                                             l.UpdateCompleted += UpdateCompleted;
                                             l.UpdateFailed += UpdateFailed;
                                             l.Update();
                                         };


        to: It was saying I couldn't reassign the local variable e.

        void l_Initialized(object sender, EventArgs e)
                {
                l.Initialized += (s, f) =>
                                         {
                                             l.UpdateCompleted += UpdateCompleted;
                                             l.UpdateFailed += UpdateFailed;
                                             l.Update();
                                      };
        
        
                  // var l = sender as FeatureLayer;
                 //  l.Update();
                }


        I'm still wonder how this ties to relate and pairing with the Relationship Event.  As stated before I can get the relate example working and I can get Jennifer's featuredatagrid editor working with the same related feature class but I can't bridge the two.

        Thanks
        Nat
        0 Kudos