NullReferenceException when using EditGeometry

2615
14
12-28-2011 05:23 PM
Hwee_KiangSim
New Contributor
Hi,

i using esri for WPF v2.3. When I use the EditGeometry, sometimes I experience a NullReferenceException and my app will crash. I cant seem to catch the exception to let my app exit gracefully and I cant seem to debug it because it has something to do with the esri dll. The most I can see in the call stack is it crashes at the point of EditGeometry.UpdateVertexPosition (i think it's a private function in the dll?) and normally occur after EditGeometry.Action == EditCanceled (but I did not explicitly call EditCancel())

The exception is random and occurs on and off which is a headache. Please help. Thanks

shweekia
0 Kudos
14 Replies
JenniferNery
Esri Regular Contributor
I am unable to reproduce this using the SDK sample for EditGeometry. Can you provide steps to reproduce? Does this happen after moving a vertex or moving the geometry? Are you using scale and rotate? CancelEdit may be called when the API detects original geometry has changed. Are you modifying this geometry outside EditGeometry? (i.e. adding/removing vertex in code-behind). Can you share the stack trace as well? Thanks.
0 Kudos
Hwee_KiangSim
New Contributor
Hi Jennifer,

Thanks for replying.
What I am trying to do is
1. start edit the polygon
2. in EditGeometry event handler, if e.action == AddVertex or MoveVertex, retrieve the edits done and apply the same edits to another existing polygon
3. call StopEdit() to save any changes
4. call StartEdit() again to continue the editing process

I really cant pin point when it will crash but it's normally AddVertex->StopEdit->StartEdit->CancelEdit->crash. I'm suspecting something to do with canceledit?
I know it's hard to help me debug when you do not have the full code, but maybe you just give me suggestions on where to start and I'll try to debug it again.

the call stack:
Object reference not set to an instance of an object.   at ESRI.ArcGIS.Client.EditGeometry.UpdateVertexPosition(MapPoint pnt, Boolean isTransformPoint)
   at ESRI.ArcGIS.Client.EditGeometry.HandleElementMove(Point screenPoint, MapPoint mapPoint)
   at ESRI.ArcGIS.Client.EditGeometry.Map_MouseMove(Object sender, MouseEventArgs e)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)


Thanks!!!
0 Kudos
JenniferNery
Esri Regular Contributor
I added the following code in the SDK sample http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#EditToolsGeometry on EditGeometry_GeometryEdit event handler.
if (e.Action == EditGeometry.Action.VextedMoved || e.Action == EditGeometry.Action.VertexAdded)
{
 editGeometry.StopEdit();
 editGeometry.StartEdit(editGraphic);
}
else if(e.Action == EditGeometry.Action.EditCompleted)    
 layer.Refresh();


However, I still cannot reproduce the NullRef exception. The stack trace you provided helps though. We'll add more null check in UpdateVertexPosition. Maybe you can share code-snippets or submit a bug to support? I see from your other post http://forums.arcgis.com/threads/46344-Graphic-disappear-on-save-edits that you are doing this to grab the intermediate geometry during edit, we'll look into making this information available in future versions of the API.
0 Kudos
Hwee_KiangSim
New Contributor
Hi Jennifer,

Yes you are right, I am trying to get intermediate data during editing so that I can apply the same edits to another map on the application that has a similar polygon. My app has two maps (i.e.  with 2 graphics layers) These polygons are referenced by their attributes id. Any edits applied to graphics layer of map 0, will need to be applied to graphics layer of map 1 (vice versa)

