Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2684 initial commit of TreeView

File size: 20.5 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
65    public TreeView ItemsTreeView { get { return itemsTreeView; } }
66
67    #endregion
68
69    public ItemCollectionTreeView() {
70      InitializeComponent();
71      itemTreeViewItemMapping = new Dictionary<T, TreeNode>();
72      treeViewChildrenMapping = new Dictionary<T, T>();
73    }
74
75    protected override void Dispose(bool disposing) {
76      if (disposing) {
77        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
78        if (components != null) components.Dispose();
79      }
80      base.Dispose(disposing);
81    }
82
83    #region eventregistration
84
85    protected override void DeregisterContentEvents() {
86      Content.ItemsAdded -= Content_ItemsAdded;
87      Content.ItemsRemoved -= Content_ItemsRemoved;
88      Content.CollectionReset -= Content_CollectionReset;
89      base.DeregisterContentEvents();
90    }
91
92    protected override void RegisterContentEvents() {
93      DeregisterContentEvents(); //prevent multiattaching
94      base.RegisterContentEvents();
95      Content.ItemsAdded += Content_ItemsAdded;
96      Content.ItemsRemoved += Content_ItemsRemoved;
97      Content.CollectionReset += Content_CollectionReset;
98    }
99
100    protected virtual void DeregisterItemEvents(T item) {
101      item.ItemImageChanged -= Item_ItemImageChanged;
102      item.ToStringChanged -= Item_ToStringChanged;
103    }
104
105    protected virtual void RegisterItemEvents(T item) {
106      DeregisterItemEvents(item); //prevent multiattaching
107      item.ItemImageChanged += Item_ItemImageChanged;
108      item.ToStringChanged += Item_ToStringChanged;
109    }
110
111    protected virtual void DeregisterViewEvents() {
112      ItemsTreeView.AfterSelect -= itemsTreeView_SelectedIndexChanged;
113      ItemsTreeView.DoubleClick -= itemsTreeView_DoubleClick;
114      ItemsTreeView.KeyDown -= itemsTreeView_KeyDown;
115    }
116
117    protected virtual void RegisterViewEvents() {
118      DeregisterViewEvents(); //prevent multiattaching
119      ItemsTreeView.AfterSelect += itemsTreeView_SelectedIndexChanged;
120      ItemsTreeView.DoubleClick += itemsTreeView_DoubleClick;
121      ItemsTreeView.KeyDown += itemsTreeView_KeyDown;
122    }
123
124    protected override void OnContentChanged() {
125      ItemsTreeView.BeginUpdate();
126      base.OnContentChanged();
127      ItemsTreeView.Nodes.Clear();
128      itemTreeViewItemMapping.Clear();
129      RebuildTreeImageList();
130      viewHost.Content = null;
131      if (Content == null) { ItemsTreeView.EndUpdate(); return; }
132      RegisterViewEvents();
133      Caption += " (" + Content.GetType().Name + ")";
134
135      foreach (T item in Content)
136        AddTreeNodeRecursive(item, null);
137      SortItemsTreeView();
138      ItemsTreeView.EndUpdate();
139    }
140
141    #endregion
142
143    protected override void SetEnabledStateOfControls() {
144      base.SetEnabledStateOfControls();
145      if (Content == null) {
146        addButton.Enabled = false;
147        sortAscendingButton.Enabled = false;
148        sortDescendingButton.Enabled = false;
149        removeButton.Enabled = false;
150        ItemsTreeView.Enabled = false;
151        detailsGroupBox.Enabled = false;
152      } else {
153        addButton.Enabled = !Content.IsReadOnly && !ReadOnly;
154        sortAscendingButton.Enabled = false;
155        sortDescendingButton.Enabled = false;
156        removeButton.Enabled = !Content.IsReadOnly && !ReadOnly && ItemsTreeView.Nodes.Count > 0;
157        ItemsTreeView.Enabled = true;
158        detailsGroupBox.Enabled = true;
159      }
160    }
161
162    protected virtual T CreateItem() {
163      if (typeSelectorDialog == null) {
164        typeSelectorDialog = new TypeSelectorDialog();
165        typeSelectorDialog.Caption = "Select Item";
166        typeSelectorDialog.TypeSelector.Caption = "Available Items";
167        typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true);
168      }
169      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
170        try {
171          return (T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
172        }
173        catch (Exception ex) {
174          ErrorHandling.ShowErrorDialog(this, ex);
175        }
176      }
177      return null;
178    }
179
180    protected virtual TreeNode CreateTreeViewNode(T item) {
181      var treeViewNode = new TreeNode("noName");
182      treeViewNode.NodeFont = DefaultFont;
183      if (item == null) {
184        treeViewNode.Text = "null";
185        ItemsTreeView.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Nothing);
186        treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
187        treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
188      } else {
189        treeViewNode.Text = item.ToString();
190        treeViewNode.ToolTipText = item.ItemName + ": " + item.ItemDescription;
191        ItemsTreeView.ImageList.Images.Add(item.ItemImage);
192        treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
193        treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
194        treeViewNode.Tag = item;
195      }
196      return treeViewNode;
197    }
198
199    #region recursive adding
200
201    protected virtual void AddTreeNode(T item) {
202      if (item != null && !itemTreeViewItemMapping.ContainsKey(item) && treeViewChildrenMapping.ContainsKey(item)) { //create TreeNode if not exists
203        RegisterItemEvents(item);
204        var node = CreateTreeViewNode(item);
205        itemTreeViewItemMapping[item] = node;
206        var p = GetParent(item);
207        (p == null ? ItemsTreeView.Nodes : itemTreeViewItemMapping[p].Nodes).Add(node);
208      }
209
210    }
211    protected void AddTreeNodeRecursive(T item, TreeNode parent) {
212      if (item == null) return;
213      Queue<T> queue = new Queue<T>();
214      queue.Enqueue(item);
215      if (!treeViewChildrenMapping.ContainsKey(item))
216        treeViewChildrenMapping.Add(item, parent != null ? parent.Tag as T : null); //Set explicitly to null in some cases. who cares??
217      while (queue.Count > 0 && (item = queue.Dequeue()) != null) {
218        AddTreeNode(item);
219        foreach (var child in ExpandTreeViewItem(item)) { //Expand queue
220          if (!treeViewChildrenMapping.ContainsKey(child)) {
221            treeViewChildrenMapping.Add(child, item);
222          }
223          queue.Enqueue(child);
224        }
225      }
226    }
227
228
229    protected void UpdateTreeNodeRecursive(T item, TreeNode parent) {
230      if (item == null) return;
231      Queue<T> queue = new Queue<T>();
232      queue.Enqueue(item);
233      //parent relation has to exist by now
234      //if (!treeViewChildrenMapping.ContainsKey(item))
235      //treeViewChildrenMapping.Add(item, parent != null ? parent.Tag as T : null); //Set explicitly to null in some cases. who cares??
236      while (queue.Count > 0 && (item = queue.Dequeue()) != null) {
237        AddTreeNode(item);
238        var newChildren = ExpandTreeViewItem(item);
239        var deadChildren =
240          itemTreeViewItemMapping[item].Nodes.Cast<TreeNode>()
241            .Select(n => n.Tag as T)
242            .Where(t => !newChildren.Contains(t)).Select(t => itemTreeViewItemMapping[t])
243            .ToList();
244
245        foreach (var child in newChildren) { //Expand queue
246          if (!treeViewChildrenMapping.ContainsKey(child)) {
247            treeViewChildrenMapping.Add(child, item);
248          }
249          queue.Enqueue(child);
250        }
251
252        foreach (var child in deadChildren) {
253          RemoveTreeViewItem(child);
254        }
255      }
256    }
257
258
259
260    /// <summary>
261    /// This function defines which subnodes are created.
262    /// Override this to change which items are used in the creation of new nodes
263    /// </summary>
264    /// <param name="parent"></param>
265    /// <returns></returns>
266    protected virtual List<T> ExpandTreeViewItem(object parent) {
267      //unpack enumerables
268      var p = parent as IEnumerable<T>;
269      if (p != null) return p.ToList();
270      //exclude LookupParameters from Value unpacking
271      if (parent is ILookupParameter) return new List<T>();
272      //unpack "value"/"Value"/"ActualValue" relationship if possible
273      object prop = GetValue(parent);
274      //return nothing if failed
275      return (prop != null) ? ExpandTreeViewItem(prop) : new List<T>();
276    }
277    #endregion
278
279    #region recursive removal
280    protected virtual void RemoveTreeViewItem(TreeNode treeViewItem) {
281      if (treeViewItem == null) return;
282      T item = treeViewItem.Tag as T;
283      if (item != null) {
284        treeViewChildrenMapping.Remove(item);
285        itemTreeViewItemMapping.Remove(item);
286        DeregisterItemEvents(item);
287      }
288      var children = GetChildren(item);
289      foreach (var node in children)
290        if (node != null && itemTreeViewItemMapping.ContainsKey(node))
291          RemoveTreeViewItem(itemTreeViewItemMapping[node]);
292      treeViewItem.Remove();
293
294    }
295    protected virtual void UpdateTreeViewItemImage(TreeNode treeViewNode) {
296      if (treeViewNode == null) throw new ArgumentNullException();
297      var item = treeViewNode.Tag as T;
298      var i = treeViewNode.ImageIndex;
299      ItemsTreeView.ImageList.Images[i] = item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage;
300      treeViewNode.ImageIndex = -1;
301      treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
302      treeViewNode.ImageIndex = i;
303    }
304    protected virtual void UpdateTreeViewItemText(TreeNode treeViewNode) {
305      if (treeViewNode == null) throw new ArgumentNullException();
306      T item = treeViewNode.Tag as T;
307      treeViewNode.Text = item == null ? "null" : item.ToString();
308      treeViewNode.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription;
309    }
310    protected virtual TreeNode GetTreeNodeForItem(T item) {
311      if (item == null)
312        return GetAllNodes(ItemsTreeView).First(x => x.Tag == null);
313      TreeNode viewItem;
314      itemTreeViewItemMapping.TryGetValue(item, out viewItem);
315      return viewItem;
316
317    }
318    #endregion
319
320    #region View Events
321    protected virtual void itemsTreeView_SelectedIndexChanged(object sender, EventArgs e) {
322      removeButton.Enabled = (Content != null) && !Content.IsReadOnly && !ReadOnly && ItemsTreeView.SelectedNode != null;
323      if (showDetailsCheckBox.Checked) {
324        var item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
325        detailsGroupBox.Enabled = true;
326        viewHost.Content = item;
327      }
328    }
329    protected virtual void itemsTreeView_KeyDown(object sender, KeyEventArgs e) {
330      if (e.KeyCode == Keys.Delete) {
331        if ((ItemsTreeView.SelectedNode != null) && !Content.IsReadOnly && !ReadOnly) {
332          //no multiselction but recursive deletion
333          var selected = GetAllNodes(ItemsTreeView.SelectedNode);
334          if (ItemCollection != null) ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag));
335          else {
336            ItemsTreeView.BeginUpdate();
337            foreach (TreeNode item in selected) { Content.Remove((T)item.Tag); }
338            ItemsTreeView.EndUpdate();
339          }
340        }
341      }
342    }
343    protected virtual void itemsTreeView_DoubleClick(object sender, EventArgs e) {
344      T item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
345      if (item == null) return;
346      IContentView view = MainFormManager.MainForm.ShowContent(item);
347      if (view == null) return;
348      view.ReadOnly = ReadOnly;
349      view.Locked = Locked;
350    }
351    #endregion
352
353    #region Button Events
354    protected virtual void addButton_Click(object sender, EventArgs e) {
355      T item = CreateItem();
356      if (item != null)
357        Content.Add(item);
358    }
359    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
360      SortItemsTreeView();
361    }
362    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
363      //DO nothing for Treeview (no custom sort order)
364    }
365    protected virtual void removeButton_Click(object sender, EventArgs e) {
366      if (ItemsTreeView.SelectedNode == null) return;
367      var selected = GetAllNodes(ItemsTreeView.SelectedNode);
368      if (ItemCollection != null) {
369        ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag));
370      } else {
371        foreach (TreeNode item in selected)
372          Content.Remove((T)item.Tag);
373      }
374      ItemsTreeView.SelectedNode = null;
375    }
376    #endregion
377
378    #region CheckBox Events
379    protected virtual void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) {
380      if (showDetailsCheckBox.Checked) {
381        splitContainer.Panel2Collapsed = false;
382        detailsGroupBox.Enabled = ItemsTreeView.SelectedNode != null;
383        viewHost.Content = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
384      } else {
385        splitContainer.Panel2Collapsed = true;
386        viewHost.Content = null;
387      }
388    }
389    #endregion
390
391    #region Content Events
392    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<T> e) {
393      if (InvokeRequired)
394        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsAdded), sender, e);
395      else {
396        ItemsTreeView.BeginUpdate();
397        foreach (T item in e.Items) {
398          AddTreeNodeRecursive(item, null);
399        }
400        RebuildTreeImageList();
401        ItemsTreeView.EndUpdate();
402      }
403
404    }
405    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<T> e) {
406      if (InvokeRequired)
407        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsRemoved), sender, e);
408      else {
409        ItemsTreeView.BeginUpdate();
410        foreach (T item in e.Items) {
411          //remove only the first matching ViewItem, because the IItem could be contained multiple times in the ItemCollection
412          var treeNode = GetTreeNodeForItem(item);
413          if (treeNode != null) RemoveTreeViewItem(treeNode);
414
415        }
416        RebuildTreeImageList();
417        ItemsTreeView.EndUpdate();
418      }
419    }
420    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<T> e) {
421      if (InvokeRequired)
422        Invoke(new CollectionItemsChangedEventHandler<T>(Content_CollectionReset), sender, e);
423      else {
424        ItemsTreeView.BeginUpdate();
425        foreach (T item in e.OldItems) {
426          //remove only the first matching ViewItem, because the IItem could be contained multiple times in the ItemCollection
427          var treeNode = GetTreeNodeForItem(item);
428          if (treeNode != null) RemoveTreeViewItem(treeNode);
429        }
430        RebuildTreeImageList();
431        foreach (T item in e.Items)
432          AddTreeNodeRecursive(item, null);
433        ItemsTreeView.EndUpdate();
434      }
435    }
436    #endregion
437
438    #region Item Events
439    protected virtual void Item_ItemImageChanged(object sender, EventArgs e) {
440      if (InvokeRequired)
441        Invoke(new EventHandler(Item_ItemImageChanged), sender, e);
442      else {
443        var item = sender as T;
444        var node = GetTreeNodeForItem(item);
445        if (node != null) { UpdateTreeViewItemImage(node); }
446
447      }
448    }
449    protected virtual void Item_ToStringChanged(object sender, EventArgs e) {
450      if (InvokeRequired)
451        Invoke(new EventHandler(Item_ToStringChanged), sender, e);
452      else {
453        var item = sender as T;
454        var itemNode = GetTreeNodeForItem(item);
455        if (itemNode == null) return; //nothing to remove
456        var parent = itemNode.Parent;
457        //update Node
458        ItemsTreeView.BeginUpdate();
459        //        if (itemNode.Nodes.Count > 0)
460        //          RemoveTreeViewItem(itemNode);
461        //        AddTreeNodeRecursive(item, parent);
462        UpdateTreeNodeRecursive(item, parent);
463        UpdateTreeViewItemText(itemNode);
464        ItemsTreeView.EndUpdate();
465      }
466    }
467    #endregion
468
469    #region Helpers
470    // no sortOrder in TreeView
471    protected virtual void SortItemsTreeView() {
472      ItemsTreeView.Sort();
473      ItemsTreeView.Sorted = true;
474    }
475    // recursive image list rebuilding
476    protected virtual void RebuildTreeImageList() {
477      if (ItemsTreeView.ImageList == null) ItemsTreeView.ImageList = new ImageList();
478      ItemsTreeView.ImageList.Images.Clear();
479      foreach (TreeNode treeNode in GetAllNodes(ItemsTreeView)) {
480        T item = treeNode.Tag as T;
481        try {
482          ItemsTreeView.ImageList.Images.Add(item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage);
483          treeNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
484          treeNode.SelectedImageIndex = treeNode.ImageIndex;
485        }
486        catch (ArgumentException) {
487          treeNode.SelectedImageIndex = ItemsTreeView.ImageList.Images.Count + 10000;
488        }
489      }
490    }
491    protected List<TreeNode> GetAllNodes(TreeView view) {
492      var n = new List<TreeNode>();
493      foreach (TreeNode node in view.Nodes) {
494        AddAllNodes(n, node);
495      }
496      return n;
497    }
498    private List<TreeNode> GetAllNodes(TreeNode node) {
499      var n = new List<TreeNode>();
500      AddAllNodes(n, node);
501      return n;
502    }
503    private static void AddAllNodes(List<TreeNode> list, TreeNode node) {
504      if (list.Contains(node)) return;
505      list.Add(node);
506      foreach (TreeNode n in node.Nodes) {
507        AddAllNodes(list, n);
508      }
509    }
510
511    protected T GetParent(T child) {
512      return treeViewChildrenMapping.ContainsKey(child) ? treeViewChildrenMapping[child] : null;
513    }
514    protected TreeNode GetParent(TreeNode child) {
515      return child.Parent;
516    }
517    private static IItem GetValue(object parent) {
518      var props = parent.GetType().GetProperties();
519      foreach (var prop in props) {
520        if (prop.Name.Equals("Value", StringComparison.InvariantCultureIgnoreCase) || prop.Name.Equals("ActualValue", StringComparison.InvariantCultureIgnoreCase)) {
521          if (typeof(IItem).IsAssignableFrom(prop.PropertyType)) {
522            try { return prop.GetValue(parent) as IItem; }
523            catch (TargetInvocationException) { }  //if getting the value is not an option (LookUpParameters,...) try other properties
524
525          }
526        }
527      }
528      return null;
529    }
530
531    protected List<T> GetChildren(T parent) {
532      return treeViewChildrenMapping.Where(x => x.Value == parent).Select(x => x.Key).ToList();
533    }
534    private int CountAllNodes(TreeNode n) {
535      var nodes = n != null ? n.Nodes : null ?? ItemsTreeView.Nodes;
536      var res = 1;
537      foreach (var node in nodes.Cast<TreeNode>()) {
538        res += CountAllNodes(node);
539      }
540      return res;
541    }
542    #endregion
543  }
544}
Note: See TracBrowser for help on using the repository browser.