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.
37 using System.Collections;
38 using System.ComponentModel;
39 using System.ComponentModel.Design;
41 using System.Runtime.InteropServices;
42 using System.Globalization;
44 using System.Collections.Generic;
47 namespace System.Windows.Forms
49 [DefaultEvent ("SelectedIndexChanged")]
50 [DefaultProperty ("Items")]
51 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
52 public class ListView : Control
54 private ItemActivation activation = ItemActivation.Standard;
55 private ListViewAlignment alignment = ListViewAlignment.Top;
56 private bool allow_column_reorder = false;
57 private bool auto_arrange = true;
58 private bool check_boxes = false;
59 private readonly CheckedIndexCollection checked_indices;
60 private readonly CheckedListViewItemCollection checked_items;
61 private readonly ColumnHeaderCollection columns;
62 internal ListViewItem focused_item;
63 private bool full_row_select = false;
64 private bool grid_lines = false;
65 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
66 private bool hide_selection = true;
67 private bool hover_selection = false;
68 private IComparer item_sorter;
69 private readonly ListViewItemCollection items;
71 private readonly ListViewGroupCollection groups;
72 private bool show_groups = true;
74 private bool label_edit = false;
75 private bool label_wrap = true;
76 private bool multiselect = true;
77 private bool scrollable = true;
78 private readonly SelectedIndexCollection selected_indices;
79 private readonly SelectedListViewItemCollection selected_items;
80 private SortOrder sort_order = SortOrder.None;
81 private ImageList state_image_list;
82 private bool updating = false;
83 private View view = View.LargeIcon;
84 private int layout_wd; // We might draw more than our client area
85 private int layout_ht; // therefore we need to have these two.
86 //private TextBox editor; // Used for editing an item text
87 HeaderControl header_control;
88 internal ItemControl item_control;
89 internal ScrollBar h_scroll; // used for scrolling horizontally
90 internal ScrollBar v_scroll; // used for scrolling vertically
91 internal int h_marker; // Position markers for scrolling
92 internal int v_marker;
93 private int keysearch_tickcnt;
94 private string keysearch_text;
95 static private readonly int keysearch_keydelay = 1000;
96 private int[] reordered_column_indices;
99 internal ImageList large_image_list;
100 internal ImageList small_image_list;
101 internal Size text_size = Size.Empty;
104 static object AfterLabelEditEvent = new object ();
105 static object BeforeLabelEditEvent = new object ();
106 static object ColumnClickEvent = new object ();
107 static object ItemActivateEvent = new object ();
108 static object ItemCheckEvent = new object ();
109 static object ItemDragEvent = new object ();
110 static object SelectedIndexChangedEvent = new object ();
112 public event LabelEditEventHandler AfterLabelEdit {
113 add { Events.AddHandler (AfterLabelEditEvent, value); }
114 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
118 [EditorBrowsable (EditorBrowsableState.Never)]
119 public new event EventHandler BackgroundImageChanged {
120 add { base.BackgroundImageChanged += value; }
121 remove { base.BackgroundImageChanged -= value; }
124 public event LabelEditEventHandler BeforeLabelEdit {
125 add { Events.AddHandler (BeforeLabelEditEvent, value); }
126 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
129 public event ColumnClickEventHandler ColumnClick {
130 add { Events.AddHandler (ColumnClickEvent, value); }
131 remove { Events.RemoveHandler (ColumnClickEvent, value); }
134 public event EventHandler ItemActivate {
135 add { Events.AddHandler (ItemActivateEvent, value); }
136 remove { Events.RemoveHandler (ItemActivateEvent, value); }
139 public event ItemCheckEventHandler ItemCheck {
140 add { Events.AddHandler (ItemCheckEvent, value); }
141 remove { Events.RemoveHandler (ItemCheckEvent, value); }
144 public event ItemDragEventHandler ItemDrag {
145 add { Events.AddHandler (ItemDragEvent, value); }
146 remove { Events.RemoveHandler (ItemDragEvent, value); }
150 [EditorBrowsable (EditorBrowsableState.Never)]
151 public new event PaintEventHandler Paint {
152 add { base.Paint += value; }
153 remove { base.Paint -= value; }
156 public event EventHandler SelectedIndexChanged {
157 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
158 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
162 [EditorBrowsable (EditorBrowsableState.Never)]
163 public new event EventHandler TextChanged {
164 add { base.TextChanged += value; }
165 remove { base.TextChanged -= value; }
170 #region Public Constructors
173 background_color = ThemeEngine.Current.ColorWindow;
174 items = new ListViewItemCollection (this);
176 groups = new ListViewGroupCollection (this);
178 checked_indices = new CheckedIndexCollection (this);
179 checked_items = new CheckedListViewItemCollection (this);
180 columns = new ColumnHeaderCollection (this);
181 foreground_color = SystemColors.WindowText;
182 selected_indices = new SelectedIndexCollection (this);
183 selected_items = new SelectedListViewItemCollection (this);
185 border_style = BorderStyle.Fixed3D;
187 header_control = new HeaderControl (this);
188 header_control.Visible = false;
189 Controls.AddImplicit (header_control);
191 item_control = new ItemControl (this);
192 Controls.AddImplicit (item_control);
194 h_scroll = new ImplicitHScrollBar ();
195 Controls.AddImplicit (this.h_scroll);
197 v_scroll = new ImplicitVScrollBar ();
198 Controls.AddImplicit (this.v_scroll);
200 h_marker = v_marker = 0;
201 keysearch_tickcnt = 0;
203 // scroll bars are disabled initially
204 h_scroll.Visible = false;
205 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
206 v_scroll.Visible = false;
207 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
210 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
211 SizeChanged += new EventHandler (ListView_SizeChanged);
212 GotFocus += new EventHandler (FocusChanged);
213 LostFocus += new EventHandler (FocusChanged);
214 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
216 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
218 | ControlStyles.UseTextForAccessibility
222 #endregion // Public Constructors
224 #region Private Internal Properties
225 internal Size CheckBoxSize {
227 if (this.check_boxes) {
228 if (this.state_image_list != null)
229 return this.state_image_list.ImageSize;
231 return ThemeEngine.Current.ListViewCheckBoxSize;
237 #endregion // Private Internal Properties
239 #region Protected Properties
240 protected override CreateParams CreateParams {
241 get { return base.CreateParams; }
244 protected override Size DefaultSize {
245 get { return ThemeEngine.Current.ListViewDefaultSize; }
247 #endregion // Protected Properties
249 #region Public Instance Properties
250 [DefaultValue (ItemActivation.Standard)]
251 public ItemActivation Activation {
252 get { return activation; }
254 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
255 value != ItemActivation.TwoClick) {
256 throw new InvalidEnumArgumentException (string.Format
257 ("Enum argument value '{0}' is not valid for Activation", value));
264 [DefaultValue (ListViewAlignment.Top)]
266 public ListViewAlignment Alignment {
267 get { return alignment; }
269 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
270 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
271 throw new InvalidEnumArgumentException (string.Format
272 ("Enum argument value '{0}' is not valid for Alignment", value));
275 if (this.alignment != value) {
277 // alignment does not matter in Details/List views
278 if (this.view == View.LargeIcon ||
279 this.View == View.SmallIcon)
285 [DefaultValue (false)]
286 public bool AllowColumnReorder {
287 get { return allow_column_reorder; }
288 set { allow_column_reorder = value; }
291 [DefaultValue (true)]
292 public bool AutoArrange {
293 get { return auto_arrange; }
295 if (auto_arrange != value) {
296 auto_arrange = value;
297 // autoarrange does not matter in Details/List views
298 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
304 public override Color BackColor {
306 if (background_color.IsEmpty)
307 return ThemeEngine.Current.ColorWindow;
309 return background_color;
311 set { background_color = value; }
315 [EditorBrowsable (EditorBrowsableState.Never)]
316 public override Image BackgroundImage {
317 get { return base.BackgroundImage; }
318 set { base.BackgroundImage = value; }
321 [DefaultValue (BorderStyle.Fixed3D)]
323 public BorderStyle BorderStyle {
324 get { return InternalBorderStyle; }
325 set { InternalBorderStyle = value; }
328 [DefaultValue (false)]
329 public bool CheckBoxes {
330 get { return check_boxes; }
332 if (check_boxes != value) {
334 if (value && View == View.Tile)
335 throw new NotSupportedException ("CheckBoxes are not"
336 + " supported in Tile view. Choose a different"
337 + " view or set CheckBoxes to false.");
347 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
348 public CheckedIndexCollection CheckedIndices {
349 get { return checked_indices; }
353 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
354 public CheckedListViewItemCollection CheckedItems {
355 get { return checked_items; }
358 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
360 [MergableProperty (false)]
361 public ColumnHeaderCollection Columns {
362 get { return columns; }
366 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
367 public ListViewItem FocusedItem {
373 public override Color ForeColor {
375 if (foreground_color.IsEmpty)
376 return ThemeEngine.Current.ColorWindowText;
378 return foreground_color;
380 set { foreground_color = value; }
383 [DefaultValue (false)]
384 public bool FullRowSelect {
385 get { return full_row_select; }
386 set { full_row_select = value; }
389 [DefaultValue (false)]
390 public bool GridLines {
391 get { return grid_lines; }
393 if (grid_lines != value) {
400 [DefaultValue (ColumnHeaderStyle.Clickable)]
401 public ColumnHeaderStyle HeaderStyle {
402 get { return header_style; }
404 if (header_style == value)
408 case ColumnHeaderStyle.Clickable:
409 case ColumnHeaderStyle.Nonclickable:
410 case ColumnHeaderStyle.None:
413 throw new InvalidEnumArgumentException (string.Format
414 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
417 header_style = value;
418 if (view == View.Details)
423 [DefaultValue (true)]
424 public bool HideSelection {
425 get { return hide_selection; }
427 if (hide_selection != value) {
428 hide_selection = value;
434 [DefaultValue (false)]
435 public bool HoverSelection {
436 get { return hover_selection; }
437 set { hover_selection = value; }
440 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
442 [MergableProperty (false)]
443 public ListViewItemCollection Items {
444 get { return items; }
447 [DefaultValue (false)]
448 public bool LabelEdit {
449 get { return label_edit; }
450 set { label_edit = value; }
453 [DefaultValue (true)]
455 public bool LabelWrap {
456 get { return label_wrap; }
458 if (label_wrap != value) {
465 [DefaultValue (null)]
466 public ImageList LargeImageList {
467 get { return large_image_list; }
469 large_image_list = value;
475 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
476 public IComparer ListViewItemSorter {
478 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
483 if (item_sorter != value) {
490 [DefaultValue (true)]
491 public bool MultiSelect {
492 get { return multiselect; }
493 set { multiselect = value; }
496 [DefaultValue (true)]
497 public bool Scrollable {
498 get { return scrollable; }
500 if (scrollable != value) {
508 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
509 public SelectedIndexCollection SelectedIndices {
510 get { return selected_indices; }
514 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
515 public SelectedListViewItemCollection SelectedItems {
516 get { return selected_items; }
521 public bool ShowGroups {
522 get { return show_groups; }
525 if (show_groups != value)
533 [LocalizableAttribute(true)]
534 public ListViewGroupCollection Groups {
535 get { return groups; }
539 [DefaultValue (null)]
540 public ImageList SmallImageList {
541 get { return small_image_list; }
543 small_image_list = value;
548 [DefaultValue (SortOrder.None)]
549 public SortOrder Sorting {
550 get { return sort_order; }
552 if (!Enum.IsDefined (typeof (SortOrder), value)) {
553 throw new InvalidEnumArgumentException ("value", (int) value,
557 if (sort_order == value)
562 if (value == SortOrder.None) {
563 if (item_sorter != null) {
564 // ListViewItemSorter should never be reset for SmallIcon
565 // and LargeIcon view
566 if (View != View.SmallIcon && View != View.LargeIcon)
570 // in .NET 1.1, only internal IComparer would be
572 if (item_sorter is ItemComparer)
578 if (item_sorter == null)
579 item_sorter = new ItemComparer (value);
580 if (item_sorter is ItemComparer) {
582 item_sorter = new ItemComparer (value);
584 // in .NET 1.1, the sort order is not updated for
585 // SmallIcon and LargeIcon views if no custom IComparer
587 if (View != View.SmallIcon && View != View.LargeIcon)
588 item_sorter = new ItemComparer (value);
596 [DefaultValue (null)]
597 public ImageList StateImageList {
598 get { return state_image_list; }
600 state_image_list = value;
607 [EditorBrowsable (EditorBrowsableState.Never)]
608 public override string Text {
609 get { return base.Text; }
611 if (value == base.Text)
620 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
621 public ListViewItem TopItem {
624 if (this.items.Count == 0)
626 // if contents are not scrolled
627 // it is the first item
628 else if (h_marker == 0 && v_marker == 0)
629 return this.items [0];
630 // do a hit test for the scrolled position
632 foreach (ListViewItem item in this.items) {
633 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
642 [MonoTODO("Implement")]
643 public bool UseCompatibleStateImageBehavior {
653 [DefaultValue (View.LargeIcon)]
657 if (!Enum.IsDefined (typeof (View), value))
658 throw new InvalidEnumArgumentException ("value", (int) value,
663 if (CheckBoxes && value == View.Tile)
664 throw new NotSupportedException ("CheckBoxes are not"
665 + " supported in Tile view. Choose a different"
666 + " view or set CheckBoxes to false.");
669 h_scroll.Value = v_scroll.Value = 0;
675 #endregion // Public Instance Properties
677 #region Internal Methods Properties
679 internal int FirstVisibleIndex {
682 if (this.items.Count == 0)
685 if (h_marker == 0 && v_marker == 0)
688 foreach (ListViewItem item in this.items) {
689 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
698 internal int LastVisibleIndex {
700 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
701 if (View == View.List || Alignment == ListViewAlignment.Left) {
702 if (Items[i].Bounds.X > ClientRectangle.Right)
705 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
710 return Items.Count - 1;
714 internal void OnSelectedIndexChanged ()
717 OnSelectedIndexChanged (EventArgs.Empty);
720 internal int TotalWidth {
721 get { return Math.Max (this.Width, this.layout_wd); }
724 internal int TotalHeight {
725 get { return Math.Max (this.Height, this.layout_ht); }
728 internal void Redraw (bool recalculate)
730 // Avoid calculations when control is being updated
735 CalculateListView (this.alignment);
740 const int text_padding = 5;
742 internal Size GetChildColumnSize (int index)
744 Size ret_size = Size.Empty;
745 ColumnHeader col = this.columns [index];
747 if (col.Width == -2) { // autosize = max(items, columnheader)
748 Size size = Size.Ceiling (this.DeviceContext.MeasureString
749 (col.Text, this.Font));
750 size.Height += text_padding;
751 size.Width += text_padding;
752 ret_size = BiggestItem (index);
753 if (size.Width > ret_size.Width)
756 else { // -1 and all the values < -2 are put under one category
757 ret_size = BiggestItem (index);
758 // fall back to empty columns' width if no subitem is available for a column
759 if (ret_size.IsEmpty) {
760 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
761 if (col.Text.Length > 0)
762 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
763 (col.Text, this.Font)).Height;
765 ret_size.Height = this.Font.Height;
769 // adjust the size for icon and checkbox for 0th column
771 ret_size.Width += (this.CheckBoxSize.Width + 4);
772 if (this.small_image_list != null)
773 ret_size.Width += this.small_image_list.ImageSize.Width;
778 // Returns the size of biggest item text in a column.
779 private Size BiggestItem (int col)
781 Size temp = Size.Empty;
782 Size ret_size = Size.Empty;
784 // 0th column holds the item text, we check the size of
785 // the various subitems falling in that column and get
786 // the biggest one's size.
787 foreach (ListViewItem item in items) {
788 if (col >= item.SubItems.Count)
791 temp = Size.Ceiling (this.DeviceContext.MeasureString
792 (item.SubItems [col].Text, this.Font));
793 if (temp.Width > ret_size.Width)
797 // adjustment for space
798 if (!ret_size.IsEmpty)
804 const int max_wrap_padding = 38;
806 // Sets the size of the biggest item text as per the view
807 private void CalcTextSize ()
809 // clear the old value
810 text_size = Size.Empty;
812 if (items.Count == 0)
815 text_size = BiggestItem (0);
817 if (view == View.LargeIcon && this.label_wrap) {
818 Size temp = Size.Empty;
819 if (this.check_boxes)
820 temp.Width += 2 * this.CheckBoxSize.Width;
821 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
822 temp.Width += icon_w + max_wrap_padding;
823 // wrapping is done for two lines only
824 if (text_size.Width > temp.Width) {
825 text_size.Width = temp.Width;
826 text_size.Height *= 2;
829 else if (view == View.List) {
830 // in list view max text shown in determined by the
831 // control width, even if scolling is enabled.
832 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
833 if (this.small_image_list != null)
834 max_wd -= this.small_image_list.ImageSize.Width;
836 if (text_size.Width > max_wd)
837 text_size.Width = max_wd;
840 // we do the default settings, if we have got 0's
841 if (text_size.Height <= 0)
842 text_size.Height = this.Font.Height;
843 if (text_size.Width <= 0)
844 text_size.Width = this.Width;
847 text_size.Width += 4;
848 text_size.Height += 2;
851 private void Scroll (ScrollBar scrollbar, int delta)
853 if (delta == 0 || !scrollbar.Visible)
857 if (scrollbar == h_scroll)
858 max = h_scroll.Maximum - item_control.Width;
860 max = v_scroll.Maximum - item_control.Height;
862 int val = scrollbar.Value + delta;
865 else if (val < scrollbar.Minimum)
866 val = scrollbar.Minimum;
867 scrollbar.Value = val;
870 private void CalculateScrollBars ()
872 Rectangle client_area = ClientRectangle;
874 if (!this.scrollable || this.items.Count <= 0) {
875 h_scroll.Visible = false;
876 v_scroll.Visible = false;
877 item_control.Location = new Point (0, header_control.Height);
878 item_control.Height = ClientRectangle.Width - header_control.Height;
879 item_control.Width = ClientRectangle.Width;
880 header_control.Width = ClientRectangle.Width;
884 // Don't calculate if the view is not displayable
885 if (client_area.Height < 0 || client_area.Width < 0)
888 // making a scroll bar visible might make
889 // other scroll bar visible
890 if (layout_wd > client_area.Right) {
891 h_scroll.Visible = true;
892 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
893 v_scroll.Visible = true;
895 v_scroll.Visible = false;
896 } else if (layout_ht > client_area.Bottom) {
897 v_scroll.Visible = true;
898 if ((layout_wd + v_scroll.Width) > client_area.Right)
899 h_scroll.Visible = true;
901 h_scroll.Visible = false;
903 h_scroll.Visible = false;
904 v_scroll.Visible = false;
907 item_control.Height = ClientRectangle.Height - header_control.Height;
909 if (h_scroll.is_visible) {
910 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
911 h_scroll.Minimum = 0;
913 // if v_scroll is visible, adjust the maximum of the
914 // h_scroll to account for the width of v_scroll
915 if (v_scroll.Visible) {
916 h_scroll.Maximum = layout_wd + v_scroll.Width;
917 h_scroll.Width = client_area.Width - v_scroll.Width;
920 h_scroll.Maximum = layout_wd;
921 h_scroll.Width = client_area.Width;
924 h_scroll.LargeChange = client_area.Width;
925 h_scroll.SmallChange = Font.Height;
926 item_control.Height -= h_scroll.Height;
929 if (header_control.is_visible)
930 header_control.Width = ClientRectangle.Width;
931 item_control.Width = ClientRectangle.Width;
933 if (v_scroll.is_visible) {
934 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
935 v_scroll.Minimum = 0;
937 // if h_scroll is visible, adjust the maximum of the
938 // v_scroll to account for the height of h_scroll
939 if (h_scroll.Visible) {
940 v_scroll.Maximum = layout_ht + h_scroll.Height;
941 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
943 v_scroll.Maximum = layout_ht;
944 v_scroll.Height = client_area.Height;
947 v_scroll.LargeChange = client_area.Height;
948 v_scroll.SmallChange = Font.Height;
949 if (header_control.Visible)
950 header_control.Width -= v_scroll.Width;
951 item_control.Width -= v_scroll.Width;
955 ColumnHeader GetReorderedColumn (int index)
957 if (reordered_column_indices == null)
958 return Columns [index];
960 return Columns [reordered_column_indices [index]];
963 void ReorderColumn (ColumnHeader col, int index)
966 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
968 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
972 header_control.Invalidate ();
973 item_control.Invalidate ();
978 if (reordered_column_indices == null) {
979 reordered_column_indices = new int [Columns.Count];
980 for (int i = 0; i < Columns.Count; i++)
981 reordered_column_indices [i] = i;
984 if (reordered_column_indices [index] == col.Index)
987 int[] curr = reordered_column_indices;
988 int[] result = new int [Columns.Count];
990 for (int i = 0; i < Columns.Count; i++) {
991 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
995 result [i] = col.Index;
997 result [i] = curr [curr_idx++];
1000 reordered_column_indices = result;
1002 header_control.Invalidate ();
1003 item_control.Invalidate ();
1006 Size LargeIconItemSize {
1008 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1009 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1010 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
1011 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1012 return new Size (w, h);
1016 Size SmallIconItemSize {
1018 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1019 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1020 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
1021 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1022 return new Size (w, h);
1028 ListViewItem[,] item_matrix;
1030 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
1032 header_control.Visible = false;
1033 header_control.Size = Size.Empty;
1034 item_control.Visible = true;
1035 item_control.Location = Point.Empty;
1037 if (items.Count == 0)
1040 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
1042 Rectangle area = ClientRectangle;
1045 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
1048 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1050 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
1053 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1056 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1057 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1058 item_matrix = new ListViewItem [rows, cols];
1061 foreach (ListViewItem item in items) {
1062 int x = col * (sz.Width + x_spacing);
1063 int y = row * (sz.Height + y_spacing);
1064 item.Location = new Point (x, y);
1068 item_matrix [row, col] = item;
1070 if (++row == rows) {
1075 if (++col == cols) {
1082 item_control.Size = new Size (layout_wd, layout_ht);
1085 void LayoutHeader ()
1088 for (int i = 0; i < Columns.Count; i++) {
1089 ColumnHeader col = GetReorderedColumn (i);
1092 col.CalcColumnHeader ();
1096 if (x < ClientRectangle.Width)
1097 x = ClientRectangle.Width;
1099 if (header_style == ColumnHeaderStyle.None) {
1100 header_control.Visible = false;
1101 header_control.Size = Size.Empty;
1103 header_control.Width = x;
1104 header_control.Height = columns [0].Ht;
1105 header_control.Visible = true;
1109 void LayoutDetails ()
1111 if (columns.Count == 0) {
1112 header_control.Visible = false;
1113 item_control.Visible = false;
1119 item_control.Visible = true;
1120 item_control.Location = new Point (0, header_control.Height);
1123 if (items.Count > 0) {
1124 foreach (ListViewItem item in items) {
1126 item.Location = new Point (0, y);
1127 y += item.Bounds.Height + 2;
1130 // some space for bottom gridline
1135 layout_wd = Math.Max (header_control.Width, item_control.Width);
1136 layout_ht = y + header_control.Height;
1139 private void CalculateListView (ListViewAlignment align)
1148 case View.SmallIcon:
1149 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1152 case View.LargeIcon:
1153 LayoutIcons (true, alignment == ListViewAlignment.Left,
1154 ThemeEngine.Current.ListViewHorizontalSpacing,
1155 ThemeEngine.Current.ListViewVerticalSpacing);
1159 LayoutIcons (false, true, 4, 2);
1163 CalculateScrollBars ();
1166 private bool KeySearchString (KeyEventArgs ke)
1168 int current_tickcnt = Environment.TickCount;
1169 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1170 keysearch_text = string.Empty;
1173 keysearch_text += (char) ke.KeyData;
1174 keysearch_tickcnt = current_tickcnt;
1176 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1179 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1180 CompareOptions.IgnoreCase)) {
1181 SetFocusedItem (Items [i]);
1182 items [i].Selected = true;
1186 i = (i + 1 < Items.Count) ? i+1 : 0;
1194 int GetAdjustedIndex (Keys key)
1198 if (View == View.Details) {
1200 result = FocusedItem.Index - 1;
1201 else if (key == Keys.Down) {
1202 result = FocusedItem.Index + 1;
1203 if (result == items.Count)
1209 int row = FocusedItem.row;
1210 int col = FocusedItem.col;
1216 return item_matrix [row, col - 1].Index;
1219 if (col == (cols - 1))
1221 while (item_matrix [row, col + 1] == null) {
1226 return item_matrix [row, col + 1].Index;
1231 return item_matrix [row - 1, col].Index;
1234 if (row == (rows - 1) || row == Items.Count - 1)
1236 while (item_matrix [row + 1, col] == null) {
1241 return item_matrix [row + 1, col].Index;
1248 ListViewItem selection_start;
1250 private bool SelectItems (ArrayList sel_items)
1252 bool changed = false;
1253 ArrayList curr_items = SelectedItems.List;
1254 foreach (ListViewItem item in curr_items)
1255 if (!sel_items.Contains (item)) {
1256 item.Selected = false;
1259 foreach (ListViewItem item in sel_items)
1260 if (!item.Selected) {
1261 item.Selected = true;
1267 private void UpdateMultiSelection (int index)
1269 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1270 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1271 ListViewItem item = items [index];
1273 if (shift_pressed && selection_start != null) {
1274 ArrayList list = new ArrayList ();
1275 int start = Math.Min (selection_start.Index, index);
1276 int end = Math.Max (selection_start.Index, index);
1277 if (View == View.Details) {
1278 for (int i = start; i <= end; i++)
1279 list.Add (items [i]);
1281 int left = Math.Min (items [start].col, items [end].col);
1282 int right = Math.Max (items [start].col, items [end].col);
1283 int top = Math.Min (items [start].row, items [end].row);
1284 int bottom = Math.Max (items [start].row, items [end].row);
1285 foreach (ListViewItem curr in items)
1286 if (curr.row >= top && curr.row <= bottom &&
1287 curr.col >= left && curr.col <= right)
1290 if (SelectItems (list))
1291 OnSelectedIndexChanged (EventArgs.Empty);
1292 } else if (ctrl_pressed) {
1293 item.Selected = !item.Selected;
1294 selection_start = item;
1295 OnSelectedIndexChanged (EventArgs.Empty);
1297 SelectedItems.Clear ();
1298 item.Selected = true;
1299 selection_start = item;
1300 OnSelectedIndexChanged (EventArgs.Empty);
1304 internal override bool InternalPreProcessMessage (ref Message msg)
1306 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1307 Keys key_data = (Keys)msg.WParam.ToInt32();
1308 if (HandleNavKeys (key_data))
1311 return base.InternalPreProcessMessage (ref msg);
1314 bool HandleNavKeys (Keys key_data)
1316 if (Items.Count == 0 || !item_control.Visible)
1319 if (FocusedItem == null)
1320 SetFocusedItem (Items [0]);
1324 SelectIndex (Items.Count - 1);
1335 SelectIndex (GetAdjustedIndex (key_data));
1345 void SelectIndex (int index)
1351 UpdateMultiSelection (index);
1352 else if (!items [index].Selected) {
1353 items [index].Selected = true;
1354 OnSelectedIndexChanged (EventArgs.Empty);
1357 SetFocusedItem (items [index]);
1358 EnsureVisible (index);
1361 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1363 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1366 ke.Handled = KeySearchString (ke);
1369 internal class ItemControl : Control {
1372 ListViewItem clicked_item;
1373 ListViewItem last_clicked_item;
1374 bool hover_processed = false;
1375 bool checking = false;
1377 ListViewLabelEditTextBox edit_text_box;
1378 internal ListViewItem edit_item;
1379 LabelEditEventArgs edit_args;
1381 public ItemControl (ListView owner)
1384 DoubleClick += new EventHandler(ItemsDoubleClick);
1385 MouseDown += new MouseEventHandler(ItemsMouseDown);
1386 MouseMove += new MouseEventHandler(ItemsMouseMove);
1387 MouseHover += new EventHandler(ItemsMouseHover);
1388 MouseUp += new MouseEventHandler(ItemsMouseUp);
1391 void ItemsDoubleClick (object sender, EventArgs e)
1393 if (owner.activation == ItemActivation.Standard)
1394 owner.OnItemActivate (EventArgs.Empty);
1404 BoxSelect box_select_mode = BoxSelect.None;
1405 ArrayList prev_selection;
1406 Point box_select_start;
1408 Rectangle box_select_rect;
1409 internal Rectangle BoxSelectRectangle {
1410 get { return box_select_rect; }
1412 if (box_select_rect == value)
1415 InvalidateBoxSelectRect ();
1416 box_select_rect = value;
1417 InvalidateBoxSelectRect ();
1421 void InvalidateBoxSelectRect ()
1423 if (BoxSelectRectangle.Size.IsEmpty)
1426 Rectangle edge = BoxSelectRectangle;
1432 edge.Y = BoxSelectRectangle.Bottom - 1;
1434 edge.Y = BoxSelectRectangle.Y - 1;
1436 edge.Height = BoxSelectRectangle.Height + 2;
1438 edge.X = BoxSelectRectangle.Right - 1;
1442 private Rectangle CalculateBoxSelectRectangle (Point pt)
1444 int left = Math.Min (box_select_start.X, pt.X);
1445 int right = Math.Max (box_select_start.X, pt.X);
1446 int top = Math.Min (box_select_start.Y, pt.Y);
1447 int bottom = Math.Max (box_select_start.Y, pt.Y);
1448 return Rectangle.FromLTRB (left, top, right, bottom);
1451 ArrayList BoxSelectedItems {
1453 ArrayList result = new ArrayList ();
1454 foreach (ListViewItem item in owner.Items) {
1455 Rectangle r = item.Bounds;
1457 r.Y += r.Height / 4;
1460 if (BoxSelectRectangle.IntersectsWith (r))
1467 private bool PerformBoxSelection (Point pt)
1469 if (box_select_mode == BoxSelect.None)
1472 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1474 ArrayList box_items = BoxSelectedItems;
1478 switch (box_select_mode) {
1480 case BoxSelect.Normal:
1484 case BoxSelect.Control:
1485 items = new ArrayList ();
1486 foreach (ListViewItem item in prev_selection)
1487 if (!box_items.Contains (item))
1489 foreach (ListViewItem item in box_items)
1490 if (!prev_selection.Contains (item))
1494 case BoxSelect.Shift:
1496 foreach (ListViewItem item in box_items)
1497 prev_selection.Remove (item);
1498 foreach (ListViewItem item in prev_selection)
1503 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1507 owner.SelectItems (items);
1513 private void ToggleCheckState (ListViewItem item)
1515 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1516 item.Checked = !item.Checked;
1517 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1519 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1520 owner.OnItemCheck (ice);
1523 private void ItemsMouseDown (object sender, MouseEventArgs me)
1525 if (owner.items.Count == 0)
1528 Point pt = new Point (me.X, me.Y);
1529 foreach (ListViewItem item in owner.items) {
1530 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1534 ToggleCheckState (item);
1538 if (owner.View == View.Details && !owner.FullRowSelect) {
1539 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1540 clicked_item = item;
1544 if (item.Bounds.Contains (pt)) {
1545 clicked_item = item;
1552 if (clicked_item != null) {
1553 owner.SetFocusedItem (clicked_item);
1554 bool changed = !clicked_item.Selected;
1555 if (owner.MultiSelect)
1556 owner.UpdateMultiSelection (clicked_item.Index);
1558 clicked_item.Selected = true;
1561 owner.OnSelectedIndexChanged (EventArgs.Empty);
1563 // Raise double click if the item was clicked. On MS the
1564 // double click is only raised if you double click an item
1565 if (me.Clicks > 1) {
1566 owner.OnDoubleClick (EventArgs.Empty);
1567 if (owner.CheckBoxes)
1568 ToggleCheckState (clicked_item);
1569 } else if (me.Clicks == 1) {
1570 owner.OnClick (EventArgs.Empty);
1571 if (owner.LabelEdit && !changed)
1572 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
1575 if (owner.MultiSelect) {
1576 Keys mods = XplatUI.State.ModifierKeys;
1577 if ((mods & Keys.Shift) != 0)
1578 box_select_mode = BoxSelect.Shift;
1579 else if ((mods & Keys.Control) != 0)
1580 box_select_mode = BoxSelect.Control;
1582 box_select_mode = BoxSelect.Normal;
1583 box_select_start = pt;
1584 prev_selection = owner.SelectedItems.List;
1585 } else if (owner.SelectedItems.Count > 0) {
1586 owner.SelectedItems.Clear ();
1587 owner.OnSelectedIndexChanged (EventArgs.Empty);
1592 private void ItemsMouseMove (object sender, MouseEventArgs me)
1594 if (PerformBoxSelection (new Point (me.X, me.Y)))
1597 if (owner.HoverSelection && hover_processed) {
1599 Point pt = PointToClient (Control.MousePosition);
1600 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1601 if (item == null || item.Selected)
1604 hover_processed = false;
1605 XplatUI.ResetMouseHover (Handle);
1610 private void ItemsMouseHover (object sender, EventArgs e)
1612 if (Capture || !owner.HoverSelection)
1615 hover_processed = true;
1616 Point pt = PointToClient (Control.MousePosition);
1617 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1622 item.Selected = true;
1623 owner.OnSelectedIndexChanged (new EventArgs ());
1626 private void ItemsMouseUp (object sender, MouseEventArgs me)
1629 if (owner.Items.Count == 0)
1632 Point pt = new Point (me.X, me.Y);
1634 Rectangle rect = Rectangle.Empty;
1635 if (clicked_item != null) {
1636 if (owner.view == View.Details && !owner.full_row_select)
1637 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1639 rect = clicked_item.Bounds;
1641 if (rect.Contains (pt)) {
1642 switch (owner.activation) {
1643 case ItemActivation.OneClick:
1644 owner.OnItemActivate (EventArgs.Empty);
1647 case ItemActivation.TwoClick:
1648 if (last_clicked_item == clicked_item) {
1649 owner.OnItemActivate (EventArgs.Empty);
1650 last_clicked_item = null;
1652 last_clicked_item = clicked_item;
1655 // DoubleClick activation is handled in another handler
1659 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1660 // Need this to clean up background clicks
1661 owner.SelectedItems.Clear ();
1662 owner.OnSelectedIndexChanged (EventArgs.Empty);
1665 clicked_item = null;
1666 box_select_start = Point.Empty;
1667 BoxSelectRectangle = Rectangle.Empty;
1668 prev_selection = null;
1669 box_select_mode = BoxSelect.None;
1673 internal void LabelEditFinished (object sender, EventArgs e)
1675 EndEdit (edit_item);
1678 internal void BeginEdit (ListViewItem item)
1680 if (edit_item != null)
1681 EndEdit (edit_item);
1683 if (edit_text_box == null) {
1684 edit_text_box = new ListViewLabelEditTextBox ();
1685 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
1686 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
1687 edit_text_box.Visible = false;
1688 Controls.Add (edit_text_box);
1691 item.EnsureVisible();
1693 edit_text_box.Reset ();
1695 switch (owner.view) {
1697 case View.SmallIcon:
1699 edit_text_box.TextAlign = HorizontalAlignment.Left;
1700 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1701 SizeF sizef = DeviceContext.MeasureString (item.Text, item.Font);
1702 edit_text_box.Width = (int)sizef.Width + 4;
1703 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
1704 edit_text_box.WordWrap = false;
1705 edit_text_box.Multiline = false;
1707 case View.LargeIcon:
1708 edit_text_box.TextAlign = HorizontalAlignment.Center;
1709 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1710 sizef = DeviceContext.MeasureString (item.Text, item.Font);
1711 edit_text_box.Width = (int)sizef.Width + 4;
1712 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
1713 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
1714 edit_text_box.WordWrap = true;
1715 edit_text_box.Multiline = true;
1719 edit_text_box.Text = item.Text;
1720 edit_text_box.Font = item.Font;
1721 edit_text_box.Visible = true;
1722 edit_text_box.Focus ();
1723 edit_text_box.SelectAll ();
1725 edit_args = new LabelEditEventArgs (owner.Items.IndexOf(edit_item));
1726 owner.OnBeforeLabelEdit (edit_args);
1728 if (edit_args.CancelEdit)
1734 internal void EndEdit (ListViewItem item)
1736 if (edit_text_box != null && edit_text_box.Visible) {
1737 edit_text_box.Visible = false;
1740 if (edit_item != null && edit_item == item) {
1741 owner.OnAfterLabelEdit (edit_args);
1743 if (!edit_args.CancelEdit) {
1744 if (edit_args.Label != null)
1745 edit_item.Text = edit_args.Label;
1747 edit_item.Text = edit_text_box.Text;
1756 internal override void OnPaintInternal (PaintEventArgs pe)
1758 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1761 internal override void OnGotFocusInternal (EventArgs e)
1767 internal class ListViewLabelEditTextBox : TextBox
1772 int max_height = -1;
1773 int min_height = -1;
1775 int old_number_lines = 1;
1777 SizeF text_size_one_char;
1779 public ListViewLabelEditTextBox ()
1781 min_height = DefaultSize.Height;
1782 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1785 public int MaxWidth {
1787 if (value < min_width)
1788 max_width = min_width;
1794 public int MaxHeight {
1796 if (value < min_height)
1797 max_height = min_height;
1803 public new int Width {
1813 public override Font Font {
1819 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1823 protected override void OnTextChanged (EventArgs e)
1825 SizeF text_size = DeviceContext.MeasureString (Text, Font);
1827 int new_width = (int)text_size.Width + 8;
1830 ResizeTextBoxWidth (new_width);
1832 if (Width != max_width)
1833 ResizeTextBoxWidth (new_width);
1835 int number_lines = Lines.Length;
1837 if (number_lines != old_number_lines) {
1838 int new_height = number_lines * (int)text_size_one_char.Height + 4;
1839 old_number_lines = number_lines;
1841 ResizeTextBoxHeight (new_height);
1845 base.OnTextChanged (e);
1848 protected override bool IsInputKey (Keys key_data)
1850 if ((key_data & Keys.Alt) == 0) {
1851 switch (key_data & Keys.KeyCode) {
1856 return base.IsInputKey (key_data);
1859 protected override void OnKeyDown (KeyEventArgs e)
1861 if (e.KeyCode == Keys.Return && Visible) {
1862 this.Visible = false;
1863 OnEditingFinished (e);
1867 protected override void OnLostFocus (EventArgs e)
1870 OnEditingFinished (e);
1874 protected void OnEditingFinished (EventArgs e)
1876 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
1881 private void ResizeTextBoxWidth (int new_width)
1883 if (new_width > max_width)
1884 base.Width = max_width;
1886 if (new_width >= min_width)
1887 base.Width = new_width;
1889 base.Width = min_width;
1892 private void ResizeTextBoxHeight (int new_height)
1894 if (new_height > max_height)
1895 base.Height = max_height;
1897 if (new_height >= min_height)
1898 base.Height = new_height;
1900 base.Height = min_height;
1903 public void Reset ()
1910 old_number_lines = 1;
1912 Text = String.Empty;
1917 static object EditingFinishedEvent = new object ();
1918 public event EventHandler EditingFinished {
1919 add { Events.AddHandler (EditingFinishedEvent, value); }
1920 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
1924 internal override void OnPaintInternal (PaintEventArgs pe)
1929 CalculateScrollBars ();
1932 void FocusChanged (object o, EventArgs args)
1934 if (Items.Count == 0)
1937 if (FocusedItem == null)
1938 SetFocusedItem (Items [0]);
1940 item_control.Invalidate (FocusedItem.Bounds);
1943 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1945 if (Items.Count == 0)
1948 int lines = me.Delta / 120;
1955 case View.SmallIcon:
1956 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1958 case View.LargeIcon:
1959 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1962 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1967 private void ListView_SizeChanged (object sender, EventArgs e)
1969 CalculateListView (alignment);
1972 private void SetFocusedItem (ListViewItem item)
1974 if (focused_item != null)
1975 focused_item.Focused = false;
1978 item.Focused = true;
1980 focused_item = item;
1983 private void HorizontalScroller (object sender, EventArgs e)
1985 item_control.EndEdit (item_control.edit_item);
1987 // Avoid unnecessary flickering, when button is
1988 // kept pressed at the end
1989 if (h_marker != h_scroll.Value) {
1991 int pixels = h_marker - h_scroll.Value;
1993 h_marker = h_scroll.Value;
1994 if (header_control.Visible)
1995 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1997 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
2001 private void VerticalScroller (object sender, EventArgs e)
2003 item_control.EndEdit (item_control.edit_item);
2005 // Avoid unnecessary flickering, when button is
2006 // kept pressed at the end
2007 if (v_marker != v_scroll.Value) {
2008 int pixels = v_marker - v_scroll.Value;
2009 Rectangle area = item_control.ClientRectangle;
2010 v_marker = v_scroll.Value;
2011 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
2014 #endregion // Internal Methods Properties
2016 #region Protected Methods
2017 protected override void CreateHandle ()
2019 base.CreateHandle ();
2020 for (int i = 0; i < SelectedItems.Count; i++)
2021 OnSelectedIndexChanged (EventArgs.Empty);
2024 protected override void Dispose (bool disposing)
2027 h_scroll.Dispose ();
2028 v_scroll.Dispose ();
2030 large_image_list = null;
2031 small_image_list = null;
2032 state_image_list = null;
2035 base.Dispose (disposing);
2038 protected override bool IsInputKey (Keys keyData)
2055 return base.IsInputKey (keyData);
2058 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
2060 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
2065 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
2067 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
2072 protected virtual void OnColumnClick (ColumnClickEventArgs e)
2074 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
2079 protected override void OnEnabledChanged (EventArgs e)
2081 base.OnEnabledChanged (e);
2084 protected override void OnFontChanged (EventArgs e)
2086 base.OnFontChanged (e);
2090 protected override void OnHandleCreated (EventArgs e)
2092 base.OnHandleCreated (e);
2096 protected override void OnHandleDestroyed (EventArgs e)
2098 base.OnHandleDestroyed (e);
2101 protected virtual void OnItemActivate (EventArgs e)
2103 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
2108 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
2110 EventHandler eh = (EventHandler)(Events [ItemCheckEvent]);
2115 protected virtual void OnItemDrag (ItemDragEventArgs e)
2117 EventHandler eh = (EventHandler)(Events [ItemDragEvent]);
2122 protected virtual void OnSelectedIndexChanged (EventArgs e)
2124 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
2129 protected override void OnSystemColorsChanged (EventArgs e)
2131 base.OnSystemColorsChanged (e);
2134 protected void RealizeProperties ()
2139 protected void UpdateExtendedStyles ()
2144 protected override void WndProc (ref Message m)
2146 base.WndProc (ref m);
2148 #endregion // Protected Methods
2150 #region Public Instance Methods
2151 public void ArrangeIcons ()
2153 ArrangeIcons (this.alignment);
2156 public void ArrangeIcons (ListViewAlignment alignment)
2158 // Icons are arranged only if view is set to LargeIcon or SmallIcon
2159 if (view == View.LargeIcon || view == View.SmallIcon) {
2160 this.CalculateListView (alignment);
2161 // we have done the calculations already
2162 this.Redraw (false);
2166 public void BeginUpdate ()
2168 // flag to avoid painting
2172 public void Clear ()
2175 items.Clear (); // Redraw (true) called here
2178 public void EndUpdate ()
2180 // flag to avoid painting
2183 // probably, now we need a redraw with recalculations
2187 public void EnsureVisible (int index)
2189 if (index < 0 || index >= items.Count || scrollable == false)
2192 Rectangle view_rect = item_control.ClientRectangle;
2193 Rectangle bounds = items [index].Bounds;
2195 if (view_rect.Contains (bounds))
2198 if (bounds.Left < 0)
2199 h_scroll.Value += bounds.Left;
2200 else if (bounds.Right > view_rect.Right)
2201 h_scroll.Value += (bounds.Right - view_rect.Right);
2204 v_scroll.Value += bounds.Top;
2205 else if (bounds.Bottom > view_rect.Bottom)
2206 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
2209 public ListViewItem GetItemAt (int x, int y)
2211 foreach (ListViewItem item in items) {
2212 if (item.Bounds.Contains (x, y))
2218 public Rectangle GetItemRect (int index)
2220 return GetItemRect (index, ItemBoundsPortion.Entire);
2223 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
2225 if (index < 0 || index >= items.Count)
2226 throw new IndexOutOfRangeException ("index");
2228 return items [index].GetBounds (portion);
2236 // we need this overload to reuse the logic for sorting, while allowing
2237 // redrawing to be done by caller or have it done by this method when
2238 // sorting is really performed
2240 // ListViewItemCollection's Add and AddRange methods call this overload
2241 // with redraw set to false, as they take care of redrawing themselves
2242 // (they even want to redraw the listview if no sort is performed, as
2243 // an item was added), while ListView.Sort () only wants to redraw if
2244 // sorting was actually performed
2245 private void Sort (bool redraw)
2247 if (!IsHandleCreated || item_sorter == null) {
2251 items.Sort (item_sorter);
2256 public override string ToString ()
2258 int count = this.Items.Count;
2261 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
2263 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
2265 #endregion // Public Instance Methods
2270 class HeaderControl : Control {
2273 bool column_resize_active = false;
2274 ColumnHeader resize_column;
2275 ColumnHeader clicked_column;
2276 ColumnHeader drag_column;
2278 int drag_to_index = -1;
2280 public HeaderControl (ListView owner)
2283 MouseDown += new MouseEventHandler (HeaderMouseDown);
2284 MouseMove += new MouseEventHandler (HeaderMouseMove);
2285 MouseUp += new MouseEventHandler (HeaderMouseUp);
2288 private ColumnHeader ColumnAtX (int x)
2290 Point pt = new Point (x, 0);
2291 ColumnHeader result = null;
2292 foreach (ColumnHeader col in owner.Columns) {
2293 if (col.Rect.Contains (pt)) {
2301 private int GetReorderedIndex (ColumnHeader col)
2303 if (owner.reordered_column_indices == null)
2306 for (int i = 0; i < owner.Columns.Count; i++)
2307 if (owner.reordered_column_indices [i] == col.Index)
2309 throw new Exception ("Column index missing from reordered array");
2312 private void HeaderMouseDown (object sender, MouseEventArgs me)
2314 if (resize_column != null) {
2315 column_resize_active = true;
2320 clicked_column = ColumnAtX (me.X + owner.h_marker);
2322 if (clicked_column != null) {
2324 if (owner.AllowColumnReorder) {
2326 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2327 drag_column.column_rect = clicked_column.Rect;
2328 drag_to_index = GetReorderedIndex (clicked_column);
2330 clicked_column.pressed = true;
2331 Rectangle bounds = clicked_column.Rect;
2332 bounds.X -= owner.h_marker;
2333 Invalidate (bounds);
2338 private void HeaderMouseMove (object sender, MouseEventArgs me)
2340 Point pt = new Point (me.X + owner.h_marker, me.Y);
2342 if (column_resize_active) {
2343 resize_column.Width = pt.X - resize_column.X;
2344 if (resize_column.Width < 0)
2345 resize_column.Width = 0;
2349 resize_column = null;
2351 if (clicked_column != null) {
2352 if (owner.AllowColumnReorder) {
2355 r = drag_column.column_rect;
2356 r.X = clicked_column.Rect.X + me.X - drag_x;
2357 drag_column.column_rect = r;
2359 int x = me.X + owner.h_marker;
2360 ColumnHeader over = ColumnAtX (x);
2362 drag_to_index = owner.Columns.Count;
2363 else if (x < over.X + over.Width / 2)
2364 drag_to_index = GetReorderedIndex (over);
2366 drag_to_index = GetReorderedIndex (over) + 1;
2369 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2370 bool pressed = clicked_column.pressed;
2371 clicked_column.pressed = over == clicked_column;
2372 if (clicked_column.pressed ^ pressed) {
2373 Rectangle bounds = clicked_column.Rect;
2374 bounds.X -= owner.h_marker;
2375 Invalidate (bounds);
2381 for (int i = 0; i < owner.Columns.Count; i++) {
2382 Rectangle zone = owner.Columns [i].Rect;
2383 zone.X = zone.Right - 5;
2385 if (zone.Contains (pt)) {
2386 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2388 resize_column = owner.Columns [i];
2393 if (resize_column == null)
2394 Cursor = Cursors.Default;
2396 Cursor = Cursors.VSplit;
2399 void HeaderMouseUp (object sender, MouseEventArgs me)
2403 if (column_resize_active) {
2404 column_resize_active = false;
2405 resize_column = null;
2406 Cursor = Cursors.Default;
2410 if (clicked_column != null && clicked_column.pressed) {
2411 clicked_column.pressed = false;
2412 Rectangle bounds = clicked_column.Rect;
2413 bounds.X -= owner.h_marker;
2414 Invalidate (bounds);
2415 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2418 if (drag_column != null && owner.AllowColumnReorder) {
2420 if (drag_to_index > GetReorderedIndex (clicked_column))
2422 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2423 owner.ReorderColumn (clicked_column, drag_to_index);
2428 clicked_column = null;
2431 internal override void OnPaintInternal (PaintEventArgs pe)
2436 Theme theme = ThemeEngine.Current;
2437 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2439 if (drag_column == null)
2443 if (drag_to_index == owner.Columns.Count)
2444 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2446 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2447 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2450 protected override void WndProc (ref Message m)
2452 switch ((Msg)m.Msg) {
2453 case Msg.WM_SETFOCUS:
2457 base.WndProc (ref m);
2463 private class ItemComparer : IComparer {
2464 readonly SortOrder sort_order;
2466 public ItemComparer (SortOrder sortOrder)
2468 sort_order = sortOrder;
2471 public int Compare (object x, object y)
2473 ListViewItem item_x = x as ListViewItem;
2474 ListViewItem item_y = y as ListViewItem;
2475 if (sort_order == SortOrder.Ascending)
2476 return String.Compare (item_x.Text, item_y.Text);
2478 return String.Compare (item_y.Text, item_x.Text);
2482 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2484 private readonly ListView owner;
2486 #region Public Constructor
2487 public CheckedIndexCollection (ListView owner)
2491 #endregion // Public Constructor
2493 #region Public Properties
2496 get { return owner.CheckedItems.Count; }
2499 public bool IsReadOnly {
2500 get { return true; }
2503 public int this [int index] {
2505 int [] indices = GetIndices ();
2506 if (index < 0 || index >= indices.Length)
2507 throw new ArgumentOutOfRangeException ("index");
2508 return indices [index];
2512 bool ICollection.IsSynchronized {
2513 get { return false; }
2516 object ICollection.SyncRoot {
2517 get { return this; }
2520 bool IList.IsFixedSize {
2521 get { return true; }
2524 object IList.this [int index] {
2525 get { return this [index]; }
2526 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2528 #endregion // Public Properties
2530 #region Public Methods
2531 public bool Contains (int checkedIndex)
2533 int [] indices = GetIndices ();
2534 for (int i = 0; i < indices.Length; i++) {
2535 if (indices [i] == checkedIndex)
2541 public IEnumerator GetEnumerator ()
2543 int [] indices = GetIndices ();
2544 return indices.GetEnumerator ();
2547 void ICollection.CopyTo (Array dest, int index)
2549 int [] indices = GetIndices ();
2550 Array.Copy (indices, 0, dest, index, indices.Length);
2553 int IList.Add (object value)
2555 throw new NotSupportedException ("Add operation is not supported.");
2560 throw new NotSupportedException ("Clear operation is not supported.");
2563 bool IList.Contains (object checkedIndex)
2565 if (!(checkedIndex is int))
2567 return Contains ((int) checkedIndex);
2570 int IList.IndexOf (object checkedIndex)
2572 if (!(checkedIndex is int))
2574 return IndexOf ((int) checkedIndex);
2577 void IList.Insert (int index, object value)
2579 throw new NotSupportedException ("Insert operation is not supported.");
2582 void IList.Remove (object value)
2584 throw new NotSupportedException ("Remove operation is not supported.");
2587 void IList.RemoveAt (int index)
2589 throw new NotSupportedException ("RemoveAt operation is not supported.");
2592 public int IndexOf (int checkedIndex)
2594 int [] indices = GetIndices ();
2595 for (int i = 0; i < indices.Length; i++) {
2596 if (indices [i] == checkedIndex)
2601 #endregion // Public Methods
2603 private int [] GetIndices ()
2605 ArrayList checked_items = owner.CheckedItems.List;
2606 int [] indices = new int [checked_items.Count];
2607 for (int i = 0; i < checked_items.Count; i++) {
2608 ListViewItem item = (ListViewItem) checked_items [i];
2609 indices [i] = item.Index;
2613 } // CheckedIndexCollection
2615 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2617 private readonly ListView owner;
2618 private ArrayList list;
2620 #region Public Constructor
2621 public CheckedListViewItemCollection (ListView owner)
2624 this.owner.Items.Changed += new CollectionChangedHandler (
2625 ItemsCollection_Changed);
2627 #endregion // Public Constructor
2629 #region Public Properties
2633 if (!owner.CheckBoxes)
2639 public bool IsReadOnly {
2640 get { return true; }
2643 public ListViewItem this [int index] {
2645 ArrayList checked_items = List;
2646 if (index < 0 || index >= checked_items.Count)
2647 throw new ArgumentOutOfRangeException ("index");
2648 return (ListViewItem) checked_items [index];
2652 bool ICollection.IsSynchronized {
2653 get { return false; }
2656 object ICollection.SyncRoot {
2657 get { return this; }
2660 bool IList.IsFixedSize {
2661 get { return true; }
2664 object IList.this [int index] {
2665 get { return this [index]; }
2666 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2668 #endregion // Public Properties
2670 #region Public Methods
2671 public bool Contains (ListViewItem item)
2673 if (!owner.CheckBoxes)
2675 return List.Contains (item);
2678 public void CopyTo (Array dest, int index)
2680 if (!owner.CheckBoxes)
2682 List.CopyTo (dest, index);
2685 public IEnumerator GetEnumerator ()
2687 if (!owner.CheckBoxes)
2688 return (new ListViewItem [0]).GetEnumerator ();
2689 return List.GetEnumerator ();
2692 int IList.Add (object value)
2694 throw new NotSupportedException ("Add operation is not supported.");
2699 throw new NotSupportedException ("Clear operation is not supported.");
2702 bool IList.Contains (object item)
2704 if (!(item is ListViewItem))
2706 return Contains ((ListViewItem) item);
2709 int IList.IndexOf (object item)
2711 if (!(item is ListViewItem))
2713 return IndexOf ((ListViewItem) item);
2716 void IList.Insert (int index, object value)
2718 throw new NotSupportedException ("Insert operation is not supported.");
2721 void IList.Remove (object value)
2723 throw new NotSupportedException ("Remove operation is not supported.");
2726 void IList.RemoveAt (int index)
2728 throw new NotSupportedException ("RemoveAt operation is not supported.");
2731 public int IndexOf (ListViewItem item)
2733 if (!owner.CheckBoxes)
2735 return List.IndexOf (item);
2737 #endregion // Public Methods
2739 internal ArrayList List {
2742 list = new ArrayList ();
2743 foreach (ListViewItem item in owner.Items) {
2752 internal void Reset ()
2754 // force re-population of list
2758 private void ItemsCollection_Changed ()
2762 } // CheckedListViewItemCollection
2764 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2766 internal ArrayList list;
2767 private ListView owner;
2769 #region Public Constructor
2770 public ColumnHeaderCollection (ListView owner)
2772 list = new ArrayList ();
2775 #endregion // Public Constructor
2777 #region Public Properties
2780 get { return list.Count; }
2783 public bool IsReadOnly {
2784 get { return false; }
2787 public virtual ColumnHeader this [int index] {
2789 if (index < 0 || index >= list.Count)
2790 throw new ArgumentOutOfRangeException ("index");
2791 return (ColumnHeader) list [index];
2796 public virtual ColumnHeader this [string key] {
2798 int idx = IndexOfKey (key);
2802 return (ColumnHeader) list [idx];
2807 bool ICollection.IsSynchronized {
2808 get { return true; }
2811 object ICollection.SyncRoot {
2812 get { return this; }
2815 bool IList.IsFixedSize {
2816 get { return list.IsFixedSize; }
2819 object IList.this [int index] {
2820 get { return this [index]; }
2821 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2823 #endregion // Public Properties
2825 #region Public Methods
2826 public virtual int Add (ColumnHeader value)
2829 value.owner = this.owner;
2830 idx = list.Add (value);
2831 if (owner.IsHandleCreated) {
2832 owner.Redraw (true);
2837 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2839 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2840 this.Add (colHeader);
2845 public virtual ColumnHeader Add (string text)
2847 return Add (String.Empty, text);
2850 public virtual ColumnHeader Add (string text, int iwidth)
2852 return Add (String.Empty, text, iwidth);
2855 public virtual ColumnHeader Add (string key, string text)
2857 ColumnHeader colHeader = new ColumnHeader ();
2858 colHeader.Name = key;
2859 colHeader.Text = text;
2864 public virtual ColumnHeader Add (string key, string text, int iwidth)
2866 return Add (key, text, iwidth, HorizontalAlignment.Left, -1);
2869 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, int imageIndex)
2871 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
2872 colHeader.ImageIndex = imageIndex;
2877 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, string imageKey)
2879 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
2880 colHeader.ImageKey = imageKey;
2886 public virtual void AddRange (ColumnHeader [] values)
2888 foreach (ColumnHeader colHeader in values) {
2889 colHeader.owner = this.owner;
2893 owner.Redraw (true);
2896 public virtual void Clear ()
2899 owner.Redraw (true);
2902 public bool Contains (ColumnHeader value)
2904 return list.Contains (value);
2908 public virtual bool ContainsKey (string key)
2910 return IndexOfKey (key) != -1;
2914 public IEnumerator GetEnumerator ()
2916 return list.GetEnumerator ();
2919 void ICollection.CopyTo (Array dest, int index)
2921 list.CopyTo (dest, index);
2924 int IList.Add (object value)
2926 if (! (value is ColumnHeader)) {
2927 throw new ArgumentException ("Not of type ColumnHeader", "value");
2930 return this.Add ((ColumnHeader) value);
2933 bool IList.Contains (object value)
2935 if (! (value is ColumnHeader)) {
2936 throw new ArgumentException ("Not of type ColumnHeader", "value");
2939 return this.Contains ((ColumnHeader) value);
2942 int IList.IndexOf (object value)
2944 if (! (value is ColumnHeader)) {
2945 throw new ArgumentException ("Not of type ColumnHeader", "value");
2948 return this.IndexOf ((ColumnHeader) value);
2951 void IList.Insert (int index, object value)
2953 if (! (value is ColumnHeader)) {
2954 throw new ArgumentException ("Not of type ColumnHeader", "value");
2957 this.Insert (index, (ColumnHeader) value);
2960 void IList.Remove (object value)
2962 if (! (value is ColumnHeader)) {
2963 throw new ArgumentException ("Not of type ColumnHeader", "value");
2966 this.Remove ((ColumnHeader) value);
2969 public int IndexOf (ColumnHeader value)
2971 return list.IndexOf (value);
2975 public virtual int IndexOfKey (string key)
2977 if (key == null || key.Length == 0)
2980 for (int i = 0; i < list.Count; i++) {
2981 ColumnHeader col = (ColumnHeader) list [i];
2982 if (String.Compare (key, col.Name, true) == 0)
2990 public void Insert (int index, ColumnHeader value)
2992 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2993 // but it's really only greater.
2994 if (index < 0 || index > list.Count)
2995 throw new ArgumentOutOfRangeException ("index");
2997 value.owner = this.owner;
2998 list.Insert (index, value);
2999 owner.Redraw (true);
3003 public void Insert (int index, string text)
3005 Insert (index, String.Empty, text);
3008 public void Insert (int index, string text, int width)
3010 Insert (index, String.Empty, text, width);
3013 public void Insert (int index, string key, string text)
3015 ColumnHeader colHeader = new ColumnHeader ();
3016 colHeader.Name = key;
3017 colHeader.Text = text;
3018 Insert (index, colHeader);
3021 public void Insert (int index, string key, string text, int width)
3023 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
3024 Insert (index, colHeader);
3027 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
3029 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
3030 colHeader.ImageIndex = imageIndex;
3031 Insert (index, colHeader);
3034 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
3036 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
3037 colHeader.ImageKey = imageKey;
3038 Insert (index, colHeader);
3042 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
3044 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
3045 this.Insert (index, colHeader);
3048 public virtual void Remove (ColumnHeader column)
3050 // TODO: Update Column internal index ?
3051 list.Remove (column);
3052 owner.Redraw (true);
3056 public virtual void RemoveByKey (string key)
3058 int idx = IndexOfKey (key);
3064 public virtual void RemoveAt (int index)
3066 if (index < 0 || index >= list.Count)
3067 throw new ArgumentOutOfRangeException ("index");
3069 // TODO: Update Column internal index ?
3070 list.RemoveAt (index);
3071 owner.Redraw (true);
3073 #endregion // Public Methods
3076 } // ColumnHeaderCollection
3078 public class ListViewItemCollection : IList, ICollection, IEnumerable
3080 private readonly ArrayList list;
3081 private readonly ListView owner;
3083 #region Public Constructor
3084 public ListViewItemCollection (ListView owner)
3086 list = new ArrayList ();
3089 #endregion // Public Constructor
3091 #region Public Properties
3094 get { return list.Count; }
3097 public bool IsReadOnly {
3098 get { return false; }
3101 public virtual ListViewItem this [int displayIndex] {
3103 if (displayIndex < 0 || displayIndex >= list.Count)
3104 throw new ArgumentOutOfRangeException ("displayIndex");
3105 return (ListViewItem) list [displayIndex];
3109 if (displayIndex < 0 || displayIndex >= list.Count)
3110 throw new ArgumentOutOfRangeException ("displayIndex");
3112 if (list.Contains (value))
3113 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
3115 if (value.ListView != null && value.ListView != owner)
3116 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");
3118 value.Owner = owner;
3119 list [displayIndex] = value;
3122 owner.Redraw (true);
3127 public virtual ListViewItem this [string key] {
3129 int idx = IndexOfKey (key);
3133 return (ListViewItem) list [idx];
3138 bool ICollection.IsSynchronized {
3139 get { return true; }
3142 object ICollection.SyncRoot {
3143 get { return this; }
3146 bool IList.IsFixedSize {
3147 get { return list.IsFixedSize; }
3150 object IList.this [int index] {
3151 get { return this [index]; }
3153 if (value is ListViewItem)
3154 this [index] = (ListViewItem) value;
3156 this [index] = new ListViewItem (value.ToString ());
3160 #endregion // Public Properties
3162 #region Public Methods
3163 public virtual ListViewItem Add (ListViewItem value)
3165 if (list.Contains (value))
3166 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
3168 if (value.ListView != null && value.ListView != owner)
3169 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");
3171 value.Owner = owner;
3174 if (this.owner != null)
3178 owner.Redraw (true);
3184 public virtual ListViewItem Add (string text)
3186 ListViewItem item = new ListViewItem (text);
3187 return this.Add (item);
3190 public virtual ListViewItem Add (string text, int imageIndex)
3192 ListViewItem item = new ListViewItem (text, imageIndex);
3193 return this.Add (item);
3197 public virtual ListViewItem Add (string text, string imageKey)
3199 ListViewItem item = new ListViewItem (text, imageKey);
3200 return this.Add (item);
3203 public virtual ListViewItem Add (string key, string text, int imageIndex)
3205 ListViewItem item = new ListViewItem (text, imageIndex);
3207 return this.Add (item);
3210 public virtual ListViewItem Add (string key, string text, string imageKey)
3212 ListViewItem item = new ListViewItem (text, imageKey);
3214 return this.Add (item);
3218 public void AddRange (ListViewItem [] values)
3221 throw new ArgumentNullException ("Argument cannot be null!", "values");
3223 foreach (ListViewItem item in values) {
3229 public void AddRange (ListViewItemCollection items)
3232 throw new ArgumentNullException ("Argument cannot be null!", "items");
3234 ListViewItem[] itemArray = new ListViewItem[items.Count];
3235 items.CopyTo (itemArray,0);
3236 this.AddRange (itemArray);
3240 public virtual void Clear ()
3242 owner.SetFocusedItem (null);
3243 owner.h_scroll.Value = owner.v_scroll.Value = 0;
3246 owner.Redraw (true);
3249 public bool Contains (ListViewItem item)
3251 return list.Contains (item);
3255 public virtual bool ContainsKey (string key)
3257 return IndexOfKey (key) != -1;
3261 public void CopyTo (Array dest, int index)
3263 list.CopyTo (dest, index);
3267 public ListViewItem [] Find (string key, bool searchAllSubitems)
3270 return new ListViewItem [0];
3272 List<ListViewItem> temp_list = new List<ListViewItem> ();
3274 for (int i = 0; i < list.Count; i++) {
3275 ListViewItem lvi = (ListViewItem) list [i];
3276 if (String.Compare (key, lvi.Name, true) == 0)
3277 temp_list.Add (lvi);
3280 ListViewItem [] retval = new ListViewItem [temp_list.Count];
3281 temp_list.CopyTo (retval);
3287 public IEnumerator GetEnumerator ()
3289 return list.GetEnumerator ();
3292 int IList.Add (object item)
3297 if (item is ListViewItem) {
3298 li = (ListViewItem) item;
3299 if (list.Contains (li))
3300 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3302 if (li.ListView != null && li.ListView != owner)
3303 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");
3306 li = new ListViewItem (item.ToString ());
3309 result = list.Add (li);
3311 owner.Redraw (true);
3316 bool IList.Contains (object item)
3318 return list.Contains (item);
3321 int IList.IndexOf (object item)
3323 return list.IndexOf (item);
3326 void IList.Insert (int index, object item)
3328 if (item is ListViewItem)
3329 this.Insert (index, (ListViewItem) item);
3331 this.Insert (index, item.ToString ());
3334 void IList.Remove (object item)
3336 Remove ((ListViewItem) item);
3339 public int IndexOf (ListViewItem item)
3341 return list.IndexOf (item);
3345 public int IndexOfKey (string key)
3347 if (key == null || key.Length == 0)
3350 for (int i = 0; i < list.Count; i++) {
3351 ListViewItem lvi = (ListViewItem) list [i];
3352 if (String.Compare (key, lvi.Name, true) == 0)
3360 public ListViewItem Insert (int index, ListViewItem item)
3362 if (index < 0 || index > list.Count)
3363 throw new ArgumentOutOfRangeException ("index");
3365 if (list.Contains (item))
3366 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3368 if (item.ListView != null && item.ListView != owner)
3369 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");
3372 list.Insert (index, item);
3374 owner.Redraw (true);
3378 public ListViewItem Insert (int index, string text)
3380 return this.Insert (index, new ListViewItem (text));
3383 public ListViewItem Insert (int index, string text, int imageIndex)
3385 return this.Insert (index, new ListViewItem (text, imageIndex));
3389 public ListViewItem Insert (int index, string key, string text, int imageIndex)
3391 ListViewItem lvi = new ListViewItem (text, imageIndex);
3393 return Insert (index, lvi);
3397 public virtual void Remove (ListViewItem item)
3399 if (!list.Contains (item))
3402 bool selection_changed = owner.SelectedItems.Contains (item);
3405 owner.Redraw (true);
3406 if (selection_changed)
3407 owner.OnSelectedIndexChanged (EventArgs.Empty);
3410 public virtual void RemoveAt (int index)
3412 if (index < 0 || index >= Count)
3413 throw new ArgumentOutOfRangeException ("index");
3414 bool selection_changed = owner.SelectedIndices.Contains (index);
3415 list.RemoveAt (index);
3417 owner.Redraw (false);
3418 if (selection_changed)
3419 owner.OnSelectedIndexChanged (EventArgs.Empty);
3423 public void RemoveByKey (string key)
3425 int idx = IndexOfKey (key);
3431 #endregion // Public Methods
3433 internal event CollectionChangedHandler Changed;
3435 internal void Sort (IComparer comparer)
3437 list.Sort (comparer);
3441 internal void OnChange ()
3443 if (Changed != null)
3446 } // ListViewItemCollection
3448 public class SelectedIndexCollection : IList, ICollection, IEnumerable
3450 private readonly ListView owner;
3452 #region Public Constructor
3453 public SelectedIndexCollection (ListView owner)
3457 #endregion // Public Constructor
3459 #region Public Properties
3463 return owner.SelectedItems.Count;
3467 public bool IsReadOnly {
3477 public int this [int index] {
3479 int [] indices = GetIndices ();
3480 if (index < 0 || index >= indices.Length)
3481 throw new ArgumentOutOfRangeException ("index");
3482 return indices [index];
3486 bool ICollection.IsSynchronized {
3487 get { return false; }
3490 object ICollection.SyncRoot {
3491 get { return this; }
3494 bool IList.IsFixedSize {
3504 object IList.this [int index] {
3505 get { return this [index]; }
3506 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3508 #endregion // Public Properties
3510 #region Public Methods
3512 public int Add (int itemIndex)
3514 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
3515 throw new ArgumentOutOfRangeException ("index");
3517 owner.Items [itemIndex].Selected = true;
3518 if (!owner.IsHandleCreated)
3521 return owner.SelectedItems.Count;
3524 public void Clear ()
3526 owner.SelectedItems.Clear ();
3529 public bool Contains (int selectedIndex)
3531 int [] indices = GetIndices ();
3532 for (int i = 0; i < indices.Length; i++) {
3533 if (indices [i] == selectedIndex)
3539 public void CopyTo (Array dest, int index)
3541 int [] indices = GetIndices ();
3542 Array.Copy (indices, 0, dest, index, indices.Length);
3545 public IEnumerator GetEnumerator ()
3547 int [] indices = GetIndices ();
3548 return indices.GetEnumerator ();
3551 int IList.Add (object value)
3553 throw new NotSupportedException ("Add operation is not supported.");
3558 throw new NotSupportedException ("Clear operation is not supported.");
3561 bool IList.Contains (object selectedIndex)
3563 if (!(selectedIndex is int))
3565 return Contains ((int) selectedIndex);
3568 int IList.IndexOf (object selectedIndex)
3570 if (!(selectedIndex is int))
3572 return IndexOf ((int) selectedIndex);
3575 void IList.Insert (int index, object value)
3577 throw new NotSupportedException ("Insert operation is not supported.");
3580 void IList.Remove (object value)
3582 throw new NotSupportedException ("Remove operation is not supported.");
3585 void IList.RemoveAt (int index)
3587 throw new NotSupportedException ("RemoveAt operation is not supported.");
3590 public int IndexOf (int selectedIndex)
3592 int [] indices = GetIndices ();
3593 for (int i = 0; i < indices.Length; i++) {
3594 if (indices [i] == selectedIndex)
3601 public void Remove (int itemIndex)
3603 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
3604 throw new ArgumentOutOfRangeException ("itemIndex");
3606 owner.Items [itemIndex].Selected = false;
3609 #endregion // Public Methods
3611 private int [] GetIndices ()
3613 ArrayList selected_items = owner.SelectedItems.List;
3614 int [] indices = new int [selected_items.Count];
3615 for (int i = 0; i < selected_items.Count; i++) {
3616 ListViewItem item = (ListViewItem) selected_items [i];
3617 indices [i] = item.Index;
3621 } // SelectedIndexCollection
3623 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
3625 private readonly ListView owner;
3626 private ArrayList list;
3628 #region Public Constructor
3629 public SelectedListViewItemCollection (ListView owner)
3632 this.owner.Items.Changed += new CollectionChangedHandler (
3633 ItemsCollection_Changed);
3635 #endregion // Public Constructor
3637 #region Public Properties
3641 if (!owner.IsHandleCreated)
3647 public bool IsReadOnly {
3648 get { return true; }
3651 public ListViewItem this [int index] {
3653 ArrayList selected_items = List;
3654 if (!owner.IsHandleCreated || index < 0 || index >= selected_items.Count)
3655 throw new ArgumentOutOfRangeException ("index");
3656 return (ListViewItem) selected_items [index];
3661 public ListViewItem this [string key] {
3663 int idx = IndexOfKey (key);
3667 return (ListViewItem) List [idx];
3672 bool ICollection.IsSynchronized {
3673 get { return false; }
3676 object ICollection.SyncRoot {
3677 get { return this; }
3680 bool IList.IsFixedSize {
3681 get { return true; }
3684 object IList.this [int index] {
3685 get { return this [index]; }
3686 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3688 #endregion // Public Properties
3690 #region Public Methods
3691 public void Clear ()
3693 if (!owner.IsHandleCreated)
3696 foreach (ListViewItem item in List)
3697 item.Selected = false;
3700 public bool Contains (ListViewItem item)
3702 if (!owner.IsHandleCreated)
3704 return List.Contains (item);
3708 public bool ContainsKey (string key)
3710 return IndexOfKey (key) != -1;
3714 public void CopyTo (Array dest, int index)
3716 if (!owner.IsHandleCreated)
3718 List.CopyTo (dest, index);
3721 public IEnumerator GetEnumerator ()
3723 if (!owner.IsHandleCreated)
3724 return (new ListViewItem [0]).GetEnumerator ();
3725 return List.GetEnumerator ();
3728 int IList.Add (object value)
3730 throw new NotSupportedException ("Add operation is not supported.");
3733 bool IList.Contains (object item)
3735 if (!(item is ListViewItem))
3737 return Contains ((ListViewItem) item);
3740 int IList.IndexOf (object item)
3742 if (!(item is ListViewItem))
3744 return IndexOf ((ListViewItem) item);
3747 void IList.Insert (int index, object value)
3749 throw new NotSupportedException ("Insert operation is not supported.");
3752 void IList.Remove (object value)
3754 throw new NotSupportedException ("Remove operation is not supported.");
3757 void IList.RemoveAt (int index)
3759 throw new NotSupportedException ("RemoveAt operation is not supported.");
3762 public int IndexOf (ListViewItem item)
3764 if (!owner.IsHandleCreated)
3766 return List.IndexOf (item);
3770 public virtual int IndexOfKey (string key)
3772 if (!owner.IsHandleCreated || key == null || key.Length == 0)
3775 ArrayList selected_items = List;
3776 for (int i = 0; i < selected_items.Count; i++) {
3777 ListViewItem item = (ListViewItem) selected_items [i];
3778 if (String.Compare (item.Name, key, true) == 0)
3785 #endregion // Public Methods
3787 internal ArrayList List {
3790 list = new ArrayList ();
3791 foreach (ListViewItem item in owner.Items) {
3800 internal void Reset ()
3802 // force re-population of list
3806 private void ItemsCollection_Changed ()
3810 } // SelectedListViewItemCollection
3812 internal delegate void CollectionChangedHandler ();
3814 #endregion // Subclasses
3816 protected override void OnResize (EventArgs e)
3821 protected override void OnMouseLeave (EventArgs e)
3823 base.OnMouseLeave (e);
3827 // ColumnReorder event
3829 static object ColumnReorderedEvent = new object ();
3830 public event ColumnReorderedEventHandler ColumnReordered {
3831 add { Events.AddHandler (ColumnReorderedEvent, value); }
3832 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
3835 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
3837 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);