using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; namespace ESRI.ArcGIS.Client.Toolkit.Primitives { /// /// Internal class encapsulating a layer item representing the virtual root item for the legend tree. /// The LayerItems collection of this item is the collection of map layer item displayed at the first level of the TOC. /// This class manages the events coming from the map, from the map layers and from the map layer items. /// internal sealed class LegendTree : LayerItemViewModel { #region Constructor public LegendTree() { LayerItemsOptions = new LayerItemsOpts(false, false, true, true); Attach(this); } ~LegendTree() { Detach(); } #endregion #region LegendItemTemplate /// /// Gets or sets the legend item template. /// /// The legend item template. private DataTemplate _legendItemTemplate; internal DataTemplate LegendItemTemplate { get { return _legendItemTemplate; } set { if (_legendItemTemplate != value) { _legendItemTemplate = value; PropagateTemplate(); UpdateLayerItemsOptions(); } } } #endregion #region LayerTemplate private DataTemplate _layerTemplate; /// /// Gets or sets the layer template i.e. the template used to display a layer in the legend. /// /// The layer template. internal DataTemplate LayerTemplate { get { return _layerTemplate; } set { if (_layerTemplate != value) { _layerTemplate = value; PropagateTemplate(); } } } #endregion #region MapLayerTemplate private DataTemplate _mapLayerTemplate; /// /// Gets or sets the map layer template. /// /// The map layer template. internal DataTemplate MapLayerTemplate { get { return _mapLayerTemplate; } set { if (_mapLayerTemplate != value) { _mapLayerTemplate = value; PropagateTemplate(); } } } #endregion #region Map private Map _map; private LayerCollection _oldLayers = null; // to be able to unhook when Layers changes /// /// Gets or sets the map that the legend control is buddied to. /// /// The map. internal Map Map { get { return _map; } set { if (_map != value) { if (_map != null) { _map.PropertyChanged -= Map_PropertyChanged; _map.ExtentChanged -= new EventHandler(Map_ExtentChanged); if (_map.Layers != null) _map.Layers.CollectionChanged -= Layers_CollectionChanged; } _map = value; if (_map != null) { _map.PropertyChanged += Map_PropertyChanged; _map.ExtentChanged += new EventHandler(Map_ExtentChanged); if (_map.Layers != null) _map.Layers.CollectionChanged += Layers_CollectionChanged; } UpdateMapLayerItems(); } } } #endregion #region LayerIDs private IEnumerable _layerIDs = null; /// /// Gets or sets the layer IDs of the layers participating in the legend. /// /// /// Specified in XAML and in Blend as a comma-delimited string: If a layer /// name contains a comma, please use , instead of the comma. /// If null/empty, legend from all layers is generated. Order of /// the layer ids is respected in generating the legend. /// /// The layer IDs. internal IEnumerable LayerIDs { get { return _layerIDs; } set { if (_layerIDs != value) { _layerIDs = value; UpdateMapLayerItems(); } } } #endregion #region ShowOnlyVisibleLayers private bool _showOnlyVisibleLayers = true; /// /// Gets or sets a value indicating whether only the visible layers are participating to the legend. /// /// /// true if only the visible layers are participating to the legend; otherwise, false. /// internal bool ShowOnlyVisibleLayers { get { return _showOnlyVisibleLayers; } set { _showOnlyVisibleLayers = value; LayerItemsOpts mode = LayerItemsOptions; mode.ShowOnlyVisibleLayers = value; PropagateLayerItemsOptions(mode); } } #endregion #region Refresh /// /// Refreshes the legend control. /// /// Note : In most cases, the control is always up to date without calling the refresh method. internal void Refresh() { MapLayerItems.ForEach(mapLayerItem => mapLayerItem.Refresh()); } #endregion #region Event Refreshed /// /// Occurs when the legend is refreshed. /// Give the opportunity for an application to add or remove legend items. /// internal event EventHandler Refreshed; internal void OnRefreshed(object sender, Legend.RefreshedEventArgs args) { EventHandler refreshed = Refreshed; if (refreshed != null) { refreshed(sender, args); } } #endregion #region Map Event Handlers private void Map_ExtentChanged(object sender, ExtentEventArgs e) { if (e.NewExtent != null) { if (e.OldExtent == null || e.NewExtent.Height != e.OldExtent.Height || e.NewExtent.Width != e.OldExtent.Width) { UpdateLayerVisibilities(); } } } private void Map_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Layers") { if (_oldLayers != null) _oldLayers.CollectionChanged -= Layers_CollectionChanged; _oldLayers = (sender as Map).Layers; if (_oldLayers != null) _oldLayers.CollectionChanged += Layers_CollectionChanged; UpdateMapLayerItems(); } } private void Layers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { UpdateMapLayerItems(); } #endregion #region Propagate methods propagating a property to all legend items of the legend tree private void PropagateTemplate() { // set the template on all descendants including the legend items LayerItems.Descendants(item => item.LayerItems).ForEach(item => { item.Template = item.GetTemplate(); item.LegendItems.ForEach(legendItem => legendItem.Template = legendItem.GetTemplate()); }); } private void PropagateLayerItemsOptions(LayerItemsOpts layerItemsOptions) { if (!LayerItemsOptions.Equals(layerItemsOptions)) { DeferLayerItemsSourceChanged = true; LayerItemsOptions = layerItemsOptions; // set value on all descendants LayerItems.Descendants(layerItem => layerItem.LayerItems).ForEach(layerItem => layerItem.LayerItemsOptions = layerItemsOptions); DeferLayerItemsSourceChanged = false; } } #endregion #region Private Methods private static IEnumerable GetLayers(IEnumerable ids, Map map) { if (map != null && map.Layers != null) { if (ids != null) { foreach (string item in ids) { if (!string.IsNullOrEmpty(item) && map.Layers[item] != null) yield return map.Layers[item]; } } else { foreach (Layer layer in map.Layers) { yield return layer; } } } } private void UpdateMapLayerItems() { ObservableCollection mapLayerItems = new ObservableCollection(); foreach (Layer layer in GetLayers(LayerIDs, Map)) { MapLayerItem mapLayerItem = FindMapLayerItem(layer); if (mapLayerItem == null) // else reuse existing map layer item to avoid query again the legend { // Create a new map layer item mapLayerItem = new MapLayerItem(layer) {LegendTree = this}; mapLayerItem.Refresh(); } mapLayerItems.Add(mapLayerItem); } LayerItems = mapLayerItems; } private IEnumerable MapLayerItems { get { if (LayerItems == null) return null; return LayerItems.OfType(); } } private MapLayerItem FindMapLayerItem(Layer layer) { return MapLayerItems == null ? null : MapLayerItems.FirstOrDefault(mapLayerItem => mapLayerItem.Layer == layer) as MapLayerItem; } private void UpdateLayerVisibilities() { LayerItems.ForEach(layerItem => { layerItem.DeferLayerItemsSourceChanged = true; layerItem.UpdateLayerVisibilities(true, true); layerItem.DeferLayerItemsSourceChanged = false; } ); } #endregion #region LayerItemsMode private Legend.Mode _layerItemsMode = Legend.Mode.Flat; internal Legend.Mode LayerItemsMode { get { return _layerItemsMode; } set { if (value != _layerItemsMode) { _layerItemsMode = value; UpdateLayerItemsOptions(); } } } private void UpdateLayerItemsOptions() { LayerItemsOpts layerItemsOptions; bool returnsLegendItems = (LegendItemTemplate != null); switch (LayerItemsMode) { case Legend.Mode.Tree: layerItemsOptions = new LayerItemsOpts(true, true, returnsLegendItems, ShowOnlyVisibleLayers); break; default: layerItemsOptions = new LayerItemsOpts(false, false, returnsLegendItems, ShowOnlyVisibleLayers); break; } PropagateLayerItemsOptions(layerItemsOptions); } #endregion } }