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 hot_item_index = -1;
106 private bool hot_tracking;
107 private ListViewInsertionMark insertion_mark;
108 private bool show_item_tooltips;
109 private ToolTip item_tooltip;
110 private Size tile_size;
111 private bool virtual_mode;
112 private int virtual_list_size;
113 private bool right_to_left_layout;
116 // internal variables
117 internal ImageList large_image_list;
118 internal ImageList small_image_list;
119 internal Size text_size = Size.Empty;
122 static object AfterLabelEditEvent = new object ();
123 static object BeforeLabelEditEvent = new object ();
124 static object ColumnClickEvent = new object ();
125 static object ItemActivateEvent = new object ();
126 static object ItemCheckEvent = new object ();
127 static object ItemDragEvent = new object ();
128 static object SelectedIndexChangedEvent = new object ();
130 static object DrawColumnHeaderEvent = new object();
131 static object DrawItemEvent = new object();
132 static object DrawSubItemEvent = new object();
133 static object ItemCheckedEvent = new object ();
134 static object ItemMouseHoverEvent = new object ();
135 static object ItemSelectionChangedEvent = new object ();
136 static object CacheVirtualItemsEvent = new object ();
137 static object RetrieveVirtualItemEvent = new object ();
138 static object RightToLeftLayoutChangedEvent = new object ();
139 static object SearchForVirtualItemEvent = new object ();
140 static object VirtualItemsSelectionRangeChangedEvent = new object ();
143 public event LabelEditEventHandler AfterLabelEdit {
144 add { Events.AddHandler (AfterLabelEditEvent, value); }
145 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
150 [EditorBrowsable (EditorBrowsableState.Never)]
151 public new event EventHandler BackgroundImageChanged {
152 add { base.BackgroundImageChanged += value; }
153 remove { base.BackgroundImageChanged -= value; }
159 [EditorBrowsable (EditorBrowsableState.Never)]
160 public new event EventHandler BackgroundImageLayoutChanged {
161 add { base.BackgroundImageLayoutChanged += value; }
162 remove { base.BackgroundImageLayoutChanged -= value; }
166 public event LabelEditEventHandler BeforeLabelEdit {
167 add { Events.AddHandler (BeforeLabelEditEvent, value); }
168 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
171 public event ColumnClickEventHandler ColumnClick {
172 add { Events.AddHandler (ColumnClickEvent, value); }
173 remove { Events.RemoveHandler (ColumnClickEvent, value); }
177 public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
178 add { Events.AddHandler(DrawColumnHeaderEvent, value); }
179 remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
182 public event DrawListViewItemEventHandler DrawItem {
183 add { Events.AddHandler(DrawItemEvent, value); }
184 remove { Events.RemoveHandler(DrawItemEvent, value); }
187 public event DrawListViewSubItemEventHandler DrawSubItem {
188 add { Events.AddHandler(DrawSubItemEvent, value); }
189 remove { Events.RemoveHandler(DrawSubItemEvent, value); }
193 public event EventHandler ItemActivate {
194 add { Events.AddHandler (ItemActivateEvent, value); }
195 remove { Events.RemoveHandler (ItemActivateEvent, value); }
198 public event ItemCheckEventHandler ItemCheck {
199 add { Events.AddHandler (ItemCheckEvent, value); }
200 remove { Events.RemoveHandler (ItemCheckEvent, value); }
204 public event ItemCheckedEventHandler ItemChecked {
205 add { Events.AddHandler (ItemCheckedEvent, value); }
206 remove { Events.RemoveHandler (ItemCheckedEvent, value); }
210 public event ItemDragEventHandler ItemDrag {
211 add { Events.AddHandler (ItemDragEvent, value); }
212 remove { Events.RemoveHandler (ItemDragEvent, value); }
216 public event ListViewItemMouseHoverEventHandler ItemMouseHover {
217 add { Events.AddHandler (ItemMouseHoverEvent, value); }
218 remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
221 public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
222 add { Events.AddHandler (ItemSelectionChangedEvent, value); }
223 remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
227 [EditorBrowsable (EditorBrowsableState.Never)]
228 public new event EventHandler PaddingChanged {
229 add { base.PaddingChanged += value; }
230 remove { base.PaddingChanged -= value; }
235 [EditorBrowsable (EditorBrowsableState.Never)]
236 public new event PaintEventHandler Paint {
237 add { base.Paint += value; }
238 remove { base.Paint -= value; }
241 public event EventHandler SelectedIndexChanged {
242 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
243 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
247 [EditorBrowsable (EditorBrowsableState.Never)]
248 public new event EventHandler TextChanged {
249 add { base.TextChanged += value; }
250 remove { base.TextChanged -= value; }
254 public event CacheVirtualItemsEventHandler CacheVirtualItems {
255 add { Events.AddHandler (CacheVirtualItemsEvent, value); }
256 remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
259 public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
260 add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
261 remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
264 public event EventHandler RightToLeftLayoutChanged {
265 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
266 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
269 public event SearchForVirtualItemEventHandler SearchForVirtualItem {
270 add { Events.AddHandler (SearchForVirtualItemEvent, value); }
271 remove { Events.AddHandler (SearchForVirtualItemEvent, value); }
274 public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged {
275 add { Events.AddHandler (VirtualItemsSelectionRangeChangedEvent, value); }
276 remove { Events.RemoveHandler (VirtualItemsSelectionRangeChangedEvent, value); }
282 #region Public Constructors
285 background_color = ThemeEngine.Current.ColorWindow;
287 groups = new ListViewGroupCollection (this);
289 items = new ListViewItemCollection (this);
290 checked_indices = new CheckedIndexCollection (this);
291 checked_items = new CheckedListViewItemCollection (this);
292 columns = new ColumnHeaderCollection (this);
293 foreground_color = SystemColors.WindowText;
294 selected_indices = new SelectedIndexCollection (this);
295 selected_items = new SelectedListViewItemCollection (this);
296 items_location = new Point [16];
297 items_matrix_location = new ItemMatrixLocation [16];
298 reordered_items_indices = new int [16];
300 item_tooltip = new ToolTip ();
301 item_tooltip.Active = false;
302 insertion_mark = new ListViewInsertionMark (this);
305 InternalBorderStyle = BorderStyle.Fixed3D;
307 header_control = new HeaderControl (this);
308 header_control.Visible = false;
309 Controls.AddImplicit (header_control);
311 item_control = new ItemControl (this);
312 Controls.AddImplicit (item_control);
314 h_scroll = new ImplicitHScrollBar ();
315 Controls.AddImplicit (this.h_scroll);
317 v_scroll = new ImplicitVScrollBar ();
318 Controls.AddImplicit (this.v_scroll);
320 h_marker = v_marker = 0;
321 keysearch_tickcnt = 0;
323 // scroll bars are disabled initially
324 h_scroll.Visible = false;
325 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
326 v_scroll.Visible = false;
327 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
330 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
331 SizeChanged += new EventHandler (ListView_SizeChanged);
332 GotFocus += new EventHandler (FocusChanged);
333 LostFocus += new EventHandler (FocusChanged);
334 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
335 MouseEnter += new EventHandler (ListView_MouseEnter);
338 BackgroundImageTiled = false;
341 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
343 | ControlStyles.UseTextForAccessibility
347 #endregion // Public Constructors
349 #region Private Internal Properties
350 internal Size CheckBoxSize {
352 if (this.check_boxes) {
353 if (this.state_image_list != null)
354 return this.state_image_list.ImageSize;
356 return ThemeEngine.Current.ListViewCheckBoxSize;
362 internal Size ItemSize {
364 if (view != View.Details)
367 Size size = new Size ();
368 size.Height = item_size.Height;
369 for (int i = 0; i < columns.Count; i++)
370 size.Width += columns [i].Wd;
379 internal int HotItemIndex {
381 return hot_item_index;
384 hot_item_index = value;
388 #endregion // Private Internal Properties
390 #region Protected Properties
391 protected override CreateParams CreateParams {
392 get { return base.CreateParams; }
395 protected override Size DefaultSize {
396 get { return ThemeEngine.Current.ListViewDefaultSize; }
399 protected override bool DoubleBuffered {
401 return base.DoubleBuffered;
404 base.DoubleBuffered = value;
408 #endregion // Protected Properties
410 #region Public Instance Properties
411 [DefaultValue (ItemActivation.Standard)]
412 public ItemActivation Activation {
413 get { return activation; }
415 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
416 value != ItemActivation.TwoClick) {
417 throw new InvalidEnumArgumentException (string.Format
418 ("Enum argument value '{0}' is not valid for Activation", value));
421 if (hot_tracking && value != ItemActivation.OneClick)
422 throw new ArgumentException ("When HotTracking is on, activation must be ItemActivation.OneClick");
429 [DefaultValue (ListViewAlignment.Top)]
431 public ListViewAlignment Alignment {
432 get { return alignment; }
434 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
435 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
436 throw new InvalidEnumArgumentException (string.Format
437 ("Enum argument value '{0}' is not valid for Alignment", value));
440 if (this.alignment != value) {
442 // alignment does not matter in Details/List views
443 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
449 [DefaultValue (false)]
450 public bool AllowColumnReorder {
451 get { return allow_column_reorder; }
452 set { allow_column_reorder = value; }
455 [DefaultValue (true)]
456 public bool AutoArrange {
457 get { return auto_arrange; }
459 if (auto_arrange != value) {
460 auto_arrange = value;
461 // autoarrange does not matter in Details/List views
462 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
468 public override Color BackColor {
470 if (background_color.IsEmpty)
471 return ThemeEngine.Current.ColorWindow;
473 return background_color;
476 background_color = value;
477 item_control.BackColor = value;
483 [EditorBrowsable (EditorBrowsableState.Never)]
484 public override Image BackgroundImage {
485 get { return base.BackgroundImage; }
486 set { base.BackgroundImage = value; }
492 [EditorBrowsable (EditorBrowsableState.Never)]
493 public override ImageLayout BackgroundImageLayout {
495 return base.BackgroundImageLayout;
498 base.BackgroundImageLayout = value;
502 [DefaultValue (false)]
503 public bool BackgroundImageTiled {
505 return item_control.BackgroundImageLayout == ImageLayout.Tile;
508 ImageLayout new_image_layout = value ? ImageLayout.Tile : ImageLayout.None;
509 if (new_image_layout == item_control.BackgroundImageLayout)
512 item_control.BackgroundImageLayout = new_image_layout;
517 [DefaultValue (BorderStyle.Fixed3D)]
519 public BorderStyle BorderStyle {
520 get { return InternalBorderStyle; }
521 set { InternalBorderStyle = value; }
524 [DefaultValue (false)]
525 public bool CheckBoxes {
526 get { return check_boxes; }
528 if (check_boxes != value) {
530 if (value && View == View.Tile)
531 throw new NotSupportedException ("CheckBoxes are not"
532 + " supported in Tile view. Choose a different"
533 + " view or set CheckBoxes to false.");
543 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
544 public CheckedIndexCollection CheckedIndices {
545 get { return checked_indices; }
549 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
550 public CheckedListViewItemCollection CheckedItems {
551 get { return checked_items; }
555 [Editor ("System.Windows.Forms.Design.ColumnHeaderCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
557 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
559 [MergableProperty (false)]
560 public ColumnHeaderCollection Columns {
561 get { return columns; }
565 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
566 public ListViewItem FocusedItem {
568 if (focused_item_index == -1)
571 return items [focused_item_index];
575 if (value == null || value.ListView != this ||
579 SetFocusedItem (value.Index);
584 public override Color ForeColor {
586 if (foreground_color.IsEmpty)
587 return ThemeEngine.Current.ColorWindowText;
589 return foreground_color;
591 set { foreground_color = value; }
594 [DefaultValue (false)]
595 public bool FullRowSelect {
596 get { return full_row_select; }
598 if (full_row_select != value) {
599 full_row_select = value;
600 InvalidateSelection ();
605 [DefaultValue (false)]
606 public bool GridLines {
607 get { return grid_lines; }
609 if (grid_lines != value) {
616 [DefaultValue (ColumnHeaderStyle.Clickable)]
617 public ColumnHeaderStyle HeaderStyle {
618 get { return header_style; }
620 if (header_style == value)
624 case ColumnHeaderStyle.Clickable:
625 case ColumnHeaderStyle.Nonclickable:
626 case ColumnHeaderStyle.None:
629 throw new InvalidEnumArgumentException (string.Format
630 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
633 header_style = value;
634 if (view == View.Details)
639 [DefaultValue (true)]
640 public bool HideSelection {
641 get { return hide_selection; }
643 if (hide_selection != value) {
644 hide_selection = value;
645 InvalidateSelection ();
651 [DefaultValue (false)]
652 public bool HotTracking {
657 if (hot_tracking == value)
660 hot_tracking = value;
662 hover_selection = true;
663 activation = ItemActivation.OneClick;
669 [DefaultValue (false)]
670 public bool HoverSelection {
671 get { return hover_selection; }
674 if (hot_tracking && value == false)
675 throw new ArgumentException ("When HotTracking is on, hover selection must be true");
677 hover_selection = value;
682 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
684 public ListViewInsertionMark InsertionMark {
686 return insertion_mark;
692 [Editor ("System.Windows.Forms.Design.ListViewItemCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
694 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
696 [MergableProperty (false)]
697 public ListViewItemCollection Items {
698 get { return items; }
701 [DefaultValue (false)]
702 public bool LabelEdit {
703 get { return label_edit; }
704 set { label_edit = value; }
707 [DefaultValue (true)]
709 public bool LabelWrap {
710 get { return label_wrap; }
712 if (label_wrap != value) {
719 [DefaultValue (null)]
720 public ImageList LargeImageList {
721 get { return large_image_list; }
723 large_image_list = value;
729 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
730 public IComparer ListViewItemSorter {
732 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
737 if (item_sorter != value) {
744 [DefaultValue (true)]
745 public bool MultiSelect {
746 get { return multiselect; }
747 set { multiselect = value; }
752 [DefaultValue(false)]
753 public bool OwnerDraw {
754 get { return owner_draw; }
762 [EditorBrowsable (EditorBrowsableState.Never)]
763 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
764 public new Padding Padding {
769 base.Padding = value;
773 [MonoTODO ("RTL not supported")]
775 [DefaultValue (false)]
776 public virtual bool RightToLeftLayout {
777 get { return right_to_left_layout; }
779 if (right_to_left_layout != value) {
780 right_to_left_layout = value;
781 OnRightToLeftLayoutChanged (EventArgs.Empty);
787 [DefaultValue (true)]
788 public bool Scrollable {
789 get { return scrollable; }
791 if (scrollable != value) {
799 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
800 public SelectedIndexCollection SelectedIndices {
801 get { return selected_indices; }
805 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
806 public SelectedListViewItemCollection SelectedItems {
807 get { return selected_items; }
812 public bool ShowGroups {
813 get { return show_groups; }
815 if (show_groups != value) {
822 [LocalizableAttribute (true)]
823 [MergableProperty (false)]
824 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
825 [Editor ("System.Windows.Forms.Design.ListViewGroupCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
826 public ListViewGroupCollection Groups {
827 get { return groups; }
830 [DefaultValue (false)]
831 public bool ShowItemToolTips {
833 return show_item_tooltips;
836 show_item_tooltips = value;
837 item_tooltip.Active = false;
842 [DefaultValue (null)]
843 public ImageList SmallImageList {
844 get { return small_image_list; }
846 small_image_list = value;
851 [DefaultValue (SortOrder.None)]
852 public SortOrder Sorting {
853 get { return sort_order; }
855 if (!Enum.IsDefined (typeof (SortOrder), value)) {
856 throw new InvalidEnumArgumentException ("value", (int) value,
860 if (sort_order == value)
866 if (virtual_mode) // Sorting is not allowed in virtual mode
870 if (value == SortOrder.None) {
871 if (item_sorter != null) {
872 // ListViewItemSorter should never be reset for SmallIcon
873 // and LargeIcon view
874 if (View != View.SmallIcon && View != View.LargeIcon)
878 // in .NET 1.1, only internal IComparer would be
880 if (item_sorter is ItemComparer)
886 if (item_sorter == null)
887 item_sorter = new ItemComparer (value);
888 if (item_sorter is ItemComparer) {
890 item_sorter = new ItemComparer (value);
892 // in .NET 1.1, the sort order is not updated for
893 // SmallIcon and LargeIcon views if no custom IComparer
895 if (View != View.SmallIcon && View != View.LargeIcon)
896 item_sorter = new ItemComparer (value);
904 private void OnImageListChanged (object sender, EventArgs args)
906 item_control.Invalidate ();
909 [DefaultValue (null)]
910 public ImageList StateImageList {
911 get { return state_image_list; }
913 if (state_image_list == value)
916 if (state_image_list != null)
917 state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
919 state_image_list = value;
921 if (state_image_list != null)
922 state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
930 [EditorBrowsable (EditorBrowsableState.Never)]
931 public override string Text {
932 get { return base.Text; }
934 if (value == base.Text)
944 public Size TileSize {
949 if (value.Width <= 0 || value.Height <= 0)
950 throw new ArgumentOutOfRangeException ("value");
959 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
960 public ListViewItem TopItem {
963 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
964 throw new InvalidOperationException ("Cannot get the top item in LargeIcon, SmallIcon or Tile view.");
967 if (this.items.Count == 0)
969 // if contents are not scrolled
970 // it is the first item
971 else if (h_marker == 0 && v_marker == 0)
972 return this.items [0];
973 // do a hit test for the scrolled position
975 for (int i = 0; i < items.Count; i++) {
976 Point item_loc = GetItemLocation (i);
977 if (item_loc.X >= 0 && item_loc.Y >= 0)
985 if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
986 throw new InvalidOperationException ("Cannot set the top item in LargeIcon, SmallIcon or Tile view.");
988 // .Net doesn't throw any exception in the cases below
989 if (value == null || value.ListView != this)
992 EnsureVisible (value.Index);
998 [EditorBrowsable (EditorBrowsableState.Advanced)]
999 [DefaultValue (true)]
1001 [MonoInternalNote ("Stub, not implemented")]
1002 public bool UseCompatibleStateImageBehavior {
1011 [DefaultValue (View.LargeIcon)]
1013 get { return view; }
1015 if (!Enum.IsDefined (typeof (View), value))
1016 throw new InvalidEnumArgumentException ("value", (int) value,
1019 if (view != value) {
1021 if (CheckBoxes && value == View.Tile)
1022 throw new NotSupportedException ("CheckBoxes are not"
1023 + " supported in Tile view. Choose a different"
1024 + " view or set CheckBoxes to false.");
1027 h_scroll.Value = v_scroll.Value = 0;
1035 [DefaultValue (false)]
1036 [RefreshProperties (RefreshProperties.Repaint)]
1037 public bool VirtualMode {
1039 return virtual_mode;
1042 if (virtual_mode == value)
1045 if (!virtual_mode && items.Count > 0)
1046 throw new InvalidOperationException ();
1048 virtual_mode = value;
1054 [RefreshProperties (RefreshProperties.Repaint)]
1055 public int VirtualListSize {
1057 return virtual_list_size;
1061 throw new ArgumentException ("value");
1063 if (virtual_list_size == value)
1066 virtual_list_size = value;
1072 #endregion // Public Instance Properties
1074 #region Internal Methods Properties
1076 internal int FirstVisibleIndex {
1079 if (this.items.Count == 0)
1082 if (h_marker == 0 && v_marker == 0)
1085 Size item_size = ItemSize;
1086 for (int i = 0; i < items.Count; i++) {
1087 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
1088 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
1097 internal int LastVisibleIndex {
1099 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
1100 if (View == View.List || Alignment == ListViewAlignment.Left) {
1101 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
1104 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
1109 return Items.Count - 1;
1113 internal void OnSelectedIndexChanged ()
1115 if (IsHandleCreated)
1116 OnSelectedIndexChanged (EventArgs.Empty);
1119 internal int TotalWidth {
1120 get { return Math.Max (this.Width, this.layout_wd); }
1123 internal int TotalHeight {
1124 get { return Math.Max (this.Height, this.layout_ht); }
1127 internal void Redraw (bool recalculate)
1129 // Avoid calculations when control is being updated
1133 // VirtualMode doesn't do any calculations until handle is created
1134 if (virtual_mode && !IsHandleCreated)
1140 CalculateListView (this.alignment);
1145 void InvalidateSelection ()
1147 foreach (int selected_index in SelectedIndices)
1148 items [selected_index].Invalidate ();
1151 const int text_padding = 15;
1153 internal Size GetChildColumnSize (int index)
1155 Size ret_size = Size.Empty;
1156 ColumnHeader col = this.columns [index];
1158 if (col.Width == -2) { // autosize = max(items, columnheader)
1159 Size size = Size.Ceiling (TextRenderer.MeasureString
1160 (col.Text, this.Font));
1161 size.Width += text_padding;
1162 ret_size = BiggestItem (index);
1163 if (size.Width > ret_size.Width)
1166 else { // -1 and all the values < -2 are put under one category
1167 ret_size = BiggestItem (index);
1168 // fall back to empty columns' width if no subitem is available for a column
1169 if (ret_size.IsEmpty) {
1170 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
1171 if (col.Text.Length > 0)
1172 ret_size.Height = Size.Ceiling (TextRenderer.MeasureString
1173 (col.Text, this.Font)).Height;
1175 ret_size.Height = this.Font.Height;
1179 ret_size.Height += text_padding;
1181 // adjust the size for icon and checkbox for 0th column
1183 ret_size.Width += (this.CheckBoxSize.Width + 4);
1184 if (this.small_image_list != null)
1185 ret_size.Width += this.small_image_list.ImageSize.Width;
1190 // Returns the size of biggest item text in a column
1191 // or the sum of the text and indent count if we are on 2.0
1192 private Size BiggestItem (int col)
1194 Size temp = Size.Empty;
1195 Size ret_size = Size.Empty;
1197 bool use_indent_count = small_image_list != null;
1199 // VirtualMode uses the first item text size
1200 if (virtual_mode && items.Count > 0) {
1201 ListViewItem item = items [0];
1202 ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
1205 if (use_indent_count)
1206 ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
1209 // 0th column holds the item text, we check the size of
1210 // the various subitems falling in that column and get
1211 // the biggest one's size.
1212 foreach (ListViewItem item in items) {
1213 if (col >= item.SubItems.Count)
1216 temp = Size.Ceiling (TextRenderer.MeasureString
1217 (item.SubItems [col].Text, Font));
1220 if (use_indent_count)
1221 temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
1224 if (temp.Width > ret_size.Width)
1231 // adjustment for space in Details view
1232 if (!ret_size.IsEmpty && view == View.Details)
1233 ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
1238 const int max_wrap_padding = 30;
1240 // Sets the size of the biggest item text as per the view
1241 private void CalcTextSize ()
1243 // clear the old value
1244 text_size = Size.Empty;
1246 if (items.Count == 0)
1249 text_size = BiggestItem (0);
1251 if (view == View.LargeIcon && this.label_wrap) {
1252 Size temp = Size.Empty;
1253 if (this.check_boxes)
1254 temp.Width += 2 * this.CheckBoxSize.Width;
1255 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1256 temp.Width += icon_w + max_wrap_padding;
1257 // wrapping is done for two lines only
1258 if (text_size.Width > temp.Width) {
1259 text_size.Width = temp.Width;
1260 text_size.Height *= 2;
1263 else if (view == View.List) {
1264 // in list view max text shown in determined by the
1265 // control width, even if scolling is enabled.
1266 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1267 if (this.small_image_list != null)
1268 max_wd -= this.small_image_list.ImageSize.Width;
1270 if (text_size.Width > max_wd)
1271 text_size.Width = max_wd;
1274 // we do the default settings, if we have got 0's
1275 if (text_size.Height <= 0)
1276 text_size.Height = this.Font.Height;
1277 if (text_size.Width <= 0)
1278 text_size.Width = this.Width;
1280 // little adjustment
1281 text_size.Width += 2;
1282 text_size.Height += 2;
1285 private void Scroll (ScrollBar scrollbar, int delta)
1287 if (delta == 0 || !scrollbar.Visible)
1291 if (scrollbar == h_scroll)
1292 max = h_scroll.Maximum - item_control.Width;
1294 max = v_scroll.Maximum - item_control.Height;
1296 int val = scrollbar.Value + delta;
1299 else if (val < scrollbar.Minimum)
1300 val = scrollbar.Minimum;
1301 scrollbar.Value = val;
1304 private void CalculateScrollBars ()
1306 if (!IsHandleCreated)
1309 Rectangle client_area = ClientRectangle;
1312 h_scroll.Visible = false;
1313 v_scroll.Visible = false;
1314 item_control.Height = client_area.Height;
1315 item_control.Width = client_area.Width;
1316 header_control.Width = client_area.Width;
1320 // Don't calculate if the view is not displayable
1321 if (client_area.Height < 0 || client_area.Width < 0)
1324 // making a scroll bar visible might make
1325 // other scroll bar visible
1326 if (layout_wd > client_area.Right) {
1327 h_scroll.Visible = true;
1328 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1329 v_scroll.Visible = true;
1331 v_scroll.Visible = false;
1332 } else if (layout_ht > client_area.Bottom) {
1333 v_scroll.Visible = true;
1334 if ((layout_wd + v_scroll.Width) > client_area.Right)
1335 h_scroll.Visible = true;
1337 h_scroll.Visible = false;
1339 h_scroll.Visible = false;
1340 v_scroll.Visible = false;
1343 item_control.Height = client_area.Height;
1345 if (h_scroll.is_visible) {
1346 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1347 h_scroll.Minimum = 0;
1349 // if v_scroll is visible, adjust the maximum of the
1350 // h_scroll to account for the width of v_scroll
1351 if (v_scroll.Visible) {
1352 h_scroll.Maximum = layout_wd + v_scroll.Width;
1353 h_scroll.Width = client_area.Width - v_scroll.Width;
1356 h_scroll.Maximum = layout_wd;
1357 h_scroll.Width = client_area.Width;
1360 h_scroll.LargeChange = client_area.Width;
1361 h_scroll.SmallChange = Font.Height;
1362 item_control.Height -= h_scroll.Height;
1365 if (header_control.is_visible)
1366 header_control.Width = client_area.Width;
1367 item_control.Width = client_area.Width;
1369 if (v_scroll.is_visible) {
1370 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1371 v_scroll.Minimum = 0;
1373 // if h_scroll is visible, adjust the maximum of the
1374 // v_scroll to account for the height of h_scroll
1375 if (h_scroll.Visible) {
1376 v_scroll.Maximum = layout_ht + h_scroll.Height;
1377 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1379 v_scroll.Maximum = layout_ht;
1380 v_scroll.Height = client_area.Height;
1383 v_scroll.LargeChange = client_area.Height;
1384 v_scroll.SmallChange = Font.Height;
1385 if (header_control.Visible)
1386 header_control.Width -= v_scroll.Width;
1387 item_control.Width -= v_scroll.Width;
1392 internal int GetReorderedColumnIndex (ColumnHeader column)
1394 if (reordered_column_indices == null)
1395 return column.Index;
1397 for (int i = 0; i < Columns.Count; i++)
1398 if (reordered_column_indices [i] == column.Index)
1405 internal ColumnHeader GetReorderedColumn (int index)
1407 if (reordered_column_indices == null)
1408 return Columns [index];
1410 return Columns [reordered_column_indices [index]];
1413 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1417 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1419 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1423 header_control.Invalidate ();
1424 item_control.Invalidate ();
1430 int column_count = Columns.Count;
1432 if (reordered_column_indices == null) {
1433 reordered_column_indices = new int [column_count];
1434 for (int i = 0; i < column_count; i++)
1435 reordered_column_indices [i] = i;
1438 if (reordered_column_indices [index] == col.Index)
1441 int[] curr = reordered_column_indices;
1442 int [] result = new int [column_count];
1444 for (int i = 0; i < column_count; i++) {
1445 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1449 result [i] = col.Index;
1451 result [i] = curr [curr_idx++];
1454 ReorderColumns (result, true);
1457 internal void ReorderColumns (int [] display_indices, bool redraw)
1459 reordered_column_indices = display_indices;
1460 for (int i = 0; i < Columns.Count; i++) {
1461 ColumnHeader col = Columns [i];
1462 col.InternalDisplayIndex = reordered_column_indices [i];
1464 if (redraw && view == View.Details && IsHandleCreated) {
1466 header_control.Invalidate ();
1467 item_control.Invalidate ();
1471 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1473 int column_count = Columns.Count;
1474 newCol.SetListView (this);
1476 int [] display_indices = new int [column_count];
1477 for (int i = 0; i < column_count; i++) {
1478 ColumnHeader col = Columns [i];
1480 display_indices [i] = index;
1482 int display_index = col.InternalDisplayIndex;
1483 if (display_index < index) {
1484 display_indices [i] = display_index;
1486 display_indices [i] = (display_index + 1);
1491 ReorderColumns (display_indices, redraw);
1495 Size LargeIconItemSize
1498 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1499 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1500 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1501 int w = Math.Max (text_size.Width, image_w);
1504 w += 2 + CheckBoxSize.Width;
1506 return new Size (w, h);
1510 Size SmallIconItemSize {
1512 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1513 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1514 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1515 int w = text_size.Width + image_w;
1518 w += 2 + CheckBoxSize.Width;
1520 return new Size (w, h);
1527 // Calculate tile size if needed
1528 // It appears that using Font.Size instead of a SizeF value can give us
1529 // a slightly better approach to the proportions defined in .Net
1530 if (tile_size == Size.Empty) {
1531 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1532 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1533 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1534 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1536 tile_size = new Size (w, h);
1544 int GetDetailsItemHeight ()
1547 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1548 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1549 item_height = Math.Max (checkbox_height, text_size.Height);
1550 item_height = Math.Max (item_height, small_image_height);
1554 void SetItemLocation (int index, int x, int y, int row, int col)
1556 Point old_location = items_location [index];
1557 if (old_location.X == x && old_location.Y == y)
1560 Size item_size = ItemSize;
1561 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1563 items_location [index] = new Point (x, y);
1564 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1567 // Initial position matches item's position in ListViewItemCollection
1569 reordered_items_indices [index] = index;
1571 // Invalidate both previous and new bounds
1572 item_control.Invalidate (old_rect);
1573 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1577 void ShiftItemsPositions (int from, int to, bool forward)
1580 for (int i = to + 1; i > from; i--) {
1581 reordered_items_indices [i] = reordered_items_indices [i - 1];
1583 ListViewItem item = items [reordered_items_indices [i]];
1584 item_control.Invalidate (item.Bounds);
1585 item.DisplayIndex = i;
1586 item_control.Invalidate (item.Bounds);
1589 for (int i = from - 1; i < to; i++) {
1590 reordered_items_indices [i] = reordered_items_indices [i + 1];
1592 ListViewItem item = items [reordered_items_indices [i]];
1593 item_control.Invalidate (item.Bounds);
1594 item.DisplayIndex = i;
1595 item_control.Invalidate (item.Bounds);
1600 internal void ChangeItemLocation (int display_index, Point new_pos)
1602 int new_display_index = GetDisplayIndexFromLocation (new_pos);
1603 if (new_display_index == display_index)
1606 int item_index = reordered_items_indices [display_index];
1607 ListViewItem item = items [item_index];
1609 bool forward = new_display_index < display_index;
1610 int index_from, index_to;
1612 index_from = new_display_index;
1613 index_to = display_index - 1;
1615 index_from = display_index + 1;
1616 index_to = new_display_index;
1619 ShiftItemsPositions (index_from, index_to, forward);
1621 reordered_items_indices [new_display_index] = item_index;
1623 item_control.Invalidate (item.Bounds);
1624 item.DisplayIndex = new_display_index;
1625 item_control.Invalidate (item.Bounds);
1628 int GetDisplayIndexFromLocation (Point loc)
1630 int display_index = -1;
1631 Rectangle item_area;
1634 if (loc.X < 0 || loc.Y < 0)
1637 // Adjustment to put in the next position refered by 'loc'
1638 loc.X -= item_size.Width / 2;
1642 for (int i = 0; i < items.Count; i++) {
1643 item_area = new Rectangle (GetItemLocation (i), item_size);
1644 item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
1645 ThemeEngine.Current.ListViewVerticalSpacing);
1647 if (item_area.Contains (loc)) {
1653 // Put in in last position
1654 if (display_index == -1)
1655 display_index = items.Count - 1;
1657 return display_index;
1660 // When using groups, the items with no group assigned
1661 // belong to the DefaultGroup
1662 int GetDefaultGroupItems ()
1665 foreach (ListViewItem item in items)
1666 if (item.Group == null)
1675 int[,] item_index_matrix;
1677 void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1679 Rectangle area = ClientRectangle;
1681 if (show_groups && groups.Count > 0 && view != View.List) {
1682 // When groups are used the alignment is always top-aligned
1686 groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
1687 for (int i = 0; i < groups.InternalCount; i++) {
1688 ListViewGroup group = groups.GetInternalGroup (i);
1689 int items_in_group = group.ItemCount;
1691 if (items_in_group == 0)
1694 int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1695 if (group_cols <= 0)
1697 int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
1699 group.starting_row = rows;
1700 group.rows = group_rows;
1702 cols = Math.Max (group_cols, cols);
1708 // Simple matrix if no groups are used
1710 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
1713 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1715 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
1718 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1722 item_index_matrix = new int [rows, cols];
1725 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1727 header_control.Visible = false;
1728 header_control.Size = Size.Empty;
1729 item_control.Visible = true;
1730 item_control.Location = Point.Empty;
1731 ItemSize = item_size; // Cache item size
1733 if (items.Count == 0)
1736 Size sz = item_size;
1738 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1741 CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
1743 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1744 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1748 CalculateGroupsLayout (sz, y_spacing, 0);
1751 int row = 0, col = 0;
1754 for (int i = 0; i < items.Count; i++) {
1755 ListViewItem item = items [i];
1758 ListViewGroup group = item.Group;
1760 group = groups.DefaultGroup;
1762 Point group_items_loc = group.items_area_location;
1763 int current_item = group.current_item++;
1764 int starting_row = group.starting_row;
1766 row = (current_item / cols);
1767 col = current_item % cols;
1769 x = col * (item_size.Width + x_spacing);
1770 y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
1772 SetItemLocation (i, x, y, row + starting_row, col);
1773 item_index_matrix [row + starting_row, col] = i;
1777 x = col * (item_size.Width + x_spacing);
1778 y = row * (item_size.Height + y_spacing);
1780 SetItemLocation (i, x, y, row, col);
1781 item_index_matrix [row, col] = i;
1790 if (++col == cols) {
1801 item.DisplayIndex = i;
1802 item.SetPosition (new Point (x, y));
1808 item_control.Size = new Size (layout_wd, layout_ht);
1812 void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
1815 bool details = view == View.Details;
1817 for (int i = 0; i < groups.InternalCount; i++) {
1818 ListViewGroup group = groups.GetInternalGroup (i);
1819 if (group.ItemCount == 0)
1822 group.current_item = 0; // Reset layout
1823 y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
1826 layout_ht = y; // Update height taking into account Groups' headers heights
1829 int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
1831 Rectangle client_area = ClientRectangle;
1832 int header_height = text_size.Height + 10;
1834 group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
1835 group.items_area_location = new Point (0, y_origin + header_height);
1837 int items_area_height = ((item_height + y_spacing) * rows);
1838 return header_height + items_area_height + 10; // Add a small bottom margin
1842 void LayoutHeader ()
1845 for (int i = 0; i < Columns.Count; i++) {
1846 ColumnHeader col = GetReorderedColumn (i);
1849 col.CalcColumnHeader ();
1855 if (x < ClientRectangle.Width)
1856 x = ClientRectangle.Width;
1858 if (header_style == ColumnHeaderStyle.None) {
1859 header_control.Visible = false;
1860 header_control.Size = Size.Empty;
1861 layout_wd = ClientRectangle.Width;
1863 header_control.Width = x;
1864 header_control.Height = columns.Count > 0 ? columns [0].Ht : Font.Height + 5;
1865 header_control.Visible = true;
1869 void LayoutDetails ()
1873 if (columns.Count == 0) {
1874 item_control.Visible = false;
1875 layout_wd = ClientRectangle.Width;
1876 layout_ht = ClientRectangle.Height;
1880 item_control.Visible = true;
1881 item_control.Location = Point.Empty;
1882 item_control.Width = ClientRectangle.Width;
1884 int item_height = GetDetailsItemHeight ();
1885 ItemSize = new Size (0, item_height); // We only cache Height for details view
1886 int y = header_control.Height;
1888 bool using_groups = show_groups && groups.Count > 0 && view != View.List;
1890 CalculateGroupsLayout (ItemSize, 2, y);
1893 for (int i = 0; i < items.Count; i++) {
1894 ListViewItem item = items [i];
1896 if (!virtual_mode) // Virtual mode sets Layout until draw time
1900 item.DisplayIndex = i;
1905 ListViewGroup group = item.Group;
1907 group = groups.DefaultGroup;
1909 int current_item = group.current_item++;
1910 Point group_items_loc = group.items_area_location;
1912 y = current_item * (item_height + 2) + group_items_loc.Y;
1913 SetItemLocation (i, 0, y, 0, 0);
1914 item.SetPosition (new Point (0, y));
1918 SetItemLocation (i, 0, y, 0, 0);
1919 item.SetPosition (new Point (0, y));
1924 // some space for bottom gridline
1925 if (items.Count > 0 && grid_lines)
1929 if (!using_groups) // With groups it has been previously computed
1934 private void AdjustItemsPositionArray (int count)
1936 if (items_location.Length >= count)
1939 // items_location, items_matrix_location and reordered_items_indices must keep the same length
1940 count = Math.Max (count, items_location.Length * 2);
1941 items_location = new Point [count];
1942 items_matrix_location = new ItemMatrixLocation [count];
1943 reordered_items_indices = new int [count];
1946 private void CalculateListView (ListViewAlignment align)
1950 AdjustItemsPositionArray (items.Count);
1957 case View.SmallIcon:
1958 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
1959 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1962 case View.LargeIcon:
1963 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
1964 ThemeEngine.Current.ListViewHorizontalSpacing,
1965 ThemeEngine.Current.ListViewVerticalSpacing);
1969 LayoutIcons (SmallIconItemSize, true,
1970 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1974 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
1975 ThemeEngine.Current.ListViewHorizontalSpacing,
1976 ThemeEngine.Current.ListViewVerticalSpacing);
1981 CalculateScrollBars ();
1984 internal Point GetItemLocation (int index)
1986 Point loc = items_location [index];
1987 loc.X -= h_marker; // Adjust to scroll
1993 internal int GetItemIndex (int display_index)
1995 return reordered_items_indices [display_index];
1998 private bool KeySearchString (KeyEventArgs ke)
2000 int current_tickcnt = Environment.TickCount;
2001 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
2002 keysearch_text = string.Empty;
2005 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
2008 keysearch_text += (char)ke.KeyCode;
2009 keysearch_tickcnt = current_tickcnt;
2011 int start = FocusedItem == null ? 0 : FocusedItem.Index;
2014 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
2015 CompareOptions.IgnoreCase)) {
2017 items [i].Selected = true;
2021 i = (i + 1 < Items.Count) ? i+1 : 0;
2029 int GetAdjustedIndex (Keys key)
2033 if (View == View.Details) {
2036 result = FocusedItem.Index - 1;
2039 result = FocusedItem.Index + 1;
2040 if (result == items.Count)
2044 int last_index = LastVisibleIndex;
2045 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
2046 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
2048 if (FocusedItem.Index == last_index) {
2049 if (FocusedItem.Index < Items.Count - 1) {
2050 int page_size = item_control.Height / ItemSize.Height - 1;
2051 result = FocusedItem.Index + page_size - 1;
2052 if (result >= Items.Count)
2053 result = Items.Count - 1;
2056 result = last_index;
2059 int first_index = FirstVisibleIndex;
2060 if (GetItemLocation (first_index).Y < 0)
2062 if (FocusedItem.Index == first_index) {
2063 if (first_index > 0) {
2064 int page_size = item_control.Height / ItemSize.Height - 1;
2065 result = first_index - page_size + 1;
2070 result = first_index;
2076 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.Index];
2077 int row = item_matrix_location.Row;
2078 int col = item_matrix_location.Col;
2084 return item_index_matrix [row, col - 1];
2087 if (col == (cols - 1))
2089 while (item_index_matrix [row, col + 1] == 0) {
2094 return item_index_matrix [row, col + 1];
2099 while (item_index_matrix [row - 1, col] == 0 && row != 1) {
2104 return item_index_matrix [row - 1, col];
2107 if (row == (rows - 1) || row == Items.Count - 1)
2109 while (item_index_matrix [row + 1, col] == 0) {
2114 return item_index_matrix [row + 1, col];
2121 ListViewItem selection_start;
2123 private bool SelectItems (ArrayList sel_items)
2125 bool changed = false;
2126 foreach (ListViewItem item in SelectedItems)
2127 if (!sel_items.Contains (item)) {
2128 item.Selected = false;
2131 foreach (ListViewItem item in sel_items)
2132 if (!item.Selected) {
2133 item.Selected = true;
2139 private void UpdateMultiSelection (int index, bool reselect)
2141 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
2142 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
2143 ListViewItem item = items [index];
2145 if (shift_pressed && selection_start != null) {
2146 ArrayList list = new ArrayList ();
2147 int start_index = selection_start.Index;
2148 int start = Math.Min (start_index, index);
2149 int end = Math.Max (start_index, index);
2150 if (View == View.Details) {
2151 for (int i = start; i <= end; i++)
2152 list.Add (items [i]);
2154 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
2155 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
2156 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
2157 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
2158 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
2159 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
2161 for (int i = 0; i < items.Count; i++) {
2162 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
2164 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
2165 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
2166 list.Add (items [i]);
2170 } else if (ctrl_pressed) {
2171 item.Selected = !item.Selected;
2172 selection_start = item;
2175 // do not unselect, and reselect the item
2176 foreach (int itemIndex in SelectedIndices) {
2177 if (index == itemIndex)
2179 items [itemIndex].Selected = false;
2182 SelectedItems.Clear ();
2183 item.Selected = true;
2185 selection_start = item;
2189 internal override bool InternalPreProcessMessage (ref Message msg)
2191 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
2192 Keys key_data = (Keys)msg.WParam.ToInt32();
2194 HandleNavKeys (key_data);
2197 return base.InternalPreProcessMessage (ref msg);
2200 bool HandleNavKeys (Keys key_data)
2202 if (Items.Count == 0 || !item_control.Visible)
2205 if (FocusedItem == null)
2210 SelectIndex (Items.Count - 1);
2223 SelectIndex (GetAdjustedIndex (key_data));
2227 ToggleItemsCheckState ();
2230 if (selected_indices.Count > 0)
2231 OnItemActivate (EventArgs.Empty);
2241 void ToggleItemsCheckState ()
2246 // Don't modify check state if StateImageList has less than 2 elements
2247 if (StateImageList != null && StateImageList.Images.Count < 2)
2250 if (SelectedIndices.Count > 0) {
2251 for (int i = 0; i < SelectedIndices.Count; i++) {
2252 ListViewItem item = Items [SelectedIndices [i]];
2253 item.Checked = !item.Checked;
2258 if (FocusedItem != null) {
2259 FocusedItem.Checked = !FocusedItem.Checked;
2260 SelectIndex (FocusedItem.Index);
2264 void SelectIndex (int index)
2270 UpdateMultiSelection (index, true);
2271 else if (!items [index].Selected)
2272 items [index].Selected = true;
2274 SetFocusedItem (index);
2275 EnsureVisible (index);
2278 private void ListView_KeyDown (object sender, KeyEventArgs ke)
2280 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
2283 if (ke.Alt || ke.Control)
2286 ke.Handled = KeySearchString (ke);
2289 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
2291 Point loc = PointToClient (Control.MousePosition);
2292 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
2295 internal class ItemControl : Control {
2298 ListViewItem clicked_item;
2299 ListViewItem last_clicked_item;
2300 bool hover_processed = false;
2301 bool checking = false;
2302 ListViewItem prev_hovered_item;
2304 ListViewItem prev_tooltip_item;
2307 Point drag_begin = new Point (-1, -1);
2308 internal int dragged_item_index = -1;
2310 ListViewLabelEditTextBox edit_text_box;
2311 internal ListViewItem edit_item;
2312 LabelEditEventArgs edit_args;
2314 public ItemControl (ListView owner)
2317 DoubleClick += new EventHandler(ItemsDoubleClick);
2318 MouseDown += new MouseEventHandler(ItemsMouseDown);
2319 MouseMove += new MouseEventHandler(ItemsMouseMove);
2320 MouseHover += new EventHandler(ItemsMouseHover);
2321 MouseUp += new MouseEventHandler(ItemsMouseUp);
2324 void ItemsDoubleClick (object sender, EventArgs e)
2326 if (owner.activation == ItemActivation.Standard)
2327 owner.OnItemActivate (EventArgs.Empty);
2337 BoxSelect box_select_mode = BoxSelect.None;
2338 IList prev_selection;
2339 Point box_select_start;
2341 Rectangle box_select_rect;
2342 internal Rectangle BoxSelectRectangle {
2343 get { return box_select_rect; }
2345 if (box_select_rect == value)
2348 InvalidateBoxSelectRect ();
2349 box_select_rect = value;
2350 InvalidateBoxSelectRect ();
2354 void InvalidateBoxSelectRect ()
2356 if (BoxSelectRectangle.Size.IsEmpty)
2359 Rectangle edge = BoxSelectRectangle;
2365 edge.Y = BoxSelectRectangle.Bottom - 1;
2367 edge.Y = BoxSelectRectangle.Y - 1;
2369 edge.Height = BoxSelectRectangle.Height + 2;
2371 edge.X = BoxSelectRectangle.Right - 1;
2375 private Rectangle CalculateBoxSelectRectangle (Point pt)
2377 int left = Math.Min (box_select_start.X, pt.X);
2378 int right = Math.Max (box_select_start.X, pt.X);
2379 int top = Math.Min (box_select_start.Y, pt.Y);
2380 int bottom = Math.Max (box_select_start.Y, pt.Y);
2381 return Rectangle.FromLTRB (left, top, right, bottom);
2384 bool BoxIntersectsItem (int index)
2386 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
2387 if (owner.View != View.Details) {
2389 r.Y += r.Height / 4;
2393 return BoxSelectRectangle.IntersectsWith (r);
2396 bool BoxIntersectsText (int index)
2398 Rectangle r = owner.Items [index].TextBounds;
2399 return BoxSelectRectangle.IntersectsWith (r);
2402 ArrayList BoxSelectedItems {
2404 ArrayList result = new ArrayList ();
2405 for (int i = 0; i < owner.Items.Count; i++) {
2407 if (owner.View == View.Details && !owner.FullRowSelect)
2408 intersects = BoxIntersectsText (i);
2410 intersects = BoxIntersectsItem (i);
2413 result.Add (owner.Items [i]);
2419 private bool PerformBoxSelection (Point pt)
2421 if (box_select_mode == BoxSelect.None)
2424 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
2426 ArrayList box_items = BoxSelectedItems;
2430 switch (box_select_mode) {
2432 case BoxSelect.Normal:
2436 case BoxSelect.Control:
2437 items = new ArrayList ();
2438 foreach (int index in prev_selection)
2439 if (!box_items.Contains (owner.Items [index]))
2440 items.Add (owner.Items [index]);
2441 foreach (ListViewItem item in box_items)
2442 if (!prev_selection.Contains (item.Index))
2446 case BoxSelect.Shift:
2448 foreach (ListViewItem item in box_items)
2449 prev_selection.Remove (item.Index);
2450 foreach (int index in prev_selection)
2451 items.Add (owner.Items [index]);
2455 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
2459 owner.SelectItems (items);
2465 private void ItemsMouseDown (object sender, MouseEventArgs me)
2467 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2468 if (owner.items.Count == 0)
2471 bool box_selecting = false;
2472 Size item_size = owner.ItemSize;
2473 Point pt = new Point (me.X, me.Y);
2474 for (int i = 0; i < owner.items.Count; i++) {
2475 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2476 if (!item_rect.Contains (pt))
2479 // Actual item in 'i' position
2480 ListViewItem item = owner.items [owner.reordered_items_indices [i]];
2482 if (item.CheckRectReal.Contains (pt)) {
2483 // Don't modify check state if we have only one image
2484 // and if we are in 1.1 profile only take into account
2486 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2493 // Generate an extra ItemCheck event when we got two clicks
2494 // (Match weird .Net behaviour)
2496 item.Checked = !item.Checked;
2498 item.Checked = !item.Checked;
2503 if (owner.View == View.Details) {
2504 bool over_text = item.TextBounds.Contains (pt);
2505 if (owner.FullRowSelect) {
2506 clicked_item = owner.items [i];
2507 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2508 if (!over_text && over_item_column && owner.MultiSelect)
2509 box_selecting = true;
2510 } else if (over_text)
2511 clicked_item = item;
2513 owner.SetFocusedItem (i);
2515 clicked_item = item;
2521 if (clicked_item != null) {
2522 bool changed = !clicked_item.Selected;
2523 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2524 owner.SetFocusedItem (clicked_item.Index);
2526 if (owner.MultiSelect) {
2527 bool reselect = (!owner.LabelEdit || changed);
2528 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2529 owner.UpdateMultiSelection (clicked_item.Index, reselect);
2531 clicked_item.Selected = true;
2535 if (owner.VirtualMode && changed) {
2536 // Broken event - It's not fired from Item.Selected also
2537 ListViewVirtualItemsSelectionRangeChangedEventArgs args =
2538 new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
2540 owner.OnVirtualItemsSelectionRangeChanged (args);
2543 // Report clicks only if the item was clicked. On MS the
2544 // clicks are only raised if you click an item
2546 if (me.Clicks > 1) {
2547 if (owner.CheckBoxes)
2548 clicked_item.Checked = !clicked_item.Checked;
2549 } else if (me.Clicks == 1) {
2550 if (owner.LabelEdit && !changed)
2551 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2554 if (owner.MultiSelect)
2555 box_selecting = true;
2556 else if (owner.SelectedItems.Count > 0)
2557 owner.SelectedItems.Clear ();
2560 if (box_selecting) {
2561 Keys mods = XplatUI.State.ModifierKeys;
2562 if ((mods & Keys.Shift) != 0)
2563 box_select_mode = BoxSelect.Shift;
2564 else if ((mods & Keys.Control) != 0)
2565 box_select_mode = BoxSelect.Control;
2567 box_select_mode = BoxSelect.Normal;
2568 box_select_start = pt;
2569 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2573 private void ItemsMouseMove (object sender, MouseEventArgs me)
2575 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2577 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2581 if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
2582 !hover_processed && owner.Activation != ItemActivation.OneClick
2584 && !owner.ShowItemToolTips
2589 Point pt = PointToClient (Control.MousePosition);
2590 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2592 if (hover_processed && item != null && item != prev_hovered_item) {
2593 hover_processed = false;
2594 XplatUI.ResetMouseHover (Handle);
2597 // Need to invalidate the item in HotTracking to show/hide the underline style
2598 if (owner.Activation == ItemActivation.OneClick) {
2599 if (item == null && owner.HotItemIndex != -1) {
2601 if (owner.HotTracking)
2602 Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
2605 Cursor = Cursors.Default;
2606 owner.HotItemIndex = -1;
2607 } else if (item != null && owner.HotItemIndex == -1) {
2609 if (owner.HotTracking)
2610 Invalidate (item.Bounds);
2613 Cursor = Cursors.Hand;
2614 owner.HotItemIndex = item.Index;
2618 if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
2619 if (drag_begin.X == -1 && drag_begin.Y == -1) {
2621 drag_begin = new Point (me.X, me.Y);
2622 dragged_item_index = item.Index;
2626 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2627 if (!r.Contains (me.X, me.Y)) {
2628 ListViewItem dragged_item = owner.items [dragged_item_index];
2629 owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
2631 drag_begin = new Point (-1, -1);
2632 dragged_item_index = -1;
2638 if (owner.ShowItemToolTips) {
2640 owner.item_tooltip.Active = false;
2641 prev_tooltip_item = null;
2642 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2643 owner.item_tooltip.Active = true;
2644 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2645 prev_tooltip_item = item;
2652 private void ItemsMouseHover (object sender, EventArgs e)
2654 if (owner.hover_pending) {
2655 owner.OnMouseHover (e);
2656 owner.hover_pending = false;
2662 hover_processed = true;
2663 Point pt = PointToClient (Control.MousePosition);
2664 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2668 prev_hovered_item = item;
2670 if (owner.HoverSelection) {
2671 if (owner.MultiSelect)
2672 owner.UpdateMultiSelection (item.Index, true);
2674 item.Selected = true;
2676 owner.SetFocusedItem (item.Index);
2677 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2681 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2685 void HandleClicks (MouseEventArgs me)
2687 // if the click is not on an item,
2688 // clicks remains as 0
2691 owner.OnDoubleClick (EventArgs.Empty);
2692 } else if (clicks == 1) {
2693 owner.OnClick (EventArgs.Empty);
2695 owner.OnDoubleClick (EventArgs.Empty);
2696 owner.OnMouseDoubleClick (me);
2697 } else if (clicks == 1) {
2698 owner.OnClick (EventArgs.Empty);
2699 owner.OnMouseClick (me);
2706 private void ItemsMouseUp (object sender, MouseEventArgs me)
2708 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2709 HandleClicks (owner_me);
2712 if (owner.Items.Count == 0) {
2714 owner.OnMouseUp (owner_me);
2718 Point pt = new Point (me.X, me.Y);
2720 Rectangle rect = Rectangle.Empty;
2721 if (clicked_item != null) {
2722 if (owner.view == View.Details && !owner.full_row_select)
2723 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2725 rect = clicked_item.Bounds;
2727 if (rect.Contains (pt)) {
2728 switch (owner.activation) {
2729 case ItemActivation.OneClick:
2730 owner.OnItemActivate (EventArgs.Empty);
2733 case ItemActivation.TwoClick:
2734 if (last_clicked_item == clicked_item) {
2735 owner.OnItemActivate (EventArgs.Empty);
2736 last_clicked_item = null;
2738 last_clicked_item = clicked_item;
2741 // DoubleClick activation is handled in another handler
2745 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2746 // Need this to clean up background clicks
2747 owner.SelectedItems.Clear ();
2751 owner.OnMouseUp (owner_me);
2754 private void ResetMouseState ()
2756 clicked_item = null;
2757 box_select_start = Point.Empty;
2758 BoxSelectRectangle = Rectangle.Empty;
2759 prev_selection = null;
2760 box_select_mode = BoxSelect.None;
2763 // Clean these bits in case the mouse buttons were
2764 // released before firing ItemDrag
2765 dragged_item_index = -1;
2766 drag_begin = new Point (-1, -1);
2769 private void LabelEditFinished (object sender, EventArgs e)
2771 EndEdit (edit_item);
2774 private void LabelEditCancelled (object sender, EventArgs e)
2776 edit_args.SetLabel (null);
2777 EndEdit (edit_item);
2780 private void LabelTextChanged (object sender, EventArgs e)
2782 if (edit_args != null)
2783 edit_args.SetLabel (edit_text_box.Text);
2786 internal void BeginEdit (ListViewItem item)
2788 if (edit_item != null)
2789 EndEdit (edit_item);
2791 if (edit_text_box == null) {
2792 edit_text_box = new ListViewLabelEditTextBox ();
2793 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2794 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2795 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2796 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2797 edit_text_box.Visible = false;
2798 Controls.Add (edit_text_box);
2801 item.EnsureVisible();
2803 edit_text_box.Reset ();
2805 switch (owner.view) {
2807 case View.SmallIcon:
2809 edit_text_box.TextAlign = HorizontalAlignment.Left;
2810 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2811 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2812 edit_text_box.Width = (int)sizef.Width + 4;
2813 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2814 edit_text_box.WordWrap = false;
2815 edit_text_box.Multiline = false;
2817 case View.LargeIcon:
2818 edit_text_box.TextAlign = HorizontalAlignment.Center;
2819 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2820 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2821 edit_text_box.Width = (int)sizef.Width + 4;
2822 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2823 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2824 edit_text_box.WordWrap = true;
2825 edit_text_box.Multiline = true;
2831 edit_text_box.Text = item.Text;
2832 edit_text_box.Font = item.Font;
2833 edit_text_box.Visible = true;
2834 edit_text_box.Focus ();
2835 edit_text_box.SelectAll ();
2837 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2838 owner.OnBeforeLabelEdit (edit_args);
2840 if (edit_args.CancelEdit)
2844 internal void CancelEdit (ListViewItem item)
2846 // do nothing if there's no item being edited, or if the
2847 // item being edited is not the one passed in
2848 if (edit_item == null || edit_item != item)
2851 edit_args.SetLabel (null);
2855 internal void EndEdit (ListViewItem item)
2857 // do nothing if there's no item being edited, or if the
2858 // item being edited is not the one passed in
2859 if (edit_item == null || edit_item != item)
2862 if (edit_text_box != null) {
2863 if (edit_text_box.Visible)
2864 edit_text_box.Visible = false;
2865 // ensure listview gets focus
2869 // Same as TreeView.EndEdit: need to have focus in synch
2870 Application.DoEvents ();
2873 // Create a new instance, since we could get a call to BeginEdit
2874 // from the handler and have fields out of synch
2876 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2879 owner.OnAfterLabelEdit (args);
2880 if (!args.CancelEdit && args.Label != null)
2881 item.Text = args.Label;
2884 internal override void OnPaintInternal (PaintEventArgs pe)
2886 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2889 protected override void WndProc (ref Message m)
2891 switch ((Msg)m.Msg) {
2892 case Msg.WM_KILLFOCUS:
2893 owner.Select (false, true);
2895 case Msg.WM_SETFOCUS:
2896 owner.Select (false, true);
2898 case Msg.WM_LBUTTONDOWN:
2900 owner.Select (false, true);
2902 case Msg.WM_RBUTTONDOWN:
2904 owner.Select (false, true);
2909 base.WndProc (ref m);
2913 internal class ListViewLabelEditTextBox : TextBox
2918 int max_height = -1;
2919 int min_height = -1;
2921 int old_number_lines = 1;
2923 SizeF text_size_one_char;
2925 public ListViewLabelEditTextBox ()
2927 min_height = DefaultSize.Height;
2928 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2931 public int MaxWidth {
2933 if (value < min_width)
2934 max_width = min_width;
2940 public int MaxHeight {
2942 if (value < min_height)
2943 max_height = min_height;
2949 public new int Width {
2959 public override Font Font {
2965 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2969 protected override void OnTextChanged (EventArgs e)
2971 SizeF text_size = TextRenderer.MeasureString (Text, Font);
2973 int new_width = (int)text_size.Width + 8;
2976 ResizeTextBoxWidth (new_width);
2978 if (Width != max_width)
2979 ResizeTextBoxWidth (new_width);
2981 int number_lines = Lines.Length;
2983 if (number_lines != old_number_lines) {
2984 int new_height = number_lines * (int)text_size_one_char.Height + 4;
2985 old_number_lines = number_lines;
2987 ResizeTextBoxHeight (new_height);
2991 base.OnTextChanged (e);
2994 protected override bool IsInputKey (Keys key_data)
2996 if ((key_data & Keys.Alt) == 0) {
2997 switch (key_data & Keys.KeyCode) {
3004 return base.IsInputKey (key_data);
3007 protected override void OnKeyDown (KeyEventArgs e)
3012 switch (e.KeyCode) {
3016 OnEditingFinished (e);
3021 OnEditingCancelled (e);
3026 protected override void OnLostFocus (EventArgs e)
3029 OnEditingFinished (e);
3033 protected void OnEditingCancelled (EventArgs e)
3035 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3040 protected void OnEditingFinished (EventArgs e)
3042 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3047 private void ResizeTextBoxWidth (int new_width)
3049 if (new_width > max_width)
3050 base.Width = max_width;
3052 if (new_width >= min_width)
3053 base.Width = new_width;
3055 base.Width = min_width;
3058 private void ResizeTextBoxHeight (int new_height)
3060 if (new_height > max_height)
3061 base.Height = max_height;
3063 if (new_height >= min_height)
3064 base.Height = new_height;
3066 base.Height = min_height;
3069 public void Reset ()
3076 old_number_lines = 1;
3078 Text = String.Empty;
3083 static object EditingCancelledEvent = new object ();
3084 public event EventHandler EditingCancelled {
3085 add { Events.AddHandler (EditingCancelledEvent, value); }
3086 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3089 static object EditingFinishedEvent = new object ();
3090 public event EventHandler EditingFinished {
3091 add { Events.AddHandler (EditingFinishedEvent, value); }
3092 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3096 internal override void OnPaintInternal (PaintEventArgs pe)
3101 CalculateScrollBars ();
3104 void FocusChanged (object o, EventArgs args)
3106 if (Items.Count == 0)
3109 if (FocusedItem == null)
3112 ListViewItem focused_item = FocusedItem;
3114 if (focused_item.ListView != null) {
3115 item_control.Invalidate (focused_item.Bounds);
3116 focused_item.Layout ();
3117 item_control.Invalidate (focused_item.Bounds);
3121 private void ListView_MouseEnter (object sender, EventArgs args)
3123 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3126 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3128 if (Items.Count == 0)
3131 int lines = me.Delta / 120;
3138 case View.SmallIcon:
3139 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3141 case View.LargeIcon:
3142 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3145 Scroll (h_scroll, -ItemSize.Width * lines);
3149 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3155 private void ListView_SizeChanged (object sender, EventArgs e)
3157 CalculateListView (alignment);
3160 private void SetFocusedItem (int index)
3163 items [index].Focused = true;
3164 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3165 items [focused_item_index].Focused = false;
3167 focused_item_index = index;
3170 private void HorizontalScroller (object sender, EventArgs e)
3172 item_control.EndEdit (item_control.edit_item);
3174 // Avoid unnecessary flickering, when button is
3175 // kept pressed at the end
3176 if (h_marker != h_scroll.Value) {
3178 int pixels = h_marker - h_scroll.Value;
3180 h_marker = h_scroll.Value;
3181 if (header_control.Visible)
3182 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3184 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3188 private void VerticalScroller (object sender, EventArgs e)
3190 item_control.EndEdit (item_control.edit_item);
3192 // Avoid unnecessary flickering, when button is
3193 // kept pressed at the end
3194 if (v_marker != v_scroll.Value) {
3195 int pixels = v_marker - v_scroll.Value;
3196 Rectangle area = item_control.ClientRectangle;
3197 if (header_control.Visible) {
3198 area.Y += header_control.Height;
3199 area.Height -= header_control.Height;
3202 v_marker = v_scroll.Value;
3203 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3207 internal override bool IsInputCharInternal (char charCode)
3211 #endregion // Internal Methods Properties
3213 #region Protected Methods
3214 protected override void CreateHandle ()
3216 base.CreateHandle ();
3217 for (int i = 0; i < SelectedItems.Count; i++)
3218 OnSelectedIndexChanged (EventArgs.Empty);
3221 protected override void Dispose (bool disposing)
3224 h_scroll.Dispose ();
3225 v_scroll.Dispose ();
3227 large_image_list = null;
3228 small_image_list = null;
3229 state_image_list = null;
3231 foreach (ColumnHeader col in columns)
3232 col.SetListView (null);
3235 if (!virtual_mode) // In virtual mode we don't save the items
3237 foreach (ListViewItem item in items)
3241 base.Dispose (disposing);
3244 protected override bool IsInputKey (Keys keyData)
3261 return base.IsInputKey (keyData);
3264 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3266 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3272 protected override void OnBackgroundImageChanged (EventArgs args)
3274 item_control.BackgroundImage = BackgroundImage;
3275 base.OnBackgroundImageChanged (args);
3279 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3281 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3286 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3288 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3294 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3296 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3301 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3303 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3308 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3310 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3316 protected override void OnEnabledChanged (EventArgs e)
3318 base.OnEnabledChanged (e);
3322 protected override void OnFontChanged (EventArgs e)
3324 base.OnFontChanged (e);
3328 protected override void OnHandleCreated (EventArgs e)
3330 base.OnHandleCreated (e);
3331 CalculateListView (alignment);
3333 if (!virtual_mode) // Sorting is not allowed in virtual mode
3338 protected override void OnHandleDestroyed (EventArgs e)
3340 base.OnHandleDestroyed (e);
3343 protected virtual void OnItemActivate (EventArgs e)
3345 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3350 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3352 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3358 protected internal virtual void OnItemChecked (ItemCheckedEventArgs icea)
3360 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3366 protected virtual void OnItemDrag (ItemDragEventArgs e)
3368 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3374 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs args)
3376 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3381 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs args)
3383 ListViewItemSelectionChangedEventHandler eh =
3384 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3389 protected override void OnMouseHover (EventArgs args)
3391 base.OnMouseHover (args);
3394 protected override void OnParentChanged (EventArgs args)
3396 base.OnParentChanged (args);
3400 protected virtual void OnSelectedIndexChanged (EventArgs e)
3402 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3407 protected override void OnSystemColorsChanged (EventArgs e)
3409 base.OnSystemColorsChanged (e);
3413 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs args)
3415 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3420 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs args)
3422 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3427 [EditorBrowsable (EditorBrowsableState.Advanced)]
3428 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3430 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3435 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs args)
3437 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3442 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs args)
3444 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3445 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3451 protected void RealizeProperties ()
3456 protected void UpdateExtendedStyles ()
3461 bool refocusing = false;
3463 protected override void WndProc (ref Message m)
3465 switch ((Msg)m.Msg) {
3466 case Msg.WM_KILLFOCUS:
3467 Control receiver = Control.FromHandle (m.WParam);
3468 if (receiver == item_control) {
3474 case Msg.WM_SETFOCUS:
3484 base.WndProc (ref m);
3486 #endregion // Protected Methods
3488 #region Public Instance Methods
3489 public void ArrangeIcons ()
3491 ArrangeIcons (this.alignment);
3494 public void ArrangeIcons (ListViewAlignment alignment)
3496 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3497 if (view == View.LargeIcon || view == View.SmallIcon) {
3498 this.CalculateListView (alignment);
3499 // we have done the calculations already
3500 this.Redraw (false);
3505 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3507 if (columnIndex < 0 || columnIndex >= columns.Count)
3508 throw new ArgumentOutOfRangeException ("columnIndex");
3510 columns [columnIndex].AutoResize (headerAutoResize);
3513 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3516 foreach (ColumnHeader col in columns)
3517 col.AutoResize (headerAutoResize);
3522 public void BeginUpdate ()
3524 // flag to avoid painting
3528 public void Clear ()
3531 items.Clear (); // Redraw (true) called here
3534 public void EndUpdate ()
3536 // flag to avoid painting
3539 // probably, now we need a redraw with recalculations
3543 public void EnsureVisible (int index)
3545 if (index < 0 || index >= items.Count || scrollable == false)
3548 Rectangle view_rect = item_control.ClientRectangle;
3549 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3551 if (view_rect.Contains (bounds))
3554 if (View != View.Details) {
3555 if (bounds.Left < 0)
3556 h_scroll.Value += bounds.Left;
3557 else if (bounds.Right > view_rect.Right)
3558 h_scroll.Value += (bounds.Right - view_rect.Right);
3562 v_scroll.Value += bounds.Top;
3563 else if (bounds.Bottom > view_rect.Bottom)
3564 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3568 public ListViewItem FindItemWithText (string text)
3570 if (items.Count == 0)
3573 return FindItemWithText (text, true, 0, true);
3576 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex)
3578 return FindItemWithText (text, includeSubItems, startIndex, true);
3581 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex, bool prefixSearch)
3583 if (startIndex < 0 || startIndex >= items.Count)
3584 throw new ArgumentOutOfRangeException ("startIndex");
3587 throw new ArgumentNullException ("text");
3590 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3591 prefixSearch, includeSubItems, text, Point.Empty,
3592 SearchDirectionHint.Down, startIndex);
3594 OnSearchForVirtualItem (args);
3595 int idx = args.Index;
3596 if (idx >= 0 && idx < virtual_list_size)
3602 for (int i = startIndex; i < items.Count; i++) {
3603 ListViewItem lvi = items [i];
3605 if ((prefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3606 || String.Compare (lvi.Text, text, true) == 0) // match
3610 if (includeSubItems) {
3611 for (int i = startIndex; i < items.Count; i++) {
3612 ListViewItem lvi = items [i];
3613 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3614 if ((prefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3615 || String.Compare (sub_item.Text, text, true) == 0)
3623 public ListViewItem FindNearestItem (SearchDirectionHint direction, int x, int y)
3625 return FindNearestItem (direction, new Point (x, y));
3628 public ListViewItem FindNearestItem (SearchDirectionHint direction, Point location)
3630 if (direction < SearchDirectionHint.Left || direction > SearchDirectionHint.Down)
3631 throw new ArgumentOutOfRangeException ("searchDirection");
3633 if (view != View.LargeIcon && view != View.SmallIcon)
3634 throw new InvalidOperationException ();
3637 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3638 false, false, String.Empty, location,
3641 OnSearchForVirtualItem (args);
3642 int idx = args.Index;
3643 if (idx >= 0 && idx < virtual_list_size)
3649 ListViewItem item = null;
3650 int min_dist = Int32.MaxValue;
3653 // It looks like .Net does a previous adjustment
3655 switch (direction) {
3656 case SearchDirectionHint.Up:
3657 location.Y -= item_size.Height;
3659 case SearchDirectionHint.Down:
3660 location.Y += item_size.Height;
3662 case SearchDirectionHint.Left:
3663 location.X -= item_size.Width;
3665 case SearchDirectionHint.Right:
3666 location.X += item_size.Width;
3670 for (int i = 0; i < items.Count; i++) {
3671 Point item_loc = GetItemLocation (i);
3673 if (direction == SearchDirectionHint.Up) {
3674 if (location.Y < item_loc.Y)
3676 } else if (direction == SearchDirectionHint.Down) {
3677 if (location.Y > item_loc.Y)
3679 } else if (direction == SearchDirectionHint.Left) {
3680 if (location.X < item_loc.X)
3682 } else if (direction == SearchDirectionHint.Right) {
3683 if (location.X > item_loc.X)
3687 int x_dist = location.X - item_loc.X;
3688 int y_dist = location.Y - item_loc.Y;
3690 int dist = x_dist * x_dist + y_dist * y_dist;
3691 if (dist < min_dist) {
3701 public ListViewItem GetItemAt (int x, int y)
3703 Size item_size = ItemSize;
3704 for (int i = 0; i < items.Count; i++) {
3705 Point item_location = GetItemLocation (i);
3706 Rectangle item_rect = new Rectangle (item_location, item_size);
3707 if (item_rect.Contains (x, y))
3714 public Rectangle GetItemRect (int index)
3716 return GetItemRect (index, ItemBoundsPortion.Entire);
3719 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3721 if (index < 0 || index >= items.Count)
3722 throw new IndexOutOfRangeException ("index");
3724 return items [index].GetBounds (portion);
3728 public ListViewHitTestInfo HitTest (Point pt)
3730 return HitTest (pt.X, pt.Y);
3733 public ListViewHitTestInfo HitTest (int x, int y)
3736 throw new ArgumentOutOfRangeException ("x");
3738 throw new ArgumentOutOfRangeException ("y");
3740 ListViewItem item = GetItemAt (x, y);
3742 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3744 ListViewHitTestLocations locations = 0;
3745 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3746 locations |= ListViewHitTestLocations.Label;
3747 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3748 locations |= ListViewHitTestLocations.Image;
3749 else if (item.CheckRectReal.Contains (x, y))
3750 locations |= ListViewHitTestLocations.StateImage;
3752 ListViewItem.ListViewSubItem subitem = null;
3753 if (view == View.Details)
3754 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3755 if (si.Bounds.Contains (x, y)) {
3760 return new ListViewHitTestInfo (item, subitem, locations);
3763 [EditorBrowsable (EditorBrowsableState.Advanced)]
3764 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3766 if (startIndex < 0 || startIndex >= items.Count)
3767 throw new ArgumentOutOfRangeException ("startIndex");
3768 if (endIndex < 0 || endIndex >= items.Count)
3769 throw new ArgumentOutOfRangeException ("endIndex");
3770 if (startIndex > endIndex)
3771 throw new ArgumentException ("startIndex");
3776 for (int i = startIndex; i <= endIndex; i++)
3777 item_control.Invalidate (items [i].Bounds);
3779 if (!invalidateOnly)
3788 throw new InvalidOperationException ();
3794 // we need this overload to reuse the logic for sorting, while allowing
3795 // redrawing to be done by caller or have it done by this method when
3796 // sorting is really performed
3798 // ListViewItemCollection's Add and AddRange methods call this overload
3799 // with redraw set to false, as they take care of redrawing themselves
3800 // (they even want to redraw the listview if no sort is performed, as
3801 // an item was added), while ListView.Sort () only wants to redraw if
3802 // sorting was actually performed
3803 private void Sort (bool redraw)
3805 if (!IsHandleCreated || item_sorter == null) {
3809 items.Sort (item_sorter);
3814 public override string ToString ()
3816 int count = this.Items.Count;
3819 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3821 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3823 #endregion // Public Instance Methods
3828 class HeaderControl : Control {
3831 bool column_resize_active = false;
3832 ColumnHeader resize_column;
3833 ColumnHeader clicked_column;
3834 ColumnHeader drag_column;
3836 int drag_to_index = -1;
3838 public HeaderControl (ListView owner)
3841 MouseDown += new MouseEventHandler (HeaderMouseDown);
3842 MouseMove += new MouseEventHandler (HeaderMouseMove);
3843 MouseUp += new MouseEventHandler (HeaderMouseUp);
3846 private ColumnHeader ColumnAtX (int x)
3848 Point pt = new Point (x, 0);
3849 ColumnHeader result = null;
3850 foreach (ColumnHeader col in owner.Columns) {
3851 if (col.Rect.Contains (pt)) {
3859 private int GetReorderedIndex (ColumnHeader col)
3861 if (owner.reordered_column_indices == null)
3864 for (int i = 0; i < owner.Columns.Count; i++)
3865 if (owner.reordered_column_indices [i] == col.Index)
3867 throw new Exception ("Column index missing from reordered array");
3870 private void HeaderMouseDown (object sender, MouseEventArgs me)
3872 if (resize_column != null) {
3873 column_resize_active = true;
3878 clicked_column = ColumnAtX (me.X + owner.h_marker);
3880 if (clicked_column != null) {
3882 if (owner.AllowColumnReorder) {
3884 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3885 drag_column.Rect = clicked_column.Rect;
3886 drag_to_index = GetReorderedIndex (clicked_column);
3888 clicked_column.Pressed = true;
3889 Rectangle bounds = clicked_column.Rect;
3890 bounds.X -= owner.h_marker;
3891 Invalidate (bounds);
3898 column_resize_active = false;
3899 resize_column = null;
3901 Cursor = Cursors.Default;
3904 private void HeaderMouseMove (object sender, MouseEventArgs me)
3906 Point pt = new Point (me.X + owner.h_marker, me.Y);
3908 if (column_resize_active) {
3909 int width = pt.X - resize_column.X;
3913 if (!owner.CanProceedWithResize (resize_column, width)){
3917 resize_column.Width = width;
3921 resize_column = null;
3923 if (clicked_column != null) {
3924 if (owner.AllowColumnReorder) {
3927 r = drag_column.Rect;
3928 r.X = clicked_column.Rect.X + me.X - drag_x;
3929 drag_column.Rect = r;
3931 int x = me.X + owner.h_marker;
3932 ColumnHeader over = ColumnAtX (x);
3934 drag_to_index = owner.Columns.Count;
3935 else if (x < over.X + over.Width / 2)
3936 drag_to_index = GetReorderedIndex (over);
3938 drag_to_index = GetReorderedIndex (over) + 1;
3941 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
3942 bool pressed = clicked_column.Pressed;
3943 clicked_column.Pressed = over == clicked_column;
3944 if (clicked_column.Pressed ^ pressed) {
3945 Rectangle bounds = clicked_column.Rect;
3946 bounds.X -= owner.h_marker;
3947 Invalidate (bounds);
3953 for (int i = 0; i < owner.Columns.Count; i++) {
3954 Rectangle zone = owner.Columns [i].Rect;
3955 zone.X = zone.Right - 5;
3957 if (zone.Contains (pt)) {
3958 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
3960 resize_column = owner.Columns [i];
3965 if (resize_column == null)
3966 Cursor = Cursors.Default;
3968 Cursor = Cursors.VSplit;
3971 void HeaderMouseUp (object sender, MouseEventArgs me)
3975 if (column_resize_active) {
3976 int column_idx = resize_column.Index;
3978 owner.RaiseColumnWidthChanged (column_idx);
3982 if (clicked_column != null && clicked_column.Pressed) {
3983 clicked_column.Pressed = false;
3984 Rectangle bounds = clicked_column.Rect;
3985 bounds.X -= owner.h_marker;
3986 Invalidate (bounds);
3987 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
3990 if (drag_column != null && owner.AllowColumnReorder) {
3992 if (drag_to_index > GetReorderedIndex (clicked_column))
3994 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
3995 owner.ReorderColumn (clicked_column, drag_to_index, true);
4000 clicked_column = null;
4003 internal override void OnPaintInternal (PaintEventArgs pe)
4008 Theme theme = ThemeEngine.Current;
4009 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4011 if (drag_column == null)
4015 if (drag_to_index == owner.Columns.Count)
4016 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4018 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4019 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4022 protected override void WndProc (ref Message m)
4024 switch ((Msg)m.Msg) {
4025 case Msg.WM_SETFOCUS:
4029 base.WndProc (ref m);
4035 private class ItemComparer : IComparer {
4036 readonly SortOrder sort_order;
4038 public ItemComparer (SortOrder sortOrder)
4040 sort_order = sortOrder;
4043 public int Compare (object x, object y)
4045 ListViewItem item_x = x as ListViewItem;
4046 ListViewItem item_y = y as ListViewItem;
4047 if (sort_order == SortOrder.Ascending)
4048 return String.Compare (item_x.Text, item_y.Text);
4050 return String.Compare (item_y.Text, item_x.Text);
4055 [ListBindable (false)]
4057 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4059 private readonly ListView owner;
4061 #region Public Constructor
4062 public CheckedIndexCollection (ListView owner)
4066 #endregion // Public Constructor
4068 #region Public Properties
4071 get { return owner.CheckedItems.Count; }
4074 public bool IsReadOnly {
4075 get { return true; }
4078 public int this [int index] {
4080 int [] indices = GetIndices ();
4081 if (index < 0 || index >= indices.Length)
4082 throw new ArgumentOutOfRangeException ("index");
4083 return indices [index];
4087 bool ICollection.IsSynchronized {
4088 get { return false; }
4091 object ICollection.SyncRoot {
4092 get { return this; }
4095 bool IList.IsFixedSize {
4096 get { return true; }
4099 object IList.this [int index] {
4100 get { return this [index]; }
4101 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4103 #endregion // Public Properties
4105 #region Public Methods
4106 public bool Contains (int checkedIndex)
4108 int [] indices = GetIndices ();
4109 for (int i = 0; i < indices.Length; i++) {
4110 if (indices [i] == checkedIndex)
4116 public IEnumerator GetEnumerator ()
4118 int [] indices = GetIndices ();
4119 return indices.GetEnumerator ();
4122 void ICollection.CopyTo (Array dest, int index)
4124 int [] indices = GetIndices ();
4125 Array.Copy (indices, 0, dest, index, indices.Length);
4128 int IList.Add (object value)
4130 throw new NotSupportedException ("Add operation is not supported.");
4135 throw new NotSupportedException ("Clear operation is not supported.");
4138 bool IList.Contains (object checkedIndex)
4140 if (!(checkedIndex is int))
4142 return Contains ((int) checkedIndex);
4145 int IList.IndexOf (object checkedIndex)
4147 if (!(checkedIndex is int))
4149 return IndexOf ((int) checkedIndex);
4152 void IList.Insert (int index, object value)
4154 throw new NotSupportedException ("Insert operation is not supported.");
4157 void IList.Remove (object value)
4159 throw new NotSupportedException ("Remove operation is not supported.");
4162 void IList.RemoveAt (int index)
4164 throw new NotSupportedException ("RemoveAt operation is not supported.");
4167 public int IndexOf (int checkedIndex)
4169 int [] indices = GetIndices ();
4170 for (int i = 0; i < indices.Length; i++) {
4171 if (indices [i] == checkedIndex)
4176 #endregion // Public Methods
4178 private int [] GetIndices ()
4180 ArrayList checked_items = owner.CheckedItems.List;
4181 int [] indices = new int [checked_items.Count];
4182 for (int i = 0; i < checked_items.Count; i++) {
4183 ListViewItem item = (ListViewItem) checked_items [i];
4184 indices [i] = item.Index;
4188 } // CheckedIndexCollection
4191 [ListBindable (false)]
4193 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4195 private readonly ListView owner;
4196 private ArrayList list;
4198 #region Public Constructor
4199 public CheckedListViewItemCollection (ListView owner)
4202 this.owner.Items.Changed += new CollectionChangedHandler (
4203 ItemsCollection_Changed);
4205 #endregion // Public Constructor
4207 #region Public Properties
4211 if (!owner.CheckBoxes)
4217 public bool IsReadOnly {
4218 get { return true; }
4221 public ListViewItem this [int index] {
4224 if (owner.VirtualMode)
4225 throw new InvalidOperationException ();
4227 ArrayList checked_items = List;
4228 if (index < 0 || index >= checked_items.Count)
4229 throw new ArgumentOutOfRangeException ("index");
4230 return (ListViewItem) checked_items [index];
4235 public virtual ListViewItem this [string key] {
4237 int idx = IndexOfKey (key);
4238 return idx == -1 ? null : (ListViewItem) List [idx];
4243 bool ICollection.IsSynchronized {
4244 get { return false; }
4247 object ICollection.SyncRoot {
4248 get { return this; }
4251 bool IList.IsFixedSize {
4252 get { return true; }
4255 object IList.this [int index] {
4256 get { return this [index]; }
4257 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4259 #endregion // Public Properties
4261 #region Public Methods
4262 public bool Contains (ListViewItem item)
4264 if (!owner.CheckBoxes)
4266 return List.Contains (item);
4270 public virtual bool ContainsKey (string key)
4272 return IndexOfKey (key) != -1;
4276 public void CopyTo (Array dest, int index)
4279 if (owner.VirtualMode)
4280 throw new InvalidOperationException ();
4282 if (!owner.CheckBoxes)
4284 List.CopyTo (dest, index);
4287 public IEnumerator GetEnumerator ()
4290 if (owner.VirtualMode)
4291 throw new InvalidOperationException ();
4293 if (!owner.CheckBoxes)
4294 return (new ListViewItem [0]).GetEnumerator ();
4295 return List.GetEnumerator ();
4298 int IList.Add (object value)
4300 throw new NotSupportedException ("Add operation is not supported.");
4305 throw new NotSupportedException ("Clear operation is not supported.");
4308 bool IList.Contains (object item)
4310 if (!(item is ListViewItem))
4312 return Contains ((ListViewItem) item);
4315 int IList.IndexOf (object item)
4317 if (!(item is ListViewItem))
4319 return IndexOf ((ListViewItem) item);
4322 void IList.Insert (int index, object value)
4324 throw new NotSupportedException ("Insert operation is not supported.");
4327 void IList.Remove (object value)
4329 throw new NotSupportedException ("Remove operation is not supported.");
4332 void IList.RemoveAt (int index)
4334 throw new NotSupportedException ("RemoveAt operation is not supported.");
4337 public int IndexOf (ListViewItem item)
4340 if (owner.VirtualMode)
4341 throw new InvalidOperationException ();
4343 if (!owner.CheckBoxes)
4345 return List.IndexOf (item);
4349 public virtual int IndexOfKey (string key)
4352 if (owner.VirtualMode)
4353 throw new InvalidOperationException ();
4355 if (key == null || key.Length == 0)
4358 ArrayList checked_items = List;
4359 for (int i = 0; i < checked_items.Count; i++) {
4360 ListViewItem item = (ListViewItem) checked_items [i];
4361 if (String.Compare (key, item.Name, true) == 0)
4368 #endregion // Public Methods
4370 internal ArrayList List {
4373 list = new ArrayList ();
4374 foreach (ListViewItem item in owner.Items) {
4383 internal void Reset ()
4385 // force re-population of list
4389 private void ItemsCollection_Changed ()
4393 } // CheckedListViewItemCollection
4396 [ListBindable (false)]
4398 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4400 internal ArrayList list;
4401 private ListView owner;
4403 #region Public Constructor
4404 public ColumnHeaderCollection (ListView owner)
4406 list = new ArrayList ();
4409 #endregion // Public Constructor
4411 #region Public Properties
4414 get { return list.Count; }
4417 public bool IsReadOnly {
4418 get { return false; }
4421 public virtual ColumnHeader this [int index] {
4423 if (index < 0 || index >= list.Count)
4424 throw new ArgumentOutOfRangeException ("index");
4425 return (ColumnHeader) list [index];
4430 public virtual ColumnHeader this [string key] {
4432 int idx = IndexOfKey (key);
4436 return (ColumnHeader) list [idx];
4441 bool ICollection.IsSynchronized {
4442 get { return true; }
4445 object ICollection.SyncRoot {
4446 get { return this; }
4449 bool IList.IsFixedSize {
4450 get { return list.IsFixedSize; }
4453 object IList.this [int index] {
4454 get { return this [index]; }
4455 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4457 #endregion // Public Properties
4459 #region Public Methods
4460 public virtual int Add (ColumnHeader value)
4462 int idx = list.Add (value);
4463 owner.AddColumn (value, idx, true);
4467 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4469 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4470 this.Add (colHeader);
4475 public virtual ColumnHeader Add (string text)
4477 return Add (String.Empty, text);
4480 public virtual ColumnHeader Add (string text, int iwidth)
4482 return Add (String.Empty, text, iwidth);
4485 public virtual ColumnHeader Add (string key, string text)
4487 ColumnHeader colHeader = new ColumnHeader ();
4488 colHeader.Name = key;
4489 colHeader.Text = text;
4494 public virtual ColumnHeader Add (string key, string text, int iwidth)
4496 return Add (key, text, iwidth, HorizontalAlignment.Left, -1);
4499 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, int imageIndex)
4501 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
4502 colHeader.ImageIndex = imageIndex;
4507 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, string imageKey)
4509 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
4510 colHeader.ImageKey = imageKey;
4516 public virtual void AddRange (ColumnHeader [] values)
4518 foreach (ColumnHeader colHeader in values) {
4519 int idx = list.Add (colHeader);
4520 owner.AddColumn (colHeader, idx, false);
4523 owner.Redraw (true);
4526 public virtual void Clear ()
4528 foreach (ColumnHeader col in list)
4529 col.SetListView (null);
4531 owner.ReorderColumns (new int [0], true);
4534 public bool Contains (ColumnHeader value)
4536 return list.Contains (value);
4540 public virtual bool ContainsKey (string key)
4542 return IndexOfKey (key) != -1;
4546 public IEnumerator GetEnumerator ()
4548 return list.GetEnumerator ();
4551 void ICollection.CopyTo (Array dest, int index)
4553 list.CopyTo (dest, index);
4556 int IList.Add (object value)
4558 if (! (value is ColumnHeader)) {
4559 throw new ArgumentException ("Not of type ColumnHeader", "value");
4562 return this.Add ((ColumnHeader) value);
4565 bool IList.Contains (object value)
4567 if (! (value is ColumnHeader)) {
4568 throw new ArgumentException ("Not of type ColumnHeader", "value");
4571 return this.Contains ((ColumnHeader) value);
4574 int IList.IndexOf (object value)
4576 if (! (value is ColumnHeader)) {
4577 throw new ArgumentException ("Not of type ColumnHeader", "value");
4580 return this.IndexOf ((ColumnHeader) value);
4583 void IList.Insert (int index, object value)
4585 if (! (value is ColumnHeader)) {
4586 throw new ArgumentException ("Not of type ColumnHeader", "value");
4589 this.Insert (index, (ColumnHeader) value);
4592 void IList.Remove (object value)
4594 if (! (value is ColumnHeader)) {
4595 throw new ArgumentException ("Not of type ColumnHeader", "value");
4598 this.Remove ((ColumnHeader) value);
4601 public int IndexOf (ColumnHeader value)
4603 return list.IndexOf (value);
4607 public virtual int IndexOfKey (string key)
4609 if (key == null || key.Length == 0)
4612 for (int i = 0; i < list.Count; i++) {
4613 ColumnHeader col = (ColumnHeader) list [i];
4614 if (String.Compare (key, col.Name, true) == 0)
4622 public void Insert (int index, ColumnHeader value)
4624 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4625 // but it's really only greater.
4626 if (index < 0 || index > list.Count)
4627 throw new ArgumentOutOfRangeException ("index");
4629 list.Insert (index, value);
4630 owner.AddColumn (value, index, true);
4634 public void Insert (int index, string text)
4636 Insert (index, String.Empty, text);
4639 public void Insert (int index, string text, int width)
4641 Insert (index, String.Empty, text, width);
4644 public void Insert (int index, string key, string text)
4646 ColumnHeader colHeader = new ColumnHeader ();
4647 colHeader.Name = key;
4648 colHeader.Text = text;
4649 Insert (index, colHeader);
4652 public void Insert (int index, string key, string text, int width)
4654 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4655 Insert (index, colHeader);
4658 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4660 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4661 colHeader.ImageIndex = imageIndex;
4662 Insert (index, colHeader);
4665 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4667 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4668 colHeader.ImageKey = imageKey;
4669 Insert (index, colHeader);
4673 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4675 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4676 this.Insert (index, colHeader);
4679 public virtual void Remove (ColumnHeader column)
4681 if (!Contains (column))
4684 list.Remove (column);
4685 column.SetListView (null);
4687 int rem_display_index = column.InternalDisplayIndex;
4688 int [] display_indices = new int [list.Count];
4689 for (int i = 0; i < display_indices.Length; i++) {
4690 ColumnHeader col = (ColumnHeader) list [i];
4691 int display_index = col.InternalDisplayIndex;
4692 if (display_index < rem_display_index) {
4693 display_indices [i] = display_index;
4695 display_indices [i] = (display_index - 1);
4699 column.InternalDisplayIndex = -1;
4700 owner.ReorderColumns (display_indices, true);
4704 public virtual void RemoveByKey (string key)
4706 int idx = IndexOfKey (key);
4712 public virtual void RemoveAt (int index)
4714 if (index < 0 || index >= list.Count)
4715 throw new ArgumentOutOfRangeException ("index");
4717 ColumnHeader col = (ColumnHeader) list [index];
4720 #endregion // Public Methods
4723 } // ColumnHeaderCollection
4726 [ListBindable (false)]
4728 public class ListViewItemCollection : IList, ICollection, IEnumerable
4730 private readonly ArrayList list;
4731 private ListView owner;
4733 private ListViewGroup group;
4736 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4737 // In the later case ListViewItem.ListView never gets modified
4738 private bool is_main_collection = true;
4740 #region Public Constructor
4741 public ListViewItemCollection (ListView owner)
4743 list = new ArrayList (0);
4746 #endregion // Public Constructor
4749 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4752 is_main_collection = false;
4756 #region Public Properties
4761 if (owner != null && owner.VirtualMode)
4762 return owner.VirtualListSize;
4769 public bool IsReadOnly {
4770 get { return false; }
4773 public virtual ListViewItem this [int displayIndex] {
4775 if (displayIndex < 0 || displayIndex >= Count)
4776 throw new ArgumentOutOfRangeException ("displayIndex");
4779 if (owner != null && owner.VirtualMode)
4780 return RetrieveVirtualItemFromOwner (displayIndex);
4782 return (ListViewItem) list [displayIndex];
4786 if (displayIndex < 0 || displayIndex >= Count)
4787 throw new ArgumentOutOfRangeException ("displayIndex");
4790 if (owner != null && owner.VirtualMode)
4791 throw new InvalidOperationException ();
4794 if (list.Contains (value))
4795 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4797 if (value.ListView != null && value.ListView != owner)
4798 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");
4800 if (is_main_collection)
4801 value.Owner = owner;
4804 if (value.Group != null)
4805 value.Group.Items.Remove (value);
4807 value.SetGroup (group);
4811 list [displayIndex] = value;
4812 CollectionChanged (true);
4817 public virtual ListViewItem this [string key] {
4819 int idx = IndexOfKey (key);
4828 bool ICollection.IsSynchronized {
4829 get { return true; }
4832 object ICollection.SyncRoot {
4833 get { return this; }
4836 bool IList.IsFixedSize {
4837 get { return list.IsFixedSize; }
4840 object IList.this [int index] {
4841 get { return this [index]; }
4843 if (value is ListViewItem)
4844 this [index] = (ListViewItem) value;
4846 this [index] = new ListViewItem (value.ToString ());
4850 #endregion // Public Properties
4852 #region Public Methods
4853 public virtual ListViewItem Add (ListViewItem value)
4856 if (owner != null && owner.VirtualMode)
4857 throw new InvalidOperationException ();
4861 CollectionChanged (true);
4866 public virtual ListViewItem Add (string text)
4868 ListViewItem item = new ListViewItem (text);
4869 return this.Add (item);
4872 public virtual ListViewItem Add (string text, int imageIndex)
4874 ListViewItem item = new ListViewItem (text, imageIndex);
4875 return this.Add (item);
4879 public virtual ListViewItem Add (string text, string imageKey)
4881 ListViewItem item = new ListViewItem (text, imageKey);
4882 return this.Add (item);
4885 public virtual ListViewItem Add (string key, string text, int imageIndex)
4887 ListViewItem item = new ListViewItem (text, imageIndex);
4889 return this.Add (item);
4892 public virtual ListViewItem Add (string key, string text, string imageKey)
4894 ListViewItem item = new ListViewItem (text, imageKey);
4896 return this.Add (item);
4900 public void AddRange (ListViewItem [] values)
4903 throw new ArgumentNullException ("Argument cannot be null!", "values");
4905 if (owner != null && owner.VirtualMode)
4906 throw new InvalidOperationException ();
4909 foreach (ListViewItem item in values)
4912 CollectionChanged (true);
4916 public void AddRange (ListViewItemCollection items)
4919 throw new ArgumentNullException ("Argument cannot be null!", "items");
4921 ListViewItem[] itemArray = new ListViewItem[items.Count];
4922 items.CopyTo (itemArray,0);
4923 this.AddRange (itemArray);
4927 public virtual void Clear ()
4930 if (owner != null && owner.VirtualMode)
4931 throw new InvalidOperationException ();
4933 if (is_main_collection && owner != null) {
4934 owner.SetFocusedItem (-1);
4935 owner.h_scroll.Value = owner.v_scroll.Value = 0;
4937 foreach (ListViewItem item in list) {
4938 owner.item_control.CancelEdit (item);
4945 foreach (ListViewItem item in list)
4946 item.SetGroup (null);
4950 CollectionChanged (false);
4953 public bool Contains (ListViewItem item)
4955 return IndexOf (item) != -1;
4959 public virtual bool ContainsKey (string key)
4961 return IndexOfKey (key) != -1;
4965 public void CopyTo (Array dest, int index)
4967 list.CopyTo (dest, index);
4971 public ListViewItem [] Find (string key, bool searchAllSubitems)
4974 return new ListViewItem [0];
4976 List<ListViewItem> temp_list = new List<ListViewItem> ();
4978 for (int i = 0; i < list.Count; i++) {
4979 ListViewItem lvi = (ListViewItem) list [i];
4980 if (String.Compare (key, lvi.Name, true) == 0)
4981 temp_list.Add (lvi);
4984 ListViewItem [] retval = new ListViewItem [temp_list.Count];
4985 temp_list.CopyTo (retval);
4991 public IEnumerator GetEnumerator ()
4994 if (owner != null && owner.VirtualMode)
4995 throw new InvalidOperationException ();
4998 return list.GetEnumerator ();
5001 int IList.Add (object item)
5007 if (owner != null && owner.VirtualMode)
5008 throw new InvalidOperationException ();
5011 if (item is ListViewItem) {
5012 li = (ListViewItem) item;
5013 if (list.Contains (li))
5014 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5016 if (li.ListView != null && li.ListView != owner)
5017 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");
5020 li = new ListViewItem (item.ToString ());
5023 result = list.Add (li);
5024 CollectionChanged (true);
5029 bool IList.Contains (object item)
5031 return Contains ((ListViewItem) item);
5034 int IList.IndexOf (object item)
5036 return IndexOf ((ListViewItem) item);
5039 void IList.Insert (int index, object item)
5041 if (item is ListViewItem)
5042 this.Insert (index, (ListViewItem) item);
5044 this.Insert (index, item.ToString ());
5047 void IList.Remove (object item)
5049 Remove ((ListViewItem) item);
5052 public int IndexOf (ListViewItem item)
5055 if (owner != null && owner.VirtualMode) {
5056 for (int i = 0; i < Count; i++)
5057 if (RetrieveVirtualItemFromOwner (i) == item)
5064 return list.IndexOf (item);
5068 public virtual int IndexOfKey (string key)
5070 if (key == null || key.Length == 0)
5073 for (int i = 0; i < Count; i++) {
5074 ListViewItem lvi = this [i];
5075 if (String.Compare (key, lvi.Name, true) == 0)
5083 public ListViewItem Insert (int index, ListViewItem item)
5085 if (index < 0 || index > list.Count)
5086 throw new ArgumentOutOfRangeException ("index");
5089 if (owner != null && owner.VirtualMode)
5090 throw new InvalidOperationException ();
5093 if (list.Contains (item))
5094 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5096 if (item.ListView != null && item.ListView != owner)
5097 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");
5099 if (is_main_collection)
5103 if (item.Group != null)
5104 item.Group.Items.Remove (item);
5106 item.SetGroup (group);
5110 list.Insert (index, item);
5111 CollectionChanged (true);
5115 public ListViewItem Insert (int index, string text)
5117 return this.Insert (index, new ListViewItem (text));
5120 public ListViewItem Insert (int index, string text, int imageIndex)
5122 return this.Insert (index, new ListViewItem (text, imageIndex));
5126 public ListViewItem Insert (int index, string text, string imageKey)
5128 ListViewItem lvi = new ListViewItem (text, imageKey);
5129 return Insert (index, lvi);
5132 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5134 ListViewItem lvi = new ListViewItem (text, imageIndex);
5136 return Insert (index, lvi);
5139 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5141 ListViewItem lvi = new ListViewItem (text, imageKey);
5143 return Insert (index, lvi);
5147 public virtual void Remove (ListViewItem item)
5150 if (owner != null && owner.VirtualMode)
5151 throw new InvalidOperationException ();
5154 int idx = list.IndexOf (item);
5159 public virtual void RemoveAt (int index)
5161 if (index < 0 || index >= Count)
5162 throw new ArgumentOutOfRangeException ("index");
5165 if (owner != null && owner.VirtualMode)
5166 throw new InvalidOperationException ();
5169 ListViewItem item = (ListViewItem) list [index];
5171 bool selection_changed = false;
5172 if (is_main_collection && owner != null) {
5174 if (item.Focused && index + 1 == Count) // Last item
5175 owner.SetFocusedItem (index == 0 ? -1 : index - 1);
5177 selection_changed = owner.SelectedIndices.Contains (index);
5178 owner.item_control.CancelEdit (item);
5181 list.RemoveAt (index);
5183 if (is_main_collection)
5187 item.SetGroup (null);
5190 CollectionChanged (false);
5191 if (selection_changed && owner != null)
5192 owner.OnSelectedIndexChanged (EventArgs.Empty);
5196 public virtual void RemoveByKey (string key)
5198 int idx = IndexOfKey (key);
5204 #endregion // Public Methods
5206 internal ListView Owner {
5216 internal ListViewGroup Group {
5226 void AddItem (ListViewItem value)
5228 if (list.Contains (value))
5229 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5231 if (value.ListView != null && value.ListView != owner)
5232 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");
5233 if (is_main_collection)
5234 value.Owner = owner;
5237 if (value.Group != null)
5238 value.Group.Items.Remove (value);
5240 value.SetGroup (group);
5247 void CollectionChanged (bool sort)
5249 if (owner != null) {
5254 owner.Redraw (true);
5259 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5261 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5263 owner.OnRetrieveVirtualItem (args);
5264 ListViewItem retval = args.Item;
5265 retval.Owner = owner;
5266 retval.DisplayIndex = displayIndex;
5272 internal event CollectionChangedHandler Changed;
5274 internal void Sort (IComparer comparer)
5276 list.Sort (comparer);
5280 internal void OnChange ()
5282 if (Changed != null)
5285 } // ListViewItemCollection
5288 // In normal mode, the selection information resides in the Items,
5289 // making SelectedIndexCollection.List read-only
5291 // In virtual mode, SelectedIndexCollection directly saves the selection
5292 // information, instead of getting it from Items, making List read-and-write
5294 [ListBindable (false)]
5296 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5298 private readonly ListView owner;
5299 private ArrayList list;
5301 #region Public Constructor
5302 public SelectedIndexCollection (ListView owner)
5305 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5307 #endregion // Public Constructor
5309 #region Public Properties
5313 if (!owner.IsHandleCreated)
5320 public bool IsReadOnly {
5330 public int this [int index] {
5332 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5333 throw new ArgumentOutOfRangeException ("index");
5335 return (int) List [index];
5339 bool ICollection.IsSynchronized {
5340 get { return false; }
5343 object ICollection.SyncRoot {
5344 get { return this; }
5347 bool IList.IsFixedSize {
5357 object IList.this [int index] {
5358 get { return this [index]; }
5359 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5361 #endregion // Public Properties
5363 #region Public Methods
5365 public int Add (int itemIndex)
5367 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5368 throw new ArgumentOutOfRangeException ("index");
5370 if (owner.virtual_mode && !owner.IsHandleCreated)
5373 owner.Items [itemIndex].Selected = true;
5375 if (!owner.IsHandleCreated)
5389 if (!owner.IsHandleCreated)
5392 int [] indexes = (int []) List.ToArray (typeof (int));
5393 foreach (int index in indexes)
5394 owner.Items [index].Selected = false;
5397 public bool Contains (int selectedIndex)
5399 return IndexOf (selectedIndex) != -1;
5402 public void CopyTo (Array dest, int index)
5404 List.CopyTo (dest, index);
5407 public IEnumerator GetEnumerator ()
5409 return List.GetEnumerator ();
5412 int IList.Add (object value)
5414 throw new NotSupportedException ("Add operation is not supported.");
5422 bool IList.Contains (object selectedIndex)
5424 if (!(selectedIndex is int))
5426 return Contains ((int) selectedIndex);
5429 int IList.IndexOf (object selectedIndex)
5431 if (!(selectedIndex is int))
5433 return IndexOf ((int) selectedIndex);
5436 void IList.Insert (int index, object value)
5438 throw new NotSupportedException ("Insert operation is not supported.");
5441 void IList.Remove (object value)
5443 throw new NotSupportedException ("Remove operation is not supported.");
5446 void IList.RemoveAt (int index)
5448 throw new NotSupportedException ("RemoveAt operation is not supported.");
5451 public int IndexOf (int selectedIndex)
5453 if (!owner.IsHandleCreated)
5456 return List.IndexOf (selectedIndex);
5460 public void Remove (int itemIndex)
5462 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5463 throw new ArgumentOutOfRangeException ("itemIndex");
5465 owner.Items [itemIndex].Selected = false;
5468 #endregion // Public Methods
5470 internal ArrayList List {
5473 list = new ArrayList ();
5475 if (!owner.VirtualMode)
5477 for (int i = 0; i < owner.Items.Count; i++) {
5478 if (owner.Items [i].Selected)
5486 internal void Reset ()
5488 // force re-population of list
5490 if (!owner.VirtualMode)
5495 private void ItemsCollection_Changed ()
5501 internal void RemoveIndex (int index)
5503 int idx = List.BinarySearch (index);
5505 List.RemoveAt (idx);
5508 // actually store index in the collection
5509 // also, keep the collection sorted, as .Net does
5510 internal void InsertIndex (int index)
5513 int iMax = List.Count - 1;
5514 while (iMin <= iMax) {
5515 int iMid = (iMin + iMax) / 2;
5516 int current_index = (int) List [iMid];
5518 if (current_index == index)
5519 return; // Already added
5520 if (current_index > index)
5526 List.Insert (iMin, index);
5530 } // SelectedIndexCollection
5533 [ListBindable (false)]
5535 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5537 private readonly ListView owner;
5539 #region Public Constructor
5540 public SelectedListViewItemCollection (ListView owner)
5544 #endregion // Public Constructor
5546 #region Public Properties
5550 return owner.SelectedIndices.Count;
5554 public bool IsReadOnly {
5555 get { return true; }
5558 public ListViewItem this [int index] {
5560 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5561 throw new ArgumentOutOfRangeException ("index");
5563 int item_index = owner.SelectedIndices [index];
5564 return owner.Items [item_index];
5569 public virtual ListViewItem this [string key] {
5571 int idx = IndexOfKey (key);
5580 bool ICollection.IsSynchronized {
5581 get { return false; }
5584 object ICollection.SyncRoot {
5585 get { return this; }
5588 bool IList.IsFixedSize {
5589 get { return true; }
5592 object IList.this [int index] {
5593 get { return this [index]; }
5594 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5596 #endregion // Public Properties
5598 #region Public Methods
5599 public void Clear ()
5601 owner.SelectedIndices.Clear ();
5604 public bool Contains (ListViewItem item)
5606 return IndexOf (item) != -1;
5610 public virtual bool ContainsKey (string key)
5612 return IndexOfKey (key) != -1;
5616 public void CopyTo (Array dest, int index)
5618 if (!owner.IsHandleCreated)
5620 if (index > Count) // Throws ArgumentException instead of IOOR exception
5621 throw new ArgumentException ("index");
5623 for (int i = 0; i < Count; i++)
5624 dest.SetValue (this [i], index++);
5627 public IEnumerator GetEnumerator ()
5629 if (!owner.IsHandleCreated)
5630 return (new ListViewItem [0]).GetEnumerator ();
5632 ListViewItem [] items = new ListViewItem [Count];
5633 for (int i = 0; i < Count; i++)
5634 items [i] = this [i];
5636 return items.GetEnumerator ();
5639 int IList.Add (object value)
5641 throw new NotSupportedException ("Add operation is not supported.");
5644 bool IList.Contains (object item)
5646 if (!(item is ListViewItem))
5648 return Contains ((ListViewItem) item);
5651 int IList.IndexOf (object item)
5653 if (!(item is ListViewItem))
5655 return IndexOf ((ListViewItem) item);
5658 void IList.Insert (int index, object value)
5660 throw new NotSupportedException ("Insert operation is not supported.");
5663 void IList.Remove (object value)
5665 throw new NotSupportedException ("Remove operation is not supported.");
5668 void IList.RemoveAt (int index)
5670 throw new NotSupportedException ("RemoveAt operation is not supported.");
5673 public int IndexOf (ListViewItem item)
5675 if (!owner.IsHandleCreated)
5678 for (int i = 0; i < Count; i++)
5679 if (this [i] == item)
5686 public virtual int IndexOfKey (string key)
5688 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5691 for (int i = 0; i < Count; i++) {
5692 ListViewItem item = this [i];
5693 if (String.Compare (item.Name, key, true) == 0)
5700 #endregion // Public Methods
5702 } // SelectedListViewItemCollection
5704 internal delegate void CollectionChangedHandler ();
5706 struct ItemMatrixLocation
5711 public ItemMatrixLocation (int row, int col)
5738 #endregion // Subclasses
5740 protected override void OnResize (EventArgs e)
5745 protected override void OnMouseLeave (EventArgs e)
5747 base.OnMouseLeave (e);
5751 // ColumnReorder event
5753 static object ColumnReorderedEvent = new object ();
5754 public event ColumnReorderedEventHandler ColumnReordered {
5755 add { Events.AddHandler (ColumnReorderedEvent, value); }
5756 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5759 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5761 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5768 // ColumnWidthChanged
5770 static object ColumnWidthChangedEvent = new object ();
5771 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5772 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5773 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5776 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5778 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5783 void RaiseColumnWidthChanged (int resize_column)
5785 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5787 OnColumnWidthChanged (n);
5791 // ColumnWidthChanging
5793 static object ColumnWidthChangingEvent = new object ();
5794 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5795 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5796 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5799 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5801 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5807 // 2.0 profile based implementation
5809 bool CanProceedWithResize (ColumnHeader col, int width)
5811 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5815 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5816 cwceh (this, changing);
5817 return !changing.Cancel;
5821 // 1.0 profile based implementation
5823 bool CanProceedWithResize (ColumnHeader col, int width)
5828 void RaiseColumnWidthChanged (int resize_column)