I put a switch case in the e.Actions, for eg, when MoveVertex

                            polyEdited = e.Graphic.Geometry as ESRI.ArcGIS.Client.Geometry.Polygon;

                            if (polyEdited != null)
                            {
                                polyCol = polyEdited.Rings[0];

                                int nSelectIndex = 0;

                                //to get which vertex is currently being edited
                                MapPoint selectPoint = polyCol[nSelectIndex];
                                double dMinDiff = Math.Pow((selectPoint.X - e.OldItems[0].X), 2.0) +
                                                        Math.Pow((selectPoint.Y - e.OldItems[0].Y), 2.0);
                                dMinDiff = Math.Pow(dMinDiff, 0.5);

                                for (int idxPt = 1; idxPt < polyCol.Count; idxPt++)
                                {
                                    selectPoint = polyCol[idxPt];
                                    double dCurrDiff = Math.Pow((selectPoint.X - e.OldItems[0].X), 2.0) +
                                                        Math.Pow((selectPoint.Y - e.OldItems[0].Y), 2.0);
                                    dCurrDiff = Math.Pow(dCurrDiff, 0.5);

                                    if (dMinDiff > dCurrDiff)
                                    {
                                        dMinDiff = dCurrDiff;
                                        if (dMinDiff < 2)
                                        {
                                            nSelectIndex = idxPt;
                                        }
                                    }
                                }

                                double dXMoveOffset = e.NewItems[0].X - e.OldItems[0].X;
                                double dYMoveOffset = e.NewItems[0].Y - e.OldItems[0].Y;

                                if (nSelectIndex != -1)
                                {

                                    if (rightImgParams.currRightGraphic.Attributes["id"] != null)
                                    {
                                       //retrieve the graphic from graphic layer of another map
                                        Graphic grap0 = GetGraphic(0, (UInt64)rightImgParams.currRightGraphic.Attributes["id"]);

                                        if (grap0 != null)
                                        {
                                            ESRI.ArcGIS.Client.Geometry.Polygon correspEdited = grap0.Geometry as      ESRI.ArcGIS.Client.Geometry.Polygon;
                                            if (correspEdited != null)
                                            {
                                                //update the graphic from the other map
                                                ESRI.ArcGIS.Client.Geometry.PointCollection correspCol = correspEdited.Rings[0];
                                                correspCol[nSelectIndex].X += dXMoveOffset;
                                                correspCol[nSelectIndex].Y += dYMoveOffset;

                                                if (nSelectIndex == 0) // must update the last vertice also
                                                {
                                                    correspCol[correspCol.Count - 1].X = correspCol[nSelectIndex].X;
                                                    correspCol[correspCol.Count - 1].Y = correspCol[nSelectIndex].Y;
                                                }
                                            }

                                        }
                                    }

                                }
                                //save the edits and restart the editing process                                                                     rightImgParams.EditGeometry.StopEdit();
                                Graphic grap1 = GetGraphic(1, (UInt64)e.Graphic.Attributes["id"]);
                                if (grap1 != null)
                                {
                                    rightImgParams.EditGeometry.StartEdit(grap1);

                                    Graphic leftGraphic = GetGraphic(0, (UInt64)grap1.Attributes["id"]);
                                    if (leftGraphic != null)
                                    {
                                        leftImgParams.EditGeometry.StartEdit(leftGraphic);
                                    }
                                }



                            }// if (polyEdited != null)




Is it because I'm trying to update the polygon of another graphic layer of another map? How can I avoid EditCancel? It seems that EditCancel will surely appear just before it crashes.
Is there a beta 3.0 version for WPF version to try? I'm getting a little desperate because I need to deliver this prototype to users soon and it'll be embarassing if my app crashes, so any help is appreciated! thanks so much!
0 Kudos
JenniferNery
Esri Regular Contributor
Oh I see that's why I could not reproduce. As I've said in my earlier post, CancelEdit may be called when the API detects original geometry has changed. So if EditGeometry is active on the graphic and you update its geometry, this will trigger a CancelEdit. There is a 3.0 Beta 1 but you might still run into the same issue. So you have 2 maps that can edit and any edit on one map, you want reflected on the other map? Do you save the edit? Can the other map call layer.Update() to re-draw the graphics instead of detecting what vertex was added/moved/removed?
0 Kudos
Hwee_KiangSim
New Contributor
ok, I see what you mean.

You are right , I have 2 maps that can edit and any edit on one map, I want reflected on the other map. I need to save the edits.

I tried commenting out most of the codes but keep the part where I try to find the vertex that was updated
                               MapPoint selectPoint = polyCol[nSelectIndex];
                                double dMinDiff = Math.Pow((selectPoint.X - e.OldItems[0].X), 2.0) +
                                                        Math.Pow((selectPoint.Y - e.OldItems[0].Y), 2.0);
                                dMinDiff = Math.Pow(dMinDiff, 0.5);

                                for (int idxPt = 1; idxPt < polyCol.Count; idxPt++)
                                {
                                    selectPoint = polyCol[idxPt];
                                    double dCurrDiff = Math.Pow((selectPoint.X - e.OldItems[0].X), 2.0) +
                                                        Math.Pow((selectPoint.Y - e.OldItems[0].Y), 2.0);
                                    dCurrDiff = Math.Pow(dCurrDiff, 0.5);

                                    if (dMinDiff > dCurrDiff)
                                    {
                                        dMinDiff = dCurrDiff;
                                        if (dMinDiff < 2)
                                        {
                                            nSelectIndex = idxPt;
                                        }
                                    }
                                }

