[mapguide-commits] r6945 - in branches/2.4/MgDev/Desktop: MapViewer MapViewerTest

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Tue Aug 21 06:16:50 PDT 2012


Author: jng
Date: 2012-08-21 06:16:50 -0700 (Tue, 21 Aug 2012)
New Revision: 6945

Added:
   branches/2.4/MgDev/Desktop/MapViewer/MgLegendControlPresenter.cs
Modified:
   branches/2.4/MgDev/Desktop/MapViewer/MapViewer.csproj
   branches/2.4/MgDev/Desktop/MapViewer/MgComponent.cs
   branches/2.4/MgDev/Desktop/MapViewer/MgLegend.cs
   branches/2.4/MgDev/Desktop/MapViewer/MgLegend.resx
   branches/2.4/MgDev/Desktop/MapViewer/MgMapViewer.cs
   branches/2.4/MgDev/Desktop/MapViewerTest/MapViewerTest.csproj.user
Log:
#2096: Move out most of the presentation logic into a separate class, thus paving the way to do the refactoring with minimal disruption

Modified: branches/2.4/MgDev/Desktop/MapViewer/MapViewer.csproj
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewer/MapViewer.csproj	2012-08-21 11:07:49 UTC (rev 6944)
+++ branches/2.4/MgDev/Desktop/MapViewer/MapViewer.csproj	2012-08-21 13:16:50 UTC (rev 6945)
@@ -29,8 +29,7 @@
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>
-    </DefineConstants>
+    <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\OSGeo.MapGuide.Viewer.XML</DocumentationFile>
@@ -83,6 +82,7 @@
     <Compile Include="IMapViewerComponent.cs" />
     <Compile Include="IMapStatusBar.cs" />
     <Compile Include="IPropertyPane.cs" />
+    <Compile Include="MgLegendControlPresenter.cs" />
     <Compile Include="MgComponent.cs">
       <SubType>Component</SubType>
     </Compile>

