在使用ArcGIS Runtime Local Server实施MVVM时遇到问题

时间:2018-11-21 20:14:12

标签: c# mvvm arcgis-runtime arcgis-runtime-net

我正在尝试设置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));
        }
    }
}

2 个答案:

答案 0 :(得分:5)

除了事件处理程序外,避免使用async void

引用Async/Await - Best Practices in Asynchronous Programming

在您的情况下,您正在混合属于视图的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 Property以便能够直接访问视图模型中的图层集合。

视图模型可能最终看起来像

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>

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

答案 1 :(得分:0)

我没有可用的SDK,但是以下代码应该可以工作:

查看模型:

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>