1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com)
23 // Ravindra Kumar (rkumar@novell.com)
24 // Jordi Mas i Hernandez, jordi@ximian.com
25 // Mike Kestner (mkestner@novell.com)
26 // Daniel Nauck (dna(at)mono-project(dot)de)
27 // Carlos Alberto Cortez <calberto.cortez@gmail.com>
34 using System.Collections;
35 using System.ComponentModel;
36 using System.ComponentModel.Design;
38 using System.Runtime.InteropServices;
39 using System.Globalization;
41 using System.Collections.Generic;
44 namespace System.Windows.Forms
46 [DefaultEvent ("SelectedIndexChanged")]
47 [DefaultProperty ("Items")]
48 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
50 [ClassInterface (ClassInterfaceType.AutoDispatch)]
52 [Docking (DockingBehavior.Ask)]
54 public class ListView : Control
56 private ItemActivation activation = ItemActivation.Standard;
57 private ListViewAlignment alignment = ListViewAlignment.Top;
58 private bool allow_column_reorder;
59 private bool auto_arrange = true;
60 private bool check_boxes;
61 private readonly CheckedIndexCollection checked_indices;
62 private readonly CheckedListViewItemCollection checked_items;
63 private readonly ColumnHeaderCollection columns;
64 internal int focused_item_index = -1;
65 private bool full_row_select;
66 private bool grid_lines;
67 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
68 private bool hide_selection = true;
69 private bool hover_selection;
70 private IComparer item_sorter;
71 private readonly ListViewItemCollection items;
73 private readonly ListViewGroupCollection groups;
74 private bool owner_draw;
75 private bool show_groups = true;
77 private bool label_edit;
78 private bool label_wrap = true;
79 private bool multiselect = true;
80 private bool scrollable = true;
81 private bool hover_pending;
82 private readonly SelectedIndexCollection selected_indices;
83 private readonly SelectedListViewItemCollection selected_items;
84 private SortOrder sort_order = SortOrder.None;
85 private ImageList state_image_list;
86 internal bool updating;
87 private View view = View.LargeIcon;
88 private int layout_wd; // We might draw more than our client area
89 private int layout_ht; // therefore we need to have these two.
90 HeaderControl header_control;
91 internal ItemControl item_control;
92 internal ScrollBar h_scroll; // used for scrolling horizontally
93 internal ScrollBar v_scroll; // used for scrolling vertically
94 internal int h_marker; // Position markers for scrolling
95 internal int v_marker;
96 private int keysearch_tickcnt;
97 private string keysearch_text;
98 static private readonly int keysearch_keydelay = 1000;
99 private int[] reordered_column_indices;
100 private int[] reordered_items_indices;
101 private Point [] items_location;
102 private ItemMatrixLocation [] items_matrix_location;
103 private Size item_size; // used for caching item size
104 private int custom_column_width; // used when using Columns with SmallIcon/List views
105 private int hot_item_index = -1;
107 private bool hot_tracking;
108 private ListViewInsertionMark insertion_mark;
109 private bool show_item_tooltips;
110 private ToolTip item_tooltip;
111 private Size tile_size;
112 private bool virtual_mode;
113 private int virtual_list_size;
114 private bool right_to_left_layout;
117 // internal variables
118 internal ImageList large_image_list;
119 internal ImageList small_image_list;
120 internal Size text_size = Size.Empty;
123 static object AfterLabelEditEvent = new object ();
124 static object BeforeLabelEditEvent = new object ();
125 static object ColumnClickEvent = new object ();
126 static object ItemActivateEvent = new object ();
127 static object ItemCheckEvent = new object ();
128 static object ItemDragEvent = new object ();
129 static object SelectedIndexChangedEvent = new object ();
131 static object DrawColumnHeaderEvent = new object();
132 static object DrawItemEvent = new object();
133 static object DrawSubItemEvent = new object();
134 static object ItemCheckedEvent = new object ();
135 static object ItemMouseHoverEvent = new object ();
136 static object ItemSelectionChangedEvent = new object ();
137 static object CacheVirtualItemsEvent = new object ();
138 static object RetrieveVirtualItemEvent = new object ();
139 static object RightToLeftLayoutChangedEvent = new object ();
140 static object SearchForVirtualItemEvent = new object ();
141 static object VirtualItemsSelectionRangeChangedEvent = new object ();
144 public event LabelEditEventHandler AfterLabelEdit {
145 add { Events.AddHandler (AfterLabelEditEvent, value); }
146 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
151 [EditorBrowsable (EditorBrowsableState.Never)]
152 public new event EventHandler BackgroundImageChanged {
153 add { base.BackgroundImageChanged += value; }
154 remove { base.BackgroundImageChanged -= value; }
160 [EditorBrowsable (EditorBrowsableState.Never)]
161 public new event EventHandler BackgroundImageLayoutChanged {
162 add { base.BackgroundImageLayoutChanged += value; }
163 remove { base.BackgroundImageLayoutChanged -= value; }
167 public event LabelEditEventHandler BeforeLabelEdit {
168 add { Events.AddHandler (BeforeLabelEditEvent, value); }
169 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
172 public event ColumnClickEventHandler ColumnClick {
173 add { Events.AddHandler (ColumnClickEvent, value); }
174 remove { Events.RemoveHandler (ColumnClickEvent, value); }
178 public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
179 add { Events.AddHandler(DrawColumnHeaderEvent, value); }
180 remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
183 public event DrawListViewItemEventHandler DrawItem {
184 add { Events.AddHandler(DrawItemEvent, value); }
185 remove { Events.RemoveHandler(DrawItemEvent, value); }
188 public event DrawListViewSubItemEventHandler DrawSubItem {
189 add { Events.AddHandler(DrawSubItemEvent, value); }
190 remove { Events.RemoveHandler(DrawSubItemEvent, value); }
194 public event EventHandler ItemActivate {
195 add { Events.AddHandler (ItemActivateEvent, value); }
196 remove { Events.RemoveHandler (ItemActivateEvent, value); }
199 public event ItemCheckEventHandler ItemCheck {
200 add { Events.AddHandler (ItemCheckEvent, value); }
201 remove { Events.RemoveHandler (ItemCheckEvent, value); }
205 public event ItemCheckedEventHandler ItemChecked {
206 add { Events.AddHandler (ItemCheckedEvent, value); }
207 remove { Events.RemoveHandler (ItemCheckedEvent, value); }
211 public event ItemDragEventHandler ItemDrag {
212 add { Events.AddHandler (ItemDragEvent, value); }
213 remove { Events.RemoveHandler (ItemDragEvent, value); }
217 public event ListViewItemMouseHoverEventHandler ItemMouseHover {
218 add { Events.AddHandler (ItemMouseHoverEvent, value); }
219 remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
222 public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
223 add { Events.AddHandler (ItemSelectionChangedEvent, value); }
224 remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
228 [EditorBrowsable (EditorBrowsableState.Never)]
229 public new event EventHandler PaddingChanged {
230 add { base.PaddingChanged += value; }
231 remove { base.PaddingChanged -= value; }
236 [EditorBrowsable (EditorBrowsableState.Never)]
237 public new event PaintEventHandler Paint {
238 add { base.Paint += value; }
239 remove { base.Paint -= value; }
242 public event EventHandler SelectedIndexChanged {
243 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
244 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
248 [EditorBrowsable (EditorBrowsableState.Never)]
249 public new event EventHandler TextChanged {
250 add { base.TextChanged += value; }
251 remove { base.TextChanged -= value; }
255 public event CacheVirtualItemsEventHandler CacheVirtualItems {
256 add { Events.AddHandler (CacheVirtualItemsEvent, value); }
257 remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
260 public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
261 add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
262 remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
265 public event EventHandler RightToLeftLayoutChanged {
266 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
267 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
270 public event SearchForVirtualItemEventHandler SearchForVirtualItem {
271 add { Events.AddHandler (SearchForVirtualItemEvent, value); }
272 remove { Events.AddHandler (SearchForVirtualItemEvent, value); }
275 public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged {
276 add { Events.AddHandler (VirtualItemsSelectionRangeChangedEvent, value); }
277 remove { Events.RemoveHandler (VirtualItemsSelectionRangeChangedEvent, value); }
283 #region Public Constructors
286 background_color = ThemeEngine.Current.ColorWindow;
288 groups = new ListViewGroupCollection (this);
290 items = new ListViewItemCollection (this);
291 checked_indices = new CheckedIndexCollection (this);
292 checked_items = new CheckedListViewItemCollection (this);
293 columns = new ColumnHeaderCollection (this);
294 foreground_color = SystemColors.WindowText;
295 selected_indices = new SelectedIndexCollection (this);
296 selected_items = new SelectedListViewItemCollection (this);
297 items_location = new Point [16];
298 items_matrix_location = new ItemMatrixLocation [16];
299 reordered_items_indices = new int [16];
301 item_tooltip = new ToolTip ();
302 item_tooltip.Active = false;
303 insertion_mark = new ListViewInsertionMark (this);
306 InternalBorderStyle = BorderStyle.Fixed3D;
308 header_control = new HeaderControl (this);
309 header_control.Visible = false;
310 Controls.AddImplicit (header_control);
312 item_control = new ItemControl (this);
313 Controls.AddImplicit (item_control);
315 h_scroll = new ImplicitHScrollBar ();
316 Controls.AddImplicit (this.h_scroll);
318 v_scroll = new ImplicitVScrollBar ();
319 Controls.AddImplicit (this.v_scroll);
321 h_marker = v_marker = 0;
322 keysearch_tickcnt = 0;
324 // scroll bars are disabled initially
325 h_scroll.Visible = false;
326 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
327 v_scroll.Visible = false;
328 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
331 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
332 SizeChanged += new EventHandler (ListView_SizeChanged);
333 GotFocus += new EventHandler (FocusChanged);
334 LostFocus += new EventHandler (FocusChanged);
335 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
336 MouseEnter += new EventHandler (ListView_MouseEnter);
339 BackgroundImageTiled = false;
342 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
344 | ControlStyles.UseTextForAccessibility
348 #endregion // Public Constructors
350 #region Private Internal Properties
351 internal Size CheckBoxSize {
353 if (this.check_boxes) {
354 if (this.state_image_list != null)
355 return this.state_image_list.ImageSize;
357 return ThemeEngine.Current.ListViewCheckBoxSize;
363 internal Size ItemSize {
365 if (view != View.Details)
368 Size size = new Size ();
369 size.Height = item_size.Height;
370 for (int i = 0; i < columns.Count; i++)
371 size.Width += columns [i].Wd;
380 internal int HotItemIndex {
382 return hot_item_index;
385 hot_item_index = value;
389 internal override bool ScaleChildrenInternal {
390 get { return false; }
393 internal bool UseCustomColumnWidth {
395 return (view == View.List || view == View.SmallIcon) && columns.Count > 0;
399 #endregion // Private Internal Properties
401 #region Protected Properties
402 protected override CreateParams CreateParams {
403 get { return base.CreateParams; }
406 protected override Size DefaultSize {
407 get { return ThemeEngine.Current.ListViewDefaultSize; }
410 protected override bool DoubleBuffered {
412 return base.DoubleBuffered;
415 base.DoubleBuffered = value;
419 #endregion // Protected Properties
421 #region Public Instance Properties
422 [DefaultValue (ItemActivation.Standard)]
423 public ItemActivation Activation {
424 get { return activation; }
426 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
427 value != ItemActivation.TwoClick) {
428 throw new InvalidEnumArgumentException (string.Format
429 ("Enum argument value '{0}' is not valid for Activation", value));
432 if (hot_tracking && value != ItemActivation.OneClick)
433 throw new ArgumentException ("When HotTracking is on, activation must be ItemActivation.OneClick");
440 [DefaultValue (ListViewAlignment.Top)]
442 public ListViewAlignment Alignment {
443 get { return alignment; }
445 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
446 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
447 throw new InvalidEnumArgumentException (string.Format
448 ("Enum argument value '{0}' is not valid for Alignment", value));
451 if (this.alignment != value) {
453 // alignment does not matter in Details/List views
454 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
460 [DefaultValue (false)]
461 public bool AllowColumnReorder {
462 get { return allow_column_reorder; }
463 set { allow_column_reorder = value; }
466 [DefaultValue (true)]
467 public bool AutoArrange {
468 get { return auto_arrange; }
470 if (auto_arrange != value) {
471 auto_arrange = value;
472 // autoarrange does not matter in Details/List views
473 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
479 public override Color BackColor {
481 if (background_color.IsEmpty)
482 return ThemeEngine.Current.ColorWindow;
484 return background_color;
487 background_color = value;
488 item_control.BackColor = value;
494 [EditorBrowsable (EditorBrowsableState.Never)]
495 public override Image BackgroundImage {
496 get { return base.BackgroundImage; }
497 set { base.BackgroundImage = value; }
503 [EditorBrowsable (EditorBrowsableState.Never)]
504 public override ImageLayout BackgroundImageLayout {
506 return base.BackgroundImageLayout;
509 base.BackgroundImageLayout = value;
513 [DefaultValue (false)]
514 public bool BackgroundImageTiled {
516 return item_control.BackgroundImageLayout == ImageLayout.Tile;
519 ImageLayout new_image_layout = value ? ImageLayout.Tile : ImageLayout.None;
520 if (new_image_layout == item_control.BackgroundImageLayout)
523 item_control.BackgroundImageLayout = new_image_layout;
528 [DefaultValue (BorderStyle.Fixed3D)]
530 public BorderStyle BorderStyle {
531 get { return InternalBorderStyle; }
532 set { InternalBorderStyle = value; }
535 [DefaultValue (false)]
536 public bool CheckBoxes {
537 get { return check_boxes; }
539 if (check_boxes != value) {
541 if (value && View == View.Tile)
542 throw new NotSupportedException ("CheckBoxes are not"
543 + " supported in Tile view. Choose a different"
544 + " view or set CheckBoxes to false.");
554 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
555 public CheckedIndexCollection CheckedIndices {
556 get { return checked_indices; }
560 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
561 public CheckedListViewItemCollection CheckedItems {
562 get { return checked_items; }
566 [Editor ("System.Windows.Forms.Design.ColumnHeaderCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
568 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
570 [MergableProperty (false)]
571 public ColumnHeaderCollection Columns {
572 get { return columns; }
576 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
577 public ListViewItem FocusedItem {
579 if (focused_item_index == -1)
582 return GetItemAtDisplayIndex (focused_item_index);
586 if (value == null || value.ListView != this ||
590 SetFocusedItem (value.DisplayIndex);
595 public override Color ForeColor {
597 if (foreground_color.IsEmpty)
598 return ThemeEngine.Current.ColorWindowText;
600 return foreground_color;
602 set { foreground_color = value; }
605 [DefaultValue (false)]
606 public bool FullRowSelect {
607 get { return full_row_select; }
609 if (full_row_select != value) {
610 full_row_select = value;
611 InvalidateSelection ();
616 [DefaultValue (false)]
617 public bool GridLines {
618 get { return grid_lines; }
620 if (grid_lines != value) {
627 [DefaultValue (ColumnHeaderStyle.Clickable)]
628 public ColumnHeaderStyle HeaderStyle {
629 get { return header_style; }
631 if (header_style == value)
635 case ColumnHeaderStyle.Clickable:
636 case ColumnHeaderStyle.Nonclickable:
637 case ColumnHeaderStyle.None:
640 throw new InvalidEnumArgumentException (string.Format
641 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
644 header_style = value;
645 if (view == View.Details)
650 [DefaultValue (true)]
651 public bool HideSelection {
652 get { return hide_selection; }
654 if (hide_selection != value) {
655 hide_selection = value;
656 InvalidateSelection ();
662 [DefaultValue (false)]
663 public bool HotTracking {
668 if (hot_tracking == value)
671 hot_tracking = value;
673 hover_selection = true;
674 activation = ItemActivation.OneClick;
680 [DefaultValue (false)]
681 public bool HoverSelection {
682 get { return hover_selection; }
685 if (hot_tracking && value == false)
686 throw new ArgumentException ("When HotTracking is on, hover selection must be true");
688 hover_selection = value;
693 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
695 public ListViewInsertionMark InsertionMark {
697 return insertion_mark;
703 [Editor ("System.Windows.Forms.Design.ListViewItemCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
705 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
707 [MergableProperty (false)]
708 public ListViewItemCollection Items {
709 get { return items; }
712 [DefaultValue (false)]
713 public bool LabelEdit {
714 get { return label_edit; }
715 set { label_edit = value; }
718 [DefaultValue (true)]
720 public bool LabelWrap {
721 get { return label_wrap; }
723 if (label_wrap != value) {
730 [DefaultValue (null)]
731 public ImageList LargeImageList {
732 get { return large_image_list; }
734 large_image_list = value;
740 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
741 public IComparer ListViewItemSorter {
743 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
748 if (item_sorter != value) {
755 [DefaultValue (true)]
756 public bool MultiSelect {
757 get { return multiselect; }
758 set { multiselect = value; }
763 [DefaultValue(false)]
764 public bool OwnerDraw {
765 get { return owner_draw; }
773 [EditorBrowsable (EditorBrowsableState.Never)]
774 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
775 public new Padding Padding {
780 base.Padding = value;
784 [MonoTODO ("RTL not supported")]
786 [DefaultValue (false)]
787 public virtual bool RightToLeftLayout {
788 get { return right_to_left_layout; }
790 if (right_to_left_layout != value) {
791 right_to_left_layout = value;
792 OnRightToLeftLayoutChanged (EventArgs.Empty);
798 [DefaultValue (true)]
799 public bool Scrollable {
800 get { return scrollable; }
802 if (scrollable != value) {
810 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
811 public SelectedIndexCollection SelectedIndices {
812 get { return selected_indices; }
816 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
817 public SelectedListViewItemCollection SelectedItems {
818 get { return selected_items; }
823 public bool ShowGroups {
824 get { return show_groups; }
826 if (show_groups != value) {
833 [LocalizableAttribute (true)]
834 [MergableProperty (false)]
835 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
836 [Editor ("System.Windows.Forms.Design.ListViewGroupCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
837 public ListViewGroupCollection Groups {
838 get { return groups; }
841 [DefaultValue (false)]
842 public bool ShowItemToolTips {
844 return show_item_tooltips;
847 show_item_tooltips = value;
848 item_tooltip.Active = false;
853 [DefaultValue (null)]
854 public ImageList SmallImageList {
855 get { return small_image_list; }
857 small_image_list = value;
862 [DefaultValue (SortOrder.None)]
863 public SortOrder Sorting {
864 get { return sort_order; }
866 if (!Enum.IsDefined (typeof (SortOrder), value)) {
867 throw new InvalidEnumArgumentException ("value", (int) value,
871 if (sort_order == value)
877 if (virtual_mode) // Sorting is not allowed in virtual mode
881 if (value == SortOrder.None) {
882 if (item_sorter != null) {
883 // ListViewItemSorter should never be reset for SmallIcon
884 // and LargeIcon view
885 if (View != View.SmallIcon && View != View.LargeIcon)
889 // in .NET 1.1, only internal IComparer would be
891 if (item_sorter is ItemComparer)
897 if (item_sorter == null)
898 item_sorter = new ItemComparer (value);
899 if (item_sorter is ItemComparer) {
901 item_sorter = new ItemComparer (value);
903 // in .NET 1.1, the sort order is not updated for
904 // SmallIcon and LargeIcon views if no custom IComparer
906 if (View != View.SmallIcon && View != View.LargeIcon)
907 item_sorter = new ItemComparer (value);
915 private void OnImageListChanged (object sender, EventArgs args)
917 item_control.Invalidate ();
920 [DefaultValue (null)]
921 public ImageList StateImageList {
922 get { return state_image_list; }
924 if (state_image_list == value)
927 if (state_image_list != null)
928 state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
930 state_image_list = value;
932 if (state_image_list != null)
933 state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
941 [EditorBrowsable (EditorBrowsableState.Never)]
942 public override string Text {
943 get { return base.Text; }
945 if (value == base.Text)
955 public Size TileSize {
960 if (value.Width <= 0 || value.Height <= 0)
961 throw new ArgumentOutOfRangeException ("value");
970 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
971 public ListViewItem TopItem {
974 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
975 throw new InvalidOperationException ("Cannot get the top item in LargeIcon, SmallIcon or Tile view.");
978 if (this.items.Count == 0)
980 // if contents are not scrolled
981 // it is the first item
982 else if (h_marker == 0 && v_marker == 0)
983 return this.items [0];
984 // do a hit test for the scrolled position
986 for (int i = 0; i < items.Count; i++) {
987 Point item_loc = GetItemLocation (i);
988 if (item_loc.X >= 0 && item_loc.Y >= 0)
996 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
997 throw new InvalidOperationException ("Cannot set the top item in LargeIcon, SmallIcon or Tile view.");
999 // .Net doesn't throw any exception in the cases below
1000 if (value == null || value.ListView != this)
1003 EnsureVisible (value.Index);
1009 [EditorBrowsable (EditorBrowsableState.Advanced)]
1010 [DefaultValue (true)]
1012 [MonoInternalNote ("Stub, not implemented")]
1013 public bool UseCompatibleStateImageBehavior {
1022 [DefaultValue (View.LargeIcon)]
1024 get { return view; }
1026 if (!Enum.IsDefined (typeof (View), value))
1027 throw new InvalidEnumArgumentException ("value", (int) value,
1030 if (view != value) {
1032 if (CheckBoxes && value == View.Tile)
1033 throw new NotSupportedException ("CheckBoxes are not"
1034 + " supported in Tile view. Choose a different"
1035 + " view or set CheckBoxes to false.");
1038 h_scroll.Value = v_scroll.Value = 0;
1046 [DefaultValue (false)]
1047 [RefreshProperties (RefreshProperties.Repaint)]
1048 public bool VirtualMode {
1050 return virtual_mode;
1053 if (virtual_mode == value)
1056 if (!virtual_mode && items.Count > 0)
1057 throw new InvalidOperationException ();
1059 virtual_mode = value;
1065 [RefreshProperties (RefreshProperties.Repaint)]
1066 public int VirtualListSize {
1068 return virtual_list_size;
1072 throw new ArgumentException ("value");
1074 if (virtual_list_size == value)
1077 virtual_list_size = value;
1079 selected_indices.Reset ();
1085 #endregion // Public Instance Properties
1087 #region Internal Methods Properties
1089 internal int FirstVisibleIndex {
1092 if (this.items.Count == 0)
1095 if (h_marker == 0 && v_marker == 0)
1098 Size item_size = ItemSize;
1099 for (int i = 0; i < items.Count; i++) {
1100 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
1101 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
1110 internal int LastVisibleIndex {
1112 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
1113 if (View == View.List || Alignment == ListViewAlignment.Left) {
1114 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
1117 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
1122 return Items.Count - 1;
1126 internal void OnSelectedIndexChanged ()
1128 if (IsHandleCreated)
1129 OnSelectedIndexChanged (EventArgs.Empty);
1132 internal int TotalWidth {
1133 get { return Math.Max (this.Width, this.layout_wd); }
1136 internal int TotalHeight {
1137 get { return Math.Max (this.Height, this.layout_ht); }
1140 internal void Redraw (bool recalculate)
1142 // Avoid calculations when control is being updated
1146 // VirtualMode doesn't do any calculations until handle is created
1147 if (virtual_mode && !IsHandleCreated)
1153 CalculateListView (this.alignment);
1158 void InvalidateSelection ()
1160 foreach (int selected_index in SelectedIndices)
1161 items [selected_index].Invalidate ();
1164 const int text_padding = 15;
1166 internal Size GetChildColumnSize (int index)
1168 Size ret_size = Size.Empty;
1169 ColumnHeader col = this.columns [index];
1171 if (col.Width == -2) { // autosize = max(items, columnheader)
1172 Size size = Size.Ceiling (TextRenderer.MeasureString
1173 (col.Text, this.Font));
1174 size.Width += text_padding;
1175 ret_size = BiggestItem (index);
1176 if (size.Width > ret_size.Width)
1179 else { // -1 and all the values < -2 are put under one category
1180 ret_size = BiggestItem (index);
1181 // fall back to empty columns' width if no subitem is available for a column
1182 if (ret_size.IsEmpty) {
1183 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
1184 if (col.Text.Length > 0)
1185 ret_size.Height = Size.Ceiling (TextRenderer.MeasureString
1186 (col.Text, this.Font)).Height;
1188 ret_size.Height = this.Font.Height;
1192 ret_size.Height += text_padding;
1194 // adjust the size for icon and checkbox for 0th column
1196 ret_size.Width += (this.CheckBoxSize.Width + 4);
1197 if (this.small_image_list != null)
1198 ret_size.Width += this.small_image_list.ImageSize.Width;
1203 // Returns the size of biggest item text in a column
1204 // or the sum of the text and indent count if we are on 2.0
1205 private Size BiggestItem (int col)
1207 Size temp = Size.Empty;
1208 Size ret_size = Size.Empty;
1210 bool use_indent_count = small_image_list != null;
1212 // VirtualMode uses the first item text size
1213 if (virtual_mode && items.Count > 0) {
1214 ListViewItem item = items [0];
1215 ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
1218 if (use_indent_count)
1219 ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
1222 // 0th column holds the item text, we check the size of
1223 // the various subitems falling in that column and get
1224 // the biggest one's size.
1225 foreach (ListViewItem item in items) {
1226 if (col >= item.SubItems.Count)
1229 temp = Size.Ceiling (TextRenderer.MeasureString
1230 (item.SubItems [col].Text, Font));
1233 if (use_indent_count)
1234 temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
1237 if (temp.Width > ret_size.Width)
1244 // adjustment for space in Details view
1245 if (!ret_size.IsEmpty && view == View.Details)
1246 ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
1251 const int max_wrap_padding = 30;
1253 // Sets the size of the biggest item text as per the view
1254 private void CalcTextSize ()
1256 // clear the old value
1257 text_size = Size.Empty;
1259 if (items.Count == 0)
1262 text_size = BiggestItem (0);
1264 if (view == View.LargeIcon && this.label_wrap) {
1265 Size temp = Size.Empty;
1266 if (this.check_boxes)
1267 temp.Width += 2 * this.CheckBoxSize.Width;
1268 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1269 temp.Width += icon_w + max_wrap_padding;
1270 // wrapping is done for two lines only
1271 if (text_size.Width > temp.Width) {
1272 text_size.Width = temp.Width;
1273 text_size.Height *= 2;
1276 else if (view == View.List) {
1277 // in list view max text shown in determined by the
1278 // control width, even if scolling is enabled.
1279 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1280 if (this.small_image_list != null)
1281 max_wd -= this.small_image_list.ImageSize.Width;
1283 if (text_size.Width > max_wd)
1284 text_size.Width = max_wd;
1287 // we do the default settings, if we have got 0's
1288 if (text_size.Height <= 0)
1289 text_size.Height = this.Font.Height;
1290 if (text_size.Width <= 0)
1291 text_size.Width = this.Width;
1293 // little adjustment
1294 text_size.Width += 2;
1295 text_size.Height += 2;
1298 private void Scroll (ScrollBar scrollbar, int delta)
1300 if (delta == 0 || !scrollbar.Visible)
1304 if (scrollbar == h_scroll)
1305 max = h_scroll.Maximum - item_control.Width;
1307 max = v_scroll.Maximum - item_control.Height;
1309 int val = scrollbar.Value + delta;
1312 else if (val < scrollbar.Minimum)
1313 val = scrollbar.Minimum;
1314 scrollbar.Value = val;
1317 private void CalculateScrollBars ()
1319 if (!IsHandleCreated)
1322 Rectangle client_area = ClientRectangle;
1323 int height = client_area.Height;
1324 int width = client_area.Width;
1327 h_scroll.Visible = false;
1328 v_scroll.Visible = false;
1329 item_control.Size = new Size (width, height);
1330 header_control.Width = width;
1334 // Don't calculate if the view is not displayable
1335 if (client_area.Height < 0 || client_area.Width < 0)
1338 // making a scroll bar visible might make
1339 // other scroll bar visible
1340 if (layout_wd > client_area.Right) {
1341 h_scroll.Visible = true;
1342 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1343 v_scroll.Visible = true;
1345 v_scroll.Visible = false;
1346 } else if (layout_ht > client_area.Bottom) {
1347 v_scroll.Visible = true;
1348 if ((layout_wd + v_scroll.Width) > client_area.Right)
1349 h_scroll.Visible = true;
1351 h_scroll.Visible = false;
1353 h_scroll.Visible = false;
1354 v_scroll.Visible = false;
1358 if (h_scroll.is_visible) {
1359 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1360 h_scroll.Minimum = 0;
1362 // if v_scroll is visible, adjust the maximum of the
1363 // h_scroll to account for the width of v_scroll
1364 if (v_scroll.Visible) {
1365 h_scroll.Maximum = layout_wd + v_scroll.Width;
1366 h_scroll.Width = client_area.Width - v_scroll.Width;
1369 h_scroll.Maximum = layout_wd;
1370 h_scroll.Width = client_area.Width;
1373 h_scroll.LargeChange = client_area.Width;
1374 h_scroll.SmallChange = Font.Height;
1375 height -= h_scroll.Height;
1378 if (v_scroll.is_visible) {
1379 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1380 v_scroll.Minimum = 0;
1382 // if h_scroll is visible, adjust the maximum of the
1383 // v_scroll to account for the height of h_scroll
1384 if (h_scroll.Visible) {
1385 v_scroll.Maximum = layout_ht + h_scroll.Height;
1386 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1388 v_scroll.Maximum = layout_ht;
1389 v_scroll.Height = client_area.Height;
1392 v_scroll.LargeChange = client_area.Height;
1393 v_scroll.SmallChange = Font.Height;
1394 width -= v_scroll.Width;
1397 item_control.Size = new Size (width, height);
1399 if (header_control.is_visible)
1400 header_control.Width = width;
1404 internal int GetReorderedColumnIndex (ColumnHeader column)
1406 if (reordered_column_indices == null)
1407 return column.Index;
1409 for (int i = 0; i < Columns.Count; i++)
1410 if (reordered_column_indices [i] == column.Index)
1417 internal ColumnHeader GetReorderedColumn (int index)
1419 if (reordered_column_indices == null)
1420 return Columns [index];
1422 return Columns [reordered_column_indices [index]];
1425 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1429 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1431 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1435 header_control.Invalidate ();
1436 item_control.Invalidate ();
1442 int column_count = Columns.Count;
1444 if (reordered_column_indices == null) {
1445 reordered_column_indices = new int [column_count];
1446 for (int i = 0; i < column_count; i++)
1447 reordered_column_indices [i] = i;
1450 if (reordered_column_indices [index] == col.Index)
1453 int[] curr = reordered_column_indices;
1454 int [] result = new int [column_count];
1456 for (int i = 0; i < column_count; i++) {
1457 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1461 result [i] = col.Index;
1463 result [i] = curr [curr_idx++];
1466 ReorderColumns (result, true);
1469 internal void ReorderColumns (int [] display_indices, bool redraw)
1471 reordered_column_indices = display_indices;
1472 for (int i = 0; i < Columns.Count; i++) {
1473 ColumnHeader col = Columns [i];
1474 col.InternalDisplayIndex = reordered_column_indices [i];
1476 if (redraw && view == View.Details && IsHandleCreated) {
1478 header_control.Invalidate ();
1479 item_control.Invalidate ();
1483 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1485 int column_count = Columns.Count;
1486 newCol.SetListView (this);
1488 int [] display_indices = new int [column_count];
1489 for (int i = 0; i < column_count; i++) {
1490 ColumnHeader col = Columns [i];
1492 display_indices [i] = index;
1494 int display_index = col.InternalDisplayIndex;
1495 if (display_index < index) {
1496 display_indices [i] = display_index;
1498 display_indices [i] = (display_index + 1);
1503 ReorderColumns (display_indices, redraw);
1507 Size LargeIconItemSize
1510 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1511 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1512 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1513 int w = Math.Max (text_size.Width, image_w);
1516 w += 2 + CheckBoxSize.Width;
1518 return new Size (w, h);
1522 Size SmallIconItemSize {
1524 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1525 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1526 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1527 int w = text_size.Width + image_w;
1530 w += 2 + CheckBoxSize.Width;
1532 return new Size (w, h);
1539 // Calculate tile size if needed
1540 // It appears that using Font.Size instead of a SizeF value can give us
1541 // a slightly better approach to the proportions defined in .Net
1542 if (tile_size == Size.Empty) {
1543 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1544 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1545 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1546 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1548 tile_size = new Size (w, h);
1556 int GetDetailsItemHeight ()
1559 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1560 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1561 item_height = Math.Max (checkbox_height, text_size.Height);
1562 item_height = Math.Max (item_height, small_image_height);
1566 void SetItemLocation (int index, int x, int y, int row, int col)
1568 Point old_location = items_location [index];
1569 if (old_location.X == x && old_location.Y == y)
1572 Size item_size = ItemSize;
1573 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1575 items_location [index] = new Point (x, y);
1576 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1579 // Initial position matches item's position in ListViewItemCollection
1581 reordered_items_indices [index] = index;
1583 // Invalidate both previous and new bounds
1584 item_control.Invalidate (old_rect);
1585 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1589 void ShiftItemsPositions (int from, int to, bool forward)
1592 for (int i = to + 1; i > from; i--) {
1593 reordered_items_indices [i] = reordered_items_indices [i - 1];
1595 ListViewItem item = items [reordered_items_indices [i]];
1596 item_control.Invalidate (item.Bounds);
1597 item.DisplayIndex = i;
1598 item_control.Invalidate (item.Bounds);
1601 for (int i = from - 1; i < to; i++) {
1602 reordered_items_indices [i] = reordered_items_indices [i + 1];
1604 ListViewItem item = items [reordered_items_indices [i]];
1605 item_control.Invalidate (item.Bounds);
1606 item.DisplayIndex = i;
1607 item_control.Invalidate (item.Bounds);
1612 internal void ChangeItemLocation (int display_index, Point new_pos)
1614 int new_display_index = GetDisplayIndexFromLocation (new_pos);
1615 if (new_display_index == display_index)
1618 int item_index = reordered_items_indices [display_index];
1619 ListViewItem item = items [item_index];
1621 bool forward = new_display_index < display_index;
1622 int index_from, index_to;
1624 index_from = new_display_index;
1625 index_to = display_index - 1;
1627 index_from = display_index + 1;
1628 index_to = new_display_index;
1631 ShiftItemsPositions (index_from, index_to, forward);
1633 reordered_items_indices [new_display_index] = item_index;
1635 item_control.Invalidate (item.Bounds);
1636 item.DisplayIndex = new_display_index;
1637 item_control.Invalidate (item.Bounds);
1640 int GetDisplayIndexFromLocation (Point loc)
1642 int display_index = -1;
1643 Rectangle item_area;
1646 if (loc.X < 0 || loc.Y < 0)
1649 // Adjustment to put in the next position refered by 'loc'
1650 loc.X -= item_size.Width / 2;
1654 for (int i = 0; i < items.Count; i++) {
1655 item_area = new Rectangle (GetItemLocation (i), item_size);
1656 item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
1657 ThemeEngine.Current.ListViewVerticalSpacing);
1659 if (item_area.Contains (loc)) {
1665 // Put in in last position
1666 if (display_index == -1)
1667 display_index = items.Count - 1;
1669 return display_index;
1672 // When using groups, the items with no group assigned
1673 // belong to the DefaultGroup
1674 int GetDefaultGroupItems ()
1677 foreach (ListViewItem item in items)
1678 if (item.Group == null)
1687 int[,] item_index_matrix;
1689 void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1691 Rectangle area = ClientRectangle;
1693 if (UseCustomColumnWidth)
1694 CalculateCustomColumnWidth ();
1696 if (show_groups && groups.Count > 0 && view != View.List) {
1697 // When groups are used the alignment is always top-aligned
1702 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1703 for (int i = 0; i < groups.InternalCount; i++) {
1704 ListViewGroup group = groups.GetInternalGroup (i);
1705 int items_in_group = group.GetActualItemCount ();
1707 if (items_in_group == 0)
1710 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1711 if (group_cols <= 0)
1713 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1715 group.starting_row = rows;
1716 group.rows = group_rows;
1717 group.starting_item = items;
1718 group.current_item = 0; // Reset layout
1720 cols = Math.Max (group_cols, cols);
1722 items += items_in_group;
1727 // Simple matrix if no groups are used
1729 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1732 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1734 if (UseCustomColumnWidth)
1735 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width) / (double)(custom_column_width));
1737 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1742 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1746 item_index_matrix = new int [rows, cols];
1749 // When using custom column width, we look for the minimum one
1750 void CalculateCustomColumnWidth ()
1752 int min_width = Int32.MaxValue;
1753 for (int i = 0; i < columns.Count; i++) {
1754 int col_width = columns [i].Width;
1756 if (col_width < min_width)
1757 min_width = col_width;
1760 custom_column_width = min_width;
1763 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1765 header_control.Visible = false;
1766 header_control.Size = Size.Empty;
1767 item_control.Visible = true;
1768 item_control.Location = Point.Empty;
1769 ItemSize = item_size; // Cache item size
1771 if (items.Count == 0)
1774 Size sz = item_size;
1776 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1779 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1781 layout_wd = UseCustomColumnWidth ? cols * custom_column_width : cols * (sz.Width + x_spacing) - x_spacing;
1782 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1785 CalculateGroupsLayout (sz, y_spacing, 0);
1788 int row = 0, col = 0;
1790 int display_index = 0;
1792 for (int i = 0; i < items.Count; i++) {
1795 ListViewGroup group = items [i].Group;
1797 group = groups.DefaultGroup;
1799 Point group_items_loc = group.items_area_location;
1800 int current_item = group.current_item++;
1801 int starting_row = group.starting_row;
1803 display_index = group.starting_item + current_item;
1804 row = (current_item / cols);
1805 col = current_item % cols;
1807 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1808 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1810 SetItemLocation (display_index, x, y, row + starting_row, col);
1811 SetItemAtDisplayIndex (display_index, i);
1812 item_index_matrix [row + starting_row, col] = i;
1817 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1818 y = row * (item_size.Height + y_spacing);
1819 display_index = i; // Same as item index in Items
1821 SetItemLocation (i, x, y, row, col);
1822 item_index_matrix [row, col] = i;
1831 if (++col == cols) {
1841 ListViewItem item = items [i];
1843 item.DisplayIndex = display_index;
1844 item.SetPosition (new Point (x, y));
1850 item_control.Size = new Size (layout_wd, layout_ht);
1854 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1857 bool details = view == View.Details;
1859 for (int i = 0; i < groups.InternalCount; i++) {
1860 ListViewGroup group = groups.GetInternalGroup (i);
1861 if (group.ItemCount == 0)
1864 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1867 layout_ht = y; // Update height taking into account Groups' headers heights
1870 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1872 Rectangle client_area = ClientRectangle;
1873 int header_height = text_size.Height + 10;
1875 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1876 group.items_area_location = new Point (0, y_origin + header_height);
1878 int items_area_height = ((item_height + y_spacing) * rows);
1879 return header_height + items_area_height + 10; // Add a small bottom margin
1882 void CalculateDetailsGroupItemsCount ()
1886 for (int i = 0; i < groups.InternalCount; i++) {
1887 ListViewGroup group = groups.GetInternalGroup (i);
1888 int items_in_group = group.GetActualItemCount ();
1890 if (items_in_group == 0)
1893 group.starting_item = items;
1894 group.current_item = 0; // Reset layout.
1895 items += items_in_group;
1900 void LayoutHeader ()
1903 for (int i = 0; i < Columns.Count; i++) {
1904 ColumnHeader col = GetReorderedColumn (i);
1907 col.CalcColumnHeader ();
1913 if (x < ClientRectangle.Width)
1914 x = ClientRectangle.Width;
1916 if (header_style == ColumnHeaderStyle.None) {
1917 header_control.Visible = false;
1918 header_control.Size = Size.Empty;
1919 layout_wd = ClientRectangle.Width;
1921 header_control.Width = x;
1922 header_control.Height = columns.Count > 0 ? columns [0].Ht : Font.Height + 5;
1923 header_control.Visible = true;
1927 void LayoutDetails ()
1931 if (columns.Count == 0) {
1932 item_control.Visible = false;
1933 layout_wd = ClientRectangle.Width;
1934 layout_ht = ClientRectangle.Height;
1938 item_control.Visible = true;
1939 item_control.Location = Point.Empty;
1940 item_control.Width = ClientRectangle.Width;
1942 int item_height = GetDetailsItemHeight ();
1943 ItemSize = new Size (0, item_height); // We only cache Height for details view
1944 int y = header_control.Height;
1946 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1948 CalculateDetailsGroupItemsCount ();
1949 CalculateGroupsLayout (ItemSize, 2, y);
1953 for (int i = 0; i < items.Count; i++) {
1954 ListViewItem item = items [i];
1958 ListViewGroup group = item.Group;
1960 group = groups.DefaultGroup;
1962 int current_item = group.current_item++;
1963 Point group_items_loc = group.items_area_location;
1964 display_index = group.starting_item + current_item;
1966 y = current_item * (item_height + 2) + group_items_loc.Y;
1967 SetItemLocation (display_index, 0, y, 0, 0);
1968 SetItemAtDisplayIndex (display_index, i);
1969 item.SetPosition (new Point (0, y));
1974 SetItemLocation (i, 0, y, 0, 0);
1975 item.SetPosition (new Point (0, y));
1979 if (!virtual_mode) // Virtual mode sets Layout until draw time
1983 item.DisplayIndex = display_index;
1988 // some space for bottom gridline
1989 if (items.Count > 0 && grid_lines)
1993 if (!using_groups) // With groups it has been previously computed
1998 private void AdjustItemsPositionArray (int count)
2000 if (items_location.Length >= count)
2003 // items_location, items_matrix_location and reordered_items_indices must keep the same length
2004 count = Math.Max (count, items_location.Length * 2);
2005 items_location = new Point [count];
2006 items_matrix_location = new ItemMatrixLocation [count];
2007 reordered_items_indices = new int [count];
2010 private void CalculateListView (ListViewAlignment align)
2014 AdjustItemsPositionArray (items.Count);
2021 case View.SmallIcon:
2022 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
2023 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2026 case View.LargeIcon:
2027 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
2028 ThemeEngine.Current.ListViewHorizontalSpacing,
2029 ThemeEngine.Current.ListViewVerticalSpacing);
2033 LayoutIcons (SmallIconItemSize, true,
2034 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2038 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
2039 ThemeEngine.Current.ListViewHorizontalSpacing,
2040 ThemeEngine.Current.ListViewVerticalSpacing);
2045 CalculateScrollBars ();
2048 internal Point GetItemLocation (int index)
2050 Point loc = items_location [index];
2051 loc.X -= h_marker; // Adjust to scroll
2057 internal int GetItemIndex (int display_index)
2059 return reordered_items_indices [display_index];
2062 internal ListViewItem GetItemAtDisplayIndex (int display_index)
2064 return items [reordered_items_indices [display_index]];
2067 internal void SetItemAtDisplayIndex (int display_index, int index)
2069 reordered_items_indices [display_index] = index;
2072 private bool KeySearchString (KeyEventArgs ke)
2074 int current_tickcnt = Environment.TickCount;
2075 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2076 keysearch_text = string.Empty;
2079 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2082 keysearch_text += (char)ke.KeyCode;
2083 keysearch_tickcnt = current_tickcnt;
2085 int start = FocusedItem == null ? 0 : FocusedItem.DisplayIndex;
2088 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (GetItemAtDisplayIndex (i).Text, keysearch_text,
2089 CompareOptions.IgnoreCase)) {
2091 GetItemAtDisplayIndex (i).Selected = true;
2092 EnsureVisible (GetItemIndex (i));
2095 i = (i + 1 < Items.Count) ? i+1 : 0;
2103 int GetAdjustedIndex (Keys key)
2107 if (View == View.Details) {
2110 result = FocusedItem.DisplayIndex - 1;
2113 result = FocusedItem.DisplayIndex + 1;
2114 if (result == items.Count)
2118 int last_index = LastVisibleIndex;
2119 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2120 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2122 if (FocusedItem.DisplayIndex == last_index) {
2123 if (FocusedItem.DisplayIndex < Items.Count - 1) {
2124 int page_size = item_control.Height / ItemSize.Height - 1;
2125 result = FocusedItem.DisplayIndex + page_size - 1;
2126 if (result >= Items.Count)
2127 result = Items.Count - 1;
2130 result = last_index;
2133 int first_index = FirstVisibleIndex;
2134 if (GetItemLocation (first_index).Y < 0)
2136 if (FocusedItem.DisplayIndex == first_index) {
2137 if (first_index > 0) {
2138 int page_size = item_control.Height / ItemSize.Height - 1;
2139 result = first_index - page_size + 1;
2144 result = first_index;
2150 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.DisplayIndex];
2151 int row = item_matrix_location.Row;
2152 int col = item_matrix_location.Col;
2154 int adjusted_index = -1;
2160 adjusted_index = item_index_matrix [row, col - 1];
2164 if (col == (cols - 1))
2166 while (item_index_matrix [row, col + 1] == 0) {
2171 adjusted_index = item_index_matrix [row, col + 1];
2177 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2182 adjusted_index = item_index_matrix [row - 1, col];
2186 if (row == (rows - 1) || row == Items.Count - 1)
2188 while (item_index_matrix [row + 1, col] == 0) {
2193 adjusted_index = item_index_matrix [row + 1, col];
2200 return items [adjusted_index].DisplayIndex;
2203 ListViewItem selection_start;
2205 private bool SelectItems (ArrayList sel_items)
2207 bool changed = false;
2208 foreach (ListViewItem item in SelectedItems)
2209 if (!sel_items.Contains (item)) {
2210 item.Selected = false;
2213 foreach (ListViewItem item in sel_items)
2214 if (!item.Selected) {
2215 item.Selected = true;
2221 private void UpdateMultiSelection (int index, bool reselect)
2223 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2224 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2225 ListViewItem item = GetItemAtDisplayIndex (index);
2227 if (shift_pressed && selection_start != null) {
2228 ArrayList list = new ArrayList ();
2229 int start_index = selection_start.DisplayIndex;
2230 int start = Math.Min (start_index, index);
2231 int end = Math.Max (start_index, index);
2232 if (View == View.Details) {
2233 for (int i = start; i <= end; i++)
2234 list.Add (GetItemAtDisplayIndex (i));
2236 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2237 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2238 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2239 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2240 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2241 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2243 for (int i = 0; i < items.Count; i++) {
2244 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2246 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2247 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2248 list.Add (GetItemAtDisplayIndex (i));
2252 } else if (ctrl_pressed) {
2253 item.Selected = !item.Selected;
2254 selection_start = item;
2257 // do not unselect, and reselect the item
2258 foreach (int itemIndex in SelectedIndices) {
2259 if (index == itemIndex)
2261 items [itemIndex].Selected = false;
2264 SelectedItems.Clear ();
2265 item.Selected = true;
2267 selection_start = item;
2271 internal override bool InternalPreProcessMessage (ref Message msg)
2273 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2274 Keys key_data = (Keys)msg.WParam.ToInt32();
2276 HandleNavKeys (key_data);
2279 return base.InternalPreProcessMessage (ref msg);
2282 bool HandleNavKeys (Keys key_data)
2284 if (Items.Count == 0 || !item_control.Visible)
2287 if (FocusedItem == null)
2292 SelectIndex (Items.Count - 1);
2305 SelectIndex (GetAdjustedIndex (key_data));
2309 ToggleItemsCheckState ();
2312 if (selected_indices.Count > 0)
2313 OnItemActivate (EventArgs.Empty);
2323 void ToggleItemsCheckState ()
2328 // Don't modify check state if StateImageList has less than 2 elements
2329 if (StateImageList != null && StateImageList.Images.Count < 2)
2332 if (SelectedIndices.Count > 0) {
2333 for (int i = 0; i < SelectedIndices.Count; i++) {
2334 ListViewItem item = Items [SelectedIndices [i]];
2335 item.Checked = !item.Checked;
2340 if (FocusedItem != null) {
2341 FocusedItem.Checked = !FocusedItem.Checked;
2342 SelectIndex (FocusedItem.Index);
2346 void SelectIndex (int display_index)
2348 if (display_index == -1)
2352 UpdateMultiSelection (display_index, true);
2353 else if (!GetItemAtDisplayIndex (display_index).Selected)
2354 GetItemAtDisplayIndex (display_index).Selected = true;
2356 SetFocusedItem (display_index);
2357 EnsureVisible (GetItemIndex (display_index)); // Index in Items collection, not display index
2360 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2362 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2365 if (ke.Alt || ke.Control)
2368 ke.Handled = KeySearchString (ke);
2371 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2373 Point loc = PointToClient (Control.MousePosition);
2374 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2377 internal class ItemControl : Control {
2380 ListViewItem clicked_item;
2381 ListViewItem last_clicked_item;
2382 bool hover_processed = false;
2383 bool checking = false;
2384 ListViewItem prev_hovered_item;
2386 ListViewItem prev_tooltip_item;
2389 Point drag_begin = new Point (-1, -1);
2390 internal int dragged_item_index = -1;
2392 ListViewLabelEditTextBox edit_text_box;
2393 internal ListViewItem edit_item;
2394 LabelEditEventArgs edit_args;
2396 public ItemControl (ListView owner)
2399 DoubleClick += new EventHandler(ItemsDoubleClick);
2400 MouseDown += new MouseEventHandler(ItemsMouseDown);
2401 MouseMove += new MouseEventHandler(ItemsMouseMove);
2402 MouseHover += new EventHandler(ItemsMouseHover);
2403 MouseUp += new MouseEventHandler(ItemsMouseUp);
2406 void ItemsDoubleClick (object sender, EventArgs e)
2408 if (owner.activation == ItemActivation.Standard)
2409 owner.OnItemActivate (EventArgs.Empty);
2419 BoxSelect box_select_mode = BoxSelect.None;
2420 IList prev_selection;
2421 Point box_select_start;
2423 Rectangle box_select_rect;
2424 internal Rectangle BoxSelectRectangle {
2425 get { return box_select_rect; }
2427 if (box_select_rect == value)
2430 InvalidateBoxSelectRect ();
2431 box_select_rect = value;
2432 InvalidateBoxSelectRect ();
2436 void InvalidateBoxSelectRect ()
2438 if (BoxSelectRectangle.Size.IsEmpty)
2441 Rectangle edge = BoxSelectRectangle;
2447 edge.Y = BoxSelectRectangle.Bottom - 1;
2449 edge.Y = BoxSelectRectangle.Y - 1;
2451 edge.Height = BoxSelectRectangle.Height + 2;
2453 edge.X = BoxSelectRectangle.Right - 1;
2457 private Rectangle CalculateBoxSelectRectangle (Point pt)
2459 int left = Math.Min (box_select_start.X, pt.X);
2460 int right = Math.Max (box_select_start.X, pt.X);
2461 int top = Math.Min (box_select_start.Y, pt.Y);
2462 int bottom = Math.Max (box_select_start.Y, pt.Y);
2463 return Rectangle.FromLTRB (left, top, right, bottom);
2466 bool BoxIntersectsItem (int index)
2468 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2469 if (owner.View != View.Details) {
2471 r.Y += r.Height / 4;
2475 return BoxSelectRectangle.IntersectsWith (r);
2478 bool BoxIntersectsText (int index)
2480 Rectangle r = owner.GetItemAtDisplayIndex (index).TextBounds;
2481 return BoxSelectRectangle.IntersectsWith (r);
2484 ArrayList BoxSelectedItems {
2486 ArrayList result = new ArrayList ();
2487 for (int i = 0; i < owner.Items.Count; i++) {
2489 if (owner.View == View.Details && !owner.FullRowSelect)
2490 intersects = BoxIntersectsText (i);
2492 intersects = BoxIntersectsItem (i);
2495 result.Add (owner.GetItemAtDisplayIndex (i));
2501 private bool PerformBoxSelection (Point pt)
2503 if (box_select_mode == BoxSelect.None)
2506 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2508 ArrayList box_items = BoxSelectedItems;
2512 switch (box_select_mode) {
2514 case BoxSelect.Normal:
2518 case BoxSelect.Control:
2519 items = new ArrayList ();
2520 foreach (int index in prev_selection)
2521 if (!box_items.Contains (owner.Items [index]))
2522 items.Add (owner.Items [index]);
2523 foreach (ListViewItem item in box_items)
2524 if (!prev_selection.Contains (item.Index))
2528 case BoxSelect.Shift:
2530 foreach (ListViewItem item in box_items)
2531 prev_selection.Remove (item.Index);
2532 foreach (int index in prev_selection)
2533 items.Add (owner.Items [index]);
2537 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2541 owner.SelectItems (items);
2547 private void ItemsMouseDown (object sender, MouseEventArgs me)
2549 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2550 if (owner.items.Count == 0)
2553 bool box_selecting = false;
2554 Size item_size = owner.ItemSize;
2555 Point pt = new Point (me.X, me.Y);
2556 for (int i = 0; i < owner.items.Count; i++) {
2557 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2558 if (!item_rect.Contains (pt))
2561 // Actual item in 'i' position
2562 ListViewItem item = owner.GetItemAtDisplayIndex (i);
2564 if (item.CheckRectReal.Contains (pt)) {
2565 // Don't modify check state if we have only one image
2566 // and if we are in 1.1 profile only take into account
2568 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2575 // Generate an extra ItemCheck event when we got two clicks
2576 // (Match weird .Net behaviour)
2578 item.Checked = !item.Checked;
2580 item.Checked = !item.Checked;
2585 if (owner.View == View.Details) {
2586 bool over_text = item.TextBounds.Contains (pt);
2587 if (owner.FullRowSelect) {
2588 clicked_item = owner.items [i];
2589 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2590 if (!over_text && over_item_column && owner.MultiSelect)
2591 box_selecting = true;
2592 } else if (over_text)
2593 clicked_item = item;
2595 owner.SetFocusedItem (i);
2597 clicked_item = item;
2603 if (clicked_item != null) {
2604 bool changed = !clicked_item.Selected;
2605 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2606 owner.SetFocusedItem (clicked_item.DisplayIndex);
2608 if (owner.MultiSelect) {
2609 bool reselect = (!owner.LabelEdit || changed);
2610 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2611 owner.UpdateMultiSelection (clicked_item.DisplayIndex, reselect);
2613 clicked_item.Selected = true;
2617 if (owner.VirtualMode && changed) {
2618 // Broken event - It's not fired from Item.Selected also
2619 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2620 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2622 owner.OnVirtualItemsSelectionRangeChanged (args);
2625 // Report clicks only if the item was clicked. On MS the
2626 // clicks are only raised if you click an item
2628 if (me.Clicks > 1) {
2629 if (owner.CheckBoxes)
2630 clicked_item.Checked = !clicked_item.Checked;
2631 } else if (me.Clicks == 1) {
2632 if (owner.LabelEdit && !changed)
2633 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2636 if (owner.MultiSelect)
2637 box_selecting = true;
2638 else if (owner.SelectedItems.Count > 0)
2639 owner.SelectedItems.Clear ();
2642 if (box_selecting) {
2643 Keys mods = XplatUI.State.ModifierKeys;
2644 if ((mods & Keys.Shift) != 0)
2645 box_select_mode = BoxSelect.Shift;
2646 else if ((mods & Keys.Control) != 0)
2647 box_select_mode = BoxSelect.Control;
2649 box_select_mode = BoxSelect.Normal;
2650 box_select_start = pt;
2651 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2655 private void ItemsMouseMove (object sender, MouseEventArgs me)
2657 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2659 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2663 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2664 !hover_processed && owner.Activation != ItemActivation.OneClick
2666 && !owner.ShowItemToolTips
2671 Point pt = PointToClient (Control.MousePosition);
2672 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2674 if (hover_processed && item != null && item != prev_hovered_item) {
2675 hover_processed = false;
2676 XplatUI.ResetMouseHover (Handle);
2679 // Need to invalidate the item in HotTracking to show/hide the underline style
2680 if (owner.Activation == ItemActivation.OneClick) {
2681 if (item == null && owner.HotItemIndex != -1) {
2683 if (owner.HotTracking)
2684 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2687 Cursor = Cursors.Default;
2688 owner.HotItemIndex = -1;
2689 } else if (item != null && owner.HotItemIndex == -1) {
2691 if (owner.HotTracking)
2692 Invalidate (item.Bounds);
2695 Cursor = Cursors.Hand;
2696 owner.HotItemIndex = item.Index;
2700 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2701 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2703 drag_begin = new Point (me.X, me.Y);
2704 dragged_item_index = item.Index;
2708 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2709 if (!r.Contains (me.X, me.Y)) {
2710 ListViewItem dragged_item = owner.items [dragged_item_index];
2711 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2713 drag_begin = new Point (-1, -1);
2714 dragged_item_index = -1;
2720 if (owner.ShowItemToolTips) {
2722 owner.item_tooltip.Active = false;
2723 prev_tooltip_item = null;
2724 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2725 owner.item_tooltip.Active = true;
2726 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2727 prev_tooltip_item = item;
2734 private void ItemsMouseHover (object sender, EventArgs e)
2736 if (owner.hover_pending) {
2737 owner.OnMouseHover (e);
2738 owner.hover_pending = false;
2744 hover_processed = true;
2745 Point pt = PointToClient (Control.MousePosition);
2746 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2750 prev_hovered_item = item;
2752 if (owner.HoverSelection) {
2753 if (owner.MultiSelect)
2754 owner.UpdateMultiSelection (item.Index, true);
2756 item.Selected = true;
2758 owner.SetFocusedItem (item.DisplayIndex);
2759 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2763 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2767 void HandleClicks (MouseEventArgs me)
2769 // if the click is not on an item,
2770 // clicks remains as 0
2773 owner.OnDoubleClick (EventArgs.Empty);
2774 } else if (clicks == 1) {
2775 owner.OnClick (EventArgs.Empty);
2777 owner.OnDoubleClick (EventArgs.Empty);
2778 owner.OnMouseDoubleClick (me);
2779 } else if (clicks == 1) {
2780 owner.OnClick (EventArgs.Empty);
2781 owner.OnMouseClick (me);
2788 private void ItemsMouseUp (object sender, MouseEventArgs me)
2790 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2791 HandleClicks (owner_me);
2794 if (owner.Items.Count == 0) {
2796 owner.OnMouseUp (owner_me);
2800 Point pt = new Point (me.X, me.Y);
2802 Rectangle rect = Rectangle.Empty;
2803 if (clicked_item != null) {
2804 if (owner.view == View.Details && !owner.full_row_select)
2805 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2807 rect = clicked_item.Bounds;
2809 if (rect.Contains (pt)) {
2810 switch (owner.activation) {
2811 case ItemActivation.OneClick:
2812 owner.OnItemActivate (EventArgs.Empty);
2815 case ItemActivation.TwoClick:
2816 if (last_clicked_item == clicked_item) {
2817 owner.OnItemActivate (EventArgs.Empty);
2818 last_clicked_item = null;
2820 last_clicked_item = clicked_item;
2823 // DoubleClick activation is handled in another handler
2827 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2828 // Need this to clean up background clicks
2829 owner.SelectedItems.Clear ();
2833 owner.OnMouseUp (owner_me);
2836 private void ResetMouseState ()
2838 clicked_item = null;
2839 box_select_start = Point.Empty;
2840 BoxSelectRectangle = Rectangle.Empty;
2841 prev_selection = null;
2842 box_select_mode = BoxSelect.None;
2845 // Clean these bits in case the mouse buttons were
2846 // released before firing ItemDrag
2847 dragged_item_index = -1;
2848 drag_begin = new Point (-1, -1);
2851 private void LabelEditFinished (object sender, EventArgs e)
2853 EndEdit (edit_item);
2856 private void LabelEditCancelled (object sender, EventArgs e)
2858 edit_args.SetLabel (null);
2859 EndEdit (edit_item);
2862 private void LabelTextChanged (object sender, EventArgs e)
2864 if (edit_args != null)
2865 edit_args.SetLabel (edit_text_box.Text);
2868 internal void BeginEdit (ListViewItem item)
2870 if (edit_item != null)
2871 EndEdit (edit_item);
2873 if (edit_text_box == null) {
2874 edit_text_box = new ListViewLabelEditTextBox ();
2875 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2876 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2877 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2878 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2879 edit_text_box.Visible = false;
2880 Controls.Add (edit_text_box);
2883 item.EnsureVisible();
2885 edit_text_box.Reset ();
2887 switch (owner.view) {
2889 case View.SmallIcon:
2891 edit_text_box.TextAlign = HorizontalAlignment.Left;
2892 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2893 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2894 edit_text_box.Width = (int)sizef.Width + 4;
2895 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2896 edit_text_box.WordWrap = false;
2897 edit_text_box.Multiline = false;
2899 case View.LargeIcon:
2900 edit_text_box.TextAlign = HorizontalAlignment.Center;
2901 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2902 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2903 edit_text_box.Width = (int)sizef.Width + 4;
2904 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2905 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2906 edit_text_box.WordWrap = true;
2907 edit_text_box.Multiline = true;
2913 edit_text_box.Text = item.Text;
2914 edit_text_box.Font = item.Font;
2915 edit_text_box.Visible = true;
2916 edit_text_box.Focus ();
2917 edit_text_box.SelectAll ();
2919 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2920 owner.OnBeforeLabelEdit (edit_args);
2922 if (edit_args.CancelEdit)
2926 internal void CancelEdit (ListViewItem item)
2928 // do nothing if there's no item being edited, or if the
2929 // item being edited is not the one passed in
2930 if (edit_item == null || edit_item != item)
2933 edit_args.SetLabel (null);
2937 internal void EndEdit (ListViewItem item)
2939 // do nothing if there's no item being edited, or if the
2940 // item being edited is not the one passed in
2941 if (edit_item == null || edit_item != item)
2944 if (edit_text_box != null) {
2945 if (edit_text_box.Visible)
2946 edit_text_box.Visible = false;
2947 // ensure listview gets focus
2951 // Same as TreeView.EndEdit: need to have focus in synch
2952 Application.DoEvents ();
2955 // Create a new instance, since we could get a call to BeginEdit
2956 // from the handler and have fields out of synch
2958 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2961 owner.OnAfterLabelEdit (args);
2962 if (!args.CancelEdit && args.Label != null)
2963 item.Text = args.Label;
2966 internal override void OnPaintInternal (PaintEventArgs pe)
2968 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2971 protected override void WndProc (ref Message m)
2973 switch ((Msg)m.Msg) {
2974 case Msg.WM_KILLFOCUS:
2975 owner.Select (false, true);
2977 case Msg.WM_SETFOCUS:
2978 owner.Select (false, true);
2980 case Msg.WM_LBUTTONDOWN:
2982 owner.Select (false, true);
2984 case Msg.WM_RBUTTONDOWN:
2986 owner.Select (false, true);
2991 base.WndProc (ref m);
2995 internal class ListViewLabelEditTextBox : TextBox
3000 int max_height = -1;
3001 int min_height = -1;
3003 int old_number_lines = 1;
3005 SizeF text_size_one_char;
3007 public ListViewLabelEditTextBox ()
3009 min_height = DefaultSize.Height;
3010 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3013 public int MaxWidth {
3015 if (value < min_width)
3016 max_width = min_width;
3022 public int MaxHeight {
3024 if (value < min_height)
3025 max_height = min_height;
3031 public new int Width {
3041 public override Font Font {
3047 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3051 protected override void OnTextChanged (EventArgs e)
3053 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3055 int new_width = (int)text_size.Width + 8;
3058 ResizeTextBoxWidth (new_width);
3060 if (Width != max_width)
3061 ResizeTextBoxWidth (new_width);
3063 int number_lines = Lines.Length;
3065 if (number_lines != old_number_lines) {
3066 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3067 old_number_lines = number_lines;
3069 ResizeTextBoxHeight (new_height);
3073 base.OnTextChanged (e);
3076 protected override bool IsInputKey (Keys key_data)
3078 if ((key_data & Keys.Alt) == 0) {
3079 switch (key_data & Keys.KeyCode) {
3086 return base.IsInputKey (key_data);
3089 protected override void OnKeyDown (KeyEventArgs e)
3094 switch (e.KeyCode) {
3098 OnEditingFinished (e);
3103 OnEditingCancelled (e);
3108 protected override void OnLostFocus (EventArgs e)
3111 OnEditingFinished (e);
3115 protected void OnEditingCancelled (EventArgs e)
3117 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3122 protected void OnEditingFinished (EventArgs e)
3124 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3129 private void ResizeTextBoxWidth (int new_width)
3131 if (new_width > max_width)
3132 base.Width = max_width;
3134 if (new_width >= min_width)
3135 base.Width = new_width;
3137 base.Width = min_width;
3140 private void ResizeTextBoxHeight (int new_height)
3142 if (new_height > max_height)
3143 base.Height = max_height;
3145 if (new_height >= min_height)
3146 base.Height = new_height;
3148 base.Height = min_height;
3151 public void Reset ()
3158 old_number_lines = 1;
3160 Text = String.Empty;
3165 static object EditingCancelledEvent = new object ();
3166 public event EventHandler EditingCancelled {
3167 add { Events.AddHandler (EditingCancelledEvent, value); }
3168 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3171 static object EditingFinishedEvent = new object ();
3172 public event EventHandler EditingFinished {
3173 add { Events.AddHandler (EditingFinishedEvent, value); }
3174 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3178 internal override void OnPaintInternal (PaintEventArgs pe)
3183 CalculateScrollBars ();
3186 void FocusChanged (object o, EventArgs args)
3188 if (Items.Count == 0)
3191 if (FocusedItem == null)
3194 ListViewItem focused_item = FocusedItem;
3196 if (focused_item.ListView != null) {
3197 item_control.Invalidate (focused_item.Bounds);
3198 focused_item.Layout ();
3199 item_control.Invalidate (focused_item.Bounds);
3203 private void ListView_MouseEnter (object sender, EventArgs args)
3205 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3208 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3210 if (Items.Count == 0)
3213 int lines = me.Delta / 120;
3220 case View.SmallIcon:
3221 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3223 case View.LargeIcon:
3224 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3227 Scroll (h_scroll, -ItemSize.Width * lines);
3231 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3237 private void ListView_SizeChanged (object sender, EventArgs e)
3239 CalculateListView (alignment);
3242 private void SetFocusedItem (int display_index)
3244 if (display_index != -1)
3245 GetItemAtDisplayIndex (display_index).Focused = true;
3246 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3247 GetItemAtDisplayIndex (focused_item_index).Focused = false;
3249 focused_item_index = display_index;
3252 private void HorizontalScroller (object sender, EventArgs e)
3254 item_control.EndEdit (item_control.edit_item);
3256 // Avoid unnecessary flickering, when button is
3257 // kept pressed at the end
3258 if (h_marker != h_scroll.Value) {
3260 int pixels = h_marker - h_scroll.Value;
3262 h_marker = h_scroll.Value;
3263 if (header_control.Visible)
3264 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3266 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3270 private void VerticalScroller (object sender, EventArgs e)
3272 item_control.EndEdit (item_control.edit_item);
3274 // Avoid unnecessary flickering, when button is
3275 // kept pressed at the end
3276 if (v_marker != v_scroll.Value) {
3277 int pixels = v_marker - v_scroll.Value;
3278 Rectangle area = item_control.ClientRectangle;
3279 if (header_control.Visible) {
3280 area.Y += header_control.Height;
3281 area.Height -= header_control.Height;
3284 v_marker = v_scroll.Value;
3285 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3289 internal override bool IsInputCharInternal (char charCode)
3293 #endregion // Internal Methods Properties
3295 #region Protected Methods
3296 protected override void CreateHandle ()
3298 base.CreateHandle ();
3299 for (int i = 0; i < SelectedItems.Count; i++)
3300 OnSelectedIndexChanged (EventArgs.Empty);
3303 protected override void Dispose (bool disposing)
3306 h_scroll.Dispose ();
3307 v_scroll.Dispose ();
3309 large_image_list = null;
3310 small_image_list = null;
3311 state_image_list = null;
3313 foreach (ColumnHeader col in columns)
3314 col.SetListView (null);
3317 if (!virtual_mode) // In virtual mode we don't save the items
3319 foreach (ListViewItem item in items)
3323 base.Dispose (disposing);
3326 protected override bool IsInputKey (Keys keyData)
3343 return base.IsInputKey (keyData);
3346 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3348 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3354 protected override void OnBackgroundImageChanged (EventArgs e)
3356 item_control.BackgroundImage = BackgroundImage;
3357 base.OnBackgroundImageChanged (e);
3361 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3363 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3368 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3370 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3376 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3378 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3383 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3385 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3390 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3392 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3398 protected override void OnEnabledChanged (EventArgs e)
3400 base.OnEnabledChanged (e);
3404 protected override void OnFontChanged (EventArgs e)
3406 base.OnFontChanged (e);
3410 protected override void OnHandleCreated (EventArgs e)
3412 base.OnHandleCreated (e);
3413 CalculateListView (alignment);
3415 if (!virtual_mode) // Sorting is not allowed in virtual mode
3420 protected override void OnHandleDestroyed (EventArgs e)
3422 base.OnHandleDestroyed (e);
3425 protected virtual void OnItemActivate (EventArgs e)
3427 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3432 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3434 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3440 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3442 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3448 protected virtual void OnItemDrag (ItemDragEventArgs e)
3450 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3456 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3458 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3463 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3465 ListViewItemSelectionChangedEventHandler eh =
3466 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3471 protected override void OnMouseHover (EventArgs e)
3473 base.OnMouseHover (e);
3476 protected override void OnParentChanged (EventArgs e)
3478 base.OnParentChanged (e);
3482 protected virtual void OnSelectedIndexChanged (EventArgs e)
3484 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3489 protected override void OnSystemColorsChanged (EventArgs e)
3491 base.OnSystemColorsChanged (e);
3495 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3497 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3502 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3504 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3509 [EditorBrowsable (EditorBrowsableState.Advanced)]
3510 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3512 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3517 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3519 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3524 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3526 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3527 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3533 protected void RealizeProperties ()
3538 protected void UpdateExtendedStyles ()
3543 bool refocusing = false;
3545 protected override void WndProc (ref Message m)
3547 switch ((Msg)m.Msg) {
3548 case Msg.WM_KILLFOCUS:
3549 Control receiver = Control.FromHandle (m.WParam);
3550 if (receiver == item_control) {
3556 case Msg.WM_SETFOCUS:
3566 base.WndProc (ref m);
3568 #endregion // Protected Methods
3570 #region Public Instance Methods
3571 public void ArrangeIcons ()
3573 ArrangeIcons (this.alignment);
3576 public void ArrangeIcons (ListViewAlignment value)
3578 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3579 if (view == View.LargeIcon || view == View.SmallIcon) {
3580 this.CalculateListView (value);
3581 // we have done the calculations already
3582 this.Redraw (false);
3587 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3589 if (columnIndex < 0 || columnIndex >= columns.Count)
3590 throw new ArgumentOutOfRangeException ("columnIndex");
3592 columns [columnIndex].AutoResize (headerAutoResize);
3595 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3598 foreach (ColumnHeader col in columns)
3599 col.AutoResize (headerAutoResize);
3604 public void BeginUpdate ()
3606 // flag to avoid painting
3610 public void Clear ()
3613 items.Clear (); // Redraw (true) called here
3616 public void EndUpdate ()
3618 // flag to avoid painting
3621 // probably, now we need a redraw with recalculations
3625 public void EnsureVisible (int index)
3627 if (index < 0 || index >= items.Count || scrollable == false)
3630 Rectangle view_rect = item_control.ClientRectangle;
3631 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3633 if (view_rect.Contains (bounds))
3636 if (View != View.Details) {
3637 if (bounds.Left < 0)
3638 h_scroll.Value += bounds.Left;
3639 else if (bounds.Right > view_rect.Right)
3640 h_scroll.Value += (bounds.Right - view_rect.Right);
3644 v_scroll.Value += bounds.Top;
3645 else if (bounds.Bottom > view_rect.Bottom)
3646 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3650 public ListViewItem FindItemWithText (string text)
3652 if (items.Count == 0)
3655 return FindItemWithText (text, true, 0, true);
3658 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3660 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true);
3663 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3665 if (startIndex < 0 || startIndex >= items.Count)
3666 throw new ArgumentOutOfRangeException ("startIndex");
3669 throw new ArgumentNullException ("text");
3672 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3673 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3674 SearchDirectionHint.Down, startIndex);
3676 OnSearchForVirtualItem (args);
3677 int idx = args.Index;
3678 if (idx >= 0 && idx < virtual_list_size)
3684 for (int i = startIndex; i < items.Count; i++) {
3685 ListViewItem lvi = items [i];
3687 if ((isPrefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3688 || String.Compare (lvi.Text, text, true) == 0) // match
3692 if (includeSubItemsInSearch) {
3693 for (int i = startIndex; i < items.Count; i++) {
3694 ListViewItem lvi = items [i];
3695 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3696 if ((isPrefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3697 || String.Compare (sub_item.Text, text, true) == 0)
3705 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3707 return FindNearestItem (searchDirection, new Point (x, y));
3710 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3712 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3713 throw new ArgumentOutOfRangeException ("searchDirection");
3715 if (view != View.LargeIcon && view != View.SmallIcon)
3716 throw new InvalidOperationException ();
3719 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3720 false, false, String.Empty, point,
3723 OnSearchForVirtualItem (args);
3724 int idx = args.Index;
3725 if (idx >= 0 && idx < virtual_list_size)
3731 ListViewItem item = null;
3732 int min_dist = Int32.MaxValue;
3735 // It looks like .Net does a previous adjustment
3738 case SearchDirectionHint.Up:
3739 point.Y -= item_size.Height;
3741 case SearchDirectionHint.Down:
3742 point.Y += item_size.Height;
3744 case SearchDirectionHint.Left:
3745 point.X -= item_size.Width;
3747 case SearchDirectionHint.Right:
3748 point.X += item_size.Width;
3752 for (int i = 0; i < items.Count; i++) {
3753 Point item_loc = GetItemLocation (i);
3755 if (dir == SearchDirectionHint.Up) {
3756 if (point.Y < item_loc.Y)
3758 } else if (dir == SearchDirectionHint.Down) {
3759 if (point.Y > item_loc.Y)
3761 } else if (dir == SearchDirectionHint.Left) {
3762 if (point.X < item_loc.X)
3764 } else if (dir == SearchDirectionHint.Right) {
3765 if (point.X > item_loc.X)
3769 int x_dist = point.X - item_loc.X;
3770 int y_dist = point.Y - item_loc.Y;
3772 int dist = x_dist * x_dist + y_dist * y_dist;
3773 if (dist < min_dist) {
3783 public ListViewItem GetItemAt (int x, int y)
3785 Size item_size = ItemSize;
3786 for (int i = 0; i < items.Count; i++) {
3787 Point item_location = GetItemLocation (i);
3788 Rectangle item_rect = new Rectangle (item_location, item_size);
3789 if (item_rect.Contains (x, y))
3796 public Rectangle GetItemRect (int index)
3798 return GetItemRect (index, ItemBoundsPortion.Entire);
3801 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3803 if (index < 0 || index >= items.Count)
3804 throw new IndexOutOfRangeException ("index");
3806 return items [index].GetBounds (portion);
3810 public ListViewHitTestInfo HitTest (Point point)
3812 return HitTest (point.X, point.Y);
3815 public ListViewHitTestInfo HitTest (int x, int y)
3818 throw new ArgumentOutOfRangeException ("x");
3820 throw new ArgumentOutOfRangeException ("y");
3822 ListViewItem item = GetItemAt (x, y);
3824 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3826 ListViewHitTestLocations locations = 0;
3827 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3828 locations |= ListViewHitTestLocations.Label;
3829 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3830 locations |= ListViewHitTestLocations.Image;
3831 else if (item.CheckRectReal.Contains (x, y))
3832 locations |= ListViewHitTestLocations.StateImage;
3834 ListViewItem.ListViewSubItem subitem = null;
3835 if (view == View.Details)
3836 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3837 if (si.Bounds.Contains (x, y)) {
3842 return new ListViewHitTestInfo (item, subitem, locations);
3845 [EditorBrowsable (EditorBrowsableState.Advanced)]
3846 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3848 if (startIndex < 0 || startIndex >= items.Count)
3849 throw new ArgumentOutOfRangeException ("startIndex");
3850 if (endIndex < 0 || endIndex >= items.Count)
3851 throw new ArgumentOutOfRangeException ("endIndex");
3852 if (startIndex > endIndex)
3853 throw new ArgumentException ("startIndex");
3858 for (int i = startIndex; i <= endIndex; i++)
3859 item_control.Invalidate (items [i].Bounds);
3861 if (!invalidateOnly)
3870 throw new InvalidOperationException ();
3876 // we need this overload to reuse the logic for sorting, while allowing
3877 // redrawing to be done by caller or have it done by this method when
3878 // sorting is really performed
3880 // ListViewItemCollection's Add and AddRange methods call this overload
3881 // with redraw set to false, as they take care of redrawing themselves
3882 // (they even want to redraw the listview if no sort is performed, as
3883 // an item was added), while ListView.Sort () only wants to redraw if
3884 // sorting was actually performed
3885 private void Sort (bool redraw)
3887 if (!IsHandleCreated || item_sorter == null) {
3891 items.Sort (item_sorter);
3896 public override string ToString ()
3898 int count = this.Items.Count;
3901 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3903 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3905 #endregion // Public Instance Methods
3910 class HeaderControl : Control {
3913 bool column_resize_active = false;
3914 ColumnHeader resize_column;
3915 ColumnHeader clicked_column;
3916 ColumnHeader drag_column;
3918 int drag_to_index = -1;
3920 public HeaderControl (ListView owner)
3923 MouseDown += new MouseEventHandler (HeaderMouseDown);
3924 MouseMove += new MouseEventHandler (HeaderMouseMove);
3925 MouseUp += new MouseEventHandler (HeaderMouseUp);
3928 private ColumnHeader ColumnAtX (int x)
3930 Point pt = new Point (x, 0);
3931 ColumnHeader result = null;
3932 foreach (ColumnHeader col in owner.Columns) {
3933 if (col.Rect.Contains (pt)) {
3941 private int GetReorderedIndex (ColumnHeader col)
3943 if (owner.reordered_column_indices == null)
3946 for (int i = 0; i < owner.Columns.Count; i++)
3947 if (owner.reordered_column_indices [i] == col.Index)
3949 throw new Exception ("Column index missing from reordered array");
3952 private void HeaderMouseDown (object sender, MouseEventArgs me)
3954 if (resize_column != null) {
3955 column_resize_active = true;
3960 clicked_column = ColumnAtX (me.X + owner.h_marker);
3962 if (clicked_column != null) {
3964 if (owner.AllowColumnReorder) {
3966 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3967 drag_column.Rect = clicked_column.Rect;
3968 drag_to_index = GetReorderedIndex (clicked_column);
3970 clicked_column.Pressed = true;
3971 Rectangle bounds = clicked_column.Rect;
3972 bounds.X -= owner.h_marker;
3973 Invalidate (bounds);
3980 column_resize_active = false;
3981 resize_column = null;
3983 Cursor = Cursors.Default;
3986 private void HeaderMouseMove (object sender, MouseEventArgs me)
3988 Point pt = new Point (me.X + owner.h_marker, me.Y);
3990 if (column_resize_active) {
3991 int width = pt.X - resize_column.X;
3995 if (!owner.CanProceedWithResize (resize_column, width)){
3999 resize_column.Width = width;
4003 resize_column = null;
4005 if (clicked_column != null) {
4006 if (owner.AllowColumnReorder) {
4009 r = drag_column.Rect;
4010 r.X = clicked_column.Rect.X + me.X - drag_x;
4011 drag_column.Rect = r;
4013 int x = me.X + owner.h_marker;
4014 ColumnHeader over = ColumnAtX (x);
4016 drag_to_index = owner.Columns.Count;
4017 else if (x < over.X + over.Width / 2)
4018 drag_to_index = GetReorderedIndex (over);
4020 drag_to_index = GetReorderedIndex (over) + 1;
4023 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
4024 bool pressed = clicked_column.Pressed;
4025 clicked_column.Pressed = over == clicked_column;
4026 if (clicked_column.Pressed ^ pressed) {
4027 Rectangle bounds = clicked_column.Rect;
4028 bounds.X -= owner.h_marker;
4029 Invalidate (bounds);
4035 for (int i = 0; i < owner.Columns.Count; i++) {
4036 Rectangle zone = owner.Columns [i].Rect;
4037 zone.X = zone.Right - 5;
4039 if (zone.Contains (pt)) {
4040 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
4042 resize_column = owner.Columns [i];
4047 if (resize_column == null)
4048 Cursor = Cursors.Default;
4050 Cursor = Cursors.VSplit;
4053 void HeaderMouseUp (object sender, MouseEventArgs me)
4057 if (column_resize_active) {
4058 int column_idx = resize_column.Index;
4060 owner.RaiseColumnWidthChanged (column_idx);
4064 if (clicked_column != null && clicked_column.Pressed) {
4065 clicked_column.Pressed = false;
4066 Rectangle bounds = clicked_column.Rect;
4067 bounds.X -= owner.h_marker;
4068 Invalidate (bounds);
4069 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4072 if (drag_column != null && owner.AllowColumnReorder) {
4074 if (drag_to_index > GetReorderedIndex (clicked_column))
4076 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4077 owner.ReorderColumn (clicked_column, drag_to_index, true);
4082 clicked_column = null;
4085 internal override void OnPaintInternal (PaintEventArgs pe)
4090 Theme theme = ThemeEngine.Current;
4091 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4093 if (drag_column == null)
4097 if (drag_to_index == owner.Columns.Count)
4098 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4100 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4101 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4104 protected override void WndProc (ref Message m)
4106 switch ((Msg)m.Msg) {
4107 case Msg.WM_SETFOCUS:
4111 base.WndProc (ref m);
4117 private class ItemComparer : IComparer {
4118 readonly SortOrder sort_order;
4120 public ItemComparer (SortOrder sortOrder)
4122 sort_order = sortOrder;
4125 public int Compare (object x, object y)
4127 ListViewItem item_x = x as ListViewItem;
4128 ListViewItem item_y = y as ListViewItem;
4129 if (sort_order == SortOrder.Ascending)
4130 return String.Compare (item_x.Text, item_y.Text);
4132 return String.Compare (item_y.Text, item_x.Text);
4137 [ListBindable (false)]
4139 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4141 private readonly ListView owner;
4143 #region Public Constructor
4144 public CheckedIndexCollection (ListView owner)
4148 #endregion // Public Constructor
4150 #region Public Properties
4153 get { return owner.CheckedItems.Count; }
4156 public bool IsReadOnly {
4157 get { return true; }
4160 public int this [int index] {
4162 int [] indices = GetIndices ();
4163 if (index < 0 || index >= indices.Length)
4164 throw new ArgumentOutOfRangeException ("index");
4165 return indices [index];
4169 bool ICollection.IsSynchronized {
4170 get { return false; }
4173 object ICollection.SyncRoot {
4174 get { return this; }
4177 bool IList.IsFixedSize {
4178 get { return true; }
4181 object IList.this [int index] {
4182 get { return this [index]; }
4183 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4185 #endregion // Public Properties
4187 #region Public Methods
4188 public bool Contains (int checkedIndex)
4190 int [] indices = GetIndices ();
4191 for (int i = 0; i < indices.Length; i++) {
4192 if (indices [i] == checkedIndex)
4198 public IEnumerator GetEnumerator ()
4200 int [] indices = GetIndices ();
4201 return indices.GetEnumerator ();
4204 void ICollection.CopyTo (Array dest, int index)
4206 int [] indices = GetIndices ();
4207 Array.Copy (indices, 0, dest, index, indices.Length);
4210 int IList.Add (object value)
4212 throw new NotSupportedException ("Add operation is not supported.");
4217 throw new NotSupportedException ("Clear operation is not supported.");
4220 bool IList.Contains (object checkedIndex)
4222 if (!(checkedIndex is int))
4224 return Contains ((int) checkedIndex);
4227 int IList.IndexOf (object checkedIndex)
4229 if (!(checkedIndex is int))
4231 return IndexOf ((int) checkedIndex);
4234 void IList.Insert (int index, object value)
4236 throw new NotSupportedException ("Insert operation is not supported.");
4239 void IList.Remove (object value)
4241 throw new NotSupportedException ("Remove operation is not supported.");
4244 void IList.RemoveAt (int index)
4246 throw new NotSupportedException ("RemoveAt operation is not supported.");
4249 public int IndexOf (int checkedIndex)
4251 int [] indices = GetIndices ();
4252 for (int i = 0; i < indices.Length; i++) {
4253 if (indices [i] == checkedIndex)
4258 #endregion // Public Methods
4260 private int [] GetIndices ()
4262 ArrayList checked_items = owner.CheckedItems.List;
4263 int [] indices = new int [checked_items.Count];
4264 for (int i = 0; i < checked_items.Count; i++) {
4265 ListViewItem item = (ListViewItem) checked_items [i];
4266 indices [i] = item.Index;
4270 } // CheckedIndexCollection
4273 [ListBindable (false)]
4275 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4277 private readonly ListView owner;
4278 private ArrayList list;
4280 #region Public Constructor
4281 public CheckedListViewItemCollection (ListView owner)
4284 this.owner.Items.Changed += new CollectionChangedHandler (
4285 ItemsCollection_Changed);
4287 #endregion // Public Constructor
4289 #region Public Properties
4293 if (!owner.CheckBoxes)
4299 public bool IsReadOnly {
4300 get { return true; }
4303 public ListViewItem this [int index] {
4306 if (owner.VirtualMode)
4307 throw new InvalidOperationException ();
4309 ArrayList checked_items = List;
4310 if (index < 0 || index >= checked_items.Count)
4311 throw new ArgumentOutOfRangeException ("index");
4312 return (ListViewItem) checked_items [index];
4317 public virtual ListViewItem this [string key] {
4319 int idx = IndexOfKey (key);
4320 return idx == -1 ? null : (ListViewItem) List [idx];
4325 bool ICollection.IsSynchronized {
4326 get { return false; }
4329 object ICollection.SyncRoot {
4330 get { return this; }
4333 bool IList.IsFixedSize {
4334 get { return true; }
4337 object IList.this [int index] {
4338 get { return this [index]; }
4339 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4341 #endregion // Public Properties
4343 #region Public Methods
4344 public bool Contains (ListViewItem item)
4346 if (!owner.CheckBoxes)
4348 return List.Contains (item);
4352 public virtual bool ContainsKey (string key)
4354 return IndexOfKey (key) != -1;
4358 public void CopyTo (Array dest, int index)
4361 if (owner.VirtualMode)
4362 throw new InvalidOperationException ();
4364 if (!owner.CheckBoxes)
4366 List.CopyTo (dest, index);
4369 public IEnumerator GetEnumerator ()
4372 if (owner.VirtualMode)
4373 throw new InvalidOperationException ();
4375 if (!owner.CheckBoxes)
4376 return (new ListViewItem [0]).GetEnumerator ();
4377 return List.GetEnumerator ();
4380 int IList.Add (object value)
4382 throw new NotSupportedException ("Add operation is not supported.");
4387 throw new NotSupportedException ("Clear operation is not supported.");
4390 bool IList.Contains (object item)
4392 if (!(item is ListViewItem))
4394 return Contains ((ListViewItem) item);
4397 int IList.IndexOf (object item)
4399 if (!(item is ListViewItem))
4401 return IndexOf ((ListViewItem) item);
4404 void IList.Insert (int index, object value)
4406 throw new NotSupportedException ("Insert operation is not supported.");
4409 void IList.Remove (object value)
4411 throw new NotSupportedException ("Remove operation is not supported.");
4414 void IList.RemoveAt (int index)
4416 throw new NotSupportedException ("RemoveAt operation is not supported.");
4419 public int IndexOf (ListViewItem item)
4422 if (owner.VirtualMode)
4423 throw new InvalidOperationException ();
4425 if (!owner.CheckBoxes)
4427 return List.IndexOf (item);
4431 public virtual int IndexOfKey (string key)
4434 if (owner.VirtualMode)
4435 throw new InvalidOperationException ();
4437 if (key == null || key.Length == 0)
4440 ArrayList checked_items = List;
4441 for (int i = 0; i < checked_items.Count; i++) {
4442 ListViewItem item = (ListViewItem) checked_items [i];
4443 if (String.Compare (key, item.Name, true) == 0)
4450 #endregion // Public Methods
4452 internal ArrayList List {
4455 list = new ArrayList ();
4456 foreach (ListViewItem item in owner.Items) {
4465 internal void Reset ()
4467 // force re-population of list
4471 private void ItemsCollection_Changed ()
4475 } // CheckedListViewItemCollection
4478 [ListBindable (false)]
4480 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4482 internal ArrayList list;
4483 private ListView owner;
4485 #region Public Constructor
4486 public ColumnHeaderCollection (ListView owner)
4488 list = new ArrayList ();
4491 #endregion // Public Constructor
4493 #region Public Properties
4496 get { return list.Count; }
4499 public bool IsReadOnly {
4500 get { return false; }
4503 public virtual ColumnHeader this [int index] {
4505 if (index < 0 || index >= list.Count)
4506 throw new ArgumentOutOfRangeException ("index");
4507 return (ColumnHeader) list [index];
4512 public virtual ColumnHeader this [string key] {
4514 int idx = IndexOfKey (key);
4518 return (ColumnHeader) list [idx];
4523 bool ICollection.IsSynchronized {
4524 get { return true; }
4527 object ICollection.SyncRoot {
4528 get { return this; }
4531 bool IList.IsFixedSize {
4532 get { return list.IsFixedSize; }
4535 object IList.this [int index] {
4536 get { return this [index]; }
4537 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4539 #endregion // Public Properties
4541 #region Public Methods
4542 public virtual int Add (ColumnHeader value)
4544 int idx = list.Add (value);
4545 owner.AddColumn (value, idx, true);
4550 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4554 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4557 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4558 this.Add (colHeader);
4563 public virtual ColumnHeader Add (string text)
4565 return Add (String.Empty, text);
4568 public virtual ColumnHeader Add (string text, int width)
4570 return Add (String.Empty, text, width);
4573 public virtual ColumnHeader Add (string key, string text)
4575 ColumnHeader colHeader = new ColumnHeader ();
4576 colHeader.Name = key;
4577 colHeader.Text = text;
4582 public virtual ColumnHeader Add (string key, string text, int width)
4584 return Add (key, text, width, HorizontalAlignment.Left, -1);
4587 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4589 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4590 colHeader.ImageIndex = imageIndex;
4595 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4597 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4598 colHeader.ImageKey = imageKey;
4604 public virtual void AddRange (ColumnHeader [] values)
4606 foreach (ColumnHeader colHeader in values) {
4607 int idx = list.Add (colHeader);
4608 owner.AddColumn (colHeader, idx, false);
4611 owner.Redraw (true);
4614 public virtual void Clear ()
4616 foreach (ColumnHeader col in list)
4617 col.SetListView (null);
4619 owner.ReorderColumns (new int [0], true);
4622 public bool Contains (ColumnHeader value)
4624 return list.Contains (value);
4628 public virtual bool ContainsKey (string key)
4630 return IndexOfKey (key) != -1;
4634 public IEnumerator GetEnumerator ()
4636 return list.GetEnumerator ();
4639 void ICollection.CopyTo (Array dest, int index)
4641 list.CopyTo (dest, index);
4644 int IList.Add (object value)
4646 if (! (value is ColumnHeader)) {
4647 throw new ArgumentException ("Not of type ColumnHeader", "value");
4650 return this.Add ((ColumnHeader) value);
4653 bool IList.Contains (object value)
4655 if (! (value is ColumnHeader)) {
4656 throw new ArgumentException ("Not of type ColumnHeader", "value");
4659 return this.Contains ((ColumnHeader) value);
4662 int IList.IndexOf (object value)
4664 if (! (value is ColumnHeader)) {
4665 throw new ArgumentException ("Not of type ColumnHeader", "value");
4668 return this.IndexOf ((ColumnHeader) value);
4671 void IList.Insert (int index, object value)
4673 if (! (value is ColumnHeader)) {
4674 throw new ArgumentException ("Not of type ColumnHeader", "value");
4677 this.Insert (index, (ColumnHeader) value);
4680 void IList.Remove (object value)
4682 if (! (value is ColumnHeader)) {
4683 throw new ArgumentException ("Not of type ColumnHeader", "value");
4686 this.Remove ((ColumnHeader) value);
4689 public int IndexOf (ColumnHeader value)
4691 return list.IndexOf (value);
4695 public virtual int IndexOfKey (string key)
4697 if (key == null || key.Length == 0)
4700 for (int i = 0; i < list.Count; i++) {
4701 ColumnHeader col = (ColumnHeader) list [i];
4702 if (String.Compare (key, col.Name, true) == 0)
4710 public void Insert (int index, ColumnHeader value)
4712 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4713 // but it's really only greater.
4714 if (index < 0 || index > list.Count)
4715 throw new ArgumentOutOfRangeException ("index");
4717 list.Insert (index, value);
4718 owner.AddColumn (value, index, true);
4722 public void Insert (int index, string text)
4724 Insert (index, String.Empty, text);
4727 public void Insert (int index, string text, int width)
4729 Insert (index, String.Empty, text, width);
4732 public void Insert (int index, string key, string text)
4734 ColumnHeader colHeader = new ColumnHeader ();
4735 colHeader.Name = key;
4736 colHeader.Text = text;
4737 Insert (index, colHeader);
4740 public void Insert (int index, string key, string text, int width)
4742 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4743 Insert (index, colHeader);
4746 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4748 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4749 colHeader.ImageIndex = imageIndex;
4750 Insert (index, colHeader);
4753 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4755 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4756 colHeader.ImageKey = imageKey;
4757 Insert (index, colHeader);
4762 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4766 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4769 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4770 this.Insert (index, colHeader);
4773 public virtual void Remove (ColumnHeader column)
4775 if (!Contains (column))
4778 list.Remove (column);
4779 column.SetListView (null);
4781 int rem_display_index = column.InternalDisplayIndex;
4782 int [] display_indices = new int [list.Count];
4783 for (int i = 0; i < display_indices.Length; i++) {
4784 ColumnHeader col = (ColumnHeader) list [i];
4785 int display_index = col.InternalDisplayIndex;
4786 if (display_index < rem_display_index) {
4787 display_indices [i] = display_index;
4789 display_indices [i] = (display_index - 1);
4793 column.InternalDisplayIndex = -1;
4794 owner.ReorderColumns (display_indices, true);
4798 public virtual void RemoveByKey (string key)
4800 int idx = IndexOfKey (key);
4806 public virtual void RemoveAt (int index)
4808 if (index < 0 || index >= list.Count)
4809 throw new ArgumentOutOfRangeException ("index");
4811 ColumnHeader col = (ColumnHeader) list [index];
4814 #endregion // Public Methods
4817 } // ColumnHeaderCollection
4820 [ListBindable (false)]
4822 public class ListViewItemCollection : IList, ICollection, IEnumerable
4824 private readonly ArrayList list;
4825 private ListView owner;
4827 private ListViewGroup group;
4830 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4831 // In the later case ListViewItem.ListView never gets modified
4832 private bool is_main_collection = true;
4834 #region Public Constructor
4835 public ListViewItemCollection (ListView owner)
4837 list = new ArrayList (0);
4840 #endregion // Public Constructor
4843 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4846 is_main_collection = false;
4850 #region Public Properties
4855 if (owner != null && owner.VirtualMode)
4856 return owner.VirtualListSize;
4863 public bool IsReadOnly {
4864 get { return false; }
4868 public virtual ListViewItem this [int index] {
4870 public virtual ListViewItem this [int displayIndex] {
4874 int index = displayIndex;
4877 if (index < 0 || index >= Count)
4878 throw new ArgumentOutOfRangeException ("index");
4881 if (owner != null && owner.VirtualMode)
4882 return RetrieveVirtualItemFromOwner (index);
4884 return (ListViewItem) list [index];
4889 int index = displayIndex;
4892 if (index < 0 || index >= Count)
4893 throw new ArgumentOutOfRangeException ("index");
4896 if (owner != null && owner.VirtualMode)
4897 throw new InvalidOperationException ();
4900 if (list.Contains (value))
4901 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4903 if (value.ListView != null && value.ListView != owner)
4904 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");
4906 if (is_main_collection)
4907 value.Owner = owner;
4910 if (value.Group != null)
4911 value.Group.Items.Remove (value);
4913 value.SetGroup (group);
4917 list [index] = value;
4918 CollectionChanged (true);
4923 public virtual ListViewItem this [string key] {
4925 int idx = IndexOfKey (key);
4934 bool ICollection.IsSynchronized {
4935 get { return true; }
4938 object ICollection.SyncRoot {
4939 get { return this; }
4942 bool IList.IsFixedSize {
4943 get { return list.IsFixedSize; }
4946 object IList.this [int index] {
4947 get { return this [index]; }
4949 if (value is ListViewItem)
4950 this [index] = (ListViewItem) value;
4952 this [index] = new ListViewItem (value.ToString ());
4956 #endregion // Public Properties
4958 #region Public Methods
4959 public virtual ListViewItem Add (ListViewItem value)
4962 if (owner != null && owner.VirtualMode)
4963 throw new InvalidOperationException ();
4968 // Item is ignored until it has been added to the ListView
4969 if (is_main_collection || value.ListView != null)
4970 CollectionChanged (true);
4975 public virtual ListViewItem Add (string text)
4977 ListViewItem item = new ListViewItem (text);
4978 return this.Add (item);
4981 public virtual ListViewItem Add (string text, int imageIndex)
4983 ListViewItem item = new ListViewItem (text, imageIndex);
4984 return this.Add (item);
4988 public virtual ListViewItem Add (string text, string imageKey)
4990 ListViewItem item = new ListViewItem (text, imageKey);
4991 return this.Add (item);
4994 public virtual ListViewItem Add (string key, string text, int imageIndex)
4996 ListViewItem item = new ListViewItem (text, imageIndex);
4998 return this.Add (item);
5001 public virtual ListViewItem Add (string key, string text, string imageKey)
5003 ListViewItem item = new ListViewItem (text, imageKey);
5005 return this.Add (item);
5010 public void AddRange (ListViewItem [] items)
5013 public void AddRange (ListViewItem [] values)
5015 ListViewItem [] items = values;
5018 throw new ArgumentNullException ("Argument cannot be null!", "items");
5020 if (owner != null && owner.VirtualMode)
5021 throw new InvalidOperationException ();
5024 foreach (ListViewItem item in items)
5027 CollectionChanged (true);
5031 public void AddRange (ListViewItemCollection items)
5034 throw new ArgumentNullException ("Argument cannot be null!", "items");
5036 ListViewItem[] itemArray = new ListViewItem[items.Count];
5037 items.CopyTo (itemArray,0);
5038 this.AddRange (itemArray);
5042 public virtual void Clear ()
5045 if (owner != null && owner.VirtualMode)
5046 throw new InvalidOperationException ();
5048 if (is_main_collection && owner != null) {
5049 owner.SetFocusedItem (-1);
5050 owner.h_scroll.Value = owner.v_scroll.Value = 0;
5052 foreach (ListViewItem item in list) {
5053 owner.item_control.CancelEdit (item);
5060 foreach (ListViewItem item in list)
5061 item.SetGroup (null);
5065 CollectionChanged (false);
5068 public bool Contains (ListViewItem item)
5070 return IndexOf (item) != -1;
5074 public virtual bool ContainsKey (string key)
5076 return IndexOfKey (key) != -1;
5080 public void CopyTo (Array dest, int index)
5082 list.CopyTo (dest, index);
5086 public ListViewItem [] Find (string key, bool searchAllSubItems)
5089 return new ListViewItem [0];
5091 List<ListViewItem> temp_list = new List<ListViewItem> ();
5093 for (int i = 0; i < list.Count; i++) {
5094 ListViewItem lvi = (ListViewItem) list [i];
5095 if (String.Compare (key, lvi.Name, true) == 0)
5096 temp_list.Add (lvi);
5099 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5100 temp_list.CopyTo (retval);
5106 public IEnumerator GetEnumerator ()
5109 if (owner != null && owner.VirtualMode)
5110 throw new InvalidOperationException ();
5113 return list.GetEnumerator ();
5116 int IList.Add (object item)
5122 if (owner != null && owner.VirtualMode)
5123 throw new InvalidOperationException ();
5126 if (item is ListViewItem) {
5127 li = (ListViewItem) item;
5128 if (list.Contains (li))
5129 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5131 if (li.ListView != null && li.ListView != owner)
5132 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");
5135 li = new ListViewItem (item.ToString ());
5138 result = list.Add (li);
5139 CollectionChanged (true);
5144 bool IList.Contains (object item)
5146 return Contains ((ListViewItem) item);
5149 int IList.IndexOf (object item)
5151 return IndexOf ((ListViewItem) item);
5154 void IList.Insert (int index, object item)
5156 if (item is ListViewItem)
5157 this.Insert (index, (ListViewItem) item);
5159 this.Insert (index, item.ToString ());
5162 void IList.Remove (object item)
5164 Remove ((ListViewItem) item);
5167 public int IndexOf (ListViewItem item)
5170 if (owner != null && owner.VirtualMode) {
5171 for (int i = 0; i < Count; i++)
5172 if (RetrieveVirtualItemFromOwner (i) == item)
5179 return list.IndexOf (item);
5183 public virtual int IndexOfKey (string key)
5185 if (key == null || key.Length == 0)
5188 for (int i = 0; i < Count; i++) {
5189 ListViewItem lvi = this [i];
5190 if (String.Compare (key, lvi.Name, true) == 0)
5198 public ListViewItem Insert (int index, ListViewItem item)
5200 if (index < 0 || index > list.Count)
5201 throw new ArgumentOutOfRangeException ("index");
5204 if (owner != null && owner.VirtualMode)
5205 throw new InvalidOperationException ();
5208 if (list.Contains (item))
5209 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5211 if (item.ListView != null && item.ListView != owner)
5212 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");
5214 if (is_main_collection)
5218 if (item.Group != null)
5219 item.Group.Items.Remove (item);
5221 item.SetGroup (group);
5225 list.Insert (index, item);
5227 if (is_main_collection || item.ListView != null)
5228 CollectionChanged (true);
5233 public ListViewItem Insert (int index, string text)
5235 return this.Insert (index, new ListViewItem (text));
5238 public ListViewItem Insert (int index, string text, int imageIndex)
5240 return this.Insert (index, new ListViewItem (text, imageIndex));
5244 public ListViewItem Insert (int index, string text, string imageKey)
5246 ListViewItem lvi = new ListViewItem (text, imageKey);
5247 return Insert (index, lvi);
5250 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5252 ListViewItem lvi = new ListViewItem (text, imageIndex);
5254 return Insert (index, lvi);
5257 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5259 ListViewItem lvi = new ListViewItem (text, imageKey);
5261 return Insert (index, lvi);
5265 public virtual void Remove (ListViewItem item)
5268 if (owner != null && owner.VirtualMode)
5269 throw new InvalidOperationException ();
5272 int idx = list.IndexOf (item);
5277 public virtual void RemoveAt (int index)
5279 if (index < 0 || index >= Count)
5280 throw new ArgumentOutOfRangeException ("index");
5283 if (owner != null && owner.VirtualMode)
5284 throw new InvalidOperationException ();
5287 ListViewItem item = (ListViewItem) list [index];
5289 bool selection_changed = false;
5290 if (is_main_collection && owner != null) {
5292 int display_index = item.DisplayIndex;
5293 if (item.Focused && display_index + 1 == Count) // Last item
5294 owner.SetFocusedItem (display_index == 0 ? -1 : display_index - 1);
5296 selection_changed = owner.SelectedIndices.Contains (index);
5297 owner.item_control.CancelEdit (item);
5300 list.RemoveAt (index);
5302 if (is_main_collection)
5306 item.SetGroup (null);
5309 CollectionChanged (false);
5310 if (selection_changed && owner != null)
5311 owner.OnSelectedIndexChanged (EventArgs.Empty);
5315 public virtual void RemoveByKey (string key)
5317 int idx = IndexOfKey (key);
5323 #endregion // Public Methods
5325 internal ListView Owner {
5335 internal ListViewGroup Group {
5345 void AddItem (ListViewItem value)
5347 if (list.Contains (value))
5348 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5350 if (value.ListView != null && value.ListView != owner)
5351 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");
5352 if (is_main_collection)
5353 value.Owner = owner;
5356 if (value.Group != null)
5357 value.Group.Items.Remove (value);
5359 value.SetGroup (group);
5366 void CollectionChanged (bool sort)
5368 if (owner != null) {
5373 owner.Redraw (true);
5378 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5380 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5382 owner.OnRetrieveVirtualItem (args);
5383 ListViewItem retval = args.Item;
5384 retval.Owner = owner;
5385 retval.DisplayIndex = displayIndex;
5391 internal event CollectionChangedHandler Changed;
5393 internal void Sort (IComparer comparer)
5395 list.Sort (comparer);
5399 internal void OnChange ()
5401 if (Changed != null)
5404 } // ListViewItemCollection
5407 // In normal mode, the selection information resides in the Items,
5408 // making SelectedIndexCollection.List read-only
5410 // In virtual mode, SelectedIndexCollection directly saves the selection
5411 // information, instead of getting it from Items, making List read-and-write
5413 [ListBindable (false)]
5415 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5417 private readonly ListView owner;
5418 private ArrayList list;
5420 #region Public Constructor
5421 public SelectedIndexCollection (ListView owner)
5424 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5426 #endregion // Public Constructor
5428 #region Public Properties
5432 if (!owner.IsHandleCreated)
5439 public bool IsReadOnly {
5449 public int this [int index] {
5451 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5452 throw new ArgumentOutOfRangeException ("index");
5454 return (int) List [index];
5458 bool ICollection.IsSynchronized {
5459 get { return false; }
5462 object ICollection.SyncRoot {
5463 get { return this; }
5466 bool IList.IsFixedSize {
5476 object IList.this [int index] {
5477 get { return this [index]; }
5478 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5480 #endregion // Public Properties
5482 #region Public Methods
5484 public int Add (int itemIndex)
5486 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5487 throw new ArgumentOutOfRangeException ("index");
5489 if (owner.virtual_mode && !owner.IsHandleCreated)
5492 owner.Items [itemIndex].Selected = true;
5494 if (!owner.IsHandleCreated)
5508 if (!owner.IsHandleCreated)
5511 int [] indexes = (int []) List.ToArray (typeof (int));
5512 foreach (int index in indexes)
5513 owner.Items [index].Selected = false;
5516 public bool Contains (int selectedIndex)
5518 return IndexOf (selectedIndex) != -1;
5521 public void CopyTo (Array dest, int index)
5523 List.CopyTo (dest, index);
5526 public IEnumerator GetEnumerator ()
5528 return List.GetEnumerator ();
5531 int IList.Add (object value)
5533 throw new NotSupportedException ("Add operation is not supported.");
5541 bool IList.Contains (object selectedIndex)
5543 if (!(selectedIndex is int))
5545 return Contains ((int) selectedIndex);
5548 int IList.IndexOf (object selectedIndex)
5550 if (!(selectedIndex is int))
5552 return IndexOf ((int) selectedIndex);
5555 void IList.Insert (int index, object value)
5557 throw new NotSupportedException ("Insert operation is not supported.");
5560 void IList.Remove (object value)
5562 throw new NotSupportedException ("Remove operation is not supported.");
5565 void IList.RemoveAt (int index)
5567 throw new NotSupportedException ("RemoveAt operation is not supported.");
5570 public int IndexOf (int selectedIndex)
5572 if (!owner.IsHandleCreated)
5575 return List.IndexOf (selectedIndex);
5579 public void Remove (int itemIndex)
5581 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5582 throw new ArgumentOutOfRangeException ("itemIndex");
5584 owner.Items [itemIndex].Selected = false;
5587 #endregion // Public Methods
5589 internal ArrayList List {
5592 list = new ArrayList ();
5594 if (!owner.VirtualMode)
5596 for (int i = 0; i < owner.Items.Count; i++) {
5597 if (owner.Items [i].Selected)
5605 internal void Reset ()
5607 // force re-population of list
5611 private void ItemsCollection_Changed ()
5617 internal void RemoveIndex (int index)
5619 int idx = List.BinarySearch (index);
5621 List.RemoveAt (idx);
5624 // actually store index in the collection
5625 // also, keep the collection sorted, as .Net does
5626 internal void InsertIndex (int index)
5629 int iMax = List.Count - 1;
5630 while (iMin <= iMax) {
5631 int iMid = (iMin + iMax) / 2;
5632 int current_index = (int) List [iMid];
5634 if (current_index == index)
5635 return; // Already added
5636 if (current_index > index)
5642 List.Insert (iMin, index);
5646 } // SelectedIndexCollection
5649 [ListBindable (false)]
5651 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5653 private readonly ListView owner;
5655 #region Public Constructor
5656 public SelectedListViewItemCollection (ListView owner)
5660 #endregion // Public Constructor
5662 #region Public Properties
5666 return owner.SelectedIndices.Count;
5670 public bool IsReadOnly {
5671 get { return true; }
5674 public ListViewItem this [int index] {
5676 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5677 throw new ArgumentOutOfRangeException ("index");
5679 int item_index = owner.SelectedIndices [index];
5680 return owner.Items [item_index];
5685 public virtual ListViewItem this [string key] {
5687 int idx = IndexOfKey (key);
5696 bool ICollection.IsSynchronized {
5697 get { return false; }
5700 object ICollection.SyncRoot {
5701 get { return this; }
5704 bool IList.IsFixedSize {
5705 get { return true; }
5708 object IList.this [int index] {
5709 get { return this [index]; }
5710 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5712 #endregion // Public Properties
5714 #region Public Methods
5715 public void Clear ()
5717 owner.SelectedIndices.Clear ();
5720 public bool Contains (ListViewItem item)
5722 return IndexOf (item) != -1;
5726 public virtual bool ContainsKey (string key)
5728 return IndexOfKey (key) != -1;
5732 public void CopyTo (Array dest, int index)
5734 if (!owner.IsHandleCreated)
5736 if (index > Count) // Throws ArgumentException instead of IOOR exception
5737 throw new ArgumentException ("index");
5739 for (int i = 0; i < Count; i++)
5740 dest.SetValue (this [i], index++);
5743 public IEnumerator GetEnumerator ()
5745 if (!owner.IsHandleCreated)
5746 return (new ListViewItem [0]).GetEnumerator ();
5748 ListViewItem [] items = new ListViewItem [Count];
5749 for (int i = 0; i < Count; i++)
5750 items [i] = this [i];
5752 return items.GetEnumerator ();
5755 int IList.Add (object value)
5757 throw new NotSupportedException ("Add operation is not supported.");
5760 bool IList.Contains (object item)
5762 if (!(item is ListViewItem))
5764 return Contains ((ListViewItem) item);
5767 int IList.IndexOf (object item)
5769 if (!(item is ListViewItem))
5771 return IndexOf ((ListViewItem) item);
5774 void IList.Insert (int index, object value)
5776 throw new NotSupportedException ("Insert operation is not supported.");
5779 void IList.Remove (object value)
5781 throw new NotSupportedException ("Remove operation is not supported.");
5784 void IList.RemoveAt (int index)
5786 throw new NotSupportedException ("RemoveAt operation is not supported.");
5789 public int IndexOf (ListViewItem item)
5791 if (!owner.IsHandleCreated)
5794 for (int i = 0; i < Count; i++)
5795 if (this [i] == item)
5802 public virtual int IndexOfKey (string key)
5804 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5807 for (int i = 0; i < Count; i++) {
5808 ListViewItem item = this [i];
5809 if (String.Compare (item.Name, key, true) == 0)
5816 #endregion // Public Methods
5818 } // SelectedListViewItemCollection
5820 internal delegate void CollectionChangedHandler ();
5822 struct ItemMatrixLocation
5827 public ItemMatrixLocation (int row, int col)
5854 #endregion // Subclasses
5856 protected override void OnResize (EventArgs e)
5861 protected override void OnMouseLeave (EventArgs e)
5863 base.OnMouseLeave (e);
5867 // ColumnReorder event
5869 static object ColumnReorderedEvent = new object ();
5870 public event ColumnReorderedEventHandler ColumnReordered {
5871 add { Events.AddHandler (ColumnReorderedEvent, value); }
5872 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5875 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5877 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5884 // ColumnWidthChanged
5886 static object ColumnWidthChangedEvent = new object ();
5887 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5888 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5889 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5892 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5894 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5899 void RaiseColumnWidthChanged (int resize_column)
5901 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5903 OnColumnWidthChanged (n);
5907 // ColumnWidthChanging
5909 static object ColumnWidthChangingEvent = new object ();
5910 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5911 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5912 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5915 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5917 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5923 // 2.0 profile based implementation
5925 bool CanProceedWithResize (ColumnHeader col, int width)
5927 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5931 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5932 cwceh (this, changing);
5933 return !changing.Cancel;
5937 // 1.0 profile based implementation
5939 bool CanProceedWithResize (ColumnHeader col, int width)
5944 void RaiseColumnWidthChanged (int resize_column)