0

Using ArcGIS Runtime .Net SDK 10.2.7, with MVVM pattern I am getting 'System.NullReferenceException'. in ViewModel constructor at:

this.mapView.Map.Layers.Add(localTiledLayer);

What am I doing wrong?

What I have is:

1- ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{ 
    private Model myModel = null;
    private MapView mapView = null;
    public event PropertyChangedEventHandler PropertyChanged;
    public ViewModel()
    {
        var URI = "D:/GIS/Runtime/Tutorial/VanSchools.tpk";
        ArcGISLocalTiledLayer localTiledLayer = new ArcGISLocalTiledLayer(URI);
        localTiledLayer.ID = "SF Basemap";
        localTiledLayer.InitializeAsync();

        this.mapView.Map.Layers.Add(localTiledLayer);

    }
    public string TilePackage
    {
        get { return this.myModel.TilePackage; }
        set
        {
            this.myModel.TilePackage = value;

        }
    }

2- Model.cs

public class Model
{
    private string tilePackage = "";

    public Model() { }

    public string TilePackage
    {
        get { return this.tilePackage; }
        set
        {
            if (value != this.tilePackage)
            {
                this.tilePackage = value;
            }
        }
    }

3- MainWindow.xaml

<Window x:Class="SimpleMVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
        xmlns:local="clr-namespace:SimpleMVVM"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

        <Window.Resources>
            <local:ViewModel x:Key="VM"/>
        </Window.Resources>

        <Grid DataContext="{StaticResource VM}">
            <Grid.RowDefinitions>
                <RowDefinition Height="400" />
                <RowDefinition Height="200" />
            </Grid.RowDefinitions>

            <esri:MapView x:Name="MyMapView" Grid.Row="0"
                      LayerLoaded="MyMapView_LayerLoaded" >
                <esri:Map>  </esri:Map>
            </esri:MapView>

        </Grid>
</Window>

4- MainWindow.xaml.cs

  public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }

            private void MyMapView_LayerLoaded(object sender, LayerLoadedEventArgs e)
            {
                if (e.LoadError == null)
                    return;

                Debug.WriteLine(string.Format("Error while loading layer : {0} - {1}", e.Layer.ID, e.LoadError.Message));
            }

        }

enter image description here

halfer
  • 19,824
  • 17
  • 99
  • 186
Mona Coder
  • 6,212
  • 18
  • 66
  • 128
  • Your view model should not contain any references to mapView (or any other visual elements). That's breaking with the MVVM pattern. Instead your viewmodel should contain the map only, and you bind the map to the viewmodel's Map property in XAML. Update: Just realized that's exactly what Antti wrote below. That's the "proper" mvvm way to do it – dotMorten Nov 28 '17 at 19:11

2 Answers2

2

It looks like the problem is that mapView has not been set to an instance of an object. Do you need to say something like:

this.mapView = new Mapview();
this.mapView.Map.Layers.Add(localTiledLayer);
Chris Mack
  • 5,148
  • 2
  • 12
  • 29
1

It seems that you aren't hooking up the binding but in general you shouldn't have reference to the MapView from your ViewModel and should bind MapView.Map instead. Map is considered being a model that defines how your MapView looks.

In your view make sure that you have the binding setup

<Window.Resources>
    <vm:MainViewModel x:Key="vm"></vm:MainViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource vm}">
    <esri:MapView x:Name="MyMapView"
                  Map="{Binding Map}"
                  LayerLoaded="MyMapView_LayerLoaded">
    </esri:MapView>
</Grid>

Then in your ViewModel you can do something like this

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        // Create map that is concidered to being a model that defines the content
        // of the map
        var map = new Map();
        // Add your layers to the map
        var testServicePath = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
        map.Layers.Add(new ArcGISTiledMapServiceLayer(new Uri(testServicePath)));
        // map.Layers.Add(new ArcGISLocalTiledLayer("add your path here"));
        Map = map;
    }

    private Map _map;
    public Map Map
    {
        get
        {
            return _map;
        }
        set
        {
            _map = value;
            NotifyPropertyChanged(nameof(Map));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}