Select to view content in your preferred language

how to create custom commands

2288
1
Jump to solution
02-18-2013 10:52 AM
markcheyne
Deactivated User
I am looking for direction to docs/examples on how to implement custom commands using the ArcGIS Silverlight API.

I am using the Editor as my model for expected behavior.

For example, from the user's perspective:

1) click a button
2) click a point on the map
3) something happens using that point. For some tools that involves drawing the point, but more commonly, we just use the point geometry without drawing anything.

Such as: Clone a polygon from a map service into the current edit layer - e.g. do a spatial query against a polygon layer in a map service using the clicked point. Grab the selected polygon, and use it to replace the Graphic of the current row in an Editor layer.

From the developer's perspective, I have thought this would be: In the View, or in its ViewModel:
1) add a property ICommand CloneWaterbody {get; private set;}
2) in the constructor, assign a new instance of a DelegateCommand to that property, specifying the methods to execute for the command's Execute and CanExecute actions.
3) also in the constructor, set the button's DataContext to 'this' or the ViewModel.
4) implement the handlers for the Execute and CanExecute actions
5) in the XAML, bind the button's Command to the CloneWaterbody property

Trouble with all that, I am missing a crucial piece - interaction with the map -for example, a comparable Editor button, similarly bound to a property of the Editor, will allow you to draw on the map - say to create a line to cut a polygon, or add a new one, whatever. In my case, I usually merely need to click a point on the map, at which time I expect my button's command to execute (and be provided the Graphic or Geometry of the mouse click) - not when the button itself was clicked as with my suggested ICommand implementation above.

I've not had good luck querying the forums for things like 'custom command'... I suspect it might involve some use of the Draw class, even if I don't actually need to draw, merely grab a mouse click geometry....

Thanks very much in advance.
0 Kudos
1 Solution

Accepted Solutions
markcheyne
Deactivated User
In typical fashion, I've answered my own question. I had a few hours of hair pulling trying to understand why my Command would not utilize its CanExecute method - showing my ignorance of commanding - which boiled down to "you have to set a CommandParameter to some object that will change, and in changing causes the Command to recheck CanExecute, or you have to raise CanExecuteChanged yourself". This link finally showed me the light.

That snarl was really incidental to the nature of my original question, which was how my custom command could interact with the map. THAT boils down to "you have to use the Draw object".

Items 1-5, and part of 6 below are just basic "Commanding in Silverlight". The latter parts of 6 explain what I did with the Draw object. By way of introduction, I have a floating window called EditWidget with a number or tabs in a tab control, one being the EditTab with some buttons on it, one for cloning a waterbody from a service to an edit layer.

1) To the View or ViewModel class (EditTab view class in my case), add a property for the ICommand:
public ICommand CloneWaterbody { get; private set; }

2) In EditTab constructor, set that property to an instance of DelegateCommand (you have to write your own DelegateCommand class that implements ICommand, I cribbed this😞
this.CloneWaterbody = new DelegateCommand(CloneWaterbodyExecute, CloneWaterbodyCanExecute);


Note that both args refer to handler methods on the Edit Tab class, described below.

3) Also in the constructor, set the Button's DataContext to 'this' (because I am binding to the View, not an external ViewModel in this case).

4) In the EditTab XAML, set the Button's Command to the command property, Its CommandParameter to a public EditTab property (CurrentFeature in my case) that will indicate a change when the command should recheck its CanExecute state, and optionally set a Click event handler (e.g. to set the button's clicked style):

<Button x:Name="ButtonCloneWaterbody" DataContext="" Margin="4" Width="24" Height="24" Command="{Binding CloneWaterbody}" CommandParameter="{Binding CurrentFeature}" Click="ButtonCloneWaterbody_Click" ToolTipService.ToolTip="Clone 24K Hydro Feature">  <Button.Content>   <Image Source="images/clonewaterbody.png" Stretch="None" />  </Button.Content> </Button>



5) make sure that EditTab implements INotifyPropertyChanged, as every binding source (target?) must. In my case, the CurrentFeature property setter raises the PropertyChanged event.

6) In the EditTab class, implement handlers for the Command's Execute and CanExecute actions, the Button's Click event, and the EditTab.OnDrawComplete() method.

