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 items [focused_item_index];
586 if (value == null || value.ListView != this ||
590 SetFocusedItem (value.Index);
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
1701 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1702 for (int i = 0; i < groups.InternalCount; i++) {
1703 ListViewGroup group = groups.GetInternalGroup (i);
1704 int items_in_group = group.ItemCount;
1706 if (items_in_group == 0)
1709 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1710 if (group_cols <= 0)
1712 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1714 group.starting_row = rows;
1715 group.rows = group_rows;
1717 cols = Math.Max (group_cols, cols);
1723 // Simple matrix if no groups are used
1725 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1728 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1730 if (UseCustomColumnWidth)
1731 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width) / (double)(custom_column_width));
1733 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1738 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1742 item_index_matrix = new int [rows, cols];
1745 // When using custom column width, we look for the minimum one
1746 void CalculateCustomColumnWidth ()
1748 int min_width = Int32.MaxValue;
1749 for (int i = 0; i < columns.Count; i++) {
1750 int col_width = columns [i].Width;
1752 if (col_width < min_width)
1753 min_width = col_width;
1756 custom_column_width = min_width;
1759 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1761 header_control.Visible = false;
1762 header_control.Size = Size.Empty;
1763 item_control.Visible = true;
1764 item_control.Location = Point.Empty;
1765 ItemSize = item_size; // Cache item size
1767 if (items.Count == 0)
1770 Size sz = item_size;
1772 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1775 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1777 layout_wd = UseCustomColumnWidth ? cols * custom_column_width : cols * (sz.Width + x_spacing) - x_spacing;
1778 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1781 CalculateGroupsLayout (sz, y_spacing, 0);
1784 int row = 0, col = 0;
1787 for (int i = 0; i < items.Count; i++) {
1788 ListViewItem item = items [i];
1791 ListViewGroup group = item.Group;
1793 group = groups.DefaultGroup;
1795 Point group_items_loc = group.items_area_location;
1796 int current_item = group.current_item++;
1797 int starting_row = group.starting_row;
1799 row = (current_item / cols);
1800 col = current_item % cols;
1802 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1803 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1805 SetItemLocation (i, x, y, row + starting_row, col);
1806 item_index_matrix [row + starting_row, col] = i;
1810 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1811 y = row * (item_size.Height + y_spacing);
1813 SetItemLocation (i, x, y, row, col);
1814 item_index_matrix [row, col] = i;
1823 if (++col == cols) {
1834 item.DisplayIndex = i;
1835 item.SetPosition (new Point (x, y));
1841 item_control.Size = new Size (layout_wd, layout_ht);
1845 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1848 bool details = view == View.Details;
1850 for (int i = 0; i < groups.InternalCount; i++) {
1851 ListViewGroup group = groups.GetInternalGroup (i);
1852 if (group.ItemCount == 0)
1855 group.current_item = 0; // Reset layout
1856 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1859 layout_ht = y; // Update height taking into account Groups' headers heights
1862 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1864 Rectangle client_area = ClientRectangle;
1865 int header_height = text_size.Height + 10;
1867 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1868 group.items_area_location = new Point (0, y_origin + header_height);
1870 int items_area_height = ((item_height + y_spacing) * rows);
1871 return header_height + items_area_height + 10; // Add a small bottom margin
1875 void LayoutHeader ()
1878 for (int i = 0; i < Columns.Count; i++) {
1879 ColumnHeader col = GetReorderedColumn (i);
1882 col.CalcColumnHeader ();
1888 if (x < ClientRectangle.Width)
1889 x = ClientRectangle.Width;
1891 if (header_style == ColumnHeaderStyle.None) {
1892 header_control.Visible = false;
1893 header_control.Size = Size.Empty;
1894 layout_wd = ClientRectangle.Width;
1896 header_control.Width = x;
1897 header_control.Height = columns.Count > 0 ? columns [0].Ht : Font.Height + 5;
1898 header_control.Visible = true;
1902 void LayoutDetails ()
1906 if (columns.Count == 0) {
1907 item_control.Visible = false;
1908 layout_wd = ClientRectangle.Width;
1909 layout_ht = ClientRectangle.Height;
1913 item_control.Visible = true;
1914 item_control.Location = Point.Empty;
1915 item_control.Width = ClientRectangle.Width;
1917 int item_height = GetDetailsItemHeight ();
1918 ItemSize = new Size (0, item_height); // We only cache Height for details view
1919 int y = header_control.Height;
1921 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1923 CalculateGroupsLayout (ItemSize, 2, y);
1926 for (int i = 0; i < items.Count; i++) {
1927 ListViewItem item = items [i];
1929 if (!virtual_mode) // Virtual mode sets Layout until draw time
1933 item.DisplayIndex = i;
1938 ListViewGroup group = item.Group;
1940 group = groups.DefaultGroup;
1942 int current_item = group.current_item++;
1943 Point group_items_loc = group.items_area_location;
1945 y = current_item * (item_height + 2) + group_items_loc.Y;
1946 SetItemLocation (i, 0, y, 0, 0);
1947 item.SetPosition (new Point (0, y));
1951 SetItemLocation (i, 0, y, 0, 0);
1952 item.SetPosition (new Point (0, y));
1957 // some space for bottom gridline
1958 if (items.Count > 0 && grid_lines)
1962 if (!using_groups) // With groups it has been previously computed
1967 private void AdjustItemsPositionArray (int count)
1969 if (items_location.Length >= count)
1972 // items_location, items_matrix_location and reordered_items_indices must keep the same length
1973 count = Math.Max (count, items_location.Length * 2);
1974 items_location = new Point [count];
1975 items_matrix_location = new ItemMatrixLocation [count];
1976 reordered_items_indices = new int [count];
1979 private void CalculateListView (ListViewAlignment align)
1983 AdjustItemsPositionArray (items.Count);
1990 case View.SmallIcon:
1991 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
1992 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1995 case View.LargeIcon:
1996 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
1997 ThemeEngine.Current.ListViewHorizontalSpacing,
1998 ThemeEngine.Current.ListViewVerticalSpacing);
2002 LayoutIcons (SmallIconItemSize, true,
2003 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2007 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
2008 ThemeEngine.Current.ListViewHorizontalSpacing,
2009 ThemeEngine.Current.ListViewVerticalSpacing);
2014 CalculateScrollBars ();
2017 internal Point GetItemLocation (int index)
2019 Point loc = items_location [index];
2020 loc.X -= h_marker; // Adjust to scroll
2026 internal int GetItemIndex (int display_index)
2028 return reordered_items_indices [display_index];
2031 private bool KeySearchString (KeyEventArgs ke)
2033 int current_tickcnt = Environment.TickCount;
2034 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2035 keysearch_text = string.Empty;
2038 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2041 keysearch_text += (char)ke.KeyCode;
2042 keysearch_tickcnt = current_tickcnt;
2044 int start = FocusedItem == null ? 0 : FocusedItem.Index;
2047 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
2048 CompareOptions.IgnoreCase)) {
2050 items [i].Selected = true;
2054 i = (i + 1 < Items.Count) ? i+1 : 0;
2062 int GetAdjustedIndex (Keys key)
2066 if (View == View.Details) {
2069 result = FocusedItem.Index - 1;
2072 result = FocusedItem.Index + 1;
2073 if (result == items.Count)
2077 int last_index = LastVisibleIndex;
2078 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2079 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2081 if (FocusedItem.Index == last_index) {
2082 if (FocusedItem.Index < Items.Count - 1) {
2083 int page_size = item_control.Height / ItemSize.Height - 1;
2084 result = FocusedItem.Index + page_size - 1;
2085 if (result >= Items.Count)
2086 result = Items.Count - 1;
2089 result = last_index;
2092 int first_index = FirstVisibleIndex;
2093 if (GetItemLocation (first_index).Y < 0)
2095 if (FocusedItem.Index == first_index) {
2096 if (first_index > 0) {
2097 int page_size = item_control.Height / ItemSize.Height - 1;
2098 result = first_index - page_size + 1;
2103 result = first_index;
2109 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.Index];
2110 int row = item_matrix_location.Row;
2111 int col = item_matrix_location.Col;
2117 return item_index_matrix [row, col - 1];
2120 if (col == (cols - 1))
2122 while (item_index_matrix [row, col + 1] == 0) {
2127 return item_index_matrix [row, col + 1];
2132 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2137 return item_index_matrix [row - 1, col];
2140 if (row == (rows - 1) || row == Items.Count - 1)
2142 while (item_index_matrix [row + 1, col] == 0) {
2147 return item_index_matrix [row + 1, col];
2154 ListViewItem selection_start;
2156 private bool SelectItems (ArrayList sel_items)
2158 bool changed = false;
2159 foreach (ListViewItem item in SelectedItems)
2160 if (!sel_items.Contains (item)) {
2161 item.Selected = false;
2164 foreach (ListViewItem item in sel_items)
2165 if (!item.Selected) {
2166 item.Selected = true;
2172 private void UpdateMultiSelection (int index, bool reselect)
2174 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2175 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2176 ListViewItem item = items [index];
2178 if (shift_pressed && selection_start != null) {
2179 ArrayList list = new ArrayList ();
2180 int start_index = selection_start.Index;
2181 int start = Math.Min (start_index, index);
2182 int end = Math.Max (start_index, index);
2183 if (View == View.Details) {
2184 for (int i = start; i <= end; i++)
2185 list.Add (items [i]);
2187 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2188 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2189 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2190 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2191 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2192 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2194 for (int i = 0; i < items.Count; i++) {
2195 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2197 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2198 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2199 list.Add (items [i]);
2203 } else if (ctrl_pressed) {
2204 item.Selected = !item.Selected;
2205 selection_start = item;
2208 // do not unselect, and reselect the item
2209 foreach (int itemIndex in SelectedIndices) {
2210 if (index == itemIndex)
2212 items [itemIndex].Selected = false;
2215 SelectedItems.Clear ();
2216 item.Selected = true;
2218 selection_start = item;
2222 internal override bool InternalPreProcessMessage (ref Message msg)
2224 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2225 Keys key_data = (Keys)msg.WParam.ToInt32();
2227 HandleNavKeys (key_data);
2230 return base.InternalPreProcessMessage (ref msg);
2233 bool HandleNavKeys (Keys key_data)
2235 if (Items.Count == 0 || !item_control.Visible)
2238 if (FocusedItem == null)
2243 SelectIndex (Items.Count - 1);
2256 SelectIndex (GetAdjustedIndex (key_data));
2260 ToggleItemsCheckState ();
2263 if (selected_indices.Count > 0)
2264 OnItemActivate (EventArgs.Empty);
2274 void ToggleItemsCheckState ()
2279 // Don't modify check state if StateImageList has less than 2 elements
2280 if (StateImageList != null && StateImageList.Images.Count < 2)
2283 if (SelectedIndices.Count > 0) {
2284 for (int i = 0; i < SelectedIndices.Count; i++) {
2285 ListViewItem item = Items [SelectedIndices [i]];
2286 item.Checked = !item.Checked;
2291 if (FocusedItem != null) {
2292 FocusedItem.Checked = !FocusedItem.Checked;
2293 SelectIndex (FocusedItem.Index);
2297 void SelectIndex (int index)
2303 UpdateMultiSelection (index, true);
2304 else if (!items [index].Selected)
2305 items [index].Selected = true;
2307 SetFocusedItem (index);
2308 EnsureVisible (index);
2311 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2313 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2316 if (ke.Alt || ke.Control)
2319 ke.Handled = KeySearchString (ke);
2322 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2324 Point loc = PointToClient (Control.MousePosition);
2325 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2328 internal class ItemControl : Control {
2331 ListViewItem clicked_item;
2332 ListViewItem last_clicked_item;
2333 bool hover_processed = false;
2334 bool checking = false;
2335 ListViewItem prev_hovered_item;
2337 ListViewItem prev_tooltip_item;
2340 Point drag_begin = new Point (-1, -1);
2341 internal int dragged_item_index = -1;
2343 ListViewLabelEditTextBox edit_text_box;
2344 internal ListViewItem edit_item;
2345 LabelEditEventArgs edit_args;
2347 public ItemControl (ListView owner)
2350 DoubleClick += new EventHandler(ItemsDoubleClick);
2351 MouseDown += new MouseEventHandler(ItemsMouseDown);
2352 MouseMove += new MouseEventHandler(ItemsMouseMove);
2353 MouseHover += new EventHandler(ItemsMouseHover);
2354 MouseUp += new MouseEventHandler(ItemsMouseUp);
2357 void ItemsDoubleClick (object sender, EventArgs e)
2359 if (owner.activation == ItemActivation.Standard)
2360 owner.OnItemActivate (EventArgs.Empty);
2370 BoxSelect box_select_mode = BoxSelect.None;
2371 IList prev_selection;
2372 Point box_select_start;
2374 Rectangle box_select_rect;
2375 internal Rectangle BoxSelectRectangle {
2376 get { return box_select_rect; }
2378 if (box_select_rect == value)
2381 InvalidateBoxSelectRect ();
2382 box_select_rect = value;
2383 InvalidateBoxSelectRect ();
2387 void InvalidateBoxSelectRect ()
2389 if (BoxSelectRectangle.Size.IsEmpty)
2392 Rectangle edge = BoxSelectRectangle;
2398 edge.Y = BoxSelectRectangle.Bottom - 1;
2400 edge.Y = BoxSelectRectangle.Y - 1;
2402 edge.Height = BoxSelectRectangle.Height + 2;
2404 edge.X = BoxSelectRectangle.Right - 1;
2408 private Rectangle CalculateBoxSelectRectangle (Point pt)
2410 int left = Math.Min (box_select_start.X, pt.X);
2411 int right = Math.Max (box_select_start.X, pt.X);
2412 int top = Math.Min (box_select_start.Y, pt.Y);
2413 int bottom = Math.Max (box_select_start.Y, pt.Y);
2414 return Rectangle.FromLTRB (left, top, right, bottom);
2417 bool BoxIntersectsItem (int index)
2419 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2420 if (owner.View != View.Details) {
2422 r.Y += r.Height / 4;
2426 return BoxSelectRectangle.IntersectsWith (r);
2429 bool BoxIntersectsText (int index)
2431 Rectangle r = owner.Items [index].TextBounds;
2432 return BoxSelectRectangle.IntersectsWith (r);
2435 ArrayList BoxSelectedItems {
2437 ArrayList result = new ArrayList ();
2438 for (int i = 0; i < owner.Items.Count; i++) {
2440 if (owner.View == View.Details && !owner.FullRowSelect)
2441 intersects = BoxIntersectsText (i);
2443 intersects = BoxIntersectsItem (i);
2446 result.Add (owner.Items [i]);
2452 private bool PerformBoxSelection (Point pt)
2454 if (box_select_mode == BoxSelect.None)
2457 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2459 ArrayList box_items = BoxSelectedItems;
2463 switch (box_select_mode) {
2465 case BoxSelect.Normal:
2469 case BoxSelect.Control:
2470 items = new ArrayList ();
2471 foreach (int index in prev_selection)
2472 if (!box_items.Contains (owner.Items [index]))
2473 items.Add (owner.Items [index]);
2474 foreach (ListViewItem item in box_items)
2475 if (!prev_selection.Contains (item.Index))
2479 case BoxSelect.Shift:
2481 foreach (ListViewItem item in box_items)
2482 prev_selection.Remove (item.Index);
2483 foreach (int index in prev_selection)
2484 items.Add (owner.Items [index]);
2488 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2492 owner.SelectItems (items);
2498 private void ItemsMouseDown (object sender, MouseEventArgs me)
2500 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2501 if (owner.items.Count == 0)
2504 bool box_selecting = false;
2505 Size item_size = owner.ItemSize;
2506 Point pt = new Point (me.X, me.Y);
2507 for (int i = 0; i < owner.items.Count; i++) {
2508 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2509 if (!item_rect.Contains (pt))
2512 // Actual item in 'i' position
2513 ListViewItem item = owner.items [owner.reordered_items_indices [i]];
2515 if (item.CheckRectReal.Contains (pt)) {
2516 // Don't modify check state if we have only one image
2517 // and if we are in 1.1 profile only take into account
2519 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2526 // Generate an extra ItemCheck event when we got two clicks
2527 // (Match weird .Net behaviour)
2529 item.Checked = !item.Checked;
2531 item.Checked = !item.Checked;
2536 if (owner.View == View.Details) {
2537 bool over_text = item.TextBounds.Contains (pt);
2538 if (owner.FullRowSelect) {
2539 clicked_item = owner.items [i];
2540 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2541 if (!over_text && over_item_column && owner.MultiSelect)
2542 box_selecting = true;
2543 } else if (over_text)
2544 clicked_item = item;
2546 owner.SetFocusedItem (i);
2548 clicked_item = item;
2554 if (clicked_item != null) {
2555 bool changed = !clicked_item.Selected;
2556 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2557 owner.SetFocusedItem (clicked_item.Index);
2559 if (owner.MultiSelect) {
2560 bool reselect = (!owner.LabelEdit || changed);
2561 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2562 owner.UpdateMultiSelection (clicked_item.Index, reselect);
2564 clicked_item.Selected = true;
2568 if (owner.VirtualMode && changed) {
2569 // Broken event - It's not fired from Item.Selected also
2570 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2571 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2573 owner.OnVirtualItemsSelectionRangeChanged (args);
2576 // Report clicks only if the item was clicked. On MS the
2577 // clicks are only raised if you click an item
2579 if (me.Clicks > 1) {
2580 if (owner.CheckBoxes)
2581 clicked_item.Checked = !clicked_item.Checked;
2582 } else if (me.Clicks == 1) {
2583 if (owner.LabelEdit && !changed)
2584 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2587 if (owner.MultiSelect)
2588 box_selecting = true;
2589 else if (owner.SelectedItems.Count > 0)
2590 owner.SelectedItems.Clear ();
2593 if (box_selecting) {
2594 Keys mods = XplatUI.State.ModifierKeys;
2595 if ((mods & Keys.Shift) != 0)
2596 box_select_mode = BoxSelect.Shift;
2597 else if ((mods & Keys.Control) != 0)
2598 box_select_mode = BoxSelect.Control;
2600 box_select_mode = BoxSelect.Normal;
2601 box_select_start = pt;
2602 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2606 private void ItemsMouseMove (object sender, MouseEventArgs me)
2608 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2610 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2614 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2615 !hover_processed && owner.Activation != ItemActivation.OneClick
2617 && !owner.ShowItemToolTips
2622 Point pt = PointToClient (Control.MousePosition);
2623 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2625 if (hover_processed && item != null && item != prev_hovered_item) {
2626 hover_processed = false;
2627 XplatUI.ResetMouseHover (Handle);
2630 // Need to invalidate the item in HotTracking to show/hide the underline style
2631 if (owner.Activation == ItemActivation.OneClick) {
2632 if (item == null && owner.HotItemIndex != -1) {
2634 if (owner.HotTracking)
2635 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2638 Cursor = Cursors.Default;
2639 owner.HotItemIndex = -1;
2640 } else if (item != null && owner.HotItemIndex == -1) {
2642 if (owner.HotTracking)
2643 Invalidate (item.Bounds);
2646 Cursor = Cursors.Hand;
2647 owner.HotItemIndex = item.Index;
2651 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2652 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2654 drag_begin = new Point (me.X, me.Y);
2655 dragged_item_index = item.Index;
2659 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2660 if (!r.Contains (me.X, me.Y)) {
2661 ListViewItem dragged_item = owner.items [dragged_item_index];
2662 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2664 drag_begin = new Point (-1, -1);
2665 dragged_item_index = -1;
2671 if (owner.ShowItemToolTips) {
2673 owner.item_tooltip.Active = false;
2674 prev_tooltip_item = null;
2675 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2676 owner.item_tooltip.Active = true;
2677 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2678 prev_tooltip_item = item;
2685 private void ItemsMouseHover (object sender, EventArgs e)
2687 if (owner.hover_pending) {
2688 owner.OnMouseHover (e);
2689 owner.hover_pending = false;
2695 hover_processed = true;
2696 Point pt = PointToClient (Control.MousePosition);
2697 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2701 prev_hovered_item = item;
2703 if (owner.HoverSelection) {
2704 if (owner.MultiSelect)
2705 owner.UpdateMultiSelection (item.Index, true);
2707 item.Selected = true;
2709 owner.SetFocusedItem (item.Index);
2710 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2714 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2718 void HandleClicks (MouseEventArgs me)
2720 // if the click is not on an item,
2721 // clicks remains as 0
2724 owner.OnDoubleClick (EventArgs.Empty);
2725 } else if (clicks == 1) {
2726 owner.OnClick (EventArgs.Empty);
2728 owner.OnDoubleClick (EventArgs.Empty);
2729 owner.OnMouseDoubleClick (me);
2730 } else if (clicks == 1) {
2731 owner.OnClick (EventArgs.Empty);
2732 owner.OnMouseClick (me);
2739 private void ItemsMouseUp (object sender, MouseEventArgs me)
2741 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2742 HandleClicks (owner_me);
2745 if (owner.Items.Count == 0) {
2747 owner.OnMouseUp (owner_me);
2751 Point pt = new Point (me.X, me.Y);
2753 Rectangle rect = Rectangle.Empty;
2754 if (clicked_item != null) {
2755 if (owner.view == View.Details && !owner.full_row_select)
2756 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2758 rect = clicked_item.Bounds;
2760 if (rect.Contains (pt)) {
2761 switch (owner.activation) {
2762 case ItemActivation.OneClick:
2763 owner.OnItemActivate (EventArgs.Empty);
2766 case ItemActivation.TwoClick:
2767 if (last_clicked_item == clicked_item) {
2768 owner.OnItemActivate (EventArgs.Empty);
2769 last_clicked_item = null;
2771 last_clicked_item = clicked_item;
2774 // DoubleClick activation is handled in another handler
2778 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2779 // Need this to clean up background clicks
2780 owner.SelectedItems.Clear ();
2784 owner.OnMouseUp (owner_me);
2787 private void ResetMouseState ()
2789 clicked_item = null;
2790 box_select_start = Point.Empty;
2791 BoxSelectRectangle = Rectangle.Empty;
2792 prev_selection = null;
2793 box_select_mode = BoxSelect.None;
2796 // Clean these bits in case the mouse buttons were
2797 // released before firing ItemDrag
2798 dragged_item_index = -1;
2799 drag_begin = new Point (-1, -1);
2802 private void LabelEditFinished (object sender, EventArgs e)
2804 EndEdit (edit_item);
2807 private void LabelEditCancelled (object sender, EventArgs e)
2809 edit_args.SetLabel (null);
2810 EndEdit (edit_item);
2813 private void LabelTextChanged (object sender, EventArgs e)
2815 if (edit_args != null)
2816 edit_args.SetLabel (edit_text_box.Text);
2819 internal void BeginEdit (ListViewItem item)
2821 if (edit_item != null)
2822 EndEdit (edit_item);
2824 if (edit_text_box == null) {
2825 edit_text_box = new ListViewLabelEditTextBox ();
2826 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2827 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2828 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2829 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2830 edit_text_box.Visible = false;
2831 Controls.Add (edit_text_box);
2834 item.EnsureVisible();
2836 edit_text_box.Reset ();
2838 switch (owner.view) {
2840 case View.SmallIcon:
2842 edit_text_box.TextAlign = HorizontalAlignment.Left;
2843 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2844 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2845 edit_text_box.Width = (int)sizef.Width + 4;
2846 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2847 edit_text_box.WordWrap = false;
2848 edit_text_box.Multiline = false;
2850 case View.LargeIcon:
2851 edit_text_box.TextAlign = HorizontalAlignment.Center;
2852 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2853 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2854 edit_text_box.Width = (int)sizef.Width + 4;
2855 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2856 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2857 edit_text_box.WordWrap = true;
2858 edit_text_box.Multiline = true;
2864 edit_text_box.Text = item.Text;
2865 edit_text_box.Font = item.Font;
2866 edit_text_box.Visible = true;
2867 edit_text_box.Focus ();
2868 edit_text_box.SelectAll ();
2870 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2871 owner.OnBeforeLabelEdit (edit_args);
2873 if (edit_args.CancelEdit)
2877 internal void CancelEdit (ListViewItem item)
2879 // do nothing if there's no item being edited, or if the
2880 // item being edited is not the one passed in
2881 if (edit_item == null || edit_item != item)
2884 edit_args.SetLabel (null);
2888 internal void EndEdit (ListViewItem item)
2890 // do nothing if there's no item being edited, or if the
2891 // item being edited is not the one passed in
2892 if (edit_item == null || edit_item != item)
2895 if (edit_text_box != null) {
2896 if (edit_text_box.Visible)
2897 edit_text_box.Visible = false;
2898 // ensure listview gets focus
2902 // Same as TreeView.EndEdit: need to have focus in synch
2903 Application.DoEvents ();
2906 // Create a new instance, since we could get a call to BeginEdit
2907 // from the handler and have fields out of synch
2909 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2912 owner.OnAfterLabelEdit (args);
2913 if (!args.CancelEdit && args.Label != null)
2914 item.Text = args.Label;
2917 internal override void OnPaintInternal (PaintEventArgs pe)
2919 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2922 protected override void WndProc (ref Message m)
2924 switch ((Msg)m.Msg) {
2925 case Msg.WM_KILLFOCUS:
2926 owner.Select (false, true);
2928 case Msg.WM_SETFOCUS:
2929 owner.Select (false, true);
2931 case Msg.WM_LBUTTONDOWN:
2933 owner.Select (false, true);
2935 case Msg.WM_RBUTTONDOWN:
2937 owner.Select (false, true);
2942 base.WndProc (ref m);
2946 internal class ListViewLabelEditTextBox : TextBox
2951 int max_height = -1;
2952 int min_height = -1;
2954 int old_number_lines = 1;
2956 SizeF text_size_one_char;
2958 public ListViewLabelEditTextBox ()
2960 min_height = DefaultSize.Height;
2961 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2964 public int MaxWidth {
2966 if (value < min_width)
2967 max_width = min_width;
2973 public int MaxHeight {
2975 if (value < min_height)
2976 max_height = min_height;
2982 public new int Width {
2992 public override Font Font {
2998 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3002 protected override void OnTextChanged (EventArgs e)
3004 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3006 int new_width = (int)text_size.Width + 8;
3009 ResizeTextBoxWidth (new_width);
3011 if (Width != max_width)
3012 ResizeTextBoxWidth (new_width);
3014 int number_lines = Lines.Length;
3016 if (number_lines != old_number_lines) {
3017 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3018 old_number_lines = number_lines;
3020 ResizeTextBoxHeight (new_height);
3024 base.OnTextChanged (e);
3027 protected override bool IsInputKey (Keys key_data)
3029 if ((key_data & Keys.Alt) == 0) {
3030 switch (key_data & Keys.KeyCode) {
3037 return base.IsInputKey (key_data);
3040 protected override void OnKeyDown (KeyEventArgs e)
3045 switch (e.KeyCode) {
3049 OnEditingFinished (e);
3054 OnEditingCancelled (e);
3059 protected override void OnLostFocus (EventArgs e)
3062 OnEditingFinished (e);
3066 protected void OnEditingCancelled (EventArgs e)
3068 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3073 protected void OnEditingFinished (EventArgs e)
3075 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3080 private void ResizeTextBoxWidth (int new_width)
3082 if (new_width > max_width)
3083 base.Width = max_width;
3085 if (new_width >= min_width)
3086 base.Width = new_width;
3088 base.Width = min_width;
3091 private void ResizeTextBoxHeight (int new_height)
3093 if (new_height > max_height)
3094 base.Height = max_height;
3096 if (new_height >= min_height)
3097 base.Height = new_height;
3099 base.Height = min_height;
3102 public void Reset ()
3109 old_number_lines = 1;
3111 Text = String.Empty;
3116 static object EditingCancelledEvent = new object ();
3117 public event EventHandler EditingCancelled {
3118 add { Events.AddHandler (EditingCancelledEvent, value); }
3119 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3122 static object EditingFinishedEvent = new object ();
3123 public event EventHandler EditingFinished {
3124 add { Events.AddHandler (EditingFinishedEvent, value); }
3125 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3129 internal override void OnPaintInternal (PaintEventArgs pe)
3134 CalculateScrollBars ();
3137 void FocusChanged (object o, EventArgs args)
3139 if (Items.Count == 0)
3142 if (FocusedItem == null)
3145 ListViewItem focused_item = FocusedItem;
3147 if (focused_item.ListView != null) {
3148 item_control.Invalidate (focused_item.Bounds);
3149 focused_item.Layout ();
3150 item_control.Invalidate (focused_item.Bounds);
3154 private void ListView_MouseEnter (object sender, EventArgs args)
3156 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3159 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3161 if (Items.Count == 0)
3164 int lines = me.Delta / 120;
3171 case View.SmallIcon:
3172 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3174 case View.LargeIcon:
3175 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3178 Scroll (h_scroll, -ItemSize.Width * lines);
3182 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3188 private void ListView_SizeChanged (object sender, EventArgs e)
3190 CalculateListView (alignment);
3193 private void SetFocusedItem (int index)
3196 items [index].Focused = true;
3197 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3198 items [focused_item_index].Focused = false;
3200 focused_item_index = index;
3203 private void HorizontalScroller (object sender, EventArgs e)
3205 item_control.EndEdit (item_control.edit_item);
3207 // Avoid unnecessary flickering, when button is
3208 // kept pressed at the end
3209 if (h_marker != h_scroll.Value) {
3211 int pixels = h_marker - h_scroll.Value;
3213 h_marker = h_scroll.Value;
3214 if (header_control.Visible)
3215 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3217 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3221 private void VerticalScroller (object sender, EventArgs e)
3223 item_control.EndEdit (item_control.edit_item);
3225 // Avoid unnecessary flickering, when button is
3226 // kept pressed at the end
3227 if (v_marker != v_scroll.Value) {
3228 int pixels = v_marker - v_scroll.Value;
3229 Rectangle area = item_control.ClientRectangle;
3230 if (header_control.Visible) {
3231 area.Y += header_control.Height;
3232 area.Height -= header_control.Height;
3235 v_marker = v_scroll.Value;
3236 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3240 internal override bool IsInputCharInternal (char charCode)
3244 #endregion // Internal Methods Properties
3246 #region Protected Methods
3247 protected override void CreateHandle ()
3249 base.CreateHandle ();
3250 for (int i = 0; i < SelectedItems.Count; i++)
3251 OnSelectedIndexChanged (EventArgs.Empty);
3254 protected override void Dispose (bool disposing)
3257 h_scroll.Dispose ();
3258 v_scroll.Dispose ();
3260 large_image_list = null;
3261 small_image_list = null;
3262 state_image_list = null;
3264 foreach (ColumnHeader col in columns)
3265 col.SetListView (null);
3268 if (!virtual_mode) // In virtual mode we don't save the items
3270 foreach (ListViewItem item in items)
3274 base.Dispose (disposing);
3277 protected override bool IsInputKey (Keys keyData)
3294 return base.IsInputKey (keyData);
3297 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3299 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3305 protected override void OnBackgroundImageChanged (EventArgs e)
3307 item_control.BackgroundImage = BackgroundImage;
3308 base.OnBackgroundImageChanged (e);
3312 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3314 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3319 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3321 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3327 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3329 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3334 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3336 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3341 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3343 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3349 protected override void OnEnabledChanged (EventArgs e)
3351 base.OnEnabledChanged (e);
3355 protected override void OnFontChanged (EventArgs e)
3357 base.OnFontChanged (e);
3361 protected override void OnHandleCreated (EventArgs e)
3363 base.OnHandleCreated (e);
3364 CalculateListView (alignment);
3366 if (!virtual_mode) // Sorting is not allowed in virtual mode
3371 protected override void OnHandleDestroyed (EventArgs e)
3373 base.OnHandleDestroyed (e);
3376 protected virtual void OnItemActivate (EventArgs e)
3378 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3383 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3385 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3391 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3393 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3399 protected virtual void OnItemDrag (ItemDragEventArgs e)
3401 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3407 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3409 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3414 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3416 ListViewItemSelectionChangedEventHandler eh =
3417 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3422 protected override void OnMouseHover (EventArgs e)
3424 base.OnMouseHover (e);
3427 protected override void OnParentChanged (EventArgs e)
3429 base.OnParentChanged (e);
3433 protected virtual void OnSelectedIndexChanged (EventArgs e)
3435 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3440 protected override void OnSystemColorsChanged (EventArgs e)
3442 base.OnSystemColorsChanged (e);
3446 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3448 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3453 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3455 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3460 [EditorBrowsable (EditorBrowsableState.Advanced)]
3461 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3463 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3468 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3470 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3475 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3477 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3478 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3484 protected void RealizeProperties ()
3489 protected void UpdateExtendedStyles ()
3494 bool refocusing = false;
3496 protected override void WndProc (ref Message m)
3498 switch ((Msg)m.Msg) {
3499 case Msg.WM_KILLFOCUS:
3500 Control receiver = Control.FromHandle (m.WParam);
3501 if (receiver == item_control) {
3507 case Msg.WM_SETFOCUS:
3517 base.WndProc (ref m);
3519 #endregion // Protected Methods
3521 #region Public Instance Methods
3522 public void ArrangeIcons ()
3524 ArrangeIcons (this.alignment);
3527 public void ArrangeIcons (ListViewAlignment value)
3529 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3530 if (view == View.LargeIcon || view == View.SmallIcon) {
3531 this.CalculateListView (value);
3532 // we have done the calculations already
3533 this.Redraw (false);
3538 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3540 if (columnIndex < 0 || columnIndex >= columns.Count)
3541 throw new ArgumentOutOfRangeException ("columnIndex");
3543 columns [columnIndex].AutoResize (headerAutoResize);
3546 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3549 foreach (ColumnHeader col in columns)
3550 col.AutoResize (headerAutoResize);
3555 public void BeginUpdate ()
3557 // flag to avoid painting
3561 public void Clear ()
3564 items.Clear (); // Redraw (true) called here
3567 public void EndUpdate ()
3569 // flag to avoid painting
3572 // probably, now we need a redraw with recalculations
3576 public void EnsureVisible (int index)
3578 if (index < 0 || index >= items.Count || scrollable == false)
3581 Rectangle view_rect = item_control.ClientRectangle;
3582 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3584 if (view_rect.Contains (bounds))
3587 if (View != View.Details) {
3588 if (bounds.Left < 0)
3589 h_scroll.Value += bounds.Left;
3590 else if (bounds.Right > view_rect.Right)
3591 h_scroll.Value += (bounds.Right - view_rect.Right);
3595 v_scroll.Value += bounds.Top;
3596 else if (bounds.Bottom > view_rect.Bottom)
3597 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3601 public ListViewItem FindItemWithText (string text)
3603 if (items.Count == 0)
3606 return FindItemWithText (text, true, 0, true);
3609 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3611 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true);
3614 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3616 if (startIndex < 0 || startIndex >= items.Count)
3617 throw new ArgumentOutOfRangeException ("startIndex");
3620 throw new ArgumentNullException ("text");
3623 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3624 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3625 SearchDirectionHint.Down, startIndex);
3627 OnSearchForVirtualItem (args);
3628 int idx = args.Index;
3629 if (idx >= 0 && idx < virtual_list_size)
3635 for (int i = startIndex; i < items.Count; i++) {
3636 ListViewItem lvi = items [i];
3638 if ((isPrefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3639 || String.Compare (lvi.Text, text, true) == 0) // match
3643 if (includeSubItemsInSearch) {
3644 for (int i = startIndex; i < items.Count; i++) {
3645 ListViewItem lvi = items [i];
3646 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3647 if ((isPrefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3648 || String.Compare (sub_item.Text, text, true) == 0)
3656 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3658 return FindNearestItem (searchDirection, new Point (x, y));
3661 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3663 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3664 throw new ArgumentOutOfRangeException ("searchDirection");
3666 if (view != View.LargeIcon && view != View.SmallIcon)
3667 throw new InvalidOperationException ();
3670 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3671 false, false, String.Empty, point,
3674 OnSearchForVirtualItem (args);
3675 int idx = args.Index;
3676 if (idx >= 0 && idx < virtual_list_size)
3682 ListViewItem item = null;
3683 int min_dist = Int32.MaxValue;
3686 // It looks like .Net does a previous adjustment
3689 case SearchDirectionHint.Up:
3690 point.Y -= item_size.Height;
3692 case SearchDirectionHint.Down:
3693 point.Y += item_size.Height;
3695 case SearchDirectionHint.Left:
3696 point.X -= item_size.Width;
3698 case SearchDirectionHint.Right:
3699 point.X += item_size.Width;
3703 for (int i = 0; i < items.Count; i++) {
3704 Point item_loc = GetItemLocation (i);
3706 if (dir == SearchDirectionHint.Up) {
3707 if (point.Y < item_loc.Y)
3709 } else if (dir == SearchDirectionHint.Down) {
3710 if (point.Y > item_loc.Y)
3712 } else if (dir == SearchDirectionHint.Left) {
3713 if (point.X < item_loc.X)
3715 } else if (dir == SearchDirectionHint.Right) {
3716 if (point.X > item_loc.X)
3720 int x_dist = point.X - item_loc.X;
3721 int y_dist = point.Y - item_loc.Y;
3723 int dist = x_dist * x_dist + y_dist * y_dist;
3724 if (dist < min_dist) {
3734 public ListViewItem GetItemAt (int x, int y)
3736 Size item_size = ItemSize;
3737 for (int i = 0; i < items.Count; i++) {
3738 Point item_location = GetItemLocation (i);
3739 Rectangle item_rect = new Rectangle (item_location, item_size);
3740 if (item_rect.Contains (x, y))
3747 public Rectangle GetItemRect (int index)
3749 return GetItemRect (index, ItemBoundsPortion.Entire);
3752 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3754 if (index < 0 || index >= items.Count)
3755 throw new IndexOutOfRangeException ("index");
3757 return items [index].GetBounds (portion);
3761 public ListViewHitTestInfo HitTest (Point point)
3763 return HitTest (point.X, point.Y);
3766 public ListViewHitTestInfo HitTest (int x, int y)
3769 throw new ArgumentOutOfRangeException ("x");
3771 throw new ArgumentOutOfRangeException ("y");
3773 ListViewItem item = GetItemAt (x, y);
3775 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3777 ListViewHitTestLocations locations = 0;
3778 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3779 locations |= ListViewHitTestLocations.Label;
3780 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3781 locations |= ListViewHitTestLocations.Image;
3782 else if (item.CheckRectReal.Contains (x, y))
3783 locations |= ListViewHitTestLocations.StateImage;
3785 ListViewItem.ListViewSubItem subitem = null;
3786 if (view == View.Details)
3787 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3788 if (si.Bounds.Contains (x, y)) {
3793 return new ListViewHitTestInfo (item, subitem, locations);
3796 [EditorBrowsable (EditorBrowsableState.Advanced)]
3797 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3799 if (startIndex < 0 || startIndex >= items.Count)
3800 throw new ArgumentOutOfRangeException ("startIndex");
3801 if (endIndex < 0 || endIndex >= items.Count)
3802 throw new ArgumentOutOfRangeException ("endIndex");
3803 if (startIndex > endIndex)
3804 throw new ArgumentException ("startIndex");
3809 for (int i = startIndex; i <= endIndex; i++)
3810 item_control.Invalidate (items [i].Bounds);
3812 if (!invalidateOnly)
3821 throw new InvalidOperationException ();
3827 // we need this overload to reuse the logic for sorting, while allowing
3828 // redrawing to be done by caller or have it done by this method when
3829 // sorting is really performed
3831 // ListViewItemCollection's Add and AddRange methods call this overload
3832 // with redraw set to false, as they take care of redrawing themselves
3833 // (they even want to redraw the listview if no sort is performed, as
3834 // an item was added), while ListView.Sort () only wants to redraw if
3835 // sorting was actually performed
3836 private void Sort (bool redraw)
3838 if (!IsHandleCreated || item_sorter == null) {
3842 items.Sort (item_sorter);
3847 public override string ToString ()
3849 int count = this.Items.Count;
3852 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3854 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3856 #endregion // Public Instance Methods
3861 class HeaderControl : Control {
3864 bool column_resize_active = false;
3865 ColumnHeader resize_column;
3866 ColumnHeader clicked_column;
3867 ColumnHeader drag_column;
3869 int drag_to_index = -1;
3871 public HeaderControl (ListView owner)
3874 MouseDown += new MouseEventHandler (HeaderMouseDown);
3875 MouseMove += new MouseEventHandler (HeaderMouseMove);
3876 MouseUp += new MouseEventHandler (HeaderMouseUp);
3879 private ColumnHeader ColumnAtX (int x)
3881 Point pt = new Point (x, 0);
3882 ColumnHeader result = null;
3883 foreach (ColumnHeader col in owner.Columns) {
3884 if (col.Rect.Contains (pt)) {
3892 private int GetReorderedIndex (ColumnHeader col)
3894 if (owner.reordered_column_indices == null)
3897 for (int i = 0; i < owner.Columns.Count; i++)
3898 if (owner.reordered_column_indices [i] == col.Index)
3900 throw new Exception ("Column index missing from reordered array");
3903 private void HeaderMouseDown (object sender, MouseEventArgs me)
3905 if (resize_column != null) {
3906 column_resize_active = true;
3911 clicked_column = ColumnAtX (me.X + owner.h_marker);
3913 if (clicked_column != null) {
3915 if (owner.AllowColumnReorder) {
3917 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3918 drag_column.Rect = clicked_column.Rect;
3919 drag_to_index = GetReorderedIndex (clicked_column);
3921 clicked_column.Pressed = true;
3922 Rectangle bounds = clicked_column.Rect;
3923 bounds.X -= owner.h_marker;
3924 Invalidate (bounds);
3931 column_resize_active = false;
3932 resize_column = null;
3934 Cursor = Cursors.Default;
3937 private void HeaderMouseMove (object sender, MouseEventArgs me)
3939 Point pt = new Point (me.X + owner.h_marker, me.Y);
3941 if (column_resize_active) {
3942 int width = pt.X - resize_column.X;
3946 if (!owner.CanProceedWithResize (resize_column, width)){
3950 resize_column.Width = width;
3954 resize_column = null;
3956 if (clicked_column != null) {
3957 if (owner.AllowColumnReorder) {
3960 r = drag_column.Rect;
3961 r.X = clicked_column.Rect.X + me.X - drag_x;
3962 drag_column.Rect = r;
3964 int x = me.X + owner.h_marker;
3965 ColumnHeader over = ColumnAtX (x);
3967 drag_to_index = owner.Columns.Count;
3968 else if (x < over.X + over.Width / 2)
3969 drag_to_index = GetReorderedIndex (over);
3971 drag_to_index = GetReorderedIndex (over) + 1;
3974 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
3975 bool pressed = clicked_column.Pressed;
3976 clicked_column.Pressed = over == clicked_column;
3977 if (clicked_column.Pressed ^ pressed) {
3978 Rectangle bounds = clicked_column.Rect;
3979 bounds.X -= owner.h_marker;
3980 Invalidate (bounds);
3986 for (int i = 0; i < owner.Columns.Count; i++) {
3987 Rectangle zone = owner.Columns [i].Rect;
3988 zone.X = zone.Right - 5;
3990 if (zone.Contains (pt)) {
3991 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
3993 resize_column = owner.Columns [i];
3998 if (resize_column == null)
3999 Cursor = Cursors.Default;
4001 Cursor = Cursors.VSplit;
4004 void HeaderMouseUp (object sender, MouseEventArgs me)
4008 if (column_resize_active) {
4009 int column_idx = resize_column.Index;
4011 owner.RaiseColumnWidthChanged (column_idx);
4015 if (clicked_column != null && clicked_column.Pressed) {
4016 clicked_column.Pressed = false;
4017 Rectangle bounds = clicked_column.Rect;
4018 bounds.X -= owner.h_marker;
4019 Invalidate (bounds);
4020 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4023 if (drag_column != null && owner.AllowColumnReorder) {
4025 if (drag_to_index > GetReorderedIndex (clicked_column))
4027 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4028 owner.ReorderColumn (clicked_column, drag_to_index, true);
4033 clicked_column = null;
4036 internal override void OnPaintInternal (PaintEventArgs pe)
4041 Theme theme = ThemeEngine.Current;
4042 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4044 if (drag_column == null)
4048 if (drag_to_index == owner.Columns.Count)
4049 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4051 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4052 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4055 protected override void WndProc (ref Message m)
4057 switch ((Msg)m.Msg) {
4058 case Msg.WM_SETFOCUS:
4062 base.WndProc (ref m);
4068 private class ItemComparer : IComparer {
4069 readonly SortOrder sort_order;
4071 public ItemComparer (SortOrder sortOrder)
4073 sort_order = sortOrder;
4076 public int Compare (object x, object y)
4078 ListViewItem item_x = x as ListViewItem;
4079 ListViewItem item_y = y as ListViewItem;
4080 if (sort_order == SortOrder.Ascending)
4081 return String.Compare (item_x.Text, item_y.Text);
4083 return String.Compare (item_y.Text, item_x.Text);
4088 [ListBindable (false)]
4090 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4092 private readonly ListView owner;
4094 #region Public Constructor
4095 public CheckedIndexCollection (ListView owner)
4099 #endregion // Public Constructor
4101 #region Public Properties
4104 get { return owner.CheckedItems.Count; }
4107 public bool IsReadOnly {
4108 get { return true; }
4111 public int this [int index] {
4113 int [] indices = GetIndices ();
4114 if (index < 0 || index >= indices.Length)
4115 throw new ArgumentOutOfRangeException ("index");
4116 return indices [index];
4120 bool ICollection.IsSynchronized {
4121 get { return false; }
4124 object ICollection.SyncRoot {
4125 get { return this; }
4128 bool IList.IsFixedSize {
4129 get { return true; }
4132 object IList.this [int index] {
4133 get { return this [index]; }
4134 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4136 #endregion // Public Properties
4138 #region Public Methods
4139 public bool Contains (int checkedIndex)
4141 int [] indices = GetIndices ();
4142 for (int i = 0; i < indices.Length; i++) {
4143 if (indices [i] == checkedIndex)
4149 public IEnumerator GetEnumerator ()
4151 int [] indices = GetIndices ();
4152 return indices.GetEnumerator ();
4155 void ICollection.CopyTo (Array dest, int index)
4157 int [] indices = GetIndices ();
4158 Array.Copy (indices, 0, dest, index, indices.Length);
4161 int IList.Add (object value)
4163 throw new NotSupportedException ("Add operation is not supported.");
4168 throw new NotSupportedException ("Clear operation is not supported.");
4171 bool IList.Contains (object checkedIndex)
4173 if (!(checkedIndex is int))
4175 return Contains ((int) checkedIndex);
4178 int IList.IndexOf (object checkedIndex)
4180 if (!(checkedIndex is int))
4182 return IndexOf ((int) checkedIndex);
4185 void IList.Insert (int index, object value)
4187 throw new NotSupportedException ("Insert operation is not supported.");
4190 void IList.Remove (object value)
4192 throw new NotSupportedException ("Remove operation is not supported.");
4195 void IList.RemoveAt (int index)
4197 throw new NotSupportedException ("RemoveAt operation is not supported.");
4200 public int IndexOf (int checkedIndex)
4202 int [] indices = GetIndices ();
4203 for (int i = 0; i < indices.Length; i++) {
4204 if (indices [i] == checkedIndex)
4209 #endregion // Public Methods
4211 private int [] GetIndices ()
4213 ArrayList checked_items = owner.CheckedItems.List;
4214 int [] indices = new int [checked_items.Count];
4215 for (int i = 0; i < checked_items.Count; i++) {
4216 ListViewItem item = (ListViewItem) checked_items [i];
4217 indices [i] = item.Index;
4221 } // CheckedIndexCollection
4224 [ListBindable (false)]
4226 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4228 private readonly ListView owner;
4229 private ArrayList list;
4231 #region Public Constructor
4232 public CheckedListViewItemCollection (ListView owner)
4235 this.owner.Items.Changed += new CollectionChangedHandler (
4236 ItemsCollection_Changed);
4238 #endregion // Public Constructor
4240 #region Public Properties
4244 if (!owner.CheckBoxes)
4250 public bool IsReadOnly {
4251 get { return true; }
4254 public ListViewItem this [int index] {
4257 if (owner.VirtualMode)
4258 throw new InvalidOperationException ();
4260 ArrayList checked_items = List;
4261 if (index < 0 || index >= checked_items.Count)
4262 throw new ArgumentOutOfRangeException ("index");
4263 return (ListViewItem) checked_items [index];
4268 public virtual ListViewItem this [string key] {
4270 int idx = IndexOfKey (key);
4271 return idx == -1 ? null : (ListViewItem) List [idx];
4276 bool ICollection.IsSynchronized {
4277 get { return false; }
4280 object ICollection.SyncRoot {
4281 get { return this; }
4284 bool IList.IsFixedSize {
4285 get { return true; }
4288 object IList.this [int index] {
4289 get { return this [index]; }
4290 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4292 #endregion // Public Properties
4294 #region Public Methods
4295 public bool Contains (ListViewItem item)
4297 if (!owner.CheckBoxes)
4299 return List.Contains (item);
4303 public virtual bool ContainsKey (string key)
4305 return IndexOfKey (key) != -1;
4309 public void CopyTo (Array dest, int index)
4312 if (owner.VirtualMode)
4313 throw new InvalidOperationException ();
4315 if (!owner.CheckBoxes)
4317 List.CopyTo (dest, index);
4320 public IEnumerator GetEnumerator ()
4323 if (owner.VirtualMode)
4324 throw new InvalidOperationException ();
4326 if (!owner.CheckBoxes)
4327 return (new ListViewItem [0]).GetEnumerator ();
4328 return List.GetEnumerator ();
4331 int IList.Add (object value)
4333 throw new NotSupportedException ("Add operation is not supported.");
4338 throw new NotSupportedException ("Clear operation is not supported.");
4341 bool IList.Contains (object item)
4343 if (!(item is ListViewItem))
4345 return Contains ((ListViewItem) item);
4348 int IList.IndexOf (object item)
4350 if (!(item is ListViewItem))
4352 return IndexOf ((ListViewItem) item);
4355 void IList.Insert (int index, object value)
4357 throw new NotSupportedException ("Insert operation is not supported.");
4360 void IList.Remove (object value)
4362 throw new NotSupportedException ("Remove operation is not supported.");
4365 void IList.RemoveAt (int index)
4367 throw new NotSupportedException ("RemoveAt operation is not supported.");
4370 public int IndexOf (ListViewItem item)
4373 if (owner.VirtualMode)
4374 throw new InvalidOperationException ();
4376 if (!owner.CheckBoxes)
4378 return List.IndexOf (item);
4382 public virtual int IndexOfKey (string key)
4385 if (owner.VirtualMode)
4386 throw new InvalidOperationException ();
4388 if (key == null || key.Length == 0)
4391 ArrayList checked_items = List;
4392 for (int i = 0; i < checked_items.Count; i++) {
4393 ListViewItem item = (ListViewItem) checked_items [i];
4394 if (String.Compare (key, item.Name, true) == 0)
4401 #endregion // Public Methods
4403 internal ArrayList List {
4406 list = new ArrayList ();
4407 foreach (ListViewItem item in owner.Items) {
4416 internal void Reset ()
4418 // force re-population of list
4422 private void ItemsCollection_Changed ()
4426 } // CheckedListViewItemCollection
4429 [ListBindable (false)]
4431 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4433 internal ArrayList list;
4434 private ListView owner;
4436 #region Public Constructor
4437 public ColumnHeaderCollection (ListView owner)
4439 list = new ArrayList ();
4442 #endregion // Public Constructor
4444 #region Public Properties
4447 get { return list.Count; }
4450 public bool IsReadOnly {
4451 get { return false; }
4454 public virtual ColumnHeader this [int index] {
4456 if (index < 0 || index >= list.Count)
4457 throw new ArgumentOutOfRangeException ("index");
4458 return (ColumnHeader) list [index];
4463 public virtual ColumnHeader this [string key] {
4465 int idx = IndexOfKey (key);
4469 return (ColumnHeader) list [idx];
4474 bool ICollection.IsSynchronized {
4475 get { return true; }
4478 object ICollection.SyncRoot {
4479 get { return this; }
4482 bool IList.IsFixedSize {
4483 get { return list.IsFixedSize; }
4486 object IList.this [int index] {
4487 get { return this [index]; }
4488 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4490 #endregion // Public Properties
4492 #region Public Methods
4493 public virtual int Add (ColumnHeader value)
4495 int idx = list.Add (value);
4496 owner.AddColumn (value, idx, true);
4501 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4505 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4508 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4509 this.Add (colHeader);
4514 public virtual ColumnHeader Add (string text)
4516 return Add (String.Empty, text);
4519 public virtual ColumnHeader Add (string text, int width)
4521 return Add (String.Empty, text, width);
4524 public virtual ColumnHeader Add (string key, string text)
4526 ColumnHeader colHeader = new ColumnHeader ();
4527 colHeader.Name = key;
4528 colHeader.Text = text;
4533 public virtual ColumnHeader Add (string key, string text, int width)
4535 return Add (key, text, width, HorizontalAlignment.Left, -1);
4538 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4540 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4541 colHeader.ImageIndex = imageIndex;
4546 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4548 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4549 colHeader.ImageKey = imageKey;
4555 public virtual void AddRange (ColumnHeader [] values)
4557 foreach (ColumnHeader colHeader in values) {
4558 int idx = list.Add (colHeader);
4559 owner.AddColumn (colHeader, idx, false);
4562 owner.Redraw (true);
4565 public virtual void Clear ()
4567 foreach (ColumnHeader col in list)
4568 col.SetListView (null);
4570 owner.ReorderColumns (new int [0], true);
4573 public bool Contains (ColumnHeader value)
4575 return list.Contains (value);
4579 public virtual bool ContainsKey (string key)
4581 return IndexOfKey (key) != -1;
4585 public IEnumerator GetEnumerator ()
4587 return list.GetEnumerator ();
4590 void ICollection.CopyTo (Array dest, int index)
4592 list.CopyTo (dest, index);
4595 int IList.Add (object value)
4597 if (! (value is ColumnHeader)) {
4598 throw new ArgumentException ("Not of type ColumnHeader", "value");
4601 return this.Add ((ColumnHeader) value);
4604 bool IList.Contains (object value)
4606 if (! (value is ColumnHeader)) {
4607 throw new ArgumentException ("Not of type ColumnHeader", "value");
4610 return this.Contains ((ColumnHeader) value);
4613 int IList.IndexOf (object value)
4615 if (! (value is ColumnHeader)) {
4616 throw new ArgumentException ("Not of type ColumnHeader", "value");
4619 return this.IndexOf ((ColumnHeader) value);
4622 void IList.Insert (int index, object value)
4624 if (! (value is ColumnHeader)) {
4625 throw new ArgumentException ("Not of type ColumnHeader", "value");
4628 this.Insert (index, (ColumnHeader) value);
4631 void IList.Remove (object value)
4633 if (! (value is ColumnHeader)) {
4634 throw new ArgumentException ("Not of type ColumnHeader", "value");
4637 this.Remove ((ColumnHeader) value);
4640 public int IndexOf (ColumnHeader value)
4642 return list.IndexOf (value);
4646 public virtual int IndexOfKey (string key)
4648 if (key == null || key.Length == 0)
4651 for (int i = 0; i < list.Count; i++) {
4652 ColumnHeader col = (ColumnHeader) list [i];
4653 if (String.Compare (key, col.Name, true) == 0)
4661 public void Insert (int index, ColumnHeader value)
4663 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4664 // but it's really only greater.
4665 if (index < 0 || index > list.Count)
4666 throw new ArgumentOutOfRangeException ("index");
4668 list.Insert (index, value);
4669 owner.AddColumn (value, index, true);
4673 public void Insert (int index, string text)
4675 Insert (index, String.Empty, text);
4678 public void Insert (int index, string text, int width)
4680 Insert (index, String.Empty, text, width);
4683 public void Insert (int index, string key, string text)
4685 ColumnHeader colHeader = new ColumnHeader ();
4686 colHeader.Name = key;
4687 colHeader.Text = text;
4688 Insert (index, colHeader);
4691 public void Insert (int index, string key, string text, int width)
4693 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4694 Insert (index, colHeader);
4697 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4699 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4700 colHeader.ImageIndex = imageIndex;
4701 Insert (index, colHeader);
4704 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4706 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4707 colHeader.ImageKey = imageKey;
4708 Insert (index, colHeader);
4713 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4717 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4720 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4721 this.Insert (index, colHeader);
4724 public virtual void Remove (ColumnHeader column)
4726 if (!Contains (column))
4729 list.Remove (column);
4730 column.SetListView (null);
4732 int rem_display_index = column.InternalDisplayIndex;
4733 int [] display_indices = new int [list.Count];
4734 for (int i = 0; i < display_indices.Length; i++) {
4735 ColumnHeader col = (ColumnHeader) list [i];
4736 int display_index = col.InternalDisplayIndex;
4737 if (display_index < rem_display_index) {
4738 display_indices [i] = display_index;
4740 display_indices [i] = (display_index - 1);
4744 column.InternalDisplayIndex = -1;
4745 owner.ReorderColumns (display_indices, true);
4749 public virtual void RemoveByKey (string key)
4751 int idx = IndexOfKey (key);
4757 public virtual void RemoveAt (int index)
4759 if (index < 0 || index >= list.Count)
4760 throw new ArgumentOutOfRangeException ("index");
4762 ColumnHeader col = (ColumnHeader) list [index];
4765 #endregion // Public Methods
4768 } // ColumnHeaderCollection
4771 [ListBindable (false)]
4773 public class ListViewItemCollection : IList, ICollection, IEnumerable
4775 private readonly ArrayList list;
4776 private ListView owner;
4778 private ListViewGroup group;
4781 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4782 // In the later case ListViewItem.ListView never gets modified
4783 private bool is_main_collection = true;
4785 #region Public Constructor
4786 public ListViewItemCollection (ListView owner)
4788 list = new ArrayList (0);
4791 #endregion // Public Constructor
4794 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4797 is_main_collection = false;
4801 #region Public Properties
4806 if (owner != null && owner.VirtualMode)
4807 return owner.VirtualListSize;
4814 public bool IsReadOnly {
4815 get { return false; }
4819 public virtual ListViewItem this [int index] {
4821 public virtual ListViewItem this [int displayIndex] {
4825 int index = displayIndex;
4828 if (index < 0 || index >= Count)
4829 throw new ArgumentOutOfRangeException ("index");
4832 if (owner != null && owner.VirtualMode)
4833 return RetrieveVirtualItemFromOwner (index);
4835 return (ListViewItem) list [index];
4840 int index = displayIndex;
4843 if (index < 0 || index >= Count)
4844 throw new ArgumentOutOfRangeException ("index");
4847 if (owner != null && owner.VirtualMode)
4848 throw new InvalidOperationException ();
4851 if (list.Contains (value))
4852 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4854 if (value.ListView != null && value.ListView != owner)
4855 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");
4857 if (is_main_collection)
4858 value.Owner = owner;
4861 if (value.Group != null)
4862 value.Group.Items.Remove (value);
4864 value.SetGroup (group);
4868 list [index] = value;
4869 CollectionChanged (true);
4874 public virtual ListViewItem this [string key] {
4876 int idx = IndexOfKey (key);
4885 bool ICollection.IsSynchronized {
4886 get { return true; }
4889 object ICollection.SyncRoot {
4890 get { return this; }
4893 bool IList.IsFixedSize {
4894 get { return list.IsFixedSize; }
4897 object IList.this [int index] {
4898 get { return this [index]; }
4900 if (value is ListViewItem)
4901 this [index] = (ListViewItem) value;
4903 this [index] = new ListViewItem (value.ToString ());
4907 #endregion // Public Properties
4909 #region Public Methods
4910 public virtual ListViewItem Add (ListViewItem value)
4913 if (owner != null && owner.VirtualMode)
4914 throw new InvalidOperationException ();
4918 CollectionChanged (true);
4923 public virtual ListViewItem Add (string text)
4925 ListViewItem item = new ListViewItem (text);
4926 return this.Add (item);
4929 public virtual ListViewItem Add (string text, int imageIndex)
4931 ListViewItem item = new ListViewItem (text, imageIndex);
4932 return this.Add (item);
4936 public virtual ListViewItem Add (string text, string imageKey)
4938 ListViewItem item = new ListViewItem (text, imageKey);
4939 return this.Add (item);
4942 public virtual ListViewItem Add (string key, string text, int imageIndex)
4944 ListViewItem item = new ListViewItem (text, imageIndex);
4946 return this.Add (item);
4949 public virtual ListViewItem Add (string key, string text, string imageKey)
4951 ListViewItem item = new ListViewItem (text, imageKey);
4953 return this.Add (item);
4958 public void AddRange (ListViewItem [] items)
4961 public void AddRange (ListViewItem [] values)
4963 ListViewItem [] items = values;
4966 throw new ArgumentNullException ("Argument cannot be null!", "items");
4968 if (owner != null && owner.VirtualMode)
4969 throw new InvalidOperationException ();
4972 foreach (ListViewItem item in items)
4975 CollectionChanged (true);
4979 public void AddRange (ListViewItemCollection items)
4982 throw new ArgumentNullException ("Argument cannot be null!", "items");
4984 ListViewItem[] itemArray = new ListViewItem[items.Count];
4985 items.CopyTo (itemArray,0);
4986 this.AddRange (itemArray);
4990 public virtual void Clear ()
4993 if (owner != null && owner.VirtualMode)
4994 throw new InvalidOperationException ();
4996 if (is_main_collection && owner != null) {
4997 owner.SetFocusedItem (-1);
4998 owner.h_scroll.Value = owner.v_scroll.Value = 0;
5000 foreach (ListViewItem item in list) {
5001 owner.item_control.CancelEdit (item);
5008 foreach (ListViewItem item in list)
5009 item.SetGroup (null);
5013 CollectionChanged (false);
5016 public bool Contains (ListViewItem item)
5018 return IndexOf (item) != -1;
5022 public virtual bool ContainsKey (string key)
5024 return IndexOfKey (key) != -1;
5028 public void CopyTo (Array dest, int index)
5030 list.CopyTo (dest, index);
5034 public ListViewItem [] Find (string key, bool searchAllSubItems)
5037 return new ListViewItem [0];
5039 List<ListViewItem> temp_list = new List<ListViewItem> ();
5041 for (int i = 0; i < list.Count; i++) {
5042 ListViewItem lvi = (ListViewItem) list [i];
5043 if (String.Compare (key, lvi.Name, true) == 0)
5044 temp_list.Add (lvi);
5047 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5048 temp_list.CopyTo (retval);
5054 public IEnumerator GetEnumerator ()
5057 if (owner != null && owner.VirtualMode)
5058 throw new InvalidOperationException ();
5061 return list.GetEnumerator ();
5064 int IList.Add (object item)
5070 if (owner != null && owner.VirtualMode)
5071 throw new InvalidOperationException ();
5074 if (item is ListViewItem) {
5075 li = (ListViewItem) item;
5076 if (list.Contains (li))
5077 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5079 if (li.ListView != null && li.ListView != owner)
5080 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");
5083 li = new ListViewItem (item.ToString ());
5086 result = list.Add (li);
5087 CollectionChanged (true);
5092 bool IList.Contains (object item)
5094 return Contains ((ListViewItem) item);
5097 int IList.IndexOf (object item)
5099 return IndexOf ((ListViewItem) item);
5102 void IList.Insert (int index, object item)
5104 if (item is ListViewItem)
5105 this.Insert (index, (ListViewItem) item);
5107 this.Insert (index, item.ToString ());
5110 void IList.Remove (object item)
5112 Remove ((ListViewItem) item);
5115 public int IndexOf (ListViewItem item)
5118 if (owner != null && owner.VirtualMode) {
5119 for (int i = 0; i < Count; i++)
5120 if (RetrieveVirtualItemFromOwner (i) == item)
5127 return list.IndexOf (item);
5131 public virtual int IndexOfKey (string key)
5133 if (key == null || key.Length == 0)
5136 for (int i = 0; i < Count; i++) {
5137 ListViewItem lvi = this [i];
5138 if (String.Compare (key, lvi.Name, true) == 0)
5146 public ListViewItem Insert (int index, ListViewItem item)
5148 if (index < 0 || index > list.Count)
5149 throw new ArgumentOutOfRangeException ("index");
5152 if (owner != null && owner.VirtualMode)
5153 throw new InvalidOperationException ();
5156 if (list.Contains (item))
5157 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5159 if (item.ListView != null && item.ListView != owner)
5160 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");
5162 if (is_main_collection)
5166 if (item.Group != null)
5167 item.Group.Items.Remove (item);
5169 item.SetGroup (group);
5173 list.Insert (index, item);
5174 CollectionChanged (true);
5178 public ListViewItem Insert (int index, string text)
5180 return this.Insert (index, new ListViewItem (text));
5183 public ListViewItem Insert (int index, string text, int imageIndex)
5185 return this.Insert (index, new ListViewItem (text, imageIndex));
5189 public ListViewItem Insert (int index, string text, string imageKey)
5191 ListViewItem lvi = new ListViewItem (text, imageKey);
5192 return Insert (index, lvi);
5195 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5197 ListViewItem lvi = new ListViewItem (text, imageIndex);
5199 return Insert (index, lvi);
5202 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5204 ListViewItem lvi = new ListViewItem (text, imageKey);
5206 return Insert (index, lvi);
5210 public virtual void Remove (ListViewItem item)
5213 if (owner != null && owner.VirtualMode)
5214 throw new InvalidOperationException ();
5217 int idx = list.IndexOf (item);
5222 public virtual void RemoveAt (int index)
5224 if (index < 0 || index >= Count)
5225 throw new ArgumentOutOfRangeException ("index");
5228 if (owner != null && owner.VirtualMode)
5229 throw new InvalidOperationException ();
5232 ListViewItem item = (ListViewItem) list [index];
5234 bool selection_changed = false;
5235 if (is_main_collection && owner != null) {
5237 if (item.Focused && index + 1 == Count) // Last item
5238 owner.SetFocusedItem (index == 0 ? -1 : index - 1);
5240 selection_changed = owner.SelectedIndices.Contains (index);
5241 owner.item_control.CancelEdit (item);
5244 list.RemoveAt (index);
5246 if (is_main_collection)
5250 item.SetGroup (null);
5253 CollectionChanged (false);
5254 if (selection_changed && owner != null)
5255 owner.OnSelectedIndexChanged (EventArgs.Empty);
5259 public virtual void RemoveByKey (string key)
5261 int idx = IndexOfKey (key);
5267 #endregion // Public Methods
5269 internal ListView Owner {
5279 internal ListViewGroup Group {
5289 void AddItem (ListViewItem value)
5291 if (list.Contains (value))
5292 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5294 if (value.ListView != null && value.ListView != owner)
5295 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");
5296 if (is_main_collection)
5297 value.Owner = owner;
5300 if (value.Group != null)
5301 value.Group.Items.Remove (value);
5303 value.SetGroup (group);
5310 void CollectionChanged (bool sort)
5312 if (owner != null) {
5317 owner.Redraw (true);
5322 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5324 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5326 owner.OnRetrieveVirtualItem (args);
5327 ListViewItem retval = args.Item;
5328 retval.Owner = owner;
5329 retval.DisplayIndex = displayIndex;
5335 internal event CollectionChangedHandler Changed;
5337 internal void Sort (IComparer comparer)
5339 list.Sort (comparer);
5343 internal void OnChange ()
5345 if (Changed != null)
5348 } // ListViewItemCollection
5351 // In normal mode, the selection information resides in the Items,
5352 // making SelectedIndexCollection.List read-only
5354 // In virtual mode, SelectedIndexCollection directly saves the selection
5355 // information, instead of getting it from Items, making List read-and-write
5357 [ListBindable (false)]
5359 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5361 private readonly ListView owner;
5362 private ArrayList list;
5364 #region Public Constructor
5365 public SelectedIndexCollection (ListView owner)
5368 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5370 #endregion // Public Constructor
5372 #region Public Properties
5376 if (!owner.IsHandleCreated)
5383 public bool IsReadOnly {
5393 public int this [int index] {
5395 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5396 throw new ArgumentOutOfRangeException ("index");
5398 return (int) List [index];
5402 bool ICollection.IsSynchronized {
5403 get { return false; }
5406 object ICollection.SyncRoot {
5407 get { return this; }
5410 bool IList.IsFixedSize {
5420 object IList.this [int index] {
5421 get { return this [index]; }
5422 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5424 #endregion // Public Properties
5426 #region Public Methods
5428 public int Add (int itemIndex)
5430 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5431 throw new ArgumentOutOfRangeException ("index");
5433 if (owner.virtual_mode && !owner.IsHandleCreated)
5436 owner.Items [itemIndex].Selected = true;
5438 if (!owner.IsHandleCreated)
5452 if (!owner.IsHandleCreated)
5455 int [] indexes = (int []) List.ToArray (typeof (int));
5456 foreach (int index in indexes)
5457 owner.Items [index].Selected = false;
5460 public bool Contains (int selectedIndex)
5462 return IndexOf (selectedIndex) != -1;
5465 public void CopyTo (Array dest, int index)
5467 List.CopyTo (dest, index);
5470 public IEnumerator GetEnumerator ()
5472 return List.GetEnumerator ();
5475 int IList.Add (object value)
5477 throw new NotSupportedException ("Add operation is not supported.");
5485 bool IList.Contains (object selectedIndex)
5487 if (!(selectedIndex is int))
5489 return Contains ((int) selectedIndex);
5492 int IList.IndexOf (object selectedIndex)
5494 if (!(selectedIndex is int))
5496 return IndexOf ((int) selectedIndex);
5499 void IList.Insert (int index, object value)
5501 throw new NotSupportedException ("Insert operation is not supported.");
5504 void IList.Remove (object value)
5506 throw new NotSupportedException ("Remove operation is not supported.");
5509 void IList.RemoveAt (int index)
5511 throw new NotSupportedException ("RemoveAt operation is not supported.");
5514 public int IndexOf (int selectedIndex)
5516 if (!owner.IsHandleCreated)
5519 return List.IndexOf (selectedIndex);
5523 public void Remove (int itemIndex)
5525 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5526 throw new ArgumentOutOfRangeException ("itemIndex");
5528 owner.Items [itemIndex].Selected = false;
5531 #endregion // Public Methods
5533 internal ArrayList List {
5536 list = new ArrayList ();
5538 if (!owner.VirtualMode)
5540 for (int i = 0; i < owner.Items.Count; i++) {
5541 if (owner.Items [i].Selected)
5549 internal void Reset ()
5551 // force re-population of list
5555 private void ItemsCollection_Changed ()
5561 internal void RemoveIndex (int index)
5563 int idx = List.BinarySearch (index);
5565 List.RemoveAt (idx);
5568 // actually store index in the collection
5569 // also, keep the collection sorted, as .Net does
5570 internal void InsertIndex (int index)
5573 int iMax = List.Count - 1;
5574 while (iMin <= iMax) {
5575 int iMid = (iMin + iMax) / 2;
5576 int current_index = (int) List [iMid];
5578 if (current_index == index)
5579 return; // Already added
5580 if (current_index > index)
5586 List.Insert (iMin, index);
5590 } // SelectedIndexCollection
5593 [ListBindable (false)]
5595 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5597 private readonly ListView owner;
5599 #region Public Constructor
5600 public SelectedListViewItemCollection (ListView owner)
5604 #endregion // Public Constructor
5606 #region Public Properties
5610 return owner.SelectedIndices.Count;
5614 public bool IsReadOnly {
5615 get { return true; }
5618 public ListViewItem this [int index] {
5620 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5621 throw new ArgumentOutOfRangeException ("index");
5623 int item_index = owner.SelectedIndices [index];
5624 return owner.Items [item_index];
5629 public virtual ListViewItem this [string key] {
5631 int idx = IndexOfKey (key);
5640 bool ICollection.IsSynchronized {
5641 get { return false; }
5644 object ICollection.SyncRoot {
5645 get { return this; }
5648 bool IList.IsFixedSize {
5649 get { return true; }
5652 object IList.this [int index] {
5653 get { return this [index]; }
5654 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5656 #endregion // Public Properties
5658 #region Public Methods
5659 public void Clear ()
5661 owner.SelectedIndices.Clear ();
5664 public bool Contains (ListViewItem item)
5666 return IndexOf (item) != -1;
5670 public virtual bool ContainsKey (string key)
5672 return IndexOfKey (key) != -1;
5676 public void CopyTo (Array dest, int index)
5678 if (!owner.IsHandleCreated)
5680 if (index > Count) // Throws ArgumentException instead of IOOR exception
5681 throw new ArgumentException ("index");
5683 for (int i = 0; i < Count; i++)
5684 dest.SetValue (this [i], index++);
5687 public IEnumerator GetEnumerator ()
5689 if (!owner.IsHandleCreated)
5690 return (new ListViewItem [0]).GetEnumerator ();
5692 ListViewItem [] items = new ListViewItem [Count];
5693 for (int i = 0; i < Count; i++)
5694 items [i] = this [i];
5696 return items.GetEnumerator ();
5699 int IList.Add (object value)
5701 throw new NotSupportedException ("Add operation is not supported.");
5704 bool IList.Contains (object item)
5706 if (!(item is ListViewItem))
5708 return Contains ((ListViewItem) item);
5711 int IList.IndexOf (object item)
5713 if (!(item is ListViewItem))
5715 return IndexOf ((ListViewItem) item);
5718 void IList.Insert (int index, object value)
5720 throw new NotSupportedException ("Insert operation is not supported.");
5723 void IList.Remove (object value)
5725 throw new NotSupportedException ("Remove operation is not supported.");
5728 void IList.RemoveAt (int index)
5730 throw new NotSupportedException ("RemoveAt operation is not supported.");
5733 public int IndexOf (ListViewItem item)
5735 if (!owner.IsHandleCreated)
5738 for (int i = 0; i < Count; i++)
5739 if (this [i] == item)
5746 public virtual int IndexOfKey (string key)
5748 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5751 for (int i = 0; i < Count; i++) {
5752 ListViewItem item = this [i];
5753 if (String.Compare (item.Name, key, true) == 0)
5760 #endregion // Public Methods
5762 } // SelectedListViewItemCollection
5764 internal delegate void CollectionChangedHandler ();
5766 struct ItemMatrixLocation
5771 public ItemMatrixLocation (int row, int col)
5798 #endregion // Subclasses
5800 protected override void OnResize (EventArgs e)
5805 protected override void OnMouseLeave (EventArgs e)
5807 base.OnMouseLeave (e);
5811 // ColumnReorder event
5813 static object ColumnReorderedEvent = new object ();
5814 public event ColumnReorderedEventHandler ColumnReordered {
5815 add { Events.AddHandler (ColumnReorderedEvent, value); }
5816 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5819 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5821 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5828 // ColumnWidthChanged
5830 static object ColumnWidthChangedEvent = new object ();
5831 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5832 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5833 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5836 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5838 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5843 void RaiseColumnWidthChanged (int resize_column)
5845 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5847 OnColumnWidthChanged (n);
5851 // ColumnWidthChanging
5853 static object ColumnWidthChangingEvent = new object ();
5854 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5855 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5856 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5859 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5861 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5867 // 2.0 profile based implementation
5869 bool CanProceedWithResize (ColumnHeader col, int width)
5871 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5875 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5876 cwceh (this, changing);
5877 return !changing.Cancel;
5881 // 1.0 profile based implementation
5883 bool CanProceedWithResize (ColumnHeader col, int width)
5888 void RaiseColumnWidthChanged (int resize_column)