Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ItemTreeViews/3.3/Views/ItemCollectionTreeView.cs @ 15555

Last change on this file since 15555 was 14337, checked in by bwerth, 8 years ago

#2684 changed recursions to queue structures

File size: 19.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Reflection;
26using System.Windows.Forms;
27using HeuristicLab.Collections;
28using HeuristicLab.Core;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31using HeuristicLab.PluginInfrastructure;
32
33namespace HeuristicLab.ItemTreeViews.Views {
34  [View("ItemCollection Tree View")]
35  [Content(typeof(ItemCollection<>), false)]
36  [Content(typeof(IItemCollection<>), false)]
37  [Content(typeof(ReadOnlyItemCollection<>), false)]
38  public partial class ItemCollectionTreeView<T> : ItemView where T : class, IItem {
39
40    #region properties
41
42    protected Dictionary<T, TreeNode> itemTreeViewItemMapping;
43    protected Dictionary<T, T> treeViewChildrenMapping;
44    protected TypeSelectorDialog typeSelectorDialog;
45    protected bool validDragOperation;
46
47    public new IItemCollection<T> Content
48    {
49      get { return (IItemCollection<T>)base.Content; }
50      set
51      {
52        base.Content = value;
53        OnContentChanged();
54      }
55    }
56
57    public ObservableCollection<T> ItemCollection { get { return Content as ObservableCollection<T>; } }
58
59    public bool ShowDetails
60    {
61      get { return showDetailsCheckBox.Checked; }
62      set { showDetailsCheckBox.Checked = value; }
63    }
64    public TreeView ItemsTreeView { get { return itemsTreeView; } }
65    #endregion
66
67    public ItemCollectionTreeView() {
68      InitializeComponent();
69      itemTreeViewItemMapping = new Dictionary<T, TreeNode>();
70      treeViewChildrenMapping = new Dictionary<T, T>();
71    }
72    protected override void Dispose(bool disposing) {
73      if (disposing) {
74        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
75        if (components != null) components.Dispose();
76      }
77      base.Dispose(disposing);
78    }
79
80    #region eventregistration
81    protected override void DeregisterContentEvents() {
82      Content.ItemsAdded -= Content_ItemsAdded;
83      Content.ItemsRemoved -= Content_ItemsRemoved;
84      Content.CollectionReset -= Content_CollectionReset;
85      base.DeregisterContentEvents();
86    }
87    protected override void RegisterContentEvents() {
88      DeregisterContentEvents(); //prevent multiattaching
89      base.RegisterContentEvents();
90      Content.ItemsAdded += Content_ItemsAdded;
91      Content.ItemsRemoved += Content_ItemsRemoved;
92      Content.CollectionReset += Content_CollectionReset;
93    }
94
95    protected virtual void DeregisterItemEvents(T item) {
96      item.ItemImageChanged -= Item_ItemImageChanged;
97      item.ToStringChanged -= Item_ToStringChanged;
98    }
99    protected virtual void RegisterItemEvents(T item) {
100      DeregisterItemEvents(item); //prevent multiattaching
101      item.ItemImageChanged += Item_ItemImageChanged;
102      item.ToStringChanged += Item_ToStringChanged;
103    }
104
105    protected virtual void DeregisterViewEvents() {
106      ItemsTreeView.AfterSelect -= itemsTreeView_SelectedIndexChanged;
107      ItemsTreeView.DoubleClick -= itemsTreeView_DoubleClick;
108      ItemsTreeView.KeyDown -= itemsTreeView_KeyDown;
109    }
110    protected virtual void RegisterViewEvents() {
111      DeregisterViewEvents(); //prevent multiattaching
112      ItemsTreeView.AfterSelect += itemsTreeView_SelectedIndexChanged;
113      ItemsTreeView.DoubleClick += itemsTreeView_DoubleClick;
114      ItemsTreeView.KeyDown += itemsTreeView_KeyDown;
115    }
116
117    protected override void OnContentChanged() {
118      ItemsTreeView.BeginUpdate();
119      base.OnContentChanged();
120      ItemsTreeView.Nodes.Clear();
121      itemTreeViewItemMapping.Clear();
122      RebuildTreeImageList();
123      viewHost.Content = null;
124      if (Content == null) { ItemsTreeView.EndUpdate(); return; }
125      RegisterViewEvents();
126      Caption += " (" + Content.GetType().Name + ")";
127      foreach (T item in Content)
128        AddTreeNodeRecursive(item, null);
129      SortItemsTreeView();
130      ItemsTreeView.EndUpdate();
131    }
132    #endregion
133
134    protected override void SetEnabledStateOfControls() {
135      base.SetEnabledStateOfControls();
136      if (Content == null) {
137        addButton.Enabled = false;
138        sortAscendingButton.Enabled = false;
139        sortDescendingButton.Enabled = false;
140        removeButton.Enabled = false;
141        ItemsTreeView.Enabled = false;
142        detailsGroupBox.Enabled = false;
143      } else {
144        addButton.Enabled = !Content.IsReadOnly && !ReadOnly;
145        sortAscendingButton.Enabled = false;
146        sortDescendingButton.Enabled = false;
147        removeButton.Enabled = !Content.IsReadOnly && !ReadOnly && ItemsTreeView.Nodes.Count > 0;
148        ItemsTreeView.Enabled = true;
149        detailsGroupBox.Enabled = true;
150      }
151    }
152    protected virtual T CreateItem() {
153      if (typeSelectorDialog == null) {
154        typeSelectorDialog = new TypeSelectorDialog();
155        typeSelectorDialog.Caption = "Select Item";
156        typeSelectorDialog.TypeSelector.Caption = "Available Items";
157        typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true);
158      }
159      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
160        try {
161          return (T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
162        }
163        catch (Exception ex) {
164          ErrorHandling.ShowErrorDialog(this, ex);
165        }
166      }
167      return null;
168    }
169    protected virtual TreeNode CreateTreeViewNode(T item) {
170      var treeViewNode = new TreeNode("noName");
171      treeViewNode.NodeFont = DefaultFont;
172      if (item == null) {
173        treeViewNode.Text = "null";
174        ItemsTreeView.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Nothing);
175        treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
176        treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
177      } else {
178        treeViewNode.Text = item.ToString();
179        treeViewNode.ToolTipText = item.ItemName + ": " + item.ItemDescription;
180        ItemsTreeView.ImageList.Images.Add(item.ItemImage);
181        treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
182        treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
183        treeViewNode.Tag = item;
184      }
185      return treeViewNode;
186    }
187
188    #region adding
189    protected virtual void AddTreeNode(T item) {
190      if (item == null || itemTreeViewItemMapping.ContainsKey(item) || !treeViewChildrenMapping.ContainsKey(item))
191        return;
192      RegisterItemEvents(item);
193      var node = CreateTreeViewNode(item);
194      itemTreeViewItemMapping[item] = node;
195      var p = GetParent(item);
196      (p == null ? ItemsTreeView.Nodes : itemTreeViewItemMapping[p].Nodes).Add(node);
197
198
199    }
200    protected void AddTreeNodeRecursive(T item, TreeNode parent) {
201      if (item == null) return;
202      Queue<T> queue = new Queue<T>();
203      queue.Enqueue(item);
204      if (!treeViewChildrenMapping.ContainsKey(item))
205        treeViewChildrenMapping.Add(item, parent != null ? parent.Tag as T : null); //Set explicitly to null in some cases
206      while (queue.Count > 0 && (item = queue.Dequeue()) != null) {
207        AddTreeNode(item);
208        foreach (var child in ExpandTreeViewItem(item)) { //Expand queue
209          if (!treeViewChildrenMapping.ContainsKey(child))
210            treeViewChildrenMapping.Add(child, item);
211          queue.Enqueue(child);
212        }
213      }
214    }
215    protected void UpdateTreeNodeRecursive(T item, TreeNode parent) {
216      if (item == null) return;
217      Queue<T> queue = new Queue<T>();
218      queue.Enqueue(item);
219      while (queue.Count > 0 && (item = queue.Dequeue()) != null) {
220        AddTreeNode(item);
221        var newChildren = ExpandTreeViewItem(item);
222        var deadChildren =
223          itemTreeViewItemMapping[item].Nodes.Cast<TreeNode>()
224            .Select(n => n.Tag as T)
225            .Where(t => !newChildren.Contains(t)).Select(t => itemTreeViewItemMapping[t])
226            .ToList();
227
228        foreach (var child in newChildren) { //Expand queue
229          if (!treeViewChildrenMapping.ContainsKey(child))
230            treeViewChildrenMapping.Add(child, item);
231          queue.Enqueue(child);
232        }
233
234        foreach (var child in deadChildren) {
235          RemoveTreeViewItem(child);
236        }
237      }
238    }
239
240    /// <summary>
241    /// This function defines which subnodes are created.
242    /// Override this to change which items are used in the creation of new nodes
243    /// </summary>
244    /// <param name="parent"></param>
245    /// <returns></returns>
246    protected virtual List<T> ExpandTreeViewItem(object parent) {
247      //unpack enumerables
248      var p = parent as IEnumerable<T>;
249      if (p != null) return p.ToList();
250      //exclude LookupParameters from Value unpacking
251      if (parent is ILookupParameter) return new List<T>();
252      //unpack "value"/"Value"/"ActualValue" relationship if possible
253      object prop = GetValue(parent);
254      //return nothing if failed
255      return (prop != null) ? ExpandTreeViewItem(prop) : new List<T>();
256    }
257    #endregion
258
259    #region removal
260    private void RemoveTreeViewItem2(TreeNode treeViewItem) {
261      if (treeViewItem == null) return;
262      T item = treeViewItem.Tag as T;
263      if (item != null) {
264        treeViewChildrenMapping.Remove(item);
265        itemTreeViewItemMapping.Remove(item);
266        DeregisterItemEvents(item);
267      }
268      var children = GetChildren(item);
269      foreach (var node in children)
270        if (node != null && itemTreeViewItemMapping.ContainsKey(node))
271          RemoveTreeViewItem(itemTreeViewItemMapping[node]);
272      treeViewItem.Remove();
273    }
274
275    protected void RemoveTreeViewItem(TreeNode treeViewNode) {
276      var queue = new Queue<TreeNode>(new[] { treeViewNode });
277      treeViewNode.Remove();
278      while (queue.Count > 0) {
279        treeViewNode = queue.Dequeue();
280        T item = treeViewNode.Tag as T;
281        if (item != null) {
282          treeViewChildrenMapping.Remove(item);
283          itemTreeViewItemMapping.Remove(item);
284          DeregisterItemEvents(item);
285        }
286        var children = GetChildren(item);
287        foreach (var node in children)
288          if (node != null && itemTreeViewItemMapping.ContainsKey(node)) queue.Enqueue(itemTreeViewItemMapping[node]);
289      }
290    }
291    #endregion
292
293    protected virtual void UpdateTreeViewItemImage(TreeNode treeViewNode) {
294      if (treeViewNode == null) throw new ArgumentNullException();
295      var item = treeViewNode.Tag as T;
296      var i = treeViewNode.ImageIndex;
297      ItemsTreeView.ImageList.Images[i] = item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage;
298      treeViewNode.ImageIndex = i;
299      treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
300    }
301    protected virtual void UpdateTreeViewItemText(TreeNode treeViewNode) {
302      if (treeViewNode == null) throw new ArgumentNullException();
303      var item = treeViewNode.Tag as T;
304      treeViewNode.Text = item == null ? "null" : item.ToString();
305      treeViewNode.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription;
306    }
307    protected virtual TreeNode GetTreeNodeForItem(T item) {
308      if (item == null)
309        return GetAllNodes(ItemsTreeView).First(x => x.Tag == null);
310      TreeNode viewItem;
311      itemTreeViewItemMapping.TryGetValue(item, out viewItem);
312      return viewItem;
313    }
314
315    #region View Events
316    protected virtual void itemsTreeView_SelectedIndexChanged(object sender, EventArgs e) {
317      removeButton.Enabled = (Content != null) && !Content.IsReadOnly && !ReadOnly && ItemsTreeView.SelectedNode != null;
318      if (!showDetailsCheckBox.Checked) return;
319      var item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
320      detailsGroupBox.Enabled = true;
321      viewHost.Content = item;
322    }
323    protected virtual void itemsTreeView_KeyDown(object sender, KeyEventArgs e) {
324      if (e.KeyCode == Keys.Delete) {
325        if ((ItemsTreeView.SelectedNode != null) && !Content.IsReadOnly && !ReadOnly) {
326          var selected = GetAllNodes(ItemsTreeView.SelectedNode);
327          if (ItemCollection != null) ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag));
328          else {
329            ItemsTreeView.BeginUpdate();
330            foreach (var item in selected) Content.Remove((T)item.Tag);
331            ItemsTreeView.EndUpdate();
332          }
333        }
334      }
335    }
336    protected virtual void itemsTreeView_DoubleClick(object sender, EventArgs e) {
337      T item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
338      if (item == null) return;
339      IContentView view = MainFormManager.MainForm.ShowContent(item);
340      if (view == null) return;
341      view.ReadOnly = ReadOnly;
342      view.Locked = Locked;
343    }
344    #endregion
345
346    #region Button Events
347    protected virtual void addButton_Click(object sender, EventArgs e) {
348      T item = CreateItem();
349      if (item != null) Content.Add(item);
350    }
351    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
352      SortItemsTreeView();
353    }
354    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) { //DO nothing for Treeview (no custom sort order possible)
355    }
356    protected virtual void removeButton_Click(object sender, EventArgs e) {
357      if (ItemsTreeView.SelectedNode == null) return;
358      var selected = GetAllNodes(ItemsTreeView.SelectedNode);
359      if (ItemCollection != null) {
360        ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag));
361      } else {
362        foreach (TreeNode item in selected)
363          Content.Remove((T)item.Tag);
364      }
365      ItemsTreeView.SelectedNode = null;
366    }
367    #endregion
368
369    #region CheckBox Events
370    protected virtual void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) {
371      if (showDetailsCheckBox.Checked) {
372        splitContainer.Panel2Collapsed = false;
373        detailsGroupBox.Enabled = ItemsTreeView.SelectedNode != null;
374        viewHost.Content = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
375      } else {
376        splitContainer.Panel2Collapsed = true;
377        viewHost.Content = null;
378      }
379    }
380    #endregion
381
382    #region Content Events
383    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<T> e) {
384      if (InvokeRequired)
385        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsAdded), sender, e);
386      else {
387        ItemsTreeView.BeginUpdate();
388        foreach (T item in e.Items) AddTreeNodeRecursive(item, null);
389        RebuildTreeImageList();
390        ItemsTreeView.EndUpdate();
391      }
392
393    }
394    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<T> e) {
395      if (InvokeRequired)
396        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsRemoved), sender, e);
397      else {
398        ItemsTreeView.BeginUpdate();
399        foreach (T item in e.Items) {
400          var treeNode = GetTreeNodeForItem(item);
401          if (treeNode != null) RemoveTreeViewItem(treeNode);
402        }
403        RebuildTreeImageList();
404        ItemsTreeView.EndUpdate();
405      }
406    }
407    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<T> e) {
408      if (InvokeRequired)
409        Invoke(new CollectionItemsChangedEventHandler<T>(Content_CollectionReset), sender, e);
410      else {
411        ItemsTreeView.BeginUpdate();
412        foreach (T item in e.OldItems) {
413          var treeNode = GetTreeNodeForItem(item);
414          if (treeNode != null) RemoveTreeViewItem(treeNode);
415        }
416        RebuildTreeImageList();
417        foreach (T item in e.Items)
418          AddTreeNodeRecursive(item, null);
419        ItemsTreeView.EndUpdate();
420      }
421    }
422    #endregion
423
424    #region Item Events
425    protected virtual void Item_ItemImageChanged(object sender, EventArgs e) {
426      if (InvokeRequired)
427        Invoke(new EventHandler(Item_ItemImageChanged), sender, e);
428      else {
429        var node = GetTreeNodeForItem(sender as T);
430        if (node != null) UpdateTreeViewItemImage(node);
431      }
432    }
433    protected virtual void Item_ToStringChanged(object sender, EventArgs e) {
434      if (InvokeRequired)
435        Invoke(new EventHandler(Item_ToStringChanged), sender, e);
436      else {
437        var item = sender as T;
438        var itemNode = GetTreeNodeForItem(item);
439        if (itemNode == null) return; //nothing to remove
440        var parent = itemNode.Parent;
441        ItemsTreeView.BeginUpdate();
442        UpdateTreeNodeRecursive(item, parent);
443        UpdateTreeViewItemText(itemNode);
444        ItemsTreeView.EndUpdate();
445      }
446    }
447    #endregion
448
449    #region Helpers
450    protected virtual void SortItemsTreeView() {
451      ItemsTreeView.Sort();
452      ItemsTreeView.Sorted = true;
453    }
454    protected virtual void RebuildTreeImageList() {
455      if (ItemsTreeView.ImageList == null) ItemsTreeView.ImageList = new ImageList();
456      ItemsTreeView.ImageList.Images.Clear();
457      foreach (TreeNode treeNode in GetAllNodes(ItemsTreeView)) {
458        T item = treeNode.Tag as T;
459        try {
460          ItemsTreeView.ImageList.Images.Add(item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage);
461          treeNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
462          treeNode.SelectedImageIndex = treeNode.ImageIndex;
463        }
464        catch (ArgumentException) {
465          treeNode.SelectedImageIndex = ItemsTreeView.ImageList.Images.Count + 10000;
466        }
467      }
468    }
469    protected List<TreeNode> GetAllNodes(TreeView view) {
470      var n = new List<TreeNode>();
471      foreach (TreeNode node in view.Nodes) {
472        AddAllNodes(n, node);
473      }
474      return n;
475    }
476    private List<TreeNode> GetAllNodes(TreeNode node) {
477      var n = new List<TreeNode>();
478      AddAllNodes(n, node);
479      return n;
480    }
481    private static void AddAllNodes(List<TreeNode> list, TreeNode node) {
482      var queue = new Queue<TreeNode>(new[] { node });
483      while (queue.Count > 0) {
484        node = queue.Dequeue();
485        if (!list.Contains(node)) list.Add(node);
486        foreach (TreeNode n in node.Nodes)
487          queue.Enqueue(n);
488      }
489    }
490    protected T GetParent(T child) {
491      return treeViewChildrenMapping.ContainsKey(child) ? treeViewChildrenMapping[child] : null;
492    }
493    private static IItem GetValue(object parent) {
494      var props = parent.GetType().GetProperties();
495      foreach (var prop in props) {
496        if (prop.Name.Equals("Value", StringComparison.InvariantCultureIgnoreCase) || prop.Name.Equals("ActualValue", StringComparison.InvariantCultureIgnoreCase)) {
497          if (typeof(IItem).IsAssignableFrom(prop.PropertyType)) {
498            try { return prop.GetValue(parent) as IItem; }
499            catch (TargetInvocationException) { }  //if getting the value is not an option (LookUpParameters,...) try other properties
500          }
501        }
502      }
503      return null;
504    }
505    protected List<T> GetChildren(T parent) {
506      return treeViewChildrenMapping.Where(x => x.Value == parent).Select(x => x.Key).ToList();
507    }
508    #endregion
509  }
510}
Note: See TracBrowser for help on using the repository browser.