c# - 使用 ArcGIS Runtime 本地服务器实现 MVVM
问题描述
我正在尝试设置一个 ESRI 本地服务器来显示.mpk
. 我有一个像
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;
}
}
}
}
在ViewModel.cs
课堂上我有
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));
}
}
}
如您所见,我正在尝试以ViewModel.cs
类似的方式实现本地服务器和动态层
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);
}
但我不知道如何将此服务绑定到Model
?我试过了
myModel.Map.Layers.Add(arcGISDynamicMapServiceLayer);
但如您所知,myModel
没有任何 Map 对象。
更新
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));
}
}
}
解决方案
避免使用async void
except 事件处理程序,
在您的情况下,您正在混合属于视图的 UI 问题。视图模型应该公开视图执行其功能所需的内容。
由于所用依赖项的异步性质LocalMapService
,您应该创建一个异步事件处理程序来管理获取服务 URI 并在该任务通过绑定的属性更改事件完成时通知 UI。
例如
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));
}
}
这样,在服务异步启动后,UI 将收到更改通知。
<!-- 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>
如果目标是能够操作多个图层,那么我建议绑定到Map.Layers 属性,以便能够直接访问视图模型中的图层集合。
视图模型最终可能看起来像
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;
}
}
}
和视图
<!-- 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>
您现在可以根据需要通过代码操作图层
推荐阅读
- python - 使用 sqlalchemy 从 PostgreSQL 数据库中的列更新单个值
- javascript - 如何在动态儿童按钮上附加关闭功能?
- numpy - 用 LU 分解求 A 的逆矩阵
- transactions - 文件放置时TDengine如何实现事务?
- asp.net-core - 如何将 UserManager 与自定义用户一起使用?
- express - 填充 Express 时缺少架构错误
- sql - Insert 语句将错误的随机数插入到 Oracle 表中
- ios - iOS WKWebView:如何链接到从服务器加载的 HTML 页面中的本地文件?
- python - SnapChat 广告 - 媒体 API
- vhdl - Verilog 和 VHDL 如何处理 (n+delta) 周期后的冲突分配?