Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Core.Views/3.3/ItemCollectionView.cs @ 17118

Last change on this file since 17118 was 17118, checked in by abeham, 5 years ago

#2949: Fixed sorting in ItemCollectionView

  • Sorting was broken in r17007:17008. It worked previously, because AutoResize probably put the control in a different mode (very likely that it incurred some painting). After removing AutoResize the control is now in a different mode where it doesn't actually sort the items. The items are now added in sorted order instead of inserting them in their given order and then sorting them in the view. This should be faster and have the same effect.
File size: 19.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2019 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;
24using System.Collections.Generic;
25using System.Linq;
26using System.Text;
27using System.Windows.Forms;
28using HeuristicLab.Collections;
29using HeuristicLab.Common;
30using HeuristicLab.MainForm;
31using HeuristicLab.PluginInfrastructure;
32
33namespace HeuristicLab.Core.Views {
34  [View("ItemCollection View")]
35  [Content(typeof(ItemCollection<>), true)]
36  [Content(typeof(IItemCollection<>), false)]
37  [Content(typeof(ReadOnlyItemCollection<>), true)]
38  public partial class ItemCollectionView<T> : ItemView where T : class, IItem {
39    protected Dictionary<T, List<ListViewItem>> itemListViewItemMapping;
40    protected TypeSelectorDialog typeSelectorDialog;
41    protected bool validDragOperation;
42
43    public new IItemCollection<T> Content {
44      get { return (IItemCollection<T>)base.Content; }
45      set { base.Content = value; }
46    }
47
48    public ObservableCollection<T> ItemCollection {
49      get { return Content as ObservableCollection<T>; }
50    }
51
52    public bool ShowDetails {
53      get { return showDetailsCheckBox.Checked; }
54      set { showDetailsCheckBox.Checked = value; }
55    }
56
57    public ListView ItemsListView {
58      get { return itemsListView; }
59    }
60
61    public ItemCollectionView() {
62      InitializeComponent();
63      itemListViewItemMapping = new Dictionary<T, List<ListViewItem>>();
64    }
65
66    protected override void Dispose(bool disposing) {
67      if (disposing) {
68        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
69        if (components != null) components.Dispose();
70      }
71      base.Dispose(disposing);
72    }
73
74    protected override void DeregisterContentEvents() {
75      Content.ItemsAdded -= new CollectionItemsChangedEventHandler<T>(Content_ItemsAdded);
76      Content.ItemsRemoved -= new CollectionItemsChangedEventHandler<T>(Content_ItemsRemoved);
77      Content.CollectionReset -= new CollectionItemsChangedEventHandler<T>(Content_CollectionReset);
78      foreach (T item in itemListViewItemMapping.Keys) {
79        DeregisterItemEvents(item);
80      }
81      base.DeregisterContentEvents();
82    }
83    protected override void RegisterContentEvents() {
84      base.RegisterContentEvents();
85      Content.ItemsAdded += new CollectionItemsChangedEventHandler<T>(Content_ItemsAdded);
86      Content.ItemsRemoved += new CollectionItemsChangedEventHandler<T>(Content_ItemsRemoved);
87      Content.CollectionReset += new CollectionItemsChangedEventHandler<T>(Content_CollectionReset);
88    }
89    protected virtual void DeregisterItemEvents(T item) {
90      item.ItemImageChanged -= new EventHandler(Item_ItemImageChanged);
91      item.ToStringChanged -= new EventHandler(Item_ToStringChanged);
92    }
93    protected virtual void RegisterItemEvents(T item) {
94      item.ItemImageChanged += new EventHandler(Item_ItemImageChanged);
95      item.ToStringChanged += new EventHandler(Item_ToStringChanged);
96    }
97
98    protected override void OnContentChanged() {
99      base.OnContentChanged();
100      itemsListView.Items.Clear();
101      itemListViewItemMapping.Clear();
102      RebuildImageList();
103      viewHost.Content = null;
104      if (Content != null) {
105        Caption += " (" + Content.GetType().Name + ")";
106        foreach (T item in Content.OrderBy(x => x.ToString()))
107          AddListViewItem(CreateListViewItem(item));
108        AdjustListViewColumnSizes();
109      }
110    }
111
112    protected override void SetEnabledStateOfControls() {
113      base.SetEnabledStateOfControls();
114      if (Content == null) {
115        addButton.Enabled = false;
116        sortAscendingButton.Enabled = false;
117        sortDescendingButton.Enabled = false;
118        removeButton.Enabled = false;
119        itemsListView.Enabled = false;
120        detailsGroupBox.Enabled = false;
121      } else {
122        addButton.Enabled = !Content.IsReadOnly && !ReadOnly;
123        sortAscendingButton.Enabled = itemsListView.Items.Count > 1;
124        sortDescendingButton.Enabled = itemsListView.Items.Count > 1;
125        removeButton.Enabled = !Content.IsReadOnly && !ReadOnly && itemsListView.SelectedItems.Count > 0;
126        itemsListView.Enabled = true;
127        detailsGroupBox.Enabled = itemsListView.SelectedItems.Count == 1;
128      }
129    }
130
131    protected virtual T CreateItem() {
132      if (typeSelectorDialog == null) {
133        typeSelectorDialog = new TypeSelectorDialog();
134        typeSelectorDialog.Caption = "Select Item";
135        typeSelectorDialog.TypeSelector.Caption = "Available Items";
136        typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true);
137      }
138
139      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
140        try {
141          return (T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
142        } catch (Exception ex) {
143          ErrorHandling.ShowErrorDialog(this, ex);
144        }
145      }
146      return null;
147    }
148    protected virtual ListViewItem CreateListViewItem(T item) {
149      ListViewItem listViewItem = new ListViewItem();
150      if (item == null) {
151        listViewItem.Text = "null";
152        itemsListView.SmallImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Nothing);
153        listViewItem.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
154      } else {
155        listViewItem.Text = item.ToString();
156        listViewItem.ToolTipText = item.ItemName + ": " + item.ItemDescription;
157        itemsListView.SmallImageList.Images.Add(item.ItemImage);
158        listViewItem.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
159        listViewItem.Tag = item;
160      }
161      return listViewItem;
162    }
163    protected virtual void AddListViewItem(ListViewItem listViewItem) {
164      if (listViewItem == null) throw new ArgumentNullException();
165      T item = (listViewItem.Tag as T);
166      itemsListView.Items.Add(listViewItem);
167      if (item != null) {
168        if (!itemListViewItemMapping.ContainsKey(item)) {
169          RegisterItemEvents(item);
170          itemListViewItemMapping.Add(item, new List<ListViewItem>());
171        }
172        itemListViewItemMapping[item].Add(listViewItem);
173      }
174      sortAscendingButton.Enabled = itemsListView.Items.Count > 1;
175      sortDescendingButton.Enabled = itemsListView.Items.Count > 1;
176    }
177    protected virtual void RemoveListViewItem(ListViewItem listViewItem) {
178      if (listViewItem == null) throw new ArgumentNullException();
179      T item = (listViewItem.Tag as T);
180      if (item != null) {
181        itemListViewItemMapping[item].Remove(listViewItem);
182        if (itemListViewItemMapping[item].Count == 0) {
183          itemListViewItemMapping.Remove(item);
184          DeregisterItemEvents(item);
185        }
186      }
187      listViewItem.Remove();
188      sortAscendingButton.Enabled = itemsListView.Items.Count > 1;
189      sortDescendingButton.Enabled = itemsListView.Items.Count > 1;
190    }
191    protected virtual void UpdateListViewItemImage(ListViewItem listViewItem) {
192      if (listViewItem == null) throw new ArgumentNullException();
193      T item = listViewItem.Tag as T;
194      int i = listViewItem.ImageIndex;
195      itemsListView.SmallImageList.Images[i] = item == null ? HeuristicLab.Common.Resources.VSImageLibrary.Nothing : item.ItemImage;
196      listViewItem.ImageIndex = -1;
197      listViewItem.ImageIndex = i;
198    }
199    protected virtual void UpdateListViewItemText(ListViewItem listViewItem) {
200      if (listViewItem == null) throw new ArgumentNullException();
201      T item = listViewItem.Tag as T;
202      listViewItem.Text = item == null ? "null" : item.ToString();
203      listViewItem.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription;
204    }
205    protected virtual IEnumerable<ListViewItem> GetListViewItemsForItem(T item) {
206      if (item == null) {
207        List<ListViewItem> listViewItems = new List<ListViewItem>();
208        foreach (ListViewItem listViewItem in itemsListView.Items) {
209          if (listViewItem.Tag == null) listViewItems.Add(listViewItem);
210        }
211        return listViewItems;
212      } else {
213        List<ListViewItem> listViewItems = null;
214        itemListViewItemMapping.TryGetValue(item, out listViewItems);
215        return listViewItems == null ? Enumerable.Empty<ListViewItem>() : listViewItems;
216      }
217    }
218
219    #region ListView Events
220    protected virtual void itemsListView_SelectedIndexChanged(object sender, EventArgs e) {
221      removeButton.Enabled = (Content != null) && !Content.IsReadOnly && !ReadOnly && itemsListView.SelectedItems.Count > 0;
222      if (showDetailsCheckBox.Checked) {
223        if (itemsListView.SelectedItems.Count == 1) {
224          T item = (T)itemsListView.SelectedItems[0].Tag;
225          detailsGroupBox.Enabled = true;
226          viewHost.Content = item;
227        } else {
228          viewHost.Content = null;
229          detailsGroupBox.Enabled = false;
230        }
231      }
232    }
233    protected virtual void itemsListView_KeyDown(object sender, KeyEventArgs e) {
234      if (e.KeyCode == Keys.Delete) {
235        if ((itemsListView.SelectedItems.Count > 0) && !Content.IsReadOnly && !ReadOnly) {
236          if (ItemCollection != null) ItemCollection.RemoveRange(itemsListView.SelectedItems.Cast<ListViewItem>().Select(i => (T)i.Tag));
237          else {
238            foreach (ListViewItem item in itemsListView.SelectedItems)
239              Content.Remove((T)item.Tag);
240          }
241        }
242      } else if (e.KeyData == (Keys.Control | Keys.C)) {
243        if (itemsListView.SelectedItems.Count > 0) {
244          var builder = new StringBuilder();
245          foreach (ListViewItem selected in itemsListView.SelectedItems) {
246            builder.AppendLine(selected.Text);
247          }
248          Clipboard.SetText(builder.ToString());
249        }
250      }
251    }
252    protected virtual void itemsListView_DoubleClick(object sender, EventArgs e) {
253      if (itemsListView.SelectedItems.Count == 1) {
254        T item = itemsListView.SelectedItems[0].Tag as T;
255        if (item != null) {
256          IContentView view = MainFormManager.MainForm.ShowContent(item);
257          if (view != null) {
258            view.ReadOnly = ReadOnly;
259            view.Locked = Locked;
260          }
261        }
262      }
263    }
264    protected virtual void itemsListView_ItemDrag(object sender, ItemDragEventArgs e) {
265      if (!Locked) {
266        List<T> items = new List<T>();
267        foreach (ListViewItem listViewItem in itemsListView.SelectedItems) {
268          T item = listViewItem.Tag as T;
269          if (item != null) items.Add(item);
270        }
271
272        if (items.Count > 0) {
273          DataObject data = new DataObject();
274          if (items.Count == 1) data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, items[0]);
275          else data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, items);
276          if (Content.IsReadOnly || ReadOnly) {
277            DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link);
278          } else {
279            DragDropEffects result = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
280            if ((result & DragDropEffects.Move) == DragDropEffects.Move) {
281              foreach (T item in items) Content.Remove(item);
282            }
283          }
284        }
285      }
286    }
287    protected virtual void itemsListView_DragEnter(object sender, DragEventArgs e) {
288      validDragOperation = false;
289      if (!Content.IsReadOnly && !ReadOnly && (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is T)) {
290        validDragOperation = true;
291      } else if (!Content.IsReadOnly && !ReadOnly && (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is IEnumerable)) {
292        validDragOperation = true;
293        IEnumerable items = (IEnumerable)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
294        foreach (object item in items)
295          validDragOperation = validDragOperation && (item is T);
296      }
297    }
298    protected virtual void itemsListView_DragOver(object sender, DragEventArgs e) {
299      e.Effect = DragDropEffects.None;
300      if (validDragOperation) {
301        if ((e.KeyState & 32) == 32) e.Effect = DragDropEffects.Link;  // ALT key
302        else if ((e.KeyState & 4) == 4) e.Effect = DragDropEffects.Move;  // SHIFT key
303        else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
304        else if (e.AllowedEffect.HasFlag(DragDropEffects.Move)) e.Effect = DragDropEffects.Move;
305        else if (e.AllowedEffect.HasFlag(DragDropEffects.Link)) e.Effect = DragDropEffects.Link;
306      }
307    }
308    protected virtual void itemsListView_DragDrop(object sender, DragEventArgs e) {
309      if (e.Effect != DragDropEffects.None) {
310        if (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is T) {
311          T item = (T)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
312          Content.Add(e.Effect.HasFlag(DragDropEffects.Copy) ? (T)item.Clone() : item);
313        } else if (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is IEnumerable) {
314          IEnumerable<T> items = ((IEnumerable)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat)).Cast<T>();
315          if (e.Effect.HasFlag(DragDropEffects.Copy)) {
316            Cloner cloner = new Cloner();
317            items = items.Select(x => cloner.Clone(x));
318          }
319          if (ItemCollection != null) ItemCollection.AddRange(items);
320          else {
321            foreach (T item in items)
322              Content.Add(item);
323          }
324        }
325      }
326    }
327    protected virtual void itemsListView_Layout(object sender, LayoutEventArgs e) {
328      if (itemsListView.Columns.Count == 1)
329        AdjustListViewColumnSizes();
330    }
331    #endregion
332
333    #region Button Events
334    protected virtual void addButton_Click(object sender, EventArgs e) {
335      T item = CreateItem();
336      if (item != null)
337        Content.Add(item);
338    }
339    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
340      SortItemsListView(SortOrder.Ascending);
341    }
342    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
343      SortItemsListView(SortOrder.Descending);
344    }
345    protected virtual void removeButton_Click(object sender, EventArgs e) {
346      if (itemsListView.SelectedItems.Count > 0) {
347        if (ItemCollection != null) {
348          ItemCollection.RemoveRange(itemsListView.SelectedItems.Cast<ListViewItem>().Select(i => (T)i.Tag));
349        } else {
350          foreach (ListViewItem item in itemsListView.SelectedItems)
351            Content.Remove((T)item.Tag);
352        }
353        itemsListView.SelectedItems.Clear();
354      }
355    }
356    #endregion
357
358    #region CheckBox Events
359    protected virtual void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) {
360      if (showDetailsCheckBox.Checked) {
361        splitContainer.Panel2Collapsed = false;
362        detailsGroupBox.Enabled = itemsListView.SelectedItems.Count == 1;
363        viewHost.Content = itemsListView.SelectedItems.Count == 1 ? (T)itemsListView.SelectedItems[0].Tag : null;
364      } else {
365        splitContainer.Panel2Collapsed = true;
366        viewHost.Content = null;
367      }
368    }
369    #endregion
370
371    #region Content Events
372    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<T> e) {
373      if (InvokeRequired)
374        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsAdded), sender, e);
375      else {
376        foreach (T item in e.Items)
377          AddListViewItem(CreateListViewItem(item));
378        AdjustListViewColumnSizes();
379      }
380
381    }
382    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<T> e) {
383      if (InvokeRequired)
384        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsRemoved), sender, e);
385      else {
386        foreach (T item in e.Items) {
387          //remove only the first matching ListViewItem, because the IItem could be contained multiple times in the ItemCollection
388          ListViewItem listviewItem = GetListViewItemsForItem(item).FirstOrDefault();
389          if (listviewItem != null) RemoveListViewItem(listviewItem);
390        }
391        RebuildImageList();
392      }
393    }
394    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<T> e) {
395      if (InvokeRequired)
396        Invoke(new CollectionItemsChangedEventHandler<T>(Content_CollectionReset), sender, e);
397      else {
398        foreach (T item in e.OldItems) {
399          //remove only the first matching ListViewItem, because the IItem could be contained multiple times in the ItemCollection
400          ListViewItem listviewItem = GetListViewItemsForItem(item).FirstOrDefault();
401          if (listviewItem != null) RemoveListViewItem(listviewItem);
402        }
403        RebuildImageList();
404        foreach (T item in e.Items)
405          AddListViewItem(CreateListViewItem(item));
406      }
407    }
408    #endregion
409
410    #region Item Events
411    protected virtual void Item_ItemImageChanged(object sender, EventArgs e) {
412      if (InvokeRequired)
413        Invoke(new EventHandler(Item_ItemImageChanged), sender, e);
414      else {
415        T item = (T)sender;
416        foreach (ListViewItem listViewItem in GetListViewItemsForItem(item))
417          UpdateListViewItemImage(listViewItem);
418      }
419    }
420    protected virtual void Item_ToStringChanged(object sender, EventArgs e) {
421      if (InvokeRequired)
422        Invoke(new EventHandler(Item_ToStringChanged), sender, e);
423      else {
424        T item = (T)sender;
425        foreach (ListViewItem listViewItem in GetListViewItemsForItem(item))
426          UpdateListViewItemText(listViewItem);
427        if (itemsListView.Columns.Count > 1)
428          AdjustListViewColumnSizes();
429      }
430    }
431    #endregion
432
433    #region Helpers
434    protected virtual void SortItemsListView(SortOrder sortOrder) {
435      itemsListView.Sorting = SortOrder.None;
436      itemsListView.Sorting = sortOrder;
437      itemsListView.Sorting = SortOrder.None;
438    }
439    protected virtual void AdjustListViewColumnSizes() {
440      if (itemsListView.Columns.Count == 1) {
441        if (itemsListView.Columns[0].Width != itemsListView.ClientSize.Width)
442          itemsListView.Columns[0].Width = itemsListView.ClientSize.Width;
443      } else {
444        if (itemsListView.Items.Count > 0) {
445          for (int i = 0; i < itemsListView.Columns.Count; i++)
446            itemsListView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
447        }
448      }
449    }
450    protected virtual void RebuildImageList() {
451      itemsListView.SmallImageList.Images.Clear();
452      foreach (ListViewItem listViewItem in itemsListView.Items) {
453        T item = listViewItem.Tag as T;
454        itemsListView.SmallImageList.Images.Add(item == null ? HeuristicLab.Common.Resources.VSImageLibrary.Nothing : item.ItemImage);
455        listViewItem.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
456      }
457    }
458    #endregion
459  }
460}
Note: See TracBrowser for help on using the repository browser.