Loading layers dynamically from XML file

2313
3
06-07-2011 10:08 AM
LisaChesley
New Contributor
Good afternoon, all!

I am trying to create an application that has an XML file that our GIS staff can easily modify to add new layers to the application without having to change code.

I can load the XML file into my application, and create a layer collection in my view model to bind to my map, but nothing is showing up in the final map.  When I check with Fiddler, the map services aren't being called, and there are no errors at all.

Here is the code I have so far:

MainViewModel.cs (view model)
public class MainViewModel : ViewModelBase
    {
        #region Private Variables

        private string[] dynamicLayersIDs;
        private LayerCollection mapLayerCollection;
        private XDocument userMapLayers;

        #endregion

        #region Public Properties

        public string[] DynamicLayersIDs
        {
            get { return dynamicLayersIDs; }
            set
            {
                if (dynamicLayersIDs != value)
                {
                    dynamicLayersIDs = value;
                    RaisePropertyChanged("DynamicLayersIDs");
                }
            }
        }

        public LayerCollection MapLayerCollection
        {
            get { return mapLayerCollection; }
            set
            {
                if (mapLayerCollection != value)
                {
                    mapLayerCollection = value;                    
                    RaisePropertyChanged("LayerCollection");
                }
            }
        }

        public XDocument UserMapLayers
        {
            get { return userMapLayers; }
            set
            {
                if (userMapLayers != value)
                {
                    userMapLayers = value;
                    BuildMapLayerCollection();
                    RaisePropertyChanged("UserMapLayers");
                }
            }
        }

        #endregion

        #region Constructors

        public MainViewModel()
        {
            //Load XML file.
            LoadXMLFile();
        }

        #endregion

        #region Data Methods

        protected void LoadXMLFile()
        {            
            //Get XML file information.
            WebClient webClient = new WebClient();
            webClient.OpenReadCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    if (e.Result != null)
                    {
                        using (Stream stream = e.Result)
                        {
                            UserMapLayers = XDocument.Load(stream);
                        }
                    }
                    else
                    {
                        //Error!
                    }
                }
                else
                {
                    //Error!
                }

            };

            webClient.OpenReadAsync(new Uri("/PublicMapLayers.xml", UriKind.Relative));
        }

        #endregion

        #region Map Methods

        protected void BuildMapLayerCollection()
        {
            List<string> list = new List<string>();

            MapLayerCollection = new LayerCollection();

            //Get the base layers.
            foreach (XElement element in UserMapLayers.Descendants("BaseMapLayers"))
            {
                foreach (XElement baseElement in element.Descendants("Layer"))
                {
                    ArcGISTiledMapServiceLayer baseLayer = new ArcGISTiledMapServiceLayer();
                    baseLayer.Url = baseElement.Attribute("URL").Value;
                    baseLayer.ID = baseElement.Attribute("ID").Value;
                    if (baseElement.Attribute("DefaultMap").Value == "y")
                    {
                        MapLayerCollection.Add(baseLayer);                        
                    }
                }
            }

            //Get the dynamic layers.
            foreach (XElement element in UserMapLayers.Descendants("DynamicLayers"))
            {
                foreach (XElement baseElement in element.Descendants("Layer"))
                {
                    ArcGISDynamicMapServiceLayer dynamicLayer = new ArcGISDynamicMapServiceLayer();
                    dynamicLayer.Url = baseElement.Attribute("URL").Value;
                    dynamicLayer.ID = baseElement.Attribute("ID").Value;
                    MapLayerCollection.Add(dynamicLayer);
                    list.Add(dynamicLayer.ID);
                }
            }

            DynamicLayersIDs = list.ToArray();

            //Graphics Layer.
            GraphicsLayer graphics = new GraphicsLayer();
            graphics.ID = "Graphics";

            MapLayerCollection.Add(graphics);
            RaiseCollectionChanged();
        }

        #endregion
    }


The XML file:
<?xml version="1.0" encoding="utf-8" ?>
<MapLayers>
  <BaseMapLayers>
    <Layer ID="Standard" URL="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" DefaultMap="y" />
  </BaseMapLayers>
  <DynamicLayers>
    <Layer ID="US Highway" URL="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer" />
  </DynamicLayers>
</MapLayers> 


MainPage.xaml
<UserControl
    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:viewModels="clr-namespace:CountyOfOxford.PublicMapping.ViewModels"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009" 
 x:Class="CountyOfOxford.PublicMapping.MainPage"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.DataContext>
        <viewModels:MainViewModel />
    </UserControl.DataContext>
    
    <Grid x:Name="LayoutRoot" Background="White">
     <Grid.ColumnDefinitions>
      <ColumnDefinition Width="300"/>
      <ColumnDefinition/>
     </Grid.ColumnDefinitions>
  <ScrollViewer HorizontalScrollBarVisibility="Visible">
      <esri:Legend x:Name="lgdMap" LayerItemsMode="Tree" Background="White" Map="{Binding ElementName=map}" LayerIDs="{Binding DynamicLayersIDs}">       
       <esri:Legend.MapLayerTemplate>
        <DataTemplate>
         <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Label}" FontWeight="Bold" Margin="0,0,2,0" />          
          <Slider Maximum="1" Value="{Binding Layer.Opacity, Mode=TwoWay}" Width="50" />
         </StackPanel>   
        </DataTemplate>   
       </esri:Legend.MapLayerTemplate>
       <esri:Legend.LayerTemplate>
        <DataTemplate>
         <CheckBox Content="{Binding Label}" IsChecked="{Binding IsEnabled, Mode=TwoWay}" IsEnabled="{Binding IsInScaleRange}" />
        </DataTemplate>
       </esri:Legend.LayerTemplate>
      </esri:Legend>
     </ScrollViewer>
     <esri:Map x:Name="map" Background="White" Margin="0" IsLogoVisible="False" Layers="{Binding MapLayerCollection}" Grid.Column="1"/>

    </Grid>
</UserControl>


The process is as follows :

1. In the view model constructor, I call the function that loads the XML file (asynchronously).
2. When the XML has been loaded into an XDocument (UserMapLayers), the BuildMapLayerCollection function is called, and builds the appropriate Tiled and Dynamic layers and adds them to a layer collection, MapLayerCollection.

As I've gotten this to work successfully in the past by just hardcoding the dynamic and tiled layer values in BuildMapLayerCollection, I think the addition of the asynchronous call to load the XML file is messing things up.

If I can provide any further clarification, please let me know!

Thanks, and have a great day!

Lisa
0 Kudos
3 Replies
DarinaTchountcheva
Occasional Contributor II
Lisa,

I haven't followed the logic of adding the layers to the MapLayerCollection, but I noticed that here:
public LayerCollection MapLayerCollection
        {
            get { return mapLayerCollection; }
            set
            {
                if (mapLayerCollection != value)
                {
                    mapLayerCollection = value;                    
                    RaisePropertyChanged("LayerCollection");
                }
            }
        }



the parameter of the RaisePropertyChanged method is "LayerCollection", and I believe it should be "MapLayerCollection".

Hope, that's all you have to fix.

Debug to make sure the MapLayerCollection gets populated as you expect.

Good Luck!
0 Kudos
LisaChesley
New Contributor
Oh, geez - that was totally it! 🙂  Never underestimate the value of a fresh set of eyes!

Thanks so much for the catch!

Lisa
0 Kudos
dotMorten_esri
Esri Notable Contributor
Just a small sidestep here: Have you thought about having your GIS staff using ArcGIS Online to define the map definition instead? There's full support for this in the client API. See http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#LoadWebMap
0 Kudos