Modified: branches/2.4/MgDev/Desktop/MapViewer/MgComponent.cs
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewer/MgComponent.cs	2012-08-21 11:07:49 UTC (rev 6944)
+++ branches/2.4/MgDev/Desktop/MapViewer/MgComponent.cs	2012-08-21 13:16:50 UTC (rev 6945)
@@ -138,7 +138,7 @@
             if (e.PropertyName == "IsBusy")
             {
                 var busy = this.Viewer.IsBusy;
-                Trace.TraceInformation("Dispatching busy state event to " + _listeners.Count + " listeners");
+                //Trace.TraceInformation("Dispatching busy state event to " + _listeners.Count + " listeners");
                 if (this.DisableWhenMapIsLoading)
                 {
                     foreach (var l in _listeners)

Modified: branches/2.4/MgDev/Desktop/MapViewer/MgLegend.cs
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewer/MgLegend.cs	2012-08-21 11:07:49 UTC (rev 6944)
+++ branches/2.4/MgDev/Desktop/MapViewer/MgLegend.cs	2012-08-21 13:16:50 UTC (rev 6945)
@@ -13,10 +13,12 @@
 
 namespace OSGeo.MapGuide.Viewer
 {
+    using Legend.Model;
+
     /// <summary>
     /// A control that displays and controls visibility of layers in a runtime map
     /// </summary>
-    public partial class MgLegend : UserControl, IMapLegend
+    public partial class MgLegend : UserControl, IMapLegend, ILegendView
     {
         // TODO:
         // 
@@ -36,13 +38,9 @@
         const string IMG_UNSELECT = "lc_unselect";
         const string IMG_OTHER = "icon_etc";
 
-        private MgResourceService _resSvc;
-        private MgMapViewerProvider _provider;
-        private MgMapBase _map;
+        // The thing that does all the heavy lifting and dirty work
+        private MgLegendControlPresenter _presenter;
 
-        private Image _selectableIcon;
-        private Image _unselectableIcon;
-
         /// <summary>
         /// Initializes a new instance of the <see cref="MgLegend"/> class.
         /// </summary>
@@ -51,37 +49,12 @@
             InitializeComponent();
         }
 
-        private Dictionary<string, bool> _initialLayerSelectabilityState = new Dictionary<string, bool>();
-
         internal void Init(MgMapViewerProvider provider)
         {
-            _provider = provider;
-            _map = _provider.GetMap();
-            InitInitialSelectabilityStates();
-            _resSvc = (MgResourceService)_provider.CreateService(MgServiceType.ResourceService);
-            _selectableIcon = Properties.Resources.lc_select;
-            _unselectableIcon = Properties.Resources.lc_unselect;
+            _presenter = new MgLegendControlPresenter(this, provider);
             RefreshLegend();
         }
 
-        private void InitInitialSelectabilityStates()
-        {
-            if (_map != null)
-            {
-                _initialLayerSelectabilityState.Clear();
-                var layers = _map.GetLayers();
-                for (int i = 0; i < layers.GetCount(); i++)
-                {
-                    var layer = layers.GetItem(i);
-                    _initialLayerSelectabilityState[layer.GetObjectId()] = layer.Selectable;
-                }
-            }
-        }
-
-        private Dictionary<string, MgLayerBase> _layers = new Dictionary<string, MgLayerBase>();
-        private Dictionary<string, MgLayerGroup> _groups = new Dictionary<string, MgLayerGroup>();
-        private Dictionary<string, string> _layerDefinitionContents = new Dictionary<string, string>();
-
         /// <summary>
         /// Refreshes this component
         /// </summary>
@@ -90,167 +63,18 @@
             if (_noUpdate)
                 return;
 
-            if (_map == null)
+            if (_presenter == null)
                 return;
 
-            //System.Diagnostics.Trace.TraceInformation("MgLegend.RefreshLegend()");
-            var scale = _map.ViewScale;
-            var groups = _map.GetLayerGroups();
-            var layers = _map.GetLayers();
-
             ResetTreeView();
-
-            _layerDefinitionContents.Clear();
-            _layers.Clear();
-            _groups.Clear();
-
             trvLegend.BeginUpdate();
             try
             {
-                //Process groups first
-                List<MgLayerGroup> remainingNodes = new List<MgLayerGroup>();
-                for (int i = 0; i < groups.GetCount(); i++)
-                {
-                    var group = groups.GetItem(i);
-                    _groups.Add(group.GetObjectId(), group);
-                    if (!group.GetDisplayInLegend())
-                        continue;
-
-                    //Add ones without parents first.
-                    if (group.Group != null)
-                    {
-                        remainingNodes.Add(group);
-                    }
-                    else
-                    {
-                        var node = CreateGroupNode(group);
-                        trvLegend.Nodes.Add(node);
-                    }
-
-                    while (remainingNodes.Count > 0)
-                    {
-                        List<MgLayerGroup> toRemove = new List<MgLayerGroup>();
-                        for (int j = 0; j < remainingNodes.Count; j++)
-                        {
-                            var parentId = remainingNodes[j].Group.GetObjectId();
-                            var nodes = trvLegend.Nodes.Find(parentId, false);
-                            if (nodes.Length == 1)
-                            {
-                                var node = CreateGroupNode(remainingNodes[j]);
-                                nodes[0].Nodes.Add(node);
-                                toRemove.Add(remainingNodes[j]);
-                            }
-                        }
-                        //Whittle down this list
-                        if (toRemove.Count > 0)
-                        {
-                            foreach (var g in toRemove)
-                            {
-                                remainingNodes.Remove(g);
-                            }
-                        }
-                    }
-                }
-
-                //Now process layers
-                for (int i = 0; i < layers.GetCount(); i++)
-                {
-                    var lyr = layers.GetItem(i);
-                    var ldfId = lyr.LayerDefinition;
-
-                    if (!_layerDefinitionContents.ContainsKey(ldfId.ToString()))
-                    {
-                        _layerDefinitionContents[ldfId.ToString()] = string.Empty;
-                    }
-                }
-
-                //Collect all resource contents in a batch
-                MgStringCollection layerIds = new MgStringCollection();
-                foreach (var lid in _layerDefinitionContents.Keys)
-                {
-                    layerIds.Add(lid);
-                }
-
-                MgStringCollection layerContents = _resSvc.GetResourceContents(layerIds, null);
-                for (int i = 0; i < layerIds.GetCount(); i++)
-                {
-                    string lid = layerIds.GetItem(i);
-                    _layerDefinitionContents[lid] = layerContents.GetItem(i);
-                }
-
-                List<MgLayerBase> remainingLayers = new List<MgLayerBase>();
-                for (int i = 0; i < layers.GetCount(); i++)
-                {
-                    var layer = layers.GetItem(i);
-                    _layers.Add(layer.GetObjectId(), layer);
-
-                    bool display = layer.DisplayInLegend;
-                    bool visible = _provider.IsLayerPotentiallyVisibleAtScale(layer, false);
-                    if (!display)
-                        continue;
-
-                    if (!visible)
-                        continue;
-
-                    //Add ones without parents first.
-                    if (layer.Group != null)
-                    {
-                        remainingLayers.Add(layer);
-                    }
-                    else
-                    {
-                        var node = CreateLayerNode(layer);
-                        if (node != null)
-                        {
-                            trvLegend.Nodes.Add(node);
-                            if (layer.ExpandInLegend)
-                                node.Expand();
-                        }
-                    }
-
-                    while (remainingLayers.Count > 0)
-                    {
-                        List<MgLayerBase> toRemove = new List<MgLayerBase>();
-                        for (int j = 0; j < remainingLayers.Count; j++)
-                        {
-                            var parentId = remainingLayers[j].Group.GetObjectId();
-                            var nodes = trvLegend.Nodes.Find(parentId, false);
-                            if (nodes.Length == 1)
-                            {
-                                var node = CreateLayerNode(remainingLayers[j]);
-                                if (node != null)
-                                {
-                                    nodes[0].Nodes.Add(node);
-                                    if (remainingLayers[j].ExpandInLegend)
-                                        node.Expand();
-                                }
-                                toRemove.Add(remainingLayers[j]);
-                            }
-                        }
-                        //Whittle down this list
-                        if (toRemove.Count > 0)
-                        {
-                            foreach (var g in toRemove)
-                            {
-                                remainingLayers.Remove(g);
-                            }
-                        }
-                    }
-                }
-
-                //Now expand any relevant groups
-                for (int i = 0; i < groups.GetCount(); i++)
-                {
-                    var group = groups.GetItem(i);
-                    if (group.ExpandInLegend)
-                    {
-                        var nodes = trvLegend.Nodes.Find(group.GetObjectId(), false);
-                        if (nodes.Length == 1)
-                        {
-                            nodes[0].Expand();
-                        }
-                    }
-                }
+                var sw = new Stopwatch();
+                sw.Start();
+                trvLegend.Nodes.AddRange(_presenter.CreateNodes());
+                sw.Stop();
+                Trace.TraceInformation("RefreshLegend: Completed in {0}ms", sw.ElapsedMilliseconds);
             }
             finally
             {
@@ -276,6 +100,11 @@
             nodes.Clear();
         }
 
+        void ILegendView.AddLegendIcon(string id, Image img)
+        {
+            imgLegend.Images.Add(id, img);
+        }
+
         private void ResetTreeView()
         {
             ClearNodes(trvLegend.Nodes);
@@ -291,241 +120,6 @@
             imgLegend.Images.Add(IMG_OTHER, Properties.Resources.icon_etc);
         }
 
-        private TreeNode CreateLayerNode(MgLayerBase layer)
-        {
-            var node = new TreeNode();
-            node.Name = layer.GetObjectId();
-            node.Text = layer.GetLegendLabel();
-            node.Checked = layer.GetVisible();
-            node.ContextMenuStrip = this.LayerContextMenu;
-            var lt = layer.GetLayerType();
-            var fsId = layer.GetFeatureSourceId();
-
-            if (fsId.EndsWith("DrawingSource"))
-            {
-                node.SelectedImageKey = node.ImageKey = IMG_DWF;
-                //If not in the dictionary, assume it is a dynamically added layer
-                bool bInitiallySelectable = _initialLayerSelectabilityState.ContainsKey(layer.GetObjectId()) ? _initialLayerSelectabilityState[layer.GetObjectId()] : layer.Selectable;
-                node.Tag = new LayerNodeMetadata(layer, bInitiallySelectable);
-                node.ToolTipText = string.Format(Properties.Resources.DrawingLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceId);
-            }
-            else
-            {
-                string layerData = null;
-                var ldfId = layer.LayerDefinition;
-                if (_layerDefinitionContents.ContainsKey(ldfId.ToString()))
-                    layerData = _layerDefinitionContents[ldfId.ToString()];
-
-                if (layerData == null)
-                    return null;
-
-                XmlDocument doc = new XmlDocument();
-                doc.LoadXml(layerData);
-                int type = 0;
-                XmlNodeList scaleRanges = doc.GetElementsByTagName("VectorScaleRange");
-                if (scaleRanges.Count == 0)
-                {
-                    scaleRanges = doc.GetElementsByTagName("GridScaleRange");
-                    if (scaleRanges.Count == 0)
-                    {
-                        scaleRanges = doc.GetElementsByTagName("DrawingLayerDefinition");
-                        if (scaleRanges.Count == 0)
-                            return null;
-                        type = 2;
-                    }
-                    else
-                        type = 1;
-                }
-
-                String[] typeStyles = new String[] { "PointTypeStyle", "LineTypeStyle", "AreaTypeStyle", "CompositeTypeStyle" };
-                String[] ruleNames = new String[] { "PointRule", "LineRule", "AreaRule", "CompositeRule" };
-
-                try
-                {
-                    MgByteReader layerIcon = _provider.GenerateLegendImage(layer.LayerDefinition,
-                                                                            _map.ViewScale,
-                                                                            16,
-                                                                            16,
-                                                                            "PNG",
-                                                                            -1,
-                                                                            -1);
-                    if (layerIcon != null)
-                    {
-                        try
-                        {
-                            byte[] b = new byte[layerIcon.GetLength()];
-                            layerIcon.Read(b, b.Length);
-                            using (var ms = new MemoryStream(b))
-                            {
-                                string id = Guid.NewGuid().ToString();
-                                Image img = Image.FromStream(ms);
-
-                                imgLegend.Images.Add(id, img);
-                                node.SelectedImageKey = node.ImageKey = id;
-                                //If not in the dictionary, assume it is a dynamically added layer
-                                bool bInitiallySelectable = _initialLayerSelectabilityState.ContainsKey(layer.GetObjectId()) ? _initialLayerSelectabilityState[layer.GetObjectId()] : layer.Selectable;
-                                node.Tag = new LayerNodeMetadata(layer, bInitiallySelectable)
-                                {
-                                    ThemeIcon = img
-                                };
-                                node.ToolTipText = string.Format(Properties.Resources.DefaultLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceId, layer.FeatureClassName);
-                            }
-                        }
-                        finally
-                        {
-                            layerIcon.Dispose();
-                        }
-                    }
-                    else
-                    {
-                        node.SelectedImageKey = node.ImageKey = IMG_BROKEN;
-                    }
-                }
-                catch
-                {
-                    node.SelectedImageKey = node.ImageKey = IMG_BROKEN;
-                }
-
-                for (int sc = 0; sc < scaleRanges.Count; sc++)
-                {
-                    XmlElement scaleRange = (XmlElement)scaleRanges[sc];
-                    XmlNodeList minElt = scaleRange.GetElementsByTagName("MinScale");
-                    XmlNodeList maxElt = scaleRange.GetElementsByTagName("MaxScale");
-                    String minScale, maxScale;
-                    minScale = "0";
-                    maxScale = "1000000000000.0";   // as MDF's VectorScaleRange::MAX_MAP_SCALE
-                    if (minElt.Count > 0)
-                        minScale = minElt[0].ChildNodes[0].Value;
-                    if (maxElt.Count > 0)
-                        maxScale = maxElt[0].ChildNodes[0].Value;
-                    
-                    if (type != 0)
-                        break;
-
-                    for (int geomType = 0; geomType < typeStyles.Length; geomType++)
-                    {
-                        int catIndex = 0;
-                        XmlNodeList typeStyle = scaleRange.GetElementsByTagName(typeStyles[geomType]);
-                        for (int st = 0; st < typeStyle.Count; st++)
-                        {
-                            // We will check if this typestyle is going to be shown in the legend
-                            XmlNodeList showInLegend = ((XmlElement)typeStyle[st]).GetElementsByTagName("ShowInLegend");
-                            if (showInLegend.Count > 0)
-                                if (!bool.Parse(showInLegend[0].ChildNodes[0].Value))
-                                    continue;   // This typestyle does not need to be shown in the legend
-
-                            XmlNodeList rules = ((XmlElement)typeStyle[st]).GetElementsByTagName(ruleNames[geomType]);
-                            if (rules.Count > 1)
-                            {
-                                node.SelectedImageKey = node.ImageKey = IMG_THEME;
-                                var layerMeta = node.Tag as LayerNodeMetadata;
-                                if (layerMeta != null)
-                                {
-                                    layerMeta.ThemeIcon = Properties.Resources.lc_theme;
-                                    node.ToolTipText = string.Format(Properties.Resources.ThemedLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceId, layer.FeatureClassName, rules.Count);
-                                }
-                                if (this.ThemeCompressionLimit > 0 && rules.Count > this.ThemeCompressionLimit)
-                                {
-                                    AddThemeRuleNode(layer, node, geomType, 0, rules, 0);
-                                    node.Nodes.Add(CreateCompressedThemeNode(rules.Count - 2));
-                                    AddThemeRuleNode(layer, node, geomType, rules.Count - 1, rules, rules.Count - 1);
-                                }
-                                else
-                                {
-                                    for (int r = 0; r < rules.Count; r++)
-                                    {
-                                        AddThemeRuleNode(layer, node, geomType, catIndex++, rules, r);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            
-            return node;
-        }
-
-        private void AddThemeRuleNode(MgLayerBase layer, TreeNode node, int geomType, int catIndex, XmlNodeList rules, int r)
-        {
-            XmlElement rule = (XmlElement)rules[r];
-            XmlNodeList label = rule.GetElementsByTagName("LegendLabel");
-            XmlNodeList filter = rule.GetElementsByTagName("Filter");
-
-            String labelText = "";
-            if (label != null && label.Count > 0 && label[0].ChildNodes.Count > 0)
-                labelText = label[0].ChildNodes[0].Value;
-            //String filterText = "";
-            //if (filter != null && filter.Count > 0 && filter[0].ChildNodes.Count > 0)
-            //    filterText = filter[0].ChildNodes[0].Value;
-
-            var child = CreateThemeRuleNode(layer.LayerDefinition, _map.ViewScale, labelText, (geomType + 1), catIndex);
-            node.Nodes.Add(child);
-        }
-
-        private TreeNode CreateCompressedThemeNode(int count)
-        {
-            TreeNode node = new TreeNode();
-            node.Text = (count + " other styles");
-            node.ImageKey = node.SelectedImageKey = IMG_OTHER;
-            node.Tag = new LayerNodeMetadata(null, false) {
-                IsBaseLayer = false,
-                ThemeIcon = Properties.Resources.icon_etc,
-                IsThemeRule = true
-            };
-            return node;
-        }
-
-        private TreeNode CreateThemeRuleNode(MgResourceIdentifier layerDefId, double viewScale, string labelText, int geomType, int categoryIndex)
-        {
-            MgByteReader icon = _provider.GenerateLegendImage(layerDefId,
-                                                              viewScale,
-                                                              16,
-                                                              16,
-                                                              "PNG",
-                                                              geomType,
-                                                              categoryIndex);
-            TreeNode node = new TreeNode();
-            node.Text = labelText;
-            if (icon != null)
-            {
-                try
-                {
-
-                    byte[] b = new byte[icon.GetLength()];
-                    icon.Read(b, b.Length);
-                    var tag = new LayerNodeMetadata(null, false)
-                    {
-                        IsBaseLayer = false,
-                        IsThemeRule = true
-                    };
-                    using (var ms = new MemoryStream(b))
-                    {
-                        string id = Guid.NewGuid().ToString();
-                        tag.ThemeIcon = Image.FromStream(ms);
-                    }
-                    node.Tag = tag;
-                }
-                finally
-                {
-                    icon.Dispose();
-                }
-            }
-            return node;
-        }
-
-        private TreeNode CreateGroupNode(MgLayerGroup group)
-        {
-            var node = new TreeNode();
-            node.Name = group.GetObjectId();
-            node.Text = group.GetLegendLabel();
-            node.Checked = group.IsVisible();
-            node.SelectedImageKey = node.ImageKey = IMG_GROUP;
-            node.Tag = new GroupNodeMetadata(group);
-            node.ContextMenuStrip = this.GroupContextMenu;
-            return node;
-        }
-
         private double _scale;
 
         /// <summary>
@@ -538,63 +132,8 @@
             RefreshLegend();
         }
 
-        class LegendNodeMetadata
-        {
-            public bool IsGroup { get; protected set; }
-        }
+        
 
-        class GroupNodeMetadata : LegendNodeMetadata
-        {
-            internal MgLayerGroup Group { get; set; }
-
-            public GroupNodeMetadata(MgLayerGroup group) 
-            { 
-                base.IsGroup = true;
-                this.Group = group;
-            }
-        }
-
-        class LayerNodeMetadata : LegendNodeMetadata
-        {
-            public LayerNodeMetadata(MgLayerBase layer, bool bInitiallySelectable) 
-            { 
-                base.IsGroup = false;
-                this.Layer = layer;
-                this.IsSelectable = (layer != null) ? layer.Selectable : false;
-                this.DrawSelectabilityIcon = (layer != null && bInitiallySelectable);
-                this.IsThemeRule = false;
-            }
-
-            internal MgLayerBase Layer { get; set; }
-
-            public bool DrawSelectabilityIcon { get; set; }
-
-            public bool IsSelectable { get; set; }
-
-            public bool IsThemeRule { get; set; }
-
-            public bool IsBaseLayer { get; set; }
-
-            public Image ThemeIcon { get; set; }
-        }
-
-        private bool HasVisibleParent(MgLayerGroup grp)
-        {
-            var current = grp.Group;
-            if (current != null)
-                return current.IsVisible();
-            return true;
-        }
-
-        private bool HasVisibleParent(MgLayerBase layer)
-        {
-            var current = layer.Group;
-            if (current != null)
-                return current.IsVisible();
-
-            return true;
-        }
-
         private void trvLegend_AfterCheck(object sender, TreeViewEventArgs e)
         {
             if (e.Node.Tag == null)
@@ -602,28 +141,12 @@
 
             if (((LegendNodeMetadata)e.Node.Tag).IsGroup) //Group
             {
-                if (_groups.ContainsKey(e.Node.Name))
-                {
-                    var grp = _groups[e.Node.Name];
-                    grp.SetVisible(e.Node.Checked);
-                    var bVis = HasVisibleParent(grp);
-                    if (bVis)
-                        OnRequestRefresh();
-                }
+                _presenter.SetGroupVisible(e.Node.Name, e.Node.Checked);
             }
             else //Layer
             {
-                if (_layers.ContainsKey(e.Node.Name))
-                {
-                    var layer = _layers[e.Node.Name];
-                    layer.SetVisible(e.Node.Checked);
-                    var bVis = HasVisibleParent(layer);
-                    if (bVis)
-                    {
-                        layer.ForceRefresh();
-                        OnRequestRefresh();
-                    }
-                }
+                
+                _presenter.SetLayerVisible(e.Node.Name, e.Node.Checked);
             }
         }
 
@@ -634,18 +157,11 @@
 
             if (((LegendNodeMetadata)e.Node.Tag).IsGroup) //Group
             {
-                if (_groups.ContainsKey(e.Node.Name))
-                {
-                    _provider.SetGroupExpandInLegend(_groups[e.Node.Name], true);
-                }
+                _presenter.SetGroupExpandInLegend(e.Node.Name, true);
             }
             else //Layer
             {
-                if (_layers.ContainsKey(e.Node.Name))
-                {
-                    var layer = _layers[e.Node.Name];
-                    _provider.SetLayerExpandInLegend(layer, true);
-                }
+                _presenter.SetLayerExpandInLegend(e.Node.Name, true);
             }
         }
 
@@ -656,24 +172,17 @@
 
             if (((LegendNodeMetadata)e.Node.Tag).IsGroup) //Group
             {
-                if (_groups.ContainsKey(e.Node.Name))
-                {
-                    _provider.SetGroupExpandInLegend(_groups[e.Node.Name], false);
-                }
+                _presenter.SetGroupExpandInLegend(e.Node.Name, false);
             }
             else //Layer
             {
-                if (_layers.ContainsKey(e.Node.Name))
-                {
-                    var layer = _layers[e.Node.Name];
-                    _provider.SetLayerExpandInLegend(layer, false);
-                }
+                _presenter.SetLayerExpandInLegend(e.Node.Name, false);
             }
         }
 
         private bool _noUpdate = false;
 
-        private void OnRequestRefresh()
+        void ILegendView.OnRequestRefresh()
         {
             var handler = this.VisibilityChanged;
             if (handler != null)
@@ -687,152 +196,11 @@
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
         public event EventHandler VisibilityChanged;
 
-        private static bool IsThemeLayerNode(TreeNode node)
-        {
-            var meta = node.Tag as LayerNodeMetadata;
-            if (meta != null)
-                return meta.ThemeIcon != null || meta.IsBaseLayer;
+        
 
-            return false;
-        }
-
-        private static bool IsLayerNode(TreeNode node)
-        {
-            var meta = node.Tag as LayerNodeMetadata;
-            return meta != null;
-        }
-
         private void trvLegend_DrawNode(object sender, DrawTreeNodeEventArgs e)
         {
-            if (IsLayerNode(e.Node) && !e.Bounds.IsEmpty)
-            {
-                Color backColor, foreColor;
-
-                //For some reason, the default bounds are way off from what you would
-                //expect it to be. So we apply this offset for any text/image draw operations
-                int xoffset = -36;
-                if (trvLegend.ShowPlusMinus && e.Node.Nodes.Count > 0)
-                {
-                    // Use the VisualStyles renderer to use the proper OS-defined glyphs
-                    Rectangle glyphRect = new Rectangle(e.Node.Bounds.X - 52, e.Node.Bounds.Y, 16, 16);
-                    if (Application.RenderWithVisualStyles)
-                    {
-                        VisualStyleElement element = (e.Node.IsExpanded) ?
-                            VisualStyleElement.TreeView.Glyph.Opened : VisualStyleElement.TreeView.Glyph.Closed;
-
-                        VisualStyleRenderer renderer = new VisualStyleRenderer(element);
-                        renderer.DrawBackground(e.Graphics, glyphRect);
-                    }
-                    else //Visual Styles disabled, fallback to drawing the +/- using geometric primitives
-                    {
-                        int h = 8;
-                        int w = 8;
-                        int x = glyphRect.X;
-                        int y = glyphRect.Y + (glyphRect.Height / 2) - 4;
-
-                        //Draw the -
-                        e.Graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
-                        e.Graphics.FillRectangle(new SolidBrush(Color.White), x + 1, y + 1, w - 1, h - 1);
-                        e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), x + 2, y + 4, x + w - 2, y + 4);
-
-                        //Draw the |
-                        if (!e.Node.IsExpanded)
-                            e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), x + 4, y + 2, x + 4, y + h - 2); 
-                    }
-                }
-
-                if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
-                {
-                    backColor = SystemColors.Highlight;
-                    foreColor = SystemColors.HighlightText;
-                }
-                else if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
-                {
-                    backColor = SystemColors.HotTrack;
-                    foreColor = SystemColors.HighlightText;
-                }
-                else
-                {
-                    backColor = e.Node.BackColor;
-                    foreColor = e.Node.ForeColor;
-                }
-
-                var tag = e.Node.Tag as LayerNodeMetadata;
-                var checkBoxOffset = xoffset;
-                var selectabilityOffset = xoffset + 16;
-                var iconOffsetNoSelect = xoffset + 16;
-                if (tag != null && tag.IsThemeRule) //No checkbox for theme rule nodes
-                {
-                    selectabilityOffset = xoffset;
-                    iconOffsetNoSelect = xoffset;
-                }
-                var iconOffset = selectabilityOffset + 20;
-                var textOffset = iconOffset + 20;
-                var textOffsetNoSelect = iconOffsetNoSelect + 20;
-
-                //Uncomment if you need to "see" the bounds of the node
-                //e.Graphics.DrawRectangle(Pens.Black, e.Node.Bounds);
-
-                if (tag != null && !tag.IsThemeRule) //No checkbox for theme rule nodes
-                {
-                    if (Application.RenderWithVisualStyles)
-                    {
-                        CheckBoxRenderer.DrawCheckBox(
-                            e.Graphics,
-                            new Point(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y),
-                            e.Node.Checked ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal);
-                    }
-                    else
-                    {
-                        //We don't have to do this, but with Visual Styles disabled, there is a noticeable jarring visual difference from DrawDefault'd checkboxes
-                        //So we might as well emulate that style for the sake of consistency
-                        var rect = new Rectangle(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y, 16, 16);
-                        ControlPaint.DrawCheckBox(e.Graphics, rect, e.Node.Checked ? ButtonState.Checked | ButtonState.Flat : ButtonState.Flat);
-                        rect.Inflate(-2, -2);
-                        e.Graphics.DrawRectangle(new Pen(Brushes.Black, 2.0f), rect);
-                    }
-                }
-                if (tag != null)
-                {
-                    if (tag.DrawSelectabilityIcon)
-                    {
-                        var icon = tag.IsSelectable ? _selectableIcon : _unselectableIcon;
-                        e.Graphics.DrawImage(icon, e.Node.Bounds.X + selectabilityOffset, e.Node.Bounds.Y);
-                        Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
-                    }
-                    if (tag.ThemeIcon != null)
-                    {
-                        if (tag.DrawSelectabilityIcon)
-                        {
-                            e.Graphics.DrawImage(tag.ThemeIcon, e.Node.Bounds.X + iconOffset, e.Node.Bounds.Y);
-                            Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
-                        }
-                        else
-                        {
-                            e.Graphics.DrawImage(tag.ThemeIcon, e.Node.Bounds.X + iconOffsetNoSelect, e.Node.Bounds.Y);
-                            Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
-                        }
-                    }
-
-                    using (SolidBrush brush = new SolidBrush(Color.Black))
-                    {
-                        e.Graphics.DrawString(e.Node.Text, trvLegend.Font, brush, e.Node.Bounds.X + (tag.DrawSelectabilityIcon ? textOffset : textOffsetNoSelect), e.Node.Bounds.Y);
-                    }
-                }
-                else
-                {
-                    using (SolidBrush brush = new SolidBrush(Color.Black))
-                    {
-                        e.Graphics.DrawString(e.Node.Text, trvLegend.Font, brush, e.Node.Bounds.X + 17.0f + xoffset, e.Node.Bounds.Y);
-                    }
-                }
-
-                e.DrawDefault = false;
-            }
-            else
-            {
-                e.DrawDefault = true;
-            }
+            _presenter.DrawNode(e, trvLegend.ShowPlusMinus, trvLegend.Font);
         }
 
         private ContextMenuStrip _grpContextMenu;
@@ -938,7 +306,7 @@
         /// <returns></returns>
         public MgLayerBase GetSelectedLayer()
         {
-            if (_map == null)
+            if (_presenter == null)
                 return null;
 
             if (trvLegend.SelectedNode == null)
@@ -957,7 +325,7 @@
         /// <returns></returns>
         public MgLayerGroup GetSelectedGroup()
         {
-            if (_map == null)
+            if (_presenter == null)
                 return null;
 
             if (trvLegend.SelectedNode == null)

Modified: branches/2.4/MgDev/Desktop/MapViewer/MgLegend.resx
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewer/MgLegend.resx	2012-08-21 11:07:49 UTC (rev 6944)
+++ branches/2.4/MgDev/Desktop/MapViewer/MgLegend.resx	2012-08-21 13:16:50 UTC (rev 6945)
@@ -120,4 +120,7 @@
   <metadata name="imgLegend.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
+  <metadata name="legendUpdateWorker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>130, 17</value>
+  </metadata>
 </root>
\ No newline at end of file

Added: branches/2.4/MgDev/Desktop/MapViewer/MgLegendControlPresenter.cs
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewer/MgLegendControlPresenter.cs	                        (rev 0)
+++ branches/2.4/MgDev/Desktop/MapViewer/MgLegendControlPresenter.cs	2012-08-21 13:16:50 UTC (rev 6945)
@@ -0,0 +1,718 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+using System.Windows.Forms;
+using System.Windows.Forms.VisualStyles;
+
+namespace OSGeo.MapGuide.Viewer
+{
+    using Legend.Model;
+    using System.Xml;
+    using System.IO;
+
+    interface ILegendView
+    {
+        ContextMenuStrip LayerContextMenu { get; }
+        ContextMenuStrip GroupContextMenu { get; }
+        void AddLegendIcon(string id, Image icon);
+        int ThemeCompressionLimit { get; }
+        void OnRequestRefresh();
+    }
+
+    class MgLegendControlPresenter
+    {
+        const string IMG_BROKEN = "lc_broken";
+        const string IMG_DWF = "lc_dwf";
+        const string IMG_GROUP = "lc_group";
+        const string IMG_RASTER = "lc_raster";
+        const string IMG_SELECT = "lc_select";
+        const string IMG_THEME = "lc_theme";
+        const string IMG_UNSELECT = "lc_unselect";
+        const string IMG_OTHER = "icon_etc";
+
+        private MgResourceService _resSvc;
+        private MgMapViewerProvider _provider;
+        private MgMapBase _map;
+
+        private Image _selectableIcon;
+        private Image _unselectableIcon;
+
+        private ILegendView _legend;
+
+        //TODO: Coalesce this all into a single object. ie. Merge into the respective metadata classes.
+        private Dictionary<string, bool> _initialLayerSelectabilityState = new Dictionary<string, bool>();
+        private Dictionary<string, MgLayerBase> _layers = new Dictionary<string, MgLayerBase>();
+        private Dictionary<string, MgLayerGroup> _groups = new Dictionary<string, MgLayerGroup>();
+        private Dictionary<string, string> _layerDefinitionContents = new Dictionary<string, string>();
+
+        public MgLegendControlPresenter(ILegendView legend, MgMapViewerProvider provider)
+        {
+            _legend = legend;
+            _provider = provider;
+            _map = _provider.GetMap();
+            InitInitialSelectabilityStates();
+            _resSvc = (MgResourceService)_provider.CreateService(MgServiceType.ResourceService);
+            _selectableIcon = Properties.Resources.lc_select;
+            _unselectableIcon = Properties.Resources.lc_unselect;
+        }
+
+        private void InitInitialSelectabilityStates()
+        {
+            if (_map != null)
+            {
+                _initialLayerSelectabilityState.Clear();
+                var layers = _map.GetLayers();
+                for (int i = 0; i < layers.GetCount(); i++)
+                {
+                    var layer = layers.GetItem(i);
+                    _initialLayerSelectabilityState[layer.GetObjectId()] = layer.Selectable;
+                }
+            }
+        }
+
+        private bool HasVisibleParent(MgLayerGroup grp)
+        {
+            var current = grp.Group;
+            if (current != null)
+                return current.IsVisible();
+            return true;
+        }
+
+        private bool HasVisibleParent(MgLayerBase layer)
+        {
+            var current = layer.Group;
+            if (current != null)
+                return current.IsVisible();
+
+            return true;
+        }
+
+        private TreeNode CreateLayerNode(MgLayerBase layer)
+        {
+            var node = new TreeNode();
+            node.Name = layer.GetObjectId();
+            node.Text = layer.GetLegendLabel();
+            node.Checked = layer.GetVisible();
+            node.ContextMenuStrip = _legend.LayerContextMenu;
+            var lt = layer.GetLayerType();
+            var fsId = layer.GetFeatureSourceId();
+
+            if (fsId.EndsWith("DrawingSource"))
+            {
+                node.SelectedImageKey = node.ImageKey = IMG_DWF;
+                //If not in the dictionary, assume it is a dynamically added layer
+                bool bInitiallySelectable = _initialLayerSelectabilityState.ContainsKey(layer.GetObjectId()) ? _initialLayerSelectabilityState[layer.GetObjectId()] : layer.Selectable;
+                node.Tag = new LayerNodeMetadata(layer, bInitiallySelectable);
+                node.ToolTipText = string.Format(Properties.Resources.DrawingLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceId);
+            }
+            else
+            {
+                string layerData = null;
+                var ldfId = layer.LayerDefinition;
+                if (_layerDefinitionContents.ContainsKey(ldfId.ToString()))
+                    layerData = _layerDefinitionContents[ldfId.ToString()];
+
+                if (layerData == null)
+                    return null;
+
+                XmlDocument doc = new XmlDocument();
+                doc.LoadXml(layerData);
+                int type = 0;
+                XmlNodeList scaleRanges = doc.GetElementsByTagName("VectorScaleRange");
+                if (scaleRanges.Count == 0)
+                {
+                    scaleRanges = doc.GetElementsByTagName("GridScaleRange");
+                    if (scaleRanges.Count == 0)
+                    {
+                        scaleRanges = doc.GetElementsByTagName("DrawingLayerDefinition");
+                        if (scaleRanges.Count == 0)
+                            return null;
+                        type = 2;
+                    }
+                    else
+                        type = 1;
+                }
+
+                String[] typeStyles = new String[] { "PointTypeStyle", "LineTypeStyle", "AreaTypeStyle", "CompositeTypeStyle" };
+                String[] ruleNames = new String[] { "PointRule", "LineRule", "AreaRule", "CompositeRule" };
+
+                try
+                {
+                    MgByteReader layerIcon = _provider.GenerateLegendImage(layer.LayerDefinition,
+                                                                            _map.ViewScale,
+                                                                            16,
+                                                                            16,
+                                                                            "PNG",
+                                                                            -1,
+                                                                            -1);
+                    if (layerIcon != null)
+                    {
+                        try
+                        {
+                            byte[] b = new byte[layerIcon.GetLength()];
+                            layerIcon.Read(b, b.Length);
+                            using (var ms = new MemoryStream(b))
+                            {
+                                string id = Guid.NewGuid().ToString();
+                                Image img = Image.FromStream(ms);
+
+                                _legend.AddLegendIcon(id, img);
+                                node.SelectedImageKey = node.ImageKey = id;
+                                //If not in the dictionary, assume it is a dynamically added layer
+                                bool bInitiallySelectable = _initialLayerSelectabilityState.ContainsKey(layer.GetObjectId()) ? _initialLayerSelectabilityState[layer.GetObjectId()] : layer.Selectable;
+                                node.Tag = new LayerNodeMetadata(layer, bInitiallySelectable)
+                                {
+                                    ThemeIcon = img
+                                };
+                                node.ToolTipText = string.Format(Properties.Resources.DefaultLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceId, layer.FeatureClassName);
+                            }
+                        }
+                        finally
+                        {
+                            layerIcon.Dispose();
+                        }
+                    }
+                    else
+                    {
+                        node.SelectedImageKey = node.ImageKey = IMG_BROKEN;
+                    }
+                }
+                catch
+                {
+                    node.SelectedImageKey = node.ImageKey = IMG_BROKEN;
+                }
+
+                for (int sc = 0; sc < scaleRanges.Count; sc++)
+                {
+                    XmlElement scaleRange = (XmlElement)scaleRanges[sc];
+                    XmlNodeList minElt = scaleRange.GetElementsByTagName("MinScale");
+                    XmlNodeList maxElt = scaleRange.GetElementsByTagName("MaxScale");
+                    String minScale, maxScale;
+                    minScale = "0";
+                    maxScale = "1000000000000.0";   // as MDF's VectorScaleRange::MAX_MAP_SCALE
+                    if (minElt.Count > 0)
+                        minScale = minElt[0].ChildNodes[0].Value;
+                    if (maxElt.Count > 0)
+                        maxScale = maxElt[0].ChildNodes[0].Value;
+
+                    if (type != 0)
+                        break;
+
+                    for (int geomType = 0; geomType < typeStyles.Length; geomType++)
+                    {
+                        int catIndex = 0;
+                        XmlNodeList typeStyle = scaleRange.GetElementsByTagName(typeStyles[geomType]);
+                        for (int st = 0; st < typeStyle.Count; st++)
+                        {
+                            // We will check if this typestyle is going to be shown in the legend
+                            XmlNodeList showInLegend = ((XmlElement)typeStyle[st]).GetElementsByTagName("ShowInLegend");
+                            if (showInLegend.Count > 0)
+                                if (!bool.Parse(showInLegend[0].ChildNodes[0].Value))
+                                    continue;   // This typestyle does not need to be shown in the legend
+
+                            XmlNodeList rules = ((XmlElement)typeStyle[st]).GetElementsByTagName(ruleNames[geomType]);
+                            if (rules.Count > 1)
+                            {
+                                node.SelectedImageKey = node.ImageKey = IMG_THEME;
+                                var layerMeta = node.Tag as LayerNodeMetadata;
+                                if (layerMeta != null)
+                                {
+                                    layerMeta.ThemeIcon = Properties.Resources.lc_theme;
+                                    node.ToolTipText = string.Format(Properties.Resources.ThemedLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceId, layer.FeatureClassName, rules.Count);
+                                }
+                                if (_legend.ThemeCompressionLimit > 0 && rules.Count > _legend.ThemeCompressionLimit)
+                                {
+                                    AddThemeRuleNode(layer, node, geomType, 0, rules, 0);
+                                    node.Nodes.Add(CreateCompressedThemeNode(rules.Count - 2));
+                                    AddThemeRuleNode(layer, node, geomType, rules.Count - 1, rules, rules.Count - 1);
+                                }
+                                else
+                                {
+                                    for (int r = 0; r < rules.Count; r++)
+                                    {
+                                        AddThemeRuleNode(layer, node, geomType, catIndex++, rules, r);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            return node;
+        }
+
+        private void AddThemeRuleNode(MgLayerBase layer, TreeNode node, int geomType, int catIndex, XmlNodeList rules, int r)
+        {
+            XmlElement rule = (XmlElement)rules[r];
+            XmlNodeList label = rule.GetElementsByTagName("LegendLabel");
+            XmlNodeList filter = rule.GetElementsByTagName("Filter");
+
+            String labelText = "";
+            if (label != null && label.Count > 0 && label[0].ChildNodes.Count > 0)
+                labelText = label[0].ChildNodes[0].Value;
+            //String filterText = "";
+            //if (filter != null && filter.Count > 0 && filter[0].ChildNodes.Count > 0)
+            //    filterText = filter[0].ChildNodes[0].Value;
+
+            var child = CreateThemeRuleNode(layer.LayerDefinition, _map.ViewScale, labelText, (geomType + 1), catIndex);
+            node.Nodes.Add(child);
+        }
+
+        private TreeNode CreateCompressedThemeNode(int count)
+        {
+            TreeNode node = new TreeNode();
+            node.Text = (count + " other styles");
+            node.ImageKey = node.SelectedImageKey = IMG_OTHER;
+            node.Tag = new LayerNodeMetadata(null, false)
+            {
+                IsBaseLayer = false,
+                ThemeIcon = Properties.Resources.icon_etc,
+                IsThemeRule = true
+            };
+            return node;
+        }
+
+        private TreeNode CreateThemeRuleNode(MgResourceIdentifier layerDefId, double viewScale, string labelText, int geomType, int categoryIndex)
+        {
+            MgByteReader icon = _provider.GenerateLegendImage(layerDefId,
+                                                              viewScale,
+                                                              16,
+                                                              16,
+                                                              "PNG",
+                                                              geomType,
+                                                              categoryIndex);
+            TreeNode node = new TreeNode();
+            node.Text = labelText;
+            if (icon != null)
+            {
+                try
+                {
+
+                    byte[] b = new byte[icon.GetLength()];
+                    icon.Read(b, b.Length);
+                    var tag = new LayerNodeMetadata(null, false)
+                    {
+                        IsBaseLayer = false,
+                        IsThemeRule = true
+                    };
+                    using (var ms = new MemoryStream(b))
+                    {
+                        string id = Guid.NewGuid().ToString();
+                        tag.ThemeIcon = Image.FromStream(ms);
+                    }
+                    node.Tag = tag;
+                }
+                finally
+                {
+                    icon.Dispose();
+                }
+            }
+            return node;
+        }
+
+        private TreeNode CreateGroupNode(MgLayerGroup group)
+        {
+            var node = new TreeNode();
+            node.Name = group.GetObjectId();
+            node.Text = group.GetLegendLabel();
+            node.Checked = group.IsVisible();
+            node.SelectedImageKey = node.ImageKey = IMG_GROUP;
+            node.Tag = new GroupNodeMetadata(group);
+            node.ContextMenuStrip = _legend.GroupContextMenu;
+            return node;
+        }
+
+        public TreeNode[] CreateNodes()
+        {
+            List<TreeNode> output = new List<TreeNode>();
+            var nodesById = new Dictionary<string, TreeNode>();
+
+            var scale = _map.ViewScale;
+            var groups = _map.GetLayerGroups();
+            var layers = _map.GetLayers();
+
+            _layerDefinitionContents.Clear();
+            _layers.Clear();
+            _groups.Clear();
+
+            //Process groups first
+            List<MgLayerGroup> remainingNodes = new List<MgLayerGroup>();
+            for (int i = 0; i < groups.GetCount(); i++)
+            {
+                var group = groups.GetItem(i);
+                _groups.Add(group.GetObjectId(), group);
+                if (!group.GetDisplayInLegend())
+                    continue;
+
+                //Add ones without parents first.
+                if (group.Group != null)
+                {
+                    remainingNodes.Add(group);
+                }
+                else
+                {
+                    var node = CreateGroupNode(group);
+                    output.Add(node);
+                    nodesById.Add(group.GetObjectId(), node);
+                }
+
+                while (remainingNodes.Count > 0)
+                {
+                    List<MgLayerGroup> toRemove = new List<MgLayerGroup>();
+                    for (int j = 0; j < remainingNodes.Count; j++)
+                    {
+                        var parentId = remainingNodes[j].Group.GetObjectId();
+                        if (nodesById.ContainsKey(parentId))
+                        {
+                            var node = CreateGroupNode(remainingNodes[j]);
+                            nodesById[parentId].Nodes.Add(node);
+                            toRemove.Add(remainingNodes[j]);
+                        }
+                    }
+                    //Whittle down this list
+                    if (toRemove.Count > 0)
+                    {
+                        foreach (var g in toRemove)
+                        {
+                            remainingNodes.Remove(g);
+                        }
+                    }
+                }
+            }
+
+            //Now process layers
+            for (int i = 0; i < layers.GetCount(); i++)
+            {
+                var lyr = layers.GetItem(i);
+                var ldfId = lyr.LayerDefinition;
+
+                if (!_layerDefinitionContents.ContainsKey(ldfId.ToString()))
+                {
+                    _layerDefinitionContents[ldfId.ToString()] = string.Empty;
+                }
+            }
+
+            //Collect all resource contents in a batch
+            MgStringCollection layerIds = new MgStringCollection();
+            foreach (var lid in _layerDefinitionContents.Keys)
+            {
+                layerIds.Add(lid);
+            }
+
+            MgStringCollection layerContents = _resSvc.GetResourceContents(layerIds, null);
+            for (int i = 0; i < layerIds.GetCount(); i++)
+            {
+                string lid = layerIds.GetItem(i);
+                _layerDefinitionContents[lid] = layerContents.GetItem(i);
+            }
+
+            List<MgLayerBase> remainingLayers = new List<MgLayerBase>();
+            for (int i = 0; i < layers.GetCount(); i++)
+            {
+                var layer = layers.GetItem(i);
+                _layers.Add(layer.GetObjectId(), layer);
+
+                bool display = layer.DisplayInLegend;
+                bool visible = _provider.IsLayerPotentiallyVisibleAtScale(layer, false);
+                if (!display)
+                    continue;
+
+                if (!visible)
+                    continue;
+
+                //Add ones without parents first.
+                if (layer.Group != null)
+                {
+                    remainingLayers.Add(layer);
+                }
+                else
+                {
+                    var node = CreateLayerNode(layer);
+                    if (node != null)
+                    {
+                        output.Add(node);
+                        nodesById.Add(layer.GetObjectId(), node);
+                        if (layer.ExpandInLegend)
+                            node.Expand();
+                    }
+                }
+
+                while (remainingLayers.Count > 0)
+                {
+                    List<MgLayerBase> toRemove = new List<MgLayerBase>();
+                    for (int j = 0; j < remainingLayers.Count; j++)
+                    {
+                        var parentId = remainingLayers[j].Group.GetObjectId();
+                        if (nodesById.ContainsKey(parentId))
+                        {
+                            var node = CreateLayerNode(remainingLayers[j]);
+                            if (node != null)
+                            {
+                                nodesById[parentId].Nodes.Add(node);
+                                if (remainingLayers[j].ExpandInLegend)
+                                    node.Expand();
+                            }
+                            toRemove.Add(remainingLayers[j]);
+                        }
+                    }
+                    //Whittle down this list
+                    if (toRemove.Count > 0)
+                    {
+                        foreach (var g in toRemove)
+                        {
+                            remainingLayers.Remove(g);
+                        }
+                    }
+                }
+            }
+
+            //Now expand any relevant groups
+            for (int i = 0; i < groups.GetCount(); i++)
+            {
+                var group = groups.GetItem(i);
+                if (group.ExpandInLegend)
+                {
+                    var groupId = group.GetObjectId();
+                    if (nodesById.ContainsKey(groupId))
+                    {
+                        nodesById[groupId].Expand();
+                    }
+                }
+            }
+
+            return output.ToArray();
+        }
+
+        private static bool IsThemeLayerNode(TreeNode node)
+        {
+            var meta = node.Tag as LayerNodeMetadata;
+            if (meta != null)
+                return meta.ThemeIcon != null || meta.IsBaseLayer;
+
+            return false;
+        }
+
+        private static bool IsLayerNode(TreeNode node)
+        {
+            var meta = node.Tag as LayerNodeMetadata;
+            return meta != null;
+        }
+
+        internal void DrawNode(DrawTreeNodeEventArgs e, bool showPlusMinus, Font font)
+        {
+            if (IsLayerNode(e.Node) && !e.Bounds.IsEmpty)
+            {
+                Color backColor, foreColor;
+
+                //For some reason, the default bounds are way off from what you would
+                //expect it to be. So we apply this offset for any text/image draw operations
+                int xoffset = -36;
+                if (showPlusMinus && e.Node.Nodes.Count > 0)
+                {
+                    // Use the VisualStyles renderer to use the proper OS-defined glyphs
+                    Rectangle glyphRect = new Rectangle(e.Node.Bounds.X - 52, e.Node.Bounds.Y, 16, 16);
+                    if (Application.RenderWithVisualStyles)
+                    {
+                        VisualStyleElement element = (e.Node.IsExpanded) ?
+                            VisualStyleElement.TreeView.Glyph.Opened : VisualStyleElement.TreeView.Glyph.Closed;
+
+                        VisualStyleRenderer renderer = new VisualStyleRenderer(element);
+                        renderer.DrawBackground(e.Graphics, glyphRect);
+                    }
+                    else //Visual Styles disabled, fallback to drawing the +/- using geometric primitives
+                    {
+                        int h = 8;
+                        int w = 8;
+                        int x = glyphRect.X;
+                        int y = glyphRect.Y + (glyphRect.Height / 2) - 4;
+
+                        //Draw the -
+                        e.Graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
+                        e.Graphics.FillRectangle(new SolidBrush(Color.White), x + 1, y + 1, w - 1, h - 1);
+                        e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), x + 2, y + 4, x + w - 2, y + 4);
+
+                        //Draw the |
+                        if (!e.Node.IsExpanded)
+                            e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), x + 4, y + 2, x + 4, y + h - 2);
+                    }
+                }
+
+                if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
+                {
+                    backColor = SystemColors.Highlight;
+                    foreColor = SystemColors.HighlightText;
+                }
+                else if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
+                {
+                    backColor = SystemColors.HotTrack;
+                    foreColor = SystemColors.HighlightText;
+                }
+                else
+                {
+                    backColor = e.Node.BackColor;
+                    foreColor = e.Node.ForeColor;
+                }
+
+                var tag = e.Node.Tag as LayerNodeMetadata;
+                var checkBoxOffset = xoffset;
+                var selectabilityOffset = xoffset + 16;
+                var iconOffsetNoSelect = xoffset + 16;
+                if (tag != null && tag.IsThemeRule) //No checkbox for theme rule nodes
+                {
+                    selectabilityOffset = xoffset;
+                    iconOffsetNoSelect = xoffset;
+                }
+                var iconOffset = selectabilityOffset + 20;
+                var textOffset = iconOffset + 20;
+                var textOffsetNoSelect = iconOffsetNoSelect + 20;
+
+                //Uncomment if you need to "see" the bounds of the node
+                //e.Graphics.DrawRectangle(Pens.Black, e.Node.Bounds);
+
+                if (tag != null && !tag.IsThemeRule) //No checkbox for theme rule nodes
+                {
+                    if (Application.RenderWithVisualStyles)
+                    {
+                        CheckBoxRenderer.DrawCheckBox(
+                            e.Graphics,
+                            new Point(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y),
+                            e.Node.Checked ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal);
+                    }
+                    else
+                    {
+                        //We don't have to do this, but with Visual Styles disabled, there is a noticeable jarring visual difference from DrawDefault'd checkboxes
+                        //So we might as well emulate that style for the sake of consistency
+                        var rect = new Rectangle(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y, 16, 16);
+                        ControlPaint.DrawCheckBox(e.Graphics, rect, e.Node.Checked ? ButtonState.Checked | ButtonState.Flat : ButtonState.Flat);
+                        rect.Inflate(-2, -2);
+                        e.Graphics.DrawRectangle(new Pen(Brushes.Black, 2.0f), rect);
+                    }
+                }
+                if (tag != null)
+                {
+                    if (tag.DrawSelectabilityIcon)
+                    {
+                        var icon = tag.IsSelectable ? _selectableIcon : _unselectableIcon;
+                        e.Graphics.DrawImage(icon, e.Node.Bounds.X + selectabilityOffset, e.Node.Bounds.Y);
+                        //Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
+                    }
+                    if (tag.ThemeIcon != null)
+                    {
+                        if (tag.DrawSelectabilityIcon)
+                        {
+                            e.Graphics.DrawImage(tag.ThemeIcon, e.Node.Bounds.X + iconOffset, e.Node.Bounds.Y);
+                            //Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
+                        }
+                        else
+                        {
+                            e.Graphics.DrawImage(tag.ThemeIcon, e.Node.Bounds.X + iconOffsetNoSelect, e.Node.Bounds.Y);
+                            //Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
+                        }
+                    }
+
+                    using (SolidBrush brush = new SolidBrush(Color.Black))
+                    {
+                        e.Graphics.DrawString(e.Node.Text, font, brush, e.Node.Bounds.X + (tag.DrawSelectabilityIcon ? textOffset : textOffsetNoSelect), e.Node.Bounds.Y);
+                    }
+                }
+                else
+                {
+                    using (SolidBrush brush = new SolidBrush(Color.Black))
+                    {
+                        e.Graphics.DrawString(e.Node.Text, font, brush, e.Node.Bounds.X + 17.0f + xoffset, e.Node.Bounds.Y);
+                    }
+                }
+
+                e.DrawDefault = false;
+            }
+            else
+            {
+                e.DrawDefault = true;
+            }
+        }
+
+        internal void SetGroupExpandInLegend(string groupName, bool expand)
+        {
+            if (_groups.ContainsKey(groupName))
+                _provider.SetGroupExpandInLegend(_groups[groupName], expand);
+        }
+
+        internal void SetLayerExpandInLegend(string layerName, bool expand)
+        {
+            if (_layers.ContainsKey(layerName))
+                _provider.SetLayerExpandInLegend(_layers[layerName], expand);
+        }
+
+        internal void SetGroupVisible(string name, bool bChecked)
+        {
+            if (_groups.ContainsKey(name))
+            {
+                var grp = _groups[name];
+                grp.SetVisible(bChecked);
+                var bVis = HasVisibleParent(grp);
+                if (bVis)
+                    _legend.OnRequestRefresh();
+            }
+        }
+
+        internal void SetLayerVisible(string name, bool bChecked)
+        {
+            if (_layers.ContainsKey(name))
+            {
+                var layer = _layers[name];
+                layer.SetVisible(bChecked);
+                var bVis = HasVisibleParent(layer);
+                if (bVis)
+                {
+                    layer.ForceRefresh();
+                    _legend.OnRequestRefresh();
+                }
+            }
+        }
+    }
+
+    namespace Legend.Model
+    {
+        class LegendNodeMetadata
+        {
+            public bool IsGroup { get; protected set; }
+        }
+
+        class GroupNodeMetadata : LegendNodeMetadata
+        {
+            internal MgLayerGroup Group { get; set; }
+
+            public GroupNodeMetadata(MgLayerGroup group)
+            {
+                base.IsGroup = true;
+                this.Group = group;
+            }
+        }
+
+        class LayerNodeMetadata : LegendNodeMetadata
+        {
+            public LayerNodeMetadata(MgLayerBase layer, bool bInitiallySelectable)
+            {
+                base.IsGroup = false;
+                this.Layer = layer;
+                this.IsSelectable = (layer != null) ? layer.Selectable : false;
+                this.DrawSelectabilityIcon = (layer != null && bInitiallySelectable);
+                this.IsThemeRule = false;
+            }
+
+            internal MgLayerBase Layer { get; set; }
+
+            public bool DrawSelectabilityIcon { get; set; }
+
+            public bool IsSelectable { get; set; }
+
+            public bool IsThemeRule { get; set; }
+
+            public bool IsBaseLayer { get; set; }
+
+            public Image ThemeIcon { get; set; }
+        }
+    }
+}

Modified: branches/2.4/MgDev/Desktop/MapViewer/MgMapViewer.cs
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewer/MgMapViewer.cs	2012-08-21 11:07:49 UTC (rev 6944)
+++ branches/2.4/MgDev/Desktop/MapViewer/MgMapViewer.cs	2012-08-21 13:16:50 UTC (rev 6945)
@@ -268,7 +268,7 @@
                 dPtStart.X = 0;
                 dPtStart.Y = 0;
                 this.DigitizingType = MapDigitizationType.None;
-                Trace.TraceInformation("Digitization cancelled");
+                //Trace.TraceInformation("Digitization cancelled");
                 this.Invalidate();
             }
         }
@@ -453,7 +453,7 @@
                     _selectionRenderOpts = null;
 
                     _selectionRenderOpts = CreateSelectionRenderingOptions(value.R, value.G, value.B);
-                    Trace.TraceInformation("Selection color updated to ({0}, {1}, {2})", value.R, value.G, value.B);
+                    //Trace.TraceInformation("Selection color updated to ({0}, {1}, {2})", value.R, value.G, value.B);
                 }
             }
             
@@ -515,7 +515,7 @@
         {
             base.OnPaint(e);
 
-            Trace.TraceInformation("OnPaint(e)");
+            //Trace.TraceInformation("OnPaint(e)");
             ApplyPaintTranslateTransform(e);
 
             if (mouseWheelSx.HasValue && mouseWheelSy.HasValue && mouseWheelSx.Value != 0.0 && mouseWheelSy.Value != 0.0)
@@ -529,14 +529,14 @@
 
             if (_mapImage != null)
             {
-                Trace.TraceInformation("Render buffered map image");
+                //Trace.TraceInformation("Render buffered map image");
                 e.Graphics.DrawImage(_mapImage, new PointF(0, 0));
             }
 
             //Thread.Sleep(100);
             if (_selectionImage != null)
             {
-                Trace.TraceInformation("Render buffered map selection");
+                //Trace.TraceInformation("Render buffered map selection");
                 e.Graphics.DrawImage(_selectionImage, new PointF(0, 0));
             }
 
@@ -801,9 +801,9 @@
             if (rect.HasValue)
             {
                 var r = rect.Value;
-                Trace.TraceInformation("Draw rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
+                //Trace.TraceInformation("Draw rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
                 e.Graphics.DrawRectangle(CreateOutlinePen(), r);
-                Trace.TraceInformation("Fill rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
+                //Trace.TraceInformation("Fill rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
                 e.Graphics.FillRectangle(CreateFillBrush(), r);
 
                 DrawVertexCoordinates(e, r.Left, r.Top, true);
@@ -821,9 +821,9 @@
             if (rect.HasValue)
             {
                 var r = rect.Value;
-                Trace.TraceInformation("Draw rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
+                //Trace.TraceInformation("Draw rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
                 e.Graphics.DrawRectangle(CreateOutlinePen(), r);
-                Trace.TraceInformation("Fill rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
+                //Trace.TraceInformation("Fill rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
                 e.Graphics.FillRectangle(CreateFillBrush(), r);
             }
         }
@@ -1306,13 +1306,13 @@
                 ResetMouseWheelPaintTransforms();
                 if (_map != null)
                 {
-                    Trace.TraceInformation("Performing delayed resize to (" + this.Width + ", " + this.Height + ")");
+                    //Trace.TraceInformation("Performing delayed resize to (" + this.Width + ", " + this.Height + ")");
                     _provider.SetDisplaySize(this.Width, this.Height);
                     UpdateExtents();
                     RefreshMap(false);
                 }
                 _delayedResizeTimer.Stop();
-                Trace.TraceInformation("Delayed resize timer stopped");
+                //Trace.TraceInformation("Delayed resize timer stopped");
             });
             if (this.InvokeRequired)
                 this.Invoke(action);
@@ -1326,17 +1326,17 @@
             {
                 _delayedResizeTimer = new System.Timers.Timer();
                 _delayedResizeTimer.Elapsed += OnDelayResizeTimerElapsed;
-                Trace.TraceInformation("Delay resize timer initialized");
+                //Trace.TraceInformation("Delay resize timer initialized");
             }
             if (_delayedResizeTimer.Enabled)
             {
-                Trace.TraceInformation("Stopped delayed resize");
+                //Trace.TraceInformation("Stopped delayed resize");
                 _delayedResizeTimer.Stop();
             }
 
             _delayedResizeTimer.Interval = 500;
             _delayedResizeTimer.Start();
-            Trace.TraceInformation("Delayed resize re-scheduled");
+            //Trace.TraceInformation("Delayed resize re-scheduled");
         }
 
         /// <summary>
@@ -1609,7 +1609,7 @@
 
                 _busy = value;
 #if TRACE
-                Trace.TraceInformation("IsBusy = " + _busy);
+                //Trace.TraceInformation("IsBusy = " + _busy);
                 if (value)
                 {
                     _renderSw.Reset();
@@ -1618,7 +1618,7 @@
                 else
                 {
                     _renderSw.Stop();
-                    Trace.TraceInformation("Rendering operation took {0}ms", _renderSw.ElapsedMilliseconds);
+                    //Trace.TraceInformation("Rendering operation took {0}ms", _renderSw.ElapsedMilliseconds);
                 }
 #endif
                 OnPropertyChanged("IsBusy");
@@ -1907,7 +1907,7 @@
                         this.Image.Dispose();
                         this.Image = null;
                     }
-                    Trace.TraceInformation("Set map image");
+                    //Trace.TraceInformation("Set map image");
                     this.Image = res.Image;
                     bInvalidate = true;
                 }
@@ -1918,7 +1918,7 @@
                         _selectionImage.Dispose();
                         _selectionImage = null;
                     }
-                    Trace.TraceInformation("Set selection image");
+                    //Trace.TraceInformation("Set selection image");
                     _selectionImage = res.SelectionImage;
                     bInvalidate = true;
                 }
@@ -1926,7 +1926,7 @@
                 //If there is a queued refresh action, execute it now
                 if (_queuedRefresh != null)
                 {
-                    Trace.TraceInformation("Executing queued rendering operation");
+                    //Trace.TraceInformation("Executing queued rendering operation");
                     _queuedRefresh();
                     _queuedRefresh = null;
                 }
@@ -2198,7 +2198,7 @@
             }
 #if TRACE
             sw.Stop();
-            Trace.TraceInformation("Selection processing completed in {0}ms", sw.ElapsedMilliseconds);
+            //Trace.TraceInformation("Selection processing completed in {0}ms", sw.ElapsedMilliseconds);
 #endif
             //This selection may result in nothing, so we invalidate the selection image beforehand
             if (_selectionImage != null)
@@ -2299,8 +2299,8 @@
 
             delayRenderTimer.Stop();
             delayRenderTimer.Start();
-            Trace.TraceInformation("Postponed delay render");
-            Trace.TraceInformation("Mouse delta: " + e.Delta + " (" + (e.Delta > 0 ? "Zoom in" : "Zoom out") + ")");
+            //Trace.TraceInformation("Postponed delay render");
+            //Trace.TraceInformation("Mouse delta: " + e.Delta + " (" + (e.Delta > 0 ? "Zoom in" : "Zoom out") + ")");
             //Negative delta = zoom out, Positive delta = zoom in
             //deltas are in units of 120, so treat each multiple of 120 as a "zoom unit"
 
@@ -2328,7 +2328,7 @@
                 Invalidate();
             }
 
-            Trace.TraceInformation("Delta units is: " + mouseWheelDelta);
+            //Trace.TraceInformation("Delta units is: " + mouseWheelDelta);
 
             //Completely ripped the number crunching here from the AJAX viewer with no sense of shame whatsoever :)
             delayRenderScale = GetNewScale(_map.ViewScale, mouseWheelDelta.Value);
@@ -2356,7 +2356,7 @@
             mouseWheelSx = (float)(w / (double)this.Width);
             mouseWheelSy = (float)(h / (double)this.Height);
 
-            Trace.TraceInformation("Paint transform (tx: " + mouseWheelTx + ", ty: " + mouseWheelTy + ", sx: " + mouseWheelSx + ", sy: " + mouseWheelSy + ")");
+            //Trace.TraceInformation("Paint transform (tx: " + mouseWheelTx + ", ty: " + mouseWheelTy + ", sx: " + mouseWheelSx + ", sy: " + mouseWheelSy + ")");
         }
 
         static double GetMetersPerPixel(int dpi)
@@ -2403,8 +2403,8 @@
 
         void OnDelayRender(object sender, System.Timers.ElapsedEventArgs e)
         {
-            Trace.TraceInformation("Delay rendering");
-            Trace.TraceInformation("Set new map coordinates to (" + delayRenderViewCenter.Value.X + ", " + delayRenderViewCenter.Value.Y + " at " + delayRenderScale.Value + ")");
+            //Trace.TraceInformation("Delay rendering");
+            //Trace.TraceInformation("Set new map coordinates to (" + delayRenderViewCenter.Value.X + ", " + delayRenderViewCenter.Value.Y + " at " + delayRenderScale.Value + ")");
             ResetMouseWheelPaintTransforms();
             MethodInvoker action = () => { ZoomToView(delayRenderViewCenter.Value.X, delayRenderViewCenter.Value.Y, delayRenderScale.Value, true); };
             if (this.InvokeRequired)
@@ -2422,7 +2422,7 @@
             mouseWheelTx = null;
             mouseWheelTy = null;
             mouseWheelDelta = 0;
-            Trace.TraceInformation("Mouse wheel paint transform reset");
+            //Trace.TraceInformation("Mouse wheel paint transform reset");
         }
 
         private void HandleMouseClick(MouseEventArgs e)
@@ -2542,18 +2542,18 @@
             if (e.Button == MouseButtons.Left)
             {
                 dragStart = e.Location;
-                Trace.TraceInformation("Drag started at (" + dragStart.X + ", " + dragStart.Y + ")");
+                //Trace.TraceInformation("Drag started at (" + dragStart.X + ", " + dragStart.Y + ")");
 
                 switch (this.ActiveTool)
                 {
                     case MapActiveTool.Pan:
-                        Trace.TraceInformation("START PANNING");
+                        //Trace.TraceInformation("START PANNING");
                         break;
                     case MapActiveTool.Select:
-                        Trace.TraceInformation("START SELECT");
+                        //Trace.TraceInformation("START SELECT");
                         break;
                     case MapActiveTool.ZoomIn:
-                        Trace.TraceInformation("START ZOOM");
+                        //Trace.TraceInformation("START ZOOM");
                         break;
                 }
             }
@@ -2677,7 +2677,7 @@
                     _activeTooltipText = QueryFirstVisibleTooltip(e.X, e.Y);
 #if TRACE
                     sw.Stop();
-                    Trace.TraceInformation("QueryFirstVisibleTooltip() executed in {0}ms", sw.ElapsedMilliseconds);
+                    //Trace.TraceInformation("QueryFirstVisibleTooltip() executed in {0}ms", sw.ElapsedMilliseconds);
 #endif
                 }
                 else
