/// <summary> /// Create a single multi-point feature that is comprised of 20 points. /// </summary> /// <param name="multiPointLayer">Multi-point geometry feature layer used to add the multi-point feature.</param> /// <returns></returns> private void constructSampleMultiPoints(FeatureLayer multiPointFeatureLayer) { // create a random number generator var randomGenerator = new Random(); //var areaOfInterest = await MappingModule.ActiveMapView.GetExtentAsync(); // get the feature class and its definition // start an edit operation to create new (random) multi-point feature var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Generate multi-point"; //create a list to hold the 20 coordinates of the multi-point feature IList <Coordinate> coordinateList = new List <Coordinate>(20); for (int i = 0; i < 20; i++) { // add the random coordinate to the list // use random.NextCoordinate() } // execute the operation // save the edits }
/// <summary> /// Create random sample points in the extent of the spatial reference /// </summary> /// <param name="pointLayer">Point geometry feature layer used to the generate the points.</param> /// <returns></returns> private void constructSamplePoints(FeatureLayer pointFeatureLayer) { // create a random number generator var randomGenerator = new Random(); // the database and geometry interactions are considered fine-grained and must be executed on // the main CIM thread //return QueuingTaskFactory.StartNew(() => //{ //Envelope areaOfInterest = MappingModule.ActiveMapView.GetExtentAsync().Result; // get the feature class and its definition // start an edit operation to create new (random) point features var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Generate points"; // create 20 new point geometries and queue them for creation for (int i = 0; i < 20; i++) { // use random.NextCoordinate in the construction of the MapPoint // queue feature creation //createOperation.Create(pointLayer, newMapPoint); } // execute the operation // save the edits //return EditingModule.SaveEditsAsync().Result; //}); }
/// <summary> /// Asynchronous task that splits the current sketch geometry into 100 segments of equal length. /// </summary> /// <returns>Task{bool}</returns> private async Task <bool> ExecuteDensify() { // get the current sketch geometry from the editing module var sketchGeometry = await EditingModule.GetSketchGeometryAsync(); // check if geometry is valid bool isGeometryValid = await QueuingTaskFactory.StartNew <bool>(() => { bool validGeometry = true; if (sketchGeometry == null || sketchGeometry.IsEmpty) { validGeometry = false; } return(validGeometry); }); if (!isGeometryValid) { return(false); } // get the currently selected features from the map // the selected feature uses the above selected geometry var currentlySelectedFeature = await MappingModule.ActiveMapView.Map.GetSelectionSetAsync(); // set up an edit operation EditOperation editOperation = new EditOperation(); editOperation.Name = "Densify selected geometry"; Geometry densifiedGeometry = null; // modify the geometry in the sketch geometry // the geometry operation needs to run as it own task await QueuingTaskFactory.StartNew(() => { // compute a length fur the current geometry when divided into 100 segments var segmentLength = GeometryEngine.Length(sketchGeometry) / 100; // compute a new densified geometry densifiedGeometry = GeometryEngine.Densify(sketchGeometry, segmentLength); }); // for the currently selected feature go through the layers to which the feature belongs foreach (var mapMember in currentlySelectedFeature.MapMembers) { // for each of the selections in the layer (map member) foreach (var selectedOID in currentlySelectedFeature[mapMember]) { // provide the densified geometry for the selected feature as part of the modify operation editOperation.Modify((Layer)mapMember, selectedOID, densifiedGeometry); } } // execute the edit operation and return return(await editOperation.ExecuteAsync()); }
/// <summary> /// Create sample polygon feature using the point geometries from the multi-point feature using the /// ConvexHull method provided by the GeometryEngine. /// </summary> /// <param name="polygonLayer">Polygon geometry feature layer used to add the new feature.</param> /// <param name="lineLayer">The polyline feature layer containing the features used to construct the polygon.</param> /// <returns></returns> private void constructSamplePolygon(FeatureLayer polygonLayer, FeatureLayer lineLayer) { // execute the fine grained API calls on the CIM main thread //return QueuingTaskFactory.StartNew(() => //{ // get the feature classes and their definitions // get the underlying feature class for each layer var polygonFeatureClass = polygonLayer.GetTableAsync().Result as FeatureClass; var lineFeatureClass = lineLayer.GetTableAsync().Result as FeatureClass; FeatureClassDefinition polygonDefinition = null; FeatureClassDefinition polylineDefinition = null; // construct a cursor to retrieve the line features var lineCursor = lineFeatureClass.Search(null, false); // set up the edit operation for the feature creation var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Create polygons"; // construct the polygon geometry from the convex hull of the combined polyline features // by contructing a convex hull // HINT: GeometryEngine.ConvexHull List <CoordinateCollection> combinedCoordinates = new List <CoordinateCollection>(); while (lineCursor.MoveNext()) { // TODO // add the feature geometry into the overall list of coordinate collections // retrieve the first feature //var lineFeature = ... use the current property of the cursor // add the coordinate collection of the current geometry into our overall list of collections //var polylineGeometry = lineFeature.Shape as Polyline; //combinedCoordinates.AddRange ... Hint: we want the part collection of the polyline } // TODO //construct a polygon geometry from the convex hull of the polyline //constructed from the list of coordinate collections combined from all the polylines //var polyLine = new Polyline(..., lineFeatureClass.SpatialReference) //var geom = GeometryEngine.ConvexHull(... //var newPolygon = Polygon.Clone(... // TODO // specify the create edit operation // TODO // execute the operation // TODO // save the edits //return EditingModule.SaveEditsAsync(); //}); }
/// <summary> /// Create sample polyline feature using the geometries from the point feature layer. /// </summary> /// <param name="polylineLayer">Polyline geometry feature layer used to add the new features.</param> /// <param name="pointLayer">The geometries from the point layer are used as vertices for the new line features.</param> /// <returns></returns> private Task <bool> constructSamplePolylines(FeatureLayer polylineLayer, FeatureLayer pointLayer) { // execute the fine grained API calls on the CIM main thread return(QueuingTaskFactory.StartNew(() => { // get the underlying feature class for each layer var polylineFeatureClass = polylineLayer.GetTableAsync().Result as FeatureClass; var pointFeatureClass = pointLayer.GetTableAsync().Result as FeatureClass; // construct a cursor for all point features, since we want all feature there is no // QueryFilter required var pointCursor = pointFeatureClass.Search(null, false); // retrieve the feature class schema information for the feature classes var polylineDefinition = polylineFeatureClass.Definition as FeatureClassDefinition; var pointDefinition = pointFeatureClass.Definition as FeatureClassDefinition; // initialize a counter variable int pointCounter = 0; // initialize a list to hold 5 coordinates that are used as vertices for the polyline var lineCoordinates = new List <Coordinate>(5); // set up the edit operation for the feature creation var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Create polylines"; // loop through the point features while (pointCursor.MoveNext()) { pointCounter++; var pointFeature = pointCursor.Current as Feature; // add the feature point geometry as a coordinate into the vertex list of the line // - ensure that the projection of the point geometry is converted to match the spatial reference of the line lineCoordinates.Add(((MapPoint)GeometryEngine.Project(pointFeature.Shape, polylineDefinition.SpatialReference)).Coordinate); // for every 5 geometries, construct a new polyline and queue a feature create if (pointCounter % 5 == 0) { var newPolyline = new Polyline(lineCoordinates, polylineDefinition.SpatialReference); createOperation.Create(polylineLayer, newPolyline); lineCoordinates = new List <Coordinate>(5); } } // execute the edit (create) operation var t = createOperation.ExecuteAsync().Result; // save the edits return EditingModule.SaveEditsAsync(); })); }
/// <summary> /// Create random sample points in the extent of the spatial reference /// </summary> /// <param name="pointLayer">Point geometry feature layer used to the generate the points.</param> /// <returns>Task of bool</returns> private Task <bool> constructSamplePoints(FeatureLayer pointLayer) { // create a random number generator var randomGenerator = new Random(); // the database and geometry interactions are considered fine-grained and must be executed on // the main CIM thread return(QueuingTaskFactory.StartNew(() => { // get the feature class associated with the layer var featureClass = pointLayer.GetTableAsync().Result as FeatureClass; // retrieve the class definition of the point feature class var classDefinition = featureClass.Definition as FeatureClassDefinition; // store the spatial reference as its own variable SpatialReference spatialReference = featureClass.SpatialReference; // define an area of interest. Random points are generated in the allowed // confines of the allow extent range var areaOfInterest = MappingModule.ActiveMapView.GetExtentAsync().Result; // start an edit operation to create new (random) point features var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Generate points"; // create 20 new point geometries and queue them for creation for (int i = 0; i < 20; i++) { MapPoint newMapPoint = null; // generate either 2D or 3D geometries if (classDefinition.HasZ) { newMapPoint = new MapPoint(randomGenerator.NextCoordinate(areaOfInterest, true), spatialReference); } else { newMapPoint = new MapPoint(randomGenerator.NextCoordinate(areaOfInterest, false), spatialReference); } // queue feature creation createOperation.Create(pointLayer, newMapPoint); } // execute the edit (feature creation) operation var t = createOperation.ExecuteAsync().Result; return EditingModule.SaveEditsAsync().Result; })); }
/// <summary> /// This is a basic FinishSketch method which illustrates the process of using the sketch geometry for a cut. /// 1. look at all layers in the active map /// for each layer: /// 2. Use the sketch geometry to perform a spatial query (Crosses) /// 3. Use the found features and use them to set up a cut operation /// 4. Create edit operation /// 5. Execute the edit operation /// ! edits are not saved ! /// /// </summary> /// <returns>Task of bool</returns> protected override async Task <bool> OnFinishSketch(Geometry geometry, Dictionary <string, object> attributes) { // use all layers of the active map to perform the cut foreach (var layer in ActiveMap.Layers) { // intialize a list of ObjectIDs that need to be cut List <long> cutOiDs = new List <long>(); Table fc = await layer.getFeatureClass(); // on a separate thread await QueuingTaskFactory.StartNew(() => { // find the features crossed by the sketch geometry RowCursor rc = fc.Search(geometry, SpatialRelationship.Crosses); // add the feature IDs into our prepared list while (rc.MoveNext()) { cutOiDs.Add(rc.Current.ObjectID); } }); // no features 'crossed' by the sketched line if (!cutOiDs.Any()) { continue; } // create an edit operation for the cut EditOperation op = EditingModule.CreateEditOperation(); op.Name = string.Format("Cut {0}", layer.Name); op.ProgressMessage = "Working..."; op.CancelMessage = "Operation canceled"; op.ErrorMessage = "Error cutting features"; op.SelectModifiedFeatures = false; op.SelectNewFeatures = false; // for each of the found features set up a cut method inside our edit operation // for multiple ObjectIDs the cuts with will be stacked into one operation foreach (var oid in cutOiDs) { op.Cut(layer, oid, geometry); } await op.ExecuteAsync(); } //execute the operation return(true); }
/// <summary> /// This is a basic FinishSketch method which illustrates the process of using the sketch geometry for a cut. /// 1. Create edit operation /// 2. Use the sketch geometry to perform a spatial query /// 3. Use the found features and use them to set up a cut operation /// 3. Execute the edit operation /// /// </summary> /// <returns>Task of bool</returns> protected override async Task <bool> FinishSketch(ArcGIS.Core.Geometry.Geometry geometry, Dictionary <string, object> attributes) { if (CurrentTemplate == null) { return(false); } // intialize a list of ObjectIDs that need to be cut List <int> cutOIDs = new List <int>(); Table fc = await this.CurrentTemplate.Layer.getFeatureClass(); // on a separate thread await QueuingTaskFactory.StartNew(() => { // find the features crossed by the sketch geometry RowCursor rc = fc.Search(geometry, SpatialRelationship.Crosses); // add the feature IDs into our prepared list while (rc.MoveNext()) { cutOIDs.Add(rc.Current.ObjectID); } }); if (!cutOIDs.Any()) { return(true);//nothing to cut } // create an edit operation for the cut var op = await EditingModule.CreateEditOperationAsync(); op.Name = string.Format("Cut {0}", this.CurrentTemplate.Layer.Name); op.ProgressMessage = "Working..."; op.CancelMessage = "Operation canceled"; op.ErrorMessage = "Error cutting features"; op.SelectModifiedFeatures = false; op.SelectNewFeatures = false; // for each of the found features set up a cut method inside our edit operation // for multiple ObjectIDs the cuts with will be stacked into one operation foreach (var oid in cutOIDs) { op.Cut(this.CurrentTemplate.Layer, oid, geometry); } //execute the operation return(await op.ExecuteAsync()); }
/// <summary> /// Create a single multi-point feature that is comprised of 20 points. /// </summary> /// <param name="multiPointLayer">Multi-point geometry feature layer used to add the multi-point feature.</param> /// <returns></returns> private async Task constructSampleMultiPoints(FeatureLayer multiPointLayer) { // get the feature class associated with the layer var featureClass = await multiPointLayer.GetTableAsync() as FeatureClass; // create a random number generator var randomGenerator = new Random(); // the database and geometry interactions are considered fine-grained and need to be executed on // a separate thread await QueuingTaskFactory.StartNew(async() => { // store the spatial reference as its own variable var spatialReference = featureClass.SpatialReference; // define an area of interest. Random points are generated in the allowed // confines of the allow extent range var areaOfInterest = await MappingModule.ActiveMapView.GetExtentAsync(); // start an edit operation to create new (random) multi-point feature var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Generate multi-point"; // retrieve the class definition of the point feature class var classDefinition = featureClass.Definition as FeatureClassDefinition; // create a list to hold the 20 coordinates of the multi-point feature IList <Coordinate> coordinateList = new List <Coordinate>(20); for (int i = 0; i < 20; i++) { // generate either 2D or 3D geometries if (classDefinition.HasZ) { coordinateList.Add(randomGenerator.NextCoordinate(areaOfInterest, true)); } else { coordinateList.Add(randomGenerator.NextCoordinate(areaOfInterest, false)); } } // create and execute the feature creation operation createOperation.Create(multiPointLayer, new MultiPoint(coordinateList, classDefinition.SpatialReference)); await createOperation.ExecuteAsync(); }); }
/// <summary> /// Create sample polygon feature using the point geometries from the multi-point feature using the /// ConvexHull method provided by the GeometryEngine. /// </summary> /// <param name="polygonLayer">Polygon geometry feature layer used to add the new feature.</param> /// <param name="lineLayer">The polyline feature layer containing the features used to construct the polygon.</param> /// <returns></returns> private Task <bool> constructSamplePolygon(FeatureLayer polygonLayer, FeatureLayer lineLayer) { // execute the fine grained API calls on the CIM main thread return(QueuingTaskFactory.StartNew(() => { // get the underlying feature class for each layer var polygonFeatureClass = polygonLayer.GetTableAsync().Result as FeatureClass; var lineFeatureClass = lineLayer.GetTableAsync().Result as FeatureClass; // construct a cursor to retrieve the line features var lineCursor = lineFeatureClass.Search(null, false); // retrieve the feature class schema information for the feature class var polygonDefinition = polygonFeatureClass.Definition as FeatureClassDefinition; var polylineDefinition = lineFeatureClass.Definition as FeatureClassDefinition; // set up the edit operation for the feature creation var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Create polygons"; List <CoordinateCollection> combinedCoordinates = new List <CoordinateCollection>(); while (lineCursor.MoveNext()) { // retrieve the first feature var lineFeature = lineCursor.Current as Feature; // add the coordinate collection of the current geometry into our overall list of collections var polylineGeometry = lineFeature.Shape as Polyline; combinedCoordinates.AddRange(polylineGeometry.Paths); } // use the ConvexHull method from the GeometryEngine to construct the polygon geometry var newPolygon = Polygon.Clone(GeometryEngine.ConvexHull(new Polyline(combinedCoordinates, lineFeatureClass.SpatialReference))) as Polygon; // queue the polygon creation createOperation.Create(polygonLayer, newPolygon); // execute the edit (polygon create) operation var t = createOperation.ExecuteAsync().Result; // save the edits return EditingModule.SaveEditsAsync(); })); }
public async static Task <bool> AddAndFlashGeometryAsync(this FeatureLayer targetFeatureLayer, Geometry GeometryToAdd) { var targetFeatureClass = await targetFeatureLayer.GetTableAsync() as FeatureClass; FeatureClassDefinition targetClassDefinition = null; Feature newFeature = null; var editOperation = EditingModule.CreateEditOperation(); editOperation.Name = "Flash closest geometry"; editOperation.Callback(context => { targetClassDefinition = targetFeatureClass.Definition as FeatureClassDefinition; var targetGeometry = GeometryEngine.Project(GeometryToAdd, targetClassDefinition.SpatialReference); if (GeometryToAdd.GeometryType != targetClassDefinition.ShapeType) { return; } var featureBuffer = targetFeatureClass.CreateRowBuffer(); newFeature = targetFeatureClass.CreateRow(featureBuffer) as Feature; newFeature.Shape = targetGeometry; newFeature.Store(); context.invalidate(newFeature); }, targetFeatureClass); MappingModule.ActiveMapView.FlashFeature(targetFeatureLayer, Convert.ToInt32(newFeature.ObjectID)); return(true); }
/// <summary> /// Based on the type of edit event, a corresponding message is stored in the comment field of the feature class and the attribute /// modification is stored as an operation in undo/redo stack /// </summary> /// <param name="editEventArgs">The EditEventArgs object is passed to the EditEvent subscription and contains information about the current /// adds, modifies, and deletes of the features.</param> /// <returns></returns> private static async Task TrackEditEvents(EditEventArgs editEventArgs) { // let's deal with the create events first foreach (var mapMember in editEventArgs.Creates) { // TODO // for each featuer layer containing creates // the layer that contains the creates // TODO // find the field described by the COMMENT_FIELD_NAME const Table table = null; int commentFieldIndex = -1; // prepare the comment to enter for the feature string comment = String.Format("Created @ {0:G}", DateTime.Now); // set up the edit operation to make the changes var editOperation = EditingModule.CreateEditOperation(); editOperation.Name = "Add 'created' comment."; KeyValuePair <MapMember, IReadOnlyCollection <long> > member = mapMember; editOperation.Callback(async context => { foreach (long featureOID in member.Value) { // for each feature OID retrieve the feature // TODO: get the fields from the table in order to prepare the WhereClause Row currentRow = await table.GetRowByIDAsync(featureOID); if (currentRow == null) { continue; } // if we have the comment field index if (commentFieldIndex != -1) { // add the comment into the comment attribute field currentRow[commentFieldIndex] = comment; try { currentRow.Store(); _editFlags.Add("create_" + featureOID); } catch (GeodatabaseRowException rowException) { System.Diagnostics.Debug.WriteLine(rowException.Message); } finally { context.invalidate(currentRow); } } } }, table); // don't await the edit operation editOperation.ExecuteAsync(); } foreach (var mapMember in editEventArgs.Modifies) { var featureLayer = mapMember.Key as FeatureLayer; string comment = String.Format("Changed @ {0:G}", DateTime.Now); Table table = await featureLayer.GetTableAsync(); if (mapMember.Value.Count > 0) { var editOperation = EditingModule.CreateEditOperation(); editOperation.Name = "Add 'changed' comment."; KeyValuePair <MapMember, IReadOnlyCollection <long> > member = mapMember; editOperation.Callback(context => { foreach (long featureOID in member.Value) { // check who triggered the event...this is to avoid infinite loops based on self-triggered // edit operations // if there is an existing modify operation for current feature id, // abort the operation and continue to the next feature if (_editFlags.Contains("modify_" + featureOID.ToString())) { _editFlags.Remove("modify_" + featureOID.ToString()); continue; } // if there is an existing create operation for current feature id, // abort the operation and continue to the next feature if (_editFlags.Contains("create_" + featureOID.ToString())) { //editOperation.Abort(); _editFlags.Remove("create_" + featureOID.ToString()); continue; } // find the name of the ObjectID field string oidFieldName = "OBJECTID"; var tableDefinition = table.Definition as TableDefinition; if (tableDefinition != null) { oidFieldName = tableDefinition.ObjectIDField; } var queryFilter = new QueryFilter { WhereClause = string.Format("{0} = {1}", oidFieldName, featureOID.ToString()) }; var rc = table.Search(queryFilter, false); var commentFieldIndex = rc.FindField(COMMENT_FIELD_NAME); while (rc.MoveNext()) { if (commentFieldIndex != -1) { rc.Current[commentFieldIndex] = comment; try { rc.Current.Store(); _editFlags.Add("modify_" + featureOID.ToString()); } catch (GeodatabaseRowException rowException) { System.Diagnostics.Debug.WriteLine(rowException.Message); } finally { context.invalidate(rc.Current); } } } } }, table); // don't await the edit operation editOperation.ExecuteAsync(); } } }
protected async Task <bool> ExecuteCut(EditingTemplate template, Geometry geometry, Dictionary <string, object> attributes) { if (template == null) { return(false); } if (geometry == null) { return(false); } // create an edit operation EditOperation op = EditingModule.CreateEditOperation(); op.Name = "Cut Elements"; op.ProgressMessage = "Working..."; op.CancelMessage = "Operation canceled."; op.ErrorMessage = "Error cutting polygons"; op.SelectModifiedFeatures = false; op.SelectNewFeatures = false; // get the feature class associated with the layer Table fc = await template.Layer.GetTableAsync(); // initialize a list of ObjectIDs that need to be cut var cutOIDs = new List <long>(); // on a separate thread await QueuingTaskFactory.StartNew(async() => { // TODO // find the features crossed by the sketch geometry RowCursor rc = await fc.SearchAsync(geometry, SpatialRelationship.Crosses); // add the feature IDs into our prepared list while (rc.MoveNext()) { var feature = rc.Current as Feature; if (feature == null) { break; } if (feature.Shape != null) { // we are interested in the intersection points // in case there is only one intersection then the sketch geometry doesn't enter and leave the // base geometry and the cut operation won't work. Geometry intersectionGeometry = GeometryEngine.Intersection(feature.Shape, geometry, GeometryDimension.esriGeometry0Dimension); if (intersectionGeometry is MultiPoint) { //var intersectionPoints = intersectionGeometry as MultiPoint; //// we are only interested in feature IDs where the count of intersection points is larger than 1 //// i.e., at least one entry and one exit //if (intersectionPoints.Coordinates.Count > 1) //{ // // add the current feature to the overall list of features to cut // cutOIDs.Add(rc.Current.ObjectID); //} } } } }); // add the elements to cut into the edit operation op.Cut(template.Layer, cutOIDs, geometry); //execute the operation bool operationResult = await op.ExecuteAsync(); return(operationResult); }
// TODO: change the method declaration to return Task of bool private void constructSamplePolylines(FeatureLayer polylineLayer, FeatureLayer pointLayer) { // TODO // execute the fine grained API calls on the CIM main thread //return QueuingTaskFactory.StartNew(() => { // get the feature classes and their definitions // get the underlying feature class for each layer var polylineFeatureClass = polylineLayer.GetTableAsync().Result as FeatureClass; var pointFeatureClass = pointLayer.GetTableAsync().Result as FeatureClass; FeatureClassDefinition polylineDefinition = null; FeatureClassDefinition pointDefinition = null; // construct a cursor for all point features, since we want all feature there is no // QueryFilter required var pointCursor = pointFeatureClass.Search(null, false); // set up the edit operation for the feature creation var createOperation = EditingModule.CreateEditOperation(); createOperation.Name = "Create polylines"; // initialize a counter variable int pointCounter = 0; // initialize a list to hold 5 coordinates that are used as vertices for the polyline var lineCoordinates = new List <Coordinate>(5); // with the first 20 points create polylines with 5 vertices each // be aware that the spatial reference between point geometries and line geometries is different // HINT: use GeometryEngine.Project // loop through the point features while (pointCursor.MoveNext()) { pointCounter++; // TODO // add the feature point geometry as a coordinate into the vertex list of the line //var pointFeature = ... use the current property of the cursor // - ensure that the projection of the point geometry is converted to match the spatial reference of the line //MapPoint pt = GeometryEngine.Project(pointFeature.Shape, polylineDefinition.SpatialReference) as MapPoint; //lineCoordinates.Add(pt.Coordinate); // for every 5 geometries, construct a new polyline and queue a feature create if (pointCounter % 5 == 0) { var newPolyline = new Polyline(lineCoordinates, polylineDefinition.SpatialReference); createOperation.Create(polylineLayer, newPolyline); lineCoordinates = new List <Coordinate>(5); } } // TODO // execute the edit (create) operation //HINT use createOperation.... execute async // TODO // save the edits //return EditingModule.SaveEditsAsync(); //}); }