首页 > 解决方案 > 使用 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));
        }
    }
}

标签: c#mvvmarcgis-runtimearcgis-runtime-net

解决方案


避免使用async voidexcept 事件处理程序,

参考Async/Await - 异步编程的最佳实践

在您的情况下,您正在混合属于视图的 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>

您现在可以根据需要通过代码操作图层


推荐阅读