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 hot_item_index = -1;
106 private bool hot_tracking;
107 private ListViewInsertionMark insertion_mark;
108 private bool show_item_tooltips;
109 private ToolTip item_tooltip;
110 private Size tile_size;
111 private bool virtual_mode;
112 private int virtual_list_size;
113 private bool right_to_left_layout;
116 // internal variables
117 internal ImageList large_image_list;
118 internal ImageList small_image_list;
119 internal Size text_size = Size.Empty;
122 static object AfterLabelEditEvent = new object ();
123 static object BeforeLabelEditEvent = new object ();
124 static object ColumnClickEvent = new object ();
125 static object ItemActivateEvent = new object ();
126 static object ItemCheckEvent = new object ();
127 static object ItemDragEvent = new object ();
128 static object SelectedIndexChangedEvent = new object ();
130 static object DrawColumnHeaderEvent = new object();
131 static object DrawItemEvent = new object();
132 static object DrawSubItemEvent = new object();
133 static object ItemCheckedEvent = new object ();
134 static object ItemMouseHoverEvent = new object ();
135 static object ItemSelectionChangedEvent = new object ();
136 static object CacheVirtualItemsEvent = new object ();
137 static object RetrieveVirtualItemEvent = new object ();
138 static object RightToLeftLayoutChangedEvent = new object ();
139 static object SearchForVirtualItemEvent = new object ();
140 static object VirtualItemsSelectionRangeChangedEvent = new object ();
143 public event LabelEditEventHandler AfterLabelEdit {
144 add { Events.AddHandler (AfterLabelEditEvent, value); }
145 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
150 [EditorBrowsable (EditorBrowsableState.Never)]
151 public new event EventHandler BackgroundImageChanged {
152 add { base.BackgroundImageChanged += value; }
153 remove { base.BackgroundImageChanged -= value; }
159 [EditorBrowsable (EditorBrowsableState.Never)]
160 public new event EventHandler BackgroundImageLayoutChanged {
161 add { base.BackgroundImageLayoutChanged += value; }
162 remove { base.BackgroundImageLayoutChanged -= value; }
166 public event LabelEditEventHandler BeforeLabelEdit {
167 add { Events.AddHandler (BeforeLabelEditEvent, value); }
168 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
171 public event ColumnClickEventHandler ColumnClick {
172 add { Events.AddHandler (ColumnClickEvent, value); }
173 remove { Events.RemoveHandler (ColumnClickEvent, value); }
177 public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
178 add { Events.AddHandler(DrawColumnHeaderEvent, value); }
179 remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
182 public event DrawListViewItemEventHandler DrawItem {
183 add { Events.AddHandler(DrawItemEvent, value); }
184 remove { Events.RemoveHandler(DrawItemEvent, value); }
187 public event DrawListViewSubItemEventHandler DrawSubItem {
188 add { Events.AddHandler(DrawSubItemEvent, value); }
189 remove { Events.RemoveHandler(DrawSubItemEvent, value); }
193 public event EventHandler ItemActivate {
194 add { Events.AddHandler (ItemActivateEvent, value); }
195 remove { Events.RemoveHandler (ItemActivateEvent, value); }
198 public event ItemCheckEventHandler ItemCheck {
199 add { Events.AddHandler (ItemCheckEvent, value); }
200 remove { Events.RemoveHandler (ItemCheckEvent, value); }
204 public event ItemCheckedEventHandler ItemChecked {
205 add { Events.AddHandler (ItemCheckedEvent, value); }
206 remove { Events.RemoveHandler (ItemCheckedEvent, value); }
210 public event ItemDragEventHandler ItemDrag {
211 add { Events.AddHandler (ItemDragEvent, value); }
212 remove { Events.RemoveHandler (ItemDragEvent, value); }
216 public event ListViewItemMouseHoverEventHandler ItemMouseHover {
217 add { Events.AddHandler (ItemMouseHoverEvent, value); }
218 remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
221 public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
222 add { Events.AddHandler (ItemSelectionChangedEvent, value); }
223 remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
227 [EditorBrowsable (EditorBrowsableState.Never)]
228 public new event EventHandler PaddingChanged {
229 add { base.PaddingChanged += value; }
230 remove { base.PaddingChanged -= value; }
235 [EditorBrowsable (EditorBrowsableState.Never)]
236 public new event PaintEventHandler Paint {
237 add { base.Paint += value; }
238 remove { base.Paint -= value; }
241 public event EventHandler SelectedIndexChanged {
242 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
243 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
247 [EditorBrowsable (EditorBrowsableState.Never)]
248 public new event EventHandler TextChanged {
249 add { base.TextChanged += value; }
250 remove { base.TextChanged -= value; }
254 public event CacheVirtualItemsEventHandler CacheVirtualItems {
255 add { Events.AddHandler (CacheVirtualItemsEvent, value); }
256 remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
259 public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
260 add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
261 remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
264 public event EventHandler RightToLeftLayoutChanged {
265 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
266 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
269 public event SearchForVirtualItemEventHandler SearchForVirtualItem {
270 add { Events.AddHandler (SearchForVirtualItemEvent, value); }
271 remove { Events.AddHandler (SearchForVirtualItemEvent, value); }
274 public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged {
275 add { Events.AddHandler (VirtualItemsSelectionRangeChangedEvent, value); }
276 remove { Events.RemoveHandler (VirtualItemsSelectionRangeChangedEvent, value); }
282 #region Public Constructors
285 background_color = ThemeEngine.Current.ColorWindow;
287 groups = new ListViewGroupCollection (this);
289 items = new ListViewItemCollection (this);
290 checked_indices = new CheckedIndexCollection (this);
291 checked_items = new CheckedListViewItemCollection (this);
292 columns = new ColumnHeaderCollection (this);
293 foreground_color = SystemColors.WindowText;
294 selected_indices = new SelectedIndexCollection (this);
295 selected_items = new SelectedListViewItemCollection (this);
296 items_location = new Point [16];
297 items_matrix_location = new ItemMatrixLocation [16];
298 reordered_items_indices = new int [16];
300 item_tooltip = new ToolTip ();
301 item_tooltip.Active = false;
302 insertion_mark = new ListViewInsertionMark (this);
305 InternalBorderStyle = BorderStyle.Fixed3D;
307 header_control = new HeaderControl (this);
308 header_control.Visible = false;
309 Controls.AddImplicit (header_control);
311 item_control = new ItemControl (this);
312 Controls.AddImplicit (item_control);
314 h_scroll = new ImplicitHScrollBar ();
315 Controls.AddImplicit (this.h_scroll);
317 v_scroll = new ImplicitVScrollBar ();
318 Controls.AddImplicit (this.v_scroll);
320 h_marker = v_marker = 0;
321 keysearch_tickcnt = 0;
323 // scroll bars are disabled initially
324 h_scroll.Visible = false;
325 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
326 v_scroll.Visible = false;
327 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
330 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
331 SizeChanged += new EventHandler (ListView_SizeChanged);
332 GotFocus += new EventHandler (FocusChanged);
333 LostFocus += new EventHandler (FocusChanged);
334 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
335 MouseEnter += new EventHandler (ListView_MouseEnter);
338 BackgroundImageTiled = false;
341 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
343 | ControlStyles.UseTextForAccessibility
347 #endregion // Public Constructors
349 #region Private Internal Properties
350 internal Size CheckBoxSize {
352 if (this.check_boxes) {
353 if (this.state_image_list != null)
354 return this.state_image_list.ImageSize;
356 return ThemeEngine.Current.ListViewCheckBoxSize;
362 internal Size ItemSize {
364 if (view != View.Details)
367 Size size = new Size ();
368 size.Height = item_size.Height;
369 for (int i = 0; i < columns.Count; i++)
370 size.Width += columns [i].Wd;
379 internal int HotItemIndex {
381 return hot_item_index;
384 hot_item_index = value;
388 internal override bool ScaleChildrenInternal {
389 get { return false; }
392 #endregion // Private Internal Properties
394 #region Protected Properties
395 protected override CreateParams CreateParams {
396 get { return base.CreateParams; }
399 protected override Size DefaultSize {
400 get { return ThemeEngine.Current.ListViewDefaultSize; }
403 protected override bool DoubleBuffered {
405 return base.DoubleBuffered;
408 base.DoubleBuffered = value;
412 #endregion // Protected Properties
414 #region Public Instance Properties
415 [DefaultValue (ItemActivation.Standard)]
416 public ItemActivation Activation {
417 get { return activation; }
419 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
420 value != ItemActivation.TwoClick) {
421 throw new InvalidEnumArgumentException (string.Format
422 ("Enum argument value '{0}' is not valid for Activation", value));
425 if (hot_tracking && value != ItemActivation.OneClick)
426 throw new ArgumentException ("When HotTracking is on, activation must be ItemActivation.OneClick");
433 [DefaultValue (ListViewAlignment.Top)]
435 public ListViewAlignment Alignment {
436 get { return alignment; }
438 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
439 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
440 throw new InvalidEnumArgumentException (string.Format
441 ("Enum argument value '{0}' is not valid for Alignment", value));
444 if (this.alignment != value) {
446 // alignment does not matter in Details/List views
447 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
453 [DefaultValue (false)]
454 public bool AllowColumnReorder {
455 get { return allow_column_reorder; }
456 set { allow_column_reorder = value; }
459 [DefaultValue (true)]
460 public bool AutoArrange {
461 get { return auto_arrange; }
463 if (auto_arrange != value) {
464 auto_arrange = value;
465 // autoarrange does not matter in Details/List views
466 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
472 public override Color BackColor {
474 if (background_color.IsEmpty)
475 return ThemeEngine.Current.ColorWindow;
477 return background_color;
480 background_color = value;
481 item_control.BackColor = value;
487 [EditorBrowsable (EditorBrowsableState.Never)]
488 public override Image BackgroundImage {
489 get { return base.BackgroundImage; }
490 set { base.BackgroundImage = value; }
496 [EditorBrowsable (EditorBrowsableState.Never)]
497 public override ImageLayout BackgroundImageLayout {
499 return base.BackgroundImageLayout;
502 base.BackgroundImageLayout = value;
506 [DefaultValue (false)]
507 public bool BackgroundImageTiled {
509 return item_control.BackgroundImageLayout == ImageLayout.Tile;
512 ImageLayout new_image_layout = value ? ImageLayout.Tile : ImageLayout.None;
513 if (new_image_layout == item_control.BackgroundImageLayout)
516 item_control.BackgroundImageLayout = new_image_layout;
521 [DefaultValue (BorderStyle.Fixed3D)]
523 public BorderStyle BorderStyle {
524 get { return InternalBorderStyle; }
525 set { InternalBorderStyle = value; }
528 [DefaultValue (false)]
529 public bool CheckBoxes {
530 get { return check_boxes; }
532 if (check_boxes != value) {
534 if (value && View == View.Tile)
535 throw new NotSupportedException ("CheckBoxes are not"
536 + " supported in Tile view. Choose a different"
537 + " view or set CheckBoxes to false.");
547 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
548 public CheckedIndexCollection CheckedIndices {
549 get { return checked_indices; }
553 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
554 public CheckedListViewItemCollection CheckedItems {
555 get { return checked_items; }
559 [Editor ("System.Windows.Forms.Design.ColumnHeaderCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
561 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
563 [MergableProperty (false)]
564 public ColumnHeaderCollection Columns {
565 get { return columns; }
569 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
570 public ListViewItem FocusedItem {
572 if (focused_item_index == -1)
575 return items [focused_item_index];
579 if (value == null || value.ListView != this ||
583 SetFocusedItem (value.Index);
588 public override Color ForeColor {
590 if (foreground_color.IsEmpty)
591 return ThemeEngine.Current.ColorWindowText;
593 return foreground_color;
595 set { foreground_color = value; }
598 [DefaultValue (false)]
599 public bool FullRowSelect {
600 get { return full_row_select; }
602 if (full_row_select != value) {
603 full_row_select = value;
604 InvalidateSelection ();
609 [DefaultValue (false)]
610 public bool GridLines {
611 get { return grid_lines; }
613 if (grid_lines != value) {
620 [DefaultValue (ColumnHeaderStyle.Clickable)]
621 public ColumnHeaderStyle HeaderStyle {
622 get { return header_style; }
624 if (header_style == value)
628 case ColumnHeaderStyle.Clickable:
629 case ColumnHeaderStyle.Nonclickable:
630 case ColumnHeaderStyle.None:
633 throw new InvalidEnumArgumentException (string.Format
634 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
637 header_style = value;
638 if (view == View.Details)
643 [DefaultValue (true)]
644 public bool HideSelection {
645 get { return hide_selection; }
647 if (hide_selection != value) {
648 hide_selection = value;
649 InvalidateSelection ();
655 [DefaultValue (false)]
656 public bool HotTracking {
661 if (hot_tracking == value)
664 hot_tracking = value;
666 hover_selection = true;
667 activation = ItemActivation.OneClick;
673 [DefaultValue (false)]
674 public bool HoverSelection {
675 get { return hover_selection; }
678 if (hot_tracking && value == false)
679 throw new ArgumentException ("When HotTracking is on, hover selection must be true");
681 hover_selection = value;
686 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
688 public ListViewInsertionMark InsertionMark {
690 return insertion_mark;
696 [Editor ("System.Windows.Forms.Design.ListViewItemCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
698 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
700 [MergableProperty (false)]
701 public ListViewItemCollection Items {
702 get { return items; }
705 [DefaultValue (false)]
706 public bool LabelEdit {
707 get { return label_edit; }
708 set { label_edit = value; }
711 [DefaultValue (true)]
713 public bool LabelWrap {
714 get { return label_wrap; }
716 if (label_wrap != value) {
723 [DefaultValue (null)]
724 public ImageList LargeImageList {
725 get { return large_image_list; }
727 large_image_list = value;
733 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
734 public IComparer ListViewItemSorter {
736 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
741 if (item_sorter != value) {
748 [DefaultValue (true)]
749 public bool MultiSelect {
750 get { return multiselect; }
751 set { multiselect = value; }
756 [DefaultValue(false)]
757 public bool OwnerDraw {
758 get { return owner_draw; }
766 [EditorBrowsable (EditorBrowsableState.Never)]
767 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
768 public new Padding Padding {
773 base.Padding = value;
777 [MonoTODO ("RTL not supported")]
779 [DefaultValue (false)]
780 public virtual bool RightToLeftLayout {
781 get { return right_to_left_layout; }
783 if (right_to_left_layout != value) {
784 right_to_left_layout = value;
785 OnRightToLeftLayoutChanged (EventArgs.Empty);
791 [DefaultValue (true)]
792 public bool Scrollable {
793 get { return scrollable; }
795 if (scrollable != value) {
803 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
804 public SelectedIndexCollection SelectedIndices {
805 get { return selected_indices; }
809 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
810 public SelectedListViewItemCollection SelectedItems {
811 get { return selected_items; }
816 public bool ShowGroups {
817 get { return show_groups; }
819 if (show_groups != value) {
826 [LocalizableAttribute (true)]
827 [MergableProperty (false)]
828 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
829 [Editor ("System.Windows.Forms.Design.ListViewGroupCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
830 public ListViewGroupCollection Groups {
831 get { return groups; }
834 [DefaultValue (false)]
835 public bool ShowItemToolTips {
837 return show_item_tooltips;
840 show_item_tooltips = value;
841 item_tooltip.Active = false;
846 [DefaultValue (null)]
847 public ImageList SmallImageList {
848 get { return small_image_list; }
850 small_image_list = value;
855 [DefaultValue (SortOrder.None)]
856 public SortOrder Sorting {
857 get { return sort_order; }
859 if (!Enum.IsDefined (typeof (SortOrder), value)) {
860 throw new InvalidEnumArgumentException ("value", (int) value,
864 if (sort_order == value)
870 if (virtual_mode) // Sorting is not allowed in virtual mode
874 if (value == SortOrder.None) {
875 if (item_sorter != null) {
876 // ListViewItemSorter should never be reset for SmallIcon
877 // and LargeIcon view
878 if (View != View.SmallIcon && View != View.LargeIcon)
882 // in .NET 1.1, only internal IComparer would be
884 if (item_sorter is ItemComparer)
890 if (item_sorter == null)
891 item_sorter = new ItemComparer (value);
892 if (item_sorter is ItemComparer) {
894 item_sorter = new ItemComparer (value);
896 // in .NET 1.1, the sort order is not updated for
897 // SmallIcon and LargeIcon views if no custom IComparer
899 if (View != View.SmallIcon && View != View.LargeIcon)
900 item_sorter = new ItemComparer (value);
908 private void OnImageListChanged (object sender, EventArgs args)
910 item_control.Invalidate ();
913 [DefaultValue (null)]
914 public ImageList StateImageList {
915 get { return state_image_list; }
917 if (state_image_list == value)
920 if (state_image_list != null)
921 state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
923 state_image_list = value;
925 if (state_image_list != null)
926 state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
934 [EditorBrowsable (EditorBrowsableState.Never)]
935 public override string Text {
936 get { return base.Text; }
938 if (value == base.Text)
948 public Size TileSize {
953 if (value.Width <= 0 || value.Height <= 0)
954 throw new ArgumentOutOfRangeException ("value");
963 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
964 public ListViewItem TopItem {
967 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
968 throw new InvalidOperationException ("Cannot get the top item in LargeIcon, SmallIcon or Tile view.");
971 if (this.items.Count == 0)
973 // if contents are not scrolled
974 // it is the first item
975 else if (h_marker == 0 && v_marker == 0)
976 return this.items [0];
977 // do a hit test for the scrolled position
979 for (int i = 0; i < items.Count; i++) {
980 Point item_loc = GetItemLocation (i);
981 if (item_loc.X >= 0 && item_loc.Y >= 0)
989 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
990 throw new InvalidOperationException ("Cannot set the top item in LargeIcon, SmallIcon or Tile view.");
992 // .Net doesn't throw any exception in the cases below
993 if (value == null || value.ListView != this)
996 EnsureVisible (value.Index);
1002 [EditorBrowsable (EditorBrowsableState.Advanced)]
1003 [DefaultValue (true)]
1005 [MonoInternalNote ("Stub, not implemented")]
1006 public bool UseCompatibleStateImageBehavior {
1015 [DefaultValue (View.LargeIcon)]
1017 get { return view; }
1019 if (!Enum.IsDefined (typeof (View), value))
1020 throw new InvalidEnumArgumentException ("value", (int) value,
1023 if (view != value) {
1025 if (CheckBoxes && value == View.Tile)
1026 throw new NotSupportedException ("CheckBoxes are not"
1027 + " supported in Tile view. Choose a different"
1028 + " view or set CheckBoxes to false.");
1031 h_scroll.Value = v_scroll.Value = 0;
1039 [DefaultValue (false)]
1040 [RefreshProperties (RefreshProperties.Repaint)]
1041 public bool VirtualMode {
1043 return virtual_mode;
1046 if (virtual_mode == value)
1049 if (!virtual_mode && items.Count > 0)
1050 throw new InvalidOperationException ();
1052 virtual_mode = value;
1058 [RefreshProperties (RefreshProperties.Repaint)]
1059 public int VirtualListSize {
1061 return virtual_list_size;
1065 throw new ArgumentException ("value");
1067 if (virtual_list_size == value)
1070 virtual_list_size = value;
1076 #endregion // Public Instance Properties
1078 #region Internal Methods Properties
1080 internal int FirstVisibleIndex {
1083 if (this.items.Count == 0)
1086 if (h_marker == 0 && v_marker == 0)
1089 Size item_size = ItemSize;
1090 for (int i = 0; i < items.Count; i++) {
1091 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
1092 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
1101 internal int LastVisibleIndex {
1103 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
1104 if (View == View.List || Alignment == ListViewAlignment.Left) {
1105 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
1108 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
1113 return Items.Count - 1;
1117 internal void OnSelectedIndexChanged ()
1119 if (IsHandleCreated)
1120 OnSelectedIndexChanged (EventArgs.Empty);
1123 internal int TotalWidth {
1124 get { return Math.Max (this.Width, this.layout_wd); }
1127 internal int TotalHeight {
1128 get { return Math.Max (this.Height, this.layout_ht); }
1131 internal void Redraw (bool recalculate)
1133 // Avoid calculations when control is being updated
1137 // VirtualMode doesn't do any calculations until handle is created
1138 if (virtual_mode && !IsHandleCreated)
1144 CalculateListView (this.alignment);
1149 void InvalidateSelection ()
1151 foreach (int selected_index in SelectedIndices)
1152 items [selected_index].Invalidate ();
1155 const int text_padding = 15;
1157 internal Size GetChildColumnSize (int index)
1159 Size ret_size = Size.Empty;
1160 ColumnHeader col = this.columns [index];
1162 if (col.Width == -2) { // autosize = max(items, columnheader)
1163 Size size = Size.Ceiling (TextRenderer.MeasureString
1164 (col.Text, this.Font));
1165 size.Width += text_padding;
1166 ret_size = BiggestItem (index);
1167 if (size.Width > ret_size.Width)
1170 else { // -1 and all the values < -2 are put under one category
1171 ret_size = BiggestItem (index);
1172 // fall back to empty columns' width if no subitem is available for a column
1173 if (ret_size.IsEmpty) {
1174 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
1175 if (col.Text.Length > 0)
1176 ret_size.Height = Size.Ceiling (TextRenderer.MeasureString
1177 (col.Text, this.Font)).Height;
1179 ret_size.Height = this.Font.Height;
1183 ret_size.Height += text_padding;
1185 // adjust the size for icon and checkbox for 0th column
1187 ret_size.Width += (this.CheckBoxSize.Width + 4);
1188 if (this.small_image_list != null)
1189 ret_size.Width += this.small_image_list.ImageSize.Width;
1194 // Returns the size of biggest item text in a column
1195 // or the sum of the text and indent count if we are on 2.0
1196 private Size BiggestItem (int col)
1198 Size temp = Size.Empty;
1199 Size ret_size = Size.Empty;
1201 bool use_indent_count = small_image_list != null;
1203 // VirtualMode uses the first item text size
1204 if (virtual_mode && items.Count > 0) {
1205 ListViewItem item = items [0];
1206 ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
1209 if (use_indent_count)
1210 ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
1213 // 0th column holds the item text, we check the size of
1214 // the various subitems falling in that column and get
1215 // the biggest one's size.
1216 foreach (ListViewItem item in items) {
1217 if (col >= item.SubItems.Count)
1220 temp = Size.Ceiling (TextRenderer.MeasureString
1221 (item.SubItems [col].Text, Font));
1224 if (use_indent_count)
1225 temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
1228 if (temp.Width > ret_size.Width)
1235 // adjustment for space in Details view
1236 if (!ret_size.IsEmpty && view == View.Details)
1237 ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
1242 const int max_wrap_padding = 30;
1244 // Sets the size of the biggest item text as per the view
1245 private void CalcTextSize ()
1247 // clear the old value
1248 text_size = Size.Empty;
1250 if (items.Count == 0)
1253 text_size = BiggestItem (0);
1255 if (view == View.LargeIcon && this.label_wrap) {
1256 Size temp = Size.Empty;
1257 if (this.check_boxes)
1258 temp.Width += 2 * this.CheckBoxSize.Width;
1259 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1260 temp.Width += icon_w + max_wrap_padding;
1261 // wrapping is done for two lines only
1262 if (text_size.Width > temp.Width) {
1263 text_size.Width = temp.Width;
1264 text_size.Height *= 2;
1267 else if (view == View.List) {
1268 // in list view max text shown in determined by the
1269 // control width, even if scolling is enabled.
1270 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1271 if (this.small_image_list != null)
1272 max_wd -= this.small_image_list.ImageSize.Width;
1274 if (text_size.Width > max_wd)
1275 text_size.Width = max_wd;
1278 // we do the default settings, if we have got 0's
1279 if (text_size.Height <= 0)
1280 text_size.Height = this.Font.Height;
1281 if (text_size.Width <= 0)
1282 text_size.Width = this.Width;
1284 // little adjustment
1285 text_size.Width += 2;
1286 text_size.Height += 2;
1289 private void Scroll (ScrollBar scrollbar, int delta)
1291 if (delta == 0 || !scrollbar.Visible)
1295 if (scrollbar == h_scroll)
1296 max = h_scroll.Maximum - item_control.Width;
1298 max = v_scroll.Maximum - item_control.Height;
1300 int val = scrollbar.Value + delta;
1303 else if (val < scrollbar.Minimum)
1304 val = scrollbar.Minimum;
1305 scrollbar.Value = val;
1308 private void CalculateScrollBars ()
1310 if (!IsHandleCreated)
1313 Rectangle client_area = ClientRectangle;
1316 h_scroll.Visible = false;
1317 v_scroll.Visible = false;
1318 item_control.Height = client_area.Height;
1319 item_control.Width = client_area.Width;
1320 header_control.Width = client_area.Width;
1324 // Don't calculate if the view is not displayable
1325 if (client_area.Height < 0 || client_area.Width < 0)
1328 // making a scroll bar visible might make
1329 // other scroll bar visible
1330 if (layout_wd > client_area.Right) {
1331 h_scroll.Visible = true;
1332 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1333 v_scroll.Visible = true;
1335 v_scroll.Visible = false;
1336 } else if (layout_ht > client_area.Bottom) {
1337 v_scroll.Visible = true;
1338 if ((layout_wd + v_scroll.Width) > client_area.Right)
1339 h_scroll.Visible = true;
1341 h_scroll.Visible = false;
1343 h_scroll.Visible = false;
1344 v_scroll.Visible = false;
1347 item_control.Height = client_area.Height;
1349 if (h_scroll.is_visible) {
1350 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1351 h_scroll.Minimum = 0;
1353 // if v_scroll is visible, adjust the maximum of the
1354 // h_scroll to account for the width of v_scroll
1355 if (v_scroll.Visible) {
1356 h_scroll.Maximum = layout_wd + v_scroll.Width;
1357 h_scroll.Width = client_area.Width - v_scroll.Width;
1360 h_scroll.Maximum = layout_wd;
1361 h_scroll.Width = client_area.Width;
1364 h_scroll.LargeChange = client_area.Width;
1365 h_scroll.SmallChange = Font.Height;
1366 item_control.Height -= h_scroll.Height;
1369 if (header_control.is_visible)
1370 header_control.Width = client_area.Width;
1371 item_control.Width = client_area.Width;
1373 if (v_scroll.is_visible) {
1374 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1375 v_scroll.Minimum = 0;
1377 // if h_scroll is visible, adjust the maximum of the
1378 // v_scroll to account for the height of h_scroll
1379 if (h_scroll.Visible) {
1380 v_scroll.Maximum = layout_ht + h_scroll.Height;
1381 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1383 v_scroll.Maximum = layout_ht;
1384 v_scroll.Height = client_area.Height;
1387 v_scroll.LargeChange = client_area.Height;
1388 v_scroll.SmallChange = Font.Height;
1389 if (header_control.Visible)
1390 header_control.Width -= v_scroll.Width;
1391 item_control.Width -= v_scroll.Width;
1396 internal int GetReorderedColumnIndex (ColumnHeader column)
1398 if (reordered_column_indices == null)
1399 return column.Index;
1401 for (int i = 0; i < Columns.Count; i++)
1402 if (reordered_column_indices [i] == column.Index)
1409 internal ColumnHeader GetReorderedColumn (int index)
1411 if (reordered_column_indices == null)
1412 return Columns [index];
1414 return Columns [reordered_column_indices [index]];
1417 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1421 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1423 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1427 header_control.Invalidate ();
1428 item_control.Invalidate ();
1434 int column_count = Columns.Count;
1436 if (reordered_column_indices == null) {
1437 reordered_column_indices = new int [column_count];
1438 for (int i = 0; i < column_count; i++)
1439 reordered_column_indices [i] = i;
1442 if (reordered_column_indices [index] == col.Index)
1445 int[] curr = reordered_column_indices;
1446 int [] result = new int [column_count];
1448 for (int i = 0; i < column_count; i++) {
1449 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1453 result [i] = col.Index;
1455 result [i] = curr [curr_idx++];
1458 ReorderColumns (result, true);
1461 internal void ReorderColumns (int [] display_indices, bool redraw)
1463 reordered_column_indices = display_indices;
1464 for (int i = 0; i < Columns.Count; i++) {
1465 ColumnHeader col = Columns [i];
1466 col.InternalDisplayIndex = reordered_column_indices [i];
1468 if (redraw && view == View.Details && IsHandleCreated) {
1470 header_control.Invalidate ();
1471 item_control.Invalidate ();
1475 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1477 int column_count = Columns.Count;
1478 newCol.SetListView (this);
1480 int [] display_indices = new int [column_count];
1481 for (int i = 0; i < column_count; i++) {
1482 ColumnHeader col = Columns [i];
1484 display_indices [i] = index;
1486 int display_index = col.InternalDisplayIndex;
1487 if (display_index < index) {
1488 display_indices [i] = display_index;
1490 display_indices [i] = (display_index + 1);
1495 ReorderColumns (display_indices, redraw);
1499 Size LargeIconItemSize
1502 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1503 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1504 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1505 int w = Math.Max (text_size.Width, image_w);
1508 w += 2 + CheckBoxSize.Width;
1510 return new Size (w, h);
1514 Size SmallIconItemSize {
1516 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1517 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1518 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1519 int w = text_size.Width + image_w;
1522 w += 2 + CheckBoxSize.Width;
1524 return new Size (w, h);
1531 // Calculate tile size if needed
1532 // It appears that using Font.Size instead of a SizeF value can give us
1533 // a slightly better approach to the proportions defined in .Net
1534 if (tile_size == Size.Empty) {
1535 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1536 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1537 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1538 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1540 tile_size = new Size (w, h);
1548 int GetDetailsItemHeight ()
1551 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1552 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1553 item_height = Math.Max (checkbox_height, text_size.Height);
1554 item_height = Math.Max (item_height, small_image_height);
1558 void SetItemLocation (int index, int x, int y, int row, int col)
1560 Point old_location = items_location [index];
1561 if (old_location.X == x && old_location.Y == y)
1564 Size item_size = ItemSize;
1565 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1567 items_location [index] = new Point (x, y);
1568 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1571 // Initial position matches item's position in ListViewItemCollection
1573 reordered_items_indices [index] = index;
1575 // Invalidate both previous and new bounds
1576 item_control.Invalidate (old_rect);
1577 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1581 void ShiftItemsPositions (int from, int to, bool forward)
1584 for (int i = to + 1; i > from; i--) {
1585 reordered_items_indices [i] = reordered_items_indices [i - 1];
1587 ListViewItem item = items [reordered_items_indices [i]];
1588 item_control.Invalidate (item.Bounds);
1589 item.DisplayIndex = i;
1590 item_control.Invalidate (item.Bounds);
1593 for (int i = from - 1; i < to; i++) {
1594 reordered_items_indices [i] = reordered_items_indices [i + 1];
1596 ListViewItem item = items [reordered_items_indices [i]];
1597 item_control.Invalidate (item.Bounds);
1598 item.DisplayIndex = i;
1599 item_control.Invalidate (item.Bounds);
1604 internal void ChangeItemLocation (int display_index, Point new_pos)
1606 int new_display_index = GetDisplayIndexFromLocation (new_pos);
1607 if (new_display_index == display_index)
1610 int item_index = reordered_items_indices [display_index];
1611 ListViewItem item = items [item_index];
1613 bool forward = new_display_index < display_index;
1614 int index_from, index_to;
1616 index_from = new_display_index;
1617 index_to = display_index - 1;
1619 index_from = display_index + 1;
1620 index_to = new_display_index;
1623 ShiftItemsPositions (index_from, index_to, forward);
1625 reordered_items_indices [new_display_index] = item_index;
1627 item_control.Invalidate (item.Bounds);
1628 item.DisplayIndex = new_display_index;
1629 item_control.Invalidate (item.Bounds);
1632 int GetDisplayIndexFromLocation (Point loc)
1634 int display_index = -1;
1635 Rectangle item_area;
1638 if (loc.X < 0 || loc.Y < 0)
1641 // Adjustment to put in the next position refered by 'loc'
1642 loc.X -= item_size.Width / 2;
1646 for (int i = 0; i < items.Count; i++) {
1647 item_area = new Rectangle (GetItemLocation (i), item_size);
1648 item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
1649 ThemeEngine.Current.ListViewVerticalSpacing);
1651 if (item_area.Contains (loc)) {
1657 // Put in in last position
1658 if (display_index == -1)
1659 display_index = items.Count - 1;
1661 return display_index;
1664 // When using groups, the items with no group assigned
1665 // belong to the DefaultGroup
1666 int GetDefaultGroupItems ()
1669 foreach (ListViewItem item in items)
1670 if (item.Group == null)
1679 int[,] item_index_matrix;
1681 void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1683 Rectangle area = ClientRectangle;
1685 if (show_groups && groups.Count > 0 && view != View.List) {
1686 // When groups are used the alignment is always top-aligned
1690 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1691 for (int i = 0; i < groups.InternalCount; i++) {
1692 ListViewGroup group = groups.GetInternalGroup (i);
1693 int items_in_group = group.ItemCount;
1695 if (items_in_group == 0)
1698 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1699 if (group_cols <= 0)
1701 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1703 group.starting_row = rows;
1704 group.rows = group_rows;
1706 cols = Math.Max (group_cols, cols);
1712 // Simple matrix if no groups are used
1714 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1717 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1719 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1722 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1726 item_index_matrix = new int [rows, cols];
1729 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1731 header_control.Visible = false;
1732 header_control.Size = Size.Empty;
1733 item_control.Visible = true;
1734 item_control.Location = Point.Empty;
1735 ItemSize = item_size; // Cache item size
1737 if (items.Count == 0)
1740 Size sz = item_size;
1742 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1745 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1747 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1748 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1752 CalculateGroupsLayout (sz, y_spacing, 0);
1755 int row = 0, col = 0;
1758 for (int i = 0; i < items.Count; i++) {
1759 ListViewItem item = items [i];
1762 ListViewGroup group = item.Group;
1764 group = groups.DefaultGroup;
1766 Point group_items_loc = group.items_area_location;
1767 int current_item = group.current_item++;
1768 int starting_row = group.starting_row;
1770 row = (current_item / cols);
1771 col = current_item % cols;
1773 x = col * (item_size.Width + x_spacing);
1774 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1776 SetItemLocation (i, x, y, row + starting_row, col);
1777 item_index_matrix [row + starting_row, col] = i;
1781 x = col * (item_size.Width + x_spacing);
1782 y = row * (item_size.Height + y_spacing);
1784 SetItemLocation (i, x, y, row, col);
1785 item_index_matrix [row, col] = i;
1794 if (++col == cols) {
1805 item.DisplayIndex = i;
1806 item.SetPosition (new Point (x, y));
1812 item_control.Size = new Size (layout_wd, layout_ht);
1816 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1819 bool details = view == View.Details;
1821 for (int i = 0; i < groups.InternalCount; i++) {
1822 ListViewGroup group = groups.GetInternalGroup (i);
1823 if (group.ItemCount == 0)
1826 group.current_item = 0; // Reset layout
1827 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1830 layout_ht = y; // Update height taking into account Groups' headers heights
1833 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1835 Rectangle client_area = ClientRectangle;
1836 int header_height = text_size.Height + 10;
1838 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1839 group.items_area_location = new Point (0, y_origin + header_height);
1841 int items_area_height = ((item_height + y_spacing) * rows);
1842 return header_height + items_area_height + 10; // Add a small bottom margin
1846 void LayoutHeader ()
1849 for (int i = 0; i < Columns.Count; i++) {
1850 ColumnHeader col = GetReorderedColumn (i);
1853 col.CalcColumnHeader ();
1859 if (x < ClientRectangle.Width)
1860 x = ClientRectangle.Width;
1862 if (header_style == ColumnHeaderStyle.None) {
1863 header_control.Visible = false;
1864 header_control.Size = Size.Empty;
1865 layout_wd = ClientRectangle.Width;
1867 header_control.Width = x;
1868 header_control.Height = columns.Count > 0 ? columns [0].Ht : Font.Height + 5;
1869 header_control.Visible = true;
1873 void LayoutDetails ()
1877 if (columns.Count == 0) {
1878 item_control.Visible = false;
1879 layout_wd = ClientRectangle.Width;
1880 layout_ht = ClientRectangle.Height;
1884 item_control.Visible = true;
1885 item_control.Location = Point.Empty;
1886 item_control.Width = ClientRectangle.Width;
1888 int item_height = GetDetailsItemHeight ();
1889 ItemSize = new Size (0, item_height); // We only cache Height for details view
1890 int y = header_control.Height;
1892 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1894 CalculateGroupsLayout (ItemSize, 2, y);
1897 for (int i = 0; i < items.Count; i++) {
1898 ListViewItem item = items [i];
1900 if (!virtual_mode) // Virtual mode sets Layout until draw time
1904 item.DisplayIndex = i;
1909 ListViewGroup group = item.Group;
1911 group = groups.DefaultGroup;
1913 int current_item = group.current_item++;
1914 Point group_items_loc = group.items_area_location;
1916 y = current_item * (item_height + 2) + group_items_loc.Y;
1917 SetItemLocation (i, 0, y, 0, 0);
1918 item.SetPosition (new Point (0, y));
1922 SetItemLocation (i, 0, y, 0, 0);
1923 item.SetPosition (new Point (0, y));
1928 // some space for bottom gridline
1929 if (items.Count > 0 && grid_lines)
1933 if (!using_groups) // With groups it has been previously computed
1938 private void AdjustItemsPositionArray (int count)
1940 if (items_location.Length >= count)
1943 // items_location, items_matrix_location and reordered_items_indices must keep the same length
1944 count = Math.Max (count, items_location.Length * 2);
1945 items_location = new Point [count];
1946 items_matrix_location = new ItemMatrixLocation [count];
1947 reordered_items_indices = new int [count];
1950 private void CalculateListView (ListViewAlignment align)
1954 AdjustItemsPositionArray (items.Count);
1961 case View.SmallIcon:
1962 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
1963 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1966 case View.LargeIcon:
1967 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
1968 ThemeEngine.Current.ListViewHorizontalSpacing,
1969 ThemeEngine.Current.ListViewVerticalSpacing);
1973 LayoutIcons (SmallIconItemSize, true,
1974 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1978 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
1979 ThemeEngine.Current.ListViewHorizontalSpacing,
1980 ThemeEngine.Current.ListViewVerticalSpacing);
1985 CalculateScrollBars ();
1988 internal Point GetItemLocation (int index)
1990 Point loc = items_location [index];
1991 loc.X -= h_marker; // Adjust to scroll
1997 internal int GetItemIndex (int display_index)
1999 return reordered_items_indices [display_index];
2002 private bool KeySearchString (KeyEventArgs ke)
2004 int current_tickcnt = Environment.TickCount;
2005 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2006 keysearch_text = string.Empty;
2009 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2012 keysearch_text += (char)ke.KeyCode;
2013 keysearch_tickcnt = current_tickcnt;
2015 int start = FocusedItem == null ? 0 : FocusedItem.Index;
2018 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
2019 CompareOptions.IgnoreCase)) {
2021 items [i].Selected = true;
2025 i = (i + 1 < Items.Count) ? i+1 : 0;
2033 int GetAdjustedIndex (Keys key)
2037 if (View == View.Details) {
2040 result = FocusedItem.Index - 1;
2043 result = FocusedItem.Index + 1;
2044 if (result == items.Count)
2048 int last_index = LastVisibleIndex;
2049 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2050 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2052 if (FocusedItem.Index == last_index) {
2053 if (FocusedItem.Index < Items.Count - 1) {
2054 int page_size = item_control.Height / ItemSize.Height - 1;
2055 result = FocusedItem.Index + page_size - 1;
2056 if (result >= Items.Count)
2057 result = Items.Count - 1;
2060 result = last_index;
2063 int first_index = FirstVisibleIndex;
2064 if (GetItemLocation (first_index).Y < 0)
2066 if (FocusedItem.Index == first_index) {
2067 if (first_index > 0) {
2068 int page_size = item_control.Height / ItemSize.Height - 1;
2069 result = first_index - page_size + 1;
2074 result = first_index;
2080 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.Index];
2081 int row = item_matrix_location.Row;
2082 int col = item_matrix_location.Col;
2088 return item_index_matrix [row, col - 1];
2091 if (col == (cols - 1))
2093 while (item_index_matrix [row, col + 1] == 0) {
2098 return item_index_matrix [row, col + 1];
2103 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2108 return item_index_matrix [row - 1, col];
2111 if (row == (rows - 1) || row == Items.Count - 1)
2113 while (item_index_matrix [row + 1, col] == 0) {
2118 return item_index_matrix [row + 1, col];
2125 ListViewItem selection_start;
2127 private bool SelectItems (ArrayList sel_items)
2129 bool changed = false;
2130 foreach (ListViewItem item in SelectedItems)
2131 if (!sel_items.Contains (item)) {
2132 item.Selected = false;
2135 foreach (ListViewItem item in sel_items)
2136 if (!item.Selected) {
2137 item.Selected = true;
2143 private void UpdateMultiSelection (int index, bool reselect)
2145 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2146 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2147 ListViewItem item = items [index];
2149 if (shift_pressed && selection_start != null) {
2150 ArrayList list = new ArrayList ();
2151 int start_index = selection_start.Index;
2152 int start = Math.Min (start_index, index);
2153 int end = Math.Max (start_index, index);
2154 if (View == View.Details) {
2155 for (int i = start; i <= end; i++)
2156 list.Add (items [i]);
2158 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2159 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2160 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2161 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2162 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2163 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2165 for (int i = 0; i < items.Count; i++) {
2166 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2168 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2169 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2170 list.Add (items [i]);
2174 } else if (ctrl_pressed) {
2175 item.Selected = !item.Selected;
2176 selection_start = item;
2179 // do not unselect, and reselect the item
2180 foreach (int itemIndex in SelectedIndices) {
2181 if (index == itemIndex)
2183 items [itemIndex].Selected = false;
2186 SelectedItems.Clear ();
2187 item.Selected = true;
2189 selection_start = item;
2193 internal override bool InternalPreProcessMessage (ref Message msg)
2195 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2196 Keys key_data = (Keys)msg.WParam.ToInt32();
2198 HandleNavKeys (key_data);
2201 return base.InternalPreProcessMessage (ref msg);
2204 bool HandleNavKeys (Keys key_data)
2206 if (Items.Count == 0 || !item_control.Visible)
2209 if (FocusedItem == null)
2214 SelectIndex (Items.Count - 1);
2227 SelectIndex (GetAdjustedIndex (key_data));
2231 ToggleItemsCheckState ();
2234 if (selected_indices.Count > 0)
2235 OnItemActivate (EventArgs.Empty);
2245 void ToggleItemsCheckState ()
2250 // Don't modify check state if StateImageList has less than 2 elements
2251 if (StateImageList != null && StateImageList.Images.Count < 2)
2254 if (SelectedIndices.Count > 0) {
2255 for (int i = 0; i < SelectedIndices.Count; i++) {
2256 ListViewItem item = Items [SelectedIndices [i]];
2257 item.Checked = !item.Checked;
2262 if (FocusedItem != null) {
2263 FocusedItem.Checked = !FocusedItem.Checked;
2264 SelectIndex (FocusedItem.Index);
2268 void SelectIndex (int index)
2274 UpdateMultiSelection (index, true);
2275 else if (!items [index].Selected)
2276 items [index].Selected = true;
2278 SetFocusedItem (index);
2279 EnsureVisible (index);
2282 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2284 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2287 if (ke.Alt || ke.Control)
2290 ke.Handled = KeySearchString (ke);
2293 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2295 Point loc = PointToClient (Control.MousePosition);
2296 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2299 internal class ItemControl : Control {
2302 ListViewItem clicked_item;
2303 ListViewItem last_clicked_item;
2304 bool hover_processed = false;
2305 bool checking = false;
2306 ListViewItem prev_hovered_item;
2308 ListViewItem prev_tooltip_item;
2311 Point drag_begin = new Point (-1, -1);
2312 internal int dragged_item_index = -1;
2314 ListViewLabelEditTextBox edit_text_box;
2315 internal ListViewItem edit_item;
2316 LabelEditEventArgs edit_args;
2318 public ItemControl (ListView owner)
2321 DoubleClick += new EventHandler(ItemsDoubleClick);
2322 MouseDown += new MouseEventHandler(ItemsMouseDown);
2323 MouseMove += new MouseEventHandler(ItemsMouseMove);
2324 MouseHover += new EventHandler(ItemsMouseHover);
2325 MouseUp += new MouseEventHandler(ItemsMouseUp);
2328 void ItemsDoubleClick (object sender, EventArgs e)
2330 if (owner.activation == ItemActivation.Standard)
2331 owner.OnItemActivate (EventArgs.Empty);
2341 BoxSelect box_select_mode = BoxSelect.None;
2342 IList prev_selection;
2343 Point box_select_start;
2345 Rectangle box_select_rect;
2346 internal Rectangle BoxSelectRectangle {
2347 get { return box_select_rect; }
2349 if (box_select_rect == value)
2352 InvalidateBoxSelectRect ();
2353 box_select_rect = value;
2354 InvalidateBoxSelectRect ();
2358 void InvalidateBoxSelectRect ()
2360 if (BoxSelectRectangle.Size.IsEmpty)
2363 Rectangle edge = BoxSelectRectangle;
2369 edge.Y = BoxSelectRectangle.Bottom - 1;
2371 edge.Y = BoxSelectRectangle.Y - 1;
2373 edge.Height = BoxSelectRectangle.Height + 2;
2375 edge.X = BoxSelectRectangle.Right - 1;
2379 private Rectangle CalculateBoxSelectRectangle (Point pt)
2381 int left = Math.Min (box_select_start.X, pt.X);
2382 int right = Math.Max (box_select_start.X, pt.X);
2383 int top = Math.Min (box_select_start.Y, pt.Y);
2384 int bottom = Math.Max (box_select_start.Y, pt.Y);
2385 return Rectangle.FromLTRB (left, top, right, bottom);
2388 bool BoxIntersectsItem (int index)
2390 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2391 if (owner.View != View.Details) {
2393 r.Y += r.Height / 4;
2397 return BoxSelectRectangle.IntersectsWith (r);
2400 bool BoxIntersectsText (int index)
2402 Rectangle r = owner.Items [index].TextBounds;
2403 return BoxSelectRectangle.IntersectsWith (r);
2406 ArrayList BoxSelectedItems {
2408 ArrayList result = new ArrayList ();
2409 for (int i = 0; i < owner.Items.Count; i++) {
2411 if (owner.View == View.Details && !owner.FullRowSelect)
2412 intersects = BoxIntersectsText (i);
2414 intersects = BoxIntersectsItem (i);
2417 result.Add (owner.Items [i]);
2423 private bool PerformBoxSelection (Point pt)
2425 if (box_select_mode == BoxSelect.None)
2428 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2430 ArrayList box_items = BoxSelectedItems;
2434 switch (box_select_mode) {
2436 case BoxSelect.Normal:
2440 case BoxSelect.Control:
2441 items = new ArrayList ();
2442 foreach (int index in prev_selection)
2443 if (!box_items.Contains (owner.Items [index]))
2444 items.Add (owner.Items [index]);
2445 foreach (ListViewItem item in box_items)
2446 if (!prev_selection.Contains (item.Index))
2450 case BoxSelect.Shift:
2452 foreach (ListViewItem item in box_items)
2453 prev_selection.Remove (item.Index);
2454 foreach (int index in prev_selection)
2455 items.Add (owner.Items [index]);
2459 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2463 owner.SelectItems (items);
2469 private void ItemsMouseDown (object sender, MouseEventArgs me)
2471 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2472 if (owner.items.Count == 0)
2475 bool box_selecting = false;
2476 Size item_size = owner.ItemSize;
2477 Point pt = new Point (me.X, me.Y);
2478 for (int i = 0; i < owner.items.Count; i++) {
2479 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2480 if (!item_rect.Contains (pt))
2483 // Actual item in 'i' position
2484 ListViewItem item = owner.items [owner.reordered_items_indices [i]];
2486 if (item.CheckRectReal.Contains (pt)) {
2487 // Don't modify check state if we have only one image
2488 // and if we are in 1.1 profile only take into account
2490 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2497 // Generate an extra ItemCheck event when we got two clicks
2498 // (Match weird .Net behaviour)
2500 item.Checked = !item.Checked;
2502 item.Checked = !item.Checked;
2507 if (owner.View == View.Details) {
2508 bool over_text = item.TextBounds.Contains (pt);
2509 if (owner.FullRowSelect) {
2510 clicked_item = owner.items [i];
2511 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2512 if (!over_text && over_item_column && owner.MultiSelect)
2513 box_selecting = true;
2514 } else if (over_text)
2515 clicked_item = item;
2517 owner.SetFocusedItem (i);
2519 clicked_item = item;
2525 if (clicked_item != null) {
2526 bool changed = !clicked_item.Selected;
2527 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2528 owner.SetFocusedItem (clicked_item.Index);
2530 if (owner.MultiSelect) {
2531 bool reselect = (!owner.LabelEdit || changed);
2532 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2533 owner.UpdateMultiSelection (clicked_item.Index, reselect);
2535 clicked_item.Selected = true;
2539 if (owner.VirtualMode && changed) {
2540 // Broken event - It's not fired from Item.Selected also
2541 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2542 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2544 owner.OnVirtualItemsSelectionRangeChanged (args);
2547 // Report clicks only if the item was clicked. On MS the
2548 // clicks are only raised if you click an item
2550 if (me.Clicks > 1) {
2551 if (owner.CheckBoxes)
2552 clicked_item.Checked = !clicked_item.Checked;
2553 } else if (me.Clicks == 1) {
2554 if (owner.LabelEdit && !changed)
2555 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2558 if (owner.MultiSelect)
2559 box_selecting = true;
2560 else if (owner.SelectedItems.Count > 0)
2561 owner.SelectedItems.Clear ();
2564 if (box_selecting) {
2565 Keys mods = XplatUI.State.ModifierKeys;
2566 if ((mods & Keys.Shift) != 0)
2567 box_select_mode = BoxSelect.Shift;
2568 else if ((mods & Keys.Control) != 0)
2569 box_select_mode = BoxSelect.Control;
2571 box_select_mode = BoxSelect.Normal;
2572 box_select_start = pt;
2573 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2577 private void ItemsMouseMove (object sender, MouseEventArgs me)
2579 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2581 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2585 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2586 !hover_processed && owner.Activation != ItemActivation.OneClick
2588 && !owner.ShowItemToolTips
2593 Point pt = PointToClient (Control.MousePosition);
2594 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2596 if (hover_processed && item != null && item != prev_hovered_item) {
2597 hover_processed = false;
2598 XplatUI.ResetMouseHover (Handle);
2601 // Need to invalidate the item in HotTracking to show/hide the underline style
2602 if (owner.Activation == ItemActivation.OneClick) {
2603 if (item == null && owner.HotItemIndex != -1) {
2605 if (owner.HotTracking)
2606 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2609 Cursor = Cursors.Default;
2610 owner.HotItemIndex = -1;
2611 } else if (item != null && owner.HotItemIndex == -1) {
2613 if (owner.HotTracking)
2614 Invalidate (item.Bounds);
2617 Cursor = Cursors.Hand;
2618 owner.HotItemIndex = item.Index;
2622 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2623 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2625 drag_begin = new Point (me.X, me.Y);
2626 dragged_item_index = item.Index;
2630 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2631 if (!r.Contains (me.X, me.Y)) {
2632 ListViewItem dragged_item = owner.items [dragged_item_index];
2633 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2635 drag_begin = new Point (-1, -1);
2636 dragged_item_index = -1;
2642 if (owner.ShowItemToolTips) {
2644 owner.item_tooltip.Active = false;
2645 prev_tooltip_item = null;
2646 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2647 owner.item_tooltip.Active = true;
2648 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2649 prev_tooltip_item = item;
2656 private void ItemsMouseHover (object sender, EventArgs e)
2658 if (owner.hover_pending) {
2659 owner.OnMouseHover (e);
2660 owner.hover_pending = false;
2666 hover_processed = true;
2667 Point pt = PointToClient (Control.MousePosition);
2668 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2672 prev_hovered_item = item;
2674 if (owner.HoverSelection) {
2675 if (owner.MultiSelect)
2676 owner.UpdateMultiSelection (item.Index, true);
2678 item.Selected = true;
2680 owner.SetFocusedItem (item.Index);
2681 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2685 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2689 void HandleClicks (MouseEventArgs me)
2691 // if the click is not on an item,
2692 // clicks remains as 0
2695 owner.OnDoubleClick (EventArgs.Empty);
2696 } else if (clicks == 1) {
2697 owner.OnClick (EventArgs.Empty);
2699 owner.OnDoubleClick (EventArgs.Empty);
2700 owner.OnMouseDoubleClick (me);
2701 } else if (clicks == 1) {
2702 owner.OnClick (EventArgs.Empty);
2703 owner.OnMouseClick (me);
2710 private void ItemsMouseUp (object sender, MouseEventArgs me)
2712 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2713 HandleClicks (owner_me);
2716 if (owner.Items.Count == 0) {
2718 owner.OnMouseUp (owner_me);
2722 Point pt = new Point (me.X, me.Y);
2724 Rectangle rect = Rectangle.Empty;
2725 if (clicked_item != null) {
2726 if (owner.view == View.Details && !owner.full_row_select)
2727 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2729 rect = clicked_item.Bounds;
2731 if (rect.Contains (pt)) {
2732 switch (owner.activation) {
2733 case ItemActivation.OneClick:
2734 owner.OnItemActivate (EventArgs.Empty);
2737 case ItemActivation.TwoClick:
2738 if (last_clicked_item == clicked_item) {
2739 owner.OnItemActivate (EventArgs.Empty);
2740 last_clicked_item = null;
2742 last_clicked_item = clicked_item;
2745 // DoubleClick activation is handled in another handler
2749 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2750 // Need this to clean up background clicks
2751 owner.SelectedItems.Clear ();
2755 owner.OnMouseUp (owner_me);
2758 private void ResetMouseState ()
2760 clicked_item = null;
2761 box_select_start = Point.Empty;
2762 BoxSelectRectangle = Rectangle.Empty;
2763 prev_selection = null;
2764 box_select_mode = BoxSelect.None;
2767 // Clean these bits in case the mouse buttons were
2768 // released before firing ItemDrag
2769 dragged_item_index = -1;
2770 drag_begin = new Point (-1, -1);
2773 private void LabelEditFinished (object sender, EventArgs e)
2775 EndEdit (edit_item);
2778 private void LabelEditCancelled (object sender, EventArgs e)
2780 edit_args.SetLabel (null);
2781 EndEdit (edit_item);
2784 private void LabelTextChanged (object sender, EventArgs e)
2786 if (edit_args != null)
2787 edit_args.SetLabel (edit_text_box.Text);
2790 internal void BeginEdit (ListViewItem item)
2792 if (edit_item != null)
2793 EndEdit (edit_item);
2795 if (edit_text_box == null) {
2796 edit_text_box = new ListViewLabelEditTextBox ();
2797 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2798 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2799 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2800 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2801 edit_text_box.Visible = false;
2802 Controls.Add (edit_text_box);
2805 item.EnsureVisible();
2807 edit_text_box.Reset ();
2809 switch (owner.view) {
2811 case View.SmallIcon:
2813 edit_text_box.TextAlign = HorizontalAlignment.Left;
2814 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2815 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2816 edit_text_box.Width = (int)sizef.Width + 4;
2817 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2818 edit_text_box.WordWrap = false;
2819 edit_text_box.Multiline = false;
2821 case View.LargeIcon:
2822 edit_text_box.TextAlign = HorizontalAlignment.Center;
2823 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2824 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2825 edit_text_box.Width = (int)sizef.Width + 4;
2826 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2827 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2828 edit_text_box.WordWrap = true;
2829 edit_text_box.Multiline = true;
2835 edit_text_box.Text = item.Text;
2836 edit_text_box.Font = item.Font;
2837 edit_text_box.Visible = true;
2838 edit_text_box.Focus ();
2839 edit_text_box.SelectAll ();
2841 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2842 owner.OnBeforeLabelEdit (edit_args);
2844 if (edit_args.CancelEdit)
2848 internal void CancelEdit (ListViewItem item)
2850 // do nothing if there's no item being edited, or if the
2851 // item being edited is not the one passed in
2852 if (edit_item == null || edit_item != item)
2855 edit_args.SetLabel (null);
2859 internal void EndEdit (ListViewItem item)
2861 // do nothing if there's no item being edited, or if the
2862 // item being edited is not the one passed in
2863 if (edit_item == null || edit_item != item)
2866 if (edit_text_box != null) {
2867 if (edit_text_box.Visible)
2868 edit_text_box.Visible = false;
2869 // ensure listview gets focus
2873 // Same as TreeView.EndEdit: need to have focus in synch
2874 Application.DoEvents ();
2877 // Create a new instance, since we could get a call to BeginEdit
2878 // from the handler and have fields out of synch
2880 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2883 owner.OnAfterLabelEdit (args);
2884 if (!args.CancelEdit && args.Label != null)
2885 item.Text = args.Label;
2888 internal override void OnPaintInternal (PaintEventArgs pe)
2890 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2893 protected override void WndProc (ref Message m)
2895 switch ((Msg)m.Msg) {
2896 case Msg.WM_KILLFOCUS:
2897 owner.Select (false, true);
2899 case Msg.WM_SETFOCUS:
2900 owner.Select (false, true);
2902 case Msg.WM_LBUTTONDOWN:
2904 owner.Select (false, true);
2906 case Msg.WM_RBUTTONDOWN:
2908 owner.Select (false, true);
2913 base.WndProc (ref m);
2917 internal class ListViewLabelEditTextBox : TextBox
2922 int max_height = -1;
2923 int min_height = -1;
2925 int old_number_lines = 1;
2927 SizeF text_size_one_char;
2929 public ListViewLabelEditTextBox ()
2931 min_height = DefaultSize.Height;
2932 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2935 public int MaxWidth {
2937 if (value < min_width)
2938 max_width = min_width;
2944 public int MaxHeight {
2946 if (value < min_height)
2947 max_height = min_height;
2953 public new int Width {
2963 public override Font Font {
2969 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2973 protected override void OnTextChanged (EventArgs e)
2975 SizeF text_size = TextRenderer.MeasureString (Text, Font);
2977 int new_width = (int)text_size.Width + 8;
2980 ResizeTextBoxWidth (new_width);
2982 if (Width != max_width)
2983 ResizeTextBoxWidth (new_width);
2985 int number_lines = Lines.Length;
2987 if (number_lines != old_number_lines) {
2988 int new_height = number_lines * (int)text_size_one_char.Height + 4;
2989 old_number_lines = number_lines;
2991 ResizeTextBoxHeight (new_height);
2995 base.OnTextChanged (e);
2998 protected override bool IsInputKey (Keys key_data)
3000 if ((key_data & Keys.Alt) == 0) {
3001 switch (key_data & Keys.KeyCode) {
3008 return base.IsInputKey (key_data);
3011 protected override void OnKeyDown (KeyEventArgs e)
3016 switch (e.KeyCode) {
3020 OnEditingFinished (e);
3025 OnEditingCancelled (e);
3030 protected override void OnLostFocus (EventArgs e)
3033 OnEditingFinished (e);
3037 protected void OnEditingCancelled (EventArgs e)
3039 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3044 protected void OnEditingFinished (EventArgs e)
3046 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3051 private void ResizeTextBoxWidth (int new_width)
3053 if (new_width > max_width)
3054 base.Width = max_width;
3056 if (new_width >= min_width)
3057 base.Width = new_width;
3059 base.Width = min_width;
3062 private void ResizeTextBoxHeight (int new_height)
3064 if (new_height > max_height)
3065 base.Height = max_height;
3067 if (new_height >= min_height)
3068 base.Height = new_height;
3070 base.Height = min_height;
3073 public void Reset ()
3080 old_number_lines = 1;
3082 Text = String.Empty;
3087 static object EditingCancelledEvent = new object ();
3088 public event EventHandler EditingCancelled {
3089 add { Events.AddHandler (EditingCancelledEvent, value); }
3090 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3093 static object EditingFinishedEvent = new object ();
3094 public event EventHandler EditingFinished {
3095 add { Events.AddHandler (EditingFinishedEvent, value); }
3096 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3100 internal override void OnPaintInternal (PaintEventArgs pe)
3105 CalculateScrollBars ();
3108 void FocusChanged (object o, EventArgs args)
3110 if (Items.Count == 0)
3113 if (FocusedItem == null)
3116 ListViewItem focused_item = FocusedItem;
3118 if (focused_item.ListView != null) {
3119 item_control.Invalidate (focused_item.Bounds);
3120 focused_item.Layout ();
3121 item_control.Invalidate (focused_item.Bounds);
3125 private void ListView_MouseEnter (object sender, EventArgs args)
3127 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3130 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3132 if (Items.Count == 0)
3135 int lines = me.Delta / 120;
3142 case View.SmallIcon:
3143 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3145 case View.LargeIcon:
3146 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3149 Scroll (h_scroll, -ItemSize.Width * lines);
3153 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3159 private void ListView_SizeChanged (object sender, EventArgs e)
3161 CalculateListView (alignment);
3164 private void SetFocusedItem (int index)
3167 items [index].Focused = true;
3168 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3169 items [focused_item_index].Focused = false;
3171 focused_item_index = index;
3174 private void HorizontalScroller (object sender, EventArgs e)
3176 item_control.EndEdit (item_control.edit_item);
3178 // Avoid unnecessary flickering, when button is
3179 // kept pressed at the end
3180 if (h_marker != h_scroll.Value) {
3182 int pixels = h_marker - h_scroll.Value;
3184 h_marker = h_scroll.Value;
3185 if (header_control.Visible)
3186 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3188 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3192 private void VerticalScroller (object sender, EventArgs e)
3194 item_control.EndEdit (item_control.edit_item);
3196 // Avoid unnecessary flickering, when button is
3197 // kept pressed at the end
3198 if (v_marker != v_scroll.Value) {
3199 int pixels = v_marker - v_scroll.Value;
3200 Rectangle area = item_control.ClientRectangle;
3201 if (header_control.Visible) {
3202 area.Y += header_control.Height;
3203 area.Height -= header_control.Height;
3206 v_marker = v_scroll.Value;
3207 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3211 internal override bool IsInputCharInternal (char charCode)
3215 #endregion // Internal Methods Properties
3217 #region Protected Methods
3218 protected override void CreateHandle ()
3220 base.CreateHandle ();
3221 for (int i = 0; i < SelectedItems.Count; i++)
3222 OnSelectedIndexChanged (EventArgs.Empty);
3225 protected override void Dispose (bool disposing)
3228 h_scroll.Dispose ();
3229 v_scroll.Dispose ();
3231 large_image_list = null;
3232 small_image_list = null;
3233 state_image_list = null;
3235 foreach (ColumnHeader col in columns)
3236 col.SetListView (null);
3239 if (!virtual_mode) // In virtual mode we don't save the items
3241 foreach (ListViewItem item in items)
3245 base.Dispose (disposing);
3248 protected override bool IsInputKey (Keys keyData)
3265 return base.IsInputKey (keyData);
3268 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3270 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3276 protected override void OnBackgroundImageChanged (EventArgs args)
3278 item_control.BackgroundImage = BackgroundImage;
3279 base.OnBackgroundImageChanged (args);
3283 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3285 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3290 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3292 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3298 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3300 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3305 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3307 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3312 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3314 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3320 protected override void OnEnabledChanged (EventArgs e)
3322 base.OnEnabledChanged (e);
3326 protected override void OnFontChanged (EventArgs e)
3328 base.OnFontChanged (e);
3332 protected override void OnHandleCreated (EventArgs e)
3334 base.OnHandleCreated (e);
3335 CalculateListView (alignment);
3337 if (!virtual_mode) // Sorting is not allowed in virtual mode
3342 protected override void OnHandleDestroyed (EventArgs e)
3344 base.OnHandleDestroyed (e);
3347 protected virtual void OnItemActivate (EventArgs e)
3349 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3354 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3356 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3362 protected internal virtual void OnItemChecked (ItemCheckedEventArgs icea)
3364 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3370 protected virtual void OnItemDrag (ItemDragEventArgs e)
3372 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3378 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs args)
3380 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3385 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs args)
3387 ListViewItemSelectionChangedEventHandler eh =
3388 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3393 protected override void OnMouseHover (EventArgs args)
3395 base.OnMouseHover (args);
3398 protected override void OnParentChanged (EventArgs args)
3400 base.OnParentChanged (args);
3404 protected virtual void OnSelectedIndexChanged (EventArgs e)
3406 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3411 protected override void OnSystemColorsChanged (EventArgs e)
3413 base.OnSystemColorsChanged (e);
3417 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs args)
3419 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3424 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs args)
3426 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3431 [EditorBrowsable (EditorBrowsableState.Advanced)]
3432 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3434 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3439 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs args)
3441 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3446 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs args)
3448 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3449 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3455 protected void RealizeProperties ()
3460 protected void UpdateExtendedStyles ()
3465 bool refocusing = false;
3467 protected override void WndProc (ref Message m)
3469 switch ((Msg)m.Msg) {
3470 case Msg.WM_KILLFOCUS:
3471 Control receiver = Control.FromHandle (m.WParam);
3472 if (receiver == item_control) {
3478 case Msg.WM_SETFOCUS:
3488 base.WndProc (ref m);
3490 #endregion // Protected Methods
3492 #region Public Instance Methods
3493 public void ArrangeIcons ()
3495 ArrangeIcons (this.alignment);
3498 public void ArrangeIcons (ListViewAlignment alignment)
3500 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3501 if (view == View.LargeIcon || view == View.SmallIcon) {
3502 this.CalculateListView (alignment);
3503 // we have done the calculations already
3504 this.Redraw (false);
3509 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3511 if (columnIndex < 0 || columnIndex >= columns.Count)
3512 throw new ArgumentOutOfRangeException ("columnIndex");
3514 columns [columnIndex].AutoResize (headerAutoResize);
3517 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3520 foreach (ColumnHeader col in columns)
3521 col.AutoResize (headerAutoResize);
3526 public void BeginUpdate ()
3528 // flag to avoid painting
3532 public void Clear ()
3535 items.Clear (); // Redraw (true) called here
3538 public void EndUpdate ()
3540 // flag to avoid painting
3543 // probably, now we need a redraw with recalculations
3547 public void EnsureVisible (int index)
3549 if (index < 0 || index >= items.Count || scrollable == false)
3552 Rectangle view_rect = item_control.ClientRectangle;
3553 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3555 if (view_rect.Contains (bounds))
3558 if (View != View.Details) {
3559 if (bounds.Left < 0)
3560 h_scroll.Value += bounds.Left;
3561 else if (bounds.Right > view_rect.Right)
3562 h_scroll.Value += (bounds.Right - view_rect.Right);
3566 v_scroll.Value += bounds.Top;
3567 else if (bounds.Bottom > view_rect.Bottom)
3568 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3572 public ListViewItem FindItemWithText (string text)
3574 if (items.Count == 0)
3577 return FindItemWithText (text, true, 0, true);
3580 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex)
3582 return FindItemWithText (text, includeSubItems, startIndex, true);
3585 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex, bool prefixSearch)
3587 if (startIndex < 0 || startIndex >= items.Count)
3588 throw new ArgumentOutOfRangeException ("startIndex");
3591 throw new ArgumentNullException ("text");
3594 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3595 prefixSearch, includeSubItems, text, Point.Empty,
3596 SearchDirectionHint.Down, startIndex);
3598 OnSearchForVirtualItem (args);
3599 int idx = args.Index;
3600 if (idx >= 0 && idx < virtual_list_size)
3606 for (int i = startIndex; i < items.Count; i++) {
3607 ListViewItem lvi = items [i];
3609 if ((prefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3610 || String.Compare (lvi.Text, text, true) == 0) // match
3614 if (includeSubItems) {
3615 for (int i = startIndex; i < items.Count; i++) {
3616 ListViewItem lvi = items [i];
3617 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3618 if ((prefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3619 || String.Compare (sub_item.Text, text, true) == 0)
3627 public ListViewItem FindNearestItem (SearchDirectionHint direction, int x, int y)
3629 return FindNearestItem (direction, new Point (x, y));
3632 public ListViewItem FindNearestItem (SearchDirectionHint direction, Point location)
3634 if (direction < SearchDirectionHint.Left || direction > SearchDirectionHint.Down)
3635 throw new ArgumentOutOfRangeException ("searchDirection");
3637 if (view != View.LargeIcon && view != View.SmallIcon)
3638 throw new InvalidOperationException ();
3641 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3642 false, false, String.Empty, location,
3645 OnSearchForVirtualItem (args);
3646 int idx = args.Index;
3647 if (idx >= 0 && idx < virtual_list_size)
3653 ListViewItem item = null;
3654 int min_dist = Int32.MaxValue;
3657 // It looks like .Net does a previous adjustment
3659 switch (direction) {
3660 case SearchDirectionHint.Up:
3661 location.Y -= item_size.Height;
3663 case SearchDirectionHint.Down:
3664 location.Y += item_size.Height;
3666 case SearchDirectionHint.Left:
3667 location.X -= item_size.Width;
3669 case SearchDirectionHint.Right:
3670 location.X += item_size.Width;
3674 for (int i = 0; i < items.Count; i++) {
3675 Point item_loc = GetItemLocation (i);
3677 if (direction == SearchDirectionHint.Up) {
3678 if (location.Y < item_loc.Y)
3680 } else if (direction == SearchDirectionHint.Down) {
3681 if (location.Y > item_loc.Y)
3683 } else if (direction == SearchDirectionHint.Left) {
3684 if (location.X < item_loc.X)
3686 } else if (direction == SearchDirectionHint.Right) {
3687 if (location.X > item_loc.X)
3691 int x_dist = location.X - item_loc.X;
3692 int y_dist = location.Y - item_loc.Y;
3694 int dist = x_dist * x_dist + y_dist * y_dist;
3695 if (dist < min_dist) {
3705 public ListViewItem GetItemAt (int x, int y)
3707 Size item_size = ItemSize;
3708 for (int i = 0; i < items.Count; i++) {
3709 Point item_location = GetItemLocation (i);
3710 Rectangle item_rect = new Rectangle (item_location, item_size);
3711 if (item_rect.Contains (x, y))
3718 public Rectangle GetItemRect (int index)
3720 return GetItemRect (index, ItemBoundsPortion.Entire);
3723 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3725 if (index < 0 || index >= items.Count)
3726 throw new IndexOutOfRangeException ("index");
3728 return items [index].GetBounds (portion);
3732 public ListViewHitTestInfo HitTest (Point pt)
3734 return HitTest (pt.X, pt.Y);
3737 public ListViewHitTestInfo HitTest (int x, int y)
3740 throw new ArgumentOutOfRangeException ("x");
3742 throw new ArgumentOutOfRangeException ("y");
3744 ListViewItem item = GetItemAt (x, y);
3746 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3748 ListViewHitTestLocations locations = 0;
3749 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3750 locations |= ListViewHitTestLocations.Label;
3751 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3752 locations |= ListViewHitTestLocations.Image;
3753 else if (item.CheckRectReal.Contains (x, y))
3754 locations |= ListViewHitTestLocations.StateImage;
3756 ListViewItem.ListViewSubItem subitem = null;
3757 if (view == View.Details)
3758 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3759 if (si.Bounds.Contains (x, y)) {
3764 return new ListViewHitTestInfo (item, subitem, locations);
3767 [EditorBrowsable (EditorBrowsableState.Advanced)]
3768 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3770 if (startIndex < 0 || startIndex >= items.Count)
3771 throw new ArgumentOutOfRangeException ("startIndex");
3772 if (endIndex < 0 || endIndex >= items.Count)
3773 throw new ArgumentOutOfRangeException ("endIndex");
3774 if (startIndex > endIndex)
3775 throw new ArgumentException ("startIndex");
3780 for (int i = startIndex; i <= endIndex; i++)
3781 item_control.Invalidate (items [i].Bounds);
3783 if (!invalidateOnly)
3792 throw new InvalidOperationException ();
3798 // we need this overload to reuse the logic for sorting, while allowing
3799 // redrawing to be done by caller or have it done by this method when
3800 // sorting is really performed
3802 // ListViewItemCollection's Add and AddRange methods call this overload
3803 // with redraw set to false, as they take care of redrawing themselves
3804 // (they even want to redraw the listview if no sort is performed, as
3805 // an item was added), while ListView.Sort () only wants to redraw if
3806 // sorting was actually performed
3807 private void Sort (bool redraw)
3809 if (!IsHandleCreated || item_sorter == null) {
3813 items.Sort (item_sorter);
3818 public override string ToString ()
3820 int count = this.Items.Count;
3823 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3825 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3827 #endregion // Public Instance Methods
3832 class HeaderControl : Control {
3835 bool column_resize_active = false;
3836 ColumnHeader resize_column;
3837 ColumnHeader clicked_column;
3838 ColumnHeader drag_column;
3840 int drag_to_index = -1;
3842 public HeaderControl (ListView owner)
3845 MouseDown += new MouseEventHandler (HeaderMouseDown);
3846 MouseMove += new MouseEventHandler (HeaderMouseMove);
3847 MouseUp += new MouseEventHandler (HeaderMouseUp);
3850 private ColumnHeader ColumnAtX (int x)
3852 Point pt = new Point (x, 0);
3853 ColumnHeader result = null;
3854 foreach (ColumnHeader col in owner.Columns) {
3855 if (col.Rect.Contains (pt)) {
3863 private int GetReorderedIndex (ColumnHeader col)
3865 if (owner.reordered_column_indices == null)
3868 for (int i = 0; i < owner.Columns.Count; i++)
3869 if (owner.reordered_column_indices [i] == col.Index)
3871 throw new Exception ("Column index missing from reordered array");
3874 private void HeaderMouseDown (object sender, MouseEventArgs me)
3876 if (resize_column != null) {
3877 column_resize_active = true;
3882 clicked_column = ColumnAtX (me.X + owner.h_marker);
3884 if (clicked_column != null) {
3886 if (owner.AllowColumnReorder) {
3888 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3889 drag_column.Rect = clicked_column.Rect;
3890 drag_to_index = GetReorderedIndex (clicked_column);
3892 clicked_column.Pressed = true;
3893 Rectangle bounds = clicked_column.Rect;
3894 bounds.X -= owner.h_marker;
3895 Invalidate (bounds);
3902 column_resize_active = false;
3903 resize_column = null;
3905 Cursor = Cursors.Default;
3908 private void HeaderMouseMove (object sender, MouseEventArgs me)
3910 Point pt = new Point (me.X + owner.h_marker, me.Y);
3912 if (column_resize_active) {
3913 int width = pt.X - resize_column.X;
3917 if (!owner.CanProceedWithResize (resize_column, width)){
3921 resize_column.Width = width;
3925 resize_column = null;
3927 if (clicked_column != null) {
3928 if (owner.AllowColumnReorder) {
3931 r = drag_column.Rect;
3932 r.X = clicked_column.Rect.X + me.X - drag_x;
3933 drag_column.Rect = r;
3935 int x = me.X + owner.h_marker;
3936 ColumnHeader over = ColumnAtX (x);
3938 drag_to_index = owner.Columns.Count;
3939 else if (x < over.X + over.Width / 2)
3940 drag_to_index = GetReorderedIndex (over);
3942 drag_to_index = GetReorderedIndex (over) + 1;
3945 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
3946 bool pressed = clicked_column.Pressed;
3947 clicked_column.Pressed = over == clicked_column;
3948 if (clicked_column.Pressed ^ pressed) {
3949 Rectangle bounds = clicked_column.Rect;
3950 bounds.X -= owner.h_marker;
3951 Invalidate (bounds);
3957 for (int i = 0; i < owner.Columns.Count; i++) {
3958 Rectangle zone = owner.Columns [i].Rect;
3959 zone.X = zone.Right - 5;
3961 if (zone.Contains (pt)) {
3962 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
3964 resize_column = owner.Columns [i];
3969 if (resize_column == null)
3970 Cursor = Cursors.Default;
3972 Cursor = Cursors.VSplit;
3975 void HeaderMouseUp (object sender, MouseEventArgs me)
3979 if (column_resize_active) {
3980 int column_idx = resize_column.Index;
3982 owner.RaiseColumnWidthChanged (column_idx);
3986 if (clicked_column != null && clicked_column.Pressed) {
3987 clicked_column.Pressed = false;
3988 Rectangle bounds = clicked_column.Rect;
3989 bounds.X -= owner.h_marker;
3990 Invalidate (bounds);
3991 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
3994 if (drag_column != null && owner.AllowColumnReorder) {
3996 if (drag_to_index > GetReorderedIndex (clicked_column))
3998 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
3999 owner.ReorderColumn (clicked_column, drag_to_index, true);
4004 clicked_column = null;
4007 internal override void OnPaintInternal (PaintEventArgs pe)
4012 Theme theme = ThemeEngine.Current;
4013 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4015 if (drag_column == null)
4019 if (drag_to_index == owner.Columns.Count)
4020 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4022 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4023 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4026 protected override void WndProc (ref Message m)
4028 switch ((Msg)m.Msg) {
4029 case Msg.WM_SETFOCUS:
4033 base.WndProc (ref m);
4039 private class ItemComparer : IComparer {
4040 readonly SortOrder sort_order;
4042 public ItemComparer (SortOrder sortOrder)
4044 sort_order = sortOrder;
4047 public int Compare (object x, object y)
4049 ListViewItem item_x = x as ListViewItem;
4050 ListViewItem item_y = y as ListViewItem;
4051 if (sort_order == SortOrder.Ascending)
4052 return String.Compare (item_x.Text, item_y.Text);
4054 return String.Compare (item_y.Text, item_x.Text);
4059 [ListBindable (false)]
4061 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4063 private readonly ListView owner;
4065 #region Public Constructor
4066 public CheckedIndexCollection (ListView owner)
4070 #endregion // Public Constructor
4072 #region Public Properties
4075 get { return owner.CheckedItems.Count; }
4078 public bool IsReadOnly {
4079 get { return true; }
4082 public int this [int index] {
4084 int [] indices = GetIndices ();
4085 if (index < 0 || index >= indices.Length)
4086 throw new ArgumentOutOfRangeException ("index");
4087 return indices [index];
4091 bool ICollection.IsSynchronized {
4092 get { return false; }
4095 object ICollection.SyncRoot {
4096 get { return this; }
4099 bool IList.IsFixedSize {
4100 get { return true; }
4103 object IList.this [int index] {
4104 get { return this [index]; }
4105 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4107 #endregion // Public Properties
4109 #region Public Methods
4110 public bool Contains (int checkedIndex)
4112 int [] indices = GetIndices ();
4113 for (int i = 0; i < indices.Length; i++) {
4114 if (indices [i] == checkedIndex)
4120 public IEnumerator GetEnumerator ()
4122 int [] indices = GetIndices ();
4123 return indices.GetEnumerator ();
4126 void ICollection.CopyTo (Array dest, int index)
4128 int [] indices = GetIndices ();
4129 Array.Copy (indices, 0, dest, index, indices.Length);
4132 int IList.Add (object value)
4134 throw new NotSupportedException ("Add operation is not supported.");
4139 throw new NotSupportedException ("Clear operation is not supported.");
4142 bool IList.Contains (object checkedIndex)
4144 if (!(checkedIndex is int))
4146 return Contains ((int) checkedIndex);
4149 int IList.IndexOf (object checkedIndex)
4151 if (!(checkedIndex is int))
4153 return IndexOf ((int) checkedIndex);
4156 void IList.Insert (int index, object value)
4158 throw new NotSupportedException ("Insert operation is not supported.");
4161 void IList.Remove (object value)
4163 throw new NotSupportedException ("Remove operation is not supported.");
4166 void IList.RemoveAt (int index)
4168 throw new NotSupportedException ("RemoveAt operation is not supported.");
4171 public int IndexOf (int checkedIndex)
4173 int [] indices = GetIndices ();
4174 for (int i = 0; i < indices.Length; i++) {
4175 if (indices [i] == checkedIndex)
4180 #endregion // Public Methods
4182 private int [] GetIndices ()
4184 ArrayList checked_items = owner.CheckedItems.List;
4185 int [] indices = new int [checked_items.Count];
4186 for (int i = 0; i < checked_items.Count; i++) {
4187 ListViewItem item = (ListViewItem) checked_items [i];
4188 indices [i] = item.Index;
4192 } // CheckedIndexCollection
4195 [ListBindable (false)]
4197 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4199 private readonly ListView owner;
4200 private ArrayList list;
4202 #region Public Constructor
4203 public CheckedListViewItemCollection (ListView owner)
4206 this.owner.Items.Changed += new CollectionChangedHandler (
4207 ItemsCollection_Changed);
4209 #endregion // Public Constructor
4211 #region Public Properties
4215 if (!owner.CheckBoxes)
4221 public bool IsReadOnly {
4222 get { return true; }
4225 public ListViewItem this [int index] {
4228 if (owner.VirtualMode)
4229 throw new InvalidOperationException ();
4231 ArrayList checked_items = List;
4232 if (index < 0 || index >= checked_items.Count)
4233 throw new ArgumentOutOfRangeException ("index");
4234 return (ListViewItem) checked_items [index];
4239 public virtual ListViewItem this [string key] {
4241 int idx = IndexOfKey (key);
4242 return idx == -1 ? null : (ListViewItem) List [idx];
4247 bool ICollection.IsSynchronized {
4248 get { return false; }
4251 object ICollection.SyncRoot {
4252 get { return this; }
4255 bool IList.IsFixedSize {
4256 get { return true; }
4259 object IList.this [int index] {
4260 get { return this [index]; }
4261 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4263 #endregion // Public Properties
4265 #region Public Methods
4266 public bool Contains (ListViewItem item)
4268 if (!owner.CheckBoxes)
4270 return List.Contains (item);
4274 public virtual bool ContainsKey (string key)
4276 return IndexOfKey (key) != -1;
4280 public void CopyTo (Array dest, int index)
4283 if (owner.VirtualMode)
4284 throw new InvalidOperationException ();
4286 if (!owner.CheckBoxes)
4288 List.CopyTo (dest, index);
4291 public IEnumerator GetEnumerator ()
4294 if (owner.VirtualMode)
4295 throw new InvalidOperationException ();
4297 if (!owner.CheckBoxes)
4298 return (new ListViewItem [0]).GetEnumerator ();
4299 return List.GetEnumerator ();
4302 int IList.Add (object value)
4304 throw new NotSupportedException ("Add operation is not supported.");
4309 throw new NotSupportedException ("Clear operation is not supported.");
4312 bool IList.Contains (object item)
4314 if (!(item is ListViewItem))
4316 return Contains ((ListViewItem) item);
4319 int IList.IndexOf (object item)
4321 if (!(item is ListViewItem))
4323 return IndexOf ((ListViewItem) item);
4326 void IList.Insert (int index, object value)
4328 throw new NotSupportedException ("Insert operation is not supported.");
4331 void IList.Remove (object value)
4333 throw new NotSupportedException ("Remove operation is not supported.");
4336 void IList.RemoveAt (int index)
4338 throw new NotSupportedException ("RemoveAt operation is not supported.");
4341 public int IndexOf (ListViewItem item)
4344 if (owner.VirtualMode)
4345 throw new InvalidOperationException ();
4347 if (!owner.CheckBoxes)
4349 return List.IndexOf (item);
4353 public virtual int IndexOfKey (string key)
4356 if (owner.VirtualMode)
4357 throw new InvalidOperationException ();
4359 if (key == null || key.Length == 0)
4362 ArrayList checked_items = List;
4363 for (int i = 0; i < checked_items.Count; i++) {
4364 ListViewItem item = (ListViewItem) checked_items [i];
4365 if (String.Compare (key, item.Name, true) == 0)
4372 #endregion // Public Methods
4374 internal ArrayList List {
4377 list = new ArrayList ();
4378 foreach (ListViewItem item in owner.Items) {
4387 internal void Reset ()
4389 // force re-population of list
4393 private void ItemsCollection_Changed ()
4397 } // CheckedListViewItemCollection
4400 [ListBindable (false)]
4402 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4404 internal ArrayList list;
4405 private ListView owner;
4407 #region Public Constructor
4408 public ColumnHeaderCollection (ListView owner)
4410 list = new ArrayList ();
4413 #endregion // Public Constructor
4415 #region Public Properties
4418 get { return list.Count; }
4421 public bool IsReadOnly {
4422 get { return false; }
4425 public virtual ColumnHeader this [int index] {
4427 if (index < 0 || index >= list.Count)
4428 throw new ArgumentOutOfRangeException ("index");
4429 return (ColumnHeader) list [index];
4434 public virtual ColumnHeader this [string key] {
4436 int idx = IndexOfKey (key);
4440 return (ColumnHeader) list [idx];
4445 bool ICollection.IsSynchronized {
4446 get { return true; }
4449 object ICollection.SyncRoot {
4450 get { return this; }
4453 bool IList.IsFixedSize {
4454 get { return list.IsFixedSize; }
4457 object IList.this [int index] {
4458 get { return this [index]; }
4459 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4461 #endregion // Public Properties
4463 #region Public Methods
4464 public virtual int Add (ColumnHeader value)
4466 int idx = list.Add (value);
4467 owner.AddColumn (value, idx, true);
4471 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4473 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4474 this.Add (colHeader);
4479 public virtual ColumnHeader Add (string text)
4481 return Add (String.Empty, text);
4484 public virtual ColumnHeader Add (string text, int iwidth)
4486 return Add (String.Empty, text, iwidth);
4489 public virtual ColumnHeader Add (string key, string text)
4491 ColumnHeader colHeader = new ColumnHeader ();
4492 colHeader.Name = key;
4493 colHeader.Text = text;
4498 public virtual ColumnHeader Add (string key, string text, int iwidth)
4500 return Add (key, text, iwidth, HorizontalAlignment.Left, -1);
4503 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, int imageIndex)
4505 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
4506 colHeader.ImageIndex = imageIndex;
4511 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, string imageKey)
4513 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
4514 colHeader.ImageKey = imageKey;
4520 public virtual void AddRange (ColumnHeader [] values)
4522 foreach (ColumnHeader colHeader in values) {
4523 int idx = list.Add (colHeader);
4524 owner.AddColumn (colHeader, idx, false);
4527 owner.Redraw (true);
4530 public virtual void Clear ()
4532 foreach (ColumnHeader col in list)
4533 col.SetListView (null);
4535 owner.ReorderColumns (new int [0], true);
4538 public bool Contains (ColumnHeader value)
4540 return list.Contains (value);
4544 public virtual bool ContainsKey (string key)
4546 return IndexOfKey (key) != -1;
4550 public IEnumerator GetEnumerator ()
4552 return list.GetEnumerator ();
4555 void ICollection.CopyTo (Array dest, int index)
4557 list.CopyTo (dest, index);
4560 int IList.Add (object value)
4562 if (! (value is ColumnHeader)) {
4563 throw new ArgumentException ("Not of type ColumnHeader", "value");
4566 return this.Add ((ColumnHeader) value);
4569 bool IList.Contains (object value)
4571 if (! (value is ColumnHeader)) {
4572 throw new ArgumentException ("Not of type ColumnHeader", "value");
4575 return this.Contains ((ColumnHeader) value);
4578 int IList.IndexOf (object value)
4580 if (! (value is ColumnHeader)) {
4581 throw new ArgumentException ("Not of type ColumnHeader", "value");
4584 return this.IndexOf ((ColumnHeader) value);
4587 void IList.Insert (int index, object value)
4589 if (! (value is ColumnHeader)) {
4590 throw new ArgumentException ("Not of type ColumnHeader", "value");
4593 this.Insert (index, (ColumnHeader) value);
4596 void IList.Remove (object value)
4598 if (! (value is ColumnHeader)) {
4599 throw new ArgumentException ("Not of type ColumnHeader", "value");
4602 this.Remove ((ColumnHeader) value);
4605 public int IndexOf (ColumnHeader value)
4607 return list.IndexOf (value);
4611 public virtual int IndexOfKey (string key)
4613 if (key == null || key.Length == 0)
4616 for (int i = 0; i < list.Count; i++) {
4617 ColumnHeader col = (ColumnHeader) list [i];
4618 if (String.Compare (key, col.Name, true) == 0)
4626 public void Insert (int index, ColumnHeader value)
4628 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4629 // but it's really only greater.
4630 if (index < 0 || index > list.Count)
4631 throw new ArgumentOutOfRangeException ("index");
4633 list.Insert (index, value);
4634 owner.AddColumn (value, index, true);
4638 public void Insert (int index, string text)
4640 Insert (index, String.Empty, text);
4643 public void Insert (int index, string text, int width)
4645 Insert (index, String.Empty, text, width);
4648 public void Insert (int index, string key, string text)
4650 ColumnHeader colHeader = new ColumnHeader ();
4651 colHeader.Name = key;
4652 colHeader.Text = text;
4653 Insert (index, colHeader);
4656 public void Insert (int index, string key, string text, int width)
4658 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4659 Insert (index, colHeader);
4662 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4664 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4665 colHeader.ImageIndex = imageIndex;
4666 Insert (index, colHeader);
4669 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4671 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4672 colHeader.ImageKey = imageKey;
4673 Insert (index, colHeader);
4677 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4679 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4680 this.Insert (index, colHeader);
4683 public virtual void Remove (ColumnHeader column)
4685 if (!Contains (column))
4688 list.Remove (column);
4689 column.SetListView (null);
4691 int rem_display_index = column.InternalDisplayIndex;
4692 int [] display_indices = new int [list.Count];
4693 for (int i = 0; i < display_indices.Length; i++) {
4694 ColumnHeader col = (ColumnHeader) list [i];
4695 int display_index = col.InternalDisplayIndex;
4696 if (display_index < rem_display_index) {
4697 display_indices [i] = display_index;
4699 display_indices [i] = (display_index - 1);
4703 column.InternalDisplayIndex = -1;
4704 owner.ReorderColumns (display_indices, true);
4708 public virtual void RemoveByKey (string key)
4710 int idx = IndexOfKey (key);
4716 public virtual void RemoveAt (int index)
4718 if (index < 0 || index >= list.Count)
4719 throw new ArgumentOutOfRangeException ("index");
4721 ColumnHeader col = (ColumnHeader) list [index];
4724 #endregion // Public Methods
4727 } // ColumnHeaderCollection
4730 [ListBindable (false)]
4732 public class ListViewItemCollection : IList, ICollection, IEnumerable
4734 private readonly ArrayList list;
4735 private ListView owner;
4737 private ListViewGroup group;
4740 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4741 // In the later case ListViewItem.ListView never gets modified
4742 private bool is_main_collection = true;
4744 #region Public Constructor
4745 public ListViewItemCollection (ListView owner)
4747 list = new ArrayList (0);
4750 #endregion // Public Constructor
4753 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4756 is_main_collection = false;
4760 #region Public Properties
4765 if (owner != null && owner.VirtualMode)
4766 return owner.VirtualListSize;
4773 public bool IsReadOnly {
4774 get { return false; }
4777 public virtual ListViewItem this [int displayIndex] {
4779 if (displayIndex < 0 || displayIndex >= Count)
4780 throw new ArgumentOutOfRangeException ("displayIndex");
4783 if (owner != null && owner.VirtualMode)
4784 return RetrieveVirtualItemFromOwner (displayIndex);
4786 return (ListViewItem) list [displayIndex];
4790 if (displayIndex < 0 || displayIndex >= Count)
4791 throw new ArgumentOutOfRangeException ("displayIndex");
4794 if (owner != null && owner.VirtualMode)
4795 throw new InvalidOperationException ();
4798 if (list.Contains (value))
4799 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4801 if (value.ListView != null && value.ListView != owner)
4802 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");
4804 if (is_main_collection)
4805 value.Owner = owner;
4808 if (value.Group != null)
4809 value.Group.Items.Remove (value);
4811 value.SetGroup (group);
4815 list [displayIndex] = value;
4816 CollectionChanged (true);
4821 public virtual ListViewItem this [string key] {
4823 int idx = IndexOfKey (key);
4832 bool ICollection.IsSynchronized {
4833 get { return true; }
4836 object ICollection.SyncRoot {
4837 get { return this; }
4840 bool IList.IsFixedSize {
4841 get { return list.IsFixedSize; }
4844 object IList.this [int index] {
4845 get { return this [index]; }
4847 if (value is ListViewItem)
4848 this [index] = (ListViewItem) value;
4850 this [index] = new ListViewItem (value.ToString ());
4854 #endregion // Public Properties
4856 #region Public Methods
4857 public virtual ListViewItem Add (ListViewItem value)
4860 if (owner != null && owner.VirtualMode)
4861 throw new InvalidOperationException ();
4865 CollectionChanged (true);
4870 public virtual ListViewItem Add (string text)
4872 ListViewItem item = new ListViewItem (text);
4873 return this.Add (item);
4876 public virtual ListViewItem Add (string text, int imageIndex)
4878 ListViewItem item = new ListViewItem (text, imageIndex);
4879 return this.Add (item);
4883 public virtual ListViewItem Add (string text, string imageKey)
4885 ListViewItem item = new ListViewItem (text, imageKey);
4886 return this.Add (item);
4889 public virtual ListViewItem Add (string key, string text, int imageIndex)
4891 ListViewItem item = new ListViewItem (text, imageIndex);
4893 return this.Add (item);
4896 public virtual ListViewItem Add (string key, string text, string imageKey)
4898 ListViewItem item = new ListViewItem (text, imageKey);
4900 return this.Add (item);
4904 public void AddRange (ListViewItem [] values)
4907 throw new ArgumentNullException ("Argument cannot be null!", "values");
4909 if (owner != null && owner.VirtualMode)
4910 throw new InvalidOperationException ();
4913 foreach (ListViewItem item in values)
4916 CollectionChanged (true);
4920 public void AddRange (ListViewItemCollection items)
4923 throw new ArgumentNullException ("Argument cannot be null!", "items");
4925 ListViewItem[] itemArray = new ListViewItem[items.Count];
4926 items.CopyTo (itemArray,0);
4927 this.AddRange (itemArray);
4931 public virtual void Clear ()
4934 if (owner != null && owner.VirtualMode)
4935 throw new InvalidOperationException ();
4937 if (is_main_collection && owner != null) {
4938 owner.SetFocusedItem (-1);
4939 owner.h_scroll.Value = owner.v_scroll.Value = 0;
4941 foreach (ListViewItem item in list) {
4942 owner.item_control.CancelEdit (item);
4949 foreach (ListViewItem item in list)
4950 item.SetGroup (null);
4954 CollectionChanged (false);
4957 public bool Contains (ListViewItem item)
4959 return IndexOf (item) != -1;
4963 public virtual bool ContainsKey (string key)
4965 return IndexOfKey (key) != -1;
4969 public void CopyTo (Array dest, int index)
4971 list.CopyTo (dest, index);
4975 public ListViewItem [] Find (string key, bool searchAllSubitems)
4978 return new ListViewItem [0];
4980 List<ListViewItem> temp_list = new List<ListViewItem> ();
4982 for (int i = 0; i < list.Count; i++) {
4983 ListViewItem lvi = (ListViewItem) list [i];
4984 if (String.Compare (key, lvi.Name, true) == 0)
4985 temp_list.Add (lvi);
4988 ListViewItem [] retval = new ListViewItem [temp_list.Count];
4989 temp_list.CopyTo (retval);
4995 public IEnumerator GetEnumerator ()
4998 if (owner != null && owner.VirtualMode)
4999 throw new InvalidOperationException ();
5002 return list.GetEnumerator ();
5005 int IList.Add (object item)
5011 if (owner != null && owner.VirtualMode)
5012 throw new InvalidOperationException ();
5015 if (item is ListViewItem) {
5016 li = (ListViewItem) item;
5017 if (list.Contains (li))
5018 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5020 if (li.ListView != null && li.ListView != owner)
5021 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");
5024 li = new ListViewItem (item.ToString ());
5027 result = list.Add (li);
5028 CollectionChanged (true);
5033 bool IList.Contains (object item)
5035 return Contains ((ListViewItem) item);
5038 int IList.IndexOf (object item)
5040 return IndexOf ((ListViewItem) item);
5043 void IList.Insert (int index, object item)
5045 if (item is ListViewItem)
5046 this.Insert (index, (ListViewItem) item);
5048 this.Insert (index, item.ToString ());
5051 void IList.Remove (object item)
5053 Remove ((ListViewItem) item);
5056 public int IndexOf (ListViewItem item)
5059 if (owner != null && owner.VirtualMode) {
5060 for (int i = 0; i < Count; i++)
5061 if (RetrieveVirtualItemFromOwner (i) == item)
5068 return list.IndexOf (item);
5072 public virtual int IndexOfKey (string key)
5074 if (key == null || key.Length == 0)
5077 for (int i = 0; i < Count; i++) {
5078 ListViewItem lvi = this [i];
5079 if (String.Compare (key, lvi.Name, true) == 0)
5087 public ListViewItem Insert (int index, ListViewItem item)
5089 if (index < 0 || index > list.Count)
5090 throw new ArgumentOutOfRangeException ("index");
5093 if (owner != null && owner.VirtualMode)
5094 throw new InvalidOperationException ();
5097 if (list.Contains (item))
5098 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5100 if (item.ListView != null && item.ListView != owner)
5101 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");
5103 if (is_main_collection)
5107 if (item.Group != null)
5108 item.Group.Items.Remove (item);
5110 item.SetGroup (group);
5114 list.Insert (index, item);
5115 CollectionChanged (true);
5119 public ListViewItem Insert (int index, string text)
5121 return this.Insert (index, new ListViewItem (text));
5124 public ListViewItem Insert (int index, string text, int imageIndex)
5126 return this.Insert (index, new ListViewItem (text, imageIndex));
5130 public ListViewItem Insert (int index, string text, string imageKey)
5132 ListViewItem lvi = new ListViewItem (text, imageKey);
5133 return Insert (index, lvi);
5136 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5138 ListViewItem lvi = new ListViewItem (text, imageIndex);
5140 return Insert (index, lvi);
5143 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5145 ListViewItem lvi = new ListViewItem (text, imageKey);
5147 return Insert (index, lvi);
5151 public virtual void Remove (ListViewItem item)
5154 if (owner != null && owner.VirtualMode)
5155 throw new InvalidOperationException ();
5158 int idx = list.IndexOf (item);
5163 public virtual void RemoveAt (int index)
5165 if (index < 0 || index >= Count)
5166 throw new ArgumentOutOfRangeException ("index");
5169 if (owner != null && owner.VirtualMode)
5170 throw new InvalidOperationException ();
5173 ListViewItem item = (ListViewItem) list [index];
5175 bool selection_changed = false;
5176 if (is_main_collection && owner != null) {
5178 if (item.Focused && index + 1 == Count) // Last item
5179 owner.SetFocusedItem (index == 0 ? -1 : index - 1);
5181 selection_changed = owner.SelectedIndices.Contains (index);
5182 owner.item_control.CancelEdit (item);
5185 list.RemoveAt (index);
5187 if (is_main_collection)
5191 item.SetGroup (null);
5194 CollectionChanged (false);
5195 if (selection_changed && owner != null)
5196 owner.OnSelectedIndexChanged (EventArgs.Empty);
5200 public virtual void RemoveByKey (string key)
5202 int idx = IndexOfKey (key);
5208 #endregion // Public Methods
5210 internal ListView Owner {
5220 internal ListViewGroup Group {
5230 void AddItem (ListViewItem value)
5232 if (list.Contains (value))
5233 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5235 if (value.ListView != null && value.ListView != owner)
5236 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");
5237 if (is_main_collection)
5238 value.Owner = owner;
5241 if (value.Group != null)
5242 value.Group.Items.Remove (value);
5244 value.SetGroup (group);
5251 void CollectionChanged (bool sort)
5253 if (owner != null) {
5258 owner.Redraw (true);
5263 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5265 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5267 owner.OnRetrieveVirtualItem (args);
5268 ListViewItem retval = args.Item;
5269 retval.Owner = owner;
5270 retval.DisplayIndex = displayIndex;
5276 internal event CollectionChangedHandler Changed;
5278 internal void Sort (IComparer comparer)
5280 list.Sort (comparer);
5284 internal void OnChange ()
5286 if (Changed != null)
5289 } // ListViewItemCollection
5292 // In normal mode, the selection information resides in the Items,
5293 // making SelectedIndexCollection.List read-only
5295 // In virtual mode, SelectedIndexCollection directly saves the selection
5296 // information, instead of getting it from Items, making List read-and-write
5298 [ListBindable (false)]
5300 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5302 private readonly ListView owner;
5303 private ArrayList list;
5305 #region Public Constructor
5306 public SelectedIndexCollection (ListView owner)
5309 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5311 #endregion // Public Constructor
5313 #region Public Properties
5317 if (!owner.IsHandleCreated)
5324 public bool IsReadOnly {
5334 public int this [int index] {
5336 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5337 throw new ArgumentOutOfRangeException ("index");
5339 return (int) List [index];
5343 bool ICollection.IsSynchronized {
5344 get { return false; }
5347 object ICollection.SyncRoot {
5348 get { return this; }
5351 bool IList.IsFixedSize {
5361 object IList.this [int index] {
5362 get { return this [index]; }
5363 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5365 #endregion // Public Properties
5367 #region Public Methods
5369 public int Add (int itemIndex)
5371 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5372 throw new ArgumentOutOfRangeException ("index");
5374 if (owner.virtual_mode && !owner.IsHandleCreated)
5377 owner.Items [itemIndex].Selected = true;
5379 if (!owner.IsHandleCreated)
5393 if (!owner.IsHandleCreated)
5396 int [] indexes = (int []) List.ToArray (typeof (int));
5397 foreach (int index in indexes)
5398 owner.Items [index].Selected = false;
5401 public bool Contains (int selectedIndex)
5403 return IndexOf (selectedIndex) != -1;
5406 public void CopyTo (Array dest, int index)
5408 List.CopyTo (dest, index);
5411 public IEnumerator GetEnumerator ()
5413 return List.GetEnumerator ();
5416 int IList.Add (object value)
5418 throw new NotSupportedException ("Add operation is not supported.");
5426 bool IList.Contains (object selectedIndex)
5428 if (!(selectedIndex is int))
5430 return Contains ((int) selectedIndex);
5433 int IList.IndexOf (object selectedIndex)
5435 if (!(selectedIndex is int))
5437 return IndexOf ((int) selectedIndex);
5440 void IList.Insert (int index, object value)
5442 throw new NotSupportedException ("Insert operation is not supported.");
5445 void IList.Remove (object value)
5447 throw new NotSupportedException ("Remove operation is not supported.");
5450 void IList.RemoveAt (int index)
5452 throw new NotSupportedException ("RemoveAt operation is not supported.");
5455 public int IndexOf (int selectedIndex)
5457 if (!owner.IsHandleCreated)
5460 return List.IndexOf (selectedIndex);
5464 public void Remove (int itemIndex)
5466 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5467 throw new ArgumentOutOfRangeException ("itemIndex");
5469 owner.Items [itemIndex].Selected = false;
5472 #endregion // Public Methods
5474 internal ArrayList List {
5477 list = new ArrayList ();
5479 if (!owner.VirtualMode)
5481 for (int i = 0; i < owner.Items.Count; i++) {
5482 if (owner.Items [i].Selected)
5490 internal void Reset ()
5492 // force re-population of list
5494 if (!owner.VirtualMode)
5499 private void ItemsCollection_Changed ()
5505 internal void RemoveIndex (int index)
5507 int idx = List.BinarySearch (index);
5509 List.RemoveAt (idx);
5512 // actually store index in the collection
5513 // also, keep the collection sorted, as .Net does
5514 internal void InsertIndex (int index)
5517 int iMax = List.Count - 1;
5518 while (iMin <= iMax) {
5519 int iMid = (iMin + iMax) / 2;
5520 int current_index = (int) List [iMid];
5522 if (current_index == index)
5523 return; // Already added
5524 if (current_index > index)
5530 List.Insert (iMin, index);
5534 } // SelectedIndexCollection
5537 [ListBindable (false)]
5539 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5541 private readonly ListView owner;
5543 #region Public Constructor
5544 public SelectedListViewItemCollection (ListView owner)
5548 #endregion // Public Constructor
5550 #region Public Properties
5554 return owner.SelectedIndices.Count;
5558 public bool IsReadOnly {
5559 get { return true; }
5562 public ListViewItem this [int index] {
5564 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5565 throw new ArgumentOutOfRangeException ("index");
5567 int item_index = owner.SelectedIndices [index];
5568 return owner.Items [item_index];
5573 public virtual ListViewItem this [string key] {
5575 int idx = IndexOfKey (key);
5584 bool ICollection.IsSynchronized {
5585 get { return false; }
5588 object ICollection.SyncRoot {
5589 get { return this; }
5592 bool IList.IsFixedSize {
5593 get { return true; }
5596 object IList.this [int index] {
5597 get { return this [index]; }
5598 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5600 #endregion // Public Properties
5602 #region Public Methods
5603 public void Clear ()
5605 owner.SelectedIndices.Clear ();
5608 public bool Contains (ListViewItem item)
5610 return IndexOf (item) != -1;
5614 public virtual bool ContainsKey (string key)
5616 return IndexOfKey (key) != -1;
5620 public void CopyTo (Array dest, int index)
5622 if (!owner.IsHandleCreated)
5624 if (index > Count) // Throws ArgumentException instead of IOOR exception
5625 throw new ArgumentException ("index");
5627 for (int i = 0; i < Count; i++)
5628 dest.SetValue (this [i], index++);
5631 public IEnumerator GetEnumerator ()
5633 if (!owner.IsHandleCreated)
5634 return (new ListViewItem [0]).GetEnumerator ();
5636 ListViewItem [] items = new ListViewItem [Count];
5637 for (int i = 0; i < Count; i++)
5638 items [i] = this [i];
5640 return items.GetEnumerator ();
5643 int IList.Add (object value)
5645 throw new NotSupportedException ("Add operation is not supported.");
5648 bool IList.Contains (object item)
5650 if (!(item is ListViewItem))
5652 return Contains ((ListViewItem) item);
5655 int IList.IndexOf (object item)
5657 if (!(item is ListViewItem))
5659 return IndexOf ((ListViewItem) item);
5662 void IList.Insert (int index, object value)
5664 throw new NotSupportedException ("Insert operation is not supported.");
5667 void IList.Remove (object value)
5669 throw new NotSupportedException ("Remove operation is not supported.");
5672 void IList.RemoveAt (int index)
5674 throw new NotSupportedException ("RemoveAt operation is not supported.");
5677 public int IndexOf (ListViewItem item)
5679 if (!owner.IsHandleCreated)
5682 for (int i = 0; i < Count; i++)
5683 if (this [i] == item)
5690 public virtual int IndexOfKey (string key)
5692 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5695 for (int i = 0; i < Count; i++) {
5696 ListViewItem item = this [i];
5697 if (String.Compare (item.Name, key, true) == 0)
5704 #endregion // Public Methods
5706 } // SelectedListViewItemCollection
5708 internal delegate void CollectionChangedHandler ();
5710 struct ItemMatrixLocation
5715 public ItemMatrixLocation (int row, int col)
5742 #endregion // Subclasses
5744 protected override void OnResize (EventArgs e)
5749 protected override void OnMouseLeave (EventArgs e)
5751 base.OnMouseLeave (e);
5755 // ColumnReorder event
5757 static object ColumnReorderedEvent = new object ();
5758 public event ColumnReorderedEventHandler ColumnReordered {
5759 add { Events.AddHandler (ColumnReorderedEvent, value); }
5760 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5763 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5765 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5772 // ColumnWidthChanged
5774 static object ColumnWidthChangedEvent = new object ();
5775 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5776 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5777 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5780 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5782 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5787 void RaiseColumnWidthChanged (int resize_column)
5789 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5791 OnColumnWidthChanged (n);
5795 // ColumnWidthChanging
5797 static object ColumnWidthChangingEvent = new object ();
5798 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5799 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5800 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5803 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5805 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5811 // 2.0 profile based implementation
5813 bool CanProceedWithResize (ColumnHeader col, int width)
5815 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5819 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5820 cwceh (this, changing);
5821 return !changing.Cancel;
5825 // 1.0 profile based implementation
5827 bool CanProceedWithResize (ColumnHeader col, int width)
5832 void RaiseColumnWidthChanged (int resize_column)