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 prev_focused = FocusedItem == null ? 0 : FocusedItem.DisplayIndex;
2086 int start = prev_focused + 1 < Items.Count ? prev_focused + 1 : 0;
2088 ListViewItem item = FindItemWithText (keysearch_text, false, start, true, true);
2089 if (item != null && prev_focused != item.DisplayIndex) {
2090 selected_indices.Clear ();
2092 SetFocusedItem (item.DisplayIndex);
2093 item.Selected = true;
2094 EnsureVisible (GetItemIndex (item.DisplayIndex));
2100 int GetAdjustedIndex (Keys key)
2104 if (View == View.Details) {
2107 result = FocusedItem.DisplayIndex - 1;
2110 result = FocusedItem.DisplayIndex + 1;
2111 if (result == items.Count)
2115 int last_index = LastVisibleIndex;
2116 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2117 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2119 if (FocusedItem.DisplayIndex == last_index) {
2120 if (FocusedItem.DisplayIndex < Items.Count - 1) {
2121 int page_size = item_control.Height / ItemSize.Height - 1;
2122 result = FocusedItem.DisplayIndex + page_size - 1;
2123 if (result >= Items.Count)
2124 result = Items.Count - 1;
2127 result = last_index;
2130 int first_index = FirstVisibleIndex;
2131 if (GetItemLocation (first_index).Y < 0)
2133 if (FocusedItem.DisplayIndex == first_index) {
2134 if (first_index > 0) {
2135 int page_size = item_control.Height / ItemSize.Height - 1;
2136 result = first_index - page_size + 1;
2141 result = first_index;
2147 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.DisplayIndex];
2148 int row = item_matrix_location.Row;
2149 int col = item_matrix_location.Col;
2151 int adjusted_index = -1;
2157 adjusted_index = item_index_matrix [row, col - 1];
2161 if (col == (cols - 1))
2163 while (item_index_matrix [row, col + 1] == 0) {
2168 adjusted_index = item_index_matrix [row, col + 1];
2174 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2179 adjusted_index = item_index_matrix [row - 1, col];
2183 if (row == (rows - 1) || row == Items.Count - 1)
2185 while (item_index_matrix [row + 1, col] == 0) {
2190 adjusted_index = item_index_matrix [row + 1, col];
2197 return items [adjusted_index].DisplayIndex;
2200 ListViewItem selection_start;
2202 private bool SelectItems (ArrayList sel_items)
2204 bool changed = false;
2205 foreach (ListViewItem item in SelectedItems)
2206 if (!sel_items.Contains (item)) {
2207 item.Selected = false;
2210 foreach (ListViewItem item in sel_items)
2211 if (!item.Selected) {
2212 item.Selected = true;
2218 private void UpdateMultiSelection (int index, bool reselect)
2220 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2221 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2222 ListViewItem item = GetItemAtDisplayIndex (index);
2224 if (shift_pressed && selection_start != null) {
2225 ArrayList list = new ArrayList ();
2226 int start_index = selection_start.DisplayIndex;
2227 int start = Math.Min (start_index, index);
2228 int end = Math.Max (start_index, index);
2229 if (View == View.Details) {
2230 for (int i = start; i <= end; i++)
2231 list.Add (GetItemAtDisplayIndex (i));
2233 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2234 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2235 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2236 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2237 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2238 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2240 for (int i = 0; i < items.Count; i++) {
2241 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2243 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2244 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2245 list.Add (GetItemAtDisplayIndex (i));
2249 } else if (ctrl_pressed) {
2250 item.Selected = !item.Selected;
2251 selection_start = item;
2254 // do not unselect, and reselect the item
2255 foreach (int itemIndex in SelectedIndices) {
2256 if (index == itemIndex)
2258 items [itemIndex].Selected = false;
2261 SelectedItems.Clear ();
2262 item.Selected = true;
2264 selection_start = item;
2268 internal override bool InternalPreProcessMessage (ref Message msg)
2270 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2271 Keys key_data = (Keys)msg.WParam.ToInt32();
2273 HandleNavKeys (key_data);
2276 return base.InternalPreProcessMessage (ref msg);
2279 bool HandleNavKeys (Keys key_data)
2281 if (Items.Count == 0 || !item_control.Visible)
2284 if (FocusedItem == null)
2289 SelectIndex (Items.Count - 1);
2302 SelectIndex (GetAdjustedIndex (key_data));
2306 ToggleItemsCheckState ();
2309 if (selected_indices.Count > 0)
2310 OnItemActivate (EventArgs.Empty);
2320 void ToggleItemsCheckState ()
2325 // Don't modify check state if StateImageList has less than 2 elements
2326 if (StateImageList != null && StateImageList.Images.Count < 2)
2329 if (SelectedIndices.Count > 0) {
2330 for (int i = 0; i < SelectedIndices.Count; i++) {
2331 ListViewItem item = Items [SelectedIndices [i]];
2332 item.Checked = !item.Checked;
2337 if (FocusedItem != null) {
2338 FocusedItem.Checked = !FocusedItem.Checked;
2339 SelectIndex (FocusedItem.Index);
2343 void SelectIndex (int display_index)
2345 if (display_index == -1)
2349 UpdateMultiSelection (display_index, true);
2350 else if (!GetItemAtDisplayIndex (display_index).Selected)
2351 GetItemAtDisplayIndex (display_index).Selected = true;
2353 SetFocusedItem (display_index);
2354 EnsureVisible (GetItemIndex (display_index)); // Index in Items collection, not display index
2357 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2359 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2362 if (ke.Alt || ke.Control)
2365 ke.Handled = KeySearchString (ke);
2368 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2370 Point loc = PointToClient (Control.MousePosition);
2371 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2374 internal class ItemControl : Control {
2377 ListViewItem clicked_item;
2378 ListViewItem last_clicked_item;
2379 bool hover_processed = false;
2380 bool checking = false;
2381 ListViewItem prev_hovered_item;
2383 ListViewItem prev_tooltip_item;
2386 Point drag_begin = new Point (-1, -1);
2387 internal int dragged_item_index = -1;
2389 ListViewLabelEditTextBox edit_text_box;
2390 internal ListViewItem edit_item;
2391 LabelEditEventArgs edit_args;
2393 public ItemControl (ListView owner)
2396 DoubleClick += new EventHandler(ItemsDoubleClick);
2397 MouseDown += new MouseEventHandler(ItemsMouseDown);
2398 MouseMove += new MouseEventHandler(ItemsMouseMove);
2399 MouseHover += new EventHandler(ItemsMouseHover);
2400 MouseUp += new MouseEventHandler(ItemsMouseUp);
2403 void ItemsDoubleClick (object sender, EventArgs e)
2405 if (owner.activation == ItemActivation.Standard)
2406 owner.OnItemActivate (EventArgs.Empty);
2416 BoxSelect box_select_mode = BoxSelect.None;
2417 IList prev_selection;
2418 Point box_select_start;
2420 Rectangle box_select_rect;
2421 internal Rectangle BoxSelectRectangle {
2422 get { return box_select_rect; }
2424 if (box_select_rect == value)
2427 InvalidateBoxSelectRect ();
2428 box_select_rect = value;
2429 InvalidateBoxSelectRect ();
2433 void InvalidateBoxSelectRect ()
2435 if (BoxSelectRectangle.Size.IsEmpty)
2438 Rectangle edge = BoxSelectRectangle;
2444 edge.Y = BoxSelectRectangle.Bottom - 1;
2446 edge.Y = BoxSelectRectangle.Y - 1;
2448 edge.Height = BoxSelectRectangle.Height + 2;
2450 edge.X = BoxSelectRectangle.Right - 1;
2454 private Rectangle CalculateBoxSelectRectangle (Point pt)
2456 int left = Math.Min (box_select_start.X, pt.X);
2457 int right = Math.Max (box_select_start.X, pt.X);
2458 int top = Math.Min (box_select_start.Y, pt.Y);
2459 int bottom = Math.Max (box_select_start.Y, pt.Y);
2460 return Rectangle.FromLTRB (left, top, right, bottom);
2463 bool BoxIntersectsItem (int index)
2465 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2466 if (owner.View != View.Details) {
2468 r.Y += r.Height / 4;
2472 return BoxSelectRectangle.IntersectsWith (r);
2475 bool BoxIntersectsText (int index)
2477 Rectangle r = owner.GetItemAtDisplayIndex (index).TextBounds;
2478 return BoxSelectRectangle.IntersectsWith (r);
2481 ArrayList BoxSelectedItems {
2483 ArrayList result = new ArrayList ();
2484 for (int i = 0; i < owner.Items.Count; i++) {
2487 // Can't iterate over specific items properties in virtualmode
2488 if (owner.View == View.Details && !owner.FullRowSelect && !owner.VirtualMode)
2490 if (owner.View == View.Details && !owner.FullRowSelect)
2492 intersects = BoxIntersectsText (i);
2494 intersects = BoxIntersectsItem (i);
2497 result.Add (owner.GetItemAtDisplayIndex (i));
2503 private bool PerformBoxSelection (Point pt)
2505 if (box_select_mode == BoxSelect.None)
2508 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2510 ArrayList box_items = BoxSelectedItems;
2514 switch (box_select_mode) {
2516 case BoxSelect.Normal:
2520 case BoxSelect.Control:
2521 items = new ArrayList ();
2522 foreach (int index in prev_selection)
2523 if (!box_items.Contains (owner.Items [index]))
2524 items.Add (owner.Items [index]);
2525 foreach (ListViewItem item in box_items)
2526 if (!prev_selection.Contains (item.Index))
2530 case BoxSelect.Shift:
2532 foreach (ListViewItem item in box_items)
2533 prev_selection.Remove (item.Index);
2534 foreach (int index in prev_selection)
2535 items.Add (owner.Items [index]);
2539 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2543 owner.SelectItems (items);
2549 private void ItemsMouseDown (object sender, MouseEventArgs me)
2551 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2552 if (owner.items.Count == 0)
2555 bool box_selecting = false;
2556 Size item_size = owner.ItemSize;
2557 Point pt = new Point (me.X, me.Y);
2558 for (int i = 0; i < owner.items.Count; i++) {
2559 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2560 if (!item_rect.Contains (pt))
2563 // Actual item in 'i' position
2564 ListViewItem item = owner.GetItemAtDisplayIndex (i);
2566 if (item.CheckRectReal.Contains (pt)) {
2567 // Don't modify check state if we have only one image
2568 // and if we are in 1.1 profile only take into account
2570 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2577 // Generate an extra ItemCheck event when we got two clicks
2578 // (Match weird .Net behaviour)
2580 item.Checked = !item.Checked;
2582 item.Checked = !item.Checked;
2587 if (owner.View == View.Details) {
2588 bool over_text = item.TextBounds.Contains (pt);
2589 if (owner.FullRowSelect) {
2590 clicked_item = owner.items [i];
2591 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2592 if (!over_text && over_item_column && owner.MultiSelect)
2593 box_selecting = true;
2594 } else if (over_text)
2595 clicked_item = item;
2597 owner.SetFocusedItem (i);
2599 clicked_item = item;
2605 if (clicked_item != null) {
2606 bool changed = !clicked_item.Selected;
2607 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2608 owner.SetFocusedItem (clicked_item.DisplayIndex);
2610 if (owner.MultiSelect) {
2611 bool reselect = (!owner.LabelEdit || changed);
2612 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2613 owner.UpdateMultiSelection (clicked_item.DisplayIndex, reselect);
2615 clicked_item.Selected = true;
2619 if (owner.VirtualMode && changed) {
2620 // Broken event - It's not fired from Item.Selected also
2621 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2622 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2624 owner.OnVirtualItemsSelectionRangeChanged (args);
2627 // Report clicks only if the item was clicked. On MS the
2628 // clicks are only raised if you click an item
2630 if (me.Clicks > 1) {
2631 if (owner.CheckBoxes)
2632 clicked_item.Checked = !clicked_item.Checked;
2633 } else if (me.Clicks == 1) {
2634 if (owner.LabelEdit && !changed)
2635 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2638 if (owner.MultiSelect)
2639 box_selecting = true;
2640 else if (owner.SelectedItems.Count > 0)
2641 owner.SelectedItems.Clear ();
2644 if (box_selecting) {
2645 Keys mods = XplatUI.State.ModifierKeys;
2646 if ((mods & Keys.Shift) != 0)
2647 box_select_mode = BoxSelect.Shift;
2648 else if ((mods & Keys.Control) != 0)
2649 box_select_mode = BoxSelect.Control;
2651 box_select_mode = BoxSelect.Normal;
2652 box_select_start = pt;
2653 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2657 private void ItemsMouseMove (object sender, MouseEventArgs me)
2659 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2661 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2665 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2666 !hover_processed && owner.Activation != ItemActivation.OneClick
2668 && !owner.ShowItemToolTips
2673 Point pt = PointToClient (Control.MousePosition);
2674 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2676 if (hover_processed && item != null && item != prev_hovered_item) {
2677 hover_processed = false;
2678 XplatUI.ResetMouseHover (Handle);
2681 // Need to invalidate the item in HotTracking to show/hide the underline style
2682 if (owner.Activation == ItemActivation.OneClick) {
2683 if (item == null && owner.HotItemIndex != -1) {
2685 if (owner.HotTracking)
2686 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2689 Cursor = Cursors.Default;
2690 owner.HotItemIndex = -1;
2691 } else if (item != null && owner.HotItemIndex == -1) {
2693 if (owner.HotTracking)
2694 Invalidate (item.Bounds);
2697 Cursor = Cursors.Hand;
2698 owner.HotItemIndex = item.Index;
2702 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2703 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2705 drag_begin = new Point (me.X, me.Y);
2706 dragged_item_index = item.Index;
2710 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2711 if (!r.Contains (me.X, me.Y)) {
2712 ListViewItem dragged_item = owner.items [dragged_item_index];
2713 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2715 drag_begin = new Point (-1, -1);
2716 dragged_item_index = -1;
2722 if (owner.ShowItemToolTips) {
2724 owner.item_tooltip.Active = false;
2725 prev_tooltip_item = null;
2726 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2727 owner.item_tooltip.Active = true;
2728 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2729 prev_tooltip_item = item;
2736 private void ItemsMouseHover (object sender, EventArgs e)
2738 if (owner.hover_pending) {
2739 owner.OnMouseHover (e);
2740 owner.hover_pending = false;
2746 hover_processed = true;
2747 Point pt = PointToClient (Control.MousePosition);
2748 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2752 prev_hovered_item = item;
2754 if (owner.HoverSelection) {
2755 if (owner.MultiSelect)
2756 owner.UpdateMultiSelection (item.Index, true);
2758 item.Selected = true;
2760 owner.SetFocusedItem (item.DisplayIndex);
2761 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2765 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2769 void HandleClicks (MouseEventArgs me)
2771 // if the click is not on an item,
2772 // clicks remains as 0
2775 owner.OnDoubleClick (EventArgs.Empty);
2776 } else if (clicks == 1) {
2777 owner.OnClick (EventArgs.Empty);
2779 owner.OnDoubleClick (EventArgs.Empty);
2780 owner.OnMouseDoubleClick (me);
2781 } else if (clicks == 1) {
2782 owner.OnClick (EventArgs.Empty);
2783 owner.OnMouseClick (me);
2790 private void ItemsMouseUp (object sender, MouseEventArgs me)
2792 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2793 HandleClicks (owner_me);
2796 if (owner.Items.Count == 0) {
2798 owner.OnMouseUp (owner_me);
2802 Point pt = new Point (me.X, me.Y);
2804 Rectangle rect = Rectangle.Empty;
2805 if (clicked_item != null) {
2806 if (owner.view == View.Details && !owner.full_row_select)
2807 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2809 rect = clicked_item.Bounds;
2811 if (rect.Contains (pt)) {
2812 switch (owner.activation) {
2813 case ItemActivation.OneClick:
2814 owner.OnItemActivate (EventArgs.Empty);
2817 case ItemActivation.TwoClick:
2818 if (last_clicked_item == clicked_item) {
2819 owner.OnItemActivate (EventArgs.Empty);
2820 last_clicked_item = null;
2822 last_clicked_item = clicked_item;
2825 // DoubleClick activation is handled in another handler
2829 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2830 // Need this to clean up background clicks
2831 owner.SelectedItems.Clear ();
2835 owner.OnMouseUp (owner_me);
2838 private void ResetMouseState ()
2840 clicked_item = null;
2841 box_select_start = Point.Empty;
2842 BoxSelectRectangle = Rectangle.Empty;
2843 prev_selection = null;
2844 box_select_mode = BoxSelect.None;
2847 // Clean these bits in case the mouse buttons were
2848 // released before firing ItemDrag
2849 dragged_item_index = -1;
2850 drag_begin = new Point (-1, -1);
2853 private void LabelEditFinished (object sender, EventArgs e)
2855 EndEdit (edit_item);
2858 private void LabelEditCancelled (object sender, EventArgs e)
2860 edit_args.SetLabel (null);
2861 EndEdit (edit_item);
2864 private void LabelTextChanged (object sender, EventArgs e)
2866 if (edit_args != null)
2867 edit_args.SetLabel (edit_text_box.Text);
2870 internal void BeginEdit (ListViewItem item)
2872 if (edit_item != null)
2873 EndEdit (edit_item);
2875 if (edit_text_box == null) {
2876 edit_text_box = new ListViewLabelEditTextBox ();
2877 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2878 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2879 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2880 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2881 edit_text_box.Visible = false;
2882 Controls.Add (edit_text_box);
2885 item.EnsureVisible();
2887 edit_text_box.Reset ();
2889 switch (owner.view) {
2891 case View.SmallIcon:
2893 edit_text_box.TextAlign = HorizontalAlignment.Left;
2894 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2895 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2896 edit_text_box.Width = (int)sizef.Width + 4;
2897 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2898 edit_text_box.WordWrap = false;
2899 edit_text_box.Multiline = false;
2901 case View.LargeIcon:
2902 edit_text_box.TextAlign = HorizontalAlignment.Center;
2903 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2904 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2905 edit_text_box.Width = (int)sizef.Width + 4;
2906 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2907 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2908 edit_text_box.WordWrap = true;
2909 edit_text_box.Multiline = true;
2915 edit_text_box.Text = item.Text;
2916 edit_text_box.Font = item.Font;
2917 edit_text_box.Visible = true;
2918 edit_text_box.Focus ();
2919 edit_text_box.SelectAll ();
2921 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2922 owner.OnBeforeLabelEdit (edit_args);
2924 if (edit_args.CancelEdit)
2928 internal void CancelEdit (ListViewItem item)
2930 // do nothing if there's no item being edited, or if the
2931 // item being edited is not the one passed in
2932 if (edit_item == null || edit_item != item)
2935 edit_args.SetLabel (null);
2939 internal void EndEdit (ListViewItem item)
2941 // do nothing if there's no item being edited, or if the
2942 // item being edited is not the one passed in
2943 if (edit_item == null || edit_item != item)
2946 if (edit_text_box != null) {
2947 if (edit_text_box.Visible)
2948 edit_text_box.Visible = false;
2949 // ensure listview gets focus
2953 // Same as TreeView.EndEdit: need to have focus in synch
2954 Application.DoEvents ();
2957 // Create a new instance, since we could get a call to BeginEdit
2958 // from the handler and have fields out of synch
2960 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2963 owner.OnAfterLabelEdit (args);
2964 if (!args.CancelEdit && args.Label != null)
2965 item.Text = args.Label;
2968 internal override void OnPaintInternal (PaintEventArgs pe)
2970 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2973 protected override void WndProc (ref Message m)
2975 switch ((Msg)m.Msg) {
2976 case Msg.WM_KILLFOCUS:
2977 owner.Select (false, true);
2979 case Msg.WM_SETFOCUS:
2980 owner.Select (false, true);
2982 case Msg.WM_LBUTTONDOWN:
2984 owner.Select (false, true);
2986 case Msg.WM_RBUTTONDOWN:
2988 owner.Select (false, true);
2993 base.WndProc (ref m);
2997 internal class ListViewLabelEditTextBox : TextBox
3002 int max_height = -1;
3003 int min_height = -1;
3005 int old_number_lines = 1;
3007 SizeF text_size_one_char;
3009 public ListViewLabelEditTextBox ()
3011 min_height = DefaultSize.Height;
3012 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3015 public int MaxWidth {
3017 if (value < min_width)
3018 max_width = min_width;
3024 public int MaxHeight {
3026 if (value < min_height)
3027 max_height = min_height;
3033 public new int Width {
3043 public override Font Font {
3049 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3053 protected override void OnTextChanged (EventArgs e)
3055 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3057 int new_width = (int)text_size.Width + 8;
3060 ResizeTextBoxWidth (new_width);
3062 if (Width != max_width)
3063 ResizeTextBoxWidth (new_width);
3065 int number_lines = Lines.Length;
3067 if (number_lines != old_number_lines) {
3068 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3069 old_number_lines = number_lines;
3071 ResizeTextBoxHeight (new_height);
3075 base.OnTextChanged (e);
3078 protected override bool IsInputKey (Keys key_data)
3080 if ((key_data & Keys.Alt) == 0) {
3081 switch (key_data & Keys.KeyCode) {
3088 return base.IsInputKey (key_data);
3091 protected override void OnKeyDown (KeyEventArgs e)
3096 switch (e.KeyCode) {
3100 OnEditingFinished (e);
3105 OnEditingCancelled (e);
3110 protected override void OnLostFocus (EventArgs e)
3113 OnEditingFinished (e);
3117 protected void OnEditingCancelled (EventArgs e)
3119 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3124 protected void OnEditingFinished (EventArgs e)
3126 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3131 private void ResizeTextBoxWidth (int new_width)
3133 if (new_width > max_width)
3134 base.Width = max_width;
3136 if (new_width >= min_width)
3137 base.Width = new_width;
3139 base.Width = min_width;
3142 private void ResizeTextBoxHeight (int new_height)
3144 if (new_height > max_height)
3145 base.Height = max_height;
3147 if (new_height >= min_height)
3148 base.Height = new_height;
3150 base.Height = min_height;
3153 public void Reset ()
3160 old_number_lines = 1;
3162 Text = String.Empty;
3167 static object EditingCancelledEvent = new object ();
3168 public event EventHandler EditingCancelled {
3169 add { Events.AddHandler (EditingCancelledEvent, value); }
3170 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3173 static object EditingFinishedEvent = new object ();
3174 public event EventHandler EditingFinished {
3175 add { Events.AddHandler (EditingFinishedEvent, value); }
3176 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3180 internal override void OnPaintInternal (PaintEventArgs pe)
3185 CalculateScrollBars ();
3188 void FocusChanged (object o, EventArgs args)
3190 if (Items.Count == 0)
3193 if (FocusedItem == null)
3196 ListViewItem focused_item = FocusedItem;
3198 if (focused_item.ListView != null) {
3199 item_control.Invalidate (focused_item.Bounds);
3200 focused_item.Layout ();
3201 item_control.Invalidate (focused_item.Bounds);
3205 private void ListView_MouseEnter (object sender, EventArgs args)
3207 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3210 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3212 if (Items.Count == 0)
3215 int lines = me.Delta / 120;
3222 case View.SmallIcon:
3223 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3225 case View.LargeIcon:
3226 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3229 Scroll (h_scroll, -ItemSize.Width * lines);
3233 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3239 private void ListView_SizeChanged (object sender, EventArgs e)
3241 CalculateListView (alignment);
3244 private void SetFocusedItem (int display_index)
3246 if (display_index != -1)
3247 GetItemAtDisplayIndex (display_index).Focused = true;
3248 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3249 GetItemAtDisplayIndex (focused_item_index).Focused = false;
3251 focused_item_index = display_index;
3254 private void HorizontalScroller (object sender, EventArgs e)
3256 item_control.EndEdit (item_control.edit_item);
3258 // Avoid unnecessary flickering, when button is
3259 // kept pressed at the end
3260 if (h_marker != h_scroll.Value) {
3262 int pixels = h_marker - h_scroll.Value;
3264 h_marker = h_scroll.Value;
3265 if (header_control.Visible)
3266 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3268 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3272 private void VerticalScroller (object sender, EventArgs e)
3274 item_control.EndEdit (item_control.edit_item);
3276 // Avoid unnecessary flickering, when button is
3277 // kept pressed at the end
3278 if (v_marker != v_scroll.Value) {
3279 int pixels = v_marker - v_scroll.Value;
3280 Rectangle area = item_control.ClientRectangle;
3281 if (header_control.Visible) {
3282 area.Y += header_control.Height;
3283 area.Height -= header_control.Height;
3286 v_marker = v_scroll.Value;
3287 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3291 internal override bool IsInputCharInternal (char charCode)
3295 #endregion // Internal Methods Properties
3297 #region Protected Methods
3298 protected override void CreateHandle ()
3300 base.CreateHandle ();
3301 for (int i = 0; i < SelectedItems.Count; i++)
3302 OnSelectedIndexChanged (EventArgs.Empty);
3305 protected override void Dispose (bool disposing)
3308 h_scroll.Dispose ();
3309 v_scroll.Dispose ();
3311 large_image_list = null;
3312 small_image_list = null;
3313 state_image_list = null;
3315 foreach (ColumnHeader col in columns)
3316 col.SetListView (null);
3319 if (!virtual_mode) // In virtual mode we don't save the items
3321 foreach (ListViewItem item in items)
3325 base.Dispose (disposing);
3328 protected override bool IsInputKey (Keys keyData)
3345 return base.IsInputKey (keyData);
3348 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3350 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3356 protected override void OnBackgroundImageChanged (EventArgs e)
3358 item_control.BackgroundImage = BackgroundImage;
3359 base.OnBackgroundImageChanged (e);
3363 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3365 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3370 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3372 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3378 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3380 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3385 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3387 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3392 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3394 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3400 protected override void OnEnabledChanged (EventArgs e)
3402 base.OnEnabledChanged (e);
3406 protected override void OnFontChanged (EventArgs e)
3408 base.OnFontChanged (e);
3412 protected override void OnHandleCreated (EventArgs e)
3414 base.OnHandleCreated (e);
3415 CalculateListView (alignment);
3417 if (!virtual_mode) // Sorting is not allowed in virtual mode
3422 protected override void OnHandleDestroyed (EventArgs e)
3424 base.OnHandleDestroyed (e);
3427 protected virtual void OnItemActivate (EventArgs e)
3429 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3434 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3436 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3442 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3444 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3450 protected virtual void OnItemDrag (ItemDragEventArgs e)
3452 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3458 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3460 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3465 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3467 ListViewItemSelectionChangedEventHandler eh =
3468 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3473 protected override void OnMouseHover (EventArgs e)
3475 base.OnMouseHover (e);
3478 protected override void OnParentChanged (EventArgs e)
3480 base.OnParentChanged (e);
3484 protected virtual void OnSelectedIndexChanged (EventArgs e)
3486 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3491 protected override void OnSystemColorsChanged (EventArgs e)
3493 base.OnSystemColorsChanged (e);
3497 protected internal virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3499 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3504 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3506 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3511 [EditorBrowsable (EditorBrowsableState.Advanced)]
3512 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3514 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3519 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3521 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3526 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3528 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3529 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3535 protected void RealizeProperties ()
3540 protected void UpdateExtendedStyles ()
3545 bool refocusing = false;
3547 protected override void WndProc (ref Message m)
3549 switch ((Msg)m.Msg) {
3550 case Msg.WM_KILLFOCUS:
3551 Control receiver = Control.FromHandle (m.WParam);
3552 if (receiver == item_control) {
3558 case Msg.WM_SETFOCUS:
3568 base.WndProc (ref m);
3570 #endregion // Protected Methods
3572 #region Public Instance Methods
3573 public void ArrangeIcons ()
3575 ArrangeIcons (this.alignment);
3578 public void ArrangeIcons (ListViewAlignment value)
3580 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3581 if (view == View.LargeIcon || view == View.SmallIcon) {
3582 this.CalculateListView (value);
3583 // we have done the calculations already
3584 this.Redraw (false);
3589 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3591 if (columnIndex < 0 || columnIndex >= columns.Count)
3592 throw new ArgumentOutOfRangeException ("columnIndex");
3594 columns [columnIndex].AutoResize (headerAutoResize);
3597 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3600 foreach (ColumnHeader col in columns)
3601 col.AutoResize (headerAutoResize);
3606 public void BeginUpdate ()
3608 // flag to avoid painting
3612 public void Clear ()
3615 items.Clear (); // Redraw (true) called here
3618 public void EndUpdate ()
3620 // flag to avoid painting
3623 // probably, now we need a redraw with recalculations
3627 public void EnsureVisible (int index)
3629 if (index < 0 || index >= items.Count || scrollable == false)
3632 Rectangle view_rect = item_control.ClientRectangle;
3633 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3635 if (view_rect.Contains (bounds))
3638 if (View != View.Details) {
3639 if (bounds.Left < 0)
3640 h_scroll.Value += bounds.Left;
3641 else if (bounds.Right > view_rect.Right)
3642 h_scroll.Value += (bounds.Right - view_rect.Right);
3646 v_scroll.Value += bounds.Top;
3647 else if (bounds.Bottom > view_rect.Bottom)
3648 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3652 public ListViewItem FindItemWithText (string text)
3654 if (items.Count == 0)
3657 return FindItemWithText (text, true, 0, true);
3660 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3662 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true, false);
3665 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3667 return FindItemWithText (text, includeSubItemsInSearch, startIndex, isPrefixSearch, false);
3674 ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch, bool roundtrip)
3676 if (startIndex < 0 || startIndex >= items.Count)
3677 throw new ArgumentOutOfRangeException ("startIndex");
3680 throw new ArgumentNullException ("text");
3684 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3685 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3686 SearchDirectionHint.Down, startIndex);
3688 OnSearchForVirtualItem (args);
3689 int idx = args.Index;
3690 if (idx >= 0 && idx < virtual_list_size)
3699 ListViewItem lvi = items [i];
3701 if (isPrefixSearch) { // prefix search
3702 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (lvi.Text, text, CompareOptions.IgnoreCase))
3704 } else if (String.Compare (lvi.Text, text, true) == 0) // match
3707 if (i + 1 >= items.Count) {
3715 if (i == startIndex)
3719 // Subitems have a minor priority, so we have to do a second linear search
3720 // Also, we don't need to to a roundtrip search for them by now
3721 if (includeSubItemsInSearch) {
3722 for (i = startIndex; i < items.Count; i++) {
3723 ListViewItem lvi = items [i];
3724 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3725 if (isPrefixSearch) {
3726 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (sub_item.Text,
3727 text, CompareOptions.IgnoreCase))
3729 } else if (String.Compare (sub_item.Text, text, true) == 0)
3738 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3740 return FindNearestItem (searchDirection, new Point (x, y));
3743 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3745 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3746 throw new ArgumentOutOfRangeException ("searchDirection");
3748 if (view != View.LargeIcon && view != View.SmallIcon)
3749 throw new InvalidOperationException ();
3752 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3753 false, false, String.Empty, point,
3756 OnSearchForVirtualItem (args);
3757 int idx = args.Index;
3758 if (idx >= 0 && idx < virtual_list_size)
3764 ListViewItem item = null;
3765 int min_dist = Int32.MaxValue;
3768 // It looks like .Net does a previous adjustment
3771 case SearchDirectionHint.Up:
3772 point.Y -= item_size.Height;
3774 case SearchDirectionHint.Down:
3775 point.Y += item_size.Height;
3777 case SearchDirectionHint.Left:
3778 point.X -= item_size.Width;
3780 case SearchDirectionHint.Right:
3781 point.X += item_size.Width;
3785 for (int i = 0; i < items.Count; i++) {
3786 Point item_loc = GetItemLocation (i);
3788 if (dir == SearchDirectionHint.Up) {
3789 if (point.Y < item_loc.Y)
3791 } else if (dir == SearchDirectionHint.Down) {
3792 if (point.Y > item_loc.Y)
3794 } else if (dir == SearchDirectionHint.Left) {
3795 if (point.X < item_loc.X)
3797 } else if (dir == SearchDirectionHint.Right) {
3798 if (point.X > item_loc.X)
3802 int x_dist = point.X - item_loc.X;
3803 int y_dist = point.Y - item_loc.Y;
3805 int dist = x_dist * x_dist + y_dist * y_dist;
3806 if (dist < min_dist) {
3816 public ListViewItem GetItemAt (int x, int y)
3818 Size item_size = ItemSize;
3819 for (int i = 0; i < items.Count; i++) {
3820 Point item_location = GetItemLocation (i);
3821 Rectangle item_rect = new Rectangle (item_location, item_size);
3822 if (item_rect.Contains (x, y))
3829 public Rectangle GetItemRect (int index)
3831 return GetItemRect (index, ItemBoundsPortion.Entire);
3834 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3836 if (index < 0 || index >= items.Count)
3837 throw new IndexOutOfRangeException ("index");
3839 return items [index].GetBounds (portion);
3843 public ListViewHitTestInfo HitTest (Point point)
3845 return HitTest (point.X, point.Y);
3848 public ListViewHitTestInfo HitTest (int x, int y)
3851 throw new ArgumentOutOfRangeException ("x");
3853 throw new ArgumentOutOfRangeException ("y");
3855 ListViewItem item = GetItemAt (x, y);
3857 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3859 ListViewHitTestLocations locations = 0;
3860 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3861 locations |= ListViewHitTestLocations.Label;
3862 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3863 locations |= ListViewHitTestLocations.Image;
3864 else if (item.CheckRectReal.Contains (x, y))
3865 locations |= ListViewHitTestLocations.StateImage;
3867 ListViewItem.ListViewSubItem subitem = null;
3868 if (view == View.Details)
3869 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3870 if (si.Bounds.Contains (x, y)) {
3875 return new ListViewHitTestInfo (item, subitem, locations);
3878 [EditorBrowsable (EditorBrowsableState.Advanced)]
3879 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3881 if (startIndex < 0 || startIndex >= items.Count)
3882 throw new ArgumentOutOfRangeException ("startIndex");
3883 if (endIndex < 0 || endIndex >= items.Count)
3884 throw new ArgumentOutOfRangeException ("endIndex");
3885 if (startIndex > endIndex)
3886 throw new ArgumentException ("startIndex");
3891 for (int i = startIndex; i <= endIndex; i++)
3892 item_control.Invalidate (items [i].Bounds);
3894 if (!invalidateOnly)
3903 throw new InvalidOperationException ();
3909 // we need this overload to reuse the logic for sorting, while allowing
3910 // redrawing to be done by caller or have it done by this method when
3911 // sorting is really performed
3913 // ListViewItemCollection's Add and AddRange methods call this overload
3914 // with redraw set to false, as they take care of redrawing themselves
3915 // (they even want to redraw the listview if no sort is performed, as
3916 // an item was added), while ListView.Sort () only wants to redraw if
3917 // sorting was actually performed
3918 private void Sort (bool redraw)
3920 if (!IsHandleCreated || item_sorter == null) {
3924 items.Sort (item_sorter);
3929 public override string ToString ()
3931 int count = this.Items.Count;
3934 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3936 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3938 #endregion // Public Instance Methods
3943 class HeaderControl : Control {
3946 bool column_resize_active = false;
3947 ColumnHeader resize_column;
3948 ColumnHeader clicked_column;
3949 ColumnHeader drag_column;
3951 int drag_to_index = -1;
3953 public HeaderControl (ListView owner)
3956 MouseDown += new MouseEventHandler (HeaderMouseDown);
3957 MouseMove += new MouseEventHandler (HeaderMouseMove);
3958 MouseUp += new MouseEventHandler (HeaderMouseUp);
3961 private ColumnHeader ColumnAtX (int x)
3963 Point pt = new Point (x, 0);
3964 ColumnHeader result = null;
3965 foreach (ColumnHeader col in owner.Columns) {
3966 if (col.Rect.Contains (pt)) {
3974 private int GetReorderedIndex (ColumnHeader col)
3976 if (owner.reordered_column_indices == null)
3979 for (int i = 0; i < owner.Columns.Count; i++)
3980 if (owner.reordered_column_indices [i] == col.Index)
3982 throw new Exception ("Column index missing from reordered array");
3985 private void HeaderMouseDown (object sender, MouseEventArgs me)
3987 if (resize_column != null) {
3988 column_resize_active = true;
3993 clicked_column = ColumnAtX (me.X + owner.h_marker);
3995 if (clicked_column != null) {
3997 if (owner.AllowColumnReorder) {
3999 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
4000 drag_column.Rect = clicked_column.Rect;
4001 drag_to_index = GetReorderedIndex (clicked_column);
4003 clicked_column.Pressed = true;
4004 Rectangle bounds = clicked_column.Rect;
4005 bounds.X -= owner.h_marker;
4006 Invalidate (bounds);
4013 column_resize_active = false;
4014 resize_column = null;
4016 Cursor = Cursors.Default;
4019 private void HeaderMouseMove (object sender, MouseEventArgs me)
4021 Point pt = new Point (me.X + owner.h_marker, me.Y);
4023 if (column_resize_active) {
4024 int width = pt.X - resize_column.X;
4028 if (!owner.CanProceedWithResize (resize_column, width)){
4032 resize_column.Width = width;
4036 resize_column = null;
4038 if (clicked_column != null) {
4039 if (owner.AllowColumnReorder) {
4042 r = drag_column.Rect;
4043 r.X = clicked_column.Rect.X + me.X - drag_x;
4044 drag_column.Rect = r;
4046 int x = me.X + owner.h_marker;
4047 ColumnHeader over = ColumnAtX (x);
4049 drag_to_index = owner.Columns.Count;
4050 else if (x < over.X + over.Width / 2)
4051 drag_to_index = GetReorderedIndex (over);
4053 drag_to_index = GetReorderedIndex (over) + 1;
4056 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
4057 bool pressed = clicked_column.Pressed;
4058 clicked_column.Pressed = over == clicked_column;
4059 if (clicked_column.Pressed ^ pressed) {
4060 Rectangle bounds = clicked_column.Rect;
4061 bounds.X -= owner.h_marker;
4062 Invalidate (bounds);
4068 for (int i = 0; i < owner.Columns.Count; i++) {
4069 Rectangle zone = owner.Columns [i].Rect;
4070 zone.X = zone.Right - 5;
4072 if (zone.Contains (pt)) {
4073 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
4075 resize_column = owner.Columns [i];
4080 if (resize_column == null)
4081 Cursor = Cursors.Default;
4083 Cursor = Cursors.VSplit;
4086 void HeaderMouseUp (object sender, MouseEventArgs me)
4090 if (column_resize_active) {
4091 int column_idx = resize_column.Index;
4093 owner.RaiseColumnWidthChanged (column_idx);
4097 if (clicked_column != null && clicked_column.Pressed) {
4098 clicked_column.Pressed = false;
4099 Rectangle bounds = clicked_column.Rect;
4100 bounds.X -= owner.h_marker;
4101 Invalidate (bounds);
4102 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4105 if (drag_column != null && owner.AllowColumnReorder) {
4107 if (drag_to_index > GetReorderedIndex (clicked_column))
4109 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4110 owner.ReorderColumn (clicked_column, drag_to_index, true);
4115 clicked_column = null;
4118 internal override void OnPaintInternal (PaintEventArgs pe)
4123 Theme theme = ThemeEngine.Current;
4124 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4126 if (drag_column == null)
4130 if (drag_to_index == owner.Columns.Count)
4131 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4133 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4134 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4137 protected override void WndProc (ref Message m)
4139 switch ((Msg)m.Msg) {
4140 case Msg.WM_SETFOCUS:
4144 base.WndProc (ref m);
4150 private class ItemComparer : IComparer {
4151 readonly SortOrder sort_order;
4153 public ItemComparer (SortOrder sortOrder)
4155 sort_order = sortOrder;
4158 public int Compare (object x, object y)
4160 ListViewItem item_x = x as ListViewItem;
4161 ListViewItem item_y = y as ListViewItem;
4162 if (sort_order == SortOrder.Ascending)
4163 return String.Compare (item_x.Text, item_y.Text);
4165 return String.Compare (item_y.Text, item_x.Text);
4170 [ListBindable (false)]
4172 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4174 private readonly ListView owner;
4176 #region Public Constructor
4177 public CheckedIndexCollection (ListView owner)
4181 #endregion // Public Constructor
4183 #region Public Properties
4186 get { return owner.CheckedItems.Count; }
4189 public bool IsReadOnly {
4190 get { return true; }
4193 public int this [int index] {
4195 int [] indices = GetIndices ();
4196 if (index < 0 || index >= indices.Length)
4197 throw new ArgumentOutOfRangeException ("index");
4198 return indices [index];
4202 bool ICollection.IsSynchronized {
4203 get { return false; }
4206 object ICollection.SyncRoot {
4207 get { return this; }
4210 bool IList.IsFixedSize {
4211 get { return true; }
4214 object IList.this [int index] {
4215 get { return this [index]; }
4216 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4218 #endregion // Public Properties
4220 #region Public Methods
4221 public bool Contains (int checkedIndex)
4223 int [] indices = GetIndices ();
4224 for (int i = 0; i < indices.Length; i++) {
4225 if (indices [i] == checkedIndex)
4231 public IEnumerator GetEnumerator ()
4233 int [] indices = GetIndices ();
4234 return indices.GetEnumerator ();
4237 void ICollection.CopyTo (Array dest, int index)
4239 int [] indices = GetIndices ();
4240 Array.Copy (indices, 0, dest, index, indices.Length);
4243 int IList.Add (object value)
4245 throw new NotSupportedException ("Add operation is not supported.");
4250 throw new NotSupportedException ("Clear operation is not supported.");
4253 bool IList.Contains (object checkedIndex)
4255 if (!(checkedIndex is int))
4257 return Contains ((int) checkedIndex);
4260 int IList.IndexOf (object checkedIndex)
4262 if (!(checkedIndex is int))
4264 return IndexOf ((int) checkedIndex);
4267 void IList.Insert (int index, object value)
4269 throw new NotSupportedException ("Insert operation is not supported.");
4272 void IList.Remove (object value)
4274 throw new NotSupportedException ("Remove operation is not supported.");
4277 void IList.RemoveAt (int index)
4279 throw new NotSupportedException ("RemoveAt operation is not supported.");
4282 public int IndexOf (int checkedIndex)
4284 int [] indices = GetIndices ();
4285 for (int i = 0; i < indices.Length; i++) {
4286 if (indices [i] == checkedIndex)
4291 #endregion // Public Methods
4293 private int [] GetIndices ()
4295 ArrayList checked_items = owner.CheckedItems.List;
4296 int [] indices = new int [checked_items.Count];
4297 for (int i = 0; i < checked_items.Count; i++) {
4298 ListViewItem item = (ListViewItem) checked_items [i];
4299 indices [i] = item.Index;
4303 } // CheckedIndexCollection
4306 [ListBindable (false)]
4308 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4310 private readonly ListView owner;
4311 private ArrayList list;
4313 #region Public Constructor
4314 public CheckedListViewItemCollection (ListView owner)
4317 this.owner.Items.Changed += new CollectionChangedHandler (
4318 ItemsCollection_Changed);
4320 #endregion // Public Constructor
4322 #region Public Properties
4326 if (!owner.CheckBoxes)
4332 public bool IsReadOnly {
4333 get { return true; }
4336 public ListViewItem this [int index] {
4339 if (owner.VirtualMode)
4340 throw new InvalidOperationException ();
4342 ArrayList checked_items = List;
4343 if (index < 0 || index >= checked_items.Count)
4344 throw new ArgumentOutOfRangeException ("index");
4345 return (ListViewItem) checked_items [index];
4350 public virtual ListViewItem this [string key] {
4352 int idx = IndexOfKey (key);
4353 return idx == -1 ? null : (ListViewItem) List [idx];
4358 bool ICollection.IsSynchronized {
4359 get { return false; }
4362 object ICollection.SyncRoot {
4363 get { return this; }
4366 bool IList.IsFixedSize {
4367 get { return true; }
4370 object IList.this [int index] {
4371 get { return this [index]; }
4372 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4374 #endregion // Public Properties
4376 #region Public Methods
4377 public bool Contains (ListViewItem item)
4379 if (!owner.CheckBoxes)
4381 return List.Contains (item);
4385 public virtual bool ContainsKey (string key)
4387 return IndexOfKey (key) != -1;
4391 public void CopyTo (Array dest, int index)
4394 if (owner.VirtualMode)
4395 throw new InvalidOperationException ();
4397 if (!owner.CheckBoxes)
4399 List.CopyTo (dest, index);
4402 public IEnumerator GetEnumerator ()
4405 if (owner.VirtualMode)
4406 throw new InvalidOperationException ();
4408 if (!owner.CheckBoxes)
4409 return (new ListViewItem [0]).GetEnumerator ();
4410 return List.GetEnumerator ();
4413 int IList.Add (object value)
4415 throw new NotSupportedException ("Add operation is not supported.");
4420 throw new NotSupportedException ("Clear operation is not supported.");
4423 bool IList.Contains (object item)
4425 if (!(item is ListViewItem))
4427 return Contains ((ListViewItem) item);
4430 int IList.IndexOf (object item)
4432 if (!(item is ListViewItem))
4434 return IndexOf ((ListViewItem) item);
4437 void IList.Insert (int index, object value)
4439 throw new NotSupportedException ("Insert operation is not supported.");
4442 void IList.Remove (object value)
4444 throw new NotSupportedException ("Remove operation is not supported.");
4447 void IList.RemoveAt (int index)
4449 throw new NotSupportedException ("RemoveAt operation is not supported.");
4452 public int IndexOf (ListViewItem item)
4455 if (owner.VirtualMode)
4456 throw new InvalidOperationException ();
4458 if (!owner.CheckBoxes)
4460 return List.IndexOf (item);
4464 public virtual int IndexOfKey (string key)
4467 if (owner.VirtualMode)
4468 throw new InvalidOperationException ();
4470 if (key == null || key.Length == 0)
4473 ArrayList checked_items = List;
4474 for (int i = 0; i < checked_items.Count; i++) {
4475 ListViewItem item = (ListViewItem) checked_items [i];
4476 if (String.Compare (key, item.Name, true) == 0)
4483 #endregion // Public Methods
4485 internal ArrayList List {
4488 list = new ArrayList ();
4489 foreach (ListViewItem item in owner.Items) {
4498 internal void Reset ()
4500 // force re-population of list
4504 private void ItemsCollection_Changed ()
4508 } // CheckedListViewItemCollection
4511 [ListBindable (false)]
4513 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4515 internal ArrayList list;
4516 private ListView owner;
4518 #region Public Constructor
4519 public ColumnHeaderCollection (ListView owner)
4521 list = new ArrayList ();
4524 #endregion // Public Constructor
4526 #region Public Properties
4529 get { return list.Count; }
4532 public bool IsReadOnly {
4533 get { return false; }
4536 public virtual ColumnHeader this [int index] {
4538 if (index < 0 || index >= list.Count)
4539 throw new ArgumentOutOfRangeException ("index");
4540 return (ColumnHeader) list [index];
4545 public virtual ColumnHeader this [string key] {
4547 int idx = IndexOfKey (key);
4551 return (ColumnHeader) list [idx];
4556 bool ICollection.IsSynchronized {
4557 get { return true; }
4560 object ICollection.SyncRoot {
4561 get { return this; }
4564 bool IList.IsFixedSize {
4565 get { return list.IsFixedSize; }
4568 object IList.this [int index] {
4569 get { return this [index]; }
4570 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4572 #endregion // Public Properties
4574 #region Public Methods
4575 public virtual int Add (ColumnHeader value)
4577 int idx = list.Add (value);
4578 owner.AddColumn (value, idx, true);
4583 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4587 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4590 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4591 this.Add (colHeader);
4596 public virtual ColumnHeader Add (string text)
4598 return Add (String.Empty, text);
4601 public virtual ColumnHeader Add (string text, int width)
4603 return Add (String.Empty, text, width);
4606 public virtual ColumnHeader Add (string key, string text)
4608 ColumnHeader colHeader = new ColumnHeader ();
4609 colHeader.Name = key;
4610 colHeader.Text = text;
4615 public virtual ColumnHeader Add (string key, string text, int width)
4617 return Add (key, text, width, HorizontalAlignment.Left, -1);
4620 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4622 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4623 colHeader.ImageIndex = imageIndex;
4628 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4630 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4631 colHeader.ImageKey = imageKey;
4637 public virtual void AddRange (ColumnHeader [] values)
4639 foreach (ColumnHeader colHeader in values) {
4640 int idx = list.Add (colHeader);
4641 owner.AddColumn (colHeader, idx, false);
4644 owner.Redraw (true);
4647 public virtual void Clear ()
4649 foreach (ColumnHeader col in list)
4650 col.SetListView (null);
4652 owner.ReorderColumns (new int [0], true);
4655 public bool Contains (ColumnHeader value)
4657 return list.Contains (value);
4661 public virtual bool ContainsKey (string key)
4663 return IndexOfKey (key) != -1;
4667 public IEnumerator GetEnumerator ()
4669 return list.GetEnumerator ();
4672 void ICollection.CopyTo (Array dest, int index)
4674 list.CopyTo (dest, index);
4677 int IList.Add (object value)
4679 if (! (value is ColumnHeader)) {
4680 throw new ArgumentException ("Not of type ColumnHeader", "value");
4683 return this.Add ((ColumnHeader) value);
4686 bool IList.Contains (object value)
4688 if (! (value is ColumnHeader)) {
4689 throw new ArgumentException ("Not of type ColumnHeader", "value");
4692 return this.Contains ((ColumnHeader) value);
4695 int IList.IndexOf (object value)
4697 if (! (value is ColumnHeader)) {
4698 throw new ArgumentException ("Not of type ColumnHeader", "value");
4701 return this.IndexOf ((ColumnHeader) value);
4704 void IList.Insert (int index, object value)
4706 if (! (value is ColumnHeader)) {
4707 throw new ArgumentException ("Not of type ColumnHeader", "value");
4710 this.Insert (index, (ColumnHeader) value);
4713 void IList.Remove (object value)
4715 if (! (value is ColumnHeader)) {
4716 throw new ArgumentException ("Not of type ColumnHeader", "value");
4719 this.Remove ((ColumnHeader) value);
4722 public int IndexOf (ColumnHeader value)
4724 return list.IndexOf (value);
4728 public virtual int IndexOfKey (string key)
4730 if (key == null || key.Length == 0)
4733 for (int i = 0; i < list.Count; i++) {
4734 ColumnHeader col = (ColumnHeader) list [i];
4735 if (String.Compare (key, col.Name, true) == 0)
4743 public void Insert (int index, ColumnHeader value)
4745 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4746 // but it's really only greater.
4747 if (index < 0 || index > list.Count)
4748 throw new ArgumentOutOfRangeException ("index");
4750 list.Insert (index, value);
4751 owner.AddColumn (value, index, true);
4755 public void Insert (int index, string text)
4757 Insert (index, String.Empty, text);
4760 public void Insert (int index, string text, int width)
4762 Insert (index, String.Empty, text, width);
4765 public void Insert (int index, string key, string text)
4767 ColumnHeader colHeader = new ColumnHeader ();
4768 colHeader.Name = key;
4769 colHeader.Text = text;
4770 Insert (index, colHeader);
4773 public void Insert (int index, string key, string text, int width)
4775 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4776 Insert (index, colHeader);
4779 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4781 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4782 colHeader.ImageIndex = imageIndex;
4783 Insert (index, colHeader);
4786 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4788 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4789 colHeader.ImageKey = imageKey;
4790 Insert (index, colHeader);
4795 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4799 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4802 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4803 this.Insert (index, colHeader);
4806 public virtual void Remove (ColumnHeader column)
4808 if (!Contains (column))
4811 list.Remove (column);
4812 column.SetListView (null);
4814 int rem_display_index = column.InternalDisplayIndex;
4815 int [] display_indices = new int [list.Count];
4816 for (int i = 0; i < display_indices.Length; i++) {
4817 ColumnHeader col = (ColumnHeader) list [i];
4818 int display_index = col.InternalDisplayIndex;
4819 if (display_index < rem_display_index) {
4820 display_indices [i] = display_index;
4822 display_indices [i] = (display_index - 1);
4826 column.InternalDisplayIndex = -1;
4827 owner.ReorderColumns (display_indices, true);
4831 public virtual void RemoveByKey (string key)
4833 int idx = IndexOfKey (key);
4839 public virtual void RemoveAt (int index)
4841 if (index < 0 || index >= list.Count)
4842 throw new ArgumentOutOfRangeException ("index");
4844 ColumnHeader col = (ColumnHeader) list [index];
4847 #endregion // Public Methods
4850 } // ColumnHeaderCollection
4853 [ListBindable (false)]
4855 public class ListViewItemCollection : IList, ICollection, IEnumerable
4857 private readonly ArrayList list;
4858 private ListView owner;
4860 private ListViewGroup group;
4863 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4864 // In the later case ListViewItem.ListView never gets modified
4865 private bool is_main_collection = true;
4867 #region Public Constructor
4868 public ListViewItemCollection (ListView owner)
4870 list = new ArrayList (0);
4873 #endregion // Public Constructor
4876 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4879 is_main_collection = false;
4883 #region Public Properties
4888 if (owner != null && owner.VirtualMode)
4889 return owner.VirtualListSize;
4896 public bool IsReadOnly {
4897 get { return false; }
4901 public virtual ListViewItem this [int index] {
4903 public virtual ListViewItem this [int displayIndex] {
4907 int index = displayIndex;
4910 if (index < 0 || index >= Count)
4911 throw new ArgumentOutOfRangeException ("index");
4914 if (owner != null && owner.VirtualMode)
4915 return RetrieveVirtualItemFromOwner (index);
4917 return (ListViewItem) list [index];
4922 int index = displayIndex;
4925 if (index < 0 || index >= Count)
4926 throw new ArgumentOutOfRangeException ("index");
4929 if (owner != null && owner.VirtualMode)
4930 throw new InvalidOperationException ();
4933 if (list.Contains (value))
4934 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4936 if (value.ListView != null && value.ListView != owner)
4937 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");
4939 if (is_main_collection)
4940 value.Owner = owner;
4943 if (value.Group != null)
4944 value.Group.Items.Remove (value);
4946 value.SetGroup (group);
4950 list [index] = value;
4951 CollectionChanged (true);
4956 public virtual ListViewItem this [string key] {
4958 int idx = IndexOfKey (key);
4967 bool ICollection.IsSynchronized {
4968 get { return true; }
4971 object ICollection.SyncRoot {
4972 get { return this; }
4975 bool IList.IsFixedSize {
4976 get { return list.IsFixedSize; }
4979 object IList.this [int index] {
4980 get { return this [index]; }
4982 if (value is ListViewItem)
4983 this [index] = (ListViewItem) value;
4985 this [index] = new ListViewItem (value.ToString ());
4989 #endregion // Public Properties
4991 #region Public Methods
4992 public virtual ListViewItem Add (ListViewItem value)
4995 if (owner != null && owner.VirtualMode)
4996 throw new InvalidOperationException ();
5001 // Item is ignored until it has been added to the ListView
5002 if (is_main_collection || value.ListView != null)
5003 CollectionChanged (true);
5008 public virtual ListViewItem Add (string text)
5010 ListViewItem item = new ListViewItem (text);
5011 return this.Add (item);
5014 public virtual ListViewItem Add (string text, int imageIndex)
5016 ListViewItem item = new ListViewItem (text, imageIndex);
5017 return this.Add (item);
5021 public virtual ListViewItem Add (string text, string imageKey)
5023 ListViewItem item = new ListViewItem (text, imageKey);
5024 return this.Add (item);
5027 public virtual ListViewItem Add (string key, string text, int imageIndex)
5029 ListViewItem item = new ListViewItem (text, imageIndex);
5031 return this.Add (item);
5034 public virtual ListViewItem Add (string key, string text, string imageKey)
5036 ListViewItem item = new ListViewItem (text, imageKey);
5038 return this.Add (item);
5043 public void AddRange (ListViewItem [] items)
5046 public void AddRange (ListViewItem [] values)
5048 ListViewItem [] items = values;
5051 throw new ArgumentNullException ("Argument cannot be null!", "items");
5053 if (owner != null && owner.VirtualMode)
5054 throw new InvalidOperationException ();
5057 foreach (ListViewItem item in items)
5060 CollectionChanged (true);
5064 public void AddRange (ListViewItemCollection items)
5067 throw new ArgumentNullException ("Argument cannot be null!", "items");
5069 ListViewItem[] itemArray = new ListViewItem[items.Count];
5070 items.CopyTo (itemArray,0);
5071 this.AddRange (itemArray);
5075 public virtual void Clear ()
5078 if (owner != null && owner.VirtualMode)
5079 throw new InvalidOperationException ();
5081 if (is_main_collection && owner != null) {
5082 owner.SetFocusedItem (-1);
5083 owner.h_scroll.Value = owner.v_scroll.Value = 0;
5085 foreach (ListViewItem item in list) {
5086 owner.item_control.CancelEdit (item);
5093 foreach (ListViewItem item in list)
5094 item.SetGroup (null);
5098 CollectionChanged (false);
5101 public bool Contains (ListViewItem item)
5103 return IndexOf (item) != -1;
5107 public virtual bool ContainsKey (string key)
5109 return IndexOfKey (key) != -1;
5113 public void CopyTo (Array dest, int index)
5115 list.CopyTo (dest, index);
5119 public ListViewItem [] Find (string key, bool searchAllSubItems)
5122 return new ListViewItem [0];
5124 List<ListViewItem> temp_list = new List<ListViewItem> ();
5126 for (int i = 0; i < list.Count; i++) {
5127 ListViewItem lvi = (ListViewItem) list [i];
5128 if (String.Compare (key, lvi.Name, true) == 0)
5129 temp_list.Add (lvi);
5132 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5133 temp_list.CopyTo (retval);
5139 public IEnumerator GetEnumerator ()
5142 if (owner != null && owner.VirtualMode)
5143 throw new InvalidOperationException ();
5146 return list.GetEnumerator ();
5149 int IList.Add (object item)
5155 if (owner != null && owner.VirtualMode)
5156 throw new InvalidOperationException ();
5159 if (item is ListViewItem) {
5160 li = (ListViewItem) item;
5161 if (list.Contains (li))
5162 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5164 if (li.ListView != null && li.ListView != owner)
5165 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");
5168 li = new ListViewItem (item.ToString ());
5171 result = list.Add (li);
5172 CollectionChanged (true);
5177 bool IList.Contains (object item)
5179 return Contains ((ListViewItem) item);
5182 int IList.IndexOf (object item)
5184 return IndexOf ((ListViewItem) item);
5187 void IList.Insert (int index, object item)
5189 if (item is ListViewItem)
5190 this.Insert (index, (ListViewItem) item);
5192 this.Insert (index, item.ToString ());
5195 void IList.Remove (object item)
5197 Remove ((ListViewItem) item);
5200 public int IndexOf (ListViewItem item)
5203 if (owner != null && owner.VirtualMode) {
5204 for (int i = 0; i < Count; i++)
5205 if (RetrieveVirtualItemFromOwner (i) == item)
5212 return list.IndexOf (item);
5216 public virtual int IndexOfKey (string key)
5218 if (key == null || key.Length == 0)
5221 for (int i = 0; i < Count; i++) {
5222 ListViewItem lvi = this [i];
5223 if (String.Compare (key, lvi.Name, true) == 0)
5231 public ListViewItem Insert (int index, ListViewItem item)
5233 if (index < 0 || index > list.Count)
5234 throw new ArgumentOutOfRangeException ("index");
5237 if (owner != null && owner.VirtualMode)
5238 throw new InvalidOperationException ();
5241 if (list.Contains (item))
5242 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5244 if (item.ListView != null && item.ListView != owner)
5245 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");
5247 if (is_main_collection)
5251 if (item.Group != null)
5252 item.Group.Items.Remove (item);
5254 item.SetGroup (group);
5258 list.Insert (index, item);
5260 if (is_main_collection || item.ListView != null)
5261 CollectionChanged (true);
5266 public ListViewItem Insert (int index, string text)
5268 return this.Insert (index, new ListViewItem (text));
5271 public ListViewItem Insert (int index, string text, int imageIndex)
5273 return this.Insert (index, new ListViewItem (text, imageIndex));
5277 public ListViewItem Insert (int index, string text, string imageKey)
5279 ListViewItem lvi = new ListViewItem (text, imageKey);
5280 return Insert (index, lvi);
5283 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5285 ListViewItem lvi = new ListViewItem (text, imageIndex);
5287 return Insert (index, lvi);
5290 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5292 ListViewItem lvi = new ListViewItem (text, imageKey);
5294 return Insert (index, lvi);
5298 public virtual void Remove (ListViewItem item)
5301 if (owner != null && owner.VirtualMode)
5302 throw new InvalidOperationException ();
5305 int idx = list.IndexOf (item);
5310 public virtual void RemoveAt (int index)
5312 if (index < 0 || index >= Count)
5313 throw new ArgumentOutOfRangeException ("index");
5316 if (owner != null && owner.VirtualMode)
5317 throw new InvalidOperationException ();
5320 ListViewItem item = (ListViewItem) list [index];
5322 bool selection_changed = false;
5323 if (is_main_collection && owner != null) {
5325 int display_index = item.DisplayIndex;
5326 if (item.Focused && display_index + 1 == Count) // Last item
5327 owner.SetFocusedItem (display_index == 0 ? -1 : display_index - 1);
5329 selection_changed = owner.SelectedIndices.Contains (index);
5330 owner.item_control.CancelEdit (item);
5333 list.RemoveAt (index);
5335 if (is_main_collection)
5339 item.SetGroup (null);
5342 CollectionChanged (false);
5343 if (selection_changed && owner != null)
5344 owner.OnSelectedIndexChanged (EventArgs.Empty);
5348 public virtual void RemoveByKey (string key)
5350 int idx = IndexOfKey (key);
5356 #endregion // Public Methods
5358 internal ListView Owner {
5368 internal ListViewGroup Group {
5378 void AddItem (ListViewItem value)
5380 if (list.Contains (value))
5381 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5383 if (value.ListView != null && value.ListView != owner)
5384 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");
5385 if (is_main_collection)
5386 value.Owner = owner;
5389 if (value.Group != null)
5390 value.Group.Items.Remove (value);
5392 value.SetGroup (group);
5399 void CollectionChanged (bool sort)
5401 if (owner != null) {
5406 owner.Redraw (true);
5411 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5413 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5415 owner.OnRetrieveVirtualItem (args);
5416 ListViewItem retval = args.Item;
5417 retval.Owner = owner;
5418 retval.DisplayIndex = displayIndex;
5424 internal event CollectionChangedHandler Changed;
5426 internal void Sort (IComparer comparer)
5428 list.Sort (comparer);
5432 internal void OnChange ()
5434 if (Changed != null)
5437 } // ListViewItemCollection
5440 // In normal mode, the selection information resides in the Items,
5441 // making SelectedIndexCollection.List read-only
5443 // In virtual mode, SelectedIndexCollection directly saves the selection
5444 // information, instead of getting it from Items, making List read-and-write
5446 [ListBindable (false)]
5448 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5450 private readonly ListView owner;
5451 private ArrayList list;
5453 #region Public Constructor
5454 public SelectedIndexCollection (ListView owner)
5457 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5459 #endregion // Public Constructor
5461 #region Public Properties
5465 if (!owner.IsHandleCreated)
5472 public bool IsReadOnly {
5482 public int this [int index] {
5484 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5485 throw new ArgumentOutOfRangeException ("index");
5487 return (int) List [index];
5491 bool ICollection.IsSynchronized {
5492 get { return false; }
5495 object ICollection.SyncRoot {
5496 get { return this; }
5499 bool IList.IsFixedSize {
5509 object IList.this [int index] {
5510 get { return this [index]; }
5511 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5513 #endregion // Public Properties
5515 #region Public Methods
5517 public int Add (int itemIndex)
5519 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5520 throw new ArgumentOutOfRangeException ("index");
5522 if (owner.virtual_mode && !owner.IsHandleCreated)
5525 owner.Items [itemIndex].Selected = true;
5527 if (!owner.IsHandleCreated)
5541 if (!owner.IsHandleCreated)
5544 int [] indexes = (int []) List.ToArray (typeof (int));
5545 foreach (int index in indexes)
5546 owner.Items [index].Selected = false;
5549 public bool Contains (int selectedIndex)
5551 return IndexOf (selectedIndex) != -1;
5554 public void CopyTo (Array dest, int index)
5556 List.CopyTo (dest, index);
5559 public IEnumerator GetEnumerator ()
5561 return List.GetEnumerator ();
5564 int IList.Add (object value)
5566 throw new NotSupportedException ("Add operation is not supported.");
5574 bool IList.Contains (object selectedIndex)
5576 if (!(selectedIndex is int))
5578 return Contains ((int) selectedIndex);
5581 int IList.IndexOf (object selectedIndex)
5583 if (!(selectedIndex is int))
5585 return IndexOf ((int) selectedIndex);
5588 void IList.Insert (int index, object value)
5590 throw new NotSupportedException ("Insert operation is not supported.");
5593 void IList.Remove (object value)
5595 throw new NotSupportedException ("Remove operation is not supported.");
5598 void IList.RemoveAt (int index)
5600 throw new NotSupportedException ("RemoveAt operation is not supported.");
5603 public int IndexOf (int selectedIndex)
5605 if (!owner.IsHandleCreated)
5608 return List.IndexOf (selectedIndex);
5612 public void Remove (int itemIndex)
5614 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5615 throw new ArgumentOutOfRangeException ("itemIndex");
5617 owner.Items [itemIndex].Selected = false;
5620 #endregion // Public Methods
5622 internal ArrayList List {
5625 list = new ArrayList ();
5627 if (!owner.VirtualMode)
5629 for (int i = 0; i < owner.Items.Count; i++) {
5630 if (owner.Items [i].Selected)
5638 internal void Reset ()
5640 // force re-population of list
5644 private void ItemsCollection_Changed ()
5650 internal void RemoveIndex (int index)
5652 int idx = List.BinarySearch (index);
5654 List.RemoveAt (idx);
5657 // actually store index in the collection
5658 // also, keep the collection sorted, as .Net does
5659 internal void InsertIndex (int index)
5662 int iMax = List.Count - 1;
5663 while (iMin <= iMax) {
5664 int iMid = (iMin + iMax) / 2;
5665 int current_index = (int) List [iMid];
5667 if (current_index == index)
5668 return; // Already added
5669 if (current_index > index)
5675 List.Insert (iMin, index);
5679 } // SelectedIndexCollection
5682 [ListBindable (false)]
5684 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5686 private readonly ListView owner;
5688 #region Public Constructor
5689 public SelectedListViewItemCollection (ListView owner)
5693 #endregion // Public Constructor
5695 #region Public Properties
5699 return owner.SelectedIndices.Count;
5703 public bool IsReadOnly {
5704 get { return true; }
5707 public ListViewItem this [int index] {
5709 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5710 throw new ArgumentOutOfRangeException ("index");
5712 int item_index = owner.SelectedIndices [index];
5713 return owner.Items [item_index];
5718 public virtual ListViewItem this [string key] {
5720 int idx = IndexOfKey (key);
5729 bool ICollection.IsSynchronized {
5730 get { return false; }
5733 object ICollection.SyncRoot {
5734 get { return this; }
5737 bool IList.IsFixedSize {
5738 get { return true; }
5741 object IList.this [int index] {
5742 get { return this [index]; }
5743 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5745 #endregion // Public Properties
5747 #region Public Methods
5748 public void Clear ()
5750 owner.SelectedIndices.Clear ();
5753 public bool Contains (ListViewItem item)
5755 return IndexOf (item) != -1;
5759 public virtual bool ContainsKey (string key)
5761 return IndexOfKey (key) != -1;
5765 public void CopyTo (Array dest, int index)
5767 if (!owner.IsHandleCreated)
5769 if (index > Count) // Throws ArgumentException instead of IOOR exception
5770 throw new ArgumentException ("index");
5772 for (int i = 0; i < Count; i++)
5773 dest.SetValue (this [i], index++);
5776 public IEnumerator GetEnumerator ()
5778 if (!owner.IsHandleCreated)
5779 return (new ListViewItem [0]).GetEnumerator ();
5781 ListViewItem [] items = new ListViewItem [Count];
5782 for (int i = 0; i < Count; i++)
5783 items [i] = this [i];
5785 return items.GetEnumerator ();
5788 int IList.Add (object value)
5790 throw new NotSupportedException ("Add operation is not supported.");
5793 bool IList.Contains (object item)
5795 if (!(item is ListViewItem))
5797 return Contains ((ListViewItem) item);
5800 int IList.IndexOf (object item)
5802 if (!(item is ListViewItem))
5804 return IndexOf ((ListViewItem) item);
5807 void IList.Insert (int index, object value)
5809 throw new NotSupportedException ("Insert operation is not supported.");
5812 void IList.Remove (object value)
5814 throw new NotSupportedException ("Remove operation is not supported.");
5817 void IList.RemoveAt (int index)
5819 throw new NotSupportedException ("RemoveAt operation is not supported.");
5822 public int IndexOf (ListViewItem item)
5824 if (!owner.IsHandleCreated)
5827 for (int i = 0; i < Count; i++)
5828 if (this [i] == item)
5835 public virtual int IndexOfKey (string key)
5837 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5840 for (int i = 0; i < Count; i++) {
5841 ListViewItem item = this [i];
5842 if (String.Compare (item.Name, key, true) == 0)
5849 #endregion // Public Methods
5851 } // SelectedListViewItemCollection
5853 internal delegate void CollectionChangedHandler ();
5855 struct ItemMatrixLocation
5860 public ItemMatrixLocation (int row, int col)
5887 #endregion // Subclasses
5889 protected override void OnResize (EventArgs e)
5894 protected override void OnMouseLeave (EventArgs e)
5896 base.OnMouseLeave (e);
5900 // ColumnReorder event
5902 static object ColumnReorderedEvent = new object ();
5903 public event ColumnReorderedEventHandler ColumnReordered {
5904 add { Events.AddHandler (ColumnReorderedEvent, value); }
5905 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5908 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5910 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5917 // ColumnWidthChanged
5919 static object ColumnWidthChangedEvent = new object ();
5920 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5921 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5922 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5925 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5927 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5932 void RaiseColumnWidthChanged (int resize_column)
5934 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5936 OnColumnWidthChanged (n);
5940 // ColumnWidthChanging
5942 static object ColumnWidthChangingEvent = new object ();
5943 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5944 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5945 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5948 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5950 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5956 // 2.0 profile based implementation
5958 bool CanProceedWithResize (ColumnHeader col, int width)
5960 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5964 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5965 cwceh (this, changing);
5966 return !changing.Cancel;
5970 // 1.0 profile based implementation
5972 bool CanProceedWithResize (ColumnHeader col, int width)
5977 void RaiseColumnWidthChanged (int resize_column)