Implementing MVVM with ArcGIS Runtime local server
Avoid using async void
except for event handlers,
Reference Async/Await - Best Practices in Asynchronous Programming
In your case you are mixing UI concerns that belong in view. The view model should expose what the view needs in order to perform its function.
Because of the async nature of the used dependency LocalMapService
, you should create an async event handler to manage getting the service URI and notify the UI when that task is completed via a bound property change event.
For example
public class ViewModel : ViewModelBase {
private readonly LocalMapService localMapService;
private readonly Model myModel;
private string serviceUri;
public ViewModel() {
myModel = new Model();
localMapService = new LocalMapService(myModel.MapPackage);
starting += onStarting;
starting(this, EventArgs.Empty);
}
private event EventHandler starting = delegate { };
private async void onStarting(object sender, EventArgs args) {
starting -= onStarting; //optional
// the following runs on background thread
await localMapService.StartAsync();
// returned to the UI thread
ServiceUri = localMapService.UrlMapService; //notifies UI
}
public string ServiceUri {
get { return serviceUri; }
set {
serviceUri = value;
OnPropertyChanged();
}
}
}
public class ViewModelBase : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged([CallerMemberName] string member = "") {
PropertyChanged(this, new PropertyChangedEventArgs(member));
}
}
That way after the async starting of the service, the UI will get notified of the change.
<!-- Add a MapView Control. -->
<esriControls:MapView x:Name="MapView1">
<!-- Add a Map. -->
<esriControls:Map>
<!-- Add an ArcGISDynamicMapServiceLayer via XAML. -->
<esriLayers:ArcGISDynamicMapServiceLayer ID="mpklayer"
ServiceUri="{Bind ServiceUri}"/>
</esriControls:Map>
</esriControls:MapView>
If the goal is to be able to manipulate multiple layers then I would suggest binding to the Map.Layers Property to be able to have direct access to the layers collection in the view model.
The view model could end up looking like
public class ViewModel : ViewModelBase {
private readonly LocalMapService localMapService;
private readonly Model myModel;
private LayerCollection layers;
public ViewModel() {
myModel = new Model();
layers = new LayerCollection();
localMapService = new LocalMapService(myModel.MapPackage);
starting += onStarting;
starting(this, EventArgs.Empty);
}
private event EventHandler starting = delegate { };
private async void onStarting(object sender, EventArgs args) {
starting -= onStarting; //optional
// the following runs on background thread
await localMapService.StartAsync();
// returned to the UI thread
var serviceLayer = new ArcGISDynamicMapServiceLayer() {
ID = "mpklayer",
ServiceUri = localMapService.UrlMapService,
};
Layers.Add(serviceLayer);
}
public LayerCollection Layers {
get {
return layers;
}
}
}
And the view
<!-- Add a MapView Control. -->
<esriControls:MapView x:Name="MapView1">
<!-- Add a Map. with layers via binding-->
<esriControls:Map Layers="{Bind Layers, Mode=OneWay}" />
</esriControls:MapView>
You can now manipulate layers via code as needed