Hi everyone,
I've a problem with drawing on map on ArcGIS SDK 100.7 for .NET.
I'm using SketchEditor and I need to configure visibility of a button with SketchEditor.CompleteCommand.CanExecute.
My problem is that the execution of my test is always done before the drawing.
Sequence step by step :
- //Step 1
- Geometry g = await MapView.SketchEditor.StartAsync(SketchCreationMode.Point, true); //Async method
- MapView.SketchEditor.CompleteCommand.CanExecute(null); //Return false
- //Step 2 : Click on the map
- MapView.SketchEditor.CompleteCommand.CanExecute(null); //Return false
- //The 1st drawing appears on the map
- //Step 3 : Click on the map
- MapView.SketchEditor.CompleteCommand.CanExecute(null); //Return true but detect only 1st point instead of 1st & 2nd.
- //The 2nd drawing appears on the map
My goal is to detect the first point into my first control.
The difficult is my initialisation are asynchrone, but my control is synchrone.
I tried to pass the 2 asynchronously but the error is still present.
Currently, my button appears when creating the 2nd point. I have the same problem for polylines and polygons. My button appears at the 3rd point for my polyline, and at my 4th point for my polygon. There is always a point of delay.
Have you got any idea?
Thank you
The CompleteCommand is managed by the API and cannot be used in the ways you are describing.
When you use the SketchEditor you basically give control of the map to device input. Once you call
geometry = await SketchEditor.StartAsync(sketchCreationMode, false);
The Map is now waiting for input, (i.e., a mouse-click or tap). The API basically controls things until the correct input is received.
I am not sure I understand the goal you are trying to achieve. Do you want the use to be able to accept the placement of the geometry?
-Joe
Thanks for your answer.
My real problem is the order of event execution.
After a click, the MapView Tapped event is detected before the geometry creation.
So, I don't know how control future geometry because it doesn't yet exist.
Do you know how to reverse the order of execution of events?
I'm not sure what you mean by 'control future geometry.' The geometry is created by the SketchEditor. In the method you are calling
mapPoint = (MapPoint)await SketchEditor.StartAsync(SketchCreationMode.Point, false);
The next line of code will have the geometry after the MapView is tapped. You really don't need to do anything in the MapView.Tapped event handler.
What do you want your button to be doing? I am not clear what your workflow is. You will need some kind of Command to initiate the call above to the SketchEditor. In the esri provided sample they have a button that when invoked Starts the sketch editor.
What I want to do is very simple.
If any geometry is created, then my button is visible.
My button is here to validate the draw. I need to detect that the drawing can be completed.
-If SketchEditor initialized but no drawing, then button "Validate" hidden
-If SketchEditor.Command.canComplete then button "Validate" visible
The easiest is to just bind the command to the button, and it'll automatically enable/disable. That's actually the main reason it's a command.
ie
<Button Command="{Binding SketchEditor.CompleteCommand, ElementName=mapView}" Content="Complete" />
If you want to do it programmatically, use the CanExecuteChanged event on the ICommand interface to detect when the state of the command might have changed.
Yup I'll have to eat a little crow here, I have set the SketchCreationMode to false in our standard configuration which behaves differently and just triggers on the click. Although I don't think binding directly to SketchEditor.CompleteCommand does what is being asked.
In Xaml:
<Button Command="{Binding CompleteCommand}" Text="Yo Dude" Height="30" IsVisible="{Binding YoVisible}"/>
In ViewModel (I have SketchEditor initialized in ViewModels through an event, so this would be slightly different in the MapView's ViewModel):
EventAggregator.GetEvent<SketchEditorInitializedEvent>().Subscribe(se =>
{
SketchEditor = se;
SketchEditor.CompleteCommand.CanExecuteChanged += CompleteCommandOnCanExecuteChanged;
}));
private void CompleteCommandOnCanExecuteChanged(object sender, EventArgs e)
{
var x = SketchEditor.Geometry;
if ( x != null )
{
YoVisible = true;
}
Console.WriteLine("Here");
}
public bool YoVisible
{
get => _yoVisible;
set => SetProperty(ref _yoVisible, value);
}
public ICommand CompleteCommand => new DelegateCommand<object>(ExecuteComplete);
private void ExecuteComplete(object obj)
{
if ( SketchEditor.CompleteCommand.CanExecute(null) )
{
SketchEditor.CompleteCommand.Execute(null);
}
}
Although you really don't need the if statement in the ExecuteComplete as the button is only visible if the CanExecute is True
I don't see any way that you could actually know what CanExecute is besides there being a geometry associated to the SketchEditor
Thanks for your answers, but it's not exactly that.
You'll find my code below to better understand.
In Xaml :
<AppBarButton Margin="0,0,-15,0"
Command="{Binding ValiderTraceCommand}"
Icon="Accept"
Label="Valider le tracé"
Style="{StaticResource DefaultAppBarButtonStyle}"
Visibility="{Binding ValiderTraceCommandIsVisible, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"
Width="100"
FontSize="10"/>
<Border Grid.Column="1"
BorderBrush="Gray"
BorderThickness="1,0,0,0">
<esriControls:MapView x:Name="mapView"
Map="{Binding Map}" Loaded="mapView_Loaded">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="GeoViewTapped">
<core:InvokeCommandAction Command="{Binding OnMapViewTappedCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</esriControls:MapView>
</Border>
In View :
public bool ValiderTraceCommandIsVisible
{
get
{
return ValiderTraceCommand.CanExecute(null);
}
}
public RelayCommand ValiderTraceCommand
{
get
{
return _validerTraceCommand ?? (_validerTraceCommand = new RelayCommand(async () => await ValiderTrace(), CanValiderTrace));
}
}
private async Task ValiderTrace()
{
if (MapView.SketchEditor.CompleteCommand.CanExecute(null))
{
MapView.SketchEditor.CompleteCommand.Execute(null);
}
}
private Boolean CanValiderTrace()
{
return MapView.SketchEditor.CompleteCommand.CanExecute(null) && otherVariable;
}
public RelayCommand<object> OnMapViewTappedCommand
{
get
{
return _onMapViewTappedCommand ??
(_onMapViewTappedCommand =
new RelayCommand<object>(async mapView => await OnMapViewTapped(mapView)));
}
}
private async Task OnMapViewTapped(object mapViewInputEventArgs)
{
ValiderTraceCommand.RaiseCanExecuteChanged();
RaisePropertyChanged(() => ValiderTraceCommandIsVisible);
}
SketchEditor is initialized by :
Geometry g = await MapView.SketchEditor.StartAsync(SketchCreationMode.Polygon, true);
Here's the problem, when I click on the map, OnMapViewTapped runs before drawing.
Therefore, during the third point creation, my test detect only two point. Always one point behind.
How can I launch this test after the drawing ?
I am not clear where this part is in your code, what triggers the SketchEditor to Start?
Geometry g = await MapView.SketchEditor.StartAsync(SketchCreationMode.Polygon, true);
Using the SketchEditor you don't need to be handling GeoViewTapped. It will move past the above line of code when the shape is complete. Once it moves past the geometry is drawn on the MapView and the CompleteCommand.CanExecute will be true
Yes, the line after the initialisation runs when the drawing is complete but I want to run drawing test before the end of the drawing. That is why I use GeoViewTapped.
I want to check that the drawing can be completed before it is complete.