@@ -2695,7 +2695,7 @@
                     //Fix the last one, can't edit last one because points are value types
                     dPath.RemoveAt(dPath.Count - 1);
                     dPath.Add(new Point(e.X, e.Y));
-                    Trace.TraceInformation("Updating last point of a {0} point path", dPath.Count);
+                    //Trace.TraceInformation("Updating last point of a {0} point path", dPath.Count);
                 }
                 Invalidate();
             }
@@ -2728,7 +2728,7 @@
                     double mdy = coord.Y - pt.Y;
 
                     ZoomToView(coord.X, coord.Y, _map.ViewScale, true);
-                    Trace.TraceInformation("END PANNING");
+                    //Trace.TraceInformation("END PANNING");
                 }
                 else if (this.ActiveTool == MapActiveTool.Select)
                 {

Modified: branches/2.4/MgDev/Desktop/MapViewerTest/MapViewerTest.csproj.user
===================================================================
--- branches/2.4/MgDev/Desktop/MapViewerTest/MapViewerTest.csproj.user	2012-08-21 11:07:49 UTC (rev 6944)
+++ branches/2.4/MgDev/Desktop/MapViewerTest/MapViewerTest.csproj.user	2012-08-21 13:16:50 UTC (rev 6945)
@@ -1,7 +1,7 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
     <StartArguments>Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition</StartArguments>
-    <EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
+    <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
     <StartArguments>Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition</StartArguments>



More information about the mapguide-commits mailing list