it also crashes. But if I comment out everything in the editgeometry event, my app is ok( but that's not what i want)

So Im assuming when during an edit i cannot try to access the geometry being edited else I risk running into editcancel?

Can the other map call layer.Update() to re-draw the graphics instead of detecting what vertex was added/moved/removed?

Sorry but I do not understand how I can do this. If I call an update, how do I know which vertex was added/moved/removed and the delta?

Thanks so much for trying to help me solve it. I'm open to any suggestions / workaround.
0 Kudos
JenniferNery
Esri Regular Contributor
Here's a quick sample. Notice that to simplify, I use Graphic.PropertyChanged event and Geometry.Clone().

<Grid x:Name="LayoutRoot" Background="White">
  <Grid.Resources>
   <esri:SimpleFillSymbol x:Key="RedFillSymbol" Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" />
  </Grid.Resources>
  <Grid.ColumnDefinitions>
   <ColumnDefinition/>
   <ColumnDefinition/>
  </Grid.ColumnDefinitions>
  <esri:Map x:Name="MyMap" WrapAround="True" Background="White">
   <esri:ArcGISTiledMapServiceLayer ID="PhysicalTiledLayer" Url="http://services.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer"/>
   <esri:GraphicsLayer ID="MyLayer" MouseLeftButtonDown="GraphicsLayer_MouseLeftButtonDown" Initialized="GraphicsLayer_Initialized">
    <esri:GraphicsLayer.Graphics>
    <esri:Graphic Symbol="{StaticResource RedFillSymbol}">
    <esri:Polygon >
     <esri:Polygon.Rings>
      <esri:PointCollection>
       <esri:MapPoint X="110.039" Y="-20.303" />
       <esri:MapPoint X="132.539" Y="-7.0137" />
       <esri:MapPoint X="153.281" Y="-13.923" />
       <esri:MapPoint X="162.773" Y="-35.174" />
       <esri:MapPoint X="133.594" Y="-43.180" />
       <esri:MapPoint X="111.797" Y="-36.032" />
       <esri:MapPoint X="110.039" Y="-20.303" />
      </esri:PointCollection>
     </esri:Polygon.Rings>
    </esri:Polygon>
    </esri:Graphic>
    </esri:GraphicsLayer.Graphics>
   </esri:GraphicsLayer>
  </esri:Map> 
  <esri:Map x:Name="MirroredMap" Grid.Column="1" />
 </Grid>


EditGeometry editGeometry;
  public MainPage()
  {
   InitializeComponent();
   editGeometry = new EditGeometry(MyMap);
   editGeometry.IsEnabled = true;

   MyMap.ExtentChanged += (s, e) =>
    {
     MirroredMap.Extent = MyMap.Extent;
    };
   MyMap.Layers.LayersInitialized += (s, e) =>
    {
     foreach (var l in MyMap.Layers)
     {
      if (l is ArcGISTiledMapServiceLayer)
      {
       MirroredMap.Layers.Add(new ArcGISTiledMapServiceLayer() { ID = l.ID, Url = (l as ArcGISTiledMapServiceLayer).Url });
      }
      else if (l is GraphicsLayer)
      {
       var graphicsLayer = new GraphicsLayer() { ID = l.ID };
       if ((l as GraphicsLayer).Graphics != null)
       {
        foreach (var g in (l as GraphicsLayer).Graphics)
        {
         g.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(g_PropertyChanged);
         var clonedGraphic = new Graphic() { Geometry = Geometry.Clone(g.Geometry), Symbol = g.Symbol };
         foreach (var item in g.Attributes)
          clonedGraphic.Attributes[item.Key] = item.Value;
         graphicsLayer.Graphics.Add(clonedGraphic);
        }
       }
       MirroredMap.Layers.Add(graphicsLayer);
      }

     }
    };
  }

  void g_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  {
   if (e.PropertyName == "Geometry")
   {
    var graphic = sender as Graphic;
    var layer = MirroredMap.Layers["MyLayer"] as GraphicsLayer;
    var clonedGraphic = layer.Graphics.FirstOrDefault(g => (int) g.Attributes["ObjectID"] == (int) graphic.Attributes["ObjectID"]);
    clonedGraphic.Geometry = Geometry.Clone(graphic.Geometry);
   }
  }

  private void GraphicsLayer_MouseLeftButtonDown(object sender, GraphicMouseButtonEventArgs e)
  {
   editGeometry.StartEdit(e.Graphic);
  }

  private void GraphicsLayer_Initialized(object sender, EventArgs e)
  {
   var l = sender as GraphicsLayer;
   int i = 0;
   foreach (var g in l.Graphics)
   {
    g.Attributes["ObjectID"] = ++i;
   }
  }
0 Kudos
Hwee_KiangSim
New Contributor
Hi Jennifer!

Thanks for the sample! It's not quite what I want but I guess I can start from there.

what I observed is that doing a StopEdit() and StartEdit() within the editgeometry handler will cause the app to crash so I took it out entirely. Hopefully the next release will allow the graphics to be updated real time during edits (i.e something like OnEditGeometry?).

Thanks a million for your help so far!
0 Kudos
JenniferNery
Esri Regular Contributor
No problem:) It is by design that we cancel the edit when geometry is updated outside editGeometry. I've put in a work item to include current state of the geometry in the GeometryEditEventArgs. I think this was the reason you had to Stop/StartEdit.
0 Kudos