Is there a Pattern for migrating from ITool to WPF MVVM?

111
4
08-14-2019 12:08 PM
Highlighted
Occasional Contributor

Hi -

I am trying to port an app developed with ArcGIS Engine (.NET) to .NET Runtime SDK.

Is there a document somewhere to help guide migration from ArcObjects to .NET runtime SDK?

The ArcEngine app heavily leverages the ITool interface along with IToolbarBuddy.CurrentTool, and IMapControl.CurrentTool.  We would like to continue using this pattern, or something close to it.  However, I haven't found a sample showing how to implement this pattern with WPF MVVM.

I've looked at the MeasureToolbar in the toolkit, but it doesn't seem very extensible.

Here are just a few questions:

Could an Attached Behavior on the MapView be used for CurrentTool?

Should each ITool class contain it's own SketchEditor?

Is there some way to suppress the TaskCanceledException thrown when SketchEditor.CancelCommand is executed?

Is there a way to implement rubber-banding similar to the way to all the classes that implement IDisplayFeedback?

Thanks, Kirk

Reply
0 Kudos
4 Replies
Highlighted
Esri Frequent Contributor

> Is there some way to suppress the TaskCanceledException thrown when SketchEditor.CancelCommand is executed?

You should always try/catch(TaskCancelledException) when you start and away a draw from the sketch editor. 

> Is there a way to implement rubber-banding similar to the way to all the classes that implement IDisplayFeedback?

Currently not, but it's something we're currently talking about adding support for.

I don't know the ITool bits from ArcObjects, so hopefully someone else can answer that. Generally though, the goals and design of Engine and Runtime are quite different, so you definitely have to rethink some existing approaches.

Highlighted
MVP Regular Contributor

If you want to do something like ITool you basically need to roll your own. In ArcGIS Runtime you are building the user interface from scratch, not integrating into another user interface framework.

ITool is tricky because of the on/off behavior.  In Runtime this is more complex because you then have be capturing MapView events or in this case using SketchEditor

We developed an approach that was more around doing an ICommand using an ItemsControl in Xaml which defines the DataTemplate of your button.  Along these lines

<ItemsControl Margin="0, 0, 10, 0" x:Name="ToolbarCommands" HorizontalAlignment="Stretch" ItemsSource="{Binding ToolbarCommands}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Padding="10" Style="{DynamicResource ToolbarButtonStyle}" Command="{Binding Command}"
CommandParameter="{Binding}">

<Button.ToolTip>
<StackPanel Background="LightYellow" Width="180">
<Label Content="{Binding Name}" FontWeight="Bold" Margin="0 0 0 5"/>
<TextBlock Text="{Binding ToolTip}" TextWrapping="Wrap" />
</StackPanel>
</Button.ToolTip>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{Binding ImageUri}"/>
</Style>

</Image.Style>
</Image>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Then you can have your own tool interface (in this case IToolbarCommand)

namespace Mobile.Framework.UI
{
public interface IToolbarCommand
{
/// <summary>
/// Tool name
/// </summary>
string Name { get; set; }

/// <summary>
/// Detailed tooltip describing tool usage
/// </summary>
string ToolTip { get; set; }

/// <summary>
/// The order (from left to right) that tool will be placed
/// </summary>
int Order { get; set; }

/// <summary>
/// Uri of the image to display on the ToolbarCommand
/// </summary>
Uri ImageUri { get; set; }

/// <summary>
/// The ICommand that is invoked by the ToolbarCommand
/// </summary>
ICommand Command { get; set; }
}
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Then the trick is to just come-up with the way to load these at runtime that fits your application architecture.  Our approach was to use tools defined in Prism Modules which fired an event that was received in the main user interface bound view model.

Unfortunately for things that had an On./Off state never really defined as clean an approach.  What we generally did was have a custom ToggleButton style that had pressed/unpressed look with a bound IsChecked property.  The initial click would trigger an ICommand and when it was time to give control back any handlers were released and the IsChecked set back to false.  SketchEditor is nice because it is awaitable so things can often be done in a single method.

Good luck

Highlighted
Occasional Contributor

I've backed off of trying to do ITool. Instead, I've been able to do something more similar to ArcGIS Pro's MapTool abstract class.

The viewmodel (MapVM) that's on MapView.DataContext has a CurrentTool property.

The toolbar switches the CurrentTool on the MapVM.


The MapView has a Behavior<MapView> wired to listen to a  MapVM.CurrentToolChanged event.


When that event fires, the behavior disables the previous MapView.SketchEditor before setting MapView.SketchEditor to the newly selected MapTool.SketchEditor.


I don't want the user to have to select the tool from the tool bar after each completion, so it loops while MapTool.SketchEditor.IsEnabled

To do some of the IDisplayFeedback types of things, I'm leveraging this handy AdornerContentPresenter, and adding it to the AdornerLayer in an AdornerDecorator that the MapView control sits within.

I'm making some assumptions:

  • the adorner won't collide with any internal adorner the mapview might use.
  • it's ok to switch out the MapView.SketchEditor when it's disabled (like the MeasureTool in the toolkit)
  • nothing special needs to be done when TaskCanceledException is thrown
Highlighted
MVP Regular Contributor

Sounds like an interesting approach.  I am working almost exclusively in Xamarin these days but if I get back to any WPF stuff will have to take a look

Cheers

Reply
0 Kudos