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

Last change on this file since 17008 was 17008, checked in by abeham, 16 months ago

#2949: Disable autosizing when there is only one column and use width of list view as size of column. In addition no update occurs on ToStringChanged when there is only one column (most common case)

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)
107          AddListViewItem(CreateListViewItem(item));
108        AdjustListViewColumnSizes();
109        SortItemsListView(SortOrder.Ascending);
110      }
111    }
112
113    protected override void SetEnabledStateOfControls() {
114      base.SetEnabledStateOfControls();
115      if (Content == null) {
116        addButton.Enabled = false;
117        sortAscendingButton.Enabled = false;
118        sortDescendingButton.Enabled = false;
119        removeButton.Enabled = false;
120        itemsListView.Enabled = false;
121        detailsGroupBox.Enabled = false;
122      } else {
123        addButton.Enabled = !Content.IsReadOnly && !ReadOnly;
124        sortAscendingButton.Enabled = itemsListView.Items.Count > 1;
125        sortDescendingButton.Enabled = itemsListView.Items.Count > 1;
126        removeButton.Enabled = !Content.IsReadOnly && !ReadOnly && itemsListView.SelectedItems.Count > 0;
127        itemsListView.Enabled = true;
128        detailsGroupBox.Enabled = itemsListView.SelectedItems.Count == 1;
129      }
130    }
131
132    protected virtual T CreateItem() {
133      if (typeSelectorDialog == null) {
134        typeSelectorDialog = new TypeSelectorDialog();
135        typeSelectorDialog.Caption = "Select Item";
136        typeSelectorDialog.TypeSelector.Caption = "Available Items";
137        typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true);
138      }
139
140      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
141        try {
142          return (T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
143        } catch (Exception ex) {
144          ErrorHandling.ShowErrorDialog(this, ex);
145        }
146      }
147      return null;
148    }
149    protected virtual ListViewItem CreateListViewItem(T item) {
150      ListViewItem listViewItem = new ListViewItem();
151      if (item == null) {
152        listViewItem.Text = "null";
153        itemsListView.SmallImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Nothing);
154        listViewItem.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
155      } else {
156        listViewItem.Text = item.ToString();
157        listViewItem.ToolTipText = item.ItemName + ": " + item.ItemDescription;
158        itemsListView.SmallImageList.Images.Add(item.ItemImage);
159        listViewItem.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
160        listViewItem.Tag = item;
161      }
162      return listViewItem;
163    }
164    protected virtual void AddListViewItem(ListViewItem listViewItem) {
165      if (listViewItem == null) throw new ArgumentNullException();
166      T item = (listViewItem.Tag as T);
167      itemsListView.Items.Add(listViewItem);
168      if (item != null) {
169        if (!itemListViewItemMapping.ContainsKey(item)) {
170          RegisterItemEvents(item);
171          itemListViewItemMapping.Add(item, new List<ListViewItem>());
172        }
173        itemListViewItemMapping[item].Add(listViewItem);
174      }
175      sortAscendingButton.Enabled = itemsListView.Items.Count > 1;
176      sortDescendingButton.Enabled = itemsListView.Items.Count > 1;
177    }
178    protected virtual void RemoveListViewItem(ListViewItem listViewItem) {
179      if (listViewItem == null) throw new ArgumentNullException();
180      T item = (listViewItem.Tag as T);
181      if (item != null) {
182        itemListViewItemMapping[item].Remove(listViewItem);
183        if (itemListViewItemMapping[item].Count == 0) {
184          itemListViewItemMapping.Remove(item);
185          DeregisterItemEvents(item);
186        }
187      }
188      listViewItem.Remove();
189      sortAscendingButton.Enabled = itemsListView.Items.Count > 1;
190      sortDescendingButton.Enabled = itemsListView.Items.Count > 1;
191    }
192    protected virtual void UpdateListViewItemImage(ListViewItem listViewItem) {
193      if (listViewItem == null) throw new ArgumentNullException();
194      T item = listViewItem.Tag as T;
195      int i = listViewItem.ImageIndex;
196      itemsListView.SmallImageList.Images[i] = item == null ? HeuristicLab.Common.Resources.VSImageLibrary.Nothing : item.ItemImage;
197      listViewItem.ImageIndex = -1;
198      listViewItem.ImageIndex = i;
199    }
200    protected virtual void UpdateListViewItemText(ListViewItem listViewItem) {
201      if (listViewItem == null) throw new ArgumentNullException();
202      T item = listViewItem.Tag as T;
203      listViewItem.Text = item == null ? "null" : item.ToString();
204      listViewItem.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription;
205    }
206    protected virtual IEnumerable<ListViewItem> GetListViewItemsForItem(T item) {
207      if (item == null) {
208        List<ListViewItem> listViewItems = new List<ListViewItem>();
209        foreach (ListViewItem listViewItem in itemsListView.Items) {
210          if (listViewItem.Tag == null) listViewItems.Add(listViewItem);
211        }
212        return listViewItems;
213      } else {
214        List<ListViewItem> listViewItems = null;
215        itemListViewItemMapping.TryGetValue(item, out listViewItems);
216        return listViewItems == null ? Enumerable.Empty<ListViewItem>() : listViewItems;
217      }
218    }
219
220    #region ListView Events
221    protected virtual void itemsListView_SelectedIndexChanged(object sender, EventArgs e) {
222      removeButton.Enabled = (Content != null) && !Content.IsReadOnly && !ReadOnly && itemsListView.SelectedItems.Count > 0;
223      if (showDetailsCheckBox.Checked) {
224        if (itemsListView.SelectedItems.Count == 1) {
225          T item = (T)itemsListView.SelectedItems[0].Tag;
226          detailsGroupBox.Enabled = true;
227          viewHost.Content = item;
228        } else {
229          viewHost.Content = null;
230          detailsGroupBox.Enabled = false;
231        }
232      }
233    }
234    protected virtual void itemsListView_KeyDown(object sender, KeyEventArgs e) {
235      if (e.KeyCode == Keys.Delete) {
236        if ((itemsListView.SelectedItems.Count > 0) && !Content.IsReadOnly && !ReadOnly) {
237          if (ItemCollection != null) ItemCollection.RemoveRange(itemsListView.SelectedItems.Cast<ListViewItem>().Select(i => (T)i.Tag));
238          else {
239            foreach (ListViewItem item in itemsListView.SelectedItems)
240              Content.Remove((T)item.Tag);
241          }
242        }
243      } else if (e.KeyData == (Keys.Control | Keys.C)) {
244        if (itemsListView.SelectedItems.Count > 0) {
245          var builder = new StringBuilder();
246          foreach (ListViewItem selected in itemsListView.SelectedItems) {
247            builder.AppendLine(selected.Text);
248          }
249          Clipboard.SetText(builder.ToString());
250        }
251      }
252    }
253    protected virtual void itemsListView_DoubleClick(object sender, EventArgs e) {
254      if (itemsListView.SelectedItems.Count == 1) {
255        T item = itemsListView.SelectedItems[0].Tag as T;
256        if (item != null) {
257          IContentView view = MainFormManager.MainForm.ShowContent(item);
258          if (view != null) {
259            view.ReadOnly = ReadOnly;
260            view.Locked = Locked;
261          }
262        }
263      }
264    }
265    protected virtual void itemsListView_ItemDrag(object sender, ItemDragEventArgs e) {
266      if (!Locked) {
267        List<T> items = new List<T>();
268        foreach (ListViewItem listViewItem in itemsListView.SelectedItems) {
269          T item = listViewItem.Tag as T;
270          if (item != null) items.Add(item);
271        }
272
273        if (items.Count > 0) {
274          DataObject data = new DataObject();
275          if (items.Count == 1) data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, items[0]);
276          else data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, items);
277          if (Content.IsReadOnly || ReadOnly) {
278            DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link);
279          } else {
280            DragDropEffects result = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
281            if ((result & DragDropEffects.Move) == DragDropEffects.Move) {
282              foreach (T item in items) Content.Remove(item);
283            }
284          }
285        }
286      }
287    }
288    protected virtual void itemsListView_DragEnter(object sender, DragEventArgs e) {
289      validDragOperation = false;
290      if (!Content.IsReadOnly && !ReadOnly && (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is T)) {
291        validDragOperation = true;
292      } else if (!Content.IsReadOnly && !ReadOnly && (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is IEnumerable)) {
293        validDragOperation = true;
294        IEnumerable items = (IEnumerable)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
295        foreach (object item in items)
296          validDragOperation = validDragOperation && (item is T);
297      }
298    }
299    protected virtual void itemsListView_DragOver(object sender, DragEventArgs e) {
300      e.Effect = DragDropEffects.None;
301      if (validDragOperation) {
302        if ((e.KeyState & 32) == 32) e.Effect = DragDropEffects.Link;  // ALT key
303        else if ((e.KeyState & 4) == 4) e.Effect = DragDropEffects.Move;  // SHIFT key
304        else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
305        else if (e.AllowedEffect.HasFlag(DragDropEffects.Move)) e.Effect = DragDropEffects.Move;
306        else if (e.AllowedEffect.HasFlag(DragDropEffects.Link)) e.Effect = DragDropEffects.Link;
307      }
308    }
309    protected virtual void itemsListView_DragDrop(object sender, DragEventArgs e) {
310      if (e.Effect != DragDropEffects.None) {
311        if (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is T) {
312          T item = (T)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
313          Content.Add(e.Effect.HasFlag(DragDropEffects.Copy) ? (T)item.Clone() : item);
314        } else if (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is IEnumerable) {
315          IEnumerable<T> items = ((IEnumerable)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat)).Cast<T>();
316          if (e.Effect.HasFlag(DragDropEffects.Copy)) {
317            Cloner cloner = new Cloner();
318            items = items.Select(x => cloner.Clone(x));
319          }
320          if (ItemCollection != null) ItemCollection.AddRange(items);
321          else {
322            foreach (T item in items)
323              Content.Add(item);
324          }
325        }
326      }
327    }
328    protected virtual void itemsListView_Layout(object sender, LayoutEventArgs e) {
329      if (itemsListView.Columns.Count == 1)
330        AdjustListViewColumnSizes();
331    }
332    #endregion
333
334    #region Button Events
335    protected virtual void addButton_Click(object sender, EventArgs e) {
336      T item = CreateItem();
337      if (item != null)
338        Content.Add(item);
339    }
340    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
341      SortItemsListView(SortOrder.Ascending);
342    }
343    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
344      SortItemsListView(SortOrder.Descending);
345    }
346    protected virtual void removeButton_Click(object sender, EventArgs e) {
347      if (itemsListView.SelectedItems.Count > 0) {
348        if (ItemCollection != null) {
349          ItemCollection.RemoveRange(itemsListView.SelectedItems.Cast<ListViewItem>().Select(i => (T)i.Tag));
350        } else {
351          foreach (ListViewItem item in itemsListView.SelectedItems)
352            Content.Remove((T)item.Tag);
353        }
354        itemsListView.SelectedItems.Clear();
355      }
356    }
357    #endregion
358
359    #region CheckBox Events
360    protected virtual void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) {
361      if (showDetailsCheckBox.Checked) {
362        splitContainer.Panel2Collapsed = false;
363        detailsGroupBox.Enabled = itemsListView.SelectedItems.Count == 1;
364        viewHost.Content = itemsListView.SelectedItems.Count == 1 ? (T)itemsListView.SelectedItems[0].Tag : null;
365      } else {
366        splitContainer.Panel2Collapsed = true;
367        viewHost.Content = null;
368      }
369    }
370    #endregion
371
372    #region Content Events
373    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<T> e) {
374      if (InvokeRequired)
375        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsAdded), sender, e);
376      else {
377        foreach (T item in e.Items)
378          AddListViewItem(CreateListViewItem(item));
379        AdjustListViewColumnSizes();
380      }
381
382    }
383    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<T> e) {
384      if (InvokeRequired)
385        Invoke(new CollectionItemsChangedEventHandler<T>(Content_ItemsRemoved), sender, e);
386      else {
387        foreach (T item in e.Items) {
388          //remove only the first matching ListViewItem, because the IItem could be contained multiple times in the ItemCollection
389          ListViewItem listviewItem = GetListViewItemsForItem(item).FirstOrDefault();
390          if (listviewItem != null) RemoveListViewItem(listviewItem);
391        }
392        RebuildImageList();
393      }
394    }
395    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<T> e) {
396      if (InvokeRequired)
397        Invoke(new CollectionItemsChangedEventHandler<T>(Content_CollectionReset), sender, e);
398      else {
399        foreach (T item in e.OldItems) {
400          //remove only the first matching ListViewItem, because the IItem could be contained multiple times in the ItemCollection
401          ListViewItem listviewItem = GetListViewItemsForItem(item).FirstOrDefault();
402          if (listviewItem != null) RemoveListViewItem(listviewItem);
403        }
404        RebuildImageList();
405        foreach (T item in e.Items)
406          AddListViewItem(CreateListViewItem(item));
407      }
408    }
409    #endregion
410
411    #region Item Events
412    protected virtual void Item_ItemImageChanged(object sender, EventArgs e) {
413      if (InvokeRequired)
414        Invoke(new EventHandler(Item_ItemImageChanged), sender, e);
415      else {
416        T item = (T)sender;
417        foreach (ListViewItem listViewItem in GetListViewItemsForItem(item))
418          UpdateListViewItemImage(listViewItem);
419      }
420    }
421    protected virtual void Item_ToStringChanged(object sender, EventArgs e) {
422      if (InvokeRequired)
423        Invoke(new EventHandler(Item_ToStringChanged), sender, e);
424      else {
425        T item = (T)sender;
426        foreach (ListViewItem listViewItem in GetListViewItemsForItem(item))
427          UpdateListViewItemText(listViewItem);
428        if (itemsListView.Columns.Count > 1)
429          AdjustListViewColumnSizes();
430      }
431    }
432    #endregion
433
434    #region Helpers
435    protected virtual void SortItemsListView(SortOrder sortOrder) {
436      itemsListView.Sorting = SortOrder.None;
437      itemsListView.Sorting = sortOrder;
438      itemsListView.Sorting = SortOrder.None;
439    }
440    protected virtual void AdjustListViewColumnSizes() {
441      if (itemsListView.Columns.Count == 1) {
442        if (itemsListView.Columns[0].Width != itemsListView.ClientSize.Width)
443          itemsListView.Columns[0].Width = itemsListView.ClientSize.Width;
444      } else {
445        if (itemsListView.Items.Count > 0) {
446          for (int i = 0; i < itemsListView.Columns.Count; i++)
447            itemsListView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
448        }
449      }
450    }
451    protected virtual void RebuildImageList() {
452      itemsListView.SmallImageList.Images.Clear();
453      foreach (ListViewItem listViewItem in itemsListView.Items) {
454        T item = listViewItem.Tag as T;
455        itemsListView.SmallImageList.Images.Add(item == null ? HeuristicLab.Common.Resources.VSImageLibrary.Nothing : item.ItemImage);
456        listViewItem.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
457      }
458    }
459    #endregion
460  }
461}
Note: See TracBrowser for help on using the repository browser.