1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com)
23 // Ravindra Kumar (rkumar@novell.com)
24 // Jordi Mas i Hernandez, jordi@ximian.com
25 // Mike Kestner (mkestner@novell.com)
26 // Daniel Nauck (dna(at)mono-project(dot)de)
27 // Carlos Alberto Cortez <calberto.cortez@gmail.com>
34 using System.Collections;
35 using System.ComponentModel;
36 using System.ComponentModel.Design;
38 using System.Runtime.InteropServices;
39 using System.Globalization;
41 using System.Collections.Generic;
44 namespace System.Windows.Forms
46 [DefaultEvent ("SelectedIndexChanged")]
47 [DefaultProperty ("Items")]
48 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
50 [ClassInterface (ClassInterfaceType.AutoDispatch)]
52 [Docking (DockingBehavior.Ask)]
54 public class ListView : Control
56 private ItemActivation activation = ItemActivation.Standard;
57 private ListViewAlignment alignment = ListViewAlignment.Top;
58 private bool allow_column_reorder;
59 private bool auto_arrange = true;
60 private bool check_boxes;
61 private readonly CheckedIndexCollection checked_indices;
62 private readonly CheckedListViewItemCollection checked_items;
63 private readonly ColumnHeaderCollection columns;
64 internal int focused_item_index = -1;
65 private bool full_row_select;
66 private bool grid_lines;
67 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
68 private bool hide_selection = true;
69 private bool hover_selection;
70 private IComparer item_sorter;
71 private readonly ListViewItemCollection items;
73 private readonly ListViewGroupCollection groups;
74 private bool owner_draw;
75 private bool show_groups = true;
77 private bool label_edit;
78 private bool label_wrap = true;
79 private bool multiselect = true;
80 private bool scrollable = true;
81 private bool hover_pending;
82 private readonly SelectedIndexCollection selected_indices;
83 private readonly SelectedListViewItemCollection selected_items;
84 private SortOrder sort_order = SortOrder.None;
85 private ImageList state_image_list;
86 internal bool updating;
87 private View view = View.LargeIcon;
88 private int layout_wd; // We might draw more than our client area
89 private int layout_ht; // therefore we need to have these two.
90 HeaderControl header_control;
91 internal ItemControl item_control;
92 internal ScrollBar h_scroll; // used for scrolling horizontally
93 internal ScrollBar v_scroll; // used for scrolling vertically
94 internal int h_marker; // Position markers for scrolling
95 internal int v_marker;
96 private int keysearch_tickcnt;
97 private string keysearch_text;
98 static private readonly int keysearch_keydelay = 1000;
99 private int[] reordered_column_indices;
100 private int[] reordered_items_indices;
101 private Point [] items_location;
102 private ItemMatrixLocation [] items_matrix_location;
103 private Size item_size; // used for caching item size
104 private int custom_column_width; // used when using Columns with SmallIcon/List views
105 private int hot_item_index = -1;
107 private bool hot_tracking;
108 private ListViewInsertionMark insertion_mark;
109 private bool show_item_tooltips;
110 private ToolTip item_tooltip;
111 private Size tile_size;
112 private bool virtual_mode;
113 private int virtual_list_size;
114 private bool right_to_left_layout;
117 // internal variables
118 internal ImageList large_image_list;
119 internal ImageList small_image_list;
120 internal Size text_size = Size.Empty;
123 static object AfterLabelEditEvent = new object ();
124 static object BeforeLabelEditEvent = new object ();
125 static object ColumnClickEvent = new object ();
126 static object ItemActivateEvent = new object ();
127 static object ItemCheckEvent = new object ();
128 static object ItemDragEvent = new object ();
129 static object SelectedIndexChangedEvent = new object ();
131 static object DrawColumnHeaderEvent = new object();
132 static object DrawItemEvent = new object();
133 static object DrawSubItemEvent = new object();
134 static object ItemCheckedEvent = new object ();
135 static object ItemMouseHoverEvent = new object ();
136 static object ItemSelectionChangedEvent = new object ();
137 static object CacheVirtualItemsEvent = new object ();
138 static object RetrieveVirtualItemEvent = new object ();
139 static object RightToLeftLayoutChangedEvent = new object ();
140 static object SearchForVirtualItemEvent = new object ();
141 static object VirtualItemsSelectionRangeChangedEvent = new object ();
144 public event LabelEditEventHandler AfterLabelEdit {
145 add { Events.AddHandler (AfterLabelEditEvent, value); }
146 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
151 [EditorBrowsable (EditorBrowsableState.Never)]
152 public new event EventHandler BackgroundImageChanged {
153 add { base.BackgroundImageChanged += value; }
154 remove { base.BackgroundImageChanged -= value; }
160 [EditorBrowsable (EditorBrowsableState.Never)]
161 public new event EventHandler BackgroundImageLayoutChanged {
162 add { base.BackgroundImageLayoutChanged += value; }
163 remove { base.BackgroundImageLayoutChanged -= value; }
167 public event LabelEditEventHandler BeforeLabelEdit {
168 add { Events.AddHandler (BeforeLabelEditEvent, value); }
169 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
172 public event ColumnClickEventHandler ColumnClick {
173 add { Events.AddHandler (ColumnClickEvent, value); }
174 remove { Events.RemoveHandler (ColumnClickEvent, value); }
178 public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
179 add { Events.AddHandler(DrawColumnHeaderEvent, value); }
180 remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
183 public event DrawListViewItemEventHandler DrawItem {
184 add { Events.AddHandler(DrawItemEvent, value); }
185 remove { Events.RemoveHandler(DrawItemEvent, value); }
188 public event DrawListViewSubItemEventHandler DrawSubItem {
189 add { Events.AddHandler(DrawSubItemEvent, value); }
190 remove { Events.RemoveHandler(DrawSubItemEvent, value); }
194 public event EventHandler ItemActivate {
195 add { Events.AddHandler (ItemActivateEvent, value); }
196 remove { Events.RemoveHandler (ItemActivateEvent, value); }
199 public event ItemCheckEventHandler ItemCheck {
200 add { Events.AddHandler (ItemCheckEvent, value); }
201 remove { Events.RemoveHandler (ItemCheckEvent, value); }
205 public event ItemCheckedEventHandler ItemChecked {
206 add { Events.AddHandler (ItemCheckedEvent, value); }
207 remove { Events.RemoveHandler (ItemCheckedEvent, value); }
211 public event ItemDragEventHandler ItemDrag {
212 add { Events.AddHandler (ItemDragEvent, value); }
213 remove { Events.RemoveHandler (ItemDragEvent, value); }
217 public event ListViewItemMouseHoverEventHandler ItemMouseHover {
218 add { Events.AddHandler (ItemMouseHoverEvent, value); }
219 remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
222 public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
223 add { Events.AddHandler (ItemSelectionChangedEvent, value); }
224 remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
228 [EditorBrowsable (EditorBrowsableState.Never)]
229 public new event EventHandler PaddingChanged {
230 add { base.PaddingChanged += value; }
231 remove { base.PaddingChanged -= value; }
236 [EditorBrowsable (EditorBrowsableState.Never)]
237 public new event PaintEventHandler Paint {
238 add { base.Paint += value; }
239 remove { base.Paint -= value; }
242 public event EventHandler SelectedIndexChanged {
243 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
244 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
248 [EditorBrowsable (EditorBrowsableState.Never)]
249 public new event EventHandler TextChanged {
250 add { base.TextChanged += value; }
251 remove { base.TextChanged -= value; }
255 public event CacheVirtualItemsEventHandler CacheVirtualItems {
256 add { Events.AddHandler (CacheVirtualItemsEvent, value); }
257 remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
260 public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
261 add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
262 remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
265 public event EventHandler RightToLeftLayoutChanged {
266 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
267 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
270 public event SearchForVirtualItemEventHandler SearchForVirtualItem {
271 add { Events.AddHandler (SearchForVirtualItemEvent, value); }
272 remove { Events.AddHandler (SearchForVirtualItemEvent, value); }
275 public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged {
276 add { Events.AddHandler (VirtualItemsSelectionRangeChangedEvent, value); }
277 remove { Events.RemoveHandler (VirtualItemsSelectionRangeChangedEvent, value); }
283 #region Public Constructors
286 background_color = ThemeEngine.Current.ColorWindow;
288 groups = new ListViewGroupCollection (this);
290 items = new ListViewItemCollection (this);
291 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]];
1601 item_control.Invalidate (item.Bounds);
1602 item.DisplayIndex = i;
1603 item_control.Invalidate (item.Bounds);
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]];
1610 item_control.Invalidate (item.Bounds);
1611 item.DisplayIndex = i;
1612 item_control.Invalidate (item.Bounds);
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;
1640 item_control.Invalidate (item.Bounds);
1641 item.DisplayIndex = new_display_index;
1642 item_control.Invalidate (item.Bounds);
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;
1849 item.SetPosition (new Point (x, y));
1855 item_control.Size = new Size (layout_wd, layout_ht);
1859 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1862 bool details = view == View.Details;
1864 for (int i = 0; i < groups.InternalCount; i++) {
1865 ListViewGroup group = groups.GetInternalGroup (i);
1866 if (group.ItemCount == 0)
1869 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1872 layout_ht = y; // Update height taking into account Groups' headers heights
1875 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1877 Rectangle client_area = ClientRectangle;
1878 int header_height = text_size.Height + 10;
1880 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1881 group.items_area_location = new Point (0, y_origin + header_height);
1883 int items_area_height = ((item_height + y_spacing) * rows);
1884 return header_height + items_area_height + 10; // Add a small bottom margin
1887 void CalculateDetailsGroupItemsCount ()
1891 for (int i = 0; i < groups.InternalCount; i++) {
1892 ListViewGroup group = groups.GetInternalGroup (i);
1893 int items_in_group = group.GetActualItemCount ();
1895 if (items_in_group == 0)
1898 group.starting_item = items;
1899 group.current_item = 0; // Reset layout.
1900 items += items_in_group;
1905 void LayoutHeader ()
1908 for (int i = 0; i < Columns.Count; i++) {
1909 ColumnHeader col = GetReorderedColumn (i);
1912 col.CalcColumnHeader ();
1918 if (x < ClientRectangle.Width)
1919 x = ClientRectangle.Width;
1921 if (header_style == ColumnHeaderStyle.None) {
1922 header_control.Visible = false;
1923 header_control.Size = Size.Empty;
1924 layout_wd = ClientRectangle.Width;
1926 header_control.Width = x;
1927 header_control.Height = columns.Count > 0 ? columns [0].Ht : ThemeEngine.Current.ListViewGetHeaderHeight (this, Font);
1928 header_control.Visible = true;
1932 void LayoutDetails ()
1936 if (columns.Count == 0) {
1937 item_control.Visible = false;
1938 layout_wd = ClientRectangle.Width;
1939 layout_ht = ClientRectangle.Height;
1943 item_control.Visible = true;
1944 item_control.Location = Point.Empty;
1945 item_control.Width = ClientRectangle.Width;
1947 int item_height = GetDetailsItemHeight ();
1948 ItemSize = new Size (0, item_height); // We only cache Height for details view
1949 int y = header_control.Height;
1951 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1953 CalculateDetailsGroupItemsCount ();
1954 CalculateGroupsLayout (ItemSize, 2, y);
1958 for (int i = 0; i < items.Count; i++) {
1959 ListViewItem item = items [i];
1963 ListViewGroup group = item.Group;
1965 group = groups.DefaultGroup;
1967 int current_item = group.current_item++;
1968 Point group_items_loc = group.items_area_location;
1969 display_index = group.starting_item + current_item;
1971 y = current_item * (item_height + 2) + group_items_loc.Y;
1972 SetItemLocation (display_index, 0, y, 0, 0);
1973 SetItemAtDisplayIndex (display_index, i);
1974 item.SetPosition (new Point (0, y));
1979 SetItemLocation (i, 0, y, 0, 0);
1980 item.SetPosition (new Point (0, y));
1984 if (!virtual_mode) // Virtual mode sets Layout until draw time
1988 item.DisplayIndex = display_index;
1993 // some space for bottom gridline
1994 if (items.Count > 0 && grid_lines)
1998 if (!using_groups) // With groups it has been previously computed
2003 private void AdjustItemsPositionArray (int count)
2005 if (items_location.Length >= count)
2008 // items_location, items_matrix_location and reordered_items_indices must keep the same length
2009 count = Math.Max (count, items_location.Length * 2);
2010 items_location = new Point [count];
2011 items_matrix_location = new ItemMatrixLocation [count];
2012 reordered_items_indices = new int [count];
2015 private void CalculateListView (ListViewAlignment align)
2019 AdjustItemsPositionArray (items.Count);
2026 case View.SmallIcon:
2027 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
2028 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2031 case View.LargeIcon:
2032 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
2033 ThemeEngine.Current.ListViewHorizontalSpacing,
2034 ThemeEngine.Current.ListViewVerticalSpacing);
2038 LayoutIcons (SmallIconItemSize, true,
2039 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
2043 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
2044 ThemeEngine.Current.ListViewHorizontalSpacing,
2045 ThemeEngine.Current.ListViewVerticalSpacing);
2050 CalculateScrollBars ();
2053 internal Point GetItemLocation (int index)
2055 Point loc = items_location [index];
2056 loc.X -= h_marker; // Adjust to scroll
2062 internal int GetItemIndex (int display_index)
2064 return reordered_items_indices [display_index];
2067 internal ListViewItem GetItemAtDisplayIndex (int display_index)
2069 return items [reordered_items_indices [display_index]];
2072 internal void SetItemAtDisplayIndex (int display_index, int index)
2074 reordered_items_indices [display_index] = index;
2077 private bool KeySearchString (KeyEventArgs ke)
2079 int current_tickcnt = Environment.TickCount;
2080 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2081 keysearch_text = string.Empty;
2084 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2087 keysearch_text += (char)ke.KeyCode;
2088 keysearch_tickcnt = current_tickcnt;
2090 int prev_focused = FocusedItem == null ? 0 : FocusedItem.DisplayIndex;
2091 int start = prev_focused + 1 < Items.Count ? prev_focused + 1 : 0;
2093 ListViewItem item = FindItemWithText (keysearch_text, false, start, true, true);
2094 if (item != null && prev_focused != item.DisplayIndex) {
2095 selected_indices.Clear ();
2097 SetFocusedItem (item.DisplayIndex);
2098 item.Selected = true;
2099 EnsureVisible (GetItemIndex (item.DisplayIndex));
2105 private void OnItemsChanged ()
2107 ResetSearchString ();
2110 private void ResetSearchString ()
2112 keysearch_text = String.Empty;
2115 int GetAdjustedIndex (Keys key)
2119 if (View == View.Details) {
2122 result = FocusedItem.DisplayIndex - 1;
2125 result = FocusedItem.DisplayIndex + 1;
2126 if (result == items.Count)
2130 int last_index = LastVisibleIndex;
2131 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2132 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2134 if (FocusedItem.DisplayIndex == last_index) {
2135 if (FocusedItem.DisplayIndex < Items.Count - 1) {
2136 int page_size = item_control.Height / ItemSize.Height - 1;
2137 result = FocusedItem.DisplayIndex + page_size - 1;
2138 if (result >= Items.Count)
2139 result = Items.Count - 1;
2142 result = last_index;
2145 int first_index = FirstVisibleIndex;
2146 if (GetItemLocation (first_index).Y < 0)
2148 if (FocusedItem.DisplayIndex == first_index) {
2149 if (first_index > 0) {
2150 int page_size = item_control.Height / ItemSize.Height - 1;
2151 result = first_index - page_size + 1;
2156 result = first_index;
2162 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.DisplayIndex];
2163 int row = item_matrix_location.Row;
2164 int col = item_matrix_location.Col;
2166 int adjusted_index = -1;
2172 adjusted_index = item_index_matrix [row, col - 1];
2176 if (col == (cols - 1))
2178 while (item_index_matrix [row, col + 1] == 0) {
2183 adjusted_index = item_index_matrix [row, col + 1];
2189 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2194 adjusted_index = item_index_matrix [row - 1, col];
2198 if (row == (rows - 1) || row == Items.Count - 1)
2200 while (item_index_matrix [row + 1, col] == 0) {
2205 adjusted_index = item_index_matrix [row + 1, col];
2212 return items [adjusted_index].DisplayIndex;
2215 ListViewItem selection_start;
2217 private bool SelectItems (ArrayList sel_items)
2219 bool changed = false;
2220 foreach (ListViewItem item in SelectedItems)
2221 if (!sel_items.Contains (item)) {
2222 item.Selected = false;
2225 foreach (ListViewItem item in sel_items)
2226 if (!item.Selected) {
2227 item.Selected = true;
2233 private void UpdateMultiSelection (int index, bool reselect)
2235 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2236 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2237 ListViewItem item = GetItemAtDisplayIndex (index);
2239 if (shift_pressed && selection_start != null) {
2240 ArrayList list = new ArrayList ();
2241 int start_index = selection_start.DisplayIndex;
2242 int start = Math.Min (start_index, index);
2243 int end = Math.Max (start_index, index);
2244 if (View == View.Details) {
2245 for (int i = start; i <= end; i++)
2246 list.Add (GetItemAtDisplayIndex (i));
2248 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2249 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2250 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2251 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2252 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2253 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2255 for (int i = 0; i < items.Count; i++) {
2256 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2258 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2259 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2260 list.Add (GetItemAtDisplayIndex (i));
2264 } else if (ctrl_pressed) {
2265 item.Selected = !item.Selected;
2266 selection_start = item;
2269 // do not unselect, and reselect the item
2270 foreach (int itemIndex in SelectedIndices) {
2271 if (index == itemIndex)
2273 items [itemIndex].Selected = false;
2276 SelectedItems.Clear ();
2277 item.Selected = true;
2279 selection_start = item;
2283 internal override bool InternalPreProcessMessage (ref Message msg)
2285 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2286 Keys key_data = (Keys)msg.WParam.ToInt32();
2288 HandleNavKeys (key_data);
2291 return base.InternalPreProcessMessage (ref msg);
2294 bool HandleNavKeys (Keys key_data)
2296 if (Items.Count == 0 || !item_control.Visible)
2299 if (FocusedItem == null)
2304 SelectIndex (Items.Count - 1);
2317 SelectIndex (GetAdjustedIndex (key_data));
2321 SelectIndex (focused_item_index);
2322 ToggleItemsCheckState ();
2325 if (selected_indices.Count > 0)
2326 OnItemActivate (EventArgs.Empty);
2336 void ToggleItemsCheckState ()
2341 // Don't modify check state if StateImageList has less than 2 elements
2342 if (StateImageList != null && StateImageList.Images.Count < 2)
2345 if (SelectedIndices.Count > 0) {
2346 for (int i = 0; i < SelectedIndices.Count; i++) {
2347 ListViewItem item = Items [SelectedIndices [i]];
2348 item.Checked = !item.Checked;
2353 if (FocusedItem != null) {
2354 FocusedItem.Checked = !FocusedItem.Checked;
2355 SelectIndex (FocusedItem.Index);
2359 void SelectIndex (int display_index)
2361 if (display_index == -1)
2365 UpdateMultiSelection (display_index, true);
2366 else if (!GetItemAtDisplayIndex (display_index).Selected)
2367 GetItemAtDisplayIndex (display_index).Selected = true;
2369 SetFocusedItem (display_index);
2370 EnsureVisible (GetItemIndex (display_index)); // Index in Items collection, not display index
2373 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2375 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2378 if (ke.Alt || ke.Control)
2381 ke.Handled = KeySearchString (ke);
2384 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2386 Point loc = PointToClient (Control.MousePosition);
2387 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2390 internal class ItemControl : Control {
2393 ListViewItem clicked_item;
2394 ListViewItem last_clicked_item;
2395 bool hover_processed = false;
2396 bool checking = false;
2397 ListViewItem prev_hovered_item;
2399 ListViewItem prev_tooltip_item;
2402 Point drag_begin = new Point (-1, -1);
2403 internal int dragged_item_index = -1;
2405 ListViewLabelEditTextBox edit_text_box;
2406 internal ListViewItem edit_item;
2407 LabelEditEventArgs edit_args;
2409 public ItemControl (ListView owner)
2412 this.SetStyle (ControlStyles.DoubleBuffer, true);
2413 DoubleClick += new EventHandler(ItemsDoubleClick);
2414 MouseDown += new MouseEventHandler(ItemsMouseDown);
2415 MouseMove += new MouseEventHandler(ItemsMouseMove);
2416 MouseHover += new EventHandler(ItemsMouseHover);
2417 MouseUp += new MouseEventHandler(ItemsMouseUp);
2420 void ItemsDoubleClick (object sender, EventArgs e)
2422 if (owner.activation == ItemActivation.Standard)
2423 owner.OnItemActivate (EventArgs.Empty);
2433 BoxSelect box_select_mode = BoxSelect.None;
2434 IList prev_selection;
2435 Point box_select_start;
2437 Rectangle box_select_rect;
2438 internal Rectangle BoxSelectRectangle {
2439 get { return box_select_rect; }
2441 if (box_select_rect == value)
2444 InvalidateBoxSelectRect ();
2445 box_select_rect = value;
2446 InvalidateBoxSelectRect ();
2450 void InvalidateBoxSelectRect ()
2452 if (BoxSelectRectangle.Size.IsEmpty)
2455 Rectangle edge = BoxSelectRectangle;
2461 edge.Y = BoxSelectRectangle.Bottom - 1;
2463 edge.Y = BoxSelectRectangle.Y - 1;
2465 edge.Height = BoxSelectRectangle.Height + 2;
2467 edge.X = BoxSelectRectangle.Right - 1;
2471 private Rectangle CalculateBoxSelectRectangle (Point pt)
2473 int left = Math.Min (box_select_start.X, pt.X);
2474 int right = Math.Max (box_select_start.X, pt.X);
2475 int top = Math.Min (box_select_start.Y, pt.Y);
2476 int bottom = Math.Max (box_select_start.Y, pt.Y);
2477 return Rectangle.FromLTRB (left, top, right, bottom);
2480 bool BoxIntersectsItem (int index)
2482 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2483 if (owner.View != View.Details) {
2485 r.Y += r.Height / 4;
2489 return BoxSelectRectangle.IntersectsWith (r);
2492 bool BoxIntersectsText (int index)
2494 Rectangle r = owner.GetItemAtDisplayIndex (index).TextBounds;
2495 return BoxSelectRectangle.IntersectsWith (r);
2498 ArrayList BoxSelectedItems {
2500 ArrayList result = new ArrayList ();
2501 for (int i = 0; i < owner.Items.Count; i++) {
2504 // Can't iterate over specific items properties in virtualmode
2505 if (owner.View == View.Details && !owner.FullRowSelect && !owner.VirtualMode)
2507 if (owner.View == View.Details && !owner.FullRowSelect)
2509 intersects = BoxIntersectsText (i);
2511 intersects = BoxIntersectsItem (i);
2514 result.Add (owner.GetItemAtDisplayIndex (i));
2520 private bool PerformBoxSelection (Point pt)
2522 if (box_select_mode == BoxSelect.None)
2525 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2527 ArrayList box_items = BoxSelectedItems;
2531 switch (box_select_mode) {
2533 case BoxSelect.Normal:
2537 case BoxSelect.Control:
2538 items = new ArrayList ();
2539 foreach (int index in prev_selection)
2540 if (!box_items.Contains (owner.Items [index]))
2541 items.Add (owner.Items [index]);
2542 foreach (ListViewItem item in box_items)
2543 if (!prev_selection.Contains (item.Index))
2547 case BoxSelect.Shift:
2549 foreach (ListViewItem item in box_items)
2550 prev_selection.Remove (item.Index);
2551 foreach (int index in prev_selection)
2552 items.Add (owner.Items [index]);
2556 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2560 owner.SelectItems (items);
2566 private void ItemsMouseDown (object sender, MouseEventArgs me)
2568 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2569 if (owner.items.Count == 0)
2572 bool box_selecting = false;
2573 Size item_size = owner.ItemSize;
2574 Point pt = new Point (me.X, me.Y);
2575 for (int i = 0; i < owner.items.Count; i++) {
2576 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2577 if (!item_rect.Contains (pt))
2580 // Actual item in 'i' position
2581 ListViewItem item = owner.GetItemAtDisplayIndex (i);
2583 if (item.CheckRectReal.Contains (pt)) {
2584 // Don't modify check state if we have only one image
2585 // and if we are in 1.1 profile only take into account
2587 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2594 // Generate an extra ItemCheck event when we got two clicks
2595 // (Match weird .Net behaviour)
2597 item.Checked = !item.Checked;
2599 item.Checked = !item.Checked;
2604 if (owner.View == View.Details) {
2605 bool over_text = item.TextBounds.Contains (pt);
2606 if (owner.FullRowSelect) {
2607 clicked_item = owner.items [i];
2608 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2609 if (!over_text && over_item_column && owner.MultiSelect)
2610 box_selecting = true;
2611 } else if (over_text)
2612 clicked_item = item;
2614 owner.SetFocusedItem (i);
2616 clicked_item = item;
2622 if (clicked_item != null) {
2623 bool changed = !clicked_item.Selected;
2624 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2625 owner.SetFocusedItem (clicked_item.DisplayIndex);
2627 if (owner.MultiSelect) {
2628 bool reselect = (!owner.LabelEdit || changed);
2629 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2630 owner.UpdateMultiSelection (clicked_item.DisplayIndex, reselect);
2632 clicked_item.Selected = true;
2636 if (owner.VirtualMode && changed) {
2637 // Broken event - It's not fired from Item.Selected also
2638 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2639 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2641 owner.OnVirtualItemsSelectionRangeChanged (args);
2644 // Report clicks only if the item was clicked. On MS the
2645 // clicks are only raised if you click an item
2647 if (me.Clicks > 1) {
2648 if (owner.CheckBoxes)
2649 clicked_item.Checked = !clicked_item.Checked;
2650 } else if (me.Clicks == 1) {
2651 if (owner.LabelEdit && !changed)
2652 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2655 if (owner.MultiSelect)
2656 box_selecting = true;
2657 else if (owner.SelectedItems.Count > 0)
2658 owner.SelectedItems.Clear ();
2661 if (box_selecting) {
2662 Keys mods = XplatUI.State.ModifierKeys;
2663 if ((mods & Keys.Shift) != 0)
2664 box_select_mode = BoxSelect.Shift;
2665 else if ((mods & Keys.Control) != 0)
2666 box_select_mode = BoxSelect.Control;
2668 box_select_mode = BoxSelect.Normal;
2669 box_select_start = pt;
2670 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2674 private void ItemsMouseMove (object sender, MouseEventArgs me)
2676 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2678 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2682 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2683 !hover_processed && owner.Activation != ItemActivation.OneClick
2685 && !owner.ShowItemToolTips
2690 Point pt = PointToClient (Control.MousePosition);
2691 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2693 if (hover_processed && item != null && item != prev_hovered_item) {
2694 hover_processed = false;
2695 XplatUI.ResetMouseHover (Handle);
2698 // Need to invalidate the item in HotTracking to show/hide the underline style
2699 if (owner.Activation == ItemActivation.OneClick) {
2700 if (item == null && owner.HotItemIndex != -1) {
2702 if (owner.HotTracking)
2703 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2706 Cursor = Cursors.Default;
2707 owner.HotItemIndex = -1;
2708 } else if (item != null && owner.HotItemIndex == -1) {
2710 if (owner.HotTracking)
2711 Invalidate (item.Bounds);
2714 Cursor = Cursors.Hand;
2715 owner.HotItemIndex = item.Index;
2719 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2720 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2722 drag_begin = new Point (me.X, me.Y);
2723 dragged_item_index = item.Index;
2727 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2728 if (!r.Contains (me.X, me.Y)) {
2729 ListViewItem dragged_item = owner.items [dragged_item_index];
2730 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2732 drag_begin = new Point (-1, -1);
2733 dragged_item_index = -1;
2739 if (owner.ShowItemToolTips) {
2741 owner.item_tooltip.Active = false;
2742 prev_tooltip_item = null;
2743 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2744 owner.item_tooltip.Active = true;
2745 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2746 prev_tooltip_item = item;
2753 private void ItemsMouseHover (object sender, EventArgs e)
2755 if (owner.hover_pending) {
2756 owner.OnMouseHover (e);
2757 owner.hover_pending = false;
2763 hover_processed = true;
2764 Point pt = PointToClient (Control.MousePosition);
2765 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2769 prev_hovered_item = item;
2771 if (owner.HoverSelection) {
2772 if (owner.MultiSelect)
2773 owner.UpdateMultiSelection (item.Index, true);
2775 item.Selected = true;
2777 owner.SetFocusedItem (item.DisplayIndex);
2778 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2782 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2786 void HandleClicks (MouseEventArgs me)
2788 // if the click is not on an item,
2789 // clicks remains as 0
2792 owner.OnDoubleClick (EventArgs.Empty);
2793 } else if (clicks == 1) {
2794 owner.OnClick (EventArgs.Empty);
2796 owner.OnDoubleClick (EventArgs.Empty);
2797 owner.OnMouseDoubleClick (me);
2798 } else if (clicks == 1) {
2799 owner.OnClick (EventArgs.Empty);
2800 owner.OnMouseClick (me);
2807 private void ItemsMouseUp (object sender, MouseEventArgs me)
2809 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2810 HandleClicks (owner_me);
2813 if (owner.Items.Count == 0) {
2815 owner.OnMouseUp (owner_me);
2819 Point pt = new Point (me.X, me.Y);
2821 Rectangle rect = Rectangle.Empty;
2822 if (clicked_item != null) {
2823 if (owner.view == View.Details && !owner.full_row_select)
2824 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2826 rect = clicked_item.Bounds;
2828 if (rect.Contains (pt)) {
2829 switch (owner.activation) {
2830 case ItemActivation.OneClick:
2831 owner.OnItemActivate (EventArgs.Empty);
2834 case ItemActivation.TwoClick:
2835 if (last_clicked_item == clicked_item) {
2836 owner.OnItemActivate (EventArgs.Empty);
2837 last_clicked_item = null;
2839 last_clicked_item = clicked_item;
2842 // DoubleClick activation is handled in another handler
2846 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2847 // Need this to clean up background clicks
2848 owner.SelectedItems.Clear ();
2852 owner.OnMouseUp (owner_me);
2855 private void ResetMouseState ()
2857 clicked_item = null;
2858 box_select_start = Point.Empty;
2859 BoxSelectRectangle = Rectangle.Empty;
2860 prev_selection = null;
2861 box_select_mode = BoxSelect.None;
2864 // Clean these bits in case the mouse buttons were
2865 // released before firing ItemDrag
2866 dragged_item_index = -1;
2867 drag_begin = new Point (-1, -1);
2870 private void LabelEditFinished (object sender, EventArgs e)
2872 EndEdit (edit_item);
2875 private void LabelEditCancelled (object sender, EventArgs e)
2877 edit_args.SetLabel (null);
2878 EndEdit (edit_item);
2881 private void LabelTextChanged (object sender, EventArgs e)
2883 if (edit_args != null)
2884 edit_args.SetLabel (edit_text_box.Text);
2887 internal void BeginEdit (ListViewItem item)
2889 if (edit_item != null)
2890 EndEdit (edit_item);
2892 if (edit_text_box == null) {
2893 edit_text_box = new ListViewLabelEditTextBox ();
2894 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2895 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2896 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2897 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2898 edit_text_box.Visible = false;
2899 Controls.Add (edit_text_box);
2902 item.EnsureVisible();
2904 edit_text_box.Reset ();
2906 switch (owner.view) {
2908 case View.SmallIcon:
2910 edit_text_box.TextAlign = HorizontalAlignment.Left;
2911 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2912 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2913 edit_text_box.Width = (int)sizef.Width + 4;
2914 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2915 edit_text_box.WordWrap = false;
2916 edit_text_box.Multiline = false;
2918 case View.LargeIcon:
2919 edit_text_box.TextAlign = HorizontalAlignment.Center;
2920 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2921 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2922 edit_text_box.Width = (int)sizef.Width + 4;
2923 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2924 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2925 edit_text_box.WordWrap = true;
2926 edit_text_box.Multiline = true;
2932 edit_text_box.Text = item.Text;
2933 edit_text_box.Font = item.Font;
2934 edit_text_box.Visible = true;
2935 edit_text_box.Focus ();
2936 edit_text_box.SelectAll ();
2938 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2939 owner.OnBeforeLabelEdit (edit_args);
2941 if (edit_args.CancelEdit)
2945 internal void CancelEdit (ListViewItem item)
2947 // do nothing if there's no item being edited, or if the
2948 // item being edited is not the one passed in
2949 if (edit_item == null || edit_item != item)
2952 edit_args.SetLabel (null);
2956 internal void EndEdit (ListViewItem item)
2958 // do nothing if there's no item being edited, or if the
2959 // item being edited is not the one passed in
2960 if (edit_item == null || edit_item != item)
2963 if (edit_text_box != null) {
2964 if (edit_text_box.Visible)
2965 edit_text_box.Visible = false;
2966 // ensure listview gets focus
2970 // Same as TreeView.EndEdit: need to have focus in synch
2971 Application.DoEvents ();
2974 // Create a new instance, since we could get a call to BeginEdit
2975 // from the handler and have fields out of synch
2977 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2980 owner.OnAfterLabelEdit (args);
2981 if (!args.CancelEdit && args.Label != null)
2982 item.Text = args.Label;
2985 internal override void OnPaintInternal (PaintEventArgs pe)
2987 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2990 protected override void WndProc (ref Message m)
2992 switch ((Msg)m.Msg) {
2993 case Msg.WM_KILLFOCUS:
2994 owner.Select (false, true);
2996 case Msg.WM_SETFOCUS:
2997 owner.Select (false, true);
2999 case Msg.WM_LBUTTONDOWN:
3001 owner.Select (false, true);
3003 case Msg.WM_RBUTTONDOWN:
3005 owner.Select (false, true);
3010 base.WndProc (ref m);
3014 internal class ListViewLabelEditTextBox : TextBox
3019 int max_height = -1;
3020 int min_height = -1;
3022 int old_number_lines = 1;
3024 SizeF text_size_one_char;
3026 public ListViewLabelEditTextBox ()
3028 min_height = DefaultSize.Height;
3029 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3032 public int MaxWidth {
3034 if (value < min_width)
3035 max_width = min_width;
3041 public int MaxHeight {
3043 if (value < min_height)
3044 max_height = min_height;
3050 public new int Width {
3060 public override Font Font {
3066 text_size_one_char = TextRenderer.MeasureString ("B", Font);
3070 protected override void OnTextChanged (EventArgs e)
3072 SizeF text_size = TextRenderer.MeasureString (Text, Font);
3074 int new_width = (int)text_size.Width + 8;
3077 ResizeTextBoxWidth (new_width);
3079 if (Width != max_width)
3080 ResizeTextBoxWidth (new_width);
3082 int number_lines = Lines.Length;
3084 if (number_lines != old_number_lines) {
3085 int new_height = number_lines * (int)text_size_one_char.Height + 4;
3086 old_number_lines = number_lines;
3088 ResizeTextBoxHeight (new_height);
3092 base.OnTextChanged (e);
3095 protected override bool IsInputKey (Keys key_data)
3097 if ((key_data & Keys.Alt) == 0) {
3098 switch (key_data & Keys.KeyCode) {
3105 return base.IsInputKey (key_data);
3108 protected override void OnKeyDown (KeyEventArgs e)
3113 switch (e.KeyCode) {
3117 OnEditingFinished (e);
3122 OnEditingCancelled (e);
3127 protected override void OnLostFocus (EventArgs e)
3130 OnEditingFinished (e);
3134 protected void OnEditingCancelled (EventArgs e)
3136 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3141 protected void OnEditingFinished (EventArgs e)
3143 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3148 private void ResizeTextBoxWidth (int new_width)
3150 if (new_width > max_width)
3151 base.Width = max_width;
3153 if (new_width >= min_width)
3154 base.Width = new_width;
3156 base.Width = min_width;
3159 private void ResizeTextBoxHeight (int new_height)
3161 if (new_height > max_height)
3162 base.Height = max_height;
3164 if (new_height >= min_height)
3165 base.Height = new_height;
3167 base.Height = min_height;
3170 public void Reset ()
3177 old_number_lines = 1;
3179 Text = String.Empty;
3184 static object EditingCancelledEvent = new object ();
3185 public event EventHandler EditingCancelled {
3186 add { Events.AddHandler (EditingCancelledEvent, value); }
3187 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3190 static object EditingFinishedEvent = new object ();
3191 public event EventHandler EditingFinished {
3192 add { Events.AddHandler (EditingFinishedEvent, value); }
3193 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3197 internal override void OnPaintInternal (PaintEventArgs pe)
3202 CalculateScrollBars ();
3205 void FocusChanged (object o, EventArgs args)
3207 if (Items.Count == 0)
3210 if (FocusedItem == null)
3213 ListViewItem focused_item = FocusedItem;
3215 if (focused_item.ListView != null) {
3216 item_control.Invalidate (focused_item.Bounds);
3217 focused_item.Layout ();
3218 item_control.Invalidate (focused_item.Bounds);
3222 private void ListView_Invalidated (object sender, InvalidateEventArgs e)
3224 // When the ListView is invalidated, we need to invalidate
3225 // the child controls.
3226 header_control.Invalidate ();
3227 item_control.Invalidate ();
3230 private void ListView_MouseEnter (object sender, EventArgs args)
3232 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3235 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3237 if (Items.Count == 0)
3240 int lines = me.Delta / 120;
3247 case View.SmallIcon:
3248 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3250 case View.LargeIcon:
3251 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3254 Scroll (h_scroll, -ItemSize.Width * lines);
3258 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3264 private void ListView_SizeChanged (object sender, EventArgs e)
3266 CalculateListView (alignment);
3269 private void SetFocusedItem (int display_index)
3271 if (display_index != -1)
3272 GetItemAtDisplayIndex (display_index).Focused = true;
3273 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3274 GetItemAtDisplayIndex (focused_item_index).Focused = false;
3276 focused_item_index = display_index;
3279 private void HorizontalScroller (object sender, EventArgs e)
3281 item_control.EndEdit (item_control.edit_item);
3283 // Avoid unnecessary flickering, when button is
3284 // kept pressed at the end
3285 if (h_marker != h_scroll.Value) {
3287 int pixels = h_marker - h_scroll.Value;
3289 h_marker = h_scroll.Value;
3290 if (header_control.Visible)
3291 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3293 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3297 private void VerticalScroller (object sender, EventArgs e)
3299 item_control.EndEdit (item_control.edit_item);
3301 // Avoid unnecessary flickering, when button is
3302 // kept pressed at the end
3303 if (v_marker != v_scroll.Value) {
3304 int pixels = v_marker - v_scroll.Value;
3305 Rectangle area = item_control.ClientRectangle;
3306 if (header_control.Visible) {
3307 area.Y += header_control.Height;
3308 area.Height -= header_control.Height;
3311 v_marker = v_scroll.Value;
3312 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3316 internal override bool IsInputCharInternal (char charCode)
3320 #endregion // Internal Methods Properties
3322 #region Protected Methods
3323 protected override void CreateHandle ()
3325 base.CreateHandle ();
3326 for (int i = 0; i < SelectedItems.Count; i++)
3327 OnSelectedIndexChanged (EventArgs.Empty);
3330 protected override void Dispose (bool disposing)
3333 h_scroll.Dispose ();
3334 v_scroll.Dispose ();
3336 large_image_list = null;
3337 small_image_list = null;
3338 state_image_list = null;
3340 foreach (ColumnHeader col in columns)
3341 col.SetListView (null);
3344 if (!virtual_mode) // In virtual mode we don't save the items
3346 foreach (ListViewItem item in items)
3350 base.Dispose (disposing);
3353 protected override bool IsInputKey (Keys keyData)
3370 return base.IsInputKey (keyData);
3373 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3375 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3381 protected override void OnBackgroundImageChanged (EventArgs e)
3383 item_control.BackgroundImage = BackgroundImage;
3384 base.OnBackgroundImageChanged (e);
3388 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3390 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3395 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3397 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3403 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3405 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3410 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3412 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3417 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3419 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3425 protected override void OnEnabledChanged (EventArgs e)
3427 base.OnEnabledChanged (e);
3431 protected override void OnFontChanged (EventArgs e)
3433 base.OnFontChanged (e);
3437 protected override void OnHandleCreated (EventArgs e)
3439 base.OnHandleCreated (e);
3440 CalculateListView (alignment);
3442 if (!virtual_mode) // Sorting is not allowed in virtual mode
3447 protected override void OnHandleDestroyed (EventArgs e)
3449 base.OnHandleDestroyed (e);
3452 protected virtual void OnItemActivate (EventArgs e)
3454 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3459 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3461 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3467 protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
3469 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3475 protected virtual void OnItemDrag (ItemDragEventArgs e)
3477 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3483 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
3485 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3490 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
3492 ListViewItemSelectionChangedEventHandler eh =
3493 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3498 protected override void OnMouseHover (EventArgs e)
3500 base.OnMouseHover (e);
3503 protected override void OnParentChanged (EventArgs e)
3505 base.OnParentChanged (e);
3509 protected virtual void OnSelectedIndexChanged (EventArgs e)
3511 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3516 protected override void OnSystemColorsChanged (EventArgs e)
3518 base.OnSystemColorsChanged (e);
3522 protected internal virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
3524 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3529 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
3531 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3536 [EditorBrowsable (EditorBrowsableState.Advanced)]
3537 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3539 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3544 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
3546 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3551 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
3553 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3554 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3560 protected void RealizeProperties ()
3565 protected void UpdateExtendedStyles ()
3570 bool refocusing = false;
3572 protected override void WndProc (ref Message m)
3574 switch ((Msg)m.Msg) {
3575 case Msg.WM_KILLFOCUS:
3576 Control receiver = Control.FromHandle (m.WParam);
3577 if (receiver == item_control) {
3583 case Msg.WM_SETFOCUS:
3593 base.WndProc (ref m);
3595 #endregion // Protected Methods
3597 #region Public Instance Methods
3598 public void ArrangeIcons ()
3600 ArrangeIcons (this.alignment);
3603 public void ArrangeIcons (ListViewAlignment value)
3605 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3606 if (view == View.LargeIcon || view == View.SmallIcon) {
3607 this.CalculateListView (value);
3608 // we have done the calculations already
3609 this.Redraw (false);
3614 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3616 if (columnIndex < 0 || columnIndex >= columns.Count)
3617 throw new ArgumentOutOfRangeException ("columnIndex");
3619 columns [columnIndex].AutoResize (headerAutoResize);
3622 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3625 foreach (ColumnHeader col in columns)
3626 col.AutoResize (headerAutoResize);
3631 public void BeginUpdate ()
3633 // flag to avoid painting
3637 public void Clear ()
3640 items.Clear (); // Redraw (true) called here
3643 public void EndUpdate ()
3645 // flag to avoid painting
3648 // probably, now we need a redraw with recalculations
3652 public void EnsureVisible (int index)
3654 if (index < 0 || index >= items.Count || scrollable == false)
3657 Rectangle view_rect = item_control.ClientRectangle;
3658 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3660 if (view_rect.Contains (bounds))
3663 if (View != View.Details) {
3664 if (bounds.Left < 0)
3665 h_scroll.Value += bounds.Left;
3666 else if (bounds.Right > view_rect.Right)
3667 h_scroll.Value += (bounds.Right - view_rect.Right);
3671 v_scroll.Value += bounds.Top;
3672 else if (bounds.Bottom > view_rect.Bottom)
3673 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3677 public ListViewItem FindItemWithText (string text)
3679 if (items.Count == 0)
3682 return FindItemWithText (text, true, 0, true);
3685 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
3687 return FindItemWithText (text, includeSubItemsInSearch, startIndex, true, false);
3690 public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
3692 return FindItemWithText (text, includeSubItemsInSearch, startIndex, isPrefixSearch, false);
3696 internal ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch, bool roundtrip)
3698 if (startIndex < 0 || startIndex >= items.Count)
3699 throw new ArgumentOutOfRangeException ("startIndex");
3702 throw new ArgumentNullException ("text");
3706 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3707 isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
3708 SearchDirectionHint.Down, startIndex);
3710 OnSearchForVirtualItem (args);
3711 int idx = args.Index;
3712 if (idx >= 0 && idx < virtual_list_size)
3721 ListViewItem lvi = items [i];
3723 if (isPrefixSearch) { // prefix search
3724 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (lvi.Text, text, CompareOptions.IgnoreCase))
3726 } else if (String.Compare (lvi.Text, text, true) == 0) // match
3729 if (i + 1 >= items.Count) {
3737 if (i == startIndex)
3741 // Subitems have a minor priority, so we have to do a second linear search
3742 // Also, we don't need to to a roundtrip search for them by now
3743 if (includeSubItemsInSearch) {
3744 for (i = startIndex; i < items.Count; i++) {
3745 ListViewItem lvi = items [i];
3746 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3747 if (isPrefixSearch) {
3748 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (sub_item.Text,
3749 text, CompareOptions.IgnoreCase))
3751 } else if (String.Compare (sub_item.Text, text, true) == 0)
3760 public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
3762 return FindNearestItem (searchDirection, new Point (x, y));
3765 public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
3767 if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
3768 throw new ArgumentOutOfRangeException ("searchDirection");
3770 if (view != View.LargeIcon && view != View.SmallIcon)
3771 throw new InvalidOperationException ();
3774 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3775 false, false, String.Empty, point,
3778 OnSearchForVirtualItem (args);
3779 int idx = args.Index;
3780 if (idx >= 0 && idx < virtual_list_size)
3786 ListViewItem item = null;
3787 int min_dist = Int32.MaxValue;
3790 // It looks like .Net does a previous adjustment
3793 case SearchDirectionHint.Up:
3794 point.Y -= item_size.Height;
3796 case SearchDirectionHint.Down:
3797 point.Y += item_size.Height;
3799 case SearchDirectionHint.Left:
3800 point.X -= item_size.Width;
3802 case SearchDirectionHint.Right:
3803 point.X += item_size.Width;
3807 for (int i = 0; i < items.Count; i++) {
3808 Point item_loc = GetItemLocation (i);
3810 if (dir == SearchDirectionHint.Up) {
3811 if (point.Y < item_loc.Y)
3813 } else if (dir == SearchDirectionHint.Down) {
3814 if (point.Y > item_loc.Y)
3816 } else if (dir == SearchDirectionHint.Left) {
3817 if (point.X < item_loc.X)
3819 } else if (dir == SearchDirectionHint.Right) {
3820 if (point.X > item_loc.X)
3824 int x_dist = point.X - item_loc.X;
3825 int y_dist = point.Y - item_loc.Y;
3827 int dist = x_dist * x_dist + y_dist * y_dist;
3828 if (dist < min_dist) {
3838 public ListViewItem GetItemAt (int x, int y)
3840 Size item_size = ItemSize;
3841 for (int i = 0; i < items.Count; i++) {
3842 Point item_location = GetItemLocation (i);
3843 Rectangle item_rect = new Rectangle (item_location, item_size);
3844 if (item_rect.Contains (x, y))
3851 public Rectangle GetItemRect (int index)
3853 return GetItemRect (index, ItemBoundsPortion.Entire);
3856 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3858 if (index < 0 || index >= items.Count)
3859 throw new IndexOutOfRangeException ("index");
3861 return items [index].GetBounds (portion);
3865 public ListViewHitTestInfo HitTest (Point point)
3867 return HitTest (point.X, point.Y);
3870 public ListViewHitTestInfo HitTest (int x, int y)
3873 throw new ArgumentOutOfRangeException ("x");
3875 throw new ArgumentOutOfRangeException ("y");
3877 ListViewItem item = GetItemAt (x, y);
3879 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3881 ListViewHitTestLocations locations = 0;
3882 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3883 locations |= ListViewHitTestLocations.Label;
3884 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3885 locations |= ListViewHitTestLocations.Image;
3886 else if (item.CheckRectReal.Contains (x, y))
3887 locations |= ListViewHitTestLocations.StateImage;
3889 ListViewItem.ListViewSubItem subitem = null;
3890 if (view == View.Details)
3891 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3892 if (si.Bounds.Contains (x, y)) {
3897 return new ListViewHitTestInfo (item, subitem, locations);
3900 [EditorBrowsable (EditorBrowsableState.Advanced)]
3901 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3903 if (startIndex < 0 || startIndex >= items.Count)
3904 throw new ArgumentOutOfRangeException ("startIndex");
3905 if (endIndex < 0 || endIndex >= items.Count)
3906 throw new ArgumentOutOfRangeException ("endIndex");
3907 if (startIndex > endIndex)
3908 throw new ArgumentException ("startIndex");
3913 for (int i = startIndex; i <= endIndex; i++)
3914 item_control.Invalidate (items [i].Bounds);
3916 if (!invalidateOnly)
3925 throw new InvalidOperationException ();
3931 // we need this overload to reuse the logic for sorting, while allowing
3932 // redrawing to be done by caller or have it done by this method when
3933 // sorting is really performed
3935 // ListViewItemCollection's Add and AddRange methods call this overload
3936 // with redraw set to false, as they take care of redrawing themselves
3937 // (they even want to redraw the listview if no sort is performed, as
3938 // an item was added), while ListView.Sort () only wants to redraw if
3939 // sorting was actually performed
3940 private void Sort (bool redraw)
3942 if (!IsHandleCreated || item_sorter == null) {
3946 items.Sort (item_sorter);
3951 public override string ToString ()
3953 int count = this.Items.Count;
3956 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3958 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3960 #endregion // Public Instance Methods
3965 class HeaderControl : Control {
3968 bool column_resize_active = false;
3969 ColumnHeader resize_column;
3970 ColumnHeader clicked_column;
3971 ColumnHeader drag_column;
3973 int drag_to_index = -1;
3974 ColumnHeader entered_column_header;
3976 public HeaderControl (ListView owner)
3979 this.SetStyle (ControlStyles.DoubleBuffer, true);
3980 MouseDown += new MouseEventHandler (HeaderMouseDown);
3981 MouseMove += new MouseEventHandler (HeaderMouseMove);
3982 MouseUp += new MouseEventHandler (HeaderMouseUp);
3983 MouseLeave += new EventHandler (OnMouseLeave);
3986 internal ColumnHeader EnteredColumnHeader {
3987 get { return entered_column_header; }
3989 if (entered_column_header == value)
3991 if (ThemeEngine.Current.ListViewHasHotHeaderStyle) {
3992 Region region_to_invalidate = new Region ();
3993 region_to_invalidate.MakeEmpty ();
3994 if (entered_column_header != null)
3995 region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
3996 entered_column_header = value;
3997 if (entered_column_header != null)
3998 region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
3999 Invalidate (region_to_invalidate);
4000 region_to_invalidate.Dispose ();
4002 entered_column_header = value;
4006 void OnMouseLeave (object sender, EventArgs e)
4008 EnteredColumnHeader = null;
4011 private ColumnHeader ColumnAtX (int x)
4013 Point pt = new Point (x, 0);
4014 ColumnHeader result = null;
4015 foreach (ColumnHeader col in owner.Columns) {
4016 if (col.Rect.Contains (pt)) {
4024 private int GetReorderedIndex (ColumnHeader col)
4026 if (owner.reordered_column_indices == null)
4029 for (int i = 0; i < owner.Columns.Count; i++)
4030 if (owner.reordered_column_indices [i] == col.Index)
4032 throw new Exception ("Column index missing from reordered array");
4035 private void HeaderMouseDown (object sender, MouseEventArgs me)
4037 if (resize_column != null) {
4038 column_resize_active = true;
4043 clicked_column = ColumnAtX (me.X + owner.h_marker);
4045 if (clicked_column != null) {
4047 if (owner.AllowColumnReorder) {
4049 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
4050 drag_column.Rect = clicked_column.Rect;
4051 drag_to_index = GetReorderedIndex (clicked_column);
4053 clicked_column.Pressed = true;
4054 Invalidate (clicked_column);
4059 void Invalidate (ColumnHeader columnHeader)
4061 Invalidate (GetColumnHeaderInvalidateArea (columnHeader));
4064 Rectangle GetColumnHeaderInvalidateArea (ColumnHeader columnHeader)
4066 Rectangle bounds = columnHeader.Rect;
4067 bounds.X -= owner.h_marker;
4073 column_resize_active = false;
4074 resize_column = null;
4076 Cursor = Cursors.Default;
4079 private void HeaderMouseMove (object sender, MouseEventArgs me)
4081 Point pt = new Point (me.X + owner.h_marker, me.Y);
4083 if (column_resize_active) {
4084 int width = pt.X - resize_column.X;
4088 if (!owner.CanProceedWithResize (resize_column, width)){
4092 resize_column.Width = width;
4096 resize_column = null;
4098 if (clicked_column != null) {
4099 if (owner.AllowColumnReorder) {
4102 r = drag_column.Rect;
4103 r.X = clicked_column.Rect.X + me.X - drag_x;
4104 drag_column.Rect = r;
4106 int x = me.X + owner.h_marker;
4107 ColumnHeader over = ColumnAtX (x);
4109 drag_to_index = owner.Columns.Count;
4110 else if (x < over.X + over.Width / 2)
4111 drag_to_index = GetReorderedIndex (over);
4113 drag_to_index = GetReorderedIndex (over) + 1;
4116 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
4117 bool pressed = clicked_column.Pressed;
4118 clicked_column.Pressed = over == clicked_column;
4119 if (clicked_column.Pressed ^ pressed)
4120 Invalidate (clicked_column);
4125 for (int i = 0; i < owner.Columns.Count; i++) {
4126 Rectangle zone = owner.Columns [i].Rect;
4127 if (zone.Contains (pt))
4128 EnteredColumnHeader = owner.Columns [i];
4129 zone.X = zone.Right - 5;
4131 if (zone.Contains (pt)) {
4132 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
4134 resize_column = owner.Columns [i];
4139 if (resize_column == null)
4140 Cursor = Cursors.Default;
4142 Cursor = Cursors.VSplit;
4145 void HeaderMouseUp (object sender, MouseEventArgs me)
4149 if (column_resize_active) {
4150 int column_idx = resize_column.Index;
4152 owner.RaiseColumnWidthChanged (column_idx);
4156 if (clicked_column != null && clicked_column.Pressed) {
4157 clicked_column.Pressed = false;
4158 Invalidate (clicked_column);
4159 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
4162 if (drag_column != null && owner.AllowColumnReorder) {
4164 if (drag_to_index > GetReorderedIndex (clicked_column))
4166 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
4167 owner.ReorderColumn (clicked_column, drag_to_index, true);
4172 clicked_column = null;
4175 internal override void OnPaintInternal (PaintEventArgs pe)
4180 Theme theme = ThemeEngine.Current;
4181 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4183 if (drag_column == null)
4187 if (drag_to_index == owner.Columns.Count)
4188 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4190 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4191 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4194 protected override void WndProc (ref Message m)
4196 switch ((Msg)m.Msg) {
4197 case Msg.WM_SETFOCUS:
4201 base.WndProc (ref m);
4207 private class ItemComparer : IComparer {
4208 readonly SortOrder sort_order;
4210 public ItemComparer (SortOrder sortOrder)
4212 sort_order = sortOrder;
4215 public int Compare (object x, object y)
4217 ListViewItem item_x = x as ListViewItem;
4218 ListViewItem item_y = y as ListViewItem;
4219 if (sort_order == SortOrder.Ascending)
4220 return String.Compare (item_x.Text, item_y.Text);
4222 return String.Compare (item_y.Text, item_x.Text);
4227 [ListBindable (false)]
4229 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4231 private readonly ListView owner;
4233 #region Public Constructor
4234 public CheckedIndexCollection (ListView owner)
4238 #endregion // Public Constructor
4240 #region Public Properties
4243 get { return owner.CheckedItems.Count; }
4246 public bool IsReadOnly {
4247 get { return true; }
4250 public int this [int index] {
4252 int [] indices = GetIndices ();
4253 if (index < 0 || index >= indices.Length)
4254 throw new ArgumentOutOfRangeException ("index");
4255 return indices [index];
4259 bool ICollection.IsSynchronized {
4260 get { return false; }
4263 object ICollection.SyncRoot {
4264 get { return this; }
4267 bool IList.IsFixedSize {
4268 get { return true; }
4271 object IList.this [int index] {
4272 get { return this [index]; }
4273 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4275 #endregion // Public Properties
4277 #region Public Methods
4278 public bool Contains (int checkedIndex)
4280 int [] indices = GetIndices ();
4281 for (int i = 0; i < indices.Length; i++) {
4282 if (indices [i] == checkedIndex)
4288 public IEnumerator GetEnumerator ()
4290 int [] indices = GetIndices ();
4291 return indices.GetEnumerator ();
4294 void ICollection.CopyTo (Array dest, int index)
4296 int [] indices = GetIndices ();
4297 Array.Copy (indices, 0, dest, index, indices.Length);
4300 int IList.Add (object value)
4302 throw new NotSupportedException ("Add operation is not supported.");
4307 throw new NotSupportedException ("Clear operation is not supported.");
4310 bool IList.Contains (object checkedIndex)
4312 if (!(checkedIndex is int))
4314 return Contains ((int) checkedIndex);
4317 int IList.IndexOf (object checkedIndex)
4319 if (!(checkedIndex is int))
4321 return IndexOf ((int) checkedIndex);
4324 void IList.Insert (int index, object value)
4326 throw new NotSupportedException ("Insert operation is not supported.");
4329 void IList.Remove (object value)
4331 throw new NotSupportedException ("Remove operation is not supported.");
4334 void IList.RemoveAt (int index)
4336 throw new NotSupportedException ("RemoveAt operation is not supported.");
4339 public int IndexOf (int checkedIndex)
4341 int [] indices = GetIndices ();
4342 for (int i = 0; i < indices.Length; i++) {
4343 if (indices [i] == checkedIndex)
4348 #endregion // Public Methods
4350 private int [] GetIndices ()
4352 ArrayList checked_items = owner.CheckedItems.List;
4353 int [] indices = new int [checked_items.Count];
4354 for (int i = 0; i < checked_items.Count; i++) {
4355 ListViewItem item = (ListViewItem) checked_items [i];
4356 indices [i] = item.Index;
4360 } // CheckedIndexCollection
4363 [ListBindable (false)]
4365 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4367 private readonly ListView owner;
4368 private ArrayList list;
4370 #region Public Constructor
4371 public CheckedListViewItemCollection (ListView owner)
4374 this.owner.Items.Changed += new CollectionChangedHandler (
4375 ItemsCollection_Changed);
4377 #endregion // Public Constructor
4379 #region Public Properties
4383 if (!owner.CheckBoxes)
4389 public bool IsReadOnly {
4390 get { return true; }
4393 public ListViewItem this [int index] {
4396 if (owner.VirtualMode)
4397 throw new InvalidOperationException ();
4399 ArrayList checked_items = List;
4400 if (index < 0 || index >= checked_items.Count)
4401 throw new ArgumentOutOfRangeException ("index");
4402 return (ListViewItem) checked_items [index];
4407 public virtual ListViewItem this [string key] {
4409 int idx = IndexOfKey (key);
4410 return idx == -1 ? null : (ListViewItem) List [idx];
4415 bool ICollection.IsSynchronized {
4416 get { return false; }
4419 object ICollection.SyncRoot {
4420 get { return this; }
4423 bool IList.IsFixedSize {
4424 get { return true; }
4427 object IList.this [int index] {
4428 get { return this [index]; }
4429 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4431 #endregion // Public Properties
4433 #region Public Methods
4434 public bool Contains (ListViewItem item)
4436 if (!owner.CheckBoxes)
4438 return List.Contains (item);
4442 public virtual bool ContainsKey (string key)
4444 return IndexOfKey (key) != -1;
4448 public void CopyTo (Array dest, int index)
4451 if (owner.VirtualMode)
4452 throw new InvalidOperationException ();
4454 if (!owner.CheckBoxes)
4456 List.CopyTo (dest, index);
4459 public IEnumerator GetEnumerator ()
4462 if (owner.VirtualMode)
4463 throw new InvalidOperationException ();
4465 if (!owner.CheckBoxes)
4466 return (new ListViewItem [0]).GetEnumerator ();
4467 return List.GetEnumerator ();
4470 int IList.Add (object value)
4472 throw new NotSupportedException ("Add operation is not supported.");
4477 throw new NotSupportedException ("Clear operation is not supported.");
4480 bool IList.Contains (object item)
4482 if (!(item is ListViewItem))
4484 return Contains ((ListViewItem) item);
4487 int IList.IndexOf (object item)
4489 if (!(item is ListViewItem))
4491 return IndexOf ((ListViewItem) item);
4494 void IList.Insert (int index, object value)
4496 throw new NotSupportedException ("Insert operation is not supported.");
4499 void IList.Remove (object value)
4501 throw new NotSupportedException ("Remove operation is not supported.");
4504 void IList.RemoveAt (int index)
4506 throw new NotSupportedException ("RemoveAt operation is not supported.");
4509 public int IndexOf (ListViewItem item)
4512 if (owner.VirtualMode)
4513 throw new InvalidOperationException ();
4515 if (!owner.CheckBoxes)
4517 return List.IndexOf (item);
4521 public virtual int IndexOfKey (string key)
4524 if (owner.VirtualMode)
4525 throw new InvalidOperationException ();
4527 if (key == null || key.Length == 0)
4530 ArrayList checked_items = List;
4531 for (int i = 0; i < checked_items.Count; i++) {
4532 ListViewItem item = (ListViewItem) checked_items [i];
4533 if (String.Compare (key, item.Name, true) == 0)
4540 #endregion // Public Methods
4542 internal ArrayList List {
4545 list = new ArrayList ();
4546 foreach (ListViewItem item in owner.Items) {
4555 internal void Reset ()
4557 // force re-population of list
4561 private void ItemsCollection_Changed ()
4565 } // CheckedListViewItemCollection
4568 [ListBindable (false)]
4570 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4572 internal ArrayList list;
4573 private ListView owner;
4575 #region Public Constructor
4576 public ColumnHeaderCollection (ListView owner)
4578 list = new ArrayList ();
4581 #endregion // Public Constructor
4583 #region Public Properties
4586 get { return list.Count; }
4589 public bool IsReadOnly {
4590 get { return false; }
4593 public virtual ColumnHeader this [int index] {
4595 if (index < 0 || index >= list.Count)
4596 throw new ArgumentOutOfRangeException ("index");
4597 return (ColumnHeader) list [index];
4602 public virtual ColumnHeader this [string key] {
4604 int idx = IndexOfKey (key);
4608 return (ColumnHeader) list [idx];
4613 bool ICollection.IsSynchronized {
4614 get { return true; }
4617 object ICollection.SyncRoot {
4618 get { return this; }
4621 bool IList.IsFixedSize {
4622 get { return list.IsFixedSize; }
4625 object IList.this [int index] {
4626 get { return this [index]; }
4627 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4629 #endregion // Public Properties
4631 #region Public Methods
4632 public virtual int Add (ColumnHeader value)
4634 int idx = list.Add (value);
4635 owner.AddColumn (value, idx, true);
4640 public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
4644 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4647 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4648 this.Add (colHeader);
4653 public virtual ColumnHeader Add (string text)
4655 return Add (String.Empty, text);
4658 public virtual ColumnHeader Add (string text, int width)
4660 return Add (String.Empty, text, width);
4663 public virtual ColumnHeader Add (string key, string text)
4665 ColumnHeader colHeader = new ColumnHeader ();
4666 colHeader.Name = key;
4667 colHeader.Text = text;
4672 public virtual ColumnHeader Add (string key, string text, int width)
4674 return Add (key, text, width, HorizontalAlignment.Left, -1);
4677 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4679 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4680 colHeader.ImageIndex = imageIndex;
4685 public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4687 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4688 colHeader.ImageKey = imageKey;
4694 public virtual void AddRange (ColumnHeader [] values)
4696 foreach (ColumnHeader colHeader in values) {
4697 int idx = list.Add (colHeader);
4698 owner.AddColumn (colHeader, idx, false);
4701 owner.Redraw (true);
4704 public virtual void Clear ()
4706 foreach (ColumnHeader col in list)
4707 col.SetListView (null);
4709 owner.ReorderColumns (new int [0], true);
4712 public bool Contains (ColumnHeader value)
4714 return list.Contains (value);
4718 public virtual bool ContainsKey (string key)
4720 return IndexOfKey (key) != -1;
4724 public IEnumerator GetEnumerator ()
4726 return list.GetEnumerator ();
4729 void ICollection.CopyTo (Array dest, int index)
4731 list.CopyTo (dest, index);
4734 int IList.Add (object value)
4736 if (! (value is ColumnHeader)) {
4737 throw new ArgumentException ("Not of type ColumnHeader", "value");
4740 return this.Add ((ColumnHeader) value);
4743 bool IList.Contains (object value)
4745 if (! (value is ColumnHeader)) {
4746 throw new ArgumentException ("Not of type ColumnHeader", "value");
4749 return this.Contains ((ColumnHeader) value);
4752 int IList.IndexOf (object value)
4754 if (! (value is ColumnHeader)) {
4755 throw new ArgumentException ("Not of type ColumnHeader", "value");
4758 return this.IndexOf ((ColumnHeader) value);
4761 void IList.Insert (int index, object value)
4763 if (! (value is ColumnHeader)) {
4764 throw new ArgumentException ("Not of type ColumnHeader", "value");
4767 this.Insert (index, (ColumnHeader) value);
4770 void IList.Remove (object value)
4772 if (! (value is ColumnHeader)) {
4773 throw new ArgumentException ("Not of type ColumnHeader", "value");
4776 this.Remove ((ColumnHeader) value);
4779 public int IndexOf (ColumnHeader value)
4781 return list.IndexOf (value);
4785 public virtual int IndexOfKey (string key)
4787 if (key == null || key.Length == 0)
4790 for (int i = 0; i < list.Count; i++) {
4791 ColumnHeader col = (ColumnHeader) list [i];
4792 if (String.Compare (key, col.Name, true) == 0)
4800 public void Insert (int index, ColumnHeader value)
4802 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4803 // but it's really only greater.
4804 if (index < 0 || index > list.Count)
4805 throw new ArgumentOutOfRangeException ("index");
4807 list.Insert (index, value);
4808 owner.AddColumn (value, index, true);
4812 public void Insert (int index, string text)
4814 Insert (index, String.Empty, text);
4817 public void Insert (int index, string text, int width)
4819 Insert (index, String.Empty, text, width);
4822 public void Insert (int index, string key, string text)
4824 ColumnHeader colHeader = new ColumnHeader ();
4825 colHeader.Name = key;
4826 colHeader.Text = text;
4827 Insert (index, colHeader);
4830 public void Insert (int index, string key, string text, int width)
4832 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4833 Insert (index, colHeader);
4836 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4838 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4839 colHeader.ImageIndex = imageIndex;
4840 Insert (index, colHeader);
4843 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4845 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4846 colHeader.ImageKey = imageKey;
4847 Insert (index, colHeader);
4852 public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
4856 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4859 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4860 this.Insert (index, colHeader);
4863 public virtual void Remove (ColumnHeader column)
4865 if (!Contains (column))
4868 list.Remove (column);
4869 column.SetListView (null);
4871 int rem_display_index = column.InternalDisplayIndex;
4872 int [] display_indices = new int [list.Count];
4873 for (int i = 0; i < display_indices.Length; i++) {
4874 ColumnHeader col = (ColumnHeader) list [i];
4875 int display_index = col.InternalDisplayIndex;
4876 if (display_index < rem_display_index) {
4877 display_indices [i] = display_index;
4879 display_indices [i] = (display_index - 1);
4883 column.InternalDisplayIndex = -1;
4884 owner.ReorderColumns (display_indices, true);
4888 public virtual void RemoveByKey (string key)
4890 int idx = IndexOfKey (key);
4896 public virtual void RemoveAt (int index)
4898 if (index < 0 || index >= list.Count)
4899 throw new ArgumentOutOfRangeException ("index");
4901 ColumnHeader col = (ColumnHeader) list [index];
4904 #endregion // Public Methods
4907 } // ColumnHeaderCollection
4910 [ListBindable (false)]
4912 public class ListViewItemCollection : IList, ICollection, IEnumerable
4914 private readonly ArrayList list;
4915 private ListView owner;
4917 private ListViewGroup group;
4920 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4921 // In the later case ListViewItem.ListView never gets modified
4922 private bool is_main_collection = true;
4924 #region Public Constructor
4925 public ListViewItemCollection (ListView owner)
4927 list = new ArrayList (0);
4930 #endregion // Public Constructor
4933 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4936 is_main_collection = false;
4940 #region Public Properties
4945 if (owner != null && owner.VirtualMode)
4946 return owner.VirtualListSize;
4953 public bool IsReadOnly {
4954 get { return false; }
4958 public virtual ListViewItem this [int index] {
4960 public virtual ListViewItem this [int displayIndex] {
4964 int index = displayIndex;
4967 if (index < 0 || index >= Count)
4968 throw new ArgumentOutOfRangeException ("index");
4971 if (owner != null && owner.VirtualMode)
4972 return RetrieveVirtualItemFromOwner (index);
4974 return (ListViewItem) list [index];
4979 int index = displayIndex;
4982 if (index < 0 || index >= Count)
4983 throw new ArgumentOutOfRangeException ("index");
4986 if (owner != null && owner.VirtualMode)
4987 throw new InvalidOperationException ();
4990 if (list.Contains (value))
4991 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4993 if (value.ListView != null && value.ListView != owner)
4994 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");
4996 if (is_main_collection)
4997 value.Owner = owner;
5000 if (value.Group != null)
5001 value.Group.Items.Remove (value);
5003 value.SetGroup (group);
5007 list [index] = value;
5008 CollectionChanged (true);
5013 public virtual ListViewItem this [string key] {
5015 int idx = IndexOfKey (key);
5024 bool ICollection.IsSynchronized {
5025 get { return true; }
5028 object ICollection.SyncRoot {
5029 get { return this; }
5032 bool IList.IsFixedSize {
5033 get { return list.IsFixedSize; }
5036 object IList.this [int index] {
5037 get { return this [index]; }
5039 if (value is ListViewItem)
5040 this [index] = (ListViewItem) value;
5042 this [index] = new ListViewItem (value.ToString ());
5046 #endregion // Public Properties
5048 #region Public Methods
5049 public virtual ListViewItem Add (ListViewItem value)
5052 if (owner != null && owner.VirtualMode)
5053 throw new InvalidOperationException ();
5058 // Item is ignored until it has been added to the ListView
5059 if (is_main_collection || value.ListView != null)
5060 CollectionChanged (true);
5065 public virtual ListViewItem Add (string text)
5067 ListViewItem item = new ListViewItem (text);
5068 return this.Add (item);
5071 public virtual ListViewItem Add (string text, int imageIndex)
5073 ListViewItem item = new ListViewItem (text, imageIndex);
5074 return this.Add (item);
5078 public virtual ListViewItem Add (string text, string imageKey)
5080 ListViewItem item = new ListViewItem (text, imageKey);
5081 return this.Add (item);
5084 public virtual ListViewItem Add (string key, string text, int imageIndex)
5086 ListViewItem item = new ListViewItem (text, imageIndex);
5088 return this.Add (item);
5091 public virtual ListViewItem Add (string key, string text, string imageKey)
5093 ListViewItem item = new ListViewItem (text, imageKey);
5095 return this.Add (item);
5100 public void AddRange (ListViewItem [] items)
5103 public void AddRange (ListViewItem [] values)
5105 ListViewItem [] items = values;
5108 throw new ArgumentNullException ("Argument cannot be null!", "items");
5110 if (owner != null && owner.VirtualMode)
5111 throw new InvalidOperationException ();
5114 owner.BeginUpdate ();
5116 foreach (ListViewItem item in items)
5121 CollectionChanged (true);
5125 public void AddRange (ListViewItemCollection items)
5128 throw new ArgumentNullException ("Argument cannot be null!", "items");
5130 ListViewItem[] itemArray = new ListViewItem[items.Count];
5131 items.CopyTo (itemArray,0);
5132 this.AddRange (itemArray);
5136 public virtual void Clear ()
5139 if (owner != null && owner.VirtualMode)
5140 throw new InvalidOperationException ();
5142 if (is_main_collection && owner != null) {
5143 owner.SetFocusedItem (-1);
5144 owner.h_scroll.Value = owner.v_scroll.Value = 0;
5146 foreach (ListViewItem item in list) {
5147 owner.item_control.CancelEdit (item);
5154 foreach (ListViewItem item in list)
5155 item.SetGroup (null);
5159 CollectionChanged (false);
5162 public bool Contains (ListViewItem item)
5164 return IndexOf (item) != -1;
5168 public virtual bool ContainsKey (string key)
5170 return IndexOfKey (key) != -1;
5174 public void CopyTo (Array dest, int index)
5176 list.CopyTo (dest, index);
5180 public ListViewItem [] Find (string key, bool searchAllSubItems)
5183 return new ListViewItem [0];
5185 List<ListViewItem> temp_list = new List<ListViewItem> ();
5187 for (int i = 0; i < list.Count; i++) {
5188 ListViewItem lvi = (ListViewItem) list [i];
5189 if (String.Compare (key, lvi.Name, true) == 0)
5190 temp_list.Add (lvi);
5193 ListViewItem [] retval = new ListViewItem [temp_list.Count];
5194 temp_list.CopyTo (retval);
5200 public IEnumerator GetEnumerator ()
5203 if (owner != null && owner.VirtualMode)
5204 throw new InvalidOperationException ();
5207 return list.GetEnumerator ();
5210 int IList.Add (object item)
5216 if (owner != null && owner.VirtualMode)
5217 throw new InvalidOperationException ();
5220 if (item is ListViewItem) {
5221 li = (ListViewItem) item;
5222 if (list.Contains (li))
5223 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5225 if (li.ListView != null && li.ListView != owner)
5226 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");
5229 li = new ListViewItem (item.ToString ());
5234 result = list.Add (li);
5235 CollectionChanged (true);
5240 bool IList.Contains (object item)
5242 return Contains ((ListViewItem) item);
5245 int IList.IndexOf (object item)
5247 return IndexOf ((ListViewItem) item);
5250 void IList.Insert (int index, object item)
5252 if (item is ListViewItem)
5253 this.Insert (index, (ListViewItem) item);
5255 this.Insert (index, item.ToString ());
5258 void IList.Remove (object item)
5260 Remove ((ListViewItem) item);
5263 public int IndexOf (ListViewItem item)
5266 if (owner != null && owner.VirtualMode) {
5267 for (int i = 0; i < Count; i++)
5268 if (RetrieveVirtualItemFromOwner (i) == item)
5275 return list.IndexOf (item);
5279 public virtual int IndexOfKey (string key)
5281 if (key == null || key.Length == 0)
5284 for (int i = 0; i < Count; i++) {
5285 ListViewItem lvi = this [i];
5286 if (String.Compare (key, lvi.Name, true) == 0)
5294 public ListViewItem Insert (int index, ListViewItem item)
5296 if (index < 0 || index > list.Count)
5297 throw new ArgumentOutOfRangeException ("index");
5300 if (owner != null && owner.VirtualMode)
5301 throw new InvalidOperationException ();
5304 if (list.Contains (item))
5305 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5307 if (item.ListView != null && item.ListView != owner)
5308 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");
5310 if (is_main_collection)
5314 if (item.Group != null)
5315 item.Group.Items.Remove (item);
5317 item.SetGroup (group);
5321 list.Insert (index, item);
5323 if (is_main_collection || item.ListView != null)
5324 CollectionChanged (true);
5329 public ListViewItem Insert (int index, string text)
5331 return this.Insert (index, new ListViewItem (text));
5334 public ListViewItem Insert (int index, string text, int imageIndex)
5336 return this.Insert (index, new ListViewItem (text, imageIndex));
5340 public ListViewItem Insert (int index, string text, string imageKey)
5342 ListViewItem lvi = new ListViewItem (text, imageKey);
5343 return Insert (index, lvi);
5346 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5348 ListViewItem lvi = new ListViewItem (text, imageIndex);
5350 return Insert (index, lvi);
5353 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5355 ListViewItem lvi = new ListViewItem (text, imageKey);
5357 return Insert (index, lvi);
5361 public virtual void Remove (ListViewItem item)
5364 if (owner != null && owner.VirtualMode)
5365 throw new InvalidOperationException ();
5368 int idx = list.IndexOf (item);
5373 public virtual void RemoveAt (int index)
5375 if (index < 0 || index >= Count)
5376 throw new ArgumentOutOfRangeException ("index");
5379 if (owner != null && owner.VirtualMode)
5380 throw new InvalidOperationException ();
5383 ListViewItem item = (ListViewItem) list [index];
5385 bool selection_changed = false;
5386 if (is_main_collection && owner != null) {
5388 int display_index = item.DisplayIndex;
5389 if (item.Focused && display_index + 1 == Count) // Last item
5390 owner.SetFocusedItem (display_index == 0 ? -1 : display_index - 1);
5392 selection_changed = owner.SelectedIndices.Contains (index);
5393 owner.item_control.CancelEdit (item);
5396 list.RemoveAt (index);
5398 if (is_main_collection)
5402 item.SetGroup (null);
5405 CollectionChanged (false);
5406 if (selection_changed && owner != null)
5407 owner.OnSelectedIndexChanged (EventArgs.Empty);
5411 public virtual void RemoveByKey (string key)
5413 int idx = IndexOfKey (key);
5419 #endregion // Public Methods
5421 internal ListView Owner {
5431 internal ListViewGroup Group {
5441 void AddItem (ListViewItem value)
5443 if (list.Contains (value))
5444 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5446 if (value.ListView != null && value.ListView != owner)
5447 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");
5448 if (is_main_collection)
5449 value.Owner = owner;
5452 if (value.Group != null)
5453 value.Group.Items.Remove (value);
5455 value.SetGroup (group);
5462 void CollectionChanged (bool sort)
5464 if (owner != null) {
5469 owner.Redraw (true);
5474 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5476 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5478 owner.OnRetrieveVirtualItem (args);
5479 ListViewItem retval = args.Item;
5480 retval.Owner = owner;
5481 retval.DisplayIndex = displayIndex;
5487 internal event CollectionChangedHandler Changed;
5489 internal void Sort (IComparer comparer)
5491 list.Sort (comparer);
5495 internal void OnChange ()
5497 if (Changed != null)
5500 } // ListViewItemCollection
5503 // In normal mode, the selection information resides in the Items,
5504 // making SelectedIndexCollection.List read-only
5506 // In virtual mode, SelectedIndexCollection directly saves the selection
5507 // information, instead of getting it from Items, making List read-and-write
5509 [ListBindable (false)]
5511 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5513 private readonly ListView owner;
5514 private ArrayList list;
5516 #region Public Constructor
5517 public SelectedIndexCollection (ListView owner)
5520 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5522 #endregion // Public Constructor
5524 #region Public Properties
5528 if (!owner.IsHandleCreated)
5535 public bool IsReadOnly {
5545 public int this [int index] {
5547 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5548 throw new ArgumentOutOfRangeException ("index");
5550 return (int) List [index];
5554 bool ICollection.IsSynchronized {
5555 get { return false; }
5558 object ICollection.SyncRoot {
5559 get { return this; }
5562 bool IList.IsFixedSize {
5572 object IList.this [int index] {
5573 get { return this [index]; }
5574 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5576 #endregion // Public Properties
5578 #region Public Methods
5580 public int Add (int itemIndex)
5582 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5583 throw new ArgumentOutOfRangeException ("index");
5585 if (owner.virtual_mode && !owner.IsHandleCreated)
5588 owner.Items [itemIndex].Selected = true;
5590 if (!owner.IsHandleCreated)
5604 if (!owner.IsHandleCreated)
5607 int [] indexes = (int []) List.ToArray (typeof (int));
5608 foreach (int index in indexes)
5609 owner.Items [index].Selected = false;
5612 public bool Contains (int selectedIndex)
5614 return IndexOf (selectedIndex) != -1;
5617 public void CopyTo (Array dest, int index)
5619 List.CopyTo (dest, index);
5622 public IEnumerator GetEnumerator ()
5624 return List.GetEnumerator ();
5627 int IList.Add (object value)
5629 throw new NotSupportedException ("Add operation is not supported.");
5637 bool IList.Contains (object selectedIndex)
5639 if (!(selectedIndex is int))
5641 return Contains ((int) selectedIndex);
5644 int IList.IndexOf (object selectedIndex)
5646 if (!(selectedIndex is int))
5648 return IndexOf ((int) selectedIndex);
5651 void IList.Insert (int index, object value)
5653 throw new NotSupportedException ("Insert operation is not supported.");
5656 void IList.Remove (object value)
5658 throw new NotSupportedException ("Remove operation is not supported.");
5661 void IList.RemoveAt (int index)
5663 throw new NotSupportedException ("RemoveAt operation is not supported.");
5666 public int IndexOf (int selectedIndex)
5668 if (!owner.IsHandleCreated)
5671 return List.IndexOf (selectedIndex);
5675 public void Remove (int itemIndex)
5677 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5678 throw new ArgumentOutOfRangeException ("itemIndex");
5680 owner.Items [itemIndex].Selected = false;
5683 #endregion // Public Methods
5685 internal ArrayList List {
5688 list = new ArrayList ();
5690 if (!owner.VirtualMode)
5692 for (int i = 0; i < owner.Items.Count; i++) {
5693 if (owner.Items [i].Selected)
5701 internal void Reset ()
5703 // force re-population of list
5707 private void ItemsCollection_Changed ()
5713 internal void RemoveIndex (int index)
5715 int idx = List.BinarySearch (index);
5717 List.RemoveAt (idx);
5720 // actually store index in the collection
5721 // also, keep the collection sorted, as .Net does
5722 internal void InsertIndex (int index)
5725 int iMax = List.Count - 1;
5726 while (iMin <= iMax) {
5727 int iMid = (iMin + iMax) / 2;
5728 int current_index = (int) List [iMid];
5730 if (current_index == index)
5731 return; // Already added
5732 if (current_index > index)
5738 List.Insert (iMin, index);
5742 } // SelectedIndexCollection
5745 [ListBindable (false)]
5747 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5749 private readonly ListView owner;
5751 #region Public Constructor
5752 public SelectedListViewItemCollection (ListView owner)
5756 #endregion // Public Constructor
5758 #region Public Properties
5762 return owner.SelectedIndices.Count;
5766 public bool IsReadOnly {
5767 get { return true; }
5770 public ListViewItem this [int index] {
5772 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5773 throw new ArgumentOutOfRangeException ("index");
5775 int item_index = owner.SelectedIndices [index];
5776 return owner.Items [item_index];
5781 public virtual ListViewItem this [string key] {
5783 int idx = IndexOfKey (key);
5792 bool ICollection.IsSynchronized {
5793 get { return false; }
5796 object ICollection.SyncRoot {
5797 get { return this; }
5800 bool IList.IsFixedSize {
5801 get { return true; }
5804 object IList.this [int index] {
5805 get { return this [index]; }
5806 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5808 #endregion // Public Properties
5810 #region Public Methods
5811 public void Clear ()
5813 owner.SelectedIndices.Clear ();
5816 public bool Contains (ListViewItem item)
5818 return IndexOf (item) != -1;
5822 public virtual bool ContainsKey (string key)
5824 return IndexOfKey (key) != -1;
5828 public void CopyTo (Array dest, int index)
5830 if (!owner.IsHandleCreated)
5832 if (index > Count) // Throws ArgumentException instead of IOOR exception
5833 throw new ArgumentException ("index");
5835 for (int i = 0; i < Count; i++)
5836 dest.SetValue (this [i], index++);
5839 public IEnumerator GetEnumerator ()
5841 if (!owner.IsHandleCreated)
5842 return (new ListViewItem [0]).GetEnumerator ();
5844 ListViewItem [] items = new ListViewItem [Count];
5845 for (int i = 0; i < Count; i++)
5846 items [i] = this [i];
5848 return items.GetEnumerator ();
5851 int IList.Add (object value)
5853 throw new NotSupportedException ("Add operation is not supported.");
5856 bool IList.Contains (object item)
5858 if (!(item is ListViewItem))
5860 return Contains ((ListViewItem) item);
5863 int IList.IndexOf (object item)
5865 if (!(item is ListViewItem))
5867 return IndexOf ((ListViewItem) item);
5870 void IList.Insert (int index, object value)
5872 throw new NotSupportedException ("Insert operation is not supported.");
5875 void IList.Remove (object value)
5877 throw new NotSupportedException ("Remove operation is not supported.");
5880 void IList.RemoveAt (int index)
5882 throw new NotSupportedException ("RemoveAt operation is not supported.");
5885 public int IndexOf (ListViewItem item)
5887 if (!owner.IsHandleCreated)
5890 for (int i = 0; i < Count; i++)
5891 if (this [i] == item)
5898 public virtual int IndexOfKey (string key)
5900 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5903 for (int i = 0; i < Count; i++) {
5904 ListViewItem item = this [i];
5905 if (String.Compare (item.Name, key, true) == 0)
5912 #endregion // Public Methods
5914 } // SelectedListViewItemCollection
5916 internal delegate void CollectionChangedHandler ();
5918 struct ItemMatrixLocation
5923 public ItemMatrixLocation (int row, int col)
5950 #endregion // Subclasses
5952 protected override void OnResize (EventArgs e)
5957 protected override void OnMouseLeave (EventArgs e)
5959 base.OnMouseLeave (e);
5963 // ColumnReorder event
5965 static object ColumnReorderedEvent = new object ();
5966 public event ColumnReorderedEventHandler ColumnReordered {
5967 add { Events.AddHandler (ColumnReorderedEvent, value); }
5968 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5971 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5973 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5980 // ColumnWidthChanged
5982 static object ColumnWidthChangedEvent = new object ();
5983 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5984 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5985 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5988 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5990 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5995 void RaiseColumnWidthChanged (int resize_column)
5997 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5999 OnColumnWidthChanged (n);
6003 // ColumnWidthChanging
6005 static object ColumnWidthChangingEvent = new object ();
6006 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
6007 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
6008 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
6011 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
6013 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
6019 // 2.0 profile based implementation
6021 bool CanProceedWithResize (ColumnHeader col, int width)
6023 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
6027 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
6028 cwceh (this, changing);
6029 return !changing.Cancel;
6033 // 1.0 profile based implementation
6035 bool CanProceedWithResize (ColumnHeader col, int width)
6040 void RaiseColumnWidthChanged (int resize_column)