问题
I am trying to setup a ESRI Local Server for displaying .mpk
. I have a Model like
public class Model
{
private string basemapLayerUri = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
private string mapPackage = "D:\\App\\Data\\Canada.mpk";
public Model() { }
public string BasemapLayerUri
{
get { return this.basemapLayerUri; }
set
{
if (value != this.basemapLayerUri)
{
this.basemapLayerUri = value;
}
}
}
public string MapPackage
{
get { return this.mapPackage; }
set
{
if (value != this.mapPackage)
{
this.mapPackage = value;
}
}
}
}
in ViewModel.cs
Class I have
public class ViewModel : INotifyPropertyChanged
{
public Model myModel { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
myModel = new Model();
this.CreateLocalServiceAndDynamicLayer();
}
public string BasemapUri
{
get { return myModel.BasemapLayerUri; }
set
{
this.myModel.BasemapLayerUri = value;
OnPropertyChanged("BasemapUri");
}
}
public async void CreateLocalServiceAndDynamicLayer()
{
LocalMapService localMapService = new LocalMapService(this.MAPKMap);
await localMapService.StartAsync();
ArcGISDynamicMapServiceLayer arcGISDynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer()
{
ID = "mpklayer",
ServiceUri = localMapService.UrlMapService,
};
//myModel.Map.Layers.Add(arcGISDynamicMapServiceLayer);
}
public string MAPKMap
{
get { return myModel.MapPackage; }
set
{
this.myModel.MapPackage = value;
OnPropertyChanged("MAPKMap");
}
}
protected void OnPropertyChanged([CallerMemberName] string member = "")
{
var eventHandler = PropertyChanged;
if (eventHandler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(member));
}
}
}
As you can see I am trying to implement the local server and dynamic layer in ViewModel.cs
like
public async void CreateLocalServiceAndDynamicLayer()
{
LocalMapService localMapService = new LocalMapService(this.MAPKMap);
await localMapService.StartAsync();
ArcGISDynamicMapServiceLayer arcGISDynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer()
{
ID = "mpklayer",
ServiceUri = localMapService.UrlMapService,
};
//myModel.Map.Layers.Add(arcGISDynamicMapServiceLayer);
}
but I do not know how to bind this service to the Model
? I tried
myModel.Map.Layers.Add(arcGISDynamicMapServiceLayer);
but as you know the myModel
doesn't have any Map object.
Update
using M_PK2.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Esri.ArcGISRuntime.LocalServices;
using Esri.ArcGISRuntime.Controls;
using Esri.ArcGISRuntime.Layers;
namespace M_PK2.ViewModels
{
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);
OnPropertyChanged(nameof(Layers)); //Notify UI
}
public LayerCollection Layers
{
get
{
return layers;
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged([CallerMemberName] string member = "")
{
PropertyChanged(this, new PropertyChangedEventArgs(member));
}
}
}
回答1:
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
回答2:
I don't have SDK available to try, but following code should work:
View Model:
private readonly LocalMapService localMapService;
// initialize localMapService instance in the constructor
public string UrlMapService
{
get { return localMapService.UrlMapService; }
}
XAML:
<!-- A Map ControlView to display various GIS layers. -->
<esriControls:MapView x:Name="MapView1" Width="448" Height="480" VerticalAlignment="Top" Margin="2,2,2,2">
<!-- A Map. -->
<esriControls:Map x:Name="Map1" >
<!-- Add an ArcGISDynamicMapServiceLayer via Xaml. Set the ID and ImageFormat properties. -->
<esriLayers:ArcGISDynamicMapServiceLayer ID="serviceLayer" ImageFormat="PNG24"
ServiceUri="{Binding UrlMapService, Mode=OneWay}"/>
</esriControls:Map>
</esriControls:MapView>
来源:https://stackoverflow.com/questions/53419823/implementing-mvvm-with-arcgis-runtime-local-server