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;
1083 #endregion // Public Instance Properties
1085 #region Internal Methods Properties
1087 internal int FirstVisibleIndex {
1090 if (this.items.Count == 0)
1093 if (h_marker == 0 && v_marker == 0)
1096 Size item_size = ItemSize;
1097 for (int i = 0; i < items.Count; i++) {
1098 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
1099 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
1108 internal int LastVisibleIndex {
1110 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
1111 if (View == View.List || Alignment == ListViewAlignment.Left) {
1112 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
1115 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
1120 return Items.Count - 1;
1124 internal void OnSelectedIndexChanged ()
1126 if (IsHandleCreated)
1127 OnSelectedIndexChanged (EventArgs.Empty);
1130 internal int TotalWidth {
1131 get { return Math.Max (this.Width, this.layout_wd); }
1134 internal int TotalHeight {
1135 get { return Math.Max (this.Height, this.layout_ht); }
1138 internal void Redraw (bool recalculate)
1140 // Avoid calculations when control is being updated
1144 // VirtualMode doesn't do any calculations until handle is created
1145 if (virtual_mode && !IsHandleCreated)
1151 CalculateListView (this.alignment);
1156 void InvalidateSelection ()
1158 foreach (int selected_index in SelectedIndices)
1159 items [selected_index].Invalidate ();
1162 const int text_padding = 15;
1164 internal Size GetChildColumnSize (int index)
1166 Size ret_size = Size.Empty;
1167 ColumnHeader col = this.columns [index];
1169 if (col.Width == -2) { // autosize = max(items, columnheader)
1170 Size size = Size.Ceiling (TextRenderer.MeasureString
1171 (col.Text, this.Font));
1172 size.Width += text_padding;
1173 ret_size = BiggestItem (index);
1174 if (size.Width > ret_size.Width)
1177 else { // -1 and all the values < -2 are put under one category
1178 ret_size = BiggestItem (index);
1179 // fall back to empty columns' width if no subitem is available for a column
1180 if (ret_size.IsEmpty) {
1181 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
1182 if (col.Text.Length > 0)
1183 ret_size.Height = Size.Ceiling (TextRenderer.MeasureString
1184 (col.Text, this.Font)).Height;
1186 ret_size.Height = this.Font.Height;
1190 ret_size.Height += text_padding;
1192 // adjust the size for icon and checkbox for 0th column
1194 ret_size.Width += (this.CheckBoxSize.Width + 4);
1195 if (this.small_image_list != null)
1196 ret_size.Width += this.small_image_list.ImageSize.Width;
1201 // Returns the size of biggest item text in a column
1202 // or the sum of the text and indent count if we are on 2.0
1203 private Size BiggestItem (int col)
1205 Size temp = Size.Empty;
1206 Size ret_size = Size.Empty;
1208 bool use_indent_count = small_image_list != null;
1210 // VirtualMode uses the first item text size
1211 if (virtual_mode && items.Count > 0) {
1212 ListViewItem item = items [0];
1213 ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
1216 if (use_indent_count)
1217 ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
1220 // 0th column holds the item text, we check the size of
1221 // the various subitems falling in that column and get
1222 // the biggest one's size.
1223 foreach (ListViewItem item in items) {
1224 if (col >= item.SubItems.Count)
1227 temp = Size.Ceiling (TextRenderer.MeasureString
1228 (item.SubItems [col].Text, Font));
1231 if (use_indent_count)
1232 temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
1235 if (temp.Width > ret_size.Width)
1242 // adjustment for space in Details view
1243 if (!ret_size.IsEmpty && view == View.Details)
1244 ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
1249 const int max_wrap_padding = 30;
1251 // Sets the size of the biggest item text as per the view
1252 private void CalcTextSize ()
1254 // clear the old value
1255 text_size = Size.Empty;
1257 if (items.Count == 0)
1260 text_size = BiggestItem (0);
1262 if (view == View.LargeIcon && this.label_wrap) {
1263 Size temp = Size.Empty;
1264 if (this.check_boxes)
1265 temp.Width += 2 * this.CheckBoxSize.Width;
1266 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1267 temp.Width += icon_w + max_wrap_padding;
1268 // wrapping is done for two lines only
1269 if (text_size.Width > temp.Width) {
1270 text_size.Width = temp.Width;
1271 text_size.Height *= 2;
1274 else if (view == View.List) {
1275 // in list view max text shown in determined by the
1276 // control width, even if scolling is enabled.
1277 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1278 if (this.small_image_list != null)
1279 max_wd -= this.small_image_list.ImageSize.Width;
1281 if (text_size.Width > max_wd)
1282 text_size.Width = max_wd;
1285 // we do the default settings, if we have got 0's
1286 if (text_size.Height <= 0)
1287 text_size.Height = this.Font.Height;
1288 if (text_size.Width <= 0)
1289 text_size.Width = this.Width;
1291 // little adjustment
1292 text_size.Width += 2;
1293 text_size.Height += 2;
1296 private void Scroll (ScrollBar scrollbar, int delta)
1298 if (delta == 0 || !scrollbar.Visible)
1302 if (scrollbar == h_scroll)
1303 max = h_scroll.Maximum - item_control.Width;
1305 max = v_scroll.Maximum - item_control.Height;
1307 int val = scrollbar.Value + delta;
1310 else if (val < scrollbar.Minimum)
1311 val = scrollbar.Minimum;
1312 scrollbar.Value = val;
1315 private void CalculateScrollBars ()
1317 if (!IsHandleCreated)
1320 Rectangle client_area = ClientRectangle;
1323 h_scroll.Visible = false;
1324 v_scroll.Visible = false;
1325 item_control.Height = client_area.Height;
1326 item_control.Width = client_area.Width;
1327 header_control.Width = client_area.Width;
1331 // Don't calculate if the view is not displayable
1332 if (client_area.Height < 0 || client_area.Width < 0)
1335 // making a scroll bar visible might make
1336 // other scroll bar visible
1337 if (layout_wd > client_area.Right) {
1338 h_scroll.Visible = true;
1339 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1340 v_scroll.Visible = true;
1342 v_scroll.Visible = false;
1343 } else if (layout_ht > client_area.Bottom) {
1344 v_scroll.Visible = true;
1345 if ((layout_wd + v_scroll.Width) > client_area.Right)
1346 h_scroll.Visible = true;
1348 h_scroll.Visible = false;
1350 h_scroll.Visible = false;
1351 v_scroll.Visible = false;
1354 item_control.Height = client_area.Height;
1356 if (h_scroll.is_visible) {
1357 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1358 h_scroll.Minimum = 0;
1360 // if v_scroll is visible, adjust the maximum of the
1361 // h_scroll to account for the width of v_scroll
1362 if (v_scroll.Visible) {
1363 h_scroll.Maximum = layout_wd + v_scroll.Width;
1364 h_scroll.Width = client_area.Width - v_scroll.Width;
1367 h_scroll.Maximum = layout_wd;
1368 h_scroll.Width = client_area.Width;
1371 h_scroll.LargeChange = client_area.Width;
1372 h_scroll.SmallChange = Font.Height;
1373 item_control.Height -= h_scroll.Height;
1376 if (header_control.is_visible)
1377 header_control.Width = client_area.Width;
1378 item_control.Width = client_area.Width;
1380 if (v_scroll.is_visible) {
1381 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1382 v_scroll.Minimum = 0;
1384 // if h_scroll is visible, adjust the maximum of the
1385 // v_scroll to account for the height of h_scroll
1386 if (h_scroll.Visible) {
1387 v_scroll.Maximum = layout_ht + h_scroll.Height;
1388 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1390 v_scroll.Maximum = layout_ht;
1391 v_scroll.Height = client_area.Height;
1394 v_scroll.LargeChange = client_area.Height;
1395 v_scroll.SmallChange = Font.Height;
1396 if (header_control.Visible)
1397 header_control.Width -= v_scroll.Width;
1398 item_control.Width -= v_scroll.Width;
1403 internal int GetReorderedColumnIndex (ColumnHeader column)
1405 if (reordered_column_indices == null)
1406 return column.Index;
1408 for (int i = 0; i < Columns.Count; i++)
1409 if (reordered_column_indices [i] == column.Index)
1416 internal ColumnHeader GetReorderedColumn (int index)
1418 if (reordered_column_indices == null)
1419 return Columns [index];
1421 return Columns [reordered_column_indices [index]];
1424 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1428 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1430 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1434 header_control.Invalidate ();
1435 item_control.Invalidate ();
1441 int column_count = Columns.Count;
1443 if (reordered_column_indices == null) {
1444 reordered_column_indices = new int [column_count];
1445 for (int i = 0; i < column_count; i++)
1446 reordered_column_indices [i] = i;
1449 if (reordered_column_indices [index] == col.Index)
1452 int[] curr = reordered_column_indices;
1453 int [] result = new int [column_count];
1455 for (int i = 0; i < column_count; i++) {
1456 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1460 result [i] = col.Index;
1462 result [i] = curr [curr_idx++];
1465 ReorderColumns (result, true);
1468 internal void ReorderColumns (int [] display_indices, bool redraw)
1470 reordered_column_indices = display_indices;
1471 for (int i = 0; i < Columns.Count; i++) {
1472 ColumnHeader col = Columns [i];
1473 col.InternalDisplayIndex = reordered_column_indices [i];
1475 if (redraw && view == View.Details && IsHandleCreated) {
1477 header_control.Invalidate ();
1478 item_control.Invalidate ();
1482 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1484 int column_count = Columns.Count;
1485 newCol.SetListView (this);
1487 int [] display_indices = new int [column_count];
1488 for (int i = 0; i < column_count; i++) {
1489 ColumnHeader col = Columns [i];
1491 display_indices [i] = index;
1493 int display_index = col.InternalDisplayIndex;
1494 if (display_index < index) {
1495 display_indices [i] = display_index;
1497 display_indices [i] = (display_index + 1);
1502 ReorderColumns (display_indices, redraw);
1506 Size LargeIconItemSize
1509 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1510 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1511 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1512 int w = Math.Max (text_size.Width, image_w);
1515 w += 2 + CheckBoxSize.Width;
1517 return new Size (w, h);
1521 Size SmallIconItemSize {
1523 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1524 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1525 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1526 int w = text_size.Width + image_w;
1529 w += 2 + CheckBoxSize.Width;
1531 return new Size (w, h);
1538 // Calculate tile size if needed
1539 // It appears that using Font.Size instead of a SizeF value can give us
1540 // a slightly better approach to the proportions defined in .Net
1541 if (tile_size == Size.Empty) {
1542 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1543 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1544 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1545 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1547 tile_size = new Size (w, h);
1555 int GetDetailsItemHeight ()
1558 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1559 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1560 item_height = Math.Max (checkbox_height, text_size.Height);
1561 item_height = Math.Max (item_height, small_image_height);
1565 void SetItemLocation (int index, int x, int y, int row, int col)
1567 Point old_location = items_location [index];
1568 if (old_location.X == x && old_location.Y == y)
1571 Size item_size = ItemSize;
1572 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1574 items_location [index] = new Point (x, y);
1575 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1578 // Initial position matches item's position in ListViewItemCollection
1580 reordered_items_indices [index] = index;
1582 // Invalidate both previous and new bounds
1583 item_control.Invalidate (old_rect);
1584 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1588 void ShiftItemsPositions (int from, int to, bool forward)
1591 for (int i = to + 1; i > from; i--) {
1592 reordered_items_indices [i] = reordered_items_indices [i - 1];
1594 ListViewItem item = items [reordered_items_indices [i]];
1595 item_control.Invalidate (item.Bounds);
1596 item.DisplayIndex = i;
1597 item_control.Invalidate (item.Bounds);
1600 for (int i = from - 1; i < to; i++) {
1601 reordered_items_indices [i] = reordered_items_indices [i + 1];
1603 ListViewItem item = items [reordered_items_indices [i]];
1604 item_control.Invalidate (item.Bounds);
1605 item.DisplayIndex = i;
1606 item_control.Invalidate (item.Bounds);
1611 internal void ChangeItemLocation (int display_index, Point new_pos)
1613 int new_display_index = GetDisplayIndexFromLocation (new_pos);
1614 if (new_display_index == display_index)
1617 int item_index = reordered_items_indices [display_index];
1618 ListViewItem item = items [item_index];
1620 bool forward = new_display_index < display_index;
1621 int index_from, index_to;
1623 index_from = new_display_index;
1624 index_to = display_index - 1;
1626 index_from = display_index + 1;
1627 index_to = new_display_index;
1630 ShiftItemsPositions (index_from, index_to, forward);
1632 reordered_items_indices [new_display_index] = item_index;
1634 item_control.Invalidate (item.Bounds);
1635 item.DisplayIndex = new_display_index;
1636 item_control.Invalidate (item.Bounds);
1639 int GetDisplayIndexFromLocation (Point loc)
1641 int display_index = -1;
1642 Rectangle item_area;
1645 if (loc.X < 0 || loc.Y < 0)
1648 // Adjustment to put in the next position refered by 'loc'
1649 loc.X -= item_size.Width / 2;
1653 for (int i = 0; i < items.Count; i++) {
1654 item_area = new Rectangle (GetItemLocation (i), item_size);
1655 item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
1656 ThemeEngine.Current.ListViewVerticalSpacing);
1658 if (item_area.Contains (loc)) {
1664 // Put in in last position
1665 if (display_index == -1)
1666 display_index = items.Count - 1;
1668 return display_index;
1671 // When using groups, the items with no group assigned
1672 // belong to the DefaultGroup
1673 int GetDefaultGroupItems ()
1676 foreach (ListViewItem item in items)
1677 if (item.Group == null)
1686 int[,] item_index_matrix;
1688 void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1690 Rectangle area = ClientRectangle;
1692 if (UseCustomColumnWidth)
1693 CalculateCustomColumnWidth ();
1695 if (show_groups && groups.Count > 0 && view != View.List) {
1696 // When groups are used the alignment is always top-aligned
1700 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1701 for (int i = 0; i < groups.InternalCount; i++) {
1702 ListViewGroup group = groups.GetInternalGroup (i);
1703 int items_in_group = group.ItemCount;
1705 if (items_in_group == 0)
1708 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1709 if (group_cols <= 0)
1711 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1713 group.starting_row = rows;
1714 group.rows = group_rows;
1716 cols = Math.Max (group_cols, cols);
1722 // Simple matrix if no groups are used
1724 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1727 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1729 if (UseCustomColumnWidth)
1730 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width) / (double)(custom_column_width));
1732 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1737 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1741 item_index_matrix = new int [rows, cols];
1744 // When using custom column width, we look for the minimum one
1745 void CalculateCustomColumnWidth ()
1747 int min_width = Int32.MaxValue;
1748 for (int i = 0; i < columns.Count; i++) {
1749 int col_width = columns [i].Width;
1751 if (col_width < min_width)
1752 min_width = col_width;
1755 custom_column_width = min_width;
1758 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1760 header_control.Visible = false;
1761 header_control.Size = Size.Empty;
1762 item_control.Visible = true;
1763 item_control.Location = Point.Empty;
1764 ItemSize = item_size; // Cache item size
1766 if (items.Count == 0)
1769 Size sz = item_size;
1771 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1774 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1776 layout_wd = UseCustomColumnWidth ? cols * custom_column_width : cols * (sz.Width + x_spacing) - x_spacing;
1777 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1780 CalculateGroupsLayout (sz, y_spacing, 0);
1783 int row = 0, col = 0;
1786 for (int i = 0; i < items.Count; i++) {
1787 ListViewItem item = items [i];
1790 ListViewGroup group = item.Group;
1792 group = groups.DefaultGroup;
1794 Point group_items_loc = group.items_area_location;
1795 int current_item = group.current_item++;
1796 int starting_row = group.starting_row;
1798 row = (current_item / cols);
1799 col = current_item % cols;
1801 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1802 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1804 SetItemLocation (i, x, y, row + starting_row, col);
1805 item_index_matrix [row + starting_row, col] = i;
1809 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1810 y = row * (item_size.Height + y_spacing);
1812 SetItemLocation (i, x, y, row, col);
1813 item_index_matrix [row, col] = i;
1822 if (++col == cols) {
1833 item.DisplayIndex = i;
1834 item.SetPosition (new Point (x, y));
1840 item_control.Size = new Size (layout_wd, layout_ht);
1844 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1847 bool details = view == View.Details;
1849 for (int i = 0; i < groups.InternalCount; i++) {
1850 ListViewGroup group = groups.GetInternalGroup (i);
1851 if (group.ItemCount == 0)
1854 group.current_item = 0; // Reset layout
1855 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1858 layout_ht = y; // Update height taking into account Groups' headers heights
1861 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1863 Rectangle client_area = ClientRectangle;
1864 int header_height = text_size.Height + 10;
1866 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1867 group.items_area_location = new Point (0, y_origin + header_height);
1869 int items_area_height = ((item_height + y_spacing) * rows);
1870 return header_height + items_area_height + 10; // Add a small bottom margin
1874 void LayoutHeader ()
1877 for (int i = 0; i < Columns.Count; i++) {
1878 ColumnHeader col = GetReorderedColumn (i);
1881 col.CalcColumnHeader ();
1887 if (x < ClientRectangle.Width)
1888 x = ClientRectangle.Width;
1890 if (header_style == ColumnHeaderStyle.None) {
1891 header_control.Visible = false;
1892 header_control.Size = Size.Empty;
1893 layout_wd = ClientRectangle.Width;
1895 header_control.Width = x;
1896 header_control.Height = columns.Count > 0 ? columns [0].Ht : Font.Height + 5;
1897 header_control.Visible = true;
1901 void LayoutDetails ()
1905 if (columns.Count == 0) {
1906 item_control.Visible = false;
1907 layout_wd = ClientRectangle.Width;
1908 layout_ht = ClientRectangle.Height;
1912 item_control.Visible = true;
1913 item_control.Location = Point.Empty;
1914 item_control.Width = ClientRectangle.Width;
1916 int item_height = GetDetailsItemHeight ();
1917 ItemSize = new Size (0, item_height); // We only cache Height for details view
1918 int y = header_control.Height;
1920 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1922 CalculateGroupsLayout (ItemSize, 2, y);
1925 for (int i = 0; i < items.Count; i++) {
1926 ListViewItem item = items [i];
1928 if (!virtual_mode) // Virtual mode sets Layout until draw time
1932 item.DisplayIndex = i;
1937 ListViewGroup group = item.Group;
1939 group = groups.DefaultGroup;
1941 int current_item = group.current_item++;
1942 Point group_items_loc = group.items_area_location;
1944 y = current_item * (item_height + 2) + group_items_loc.Y;
1945 SetItemLocation (i, 0, y, 0, 0);
1946 item.SetPosition (new Point (0, y));
1950 SetItemLocation (i, 0, y, 0, 0);
1951 item.SetPosition (new Point (0, y));
1956 // some space for bottom gridline
1957 if (items.Count > 0 && grid_lines)
1961 if (!using_groups) // With groups it has been previously computed
1966 private void AdjustItemsPositionArray (int count)
1968 if (items_location.Length >= count)
1971 // items_location, items_matrix_location and reordered_items_indices must keep the same length
1972 count = Math.Max (count, items_location.Length * 2);
1973 items_location = new Point [count];
1974 items_matrix_location = new ItemMatrixLocation [count];
1975 reordered_items_indices = new int [count];
1978 private void CalculateListView (ListViewAlignment align)
1982 AdjustItemsPositionArray (items.Count);
1989 case View.SmallIcon:
1990 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
1991 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1994 case View.LargeIcon:
1995 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
1996 ThemeEngine.Current.ListViewHorizontalSpacing,
1997 ThemeEngine.Current.ListViewVerticalSpacing);
2001 LayoutIcons (SmallIconItemSize, true,
2002 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2006 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
2007 ThemeEngine.Current.ListViewHorizontalSpacing,
2008 ThemeEngine.Current.ListViewVerticalSpacing);
2013 CalculateScrollBars ();
2016 internal Point GetItemLocation (int index)
2018 Point loc = items_location [index];
2019 loc.X -= h_marker; // Adjust to scroll
2025 internal int GetItemIndex (int display_index)
2027 return reordered_items_indices [display_index];
2030 private bool KeySearchString (KeyEventArgs ke)
2032 int current_tickcnt = Environment.TickCount;
2033 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2034 keysearch_text = string.Empty;
2037 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2040 keysearch_text += (char)ke.KeyCode;
2041 keysearch_tickcnt = current_tickcnt;
2043 int start = FocusedItem == null ? 0 : FocusedItem.Index;
2046 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
2047 CompareOptions.IgnoreCase)) {
2049 items [i].Selected = true;
2053 i = (i + 1 < Items.Count) ? i+1 : 0;
2061 int GetAdjustedIndex (Keys key)
2065 if (View == View.Details) {
2068 result = FocusedItem.Index - 1;
2071 result = FocusedItem.Index + 1;
2072 if (result == items.Count)
2076 int last_index = LastVisibleIndex;
2077 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2078 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2080 if (FocusedItem.Index == last_index) {
2081 if (FocusedItem.Index < Items.Count - 1) {
2082 int page_size = item_control.Height / ItemSize.Height - 1;
2083 result = FocusedItem.Index + page_size - 1;
2084 if (result >= Items.Count)
2085 result = Items.Count - 1;
2088 result = last_index;
2091 int first_index = FirstVisibleIndex;
2092 if (GetItemLocation (first_index).Y < 0)
2094 if (FocusedItem.Index == first_index) {
2095 if (first_index > 0) {
2096 int page_size = item_control.Height / ItemSize.Height - 1;
2097 result = first_index - page_size + 1;
2102 result = first_index;
2108 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.Index];
2109 int row = item_matrix_location.Row;
2110 int col = item_matrix_location.Col;
2116 return item_index_matrix [row, col - 1];
2119 if (col == (cols - 1))
2121 while (item_index_matrix [row, col + 1] == 0) {
2126 return item_index_matrix [row, col + 1];
2131 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2136 return item_index_matrix [row - 1, col];
2139 if (row == (rows - 1) || row == Items.Count - 1)
2141 while (item_index_matrix [row + 1, col] == 0) {
2146 return item_index_matrix [row + 1, col];
2153 ListViewItem selection_start;
2155 private bool SelectItems (ArrayList sel_items)
2157 bool changed = false;
2158 foreach (ListViewItem item in SelectedItems)
2159 if (!sel_items.Contains (item)) {
2160 item.Selected = false;
2163 foreach (ListViewItem item in sel_items)
2164 if (!item.Selected) {
2165 item.Selected = true;
2171 private void UpdateMultiSelection (int index, bool reselect)
2173 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2174 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2175 ListViewItem item = items [index];
2177 if (shift_pressed && selection_start != null) {
2178 ArrayList list = new ArrayList ();
2179 int start_index = selection_start.Index;
2180 int start = Math.Min (start_index, index);
2181 int end = Math.Max (start_index, index);
2182 if (View == View.Details) {
2183 for (int i = start; i <= end; i++)
2184 list.Add (items [i]);
2186 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2187 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2188 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2189 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2190 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2191 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2193 for (int i = 0; i < items.Count; i++) {
2194 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2196 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2197 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2198 list.Add (items [i]);
2202 } else if (ctrl_pressed) {
2203 item.Selected = !item.Selected;
2204 selection_start = item;
2207 // do not unselect, and reselect the item
2208 foreach (int itemIndex in SelectedIndices) {
2209 if (index == itemIndex)
2211 items [itemIndex].Selected = false;
2214 SelectedItems.Clear ();
2215 item.Selected = true;
2217 selection_start = item;
2221 internal override bool InternalPreProcessMessage (ref Message msg)
2223 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2224 Keys key_data = (Keys)msg.WParam.ToInt32();
2226 HandleNavKeys (key_data);
2229 return base.InternalPreProcessMessage (ref msg);
2232 bool HandleNavKeys (Keys key_data)
2234 if (Items.Count == 0 || !item_control.Visible)
2237 if (FocusedItem == null)
2242 SelectIndex (Items.Count - 1);
2255 SelectIndex (GetAdjustedIndex (key_data));
2259 ToggleItemsCheckState ();
2262 if (selected_indices.Count > 0)
2263 OnItemActivate (EventArgs.Empty);
2273 void ToggleItemsCheckState ()
2278 // Don't modify check state if StateImageList has less than 2 elements
2279 if (StateImageList != null && StateImageList.Images.Count < 2)
2282 if (SelectedIndices.Count > 0) {
2283 for (int i = 0; i < SelectedIndices.Count; i++) {
2284 ListViewItem item = Items [SelectedIndices [i]];
2285 item.Checked = !item.Checked;
2290 if (FocusedItem != null) {
2291 FocusedItem.Checked = !FocusedItem.Checked;
2292 SelectIndex (FocusedItem.Index);
2296 void SelectIndex (int index)
2302 UpdateMultiSelection (index, true);
2303 else if (!items [index].Selected)
2304 items [index].Selected = true;
2306 SetFocusedItem (index);
2307 EnsureVisible (index);
2310 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2312 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2315 if (ke.Alt || ke.Control)
2318 ke.Handled = KeySearchString (ke);
2321 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2323 Point loc = PointToClient (Control.MousePosition);
2324 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2327 internal class ItemControl : Control {
2330 ListViewItem clicked_item;
2331 ListViewItem last_clicked_item;
2332 bool hover_processed = false;
2333 bool checking = false;
2334 ListViewItem prev_hovered_item;
2336 ListViewItem prev_tooltip_item;
2339 Point drag_begin = new Point (-1, -1);
2340 internal int dragged_item_index = -1;
2342 ListViewLabelEditTextBox edit_text_box;
2343 internal ListViewItem edit_item;
2344 LabelEditEventArgs edit_args;
2346 public ItemControl (ListView owner)
2349 DoubleClick += new EventHandler(ItemsDoubleClick);
2350 MouseDown += new MouseEventHandler(ItemsMouseDown);
2351 MouseMove += new MouseEventHandler(ItemsMouseMove);
2352 MouseHover += new EventHandler(ItemsMouseHover);
2353 MouseUp += new MouseEventHandler(ItemsMouseUp);
2356 void ItemsDoubleClick (object sender, EventArgs e)
2358 if (owner.activation == ItemActivation.Standard)
2359 owner.OnItemActivate (EventArgs.Empty);
2369 BoxSelect box_select_mode = BoxSelect.None;
2370 IList prev_selection;
2371 Point box_select_start;
2373 Rectangle box_select_rect;
2374 internal Rectangle BoxSelectRectangle {
2375 get { return box_select_rect; }
2377 if (box_select_rect == value)
2380 InvalidateBoxSelectRect ();
2381 box_select_rect = value;
2382 InvalidateBoxSelectRect ();
2386 void InvalidateBoxSelectRect ()
2388 if (BoxSelectRectangle.Size.IsEmpty)
2391 Rectangle edge = BoxSelectRectangle;
2397 edge.Y = BoxSelectRectangle.Bottom - 1;
2399 edge.Y = BoxSelectRectangle.Y - 1;
2401 edge.Height = BoxSelectRectangle.Height + 2;
2403 edge.X = BoxSelectRectangle.Right - 1;
2407 private Rectangle CalculateBoxSelectRectangle (Point pt)
2409 int left = Math.Min (box_select_start.X, pt.X);
2410 int right = Math.Max (box_select_start.X, pt.X);
2411 int top = Math.Min (box_select_start.Y, pt.Y);
2412 int bottom = Math.Max (box_select_start.Y, pt.Y);
2413 return Rectangle.FromLTRB (left, top, right, bottom);
2416 bool BoxIntersectsItem (int index)
2418 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2419 if (owner.View != View.Details) {
2421 r.Y += r.Height / 4;
2425 return BoxSelectRectangle.IntersectsWith (r);
2428 bool BoxIntersectsText (int index)
2430 Rectangle r = owner.Items [index].TextBounds;
2431 return BoxSelectRectangle.IntersectsWith (r);
2434 ArrayList BoxSelectedItems {
2436 ArrayList result = new ArrayList ();
2437 for (int i = 0; i < owner.Items.Count; i++) {
2439 if (owner.View == View.Details && !owner.FullRowSelect)
2440 intersects = BoxIntersectsText (i);
2442 intersects = BoxIntersectsItem (i);
2445 result.Add (owner.Items [i]);
2451 private bool PerformBoxSelection (Point pt)
2453 if (box_select_mode == BoxSelect.None)
2456 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2458 ArrayList box_items = BoxSelectedItems;
2462 switch (box_select_mode) {
2464 case BoxSelect.Normal:
2468 case BoxSelect.Control:
2469 items = new ArrayList ();
2470 foreach (int index in prev_selection)
2471 if (!box_items.Contains (owner.Items [index]))
2472 items.Add (owner.Items [index]);
2473 foreach (ListViewItem item in box_items)
2474 if (!prev_selection.Contains (item.Index))
2478 case BoxSelect.Shift:
2480 foreach (ListViewItem item in box_items)
2481 prev_selection.Remove (item.Index);
2482 foreach (int index in prev_selection)
2483 items.Add (owner.Items [index]);
2487 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2491 owner.SelectItems (items);
2497 private void ItemsMouseDown (object sender, MouseEventArgs me)
2499 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2500 if (owner.items.Count == 0)
2503 bool box_selecting = false;
2504 Size item_size = owner.ItemSize;
2505 Point pt = new Point (me.X, me.Y);
2506 for (int i = 0; i < owner.items.Count; i++) {
2507 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2508 if (!item_rect.Contains (pt))
2511 // Actual item in 'i' position
2512 ListViewItem item = owner.items [owner.reordered_items_indices [i]];
2514 if (item.CheckRectReal.Contains (pt)) {
2515 // Don't modify check state if we have only one image
2516 // and if we are in 1.1 profile only take into account
2518 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2525 // Generate an extra ItemCheck event when we got two clicks
2526 // (Match weird .Net behaviour)
2528 item.Checked = !item.Checked;
2530 item.Checked = !item.Checked;
2535 if (owner.View == View.Details) {
2536 bool over_text = item.TextBounds.Contains (pt);
2537 if (owner.FullRowSelect) {
2538 clicked_item = owner.items [i];
2539 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2540 if (!over_text && over_item_column && owner.MultiSelect)
2541 box_selecting = true;
2542 } else if (over_text)
2543 clicked_item = item;
2545 owner.SetFocusedItem (i);
2547 clicked_item = item;
2553 if (clicked_item != null) {
2554 bool changed = !clicked_item.Selected;
2555 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2556 owner.SetFocusedItem (clicked_item.Index);
2558 if (owner.MultiSelect) {
2559 bool reselect = (!owner.LabelEdit || changed);
2560 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2561 owner.UpdateMultiSelection (clicked_item.Index, reselect);
2563 clicked_item.Selected = true;
2567 if (owner.VirtualMode && changed) {
2568 // Broken event - It's not fired from Item.Selected also
2569 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2570 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2572 owner.OnVirtualItemsSelectionRangeChanged (args);
2575 // Report clicks only if the item was clicked. On MS the
2576 // clicks are only raised if you click an item
2578 if (me.Clicks > 1) {
2579 if (owner.CheckBoxes)
2580 clicked_item.Checked = !clicked_item.Checked;
2581 } else if (me.Clicks == 1) {
2582 if (owner.LabelEdit && !changed)
2583 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2586 if (owner.MultiSelect)
2587 box_selecting = true;
2588 else if (owner.SelectedItems.Count > 0)
2589 owner.SelectedItems.Clear ();
2592 if (box_selecting) {
2593 Keys mods = XplatUI.State.ModifierKeys;
2594 if ((mods & Keys.Shift) != 0)
2595 box_select_mode = BoxSelect.Shift;
2596 else if ((mods & Keys.Control) != 0)
2597 box_select_mode = BoxSelect.Control;
2599 box_select_mode = BoxSelect.Normal;
2600 box_select_start = pt;
2601 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2605 private void ItemsMouseMove (object sender, MouseEventArgs me)
2607 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2609 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2613 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2614 !hover_processed && owner.Activation != ItemActivation.OneClick
2616 && !owner.ShowItemToolTips
2621 Point pt = PointToClient (Control.MousePosition);
2622 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2624 if (hover_processed && item != null && item != prev_hovered_item) {
2625 hover_processed = false;
2626 XplatUI.ResetMouseHover (Handle);
2629 // Need to invalidate the item in HotTracking to show/hide the underline style
2630 if (owner.Activation == ItemActivation.OneClick) {
2631 if (item == null && owner.HotItemIndex != -1) {
2633 if (owner.HotTracking)
2634 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2637 Cursor = Cursors.Default;
2638 owner.HotItemIndex = -1;
2639 } else if (item != null && owner.HotItemIndex == -1) {
2641 if (owner.HotTracking)
2642 Invalidate (item.Bounds);
2645 Cursor = Cursors.Hand;
2646 owner.HotItemIndex = item.Index;
2650 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2651 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2653 drag_begin = new Point (me.X, me.Y);
2654 dragged_item_index = item.Index;
2658 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2659 if (!r.Contains (me.X, me.Y)) {
2660 ListViewItem dragged_item = owner.items [dragged_item_index];
2661 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2663 drag_begin = new Point (-1, -1);
2664 dragged_item_index = -1;
2670 if (owner.ShowItemToolTips) {
2672 owner.item_tooltip.Active = false;
2673 prev_tooltip_item = null;
2674 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2675 owner.item_tooltip.Active = true;
2676 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2677 prev_tooltip_item = item;
2684 private void ItemsMouseHover (object sender, EventArgs e)
2686 if (owner.hover_pending) {
2687 owner.OnMouseHover (e);
2688 owner.hover_pending = false;
2694 hover_processed = true;
2695 Point pt = PointToClient (Control.MousePosition);
2696 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2700 prev_hovered_item = item;
2702 if (owner.HoverSelection) {
2703 if (owner.MultiSelect)
2704 owner.UpdateMultiSelection (item.Index, true);
2706 item.Selected = true;
2708 owner.SetFocusedItem (item.Index);
2709 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2713 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2717 void HandleClicks (MouseEventArgs me)
2719 // if the click is not on an item,
2720 // clicks remains as 0
2723 owner.OnDoubleClick (EventArgs.Empty);
2724 } else if (clicks == 1) {
2725 owner.OnClick (EventArgs.Empty);
2727 owner.OnDoubleClick (EventArgs.Empty);
2728 owner.OnMouseDoubleClick (me);
2729 } else if (clicks == 1) {
2730 owner.OnClick (EventArgs.Empty);
2731 owner.OnMouseClick (me);
2738 private void ItemsMouseUp (object sender, MouseEventArgs me)
2740 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2741 HandleClicks (owner_me);
2744 if (owner.Items.Count == 0) {
2746 owner.OnMouseUp (owner_me);
2750 Point pt = new Point (me.X, me.Y);
2752 Rectangle rect = Rectangle.Empty;
2753 if (clicked_item != null) {
2754 if (owner.view == View.Details && !owner.full_row_select)
2755 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2757 rect = clicked_item.Bounds;
2759 if (rect.Contains (pt)) {
2760 switch (owner.activation) {
2761 case ItemActivation.OneClick:
2762 owner.OnItemActivate (EventArgs.Empty);
2765 case ItemActivation.TwoClick:
2766 if (last_clicked_item == clicked_item) {
2767 owner.OnItemActivate (EventArgs.Empty);
2768 last_clicked_item = null;
2770 last_clicked_item = clicked_item;
2773 // DoubleClick activation is handled in another handler
2777 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2778 // Need this to clean up background clicks
2779 owner.SelectedItems.Clear ();
2783 owner.OnMouseUp (owner_me);
2786 private void ResetMouseState ()
2788 clicked_item = null;
2789 box_select_start = Point.Empty;
2790 BoxSelectRectangle = Rectangle.Empty;
2791 prev_selection = null;
2792 box_select_mode = BoxSelect.None;
2795 // Clean these bits in case the mouse buttons were
2796 // released before firing ItemDrag
2797 dragged_item_index = -1;
2798 drag_begin = new Point (-1, -1);
2801 private void LabelEditFinished (object sender, EventArgs e)
2803 EndEdit (edit_item);
2806 private void LabelEditCancelled (object sender, EventArgs e)
2808 edit_args.SetLabel (null);
2809 EndEdit (edit_item);
2812 private void LabelTextChanged (object sender, EventArgs e)
2814 if (edit_args != null)
2815 edit_args.SetLabel (edit_text_box.Text);
2818 internal void BeginEdit (ListViewItem item)
2820 if (edit_item != null)
2821 EndEdit (edit_item);
2823 if (edit_text_box == null) {
2824 edit_text_box = new ListViewLabelEditTextBox ();
2825 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2826 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2827 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2828 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2829 edit_text_box.Visible = false;
2830 Controls.Add (edit_text_box);
2833 item.EnsureVisible();
2835 edit_text_box.Reset ();
2837 switch (owner.view) {
2839 case View.SmallIcon:
2841 edit_text_box.TextAlign = HorizontalAlignment.Left;
2842 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2843 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2844 edit_text_box.Width = (int)sizef.Width + 4;
2845 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2846 edit_text_box.WordWrap = false;
2847 edit_text_box.Multiline = false;
2849 case View.LargeIcon:
2850 edit_text_box.TextAlign = HorizontalAlignment.Center;
2851 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2852 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2853 edit_text_box.Width = (int)sizef.Width + 4;
2854 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2855 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2856 edit_text_box.WordWrap = true;
2857 edit_text_box.Multiline = true;
2863 edit_text_box.Text = item.Text;
2864 edit_text_box.Font = item.Font;
2865 edit_text_box.Visible = true;
2866 edit_text_box.Focus ();
2867 edit_text_box.SelectAll ();
2869 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2870 owner.OnBeforeLabelEdit (edit_args);
2872 if (edit_args.CancelEdit)
2876 internal void CancelEdit (ListViewItem item)
2878 // do nothing if there's no item being edited, or if the
2879 // item being edited is not the one passed in
2880 if (edit_item == null || edit_item != item)
2883 edit_args.SetLabel (null);
2887 internal void EndEdit (ListViewItem item)
2889 // do nothing if there's no item being edited, or if the
2890 // item being edited is not the one passed in
2891 if (edit_item == null || edit_item != item)
2894 if (edit_text_box != null) {
2895 if (edit_text_box.Visible)
2896 edit_text_box.Visible = false;
2897 // ensure listview gets focus
2901 // Same as TreeView.EndEdit: need to have focus in synch
2902 Application.DoEvents ();
2905 // Create a new instance, since we could get a call to BeginEdit
2906 // from the handler and have fields out of synch
2908 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2911 owner.OnAfterLabelEdit (args);
2912 if (!args.CancelEdit && args.Label != null)
2913 item.Text = args.Label;
2916 internal override void OnPaintInternal (PaintEventArgs pe)
2918 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2921 protected override void WndProc (ref Message m)
2923 switch ((Msg)m.Msg) {
2924 case Msg.WM_KILLFOCUS:
2925 owner.Select (false, true);
2927 case Msg.WM_SETFOCUS:
2928 owner.Select (false, true);
2930 case Msg.WM_LBUTTONDOWN:
2932 owner.Select (false, true);
2934 case Msg.WM_RBUTTONDOWN:
2936 owner.Select (false, true);
2941 base.WndProc (ref m);
2945 internal class ListViewLabelEditTextBox : TextBox
2950 int max_height = -1;
2951 int min_height = -1;
2953 int old_number_lines = 1;
2955 SizeF text_size_one_char;
2957 public ListViewLabelEditTextBox ()
2959 min_height = DefaultSize.Height;
2960 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2963 public int MaxWidth {
2965 if (value < min_width)
2966 max_width = min_width;
2972 public int MaxHeight {
2974 if (value < min_height)
2975 max_height = min_height;
2981 public new int Width {
2991 public override Font Font {
2997 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3001 protected override void OnTextChanged (EventArgs e)
3003 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3005 int new_width = (int)text_size.Width + 8;
3008 ResizeTextBoxWidth (new_width);
3010 if (Width != max_width)
3011 ResizeTextBoxWidth (new_width);
3013 int number_lines = Lines.Length;
3015 if (number_lines != old_number_lines) {
3016 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3017 old_number_lines = number_lines;
3019 ResizeTextBoxHeight (new_height);
3023 base.OnTextChanged (e);
3026 protected override bool IsInputKey (Keys key_data)
3028 if ((key_data & Keys.Alt) == 0) {
3029 switch (key_data & Keys.KeyCode) {
3036 return base.IsInputKey (key_data);
3039 protected override void OnKeyDown (KeyEventArgs e)
3044 switch (e.KeyCode) {
3048 OnEditingFinished (e);
3053 OnEditingCancelled (e);
3058 protected override void OnLostFocus (EventArgs e)
3061 OnEditingFinished (e);
3065 protected void OnEditingCancelled (EventArgs e)
3067 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3072 protected void OnEditingFinished (EventArgs e)
3074 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3079 private void ResizeTextBoxWidth (int new_width)
3081 if (new_width > max_width)
3082 base.Width = max_width;
3084 if (new_width >= min_width)
3085 base.Width = new_width;
3087 base.Width = min_width;
3090 private void ResizeTextBoxHeight (int new_height)
3092 if (new_height > max_height)
3093 base.Height = max_height;
3095 if (new_height >= min_height)
3096 base.Height = new_height;
3098 base.Height = min_height;
3101 public void Reset ()
3108 old_number_lines = 1;
3110 Text = String.Empty;
3115 static object EditingCancelledEvent = new object ();
3116 public event EventHandler EditingCancelled {
3117 add { Events.AddHandler (EditingCancelledEvent, value); }
3118 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3121 static object EditingFinishedEvent = new object ();
3122 public event EventHandler EditingFinished {
3123 add { Events.AddHandler (EditingFinishedEvent, value); }
3124 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3128 internal override void OnPaintInternal (PaintEventArgs pe)
3133 CalculateScrollBars ();
3136 void FocusChanged (object o, EventArgs args)
3138 if (Items.Count == 0)
3141 if (FocusedItem == null)
3144 ListViewItem focused_item = FocusedItem;
3146 if (focused_item.ListView != null) {
3147 item_control.Invalidate (focused_item.Bounds);
3148 focused_item.Layout ();
3149 item_control.Invalidate (focused_item.Bounds);
3153 private void ListView_MouseEnter (object sender, EventArgs args)
3155 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3158 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3160 if (Items.Count == 0)
3163 int lines = me.Delta / 120;
3170 case View.SmallIcon:
3171 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3173 case View.LargeIcon:
3174 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3177 Scroll (h_scroll, -ItemSize.Width * lines);
3181 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3187 private void ListView_SizeChanged (object sender, EventArgs e)
3189 CalculateListView (alignment);
3192 private void SetFocusedItem (int index)
3195 items [index].Focused = true;
3196 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3197 items [focused_item_index].Focused = false;
3199 focused_item_index = index;
3202 private void HorizontalScroller (object sender, EventArgs e)
3204 item_control.EndEdit (item_control.edit_item);
3206 // Avoid unnecessary flickering, when button is
3207 // kept pressed at the end
3208 if (h_marker != h_scroll.Value) {
3210 int pixels = h_marker - h_scroll.Value;
3212 h_marker = h_scroll.Value;
3213 if (header_control.Visible)
3214 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3216 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3220 private void VerticalScroller (object sender, EventArgs e)
3222 item_control.EndEdit (item_control.edit_item);
3224 // Avoid unnecessary flickering, when button is
3225 // kept pressed at the end
3226 if (v_marker != v_scroll.Value) {
3227 int pixels = v_marker - v_scroll.Value;
3228 Rectangle area = item_control.ClientRectangle;
3229 if (header_control.Visible) {
3230 area.Y += header_control.Height;
3231 area.Height -= header_control.Height;
3234 v_marker = v_scroll.Value;
3235 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3239 internal override bool IsInputCharInternal (char charCode)
3243 #endregion // Internal Methods Properties
3245 #region Protected Methods
3246 protected override void CreateHandle ()
3248 base.CreateHandle ();
3249 for (int i = 0; i < SelectedItems.Count; i++)
3250 OnSelectedIndexChanged (EventArgs.Empty);
3253 protected override void Dispose (bool disposing)
3256 h_scroll.Dispose ();
3257 v_scroll.Dispose ();
3259 large_image_list = null;
3260 small_image_list = null;
3261 state_image_list = null;
3263 foreach (ColumnHeader col in columns)
3264 col.SetListView (null);
3267 if (!virtual_mode) // In virtual mode we don't save the items
3269 foreach (ListViewItem item in items)
3273 base.Dispose (disposing);
3276 protected override bool IsInputKey (Keys keyData)
3293 return base.IsInputKey (keyData);
3296 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3298 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3304 protected override void OnBackgroundImageChanged (EventArgs e)
3306 item_control.BackgroundImage = BackgroundImage;
3307 base.OnBackgroundImageChanged (e);
3311 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3313 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3318 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3320 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3326 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3328 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3333 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3335 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3340 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3342 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3348 protected override void OnEnabledChanged (EventArgs e)
3350 base.OnEnabledChanged (e);
3354 protected override void OnFontChanged (EventArgs e)
3356 base.OnFontChanged (e);
3360 protected override void OnHandleCreated (EventArgs e)
3362 base.OnHandleCreated (e);
3363 CalculateListView (alignment);
3365 if (!virtual_mode) // Sorting is not allowed in virtual mode
3370 protected override void OnHandleDestroyed (EventArgs e)
3372 base.OnHandleDestroyed (e);
3375 protected virtual void OnItemActivate (EventArgs e)
3377 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3382 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3384 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3390 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3392 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3398 protected virtual void OnItemDrag (ItemDragEventArgs e)
3400 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3406 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3408 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3413 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3415 ListViewItemSelectionChangedEventHandler eh =
3416 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3421 protected override void OnMouseHover (EventArgs e)
3423 base.OnMouseHover (e);
3426 protected override void OnParentChanged (EventArgs e)
3428 base.OnParentChanged (e);
3432 protected virtual void OnSelectedIndexChanged (EventArgs e)
3434 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3439 protected override void OnSystemColorsChanged (EventArgs e)
3441 base.OnSystemColorsChanged (e);
3445 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3447 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3452 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3454 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3459 [EditorBrowsable (EditorBrowsableState.Advanced)]
3460 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3462 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3467 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3469 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3474 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3476 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3477 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3483 protected void RealizeProperties ()
3488 protected void UpdateExtendedStyles ()
3493 bool refocusing = false;
3495 protected override void WndProc (ref Message m)
3497 switch ((Msg)m.Msg) {
3498 case Msg.WM_KILLFOCUS:
3499 Control receiver = Control.FromHandle (m.WParam);
3500 if (receiver == item_control) {
3506 case Msg.WM_SETFOCUS:
3516 base.WndProc (ref m);
3518 #endregion // Protected Methods
3520 #region Public Instance Methods
3521 public void ArrangeIcons ()
3523 ArrangeIcons (this.alignment);
3526 public void ArrangeIcons (ListViewAlignment value)
3528 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3529 if (view == View.LargeIcon || view == View.SmallIcon) {
3530 this.CalculateListView (value);
3531 // we have done the calculations already
3532 this.Redraw (false);
3537 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3539 if (columnIndex < 0 || columnIndex >= columns.Count)
3540 throw new ArgumentOutOfRangeException ("columnIndex");
3542 columns [columnIndex].AutoResize (headerAutoResize);
3545 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3548 foreach (ColumnHeader col in columns)
3549 col.AutoResize (headerAutoResize);
3554 public void BeginUpdate ()
3556 // flag to avoid painting
3560 public void Clear ()
3563 items.Clear (); // Redraw (true) called here
3566 public void EndUpdate ()
3568 // flag to avoid painting
3571 // probably, now we need a redraw with recalculations
3575 public void EnsureVisible (int index)
3577 if (index < 0 || index >= items.Count || scrollable == false)
3580 Rectangle view_rect = item_control.ClientRectangle;
3581 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3583 if (view_rect.Contains (bounds))
3586 if (View != View.Details) {
3587 if (bounds.Left < 0)
3588 h_scroll.Value += bounds.Left;
3589 else if (bounds.Right > view_rect.Right)
3590 h_scroll.Value += (bounds.Right - view_rect.Right);
3594 v_scroll.Value += bounds.Top;
3595 else if (bounds.Bottom > view_rect.Bottom)
3596 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3600 public ListViewItem FindItemWithText (string text)
3602 if (items.Count == 0)
3605 return FindItemWithText (text, true, 0, true);
3608 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3610 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true);
3613 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3615 if (startIndex < 0 || startIndex >= items.Count)
3616 throw new ArgumentOutOfRangeException ("startIndex");
3619 throw new ArgumentNullException ("text");
3622 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3623 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3624 SearchDirectionHint.Down, startIndex);
3626 OnSearchForVirtualItem (args);
3627 int idx = args.Index;
3628 if (idx >= 0 && idx < virtual_list_size)
3634 for (int i = startIndex; i < items.Count; i++) {
3635 ListViewItem lvi = items [i];
3637 if ((isPrefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3638 || String.Compare (lvi.Text, text, true) == 0) // match
3642 if (includeSubItemsInSearch) {
3643 for (int i = startIndex; i < items.Count; i++) {
3644 ListViewItem lvi = items [i];
3645 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3646 if ((isPrefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3647 || String.Compare (sub_item.Text, text, true) == 0)
3655 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3657 return FindNearestItem (searchDirection, new Point (x, y));
3660 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3662 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3663 throw new ArgumentOutOfRangeException ("searchDirection");
3665 if (view != View.LargeIcon && view != View.SmallIcon)
3666 throw new InvalidOperationException ();
3669 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3670 false, false, String.Empty, point,
3673 OnSearchForVirtualItem (args);
3674 int idx = args.Index;
3675 if (idx >= 0 && idx < virtual_list_size)
3681 ListViewItem item = null;
3682 int min_dist = Int32.MaxValue;
3685 // It looks like .Net does a previous adjustment
3688 case SearchDirectionHint.Up:
3689 point.Y -= item_size.Height;
3691 case SearchDirectionHint.Down:
3692 point.Y += item_size.Height;
3694 case SearchDirectionHint.Left:
3695 point.X -= item_size.Width;
3697 case SearchDirectionHint.Right:
3698 point.X += item_size.Width;
3702 for (int i = 0; i < items.Count; i++) {
3703 Point item_loc = GetItemLocation (i);
3705 if (dir == SearchDirectionHint.Up) {
3706 if (point.Y < item_loc.Y)
3708 } else if (dir == SearchDirectionHint.Down) {
3709 if (point.Y > item_loc.Y)
3711 } else if (dir == SearchDirectionHint.Left) {
3712 if (point.X < item_loc.X)
3714 } else if (dir == SearchDirectionHint.Right) {
3715 if (point.X > item_loc.X)
3719 int x_dist = point.X - item_loc.X;
3720 int y_dist = point.Y - item_loc.Y;
3722 int dist = x_dist * x_dist + y_dist * y_dist;
3723 if (dist < min_dist) {
3733 public ListViewItem GetItemAt (int x, int y)
3735 Size item_size = ItemSize;
3736 for (int i = 0; i < items.Count; i++) {
3737 Point item_location = GetItemLocation (i);
3738 Rectangle item_rect = new Rectangle (item_location, item_size);
3739 if (item_rect.Contains (x, y))
3746 public Rectangle GetItemRect (int index)
3748 return GetItemRect (index, ItemBoundsPortion.Entire);
3751 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3753 if (index < 0 || index >= items.Count)
3754 throw new IndexOutOfRangeException ("index");
3756 return items [index].GetBounds (portion);
3760 public ListViewHitTestInfo HitTest (Point point)
3762 return HitTest (point.X, point.Y);
3765 public ListViewHitTestInfo HitTest (int x, int y)
3768 throw new ArgumentOutOfRangeException ("x");
3770 throw new ArgumentOutOfRangeException ("y");
3772 ListViewItem item = GetItemAt (x, y);
3774 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3776 ListViewHitTestLocations locations = 0;
3777 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3778 locations |= ListViewHitTestLocations.Label;
3779 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3780 locations |= ListViewHitTestLocations.Image;
3781 else if (item.CheckRectReal.Contains (x, y))
3782 locations |= ListViewHitTestLocations.StateImage;
3784 ListViewItem.ListViewSubItem subitem = null;
3785 if (view == View.Details)
3786 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3787 if (si.Bounds.Contains (x, y)) {
3792 return new ListViewHitTestInfo (item, subitem, locations);
3795 [EditorBrowsable (EditorBrowsableState.Advanced)]
3796 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3798 if (startIndex < 0 || startIndex >= items.Count)
3799 throw new ArgumentOutOfRangeException ("startIndex");
3800 if (endIndex < 0 || endIndex >= items.Count)
3801 throw new ArgumentOutOfRangeException ("endIndex");
3802 if (startIndex > endIndex)
3803 throw new ArgumentException ("startIndex");
3808 for (int i = startIndex; i <= endIndex; i++)
3809 item_control.Invalidate (items [i].Bounds);
3811 if (!invalidateOnly)
3820 throw new InvalidOperationException ();
3826 // we need this overload to reuse the logic for sorting, while allowing
3827 // redrawing to be done by caller or have it done by this method when
3828 // sorting is really performed
3830 // ListViewItemCollection's Add and AddRange methods call this overload
3831 // with redraw set to false, as they take care of redrawing themselves
3832 // (they even want to redraw the listview if no sort is performed, as
3833 // an item was added), while ListView.Sort () only wants to redraw if
3834 // sorting was actually performed
3835 private void Sort (bool redraw)
3837 if (!IsHandleCreated || item_sorter == null) {
3841 items.Sort (item_sorter);
3846 public override string ToString ()
3848 int count = this.Items.Count;
3851 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3853 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3855 #endregion // Public Instance Methods
3860 class HeaderControl : Control {
3863 bool column_resize_active = false;
3864 ColumnHeader resize_column;
3865 ColumnHeader clicked_column;
3866 ColumnHeader drag_column;
3868 int drag_to_index = -1;
3870 public HeaderControl (ListView owner)
3873 MouseDown += new MouseEventHandler (HeaderMouseDown);
3874 MouseMove += new MouseEventHandler (HeaderMouseMove);
3875 MouseUp += new MouseEventHandler (HeaderMouseUp);
3878 private ColumnHeader ColumnAtX (int x)
3880 Point pt = new Point (x, 0);
3881 ColumnHeader result = null;
3882 foreach (ColumnHeader col in owner.Columns) {
3883 if (col.Rect.Contains (pt)) {
3891 private int GetReorderedIndex (ColumnHeader col)
3893 if (owner.reordered_column_indices == null)
3896 for (int i = 0; i < owner.Columns.Count; i++)
3897 if (owner.reordered_column_indices [i] == col.Index)
3899 throw new Exception ("Column index missing from reordered array");
3902 private void HeaderMouseDown (object sender, MouseEventArgs me)
3904 if (resize_column != null) {
3905 column_resize_active = true;
3910 clicked_column = ColumnAtX (me.X + owner.h_marker);
3912 if (clicked_column != null) {
3914 if (owner.AllowColumnReorder) {
3916 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3917 drag_column.Rect = clicked_column.Rect;
3918 drag_to_index = GetReorderedIndex (clicked_column);
3920 clicked_column.Pressed = true;
3921 Rectangle bounds = clicked_column.Rect;
3922 bounds.X -= owner.h_marker;
3923 Invalidate (bounds);
3930 column_resize_active = false;
3931 resize_column = null;
3933 Cursor = Cursors.Default;
3936 private void HeaderMouseMove (object sender, MouseEventArgs me)
3938 Point pt = new Point (me.X + owner.h_marker, me.Y);
3940 if (column_resize_active) {
3941 int width = pt.X - resize_column.X;
3945 if (!owner.CanProceedWithResize (resize_column, width)){
3949 resize_column.Width = width;
3953 resize_column = null;
3955 if (clicked_column != null) {
3956 if (owner.AllowColumnReorder) {
3959 r = drag_column.Rect;
3960 r.X = clicked_column.Rect.X + me.X - drag_x;
3961 drag_column.Rect = r;
3963 int x = me.X + owner.h_marker;
3964 ColumnHeader over = ColumnAtX (x);
3966 drag_to_index = owner.Columns.Count;
3967 else if (x < over.X + over.Width / 2)
3968 drag_to_index = GetReorderedIndex (over);
3970 drag_to_index = GetReorderedIndex (over) + 1;
3973 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
3974 bool pressed = clicked_column.Pressed;
3975 clicked_column.Pressed = over == clicked_column;
3976 if (clicked_column.Pressed ^ pressed) {
3977 Rectangle bounds = clicked_column.Rect;
3978 bounds.X -= owner.h_marker;
3979 Invalidate (bounds);
3985 for (int i = 0; i < owner.Columns.Count; i++) {
3986 Rectangle zone = owner.Columns [i].Rect;
3987 zone.X = zone.Right - 5;
3989 if (zone.Contains (pt)) {
3990 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
3992 resize_column = owner.Columns [i];
3997 if (resize_column == null)
3998 Cursor = Cursors.Default;
4000 Cursor = Cursors.VSplit;
4003 void HeaderMouseUp (object sender, MouseEventArgs me)
4007 if (column_resize_active) {
4008 int column_idx = resize_column.Index;
4010 owner.RaiseColumnWidthChanged (column_idx);
4014 if (clicked_column != null && clicked_column.Pressed) {
4015 clicked_column.Pressed = false;
4016 Rectangle bounds = clicked_column.Rect;
4017 bounds.X -= owner.h_marker;
4018 Invalidate (bounds);
4019 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4022 if (drag_column != null && owner.AllowColumnReorder) {
4024 if (drag_to_index > GetReorderedIndex (clicked_column))
4026 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4027 owner.ReorderColumn (clicked_column, drag_to_index, true);
4032 clicked_column = null;
4035 internal override void OnPaintInternal (PaintEventArgs pe)
4040 Theme theme = ThemeEngine.Current;
4041 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4043 if (drag_column == null)
4047 if (drag_to_index == owner.Columns.Count)
4048 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4050 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4051 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4054 protected override void WndProc (ref Message m)
4056 switch ((Msg)m.Msg) {
4057 case Msg.WM_SETFOCUS:
4061 base.WndProc (ref m);
4067 private class ItemComparer : IComparer {
4068 readonly SortOrder sort_order;
4070 public ItemComparer (SortOrder sortOrder)
4072 sort_order = sortOrder;
4075 public int Compare (object x, object y)
4077 ListViewItem item_x = x as ListViewItem;
4078 ListViewItem item_y = y as ListViewItem;
4079 if (sort_order == SortOrder.Ascending)
4080 return String.Compare (item_x.Text, item_y.Text);
4082 return String.Compare (item_y.Text, item_x.Text);
4087 [ListBindable (false)]
4089 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4091 private readonly ListView owner;
4093 #region Public Constructor
4094 public CheckedIndexCollection (ListView owner)
4098 #endregion // Public Constructor
4100 #region Public Properties
4103 get { return owner.CheckedItems.Count; }
4106 public bool IsReadOnly {
4107 get { return true; }
4110 public int this [int index] {
4112 int [] indices = GetIndices ();
4113 if (index < 0 || index >= indices.Length)
4114 throw new ArgumentOutOfRangeException ("index");
4115 return indices [index];
4119 bool ICollection.IsSynchronized {
4120 get { return false; }
4123 object ICollection.SyncRoot {
4124 get { return this; }
4127 bool IList.IsFixedSize {
4128 get { return true; }
4131 object IList.this [int index] {
4132 get { return this [index]; }
4133 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4135 #endregion // Public Properties
4137 #region Public Methods
4138 public bool Contains (int checkedIndex)
4140 int [] indices = GetIndices ();
4141 for (int i = 0; i < indices.Length; i++) {
4142 if (indices [i] == checkedIndex)
4148 public IEnumerator GetEnumerator ()
4150 int [] indices = GetIndices ();
4151 return indices.GetEnumerator ();
4154 void ICollection.CopyTo (Array dest, int index)
4156 int [] indices = GetIndices ();
4157 Array.Copy (indices, 0, dest, index, indices.Length);
4160 int IList.Add (object value)
4162 throw new NotSupportedException ("Add operation is not supported.");
4167 throw new NotSupportedException ("Clear operation is not supported.");
4170 bool IList.Contains (object checkedIndex)
4172 if (!(checkedIndex is int))
4174 return Contains ((int) checkedIndex);
4177 int IList.IndexOf (object checkedIndex)
4179 if (!(checkedIndex is int))
4181 return IndexOf ((int) checkedIndex);
4184 void IList.Insert (int index, object value)
4186 throw new NotSupportedException ("Insert operation is not supported.");
4189 void IList.Remove (object value)
4191 throw new NotSupportedException ("Remove operation is not supported.");
4194 void IList.RemoveAt (int index)
4196 throw new NotSupportedException ("RemoveAt operation is not supported.");
4199 public int IndexOf (int checkedIndex)
4201 int [] indices = GetIndices ();
4202 for (int i = 0; i < indices.Length; i++) {
4203 if (indices [i] == checkedIndex)
4208 #endregion // Public Methods
4210 private int [] GetIndices ()
4212 ArrayList checked_items = owner.CheckedItems.List;
4213 int [] indices = new int [checked_items.Count];
4214 for (int i = 0; i < checked_items.Count; i++) {
4215 ListViewItem item = (ListViewItem) checked_items [i];
4216 indices [i] = item.Index;
4220 } // CheckedIndexCollection
4223 [ListBindable (false)]
4225 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4227 private readonly ListView owner;
4228 private ArrayList list;
4230 #region Public Constructor
4231 public CheckedListViewItemCollection (ListView owner)
4234 this.owner.Items.Changed += new CollectionChangedHandler (
4235 ItemsCollection_Changed);
4237 #endregion // Public Constructor
4239 #region Public Properties
4243 if (!owner.CheckBoxes)
4249 public bool IsReadOnly {
4250 get { return true; }
4253 public ListViewItem this [int index] {
4256 if (owner.VirtualMode)
4257 throw new InvalidOperationException ();
4259 ArrayList checked_items = List;
4260 if (index < 0 || index >= checked_items.Count)
4261 throw new ArgumentOutOfRangeException ("index");
4262 return (ListViewItem) checked_items [index];
4267 public virtual ListViewItem this [string key] {
4269 int idx = IndexOfKey (key);
4270 return idx == -1 ? null : (ListViewItem) List [idx];
4275 bool ICollection.IsSynchronized {
4276 get { return false; }
4279 object ICollection.SyncRoot {
4280 get { return this; }
4283 bool IList.IsFixedSize {
4284 get { return true; }
4287 object IList.this [int index] {
4288 get { return this [index]; }
4289 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4291 #endregion // Public Properties
4293 #region Public Methods
4294 public bool Contains (ListViewItem item)
4296 if (!owner.CheckBoxes)
4298 return List.Contains (item);
4302 public virtual bool ContainsKey (string key)
4304 return IndexOfKey (key) != -1;
4308 public void CopyTo (Array dest, int index)
4311 if (owner.VirtualMode)
4312 throw new InvalidOperationException ();
4314 if (!owner.CheckBoxes)
4316 List.CopyTo (dest, index);
4319 public IEnumerator GetEnumerator ()
4322 if (owner.VirtualMode)
4323 throw new InvalidOperationException ();
4325 if (!owner.CheckBoxes)
4326 return (new ListViewItem [0]).GetEnumerator ();
4327 return List.GetEnumerator ();
4330 int IList.Add (object value)
4332 throw new NotSupportedException ("Add operation is not supported.");
4337 throw new NotSupportedException ("Clear operation is not supported.");
4340 bool IList.Contains (object item)
4342 if (!(item is ListViewItem))
4344 return Contains ((ListViewItem) item);
4347 int IList.IndexOf (object item)
4349 if (!(item is ListViewItem))
4351 return IndexOf ((ListViewItem) item);
4354 void IList.Insert (int index, object value)
4356 throw new NotSupportedException ("Insert operation is not supported.");
4359 void IList.Remove (object value)
4361 throw new NotSupportedException ("Remove operation is not supported.");
4364 void IList.RemoveAt (int index)
4366 throw new NotSupportedException ("RemoveAt operation is not supported.");
4369 public int IndexOf (ListViewItem item)
4372 if (owner.VirtualMode)
4373 throw new InvalidOperationException ();
4375 if (!owner.CheckBoxes)
4377 return List.IndexOf (item);
4381 public virtual int IndexOfKey (string key)
4384 if (owner.VirtualMode)
4385 throw new InvalidOperationException ();
4387 if (key == null || key.Length == 0)
4390 ArrayList checked_items = List;
4391 for (int i = 0; i < checked_items.Count; i++) {
4392 ListViewItem item = (ListViewItem) checked_items [i];
4393 if (String.Compare (key, item.Name, true) == 0)
4400 #endregion // Public Methods
4402 internal ArrayList List {
4405 list = new ArrayList ();
4406 foreach (ListViewItem item in owner.Items) {
4415 internal void Reset ()
4417 // force re-population of list
4421 private void ItemsCollection_Changed ()
4425 } // CheckedListViewItemCollection
4428 [ListBindable (false)]
4430 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4432 internal ArrayList list;
4433 private ListView owner;
4435 #region Public Constructor
4436 public ColumnHeaderCollection (ListView owner)
4438 list = new ArrayList ();
4441 #endregion // Public Constructor
4443 #region Public Properties
4446 get { return list.Count; }
4449 public bool IsReadOnly {
4450 get { return false; }
4453 public virtual ColumnHeader this [int index] {
4455 if (index < 0 || index >= list.Count)
4456 throw new ArgumentOutOfRangeException ("index");
4457 return (ColumnHeader) list [index];
4462 public virtual ColumnHeader this [string key] {
4464 int idx = IndexOfKey (key);
4468 return (ColumnHeader) list [idx];
4473 bool ICollection.IsSynchronized {
4474 get { return true; }
4477 object ICollection.SyncRoot {
4478 get { return this; }
4481 bool IList.IsFixedSize {
4482 get { return list.IsFixedSize; }
4485 object IList.this [int index] {
4486 get { return this [index]; }
4487 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4489 #endregion // Public Properties
4491 #region Public Methods
4492 public virtual int Add (ColumnHeader value)
4494 int idx = list.Add (value);
4495 owner.AddColumn (value, idx, true);
4499 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4501 ColumnHeader colHeader = new ColumnHeader (this.owner, text, textAlign, width);
4502 this.Add (colHeader);
4507 public virtual ColumnHeader Add (string text)
4509 return Add (String.Empty, text);
4512 public virtual ColumnHeader Add (string text, int width)
4514 return Add (String.Empty, text, width);
4517 public virtual ColumnHeader Add (string key, string text)
4519 ColumnHeader colHeader = new ColumnHeader ();
4520 colHeader.Name = key;
4521 colHeader.Text = text;
4526 public virtual ColumnHeader Add (string key, string text, int width)
4528 return Add (key, text, width, HorizontalAlignment.Left, -1);
4531 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4533 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4534 colHeader.ImageIndex = imageIndex;
4539 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4541 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4542 colHeader.ImageKey = imageKey;
4548 public virtual void AddRange (ColumnHeader [] values)
4550 foreach (ColumnHeader colHeader in values) {
4551 int idx = list.Add (colHeader);
4552 owner.AddColumn (colHeader, idx, false);
4555 owner.Redraw (true);
4558 public virtual void Clear ()
4560 foreach (ColumnHeader col in list)
4561 col.SetListView (null);
4563 owner.ReorderColumns (new int [0], true);
4566 public bool Contains (ColumnHeader value)
4568 return list.Contains (value);
4572 public virtual bool ContainsKey (string key)
4574 return IndexOfKey (key) != -1;
4578 public IEnumerator GetEnumerator ()
4580 return list.GetEnumerator ();
4583 void ICollection.CopyTo (Array dest, int index)
4585 list.CopyTo (dest, index);
4588 int IList.Add (object value)
4590 if (! (value is ColumnHeader)) {
4591 throw new ArgumentException ("Not of type ColumnHeader", "value");
4594 return this.Add ((ColumnHeader) value);
4597 bool IList.Contains (object value)
4599 if (! (value is ColumnHeader)) {
4600 throw new ArgumentException ("Not of type ColumnHeader", "value");
4603 return this.Contains ((ColumnHeader) value);
4606 int IList.IndexOf (object value)
4608 if (! (value is ColumnHeader)) {
4609 throw new ArgumentException ("Not of type ColumnHeader", "value");
4612 return this.IndexOf ((ColumnHeader) value);
4615 void IList.Insert (int index, object value)
4617 if (! (value is ColumnHeader)) {
4618 throw new ArgumentException ("Not of type ColumnHeader", "value");
4621 this.Insert (index, (ColumnHeader) value);
4624 void IList.Remove (object value)
4626 if (! (value is ColumnHeader)) {
4627 throw new ArgumentException ("Not of type ColumnHeader", "value");
4630 this.Remove ((ColumnHeader) value);
4633 public int IndexOf (ColumnHeader value)
4635 return list.IndexOf (value);
4639 public virtual int IndexOfKey (string key)
4641 if (key == null || key.Length == 0)
4644 for (int i = 0; i < list.Count; i++) {
4645 ColumnHeader col = (ColumnHeader) list [i];
4646 if (String.Compare (key, col.Name, true) == 0)
4654 public void Insert (int index, ColumnHeader value)
4656 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4657 // but it's really only greater.
4658 if (index < 0 || index > list.Count)
4659 throw new ArgumentOutOfRangeException ("index");
4661 list.Insert (index, value);
4662 owner.AddColumn (value, index, true);
4666 public void Insert (int index, string text)
4668 Insert (index, String.Empty, text);
4671 public void Insert (int index, string text, int width)
4673 Insert (index, String.Empty, text, width);
4676 public void Insert (int index, string key, string text)
4678 ColumnHeader colHeader = new ColumnHeader ();
4679 colHeader.Name = key;
4680 colHeader.Text = text;
4681 Insert (index, colHeader);
4684 public void Insert (int index, string key, string text, int width)
4686 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4687 Insert (index, colHeader);
4690 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4692 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4693 colHeader.ImageIndex = imageIndex;
4694 Insert (index, colHeader);
4697 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4699 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4700 colHeader.ImageKey = imageKey;
4701 Insert (index, colHeader);
4705 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4707 ColumnHeader colHeader = new ColumnHeader (this.owner, text, textAlign, width);
4708 this.Insert (index, colHeader);
4711 public virtual void Remove (ColumnHeader column)
4713 if (!Contains (column))
4716 list.Remove (column);
4717 column.SetListView (null);
4719 int rem_display_index = column.InternalDisplayIndex;
4720 int [] display_indices = new int [list.Count];
4721 for (int i = 0; i < display_indices.Length; i++) {
4722 ColumnHeader col = (ColumnHeader) list [i];
4723 int display_index = col.InternalDisplayIndex;
4724 if (display_index < rem_display_index) {
4725 display_indices [i] = display_index;
4727 display_indices [i] = (display_index - 1);
4731 column.InternalDisplayIndex = -1;
4732 owner.ReorderColumns (display_indices, true);
4736 public virtual void RemoveByKey (string key)
4738 int idx = IndexOfKey (key);
4744 public virtual void RemoveAt (int index)
4746 if (index < 0 || index >= list.Count)
4747 throw new ArgumentOutOfRangeException ("index");
4749 ColumnHeader col = (ColumnHeader) list [index];
4752 #endregion // Public Methods
4755 } // ColumnHeaderCollection
4758 [ListBindable (false)]
4760 public class ListViewItemCollection : IList, ICollection, IEnumerable
4762 private readonly ArrayList list;
4763 private ListView owner;
4765 private ListViewGroup group;
4768 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4769 // In the later case ListViewItem.ListView never gets modified
4770 private bool is_main_collection = true;
4772 #region Public Constructor
4773 public ListViewItemCollection (ListView owner)
4775 list = new ArrayList (0);
4778 #endregion // Public Constructor
4781 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4784 is_main_collection = false;
4788 #region Public Properties
4793 if (owner != null && owner.VirtualMode)
4794 return owner.VirtualListSize;
4801 public bool IsReadOnly {
4802 get { return false; }
4805 public virtual ListViewItem this [int index] {
4807 if (index < 0 || index >= Count)
4808 throw new ArgumentOutOfRangeException ("displayIndex");
4811 if (owner != null && owner.VirtualMode)
4812 return RetrieveVirtualItemFromOwner (index);
4814 return (ListViewItem) list [index];
4818 if (index < 0 || index >= Count)
4819 throw new ArgumentOutOfRangeException ("displayIndex");
4822 if (owner != null && owner.VirtualMode)
4823 throw new InvalidOperationException ();
4826 if (list.Contains (value))
4827 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4829 if (value.ListView != null && value.ListView != owner)
4830 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");
4832 if (is_main_collection)
4833 value.Owner = owner;
4836 if (value.Group != null)
4837 value.Group.Items.Remove (value);
4839 value.SetGroup (group);
4843 list [index] = value;
4844 CollectionChanged (true);
4849 public virtual ListViewItem this [string key] {
4851 int idx = IndexOfKey (key);
4860 bool ICollection.IsSynchronized {
4861 get { return true; }
4864 object ICollection.SyncRoot {
4865 get { return this; }
4868 bool IList.IsFixedSize {
4869 get { return list.IsFixedSize; }
4872 object IList.this [int index] {
4873 get { return this [index]; }
4875 if (value is ListViewItem)
4876 this [index] = (ListViewItem) value;
4878 this [index] = new ListViewItem (value.ToString ());
4882 #endregion // Public Properties
4884 #region Public Methods
4885 public virtual ListViewItem Add (ListViewItem value)
4888 if (owner != null && owner.VirtualMode)
4889 throw new InvalidOperationException ();
4893 CollectionChanged (true);
4898 public virtual ListViewItem Add (string text)
4900 ListViewItem item = new ListViewItem (text);
4901 return this.Add (item);
4904 public virtual ListViewItem Add (string text, int imageIndex)
4906 ListViewItem item = new ListViewItem (text, imageIndex);
4907 return this.Add (item);
4911 public virtual ListViewItem Add (string text, string imageKey)
4913 ListViewItem item = new ListViewItem (text, imageKey);
4914 return this.Add (item);
4917 public virtual ListViewItem Add (string key, string text, int imageIndex)
4919 ListViewItem item = new ListViewItem (text, imageIndex);
4921 return this.Add (item);
4924 public virtual ListViewItem Add (string key, string text, string imageKey)
4926 ListViewItem item = new ListViewItem (text, imageKey);
4928 return this.Add (item);
4932 public void AddRange (ListViewItem [] items)
4935 throw new ArgumentNullException ("Argument cannot be null!", "values");
4937 if (owner != null && owner.VirtualMode)
4938 throw new InvalidOperationException ();
4941 foreach (ListViewItem item in items)
4944 CollectionChanged (true);
4948 public void AddRange (ListViewItemCollection items)
4951 throw new ArgumentNullException ("Argument cannot be null!", "items");
4953 ListViewItem[] itemArray = new ListViewItem[items.Count];
4954 items.CopyTo (itemArray,0);
4955 this.AddRange (itemArray);
4959 public virtual void Clear ()
4962 if (owner != null && owner.VirtualMode)
4963 throw new InvalidOperationException ();
4965 if (is_main_collection && owner != null) {
4966 owner.SetFocusedItem (-1);
4967 owner.h_scroll.Value = owner.v_scroll.Value = 0;
4969 foreach (ListViewItem item in list) {
4970 owner.item_control.CancelEdit (item);
4977 foreach (ListViewItem item in list)
4978 item.SetGroup (null);
4982 CollectionChanged (false);
4985 public bool Contains (ListViewItem item)
4987 return IndexOf (item) != -1;
4991 public virtual bool ContainsKey (string key)
4993 return IndexOfKey (key) != -1;
4997 public void CopyTo (Array dest, int index)
4999 list.CopyTo (dest, index);
5003 public ListViewItem [] Find (string key, bool searchAllSubItems)
5006 return new ListViewItem [0];
5008 List<ListViewItem> temp_list = new List<ListViewItem> ();
5010 for (int i = 0; i < list.Count; i++) {
5011 ListViewItem lvi = (ListViewItem) list [i];
5012 if (String.Compare (key, lvi.Name, true) == 0)
5013 temp_list.Add (lvi);
5016 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5017 temp_list.CopyTo (retval);
5023 public IEnumerator GetEnumerator ()
5026 if (owner != null && owner.VirtualMode)
5027 throw new InvalidOperationException ();
5030 return list.GetEnumerator ();
5033 int IList.Add (object item)
5039 if (owner != null && owner.VirtualMode)
5040 throw new InvalidOperationException ();
5043 if (item is ListViewItem) {
5044 li = (ListViewItem) item;
5045 if (list.Contains (li))
5046 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5048 if (li.ListView != null && li.ListView != owner)
5049 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");
5052 li = new ListViewItem (item.ToString ());
5055 result = list.Add (li);
5056 CollectionChanged (true);
5061 bool IList.Contains (object item)
5063 return Contains ((ListViewItem) item);
5066 int IList.IndexOf (object item)
5068 return IndexOf ((ListViewItem) item);
5071 void IList.Insert (int index, object item)
5073 if (item is ListViewItem)
5074 this.Insert (index, (ListViewItem) item);
5076 this.Insert (index, item.ToString ());
5079 void IList.Remove (object item)
5081 Remove ((ListViewItem) item);
5084 public int IndexOf (ListViewItem item)
5087 if (owner != null && owner.VirtualMode) {
5088 for (int i = 0; i < Count; i++)
5089 if (RetrieveVirtualItemFromOwner (i) == item)
5096 return list.IndexOf (item);
5100 public virtual int IndexOfKey (string key)
5102 if (key == null || key.Length == 0)
5105 for (int i = 0; i < Count; i++) {
5106 ListViewItem lvi = this [i];
5107 if (String.Compare (key, lvi.Name, true) == 0)
5115 public ListViewItem Insert (int index, ListViewItem item)
5117 if (index < 0 || index > list.Count)
5118 throw new ArgumentOutOfRangeException ("index");
5121 if (owner != null && owner.VirtualMode)
5122 throw new InvalidOperationException ();
5125 if (list.Contains (item))
5126 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5128 if (item.ListView != null && item.ListView != owner)
5129 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");
5131 if (is_main_collection)
5135 if (item.Group != null)
5136 item.Group.Items.Remove (item);
5138 item.SetGroup (group);
5142 list.Insert (index, item);
5143 CollectionChanged (true);
5147 public ListViewItem Insert (int index, string text)
5149 return this.Insert (index, new ListViewItem (text));
5152 public ListViewItem Insert (int index, string text, int imageIndex)
5154 return this.Insert (index, new ListViewItem (text, imageIndex));
5158 public ListViewItem Insert (int index, string text, string imageKey)
5160 ListViewItem lvi = new ListViewItem (text, imageKey);
5161 return Insert (index, lvi);
5164 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5166 ListViewItem lvi = new ListViewItem (text, imageIndex);
5168 return Insert (index, lvi);
5171 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5173 ListViewItem lvi = new ListViewItem (text, imageKey);
5175 return Insert (index, lvi);
5179 public virtual void Remove (ListViewItem item)
5182 if (owner != null && owner.VirtualMode)
5183 throw new InvalidOperationException ();
5186 int idx = list.IndexOf (item);
5191 public virtual void RemoveAt (int index)
5193 if (index < 0 || index >= Count)
5194 throw new ArgumentOutOfRangeException ("index");
5197 if (owner != null && owner.VirtualMode)
5198 throw new InvalidOperationException ();
5201 ListViewItem item = (ListViewItem) list [index];
5203 bool selection_changed = false;
5204 if (is_main_collection && owner != null) {
5206 if (item.Focused && index + 1 == Count) // Last item
5207 owner.SetFocusedItem (index == 0 ? -1 : index - 1);
5209 selection_changed = owner.SelectedIndices.Contains (index);
5210 owner.item_control.CancelEdit (item);
5213 list.RemoveAt (index);
5215 if (is_main_collection)
5219 item.SetGroup (null);
5222 CollectionChanged (false);
5223 if (selection_changed && owner != null)
5224 owner.OnSelectedIndexChanged (EventArgs.Empty);
5228 public virtual void RemoveByKey (string key)
5230 int idx = IndexOfKey (key);
5236 #endregion // Public Methods
5238 internal ListView Owner {
5248 internal ListViewGroup Group {
5258 void AddItem (ListViewItem value)
5260 if (list.Contains (value))
5261 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5263 if (value.ListView != null && value.ListView != owner)
5264 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");
5265 if (is_main_collection)
5266 value.Owner = owner;
5269 if (value.Group != null)
5270 value.Group.Items.Remove (value);
5272 value.SetGroup (group);
5279 void CollectionChanged (bool sort)
5281 if (owner != null) {
5286 owner.Redraw (true);
5291 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5293 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5295 owner.OnRetrieveVirtualItem (args);
5296 ListViewItem retval = args.Item;
5297 retval.Owner = owner;
5298 retval.DisplayIndex = displayIndex;
5304 internal event CollectionChangedHandler Changed;
5306 internal void Sort (IComparer comparer)
5308 list.Sort (comparer);
5312 internal void OnChange ()
5314 if (Changed != null)
5317 } // ListViewItemCollection
5320 // In normal mode, the selection information resides in the Items,
5321 // making SelectedIndexCollection.List read-only
5323 // In virtual mode, SelectedIndexCollection directly saves the selection
5324 // information, instead of getting it from Items, making List read-and-write
5326 [ListBindable (false)]
5328 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5330 private readonly ListView owner;
5331 private ArrayList list;
5333 #region Public Constructor
5334 public SelectedIndexCollection (ListView owner)
5337 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5339 #endregion // Public Constructor
5341 #region Public Properties
5345 if (!owner.IsHandleCreated)
5352 public bool IsReadOnly {
5362 public int this [int index] {
5364 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5365 throw new ArgumentOutOfRangeException ("index");
5367 return (int) List [index];
5371 bool ICollection.IsSynchronized {
5372 get { return false; }
5375 object ICollection.SyncRoot {
5376 get { return this; }
5379 bool IList.IsFixedSize {
5389 object IList.this [int index] {
5390 get { return this [index]; }
5391 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5393 #endregion // Public Properties
5395 #region Public Methods
5397 public int Add (int itemIndex)
5399 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5400 throw new ArgumentOutOfRangeException ("index");
5402 if (owner.virtual_mode && !owner.IsHandleCreated)
5405 owner.Items [itemIndex].Selected = true;
5407 if (!owner.IsHandleCreated)
5421 if (!owner.IsHandleCreated)
5424 int [] indexes = (int []) List.ToArray (typeof (int));
5425 foreach (int index in indexes)
5426 owner.Items [index].Selected = false;
5429 public bool Contains (int selectedIndex)
5431 return IndexOf (selectedIndex) != -1;
5434 public void CopyTo (Array dest, int index)
5436 List.CopyTo (dest, index);
5439 public IEnumerator GetEnumerator ()
5441 return List.GetEnumerator ();
5444 int IList.Add (object value)
5446 throw new NotSupportedException ("Add operation is not supported.");
5454 bool IList.Contains (object selectedIndex)
5456 if (!(selectedIndex is int))
5458 return Contains ((int) selectedIndex);
5461 int IList.IndexOf (object selectedIndex)
5463 if (!(selectedIndex is int))
5465 return IndexOf ((int) selectedIndex);
5468 void IList.Insert (int index, object value)
5470 throw new NotSupportedException ("Insert operation is not supported.");
5473 void IList.Remove (object value)
5475 throw new NotSupportedException ("Remove operation is not supported.");
5478 void IList.RemoveAt (int index)
5480 throw new NotSupportedException ("RemoveAt operation is not supported.");
5483 public int IndexOf (int selectedIndex)
5485 if (!owner.IsHandleCreated)
5488 return List.IndexOf (selectedIndex);
5492 public void Remove (int itemIndex)
5494 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5495 throw new ArgumentOutOfRangeException ("itemIndex");
5497 owner.Items [itemIndex].Selected = false;
5500 #endregion // Public Methods
5502 internal ArrayList List {
5505 list = new ArrayList ();
5507 if (!owner.VirtualMode)
5509 for (int i = 0; i < owner.Items.Count; i++) {
5510 if (owner.Items [i].Selected)
5518 internal void Reset ()
5520 // force re-population of list
5522 if (!owner.VirtualMode)
5527 private void ItemsCollection_Changed ()
5533 internal void RemoveIndex (int index)
5535 int idx = List.BinarySearch (index);
5537 List.RemoveAt (idx);
5540 // actually store index in the collection
5541 // also, keep the collection sorted, as .Net does
5542 internal void InsertIndex (int index)
5545 int iMax = List.Count - 1;
5546 while (iMin <= iMax) {
5547 int iMid = (iMin + iMax) / 2;
5548 int current_index = (int) List [iMid];
5550 if (current_index == index)
5551 return; // Already added
5552 if (current_index > index)
5558 List.Insert (iMin, index);
5562 } // SelectedIndexCollection
5565 [ListBindable (false)]
5567 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5569 private readonly ListView owner;
5571 #region Public Constructor
5572 public SelectedListViewItemCollection (ListView owner)
5576 #endregion // Public Constructor
5578 #region Public Properties
5582 return owner.SelectedIndices.Count;
5586 public bool IsReadOnly {
5587 get { return true; }
5590 public ListViewItem this [int index] {
5592 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5593 throw new ArgumentOutOfRangeException ("index");
5595 int item_index = owner.SelectedIndices [index];
5596 return owner.Items [item_index];
5601 public virtual ListViewItem this [string key] {
5603 int idx = IndexOfKey (key);
5612 bool ICollection.IsSynchronized {
5613 get { return false; }
5616 object ICollection.SyncRoot {
5617 get { return this; }
5620 bool IList.IsFixedSize {
5621 get { return true; }
5624 object IList.this [int index] {
5625 get { return this [index]; }
5626 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5628 #endregion // Public Properties
5630 #region Public Methods
5631 public void Clear ()
5633 owner.SelectedIndices.Clear ();
5636 public bool Contains (ListViewItem item)
5638 return IndexOf (item) != -1;
5642 public virtual bool ContainsKey (string key)
5644 return IndexOfKey (key) != -1;
5648 public void CopyTo (Array dest, int index)
5650 if (!owner.IsHandleCreated)
5652 if (index > Count) // Throws ArgumentException instead of IOOR exception
5653 throw new ArgumentException ("index");
5655 for (int i = 0; i < Count; i++)
5656 dest.SetValue (this [i], index++);
5659 public IEnumerator GetEnumerator ()
5661 if (!owner.IsHandleCreated)
5662 return (new ListViewItem [0]).GetEnumerator ();
5664 ListViewItem [] items = new ListViewItem [Count];
5665 for (int i = 0; i < Count; i++)
5666 items [i] = this [i];
5668 return items.GetEnumerator ();
5671 int IList.Add (object value)
5673 throw new NotSupportedException ("Add operation is not supported.");
5676 bool IList.Contains (object item)
5678 if (!(item is ListViewItem))
5680 return Contains ((ListViewItem) item);
5683 int IList.IndexOf (object item)
5685 if (!(item is ListViewItem))
5687 return IndexOf ((ListViewItem) item);
5690 void IList.Insert (int index, object value)
5692 throw new NotSupportedException ("Insert operation is not supported.");
5695 void IList.Remove (object value)
5697 throw new NotSupportedException ("Remove operation is not supported.");
5700 void IList.RemoveAt (int index)
5702 throw new NotSupportedException ("RemoveAt operation is not supported.");
5705 public int IndexOf (ListViewItem item)
5707 if (!owner.IsHandleCreated)
5710 for (int i = 0; i < Count; i++)
5711 if (this [i] == item)
5718 public virtual int IndexOfKey (string key)
5720 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5723 for (int i = 0; i < Count; i++) {
5724 ListViewItem item = this [i];
5725 if (String.Compare (item.Name, key, true) == 0)
5732 #endregion // Public Methods
5734 } // SelectedListViewItemCollection
5736 internal delegate void CollectionChangedHandler ();
5738 struct ItemMatrixLocation
5743 public ItemMatrixLocation (int row, int col)
5770 #endregion // Subclasses
5772 protected override void OnResize (EventArgs e)
5777 protected override void OnMouseLeave (EventArgs e)
5779 base.OnMouseLeave (e);
5783 // ColumnReorder event
5785 static object ColumnReorderedEvent = new object ();
5786 public event ColumnReorderedEventHandler ColumnReordered {
5787 add { Events.AddHandler (ColumnReorderedEvent, value); }
5788 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5791 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5793 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5800 // ColumnWidthChanged
5802 static object ColumnWidthChangedEvent = new object ();
5803 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5804 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5805 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5808 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5810 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5815 void RaiseColumnWidthChanged (int resize_column)
5817 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5819 OnColumnWidthChanged (n);
5823 // ColumnWidthChanging
5825 static object ColumnWidthChangingEvent = new object ();
5826 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5827 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5828 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5831 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5833 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5839 // 2.0 profile based implementation
5841 bool CanProceedWithResize (ColumnHeader col, int width)
5843 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5847 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5848 cwceh (this, changing);
5849 return !changing.Cancel;
5853 // 1.0 profile based implementation
5855 bool CanProceedWithResize (ColumnHeader col, int width)
5860 void RaiseColumnWidthChanged (int resize_column)