I tend to always wonder why someone doesn't want to use an external MVVM library, but that is a conversation for another day.
Without an external library generally, one would want to roll their own delegate command ICommand implementation which is pretty straight forward
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
#region ICommand Members
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
With this one can just create an ICommand and pass the action into this DelegateCommand.
To simplify the MainWindow Xaml, instead of making the view model a static resource, bind it as the DataContext. By doing this one does not need to set the source of the binding, it will be the defined DatContext
<Window x:Class="WpfApp2.MainWindow"
...
xmlns:local="clr-namespace:WpfApp2"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" >
<Window.DataContext>
<local:MapViewModel />
</Window.DataContext>
<Grid>
<esri:MapView Map="{Binding Map}" x:Name="mapView" />
<!-- Command is bound to ICommand on ViewModel -->
<Button Content="Add Layer" Command="{Binding AddLayerCommand}"
Height="40" Width="80" Margin="10,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</Window>
Now we just wire up our ViewModel
public class MapViewModel : INotifyPropertyChanged
{
public MapViewModel()
{
CreateNewMap();
}
private void CreateNewMap()
{
//For sample using the viewpoint of the layer in code,
// personally I would not set an initial extent from a feature layer that needed to load at startup,
// but that's also a conversation for another day
var targetGeometry =
Geometry.FromJson(
"{\"xmin\":-13240129.679701095,\"ymin\":3994281.9887753138,\"xmax\":-13106722.92583799,\"ymax\":4101417.5063218847,\"spatialReference\":{\"wkid\":102100,\"latestWkid\":3857}}");
Map = new Map(Basemap.CreateImageryWithLabels())
{
InitialViewpoint = new Viewpoint(targetGeometry!)
};
Map.Loaded += (sender, args) => { Console.WriteLine("all done loading"); };
}
private Map _map;
public Map Map
{
get => _map;
set
{
_map = value;
OnPropertyChanged(nameof(Map));
}
}
//So here just create a DelegateCommand passing in the delegate, it will execute when invoked
public ICommand AddLayerCommand => new DelegateCommand(ExecuteAddLayer);
//As pointed out by Morten, generally an async should return a Task,
// In this situation void is proper because it is a Delegate command
private async void ExecuteAddLayer(object obj)
{
try
{
var trailHeadsLayer = new FeatureLayer(new Uri("https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0"));
await trailHeadsLayer.LoadAsync();
Map.OperationalLayers.Add(trailHeadsLayer);
await Map.LoadAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
//remove INotifyPropertyChanged for sample purposes
}
Thanks,
-Joe