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)
29 // - Feedback for item activation, change in cursor types as mouse moves.
36 using System.Collections;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
40 using System.Runtime.InteropServices;
41 using System.Globalization;
43 using System.Collections.Generic;
46 namespace System.Windows.Forms
48 [DefaultEvent ("SelectedIndexChanged")]
49 [DefaultProperty ("Items")]
50 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
52 [ClassInterface (ClassInterfaceType.AutoDispatch)]
54 [Docking (DockingBehavior.Ask)]
56 public class ListView : Control
58 private ItemActivation activation = ItemActivation.Standard;
59 private ListViewAlignment alignment = ListViewAlignment.Top;
60 private bool allow_column_reorder;
61 private bool auto_arrange = true;
62 private bool check_boxes;
63 private readonly CheckedIndexCollection checked_indices;
64 private readonly CheckedListViewItemCollection checked_items;
65 private readonly ColumnHeaderCollection columns;
66 internal ListViewItem focused_item;
67 private bool full_row_select;
68 private bool grid_lines;
69 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
70 private bool hide_selection = true;
71 private bool hover_selection;
72 private IComparer item_sorter;
73 private readonly ListViewItemCollection items;
75 private readonly ListViewGroupCollection groups;
76 private bool owner_draw;
77 private bool show_groups = true;
79 private bool label_edit;
80 private bool label_wrap = true;
81 private bool multiselect = true;
82 private bool scrollable = true;
83 private bool hover_pending;
84 private readonly SelectedIndexCollection selected_indices;
85 private readonly SelectedListViewItemCollection selected_items;
86 private SortOrder sort_order = SortOrder.None;
87 private ImageList state_image_list;
88 private bool updating;
89 private View view = View.LargeIcon;
90 private int layout_wd; // We might draw more than our client area
91 private int layout_ht; // therefore we need to have these two.
92 HeaderControl header_control;
93 internal ItemControl item_control;
94 internal ScrollBar h_scroll; // used for scrolling horizontally
95 internal ScrollBar v_scroll; // used for scrolling vertically
96 internal int h_marker; // Position markers for scrolling
97 internal int v_marker;
98 private int keysearch_tickcnt;
99 private string keysearch_text;
100 static private readonly int keysearch_keydelay = 1000;
101 private int[] reordered_column_indices;
102 private Point [] items_location;
103 private ItemMatrixLocation [] items_matrix_location;
104 private Size item_size; // used for caching item size
106 private bool show_item_tooltips;
107 private ToolTip item_tooltip;
108 private Size tile_size;
109 private bool virtual_mode;
110 private int virtual_list_size;
113 // internal variables
114 internal ImageList large_image_list;
115 internal ImageList small_image_list;
116 internal Size text_size = Size.Empty;
119 static object AfterLabelEditEvent = new object ();
120 static object BeforeLabelEditEvent = new object ();
121 static object ColumnClickEvent = new object ();
122 static object ItemActivateEvent = new object ();
123 static object ItemCheckEvent = new object ();
124 static object ItemDragEvent = new object ();
125 static object SelectedIndexChangedEvent = new object ();
127 static object DrawColumnHeaderEvent = new object();
128 static object DrawItemEvent = new object();
129 static object DrawSubItemEvent = new object();
130 static object ItemCheckedEvent = new object ();
131 static object ItemMouseHoverEvent = new object ();
132 static object ItemSelectionChangedEvent = new object ();
133 static object CacheVirtualItemsEvent = new object ();
134 static object RetrieveVirtualItemEvent = new object ();
137 public event LabelEditEventHandler AfterLabelEdit {
138 add { Events.AddHandler (AfterLabelEditEvent, value); }
139 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
143 [EditorBrowsable (EditorBrowsableState.Never)]
144 public new event EventHandler BackgroundImageChanged {
145 add { base.BackgroundImageChanged += value; }
146 remove { base.BackgroundImageChanged -= value; }
149 public event LabelEditEventHandler BeforeLabelEdit {
150 add { Events.AddHandler (BeforeLabelEditEvent, value); }
151 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
154 public event ColumnClickEventHandler ColumnClick {
155 add { Events.AddHandler (ColumnClickEvent, value); }
156 remove { Events.RemoveHandler (ColumnClickEvent, value); }
160 public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
161 add { Events.AddHandler(DrawColumnHeaderEvent, value); }
162 remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
165 public event DrawListViewItemEventHandler DrawItem {
166 add { Events.AddHandler(DrawItemEvent, value); }
167 remove { Events.RemoveHandler(DrawItemEvent, value); }
170 public event DrawListViewSubItemEventHandler DrawSubItem {
171 add { Events.AddHandler(DrawSubItemEvent, value); }
172 remove { Events.RemoveHandler(DrawSubItemEvent, value); }
176 public event EventHandler ItemActivate {
177 add { Events.AddHandler (ItemActivateEvent, value); }
178 remove { Events.RemoveHandler (ItemActivateEvent, value); }
181 public event ItemCheckEventHandler ItemCheck {
182 add { Events.AddHandler (ItemCheckEvent, value); }
183 remove { Events.RemoveHandler (ItemCheckEvent, value); }
187 public event ItemCheckedEventHandler ItemChecked {
188 add { Events.AddHandler (ItemCheckedEvent, value); }
189 remove { Events.RemoveHandler (ItemCheckedEvent, value); }
193 public event ItemDragEventHandler ItemDrag {
194 add { Events.AddHandler (ItemDragEvent, value); }
195 remove { Events.RemoveHandler (ItemDragEvent, value); }
199 public event ListViewItemMouseHoverEventHandler ItemMouseHover {
200 add { Events.AddHandler (ItemMouseHoverEvent, value); }
201 remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
204 public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
205 add { Events.AddHandler (ItemSelectionChangedEvent, value); }
206 remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
211 [EditorBrowsable (EditorBrowsableState.Never)]
212 public new event PaintEventHandler Paint {
213 add { base.Paint += value; }
214 remove { base.Paint -= value; }
217 public event EventHandler SelectedIndexChanged {
218 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
219 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
223 [EditorBrowsable (EditorBrowsableState.Never)]
224 public new event EventHandler TextChanged {
225 add { base.TextChanged += value; }
226 remove { base.TextChanged -= value; }
230 public event CacheVirtualItemsEventHandler CacheVirtualItems {
231 add { Events.AddHandler (CacheVirtualItemsEvent, value); }
232 remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
235 public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
236 add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
237 remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
243 #region Public Constructors
246 background_color = ThemeEngine.Current.ColorWindow;
247 items = new ListViewItemCollection (this);
249 groups = new ListViewGroupCollection (this);
251 checked_indices = new CheckedIndexCollection (this);
252 checked_items = new CheckedListViewItemCollection (this);
253 columns = new ColumnHeaderCollection (this);
254 foreground_color = SystemColors.WindowText;
255 selected_indices = new SelectedIndexCollection (this);
256 selected_items = new SelectedListViewItemCollection (this);
257 items_location = new Point [16];
258 items_matrix_location = new ItemMatrixLocation [16];
260 item_tooltip = new ToolTip ();
261 item_tooltip.Active = false;
264 InternalBorderStyle = BorderStyle.Fixed3D;
266 header_control = new HeaderControl (this);
267 header_control.Visible = false;
268 Controls.AddImplicit (header_control);
270 item_control = new ItemControl (this);
271 Controls.AddImplicit (item_control);
273 h_scroll = new ImplicitHScrollBar ();
274 Controls.AddImplicit (this.h_scroll);
276 v_scroll = new ImplicitVScrollBar ();
277 Controls.AddImplicit (this.v_scroll);
279 h_marker = v_marker = 0;
280 keysearch_tickcnt = 0;
282 // scroll bars are disabled initially
283 h_scroll.Visible = false;
284 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
285 v_scroll.Visible = false;
286 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
289 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
290 SizeChanged += new EventHandler (ListView_SizeChanged);
291 GotFocus += new EventHandler (FocusChanged);
292 LostFocus += new EventHandler (FocusChanged);
293 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
294 MouseEnter += new EventHandler (ListView_MouseEnter);
296 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
298 | ControlStyles.UseTextForAccessibility
302 #endregion // Public Constructors
304 #region Private Internal Properties
305 internal Size CheckBoxSize {
307 if (this.check_boxes) {
308 if (this.state_image_list != null)
309 return this.state_image_list.ImageSize;
311 return ThemeEngine.Current.ListViewCheckBoxSize;
317 internal Size ItemSize {
319 if (view != View.Details)
322 Size size = new Size ();
323 size.Height = item_size.Height;
324 for (int i = 0; i < columns.Count; i++)
325 size.Width += columns [i].Wd;
334 #endregion // Private Internal Properties
336 #region Protected Properties
337 protected override CreateParams CreateParams {
338 get { return base.CreateParams; }
341 protected override Size DefaultSize {
342 get { return ThemeEngine.Current.ListViewDefaultSize; }
344 #endregion // Protected Properties
346 #region Public Instance Properties
347 [DefaultValue (ItemActivation.Standard)]
348 public ItemActivation Activation {
349 get { return activation; }
351 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
352 value != ItemActivation.TwoClick) {
353 throw new InvalidEnumArgumentException (string.Format
354 ("Enum argument value '{0}' is not valid for Activation", value));
361 [DefaultValue (ListViewAlignment.Top)]
363 public ListViewAlignment Alignment {
364 get { return alignment; }
366 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
367 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
368 throw new InvalidEnumArgumentException (string.Format
369 ("Enum argument value '{0}' is not valid for Alignment", value));
372 if (this.alignment != value) {
374 // alignment does not matter in Details/List views
375 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
381 [DefaultValue (false)]
382 public bool AllowColumnReorder {
383 get { return allow_column_reorder; }
384 set { allow_column_reorder = value; }
387 [DefaultValue (true)]
388 public bool AutoArrange {
389 get { return auto_arrange; }
391 if (auto_arrange != value) {
392 auto_arrange = value;
393 // autoarrange does not matter in Details/List views
394 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
400 public override Color BackColor {
402 if (background_color.IsEmpty)
403 return ThemeEngine.Current.ColorWindow;
405 return background_color;
407 set { background_color = value; }
411 [EditorBrowsable (EditorBrowsableState.Never)]
412 public override Image BackgroundImage {
413 get { return base.BackgroundImage; }
414 set { base.BackgroundImage = value; }
417 [DefaultValue (BorderStyle.Fixed3D)]
419 public BorderStyle BorderStyle {
420 get { return InternalBorderStyle; }
421 set { InternalBorderStyle = value; }
424 [DefaultValue (false)]
425 public bool CheckBoxes {
426 get { return check_boxes; }
428 if (check_boxes != value) {
430 if (value && View == View.Tile)
431 throw new NotSupportedException ("CheckBoxes are not"
432 + " supported in Tile view. Choose a different"
433 + " view or set CheckBoxes to false.");
443 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
444 public CheckedIndexCollection CheckedIndices {
445 get { return checked_indices; }
449 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
450 public CheckedListViewItemCollection CheckedItems {
451 get { return checked_items; }
454 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
456 [MergableProperty (false)]
457 public ColumnHeaderCollection Columns {
458 get { return columns; }
462 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
463 public ListViewItem FocusedItem {
469 throw new NotImplementedException ();
474 public override Color ForeColor {
476 if (foreground_color.IsEmpty)
477 return ThemeEngine.Current.ColorWindowText;
479 return foreground_color;
481 set { foreground_color = value; }
484 [DefaultValue (false)]
485 public bool FullRowSelect {
486 get { return full_row_select; }
488 if (full_row_select != value) {
489 full_row_select = value;
490 InvalidateSelection ();
495 [DefaultValue (false)]
496 public bool GridLines {
497 get { return grid_lines; }
499 if (grid_lines != value) {
506 [DefaultValue (ColumnHeaderStyle.Clickable)]
507 public ColumnHeaderStyle HeaderStyle {
508 get { return header_style; }
510 if (header_style == value)
514 case ColumnHeaderStyle.Clickable:
515 case ColumnHeaderStyle.Nonclickable:
516 case ColumnHeaderStyle.None:
519 throw new InvalidEnumArgumentException (string.Format
520 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
523 header_style = value;
524 if (view == View.Details)
529 [DefaultValue (true)]
530 public bool HideSelection {
531 get { return hide_selection; }
533 if (hide_selection != value) {
534 hide_selection = value;
535 InvalidateSelection ();
540 [DefaultValue (false)]
541 public bool HoverSelection {
542 get { return hover_selection; }
543 set { hover_selection = value; }
546 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
548 [MergableProperty (false)]
549 public ListViewItemCollection Items {
550 get { return items; }
553 [DefaultValue (false)]
554 public bool LabelEdit {
555 get { return label_edit; }
556 set { label_edit = value; }
559 [DefaultValue (true)]
561 public bool LabelWrap {
562 get { return label_wrap; }
564 if (label_wrap != value) {
571 [DefaultValue (null)]
572 public ImageList LargeImageList {
573 get { return large_image_list; }
575 large_image_list = value;
581 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
582 public IComparer ListViewItemSorter {
584 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
589 if (item_sorter != value) {
596 [DefaultValue (true)]
597 public bool MultiSelect {
598 get { return multiselect; }
599 set { multiselect = value; }
604 [DefaultValue(false)]
605 public bool OwnerDraw {
606 get { return owner_draw; }
610 [DefaultValue (true)]
611 public bool Scrollable {
612 get { return scrollable; }
614 if (scrollable != value) {
622 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
623 public SelectedIndexCollection SelectedIndices {
624 get { return selected_indices; }
628 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
629 public SelectedListViewItemCollection SelectedItems {
630 get { return selected_items; }
635 public bool ShowGroups {
636 get { return show_groups; }
638 if (show_groups != value) {
645 [LocalizableAttribute (true)]
646 [MergableProperty (false)]
647 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
648 public ListViewGroupCollection Groups {
649 get { return groups; }
652 [DefaultValue (false)]
653 public bool ShowItemToolTips {
655 return show_item_tooltips;
658 show_item_tooltips = value;
659 item_tooltip.Active = false;
664 [DefaultValue (null)]
665 public ImageList SmallImageList {
666 get { return small_image_list; }
668 small_image_list = value;
673 [DefaultValue (SortOrder.None)]
674 public SortOrder Sorting {
675 get { return sort_order; }
677 if (!Enum.IsDefined (typeof (SortOrder), value)) {
678 throw new InvalidEnumArgumentException ("value", (int) value,
682 if (sort_order == value)
688 if (virtual_mode) // Sorting is not allowed in virtual mode
692 if (value == SortOrder.None) {
693 if (item_sorter != null) {
694 // ListViewItemSorter should never be reset for SmallIcon
695 // and LargeIcon view
696 if (View != View.SmallIcon && View != View.LargeIcon)
700 // in .NET 1.1, only internal IComparer would be
702 if (item_sorter is ItemComparer)
708 if (item_sorter == null)
709 item_sorter = new ItemComparer (value);
710 if (item_sorter is ItemComparer) {
712 item_sorter = new ItemComparer (value);
714 // in .NET 1.1, the sort order is not updated for
715 // SmallIcon and LargeIcon views if no custom IComparer
717 if (View != View.SmallIcon && View != View.LargeIcon)
718 item_sorter = new ItemComparer (value);
726 private void OnImageListChanged (object sender, EventArgs args)
728 item_control.Invalidate ();
731 [DefaultValue (null)]
732 public ImageList StateImageList {
733 get { return state_image_list; }
735 if (state_image_list == value)
738 if (state_image_list != null)
739 state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
741 state_image_list = value;
743 if (state_image_list != null)
744 state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
752 [EditorBrowsable (EditorBrowsableState.Never)]
753 public override string Text {
754 get { return base.Text; }
756 if (value == base.Text)
766 public Size TileSize {
771 if (value.Width <= 0 || value.Height <= 0)
772 throw new ArgumentOutOfRangeException ("value");
781 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
782 public ListViewItem TopItem {
785 if (this.items.Count == 0)
787 // if contents are not scrolled
788 // it is the first item
789 else if (h_marker == 0 && v_marker == 0)
790 return this.items [0];
791 // do a hit test for the scrolled position
793 for (int i = 0; i < items.Count; i++) {
794 Point item_loc = GetItemLocation (i);
795 if (item_loc.X >= 0 && item_loc.Y >= 0)
803 throw new NotImplementedException ();
809 [EditorBrowsable (EditorBrowsableState.Advanced)]
810 [DefaultValue (true)]
812 [MonoInternalNote ("Stub, not implemented")]
813 public bool UseCompatibleStateImageBehavior {
822 [DefaultValue (View.LargeIcon)]
826 if (!Enum.IsDefined (typeof (View), value))
827 throw new InvalidEnumArgumentException ("value", (int) value,
832 if (CheckBoxes && value == View.Tile)
833 throw new NotSupportedException ("CheckBoxes are not"
834 + " supported in Tile view. Choose a different"
835 + " view or set CheckBoxes to false.");
838 h_scroll.Value = v_scroll.Value = 0;
846 [DefaultValue (false)]
847 [RefreshProperties (RefreshProperties.Repaint)]
848 public bool VirtualMode {
853 if (virtual_mode == value)
856 if (!virtual_mode && items.Count > 0)
857 throw new InvalidOperationException ();
859 virtual_mode = value;
865 [RefreshProperties (RefreshProperties.Repaint)]
866 public int VirtualListSize {
868 return virtual_list_size;
872 throw new ArgumentException ("value");
874 if (virtual_list_size == value)
877 virtual_list_size = value;
883 #endregion // Public Instance Properties
885 #region Internal Methods Properties
887 internal int FirstVisibleIndex {
890 if (this.items.Count == 0)
893 if (h_marker == 0 && v_marker == 0)
896 Size item_size = ItemSize;
897 for (int i = 0; i < items.Count; i++) {
898 Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
899 if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
908 internal int LastVisibleIndex {
910 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
911 if (View == View.List || Alignment == ListViewAlignment.Left) {
912 if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
915 if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
920 return Items.Count - 1;
924 internal void OnSelectedIndexChanged ()
927 OnSelectedIndexChanged (EventArgs.Empty);
930 internal int TotalWidth {
931 get { return Math.Max (this.Width, this.layout_wd); }
934 internal int TotalHeight {
935 get { return Math.Max (this.Height, this.layout_ht); }
938 internal void Redraw (bool recalculate)
940 // Avoid calculations when control is being updated
944 // VirtualMode doesn't do any calculations until handle is created
945 if (virtual_mode && !IsHandleCreated)
951 CalculateListView (this.alignment);
956 void InvalidateSelection ()
958 foreach (int selected_index in SelectedIndices)
959 items [selected_index].Invalidate ();
962 const int text_padding = 15;
964 internal Size GetChildColumnSize (int index)
966 Size ret_size = Size.Empty;
967 ColumnHeader col = this.columns [index];
969 if (col.Width == -2) { // autosize = max(items, columnheader)
970 Size size = Size.Ceiling (this.DeviceContext.MeasureString
971 (col.Text, this.Font));
972 size.Width += text_padding;
973 ret_size = BiggestItem (index);
974 if (size.Width > ret_size.Width)
977 else { // -1 and all the values < -2 are put under one category
978 ret_size = BiggestItem (index);
979 // fall back to empty columns' width if no subitem is available for a column
980 if (ret_size.IsEmpty) {
981 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
982 if (col.Text.Length > 0)
983 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
984 (col.Text, this.Font)).Height;
986 ret_size.Height = this.Font.Height;
990 ret_size.Height += text_padding;
992 // adjust the size for icon and checkbox for 0th column
994 ret_size.Width += (this.CheckBoxSize.Width + 4);
995 if (this.small_image_list != null)
996 ret_size.Width += this.small_image_list.ImageSize.Width;
1001 // Returns the size of biggest item text in a column.
1002 private Size BiggestItem (int col)
1004 Size temp = Size.Empty;
1005 Size ret_size = Size.Empty;
1008 // VirtualMode uses the first item text size
1009 if (virtual_mode && items.Count > 0) {
1010 ListViewItem item = items [0];
1011 ret_size = Size.Ceiling (DeviceContext.MeasureString (item.SubItems [col].Text,
1015 // 0th column holds the item text, we check the size of
1016 // the various subitems falling in that column and get
1017 // the biggest one's size.
1018 foreach (ListViewItem item in items) {
1019 if (col >= item.SubItems.Count)
1022 temp = Size.Ceiling (DeviceContext.MeasureString
1023 (item.SubItems [col].Text, Font));
1024 if (temp.Width > ret_size.Width)
1031 // adjustment for space
1032 if (!ret_size.IsEmpty)
1033 ret_size.Width += 4;
1038 const int max_wrap_padding = 38;
1040 // Sets the size of the biggest item text as per the view
1041 private void CalcTextSize ()
1043 // clear the old value
1044 text_size = Size.Empty;
1046 if (items.Count == 0)
1049 text_size = BiggestItem (0);
1051 if (view == View.LargeIcon && this.label_wrap) {
1052 Size temp = Size.Empty;
1053 if (this.check_boxes)
1054 temp.Width += 2 * this.CheckBoxSize.Width;
1055 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1056 temp.Width += icon_w + max_wrap_padding;
1057 // wrapping is done for two lines only
1058 if (text_size.Width > temp.Width) {
1059 text_size.Width = temp.Width;
1060 text_size.Height *= 2;
1063 else if (view == View.List) {
1064 // in list view max text shown in determined by the
1065 // control width, even if scolling is enabled.
1066 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
1067 if (this.small_image_list != null)
1068 max_wd -= this.small_image_list.ImageSize.Width;
1070 if (text_size.Width > max_wd)
1071 text_size.Width = max_wd;
1074 // we do the default settings, if we have got 0's
1075 if (text_size.Height <= 0)
1076 text_size.Height = this.Font.Height;
1077 if (text_size.Width <= 0)
1078 text_size.Width = this.Width;
1080 // little adjustment
1081 text_size.Width += 4;
1082 text_size.Height += 2;
1085 private void Scroll (ScrollBar scrollbar, int delta)
1087 if (delta == 0 || !scrollbar.Visible)
1091 if (scrollbar == h_scroll)
1092 max = h_scroll.Maximum - item_control.Width;
1094 max = v_scroll.Maximum - item_control.Height;
1096 int val = scrollbar.Value + delta;
1099 else if (val < scrollbar.Minimum)
1100 val = scrollbar.Minimum;
1101 scrollbar.Value = val;
1104 private void CalculateScrollBars ()
1106 Rectangle client_area = ClientRectangle;
1109 h_scroll.Visible = false;
1110 v_scroll.Visible = false;
1111 item_control.Location = new Point (0, header_control.Height);
1112 item_control.Height = ClientRectangle.Width - header_control.Height;
1113 item_control.Width = ClientRectangle.Width;
1114 header_control.Width = ClientRectangle.Width;
1118 // Don't calculate if the view is not displayable
1119 if (client_area.Height < 0 || client_area.Width < 0)
1122 // making a scroll bar visible might make
1123 // other scroll bar visible
1124 if (layout_wd > client_area.Right) {
1125 h_scroll.Visible = true;
1126 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
1127 v_scroll.Visible = true;
1129 v_scroll.Visible = false;
1130 } else if (layout_ht > client_area.Bottom) {
1131 v_scroll.Visible = true;
1132 if ((layout_wd + v_scroll.Width) > client_area.Right)
1133 h_scroll.Visible = true;
1135 h_scroll.Visible = false;
1137 h_scroll.Visible = false;
1138 v_scroll.Visible = false;
1141 item_control.Height = ClientRectangle.Height - header_control.Height;
1143 if (h_scroll.is_visible) {
1144 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
1145 h_scroll.Minimum = 0;
1147 // if v_scroll is visible, adjust the maximum of the
1148 // h_scroll to account for the width of v_scroll
1149 if (v_scroll.Visible) {
1150 h_scroll.Maximum = layout_wd + v_scroll.Width;
1151 h_scroll.Width = client_area.Width - v_scroll.Width;
1154 h_scroll.Maximum = layout_wd;
1155 h_scroll.Width = client_area.Width;
1158 h_scroll.LargeChange = client_area.Width;
1159 h_scroll.SmallChange = Font.Height;
1160 item_control.Height -= h_scroll.Height;
1163 if (header_control.is_visible)
1164 header_control.Width = ClientRectangle.Width;
1165 item_control.Width = ClientRectangle.Width;
1167 if (v_scroll.is_visible) {
1168 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
1169 v_scroll.Minimum = 0;
1171 // if h_scroll is visible, adjust the maximum of the
1172 // v_scroll to account for the height of h_scroll
1173 if (h_scroll.Visible) {
1174 v_scroll.Maximum = layout_ht + h_scroll.Height;
1175 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
1177 v_scroll.Maximum = layout_ht;
1178 v_scroll.Height = client_area.Height;
1181 v_scroll.LargeChange = client_area.Height;
1182 v_scroll.SmallChange = Font.Height;
1183 if (header_control.Visible)
1184 header_control.Width -= v_scroll.Width;
1185 item_control.Width -= v_scroll.Width;
1190 internal int GetReorderedColumnIndex (ColumnHeader column)
1192 if (reordered_column_indices == null)
1193 return column.Index;
1195 for (int i = 0; i < Columns.Count; i++)
1196 if (reordered_column_indices [i] == column.Index)
1203 internal ColumnHeader GetReorderedColumn (int index)
1205 if (reordered_column_indices == null)
1206 return Columns [index];
1208 return Columns [reordered_column_indices [index]];
1211 internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
1215 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1217 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1221 header_control.Invalidate ();
1222 item_control.Invalidate ();
1228 int column_count = Columns.Count;
1230 if (reordered_column_indices == null) {
1231 reordered_column_indices = new int [column_count];
1232 for (int i = 0; i < column_count; i++)
1233 reordered_column_indices [i] = i;
1236 if (reordered_column_indices [index] == col.Index)
1239 int[] curr = reordered_column_indices;
1240 int [] result = new int [column_count];
1242 for (int i = 0; i < column_count; i++) {
1243 if (curr_idx < column_count && curr [curr_idx] == col.Index)
1247 result [i] = col.Index;
1249 result [i] = curr [curr_idx++];
1252 ReorderColumns (result, true);
1255 internal void ReorderColumns (int [] display_indices, bool redraw)
1257 reordered_column_indices = display_indices;
1258 for (int i = 0; i < Columns.Count; i++) {
1259 ColumnHeader col = Columns [i];
1260 col.InternalDisplayIndex = reordered_column_indices [i];
1262 if (redraw && view == View.Details && IsHandleCreated) {
1264 header_control.Invalidate ();
1265 item_control.Invalidate ();
1269 internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
1271 int column_count = Columns.Count;
1272 newCol.SetListView (this);
1274 int [] display_indices = new int [column_count];
1275 for (int i = 0; i < column_count; i++) {
1276 ColumnHeader col = Columns [i];
1278 display_indices [i] = index;
1280 int display_index = col.InternalDisplayIndex;
1281 if (display_index < index) {
1282 display_indices [i] = display_index;
1284 display_indices [i] = (display_index + 1);
1289 ReorderColumns (display_indices, redraw);
1293 Size LargeIconItemSize
1296 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1297 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1298 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
1299 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1300 return new Size (w, h);
1304 Size SmallIconItemSize {
1306 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1307 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1308 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
1309 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1310 return new Size (w, h);
1317 // Calculate tile size if needed
1318 // It appears that using Font.Size instead of a SizeF value can give us
1319 // a slightly better approach to the proportions defined in .Net
1320 if (tile_size == Size.Empty) {
1321 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1322 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1323 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1324 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1326 tile_size = new Size (w, h);
1334 int GetDetailsItemHeight ()
1337 int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
1338 int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1339 item_height = Math.Max (checkbox_height, text_size.Height);
1340 item_height = Math.Max (item_height, small_image_height);
1345 void SetItemLocation (int index, int x, int y, int row, int col)
1347 Point old_location = items_location [index];
1348 if (old_location.X == x && old_location.Y == y)
1351 Size item_size = ItemSize;
1352 Rectangle old_rect = new Rectangle (GetItemLocation (index), item_size);
1354 items_location [index] = new Point (x, y);
1355 items_matrix_location [index] = new ItemMatrixLocation (row, col);
1357 // Invalidate both previous and new bounds
1358 item_control.Invalidate (old_rect);
1359 item_control.Invalidate (new Rectangle (GetItemLocation (index), item_size));
1364 int[,] item_index_matrix;
1366 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1368 header_control.Visible = false;
1369 header_control.Size = Size.Empty;
1370 item_control.Visible = true;
1371 item_control.Location = Point.Empty;
1372 ItemSize = item_size; // Cache item size
1374 if (items.Count == 0)
1377 Size sz = item_size;
1378 Rectangle area = ClientRectangle;
1381 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
1384 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1386 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
1389 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1392 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1393 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1394 item_index_matrix = new int [rows, cols];
1397 for (int i = 0; i < items.Count; i++) {
1398 int x = col * (sz.Width + x_spacing);
1399 int y = row * (sz.Height + y_spacing);
1400 SetItemLocation (i, x, y, row, col);
1401 item_index_matrix [row, col] = i;
1403 if (!virtual_mode) // Virtual mode sets Layout until draw time
1405 items [i].Layout ();
1408 if (++row == rows) {
1413 if (++col == cols) {
1420 item_control.Size = new Size (layout_wd, layout_ht);
1423 void LayoutHeader ()
1426 for (int i = 0; i < Columns.Count; i++) {
1427 ColumnHeader col = GetReorderedColumn (i);
1430 col.CalcColumnHeader ();
1434 if (x < ClientRectangle.Width)
1435 x = ClientRectangle.Width;
1437 if (header_style == ColumnHeaderStyle.None) {
1438 header_control.Visible = false;
1439 header_control.Size = Size.Empty;
1441 header_control.Width = x;
1442 header_control.Height = columns.Count > 0 ? columns [0].Ht : Font.Height + 5;
1443 header_control.Visible = true;
1447 void LayoutDetails ()
1451 if (columns.Count == 0) {
1452 item_control.Visible = false;
1453 layout_wd = ClientRectangle.Width;
1454 layout_ht = ClientRectangle.Height;
1458 item_control.Visible = true;
1459 item_control.Location = new Point (0, header_control.Height);
1460 item_control.Width = ClientRectangle.Width;
1462 int item_height = GetDetailsItemHeight ();
1463 ItemSize = new Size (0, item_height); // We only cache Height for details view
1466 if (items.Count > 0) {
1467 for (int i = 0; i < items.Count; i++) {
1468 SetItemLocation (i, 0, y, 0, 0);
1470 if (!virtual_mode) // Virtual mode sets Layout until draw time
1472 items [i].Layout ();
1474 y += item_height + 2;
1477 // some space for bottom gridline
1482 layout_wd = Math.Max (header_control.Width, item_control.Width);
1483 layout_ht = y + header_control.Height;
1486 private void AdjustItemsPositionArray (int count)
1488 if (items_location.Length >= count)
1491 // items_location and items_matrix_location must keep the same length
1492 count = Math.Max (count, items_location.Length * 2);
1493 items_location = new Point [count];
1494 items_matrix_location = new ItemMatrixLocation [count];
1497 private void CalculateListView (ListViewAlignment align)
1501 AdjustItemsPositionArray (items.Count);
1508 case View.SmallIcon:
1509 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
1510 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1513 case View.LargeIcon:
1514 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
1515 ThemeEngine.Current.ListViewHorizontalSpacing,
1516 ThemeEngine.Current.ListViewVerticalSpacing);
1520 LayoutIcons (SmallIconItemSize, true,
1521 ThemeEngine.Current.ListViewHorizontalSpacing, 2);
1525 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
1526 ThemeEngine.Current.ListViewHorizontalSpacing,
1527 ThemeEngine.Current.ListViewVerticalSpacing);
1532 CalculateScrollBars ();
1535 internal Point GetItemLocation (int index)
1537 Point loc = items_location [index];
1538 loc.X -= h_marker; // Adjust to scroll
1544 private bool KeySearchString (KeyEventArgs ke)
1546 int current_tickcnt = Environment.TickCount;
1547 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1548 keysearch_text = string.Empty;
1551 if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
1554 keysearch_text += (char)ke.KeyCode;
1555 keysearch_tickcnt = current_tickcnt;
1557 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1560 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1561 CompareOptions.IgnoreCase)) {
1562 SetFocusedItem (Items [i]);
1563 items [i].Selected = true;
1567 i = (i + 1 < Items.Count) ? i+1 : 0;
1575 int GetAdjustedIndex (Keys key)
1579 if (View == View.Details) {
1582 result = FocusedItem.Index - 1;
1585 result = FocusedItem.Index + 1;
1586 if (result == items.Count)
1590 int last_index = LastVisibleIndex;
1591 Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
1592 if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
1594 if (FocusedItem.Index == last_index) {
1595 if (FocusedItem.Index < Items.Count - 1) {
1596 int page_size = item_control.Height / ItemSize.Height - 1;
1597 result = FocusedItem.Index + page_size - 1;
1598 if (result >= Items.Count)
1599 result = Items.Count - 1;
1602 result = last_index;
1605 int first_index = FirstVisibleIndex;
1606 if (GetItemLocation (first_index).Y < 0)
1608 if (FocusedItem.Index == first_index) {
1609 if (first_index > 0) {
1610 int page_size = item_control.Height / ItemSize.Height - 1;
1611 result = first_index - page_size + 1;
1616 result = first_index;
1622 ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.Index];
1623 int row = item_matrix_location.Row;
1624 int col = item_matrix_location.Col;
1630 return item_index_matrix [row, col - 1];
1633 if (col == (cols - 1))
1635 while (item_index_matrix [row, col + 1] == 0) {
1640 return item_index_matrix [row, col + 1];
1645 return item_index_matrix [row - 1, col];
1648 if (row == (rows - 1) || row == Items.Count - 1)
1650 while (item_index_matrix [row + 1, col] == 0) {
1655 return item_index_matrix [row + 1, col];
1662 ListViewItem selection_start;
1664 private bool SelectItems (ArrayList sel_items)
1666 bool changed = false;
1667 foreach (ListViewItem item in SelectedItems)
1668 if (!sel_items.Contains (item)) {
1669 item.Selected = false;
1672 foreach (ListViewItem item in sel_items)
1673 if (!item.Selected) {
1674 item.Selected = true;
1680 private void UpdateMultiSelection (int index, bool reselect)
1682 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1683 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1684 ListViewItem item = items [index];
1686 if (shift_pressed && selection_start != null) {
1687 ArrayList list = new ArrayList ();
1688 int start_index = selection_start.Index;
1689 int start = Math.Min (start_index, index);
1690 int end = Math.Max (start_index, index);
1691 if (View == View.Details) {
1692 for (int i = start; i <= end; i++)
1693 list.Add (items [i]);
1695 ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
1696 ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
1697 int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
1698 int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
1699 int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
1700 int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
1702 for (int i = 0; i < items.Count; i++) {
1703 ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
1705 if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
1706 item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
1707 list.Add (items [i]);
1711 } else if (ctrl_pressed) {
1712 item.Selected = !item.Selected;
1713 selection_start = item;
1716 // do not unselect, and reselect the item
1717 foreach (int itemIndex in SelectedIndices) {
1718 if (index == itemIndex)
1720 items [itemIndex].Selected = false;
1723 SelectedItems.Clear ();
1724 item.Selected = true;
1726 selection_start = item;
1730 internal override bool InternalPreProcessMessage (ref Message msg)
1732 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1733 Keys key_data = (Keys)msg.WParam.ToInt32();
1735 HandleNavKeys (key_data);
1738 return base.InternalPreProcessMessage (ref msg);
1741 bool HandleNavKeys (Keys key_data)
1743 if (Items.Count == 0 || !item_control.Visible)
1746 if (FocusedItem == null)
1747 SetFocusedItem (Items [0]);
1751 SelectIndex (Items.Count - 1);
1764 SelectIndex (GetAdjustedIndex (key_data));
1768 ToggleItemsCheckState ();
1778 void ToggleItemsCheckState ()
1783 // Don't modify check state if StateImageList has less than 2 elements
1784 if (StateImageList != null && StateImageList.Images.Count < 2)
1787 if (SelectedIndices.Count > 0) {
1789 for (int i = 0; i < SelectedIndices.Count; i++) {
1790 ListViewItem item = Items [SelectedIndices [i]];
1791 item.Checked = !item.Checked;
1797 if (FocusedItem != null) {
1798 FocusedItem.Checked = !FocusedItem.Checked;
1799 SelectIndex (FocusedItem.Index);
1803 void SelectIndex (int index)
1809 UpdateMultiSelection (index, true);
1810 else if (!items [index].Selected)
1811 items [index].Selected = true;
1813 SetFocusedItem (items [index]);
1814 EnsureVisible (index);
1817 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1819 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1822 if (ke.Alt || ke.Control)
1825 ke.Handled = KeySearchString (ke);
1828 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
1830 Point loc = PointToClient (Control.MousePosition);
1831 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
1834 internal class ItemControl : Control {
1837 ListViewItem clicked_item;
1838 ListViewItem last_clicked_item;
1839 bool hover_processed = false;
1840 bool checking = false;
1841 ListViewItem prev_hovered_item;
1843 ListViewItem prev_tooltip_item;
1847 ListViewLabelEditTextBox edit_text_box;
1848 internal ListViewItem edit_item;
1849 LabelEditEventArgs edit_args;
1851 public ItemControl (ListView owner)
1854 DoubleClick += new EventHandler(ItemsDoubleClick);
1855 MouseDown += new MouseEventHandler(ItemsMouseDown);
1856 MouseMove += new MouseEventHandler(ItemsMouseMove);
1857 MouseHover += new EventHandler(ItemsMouseHover);
1858 MouseUp += new MouseEventHandler(ItemsMouseUp);
1861 void ItemsDoubleClick (object sender, EventArgs e)
1863 if (owner.activation == ItemActivation.Standard)
1864 owner.OnItemActivate (EventArgs.Empty);
1874 BoxSelect box_select_mode = BoxSelect.None;
1875 IList prev_selection;
1876 Point box_select_start;
1878 Rectangle box_select_rect;
1879 internal Rectangle BoxSelectRectangle {
1880 get { return box_select_rect; }
1882 if (box_select_rect == value)
1885 InvalidateBoxSelectRect ();
1886 box_select_rect = value;
1887 InvalidateBoxSelectRect ();
1891 void InvalidateBoxSelectRect ()
1893 if (BoxSelectRectangle.Size.IsEmpty)
1896 Rectangle edge = BoxSelectRectangle;
1902 edge.Y = BoxSelectRectangle.Bottom - 1;
1904 edge.Y = BoxSelectRectangle.Y - 1;
1906 edge.Height = BoxSelectRectangle.Height + 2;
1908 edge.X = BoxSelectRectangle.Right - 1;
1912 private Rectangle CalculateBoxSelectRectangle (Point pt)
1914 int left = Math.Min (box_select_start.X, pt.X);
1915 int right = Math.Max (box_select_start.X, pt.X);
1916 int top = Math.Min (box_select_start.Y, pt.Y);
1917 int bottom = Math.Max (box_select_start.Y, pt.Y);
1918 return Rectangle.FromLTRB (left, top, right, bottom);
1921 bool BoxIntersectsItem (int index)
1923 Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
1924 if (owner.View != View.Details) {
1926 r.Y += r.Height / 4;
1930 return BoxSelectRectangle.IntersectsWith (r);
1933 bool BoxIntersectsText (int index)
1935 Rectangle r = owner.Items [index].TextBounds;
1936 return BoxSelectRectangle.IntersectsWith (r);
1939 ArrayList BoxSelectedItems {
1941 ArrayList result = new ArrayList ();
1942 for (int i = 0; i < owner.Items.Count; i++) {
1944 if (owner.View == View.Details && !owner.FullRowSelect)
1945 intersects = BoxIntersectsText (i);
1947 intersects = BoxIntersectsItem (i);
1950 result.Add (owner.Items [i]);
1956 private bool PerformBoxSelection (Point pt)
1958 if (box_select_mode == BoxSelect.None)
1961 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1963 ArrayList box_items = BoxSelectedItems;
1967 switch (box_select_mode) {
1969 case BoxSelect.Normal:
1973 case BoxSelect.Control:
1974 items = new ArrayList ();
1975 foreach (int index in prev_selection)
1976 if (!box_items.Contains (owner.Items [index]))
1977 items.Add (owner.Items [index]);
1978 foreach (ListViewItem item in box_items)
1979 if (!prev_selection.Contains (item.Index))
1983 case BoxSelect.Shift:
1985 foreach (ListViewItem item in box_items)
1986 prev_selection.Remove (item.Index);
1987 foreach (int index in prev_selection)
1988 items.Add (owner.Items [index]);
1992 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1996 owner.SelectItems (items);
2002 private void ItemsMouseDown (object sender, MouseEventArgs me)
2004 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
2005 if (owner.items.Count == 0)
2008 bool box_selecting = false;
2009 Size item_size = owner.ItemSize;
2010 Point pt = new Point (me.X, me.Y);
2011 for (int i = 0; i < owner.items.Count; i++) {
2012 Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
2013 if (!item_rect.Contains (pt))
2016 if (owner.items [i].CheckRectReal.Contains (pt)) {
2017 ListViewItem item = owner.items [i];
2019 // Don't modify check state if we have only one image
2020 // and if we are in 1.1 profile only take into account
2022 if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
2029 // Generate an extra ItemCheck event when we got two clicks
2030 // (Match weird .Net behaviour)
2032 item.Checked = !item.Checked;
2034 item.Checked = !item.Checked;
2039 if (owner.View == View.Details) {
2040 bool over_text = owner.items [i].TextBounds.Contains (pt);
2041 if (owner.FullRowSelect) {
2042 clicked_item = owner.items [i];
2043 bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
2044 if (!over_text && over_item_column && owner.MultiSelect)
2045 box_selecting = true;
2046 } else if (over_text)
2047 clicked_item = owner.items [i];
2049 owner.SetFocusedItem (owner.Items [i]);
2051 clicked_item = owner.items [i];
2057 if (clicked_item != null) {
2058 bool changed = !clicked_item.Selected;
2059 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2060 owner.SetFocusedItem (clicked_item);
2062 if (owner.MultiSelect) {
2063 bool reselect = (!owner.LabelEdit || changed);
2064 if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
2065 owner.UpdateMultiSelection (clicked_item.Index, reselect);
2067 clicked_item.Selected = true;
2070 // Report clicks only if the item was clicked. On MS the
2071 // clicks are only raised if you click an item
2073 if (me.Clicks > 1) {
2074 if (owner.CheckBoxes)
2075 clicked_item.Checked = !clicked_item.Checked;
2076 } else if (me.Clicks == 1) {
2077 if (owner.LabelEdit && !changed)
2078 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
2081 if (owner.MultiSelect)
2082 box_selecting = true;
2083 else if (owner.SelectedItems.Count > 0)
2084 owner.SelectedItems.Clear ();
2087 if (box_selecting) {
2088 Keys mods = XplatUI.State.ModifierKeys;
2089 if ((mods & Keys.Shift) != 0)
2090 box_select_mode = BoxSelect.Shift;
2091 else if ((mods & Keys.Control) != 0)
2092 box_select_mode = BoxSelect.Control;
2094 box_select_mode = BoxSelect.Normal;
2095 box_select_start = pt;
2096 prev_selection = owner.SelectedIndices.List.Clone () as IList;
2100 private void ItemsMouseMove (object sender, MouseEventArgs me)
2102 bool done = PerformBoxSelection (new Point (me.X, me.Y));
2104 if (!done && hover_processed) {
2105 Point pt = PointToClient (Control.MousePosition);
2106 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2108 if (item != null && item != prev_hovered_item) {
2109 hover_processed = false;
2110 XplatUI.ResetMouseHover (Handle);
2115 if (!done && owner.ShowItemToolTips) {
2116 Point pt = PointToClient (Control.MousePosition);
2117 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2120 owner.item_tooltip.Active = false;
2121 prev_tooltip_item = null;
2122 } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
2123 owner.item_tooltip.Active = true;
2124 owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
2125 prev_tooltip_item = item;
2130 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
2134 private void ItemsMouseHover (object sender, EventArgs e)
2136 if (owner.hover_pending) {
2137 owner.OnMouseHover (e);
2138 owner.hover_pending = false;
2144 hover_processed = true;
2145 Point pt = PointToClient (Control.MousePosition);
2146 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
2150 prev_hovered_item = item;
2152 if (owner.HoverSelection) {
2153 if (owner.MultiSelect)
2154 owner.UpdateMultiSelection (item.Index, true);
2156 item.Selected = true;
2158 owner.SetFocusedItem (item);
2159 Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
2163 owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
2167 void HandleClicks (MouseEventArgs me)
2169 // if the click is not on an item,
2170 // clicks remains as 0
2173 owner.OnDoubleClick (EventArgs.Empty);
2174 } else if (clicks == 1) {
2175 owner.OnClick (EventArgs.Empty);
2177 owner.OnDoubleClick (EventArgs.Empty);
2178 owner.OnMouseDoubleClick (me);
2179 } else if (clicks == 1) {
2180 owner.OnClick (EventArgs.Empty);
2181 owner.OnMouseClick (me);
2188 private void ItemsMouseUp (object sender, MouseEventArgs me)
2190 MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
2191 HandleClicks (owner_me);
2194 if (owner.Items.Count == 0) {
2195 owner.OnMouseUp (owner_me);
2199 Point pt = new Point (me.X, me.Y);
2201 Rectangle rect = Rectangle.Empty;
2202 if (clicked_item != null) {
2203 if (owner.view == View.Details && !owner.full_row_select)
2204 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
2206 rect = clicked_item.Bounds;
2208 if (rect.Contains (pt)) {
2209 switch (owner.activation) {
2210 case ItemActivation.OneClick:
2211 owner.OnItemActivate (EventArgs.Empty);
2214 case ItemActivation.TwoClick:
2215 if (last_clicked_item == clicked_item) {
2216 owner.OnItemActivate (EventArgs.Empty);
2217 last_clicked_item = null;
2219 last_clicked_item = clicked_item;
2222 // DoubleClick activation is handled in another handler
2226 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
2227 // Need this to clean up background clicks
2228 owner.SelectedItems.Clear ();
2231 clicked_item = null;
2232 box_select_start = Point.Empty;
2233 BoxSelectRectangle = Rectangle.Empty;
2234 prev_selection = null;
2235 box_select_mode = BoxSelect.None;
2237 owner.OnMouseUp (owner_me);
2240 private void LabelEditFinished (object sender, EventArgs e)
2242 EndEdit (edit_item);
2245 private void LabelEditCancelled (object sender, EventArgs e)
2247 edit_args.SetLabel (null);
2248 EndEdit (edit_item);
2251 private void LabelTextChanged (object sender, EventArgs e)
2253 if (edit_args != null)
2254 edit_args.SetLabel (edit_text_box.Text);
2257 internal void BeginEdit (ListViewItem item)
2259 if (edit_item != null)
2260 EndEdit (edit_item);
2262 if (edit_text_box == null) {
2263 edit_text_box = new ListViewLabelEditTextBox ();
2264 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
2265 edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
2266 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
2267 edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
2268 edit_text_box.Visible = false;
2269 Controls.Add (edit_text_box);
2272 item.EnsureVisible();
2274 edit_text_box.Reset ();
2276 switch (owner.view) {
2278 case View.SmallIcon:
2280 edit_text_box.TextAlign = HorizontalAlignment.Left;
2281 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2282 SizeF sizef = DeviceContext.MeasureString (item.Text, item.Font);
2283 edit_text_box.Width = (int)sizef.Width + 4;
2284 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
2285 edit_text_box.WordWrap = false;
2286 edit_text_box.Multiline = false;
2288 case View.LargeIcon:
2289 edit_text_box.TextAlign = HorizontalAlignment.Center;
2290 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
2291 sizef = DeviceContext.MeasureString (item.Text, item.Font);
2292 edit_text_box.Width = (int)sizef.Width + 4;
2293 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
2294 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
2295 edit_text_box.WordWrap = true;
2296 edit_text_box.Multiline = true;
2302 edit_text_box.Text = item.Text;
2303 edit_text_box.Font = item.Font;
2304 edit_text_box.Visible = true;
2305 edit_text_box.Focus ();
2306 edit_text_box.SelectAll ();
2308 edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
2309 owner.OnBeforeLabelEdit (edit_args);
2311 if (edit_args.CancelEdit)
2315 internal void CancelEdit (ListViewItem item)
2317 // do nothing if there's no item being edited, or if the
2318 // item being edited is not the one passed in
2319 if (edit_item == null || edit_item != item)
2322 edit_args.SetLabel (null);
2326 internal void EndEdit (ListViewItem item)
2328 // do nothing if there's no item being edited, or if the
2329 // item being edited is not the one passed in
2330 if (edit_item == null || edit_item != item)
2333 owner.OnAfterLabelEdit (edit_args);
2334 if (!edit_args.CancelEdit && edit_args.Label != null)
2335 edit_item.Text = edit_text_box.Text;
2337 if (edit_text_box != null) {
2338 if (edit_text_box.Visible)
2339 edit_text_box.Visible = false;
2340 // ensure listview gets focus
2347 internal override void OnPaintInternal (PaintEventArgs pe)
2349 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
2352 protected override void WndProc (ref Message m)
2354 switch ((Msg)m.Msg) {
2355 case Msg.WM_KILLFOCUS:
2356 owner.Select (false, true);
2358 case Msg.WM_SETFOCUS:
2359 owner.Select (false, true);
2361 case Msg.WM_RBUTTONDOWN:
2362 owner.Select (false, true);
2367 base.WndProc (ref m);
2371 internal class ListViewLabelEditTextBox : TextBox
2376 int max_height = -1;
2377 int min_height = -1;
2379 int old_number_lines = 1;
2381 SizeF text_size_one_char;
2383 public ListViewLabelEditTextBox ()
2385 min_height = DefaultSize.Height;
2386 text_size_one_char = DeviceContext.MeasureString ("B", Font);
2389 public int MaxWidth {
2391 if (value < min_width)
2392 max_width = min_width;
2398 public int MaxHeight {
2400 if (value < min_height)
2401 max_height = min_height;
2407 public new int Width {
2417 public override Font Font {
2423 text_size_one_char = DeviceContext.MeasureString ("B", Font);
2427 protected override void OnTextChanged (EventArgs e)
2429 SizeF text_size = DeviceContext.MeasureString (Text, Font);
2431 int new_width = (int)text_size.Width + 8;
2434 ResizeTextBoxWidth (new_width);
2436 if (Width != max_width)
2437 ResizeTextBoxWidth (new_width);
2439 int number_lines = Lines.Length;
2441 if (number_lines != old_number_lines) {
2442 int new_height = number_lines * (int)text_size_one_char.Height + 4;
2443 old_number_lines = number_lines;
2445 ResizeTextBoxHeight (new_height);
2449 base.OnTextChanged (e);
2452 protected override bool IsInputKey (Keys key_data)
2454 if ((key_data & Keys.Alt) == 0) {
2455 switch (key_data & Keys.KeyCode) {
2462 return base.IsInputKey (key_data);
2465 protected override void OnKeyDown (KeyEventArgs e)
2470 switch (e.KeyCode) {
2474 OnEditingFinished (e);
2479 OnEditingCancelled (e);
2484 protected override void OnLostFocus (EventArgs e)
2487 OnEditingFinished (e);
2491 protected void OnEditingCancelled (EventArgs e)
2493 EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
2498 protected void OnEditingFinished (EventArgs e)
2500 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
2505 private void ResizeTextBoxWidth (int new_width)
2507 if (new_width > max_width)
2508 base.Width = max_width;
2510 if (new_width >= min_width)
2511 base.Width = new_width;
2513 base.Width = min_width;
2516 private void ResizeTextBoxHeight (int new_height)
2518 if (new_height > max_height)
2519 base.Height = max_height;
2521 if (new_height >= min_height)
2522 base.Height = new_height;
2524 base.Height = min_height;
2527 public void Reset ()
2534 old_number_lines = 1;
2536 Text = String.Empty;
2541 static object EditingCancelledEvent = new object ();
2542 public event EventHandler EditingCancelled {
2543 add { Events.AddHandler (EditingCancelledEvent, value); }
2544 remove { Events.RemoveHandler (EditingCancelledEvent, value); }
2547 static object EditingFinishedEvent = new object ();
2548 public event EventHandler EditingFinished {
2549 add { Events.AddHandler (EditingFinishedEvent, value); }
2550 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
2554 internal override void OnPaintInternal (PaintEventArgs pe)
2559 CalculateScrollBars ();
2562 void FocusChanged (object o, EventArgs args)
2564 if (Items.Count == 0)
2567 if (FocusedItem == null)
2568 SetFocusedItem (Items [0]);
2570 ListViewItem focused_item = FocusedItem;
2572 if (focused_item.ListView != null) {
2573 item_control.Invalidate (focused_item.Bounds);
2574 focused_item.Layout ();
2575 item_control.Invalidate (focused_item.Bounds);
2579 private void ListView_MouseEnter (object sender, EventArgs args)
2581 hover_pending = true; // Need a hover event for every Enter/Leave cycle
2584 private void ListView_MouseWheel (object sender, MouseEventArgs me)
2586 if (Items.Count == 0)
2589 int lines = me.Delta / 120;
2596 case View.SmallIcon:
2597 Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
2599 case View.LargeIcon:
2600 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
2603 Scroll (h_scroll, -ItemSize.Width * lines);
2607 Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
2613 private void ListView_SizeChanged (object sender, EventArgs e)
2615 CalculateListView (alignment);
2618 private void SetFocusedItem (ListViewItem item)
2620 if (focused_item != null)
2621 focused_item.Focused = false;
2624 item.Focused = true;
2626 focused_item = item;
2629 private void HorizontalScroller (object sender, EventArgs e)
2631 item_control.EndEdit (item_control.edit_item);
2633 // Avoid unnecessary flickering, when button is
2634 // kept pressed at the end
2635 if (h_marker != h_scroll.Value) {
2637 int pixels = h_marker - h_scroll.Value;
2639 h_marker = h_scroll.Value;
2640 if (header_control.Visible)
2641 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
2643 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
2647 private void VerticalScroller (object sender, EventArgs e)
2649 item_control.EndEdit (item_control.edit_item);
2651 // Avoid unnecessary flickering, when button is
2652 // kept pressed at the end
2653 if (v_marker != v_scroll.Value) {
2654 int pixels = v_marker - v_scroll.Value;
2655 Rectangle area = item_control.ClientRectangle;
2656 v_marker = v_scroll.Value;
2657 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
2660 #endregion // Internal Methods Properties
2662 #region Protected Methods
2663 protected override void CreateHandle ()
2665 base.CreateHandle ();
2666 for (int i = 0; i < SelectedItems.Count; i++)
2667 OnSelectedIndexChanged (EventArgs.Empty);
2670 protected override void Dispose (bool disposing)
2673 h_scroll.Dispose ();
2674 v_scroll.Dispose ();
2676 large_image_list = null;
2677 small_image_list = null;
2678 state_image_list = null;
2680 foreach (ColumnHeader col in columns)
2681 col.SetListView (null);
2684 if (!virtual_mode) // In virtual mode we don't save the items
2686 foreach (ListViewItem item in items)
2690 base.Dispose (disposing);
2693 protected override bool IsInputKey (Keys keyData)
2710 return base.IsInputKey (keyData);
2713 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
2715 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
2720 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
2722 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
2727 protected virtual void OnColumnClick (ColumnClickEventArgs e)
2729 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
2735 protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
2737 DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
2742 protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
2744 DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
2749 protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
2751 DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
2757 protected override void OnEnabledChanged (EventArgs e)
2759 base.OnEnabledChanged (e);
2762 protected override void OnFontChanged (EventArgs e)
2764 base.OnFontChanged (e);
2768 protected override void OnHandleCreated (EventArgs e)
2770 base.OnHandleCreated (e);
2771 CalculateListView (alignment);
2773 if (!virtual_mode) // Sorting is not allowed in virtual mode
2778 protected override void OnHandleDestroyed (EventArgs e)
2780 base.OnHandleDestroyed (e);
2783 protected virtual void OnItemActivate (EventArgs e)
2785 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
2790 protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
2792 ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
2798 protected internal virtual void OnItemChecked (ItemCheckedEventArgs icea)
2800 ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
2806 protected virtual void OnItemDrag (ItemDragEventArgs e)
2808 EventHandler eh = (EventHandler)(Events [ItemDragEvent]);
2814 protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs args)
2816 ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
2821 protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs args)
2823 ListViewItemSelectionChangedEventHandler eh =
2824 (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
2830 protected virtual void OnSelectedIndexChanged (EventArgs e)
2832 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
2837 protected override void OnSystemColorsChanged (EventArgs e)
2839 base.OnSystemColorsChanged (e);
2843 protected virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs args)
2845 EventHandler eh = (EventHandler)Events [CacheVirtualItemsEvent];
2850 protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs args)
2852 RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
2858 protected void RealizeProperties ()
2863 protected void UpdateExtendedStyles ()
2868 bool refocusing = false;
2870 protected override void WndProc (ref Message m)
2872 switch ((Msg)m.Msg) {
2873 case Msg.WM_KILLFOCUS:
2874 Control receiver = Control.FromHandle (m.WParam);
2875 if (receiver == item_control) {
2881 case Msg.WM_SETFOCUS:
2891 base.WndProc (ref m);
2893 #endregion // Protected Methods
2895 #region Public Instance Methods
2896 public void ArrangeIcons ()
2898 ArrangeIcons (this.alignment);
2901 public void ArrangeIcons (ListViewAlignment alignment)
2903 // Icons are arranged only if view is set to LargeIcon or SmallIcon
2904 if (view == View.LargeIcon || view == View.SmallIcon) {
2905 this.CalculateListView (alignment);
2906 // we have done the calculations already
2907 this.Redraw (false);
2912 public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
2914 if (columnIndex < 0 || columnIndex >= columns.Count)
2915 throw new ArgumentOutOfRangeException ("columnIndex");
2917 columns [columnIndex].AutoResize (headerAutoResize);
2920 public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
2923 foreach (ColumnHeader col in columns)
2924 col.AutoResize (headerAutoResize);
2929 public void BeginUpdate ()
2931 // flag to avoid painting
2935 public void Clear ()
2938 items.Clear (); // Redraw (true) called here
2941 public void EndUpdate ()
2943 // flag to avoid painting
2946 // probably, now we need a redraw with recalculations
2950 public void EnsureVisible (int index)
2952 if (index < 0 || index >= items.Count || scrollable == false)
2955 Rectangle view_rect = item_control.ClientRectangle;
2956 Rectangle bounds = new Rectangle (GetItemLocation (index), ItemSize);
2958 if (view_rect.Contains (bounds))
2961 if (View != View.Details) {
2962 if (bounds.Left < 0)
2963 h_scroll.Value += bounds.Left;
2964 else if (bounds.Right > view_rect.Right)
2965 h_scroll.Value += (bounds.Right - view_rect.Right);
2969 v_scroll.Value += bounds.Top;
2970 else if (bounds.Bottom > view_rect.Bottom)
2971 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
2975 public ListViewItem FindItemWithText (string text)
2977 if (items.Count == 0)
2980 return FindItemWithText (text, true, 0, true);
2983 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex)
2985 return FindItemWithText (text, includeSubItems, startIndex, true);
2988 public ListViewItem FindItemWithText (string text, bool includeSubItems, int startIndex, bool prefixSearch)
2990 if (startIndex < 0 || startIndex >= items.Count)
2991 throw new ArgumentOutOfRangeException ("startIndex");
2994 throw new ArgumentNullException ("text");
2996 for (int i = startIndex; i < items.Count; i++) {
2997 ListViewItem lvi = items [i];
2999 if ((prefixSearch && lvi.Text.StartsWith (text, true, CultureInfo.CurrentCulture)) // prefix search
3000 || String.Compare (lvi.Text, text, true) == 0) // match
3004 if (includeSubItems) {
3005 for (int i = startIndex; i < items.Count; i++) {
3006 ListViewItem lvi = items [i];
3007 foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
3008 if ((prefixSearch && sub_item.Text.StartsWith (text, true, CultureInfo.CurrentCulture))
3009 || String.Compare (sub_item.Text, text, true) == 0)
3018 public ListViewItem GetItemAt (int x, int y)
3020 Size item_size = ItemSize;
3021 for (int i = 0; i < items.Count; i++) {
3022 Point item_location = GetItemLocation (i);
3023 Rectangle item_rect = new Rectangle (item_location, item_size);
3024 if (item_rect.Contains (x, y))
3031 public Rectangle GetItemRect (int index)
3033 return GetItemRect (index, ItemBoundsPortion.Entire);
3036 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
3038 if (index < 0 || index >= items.Count)
3039 throw new IndexOutOfRangeException ("index");
3041 return items [index].GetBounds (portion);
3045 public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
3047 if (startIndex < 0 || startIndex >= items.Count)
3048 throw new ArgumentOutOfRangeException ("startIndex");
3049 if (endIndex < 0 || endIndex >= items.Count)
3050 throw new ArgumentOutOfRangeException ("endIndex");
3051 if (startIndex > endIndex)
3052 throw new ArgumentException ("startIndex");
3057 for (int i = startIndex; i <= endIndex; i++)
3058 item_control.Invalidate (items [i].Bounds);
3060 if (!invalidateOnly)
3069 throw new InvalidOperationException ();
3075 // we need this overload to reuse the logic for sorting, while allowing
3076 // redrawing to be done by caller or have it done by this method when
3077 // sorting is really performed
3079 // ListViewItemCollection's Add and AddRange methods call this overload
3080 // with redraw set to false, as they take care of redrawing themselves
3081 // (they even want to redraw the listview if no sort is performed, as
3082 // an item was added), while ListView.Sort () only wants to redraw if
3083 // sorting was actually performed
3084 private void Sort (bool redraw)
3086 if (!IsHandleCreated || item_sorter == null) {
3090 items.Sort (item_sorter);
3095 public override string ToString ()
3097 int count = this.Items.Count;
3100 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
3102 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
3104 #endregion // Public Instance Methods
3109 class HeaderControl : Control {
3112 bool column_resize_active = false;
3113 ColumnHeader resize_column;
3114 ColumnHeader clicked_column;
3115 ColumnHeader drag_column;
3117 int drag_to_index = -1;
3119 public HeaderControl (ListView owner)
3122 MouseDown += new MouseEventHandler (HeaderMouseDown);
3123 MouseMove += new MouseEventHandler (HeaderMouseMove);
3124 MouseUp += new MouseEventHandler (HeaderMouseUp);
3127 private ColumnHeader ColumnAtX (int x)
3129 Point pt = new Point (x, 0);
3130 ColumnHeader result = null;
3131 foreach (ColumnHeader col in owner.Columns) {
3132 if (col.Rect.Contains (pt)) {
3140 private int GetReorderedIndex (ColumnHeader col)
3142 if (owner.reordered_column_indices == null)
3145 for (int i = 0; i < owner.Columns.Count; i++)
3146 if (owner.reordered_column_indices [i] == col.Index)
3148 throw new Exception ("Column index missing from reordered array");
3151 private void HeaderMouseDown (object sender, MouseEventArgs me)
3153 if (resize_column != null) {
3154 column_resize_active = true;
3159 clicked_column = ColumnAtX (me.X + owner.h_marker);
3161 if (clicked_column != null) {
3163 if (owner.AllowColumnReorder) {
3165 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
3166 drag_column.Rect = clicked_column.Rect;
3167 drag_to_index = GetReorderedIndex (clicked_column);
3169 clicked_column.Pressed = true;
3170 Rectangle bounds = clicked_column.Rect;
3171 bounds.X -= owner.h_marker;
3172 Invalidate (bounds);
3179 column_resize_active = false;
3180 resize_column = null;
3182 Cursor = Cursors.Default;
3185 private void HeaderMouseMove (object sender, MouseEventArgs me)
3187 Point pt = new Point (me.X + owner.h_marker, me.Y);
3189 if (column_resize_active) {
3190 int width = pt.X - resize_column.X;
3194 if (!owner.CanProceedWithResize (resize_column, width)){
3198 resize_column.Width = width;
3202 resize_column = null;
3204 if (clicked_column != null) {
3205 if (owner.AllowColumnReorder) {
3208 r = drag_column.Rect;
3209 r.X = clicked_column.Rect.X + me.X - drag_x;
3210 drag_column.Rect = r;
3212 int x = me.X + owner.h_marker;
3213 ColumnHeader over = ColumnAtX (x);
3215 drag_to_index = owner.Columns.Count;
3216 else if (x < over.X + over.Width / 2)
3217 drag_to_index = GetReorderedIndex (over);
3219 drag_to_index = GetReorderedIndex (over) + 1;
3222 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
3223 bool pressed = clicked_column.Pressed;
3224 clicked_column.Pressed = over == clicked_column;
3225 if (clicked_column.Pressed ^ pressed) {
3226 Rectangle bounds = clicked_column.Rect;
3227 bounds.X -= owner.h_marker;
3228 Invalidate (bounds);
3234 for (int i = 0; i < owner.Columns.Count; i++) {
3235 Rectangle zone = owner.Columns [i].Rect;
3236 zone.X = zone.Right - 5;
3238 if (zone.Contains (pt)) {
3239 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
3241 resize_column = owner.Columns [i];
3246 if (resize_column == null)
3247 Cursor = Cursors.Default;
3249 Cursor = Cursors.VSplit;
3252 void HeaderMouseUp (object sender, MouseEventArgs me)
3256 if (column_resize_active) {
3257 int column_idx = resize_column.Index;
3259 owner.RaiseColumnWidthChanged (column_idx);
3263 if (clicked_column != null && clicked_column.Pressed) {
3264 clicked_column.Pressed = false;
3265 Rectangle bounds = clicked_column.Rect;
3266 bounds.X -= owner.h_marker;
3267 Invalidate (bounds);
3268 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
3271 if (drag_column != null && owner.AllowColumnReorder) {
3273 if (drag_to_index > GetReorderedIndex (clicked_column))
3275 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
3276 owner.ReorderColumn (clicked_column, drag_to_index, true);
3281 clicked_column = null;
3284 internal override void OnPaintInternal (PaintEventArgs pe)
3289 Theme theme = ThemeEngine.Current;
3290 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
3292 if (drag_column == null)
3296 if (drag_to_index == owner.Columns.Count)
3297 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
3299 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
3300 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
3303 protected override void WndProc (ref Message m)
3305 switch ((Msg)m.Msg) {
3306 case Msg.WM_SETFOCUS:
3310 base.WndProc (ref m);
3316 private class ItemComparer : IComparer {
3317 readonly SortOrder sort_order;
3319 public ItemComparer (SortOrder sortOrder)
3321 sort_order = sortOrder;
3324 public int Compare (object x, object y)
3326 ListViewItem item_x = x as ListViewItem;
3327 ListViewItem item_y = y as ListViewItem;
3328 if (sort_order == SortOrder.Ascending)
3329 return String.Compare (item_x.Text, item_y.Text);
3331 return String.Compare (item_y.Text, item_x.Text);
3335 public class CheckedIndexCollection : IList, ICollection, IEnumerable
3337 private readonly ListView owner;
3339 #region Public Constructor
3340 public CheckedIndexCollection (ListView owner)
3344 #endregion // Public Constructor
3346 #region Public Properties
3349 get { return owner.CheckedItems.Count; }
3352 public bool IsReadOnly {
3353 get { return true; }
3356 public int this [int index] {
3358 int [] indices = GetIndices ();
3359 if (index < 0 || index >= indices.Length)
3360 throw new ArgumentOutOfRangeException ("index");
3361 return indices [index];
3365 bool ICollection.IsSynchronized {
3366 get { return false; }
3369 object ICollection.SyncRoot {
3370 get { return this; }
3373 bool IList.IsFixedSize {
3374 get { return true; }
3377 object IList.this [int index] {
3378 get { return this [index]; }
3379 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3381 #endregion // Public Properties
3383 #region Public Methods
3384 public bool Contains (int checkedIndex)
3386 int [] indices = GetIndices ();
3387 for (int i = 0; i < indices.Length; i++) {
3388 if (indices [i] == checkedIndex)
3394 public IEnumerator GetEnumerator ()
3396 int [] indices = GetIndices ();
3397 return indices.GetEnumerator ();
3400 void ICollection.CopyTo (Array dest, int index)
3402 int [] indices = GetIndices ();
3403 Array.Copy (indices, 0, dest, index, indices.Length);
3406 int IList.Add (object value)
3408 throw new NotSupportedException ("Add operation is not supported.");
3413 throw new NotSupportedException ("Clear operation is not supported.");
3416 bool IList.Contains (object checkedIndex)
3418 if (!(checkedIndex is int))
3420 return Contains ((int) checkedIndex);
3423 int IList.IndexOf (object checkedIndex)
3425 if (!(checkedIndex is int))
3427 return IndexOf ((int) checkedIndex);
3430 void IList.Insert (int index, object value)
3432 throw new NotSupportedException ("Insert operation is not supported.");
3435 void IList.Remove (object value)
3437 throw new NotSupportedException ("Remove operation is not supported.");
3440 void IList.RemoveAt (int index)
3442 throw new NotSupportedException ("RemoveAt operation is not supported.");
3445 public int IndexOf (int checkedIndex)
3447 int [] indices = GetIndices ();
3448 for (int i = 0; i < indices.Length; i++) {
3449 if (indices [i] == checkedIndex)
3454 #endregion // Public Methods
3456 private int [] GetIndices ()
3458 ArrayList checked_items = owner.CheckedItems.List;
3459 int [] indices = new int [checked_items.Count];
3460 for (int i = 0; i < checked_items.Count; i++) {
3461 ListViewItem item = (ListViewItem) checked_items [i];
3462 indices [i] = item.Index;
3466 } // CheckedIndexCollection
3468 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
3470 private readonly ListView owner;
3471 private ArrayList list;
3473 #region Public Constructor
3474 public CheckedListViewItemCollection (ListView owner)
3477 this.owner.Items.Changed += new CollectionChangedHandler (
3478 ItemsCollection_Changed);
3480 #endregion // Public Constructor
3482 #region Public Properties
3486 if (!owner.CheckBoxes)
3492 public bool IsReadOnly {
3493 get { return true; }
3496 public ListViewItem this [int index] {
3499 if (owner.VirtualMode)
3500 throw new InvalidOperationException ();
3502 ArrayList checked_items = List;
3503 if (index < 0 || index >= checked_items.Count)
3504 throw new ArgumentOutOfRangeException ("index");
3505 return (ListViewItem) checked_items [index];
3510 public virtual ListViewItem this [string key] {
3512 int idx = IndexOfKey (key);
3513 return idx == -1 ? null : (ListViewItem) List [idx];
3518 bool ICollection.IsSynchronized {
3519 get { return false; }
3522 object ICollection.SyncRoot {
3523 get { return this; }
3526 bool IList.IsFixedSize {
3527 get { return true; }
3530 object IList.this [int index] {
3531 get { return this [index]; }
3532 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3534 #endregion // Public Properties
3536 #region Public Methods
3537 public bool Contains (ListViewItem item)
3539 if (!owner.CheckBoxes)
3541 return List.Contains (item);
3545 public virtual bool ContainsKey (string key)
3547 return IndexOfKey (key) != -1;
3551 public void CopyTo (Array dest, int index)
3554 if (owner.VirtualMode)
3555 throw new InvalidOperationException ();
3557 if (!owner.CheckBoxes)
3559 List.CopyTo (dest, index);
3562 public IEnumerator GetEnumerator ()
3565 if (owner.VirtualMode)
3566 throw new InvalidOperationException ();
3568 if (!owner.CheckBoxes)
3569 return (new ListViewItem [0]).GetEnumerator ();
3570 return List.GetEnumerator ();
3573 int IList.Add (object value)
3575 throw new NotSupportedException ("Add operation is not supported.");
3580 throw new NotSupportedException ("Clear operation is not supported.");
3583 bool IList.Contains (object item)
3585 if (!(item is ListViewItem))
3587 return Contains ((ListViewItem) item);
3590 int IList.IndexOf (object item)
3592 if (!(item is ListViewItem))
3594 return IndexOf ((ListViewItem) item);
3597 void IList.Insert (int index, object value)
3599 throw new NotSupportedException ("Insert operation is not supported.");
3602 void IList.Remove (object value)
3604 throw new NotSupportedException ("Remove operation is not supported.");
3607 void IList.RemoveAt (int index)
3609 throw new NotSupportedException ("RemoveAt operation is not supported.");
3612 public int IndexOf (ListViewItem item)
3615 if (owner.VirtualMode)
3616 throw new InvalidOperationException ();
3618 if (!owner.CheckBoxes)
3620 return List.IndexOf (item);
3624 public virtual int IndexOfKey (string key)
3627 if (owner.VirtualMode)
3628 throw new InvalidOperationException ();
3630 if (key == null || key.Length == 0)
3633 ArrayList checked_items = List;
3634 for (int i = 0; i < checked_items.Count; i++) {
3635 ListViewItem item = (ListViewItem) checked_items [i];
3636 if (String.Compare (key, item.Name, true) == 0)
3643 #endregion // Public Methods
3645 internal ArrayList List {
3648 list = new ArrayList ();
3649 foreach (ListViewItem item in owner.Items) {
3658 internal void Reset ()
3660 // force re-population of list
3664 private void ItemsCollection_Changed ()
3668 } // CheckedListViewItemCollection
3670 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
3672 internal ArrayList list;
3673 private ListView owner;
3675 #region Public Constructor
3676 public ColumnHeaderCollection (ListView owner)
3678 list = new ArrayList ();
3681 #endregion // Public Constructor
3683 #region Public Properties
3686 get { return list.Count; }
3689 public bool IsReadOnly {
3690 get { return false; }
3693 public virtual ColumnHeader this [int index] {
3695 if (index < 0 || index >= list.Count)
3696 throw new ArgumentOutOfRangeException ("index");
3697 return (ColumnHeader) list [index];
3702 public virtual ColumnHeader this [string key] {
3704 int idx = IndexOfKey (key);
3708 return (ColumnHeader) list [idx];
3713 bool ICollection.IsSynchronized {
3714 get { return true; }
3717 object ICollection.SyncRoot {
3718 get { return this; }
3721 bool IList.IsFixedSize {
3722 get { return list.IsFixedSize; }
3725 object IList.this [int index] {
3726 get { return this [index]; }
3727 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3729 #endregion // Public Properties
3731 #region Public Methods
3732 public virtual int Add (ColumnHeader value)
3734 int idx = list.Add (value);
3735 owner.AddColumn (value, idx, true);
3739 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
3741 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
3742 this.Add (colHeader);
3747 public virtual ColumnHeader Add (string text)
3749 return Add (String.Empty, text);
3752 public virtual ColumnHeader Add (string text, int iwidth)
3754 return Add (String.Empty, text, iwidth);
3757 public virtual ColumnHeader Add (string key, string text)
3759 ColumnHeader colHeader = new ColumnHeader ();
3760 colHeader.Name = key;
3761 colHeader.Text = text;
3766 public virtual ColumnHeader Add (string key, string text, int iwidth)
3768 return Add (key, text, iwidth, HorizontalAlignment.Left, -1);
3771 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, int imageIndex)
3773 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
3774 colHeader.ImageIndex = imageIndex;
3779 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, string imageKey)
3781 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
3782 colHeader.ImageKey = imageKey;
3788 public virtual void AddRange (ColumnHeader [] values)
3790 foreach (ColumnHeader colHeader in values) {
3791 int idx = list.Add (colHeader);
3792 owner.AddColumn (colHeader, idx, false);
3795 owner.Redraw (true);
3798 public virtual void Clear ()
3800 foreach (ColumnHeader col in list)
3801 col.SetListView (null);
3803 owner.ReorderColumns (new int [0], true);
3806 public bool Contains (ColumnHeader value)
3808 return list.Contains (value);
3812 public virtual bool ContainsKey (string key)
3814 return IndexOfKey (key) != -1;
3818 public IEnumerator GetEnumerator ()
3820 return list.GetEnumerator ();
3823 void ICollection.CopyTo (Array dest, int index)
3825 list.CopyTo (dest, index);
3828 int IList.Add (object value)
3830 if (! (value is ColumnHeader)) {
3831 throw new ArgumentException ("Not of type ColumnHeader", "value");
3834 return this.Add ((ColumnHeader) value);
3837 bool IList.Contains (object value)
3839 if (! (value is ColumnHeader)) {
3840 throw new ArgumentException ("Not of type ColumnHeader", "value");
3843 return this.Contains ((ColumnHeader) value);
3846 int IList.IndexOf (object value)
3848 if (! (value is ColumnHeader)) {
3849 throw new ArgumentException ("Not of type ColumnHeader", "value");
3852 return this.IndexOf ((ColumnHeader) value);
3855 void IList.Insert (int index, object value)
3857 if (! (value is ColumnHeader)) {
3858 throw new ArgumentException ("Not of type ColumnHeader", "value");
3861 this.Insert (index, (ColumnHeader) value);
3864 void IList.Remove (object value)
3866 if (! (value is ColumnHeader)) {
3867 throw new ArgumentException ("Not of type ColumnHeader", "value");
3870 this.Remove ((ColumnHeader) value);
3873 public int IndexOf (ColumnHeader value)
3875 return list.IndexOf (value);
3879 public virtual int IndexOfKey (string key)
3881 if (key == null || key.Length == 0)
3884 for (int i = 0; i < list.Count; i++) {
3885 ColumnHeader col = (ColumnHeader) list [i];
3886 if (String.Compare (key, col.Name, true) == 0)
3894 public void Insert (int index, ColumnHeader value)
3896 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
3897 // but it's really only greater.
3898 if (index < 0 || index > list.Count)
3899 throw new ArgumentOutOfRangeException ("index");
3901 list.Insert (index, value);
3902 owner.AddColumn (value, index, true);
3906 public void Insert (int index, string text)
3908 Insert (index, String.Empty, text);
3911 public void Insert (int index, string text, int width)
3913 Insert (index, String.Empty, text, width);
3916 public void Insert (int index, string key, string text)
3918 ColumnHeader colHeader = new ColumnHeader ();
3919 colHeader.Name = key;
3920 colHeader.Text = text;
3921 Insert (index, colHeader);
3924 public void Insert (int index, string key, string text, int width)
3926 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
3927 Insert (index, colHeader);
3930 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
3932 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
3933 colHeader.ImageIndex = imageIndex;
3934 Insert (index, colHeader);
3937 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
3939 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
3940 colHeader.ImageKey = imageKey;
3941 Insert (index, colHeader);
3945 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
3947 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
3948 this.Insert (index, colHeader);
3951 public virtual void Remove (ColumnHeader column)
3953 if (!Contains (column))
3956 list.Remove (column);
3957 column.SetListView (null);
3959 int rem_display_index = column.InternalDisplayIndex;
3960 int [] display_indices = new int [list.Count];
3961 for (int i = 0; i < display_indices.Length; i++) {
3962 ColumnHeader col = (ColumnHeader) list [i];
3963 int display_index = col.InternalDisplayIndex;
3964 if (display_index < rem_display_index) {
3965 display_indices [i] = display_index;
3967 display_indices [i] = (display_index - 1);
3971 column.InternalDisplayIndex = -1;
3972 owner.ReorderColumns (display_indices, true);
3976 public virtual void RemoveByKey (string key)
3978 int idx = IndexOfKey (key);
3984 public virtual void RemoveAt (int index)
3986 if (index < 0 || index >= list.Count)
3987 throw new ArgumentOutOfRangeException ("index");
3989 ColumnHeader col = (ColumnHeader) list [index];
3992 #endregion // Public Methods
3995 } // ColumnHeaderCollection
3997 public class ListViewItemCollection : IList, ICollection, IEnumerable
3999 private readonly ArrayList list;
4000 private readonly ListView owner;
4002 #region Public Constructor
4003 public ListViewItemCollection (ListView owner)
4005 list = new ArrayList (0);
4008 #endregion // Public Constructor
4010 #region Public Properties
4015 if (owner != null && owner.VirtualMode)
4016 return owner.VirtualListSize;
4023 public bool IsReadOnly {
4024 get { return false; }
4027 public virtual ListViewItem this [int displayIndex] {
4029 if (displayIndex < 0 || displayIndex >= Count)
4030 throw new ArgumentOutOfRangeException ("displayIndex");
4033 if (owner != null && owner.VirtualMode)
4034 return RetrieveVirtualItemFromOwner (displayIndex);
4036 return (ListViewItem) list [displayIndex];
4040 if (displayIndex < 0 || displayIndex >= Count)
4041 throw new ArgumentOutOfRangeException ("displayIndex");
4044 if (owner != null && owner.VirtualMode)
4045 throw new InvalidOperationException ();
4048 if (list.Contains (value))
4049 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4051 if (value.ListView != null && value.ListView != owner)
4052 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");
4054 value.Owner = owner;
4055 list [displayIndex] = value;
4058 owner.Redraw (true);
4063 public virtual ListViewItem this [string key] {
4065 int idx = IndexOfKey (key);
4074 bool ICollection.IsSynchronized {
4075 get { return true; }
4078 object ICollection.SyncRoot {
4079 get { return this; }
4082 bool IList.IsFixedSize {
4083 get { return list.IsFixedSize; }
4086 object IList.this [int index] {
4087 get { return this [index]; }
4089 if (value is ListViewItem)
4090 this [index] = (ListViewItem) value;
4092 this [index] = new ListViewItem (value.ToString ());
4096 #endregion // Public Properties
4098 #region Public Methods
4099 public virtual ListViewItem Add (ListViewItem value)
4102 if (owner != null && owner.VirtualMode)
4103 throw new InvalidOperationException ();
4107 CollectionChanged (true);
4112 public virtual ListViewItem Add (string text)
4114 ListViewItem item = new ListViewItem (text);
4115 return this.Add (item);
4118 public virtual ListViewItem Add (string text, int imageIndex)
4120 ListViewItem item = new ListViewItem (text, imageIndex);
4121 return this.Add (item);
4125 public virtual ListViewItem Add (string text, string imageKey)
4127 ListViewItem item = new ListViewItem (text, imageKey);
4128 return this.Add (item);
4131 public virtual ListViewItem Add (string key, string text, int imageIndex)
4133 ListViewItem item = new ListViewItem (text, imageIndex);
4135 return this.Add (item);
4138 public virtual ListViewItem Add (string key, string text, string imageKey)
4140 ListViewItem item = new ListViewItem (text, imageKey);
4142 return this.Add (item);
4146 public void AddRange (ListViewItem [] values)
4149 throw new ArgumentNullException ("Argument cannot be null!", "values");
4151 if (owner != null && owner.VirtualMode)
4152 throw new InvalidOperationException ();
4155 foreach (ListViewItem item in values)
4158 CollectionChanged (true);
4162 public void AddRange (ListViewItemCollection items)
4165 throw new ArgumentNullException ("Argument cannot be null!", "items");
4167 ListViewItem[] itemArray = new ListViewItem[items.Count];
4168 items.CopyTo (itemArray,0);
4169 this.AddRange (itemArray);
4173 public virtual void Clear ()
4176 if (owner != null && owner.VirtualMode)
4177 throw new InvalidOperationException ();
4179 owner.SetFocusedItem (null);
4180 owner.h_scroll.Value = owner.v_scroll.Value = 0;
4181 foreach (ListViewItem item in list) {
4182 owner.item_control.CancelEdit (item);
4186 CollectionChanged (false);
4189 public bool Contains (ListViewItem item)
4191 return IndexOf (item) != -1;
4195 public virtual bool ContainsKey (string key)
4197 return IndexOfKey (key) != -1;
4201 public void CopyTo (Array dest, int index)
4203 list.CopyTo (dest, index);
4207 public ListViewItem [] Find (string key, bool searchAllSubitems)
4210 return new ListViewItem [0];
4212 List<ListViewItem> temp_list = new List<ListViewItem> ();
4214 for (int i = 0; i < list.Count; i++) {
4215 ListViewItem lvi = (ListViewItem) list [i];
4216 if (String.Compare (key, lvi.Name, true) == 0)
4217 temp_list.Add (lvi);
4220 ListViewItem [] retval = new ListViewItem [temp_list.Count];
4221 temp_list.CopyTo (retval);
4227 public IEnumerator GetEnumerator ()
4230 if (owner != null && owner.VirtualMode)
4231 throw new InvalidOperationException ();
4234 return list.GetEnumerator ();
4237 int IList.Add (object item)
4243 if (owner != null && owner.VirtualMode)
4244 throw new InvalidOperationException ();
4247 if (item is ListViewItem) {
4248 li = (ListViewItem) item;
4249 if (list.Contains (li))
4250 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
4252 if (li.ListView != null && li.ListView != owner)
4253 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");
4256 li = new ListViewItem (item.ToString ());
4259 result = list.Add (li);
4260 CollectionChanged (true);
4265 bool IList.Contains (object item)
4267 return Contains ((ListViewItem) item);
4270 int IList.IndexOf (object item)
4272 return IndexOf ((ListViewItem) item);
4275 void IList.Insert (int index, object item)
4277 if (item is ListViewItem)
4278 this.Insert (index, (ListViewItem) item);
4280 this.Insert (index, item.ToString ());
4283 void IList.Remove (object item)
4285 Remove ((ListViewItem) item);
4288 public int IndexOf (ListViewItem item)
4291 if (owner != null && owner.VirtualMode) {
4292 for (int i = 0; i < Count; i++)
4293 if (RetrieveVirtualItemFromOwner (i) == item)
4300 return list.IndexOf (item);
4304 public virtual int IndexOfKey (string key)
4306 if (key == null || key.Length == 0)
4309 for (int i = 0; i < Count; i++) {
4310 ListViewItem lvi = this [i];
4311 if (String.Compare (key, lvi.Name, true) == 0)
4319 public ListViewItem Insert (int index, ListViewItem item)
4321 if (index < 0 || index > list.Count)
4322 throw new ArgumentOutOfRangeException ("index");
4325 if (owner != null && owner.VirtualMode)
4326 throw new InvalidOperationException ();
4329 if (list.Contains (item))
4330 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
4332 if (item.ListView != null && item.ListView != owner)
4333 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");
4336 list.Insert (index, item);
4337 CollectionChanged (true);
4341 public ListViewItem Insert (int index, string text)
4343 return this.Insert (index, new ListViewItem (text));
4346 public ListViewItem Insert (int index, string text, int imageIndex)
4348 return this.Insert (index, new ListViewItem (text, imageIndex));
4352 public ListViewItem Insert (int index, string key, string text, int imageIndex)
4354 ListViewItem lvi = new ListViewItem (text, imageIndex);
4356 return Insert (index, lvi);
4360 public virtual void Remove (ListViewItem item)
4363 if (owner != null && owner.VirtualMode)
4364 throw new InvalidOperationException ();
4366 if (!list.Contains (item))
4369 bool selection_changed = owner.SelectedItems.Contains (item);
4370 owner.item_control.CancelEdit (item);
4373 CollectionChanged (false);
4374 if (selection_changed)
4375 owner.OnSelectedIndexChanged (EventArgs.Empty);
4378 public virtual void RemoveAt (int index)
4380 if (index < 0 || index >= Count)
4381 throw new ArgumentOutOfRangeException ("index");
4384 if (owner != null && owner.VirtualMode)
4385 throw new InvalidOperationException ();
4388 ListViewItem item = (ListViewItem) list [index];
4393 public virtual void RemoveByKey (string key)
4395 int idx = IndexOfKey (key);
4401 #endregion // Public Methods
4403 void AddItem (ListViewItem value)
4405 if (list.Contains (value))
4406 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
4408 if (value.ListView != null && value.ListView != owner)
4409 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");
4410 value.Owner = owner;
4414 void CollectionChanged (bool sort)
4416 if (owner != null) {
4421 owner.Redraw (true);
4426 ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
4428 RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
4430 owner.OnRetrieveVirtualItem (args);
4431 ListViewItem retval = args.Item;
4432 retval.Owner = owner;
4433 retval.SetIndex (displayIndex);
4439 internal event CollectionChangedHandler Changed;
4441 internal void Sort (IComparer comparer)
4443 list.Sort (comparer);
4447 internal void OnChange ()
4449 if (Changed != null)
4452 } // ListViewItemCollection
4455 // In normal mode, the selection information resides in the Items,
4456 // making SelectedIndexCollection.List read-only
4458 // In virtual mode, SelectedIndexCollection directly saves the selection
4459 // information, instead of getting it from Items, making List read-and-write
4460 public class SelectedIndexCollection : IList, ICollection, IEnumerable
4462 private readonly ListView owner;
4463 private ArrayList list;
4465 #region Public Constructor
4466 public SelectedIndexCollection (ListView owner)
4469 owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
4471 #endregion // Public Constructor
4473 #region Public Properties
4477 if (!owner.IsHandleCreated)
4484 public bool IsReadOnly {
4494 public int this [int index] {
4496 if (!owner.IsHandleCreated || index < 0 || index >= List.Count)
4497 throw new ArgumentOutOfRangeException ("index");
4499 return (int) List [index];
4503 bool ICollection.IsSynchronized {
4504 get { return false; }
4507 object ICollection.SyncRoot {
4508 get { return this; }
4511 bool IList.IsFixedSize {
4521 object IList.this [int index] {
4522 get { return this [index]; }
4523 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4525 #endregion // Public Properties
4527 #region Public Methods
4529 public int Add (int itemIndex)
4531 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
4532 throw new ArgumentOutOfRangeException ("index");
4534 if (owner.virtual_mode && !owner.IsHandleCreated)
4537 owner.Items [itemIndex].Selected = true;
4539 if (!owner.IsHandleCreated)
4553 if (!owner.IsHandleCreated)
4556 int [] indexes = (int []) List.ToArray (typeof (int));
4557 foreach (int index in indexes)
4558 owner.Items [index].Selected = false;
4561 public bool Contains (int selectedIndex)
4563 return IndexOf (selectedIndex) != -1;
4566 public void CopyTo (Array dest, int index)
4568 List.CopyTo (dest, index);
4571 public IEnumerator GetEnumerator ()
4573 return List.GetEnumerator ();
4576 int IList.Add (object value)
4578 throw new NotSupportedException ("Add operation is not supported.");
4586 bool IList.Contains (object selectedIndex)
4588 if (!(selectedIndex is int))
4590 return Contains ((int) selectedIndex);
4593 int IList.IndexOf (object selectedIndex)
4595 if (!(selectedIndex is int))
4597 return IndexOf ((int) selectedIndex);
4600 void IList.Insert (int index, object value)
4602 throw new NotSupportedException ("Insert operation is not supported.");
4605 void IList.Remove (object value)
4607 throw new NotSupportedException ("Remove operation is not supported.");
4610 void IList.RemoveAt (int index)
4612 throw new NotSupportedException ("RemoveAt operation is not supported.");
4615 public int IndexOf (int selectedIndex)
4617 if (!owner.IsHandleCreated)
4620 return List.IndexOf (selectedIndex);
4624 public void Remove (int itemIndex)
4626 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
4627 throw new ArgumentOutOfRangeException ("itemIndex");
4629 owner.Items [itemIndex].Selected = false;
4632 #endregion // Public Methods
4634 internal ArrayList List {
4637 list = new ArrayList ();
4639 if (!owner.VirtualMode)
4641 for (int i = 0; i < owner.Items.Count; i++) {
4642 if (owner.Items [i].Selected)
4650 internal void Reset ()
4652 // force re-population of list
4654 if (!owner.VirtualMode)
4659 private void ItemsCollection_Changed ()
4665 internal void RemoveIndex (int index)
4667 int idx = List.BinarySearch (index);
4669 List.RemoveAt (idx);
4672 // actually store index in the collection
4673 // also, keep the collection sorted, as .Net does
4674 internal void InsertIndex (int index)
4677 int iMax = List.Count - 1;
4678 while (iMin <= iMax) {
4679 int iMid = (iMin + iMax) / 2;
4680 int current_index = (int) List [iMid];
4682 if (current_index == index)
4683 return; // Already added
4684 if (current_index > index)
4690 List.Insert (iMin, index);
4694 } // SelectedIndexCollection
4696 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
4698 private readonly ListView owner;
4700 #region Public Constructor
4701 public SelectedListViewItemCollection (ListView owner)
4705 #endregion // Public Constructor
4707 #region Public Properties
4711 return owner.SelectedIndices.Count;
4715 public bool IsReadOnly {
4716 get { return true; }
4719 public ListViewItem this [int index] {
4721 if (!owner.IsHandleCreated || index < 0 || index >= Count)
4722 throw new ArgumentOutOfRangeException ("index");
4724 int item_index = owner.SelectedIndices [index];
4725 return owner.Items [item_index];
4730 public virtual ListViewItem this [string key] {
4732 int idx = IndexOfKey (key);
4741 bool ICollection.IsSynchronized {
4742 get { return false; }
4745 object ICollection.SyncRoot {
4746 get { return this; }
4749 bool IList.IsFixedSize {
4750 get { return true; }
4753 object IList.this [int index] {
4754 get { return this [index]; }
4755 set { throw new NotSupportedException ("SetItem operation is not supported."); }
4757 #endregion // Public Properties
4759 #region Public Methods
4760 public void Clear ()
4762 owner.SelectedIndices.Clear ();
4765 public bool Contains (ListViewItem item)
4767 return IndexOf (item) != -1;
4771 public virtual bool ContainsKey (string key)
4773 return IndexOfKey (key) != -1;
4777 public void CopyTo (Array dest, int index)
4779 if (!owner.IsHandleCreated)
4781 if (index > Count) // Throws ArgumentException instead of IOOR exception
4782 throw new ArgumentException ("index");
4784 for (int i = 0; i < Count; i++)
4785 dest.SetValue (this [i], index++);
4788 public IEnumerator GetEnumerator ()
4790 if (!owner.IsHandleCreated)
4791 return (new ListViewItem [0]).GetEnumerator ();
4793 ListViewItem [] items = new ListViewItem [Count];
4794 for (int i = 0; i < Count; i++)
4795 items [i] = this [i];
4797 return items.GetEnumerator ();
4800 int IList.Add (object value)
4802 throw new NotSupportedException ("Add operation is not supported.");
4805 bool IList.Contains (object item)
4807 if (!(item is ListViewItem))
4809 return Contains ((ListViewItem) item);
4812 int IList.IndexOf (object item)
4814 if (!(item is ListViewItem))
4816 return IndexOf ((ListViewItem) item);
4819 void IList.Insert (int index, object value)
4821 throw new NotSupportedException ("Insert operation is not supported.");
4824 void IList.Remove (object value)
4826 throw new NotSupportedException ("Remove operation is not supported.");
4829 void IList.RemoveAt (int index)
4831 throw new NotSupportedException ("RemoveAt operation is not supported.");
4834 public int IndexOf (ListViewItem item)
4836 if (!owner.IsHandleCreated)
4839 for (int i = 0; i < Count; i++)
4840 if (this [i] == item)
4847 public virtual int IndexOfKey (string key)
4849 if (!owner.IsHandleCreated || key == null || key.Length == 0)
4852 for (int i = 0; i < Count; i++) {
4853 ListViewItem item = this [i];
4854 if (String.Compare (item.Name, key, true) == 0)
4861 #endregion // Public Methods
4863 } // SelectedListViewItemCollection
4865 internal delegate void CollectionChangedHandler ();
4867 struct ItemMatrixLocation
4872 public ItemMatrixLocation (int row, int col)
4899 #endregion // Subclasses
4901 protected override void OnResize (EventArgs e)
4906 protected override void OnMouseLeave (EventArgs e)
4908 base.OnMouseLeave (e);
4912 // ColumnReorder event
4914 static object ColumnReorderedEvent = new object ();
4915 public event ColumnReorderedEventHandler ColumnReordered {
4916 add { Events.AddHandler (ColumnReorderedEvent, value); }
4917 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
4920 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
4922 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
4929 // ColumnWidthChanged
4931 static object ColumnWidthChangedEvent = new object ();
4932 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
4933 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
4934 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
4937 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
4939 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
4944 void RaiseColumnWidthChanged (int resize_column)
4946 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
4948 OnColumnWidthChanged (n);
4952 // ColumnWidthChanging
4954 static object ColumnWidthChangingEvent = new object ();
4955 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
4956 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
4957 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
4960 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
4962 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
4968 // 2.0 profile based implementation
4970 bool CanProceedWithResize (ColumnHeader col, int width)
4972 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
4976 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
4977 cwceh (this, changing);
4978 return !changing.Cancel;
4982 // 1.0 profile based implementation
4984 bool CanProceedWithResize (ColumnHeader col, int width)
4989 void RaiseColumnWidthChanged (int resize_column)