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)
2620 drag_begin = new Point (me.X, me.Y);
2622 Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
2623 if (!r.Contains (me.X, me.Y)) {
2624 dragged_item_index = item.Index;
2627 owner.OnItemDrag (new ItemDragEventArgs (me.Button, item));
2629 drag_begin = new Point (-1, -1);
2630 dragged_item_index = -1;
2636 if (owner.ShowItemToolTips) {
2638 owner.item_tooltip.Active = false;
2639 prev_tooltip_item = null;
2640 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2641 owner.item_tooltip.Active = true;
2642 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2643 prev_tooltip_item = item;
2650 private void ItemsMouseHover (object sender, EventArgs e)
2652 if (owner.hover_pending) {
2653 owner.OnMouseHover (e);
2654 owner.hover_pending = false;
2660 hover_processed = true;
2661 Point pt = PointToClient (Control.MousePosition);
2662 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2666 prev_hovered_item = item;
2668 if (owner.HoverSelection) {
2669 if (owner.MultiSelect)
2670 owner.UpdateMultiSelection (item.Index, true);
2672 item.Selected = true;
2674 owner.SetFocusedItem (item.Index);
2675 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2679 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2683 void HandleClicks (MouseEventArgs me)
2685 // if the click is not on an item,
2686 // clicks remains as 0
2689 owner.OnDoubleClick (EventArgs.Empty);
2690 } else if (clicks == 1) {
2691 owner.OnClick (EventArgs.Empty);
2693 owner.OnDoubleClick (EventArgs.Empty);
2694 owner.OnMouseDoubleClick (me);
2695 } else if (clicks == 1) {
2696 owner.OnClick (EventArgs.Empty);
2697 owner.OnMouseClick (me);
2704 private void ItemsMouseUp (object sender, MouseEventArgs me)
2706 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2707 HandleClicks (owner_me);
2710 if (owner.Items.Count == 0) {
2712 owner.OnMouseUp (owner_me);
2716 Point pt = new Point (me.X, me.Y);
2718 Rectangle rect = Rectangle.Empty;
2719 if (clicked_item != null) {
2720 if (owner.view == View.Details && !owner.full_row_select)
2721 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2723 rect = clicked_item.Bounds;
2725 if (rect.Contains (pt)) {
2726 switch (owner.activation) {
2727 case ItemActivation.OneClick:
2728 owner.OnItemActivate (EventArgs.Empty);
2731 case ItemActivation.TwoClick:
2732 if (last_clicked_item == clicked_item) {
2733 owner.OnItemActivate (EventArgs.Empty);
2734 last_clicked_item = null;
2736 last_clicked_item = clicked_item;
2739 // DoubleClick activation is handled in another handler
2743 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2744 // Need this to clean up background clicks
2745 owner.SelectedItems.Clear ();
2749 owner.OnMouseUp (owner_me);
2752 private void ResetMouseState ()
2754 clicked_item = null;
2755 box_select_start = Point.Empty;
2756 BoxSelectRectangle = Rectangle.Empty;
2757 prev_selection = null;
2758 box_select_mode = BoxSelect.None;
2762 private void LabelEditFinished (object sender, EventArgs e)
2764 EndEdit (edit_item);
2767 private void LabelEditCancelled (object sender, EventArgs e)
2769 edit_args.SetLabel (null);
2770 EndEdit (edit_item);
2773 private void LabelTextChanged (object sender, EventArgs e)
2775 if (edit_args != null)
2776 edit_args.SetLabel (edit_text_box.Text);
2779 internal void BeginEdit (ListViewItem item)
2781 if (edit_item != null)
2782 EndEdit (edit_item);
2784 if (edit_text_box == null) {
2785 edit_text_box = new ListViewLabelEditTextBox ();
2786 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2787 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2788 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2789 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2790 edit_text_box.Visible = false;
2791 Controls.Add (edit_text_box);
2794 item.EnsureVisible();
2796 edit_text_box.Reset ();
2798 switch (owner.view) {
2800 case View.SmallIcon:
2802 edit_text_box.TextAlign = HorizontalAlignment.Left;
2803 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2804 SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
2805 edit_text_box.Width = (int)sizef.Width + 4;
2806 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2807 edit_text_box.WordWrap = false;
2808 edit_text_box.Multiline = false;
2810 case View.LargeIcon:
2811 edit_text_box.TextAlign = HorizontalAlignment.Center;
2812 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2813 sizef = TextRenderer.MeasureString (item.Text, item.Font);
2814 edit_text_box.Width = (int)sizef.Width + 4;
2815 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2816 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2817 edit_text_box.WordWrap = true;
2818 edit_text_box.Multiline = true;
2824 edit_text_box.Text = item.Text;
2825 edit_text_box.Font = item.Font;
2826 edit_text_box.Visible = true;
2827 edit_text_box.Focus ();
2828 edit_text_box.SelectAll ();
2830 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2831 owner.OnBeforeLabelEdit (edit_args);
2833 if (edit_args.CancelEdit)
2837 internal void CancelEdit (ListViewItem item)
2839 // do nothing if there's no item being edited, or if the
2840 // item being edited is not the one passed in
2841 if (edit_item == null || edit_item != item)
2844 edit_args.SetLabel (null);
2848 internal void EndEdit (ListViewItem item)
2850 // do nothing if there's no item being edited, or if the
2851 // item being edited is not the one passed in
2852 if (edit_item == null || edit_item != item)
2855 if (edit_text_box != null) {
2856 if (edit_text_box.Visible)
2857 edit_text_box.Visible = false;
2858 // ensure listview gets focus
2862 // Same as TreeView.EndEdit: need to have focus in synch
2863 Application.DoEvents ();
2866 // Create a new instance, since we could get a call to BeginEdit
2867 // from the handler and have fields out of synch
2869 LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
2872 owner.OnAfterLabelEdit (args);
2873 if (!args.CancelEdit && args.Label != null)
2874 item.Text = args.Label;
2877 internal override void OnPaintInternal (PaintEventArgs pe)
2879 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2882 protected override void WndProc (ref Message m)
2884 switch ((Msg)m.Msg) {
2885 case Msg.WM_KILLFOCUS:
2886 owner.Select (false, true);
2888 case Msg.WM_SETFOCUS:
2889 owner.Select (false, true);
2891 case Msg.WM_LBUTTONDOWN:
2893 owner.Select (false, true);
2895 case Msg.WM_RBUTTONDOWN:
2897 owner.Select (false, true);
2902 base.WndProc (ref m);
2906 internal class ListViewLabelEditTextBox : TextBox
2911 int max_height = -1;
2912 int min_height = -1;
2914 int old_number_lines = 1;
2916 SizeF text_size_one_char;
2918 public ListViewLabelEditTextBox ()
2920 min_height = DefaultSize.Height;
2921 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2924 public int MaxWidth {
2926 if (value < min_width)
2927 max_width = min_width;
2933 public int MaxHeight {
2935 if (value < min_height)
2936 max_height = min_height;
2942 public new int Width {
2952 public override Font Font {
2958 text_size_one_char = TextRenderer.MeasureString ("B", Font);
2962 protected override void OnTextChanged (EventArgs e)
2964 SizeF text_size = TextRenderer.MeasureString (Text, Font);
2966 int new_width = (int)text_size.Width + 8;
2969 ResizeTextBoxWidth (new_width);
2971 if (Width != max_width)
2972 ResizeTextBoxWidth (new_width);
2974 int number_lines = Lines.Length;
2976 if (number_lines != old_number_lines) {
2977 int new_height = number_lines * (int)text_size_one_char.Height + 4;
2978 old_number_lines = number_lines;
2980 ResizeTextBoxHeight (new_height);
2984 base.OnTextChanged (e);
2987 protected override bool IsInputKey (Keys key_data)
2989 if ((key_data & Keys.Alt) == 0) {
2990 switch (key_data & Keys.KeyCode) {
2997 return base.IsInputKey (key_data);
3000 protected override void OnKeyDown (KeyEventArgs e)
3005 switch (e.KeyCode) {
3009 OnEditingFinished (e);
3014 OnEditingCancelled (e);
3019 protected override void OnLostFocus (EventArgs e)
3022 OnEditingFinished (e);
3026 protected void OnEditingCancelled (EventArgs e)
3028 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
3033 protected void OnEditingFinished (EventArgs e)
3035 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
3040 private void ResizeTextBoxWidth (int new_width)
3042 if (new_width > max_width)
3043 base.Width = max_width;
3045 if (new_width >= min_width)
3046 base.Width = new_width;
3048 base.Width = min_width;
3051 private void ResizeTextBoxHeight (int new_height)
3053 if (new_height > max_height)
3054 base.Height = max_height;
3056 if (new_height >= min_height)
3057 base.Height = new_height;
3059 base.Height = min_height;
3062 public void Reset ()
3069 old_number_lines = 1;
3071 Text = String.Empty;
3076 static object EditingCancelledEvent = new object ();
3077 public event EventHandler EditingCancelled {
3078 add { Events.AddHandler (EditingCancelledEvent, value); }
3079 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
3082 static object EditingFinishedEvent = new object ();
3083 public event EventHandler EditingFinished {
3084 add { Events.AddHandler (EditingFinishedEvent, value); }
3085 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
3089 internal override void OnPaintInternal (PaintEventArgs pe)
3094 CalculateScrollBars ();
3097 void FocusChanged (object o, EventArgs args)
3099 if (Items.Count == 0)
3102 if (FocusedItem == null)
3105 ListViewItem focused_item = FocusedItem;
3107 if (focused_item.ListView != null) {
3108 item_control.Invalidate (focused_item.Bounds);
3109 focused_item.Layout ();
3110 item_control.Invalidate (focused_item.Bounds);
3114 private void ListView_MouseEnter (object sender, EventArgs args)
3116 hover_pending = true; // Need a hover event for every Enter/Leave cycle
3119 private void ListView_MouseWheel (object sender, MouseEventArgs me)
3121 if (Items.Count == 0)
3124 int lines = me.Delta / 120;
3131 case View.SmallIcon:
3132 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
3134 case View.LargeIcon:
3135 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
3138 Scroll (h_scroll, -ItemSize.Width * lines);
3142 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
3148 private void ListView_SizeChanged (object sender, EventArgs e)
3150 CalculateListView (alignment);
3153 private void SetFocusedItem (int index)
3156 items [index].Focused = true;
3157 else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
3158 items [focused_item_index].Focused = false;
3160 focused_item_index = index;
3163 private void HorizontalScroller (object sender, EventArgs e)
3165 item_control.EndEdit (item_control.edit_item);
3167 // Avoid unnecessary flickering, when button is
3168 // kept pressed at the end
3169 if (h_marker != h_scroll.Value) {
3171 int pixels = h_marker - h_scroll.Value;
3173 h_marker = h_scroll.Value;
3174 if (header_control.Visible)
3175 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
3177 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
3181 private void VerticalScroller (object sender, EventArgs e)
3183 item_control.EndEdit (item_control.edit_item);
3185 // Avoid unnecessary flickering, when button is
3186 // kept pressed at the end
3187 if (v_marker != v_scroll.Value) {
3188 int pixels = v_marker - v_scroll.Value;
3189 Rectangle area = item_control.ClientRectangle;
3190 if (header_control.Visible) {
3191 area.Y += header_control.Height;
3192 area.Height -= header_control.Height;
3195 v_marker = v_scroll.Value;
3196 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
3200 internal override bool IsInputCharInternal (char charCode)
3204 #endregion // Internal Methods Properties
3206 #region Protected Methods
3207 protected override void CreateHandle ()
3209 base.CreateHandle ();
3210 for (int i = 0; i < SelectedItems.Count; i++)
3211 OnSelectedIndexChanged (EventArgs.Empty);
3214 protected override void Dispose (bool disposing)
3217 h_scroll.Dispose ();
3218 v_scroll.Dispose ();
3220 large_image_list = null;
3221 small_image_list = null;
3222 state_image_list = null;
3224 foreach (ColumnHeader col in columns)
3225 col.SetListView (null);
3228 if (!virtual_mode) // In virtual mode we don't save the items
3230 foreach (ListViewItem item in items)
3234 base.Dispose (disposing);
3237 protected override bool IsInputKey (Keys keyData)
3254 return base.IsInputKey (keyData);
3257 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
3259 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
3265 protected override void OnBackgroundImageChanged (EventArgs args)
3267 item_control.BackgroundImage = BackgroundImage;
3268 base.OnBackgroundImageChanged (args);
3272 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
3274 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
3279 protected virtual void OnColumnClick (ColumnClickEventArgs e)
3281 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
3287 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
3289 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
3294 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
3296 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
3301 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
3303 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
3309 protected override void OnEnabledChanged (EventArgs e)
3311 base.OnEnabledChanged (e);
3315 protected override void OnFontChanged (EventArgs e)
3317 base.OnFontChanged (e);
3321 protected override void OnHandleCreated (EventArgs e)
3323 base.OnHandleCreated (e);
3324 CalculateListView (alignment);
3326 if (!virtual_mode) // Sorting is not allowed in virtual mode
3331 protected override void OnHandleDestroyed (EventArgs e)
3333 base.OnHandleDestroyed (e);
3336 protected virtual void OnItemActivate (EventArgs e)
3338 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
3343 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
3345 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
3351 protected internal virtual void OnItemChecked (ItemCheckedEventArgs icea)
3353 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
3359 protected virtual void OnItemDrag (ItemDragEventArgs e)
3361 ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
3367 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs args)
3369 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
3374 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs args)
3376 ListViewItemSelectionChangedEventHandler eh =
3377 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
3382 protected override void OnMouseHover (EventArgs args)
3384 base.OnMouseHover (args);
3387 protected override void OnParentChanged (EventArgs args)
3389 base.OnParentChanged (args);
3393 protected virtual void OnSelectedIndexChanged (EventArgs e)
3395 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
3400 protected override void OnSystemColorsChanged (EventArgs e)
3402 base.OnSystemColorsChanged (e);
3406 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs args)
3408 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
3413 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs args)
3415 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
3420 [EditorBrowsable (EditorBrowsableState.Advanced)]
3421 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
3423 EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
3428 protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs args)
3430 SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
3435 protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs args)
3437 ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
3438 (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
3444 protected void RealizeProperties ()
3449 protected void UpdateExtendedStyles ()
3454 bool refocusing = false;
3456 protected override void WndProc (ref Message m)
3458 switch ((Msg)m.Msg) {
3459 case Msg.WM_KILLFOCUS:
3460 Control receiver = Control.FromHandle (m.WParam);
3461 if (receiver == item_control) {
3467 case Msg.WM_SETFOCUS:
3477 base.WndProc (ref m);
3479 #endregion // Protected Methods
3481 #region Public Instance Methods
3482 public void ArrangeIcons ()
3484 ArrangeIcons (this.alignment);
3487 public void ArrangeIcons (ListViewAlignment alignment)
3489 // Icons are arranged only if view is set to LargeIcon or SmallIcon
3490 if (view == View.LargeIcon || view == View.SmallIcon) {
3491 this.CalculateListView (alignment);
3492 // we have done the calculations already
3493 this.Redraw (false);
3498 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
3500 if (columnIndex < 0 || columnIndex >= columns.Count)
3501 throw new ArgumentOutOfRangeException ("columnIndex");
3503 columns [columnIndex].AutoResize (headerAutoResize);
3506 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
3509 foreach (ColumnHeader col in columns)
3510 col.AutoResize (headerAutoResize);
3515 public void BeginUpdate ()
3517 // flag to avoid painting
3521 public void Clear ()
3524 items.Clear (); // Redraw (true) called here
3527 public void EndUpdate ()
3529 // flag to avoid painting
3532 // probably, now we need a redraw with recalculations
3536 public void EnsureVisible (int index)
3538 if (index < 0 || index >= items.Count || scrollable == false)
3541 Rectangle view_rect = item_control.ClientRectangle;
3542 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
3544 if (view_rect.Contains (bounds))
3547 if (View != View.Details) {
3548 if (bounds.Left < 0)
3549 h_scroll.Value += bounds.Left;
3550 else if (bounds.Right > view_rect.Right)
3551 h_scroll.Value += (bounds.Right - view_rect.Right);
3555 v_scroll.Value += bounds.Top;
3556 else if (bounds.Bottom > view_rect.Bottom)
3557 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
3561 public ListViewItem FindItemWithText (string text)
3563 if (items.Count == 0)
3566 return FindItemWithText (text, true, 0, true);
3569 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex)
3571 return FindItemWithText (text, includeSubItems, startIndex, true);
3574 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex, bool prefixSearch)
3576 if (startIndex < 0 || startIndex >= items.Count)
3577 throw new ArgumentOutOfRangeException ("startIndex");
3580 throw new ArgumentNullException ("text");
3583 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
3584 prefixSearch, includeSubItems, text, Point.Empty,
3585 SearchDirectionHint.Down, startIndex);
3587 OnSearchForVirtualItem (args);
3588 int idx = args.Index;
3589 if (idx >= 0 && idx < virtual_list_size)
3595 for (int i = startIndex; i < items.Count; i++) {
3596 ListViewItem lvi = items [i];
3598 if ((prefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3599 || String.Compare (lvi.Text, text, true) == 0) // match
3603 if (includeSubItems) {
3604 for (int i = startIndex; i < items.Count; i++) {
3605 ListViewItem lvi = items [i];
3606 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3607 if ((prefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3608 || String.Compare (sub_item.Text, text, true) == 0)
3616 public ListViewItem FindNearestItem (SearchDirectionHint direction, int x, int y)
3618 return FindNearestItem (direction, new Point (x, y));
3621 public ListViewItem FindNearestItem (SearchDirectionHint direction, Point location)
3623 if (direction < SearchDirectionHint.Left || direction > SearchDirectionHint.Down)
3624 throw new ArgumentOutOfRangeException ("searchDirection");
3626 if (view != View.LargeIcon && view != View.SmallIcon)
3627 throw new InvalidOperationException ();
3630 SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
3631 false, false, String.Empty, location,
3634 OnSearchForVirtualItem (args);
3635 int idx = args.Index;
3636 if (idx >= 0 && idx < virtual_list_size)
3642 ListViewItem item = null;
3643 int min_dist = Int32.MaxValue;
3646 // It looks like .Net does a previous adjustment
3648 switch (direction) {
3649 case SearchDirectionHint.Up:
3650 location.Y -= item_size.Height;
3652 case SearchDirectionHint.Down:
3653 location.Y += item_size.Height;
3655 case SearchDirectionHint.Left:
3656 location.X -= item_size.Width;
3658 case SearchDirectionHint.Right:
3659 location.X += item_size.Width;
3663 for (int i = 0; i < items.Count; i++) {
3664 Point item_loc = GetItemLocation (i);
3666 if (direction == SearchDirectionHint.Up) {
3667 if (location.Y < item_loc.Y)
3669 } else if (direction == SearchDirectionHint.Down) {
3670 if (location.Y > item_loc.Y)
3672 } else if (direction == SearchDirectionHint.Left) {
3673 if (location.X < item_loc.X)
3675 } else if (direction == SearchDirectionHint.Right) {
3676 if (location.X > item_loc.X)
3680 int x_dist = location.X - item_loc.X;
3681 int y_dist = location.Y - item_loc.Y;
3683 int dist = x_dist * x_dist + y_dist * y_dist;
3684 if (dist < min_dist) {
3694 public ListViewItem GetItemAt (int x, int y)
3696 Size item_size = ItemSize;
3697 for (int i = 0; i < items.Count; i++) {
3698 Point item_location = GetItemLocation (i);
3699 Rectangle item_rect = new Rectangle (item_location, item_size);
3700 if (item_rect.Contains (x, y))
3707 public Rectangle GetItemRect (int index)
3709 return GetItemRect (index, ItemBoundsPortion.Entire);
3712 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3714 if (index < 0 || index >= items.Count)
3715 throw new IndexOutOfRangeException ("index");
3717 return items [index].GetBounds (portion);
3721 public ListViewHitTestInfo HitTest (Point pt)
3723 return HitTest (pt.X, pt.Y);
3726 public ListViewHitTestInfo HitTest (int x, int y)
3729 throw new ArgumentOutOfRangeException ("x");
3731 throw new ArgumentOutOfRangeException ("y");
3733 ListViewItem item = GetItemAt (x, y);
3735 return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
3737 ListViewHitTestLocations locations = 0;
3738 if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
3739 locations |= ListViewHitTestLocations.Label;
3740 else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
3741 locations |= ListViewHitTestLocations.Image;
3742 else if (item.CheckRectReal.Contains (x, y))
3743 locations |= ListViewHitTestLocations.StateImage;
3745 ListViewItem.ListViewSubItem subitem = null;
3746 if (view == View.Details)
3747 foreach (ListViewItem.ListViewSubItem si in item.SubItems)
3748 if (si.Bounds.Contains (x, y)) {
3753 return new ListViewHitTestInfo (item, subitem, locations);
3756 [EditorBrowsable (EditorBrowsableState.Advanced)]
3757 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3759 if (startIndex < 0 || startIndex >= items.Count)
3760 throw new ArgumentOutOfRangeException ("startIndex");
3761 if (endIndex < 0 || endIndex >= items.Count)
3762 throw new ArgumentOutOfRangeException ("endIndex");
3763 if (startIndex > endIndex)
3764 throw new ArgumentException ("startIndex");
3769 for (int i = startIndex; i <= endIndex; i++)
3770 item_control.Invalidate (items [i].Bounds);
3772 if (!invalidateOnly)
3781 throw new InvalidOperationException ();
3787 // we need this overload to reuse the logic for sorting, while allowing
3788 // redrawing to be done by caller or have it done by this method when
3789 // sorting is really performed
3791 // ListViewItemCollection's Add and AddRange methods call this overload
3792 // with redraw set to false, as they take care of redrawing themselves
3793 // (they even want to redraw the listview if no sort is performed, as
3794 // an item was added), while ListView.Sort () only wants to redraw if
3795 // sorting was actually performed
3796 private void Sort (bool redraw)
3798 if (!IsHandleCreated || item_sorter == null) {
3802 items.Sort (item_sorter);
3807 public override string ToString ()
3809 int count = this.Items.Count;
3812 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3814 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3816 #endregion // Public Instance Methods
3821 class HeaderControl : Control {
3824 bool column_resize_active = false;
3825 ColumnHeader resize_column;
3826 ColumnHeader clicked_column;
3827 ColumnHeader drag_column;
3829 int drag_to_index = -1;
3831 public HeaderControl (ListView owner)
3834 MouseDown += new MouseEventHandler (HeaderMouseDown);
3835 MouseMove += new MouseEventHandler (HeaderMouseMove);
3836 MouseUp += new MouseEventHandler (HeaderMouseUp);
3839 private ColumnHeader ColumnAtX (int x)
3841 Point pt = new Point (x, 0);
3842 ColumnHeader result = null;
3843 foreach (ColumnHeader col in owner.Columns) {
3844 if (col.Rect.Contains (pt)) {
3852 private int GetReorderedIndex (ColumnHeader col)
3854 if (owner.reordered_column_indices == null)
3857 for (int i = 0; i < owner.Columns.Count; i++)
3858 if (owner.reordered_column_indices [i] == col.Index)
3860 throw new Exception ("Column index missing from reordered array");
3863 private void HeaderMouseDown (object sender, MouseEventArgs me)
3865 if (resize_column != null) {
3866 column_resize_active = true;
3871 clicked_column = ColumnAtX (me.X + owner.h_marker);
3873 if (clicked_column != null) {
3875 if (owner.AllowColumnReorder) {
3877 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3878 drag_column.Rect = clicked_column.Rect;
3879 drag_to_index = GetReorderedIndex (clicked_column);
3881 clicked_column.Pressed = true;
3882 Rectangle bounds = clicked_column.Rect;
3883 bounds.X -= owner.h_marker;
3884 Invalidate (bounds);
3891 column_resize_active = false;
3892 resize_column = null;
3894 Cursor = Cursors.Default;
3897 private void HeaderMouseMove (object sender, MouseEventArgs me)
3899 Point pt = new Point (me.X + owner.h_marker, me.Y);
3901 if (column_resize_active) {
3902 int width = pt.X - resize_column.X;
3906 if (!owner.CanProceedWithResize (resize_column, width)){
3910 resize_column.Width = width;
3914 resize_column = null;
3916 if (clicked_column != null) {
3917 if (owner.AllowColumnReorder) {
3920 r = drag_column.Rect;
3921 r.X = clicked_column.Rect.X + me.X - drag_x;
3922 drag_column.Rect = r;
3924 int x = me.X + owner.h_marker;
3925 ColumnHeader over = ColumnAtX (x);
3927 drag_to_index = owner.Columns.Count;
3928 else if (x < over.X + over.Width / 2)
3929 drag_to_index = GetReorderedIndex (over);
3931 drag_to_index = GetReorderedIndex (over) + 1;
3934 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
3935 bool pressed = clicked_column.Pressed;
3936 clicked_column.Pressed = over == clicked_column;
3937 if (clicked_column.Pressed ^ pressed) {
3938 Rectangle bounds = clicked_column.Rect;
3939 bounds.X -= owner.h_marker;
3940 Invalidate (bounds);
3946 for (int i = 0; i < owner.Columns.Count; i++) {
3947 Rectangle zone = owner.Columns [i].Rect;
3948 zone.X = zone.Right - 5;
3950 if (zone.Contains (pt)) {
3951 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
3953 resize_column = owner.Columns [i];
3958 if (resize_column == null)
3959 Cursor = Cursors.Default;
3961 Cursor = Cursors.VSplit;
3964 void HeaderMouseUp (object sender, MouseEventArgs me)
3968 if (column_resize_active) {
3969 int column_idx = resize_column.Index;
3971 owner.RaiseColumnWidthChanged (column_idx);
3975 if (clicked_column != null && clicked_column.Pressed) {
3976 clicked_column.Pressed = false;
3977 Rectangle bounds = clicked_column.Rect;
3978 bounds.X -= owner.h_marker;
3979 Invalidate (bounds);
3980 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
3983 if (drag_column != null && owner.AllowColumnReorder) {
3985 if (drag_to_index > GetReorderedIndex (clicked_column))
3987 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
3988 owner.ReorderColumn (clicked_column, drag_to_index, true);
3993 clicked_column = null;
3996 internal override void OnPaintInternal (PaintEventArgs pe)
4001 Theme theme = ThemeEngine.Current;
4002 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
4004 if (drag_column == null)
4008 if (drag_to_index == owner.Columns.Count)
4009 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
4011 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
4012 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
4015 protected override void WndProc (ref Message m)
4017 switch ((Msg)m.Msg) {
4018 case Msg.WM_SETFOCUS:
4022 base.WndProc (ref m);
4028 private class ItemComparer : IComparer {
4029 readonly SortOrder sort_order;
4031 public ItemComparer (SortOrder sortOrder)
4033 sort_order = sortOrder;
4036 public int Compare (object x, object y)
4038 ListViewItem item_x = x as ListViewItem;
4039 ListViewItem item_y = y as ListViewItem;
4040 if (sort_order == SortOrder.Ascending)
4041 return String.Compare (item_x.Text, item_y.Text);
4043 return String.Compare (item_y.Text, item_x.Text);
4048 [ListBindable (false)]
4050 public class CheckedIndexCollection : IList, ICollection, IEnumerable
4052 private readonly ListView owner;
4054 #region Public Constructor
4055 public CheckedIndexCollection (ListView owner)
4059 #endregion // Public Constructor
4061 #region Public Properties
4064 get { return owner.CheckedItems.Count; }
4067 public bool IsReadOnly {
4068 get { return true; }
4071 public int this [int index] {
4073 int [] indices = GetIndices ();
4074 if (index < 0 || index >= indices.Length)
4075 throw new ArgumentOutOfRangeException ("index");
4076 return indices [index];
4080 bool ICollection.IsSynchronized {
4081 get { return false; }
4084 object ICollection.SyncRoot {
4085 get { return this; }
4088 bool IList.IsFixedSize {
4089 get { return true; }
4092 object IList.this [int index] {
4093 get { return this [index]; }
4094 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4096 #endregion // Public Properties
4098 #region Public Methods
4099 public bool Contains (int checkedIndex)
4101 int [] indices = GetIndices ();
4102 for (int i = 0; i < indices.Length; i++) {
4103 if (indices [i] == checkedIndex)
4109 public IEnumerator GetEnumerator ()
4111 int [] indices = GetIndices ();
4112 return indices.GetEnumerator ();
4115 void ICollection.CopyTo (Array dest, int index)
4117 int [] indices = GetIndices ();
4118 Array.Copy (indices, 0, dest, index, indices.Length);
4121 int IList.Add (object value)
4123 throw new NotSupportedException ("Add operation is not supported.");
4128 throw new NotSupportedException ("Clear operation is not supported.");
4131 bool IList.Contains (object checkedIndex)
4133 if (!(checkedIndex is int))
4135 return Contains ((int) checkedIndex);
4138 int IList.IndexOf (object checkedIndex)
4140 if (!(checkedIndex is int))
4142 return IndexOf ((int) checkedIndex);
4145 void IList.Insert (int index, object value)
4147 throw new NotSupportedException ("Insert operation is not supported.");
4150 void IList.Remove (object value)
4152 throw new NotSupportedException ("Remove operation is not supported.");
4155 void IList.RemoveAt (int index)
4157 throw new NotSupportedException ("RemoveAt operation is not supported.");
4160 public int IndexOf (int checkedIndex)
4162 int [] indices = GetIndices ();
4163 for (int i = 0; i < indices.Length; i++) {
4164 if (indices [i] == checkedIndex)
4169 #endregion // Public Methods
4171 private int [] GetIndices ()
4173 ArrayList checked_items = owner.CheckedItems.List;
4174 int [] indices = new int [checked_items.Count];
4175 for (int i = 0; i < checked_items.Count; i++) {
4176 ListViewItem item = (ListViewItem) checked_items [i];
4177 indices [i] = item.Index;
4181 } // CheckedIndexCollection
4184 [ListBindable (false)]
4186 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
4188 private readonly ListView owner;
4189 private ArrayList list;
4191 #region Public Constructor
4192 public CheckedListViewItemCollection (ListView owner)
4195 this.owner.Items.Changed += new CollectionChangedHandler (
4196 ItemsCollection_Changed);
4198 #endregion // Public Constructor
4200 #region Public Properties
4204 if (!owner.CheckBoxes)
4210 public bool IsReadOnly {
4211 get { return true; }
4214 public ListViewItem this [int index] {
4217 if (owner.VirtualMode)
4218 throw new InvalidOperationException ();
4220 ArrayList checked_items = List;
4221 if (index < 0 || index >= checked_items.Count)
4222 throw new ArgumentOutOfRangeException ("index");
4223 return (ListViewItem) checked_items [index];
4228 public virtual ListViewItem this [string key] {
4230 int idx = IndexOfKey (key);
4231 return idx == -1 ? null : (ListViewItem) List [idx];
4236 bool ICollection.IsSynchronized {
4237 get { return false; }
4240 object ICollection.SyncRoot {
4241 get { return this; }
4244 bool IList.IsFixedSize {
4245 get { return true; }
4248 object IList.this [int index] {
4249 get { return this [index]; }
4250 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4252 #endregion // Public Properties
4254 #region Public Methods
4255 public bool Contains (ListViewItem item)
4257 if (!owner.CheckBoxes)
4259 return List.Contains (item);
4263 public virtual bool ContainsKey (string key)
4265 return IndexOfKey (key) != -1;
4269 public void CopyTo (Array dest, int index)
4272 if (owner.VirtualMode)
4273 throw new InvalidOperationException ();
4275 if (!owner.CheckBoxes)
4277 List.CopyTo (dest, index);
4280 public IEnumerator GetEnumerator ()
4283 if (owner.VirtualMode)
4284 throw new InvalidOperationException ();
4286 if (!owner.CheckBoxes)
4287 return (new ListViewItem [0]).GetEnumerator ();
4288 return List.GetEnumerator ();
4291 int IList.Add (object value)
4293 throw new NotSupportedException ("Add operation is not supported.");
4298 throw new NotSupportedException ("Clear operation is not supported.");
4301 bool IList.Contains (object item)
4303 if (!(item is ListViewItem))
4305 return Contains ((ListViewItem) item);
4308 int IList.IndexOf (object item)
4310 if (!(item is ListViewItem))
4312 return IndexOf ((ListViewItem) item);
4315 void IList.Insert (int index, object value)
4317 throw new NotSupportedException ("Insert operation is not supported.");
4320 void IList.Remove (object value)
4322 throw new NotSupportedException ("Remove operation is not supported.");
4325 void IList.RemoveAt (int index)
4327 throw new NotSupportedException ("RemoveAt operation is not supported.");
4330 public int IndexOf (ListViewItem item)
4333 if (owner.VirtualMode)
4334 throw new InvalidOperationException ();
4336 if (!owner.CheckBoxes)
4338 return List.IndexOf (item);
4342 public virtual int IndexOfKey (string key)
4345 if (owner.VirtualMode)
4346 throw new InvalidOperationException ();
4348 if (key == null || key.Length == 0)
4351 ArrayList checked_items = List;
4352 for (int i = 0; i < checked_items.Count; i++) {
4353 ListViewItem item = (ListViewItem) checked_items [i];
4354 if (String.Compare (key, item.Name, true) == 0)
4361 #endregion // Public Methods
4363 internal ArrayList List {
4366 list = new ArrayList ();
4367 foreach (ListViewItem item in owner.Items) {
4376 internal void Reset ()
4378 // force re-population of list
4382 private void ItemsCollection_Changed ()
4386 } // CheckedListViewItemCollection
4389 [ListBindable (false)]
4391 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
4393 internal ArrayList list;
4394 private ListView owner;
4396 #region Public Constructor
4397 public ColumnHeaderCollection (ListView owner)
4399 list = new ArrayList ();
4402 #endregion // Public Constructor
4404 #region Public Properties
4407 get { return list.Count; }
4410 public bool IsReadOnly {
4411 get { return false; }
4414 public virtual ColumnHeader this [int index] {
4416 if (index < 0 || index >= list.Count)
4417 throw new ArgumentOutOfRangeException ("index");
4418 return (ColumnHeader) list [index];
4423 public virtual ColumnHeader this [string key] {
4425 int idx = IndexOfKey (key);
4429 return (ColumnHeader) list [idx];
4434 bool ICollection.IsSynchronized {
4435 get { return true; }
4438 object ICollection.SyncRoot {
4439 get { return this; }
4442 bool IList.IsFixedSize {
4443 get { return list.IsFixedSize; }
4446 object IList.this [int index] {
4447 get { return this [index]; }
4448 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4450 #endregion // Public Properties
4452 #region Public Methods
4453 public virtual int Add (ColumnHeader value)
4455 int idx = list.Add (value);
4456 owner.AddColumn (value, idx, true);
4460 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
4462 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4463 this.Add (colHeader);
4468 public virtual ColumnHeader Add (string text)
4470 return Add (String.Empty, text);
4473 public virtual ColumnHeader Add (string text, int iwidth)
4475 return Add (String.Empty, text, iwidth);
4478 public virtual ColumnHeader Add (string key, string text)
4480 ColumnHeader colHeader = new ColumnHeader ();
4481 colHeader.Name = key;
4482 colHeader.Text = text;
4487 public virtual ColumnHeader Add (string key, string text, int iwidth)
4489 return Add (key, text, iwidth, HorizontalAlignment.Left, -1);
4492 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, int imageIndex)
4494 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
4495 colHeader.ImageIndex = imageIndex;
4500 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, string imageKey)
4502 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
4503 colHeader.ImageKey = imageKey;
4509 public virtual void AddRange (ColumnHeader [] values)
4511 foreach (ColumnHeader colHeader in values) {
4512 int idx = list.Add (colHeader);
4513 owner.AddColumn (colHeader, idx, false);
4516 owner.Redraw (true);
4519 public virtual void Clear ()
4521 foreach (ColumnHeader col in list)
4522 col.SetListView (null);
4524 owner.ReorderColumns (new int [0], true);
4527 public bool Contains (ColumnHeader value)
4529 return list.Contains (value);
4533 public virtual bool ContainsKey (string key)
4535 return IndexOfKey (key) != -1;
4539 public IEnumerator GetEnumerator ()
4541 return list.GetEnumerator ();
4544 void ICollection.CopyTo (Array dest, int index)
4546 list.CopyTo (dest, index);
4549 int IList.Add (object value)
4551 if (! (value is ColumnHeader)) {
4552 throw new ArgumentException ("Not of type ColumnHeader", "value");
4555 return this.Add ((ColumnHeader) value);
4558 bool IList.Contains (object value)
4560 if (! (value is ColumnHeader)) {
4561 throw new ArgumentException ("Not of type ColumnHeader", "value");
4564 return this.Contains ((ColumnHeader) value);
4567 int IList.IndexOf (object value)
4569 if (! (value is ColumnHeader)) {
4570 throw new ArgumentException ("Not of type ColumnHeader", "value");
4573 return this.IndexOf ((ColumnHeader) value);
4576 void IList.Insert (int index, object value)
4578 if (! (value is ColumnHeader)) {
4579 throw new ArgumentException ("Not of type ColumnHeader", "value");
4582 this.Insert (index, (ColumnHeader) value);
4585 void IList.Remove (object value)
4587 if (! (value is ColumnHeader)) {
4588 throw new ArgumentException ("Not of type ColumnHeader", "value");
4591 this.Remove ((ColumnHeader) value);
4594 public int IndexOf (ColumnHeader value)
4596 return list.IndexOf (value);
4600 public virtual int IndexOfKey (string key)
4602 if (key == null || key.Length == 0)
4605 for (int i = 0; i < list.Count; i++) {
4606 ColumnHeader col = (ColumnHeader) list [i];
4607 if (String.Compare (key, col.Name, true) == 0)
4615 public void Insert (int index, ColumnHeader value)
4617 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
4618 // but it's really only greater.
4619 if (index < 0 || index > list.Count)
4620 throw new ArgumentOutOfRangeException ("index");
4622 list.Insert (index, value);
4623 owner.AddColumn (value, index, true);
4627 public void Insert (int index, string text)
4629 Insert (index, String.Empty, text);
4632 public void Insert (int index, string text, int width)
4634 Insert (index, String.Empty, text, width);
4637 public void Insert (int index, string key, string text)
4639 ColumnHeader colHeader = new ColumnHeader ();
4640 colHeader.Name = key;
4641 colHeader.Text = text;
4642 Insert (index, colHeader);
4645 public void Insert (int index, string key, string text, int width)
4647 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
4648 Insert (index, colHeader);
4651 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
4653 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4654 colHeader.ImageIndex = imageIndex;
4655 Insert (index, colHeader);
4658 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
4660 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
4661 colHeader.ImageKey = imageKey;
4662 Insert (index, colHeader);
4666 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
4668 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
4669 this.Insert (index, colHeader);
4672 public virtual void Remove (ColumnHeader column)
4674 if (!Contains (column))
4677 list.Remove (column);
4678 column.SetListView (null);
4680 int rem_display_index = column.InternalDisplayIndex;
4681 int [] display_indices = new int [list.Count];
4682 for (int i = 0; i < display_indices.Length; i++) {
4683 ColumnHeader col = (ColumnHeader) list [i];
4684 int display_index = col.InternalDisplayIndex;
4685 if (display_index < rem_display_index) {
4686 display_indices [i] = display_index;
4688 display_indices [i] = (display_index - 1);
4692 column.InternalDisplayIndex = -1;
4693 owner.ReorderColumns (display_indices, true);
4697 public virtual void RemoveByKey (string key)
4699 int idx = IndexOfKey (key);
4705 public virtual void RemoveAt (int index)
4707 if (index < 0 || index >= list.Count)
4708 throw new ArgumentOutOfRangeException ("index");
4710 ColumnHeader col = (ColumnHeader) list [index];
4713 #endregion // Public Methods
4716 } // ColumnHeaderCollection
4719 [ListBindable (false)]
4721 public class ListViewItemCollection : IList, ICollection, IEnumerable
4723 private readonly ArrayList list;
4724 private ListView owner;
4726 private ListViewGroup group;
4729 // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
4730 // In the later case ListViewItem.ListView never gets modified
4731 private bool is_main_collection = true;
4733 #region Public Constructor
4734 public ListViewItemCollection (ListView owner)
4736 list = new ArrayList (0);
4739 #endregion // Public Constructor
4742 internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
4745 is_main_collection = false;
4749 #region Public Properties
4754 if (owner != null && owner.VirtualMode)
4755 return owner.VirtualListSize;
4762 public bool IsReadOnly {
4763 get { return false; }
4766 public virtual ListViewItem this [int displayIndex] {
4768 if (displayIndex < 0 || displayIndex >= Count)
4769 throw new ArgumentOutOfRangeException ("displayIndex");
4772 if (owner != null && owner.VirtualMode)
4773 return RetrieveVirtualItemFromOwner (displayIndex);
4775 return (ListViewItem) list [displayIndex];
4779 if (displayIndex < 0 || displayIndex >= Count)
4780 throw new ArgumentOutOfRangeException ("displayIndex");
4783 if (owner != null && owner.VirtualMode)
4784 throw new InvalidOperationException ();
4787 if (list.Contains (value))
4788 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4790 if (value.ListView != null && value.ListView != owner)
4791 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");
4793 if (is_main_collection)
4794 value.Owner = owner;
4797 if (value.Group != null)
4798 value.Group.Items.Remove (value);
4800 value.SetGroup (group);
4804 list [displayIndex] = value;
4805 CollectionChanged (true);
4810 public virtual ListViewItem this [string key] {
4812 int idx = IndexOfKey (key);
4821 bool ICollection.IsSynchronized {
4822 get { return true; }
4825 object ICollection.SyncRoot {
4826 get { return this; }
4829 bool IList.IsFixedSize {
4830 get { return list.IsFixedSize; }
4833 object IList.this [int index] {
4834 get { return this [index]; }
4836 if (value is ListViewItem)
4837 this [index] = (ListViewItem) value;
4839 this [index] = new ListViewItem (value.ToString ());
4843 #endregion // Public Properties
4845 #region Public Methods
4846 public virtual ListViewItem Add (ListViewItem value)
4849 if (owner != null && owner.VirtualMode)
4850 throw new InvalidOperationException ();
4854 CollectionChanged (true);
4859 public virtual ListViewItem Add (string text)
4861 ListViewItem item = new ListViewItem (text);
4862 return this.Add (item);
4865 public virtual ListViewItem Add (string text, int imageIndex)
4867 ListViewItem item = new ListViewItem (text, imageIndex);
4868 return this.Add (item);
4872 public virtual ListViewItem Add (string text, string imageKey)
4874 ListViewItem item = new ListViewItem (text, imageKey);
4875 return this.Add (item);
4878 public virtual ListViewItem Add (string key, string text, int imageIndex)
4880 ListViewItem item = new ListViewItem (text, imageIndex);
4882 return this.Add (item);
4885 public virtual ListViewItem Add (string key, string text, string imageKey)
4887 ListViewItem item = new ListViewItem (text, imageKey);
4889 return this.Add (item);
4893 public void AddRange (ListViewItem [] values)
4896 throw new ArgumentNullException ("Argument cannot be null!", "values");
4898 if (owner != null && owner.VirtualMode)
4899 throw new InvalidOperationException ();
4902 foreach (ListViewItem item in values)
4905 CollectionChanged (true);
4909 public void AddRange (ListViewItemCollection items)
4912 throw new ArgumentNullException ("Argument cannot be null!", "items");
4914 ListViewItem[] itemArray = new ListViewItem[items.Count];
4915 items.CopyTo (itemArray,0);
4916 this.AddRange (itemArray);
4920 public virtual void Clear ()
4923 if (owner != null && owner.VirtualMode)
4924 throw new InvalidOperationException ();
4926 if (is_main_collection && owner != null) {
4927 owner.SetFocusedItem (-1);
4928 owner.h_scroll.Value = owner.v_scroll.Value = 0;
4930 foreach (ListViewItem item in list) {
4931 owner.item_control.CancelEdit (item);
4938 foreach (ListViewItem item in list)
4939 item.SetGroup (null);
4943 CollectionChanged (false);
4946 public bool Contains (ListViewItem item)
4948 return IndexOf (item) != -1;
4952 public virtual bool ContainsKey (string key)
4954 return IndexOfKey (key) != -1;
4958 public void CopyTo (Array dest, int index)
4960 list.CopyTo (dest, index);
4964 public ListViewItem [] Find (string key, bool searchAllSubitems)
4967 return new ListViewItem [0];
4969 List<ListViewItem> temp_list = new List<ListViewItem> ();
4971 for (int i = 0; i < list.Count; i++) {
4972 ListViewItem lvi = (ListViewItem) list [i];
4973 if (String.Compare (key, lvi.Name, true) == 0)
4974 temp_list.Add (lvi);
4977 ListViewItem [] retval = new ListViewItem [temp_list.Count];
4978 temp_list.CopyTo (retval);
4984 public IEnumerator GetEnumerator ()
4987 if (owner != null && owner.VirtualMode)
4988 throw new InvalidOperationException ();
4991 return list.GetEnumerator ();
4994 int IList.Add (object item)
5000 if (owner != null && owner.VirtualMode)
5001 throw new InvalidOperationException ();
5004 if (item is ListViewItem) {
5005 li = (ListViewItem) item;
5006 if (list.Contains (li))
5007 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5009 if (li.ListView != null && li.ListView != owner)
5010 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");
5013 li = new ListViewItem (item.ToString ());
5016 result = list.Add (li);
5017 CollectionChanged (true);
5022 bool IList.Contains (object item)
5024 return Contains ((ListViewItem) item);
5027 int IList.IndexOf (object item)
5029 return IndexOf ((ListViewItem) item);
5032 void IList.Insert (int index, object item)
5034 if (item is ListViewItem)
5035 this.Insert (index, (ListViewItem) item);
5037 this.Insert (index, item.ToString ());
5040 void IList.Remove (object item)
5042 Remove ((ListViewItem) item);
5045 public int IndexOf (ListViewItem item)
5048 if (owner != null && owner.VirtualMode) {
5049 for (int i = 0; i < Count; i++)
5050 if (RetrieveVirtualItemFromOwner (i) == item)
5057 return list.IndexOf (item);
5061 public virtual int IndexOfKey (string key)
5063 if (key == null || key.Length == 0)
5066 for (int i = 0; i < Count; i++) {
5067 ListViewItem lvi = this [i];
5068 if (String.Compare (key, lvi.Name, true) == 0)
5076 public ListViewItem Insert (int index, ListViewItem item)
5078 if (index < 0 || index > list.Count)
5079 throw new ArgumentOutOfRangeException ("index");
5082 if (owner != null && owner.VirtualMode)
5083 throw new InvalidOperationException ();
5086 if (list.Contains (item))
5087 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
5089 if (item.ListView != null && item.ListView != owner)
5090 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");
5092 if (is_main_collection)
5096 if (item.Group != null)
5097 item.Group.Items.Remove (item);
5099 item.SetGroup (group);
5103 list.Insert (index, item);
5104 CollectionChanged (true);
5108 public ListViewItem Insert (int index, string text)
5110 return this.Insert (index, new ListViewItem (text));
5113 public ListViewItem Insert (int index, string text, int imageIndex)
5115 return this.Insert (index, new ListViewItem (text, imageIndex));
5119 public ListViewItem Insert (int index, string text, string imageKey)
5121 ListViewItem lvi = new ListViewItem (text, imageKey);
5122 return Insert (index, lvi);
5125 public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
5127 ListViewItem lvi = new ListViewItem (text, imageIndex);
5129 return Insert (index, lvi);
5132 public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
5134 ListViewItem lvi = new ListViewItem (text, imageKey);
5136 return Insert (index, lvi);
5140 public virtual void Remove (ListViewItem item)
5143 if (owner != null && owner.VirtualMode)
5144 throw new InvalidOperationException ();
5147 int idx = list.IndexOf (item);
5152 public virtual void RemoveAt (int index)
5154 if (index < 0 || index >= Count)
5155 throw new ArgumentOutOfRangeException ("index");
5158 if (owner != null && owner.VirtualMode)
5159 throw new InvalidOperationException ();
5162 ListViewItem item = (ListViewItem) list [index];
5164 bool selection_changed = false;
5165 if (is_main_collection && owner != null) {
5167 if (item.Focused && index + 1 == Count) // Last item
5168 owner.SetFocusedItem (index == 0 ? -1 : index - 1);
5170 selection_changed = owner.SelectedIndices.Contains (index);
5171 owner.item_control.CancelEdit (item);
5174 list.RemoveAt (index);
5176 if (is_main_collection)
5180 item.SetGroup (null);
5183 CollectionChanged (false);
5184 if (selection_changed && owner != null)
5185 owner.OnSelectedIndexChanged (EventArgs.Empty);
5189 public virtual void RemoveByKey (string key)
5191 int idx = IndexOfKey (key);
5197 #endregion // Public Methods
5199 internal ListView Owner {
5209 internal ListViewGroup Group {
5219 void AddItem (ListViewItem value)
5221 if (list.Contains (value))
5222 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
5224 if (value.ListView != null && value.ListView != owner)
5225 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");
5226 if (is_main_collection)
5227 value.Owner = owner;
5230 if (value.Group != null)
5231 value.Group.Items.Remove (value);
5233 value.SetGroup (group);
5240 void CollectionChanged (bool sort)
5242 if (owner != null) {
5247 owner.Redraw (true);
5252 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
5254 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
5256 owner.OnRetrieveVirtualItem (args);
5257 ListViewItem retval = args.Item;
5258 retval.Owner = owner;
5259 retval.DisplayIndex = displayIndex;
5265 internal event CollectionChangedHandler Changed;
5267 internal void Sort (IComparer comparer)
5269 list.Sort (comparer);
5273 internal void OnChange ()
5275 if (Changed != null)
5278 } // ListViewItemCollection
5281 // In normal mode, the selection information resides in the Items,
5282 // making SelectedIndexCollection.List read-only
5284 // In virtual mode, SelectedIndexCollection directly saves the selection
5285 // information, instead of getting it from Items, making List read-and-write
5287 [ListBindable (false)]
5289 public class SelectedIndexCollection : IList, ICollection, IEnumerable
5291 private readonly ListView owner;
5292 private ArrayList list;
5294 #region Public Constructor
5295 public SelectedIndexCollection (ListView owner)
5298 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
5300 #endregion // Public Constructor
5302 #region Public Properties
5306 if (!owner.IsHandleCreated)
5313 public bool IsReadOnly {
5323 public int this [int index] {
5325 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
5326 throw new ArgumentOutOfRangeException ("index");
5328 return (int) List [index];
5332 bool ICollection.IsSynchronized {
5333 get { return false; }
5336 object ICollection.SyncRoot {
5337 get { return this; }
5340 bool IList.IsFixedSize {
5350 object IList.this [int index] {
5351 get { return this [index]; }
5352 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5354 #endregion // Public Properties
5356 #region Public Methods
5358 public int Add (int itemIndex)
5360 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5361 throw new ArgumentOutOfRangeException ("index");
5363 if (owner.virtual_mode && !owner.IsHandleCreated)
5366 owner.Items [itemIndex].Selected = true;
5368 if (!owner.IsHandleCreated)
5382 if (!owner.IsHandleCreated)
5385 int [] indexes = (int []) List.ToArray (typeof (int));
5386 foreach (int index in indexes)
5387 owner.Items [index].Selected = false;
5390 public bool Contains (int selectedIndex)
5392 return IndexOf (selectedIndex) != -1;
5395 public void CopyTo (Array dest, int index)
5397 List.CopyTo (dest, index);
5400 public IEnumerator GetEnumerator ()
5402 return List.GetEnumerator ();
5405 int IList.Add (object value)
5407 throw new NotSupportedException ("Add operation is not supported.");
5415 bool IList.Contains (object selectedIndex)
5417 if (!(selectedIndex is int))
5419 return Contains ((int) selectedIndex);
5422 int IList.IndexOf (object selectedIndex)
5424 if (!(selectedIndex is int))
5426 return IndexOf ((int) selectedIndex);
5429 void IList.Insert (int index, object value)
5431 throw new NotSupportedException ("Insert operation is not supported.");
5434 void IList.Remove (object value)
5436 throw new NotSupportedException ("Remove operation is not supported.");
5439 void IList.RemoveAt (int index)
5441 throw new NotSupportedException ("RemoveAt operation is not supported.");
5444 public int IndexOf (int selectedIndex)
5446 if (!owner.IsHandleCreated)
5449 return List.IndexOf (selectedIndex);
5453 public void Remove (int itemIndex)
5455 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
5456 throw new ArgumentOutOfRangeException ("itemIndex");
5458 owner.Items [itemIndex].Selected = false;
5461 #endregion // Public Methods
5463 internal ArrayList List {
5466 list = new ArrayList ();
5468 if (!owner.VirtualMode)
5470 for (int i = 0; i < owner.Items.Count; i++) {
5471 if (owner.Items [i].Selected)
5479 internal void Reset ()
5481 // force re-population of list
5483 if (!owner.VirtualMode)
5488 private void ItemsCollection_Changed ()
5494 internal void RemoveIndex (int index)
5496 int idx = List.BinarySearch (index);
5498 List.RemoveAt (idx);
5501 // actually store index in the collection
5502 // also, keep the collection sorted, as .Net does
5503 internal void InsertIndex (int index)
5506 int iMax = List.Count - 1;
5507 while (iMin <= iMax) {
5508 int iMid = (iMin + iMax) / 2;
5509 int current_index = (int) List [iMid];
5511 if (current_index == index)
5512 return; // Already added
5513 if (current_index > index)
5519 List.Insert (iMin, index);
5523 } // SelectedIndexCollection
5526 [ListBindable (false)]
5528 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
5530 private readonly ListView owner;
5532 #region Public Constructor
5533 public SelectedListViewItemCollection (ListView owner)
5537 #endregion // Public Constructor
5539 #region Public Properties
5543 return owner.SelectedIndices.Count;
5547 public bool IsReadOnly {
5548 get { return true; }
5551 public ListViewItem this [int index] {
5553 if (!owner.IsHandleCreated || index < 0 || index >= Count)
5554 throw new ArgumentOutOfRangeException ("index");
5556 int item_index = owner.SelectedIndices [index];
5557 return owner.Items [item_index];
5562 public virtual ListViewItem this [string key] {
5564 int idx = IndexOfKey (key);
5573 bool ICollection.IsSynchronized {
5574 get { return false; }
5577 object ICollection.SyncRoot {
5578 get { return this; }
5581 bool IList.IsFixedSize {
5582 get { return true; }
5585 object IList.this [int index] {
5586 get { return this [index]; }
5587 set { throw new NotSupportedException ("SetItem operation is not supported."); }
5589 #endregion // Public Properties
5591 #region Public Methods
5592 public void Clear ()
5594 owner.SelectedIndices.Clear ();
5597 public bool Contains (ListViewItem item)
5599 return IndexOf (item) != -1;
5603 public virtual bool ContainsKey (string key)
5605 return IndexOfKey (key) != -1;
5609 public void CopyTo (Array dest, int index)
5611 if (!owner.IsHandleCreated)
5613 if (index > Count) // Throws ArgumentException instead of IOOR exception
5614 throw new ArgumentException ("index");
5616 for (int i = 0; i < Count; i++)
5617 dest.SetValue (this [i], index++);
5620 public IEnumerator GetEnumerator ()
5622 if (!owner.IsHandleCreated)
5623 return (new ListViewItem [0]).GetEnumerator ();
5625 ListViewItem [] items = new ListViewItem [Count];
5626 for (int i = 0; i < Count; i++)
5627 items [i] = this [i];
5629 return items.GetEnumerator ();
5632 int IList.Add (object value)
5634 throw new NotSupportedException ("Add operation is not supported.");
5637 bool IList.Contains (object item)
5639 if (!(item is ListViewItem))
5641 return Contains ((ListViewItem) item);
5644 int IList.IndexOf (object item)
5646 if (!(item is ListViewItem))
5648 return IndexOf ((ListViewItem) item);
5651 void IList.Insert (int index, object value)
5653 throw new NotSupportedException ("Insert operation is not supported.");
5656 void IList.Remove (object value)
5658 throw new NotSupportedException ("Remove operation is not supported.");
5661 void IList.RemoveAt (int index)
5663 throw new NotSupportedException ("RemoveAt operation is not supported.");
5666 public int IndexOf (ListViewItem item)
5668 if (!owner.IsHandleCreated)
5671 for (int i = 0; i < Count; i++)
5672 if (this [i] == item)
5679 public virtual int IndexOfKey (string key)
5681 if (!owner.IsHandleCreated || key == null || key.Length == 0)
5684 for (int i = 0; i < Count; i++) {
5685 ListViewItem item = this [i];
5686 if (String.Compare (item.Name, key, true) == 0)
5693 #endregion // Public Methods
5695 } // SelectedListViewItemCollection
5697 internal delegate void CollectionChangedHandler ();
5699 struct ItemMatrixLocation
5704 public ItemMatrixLocation (int row, int col)
5731 #endregion // Subclasses
5733 protected override void OnResize (EventArgs e)
5738 protected override void OnMouseLeave (EventArgs e)
5740 base.OnMouseLeave (e);
5744 // ColumnReorder event
5746 static object ColumnReorderedEvent = new object ();
5747 public event ColumnReorderedEventHandler ColumnReordered {
5748 add { Events.AddHandler (ColumnReorderedEvent, value); }
5749 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
5752 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
5754 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
5761 // ColumnWidthChanged
5763 static object ColumnWidthChangedEvent = new object ();
5764 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
5765 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
5766 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
5769 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
5771 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
5776 void RaiseColumnWidthChanged (int resize_column)
5778 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
5780 OnColumnWidthChanged (n);
5784 // ColumnWidthChanging
5786 static object ColumnWidthChangingEvent = new object ();
5787 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
5788 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
5789 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
5792 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
5794 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5800 // 2.0 profile based implementation
5802 bool CanProceedWithResize (ColumnHeader col, int width)
5804 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
5808 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
5809 cwceh (this, changing);
5810 return !changing.Cancel;
5814 // 1.0 profile based implementation
5816 bool CanProceedWithResize (ColumnHeader col, int width)
5821 void RaiseColumnWidthChanged (int resize_column)