1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com)
23 // Ravindra Kumar (rkumar@novell.com)
24 // Jordi Mas i Hernandez, jordi@ximian.com
25 // Mike Kestner (mkestner@novell.com)
26 // Daniel Nauck (dna(at)mono-project(dot)de)
27 // Carlos Alberto Cortez <calberto.cortez@gmail.com>
34 using System.Collections;
35 using System.ComponentModel;
36 using System.ComponentModel.Design;
38 using System.Runtime.InteropServices;
39 using System.Globalization;
41 using System.Collections.Generic;
44 namespace System.Windows.Forms
46 [DefaultEvent ("SelectedIndexChanged")]
47 [DefaultProperty ("Items")]
48 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
50 [ClassInterface (ClassInterfaceType.AutoDispatch)]
52 [Docking (DockingBehavior.Ask)]
54 public class ListView : Control
56 private ItemActivation activation = ItemActivation.Standard;
57 private ListViewAlignment alignment = ListViewAlignment.Top;
58 private bool allow_column_reorder;
59 private bool auto_arrange = true;
60 private bool check_boxes;
61 private readonly CheckedIndexCollection checked_indices;
62 private readonly CheckedListViewItemCollection checked_items;
63 private readonly ColumnHeaderCollection columns;
64 internal int focused_item_index = -1;
65 private bool full_row_select;
66 private bool grid_lines;
67 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
68 private bool hide_selection = true;
69 private bool hover_selection;
70 private IComparer item_sorter;
71 private readonly ListViewItemCollection items;
73 private readonly ListViewGroupCollection groups;
74 private bool owner_draw;
75 private bool show_groups = true;
77 private bool label_edit;
78 private bool label_wrap = true;
79 private bool multiselect = true;
80 private bool scrollable = true;
81 private bool hover_pending;
82 private readonly SelectedIndexCollection selected_indices;
83 private readonly SelectedListViewItemCollection selected_items;
84 private SortOrder sort_order = SortOrder.None;
85 private ImageList state_image_list;
86 internal bool updating;
87 private View view = View.LargeIcon;
88 private int layout_wd; // We might draw more than our client area
89 private int layout_ht; // therefore we need to have these two.
90 HeaderControl header_control;
91 internal ItemControl item_control;
92 internal ScrollBar h_scroll; // used for scrolling horizontally
93 internal ScrollBar v_scroll; // used for scrolling vertically
94 internal int h_marker; // Position markers for scrolling
95 internal int v_marker;
96 private int keysearch_tickcnt;
97 private string keysearch_text;
98 static private readonly int keysearch_keydelay = 1000;
99 private int[] reordered_column_indices;
100 private int[] reordered_items_indices;
101 private Point [] items_location;
102 private ItemMatrixLocation [] items_matrix_location;
103 private Size item_size; // used for caching item size
104 private int custom_column_width; // used when using Columns with SmallIcon/List views
105 private int hot_item_index = -1;
107 private bool hot_tracking;
108 private ListViewInsertionMark insertion_mark;
109 private bool show_item_tooltips;
110 private ToolTip item_tooltip;
111 private Size tile_size;
112 private bool virtual_mode;
113 private int virtual_list_size;
114 private bool right_to_left_layout;
117 // internal variables
118 internal ImageList large_image_list;
119 internal ImageList small_image_list;
120 internal Size text_size = Size.Empty;
123 static object AfterLabelEditEvent = new object ();
124 static object BeforeLabelEditEvent = new object ();
125 static object ColumnClickEvent = new object ();
126 static object ItemActivateEvent = new object ();
127 static object ItemCheckEvent = new object ();
128 static object ItemDragEvent = new object ();
129 static object SelectedIndexChangedEvent = new object ();
131 static object DrawColumnHeaderEvent = new object();
132 static object DrawItemEvent = new object();
133 static object DrawSubItemEvent = new object();
134 static object ItemCheckedEvent = new object ();
135 static object ItemMouseHoverEvent = new object ();
136 static object ItemSelectionChangedEvent = new object ();
137 static object CacheVirtualItemsEvent = new object ();
138 static object RetrieveVirtualItemEvent = new object ();
139 static object RightToLeftLayoutChangedEvent = new object ();
140 static object SearchForVirtualItemEvent = new object ();
141 static object VirtualItemsSelectionRangeChangedEvent = new object ();
144 public event LabelEditEventHandler AfterLabelEdit {
145 add { Events.AddHandler (AfterLabelEditEvent, value); }
146 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
151 [EditorBrowsable (EditorBrowsableState.Never)]
152 public new event EventHandler BackgroundImageChanged {
153 add { base.BackgroundImageChanged += value; }
154 remove { base.BackgroundImageChanged -= value; }
160 [EditorBrowsable (EditorBrowsableState.Never)]
161 public new event EventHandler BackgroundImageLayoutChanged {
162 add { base.BackgroundImageLayoutChanged += value; }
163 remove { base.BackgroundImageLayoutChanged -= value; }
167 public event LabelEditEventHandler BeforeLabelEdit {
168 add { Events.AddHandler (BeforeLabelEditEvent, value); }
169 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
172 public event ColumnClickEventHandler ColumnClick {
173 add { Events.AddHandler (ColumnClickEvent, value); }
174 remove { Events.RemoveHandler (ColumnClickEvent, value); }
178 public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
179 add { Events.AddHandler(DrawColumnHeaderEvent, value); }
180 remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
183 public event DrawListViewItemEventHandler DrawItem {
184 add { Events.AddHandler(DrawItemEvent, value); }
185 remove { Events.RemoveHandler(DrawItemEvent, value); }
188 public event DrawListViewSubItemEventHandler DrawSubItem {
189 add { Events.AddHandler(DrawSubItemEvent, value); }
190 remove { Events.RemoveHandler(DrawSubItemEvent, value); }
194 public event EventHandler ItemActivate {
195 add { Events.AddHandler (ItemActivateEvent, value); }
196 remove { Events.RemoveHandler (ItemActivateEvent, value); }
199 public event ItemCheckEventHandler ItemCheck {
200 add { Events.AddHandler (ItemCheckEvent, value); }
201 remove { Events.RemoveHandler (ItemCheckEvent, value); }
205 public event ItemCheckedEventHandler ItemChecked {
206 add { Events.AddHandler (ItemCheckedEvent, value); }
207 remove { Events.RemoveHandler (ItemCheckedEvent, value); }
211 public event ItemDragEventHandler ItemDrag {
212 add { Events.AddHandler (ItemDragEvent, value); }
213 remove { Events.RemoveHandler (ItemDragEvent, value); }
217 public event ListViewItemMouseHoverEventHandler ItemMouseHover {
218 add { Events.AddHandler (ItemMouseHoverEvent, value); }
219 remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
222 public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
223 add { Events.AddHandler (ItemSelectionChangedEvent, value); }
224 remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
228 [EditorBrowsable (EditorBrowsableState.Never)]
229 public new event EventHandler PaddingChanged {
230 add { base.PaddingChanged += value; }
231 remove { base.PaddingChanged -= value; }
236 [EditorBrowsable (EditorBrowsableState.Never)]
237 public new event PaintEventHandler Paint {
238 add { base.Paint += value; }
239 remove { base.Paint -= value; }
242 public event EventHandler SelectedIndexChanged {
243 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
244 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
248 [EditorBrowsable (EditorBrowsableState.Never)]
249 public new event EventHandler TextChanged {
250 add { base.TextChanged += value; }
251 remove { base.TextChanged -= value; }
255 public event CacheVirtualItemsEventHandler CacheVirtualItems {
256 add { Events.AddHandler (CacheVirtualItemsEvent, value); }
257 remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
260 public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
261 add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
262 remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
265 public event EventHandler RightToLeftLayoutChanged {
266 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
267 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
270 public event SearchForVirtualItemEventHandler SearchForVirtualItem {
271 add { Events.AddHandler (SearchForVirtualItemEvent, value); }
272 remove { Events.AddHandler (SearchForVirtualItemEvent, value); }
275 public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged {
276 add { Events.AddHandler (VirtualItemsSelectionRangeChangedEvent, value); }
277 remove { Events.RemoveHandler (VirtualItemsSelectionRangeChangedEvent, value); }
283 #region Public Constructors
286 background_color = ThemeEngine.Current.ColorWindow;
288 groups = new ListViewGroupCollection (this);
290 items = new ListViewItemCollection (this);
291 items.Changed += new CollectionChangedHandler (OnItemsChanged);
292 checked_indices = new CheckedIndexCollection (this);
293 checked_items = new CheckedListViewItemCollection (this);
294 columns = new ColumnHeaderCollection (this);
295 foreground_color = SystemColors.WindowText;
296 selected_indices = new SelectedIndexCollection (this);
297 selected_items = new SelectedListViewItemCollection (this);
298 items_location = new Point [16];
299 items_matrix_location = new ItemMatrixLocation [16];
300 reordered_items_indices = new int [16];
302 item_tooltip = new ToolTip ();
303 item_tooltip.Active = false;
304 insertion_mark = new ListViewInsertionMark (this);
307 InternalBorderStyle = BorderStyle.Fixed3D;
309 header_control = new HeaderControl (this);
310 header_control.Visible = false;
311 Controls.AddImplicit (header_control);
313 item_control = new ItemControl (this);
314 Controls.AddImplicit (item_control);
316 h_scroll = new ImplicitHScrollBar ();
317 Controls.AddImplicit (this.h_scroll);
319 v_scroll = new ImplicitVScrollBar ();
320 Controls.AddImplicit (this.v_scroll);
322 h_marker = v_marker = 0;
323 keysearch_tickcnt = 0;
325 // scroll bars are disabled initially
326 h_scroll.Visible = false;
327 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
328 v_scroll.Visible = false;
329 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
332 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
333 SizeChanged += new EventHandler (ListView_SizeChanged);
334 GotFocus += new EventHandler (FocusChanged);
335 LostFocus += new EventHandler (FocusChanged);
336 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
337 MouseEnter += new EventHandler (ListView_MouseEnter);
338 Invalidated += new InvalidateEventHandler (ListView_Invalidated);
341 BackgroundImageTiled = false;
344 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
346 | ControlStyles.UseTextForAccessibility
350 #endregion // Public Constructors
352 #region Private Internal Properties
353 internal Size CheckBoxSize {
355 if (this.check_boxes) {
356 if (this.state_image_list != null)
357 return this.state_image_list.ImageSize;
359 return ThemeEngine.Current.ListViewCheckBoxSize;
365 internal Size ItemSize {
367 if (view != View.Details)
370 Size size = new Size ();
371 size.Height = item_size.Height;
372 for (int i = 0; i < columns.Count; i++)
373 size.Width += columns [i].Wd;
382 internal int HotItemIndex {
384 return hot_item_index;
387 hot_item_index = value;
391 internal override bool ScaleChildrenInternal {
392 get { return false; }
395 internal bool UseCustomColumnWidth {
397 return (view == View.List || view == View.SmallIcon) && columns.Count > 0;
401 internal ColumnHeader EnteredColumnHeader {
403 return header_control.EnteredColumnHeader;
406 #endregion // Private Internal Properties
408 #region Protected Properties
409 protected override CreateParams CreateParams {
410 get { return base.CreateParams; }
413 protected override Size DefaultSize {
414 get { return ThemeEngine.Current.ListViewDefaultSize; }
417 protected override bool DoubleBuffered {
419 return base.DoubleBuffered;
422 base.DoubleBuffered = value;
426 #endregion // Protected Properties
428 #region Public Instance Properties
429 [DefaultValue (ItemActivation.Standard)]
430 public ItemActivation Activation {
431 get { return activation; }
433 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
434 value != ItemActivation.TwoClick) {
435 throw new InvalidEnumArgumentException (string.Format
436 ("Enum argument value '{0}' is not valid for Activation", value));
439 if (hot_tracking && value != ItemActivation.OneClick)
440 throw new ArgumentException ("When HotTracking is on, activation must be ItemActivation.OneClick");
447 [DefaultValue (ListViewAlignment.Top)]
449 public ListViewAlignment Alignment {
450 get { return alignment; }
452 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
453 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
454 throw new InvalidEnumArgumentException (string.Format
455 ("Enum argument value '{0}' is not valid for Alignment", value));
458 if (this.alignment != value) {
460 // alignment does not matter in Details/List views
461 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
467 [DefaultValue (false)]
468 public bool AllowColumnReorder {
469 get { return allow_column_reorder; }
470 set { allow_column_reorder = value; }
473 [DefaultValue (true)]
474 public bool AutoArrange {
475 get { return auto_arrange; }
477 if (auto_arrange != value) {
478 auto_arrange = value;
479 // autoarrange does not matter in Details/List views
480 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
486 public override Color BackColor {
488 if (background_color.IsEmpty)
489 return ThemeEngine.Current.ColorWindow;
491 return background_color;
494 background_color = value;
495 item_control.BackColor = value;
501 [EditorBrowsable (EditorBrowsableState.Never)]
502 public override Image BackgroundImage {
503 get { return base.BackgroundImage; }
504 set { base.BackgroundImage = value; }
510 [EditorBrowsable (EditorBrowsableState.Never)]
511 public override ImageLayout BackgroundImageLayout {
513 return base.BackgroundImageLayout;
516 base.BackgroundImageLayout = value;
520 [DefaultValue (false)]
521 public bool BackgroundImageTiled {
523 return item_control.BackgroundImageLayout == ImageLayout.Tile;
526 ImageLayout new_image_layout = value ? ImageLayout.Tile : ImageLayout.None;
527 if (new_image_layout == item_control.BackgroundImageLayout)
530 item_control.BackgroundImageLayout = new_image_layout;
535 [DefaultValue (BorderStyle.Fixed3D)]
537 public BorderStyle BorderStyle {
538 get { return InternalBorderStyle; }
539 set { InternalBorderStyle = value; }
542 [DefaultValue (false)]
543 public bool CheckBoxes {
544 get { return check_boxes; }
546 if (check_boxes != value) {
548 if (value && View == View.Tile)
549 throw new NotSupportedException ("CheckBoxes are not"
550 + " supported in Tile view. Choose a different"
551 + " view or set CheckBoxes to false.");
561 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
562 public CheckedIndexCollection CheckedIndices {
563 get { return checked_indices; }
567 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
568 public CheckedListViewItemCollection CheckedItems {
569 get { return checked_items; }
573 [Editor ("System.Windows.Forms.Design.ColumnHeaderCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
575 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
577 [MergableProperty (false)]
578 public ColumnHeaderCollection Columns {
579 get { return columns; }
583 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
584 public ListViewItem FocusedItem {
586 if (focused_item_index == -1)
589 return GetItemAtDisplayIndex (focused_item_index);
593 if (value == null || value.ListView != this ||
597 SetFocusedItem (value.DisplayIndex);
602 public override Color ForeColor {
604 if (foreground_color.IsEmpty)
605 return ThemeEngine.Current.ColorWindowText;
607 return foreground_color;
609 set { foreground_color = value; }
612 [DefaultValue (false)]
613 public bool FullRowSelect {
614 get { return full_row_select; }
616 if (full_row_select != value) {
617 full_row_select = value;
618 InvalidateSelection ();
623 [DefaultValue (false)]
624 public bool GridLines {
625 get { return grid_lines; }
627 if (grid_lines != value) {
634 [DefaultValue (ColumnHeaderStyle.Clickable)]
635 public ColumnHeaderStyle HeaderStyle {
636 get { return header_style; }
638 if (header_style == value)
642 case ColumnHeaderStyle.Clickable:
643 case ColumnHeaderStyle.Nonclickable:
644 case ColumnHeaderStyle.None:
647 throw new InvalidEnumArgumentException (string.Format
648 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
651 header_style = value;
652 if (view == View.Details)
657 [DefaultValue (true)]
658 public bool HideSelection {
659 get { return hide_selection; }
661 if (hide_selection != value) {
662 hide_selection = value;
663 InvalidateSelection ();
669 [DefaultValue (false)]
670 public bool HotTracking {
675 if (hot_tracking == value)
678 hot_tracking = value;
680 hover_selection = true;
681 activation = ItemActivation.OneClick;
687 [DefaultValue (false)]
688 public bool HoverSelection {
689 get { return hover_selection; }
692 if (hot_tracking && value == false)
693 throw new ArgumentException ("When HotTracking is on, hover selection must be true");
695 hover_selection = value;
700 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
702 public ListViewInsertionMark InsertionMark {
704 return insertion_mark;
710 [Editor ("System.Windows.Forms.Design.ListViewItemCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
712 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
714 [MergableProperty (false)]
715 public ListViewItemCollection Items {
716 get { return items; }
719 [DefaultValue (false)]
720 public bool LabelEdit {
721 get { return label_edit; }
722 set { label_edit = value; }
725 [DefaultValue (true)]
727 public bool LabelWrap {
728 get { return label_wrap; }
730 if (label_wrap != value) {
737 [DefaultValue (null)]
738 public ImageList LargeImageList {
739 get { return large_image_list; }
741 large_image_list = value;
747 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
748 public IComparer ListViewItemSorter {
750 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
755 if (item_sorter != value) {
762 [DefaultValue (true)]
763 public bool MultiSelect {
764 get { return multiselect; }
765 set { multiselect = value; }
770 [DefaultValue(false)]
771 public bool OwnerDraw {
772 get { return owner_draw; }
780 [EditorBrowsable (EditorBrowsableState.Never)]
781 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
782 public new Padding Padding {
787 base.Padding = value;
791 [MonoTODO ("RTL not supported")]
793 [DefaultValue (false)]
794 public virtual bool RightToLeftLayout {
795 get { return right_to_left_layout; }
797 if (right_to_left_layout != value) {
798 right_to_left_layout = value;
799 OnRightToLeftLayoutChanged (EventArgs.Empty);
805 [DefaultValue (true)]
806 public bool Scrollable {
807 get { return scrollable; }
809 if (scrollable != value) {
817 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
818 public SelectedIndexCollection SelectedIndices {
819 get { return selected_indices; }
823 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
824 public SelectedListViewItemCollection SelectedItems {
825 get { return selected_items; }
830 public bool ShowGroups {
831 get { return show_groups; }
833 if (show_groups != value) {
840 [LocalizableAttribute (true)]
841 [MergableProperty (false)]
842 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
843 [Editor ("System.Windows.Forms.Design.ListViewGroupCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
844 public ListViewGroupCollection Groups {
845 get { return groups; }
848 [DefaultValue (false)]
849 public bool ShowItemToolTips {
851 return show_item_tooltips;
854 show_item_tooltips = value;
855 item_tooltip.Active = false;
860 [DefaultValue (null)]
861 public ImageList SmallImageList {
862 get { return small_image_list; }
864 small_image_list = value;
869 [DefaultValue (SortOrder.None)]
870 public SortOrder Sorting {
871 get { return sort_order; }
873 if (!Enum.IsDefined (typeof (SortOrder), value)) {
874 throw new InvalidEnumArgumentException ("value", (int) value,
878 if (sort_order == value)
884 if (virtual_mode) // Sorting is not allowed in virtual mode
888 if (value == SortOrder.None) {
889 if (item_sorter != null) {
890 // ListViewItemSorter should never be reset for SmallIcon
891 // and LargeIcon view
892 if (View != View.SmallIcon && View != View.LargeIcon)
896 // in .NET 1.1, only internal IComparer would be
898 if (item_sorter is ItemComparer)
904 if (item_sorter == null)
905 item_sorter = new ItemComparer (value);
906 if (item_sorter is ItemComparer) {
908 item_sorter = new ItemComparer (value);
910 // in .NET 1.1, the sort order is not updated for
911 // SmallIcon and LargeIcon views if no custom IComparer
913 if (View != View.SmallIcon && View != View.LargeIcon)
914 item_sorter = new ItemComparer (value);
922 private void OnImageListChanged (object sender, EventArgs args)
924 item_control.Invalidate ();
927 [DefaultValue (null)]
928 public ImageList StateImageList {
929 get { return state_image_list; }
931 if (state_image_list == value)
934 if (state_image_list != null)
935 state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
937 state_image_list = value;
939 if (state_image_list != null)
940 state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
948 [EditorBrowsable (EditorBrowsableState.Never)]
949 public override string Text {
950 get { return base.Text; }
952 if (value == base.Text)
962 public Size TileSize {
967 if (value.Width <= 0 || value.Height <= 0)
968 throw new ArgumentOutOfRangeException ("value");
977 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
978 public ListViewItem TopItem {
981 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
982 throw new InvalidOperationException ("Cannot get the top item in LargeIcon, SmallIcon or Tile view.");
985 if (this.items.Count == 0)
987 // if contents are not scrolled
988 // it is the first item
989 else if (h_marker == 0 && v_marker == 0)
990 return this.items [0];
991 // do a hit test for the scrolled position
993 for (int i = 0; i < items.Count; i++) {
994 Point item_loc = GetItemLocation (i);
995 if (item_loc.X >= 0 && item_loc.Y >= 0)
1003 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
1004 throw new InvalidOperationException ("Cannot set the top item in LargeIcon, SmallIcon or Tile view.");
1006 // .Net doesn't throw any exception in the cases below
1007 if (value == null || value.ListView != this)
1010 EnsureVisible (value.Index);
1016 [EditorBrowsable (EditorBrowsableState.Advanced)]
1017 [DefaultValue (true)]
1019 [MonoInternalNote ("Stub, not implemented")]
1020 public bool UseCompatibleStateImageBehavior {
1029 [DefaultValue (View.LargeIcon)]
1031 get { return view; }
1033 if (!Enum.IsDefined (typeof (View), value))
1034 throw new InvalidEnumArgumentException ("value", (int) value,
1037 if (view != value) {
1039 if (CheckBoxes && value == View.Tile)
1040 throw new NotSupportedException ("CheckBoxes are not"
1041 + " supported in Tile view. Choose a different"
1042 + " view or set CheckBoxes to false.");
1045 h_scroll.Value = v_scroll.Value = 0;
1053 [DefaultValue (false)]
1054 [RefreshProperties (RefreshProperties.Repaint)]
1055 public bool VirtualMode {
1057 return virtual_mode;
1060 if (virtual_mode == value)
1063 if (!virtual_mode && items.Count > 0)
1064 throw new InvalidOperationException ();
1066 virtual_mode = value;
1072 [RefreshProperties (RefreshProperties.Repaint)]
1073 public int VirtualListSize {
1075 return virtual_list_size;
1079 throw new ArgumentException ("value");
1081 if (virtual_list_size == value)
1084 virtual_list_size = value;
1086 selected_indices.Reset ();
1092 #endregion // Public Instance Properties
1094 #region Internal Methods Properties
1096 internal int FirstVisibleIndex {
1099 if (this.items.Count == 0)
1102 if (h_marker == 0 && v_marker == 0)
1105 Size item_size = ItemSize;
1106 for (int i = 0; i < items.Count; i++) {
1107 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
1108 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
1117 internal int LastVisibleIndex {
1119 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
1120 if (View == View.List || Alignment == ListViewAlignment.Left) {
1121 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
1124 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
1129 return Items.Count - 1;
1133 internal void OnSelectedIndexChanged ()
1135 if (IsHandleCreated)
1136 OnSelectedIndexChanged (EventArgs.Empty);
1139 internal int TotalWidth {
1140 get { return Math.Max (this.Width, this.layout_wd); }
1143 internal int TotalHeight {
1144 get { return Math.Max (this.Height, this.layout_ht); }
1147 internal void Redraw (bool recalculate)
1149 // Avoid calculations when control is being updated
1153 // VirtualMode doesn't do any calculations until handle is created
1154 if (virtual_mode && !IsHandleCreated)
1160 CalculateListView (this.alignment);
1166 void InvalidateSelection ()
1168 foreach (int selected_index in SelectedIndices)
1169 items [selected_index].Invalidate ();
1172 const int text_padding = 15;
1174 internal Size GetChildColumnSize (int index)
1176 Size ret_size = Size.Empty;
1177 ColumnHeader col = this.columns [index];
1179 if (col.Width == -2) { // autosize = max(items, columnheader)
1180 Size size = Size.Ceiling (TextRenderer.MeasureString
1181 (col.Text, this.Font));
1182 size.Width += text_padding;
1183 ret_size = BiggestItem (index);
1184 if (size.Width > ret_size.Width)
1187 else { // -1 and all the values < -2 are put under one category
1188 ret_size = BiggestItem (index);
1189 // fall back to empty columns' width if no subitem is available for a column
1190 if (ret_size.IsEmpty) {
1191 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
1192 if (col.Text.Length > 0)
1193 ret_size.Height = Size.Ceiling (TextRenderer.MeasureString
1194 (col.Text, this.Font)).Height;
1196 ret_size.Height = this.Font.Height;
1200 ret_size.Height += text_padding;
1202 // adjust the size for icon and checkbox for 0th column
1204 ret_size.Width += (this.CheckBoxSize.Width + 4);
1205 if (this.small_image_list != null)
1206 ret_size.Width += this.small_image_list.ImageSize.Width;
1211 // Returns the size of biggest item text in a column
1212 // or the sum of the text and indent count if we are on 2.0
1213 private Size BiggestItem (int col)
1215 Size temp = Size.Empty;
1216 Size ret_size = Size.Empty;
1218 bool use_indent_count = small_image_list != null;
1220 // VirtualMode uses the first item text size
1221 if (virtual_mode && items.Count > 0) {
1222 ListViewItem item = items [0];
1223 ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
1226 if (use_indent_count)
1227 ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
1230 // 0th column holds the item text, we check the size of
1231 // the various subitems falling in that column and get
1232 // the biggest one's size.
1233 foreach (ListViewItem item in items) {
1234 if (col >= item.SubItems.Count)
1237 temp = Size.Ceiling (TextRenderer.MeasureString
1238 (item.SubItems [col].Text, Font));
1241 if (use_indent_count)
1242 temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
1245 if (temp.Width > ret_size.Width)
1252 // adjustment for space in Details view
1253 if (!ret_size.IsEmpty && view == View.Details)
1254 ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
1259 const int max_wrap_padding = 30;
1261 // Sets the size of the biggest item text as per the view
1262 private void CalcTextSize ()
1264 // clear the old value
1265 text_size = Size.Empty;
1267 if (items.Count == 0)
1270 text_size = BiggestItem (0);
1272 if (view == View.LargeIcon && this.label_wrap) {
1273 Size temp = Size.Empty;
1274 if (this.check_boxes)
1275 temp.Width += 2 * this.CheckBoxSize.Width;
1276 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1277 temp.Width += icon_w + max_wrap_padding;
1278 // wrapping is done for two lines only
1279 if (text_size.Width > temp.Width) {
1280 text_size.Width = temp.Width;
1281 text_size.Height *= 2;
1284 else if (view == View.List) {
1285 // in list view max text shown in determined by the
1286 // control width, even if scolling is enabled.
1287 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1288 if (this.small_image_list != null)
1289 max_wd -= this.small_image_list.ImageSize.Width;
1291 if (text_size.Width > max_wd)
1292 text_size.Width = max_wd;
1295 // we do the default settings, if we have got 0's
1296 if (text_size.Height <= 0)
1297 text_size.Height = this.Font.Height;
1298 if (text_size.Width <= 0)
1299 text_size.Width = this.Width;
1301 // little adjustment
1302 text_size.Width += 2;
1303 text_size.Height += 2;
1306 private void Scroll (ScrollBar scrollbar, int delta)
1308 if (delta == 0 || !scrollbar.Visible)
1312 if (scrollbar == h_scroll)
1313 max = h_scroll.Maximum - item_control.Width;
1315 max = v_scroll.Maximum - item_control.Height;
1317 int val = scrollbar.Value + delta;
1320 else if (val < scrollbar.Minimum)
1321 val = scrollbar.Minimum;
1322 scrollbar.Value = val;
1325 private void CalculateScrollBars ()
1327 if (!IsHandleCreated)
1330 Rectangle client_area = ClientRectangle;
1331 int height = client_area.Height;
1332 int width = client_area.Width;
1335 h_scroll.Visible = false;
1336 v_scroll.Visible = false;
1337 item_control.Size = new Size (width, height);
1338 header_control.Width = width;
1342 // Don't calculate if the view is not displayable
1343 if (client_area.Height < 0 || client_area.Width < 0)
1346 // making a scroll bar visible might make
1347 // other scroll bar visible
1348 if (layout_wd > client_area.Right) {
1349 h_scroll.Visible = true;
1350 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1351 v_scroll.Visible = true;
1353 v_scroll.Visible = false;
1354 } else if (layout_ht > client_area.Bottom) {
1355 v_scroll.Visible = true;
1356 if ((layout_wd + v_scroll.Width) > client_area.Right)
1357 h_scroll.Visible = true;
1359 h_scroll.Visible = false;
1361 h_scroll.Visible = false;
1362 v_scroll.Visible = false;
1366 if (h_scroll.is_visible) {
1367 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1368 h_scroll.Minimum = 0;
1370 // if v_scroll is visible, adjust the maximum of the
1371 // h_scroll to account for the width of v_scroll
1372 if (v_scroll.Visible) {
1373 h_scroll.Maximum = layout_wd + v_scroll.Width;
1374 h_scroll.Width = client_area.Width - v_scroll.Width;
1377 h_scroll.Maximum = layout_wd;
1378 h_scroll.Width = client_area.Width;
1381 h_scroll.LargeChange = client_area.Width;
1382 h_scroll.SmallChange = item_size.Width + ThemeEngine.Current.ListViewHorizontalSpacing;
1383 height -= h_scroll.Height;
1386 if (v_scroll.is_visible) {
1387 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1388 v_scroll.Minimum = 0;
1390 // if h_scroll is visible, adjust the maximum of the
1391 // v_scroll to account for the height of h_scroll
1392 if (h_scroll.Visible) {
1393 v_scroll.Maximum = layout_ht + h_scroll.Height;
1394 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1396 v_scroll.Maximum = layout_ht;
1397 v_scroll.Height = client_area.Height;
1400 v_scroll.LargeChange = client_area.Height;
1401 v_scroll.SmallChange = Font.Height;
1402 width -= v_scroll.Width;
1405 item_control.Size = new Size (width, height);
1407 if (header_control.is_visible)
1408 header_control.Width = width;
1412 internal int GetReorderedColumnIndex (ColumnHeader column)
1414 if (reordered_column_indices == null)
1415 return column.Index;
1417 for (int i = 0; i < Columns.Count; i++)
1418 if (reordered_column_indices [i] == column.Index)
1425 internal ColumnHeader GetReorderedColumn (int index)
1427 if (reordered_column_indices == null)
1428 return Columns [index];
1430 return Columns [reordered_column_indices [index]];
1433 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1437 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1439 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1443 header_control.Invalidate ();
1444 item_control.Invalidate ();
1450 int column_count = Columns.Count;
1452 if (reordered_column_indices == null) {
1453 reordered_column_indices = new int [column_count];
1454 for (int i = 0; i < column_count; i++)
1455 reordered_column_indices [i] = i;
1458 if (reordered_column_indices [index] == col.Index)
1461 int[] curr = reordered_column_indices;
1462 int [] result = new int [column_count];
1464 for (int i = 0; i < column_count; i++) {
1465 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1469 result [i] = col.Index;
1471 result [i] = curr [curr_idx++];
1474 ReorderColumns (result, true);
1477 internal void ReorderColumns (int [] display_indices, bool redraw)
1479 reordered_column_indices = display_indices;
1480 for (int i = 0; i < Columns.Count; i++) {
1481 ColumnHeader col = Columns [i];
1482 col.InternalDisplayIndex = reordered_column_indices [i];
1484 if (redraw && view == View.Details && IsHandleCreated) {
1486 header_control.Invalidate ();
1487 item_control.Invalidate ();
1491 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1493 int column_count = Columns.Count;
1494 newCol.SetListView (this);
1496 int [] display_indices = new int [column_count];
1497 for (int i = 0; i < column_count; i++) {
1498 ColumnHeader col = Columns [i];
1500 display_indices [i] = index;
1502 int display_index = col.InternalDisplayIndex;
1503 if (display_index < index) {
1504 display_indices [i] = display_index;
1506 display_indices [i] = (display_index + 1);
1511 ReorderColumns (display_indices, redraw);
1515 Size LargeIconItemSize
1518 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1519 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1520 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1521 int w = Math.Max (text_size.Width, image_w);
1524 w += 2 + CheckBoxSize.Width;
1526 return new Size (w, h);
1530 Size SmallIconItemSize {
1532 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1533 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1534 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1535 int w = text_size.Width + image_w;
1538 w += 2 + CheckBoxSize.Width;
1540 return new Size (w, h);
1547 // Calculate tile size if needed
1548 // It appears that using Font.Size instead of a SizeF value can give us
1549 // a slightly better approach to the proportions defined in .Net
1550 if (tile_size == Size.Empty) {
1551 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1552 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1553 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1554 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1556 tile_size = new Size (w, h);
1564 int GetDetailsItemHeight ()
1567 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1568 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1569 item_height = Math.Max (checkbox_height, text_size.Height);
1570 item_height = Math.Max (item_height, small_image_height);
1574 void SetItemLocation (int index, int x, int y, int row, int col)
1576 Point old_location = items_location [index];
1577 if (old_location.X == x && old_location.Y == y)
1580 Size item_size = ItemSize;
1581 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1583 items_location [index] = new Point (x, y);
1584 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1587 // Initial position matches item's position in ListViewItemCollection
1589 reordered_items_indices [index] = index;
1591 // Invalidate both previous and new bounds
1592 item_control.Invalidate (old_rect);
1593 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1597 void ShiftItemsPositions (int from, int to, bool forward)
1600 for (int i = to + 1; i > from; i--) {
1601 reordered_items_indices [i] = reordered_items_indices [i - 1];
1603 ListViewItem item = items [reordered_items_indices [i]];
1604 item_control.Invalidate (item.Bounds);
1605 item.DisplayIndex = i;
1606 item_control.Invalidate (item.Bounds);
1609 for (int i = from - 1; i < to; i++) {
1610 reordered_items_indices [i] = reordered_items_indices [i + 1];
1612 ListViewItem item = items [reordered_items_indices [i]];
1613 item_control.Invalidate (item.Bounds);
1614 item.DisplayIndex = i;
1615 item_control.Invalidate (item.Bounds);
1620 internal void ChangeItemLocation (int display_index, Point new_pos)
1622 int new_display_index = GetDisplayIndexFromLocation (new_pos);
1623 if (new_display_index == display_index)
1626 int item_index = reordered_items_indices [display_index];
1627 ListViewItem item = items [item_index];
1629 bool forward = new_display_index < display_index;
1630 int index_from, index_to;
1632 index_from = new_display_index;
1633 index_to = display_index - 1;
1635 index_from = display_index + 1;
1636 index_to = new_display_index;
1639 ShiftItemsPositions (index_from, index_to, forward);
1641 reordered_items_indices [new_display_index] = item_index;
1643 item_control.Invalidate (item.Bounds);
1644 item.DisplayIndex = new_display_index;
1645 item_control.Invalidate (item.Bounds);
1648 int GetDisplayIndexFromLocation (Point loc)
1650 int display_index = -1;
1651 Rectangle item_area;
1654 if (loc.X < 0 || loc.Y < 0)
1657 // Adjustment to put in the next position refered by 'loc'
1658 loc.X -= item_size.Width / 2;
1662 for (int i = 0; i < items.Count; i++) {
1663 item_area = new Rectangle (GetItemLocation (i), item_size);
1664 item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
1665 ThemeEngine.Current.ListViewVerticalSpacing);
1667 if (item_area.Contains (loc)) {
1673 // Put in in last position
1674 if (display_index == -1)
1675 display_index = items.Count - 1;
1677 return display_index;
1680 // When using groups, the items with no group assigned
1681 // belong to the DefaultGroup
1682 int GetDefaultGroupItems ()
1685 foreach (ListViewItem item in items)
1686 if (item.Group == null)
1695 int[,] item_index_matrix;
1697 void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1699 Rectangle area = ClientRectangle;
1701 if (UseCustomColumnWidth)
1702 CalculateCustomColumnWidth ();
1704 if (show_groups && groups.Count > 0 && view != View.List) {
1705 // When groups are used the alignment is always top-aligned
1710 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1711 for (int i = 0; i < groups.InternalCount; i++) {
1712 ListViewGroup group = groups.GetInternalGroup (i);
1713 int items_in_group = group.GetActualItemCount ();
1715 if (items_in_group == 0)
1718 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1719 if (group_cols <= 0)
1721 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1723 group.starting_row = rows;
1724 group.rows = group_rows;
1725 group.starting_item = items;
1726 group.current_item = 0; // Reset layout
1728 cols = Math.Max (group_cols, cols);
1730 items += items_in_group;
1735 // Simple matrix if no groups are used
1737 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1740 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1742 if (UseCustomColumnWidth)
1743 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width) / (double)(custom_column_width));
1745 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1750 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1754 item_index_matrix = new int [rows, cols];
1757 // When using custom column width, we look for the minimum one
1758 void CalculateCustomColumnWidth ()
1760 int min_width = Int32.MaxValue;
1761 for (int i = 0; i < columns.Count; i++) {
1762 int col_width = columns [i].Width;
1764 if (col_width < min_width)
1765 min_width = col_width;
1768 custom_column_width = min_width;
1771 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1773 header_control.Visible = false;
1774 header_control.Size = Size.Empty;
1775 item_control.Visible = true;
1776 item_control.Location = Point.Empty;
1777 ItemSize = item_size; // Cache item size
1779 if (items.Count == 0)
1782 Size sz = item_size;
1784 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1787 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1789 layout_wd = UseCustomColumnWidth ? cols * custom_column_width : cols * (sz.Width + x_spacing) - x_spacing;
1790 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1793 CalculateGroupsLayout (sz, y_spacing, 0);
1796 int row = 0, col = 0;
1798 int display_index = 0;
1800 for (int i = 0; i < items.Count; i++) {
1803 ListViewGroup group = items [i].Group;
1805 group = groups.DefaultGroup;
1807 Point group_items_loc = group.items_area_location;
1808 int current_item = group.current_item++;
1809 int starting_row = group.starting_row;
1811 display_index = group.starting_item + current_item;
1812 row = (current_item / cols);
1813 col = current_item % cols;
1815 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1816 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1818 SetItemLocation (display_index, x, y, row + starting_row, col);
1819 SetItemAtDisplayIndex (display_index, i);
1820 item_index_matrix [row + starting_row, col] = i;
1825 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1826 y = row * (item_size.Height + y_spacing);
1827 display_index = i; // Same as item index in Items
1829 SetItemLocation (i, x, y, row, col);
1830 item_index_matrix [row, col] = i;
1839 if (++col == cols) {
1849 ListViewItem item = items [i];
1851 item.DisplayIndex = display_index;
1852 item.SetPosition (new Point (x, y));
1858 item_control.Size = new Size (layout_wd, layout_ht);
1862 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1865 bool details = view == View.Details;
1867 for (int i = 0; i < groups.InternalCount; i++) {
1868 ListViewGroup group = groups.GetInternalGroup (i);
1869 if (group.ItemCount == 0)
1872 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1875 layout_ht = y; // Update height taking into account Groups' headers heights
1878 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1880 Rectangle client_area = ClientRectangle;
1881 int header_height = text_size.Height + 10;
1883 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1884 group.items_area_location = new Point (0, y_origin + header_height);
1886 int items_area_height = ((item_height + y_spacing) * rows);
1887 return header_height + items_area_height + 10; // Add a small bottom margin
1890 void CalculateDetailsGroupItemsCount ()
1894 for (int i = 0; i < groups.InternalCount; i++) {
1895 ListViewGroup group = groups.GetInternalGroup (i);
1896 int items_in_group = group.GetActualItemCount ();
1898 if (items_in_group == 0)
1901 group.starting_item = items;
1902 group.current_item = 0; // Reset layout.
1903 items += items_in_group;
1908 void LayoutHeader ()
1911 for (int i = 0; i < Columns.Count; i++) {
1912 ColumnHeader col = GetReorderedColumn (i);
1915 col.CalcColumnHeader ();
1921 if (x < ClientRectangle.Width)
1922 x = ClientRectangle.Width;
1924 if (header_style == ColumnHeaderStyle.None) {
1925 header_control.Visible = false;
1926 header_control.Size = Size.Empty;
1927 layout_wd = ClientRectangle.Width;
1929 header_control.Width = x;
1930 header_control.Height = columns.Count > 0 ? columns [0].Ht : ThemeEngine.Current.ListViewGetHeaderHeight (this, Font);
1931 header_control.Visible = true;
1935 void LayoutDetails ()
1939 if (columns.Count == 0) {
1940 item_control.Visible = false;
1941 layout_wd = ClientRectangle.Width;
1942 layout_ht = ClientRectangle.Height;
1946 item_control.Visible = true;
1947 item_control.Location = Point.Empty;
1948 item_control.Width = ClientRectangle.Width;
1950 int item_height = GetDetailsItemHeight ();
1951 ItemSize = new Size (0, item_height); // We only cache Height for details view
1952 int y = header_control.Height;
1954 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1956 CalculateDetailsGroupItemsCount ();
1957 CalculateGroupsLayout (ItemSize, 2, y);
1961 for (int i = 0; i < items.Count; i++) {
1962 ListViewItem item = items [i];
1966 ListViewGroup group = item.Group;
1968 group = groups.DefaultGroup;
1970 int current_item = group.current_item++;
1971 Point group_items_loc = group.items_area_location;
1972 display_index = group.starting_item + current_item;
1974 y = current_item * (item_height + 2) + group_items_loc.Y;
1975 SetItemLocation (display_index, 0, y, 0, 0);
1976 SetItemAtDisplayIndex (display_index, i);
1977 item.SetPosition (new Point (0, y));
1982 SetItemLocation (i, 0, y, 0, 0);
1983 item.SetPosition (new Point (0, y));
1987 if (!virtual_mode) // Virtual mode sets Layout until draw time
1991 item.DisplayIndex = display_index;
1996 // some space for bottom gridline
1997 if (items.Count > 0 && grid_lines)
2001 if (!using_groups) // With groups it has been previously computed
2006 private void AdjustItemsPositionArray (int count)
2008 if (items_location.Length >= count)
2011 // items_location, items_matrix_location and reordered_items_indices must keep the same length
2012 count = Math.Max (count, items_location.Length * 2);
2013 items_location = new Point [count];
2014 items_matrix_location = new ItemMatrixLocation [count];
2015 reordered_items_indices = new int [count];
2018 private void CalculateListView (ListViewAlignment align)
2022 AdjustItemsPositionArray (items.Count);
2029 case View.SmallIcon:
2030 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
2031 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2034 case View.LargeIcon:
2035 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
2036 ThemeEngine.Current.ListViewHorizontalSpacing,
2037 ThemeEngine.Current.ListViewVerticalSpacing);
2041 LayoutIcons (SmallIconItemSize, true,
2042 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2046 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
2047 ThemeEngine.Current.ListViewHorizontalSpacing,
2048 ThemeEngine.Current.ListViewVerticalSpacing);
2053 CalculateScrollBars ();
2056 internal Point GetItemLocation (int index)
2058 Point loc = items_location [index];
2059 loc.X -= h_marker; // Adjust to scroll
2065 internal int GetItemIndex (int display_index)
2067 return reordered_items_indices [display_index];
2070 internal ListViewItem GetItemAtDisplayIndex (int display_index)
2072 return items [reordered_items_indices [display_index]];
2075 internal void SetItemAtDisplayIndex (int display_index, int index)
2077 reordered_items_indices [display_index] = index;
2080 private bool KeySearchString (KeyEventArgs ke)
2082 int current_tickcnt = Environment.TickCount;
2083 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2084 keysearch_text = string.Empty;
2087 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2090 keysearch_text += (char)ke.KeyCode;
2091 keysearch_tickcnt = current_tickcnt;
2093 int prev_focused = FocusedItem == null ? 0 : FocusedItem.DisplayIndex;
2094 int start = prev_focused + 1 < Items.Count ? prev_focused + 1 : 0;
2096 ListViewItem item = FindItemWithText (keysearch_text, false, start, true, true);
2097 if (item != null && prev_focused != item.DisplayIndex) {
2098 selected_indices.Clear ();
2100 SetFocusedItem (item.DisplayIndex);
2101 item.Selected = true;
2102 EnsureVisible (GetItemIndex (item.DisplayIndex));
2108 private void OnItemsChanged ()
2110 ResetSearchString ();
2113 private void ResetSearchString ()
2115 keysearch_text = String.Empty;
2118 int GetAdjustedIndex (Keys key)
2122 if (View == View.Details) {
2125 result = FocusedItem.DisplayIndex - 1;
2128 result = FocusedItem.DisplayIndex + 1;
2129 if (result == items.Count)
2133 int last_index = LastVisibleIndex;
2134 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2135 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2137 if (FocusedItem.DisplayIndex == last_index) {
2138 if (FocusedItem.DisplayIndex < Items.Count - 1) {
2139 int page_size = item_control.Height / ItemSize.Height - 1;
2140 result = FocusedItem.DisplayIndex + page_size - 1;
2141 if (result >= Items.Count)
2142 result = Items.Count - 1;
2145 result = last_index;
2148 int first_index = FirstVisibleIndex;
2149 if (GetItemLocation (first_index).Y < 0)
2151 if (FocusedItem.DisplayIndex == first_index) {
2152 if (first_index > 0) {
2153 int page_size = item_control.Height / ItemSize.Height - 1;
2154 result = first_index - page_size + 1;
2159 result = first_index;
2165 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.DisplayIndex];
2166 int row = item_matrix_location.Row;
2167 int col = item_matrix_location.Col;
2169 int adjusted_index = -1;
2175 adjusted_index = item_index_matrix [row, col - 1];
2179 if (col == (cols - 1))
2181 while (item_index_matrix [row, col + 1] == 0) {
2186 adjusted_index = item_index_matrix [row, col + 1];
2192 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2197 adjusted_index = item_index_matrix [row - 1, col];
2201 if (row == (rows - 1) || row == Items.Count - 1)
2203 while (item_index_matrix [row + 1, col] == 0) {
2208 adjusted_index = item_index_matrix [row + 1, col];
2215 return items [adjusted_index].DisplayIndex;
2218 ListViewItem selection_start;
2220 private bool SelectItems (ArrayList sel_items)
2222 bool changed = false;
2223 foreach (ListViewItem item in SelectedItems)
2224 if (!sel_items.Contains (item)) {
2225 item.Selected = false;
2228 foreach (ListViewItem item in sel_items)
2229 if (!item.Selected) {
2230 item.Selected = true;
2236 private void UpdateMultiSelection (int index, bool reselect)
2238 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2239 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2240 ListViewItem item = GetItemAtDisplayIndex (index);
2242 if (shift_pressed && selection_start != null) {
2243 ArrayList list = new ArrayList ();
2244 int start_index = selection_start.DisplayIndex;
2245 int start = Math.Min (start_index, index);
2246 int end = Math.Max (start_index, index);
2247 if (View == View.Details) {
2248 for (int i = start; i <= end; i++)
2249 list.Add (GetItemAtDisplayIndex (i));
2251 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2252 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2253 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2254 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2255 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2256 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2258 for (int i = 0; i < items.Count; i++) {
2259 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2261 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2262 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2263 list.Add (GetItemAtDisplayIndex (i));
2267 } else if (ctrl_pressed) {
2268 item.Selected = !item.Selected;
2269 selection_start = item;
2272 // do not unselect, and reselect the item
2273 foreach (int itemIndex in SelectedIndices) {
2274 if (index == itemIndex)
2276 items [itemIndex].Selected = false;
2279 SelectedItems.Clear ();
2280 item.Selected = true;
2282 selection_start = item;
2286 internal override bool InternalPreProcessMessage (ref Message msg)
2288 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2289 Keys key_data = (Keys)msg.WParam.ToInt32();
2291 HandleNavKeys (key_data);
2294 return base.InternalPreProcessMessage (ref msg);
2297 bool HandleNavKeys (Keys key_data)
2299 if (Items.Count == 0 || !item_control.Visible)
2302 if (FocusedItem == null)
2307 SelectIndex (Items.Count - 1);
2320 SelectIndex (GetAdjustedIndex (key_data));
2324 SelectIndex (focused_item_index);
2325 ToggleItemsCheckState ();
2328 if (selected_indices.Count > 0)
2329 OnItemActivate (EventArgs.Empty);
2339 void ToggleItemsCheckState ()
2344 // Don't modify check state if StateImageList has less than 2 elements
2345 if (StateImageList != null && StateImageList.Images.Count < 2)
2348 if (SelectedIndices.Count > 0) {
2349 for (int i = 0; i < SelectedIndices.Count; i++) {
2350 ListViewItem item = Items [SelectedIndices [i]];
2351 item.Checked = !item.Checked;
2356 if (FocusedItem != null) {
2357 FocusedItem.Checked = !FocusedItem.Checked;
2358 SelectIndex (FocusedItem.Index);
2362 void SelectIndex (int display_index)
2364 if (display_index == -1)
2368 UpdateMultiSelection (display_index, true);
2369 else if (!GetItemAtDisplayIndex (display_index).Selected)
2370 GetItemAtDisplayIndex (display_index).Selected = true;
2372 SetFocusedItem (display_index);
2373 EnsureVisible (GetItemIndex (display_index)); // Index in Items collection, not display index
2376 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2378 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2381 if (ke.Alt || ke.Control)
2384 ke.Handled = KeySearchString (ke);
2387 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2389 Point loc = PointToClient (Control.MousePosition);
2390 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2393 internal class ItemControl : Control {
2396 ListViewItem clicked_item;
2397 ListViewItem last_clicked_item;
2398 bool hover_processed = false;
2399 bool checking = false;
2400 ListViewItem prev_hovered_item;
2402 ListViewItem prev_tooltip_item;
2405 Point drag_begin = new Point (-1, -1);
2406 internal int dragged_item_index = -1;
2408 ListViewLabelEditTextBox edit_text_box;
2409 internal ListViewItem edit_item;
2410 LabelEditEventArgs edit_args;
2412 public ItemControl (ListView owner)
2415 this.SetStyle (ControlStyles.DoubleBuffer, true);
2416 DoubleClick += new EventHandler(ItemsDoubleClick);
2417 MouseDown += new MouseEventHandler(ItemsMouseDown);
2418 MouseMove += new MouseEventHandler(ItemsMouseMove);
2419 MouseHover += new EventHandler(ItemsMouseHover);
2420 MouseUp += new MouseEventHandler(ItemsMouseUp);
2423 void ItemsDoubleClick (object sender, EventArgs e)
2425 if (owner.activation == ItemActivation.Standard)
2426 owner.OnItemActivate (EventArgs.Empty);
2436 BoxSelect box_select_mode = BoxSelect.None;
2437 IList prev_selection;
2438 Point box_select_start;
2440 Rectangle box_select_rect;
2441 internal Rectangle BoxSelectRectangle {
2442 get { return box_select_rect; }
2444 if (box_select_rect == value)
2447 InvalidateBoxSelectRect ();
2448 box_select_rect = value;
2449 InvalidateBoxSelectRect ();
2453 void InvalidateBoxSelectRect ()
2455 if (BoxSelectRectangle.Size.IsEmpty)
2458 Rectangle edge = BoxSelectRectangle;
2464 edge.Y = BoxSelectRectangle.Bottom - 1;
2466 edge.Y = BoxSelectRectangle.Y - 1;
2468 edge.Height = BoxSelectRectangle.Height + 2;
2470 edge.X = BoxSelectRectangle.Right - 1;
2474 private Rectangle CalculateBoxSelectRectangle (Point pt)
2476 int left = Math.Min (box_select_start.X, pt.X);
2477 int right = Math.Max (box_select_start.X, pt.X);
2478 int top = Math.Min (box_select_start.Y, pt.Y);
2479 int bottom = Math.Max (box_select_start.Y, pt.Y);
2480 return Rectangle.FromLTRB (left, top, right, bottom);
2483 bool BoxIntersectsItem (int index)
2485 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2486 if (owner.View != View.Details) {
2488 r.Y += r.Height / 4;
2492 return BoxSelectRectangle.IntersectsWith (r);
2495 bool BoxIntersectsText (int index)
2497 Rectangle r = owner.GetItemAtDisplayIndex (index).TextBounds;
2498 return BoxSelectRectangle.IntersectsWith (r);
2501 ArrayList BoxSelectedItems {
2503 ArrayList result = new ArrayList ();
2504 for (int i = 0; i < owner.Items.Count; i++) {
2507 // Can't iterate over specific items properties in virtualmode
2508 if (owner.View == View.Details && !owner.FullRowSelect && !owner.VirtualMode)
2510 if (owner.View == View.Details && !owner.FullRowSelect)
2512 intersects = BoxIntersectsText (i);
2514 intersects = BoxIntersectsItem (i);
2517 result.Add (owner.GetItemAtDisplayIndex (i));
2523 private bool PerformBoxSelection (Point pt)
2525 if (box_select_mode == BoxSelect.None)
2528 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2530 ArrayList box_items = BoxSelectedItems;
2534 switch (box_select_mode) {
2536 case BoxSelect.Normal:
2540 case BoxSelect.Control:
2541 items = new ArrayList ();
2542 foreach (int index in prev_selection)
2543 if (!box_items.Contains (owner.Items [index]))
2544 items.Add (owner.Items [index]);
2545 foreach (ListViewItem item in box_items)
2546 if (!prev_selection.Contains (item.Index))
2550 case BoxSelect.Shift:
2552 foreach (ListViewItem item in box_items)
2553 prev_selection.Remove (item.Index);
2554 foreach (int index in prev_selection)
2555 items.Add (owner.Items [index]);
2559 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2563 owner.SelectItems (items);
2569 private void ItemsMouseDown (object sender, MouseEventArgs me)
2571 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2572 if (owner.items.Count == 0)
2575 bool box_selecting = false;
2576 Size item_size = owner.ItemSize;
2577 Point pt = new Point (me.X, me.Y);
2578 for (int i = 0; i < owner.items.Count; i++) {
2579 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2580 if (!item_rect.Contains (pt))
2583 // Actual item in 'i' position
2584 ListViewItem item = owner.GetItemAtDisplayIndex (i);
2586 if (item.CheckRectReal.Contains (pt)) {
2587 // Don't modify check state if we have only one image
2588 // and if we are in 1.1 profile only take into account
2590 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2597 // Generate an extra ItemCheck event when we got two clicks
2598 // (Match weird .Net behaviour)
2600 item.Checked = !item.Checked;
2602 item.Checked = !item.Checked;
2607 if (owner.View == View.Details) {
2608 bool over_text = item.TextBounds.Contains (pt);
2609 if (owner.FullRowSelect) {
2610 clicked_item = owner.items [i];
2611 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2612 if (!over_text && over_item_column && owner.MultiSelect)
2613 box_selecting = true;
2614 } else if (over_text)
2615 clicked_item = item;
2617 owner.SetFocusedItem (i);
2619 clicked_item = item;
2625 if (clicked_item != null) {
2626 bool changed = !clicked_item.Selected;
2627 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2628 owner.SetFocusedItem (clicked_item.DisplayIndex);
2630 if (owner.MultiSelect) {
2631 bool reselect = (!owner.LabelEdit || changed);
2632 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2633 owner.UpdateMultiSelection (clicked_item.DisplayIndex, reselect);
2635 clicked_item.Selected = true;
2639 if (owner.VirtualMode && changed) {
2640 // Broken event - It's not fired from Item.Selected also
2641 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2642 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2644 owner.OnVirtualItemsSelectionRangeChanged (args);
2647 // Report clicks only if the item was clicked. On MS the
2648 // clicks are only raised if you click an item
2650 if (me.Clicks > 1) {
2651 if (owner.CheckBoxes)
2652 clicked_item.Checked = !clicked_item.Checked;
2653 } else if (me.Clicks == 1) {
2654 if (owner.LabelEdit && !changed)
2655 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2658 if (owner.MultiSelect)
2659 box_selecting = true;
2660 else if (owner.SelectedItems.Count > 0)
2661 owner.SelectedItems.Clear ();
2664 if (box_selecting) {
2665 Keys mods = XplatUI.State.ModifierKeys;
2666 if ((mods & Keys.Shift) != 0)
2667 box_select_mode = BoxSelect.Shift;
2668 else if ((mods & Keys.Control) != 0)
2669 box_select_mode = BoxSelect.Control;
2671 box_select_mode = BoxSelect.Normal;
2672 box_select_start = pt;
2673 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2677 private void ItemsMouseMove (object sender, MouseEventArgs me)
2679 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2681 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2685 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2686 !hover_processed && owner.Activation != ItemActivation.OneClick
2688 && !owner.ShowItemToolTips
2693 Point pt = PointToClient (Control.MousePosition);
2694 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2696 if (hover_processed && item != null && item != prev_hovered_item) {
2697 hover_processed = false;
2698 XplatUI.ResetMouseHover (Handle);
2701 // Need to invalidate the item in HotTracking to show/hide the underline style
2702 if (owner.Activation == ItemActivation.OneClick) {
2703 if (item == null && owner.HotItemIndex != -1) {
2705 if (owner.HotTracking)
2706 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2709 Cursor = Cursors.Default;
2710 owner.HotItemIndex = -1;
2711 } else if (item != null && owner.HotItemIndex == -1) {
2713 if (owner.HotTracking)
2714 Invalidate (item.Bounds);
2717 Cursor = Cursors.Hand;
2718 owner.HotItemIndex = item.Index;
2722 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2723 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2725 drag_begin = new Point (me.X, me.Y);
2726 dragged_item_index = item.Index;
2730 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2731 if (!r.Contains (me.X, me.Y)) {
2732 ListViewItem dragged_item = owner.items [dragged_item_index];
2733 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2735 drag_begin = new Point (-1, -1);
2736 dragged_item_index = -1;
2742 if (owner.ShowItemToolTips) {
2744 owner.item_tooltip.Active = false;
2745 prev_tooltip_item = null;
2746 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2747 owner.item_tooltip.Active = true;
2748 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2749 prev_tooltip_item = item;
2756 private void ItemsMouseHover (object sender, EventArgs e)
2758 if (owner.hover_pending) {
2759 owner.OnMouseHover (e);
2760 owner.hover_pending = false;
2766 hover_processed = true;
2767 Point pt = PointToClient (Control.MousePosition);
2768 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2772 prev_hovered_item = item;
2774 if (owner.HoverSelection) {
2775 if (owner.MultiSelect)
2776 owner.UpdateMultiSelection (item.Index, true);
2778 item.Selected = true;
2780 owner.SetFocusedItem (item.DisplayIndex);
2781 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2785 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2789 void HandleClicks (MouseEventArgs me)
2791 // if the click is not on an item,
2792 // clicks remains as 0
2795 owner.OnDoubleClick (EventArgs.Empty);
2796 } else if (clicks == 1) {
2797 owner.OnClick (EventArgs.Empty);
2799 owner.OnDoubleClick (EventArgs.Empty);
2800 owner.OnMouseDoubleClick (me);
2801 } else if (clicks == 1) {
2802 owner.OnClick (EventArgs.Empty);
2803 owner.OnMouseClick (me);
2810 private void ItemsMouseUp (object sender, MouseEventArgs me)
2812 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2813 HandleClicks (owner_me);
2816 if (owner.Items.Count == 0) {
2818 owner.OnMouseUp (owner_me);
2822 Point pt = new Point (me.X, me.Y);
2824 Rectangle rect = Rectangle.Empty;
2825 if (clicked_item != null) {
2826 if (owner.view == View.Details && !owner.full_row_select)
2827 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2829 rect = clicked_item.Bounds;
2831 if (rect.Contains (pt)) {
2832 switch (owner.activation) {
2833 case ItemActivation.OneClick:
2834 owner.OnItemActivate (EventArgs.Empty);
2837 case ItemActivation.TwoClick:
2838 if (last_clicked_item == clicked_item) {
2839 owner.OnItemActivate (EventArgs.Empty);
2840 last_clicked_item = null;
2842 last_clicked_item = clicked_item;
2845 // DoubleClick activation is handled in another handler
2849 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2850 // Need this to clean up background clicks
2851 owner.SelectedItems.Clear ();
2855 owner.OnMouseUp (owner_me);
2858 private void ResetMouseState ()
2860 clicked_item = null;
2861 box_select_start = Point.Empty;
2862 BoxSelectRectangle = Rectangle.Empty;
2863 prev_selection = null;
2864 box_select_mode = BoxSelect.None;
2867 // Clean these bits in case the mouse buttons were
2868 // released before firing ItemDrag
2869 dragged_item_index = -1;
2870 drag_begin = new Point (-1, -1);
2873 private void LabelEditFinished (object sender, EventArgs e)
2875 EndEdit (edit_item);
2878 private void LabelEditCancelled (object sender, EventArgs e)
2880 edit_args.SetLabel (null);
2881 EndEdit (edit_item);
2884 private void LabelTextChanged (object sender, EventArgs e)
2886 if (edit_args != null)
2887 edit_args.SetLabel (edit_text_box.Text);
2890 internal void BeginEdit (ListViewItem item)
2892 if (edit_item != null)
2893 EndEdit (edit_item);
2895 if (edit_text_box == null) {
2896 edit_text_box = new ListViewLabelEditTextBox ();
2897 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2898 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2899 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2900 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2901 edit_text_box.Visible = false;
2902 Controls.Add (edit_text_box);
2905 item.EnsureVisible();
2907 edit_text_box.Reset ();
2909 switch (owner.view) {
2911 case View.SmallIcon:
2913 edit_text_box.TextAlign = HorizontalAlignment.Left;
2914 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2915 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2916 edit_text_box.Width = (int)sizef.Width + 4;
2917 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2918 edit_text_box.WordWrap = false;
2919 edit_text_box.Multiline = false;
2921 case View.LargeIcon:
2922 edit_text_box.TextAlign = HorizontalAlignment.Center;
2923 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2924 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2925 edit_text_box.Width = (int)sizef.Width + 4;
2926 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2927 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2928 edit_text_box.WordWrap = true;
2929 edit_text_box.Multiline = true;
2935 edit_text_box.Text = item.Text;
2936 edit_text_box.Font = item.Font;
2937 edit_text_box.Visible = true;
2938 edit_text_box.Focus ();
2939 edit_text_box.SelectAll ();
2941 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2942 owner.OnBeforeLabelEdit (edit_args);
2944 if (edit_args.CancelEdit)
2948 internal void CancelEdit (ListViewItem item)
2950 // do nothing if there's no item being edited, or if the
2951 // item being edited is not the one passed in
2952 if (edit_item == null || edit_item != item)
2955 edit_args.SetLabel (null);
2959 internal void EndEdit (ListViewItem item)
2961 // do nothing if there's no item being edited, or if the
2962 // item being edited is not the one passed in
2963 if (edit_item == null || edit_item != item)
2966 if (edit_text_box != null) {
2967 if (edit_text_box.Visible)
2968 edit_text_box.Visible = false;
2969 // ensure listview gets focus
2973 // Same as TreeView.EndEdit: need to have focus in synch
2974 Application.DoEvents ();
2977 // Create a new instance, since we could get a call to BeginEdit
2978 // from the handler and have fields out of synch
2980 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2983 owner.OnAfterLabelEdit (args);
2984 if (!args.CancelEdit && args.Label != null)
2985 item.Text = args.Label;
2988 internal override void OnPaintInternal (PaintEventArgs pe)
2990 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2993 protected override void WndProc (ref Message m)
2995 switch ((Msg)m.Msg) {
2996 case Msg.WM_KILLFOCUS:
2997 owner.Select (false, true);
2999 case Msg.WM_SETFOCUS:
3000 owner.Select (false, true);
3002 case Msg.WM_LBUTTONDOWN:
3004 owner.Select (false, true);
3006 case Msg.WM_RBUTTONDOWN:
3008 owner.Select (false, true);
3013 base.WndProc (ref m);
3017 internal class ListViewLabelEditTextBox : TextBox
3022 int max_height = -1;
3023 int min_height = -1;
3025 int old_number_lines = 1;
3027 SizeF text_size_one_char;
3029 public ListViewLabelEditTextBox ()
3031 min_height = DefaultSize.Height;
3032 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3035 public int MaxWidth {
3037 if (value < min_width)
3038 max_width = min_width;
3044 public int MaxHeight {
3046 if (value < min_height)
3047 max_height = min_height;
3053 public new int Width {
3063 public override Font Font {
3069 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3073 protected override void OnTextChanged (EventArgs e)
3075 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3077 int new_width = (int)text_size.Width + 8;
3080 ResizeTextBoxWidth (new_width);
3082 if (Width != max_width)
3083 ResizeTextBoxWidth (new_width);
3085 int number_lines = Lines.Length;
3087 if (number_lines != old_number_lines) {
3088 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3089 old_number_lines = number_lines;
3091 ResizeTextBoxHeight (new_height);
3095 base.OnTextChanged (e);
3098 protected override bool IsInputKey (Keys key_data)
3100 if ((key_data & Keys.Alt) == 0) {
3101 switch (key_data & Keys.KeyCode) {
3108 return base.IsInputKey (key_data);
3111 protected override void OnKeyDown (KeyEventArgs e)
3116 switch (e.KeyCode) {
3120 OnEditingFinished (e);
3125 OnEditingCancelled (e);
3130 protected override void OnLostFocus (EventArgs e)
3133 OnEditingFinished (e);
3137 protected void OnEditingCancelled (EventArgs e)
3139 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3144 protected void OnEditingFinished (EventArgs e)
3146 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3151 private void ResizeTextBoxWidth (int new_width)
3153 if (new_width > max_width)
3154 base.Width = max_width;
3156 if (new_width >= min_width)
3157 base.Width = new_width;
3159 base.Width = min_width;
3162 private void ResizeTextBoxHeight (int new_height)
3164 if (new_height > max_height)
3165 base.Height = max_height;
3167 if (new_height >= min_height)
3168 base.Height = new_height;
3170 base.Height = min_height;
3173 public void Reset ()
3180 old_number_lines = 1;
3182 Text = String.Empty;
3187 static object EditingCancelledEvent = new object ();
3188 public event EventHandler EditingCancelled {
3189 add { Events.AddHandler (EditingCancelledEvent, value); }
3190 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3193 static object EditingFinishedEvent = new object ();
3194 public event EventHandler EditingFinished {
3195 add { Events.AddHandler (EditingFinishedEvent, value); }
3196 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3200 internal override void OnPaintInternal (PaintEventArgs pe)
3205 CalculateScrollBars ();
3208 void FocusChanged (object o, EventArgs args)
3210 if (Items.Count == 0)
3213 if (FocusedItem == null)
3216 ListViewItem focused_item = FocusedItem;
3218 if (focused_item.ListView != null) {
3219 item_control.Invalidate (focused_item.Bounds);
3220 focused_item.Layout ();
3221 item_control.Invalidate (focused_item.Bounds);
3225 private void ListView_Invalidated (object sender, InvalidateEventArgs e)
3227 // When the ListView is invalidated, we need to invalidate
3228 // the child controls.
3229 header_control.Invalidate ();
3230 item_control.Invalidate ();
3233 private void ListView_MouseEnter (object sender, EventArgs args)
3235 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3238 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3240 if (Items.Count == 0)
3243 int lines = me.Delta / 120;
3250 case View.SmallIcon:
3251 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3253 case View.LargeIcon:
3254 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3257 Scroll (h_scroll, -ItemSize.Width * lines);
3261 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3267 private void ListView_SizeChanged (object sender, EventArgs e)
3269 CalculateListView (alignment);
3272 private void SetFocusedItem (int display_index)
3274 if (display_index != -1)
3275 GetItemAtDisplayIndex (display_index).Focused = true;
3276 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3277 GetItemAtDisplayIndex (focused_item_index).Focused = false;
3279 focused_item_index = display_index;
3282 private void HorizontalScroller (object sender, EventArgs e)
3284 item_control.EndEdit (item_control.edit_item);
3286 // Avoid unnecessary flickering, when button is
3287 // kept pressed at the end
3288 if (h_marker != h_scroll.Value) {
3290 int pixels = h_marker - h_scroll.Value;
3292 h_marker = h_scroll.Value;
3293 if (header_control.Visible)
3294 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3296 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3300 private void VerticalScroller (object sender, EventArgs e)
3302 item_control.EndEdit (item_control.edit_item);
3304 // Avoid unnecessary flickering, when button is
3305 // kept pressed at the end
3306 if (v_marker != v_scroll.Value) {
3307 int pixels = v_marker - v_scroll.Value;
3308 Rectangle area = item_control.ClientRectangle;
3309 if (header_control.Visible) {
3310 area.Y += header_control.Height;
3311 area.Height -= header_control.Height;
3314 v_marker = v_scroll.Value;
3315 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3319 internal override bool IsInputCharInternal (char charCode)
3323 #endregion // Internal Methods Properties
3325 #region Protected Methods
3326 protected override void CreateHandle ()
3328 base.CreateHandle ();
3329 for (int i = 0; i < SelectedItems.Count; i++)
3330 OnSelectedIndexChanged (EventArgs.Empty);
3333 protected override void Dispose (bool disposing)
3336 h_scroll.Dispose ();
3337 v_scroll.Dispose ();
3339 large_image_list = null;
3340 small_image_list = null;
3341 state_image_list = null;
3343 foreach (ColumnHeader col in columns)
3344 col.SetListView (null);
3347 if (!virtual_mode) // In virtual mode we don't save the items
3349 foreach (ListViewItem item in items)
3353 base.Dispose (disposing);
3356 protected override bool IsInputKey (Keys keyData)
3373 return base.IsInputKey (keyData);
3376 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3378 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3384 protected override void OnBackgroundImageChanged (EventArgs e)
3386 item_control.BackgroundImage = BackgroundImage;
3387 base.OnBackgroundImageChanged (e);
3391 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3393 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3398 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3400 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3406 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3408 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3413 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3415 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3420 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3422 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3428 protected override void OnEnabledChanged (EventArgs e)
3430 base.OnEnabledChanged (e);
3434 protected override void OnFontChanged (EventArgs e)
3436 base.OnFontChanged (e);
3440 protected override void OnHandleCreated (EventArgs e)
3442 base.OnHandleCreated (e);
3443 CalculateListView (alignment);
3445 if (!virtual_mode) // Sorting is not allowed in virtual mode
3450 protected override void OnHandleDestroyed (EventArgs e)
3452 base.OnHandleDestroyed (e);
3455 protected virtual void OnItemActivate (EventArgs e)
3457 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3462 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3464 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3470 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3472 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3478 protected virtual void OnItemDrag (ItemDragEventArgs e)
3480 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3486 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3488 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3493 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3495 ListViewItemSelectionChangedEventHandler eh =
3496 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3501 protected override void OnMouseHover (EventArgs e)
3503 base.OnMouseHover (e);
3506 protected override void OnParentChanged (EventArgs e)
3508 base.OnParentChanged (e);
3512 protected virtual void OnSelectedIndexChanged (EventArgs e)
3514 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3519 protected override void OnSystemColorsChanged (EventArgs e)
3521 base.OnSystemColorsChanged (e);
3525 protected internal virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3527 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3532 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3534 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3539 [EditorBrowsable (EditorBrowsableState.Advanced)]
3540 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3542 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3547 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3549 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3554 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3556 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3557 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3563 protected void RealizeProperties ()
3568 protected void UpdateExtendedStyles ()
3573 bool refocusing = false;
3575 protected override void WndProc (ref Message m)
3577 switch ((Msg)m.Msg) {
3578 case Msg.WM_KILLFOCUS:
3579 Control receiver = Control.FromHandle (m.WParam);
3580 if (receiver == item_control) {
3586 case Msg.WM_SETFOCUS:
3596 base.WndProc (ref m);
3598 #endregion // Protected Methods
3600 #region Public Instance Methods
3601 public void ArrangeIcons ()
3603 ArrangeIcons (this.alignment);
3606 public void ArrangeIcons (ListViewAlignment value)
3608 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3609 if (view == View.LargeIcon || view == View.SmallIcon) {
3610 this.CalculateListView (value);
3611 // we have done the calculations already
3612 this.Redraw (false);
3617 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3619 if (columnIndex < 0 || columnIndex >= columns.Count)
3620 throw new ArgumentOutOfRangeException ("columnIndex");
3622 columns [columnIndex].AutoResize (headerAutoResize);
3625 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3628 foreach (ColumnHeader col in columns)
3629 col.AutoResize (headerAutoResize);
3634 public void BeginUpdate ()
3636 // flag to avoid painting
3640 public void Clear ()
3643 items.Clear (); // Redraw (true) called here
3646 public void EndUpdate ()
3648 // flag to avoid painting
3651 // probably, now we need a redraw with recalculations
3655 public void EnsureVisible (int index)
3657 if (index < 0 || index >= items.Count || scrollable == false)
3660 Rectangle view_rect = item_control.ClientRectangle;
3661 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3663 if (view_rect.Contains (bounds))
3666 if (View != View.Details) {
3667 if (bounds.Left < 0)
3668 h_scroll.Value += bounds.Left;
3669 else if (bounds.Right > view_rect.Right)
3670 h_scroll.Value += (bounds.Right - view_rect.Right);
3674 v_scroll.Value += bounds.Top;
3675 else if (bounds.Bottom > view_rect.Bottom)
3676 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3680 public ListViewItem FindItemWithText (string text)
3682 if (items.Count == 0)
3685 return FindItemWithText (text, true, 0, true);
3688 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3690 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true, false);
3693 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3695 return FindItemWithText (text, includeSubItemsInSearch, startIndex, isPrefixSearch, false);
3699 internal ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch, bool roundtrip)
3701 if (startIndex < 0 || startIndex >= items.Count)
3702 throw new ArgumentOutOfRangeException ("startIndex");
3705 throw new ArgumentNullException ("text");
3709 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3710 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3711 SearchDirectionHint.Down, startIndex);
3713 OnSearchForVirtualItem (args);
3714 int idx = args.Index;
3715 if (idx >= 0 && idx < virtual_list_size)
3724 ListViewItem lvi = items [i];
3726 if (isPrefixSearch) { // prefix search
3727 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (lvi.Text, text, CompareOptions.IgnoreCase))
3729 } else if (String.Compare (lvi.Text, text, true) == 0) // match
3732 if (i + 1 >= items.Count) {
3740 if (i == startIndex)
3744 // Subitems have a minor priority, so we have to do a second linear search
3745 // Also, we don't need to to a roundtrip search for them by now
3746 if (includeSubItemsInSearch) {
3747 for (i = startIndex; i < items.Count; i++) {
3748 ListViewItem lvi = items [i];
3749 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3750 if (isPrefixSearch) {
3751 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (sub_item.Text,
3752 text, CompareOptions.IgnoreCase))
3754 } else if (String.Compare (sub_item.Text, text, true) == 0)
3763 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3765 return FindNearestItem (searchDirection, new Point (x, y));
3768 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3770 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3771 throw new ArgumentOutOfRangeException ("searchDirection");
3773 if (view != View.LargeIcon && view != View.SmallIcon)
3774 throw new InvalidOperationException ();
3777 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3778 false, false, String.Empty, point,
3781 OnSearchForVirtualItem (args);
3782 int idx = args.Index;
3783 if (idx >= 0 && idx < virtual_list_size)
3789 ListViewItem item = null;
3790 int min_dist = Int32.MaxValue;
3793 // It looks like .Net does a previous adjustment
3796 case SearchDirectionHint.Up:
3797 point.Y -= item_size.Height;
3799 case SearchDirectionHint.Down:
3800 point.Y += item_size.Height;
3802 case SearchDirectionHint.Left:
3803 point.X -= item_size.Width;
3805 case SearchDirectionHint.Right:
3806 point.X += item_size.Width;
3810 for (int i = 0; i < items.Count; i++) {
3811 Point item_loc = GetItemLocation (i);
3813 if (dir == SearchDirectionHint.Up) {
3814 if (point.Y < item_loc.Y)
3816 } else if (dir == SearchDirectionHint.Down) {
3817 if (point.Y > item_loc.Y)
3819 } else if (dir == SearchDirectionHint.Left) {
3820 if (point.X < item_loc.X)
3822 } else if (dir == SearchDirectionHint.Right) {
3823 if (point.X > item_loc.X)
3827 int x_dist = point.X - item_loc.X;
3828 int y_dist = point.Y - item_loc.Y;
3830 int dist = x_dist * x_dist + y_dist * y_dist;
3831 if (dist < min_dist) {
3841 public ListViewItem GetItemAt (int x, int y)
3843 Size item_size = ItemSize;
3844 for (int i = 0; i < items.Count; i++) {
3845 Point item_location = GetItemLocation (i);
3846 Rectangle item_rect = new Rectangle (item_location, item_size);
3847 if (item_rect.Contains (x, y))
3854 public Rectangle GetItemRect (int index)
3856 return GetItemRect (index, ItemBoundsPortion.Entire);
3859 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3861 if (index < 0 || index >= items.Count)
3862 throw new IndexOutOfRangeException ("index");
3864 return items [index].GetBounds (portion);
3868 public ListViewHitTestInfo HitTest (Point point)
3870 return HitTest (point.X, point.Y);
3873 public ListViewHitTestInfo HitTest (int x, int y)
3876 throw new ArgumentOutOfRangeException ("x");
3878 throw new ArgumentOutOfRangeException ("y");
3880 ListViewItem item = GetItemAt (x, y);
3882 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3884 ListViewHitTestLocations locations = 0;
3885 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3886 locations |= ListViewHitTestLocations.Label;
3887 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3888 locations |= ListViewHitTestLocations.Image;
3889 else if (item.CheckRectReal.Contains (x, y))
3890 locations |= ListViewHitTestLocations.StateImage;
3892 ListViewItem.ListViewSubItem subitem = null;
3893 if (view == View.Details)
3894 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3895 if (si.Bounds.Contains (x, y)) {
3900 return new ListViewHitTestInfo (item, subitem, locations);
3903 [EditorBrowsable (EditorBrowsableState.Advanced)]
3904 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3906 if (startIndex < 0 || startIndex >= items.Count)
3907 throw new ArgumentOutOfRangeException ("startIndex");
3908 if (endIndex < 0 || endIndex >= items.Count)
3909 throw new ArgumentOutOfRangeException ("endIndex");
3910 if (startIndex > endIndex)
3911 throw new ArgumentException ("startIndex");
3916 for (int i = startIndex; i <= endIndex; i++)
3917 item_control.Invalidate (items [i].Bounds);
3919 if (!invalidateOnly)
3928 throw new InvalidOperationException ();
3934 // we need this overload to reuse the logic for sorting, while allowing
3935 // redrawing to be done by caller or have it done by this method when
3936 // sorting is really performed
3938 // ListViewItemCollection's Add and AddRange methods call this overload
3939 // with redraw set to false, as they take care of redrawing themselves
3940 // (they even want to redraw the listview if no sort is performed, as
3941 // an item was added), while ListView.Sort () only wants to redraw if
3942 // sorting was actually performed
3943 private void Sort (bool redraw)
3945 if (!IsHandleCreated || item_sorter == null) {
3949 items.Sort (item_sorter);
3954 public override string ToString ()
3956 int count = this.Items.Count;
3959 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3961 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3963 #endregion // Public Instance Methods
3968 class HeaderControl : Control {
3971 bool column_resize_active = false;
3972 ColumnHeader resize_column;
3973 ColumnHeader clicked_column;
3974 ColumnHeader drag_column;
3976 int drag_to_index = -1;
3977 ColumnHeader entered_column_header;
3979 public HeaderControl (ListView owner)
3982 this.SetStyle (ControlStyles.DoubleBuffer, true);
3983 MouseDown += new MouseEventHandler (HeaderMouseDown);
3984 MouseMove += new MouseEventHandler (HeaderMouseMove);
3985 MouseUp += new MouseEventHandler (HeaderMouseUp);
3986 MouseLeave += new EventHandler (OnMouseLeave);
3989 internal ColumnHeader EnteredColumnHeader {
3990 get { return entered_column_header; }
3992 if (entered_column_header == value)
3994 if (ThemeEngine.Current.ListViewHasHotHeaderStyle) {
3995 Region region_to_invalidate = new Region ();
3996 region_to_invalidate.MakeEmpty ();
3997 if (entered_column_header != null)
3998 region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
3999 entered_column_header = value;
4000 if (entered_column_header != null)
4001 region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
4002 Invalidate (region_to_invalidate);
4003 region_to_invalidate.Dispose ();
4005 entered_column_header = value;
4009 void OnMouseLeave (object sender, EventArgs e)
4011 EnteredColumnHeader = null;
4014 private ColumnHeader ColumnAtX (int x)
4016 Point pt = new Point (x, 0);
4017 ColumnHeader result = null;
4018 foreach (ColumnHeader col in owner.Columns) {
4019 if (col.Rect.Contains (pt)) {
4027 private int GetReorderedIndex (ColumnHeader col)
4029 if (owner.reordered_column_indices == null)
4032 for (int i = 0; i < owner.Columns.Count; i++)
4033 if (owner.reordered_column_indices [i] == col.Index)
4035 throw new Exception ("Column index missing from reordered array");
4038 private void HeaderMouseDown (object sender, MouseEventArgs me)
4040 if (resize_column != null) {
4041 column_resize_active = true;
4046 clicked_column = ColumnAtX (me.X + owner.h_marker);
4048 if (clicked_column != null) {
4050 if (owner.AllowColumnReorder) {
4052 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
4053 drag_column.Rect = clicked_column.Rect;
4054 drag_to_index = GetReorderedIndex (clicked_column);
4056 clicked_column.Pressed = true;
4057 Invalidate (clicked_column);
4062 void Invalidate (ColumnHeader columnHeader)
4064 Invalidate (GetColumnHeaderInvalidateArea (columnHeader));
4067 Rectangle GetColumnHeaderInvalidateArea (ColumnHeader columnHeader)
4069 Rectangle bounds = columnHeader.Rect;
4070 bounds.X -= owner.h_marker;
4076 column_resize_active = false;
4077 resize_column = null;
4079 Cursor = Cursors.Default;
4082 private void HeaderMouseMove (object sender, MouseEventArgs me)
4084 Point pt = new Point (me.X + owner.h_marker, me.Y);
4086 if (column_resize_active) {
4087 int width = pt.X - resize_column.X;
4091 if (!owner.CanProceedWithResize (resize_column, width)){
4095 resize_column.Width = width;
4099 resize_column = null;
4101 if (clicked_column != null) {
4102 if (owner.AllowColumnReorder) {
4105 r = drag_column.Rect;
4106 r.X = clicked_column.Rect.X + me.X - drag_x;
4107 drag_column.Rect = r;
4109 int x = me.X + owner.h_marker;
4110 ColumnHeader over = ColumnAtX (x);
4112 drag_to_index = owner.Columns.Count;
4113 else if (x < over.X + over.Width / 2)
4114 drag_to_index = GetReorderedIndex (over);
4116 drag_to_index = GetReorderedIndex (over) + 1;
4119 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
4120 bool pressed = clicked_column.Pressed;
4121 clicked_column.Pressed = over == clicked_column;
4122 if (clicked_column.Pressed ^ pressed)
4123 Invalidate (clicked_column);
4128 for (int i = 0; i < owner.Columns.Count; i++) {
4129 Rectangle zone = owner.Columns [i].Rect;
4130 if (zone.Contains (pt))
4131 EnteredColumnHeader = owner.Columns [i];
4132 zone.X = zone.Right - 5;
4134 if (zone.Contains (pt)) {
4135 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
4137 resize_column = owner.Columns [i];
4142 if (resize_column == null)
4143 Cursor = Cursors.Default;
4145 Cursor = Cursors.VSplit;
4148 void HeaderMouseUp (object sender, MouseEventArgs me)
4152 if (column_resize_active) {
4153 int column_idx = resize_column.Index;
4155 owner.RaiseColumnWidthChanged (column_idx);
4159 if (clicked_column != null && clicked_column.Pressed) {
4160 clicked_column.Pressed = false;
4161 Invalidate (clicked_column);
4162 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4165 if (drag_column != null && owner.AllowColumnReorder) {
4167 if (drag_to_index > GetReorderedIndex (clicked_column))
4169 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4170 owner.ReorderColumn (clicked_column, drag_to_index, true);
4175 clicked_column = null;
4178 internal override void OnPaintInternal (PaintEventArgs pe)
4183 Theme theme = ThemeEngine.Current;
4184 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4186 if (drag_column == null)
4190 if (drag_to_index == owner.Columns.Count)
4191 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4193 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4194 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4197 protected override void WndProc (ref Message m)
4199 switch ((Msg)m.Msg) {
4200 case Msg.WM_SETFOCUS:
4204 base.WndProc (ref m);
4210 private class ItemComparer : IComparer {
4211 readonly SortOrder sort_order;
4213 public ItemComparer (SortOrder sortOrder)
4215 sort_order = sortOrder;
4218 public int Compare (object x, object y)
4220 ListViewItem item_x = x as ListViewItem;
4221 ListViewItem item_y = y as ListViewItem;
4222 if (sort_order == SortOrder.Ascending)
4223 return String.Compare (item_x.Text, item_y.Text);
4225 return String.Compare (item_y.Text, item_x.Text);
4230 [ListBindable (false)]
4232 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4234 private readonly ListView owner;
4236 #region Public Constructor
4237 public CheckedIndexCollection (ListView owner)
4241 #endregion // Public Constructor
4243 #region Public Properties
4246 get { return owner.CheckedItems.Count; }
4249 public bool IsReadOnly {
4250 get { return true; }
4253 public int this [int index] {
4255 int [] indices = GetIndices ();
4256 if (index < 0 || index >= indices.Length)
4257 throw new ArgumentOutOfRangeException ("index");
4258 return indices [index];
4262 bool ICollection.IsSynchronized {
4263 get { return false; }
4266 object ICollection.SyncRoot {
4267 get { return this; }
4270 bool IList.IsFixedSize {
4271 get { return true; }
4274 object IList.this [int index] {
4275 get { return this [index]; }
4276 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4278 #endregion // Public Properties
4280 #region Public Methods
4281 public bool Contains (int checkedIndex)
4283 int [] indices = GetIndices ();
4284 for (int i = 0; i < indices.Length; i++) {
4285 if (indices [i] == checkedIndex)
4291 public IEnumerator GetEnumerator ()
4293 int [] indices = GetIndices ();
4294 return indices.GetEnumerator ();
4297 void ICollection.CopyTo (Array dest, int index)
4299 int [] indices = GetIndices ();
4300 Array.Copy (indices, 0, dest, index, indices.Length);
4303 int IList.Add (object value)
4305 throw new NotSupportedException ("Add operation is not supported.");
4310 throw new NotSupportedException ("Clear operation is not supported.");
4313 bool IList.Contains (object checkedIndex)
4315 if (!(checkedIndex is int))
4317 return Contains ((int) checkedIndex);
4320 int IList.IndexOf (object checkedIndex)
4322 if (!(checkedIndex is int))
4324 return IndexOf ((int) checkedIndex);
4327 void IList.Insert (int index, object value)
4329 throw new NotSupportedException ("Insert operation is not supported.");
4332 void IList.Remove (object value)
4334 throw new NotSupportedException ("Remove operation is not supported.");
4337 void IList.RemoveAt (int index)
4339 throw new NotSupportedException ("RemoveAt operation is not supported.");
4342 public int IndexOf (int checkedIndex)
4344 int [] indices = GetIndices ();
4345 for (int i = 0; i < indices.Length; i++) {
4346 if (indices [i] == checkedIndex)
4351 #endregion // Public Methods
4353 private int [] GetIndices ()
4355 ArrayList checked_items = owner.CheckedItems.List;
4356 int [] indices = new int [checked_items.Count];
4357 for (int i = 0; i < checked_items.Count; i++) {
4358 ListViewItem item = (ListViewItem) checked_items [i];
4359 indices [i] = item.Index;
4363 } // CheckedIndexCollection
4366 [ListBindable (false)]
4368 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4370 private readonly ListView owner;
4371 private ArrayList list;
4373 #region Public Constructor
4374 public CheckedListViewItemCollection (ListView owner)
4377 this.owner.Items.Changed += new CollectionChangedHandler (
4378 ItemsCollection_Changed);
4380 #endregion // Public Constructor
4382 #region Public Properties
4386 if (!owner.CheckBoxes)
4392 public bool IsReadOnly {
4393 get { return true; }
4396 public ListViewItem this [int index] {
4399 if (owner.VirtualMode)
4400 throw new InvalidOperationException ();
4402 ArrayList checked_items = List;
4403 if (index < 0 || index >= checked_items.Count)
4404 throw new ArgumentOutOfRangeException ("index");
4405 return (ListViewItem) checked_items [index];
4410 public virtual ListViewItem this [string key] {
4412 int idx = IndexOfKey (key);
4413 return idx == -1 ? null : (ListViewItem) List [idx];
4418 bool ICollection.IsSynchronized {
4419 get { return false; }
4422 object ICollection.SyncRoot {
4423 get { return this; }
4426 bool IList.IsFixedSize {
4427 get { return true; }
4430 object IList.this [int index] {
4431 get { return this [index]; }
4432 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4434 #endregion // Public Properties
4436 #region Public Methods
4437 public bool Contains (ListViewItem item)
4439 if (!owner.CheckBoxes)
4441 return List.Contains (item);
4445 public virtual bool ContainsKey (string key)
4447 return IndexOfKey (key) != -1;
4451 public void CopyTo (Array dest, int index)
4454 if (owner.VirtualMode)
4455 throw new InvalidOperationException ();
4457 if (!owner.CheckBoxes)
4459 List.CopyTo (dest, index);
4462 public IEnumerator GetEnumerator ()
4465 if (owner.VirtualMode)
4466 throw new InvalidOperationException ();
4468 if (!owner.CheckBoxes)
4469 return (new ListViewItem [0]).GetEnumerator ();
4470 return List.GetEnumerator ();
4473 int IList.Add (object value)
4475 throw new NotSupportedException ("Add operation is not supported.");
4480 throw new NotSupportedException ("Clear operation is not supported.");
4483 bool IList.Contains (object item)
4485 if (!(item is ListViewItem))
4487 return Contains ((ListViewItem) item);
4490 int IList.IndexOf (object item)
4492 if (!(item is ListViewItem))
4494 return IndexOf ((ListViewItem) item);
4497 void IList.Insert (int index, object value)
4499 throw new NotSupportedException ("Insert operation is not supported.");
4502 void IList.Remove (object value)
4504 throw new NotSupportedException ("Remove operation is not supported.");
4507 void IList.RemoveAt (int index)
4509 throw new NotSupportedException ("RemoveAt operation is not supported.");
4512 public int IndexOf (ListViewItem item)
4515 if (owner.VirtualMode)
4516 throw new InvalidOperationException ();
4518 if (!owner.CheckBoxes)
4520 return List.IndexOf (item);
4524 public virtual int IndexOfKey (string key)
4527 if (owner.VirtualMode)
4528 throw new InvalidOperationException ();
4530 if (key == null || key.Length == 0)
4533 ArrayList checked_items = List;
4534 for (int i = 0; i < checked_items.Count; i++) {
4535 ListViewItem item = (ListViewItem) checked_items [i];
4536 if (String.Compare (key, item.Name, true) == 0)
4543 #endregion // Public Methods
4545 internal ArrayList List {
4548 list = new ArrayList ();
4549 foreach (ListViewItem item in owner.Items) {
4558 internal void Reset ()
4560 // force re-population of list
4564 private void ItemsCollection_Changed ()
4568 } // CheckedListViewItemCollection
4571 [ListBindable (false)]
4573 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4575 internal ArrayList list;
4576 private ListView owner;
4578 #region Public Constructor
4579 public ColumnHeaderCollection (ListView owner)
4581 list = new ArrayList ();
4584 #endregion // Public Constructor
4586 #region Public Properties
4589 get { return list.Count; }
4592 public bool IsReadOnly {
4593 get { return false; }
4596 public virtual ColumnHeader this [int index] {
4598 if (index < 0 || index >= list.Count)
4599 throw new ArgumentOutOfRangeException ("index");
4600 return (ColumnHeader) list [index];
4605 public virtual ColumnHeader this [string key] {
4607 int idx = IndexOfKey (key);
4611 return (ColumnHeader) list [idx];
4616 bool ICollection.IsSynchronized {
4617 get { return true; }
4620 object ICollection.SyncRoot {
4621 get { return this; }
4624 bool IList.IsFixedSize {
4625 get { return list.IsFixedSize; }
4628 object IList.this [int index] {
4629 get { return this [index]; }
4630 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4632 #endregion // Public Properties
4634 #region Public Methods
4635 public virtual int Add (ColumnHeader value)
4637 int idx = list.Add (value);
4638 owner.AddColumn (value, idx, true);
4643 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4647 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4650 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4651 this.Add (colHeader);
4656 public virtual ColumnHeader Add (string text)
4658 return Add (String.Empty, text);
4661 public virtual ColumnHeader Add (string text, int width)
4663 return Add (String.Empty, text, width);
4666 public virtual ColumnHeader Add (string key, string text)
4668 ColumnHeader colHeader = new ColumnHeader ();
4669 colHeader.Name = key;
4670 colHeader.Text = text;
4675 public virtual ColumnHeader Add (string key, string text, int width)
4677 return Add (key, text, width, HorizontalAlignment.Left, -1);
4680 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4682 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4683 colHeader.ImageIndex = imageIndex;
4688 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4690 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4691 colHeader.ImageKey = imageKey;
4697 public virtual void AddRange (ColumnHeader [] values)
4699 foreach (ColumnHeader colHeader in values) {
4700 int idx = list.Add (colHeader);
4701 owner.AddColumn (colHeader, idx, false);
4704 owner.Redraw (true);
4707 public virtual void Clear ()
4709 foreach (ColumnHeader col in list)
4710 col.SetListView (null);
4712 owner.ReorderColumns (new int [0], true);
4715 public bool Contains (ColumnHeader value)
4717 return list.Contains (value);
4721 public virtual bool ContainsKey (string key)
4723 return IndexOfKey (key) != -1;
4727 public IEnumerator GetEnumerator ()
4729 return list.GetEnumerator ();
4732 void ICollection.CopyTo (Array dest, int index)
4734 list.CopyTo (dest, index);
4737 int IList.Add (object value)
4739 if (! (value is ColumnHeader)) {
4740 throw new ArgumentException ("Not of type ColumnHeader", "value");
4743 return this.Add ((ColumnHeader) value);
4746 bool IList.Contains (object value)
4748 if (! (value is ColumnHeader)) {
4749 throw new ArgumentException ("Not of type ColumnHeader", "value");
4752 return this.Contains ((ColumnHeader) value);
4755 int IList.IndexOf (object value)
4757 if (! (value is ColumnHeader)) {
4758 throw new ArgumentException ("Not of type ColumnHeader", "value");
4761 return this.IndexOf ((ColumnHeader) value);
4764 void IList.Insert (int index, object value)
4766 if (! (value is ColumnHeader)) {
4767 throw new ArgumentException ("Not of type ColumnHeader", "value");
4770 this.Insert (index, (ColumnHeader) value);
4773 void IList.Remove (object value)
4775 if (! (value is ColumnHeader)) {
4776 throw new ArgumentException ("Not of type ColumnHeader", "value");
4779 this.Remove ((ColumnHeader) value);
4782 public int IndexOf (ColumnHeader value)
4784 return list.IndexOf (value);
4788 public virtual int IndexOfKey (string key)
4790 if (key == null || key.Length == 0)
4793 for (int i = 0; i < list.Count; i++) {
4794 ColumnHeader col = (ColumnHeader) list [i];
4795 if (String.Compare (key, col.Name, true) == 0)
4803 public void Insert (int index, ColumnHeader value)
4805 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4806 // but it's really only greater.
4807 if (index < 0 || index > list.Count)
4808 throw new ArgumentOutOfRangeException ("index");
4810 list.Insert (index, value);
4811 owner.AddColumn (value, index, true);
4815 public void Insert (int index, string text)
4817 Insert (index, String.Empty, text);
4820 public void Insert (int index, string text, int width)
4822 Insert (index, String.Empty, text, width);
4825 public void Insert (int index, string key, string text)
4827 ColumnHeader colHeader = new ColumnHeader ();
4828 colHeader.Name = key;
4829 colHeader.Text = text;
4830 Insert (index, colHeader);
4833 public void Insert (int index, string key, string text, int width)
4835 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4836 Insert (index, colHeader);
4839 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4841 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4842 colHeader.ImageIndex = imageIndex;
4843 Insert (index, colHeader);
4846 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4848 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4849 colHeader.ImageKey = imageKey;
4850 Insert (index, colHeader);
4855 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4859 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4862 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4863 this.Insert (index, colHeader);
4866 public virtual void Remove (ColumnHeader column)
4868 if (!Contains (column))
4871 list.Remove (column);
4872 column.SetListView (null);
4874 int rem_display_index = column.InternalDisplayIndex;
4875 int [] display_indices = new int [list.Count];
4876 for (int i = 0; i < display_indices.Length; i++) {
4877 ColumnHeader col = (ColumnHeader) list [i];
4878 int display_index = col.InternalDisplayIndex;
4879 if (display_index < rem_display_index) {
4880 display_indices [i] = display_index;
4882 display_indices [i] = (display_index - 1);
4886 column.InternalDisplayIndex = -1;
4887 owner.ReorderColumns (display_indices, true);
4891 public virtual void RemoveByKey (string key)
4893 int idx = IndexOfKey (key);
4899 public virtual void RemoveAt (int index)
4901 if (index < 0 || index >= list.Count)
4902 throw new ArgumentOutOfRangeException ("index");
4904 ColumnHeader col = (ColumnHeader) list [index];
4907 #endregion // Public Methods
4910 } // ColumnHeaderCollection
4913 [ListBindable (false)]
4915 public class ListViewItemCollection : IList, ICollection, IEnumerable
4917 private readonly ArrayList list;
4918 private ListView owner;
4920 private ListViewGroup group;
4923 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4924 // In the later case ListViewItem.ListView never gets modified
4925 private bool is_main_collection = true;
4927 #region Public Constructor
4928 public ListViewItemCollection (ListView owner)
4930 list = new ArrayList (0);
4933 #endregion // Public Constructor
4936 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4939 is_main_collection = false;
4943 #region Public Properties
4948 if (owner != null && owner.VirtualMode)
4949 return owner.VirtualListSize;
4956 public bool IsReadOnly {
4957 get { return false; }
4961 public virtual ListViewItem this [int index] {
4963 public virtual ListViewItem this [int displayIndex] {
4967 int index = displayIndex;
4970 if (index < 0 || index >= Count)
4971 throw new ArgumentOutOfRangeException ("index");
4974 if (owner != null && owner.VirtualMode)
4975 return RetrieveVirtualItemFromOwner (index);
4977 return (ListViewItem) list [index];
4982 int index = displayIndex;
4985 if (index < 0 || index >= Count)
4986 throw new ArgumentOutOfRangeException ("index");
4989 if (owner != null && owner.VirtualMode)
4990 throw new InvalidOperationException ();
4993 if (list.Contains (value))
4994 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4996 if (value.ListView != null && value.ListView != owner)
4997 throw new ArgumentException ("Cannot add or insert the item '" + value.Text + "' in more than one place. You must first remove it from its current location or clone it.", "value");
4999 if (is_main_collection)
5000 value.Owner = owner;
5003 if (value.Group != null)
5004 value.Group.Items.Remove (value);
5006 value.SetGroup (group);
5010 list [index] = value;
5011 CollectionChanged (true);
5016 public virtual ListViewItem this [string key] {
5018 int idx = IndexOfKey (key);
5027 bool ICollection.IsSynchronized {
5028 get { return true; }
5031 object ICollection.SyncRoot {
5032 get { return this; }
5035 bool IList.IsFixedSize {
5036 get { return list.IsFixedSize; }
5039 object IList.this [int index] {
5040 get { return this [index]; }
5042 if (value is ListViewItem)
5043 this [index] = (ListViewItem) value;
5045 this [index] = new ListViewItem (value.ToString ());
5049 #endregion // Public Properties
5051 #region Public Methods
5052 public virtual ListViewItem Add (ListViewItem value)
5055 if (owner != null && owner.VirtualMode)
5056 throw new InvalidOperationException ();
5061 // Item is ignored until it has been added to the ListView
5062 if (is_main_collection || value.ListView != null)
5063 CollectionChanged (true);
5068 public virtual ListViewItem Add (string text)
5070 ListViewItem item = new ListViewItem (text);
5071 return this.Add (item);
5074 public virtual ListViewItem Add (string text, int imageIndex)
5076 ListViewItem item = new ListViewItem (text, imageIndex);
5077 return this.Add (item);
5081 public virtual ListViewItem Add (string text, string imageKey)
5083 ListViewItem item = new ListViewItem (text, imageKey);
5084 return this.Add (item);
5087 public virtual ListViewItem Add (string key, string text, int imageIndex)
5089 ListViewItem item = new ListViewItem (text, imageIndex);
5091 return this.Add (item);
5094 public virtual ListViewItem Add (string key, string text, string imageKey)
5096 ListViewItem item = new ListViewItem (text, imageKey);
5098 return this.Add (item);
5103 public void AddRange (ListViewItem [] items)
5106 public void AddRange (ListViewItem [] values)
5108 ListViewItem [] items = values;
5111 throw new ArgumentNullException ("Argument cannot be null!", "items");
5113 if (owner != null && owner.VirtualMode)
5114 throw new InvalidOperationException ();
5117 owner.BeginUpdate ();
5119 foreach (ListViewItem item in items)
5124 CollectionChanged (true);
5128 public void AddRange (ListViewItemCollection items)
5131 throw new ArgumentNullException ("Argument cannot be null!", "items");
5133 ListViewItem[] itemArray = new ListViewItem[items.Count];
5134 items.CopyTo (itemArray,0);
5135 this.AddRange (itemArray);
5139 public virtual void Clear ()
5142 if (owner != null && owner.VirtualMode)
5143 throw new InvalidOperationException ();
5145 if (is_main_collection && owner != null) {
5146 owner.SetFocusedItem (-1);
5147 owner.h_scroll.Value = owner.v_scroll.Value = 0;
5149 foreach (ListViewItem item in list) {
5150 owner.item_control.CancelEdit (item);
5157 foreach (ListViewItem item in list)
5158 item.SetGroup (null);
5162 CollectionChanged (false);
5165 public bool Contains (ListViewItem item)
5167 return IndexOf (item) != -1;
5171 public virtual bool ContainsKey (string key)
5173 return IndexOfKey (key) != -1;
5177 public void CopyTo (Array dest, int index)
5179 list.CopyTo (dest, index);
5183 public ListViewItem [] Find (string key, bool searchAllSubItems)
5186 return new ListViewItem [0];
5188 List<ListViewItem> temp_list = new List<ListViewItem> ();
5190 for (int i = 0; i < list.Count; i++) {
5191 ListViewItem lvi = (ListViewItem) list [i];
5192 if (String.Compare (key, lvi.Name, true) == 0)
5193 temp_list.Add (lvi);
5196 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5197 temp_list.CopyTo (retval);
5203 public IEnumerator GetEnumerator ()
5206 if (owner != null && owner.VirtualMode)
5207 throw new InvalidOperationException ();
5210 return list.GetEnumerator ();
5213 int IList.Add (object item)
5219 if (owner != null && owner.VirtualMode)
5220 throw new InvalidOperationException ();
5223 if (item is ListViewItem) {
5224 li = (ListViewItem) item;
5225 if (list.Contains (li))
5226 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5228 if (li.ListView != null && li.ListView != owner)
5229 throw new ArgumentException ("Cannot add or insert the item '" + li.Text + "' in more than one place. You must first remove it from its current location or clone it.", "item");
5232 li = new ListViewItem (item.ToString ());
5237 result = list.Add (li);
5238 CollectionChanged (true);
5243 bool IList.Contains (object item)
5245 return Contains ((ListViewItem) item);
5248 int IList.IndexOf (object item)
5250 return IndexOf ((ListViewItem) item);
5253 void IList.Insert (int index, object item)
5255 if (item is ListViewItem)
5256 this.Insert (index, (ListViewItem) item);
5258 this.Insert (index, item.ToString ());
5261 void IList.Remove (object item)
5263 Remove ((ListViewItem) item);
5266 public int IndexOf (ListViewItem item)
5269 if (owner != null && owner.VirtualMode) {
5270 for (int i = 0; i < Count; i++)
5271 if (RetrieveVirtualItemFromOwner (i) == item)
5278 return list.IndexOf (item);
5282 public virtual int IndexOfKey (string key)
5284 if (key == null || key.Length == 0)
5287 for (int i = 0; i < Count; i++) {
5288 ListViewItem lvi = this [i];
5289 if (String.Compare (key, lvi.Name, true) == 0)
5297 public ListViewItem Insert (int index, ListViewItem item)
5299 if (index < 0 || index > list.Count)
5300 throw new ArgumentOutOfRangeException ("index");
5303 if (owner != null && owner.VirtualMode)
5304 throw new InvalidOperationException ();
5307 if (list.Contains (item))
5308 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5310 if (item.ListView != null && item.ListView != owner)
5311 throw new ArgumentException ("Cannot add or insert the item '" + item.Text + "' in more than one place. You must first remove it from its current location or clone it.", "item");
5313 if (is_main_collection)
5317 if (item.Group != null)
5318 item.Group.Items.Remove (item);
5320 item.SetGroup (group);
5324 list.Insert (index, item);
5326 if (is_main_collection || item.ListView != null)
5327 CollectionChanged (true);
5332 public ListViewItem Insert (int index, string text)
5334 return this.Insert (index, new ListViewItem (text));
5337 public ListViewItem Insert (int index, string text, int imageIndex)
5339 return this.Insert (index, new ListViewItem (text, imageIndex));
5343 public ListViewItem Insert (int index, string text, string imageKey)
5345 ListViewItem lvi = new ListViewItem (text, imageKey);
5346 return Insert (index, lvi);
5349 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5351 ListViewItem lvi = new ListViewItem (text, imageIndex);
5353 return Insert (index, lvi);
5356 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5358 ListViewItem lvi = new ListViewItem (text, imageKey);
5360 return Insert (index, lvi);
5364 public virtual void Remove (ListViewItem item)
5367 if (owner != null && owner.VirtualMode)
5368 throw new InvalidOperationException ();
5371 int idx = list.IndexOf (item);
5376 public virtual void RemoveAt (int index)
5378 if (index < 0 || index >= Count)
5379 throw new ArgumentOutOfRangeException ("index");
5382 if (owner != null && owner.VirtualMode)
5383 throw new InvalidOperationException ();
5386 ListViewItem item = (ListViewItem) list [index];
5388 bool selection_changed = false;
5389 if (is_main_collection && owner != null) {
5391 int display_index = item.DisplayIndex;
5392 if (item.Focused && display_index + 1 == Count) // Last item
5393 owner.SetFocusedItem (display_index == 0 ? -1 : display_index - 1);
5395 selection_changed = owner.SelectedIndices.Contains (index);
5396 owner.item_control.CancelEdit (item);
5399 list.RemoveAt (index);
5401 if (is_main_collection)
5405 item.SetGroup (null);
5408 CollectionChanged (false);
5409 if (selection_changed && owner != null)
5410 owner.OnSelectedIndexChanged (EventArgs.Empty);
5414 public virtual void RemoveByKey (string key)
5416 int idx = IndexOfKey (key);
5422 #endregion // Public Methods
5424 internal ListView Owner {
5434 internal ListViewGroup Group {
5444 void AddItem (ListViewItem value)
5446 if (list.Contains (value))
5447 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5449 if (value.ListView != null && value.ListView != owner)
5450 throw new ArgumentException ("Cannot add or insert the item '" + value.Text + "' in more than one place. You must first remove it from its current location or clone it.", "value");
5451 if (is_main_collection)
5452 value.Owner = owner;
5455 if (value.Group != null)
5456 value.Group.Items.Remove (value);
5458 value.SetGroup (group);
5465 void CollectionChanged (bool sort)
5467 if (owner != null) {
5472 owner.Redraw (true);
5477 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5479 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5481 owner.OnRetrieveVirtualItem (args);
5482 ListViewItem retval = args.Item;
5483 retval.Owner = owner;
5484 retval.DisplayIndex = displayIndex;
5490 internal event CollectionChangedHandler Changed;
5492 internal void Sort (IComparer comparer)
5494 list.Sort (comparer);
5498 internal void OnChange ()
5500 if (Changed != null)
5503 } // ListViewItemCollection
5506 // In normal mode, the selection information resides in the Items,
5507 // making SelectedIndexCollection.List read-only
5509 // In virtual mode, SelectedIndexCollection directly saves the selection
5510 // information, instead of getting it from Items, making List read-and-write
5512 [ListBindable (false)]
5514 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5516 private readonly ListView owner;
5517 private ArrayList list;
5519 #region Public Constructor
5520 public SelectedIndexCollection (ListView owner)
5523 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5525 #endregion // Public Constructor
5527 #region Public Properties
5531 if (!owner.IsHandleCreated)
5538 public bool IsReadOnly {
5548 public int this [int index] {
5550 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5551 throw new ArgumentOutOfRangeException ("index");
5553 return (int) List [index];
5557 bool ICollection.IsSynchronized {
5558 get { return false; }
5561 object ICollection.SyncRoot {
5562 get { return this; }
5565 bool IList.IsFixedSize {
5575 object IList.this [int index] {
5576 get { return this [index]; }
5577 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5579 #endregion // Public Properties
5581 #region Public Methods
5583 public int Add (int itemIndex)
5585 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5586 throw new ArgumentOutOfRangeException ("index");
5588 if (owner.virtual_mode && !owner.IsHandleCreated)
5591 owner.Items [itemIndex].Selected = true;
5593 if (!owner.IsHandleCreated)
5607 if (!owner.IsHandleCreated)
5610 int [] indexes = (int []) List.ToArray (typeof (int));
5611 foreach (int index in indexes)
5612 owner.Items [index].Selected = false;
5615 public bool Contains (int selectedIndex)
5617 return IndexOf (selectedIndex) != -1;
5620 public void CopyTo (Array dest, int index)
5622 List.CopyTo (dest, index);
5625 public IEnumerator GetEnumerator ()
5627 return List.GetEnumerator ();
5630 int IList.Add (object value)
5632 throw new NotSupportedException ("Add operation is not supported.");
5640 bool IList.Contains (object selectedIndex)
5642 if (!(selectedIndex is int))
5644 return Contains ((int) selectedIndex);
5647 int IList.IndexOf (object selectedIndex)
5649 if (!(selectedIndex is int))
5651 return IndexOf ((int) selectedIndex);
5654 void IList.Insert (int index, object value)
5656 throw new NotSupportedException ("Insert operation is not supported.");
5659 void IList.Remove (object value)
5661 throw new NotSupportedException ("Remove operation is not supported.");
5664 void IList.RemoveAt (int index)
5666 throw new NotSupportedException ("RemoveAt operation is not supported.");
5669 public int IndexOf (int selectedIndex)
5671 if (!owner.IsHandleCreated)
5674 return List.IndexOf (selectedIndex);
5678 public void Remove (int itemIndex)
5680 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5681 throw new ArgumentOutOfRangeException ("itemIndex");
5683 owner.Items [itemIndex].Selected = false;
5686 #endregion // Public Methods
5688 internal ArrayList List {
5691 list = new ArrayList ();
5693 if (!owner.VirtualMode)
5695 for (int i = 0; i < owner.Items.Count; i++) {
5696 if (owner.Items [i].Selected)
5704 internal void Reset ()
5706 // force re-population of list
5710 private void ItemsCollection_Changed ()
5716 internal void RemoveIndex (int index)
5718 int idx = List.BinarySearch (index);
5720 List.RemoveAt (idx);
5723 // actually store index in the collection
5724 // also, keep the collection sorted, as .Net does
5725 internal void InsertIndex (int index)
5728 int iMax = List.Count - 1;
5729 while (iMin <= iMax) {
5730 int iMid = (iMin + iMax) / 2;
5731 int current_index = (int) List [iMid];
5733 if (current_index == index)
5734 return; // Already added
5735 if (current_index > index)
5741 List.Insert (iMin, index);
5745 } // SelectedIndexCollection
5748 [ListBindable (false)]
5750 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5752 private readonly ListView owner;
5754 #region Public Constructor
5755 public SelectedListViewItemCollection (ListView owner)
5759 #endregion // Public Constructor
5761 #region Public Properties
5765 return owner.SelectedIndices.Count;
5769 public bool IsReadOnly {
5770 get { return true; }
5773 public ListViewItem this [int index] {
5775 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5776 throw new ArgumentOutOfRangeException ("index");
5778 int item_index = owner.SelectedIndices [index];
5779 return owner.Items [item_index];
5784 public virtual ListViewItem this [string key] {
5786 int idx = IndexOfKey (key);
5795 bool ICollection.IsSynchronized {
5796 get { return false; }
5799 object ICollection.SyncRoot {
5800 get { return this; }
5803 bool IList.IsFixedSize {
5804 get { return true; }
5807 object IList.this [int index] {
5808 get { return this [index]; }
5809 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5811 #endregion // Public Properties
5813 #region Public Methods
5814 public void Clear ()
5816 owner.SelectedIndices.Clear ();
5819 public bool Contains (ListViewItem item)
5821 return IndexOf (item) != -1;
5825 public virtual bool ContainsKey (string key)
5827 return IndexOfKey (key) != -1;
5831 public void CopyTo (Array dest, int index)
5833 if (!owner.IsHandleCreated)
5835 if (index > Count) // Throws ArgumentException instead of IOOR exception
5836 throw new ArgumentException ("index");
5838 for (int i = 0; i < Count; i++)
5839 dest.SetValue (this [i], index++);
5842 public IEnumerator GetEnumerator ()
5844 if (!owner.IsHandleCreated)
5845 return (new ListViewItem [0]).GetEnumerator ();
5847 ListViewItem [] items = new ListViewItem [Count];
5848 for (int i = 0; i < Count; i++)
5849 items [i] = this [i];
5851 return items.GetEnumerator ();
5854 int IList.Add (object value)
5856 throw new NotSupportedException ("Add operation is not supported.");
5859 bool IList.Contains (object item)
5861 if (!(item is ListViewItem))
5863 return Contains ((ListViewItem) item);
5866 int IList.IndexOf (object item)
5868 if (!(item is ListViewItem))
5870 return IndexOf ((ListViewItem) item);
5873 void IList.Insert (int index, object value)
5875 throw new NotSupportedException ("Insert operation is not supported.");
5878 void IList.Remove (object value)
5880 throw new NotSupportedException ("Remove operation is not supported.");
5883 void IList.RemoveAt (int index)
5885 throw new NotSupportedException ("RemoveAt operation is not supported.");
5888 public int IndexOf (ListViewItem item)
5890 if (!owner.IsHandleCreated)
5893 for (int i = 0; i < Count; i++)
5894 if (this [i] == item)
5901 public virtual int IndexOfKey (string key)
5903 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5906 for (int i = 0; i < Count; i++) {
5907 ListViewItem item = this [i];
5908 if (String.Compare (item.Name, key, true) == 0)
5915 #endregion // Public Methods
5917 } // SelectedListViewItemCollection
5919 internal delegate void CollectionChangedHandler ();
5921 struct ItemMatrixLocation
5926 public ItemMatrixLocation (int row, int col)
5953 #endregion // Subclasses
5955 protected override void OnResize (EventArgs e)
5960 protected override void OnMouseLeave (EventArgs e)
5962 base.OnMouseLeave (e);
5966 // ColumnReorder event
5968 static object ColumnReorderedEvent = new object ();
5969 public event ColumnReorderedEventHandler ColumnReordered {
5970 add { Events.AddHandler (ColumnReorderedEvent, value); }
5971 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5974 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5976 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5983 // ColumnWidthChanged
5985 static object ColumnWidthChangedEvent = new object ();
5986 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5987 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5988 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5991 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5993 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5998 void RaiseColumnWidthChanged (int resize_column)
6000 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
6002 OnColumnWidthChanged (n);
6006 // ColumnWidthChanging
6008 static object ColumnWidthChangingEvent = new object ();
6009 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
6010 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
6011 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
6014 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
6016 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
6022 // 2.0 profile based implementation
6024 bool CanProceedWithResize (ColumnHeader col, int width)
6026 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
6030 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
6031 cwceh (this, changing);
6032 return !changing.Cancel;
6036 // 1.0 profile based implementation
6038 bool CanProceedWithResize (ColumnHeader col, int width)
6043 void RaiseColumnWidthChanged (int resize_column)