#region License Information /* HeuristicLab * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Windows.Forms; using HeuristicLab.Collections; using HeuristicLab.Core; using HeuristicLab.Core.Views; using HeuristicLab.MainForm; using HeuristicLab.PluginInfrastructure; namespace HeuristicLab.ItemTreeViews.Views { [View("ItemCollection Tree View")] [Content(typeof(ItemCollection<>), false)] [Content(typeof(IItemCollection<>), false)] [Content(typeof(ReadOnlyItemCollection<>), false)] public partial class ItemCollectionTreeView : ItemView where T : class, IItem { #region properties protected Dictionary itemTreeViewItemMapping; protected Dictionary treeViewChildrenMapping; protected TypeSelectorDialog typeSelectorDialog; protected bool validDragOperation; public new IItemCollection Content { get { return (IItemCollection)base.Content; } set { base.Content = value; OnContentChanged(); } } public ObservableCollection ItemCollection { get { return Content as ObservableCollection; } } public bool ShowDetails { get { return showDetailsCheckBox.Checked; } set { showDetailsCheckBox.Checked = value; } } public TreeView ItemsTreeView { get { return itemsTreeView; } } #endregion public ItemCollectionTreeView() { InitializeComponent(); itemTreeViewItemMapping = new Dictionary(); treeViewChildrenMapping = new Dictionary(); } protected override void Dispose(bool disposing) { if (disposing) { if (typeSelectorDialog != null) typeSelectorDialog.Dispose(); if (components != null) components.Dispose(); } base.Dispose(disposing); } #region eventregistration protected override void DeregisterContentEvents() { Content.ItemsAdded -= Content_ItemsAdded; Content.ItemsRemoved -= Content_ItemsRemoved; Content.CollectionReset -= Content_CollectionReset; base.DeregisterContentEvents(); } protected override void RegisterContentEvents() { DeregisterContentEvents(); //prevent multiattaching base.RegisterContentEvents(); Content.ItemsAdded += Content_ItemsAdded; Content.ItemsRemoved += Content_ItemsRemoved; Content.CollectionReset += Content_CollectionReset; } protected virtual void DeregisterItemEvents(T item) { item.ItemImageChanged -= Item_ItemImageChanged; item.ToStringChanged -= Item_ToStringChanged; } protected virtual void RegisterItemEvents(T item) { DeregisterItemEvents(item); //prevent multiattaching item.ItemImageChanged += Item_ItemImageChanged; item.ToStringChanged += Item_ToStringChanged; } protected virtual void DeregisterViewEvents() { ItemsTreeView.AfterSelect -= itemsTreeView_SelectedIndexChanged; ItemsTreeView.DoubleClick -= itemsTreeView_DoubleClick; ItemsTreeView.KeyDown -= itemsTreeView_KeyDown; } protected virtual void RegisterViewEvents() { DeregisterViewEvents(); //prevent multiattaching ItemsTreeView.AfterSelect += itemsTreeView_SelectedIndexChanged; ItemsTreeView.DoubleClick += itemsTreeView_DoubleClick; ItemsTreeView.KeyDown += itemsTreeView_KeyDown; } protected override void OnContentChanged() { ItemsTreeView.BeginUpdate(); base.OnContentChanged(); ItemsTreeView.Nodes.Clear(); itemTreeViewItemMapping.Clear(); RebuildTreeImageList(); viewHost.Content = null; if (Content == null) { ItemsTreeView.EndUpdate(); return; } RegisterViewEvents(); Caption += " (" + Content.GetType().Name + ")"; foreach (T item in Content) AddTreeNodeRecursive(item, null); SortItemsTreeView(); ItemsTreeView.EndUpdate(); } #endregion protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); if (Content == null) { addButton.Enabled = false; sortAscendingButton.Enabled = false; sortDescendingButton.Enabled = false; removeButton.Enabled = false; ItemsTreeView.Enabled = false; detailsGroupBox.Enabled = false; } else { addButton.Enabled = !Content.IsReadOnly && !ReadOnly; sortAscendingButton.Enabled = false; sortDescendingButton.Enabled = false; removeButton.Enabled = !Content.IsReadOnly && !ReadOnly && ItemsTreeView.Nodes.Count > 0; ItemsTreeView.Enabled = true; detailsGroupBox.Enabled = true; } } protected virtual T CreateItem() { if (typeSelectorDialog == null) { typeSelectorDialog = new TypeSelectorDialog(); typeSelectorDialog.Caption = "Select Item"; typeSelectorDialog.TypeSelector.Caption = "Available Items"; typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true); } if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) { try { return (T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType(); } catch (Exception ex) { ErrorHandling.ShowErrorDialog(this, ex); } } return null; } protected virtual TreeNode CreateTreeViewNode(T item) { var treeViewNode = new TreeNode("noName"); treeViewNode.NodeFont = DefaultFont; if (item == null) { treeViewNode.Text = "null"; ItemsTreeView.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Nothing); treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1; treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex; } else { treeViewNode.Text = item.ToString(); treeViewNode.ToolTipText = item.ItemName + ": " + item.ItemDescription; ItemsTreeView.ImageList.Images.Add(item.ItemImage); treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1; treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex; treeViewNode.Tag = item; } return treeViewNode; } #region adding protected virtual void AddTreeNode(T item) { if (item == null || itemTreeViewItemMapping.ContainsKey(item) || !treeViewChildrenMapping.ContainsKey(item)) return; RegisterItemEvents(item); var node = CreateTreeViewNode(item); itemTreeViewItemMapping[item] = node; var p = GetParent(item); (p == null ? ItemsTreeView.Nodes : itemTreeViewItemMapping[p].Nodes).Add(node); } protected void AddTreeNodeRecursive(T item, TreeNode parent) { if (item == null) return; Queue queue = new Queue(); queue.Enqueue(item); if (!treeViewChildrenMapping.ContainsKey(item)) treeViewChildrenMapping.Add(item, parent != null ? parent.Tag as T : null); //Set explicitly to null in some cases while (queue.Count > 0 && (item = queue.Dequeue()) != null) { AddTreeNode(item); foreach (var child in ExpandTreeViewItem(item)) { //Expand queue if (!treeViewChildrenMapping.ContainsKey(child)) treeViewChildrenMapping.Add(child, item); queue.Enqueue(child); } } } protected void UpdateTreeNodeRecursive(T item, TreeNode parent) { if (item == null) return; Queue queue = new Queue(); queue.Enqueue(item); while (queue.Count > 0 && (item = queue.Dequeue()) != null) { AddTreeNode(item); var newChildren = ExpandTreeViewItem(item); var deadChildren = itemTreeViewItemMapping[item].Nodes.Cast() .Select(n => n.Tag as T) .Where(t => !newChildren.Contains(t)).Select(t => itemTreeViewItemMapping[t]) .ToList(); foreach (var child in newChildren) { //Expand queue if (!treeViewChildrenMapping.ContainsKey(child)) treeViewChildrenMapping.Add(child, item); queue.Enqueue(child); } foreach (var child in deadChildren) { RemoveTreeViewItem(child); } } } /// /// This function defines which subnodes are created. /// Override this to change which items are used in the creation of new nodes /// /// /// protected virtual List ExpandTreeViewItem(object parent) { //unpack enumerables var p = parent as IEnumerable; if (p != null) return p.ToList(); //exclude LookupParameters from Value unpacking if (parent is ILookupParameter) return new List(); //unpack "value"/"Value"/"ActualValue" relationship if possible object prop = GetValue(parent); //return nothing if failed return (prop != null) ? ExpandTreeViewItem(prop) : new List(); } #endregion #region removal private void RemoveTreeViewItem2(TreeNode treeViewItem) { if (treeViewItem == null) return; T item = treeViewItem.Tag as T; if (item != null) { treeViewChildrenMapping.Remove(item); itemTreeViewItemMapping.Remove(item); DeregisterItemEvents(item); } var children = GetChildren(item); foreach (var node in children) if (node != null && itemTreeViewItemMapping.ContainsKey(node)) RemoveTreeViewItem(itemTreeViewItemMapping[node]); treeViewItem.Remove(); } protected void RemoveTreeViewItem(TreeNode treeViewNode) { var queue = new Queue(new[] { treeViewNode }); treeViewNode.Remove(); while (queue.Count > 0) { treeViewNode = queue.Dequeue(); T item = treeViewNode.Tag as T; if (item != null) { treeViewChildrenMapping.Remove(item); itemTreeViewItemMapping.Remove(item); DeregisterItemEvents(item); } var children = GetChildren(item); foreach (var node in children) if (node != null && itemTreeViewItemMapping.ContainsKey(node)) queue.Enqueue(itemTreeViewItemMapping[node]); } } #endregion protected virtual void UpdateTreeViewItemImage(TreeNode treeViewNode) { if (treeViewNode == null) throw new ArgumentNullException(); var item = treeViewNode.Tag as T; var i = treeViewNode.ImageIndex; ItemsTreeView.ImageList.Images[i] = item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage; treeViewNode.ImageIndex = i; treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex; } protected virtual void UpdateTreeViewItemText(TreeNode treeViewNode) { if (treeViewNode == null) throw new ArgumentNullException(); var item = treeViewNode.Tag as T; treeViewNode.Text = item == null ? "null" : item.ToString(); treeViewNode.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription; } protected virtual TreeNode GetTreeNodeForItem(T item) { if (item == null) return GetAllNodes(ItemsTreeView).First(x => x.Tag == null); TreeNode viewItem; itemTreeViewItemMapping.TryGetValue(item, out viewItem); return viewItem; } #region View Events protected virtual void itemsTreeView_SelectedIndexChanged(object sender, EventArgs e) { removeButton.Enabled = (Content != null) && !Content.IsReadOnly && !ReadOnly && ItemsTreeView.SelectedNode != null; if (!showDetailsCheckBox.Checked) return; var item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null; detailsGroupBox.Enabled = true; viewHost.Content = item; } protected virtual void itemsTreeView_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete) { if ((ItemsTreeView.SelectedNode != null) && !Content.IsReadOnly && !ReadOnly) { var selected = GetAllNodes(ItemsTreeView.SelectedNode); if (ItemCollection != null) ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag)); else { ItemsTreeView.BeginUpdate(); foreach (var item in selected) Content.Remove((T)item.Tag); ItemsTreeView.EndUpdate(); } } } } protected virtual void itemsTreeView_DoubleClick(object sender, EventArgs e) { T item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null; if (item == null) return; IContentView view = MainFormManager.MainForm.ShowContent(item); if (view == null) return; view.ReadOnly = ReadOnly; view.Locked = Locked; } #endregion #region Button Events protected virtual void addButton_Click(object sender, EventArgs e) { T item = CreateItem(); if (item != null) Content.Add(item); } protected virtual void sortAscendingButton_Click(object sender, EventArgs e) { SortItemsTreeView(); } protected virtual void sortDescendingButton_Click(object sender, EventArgs e) { //DO nothing for Treeview (no custom sort order possible) } protected virtual void removeButton_Click(object sender, EventArgs e) { if (ItemsTreeView.SelectedNode == null) return; var selected = GetAllNodes(ItemsTreeView.SelectedNode); if (ItemCollection != null) { ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag)); } else { foreach (TreeNode item in selected) Content.Remove((T)item.Tag); } ItemsTreeView.SelectedNode = null; } #endregion #region CheckBox Events protected virtual void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) { if (showDetailsCheckBox.Checked) { splitContainer.Panel2Collapsed = false; detailsGroupBox.Enabled = ItemsTreeView.SelectedNode != null; viewHost.Content = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null; } else { splitContainer.Panel2Collapsed = true; viewHost.Content = null; } } #endregion #region Content Events protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs e) { if (InvokeRequired) Invoke(new CollectionItemsChangedEventHandler(Content_ItemsAdded), sender, e); else { ItemsTreeView.BeginUpdate(); foreach (T item in e.Items) AddTreeNodeRecursive(item, null); RebuildTreeImageList(); ItemsTreeView.EndUpdate(); } } protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs e) { if (InvokeRequired) Invoke(new CollectionItemsChangedEventHandler(Content_ItemsRemoved), sender, e); else { ItemsTreeView.BeginUpdate(); foreach (T item in e.Items) { var treeNode = GetTreeNodeForItem(item); if (treeNode != null) RemoveTreeViewItem(treeNode); } RebuildTreeImageList(); ItemsTreeView.EndUpdate(); } } protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs e) { if (InvokeRequired) Invoke(new CollectionItemsChangedEventHandler(Content_CollectionReset), sender, e); else { ItemsTreeView.BeginUpdate(); foreach (T item in e.OldItems) { var treeNode = GetTreeNodeForItem(item); if (treeNode != null) RemoveTreeViewItem(treeNode); } RebuildTreeImageList(); foreach (T item in e.Items) AddTreeNodeRecursive(item, null); ItemsTreeView.EndUpdate(); } } #endregion #region Item Events protected virtual void Item_ItemImageChanged(object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(Item_ItemImageChanged), sender, e); else { var node = GetTreeNodeForItem(sender as T); if (node != null) UpdateTreeViewItemImage(node); } } protected virtual void Item_ToStringChanged(object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(Item_ToStringChanged), sender, e); else { var item = sender as T; var itemNode = GetTreeNodeForItem(item); if (itemNode == null) return; //nothing to remove var parent = itemNode.Parent; ItemsTreeView.BeginUpdate(); UpdateTreeNodeRecursive(item, parent); UpdateTreeViewItemText(itemNode); ItemsTreeView.EndUpdate(); } } #endregion #region Helpers protected virtual void SortItemsTreeView() { ItemsTreeView.Sort(); ItemsTreeView.Sorted = true; } protected virtual void RebuildTreeImageList() { if (ItemsTreeView.ImageList == null) ItemsTreeView.ImageList = new ImageList(); ItemsTreeView.ImageList.Images.Clear(); foreach (TreeNode treeNode in GetAllNodes(ItemsTreeView)) { T item = treeNode.Tag as T; try { ItemsTreeView.ImageList.Images.Add(item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage); treeNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1; treeNode.SelectedImageIndex = treeNode.ImageIndex; } catch (ArgumentException) { treeNode.SelectedImageIndex = ItemsTreeView.ImageList.Images.Count + 10000; } } } protected List GetAllNodes(TreeView view) { var n = new List(); foreach (TreeNode node in view.Nodes) { AddAllNodes(n, node); } return n; } private List GetAllNodes(TreeNode node) { var n = new List(); AddAllNodes(n, node); return n; } private static void AddAllNodes(List list, TreeNode node) { var queue = new Queue(new[] { node }); while (queue.Count > 0) { node = queue.Dequeue(); if (!list.Contains(node)) list.Add(node); foreach (TreeNode n in node.Nodes) queue.Enqueue(n); } } protected T GetParent(T child) { return treeViewChildrenMapping.ContainsKey(child) ? treeViewChildrenMapping[child] : null; } private static IItem GetValue(object parent) { var props = parent.GetType().GetProperties(); foreach (var prop in props) { if (prop.Name.Equals("Value", StringComparison.InvariantCultureIgnoreCase) || prop.Name.Equals("ActualValue", StringComparison.InvariantCultureIgnoreCase)) { if (typeof(IItem).IsAssignableFrom(prop.PropertyType)) { try { return prop.GetValue(parent) as IItem; } catch (TargetInvocationException) { } //if getting the value is not an option (LookUpParameters,...) try other properties } } } return null; } protected List GetChildren(T parent) { return treeViewChildrenMapping.Where(x => x.Value == parent).Select(x => x.Key).ToList(); } #endregion } }