MVVM in action (without a toolkit)

Setting up an app to use the MVVM pattern is relatively straightforward, but can involve a lot of boiler plate code. Let's illustrate this so that you can see all of the steps and the way things work exactly:

  1. Create a new ArcGIS Runtime app as you did in Chapter 1, Introduction to ArcGIS Runtime. Name your project Chapter2.
  2. Go ahead and copy the XAML and code-behind file from that project to this new project.
  3. Create a new, standard C# class. Name the file Model.cs, as shown here:
    MVVM in action (without a toolkit)
  4. Add the following code to the Model class:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Chapter2
    {
        public class Model
        {
            private string searchText = "Lancaster";
            private string basemapLayerUri = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
            private string usaLayerUri = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer";
           
            public Model()  { }
    
            public string SearchText
            {
                get { return this.searchText; }
                set
                {
                    if (value != this.searchText)
                    {
                        this.searchText = value;
                    }
                }
            }
            public string BasemapLayerUri
            {
                get {return this.basemapLayerUri; }
                set
                {
                    if (value != this.basemapLayerUri)
                    {
                        this.basemapLayerUri = value;
                    }
                }
            }
            public string USALayerUri
            {
                get  { return this.usaLayerUri; }
                set
                {
                    if (value != this.usaLayerUri)
                    {
                        this.usaLayerUri = value; 
                    }
                }
            }
        }
    }

Most of this is a pretty basic .NET code. It simply stores our layer's URIs and a search string. Now that the Model class has been created, it's time to create the ViewModel class:

  1. Add references to the System.ComponentModel and System.Runtime.CompilerServices namespaces.
  2. Add another class (name it ViewModel.cs) to the project and enter this code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    namespace Chapter2
    {
        public class ViewModel : INotifyPropertyChanged
        {
            public Model myModel { get; set; }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public ViewModel()
            {
                myModel = new Model();
            }
    
            public string BasemapUri
            {
                get { return myModel.BasemapLayerUri;  }
                set 
                { 
                    this.myModel.BasemapLayerUri = value;
                    OnPropertyChanged("BasemapUri");
                }
            }
            public string USAUri
            {
                get { return myModel.USALayerUri; }
                set 
                {
                    this.myModel.USALayerUri = value;
                    OnPropertyChanged("USAUri");
                }
            }
            public string SearchText
            {
                get { return myModel.SearchText; }
                set 
                { 
                    this.myModel.SearchText = value;
                    OnPropertyChanged("SearchText");
                }
            }
    
            protected void OnPropertyChanged([CallerMemberName] string  
                member = "")
            {
                var eventHandler = PropertyChanged;
                if (eventHandler != null)
                {
                    PropertyChanged(this, new  
                            PropertyChangedEventArgs(member));
                }
            }
        }
}

    Our ViewModel class looks a little more involved. It contains an event (PropertyChangedEventHandler) and method (OnPropertyChanged). The OnPropertyChanged method is really the method that makes binding work in WPF apps. When this method is called, a WFP element updates itself based on firing an event, such as a text change in a textbox. The CallerMemberName attribute will pass the property name that executed OnPropertyChanged. Whenever a property is changed, OnPropertyChanged is called.

  3. Open MainWindow.xaml and add the following line along with the other namespaces:
    xmlns:local="clr-namespace:Chapter2"

    Note that your namespace will be different if you used a different project name.

  4. Add the following resource for Window. This resource informs Window to set its DataContext class in the ViewModel class and use it throughout the entire Window resource:
    <Window.Resources>
        <local:ViewModel x:Key="VM"/>
    </Window.Resources>       

    These lines of code instruct the View class to use the ViewModel class and name it VM.

  5. Next, add the DataContext class to the Grid tag, which is the root element:
    <Grid DataContext="{StaticResource VM}">

    This line of code is pretty powerful. It's basically telling the View (XAML) class to use this ViewModel class as the data source. In reality, the ViewModel class is actually relying on the Model class to handle the data, but the DataContext class of View has no idea that this is occurring (remember SoC?).

  6. The next task is to update the layer binding to use the properties on the ViewModel class. Make the changes as in the following code:
    <esri:ArcGISTiledMapServiceLayer ID="Basemap"
        ServiceUri="{Binding Source={StaticResource VM}, 
        Path=BasemapUri}"/>
    <esri:ArcGISDynamicMapServiceLayer ID="USA" 
        ServiceUri="{Binding Source={StaticResource VM}, 
        Path=USAUri}"/>
  7. Also, add this line:
    <TextBox Name="SearchTextBox" Text="{Binding SearchText}"></TextBox>

    These changes bind the elements to the properties of ViewModel. Also, two ways of binding to the ViewModel class have been shown. The ServiceUri properties are referring to the ViewModel class, while SearchTextBox is just referring to the property without specifying the ViewModel class. This is possible because the property is looking up to the root element, which is our ViewModel class.

    The XAML code is shown here in its entirety:

    <Window x:Class="Chapter2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
        xmlns:local="clr-namespace:Chapter2"
            Title="MainWindow" 
            Height="600" 
            Width="800">
    
        <Window.Resources>
            <local:ViewModel x:Key="VM"/>
        </Window.Resources>
    
        <Grid DataContext="{StaticResource VM}">        
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <esri:MapView x:Name="MyMapView" Grid.Row="0"
                LayerLoaded="MyMapView_LayerLoaded" >
                <esri:Map>
                    <esri:ArcGISTiledMapServiceLayer ID="Basemap"
                        ServiceUri="{Binding Source={StaticResource VM}, 
                        Path=BasemapUri}"/>
                    <esri:ArcGISDynamicMapServiceLayer ID="USA" 
                       ServiceUri="{Binding Source={StaticResource VM}, 
                           Path=USAUri}"/>
                </esri:Map>
            </esri:MapView>
    
            <TextBlock Grid.Row="0" Name="Search" Background="#77000000" 
                HorizontalAlignment="Center"
                VerticalAlignment="Top" Padding="5" 
                Foreground="White" >
                <Run>Search for  </Run>
                <TextBox Name="SearchTextBox" 
                    Text="{Binding SearchText}">
                </TextBox>
                <Run>  in the Cities, Counties or States layer. </Run>
                <Button Content="Find" Width="30" Click="Button_Click">
                </Button>
            </TextBlock>
    
            <DataGrid Name="MyDataGrid" Grid.Row="2" 
                Height="200" ></DataGrid>
    
        </Grid>
    </Window>
  8. Run the app and you will see the same layers as you saw in Chapter 1, Introduction to ArcGIS Runtime. If you don't, find the code to be as shown here and resolve any errors, and if you need any help following along, see the sample project included with this book; it's named Chapter2.

So, with a few changes, we satisfied SoC to a certain degree. We now have Model, View, and ViewModel classes. While this may seem like a lot of changes to achieve the same app which we built in the previous chapter, you will find that as your apps become more complex, this pattern will allow multiple people to work on the app. And, as we will see, this pattern allows us to test our app.

While this is a huge improvement, there is actually a lot of boilerplate code. Imagine if your app had 20 Models, 10 ViewModels, and several UIs. We didn't even deal with the button here because it would have resulted in even more boilerplate code. In effect, you'd end up repeating a lot of this code, such as OnPropertyChanged and PropertyChangedEventHandler. You could roll a base class to handle this of course, and that would help, but the good news is that this has been dealt with by others. Enter MVVM Light.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.187.62