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 internal 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 items.Changed += new CollectionChangedHandler (OnItemsChanged);
292 checked_indices = new CheckedIndexCollection (this);
293 checked_items = new CheckedListViewItemCollection (this);
294 columns = new ColumnHeaderCollection (this);
295 foreground_color = SystemColors.WindowText;
296 selected_indices = new SelectedIndexCollection (this);
297 selected_items = new SelectedListViewItemCollection (this);
298 items_location = new Point [16];
299 items_matrix_location = new ItemMatrixLocation [16];
300 reordered_items_indices = new int [16];
302 item_tooltip = new ToolTip ();
303 item_tooltip.Active = false;
304 insertion_mark = new ListViewInsertionMark (this);
307 InternalBorderStyle = BorderStyle.Fixed3D;
309 header_control = new HeaderControl (this);
310 header_control.Visible = false;
311 Controls.AddImplicit (header_control);
313 item_control = new ItemControl (this);
314 Controls.AddImplicit (item_control);
316 h_scroll = new ImplicitHScrollBar ();
317 Controls.AddImplicit (this.h_scroll);
319 v_scroll = new ImplicitVScrollBar ();
320 Controls.AddImplicit (this.v_scroll);
322 h_marker = v_marker = 0;
323 keysearch_tickcnt = 0;
325 // scroll bars are disabled initially
326 h_scroll.Visible = false;
327 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
328 v_scroll.Visible = false;
329 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
332 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
333 SizeChanged += new EventHandler (ListView_SizeChanged);
334 GotFocus += new EventHandler (FocusChanged);
335 LostFocus += new EventHandler (FocusChanged);
336 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
337 MouseEnter += new EventHandler (ListView_MouseEnter);
338 Invalidated += new InvalidateEventHandler (ListView_Invalidated);
341 BackgroundImageTiled = false;
344 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
346 | ControlStyles.UseTextForAccessibility
350 #endregion // Public Constructors
352 #region Private Internal Properties
353 internal Size CheckBoxSize {
355 if (this.check_boxes) {
356 if (this.state_image_list != null)
357 return this.state_image_list.ImageSize;
359 return ThemeEngine.Current.ListViewCheckBoxSize;
365 internal Size ItemSize {
367 if (view != View.Details)
370 Size size = new Size ();
371 size.Height = item_size.Height;
372 for (int i = 0; i < columns.Count; i++)
373 size.Width += columns [i].Wd;
382 internal int HotItemIndex {
384 return hot_item_index;
387 hot_item_index = value;
391 internal override bool ScaleChildrenInternal {
392 get { return false; }
395 internal bool UseCustomColumnWidth {
397 return (view == View.List || view == View.SmallIcon) && columns.Count > 0;
401 internal ColumnHeader EnteredColumnHeader {
403 return header_control.EnteredColumnHeader;
406 #endregion // Private Internal Properties
408 #region Protected Properties
409 protected override CreateParams CreateParams {
410 get { return base.CreateParams; }
413 protected override Size DefaultSize {
414 get { return ThemeEngine.Current.ListViewDefaultSize; }
417 protected override bool DoubleBuffered {
419 return base.DoubleBuffered;
422 base.DoubleBuffered = value;
426 #endregion // Protected Properties
428 #region Public Instance Properties
429 [DefaultValue (ItemActivation.Standard)]
430 public ItemActivation Activation {
431 get { return activation; }
433 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
434 value != ItemActivation.TwoClick) {
435 throw new InvalidEnumArgumentException (string.Format
436 ("Enum argument value '{0}' is not valid for Activation", value));
439 if (hot_tracking && value != ItemActivation.OneClick)
440 throw new ArgumentException ("When HotTracking is on, activation must be ItemActivation.OneClick");
447 [DefaultValue (ListViewAlignment.Top)]
449 public ListViewAlignment Alignment {
450 get { return alignment; }
452 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
453 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
454 throw new InvalidEnumArgumentException (string.Format
455 ("Enum argument value '{0}' is not valid for Alignment", value));
458 if (this.alignment != value) {
460 // alignment does not matter in Details/List views
461 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
467 [DefaultValue (false)]
468 public bool AllowColumnReorder {
469 get { return allow_column_reorder; }
470 set { allow_column_reorder = value; }
473 [DefaultValue (true)]
474 public bool AutoArrange {
475 get { return auto_arrange; }
477 if (auto_arrange != value) {
478 auto_arrange = value;
479 // autoarrange does not matter in Details/List views
480 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
486 public override Color BackColor {
488 if (background_color.IsEmpty)
489 return ThemeEngine.Current.ColorWindow;
491 return background_color;
494 background_color = value;
495 item_control.BackColor = value;
501 [EditorBrowsable (EditorBrowsableState.Never)]
502 public override Image BackgroundImage {
503 get { return base.BackgroundImage; }
504 set { base.BackgroundImage = value; }
510 [EditorBrowsable (EditorBrowsableState.Never)]
511 public override ImageLayout BackgroundImageLayout {
513 return base.BackgroundImageLayout;
516 base.BackgroundImageLayout = value;
520 [DefaultValue (false)]
521 public bool BackgroundImageTiled {
523 return item_control.BackgroundImageLayout == ImageLayout.Tile;
526 ImageLayout new_image_layout = value ? ImageLayout.Tile : ImageLayout.None;
527 if (new_image_layout == item_control.BackgroundImageLayout)
530 item_control.BackgroundImageLayout = new_image_layout;
535 [DefaultValue (BorderStyle.Fixed3D)]
537 public BorderStyle BorderStyle {
538 get { return InternalBorderStyle; }
539 set { InternalBorderStyle = value; }
542 [DefaultValue (false)]
543 public bool CheckBoxes {
544 get { return check_boxes; }
546 if (check_boxes != value) {
548 if (value && View == View.Tile)
549 throw new NotSupportedException ("CheckBoxes are not"
550 + " supported in Tile view. Choose a different"
551 + " view or set CheckBoxes to false.");
561 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
562 public CheckedIndexCollection CheckedIndices {
563 get { return checked_indices; }
567 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
568 public CheckedListViewItemCollection CheckedItems {
569 get { return checked_items; }
573 [Editor ("System.Windows.Forms.Design.ColumnHeaderCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
575 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
577 [MergableProperty (false)]
578 public ColumnHeaderCollection Columns {
579 get { return columns; }
583 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
584 public ListViewItem FocusedItem {
586 if (focused_item_index == -1)
589 return GetItemAtDisplayIndex (focused_item_index);
593 if (value == null || value.ListView != this ||
597 SetFocusedItem (value.DisplayIndex);
602 public override Color ForeColor {
604 if (foreground_color.IsEmpty)
605 return ThemeEngine.Current.ColorWindowText;
607 return foreground_color;
609 set { foreground_color = value; }
612 [DefaultValue (false)]
613 public bool FullRowSelect {
614 get { return full_row_select; }
616 if (full_row_select != value) {
617 full_row_select = value;
618 InvalidateSelection ();
623 [DefaultValue (false)]
624 public bool GridLines {
625 get { return grid_lines; }
627 if (grid_lines != value) {
634 [DefaultValue (ColumnHeaderStyle.Clickable)]
635 public ColumnHeaderStyle HeaderStyle {
636 get { return header_style; }
638 if (header_style == value)
642 case ColumnHeaderStyle.Clickable:
643 case ColumnHeaderStyle.Nonclickable:
644 case ColumnHeaderStyle.None:
647 throw new InvalidEnumArgumentException (string.Format
648 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
651 header_style = value;
652 if (view == View.Details)
657 [DefaultValue (true)]
658 public bool HideSelection {
659 get { return hide_selection; }
661 if (hide_selection != value) {
662 hide_selection = value;
663 InvalidateSelection ();
669 [DefaultValue (false)]
670 public bool HotTracking {
675 if (hot_tracking == value)
678 hot_tracking = value;
680 hover_selection = true;
681 activation = ItemActivation.OneClick;
687 [DefaultValue (false)]
688 public bool HoverSelection {
689 get { return hover_selection; }
692 if (hot_tracking && value == false)
693 throw new ArgumentException ("When HotTracking is on, hover selection must be true");
695 hover_selection = value;
700 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
702 public ListViewInsertionMark InsertionMark {
704 return insertion_mark;
710 [Editor ("System.Windows.Forms.Design.ListViewItemCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
712 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
714 [MergableProperty (false)]
715 public ListViewItemCollection Items {
716 get { return items; }
719 [DefaultValue (false)]
720 public bool LabelEdit {
721 get { return label_edit; }
722 set { label_edit = value; }
725 [DefaultValue (true)]
727 public bool LabelWrap {
728 get { return label_wrap; }
730 if (label_wrap != value) {
737 [DefaultValue (null)]
738 public ImageList LargeImageList {
739 get { return large_image_list; }
741 large_image_list = value;
747 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
748 public IComparer ListViewItemSorter {
750 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
755 if (item_sorter != value) {
762 [DefaultValue (true)]
763 public bool MultiSelect {
764 get { return multiselect; }
765 set { multiselect = value; }
770 [DefaultValue(false)]
771 public bool OwnerDraw {
772 get { return owner_draw; }
780 [EditorBrowsable (EditorBrowsableState.Never)]
781 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
782 public new Padding Padding {
787 base.Padding = value;
791 [MonoTODO ("RTL not supported")]
793 [DefaultValue (false)]
794 public virtual bool RightToLeftLayout {
795 get { return right_to_left_layout; }
797 if (right_to_left_layout != value) {
798 right_to_left_layout = value;
799 OnRightToLeftLayoutChanged (EventArgs.Empty);
805 [DefaultValue (true)]
806 public bool Scrollable {
807 get { return scrollable; }
809 if (scrollable != value) {
817 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
818 public SelectedIndexCollection SelectedIndices {
819 get { return selected_indices; }
823 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
824 public SelectedListViewItemCollection SelectedItems {
825 get { return selected_items; }
830 public bool ShowGroups {
831 get { return show_groups; }
833 if (show_groups != value) {
840 [LocalizableAttribute (true)]
841 [MergableProperty (false)]
842 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
843 [Editor ("System.Windows.Forms.Design.ListViewGroupCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
844 public ListViewGroupCollection Groups {
845 get { return groups; }
848 [DefaultValue (false)]
849 public bool ShowItemToolTips {
851 return show_item_tooltips;
854 show_item_tooltips = value;
855 item_tooltip.Active = false;
860 [DefaultValue (null)]
861 public ImageList SmallImageList {
862 get { return small_image_list; }
864 small_image_list = value;
869 [DefaultValue (SortOrder.None)]
870 public SortOrder Sorting {
871 get { return sort_order; }
873 if (!Enum.IsDefined (typeof (SortOrder), value)) {
874 throw new InvalidEnumArgumentException ("value", (int) value,
878 if (sort_order == value)
884 if (virtual_mode) // Sorting is not allowed in virtual mode
888 if (value == SortOrder.None) {
889 if (item_sorter != null) {
890 // ListViewItemSorter should never be reset for SmallIcon
891 // and LargeIcon view
892 if (View != View.SmallIcon && View != View.LargeIcon)
896 // in .NET 1.1, only internal IComparer would be
898 if (item_sorter is ItemComparer)
904 if (item_sorter == null)
905 item_sorter = new ItemComparer (value);
906 if (item_sorter is ItemComparer) {
908 item_sorter = new ItemComparer (value);
910 // in .NET 1.1, the sort order is not updated for
911 // SmallIcon and LargeIcon views if no custom IComparer
913 if (View != View.SmallIcon && View != View.LargeIcon)
914 item_sorter = new ItemComparer (value);
922 private void OnImageListChanged (object sender, EventArgs args)
924 item_control.Invalidate ();
927 [DefaultValue (null)]
928 public ImageList StateImageList {
929 get { return state_image_list; }
931 if (state_image_list == value)
934 if (state_image_list != null)
935 state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
937 state_image_list = value;
939 if (state_image_list != null)
940 state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
948 [EditorBrowsable (EditorBrowsableState.Never)]
949 public override string Text {
950 get { return base.Text; }
952 if (value == base.Text)
962 public Size TileSize {
967 if (value.Width <= 0 || value.Height <= 0)
968 throw new ArgumentOutOfRangeException ("value");
977 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
978 public ListViewItem TopItem {
981 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
982 throw new InvalidOperationException ("Cannot get the top item in LargeIcon, SmallIcon or Tile view.");
985 if (this.items.Count == 0)
987 // if contents are not scrolled
988 // it is the first item
989 else if (h_marker == 0 && v_marker == 0)
990 return this.items [0];
991 // do a hit test for the scrolled position
993 for (int i = 0; i < items.Count; i++) {
994 Point item_loc = GetItemLocation (i);
995 if (item_loc.X >= 0 && item_loc.Y >= 0)
1003 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
1004 throw new InvalidOperationException ("Cannot set the top item in LargeIcon, SmallIcon or Tile view.");
1006 // .Net doesn't throw any exception in the cases below
1007 if (value == null || value.ListView != this)
1010 EnsureVisible (value.Index);
1016 [EditorBrowsable (EditorBrowsableState.Advanced)]
1017 [DefaultValue (true)]
1019 [MonoInternalNote ("Stub, not implemented")]
1020 public bool UseCompatibleStateImageBehavior {
1029 [DefaultValue (View.LargeIcon)]
1031 get { return view; }
1033 if (!Enum.IsDefined (typeof (View), value))
1034 throw new InvalidEnumArgumentException ("value", (int) value,
1037 if (view != value) {
1039 if (CheckBoxes && value == View.Tile)
1040 throw new NotSupportedException ("CheckBoxes are not"
1041 + " supported in Tile view. Choose a different"
1042 + " view or set CheckBoxes to false.");
1045 h_scroll.Value = v_scroll.Value = 0;
1053 [DefaultValue (false)]
1054 [RefreshProperties (RefreshProperties.Repaint)]
1055 public bool VirtualMode {
1057 return virtual_mode;
1060 if (virtual_mode == value)
1063 if (!virtual_mode && items.Count > 0)
1064 throw new InvalidOperationException ();
1066 virtual_mode = value;
1072 [RefreshProperties (RefreshProperties.Repaint)]
1073 public int VirtualListSize {
1075 return virtual_list_size;
1079 throw new ArgumentException ("value");
1081 if (virtual_list_size == value)
1084 virtual_list_size = value;
1086 selected_indices.Reset ();
1092 #endregion // Public Instance Properties
1094 #region Internal Methods Properties
1096 internal int FirstVisibleIndex {
1099 if (this.items.Count == 0)
1102 if (h_marker == 0 && v_marker == 0)
1105 Size item_size = ItemSize;
1106 for (int i = 0; i < items.Count; i++) {
1107 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
1108 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
1117 internal int LastVisibleIndex {
1119 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
1120 if (View == View.List || Alignment == ListViewAlignment.Left) {
1121 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
1124 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
1129 return Items.Count - 1;
1133 internal void OnSelectedIndexChanged ()
1135 if (IsHandleCreated)
1136 OnSelectedIndexChanged (EventArgs.Empty);
1139 internal int TotalWidth {
1140 get { return Math.Max (this.Width, this.layout_wd); }
1143 internal int TotalHeight {
1144 get { return Math.Max (this.Height, this.layout_ht); }
1147 internal void Redraw (bool recalculate)
1149 // Avoid calculations when control is being updated
1153 // VirtualMode doesn't do any calculations until handle is created
1154 if (virtual_mode && !IsHandleCreated)
1160 CalculateListView (this.alignment);
1166 void InvalidateSelection ()
1168 foreach (int selected_index in SelectedIndices)
1169 items [selected_index].Invalidate ();
1172 const int text_padding = 15;
1174 internal Size GetChildColumnSize (int index)
1176 Size ret_size = Size.Empty;
1177 ColumnHeader col = this.columns [index];
1179 if (col.Width == -2) { // autosize = max(items, columnheader)
1180 Size size = Size.Ceiling (TextRenderer.MeasureString
1181 (col.Text, this.Font));
1182 size.Width += text_padding;
1183 ret_size = BiggestItem (index);
1184 if (size.Width > ret_size.Width)
1187 else { // -1 and all the values < -2 are put under one category
1188 ret_size = BiggestItem (index);
1189 // fall back to empty columns' width if no subitem is available for a column
1190 if (ret_size.IsEmpty) {
1191 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
1192 if (col.Text.Length > 0)
1193 ret_size.Height = Size.Ceiling (TextRenderer.MeasureString
1194 (col.Text, this.Font)).Height;
1196 ret_size.Height = this.Font.Height;
1200 ret_size.Height += text_padding;
1202 // adjust the size for icon and checkbox for 0th column
1204 ret_size.Width += (this.CheckBoxSize.Width + 4);
1205 if (this.small_image_list != null)
1206 ret_size.Width += this.small_image_list.ImageSize.Width;
1211 // Returns the size of biggest item text in a column
1212 // or the sum of the text and indent count if we are on 2.0
1213 private Size BiggestItem (int col)
1215 Size temp = Size.Empty;
1216 Size ret_size = Size.Empty;
1218 bool use_indent_count = small_image_list != null;
1220 // VirtualMode uses the first item text size
1221 if (virtual_mode && items.Count > 0) {
1222 ListViewItem item = items [0];
1223 ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
1226 if (use_indent_count)
1227 ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
1230 // 0th column holds the item text, we check the size of
1231 // the various subitems falling in that column and get
1232 // the biggest one's size.
1233 foreach (ListViewItem item in items) {
1234 if (col >= item.SubItems.Count)
1237 temp = Size.Ceiling (TextRenderer.MeasureString
1238 (item.SubItems [col].Text, Font));
1241 if (use_indent_count)
1242 temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
1245 if (temp.Width > ret_size.Width)
1252 // adjustment for space in Details view
1253 if (!ret_size.IsEmpty && view == View.Details)
1254 ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
1259 const int max_wrap_padding = 30;
1261 // Sets the size of the biggest item text as per the view
1262 private void CalcTextSize ()
1264 // clear the old value
1265 text_size = Size.Empty;
1267 if (items.Count == 0)
1270 text_size = BiggestItem (0);
1272 if (view == View.LargeIcon && this.label_wrap) {
1273 Size temp = Size.Empty;
1274 if (this.check_boxes)
1275 temp.Width += 2 * this.CheckBoxSize.Width;
1276 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1277 temp.Width += icon_w + max_wrap_padding;
1278 // wrapping is done for two lines only
1279 if (text_size.Width > temp.Width) {
1280 text_size.Width = temp.Width;
1281 text_size.Height *= 2;
1284 else if (view == View.List) {
1285 // in list view max text shown in determined by the
1286 // control width, even if scolling is enabled.
1287 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1288 if (this.small_image_list != null)
1289 max_wd -= this.small_image_list.ImageSize.Width;
1291 if (text_size.Width > max_wd)
1292 text_size.Width = max_wd;
1295 // we do the default settings, if we have got 0's
1296 if (text_size.Height <= 0)
1297 text_size.Height = this.Font.Height;
1298 if (text_size.Width <= 0)
1299 text_size.Width = this.Width;
1301 // little adjustment
1302 text_size.Width += 2;
1303 text_size.Height += 2;
1306 private void Scroll (ScrollBar scrollbar, int delta)
1308 if (delta == 0 || !scrollbar.Visible)
1312 if (scrollbar == h_scroll)
1313 max = h_scroll.Maximum - item_control.Width;
1315 max = v_scroll.Maximum - item_control.Height;
1317 int val = scrollbar.Value + delta;
1320 else if (val < scrollbar.Minimum)
1321 val = scrollbar.Minimum;
1322 scrollbar.Value = val;
1325 private void CalculateScrollBars ()
1327 Rectangle client_area = ClientRectangle;
1328 int height = client_area.Height;
1329 int width = client_area.Width;
1332 h_scroll.Visible = false;
1333 v_scroll.Visible = false;
1334 item_control.Size = new Size (width, height);
1335 header_control.Width = width;
1339 // Don't calculate if the view is not displayable
1340 if (client_area.Height < 0 || client_area.Width < 0)
1343 // making a scroll bar visible might make
1344 // other scroll bar visible
1345 if (layout_wd > client_area.Right) {
1346 h_scroll.Visible = true;
1347 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1348 v_scroll.Visible = true;
1350 v_scroll.Visible = false;
1351 } else if (layout_ht > client_area.Bottom) {
1352 v_scroll.Visible = true;
1353 if ((layout_wd + v_scroll.Width) > client_area.Right)
1354 h_scroll.Visible = true;
1356 h_scroll.Visible = false;
1358 h_scroll.Visible = false;
1359 v_scroll.Visible = false;
1363 if (h_scroll.is_visible) {
1364 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1365 h_scroll.Minimum = 0;
1367 // if v_scroll is visible, adjust the maximum of the
1368 // h_scroll to account for the width of v_scroll
1369 if (v_scroll.Visible) {
1370 h_scroll.Maximum = layout_wd + v_scroll.Width;
1371 h_scroll.Width = client_area.Width - v_scroll.Width;
1374 h_scroll.Maximum = layout_wd;
1375 h_scroll.Width = client_area.Width;
1378 h_scroll.LargeChange = client_area.Width;
1379 h_scroll.SmallChange = item_size.Width + ThemeEngine.Current.ListViewHorizontalSpacing;
1380 height -= h_scroll.Height;
1383 if (v_scroll.is_visible) {
1384 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1385 v_scroll.Minimum = 0;
1387 // if h_scroll is visible, adjust the maximum of the
1388 // v_scroll to account for the height of h_scroll
1389 if (h_scroll.Visible) {
1390 v_scroll.Maximum = layout_ht + h_scroll.Height;
1391 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1393 v_scroll.Maximum = layout_ht;
1394 v_scroll.Height = client_area.Height;
1397 v_scroll.LargeChange = client_area.Height;
1398 v_scroll.SmallChange = Font.Height;
1399 width -= v_scroll.Width;
1402 item_control.Size = new Size (width, height);
1404 if (header_control.is_visible)
1405 header_control.Width = width;
1409 internal int GetReorderedColumnIndex (ColumnHeader column)
1411 if (reordered_column_indices == null)
1412 return column.Index;
1414 for (int i = 0; i < Columns.Count; i++)
1415 if (reordered_column_indices [i] == column.Index)
1422 internal ColumnHeader GetReorderedColumn (int index)
1424 if (reordered_column_indices == null)
1425 return Columns [index];
1427 return Columns [reordered_column_indices [index]];
1430 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1434 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1436 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1440 header_control.Invalidate ();
1441 item_control.Invalidate ();
1447 int column_count = Columns.Count;
1449 if (reordered_column_indices == null) {
1450 reordered_column_indices = new int [column_count];
1451 for (int i = 0; i < column_count; i++)
1452 reordered_column_indices [i] = i;
1455 if (reordered_column_indices [index] == col.Index)
1458 int[] curr = reordered_column_indices;
1459 int [] result = new int [column_count];
1461 for (int i = 0; i < column_count; i++) {
1462 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1466 result [i] = col.Index;
1468 result [i] = curr [curr_idx++];
1471 ReorderColumns (result, true);
1474 internal void ReorderColumns (int [] display_indices, bool redraw)
1476 reordered_column_indices = display_indices;
1477 for (int i = 0; i < Columns.Count; i++) {
1478 ColumnHeader col = Columns [i];
1479 col.InternalDisplayIndex = reordered_column_indices [i];
1481 if (redraw && view == View.Details && IsHandleCreated) {
1483 header_control.Invalidate ();
1484 item_control.Invalidate ();
1488 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1490 int column_count = Columns.Count;
1491 newCol.SetListView (this);
1493 int [] display_indices = new int [column_count];
1494 for (int i = 0; i < column_count; i++) {
1495 ColumnHeader col = Columns [i];
1497 display_indices [i] = index;
1499 int display_index = col.InternalDisplayIndex;
1500 if (display_index < index) {
1501 display_indices [i] = display_index;
1503 display_indices [i] = (display_index + 1);
1508 ReorderColumns (display_indices, redraw);
1512 Size LargeIconItemSize
1515 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1516 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1517 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1518 int w = Math.Max (text_size.Width, image_w);
1521 w += 2 + CheckBoxSize.Width;
1523 return new Size (w, h);
1527 Size SmallIconItemSize {
1529 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1530 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1531 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1532 int w = text_size.Width + image_w;
1535 w += 2 + CheckBoxSize.Width;
1537 return new Size (w, h);
1544 // Calculate tile size if needed
1545 // It appears that using Font.Size instead of a SizeF value can give us
1546 // a slightly better approach to the proportions defined in .Net
1547 if (tile_size == Size.Empty) {
1548 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1549 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1550 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1551 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1553 tile_size = new Size (w, h);
1561 int GetDetailsItemHeight ()
1564 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1565 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1566 item_height = Math.Max (checkbox_height, text_size.Height);
1567 item_height = Math.Max (item_height, small_image_height);
1571 void SetItemLocation (int index, int x, int y, int row, int col)
1573 Point old_location = items_location [index];
1574 if (old_location.X == x && old_location.Y == y)
1577 Size item_size = ItemSize;
1578 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1580 items_location [index] = new Point (x, y);
1581 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1584 // Initial position matches item's position in ListViewItemCollection
1586 reordered_items_indices [index] = index;
1588 // Invalidate both previous and new bounds
1589 item_control.Invalidate (old_rect);
1590 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1594 void ShiftItemsPositions (int from, int to, bool forward)
1597 for (int i = to + 1; i > from; i--) {
1598 reordered_items_indices [i] = reordered_items_indices [i - 1];
1600 ListViewItem item = items [reordered_items_indices [i]];
1602 item.DisplayIndex = i;
1606 for (int i = from - 1; i < to; i++) {
1607 reordered_items_indices [i] = reordered_items_indices [i + 1];
1609 ListViewItem item = items [reordered_items_indices [i]];
1611 item.DisplayIndex = i;
1617 internal void ChangeItemLocation (int display_index, Point new_pos)
1619 int new_display_index = GetDisplayIndexFromLocation (new_pos);
1620 if (new_display_index == display_index)
1623 int item_index = reordered_items_indices [display_index];
1624 ListViewItem item = items [item_index];
1626 bool forward = new_display_index < display_index;
1627 int index_from, index_to;
1629 index_from = new_display_index;
1630 index_to = display_index - 1;
1632 index_from = display_index + 1;
1633 index_to = new_display_index;
1636 ShiftItemsPositions (index_from, index_to, forward);
1638 reordered_items_indices [new_display_index] = item_index;
1641 item.DisplayIndex = new_display_index;
1645 int GetDisplayIndexFromLocation (Point loc)
1647 int display_index = -1;
1648 Rectangle item_area;
1651 if (loc.X < 0 || loc.Y < 0)
1654 // Adjustment to put in the next position refered by 'loc'
1655 loc.X -= item_size.Width / 2;
1659 for (int i = 0; i < items.Count; i++) {
1660 item_area = new Rectangle (GetItemLocation (i), item_size);
1661 item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
1662 ThemeEngine.Current.ListViewVerticalSpacing);
1664 if (item_area.Contains (loc)) {
1670 // Put in in last position
1671 if (display_index == -1)
1672 display_index = items.Count - 1;
1674 return display_index;
1677 // When using groups, the items with no group assigned
1678 // belong to the DefaultGroup
1679 int GetDefaultGroupItems ()
1682 foreach (ListViewItem item in items)
1683 if (item.Group == null)
1692 int[,] item_index_matrix;
1694 void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1696 Rectangle area = ClientRectangle;
1698 if (UseCustomColumnWidth)
1699 CalculateCustomColumnWidth ();
1701 if (show_groups && groups.Count > 0 && view != View.List) {
1702 // When groups are used the alignment is always top-aligned
1707 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1708 for (int i = 0; i < groups.InternalCount; i++) {
1709 ListViewGroup group = groups.GetInternalGroup (i);
1710 int items_in_group = group.GetActualItemCount ();
1712 if (items_in_group == 0)
1715 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1716 if (group_cols <= 0)
1718 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1720 group.starting_row = rows;
1721 group.rows = group_rows;
1722 group.starting_item = items;
1723 group.current_item = 0; // Reset layout
1725 cols = Math.Max (group_cols, cols);
1727 items += items_in_group;
1732 // Simple matrix if no groups are used
1734 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1737 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1739 if (UseCustomColumnWidth)
1740 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width) / (double)(custom_column_width));
1742 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1747 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1751 item_index_matrix = new int [rows, cols];
1754 // When using custom column width, we look for the minimum one
1755 void CalculateCustomColumnWidth ()
1757 int min_width = Int32.MaxValue;
1758 for (int i = 0; i < columns.Count; i++) {
1759 int col_width = columns [i].Width;
1761 if (col_width < min_width)
1762 min_width = col_width;
1765 custom_column_width = min_width;
1768 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1770 header_control.Visible = false;
1771 header_control.Size = Size.Empty;
1772 item_control.Visible = true;
1773 item_control.Location = Point.Empty;
1774 ItemSize = item_size; // Cache item size
1776 if (items.Count == 0)
1779 Size sz = item_size;
1781 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1784 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1786 layout_wd = UseCustomColumnWidth ? cols * custom_column_width : cols * (sz.Width + x_spacing) - x_spacing;
1787 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1790 CalculateGroupsLayout (sz, y_spacing, 0);
1793 int row = 0, col = 0;
1795 int display_index = 0;
1797 for (int i = 0; i < items.Count; i++) {
1800 ListViewGroup group = items [i].Group;
1802 group = groups.DefaultGroup;
1804 Point group_items_loc = group.items_area_location;
1805 int current_item = group.current_item++;
1806 int starting_row = group.starting_row;
1808 display_index = group.starting_item + current_item;
1809 row = (current_item / cols);
1810 col = current_item % cols;
1812 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1813 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1815 SetItemLocation (display_index, x, y, row + starting_row, col);
1816 SetItemAtDisplayIndex (display_index, i);
1817 item_index_matrix [row + starting_row, col] = i;
1822 x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
1823 y = row * (item_size.Height + y_spacing);
1824 display_index = i; // Same as item index in Items
1826 SetItemLocation (i, x, y, row, col);
1827 item_index_matrix [row, col] = i;
1836 if (++col == cols) {
1846 ListViewItem item = items [i];
1848 item.DisplayIndex = display_index;
1850 item.SetPosition (new Point (x, y));
1857 item_control.Size = new Size (layout_wd, layout_ht);
1861 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1864 bool details = view == View.Details;
1866 for (int i = 0; i < groups.InternalCount; i++) {
1867 ListViewGroup group = groups.GetInternalGroup (i);
1868 if (group.ItemCount == 0)
1871 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1874 layout_ht = y; // Update height taking into account Groups' headers heights
1877 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1879 Rectangle client_area = ClientRectangle;
1880 int header_height = text_size.Height + 10;
1882 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1883 group.items_area_location = new Point (0, y_origin + header_height);
1885 int items_area_height = ((item_height + y_spacing) * rows);
1886 return header_height + items_area_height + 10; // Add a small bottom margin
1889 void CalculateDetailsGroupItemsCount ()
1893 for (int i = 0; i < groups.InternalCount; i++) {
1894 ListViewGroup group = groups.GetInternalGroup (i);
1895 int items_in_group = group.GetActualItemCount ();
1897 if (items_in_group == 0)
1900 group.starting_item = items;
1901 group.current_item = 0; // Reset layout.
1902 items += items_in_group;
1907 void LayoutHeader ()
1910 for (int i = 0; i < Columns.Count; i++) {
1911 ColumnHeader col = GetReorderedColumn (i);
1914 col.CalcColumnHeader ();
1920 if (x < ClientRectangle.Width)
1921 x = ClientRectangle.Width;
1923 if (header_style == ColumnHeaderStyle.None) {
1924 header_control.Visible = false;
1925 header_control.Size = Size.Empty;
1926 layout_wd = ClientRectangle.Width;
1928 header_control.Width = x;
1929 header_control.Height = columns.Count > 0 ? columns [0].Ht : ThemeEngine.Current.ListViewGetHeaderHeight (this, Font);
1930 header_control.Visible = true;
1934 void LayoutDetails ()
1938 if (columns.Count == 0) {
1939 item_control.Visible = false;
1940 layout_wd = ClientRectangle.Width;
1941 layout_ht = ClientRectangle.Height;
1945 item_control.Visible = true;
1946 item_control.Location = Point.Empty;
1947 item_control.Width = ClientRectangle.Width;
1949 int item_height = GetDetailsItemHeight ();
1950 ItemSize = new Size (0, item_height); // We only cache Height for details view
1951 int y = header_control.Height;
1953 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1955 CalculateDetailsGroupItemsCount ();
1956 CalculateGroupsLayout (ItemSize, 2, y);
1960 for (int i = 0; i < items.Count; i++) {
1961 ListViewItem item = items [i];
1965 ListViewGroup group = item.Group;
1967 group = groups.DefaultGroup;
1969 int current_item = group.current_item++;
1970 Point group_items_loc = group.items_area_location;
1971 display_index = group.starting_item + current_item;
1973 y = current_item * (item_height + 2) + group_items_loc.Y;
1974 SetItemLocation (display_index, 0, y, 0, 0);
1975 SetItemAtDisplayIndex (display_index, i);
1976 item.SetPosition (new Point (0, y));
1981 SetItemLocation (i, 0, y, 0, 0);
1983 item.SetPosition (new Point (0, y));
1988 if (!virtual_mode) // Virtual mode sets Layout until draw time
1992 item.DisplayIndex = display_index;
1997 // some space for bottom gridline
1998 if (items.Count > 0 && grid_lines)
2002 if (!using_groups) // With groups it has been previously computed
2007 private void AdjustItemsPositionArray (int count)
2009 if (items_location.Length >= count)
2012 // items_location, items_matrix_location and reordered_items_indices must keep the same length
2013 count = Math.Max (count, items_location.Length * 2);
2014 items_location = new Point [count];
2015 items_matrix_location = new ItemMatrixLocation [count];
2016 reordered_items_indices = new int [count];
2019 private void CalculateListView (ListViewAlignment align)
2023 AdjustItemsPositionArray (items.Count);
2030 case View.SmallIcon:
2031 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
2032 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2035 case View.LargeIcon:
2036 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
2037 ThemeEngine.Current.ListViewHorizontalSpacing,
2038 ThemeEngine.Current.ListViewVerticalSpacing);
2042 LayoutIcons (SmallIconItemSize, true,
2043 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2047 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
2048 ThemeEngine.Current.ListViewHorizontalSpacing,
2049 ThemeEngine.Current.ListViewVerticalSpacing);
2054 CalculateScrollBars ();
2057 internal Point GetItemLocation (int index)
2059 Point loc = items_location [index];
2060 loc.X -= h_marker; // Adjust to scroll
2066 internal int GetItemIndex (int display_index)
2068 return reordered_items_indices [display_index];
2071 internal ListViewItem GetItemAtDisplayIndex (int display_index)
2073 return items [reordered_items_indices [display_index]];
2076 internal void SetItemAtDisplayIndex (int display_index, int index)
2078 reordered_items_indices [display_index] = index;
2081 private bool KeySearchString (KeyEventArgs ke)
2083 int current_tickcnt = Environment.TickCount;
2084 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2085 keysearch_text = string.Empty;
2088 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2091 keysearch_text += (char)ke.KeyCode;
2092 keysearch_tickcnt = current_tickcnt;
2094 int prev_focused = FocusedItem == null ? 0 : FocusedItem.DisplayIndex;
2095 int start = prev_focused + 1 < Items.Count ? prev_focused + 1 : 0;
2097 ListViewItem item = FindItemWithText (keysearch_text, false, start, true, true);
2098 if (item != null && prev_focused != item.DisplayIndex) {
2099 selected_indices.Clear ();
2101 SetFocusedItem (item.DisplayIndex);
2102 item.Selected = true;
2103 EnsureVisible (GetItemIndex (item.DisplayIndex));
2109 private void OnItemsChanged ()
2111 ResetSearchString ();
2114 private void ResetSearchString ()
2116 keysearch_text = String.Empty;
2119 int GetAdjustedIndex (Keys key)
2123 if (View == View.Details) {
2126 result = FocusedItem.DisplayIndex - 1;
2129 result = FocusedItem.DisplayIndex + 1;
2130 if (result == items.Count)
2134 int last_index = LastVisibleIndex;
2135 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2136 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2138 if (FocusedItem.DisplayIndex == last_index) {
2139 if (FocusedItem.DisplayIndex < Items.Count - 1) {
2140 int page_size = item_control.Height / ItemSize.Height - 1;
2141 result = FocusedItem.DisplayIndex + page_size - 1;
2142 if (result >= Items.Count)
2143 result = Items.Count - 1;
2146 result = last_index;
2149 int first_index = FirstVisibleIndex;
2150 if (GetItemLocation (first_index).Y < 0)
2152 if (FocusedItem.DisplayIndex == first_index) {
2153 if (first_index > 0) {
2154 int page_size = item_control.Height / ItemSize.Height - 1;
2155 result = first_index - page_size + 1;
2160 result = first_index;
2166 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.DisplayIndex];
2167 int row = item_matrix_location.Row;
2168 int col = item_matrix_location.Col;
2170 int adjusted_index = -1;
2176 adjusted_index = item_index_matrix [row, col - 1];
2180 if (col == (cols - 1))
2182 while (item_index_matrix [row, col + 1] == 0) {
2187 adjusted_index = item_index_matrix [row, col + 1];
2193 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2198 adjusted_index = item_index_matrix [row - 1, col];
2202 if (row == (rows - 1) || row == Items.Count - 1)
2204 while (item_index_matrix [row + 1, col] == 0) {
2209 adjusted_index = item_index_matrix [row + 1, col];
2216 return items [adjusted_index].DisplayIndex;
2219 ListViewItem selection_start;
2221 private bool SelectItems (ArrayList sel_items)
2223 bool changed = false;
2224 foreach (ListViewItem item in SelectedItems)
2225 if (!sel_items.Contains (item)) {
2226 item.Selected = false;
2229 foreach (ListViewItem item in sel_items)
2230 if (!item.Selected) {
2231 item.Selected = true;
2237 private void UpdateMultiSelection (int index, bool reselect)
2239 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2240 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2241 ListViewItem item = GetItemAtDisplayIndex (index);
2243 if (shift_pressed && selection_start != null) {
2244 ArrayList list = new ArrayList ();
2245 int start_index = selection_start.DisplayIndex;
2246 int start = Math.Min (start_index, index);
2247 int end = Math.Max (start_index, index);
2248 if (View == View.Details) {
2249 for (int i = start; i <= end; i++)
2250 list.Add (GetItemAtDisplayIndex (i));
2252 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2253 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2254 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2255 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2256 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2257 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2259 for (int i = 0; i < items.Count; i++) {
2260 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2262 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2263 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2264 list.Add (GetItemAtDisplayIndex (i));
2268 } else if (ctrl_pressed) {
2269 item.Selected = !item.Selected;
2270 selection_start = item;
2273 // do not unselect, and reselect the item
2274 foreach (int itemIndex in SelectedIndices) {
2275 if (index == itemIndex)
2277 items [itemIndex].Selected = false;
2280 SelectedItems.Clear ();
2281 item.Selected = true;
2283 selection_start = item;
2287 internal override bool InternalPreProcessMessage (ref Message msg)
2289 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2290 Keys key_data = (Keys)msg.WParam.ToInt32();
2292 HandleNavKeys (key_data);
2295 return base.InternalPreProcessMessage (ref msg);
2298 bool HandleNavKeys (Keys key_data)
2300 if (Items.Count == 0 || !item_control.Visible)
2303 if (FocusedItem == null)
2308 SelectIndex (Items.Count - 1);
2321 SelectIndex (GetAdjustedIndex (key_data));
2325 SelectIndex (focused_item_index);
2326 ToggleItemsCheckState ();
2329 if (selected_indices.Count > 0)
2330 OnItemActivate (EventArgs.Empty);
2340 void ToggleItemsCheckState ()
2345 // Don't modify check state if StateImageList has less than 2 elements
2346 if (StateImageList != null && StateImageList.Images.Count < 2)
2349 if (SelectedIndices.Count > 0) {
2350 for (int i = 0; i < SelectedIndices.Count; i++) {
2351 ListViewItem item = Items [SelectedIndices [i]];
2352 item.Checked = !item.Checked;
2357 if (FocusedItem != null) {
2358 FocusedItem.Checked = !FocusedItem.Checked;
2359 SelectIndex (FocusedItem.Index);
2363 void SelectIndex (int display_index)
2365 if (display_index == -1)
2369 UpdateMultiSelection (display_index, true);
2370 else if (!GetItemAtDisplayIndex (display_index).Selected)
2371 GetItemAtDisplayIndex (display_index).Selected = true;
2373 SetFocusedItem (display_index);
2374 EnsureVisible (GetItemIndex (display_index)); // Index in Items collection, not display index
2377 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2379 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2382 if (ke.Alt || ke.Control)
2385 ke.Handled = KeySearchString (ke);
2388 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2390 Point loc = PointToClient (Control.MousePosition);
2391 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2394 internal class ItemControl : Control {
2397 ListViewItem clicked_item;
2398 ListViewItem last_clicked_item;
2399 bool hover_processed = false;
2400 bool checking = false;
2401 ListViewItem prev_hovered_item;
2403 ListViewItem prev_tooltip_item;
2406 Point drag_begin = new Point (-1, -1);
2407 internal int dragged_item_index = -1;
2409 ListViewLabelEditTextBox edit_text_box;
2410 internal ListViewItem edit_item;
2411 LabelEditEventArgs edit_args;
2413 public ItemControl (ListView owner)
2416 this.SetStyle (ControlStyles.DoubleBuffer, true);
2417 DoubleClick += new EventHandler(ItemsDoubleClick);
2418 MouseDown += new MouseEventHandler(ItemsMouseDown);
2419 MouseMove += new MouseEventHandler(ItemsMouseMove);
2420 MouseHover += new EventHandler(ItemsMouseHover);
2421 MouseUp += new MouseEventHandler(ItemsMouseUp);
2424 void ItemsDoubleClick (object sender, EventArgs e)
2426 if (owner.activation == ItemActivation.Standard)
2427 owner.OnItemActivate (EventArgs.Empty);
2437 BoxSelect box_select_mode = BoxSelect.None;
2438 IList prev_selection;
2439 Point box_select_start;
2441 Rectangle box_select_rect;
2442 internal Rectangle BoxSelectRectangle {
2443 get { return box_select_rect; }
2445 if (box_select_rect == value)
2448 InvalidateBoxSelectRect ();
2449 box_select_rect = value;
2450 InvalidateBoxSelectRect ();
2454 void InvalidateBoxSelectRect ()
2456 if (BoxSelectRectangle.Size.IsEmpty)
2459 Rectangle edge = BoxSelectRectangle;
2465 edge.Y = BoxSelectRectangle.Bottom - 1;
2467 edge.Y = BoxSelectRectangle.Y - 1;
2469 edge.Height = BoxSelectRectangle.Height + 2;
2471 edge.X = BoxSelectRectangle.Right - 1;
2475 private Rectangle CalculateBoxSelectRectangle (Point pt)
2477 int left = Math.Min (box_select_start.X, pt.X);
2478 int right = Math.Max (box_select_start.X, pt.X);
2479 int top = Math.Min (box_select_start.Y, pt.Y);
2480 int bottom = Math.Max (box_select_start.Y, pt.Y);
2481 return Rectangle.FromLTRB (left, top, right, bottom);
2484 bool BoxIntersectsItem (int index)
2486 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2487 if (owner.View != View.Details) {
2489 r.Y += r.Height / 4;
2493 return BoxSelectRectangle.IntersectsWith (r);
2496 bool BoxIntersectsText (int index)
2498 Rectangle r = owner.GetItemAtDisplayIndex (index).TextBounds;
2499 return BoxSelectRectangle.IntersectsWith (r);
2502 ArrayList BoxSelectedItems {
2504 ArrayList result = new ArrayList ();
2505 for (int i = 0; i < owner.Items.Count; i++) {
2508 // Can't iterate over specific items properties in virtualmode
2509 if (owner.View == View.Details && !owner.FullRowSelect && !owner.VirtualMode)
2511 if (owner.View == View.Details && !owner.FullRowSelect)
2513 intersects = BoxIntersectsText (i);
2515 intersects = BoxIntersectsItem (i);
2518 result.Add (owner.GetItemAtDisplayIndex (i));
2524 private bool PerformBoxSelection (Point pt)
2526 if (box_select_mode == BoxSelect.None)
2529 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2531 ArrayList box_items = BoxSelectedItems;
2535 switch (box_select_mode) {
2537 case BoxSelect.Normal:
2541 case BoxSelect.Control:
2542 items = new ArrayList ();
2543 foreach (int index in prev_selection)
2544 if (!box_items.Contains (owner.Items [index]))
2545 items.Add (owner.Items [index]);
2546 foreach (ListViewItem item in box_items)
2547 if (!prev_selection.Contains (item.Index))
2551 case BoxSelect.Shift:
2553 foreach (ListViewItem item in box_items)
2554 prev_selection.Remove (item.Index);
2555 foreach (int index in prev_selection)
2556 items.Add (owner.Items [index]);
2560 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2564 owner.SelectItems (items);
2570 private void ItemsMouseDown (object sender, MouseEventArgs me)
2572 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2573 if (owner.items.Count == 0)
2576 bool box_selecting = false;
2577 Size item_size = owner.ItemSize;
2578 Point pt = new Point (me.X, me.Y);
2579 for (int i = 0; i < owner.items.Count; i++) {
2580 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2581 if (!item_rect.Contains (pt))
2584 // Actual item in 'i' position
2585 ListViewItem item = owner.GetItemAtDisplayIndex (i);
2587 if (item.CheckRectReal.Contains (pt)) {
2588 // Don't modify check state if we have only one image
2589 // and if we are in 1.1 profile only take into account
2591 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2598 // Generate an extra ItemCheck event when we got two clicks
2599 // (Match weird .Net behaviour)
2601 item.Checked = !item.Checked;
2603 item.Checked = !item.Checked;
2608 if (owner.View == View.Details) {
2609 bool over_text = item.TextBounds.Contains (pt);
2610 if (owner.FullRowSelect) {
2611 clicked_item = owner.items [i];
2612 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2613 if (!over_text && over_item_column && owner.MultiSelect)
2614 box_selecting = true;
2615 } else if (over_text)
2616 clicked_item = item;
2618 owner.SetFocusedItem (i);
2620 clicked_item = item;
2626 if (clicked_item != null) {
2627 bool changed = !clicked_item.Selected;
2628 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2629 owner.SetFocusedItem (clicked_item.DisplayIndex);
2631 if (owner.MultiSelect) {
2632 bool reselect = (!owner.LabelEdit || changed);
2633 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2634 owner.UpdateMultiSelection (clicked_item.DisplayIndex, reselect);
2636 clicked_item.Selected = true;
2640 if (owner.VirtualMode && changed) {
2641 // Broken event - It's not fired from Item.Selected also
2642 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2643 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2645 owner.OnVirtualItemsSelectionRangeChanged (args);
2648 // Report clicks only if the item was clicked. On MS the
2649 // clicks are only raised if you click an item
2651 if (me.Clicks > 1) {
2652 if (owner.CheckBoxes)
2653 clicked_item.Checked = !clicked_item.Checked;
2654 } else if (me.Clicks == 1) {
2655 if (owner.LabelEdit && !changed)
2656 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2659 if (owner.MultiSelect)
2660 box_selecting = true;
2661 else if (owner.SelectedItems.Count > 0)
2662 owner.SelectedItems.Clear ();
2665 if (box_selecting) {
2666 Keys mods = XplatUI.State.ModifierKeys;
2667 if ((mods & Keys.Shift) != 0)
2668 box_select_mode = BoxSelect.Shift;
2669 else if ((mods & Keys.Control) != 0)
2670 box_select_mode = BoxSelect.Control;
2672 box_select_mode = BoxSelect.Normal;
2673 box_select_start = pt;
2674 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2678 private void ItemsMouseMove (object sender, MouseEventArgs me)
2680 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2682 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2686 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2687 !hover_processed && owner.Activation != ItemActivation.OneClick
2689 && !owner.ShowItemToolTips
2694 Point pt = PointToClient (Control.MousePosition);
2695 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2697 if (hover_processed && item != null && item != prev_hovered_item) {
2698 hover_processed = false;
2699 XplatUI.ResetMouseHover (Handle);
2702 // Need to invalidate the item in HotTracking to show/hide the underline style
2703 if (owner.Activation == ItemActivation.OneClick) {
2704 if (item == null && owner.HotItemIndex != -1) {
2706 if (owner.HotTracking)
2707 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2710 Cursor = Cursors.Default;
2711 owner.HotItemIndex = -1;
2712 } else if (item != null && owner.HotItemIndex == -1) {
2714 if (owner.HotTracking)
2715 Invalidate (item.Bounds);
2718 Cursor = Cursors.Hand;
2719 owner.HotItemIndex = item.Index;
2723 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2724 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2726 drag_begin = new Point (me.X, me.Y);
2727 dragged_item_index = item.Index;
2731 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2732 if (!r.Contains (me.X, me.Y)) {
2733 ListViewItem dragged_item = owner.items [dragged_item_index];
2734 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2736 drag_begin = new Point (-1, -1);
2737 dragged_item_index = -1;
2743 if (owner.ShowItemToolTips) {
2745 owner.item_tooltip.Active = false;
2746 prev_tooltip_item = null;
2747 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2748 owner.item_tooltip.Active = true;
2749 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2750 prev_tooltip_item = item;
2757 private void ItemsMouseHover (object sender, EventArgs e)
2759 if (owner.hover_pending) {
2760 owner.OnMouseHover (e);
2761 owner.hover_pending = false;
2767 hover_processed = true;
2768 Point pt = PointToClient (Control.MousePosition);
2769 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2773 prev_hovered_item = item;
2775 if (owner.HoverSelection) {
2776 if (owner.MultiSelect)
2777 owner.UpdateMultiSelection (item.Index, true);
2779 item.Selected = true;
2781 owner.SetFocusedItem (item.DisplayIndex);
2782 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2786 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2790 void HandleClicks (MouseEventArgs me)
2792 // if the click is not on an item,
2793 // clicks remains as 0
2796 owner.OnDoubleClick (EventArgs.Empty);
2797 } else if (clicks == 1) {
2798 owner.OnClick (EventArgs.Empty);
2800 owner.OnDoubleClick (EventArgs.Empty);
2801 owner.OnMouseDoubleClick (me);
2802 } else if (clicks == 1) {
2803 owner.OnClick (EventArgs.Empty);
2804 owner.OnMouseClick (me);
2811 private void ItemsMouseUp (object sender, MouseEventArgs me)
2813 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2814 HandleClicks (owner_me);
2817 if (owner.Items.Count == 0) {
2819 owner.OnMouseUp (owner_me);
2823 Point pt = new Point (me.X, me.Y);
2825 Rectangle rect = Rectangle.Empty;
2826 if (clicked_item != null) {
2827 if (owner.view == View.Details && !owner.full_row_select)
2828 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2830 rect = clicked_item.Bounds;
2832 if (rect.Contains (pt)) {
2833 switch (owner.activation) {
2834 case ItemActivation.OneClick:
2835 owner.OnItemActivate (EventArgs.Empty);
2838 case ItemActivation.TwoClick:
2839 if (last_clicked_item == clicked_item) {
2840 owner.OnItemActivate (EventArgs.Empty);
2841 last_clicked_item = null;
2843 last_clicked_item = clicked_item;
2846 // DoubleClick activation is handled in another handler
2850 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2851 // Need this to clean up background clicks
2852 owner.SelectedItems.Clear ();
2856 owner.OnMouseUp (owner_me);
2859 private void ResetMouseState ()
2861 clicked_item = null;
2862 box_select_start = Point.Empty;
2863 BoxSelectRectangle = Rectangle.Empty;
2864 prev_selection = null;
2865 box_select_mode = BoxSelect.None;
2868 // Clean these bits in case the mouse buttons were
2869 // released before firing ItemDrag
2870 dragged_item_index = -1;
2871 drag_begin = new Point (-1, -1);
2874 private void LabelEditFinished (object sender, EventArgs e)
2876 EndEdit (edit_item);
2879 private void LabelEditCancelled (object sender, EventArgs e)
2881 edit_args.SetLabel (null);
2882 EndEdit (edit_item);
2885 private void LabelTextChanged (object sender, EventArgs e)
2887 if (edit_args != null)
2888 edit_args.SetLabel (edit_text_box.Text);
2891 internal void BeginEdit (ListViewItem item)
2893 if (edit_item != null)
2894 EndEdit (edit_item);
2896 if (edit_text_box == null) {
2897 edit_text_box = new ListViewLabelEditTextBox ();
2898 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2899 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2900 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2901 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2902 edit_text_box.Visible = false;
2903 Controls.Add (edit_text_box);
2906 item.EnsureVisible();
2908 edit_text_box.Reset ();
2910 switch (owner.view) {
2912 case View.SmallIcon:
2914 edit_text_box.TextAlign = HorizontalAlignment.Left;
2915 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2916 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2917 edit_text_box.Width = (int)sizef.Width + 4;
2918 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2919 edit_text_box.WordWrap = false;
2920 edit_text_box.Multiline = false;
2922 case View.LargeIcon:
2923 edit_text_box.TextAlign = HorizontalAlignment.Center;
2924 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2925 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2926 edit_text_box.Width = (int)sizef.Width + 4;
2927 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2928 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2929 edit_text_box.WordWrap = true;
2930 edit_text_box.Multiline = true;
2936 edit_text_box.Text = item.Text;
2937 edit_text_box.Font = item.Font;
2938 edit_text_box.Visible = true;
2939 edit_text_box.Focus ();
2940 edit_text_box.SelectAll ();
2942 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2943 owner.OnBeforeLabelEdit (edit_args);
2945 if (edit_args.CancelEdit)
2949 internal void CancelEdit (ListViewItem item)
2951 // do nothing if there's no item being edited, or if the
2952 // item being edited is not the one passed in
2953 if (edit_item == null || edit_item != item)
2956 edit_args.SetLabel (null);
2960 internal void EndEdit (ListViewItem item)
2962 // do nothing if there's no item being edited, or if the
2963 // item being edited is not the one passed in
2964 if (edit_item == null || edit_item != item)
2967 if (edit_text_box != null) {
2968 if (edit_text_box.Visible)
2969 edit_text_box.Visible = false;
2970 // ensure listview gets focus
2974 // Same as TreeView.EndEdit: need to have focus in synch
2975 Application.DoEvents ();
2978 // Create a new instance, since we could get a call to BeginEdit
2979 // from the handler and have fields out of synch
2981 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2984 owner.OnAfterLabelEdit (args);
2985 if (!args.CancelEdit && args.Label != null)
2986 item.Text = args.Label;
2989 internal override void OnPaintInternal (PaintEventArgs pe)
2991 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2994 protected override void WndProc (ref Message m)
2996 switch ((Msg)m.Msg) {
2997 case Msg.WM_KILLFOCUS:
2998 owner.Select (false, true);
3000 case Msg.WM_SETFOCUS:
3001 owner.Select (false, true);
3003 case Msg.WM_LBUTTONDOWN:
3005 owner.Select (false, true);
3007 case Msg.WM_RBUTTONDOWN:
3009 owner.Select (false, true);
3014 base.WndProc (ref m);
3018 internal class ListViewLabelEditTextBox : TextBox
3023 int max_height = -1;
3024 int min_height = -1;
3026 int old_number_lines = 1;
3028 SizeF text_size_one_char;
3030 public ListViewLabelEditTextBox ()
3032 min_height = DefaultSize.Height;
3033 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3036 public int MaxWidth {
3038 if (value < min_width)
3039 max_width = min_width;
3045 public int MaxHeight {
3047 if (value < min_height)
3048 max_height = min_height;
3054 public new int Width {
3064 public override Font Font {
3070 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3074 protected override void OnTextChanged (EventArgs e)
3076 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3078 int new_width = (int)text_size.Width + 8;
3081 ResizeTextBoxWidth (new_width);
3083 if (Width != max_width)
3084 ResizeTextBoxWidth (new_width);
3086 int number_lines = Lines.Length;
3088 if (number_lines != old_number_lines) {
3089 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3090 old_number_lines = number_lines;
3092 ResizeTextBoxHeight (new_height);
3096 base.OnTextChanged (e);
3099 protected override bool IsInputKey (Keys key_data)
3101 if ((key_data & Keys.Alt) == 0) {
3102 switch (key_data & Keys.KeyCode) {
3109 return base.IsInputKey (key_data);
3112 protected override void OnKeyDown (KeyEventArgs e)
3117 switch (e.KeyCode) {
3121 OnEditingFinished (e);
3126 OnEditingCancelled (e);
3131 protected override void OnLostFocus (EventArgs e)
3134 OnEditingFinished (e);
3138 protected void OnEditingCancelled (EventArgs e)
3140 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3145 protected void OnEditingFinished (EventArgs e)
3147 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3152 private void ResizeTextBoxWidth (int new_width)
3154 if (new_width > max_width)
3155 base.Width = max_width;
3157 if (new_width >= min_width)
3158 base.Width = new_width;
3160 base.Width = min_width;
3163 private void ResizeTextBoxHeight (int new_height)
3165 if (new_height > max_height)
3166 base.Height = max_height;
3168 if (new_height >= min_height)
3169 base.Height = new_height;
3171 base.Height = min_height;
3174 public void Reset ()
3181 old_number_lines = 1;
3183 Text = String.Empty;
3188 static object EditingCancelledEvent = new object ();
3189 public event EventHandler EditingCancelled {
3190 add { Events.AddHandler (EditingCancelledEvent, value); }
3191 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3194 static object EditingFinishedEvent = new object ();
3195 public event EventHandler EditingFinished {
3196 add { Events.AddHandler (EditingFinishedEvent, value); }
3197 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3201 internal override void OnPaintInternal (PaintEventArgs pe)
3206 CalculateScrollBars ();
3209 void FocusChanged (object o, EventArgs args)
3211 if (Items.Count == 0)
3214 if (FocusedItem == null)
3217 ListViewItem focused_item = FocusedItem;
3219 if (focused_item.ListView != null) {
3220 focused_item.Invalidate ();
3221 focused_item.Layout ();
3222 focused_item.Invalidate ();
3226 private void ListView_Invalidated (object sender, InvalidateEventArgs e)
3228 // When the ListView is invalidated, we need to invalidate
3229 // the child controls.
3230 header_control.Invalidate ();
3231 item_control.Invalidate ();
3234 private void ListView_MouseEnter (object sender, EventArgs args)
3236 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3239 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3241 if (Items.Count == 0)
3244 int lines = me.Delta / 120;
3251 case View.SmallIcon:
3252 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3254 case View.LargeIcon:
3255 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3258 Scroll (h_scroll, -ItemSize.Width * lines);
3262 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3268 private void ListView_SizeChanged (object sender, EventArgs e)
3270 CalculateListView (alignment);
3273 private void SetFocusedItem (int display_index)
3275 if (display_index != -1)
3276 GetItemAtDisplayIndex (display_index).Focused = true;
3277 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3278 GetItemAtDisplayIndex (focused_item_index).Focused = false;
3280 focused_item_index = display_index;
3283 private void HorizontalScroller (object sender, EventArgs e)
3285 item_control.EndEdit (item_control.edit_item);
3287 // Avoid unnecessary flickering, when button is
3288 // kept pressed at the end
3289 if (h_marker != h_scroll.Value) {
3291 int pixels = h_marker - h_scroll.Value;
3293 h_marker = h_scroll.Value;
3294 if (header_control.Visible)
3295 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3297 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3301 private void VerticalScroller (object sender, EventArgs e)
3303 item_control.EndEdit (item_control.edit_item);
3305 // Avoid unnecessary flickering, when button is
3306 // kept pressed at the end
3307 if (v_marker != v_scroll.Value) {
3308 int pixels = v_marker - v_scroll.Value;
3309 Rectangle area = item_control.ClientRectangle;
3310 if (header_control.Visible) {
3311 area.Y += header_control.Height;
3312 area.Height -= header_control.Height;
3315 v_marker = v_scroll.Value;
3316 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3320 internal override bool IsInputCharInternal (char charCode)
3324 #endregion // Internal Methods Properties
3326 #region Protected Methods
3327 protected override void CreateHandle ()
3329 base.CreateHandle ();
3330 for (int i = 0; i < SelectedItems.Count; i++)
3331 OnSelectedIndexChanged (EventArgs.Empty);
3334 protected override void Dispose (bool disposing)
3337 h_scroll.Dispose ();
3338 v_scroll.Dispose ();
3340 large_image_list = null;
3341 small_image_list = null;
3342 state_image_list = null;
3344 foreach (ColumnHeader col in columns)
3345 col.SetListView (null);
3348 if (!virtual_mode) // In virtual mode we don't save the items
3350 foreach (ListViewItem item in items)
3354 base.Dispose (disposing);
3357 protected override bool IsInputKey (Keys keyData)
3374 return base.IsInputKey (keyData);
3377 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3379 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3385 protected override void OnBackgroundImageChanged (EventArgs e)
3387 item_control.BackgroundImage = BackgroundImage;
3388 base.OnBackgroundImageChanged (e);
3392 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3394 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3399 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3401 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3407 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3409 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3414 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3416 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3421 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3423 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3429 protected override void OnEnabledChanged (EventArgs e)
3431 base.OnEnabledChanged (e);
3435 protected override void OnFontChanged (EventArgs e)
3437 base.OnFontChanged (e);
3441 protected override void OnHandleCreated (EventArgs e)
3443 base.OnHandleCreated (e);
3444 CalculateListView (alignment);
3446 if (!virtual_mode) // Sorting is not allowed in virtual mode
3451 protected override void OnHandleDestroyed (EventArgs e)
3453 base.OnHandleDestroyed (e);
3456 protected virtual void OnItemActivate (EventArgs e)
3458 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3463 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3465 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3471 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3473 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3479 protected virtual void OnItemDrag (ItemDragEventArgs e)
3481 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3487 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3489 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3494 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3496 ListViewItemSelectionChangedEventHandler eh =
3497 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3502 protected override void OnMouseHover (EventArgs e)
3504 base.OnMouseHover (e);
3507 protected override void OnParentChanged (EventArgs e)
3509 base.OnParentChanged (e);
3513 protected virtual void OnSelectedIndexChanged (EventArgs e)
3515 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3520 protected override void OnSystemColorsChanged (EventArgs e)
3522 base.OnSystemColorsChanged (e);
3526 protected internal virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3528 CacheVirtualItemsEventHandler eh = (CacheVirtualItemsEventHandler)Events [CacheVirtualItemsEvent];
3533 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3535 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3540 [EditorBrowsable (EditorBrowsableState.Advanced)]
3541 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3543 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3548 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3550 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3555 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3557 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3558 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3564 protected void RealizeProperties ()
3569 protected void UpdateExtendedStyles ()
3574 bool refocusing = false;
3576 protected override void WndProc (ref Message m)
3578 switch ((Msg)m.Msg) {
3579 case Msg.WM_KILLFOCUS:
3580 Control receiver = Control.FromHandle (m.WParam);
3581 if (receiver == item_control) {
3587 case Msg.WM_SETFOCUS:
3597 base.WndProc (ref m);
3599 #endregion // Protected Methods
3601 #region Public Instance Methods
3602 public void ArrangeIcons ()
3604 ArrangeIcons (this.alignment);
3607 public void ArrangeIcons (ListViewAlignment value)
3609 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3610 if (view == View.LargeIcon || view == View.SmallIcon) {
3611 this.CalculateListView (value);
3612 // we have done the calculations already
3613 this.Redraw (false);
3618 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3620 if (columnIndex < 0 || columnIndex >= columns.Count)
3621 throw new ArgumentOutOfRangeException ("columnIndex");
3623 columns [columnIndex].AutoResize (headerAutoResize);
3626 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3629 foreach (ColumnHeader col in columns)
3630 col.AutoResize (headerAutoResize);
3635 public void BeginUpdate ()
3637 // flag to avoid painting
3641 public void Clear ()
3644 items.Clear (); // Redraw (true) called here
3647 public void EndUpdate ()
3649 // flag to avoid painting
3652 // probably, now we need a redraw with recalculations
3656 public void EnsureVisible (int index)
3658 if (index < 0 || index >= items.Count || scrollable == false)
3661 Rectangle view_rect = item_control.ClientRectangle;
3662 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3664 if (view_rect.Contains (bounds))
3667 if (View != View.Details) {
3668 if (bounds.Left < 0)
3669 h_scroll.Value += bounds.Left;
3670 else if (bounds.Right > view_rect.Right)
3671 h_scroll.Value += (bounds.Right - view_rect.Right);
3675 v_scroll.Value += bounds.Top;
3676 else if (bounds.Bottom > view_rect.Bottom)
3677 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3681 public ListViewItem FindItemWithText (string text)
3683 if (items.Count == 0)
3686 return FindItemWithText (text, true, 0, true);
3689 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3691 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true, false);
3694 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3696 return FindItemWithText (text, includeSubItemsInSearch, startIndex, isPrefixSearch, false);
3700 internal ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch, bool roundtrip)
3702 if (startIndex < 0 || startIndex >= items.Count)
3703 throw new ArgumentOutOfRangeException ("startIndex");
3706 throw new ArgumentNullException ("text");
3710 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3711 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3712 SearchDirectionHint.Down, startIndex);
3714 OnSearchForVirtualItem (args);
3715 int idx = args.Index;
3716 if (idx >= 0 && idx < virtual_list_size)
3725 ListViewItem lvi = items [i];
3727 if (isPrefixSearch) { // prefix search
3728 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (lvi.Text, text, CompareOptions.IgnoreCase))
3730 } else if (String.Compare (lvi.Text, text, true) == 0) // match
3733 if (i + 1 >= items.Count) {
3741 if (i == startIndex)
3745 // Subitems have a minor priority, so we have to do a second linear search
3746 // Also, we don't need to to a roundtrip search for them by now
3747 if (includeSubItemsInSearch) {
3748 for (i = startIndex; i < items.Count; i++) {
3749 ListViewItem lvi = items [i];
3750 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3751 if (isPrefixSearch) {
3752 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (sub_item.Text,
3753 text, CompareOptions.IgnoreCase))
3755 } else if (String.Compare (sub_item.Text, text, true) == 0)
3764 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3766 return FindNearestItem (searchDirection, new Point (x, y));
3769 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3771 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3772 throw new ArgumentOutOfRangeException ("searchDirection");
3774 if (view != View.LargeIcon && view != View.SmallIcon)
3775 throw new InvalidOperationException ();
3778 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3779 false, false, String.Empty, point,
3782 OnSearchForVirtualItem (args);
3783 int idx = args.Index;
3784 if (idx >= 0 && idx < virtual_list_size)
3790 ListViewItem item = null;
3791 int min_dist = Int32.MaxValue;
3794 // It looks like .Net does a previous adjustment
3797 case SearchDirectionHint.Up:
3798 point.Y -= item_size.Height;
3800 case SearchDirectionHint.Down:
3801 point.Y += item_size.Height;
3803 case SearchDirectionHint.Left:
3804 point.X -= item_size.Width;
3806 case SearchDirectionHint.Right:
3807 point.X += item_size.Width;
3811 for (int i = 0; i < items.Count; i++) {
3812 Point item_loc = GetItemLocation (i);
3814 if (dir == SearchDirectionHint.Up) {
3815 if (point.Y < item_loc.Y)
3817 } else if (dir == SearchDirectionHint.Down) {
3818 if (point.Y > item_loc.Y)
3820 } else if (dir == SearchDirectionHint.Left) {
3821 if (point.X < item_loc.X)
3823 } else if (dir == SearchDirectionHint.Right) {
3824 if (point.X > item_loc.X)
3828 int x_dist = point.X - item_loc.X;
3829 int y_dist = point.Y - item_loc.Y;
3831 int dist = x_dist * x_dist + y_dist * y_dist;
3832 if (dist < min_dist) {
3842 public ListViewItem GetItemAt (int x, int y)
3844 Size item_size = ItemSize;
3845 for (int i = 0; i < items.Count; i++) {
3846 Point item_location = GetItemLocation (i);
3847 Rectangle item_rect = new Rectangle (item_location, item_size);
3848 if (item_rect.Contains (x, y))
3855 public Rectangle GetItemRect (int index)
3857 return GetItemRect (index, ItemBoundsPortion.Entire);
3860 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3862 if (index < 0 || index >= items.Count)
3863 throw new IndexOutOfRangeException ("index");
3865 return items [index].GetBounds (portion);
3869 public ListViewHitTestInfo HitTest (Point point)
3871 return HitTest (point.X, point.Y);
3874 public ListViewHitTestInfo HitTest (int x, int y)
3877 throw new ArgumentOutOfRangeException ("x");
3879 throw new ArgumentOutOfRangeException ("y");
3881 ListViewItem item = GetItemAt (x, y);
3883 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3885 ListViewHitTestLocations locations = 0;
3886 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3887 locations |= ListViewHitTestLocations.Label;
3888 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3889 locations |= ListViewHitTestLocations.Image;
3890 else if (item.CheckRectReal.Contains (x, y))
3891 locations |= ListViewHitTestLocations.StateImage;
3893 ListViewItem.ListViewSubItem subitem = null;
3894 if (view == View.Details)
3895 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3896 if (si.Bounds.Contains (x, y)) {
3901 return new ListViewHitTestInfo (item, subitem, locations);
3904 [EditorBrowsable (EditorBrowsableState.Advanced)]
3905 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3907 if (startIndex < 0 || startIndex >= items.Count)
3908 throw new ArgumentOutOfRangeException ("startIndex");
3909 if (endIndex < 0 || endIndex >= items.Count)
3910 throw new ArgumentOutOfRangeException ("endIndex");
3911 if (startIndex > endIndex)
3912 throw new ArgumentException ("startIndex");
3917 for (int i = startIndex; i <= endIndex; i++)
3918 items [i].Invalidate ();
3920 if (!invalidateOnly)
3929 throw new InvalidOperationException ();
3935 // we need this overload to reuse the logic for sorting, while allowing
3936 // redrawing to be done by caller or have it done by this method when
3937 // sorting is really performed
3939 // ListViewItemCollection's Add and AddRange methods call this overload
3940 // with redraw set to false, as they take care of redrawing themselves
3941 // (they even want to redraw the listview if no sort is performed, as
3942 // an item was added), while ListView.Sort () only wants to redraw if
3943 // sorting was actually performed
3944 private void Sort (bool redraw)
3946 if (!IsHandleCreated || item_sorter == null) {
3950 items.Sort (item_sorter);
3955 public override string ToString ()
3957 int count = this.Items.Count;
3960 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3962 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3964 #endregion // Public Instance Methods
3969 internal class HeaderControl : Control {
3972 bool column_resize_active = false;
3973 ColumnHeader resize_column;
3974 ColumnHeader clicked_column;
3975 ColumnHeader drag_column;
3977 int drag_to_index = -1;
3978 ColumnHeader entered_column_header;
3980 public HeaderControl (ListView owner)
3983 this.SetStyle (ControlStyles.DoubleBuffer, true);
3984 MouseDown += new MouseEventHandler (HeaderMouseDown);
3985 MouseMove += new MouseEventHandler (HeaderMouseMove);
3986 MouseUp += new MouseEventHandler (HeaderMouseUp);
3987 MouseLeave += new EventHandler (OnMouseLeave);
3990 internal ColumnHeader EnteredColumnHeader {
3991 get { return entered_column_header; }
3993 if (entered_column_header == value)
3995 if (ThemeEngine.Current.ListViewHasHotHeaderStyle) {
3996 Region region_to_invalidate = new Region ();
3997 region_to_invalidate.MakeEmpty ();
3998 if (entered_column_header != null)
3999 region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
4000 entered_column_header = value;
4001 if (entered_column_header != null)
4002 region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
4003 Invalidate (region_to_invalidate);
4004 region_to_invalidate.Dispose ();
4006 entered_column_header = value;
4010 void OnMouseLeave (object sender, EventArgs e)
4012 EnteredColumnHeader = null;
4015 private ColumnHeader ColumnAtX (int x)
4017 Point pt = new Point (x, 0);
4018 ColumnHeader result = null;
4019 foreach (ColumnHeader col in owner.Columns) {
4020 if (col.Rect.Contains (pt)) {
4028 private int GetReorderedIndex (ColumnHeader col)
4030 if (owner.reordered_column_indices == null)
4033 for (int i = 0; i < owner.Columns.Count; i++)
4034 if (owner.reordered_column_indices [i] == col.Index)
4036 throw new Exception ("Column index missing from reordered array");
4039 private void HeaderMouseDown (object sender, MouseEventArgs me)
4041 if (resize_column != null) {
4042 column_resize_active = true;
4047 clicked_column = ColumnAtX (me.X + owner.h_marker);
4049 if (clicked_column != null) {
4051 if (owner.AllowColumnReorder) {
4053 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
4054 drag_column.Rect = clicked_column.Rect;
4055 drag_to_index = GetReorderedIndex (clicked_column);
4057 clicked_column.Pressed = true;
4058 Invalidate (clicked_column);
4063 void Invalidate (ColumnHeader columnHeader)
4065 Invalidate (GetColumnHeaderInvalidateArea (columnHeader));
4068 Rectangle GetColumnHeaderInvalidateArea (ColumnHeader columnHeader)
4070 Rectangle bounds = columnHeader.Rect;
4071 bounds.X -= owner.h_marker;
4077 column_resize_active = false;
4078 resize_column = null;
4080 Cursor = Cursors.Default;
4083 private void HeaderMouseMove (object sender, MouseEventArgs me)
4085 Point pt = new Point (me.X + owner.h_marker, me.Y);
4087 if (column_resize_active) {
4088 int width = pt.X - resize_column.X;
4092 if (!owner.CanProceedWithResize (resize_column, width)){
4096 resize_column.Width = width;
4100 resize_column = null;
4102 if (clicked_column != null) {
4103 if (owner.AllowColumnReorder) {
4106 r = drag_column.Rect;
4107 r.X = clicked_column.Rect.X + me.X - drag_x;
4108 drag_column.Rect = r;
4110 int x = me.X + owner.h_marker;
4111 ColumnHeader over = ColumnAtX (x);
4113 drag_to_index = owner.Columns.Count;
4114 else if (x < over.X + over.Width / 2)
4115 drag_to_index = GetReorderedIndex (over);
4117 drag_to_index = GetReorderedIndex (over) + 1;
4120 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
4121 bool pressed = clicked_column.Pressed;
4122 clicked_column.Pressed = over == clicked_column;
4123 if (clicked_column.Pressed ^ pressed)
4124 Invalidate (clicked_column);
4129 for (int i = 0; i < owner.Columns.Count; i++) {
4130 Rectangle zone = owner.Columns [i].Rect;
4131 if (zone.Contains (pt))
4132 EnteredColumnHeader = owner.Columns [i];
4133 zone.X = zone.Right - 5;
4135 if (zone.Contains (pt)) {
4136 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
4138 resize_column = owner.Columns [i];
4143 if (resize_column == null)
4144 Cursor = Cursors.Default;
4146 Cursor = Cursors.VSplit;
4149 void HeaderMouseUp (object sender, MouseEventArgs me)
4153 if (column_resize_active) {
4154 int column_idx = resize_column.Index;
4156 owner.RaiseColumnWidthChanged (column_idx);
4160 if (clicked_column != null && clicked_column.Pressed) {
4161 clicked_column.Pressed = false;
4162 Invalidate (clicked_column);
4163 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4166 if (drag_column != null && owner.AllowColumnReorder) {
4168 if (drag_to_index > GetReorderedIndex (clicked_column))
4170 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4171 owner.ReorderColumn (clicked_column, drag_to_index, true);
4176 clicked_column = null;
4179 internal override void OnPaintInternal (PaintEventArgs pe)
4184 Theme theme = ThemeEngine.Current;
4185 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4187 if (drag_column == null)
4191 if (drag_to_index == owner.Columns.Count)
4192 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4194 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4195 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4198 protected override void WndProc (ref Message m)
4200 switch ((Msg)m.Msg) {
4201 case Msg.WM_SETFOCUS:
4205 base.WndProc (ref m);
4211 private class ItemComparer : IComparer {
4212 readonly SortOrder sort_order;
4214 public ItemComparer (SortOrder sortOrder)
4216 sort_order = sortOrder;
4219 public int Compare (object x, object y)
4221 ListViewItem item_x = x as ListViewItem;
4222 ListViewItem item_y = y as ListViewItem;
4223 if (sort_order == SortOrder.Ascending)
4224 return String.Compare (item_x.Text, item_y.Text);
4226 return String.Compare (item_y.Text, item_x.Text);
4231 [ListBindable (false)]
4233 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4235 private readonly ListView owner;
4237 #region Public Constructor
4238 public CheckedIndexCollection (ListView owner)
4242 #endregion // Public Constructor
4244 #region Public Properties
4247 get { return owner.CheckedItems.Count; }
4250 public bool IsReadOnly {
4251 get { return true; }
4254 public int this [int index] {
4256 int [] indices = GetIndices ();
4257 if (index < 0 || index >= indices.Length)
4258 throw new ArgumentOutOfRangeException ("index");
4259 return indices [index];
4263 bool ICollection.IsSynchronized {
4264 get { return false; }
4267 object ICollection.SyncRoot {
4268 get { return this; }
4271 bool IList.IsFixedSize {
4272 get { return true; }
4275 object IList.this [int index] {
4276 get { return this [index]; }
4277 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4279 #endregion // Public Properties
4281 #region Public Methods
4282 public bool Contains (int checkedIndex)
4284 int [] indices = GetIndices ();
4285 for (int i = 0; i < indices.Length; i++) {
4286 if (indices [i] == checkedIndex)
4292 public IEnumerator GetEnumerator ()
4294 int [] indices = GetIndices ();
4295 return indices.GetEnumerator ();
4298 void ICollection.CopyTo (Array dest, int index)
4300 int [] indices = GetIndices ();
4301 Array.Copy (indices, 0, dest, index, indices.Length);
4304 int IList.Add (object value)
4306 throw new NotSupportedException ("Add operation is not supported.");
4311 throw new NotSupportedException ("Clear operation is not supported.");
4314 bool IList.Contains (object checkedIndex)
4316 if (!(checkedIndex is int))
4318 return Contains ((int) checkedIndex);
4321 int IList.IndexOf (object checkedIndex)
4323 if (!(checkedIndex is int))
4325 return IndexOf ((int) checkedIndex);
4328 void IList.Insert (int index, object value)
4330 throw new NotSupportedException ("Insert operation is not supported.");
4333 void IList.Remove (object value)
4335 throw new NotSupportedException ("Remove operation is not supported.");
4338 void IList.RemoveAt (int index)
4340 throw new NotSupportedException ("RemoveAt operation is not supported.");
4343 public int IndexOf (int checkedIndex)
4345 int [] indices = GetIndices ();
4346 for (int i = 0; i < indices.Length; i++) {
4347 if (indices [i] == checkedIndex)
4352 #endregion // Public Methods
4354 private int [] GetIndices ()
4356 ArrayList checked_items = owner.CheckedItems.List;
4357 int [] indices = new int [checked_items.Count];
4358 for (int i = 0; i < checked_items.Count; i++) {
4359 ListViewItem item = (ListViewItem) checked_items [i];
4360 indices [i] = item.Index;
4364 } // CheckedIndexCollection
4367 [ListBindable (false)]
4369 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4371 private readonly ListView owner;
4372 private ArrayList list;
4374 #region Public Constructor
4375 public CheckedListViewItemCollection (ListView owner)
4378 this.owner.Items.Changed += new CollectionChangedHandler (
4379 ItemsCollection_Changed);
4381 #endregion // Public Constructor
4383 #region Public Properties
4387 if (!owner.CheckBoxes)
4393 public bool IsReadOnly {
4394 get { return true; }
4397 public ListViewItem this [int index] {
4400 if (owner.VirtualMode)
4401 throw new InvalidOperationException ();
4403 ArrayList checked_items = List;
4404 if (index < 0 || index >= checked_items.Count)
4405 throw new ArgumentOutOfRangeException ("index");
4406 return (ListViewItem) checked_items [index];
4411 public virtual ListViewItem this [string key] {
4413 int idx = IndexOfKey (key);
4414 return idx == -1 ? null : (ListViewItem) List [idx];
4419 bool ICollection.IsSynchronized {
4420 get { return false; }
4423 object ICollection.SyncRoot {
4424 get { return this; }
4427 bool IList.IsFixedSize {
4428 get { return true; }
4431 object IList.this [int index] {
4432 get { return this [index]; }
4433 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4435 #endregion // Public Properties
4437 #region Public Methods
4438 public bool Contains (ListViewItem item)
4440 if (!owner.CheckBoxes)
4442 return List.Contains (item);
4446 public virtual bool ContainsKey (string key)
4448 return IndexOfKey (key) != -1;
4452 public void CopyTo (Array dest, int index)
4455 if (owner.VirtualMode)
4456 throw new InvalidOperationException ();
4458 if (!owner.CheckBoxes)
4460 List.CopyTo (dest, index);
4463 public IEnumerator GetEnumerator ()
4466 if (owner.VirtualMode)
4467 throw new InvalidOperationException ();
4469 if (!owner.CheckBoxes)
4470 return (new ListViewItem [0]).GetEnumerator ();
4471 return List.GetEnumerator ();
4474 int IList.Add (object value)
4476 throw new NotSupportedException ("Add operation is not supported.");
4481 throw new NotSupportedException ("Clear operation is not supported.");
4484 bool IList.Contains (object item)
4486 if (!(item is ListViewItem))
4488 return Contains ((ListViewItem) item);
4491 int IList.IndexOf (object item)
4493 if (!(item is ListViewItem))
4495 return IndexOf ((ListViewItem) item);
4498 void IList.Insert (int index, object value)
4500 throw new NotSupportedException ("Insert operation is not supported.");
4503 void IList.Remove (object value)
4505 throw new NotSupportedException ("Remove operation is not supported.");
4508 void IList.RemoveAt (int index)
4510 throw new NotSupportedException ("RemoveAt operation is not supported.");
4513 public int IndexOf (ListViewItem item)
4516 if (owner.VirtualMode)
4517 throw new InvalidOperationException ();
4519 if (!owner.CheckBoxes)
4521 return List.IndexOf (item);
4525 public virtual int IndexOfKey (string key)
4528 if (owner.VirtualMode)
4529 throw new InvalidOperationException ();
4531 if (key == null || key.Length == 0)
4534 ArrayList checked_items = List;
4535 for (int i = 0; i < checked_items.Count; i++) {
4536 ListViewItem item = (ListViewItem) checked_items [i];
4537 if (String.Compare (key, item.Name, true) == 0)
4544 #endregion // Public Methods
4546 internal ArrayList List {
4549 list = new ArrayList ();
4550 foreach (ListViewItem item in owner.Items) {
4559 internal void Reset ()
4561 // force re-population of list
4565 private void ItemsCollection_Changed ()
4569 } // CheckedListViewItemCollection
4572 [ListBindable (false)]
4574 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4576 internal ArrayList list;
4577 private ListView owner;
4579 #region Public Constructor
4580 public ColumnHeaderCollection (ListView owner)
4582 list = new ArrayList ();
4585 #endregion // Public Constructor
4587 #region Public Properties
4590 get { return list.Count; }
4593 public bool IsReadOnly {
4594 get { return false; }
4597 public virtual ColumnHeader this [int index] {
4599 if (index < 0 || index >= list.Count)
4600 throw new ArgumentOutOfRangeException ("index");
4601 return (ColumnHeader) list [index];
4606 public virtual ColumnHeader this [string key] {
4608 int idx = IndexOfKey (key);
4612 return (ColumnHeader) list [idx];
4617 bool ICollection.IsSynchronized {
4618 get { return true; }
4621 object ICollection.SyncRoot {
4622 get { return this; }
4625 bool IList.IsFixedSize {
4626 get { return list.IsFixedSize; }
4629 object IList.this [int index] {
4630 get { return this [index]; }
4631 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4633 #endregion // Public Properties
4635 #region Public Methods
4636 public virtual int Add (ColumnHeader value)
4638 int idx = list.Add (value);
4639 owner.AddColumn (value, idx, true);
4644 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4648 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4651 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4652 this.Add (colHeader);
4657 public virtual ColumnHeader Add (string text)
4659 return Add (String.Empty, text);
4662 public virtual ColumnHeader Add (string text, int width)
4664 return Add (String.Empty, text, width);
4667 public virtual ColumnHeader Add (string key, string text)
4669 ColumnHeader colHeader = new ColumnHeader ();
4670 colHeader.Name = key;
4671 colHeader.Text = text;
4676 public virtual ColumnHeader Add (string key, string text, int width)
4678 return Add (key, text, width, HorizontalAlignment.Left, -1);
4681 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4683 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4684 colHeader.ImageIndex = imageIndex;
4689 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4691 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4692 colHeader.ImageKey = imageKey;
4698 public virtual void AddRange (ColumnHeader [] values)
4700 foreach (ColumnHeader colHeader in values) {
4701 int idx = list.Add (colHeader);
4702 owner.AddColumn (colHeader, idx, false);
4705 owner.Redraw (true);
4708 public virtual void Clear ()
4710 foreach (ColumnHeader col in list)
4711 col.SetListView (null);
4713 owner.ReorderColumns (new int [0], true);
4716 public bool Contains (ColumnHeader value)
4718 return list.Contains (value);
4722 public virtual bool ContainsKey (string key)
4724 return IndexOfKey (key) != -1;
4728 public IEnumerator GetEnumerator ()
4730 return list.GetEnumerator ();
4733 void ICollection.CopyTo (Array dest, int index)
4735 list.CopyTo (dest, index);
4738 int IList.Add (object value)
4740 if (! (value is ColumnHeader)) {
4741 throw new ArgumentException ("Not of type ColumnHeader", "value");
4744 return this.Add ((ColumnHeader) value);
4747 bool IList.Contains (object value)
4749 if (! (value is ColumnHeader)) {
4750 throw new ArgumentException ("Not of type ColumnHeader", "value");
4753 return this.Contains ((ColumnHeader) value);
4756 int IList.IndexOf (object value)
4758 if (! (value is ColumnHeader)) {
4759 throw new ArgumentException ("Not of type ColumnHeader", "value");
4762 return this.IndexOf ((ColumnHeader) value);
4765 void IList.Insert (int index, object value)
4767 if (! (value is ColumnHeader)) {
4768 throw new ArgumentException ("Not of type ColumnHeader", "value");
4771 this.Insert (index, (ColumnHeader) value);
4774 void IList.Remove (object value)
4776 if (! (value is ColumnHeader)) {
4777 throw new ArgumentException ("Not of type ColumnHeader", "value");
4780 this.Remove ((ColumnHeader) value);
4783 public int IndexOf (ColumnHeader value)
4785 return list.IndexOf (value);
4789 public virtual int IndexOfKey (string key)
4791 if (key == null || key.Length == 0)
4794 for (int i = 0; i < list.Count; i++) {
4795 ColumnHeader col = (ColumnHeader) list [i];
4796 if (String.Compare (key, col.Name, true) == 0)
4804 public void Insert (int index, ColumnHeader value)
4806 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4807 // but it's really only greater.
4808 if (index < 0 || index > list.Count)
4809 throw new ArgumentOutOfRangeException ("index");
4811 list.Insert (index, value);
4812 owner.AddColumn (value, index, true);
4816 public void Insert (int index, string text)
4818 Insert (index, String.Empty, text);
4821 public void Insert (int index, string text, int width)
4823 Insert (index, String.Empty, text, width);
4826 public void Insert (int index, string key, string text)
4828 ColumnHeader colHeader = new ColumnHeader ();
4829 colHeader.Name = key;
4830 colHeader.Text = text;
4831 Insert (index, colHeader);
4834 public void Insert (int index, string key, string text, int width)
4836 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4837 Insert (index, colHeader);
4840 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4842 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4843 colHeader.ImageIndex = imageIndex;
4844 Insert (index, colHeader);
4847 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4849 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4850 colHeader.ImageKey = imageKey;
4851 Insert (index, colHeader);
4856 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4860 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4863 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4864 this.Insert (index, colHeader);
4867 public virtual void Remove (ColumnHeader column)
4869 if (!Contains (column))
4872 list.Remove (column);
4873 column.SetListView (null);
4875 int rem_display_index = column.InternalDisplayIndex;
4876 int [] display_indices = new int [list.Count];
4877 for (int i = 0; i < display_indices.Length; i++) {
4878 ColumnHeader col = (ColumnHeader) list [i];
4879 int display_index = col.InternalDisplayIndex;
4880 if (display_index < rem_display_index) {
4881 display_indices [i] = display_index;
4883 display_indices [i] = (display_index - 1);
4887 column.InternalDisplayIndex = -1;
4888 owner.ReorderColumns (display_indices, true);
4892 public virtual void RemoveByKey (string key)
4894 int idx = IndexOfKey (key);
4900 public virtual void RemoveAt (int index)
4902 if (index < 0 || index >= list.Count)
4903 throw new ArgumentOutOfRangeException ("index");
4905 ColumnHeader col = (ColumnHeader) list [index];
4908 #endregion // Public Methods
4911 } // ColumnHeaderCollection
4914 [ListBindable (false)]
4916 public class ListViewItemCollection : IList, ICollection, IEnumerable
4918 private readonly ArrayList list;
4919 private ListView owner;
4921 private ListViewGroup group;
4924 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4925 // In the later case ListViewItem.ListView never gets modified
4926 private bool is_main_collection = true;
4928 #region Public Constructor
4929 public ListViewItemCollection (ListView owner)
4931 list = new ArrayList (0);
4934 #endregion // Public Constructor
4937 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4940 is_main_collection = false;
4944 #region Public Properties
4949 if (owner != null && owner.VirtualMode)
4950 return owner.VirtualListSize;
4957 public bool IsReadOnly {
4958 get { return false; }
4962 public virtual ListViewItem this [int index] {
4964 public virtual ListViewItem this [int displayIndex] {
4968 int index = displayIndex;
4971 if (index < 0 || index >= Count)
4972 throw new ArgumentOutOfRangeException ("index");
4975 if (owner != null && owner.VirtualMode)
4976 return RetrieveVirtualItemFromOwner (index);
4978 return (ListViewItem) list [index];
4983 int index = displayIndex;
4986 if (index < 0 || index >= Count)
4987 throw new ArgumentOutOfRangeException ("index");
4990 if (owner != null && owner.VirtualMode)
4991 throw new InvalidOperationException ();
4994 if (list.Contains (value))
4995 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4997 if (value.ListView != null && value.ListView != owner)
4998 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");
5000 if (is_main_collection)
5001 value.Owner = owner;
5004 if (value.Group != null)
5005 value.Group.Items.Remove (value);
5007 value.SetGroup (group);
5011 list [index] = value;
5012 CollectionChanged (true);
5017 public virtual ListViewItem this [string key] {
5019 int idx = IndexOfKey (key);
5028 bool ICollection.IsSynchronized {
5029 get { return true; }
5032 object ICollection.SyncRoot {
5033 get { return this; }
5036 bool IList.IsFixedSize {
5037 get { return list.IsFixedSize; }
5040 object IList.this [int index] {
5041 get { return this [index]; }
5043 if (value is ListViewItem)
5044 this [index] = (ListViewItem) value;
5046 this [index] = new ListViewItem (value.ToString ());
5050 #endregion // Public Properties
5052 #region Public Methods
5053 public virtual ListViewItem Add (ListViewItem value)
5056 if (owner != null && owner.VirtualMode)
5057 throw new InvalidOperationException ();
5062 // Item is ignored until it has been added to the ListView
5063 if (is_main_collection || value.ListView != null)
5064 CollectionChanged (true);
5069 public virtual ListViewItem Add (string text)
5071 ListViewItem item = new ListViewItem (text);
5072 return this.Add (item);
5075 public virtual ListViewItem Add (string text, int imageIndex)
5077 ListViewItem item = new ListViewItem (text, imageIndex);
5078 return this.Add (item);
5082 public virtual ListViewItem Add (string text, string imageKey)
5084 ListViewItem item = new ListViewItem (text, imageKey);
5085 return this.Add (item);
5088 public virtual ListViewItem Add (string key, string text, int imageIndex)
5090 ListViewItem item = new ListViewItem (text, imageIndex);
5092 return this.Add (item);
5095 public virtual ListViewItem Add (string key, string text, string imageKey)
5097 ListViewItem item = new ListViewItem (text, imageKey);
5099 return this.Add (item);
5104 public void AddRange (ListViewItem [] items)
5107 public void AddRange (ListViewItem [] values)
5109 ListViewItem [] items = values;
5112 throw new ArgumentNullException ("Argument cannot be null!", "items");
5114 if (owner != null && owner.VirtualMode)
5115 throw new InvalidOperationException ();
5118 owner.BeginUpdate ();
5120 foreach (ListViewItem item in items)
5125 CollectionChanged (true);
5129 public void AddRange (ListViewItemCollection items)
5132 throw new ArgumentNullException ("Argument cannot be null!", "items");
5134 ListViewItem[] itemArray = new ListViewItem[items.Count];
5135 items.CopyTo (itemArray,0);
5136 this.AddRange (itemArray);
5140 public virtual void Clear ()
5143 if (owner != null && owner.VirtualMode)
5144 throw new InvalidOperationException ();
5146 if (is_main_collection && owner != null) {
5147 owner.SetFocusedItem (-1);
5148 owner.h_scroll.Value = owner.v_scroll.Value = 0;
5150 foreach (ListViewItem item in list) {
5151 owner.item_control.CancelEdit (item);
5158 foreach (ListViewItem item in list)
5159 item.SetGroup (null);
5163 CollectionChanged (false);
5166 public bool Contains (ListViewItem item)
5168 return IndexOf (item) != -1;
5172 public virtual bool ContainsKey (string key)
5174 return IndexOfKey (key) != -1;
5178 public void CopyTo (Array dest, int index)
5180 list.CopyTo (dest, index);
5184 public ListViewItem [] Find (string key, bool searchAllSubItems)
5187 return new ListViewItem [0];
5189 List<ListViewItem> temp_list = new List<ListViewItem> ();
5191 for (int i = 0; i < list.Count; i++) {
5192 ListViewItem lvi = (ListViewItem) list [i];
5193 if (String.Compare (key, lvi.Name, true) == 0)
5194 temp_list.Add (lvi);
5197 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5198 temp_list.CopyTo (retval);
5204 public IEnumerator GetEnumerator ()
5207 if (owner != null && owner.VirtualMode)
5208 throw new InvalidOperationException ();
5211 return list.GetEnumerator ();
5214 int IList.Add (object item)
5220 if (owner != null && owner.VirtualMode)
5221 throw new InvalidOperationException ();
5224 if (item is ListViewItem) {
5225 li = (ListViewItem) item;
5226 if (list.Contains (li))
5227 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5229 if (li.ListView != null && li.ListView != owner)
5230 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");
5233 li = new ListViewItem (item.ToString ());
5238 result = list.Add (li);
5239 CollectionChanged (true);
5244 bool IList.Contains (object item)
5246 return Contains ((ListViewItem) item);
5249 int IList.IndexOf (object item)
5251 return IndexOf ((ListViewItem) item);
5254 void IList.Insert (int index, object item)
5256 if (item is ListViewItem)
5257 this.Insert (index, (ListViewItem) item);
5259 this.Insert (index, item.ToString ());
5262 void IList.Remove (object item)
5264 Remove ((ListViewItem) item);
5267 public int IndexOf (ListViewItem item)
5270 if (owner != null && owner.VirtualMode) {
5271 for (int i = 0; i < Count; i++)
5272 if (RetrieveVirtualItemFromOwner (i) == item)
5279 return list.IndexOf (item);
5283 public virtual int IndexOfKey (string key)
5285 if (key == null || key.Length == 0)
5288 for (int i = 0; i < Count; i++) {
5289 ListViewItem lvi = this [i];
5290 if (String.Compare (key, lvi.Name, true) == 0)
5298 public ListViewItem Insert (int index, ListViewItem item)
5300 if (index < 0 || index > list.Count)
5301 throw new ArgumentOutOfRangeException ("index");
5304 if (owner != null && owner.VirtualMode)
5305 throw new InvalidOperationException ();
5308 if (list.Contains (item))
5309 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5311 if (item.ListView != null && item.ListView != owner)
5312 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");
5314 if (is_main_collection)
5318 if (item.Group != null)
5319 item.Group.Items.Remove (item);
5321 item.SetGroup (group);
5325 list.Insert (index, item);
5327 if (is_main_collection || item.ListView != null)
5328 CollectionChanged (true);
5333 public ListViewItem Insert (int index, string text)
5335 return this.Insert (index, new ListViewItem (text));
5338 public ListViewItem Insert (int index, string text, int imageIndex)
5340 return this.Insert (index, new ListViewItem (text, imageIndex));
5344 public ListViewItem Insert (int index, string text, string imageKey)
5346 ListViewItem lvi = new ListViewItem (text, imageKey);
5347 return Insert (index, lvi);
5350 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5352 ListViewItem lvi = new ListViewItem (text, imageIndex);
5354 return Insert (index, lvi);
5357 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5359 ListViewItem lvi = new ListViewItem (text, imageKey);
5361 return Insert (index, lvi);
5365 public virtual void Remove (ListViewItem item)
5368 if (owner != null && owner.VirtualMode)
5369 throw new InvalidOperationException ();
5372 int idx = list.IndexOf (item);
5377 public virtual void RemoveAt (int index)
5379 if (index < 0 || index >= Count)
5380 throw new ArgumentOutOfRangeException ("index");
5383 if (owner != null && owner.VirtualMode)
5384 throw new InvalidOperationException ();
5387 ListViewItem item = (ListViewItem) list [index];
5389 bool selection_changed = false;
5390 if (is_main_collection && owner != null) {
5392 int display_index = item.DisplayIndex;
5393 if (item.Focused && display_index + 1 == Count) // Last item
5394 owner.SetFocusedItem (display_index == 0 ? -1 : display_index - 1);
5396 selection_changed = owner.SelectedIndices.Contains (index);
5397 owner.item_control.CancelEdit (item);
5400 list.RemoveAt (index);
5402 if (is_main_collection)
5406 item.SetGroup (null);
5409 CollectionChanged (false);
5410 if (selection_changed && owner != null)
5411 owner.OnSelectedIndexChanged (EventArgs.Empty);
5415 public virtual void RemoveByKey (string key)
5417 int idx = IndexOfKey (key);
5423 #endregion // Public Methods
5425 internal ListView Owner {
5435 internal ListViewGroup Group {
5445 void AddItem (ListViewItem value)
5447 if (list.Contains (value))
5448 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5450 if (value.ListView != null && value.ListView != owner)
5451 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");
5452 if (is_main_collection)
5453 value.Owner = owner;
5456 if (value.Group != null)
5457 value.Group.Items.Remove (value);
5459 value.SetGroup (group);
5466 void CollectionChanged (bool sort)
5468 if (owner != null) {
5473 owner.Redraw (true);
5478 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5480 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5482 owner.OnRetrieveVirtualItem (args);
5483 ListViewItem retval = args.Item;
5484 retval.Owner = owner;
5485 retval.DisplayIndex = displayIndex;
5491 internal event CollectionChangedHandler Changed;
5493 internal void Sort (IComparer comparer)
5495 list.Sort (comparer);
5499 internal void OnChange ()
5501 if (Changed != null)
5504 } // ListViewItemCollection
5507 // In normal mode, the selection information resides in the Items,
5508 // making SelectedIndexCollection.List read-only
5510 // In virtual mode, SelectedIndexCollection directly saves the selection
5511 // information, instead of getting it from Items, making List read-and-write
5513 [ListBindable (false)]
5515 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5517 private readonly ListView owner;
5518 private ArrayList list;
5520 #region Public Constructor
5521 public SelectedIndexCollection (ListView owner)
5524 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5526 #endregion // Public Constructor
5528 #region Public Properties
5532 if (!owner.IsHandleCreated)
5539 public bool IsReadOnly {
5549 public int this [int index] {
5551 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5552 throw new ArgumentOutOfRangeException ("index");
5554 return (int) List [index];
5558 bool ICollection.IsSynchronized {
5559 get { return false; }
5562 object ICollection.SyncRoot {
5563 get { return this; }
5566 bool IList.IsFixedSize {
5576 object IList.this [int index] {
5577 get { return this [index]; }
5578 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5580 #endregion // Public Properties
5582 #region Public Methods
5584 public int Add (int itemIndex)
5586 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5587 throw new ArgumentOutOfRangeException ("index");
5589 if (owner.virtual_mode && !owner.IsHandleCreated)
5592 owner.Items [itemIndex].Selected = true;
5594 if (!owner.IsHandleCreated)
5608 if (!owner.IsHandleCreated)
5611 int [] indexes = (int []) List.ToArray (typeof (int));
5612 foreach (int index in indexes)
5613 owner.Items [index].Selected = false;
5616 public bool Contains (int selectedIndex)
5618 return IndexOf (selectedIndex) != -1;
5621 public void CopyTo (Array dest, int index)
5623 List.CopyTo (dest, index);
5626 public IEnumerator GetEnumerator ()
5628 return List.GetEnumerator ();
5631 int IList.Add (object value)
5633 throw new NotSupportedException ("Add operation is not supported.");
5641 bool IList.Contains (object selectedIndex)
5643 if (!(selectedIndex is int))
5645 return Contains ((int) selectedIndex);
5648 int IList.IndexOf (object selectedIndex)
5650 if (!(selectedIndex is int))
5652 return IndexOf ((int) selectedIndex);
5655 void IList.Insert (int index, object value)
5657 throw new NotSupportedException ("Insert operation is not supported.");
5660 void IList.Remove (object value)
5662 throw new NotSupportedException ("Remove operation is not supported.");
5665 void IList.RemoveAt (int index)
5667 throw new NotSupportedException ("RemoveAt operation is not supported.");
5670 public int IndexOf (int selectedIndex)
5672 if (!owner.IsHandleCreated)
5675 return List.IndexOf (selectedIndex);
5679 public void Remove (int itemIndex)
5681 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5682 throw new ArgumentOutOfRangeException ("itemIndex");
5684 owner.Items [itemIndex].Selected = false;
5687 #endregion // Public Methods
5689 internal ArrayList List {
5692 list = new ArrayList ();
5694 if (!owner.VirtualMode)
5696 for (int i = 0; i < owner.Items.Count; i++) {
5697 if (owner.Items [i].Selected)
5705 internal void Reset ()
5707 // force re-population of list
5711 private void ItemsCollection_Changed ()
5717 internal void RemoveIndex (int index)
5719 int idx = List.BinarySearch (index);
5721 List.RemoveAt (idx);
5724 // actually store index in the collection
5725 // also, keep the collection sorted, as .Net does
5726 internal void InsertIndex (int index)
5729 int iMax = List.Count - 1;
5730 while (iMin <= iMax) {
5731 int iMid = (iMin + iMax) / 2;
5732 int current_index = (int) List [iMid];
5734 if (current_index == index)
5735 return; // Already added
5736 if (current_index > index)
5742 List.Insert (iMin, index);
5746 } // SelectedIndexCollection
5749 [ListBindable (false)]
5751 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5753 private readonly ListView owner;
5755 #region Public Constructor
5756 public SelectedListViewItemCollection (ListView owner)
5760 #endregion // Public Constructor
5762 #region Public Properties
5766 return owner.SelectedIndices.Count;
5770 public bool IsReadOnly {
5771 get { return true; }
5774 public ListViewItem this [int index] {
5776 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5777 throw new ArgumentOutOfRangeException ("index");
5779 int item_index = owner.SelectedIndices [index];
5780 return owner.Items [item_index];
5785 public virtual ListViewItem this [string key] {
5787 int idx = IndexOfKey (key);
5796 bool ICollection.IsSynchronized {
5797 get { return false; }
5800 object ICollection.SyncRoot {
5801 get { return this; }
5804 bool IList.IsFixedSize {
5805 get { return true; }
5808 object IList.this [int index] {
5809 get { return this [index]; }
5810 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5812 #endregion // Public Properties
5814 #region Public Methods
5815 public void Clear ()
5817 owner.SelectedIndices.Clear ();
5820 public bool Contains (ListViewItem item)
5822 return IndexOf (item) != -1;
5826 public virtual bool ContainsKey (string key)
5828 return IndexOfKey (key) != -1;
5832 public void CopyTo (Array dest, int index)
5834 if (!owner.IsHandleCreated)
5836 if (index > Count) // Throws ArgumentException instead of IOOR exception
5837 throw new ArgumentException ("index");
5839 for (int i = 0; i < Count; i++)
5840 dest.SetValue (this [i], index++);
5843 public IEnumerator GetEnumerator ()
5845 if (!owner.IsHandleCreated)
5846 return (new ListViewItem [0]).GetEnumerator ();
5848 ListViewItem [] items = new ListViewItem [Count];
5849 for (int i = 0; i < Count; i++)
5850 items [i] = this [i];
5852 return items.GetEnumerator ();
5855 int IList.Add (object value)
5857 throw new NotSupportedException ("Add operation is not supported.");
5860 bool IList.Contains (object item)
5862 if (!(item is ListViewItem))
5864 return Contains ((ListViewItem) item);
5867 int IList.IndexOf (object item)
5869 if (!(item is ListViewItem))
5871 return IndexOf ((ListViewItem) item);
5874 void IList.Insert (int index, object value)
5876 throw new NotSupportedException ("Insert operation is not supported.");
5879 void IList.Remove (object value)
5881 throw new NotSupportedException ("Remove operation is not supported.");
5884 void IList.RemoveAt (int index)
5886 throw new NotSupportedException ("RemoveAt operation is not supported.");
5889 public int IndexOf (ListViewItem item)
5891 if (!owner.IsHandleCreated)
5894 for (int i = 0; i < Count; i++)
5895 if (this [i] == item)
5902 public virtual int IndexOfKey (string key)
5904 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5907 for (int i = 0; i < Count; i++) {
5908 ListViewItem item = this [i];
5909 if (String.Compare (item.Name, key, true) == 0)
5916 #endregion // Public Methods
5918 } // SelectedListViewItemCollection
5920 internal delegate void CollectionChangedHandler ();
5922 struct ItemMatrixLocation
5927 public ItemMatrixLocation (int row, int col)
5954 #endregion // Subclasses
5956 protected override void OnResize (EventArgs e)
5961 protected override void OnMouseLeave (EventArgs e)
5963 base.OnMouseLeave (e);
5967 // ColumnReorder event
5969 static object ColumnReorderedEvent = new object ();
5970 public event ColumnReorderedEventHandler ColumnReordered {
5971 add { Events.AddHandler (ColumnReorderedEvent, value); }
5972 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5975 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5977 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5984 // ColumnWidthChanged
5986 static object ColumnWidthChangedEvent = new object ();
5987 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5988 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5989 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5992 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5994 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5999 void RaiseColumnWidthChanged (int resize_column)
6001 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
6003 OnColumnWidthChanged (n);
6007 // ColumnWidthChanging
6009 static object ColumnWidthChangingEvent = new object ();
6010 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
6011 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
6012 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
6015 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
6017 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
6023 // 2.0 profile based implementation
6025 bool CanProceedWithResize (ColumnHeader col, int width)
6027 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
6031 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
6032 cwceh (this, changing);
6033 return !changing.Cancel;
6037 // 1.0 profile based implementation
6039 bool CanProceedWithResize (ColumnHeader col, int width)
6044 void RaiseColumnWidthChanged (int resize_column)