CloneWaterbodyExecute(object arg)
    - mostly boilerplate - set up the Draw object, including a handler for its DrawComplete event (OnDrawComplete() in my case) and set a cursor.

//setup so we can interact with the map EditWidget.DrawObject.IsEnabled = true; EditWidget.DrawWidget = EditWidget.GetType(); EditWidget.DrawObject.DrawMode = DrawMode.Point; EditWidget.MapControl.Cursor = Cursors.Arrow;


CloneWaterbodyCanExecute(object arg)
    - just a switch over my list of CurrentFeature.EditMode values. Currently I enable for Insert and Update, else disable.

The button click event handler is optional. I use it to reset the button style to indicate it is the current tool.

OnDrawComplete() method
     - where the real work happens. Get the click point from the DrawEventArgs.Geometry property, and do stuff with it. then do cleanup stuff - clearing graphics, disabling the draw object, resetting the cursor, and clearing the button selected style.

whew!

View solution in original post

0 Kudos
1 Reply
markcheyne
Deactivated User
In typical fashion, I've answered my own question. I had a few hours of hair pulling trying to understand why my Command would not utilize its CanExecute method - showing my ignorance of commanding - which boiled down to "you have to set a CommandParameter to some object that will change, and in changing causes the Command to recheck CanExecute, or you have to raise CanExecuteChanged yourself". This link finally showed me the light.

That snarl was really incidental to the nature of my original question, which was how my custom command could interact with the map. THAT boils down to "you have to use the Draw object".

Items 1-5, and part of 6 below are just basic "Commanding in Silverlight". The latter parts of 6 explain what I did with the Draw object. By way of introduction, I have a floating window called EditWidget with a number or tabs in a tab control, one being the EditTab with some buttons on it, one for cloning a waterbody from a service to an edit layer.

1) To the View or ViewModel class (EditTab view class in my case), add a property for the ICommand:
public ICommand CloneWaterbody { get; private set; }

2) In EditTab constructor, set that property to an instance of DelegateCommand (you have to write your own DelegateCommand class that implements ICommand, I cribbed this😞
this.CloneWaterbody = new DelegateCommand(CloneWaterbodyExecute, CloneWaterbodyCanExecute);


Note that both args refer to handler methods on the Edit Tab class, described below.

3) Also in the constructor, set the Button's DataContext to 'this' (because I am binding to the View, not an external ViewModel in this case).

4) In the EditTab XAML, set the Button's Command to the command property, Its CommandParameter to a public EditTab property (CurrentFeature in my case) that will indicate a change when the command should recheck its CanExecute state, and optionally set a Click event handler (e.g. to set the button's clicked style):

<Button x:Name="ButtonCloneWaterbody" DataContext="" Margin="4" Width="24" Height="24" Command="{Binding CloneWaterbody}" CommandParameter="{Binding CurrentFeature}" Click="ButtonCloneWaterbody_Click" ToolTipService.ToolTip="Clone 24K Hydro Feature">  <Button.Content>   <Image Source="images/clonewaterbody.png" Stretch="None" />  </Button.Content> </Button>



5) make sure that EditTab implements INotifyPropertyChanged, as every binding source (target?) must. In my case, the CurrentFeature property setter raises the PropertyChanged event.

6) In the EditTab class, implement handlers for the Command's Execute and CanExecute actions, the Button's Click event, and the EditTab.OnDrawComplete() method.

CloneWaterbodyExecute(object arg)
    - mostly boilerplate - set up the Draw object, including a handler for its DrawComplete event (OnDrawComplete() in my case) and set a cursor.

//setup so we can interact with the map EditWidget.DrawObject.IsEnabled = true; EditWidget.DrawWidget = EditWidget.GetType(); EditWidget.DrawObject.DrawMode = DrawMode.Point; EditWidget.MapControl.Cursor = Cursors.Arrow;


CloneWaterbodyCanExecute(object arg)
    - just a switch over my list of CurrentFeature.EditMode values. Currently I enable for Insert and Update, else disable.

The button click event handler is optional. I use it to reset the button style to indicate it is the current tool.

OnDrawComplete() method
     - where the real work happens. Get the click point from the DrawEventArgs.Geometry property, and do stuff with it. then do cleanup stuff - clearing graphics, disabling the draw object, resetting the cursor, and clearing the button selected style.

whew!
0 Kudos