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)
28 // - 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 namespace System.Windows.Forms
45 [DefaultEvent ("SelectedIndexChanged")]
46 [DefaultProperty ("Items")]
47 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
48 public class ListView : Control
50 private ItemActivation activation = ItemActivation.Standard;
51 private ListViewAlignment alignment = ListViewAlignment.Top;
52 private bool allow_column_reorder = false;
53 private bool auto_arrange = true;
54 private bool check_boxes = false;
55 private CheckedIndexCollection checked_indices;
56 private CheckedListViewItemCollection checked_items;
57 private ColumnHeaderCollection columns;
58 internal ListViewItem focused_item;
59 private bool full_row_select = false;
60 private bool grid_lines = false;
61 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
62 private bool hide_selection = true;
63 private bool hover_selection = false;
64 private IComparer item_sorter;
65 private ListViewItemCollection items;
66 private bool label_edit = false;
67 private bool label_wrap = true;
68 private bool multiselect = true;
69 private bool scrollable = true;
70 private SelectedIndexCollection selected_indices;
71 private SelectedListViewItemCollection selected_items;
72 private SortOrder sort_order = SortOrder.None;
73 private ImageList state_image_list;
74 private bool updating = false;
75 private View view = View.LargeIcon;
76 private int layout_wd; // We might draw more than our client area
77 private int layout_ht; // therefore we need to have these two.
78 //private TextBox editor; // Used for editing an item text
79 HeaderControl header_control;
80 internal ItemControl item_control;
81 internal ScrollBar h_scroll; // used for scrolling horizontally
82 internal ScrollBar v_scroll; // used for scrolling vertically
83 internal int h_marker; // Position markers for scrolling
84 internal int v_marker;
85 private int keysearch_tickcnt;
86 private string keysearch_text;
87 static private readonly int keysearch_keydelay = 1000;
88 private int[] reordered_column_indices;
91 internal ImageList large_image_list;
92 internal ImageList small_image_list;
93 internal Size text_size = Size.Empty;
96 public event LabelEditEventHandler AfterLabelEdit;
99 [EditorBrowsable (EditorBrowsableState.Never)]
100 public new event EventHandler BackgroundImageChanged {
101 add { base.BackgroundImageChanged += value; }
102 remove { base.BackgroundImageChanged -= value; }
105 public event LabelEditEventHandler BeforeLabelEdit;
106 public event ColumnClickEventHandler ColumnClick;
107 public event EventHandler ItemActivate;
108 public event ItemCheckEventHandler ItemCheck;
109 public event ItemDragEventHandler ItemDrag;
112 [EditorBrowsable (EditorBrowsableState.Never)]
113 public new event PaintEventHandler Paint {
114 add { base.Paint += value; }
115 remove { base.Paint -= value; }
118 public event EventHandler SelectedIndexChanged;
121 [EditorBrowsable (EditorBrowsableState.Never)]
122 public new event EventHandler TextChanged {
123 add { base.TextChanged += value; }
124 remove { base.TextChanged -= value; }
129 #region Public Constructors
132 background_color = ThemeEngine.Current.ColorWindow;
133 checked_indices = new CheckedIndexCollection (this);
134 checked_items = new CheckedListViewItemCollection (this);
135 columns = new ColumnHeaderCollection (this);
136 foreground_color = SystemColors.WindowText;
137 items = new ListViewItemCollection (this);
138 selected_indices = new SelectedIndexCollection (this);
139 selected_items = new SelectedListViewItemCollection (this);
141 border_style = BorderStyle.Fixed3D;
143 header_control = new HeaderControl (this);
144 header_control.Visible = false;
145 Controls.AddImplicit (header_control);
147 item_control = new ItemControl (this);
148 Controls.AddImplicit (item_control);
150 h_scroll = new HScrollBar ();
151 Controls.AddImplicit (this.h_scroll);
153 v_scroll = new VScrollBar ();
154 Controls.AddImplicit (this.v_scroll);
156 h_marker = v_marker = 0;
157 keysearch_tickcnt = 0;
159 // scroll bars are disabled initially
160 h_scroll.Visible = false;
161 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
162 v_scroll.Visible = false;
163 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
166 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
167 SizeChanged += new EventHandler (ListView_SizeChanged);
168 GotFocus += new EventHandler (FocusChanged);
169 LostFocus += new EventHandler (FocusChanged);
170 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
172 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
174 #endregion // Public Constructors
176 #region Private Internal Properties
177 internal Size CheckBoxSize {
179 if (this.check_boxes) {
180 if (this.state_image_list != null)
181 return this.state_image_list.ImageSize;
183 return ThemeEngine.Current.ListViewCheckBoxSize;
189 #endregion // Private Internal Properties
191 #region Protected Properties
192 protected override CreateParams CreateParams {
193 get { return base.CreateParams; }
196 protected override Size DefaultSize {
197 get { return ThemeEngine.Current.ListViewDefaultSize; }
199 #endregion // Protected Properties
201 #region Public Instance Properties
202 [DefaultValue (ItemActivation.Standard)]
203 public ItemActivation Activation {
204 get { return activation; }
206 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
207 value != ItemActivation.TwoClick) {
208 throw new InvalidEnumArgumentException (string.Format
209 ("Enum argument value '{0}' is not valid for Activation", value));
216 [DefaultValue (ListViewAlignment.Top)]
218 public ListViewAlignment Alignment {
219 get { return alignment; }
221 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
222 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
223 throw new InvalidEnumArgumentException (string.Format
224 ("Enum argument value '{0}' is not valid for Alignment", value));
227 if (this.alignment != value) {
229 // alignment does not matter in Details/List views
230 if (this.view == View.LargeIcon ||
231 this.View == View.SmallIcon)
237 [DefaultValue (false)]
238 public bool AllowColumnReorder {
239 get { return allow_column_reorder; }
240 set { allow_column_reorder = value; }
243 [DefaultValue (true)]
244 public bool AutoArrange {
245 get { return auto_arrange; }
247 if (auto_arrange != value) {
248 auto_arrange = value;
249 // autoarrange does not matter in Details/List views
250 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
256 public override Color BackColor {
258 if (background_color.IsEmpty)
259 return ThemeEngine.Current.ColorWindow;
261 return background_color;
263 set { background_color = value; }
267 [EditorBrowsable (EditorBrowsableState.Never)]
268 public override Image BackgroundImage {
269 get { return background_image; }
271 if (value == background_image)
274 background_image = value;
275 OnBackgroundImageChanged (EventArgs.Empty);
279 [DefaultValue (BorderStyle.Fixed3D)]
281 public BorderStyle BorderStyle {
282 get { return InternalBorderStyle; }
283 set { InternalBorderStyle = value; }
286 [DefaultValue (false)]
287 public bool CheckBoxes {
288 get { return check_boxes; }
290 if (check_boxes != value) {
292 if (value && View == View.Tile)
293 throw new NotSupportedException ("CheckBoxes are not"
294 + " supported in Tile view. Choose a different"
295 + " view or set CheckBoxes to false.");
305 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
306 public CheckedIndexCollection CheckedIndices {
307 get { return checked_indices; }
311 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
312 public CheckedListViewItemCollection CheckedItems {
313 get { return checked_items; }
316 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
318 [MergableProperty (false)]
319 public ColumnHeaderCollection Columns {
320 get { return columns; }
324 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
325 public ListViewItem FocusedItem {
331 public override Color ForeColor {
333 if (foreground_color.IsEmpty)
334 return ThemeEngine.Current.ColorWindowText;
336 return foreground_color;
338 set { foreground_color = value; }
341 [DefaultValue (false)]
342 public bool FullRowSelect {
343 get { return full_row_select; }
344 set { full_row_select = value; }
347 [DefaultValue (false)]
348 public bool GridLines {
349 get { return grid_lines; }
351 if (grid_lines != value) {
358 [DefaultValue (ColumnHeaderStyle.Clickable)]
359 public ColumnHeaderStyle HeaderStyle {
360 get { return header_style; }
362 if (header_style == value)
366 case ColumnHeaderStyle.Clickable:
367 case ColumnHeaderStyle.Nonclickable:
368 case ColumnHeaderStyle.None:
371 throw new InvalidEnumArgumentException (string.Format
372 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
375 header_style = value;
376 if (view == View.Details)
381 [DefaultValue (true)]
382 public bool HideSelection {
383 get { return hide_selection; }
385 if (hide_selection != value) {
386 hide_selection = value;
392 [DefaultValue (false)]
393 public bool HoverSelection {
394 get { return hover_selection; }
395 set { hover_selection = value; }
398 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
400 [MergableProperty (false)]
401 public ListViewItemCollection Items {
402 get { return items; }
405 [DefaultValue (false)]
406 public bool LabelEdit {
407 get { return label_edit; }
408 set { label_edit = value; }
411 [DefaultValue (true)]
413 public bool LabelWrap {
414 get { return label_wrap; }
416 if (label_wrap != value) {
423 [DefaultValue (null)]
424 public ImageList LargeImageList {
425 get { return large_image_list; }
427 large_image_list = value;
433 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
434 public IComparer ListViewItemSorter {
436 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
441 if (item_sorter != value) {
448 [DefaultValue (true)]
449 public bool MultiSelect {
450 get { return multiselect; }
451 set { multiselect = value; }
454 [DefaultValue (true)]
455 public bool Scrollable {
456 get { return scrollable; }
458 if (scrollable != value) {
466 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
467 public SelectedIndexCollection SelectedIndices {
468 get { return selected_indices; }
472 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
473 public SelectedListViewItemCollection SelectedItems {
474 get { return selected_items; }
478 [MonoTODO("Implement")]
479 public bool ShowGroups {
489 [DefaultValue (null)]
490 public ImageList SmallImageList {
491 get { return small_image_list; }
493 small_image_list = value;
498 [DefaultValue (SortOrder.None)]
499 public SortOrder Sorting {
500 get { return sort_order; }
502 if (!Enum.IsDefined (typeof (SortOrder), value)) {
503 throw new InvalidEnumArgumentException ("value", (int) value,
507 if (sort_order == value)
512 if (value == SortOrder.None) {
513 if (item_sorter != null) {
514 // ListViewItemSorter should never be reset for SmallIcon
515 // and LargeIcon view
516 if (View != View.SmallIcon && View != View.LargeIcon)
520 // in .NET 1.1, only internal IComparer would be
522 if (item_sorter is ItemComparer)
528 if (item_sorter == null)
529 item_sorter = new ItemComparer (value);
530 if (item_sorter is ItemComparer) {
532 item_sorter = new ItemComparer (value);
534 // in .NET 1.1, the sort order is not updated for
535 // SmallIcon and LargeIcon views if no custom IComparer
537 if (View != View.SmallIcon && View != View.LargeIcon)
538 item_sorter = new ItemComparer (value);
546 [DefaultValue (null)]
547 public ImageList StateImageList {
548 get { return state_image_list; }
550 state_image_list = value;
557 [EditorBrowsable (EditorBrowsableState.Never)]
558 public override string Text {
567 OnTextChanged (EventArgs.Empty);
572 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
573 public ListViewItem TopItem {
576 if (this.items.Count == 0)
578 // if contents are not scrolled
579 // it is the first item
580 else if (h_marker == 0 && v_marker == 0)
581 return this.items [0];
582 // do a hit test for the scrolled position
584 foreach (ListViewItem item in this.items) {
585 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
594 [MonoTODO("Implement")]
595 public bool UseCompatibleStateImageBehavior {
605 [DefaultValue (View.LargeIcon)]
609 if (!Enum.IsDefined (typeof (View), value))
610 throw new InvalidEnumArgumentException ("value", (int) value,
615 if (CheckBoxes && value == View.Tile)
616 throw new NotSupportedException ("CheckBoxes are not"
617 + " supported in Tile view. Choose a different"
618 + " view or set CheckBoxes to false.");
621 h_scroll.Value = v_scroll.Value = 0;
627 #endregion // Public Instance Properties
629 #region Internal Methods Properties
631 internal int FirstVisibleIndex {
634 if (this.items.Count == 0)
637 if (h_marker == 0 && v_marker == 0)
640 foreach (ListViewItem item in this.items) {
641 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
650 internal int LastVisibleIndex {
652 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
653 if (View == View.List || Alignment == ListViewAlignment.Left) {
654 if (Items[i].Bounds.X > ClientRectangle.Right)
657 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
662 return Items.Count - 1;
666 internal int TotalWidth {
667 get { return Math.Max (this.Width, this.layout_wd); }
670 internal int TotalHeight {
671 get { return Math.Max (this.Height, this.layout_ht); }
674 internal void Redraw (bool recalculate)
676 // Avoid calculations when control is being updated
681 CalculateListView (this.alignment);
686 internal Size GetChildColumnSize (int index)
688 Size ret_size = Size.Empty;
689 ColumnHeader col = this.columns [index];
691 if (col.Width == -2) { // autosize = max(items, columnheader)
692 Size size = Size.Ceiling (this.DeviceContext.MeasureString
693 (col.Text, this.Font));
694 ret_size = BiggestItem (index);
695 if (size.Width > ret_size.Width)
698 else { // -1 and all the values < -2 are put under one category
699 ret_size = BiggestItem (index);
700 // fall back to empty columns' width if no subitem is available for a column
701 if (ret_size.IsEmpty) {
702 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
703 if (col.Text.Length > 0)
704 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
705 (col.Text, this.Font)).Height;
707 ret_size.Height = this.Font.Height;
711 // adjust the size for icon and checkbox for 0th column
713 ret_size.Width += (this.CheckBoxSize.Width + 4);
714 if (this.small_image_list != null)
715 ret_size.Width += this.small_image_list.ImageSize.Width;
720 // Returns the size of biggest item text in a column.
721 private Size BiggestItem (int col)
723 Size temp = Size.Empty;
724 Size ret_size = Size.Empty;
726 // 0th column holds the item text, we check the size of
727 // the various subitems falling in that column and get
728 // the biggest one's size.
729 foreach (ListViewItem item in items) {
730 if (col >= item.SubItems.Count)
733 temp = Size.Ceiling (this.DeviceContext.MeasureString
734 (item.SubItems [col].Text, this.Font));
735 if (temp.Width > ret_size.Width)
739 // adjustment for space
740 if (!ret_size.IsEmpty)
746 // Sets the size of the biggest item text as per the view
747 private void CalcTextSize ()
749 // clear the old value
750 text_size = Size.Empty;
752 if (items.Count == 0)
755 text_size = BiggestItem (0);
757 if (view == View.LargeIcon && this.label_wrap) {
758 Size temp = Size.Empty;
759 if (this.check_boxes)
760 temp.Width += 2 * this.CheckBoxSize.Width;
761 if (large_image_list != null)
762 temp.Width += large_image_list.ImageSize.Width;
765 // wrapping is done for two lines only
766 if (text_size.Width > temp.Width) {
767 text_size.Width = temp.Width;
768 text_size.Height *= 2;
771 else if (view == View.List) {
772 // in list view max text shown in determined by the
773 // control width, even if scolling is enabled.
774 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
775 if (this.small_image_list != null)
776 max_wd -= this.small_image_list.ImageSize.Width;
778 if (text_size.Width > max_wd)
779 text_size.Width = max_wd;
782 // we do the default settings, if we have got 0's
783 if (text_size.Height <= 0)
784 text_size.Height = this.Font.Height;
785 if (text_size.Width <= 0)
786 text_size.Width = this.Width;
789 text_size.Width += 4;
790 text_size.Height += 2;
793 private void Scroll (ScrollBar scrollbar, int delta)
795 if (delta == 0 || !scrollbar.Visible)
799 if (scrollbar == h_scroll)
800 max = h_scroll.Maximum - item_control.Width;
802 max = v_scroll.Maximum - item_control.Height;
804 int val = scrollbar.Value + delta;
807 else if (val < scrollbar.Minimum)
808 val = scrollbar.Minimum;
809 scrollbar.Value = val;
812 private void CalculateScrollBars ()
814 Rectangle client_area = ClientRectangle;
816 if (!this.scrollable || this.items.Count <= 0) {
817 h_scroll.Visible = false;
818 v_scroll.Visible = false;
819 item_control.Location = new Point (0, header_control.Height);
820 item_control.Height = ClientRectangle.Width - header_control.Height;
821 item_control.Width = ClientRectangle.Width;
822 header_control.Width = ClientRectangle.Width;
826 // Don't calculate if the view is not displayable
827 if (client_area.Height < 0 || client_area.Width < 0)
830 // making a scroll bar visible might make
831 // other scroll bar visible
832 if (layout_wd > client_area.Right) {
833 h_scroll.Visible = true;
834 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
835 v_scroll.Visible = true;
837 v_scroll.Visible = false;
838 } else if (layout_ht > client_area.Bottom) {
839 v_scroll.Visible = true;
840 if ((layout_wd + v_scroll.Width) > client_area.Right)
841 h_scroll.Visible = true;
843 h_scroll.Visible = false;
845 h_scroll.Visible = false;
846 v_scroll.Visible = false;
849 item_control.Height = ClientRectangle.Height - header_control.Height;
851 if (h_scroll.Visible) {
852 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
853 h_scroll.Minimum = 0;
855 // if v_scroll is visible, adjust the maximum of the
856 // h_scroll to account for the width of v_scroll
857 if (v_scroll.Visible) {
858 h_scroll.Maximum = layout_wd + v_scroll.Width;
859 h_scroll.Width = client_area.Width - v_scroll.Width;
862 h_scroll.Maximum = layout_wd;
863 h_scroll.Width = client_area.Width;
866 h_scroll.LargeChange = client_area.Width;
867 h_scroll.SmallChange = Font.Height;
868 item_control.Height -= h_scroll.Height;
871 if (header_control.Visible)
872 header_control.Width = ClientRectangle.Width;
873 item_control.Width = ClientRectangle.Width;
875 if (v_scroll.Visible) {
876 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
877 v_scroll.Minimum = 0;
879 // if h_scroll is visible, adjust the maximum of the
880 // v_scroll to account for the height of h_scroll
881 if (h_scroll.Visible) {
882 v_scroll.Maximum = layout_ht + h_scroll.Height;
883 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
885 v_scroll.Maximum = layout_ht;
886 v_scroll.Height = client_area.Height;
889 v_scroll.LargeChange = client_area.Height;
890 v_scroll.SmallChange = Font.Height;
891 if (header_control.Visible)
892 header_control.Width -= v_scroll.Width;
893 item_control.Width -= v_scroll.Width;
897 ColumnHeader GetReorderedColumn (int index)
899 if (reordered_column_indices == null)
900 return Columns [index];
902 return Columns [reordered_column_indices [index]];
905 void ReorderColumn (ColumnHeader col, int index)
907 if (reordered_column_indices == null) {
908 reordered_column_indices = new int [Columns.Count];
909 for (int i = 0; i < Columns.Count; i++)
910 reordered_column_indices [i] = i;
913 if (reordered_column_indices [index] == col.Index)
916 int[] curr = reordered_column_indices;
917 int[] result = new int [Columns.Count];
919 for (int i = 0; i < Columns.Count; i++) {
920 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
924 result [i] = col.Index;
926 result [i] = curr [curr_idx++];
929 reordered_column_indices = result;
931 header_control.Invalidate ();
932 item_control.Invalidate ();
935 Size LargeIconItemSize {
937 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
938 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
939 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
940 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
941 return new Size (w, h);
945 Size SmallIconItemSize {
947 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
948 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
949 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
950 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
951 return new Size (w, h);
957 ListViewItem[,] item_matrix;
959 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
961 header_control.Visible = false;
962 header_control.Size = Size.Empty;
963 item_control.Visible = true;
964 item_control.Location = Point.Empty;
966 if (items.Count == 0)
969 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
971 Rectangle area = ClientRectangle;
974 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
977 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
979 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
982 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
985 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
986 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
987 item_matrix = new ListViewItem [rows, cols];
990 foreach (ListViewItem item in items) {
991 int x = col * (sz.Width + x_spacing);
992 int y = row * (sz.Height + y_spacing);
993 item.Location = new Point (x, y);
997 item_matrix [row, col] = item;
1004 if (++col == cols) {
1011 item_control.Size = new Size (layout_wd, layout_ht);
1014 void LayoutHeader ()
1017 for (int i = 0; i < Columns.Count; i++) {
1018 ColumnHeader col = GetReorderedColumn (i);
1021 col.CalcColumnHeader ();
1025 if (x < ClientRectangle.Width)
1026 x = ClientRectangle.Width;
1028 if (header_style == ColumnHeaderStyle.None) {
1029 header_control.Visible = false;
1030 header_control.Size = Size.Empty;
1032 header_control.Width = x;
1033 header_control.Height = columns [0].Ht;
1034 header_control.Visible = true;
1038 void LayoutDetails ()
1040 if (columns.Count == 0) {
1041 header_control.Visible = false;
1042 item_control.Visible = false;
1048 item_control.Visible = true;
1049 item_control.Location = new Point (0, header_control.Height);
1052 if (items.Count > 0) {
1053 foreach (ListViewItem item in items) {
1055 item.Location = new Point (0, y);
1056 y += item.Bounds.Height + 2;
1059 // some space for bottom gridline
1064 layout_wd = Math.Max (header_control.Width, item_control.Width);
1065 layout_ht = y + header_control.Height;
1068 private void CalculateListView (ListViewAlignment align)
1077 case View.SmallIcon:
1078 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1081 case View.LargeIcon:
1082 LayoutIcons (true, alignment == ListViewAlignment.Left,
1083 ThemeEngine.Current.ListViewHorizontalSpacing,
1084 ThemeEngine.Current.ListViewVerticalSpacing);
1088 LayoutIcons (false, true, 4, 2);
1092 CalculateScrollBars ();
1097 return (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0;
1101 internal void UpdateSelection (ListViewItem item)
1103 if (item.Selected) {
1104 if (!MultiSelect && SelectedItems.Count > 0)
1105 SelectedItems.Clear ();
1107 if (!SelectedItems.list.Contains (item))
1108 SelectedItems.list.Add (item);
1110 SelectedItems.list.Remove (item);
1114 private bool KeySearchString (KeyEventArgs ke)
1116 int current_tickcnt = Environment.TickCount;
1117 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1118 keysearch_text = string.Empty;
1121 keysearch_text += (char) ke.KeyData;
1122 keysearch_tickcnt = current_tickcnt;
1124 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1127 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1128 CompareOptions.IgnoreCase)) {
1129 SetFocusedItem (Items [i]);
1130 items [i].Selected = true;
1134 i = (i + 1 < Items.Count) ? i+1 : 0;
1142 int GetAdjustedIndex (Keys key)
1146 if (View == View.Details) {
1148 result = FocusedItem.Index - 1;
1149 else if (key == Keys.Down) {
1150 result = FocusedItem.Index + 1;
1151 if (result == items.Count)
1157 int row = FocusedItem.row;
1158 int col = FocusedItem.col;
1164 return item_matrix [row, col - 1].Index;
1167 if (col == (cols - 1))
1169 while (item_matrix [row, col + 1] == null)
1171 return item_matrix [row, col + 1].Index;
1176 return item_matrix [row - 1, col].Index;
1179 if (row == (rows - 1) || row == Items.Count - 1)
1181 while (item_matrix [row + 1, col] == null)
1183 return item_matrix [row + 1, col].Index;
1190 ListViewItem selection_start;
1192 private bool SelectItems (ArrayList sel_items)
1194 bool changed = false;
1195 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1196 foreach (ListViewItem item in curr_items)
1197 if (!sel_items.Contains (item)) {
1198 item.Selected = false;
1201 foreach (ListViewItem item in sel_items)
1202 if (!item.Selected) {
1203 item.Selected = true;
1209 private void UpdateMultiSelection (int index)
1211 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1212 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1213 ListViewItem item = items [index];
1215 if (shift_pressed && selection_start != null) {
1216 ArrayList list = new ArrayList ();
1217 int start = Math.Min (selection_start.Index, index);
1218 int end = Math.Max (selection_start.Index, index);
1219 if (View == View.Details) {
1220 for (int i = start; i <= end; i++)
1221 list.Add (items [i]);
1223 int left = Math.Min (items [start].col, items [end].col);
1224 int right = Math.Max (items [start].col, items [end].col);
1225 int top = Math.Min (items [start].row, items [end].row);
1226 int bottom = Math.Max (items [start].row, items [end].row);
1227 foreach (ListViewItem curr in items)
1228 if (curr.row >= top && curr.row <= bottom &&
1229 curr.col >= left && curr.col <= right)
1232 if (SelectItems (list))
1233 OnSelectedIndexChanged (EventArgs.Empty);
1236 SelectedItems.Clear ();
1237 item.Selected = true;
1238 selection_start = item;
1239 OnSelectedIndexChanged (EventArgs.Empty);
1243 internal override bool InternalPreProcessMessage (ref Message msg)
1245 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1246 Keys key_data = (Keys)msg.WParam.ToInt32();
1247 if (HandleNavKeys (key_data))
1250 return base.InternalPreProcessMessage (ref msg);
1253 bool HandleNavKeys (Keys key_data)
1255 if (Items.Count == 0 || !item_control.Visible)
1258 if (FocusedItem == null)
1259 SetFocusedItem (Items [0]);
1263 SelectIndex (Items.Count - 1);
1274 SelectIndex (GetAdjustedIndex (key_data));
1284 void SelectIndex (int index)
1290 UpdateMultiSelection (index);
1291 else if (!items [index].Selected) {
1292 items [index].Selected = true;
1293 OnSelectedIndexChanged (EventArgs.Empty);
1296 SetFocusedItem (items [index]);
1297 EnsureVisible (index);
1300 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1302 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1305 ke.Handled = KeySearchString (ke);
1308 internal class ItemControl : Control {
1311 ListViewItem clicked_item;
1312 ListViewItem last_clicked_item;
1313 bool hover_processed = false;
1314 bool checking = false;
1316 public ItemControl (ListView owner)
1319 DoubleClick += new EventHandler(ItemsDoubleClick);
1320 MouseDown += new MouseEventHandler(ItemsMouseDown);
1321 MouseMove += new MouseEventHandler(ItemsMouseMove);
1322 MouseHover += new EventHandler(ItemsMouseHover);
1323 MouseUp += new MouseEventHandler(ItemsMouseUp);
1326 void ItemsDoubleClick (object sender, EventArgs e)
1328 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1329 owner.ItemActivate (this, e);
1339 BoxSelect box_select_mode = BoxSelect.None;
1340 ArrayList prev_selection;
1341 Point box_select_start;
1343 Rectangle box_select_rect;
1344 internal Rectangle BoxSelectRectangle {
1345 get { return box_select_rect; }
1347 if (box_select_rect == value)
1350 InvalidateBoxSelectRect ();
1351 box_select_rect = value;
1352 InvalidateBoxSelectRect ();
1356 void InvalidateBoxSelectRect ()
1358 if (BoxSelectRectangle.Size.IsEmpty)
1361 Rectangle edge = BoxSelectRectangle;
1367 edge.Y = BoxSelectRectangle.Bottom - 1;
1369 edge.Y = BoxSelectRectangle.Y - 1;
1371 edge.Height = BoxSelectRectangle.Height + 2;
1373 edge.X = BoxSelectRectangle.Right - 1;
1377 private Rectangle CalculateBoxSelectRectangle (Point pt)
1379 int left = Math.Min (box_select_start.X, pt.X);
1380 int right = Math.Max (box_select_start.X, pt.X);
1381 int top = Math.Min (box_select_start.Y, pt.Y);
1382 int bottom = Math.Max (box_select_start.Y, pt.Y);
1383 return Rectangle.FromLTRB (left, top, right, bottom);
1386 ArrayList BoxSelectedItems {
1388 ArrayList result = new ArrayList ();
1389 foreach (ListViewItem item in owner.Items) {
1390 Rectangle r = item.Bounds;
1392 r.Y += r.Height / 4;
1395 if (BoxSelectRectangle.IntersectsWith (r))
1402 private bool PerformBoxSelection (Point pt)
1404 if (box_select_mode == BoxSelect.None)
1407 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1409 ArrayList box_items = BoxSelectedItems;
1413 switch (box_select_mode) {
1415 case BoxSelect.Normal:
1419 case BoxSelect.Control:
1420 items = new ArrayList ();
1421 foreach (ListViewItem item in prev_selection)
1422 if (!box_items.Contains (item))
1424 foreach (ListViewItem item in box_items)
1425 if (!prev_selection.Contains (item))
1429 case BoxSelect.Shift:
1431 foreach (ListViewItem item in box_items)
1432 prev_selection.Remove (item);
1433 foreach (ListViewItem item in prev_selection)
1438 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1442 owner.SelectItems (items);
1448 private void ToggleCheckState (ListViewItem item)
1450 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1451 item.Checked = !item.Checked;
1452 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1454 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1455 owner.OnItemCheck (ice);
1458 private void ItemsMouseDown (object sender, MouseEventArgs me)
1460 if (owner.items.Count == 0)
1463 Point pt = new Point (me.X, me.Y);
1464 foreach (ListViewItem item in owner.items) {
1465 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1469 ToggleCheckState (item);
1473 if (owner.View == View.Details && !owner.FullRowSelect) {
1474 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1475 clicked_item = item;
1479 if (item.Bounds.Contains (pt)) {
1480 clicked_item = item;
1487 if (clicked_item != null) {
1488 owner.SetFocusedItem (clicked_item);
1489 bool changed = !clicked_item.Selected;
1490 if (owner.MultiSelect)
1491 owner.UpdateMultiSelection (clicked_item.Index);
1493 clicked_item.Selected = true;
1496 owner.OnSelectedIndexChanged (EventArgs.Empty);
1498 // Raise double click if the item was clicked. On MS the
1499 // double click is only raised if you double click an item
1500 if (me.Clicks > 1) {
1501 owner.OnDoubleClick (EventArgs.Empty);
1502 if (owner.CheckBoxes)
1503 ToggleCheckState (clicked_item);
1504 } else if (me.Clicks == 1)
1505 owner.OnClick (EventArgs.Empty);
1507 if (owner.MultiSelect) {
1508 Keys mods = XplatUI.State.ModifierKeys;
1509 if ((mods & Keys.Shift) != 0)
1510 box_select_mode = BoxSelect.Shift;
1511 else if ((mods & Keys.Control) != 0)
1512 box_select_mode = BoxSelect.Control;
1514 box_select_mode = BoxSelect.Normal;
1515 box_select_start = pt;
1516 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1517 } else if (owner.selected_indices.Count > 0) {
1518 owner.SelectedItems.Clear ();
1519 owner.OnSelectedIndexChanged (EventArgs.Empty);
1524 private void ItemsMouseMove (object sender, MouseEventArgs me)
1526 if (PerformBoxSelection (new Point (me.X, me.Y)))
1529 if (owner.HoverSelection && hover_processed) {
1531 Point pt = PointToClient (Control.MousePosition);
1532 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1533 if (item == null || item.Selected)
1536 hover_processed = false;
1537 XplatUI.ResetMouseHover (Handle);
1542 private void ItemsMouseHover (object sender, EventArgs e)
1544 if (Capture || !owner.HoverSelection)
1547 hover_processed = true;
1548 Point pt = PointToClient (Control.MousePosition);
1549 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1554 item.Selected = true;
1555 owner.OnSelectedIndexChanged (new EventArgs ());
1558 private void ItemsMouseUp (object sender, MouseEventArgs me)
1561 if (owner.Items.Count == 0)
1564 Point pt = new Point (me.X, me.Y);
1566 Rectangle rect = Rectangle.Empty;
1567 if (clicked_item != null) {
1568 if (owner.view == View.Details && !owner.full_row_select)
1569 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1571 rect = clicked_item.Bounds;
1573 if (rect.Contains (pt)) {
1574 switch (owner.activation) {
1575 case ItemActivation.OneClick:
1576 owner.OnItemActivate (EventArgs.Empty);
1579 case ItemActivation.TwoClick:
1580 if (last_clicked_item == clicked_item) {
1581 owner.OnItemActivate (EventArgs.Empty);
1582 last_clicked_item = null;
1584 last_clicked_item = clicked_item;
1587 // DoubleClick activation is handled in another handler
1591 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1592 // Need this to clean up background clicks
1593 owner.SelectedItems.Clear ();
1594 owner.OnSelectedIndexChanged (EventArgs.Empty);
1597 clicked_item = null;
1598 box_select_start = Point.Empty;
1599 BoxSelectRectangle = Rectangle.Empty;
1600 prev_selection = null;
1601 box_select_mode = BoxSelect.None;
1605 internal override void OnPaintInternal (PaintEventArgs pe)
1607 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1610 internal override void OnGotFocusInternal (EventArgs e)
1616 internal override void OnPaintInternal (PaintEventArgs pe)
1621 CalculateScrollBars ();
1624 void FocusChanged (object o, EventArgs args)
1626 if (Items.Count == 0)
1629 if (FocusedItem == null)
1630 SetFocusedItem (Items [0]);
1632 item_control.Invalidate (FocusedItem.Bounds);
1635 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1637 if (Items.Count == 0)
1640 int lines = me.Delta / 120;
1647 case View.SmallIcon:
1648 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1650 case View.LargeIcon:
1651 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1654 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1659 private void ListView_SizeChanged (object sender, EventArgs e)
1661 CalculateListView (alignment);
1664 private void SetFocusedItem (ListViewItem item)
1666 if (focused_item != null)
1667 focused_item.Focused = false;
1670 item.Focused = true;
1672 focused_item = item;
1675 private void HorizontalScroller (object sender, EventArgs e)
1677 // Avoid unnecessary flickering, when button is
1678 // kept pressed at the end
1679 if (h_marker != h_scroll.Value) {
1681 int pixels = h_marker - h_scroll.Value;
1683 h_marker = h_scroll.Value;
1684 if (header_control.Visible)
1685 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1687 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1691 private void VerticalScroller (object sender, EventArgs e)
1693 // Avoid unnecessary flickering, when button is
1694 // kept pressed at the end
1695 if (v_marker != v_scroll.Value) {
1696 int pixels = v_marker - v_scroll.Value;
1697 Rectangle area = item_control.ClientRectangle;
1698 v_marker = v_scroll.Value;
1699 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1702 #endregion // Internal Methods Properties
1704 #region Protected Methods
1705 protected override void CreateHandle ()
1707 base.CreateHandle ();
1710 protected override void Dispose (bool disposing)
1713 h_scroll.Dispose ();
1714 v_scroll.Dispose ();
1716 large_image_list = null;
1717 small_image_list = null;
1718 state_image_list = null;
1721 base.Dispose (disposing);
1724 protected override bool IsInputKey (Keys keyData)
1741 return base.IsInputKey (keyData);
1744 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1746 if (AfterLabelEdit != null)
1747 AfterLabelEdit (this, e);
1750 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1752 if (BeforeLabelEdit != null)
1753 BeforeLabelEdit (this, e);
1756 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1758 if (ColumnClick != null)
1759 ColumnClick (this, e);
1762 protected override void OnEnabledChanged (EventArgs e)
1764 base.OnEnabledChanged (e);
1767 protected override void OnFontChanged (EventArgs e)
1769 base.OnFontChanged (e);
1773 protected override void OnHandleCreated (EventArgs e)
1775 base.OnHandleCreated (e);
1779 protected override void OnHandleDestroyed (EventArgs e)
1781 base.OnHandleDestroyed (e);
1784 protected virtual void OnItemActivate (EventArgs e)
1786 if (ItemActivate != null)
1787 ItemActivate (this, e);
1790 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1792 if (ItemCheck != null)
1793 ItemCheck (this, ice);
1796 protected virtual void OnItemDrag (ItemDragEventArgs e)
1798 if (ItemDrag != null)
1802 protected virtual void OnSelectedIndexChanged (EventArgs e)
1804 if (SelectedIndexChanged != null)
1805 SelectedIndexChanged (this, e);
1808 protected override void OnSystemColorsChanged (EventArgs e)
1810 base.OnSystemColorsChanged (e);
1813 protected void RealizeProperties ()
1818 protected void UpdateExtendedStyles ()
1823 protected override void WndProc (ref Message m)
1825 base.WndProc (ref m);
1827 #endregion // Protected Methods
1829 #region Public Instance Methods
1830 public void ArrangeIcons ()
1832 ArrangeIcons (this.alignment);
1835 public void ArrangeIcons (ListViewAlignment alignment)
1837 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1838 if (view == View.LargeIcon || view == View.SmallIcon) {
1839 this.CalculateListView (alignment);
1840 // we have done the calculations already
1841 this.Redraw (false);
1845 public void BeginUpdate ()
1847 // flag to avoid painting
1851 public void Clear ()
1854 items.Clear (); // Redraw (true) called here
1857 public void EndUpdate ()
1859 // flag to avoid painting
1862 // probably, now we need a redraw with recalculations
1866 public void EnsureVisible (int index)
1868 if (index < 0 || index >= items.Count || scrollable == false)
1871 Rectangle view_rect = item_control.ClientRectangle;
1872 Rectangle bounds = items [index].Bounds;
1874 if (view_rect.Contains (bounds))
1877 if (bounds.Left < 0)
1878 h_scroll.Value += bounds.Left;
1879 else if (bounds.Right > view_rect.Right)
1880 h_scroll.Value += (bounds.Right - view_rect.Right);
1883 v_scroll.Value += bounds.Top;
1884 else if (bounds.Bottom > view_rect.Bottom)
1885 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1888 public ListViewItem GetItemAt (int x, int y)
1890 foreach (ListViewItem item in items) {
1891 if (item.Bounds.Contains (x, y))
1897 public Rectangle GetItemRect (int index)
1899 return GetItemRect (index, ItemBoundsPortion.Entire);
1902 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1904 if (index < 0 || index >= items.Count)
1905 throw new IndexOutOfRangeException ("index");
1907 return items [index].GetBounds (portion);
1915 // we need this overload to reuse the logic for sorting, while allowing
1916 // redrawing to be done by caller or have it done by this method when
1917 // sorting is really performed
1919 // ListViewItemCollection's Add and AddRange methods call this overload
1920 // with redraw set to false, as they take care of redrawing themselves
1921 // (they even want to redraw the listview if no sort is performed, as
1922 // an item was added), while ListView.Sort () only wants to redraw if
1923 // sorting was actually performed
1924 private void Sort (bool redraw)
1926 if (!IsHandleCreated || item_sorter == null) {
1930 items.list.Sort (item_sorter);
1935 public override string ToString ()
1937 int count = this.Items.Count;
1940 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1942 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1944 #endregion // Public Instance Methods
1949 class HeaderControl : Control {
1952 bool column_resize_active = false;
1953 ColumnHeader resize_column;
1954 ColumnHeader clicked_column;
1955 ColumnHeader drag_column;
1957 int drag_to_index = -1;
1959 public HeaderControl (ListView owner)
1962 MouseDown += new MouseEventHandler (HeaderMouseDown);
1963 MouseMove += new MouseEventHandler (HeaderMouseMove);
1964 MouseUp += new MouseEventHandler (HeaderMouseUp);
1967 private ColumnHeader ColumnAtX (int x)
1969 Point pt = new Point (x, 0);
1970 ColumnHeader result = null;
1971 foreach (ColumnHeader col in owner.Columns) {
1972 if (col.Rect.Contains (pt)) {
1980 private int GetReorderedIndex (ColumnHeader col)
1982 if (owner.reordered_column_indices == null)
1985 for (int i = 0; i < owner.Columns.Count; i++)
1986 if (owner.reordered_column_indices [i] == col.Index)
1988 throw new Exception ("Column index missing from reordered array");
1991 private void HeaderMouseDown (object sender, MouseEventArgs me)
1993 if (resize_column != null) {
1994 column_resize_active = true;
1999 clicked_column = ColumnAtX (me.X + owner.h_marker);
2001 if (clicked_column != null) {
2003 if (owner.AllowColumnReorder) {
2005 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2006 drag_column.column_rect = clicked_column.Rect;
2007 drag_to_index = GetReorderedIndex (clicked_column);
2009 clicked_column.pressed = true;
2010 Rectangle bounds = clicked_column.Rect;
2011 bounds.X -= owner.h_marker;
2012 Invalidate (bounds);
2017 private void HeaderMouseMove (object sender, MouseEventArgs me)
2019 Point pt = new Point (me.X + owner.h_marker, me.Y);
2021 if (column_resize_active) {
2022 resize_column.Width = pt.X - resize_column.X;
2023 if (resize_column.Width < 0)
2024 resize_column.Width = 0;
2028 resize_column = null;
2030 if (clicked_column != null) {
2031 if (owner.AllowColumnReorder) {
2034 r = drag_column.column_rect;
2035 r.X = clicked_column.Rect.X + me.X - drag_x;
2036 drag_column.column_rect = r;
2038 int x = me.X + owner.h_marker;
2039 ColumnHeader over = ColumnAtX (x);
2041 drag_to_index = owner.Columns.Count;
2042 else if (x < over.X + over.Width / 2)
2043 drag_to_index = GetReorderedIndex (over);
2045 drag_to_index = GetReorderedIndex (over) + 1;
2048 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2049 bool pressed = clicked_column.pressed;
2050 clicked_column.pressed = over == clicked_column;
2051 if (clicked_column.pressed ^ pressed) {
2052 Rectangle bounds = clicked_column.Rect;
2053 bounds.X -= owner.h_marker;
2054 Invalidate (bounds);
2060 for (int i = 0; i < owner.Columns.Count; i++) {
2061 Rectangle zone = owner.Columns [i].Rect;
2062 zone.X = zone.Right - 5;
2064 if (zone.Contains (pt)) {
2065 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2067 resize_column = owner.Columns [i];
2072 if (resize_column == null)
2073 Cursor = Cursors.Default;
2075 Cursor = Cursors.VSplit;
2078 void HeaderMouseUp (object sender, MouseEventArgs me)
2082 if (column_resize_active) {
2083 column_resize_active = false;
2084 resize_column = null;
2085 Cursor = Cursors.Default;
2089 if (clicked_column != null && clicked_column.pressed) {
2090 clicked_column.pressed = false;
2091 Rectangle bounds = clicked_column.Rect;
2092 bounds.X -= owner.h_marker;
2093 Invalidate (bounds);
2094 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2097 if (drag_column != null && owner.AllowColumnReorder) {
2099 if (drag_to_index > GetReorderedIndex (clicked_column))
2101 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2102 owner.ReorderColumn (clicked_column, drag_to_index);
2107 clicked_column = null;
2110 internal override void OnPaintInternal (PaintEventArgs pe)
2115 Theme theme = ThemeEngine.Current;
2116 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2118 if (drag_column == null)
2122 if (drag_to_index == owner.Columns.Count)
2123 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2125 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2126 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2129 protected override void WndProc (ref Message m)
2131 switch ((Msg)m.Msg) {
2132 case Msg.WM_SETFOCUS:
2136 base.WndProc (ref m);
2142 private class ItemComparer : IComparer {
2143 readonly SortOrder sort_order;
2145 public ItemComparer (SortOrder sortOrder)
2147 sort_order = sortOrder;
2150 public int Compare (object x, object y)
2152 ListViewItem item_x = x as ListViewItem;
2153 ListViewItem item_y = y as ListViewItem;
2154 if (sort_order == SortOrder.Ascending)
2155 return String.Compare (item_x.Text, item_y.Text);
2157 return String.Compare (item_y.Text, item_x.Text);
2161 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2163 private readonly ListView owner;
2165 #region Public Constructor
2166 public CheckedIndexCollection (ListView owner)
2170 #endregion // Public Constructor
2172 #region Public Properties
2175 get { return owner.CheckedItems.Count; }
2178 public bool IsReadOnly {
2179 get { return true; }
2182 public int this [int index] {
2184 int [] indices = GetIndices ();
2185 if (index < 0 || index >= indices.Length)
2186 throw new ArgumentOutOfRangeException ("index");
2187 return indices [index];
2191 bool ICollection.IsSynchronized {
2192 get { return false; }
2195 object ICollection.SyncRoot {
2196 get { return this; }
2199 bool IList.IsFixedSize {
2200 get { return true; }
2203 object IList.this [int index] {
2204 get { return this [index]; }
2205 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2207 #endregion // Public Properties
2209 #region Public Methods
2210 public bool Contains (int checkedIndex)
2212 int [] indices = GetIndices ();
2213 for (int i = 0; i < indices.Length; i++) {
2214 if (indices [i] == checkedIndex)
2220 public IEnumerator GetEnumerator ()
2222 int [] indices = GetIndices ();
2223 return indices.GetEnumerator ();
2226 void ICollection.CopyTo (Array dest, int index)
2228 int [] indices = GetIndices ();
2229 Array.Copy (indices, 0, dest, index, indices.Length);
2232 int IList.Add (object value)
2234 throw new NotSupportedException ("Add operation is not supported.");
2239 throw new NotSupportedException ("Clear operation is not supported.");
2242 bool IList.Contains (object checkedIndex)
2244 if (!(checkedIndex is int))
2246 return Contains ((int) checkedIndex);
2249 int IList.IndexOf (object checkedIndex)
2251 if (!(checkedIndex is int))
2253 return IndexOf ((int) checkedIndex);
2256 void IList.Insert (int index, object value)
2258 throw new NotSupportedException ("Insert operation is not supported.");
2261 void IList.Remove (object value)
2263 throw new NotSupportedException ("Remove operation is not supported.");
2266 void IList.RemoveAt (int index)
2268 throw new NotSupportedException ("RemoveAt operation is not supported.");
2271 public int IndexOf (int checkedIndex)
2273 int [] indices = GetIndices ();
2274 for (int i = 0; i < indices.Length; i++) {
2275 if (indices [i] == checkedIndex)
2280 #endregion // Public Methods
2282 private int [] GetIndices ()
2284 int [] indices = new int [Count];
2285 for (int i = 0; i < owner.CheckedItems.Count; i++) {
2286 ListViewItem item = owner.CheckedItems [i];
2287 indices [i] = item.Index;
2291 } // CheckedIndexCollection
2293 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2295 internal readonly ArrayList list;
2296 private readonly ListView owner;
2298 #region Public Constructor
2299 public CheckedListViewItemCollection (ListView owner)
2301 list = new ArrayList ();
2304 #endregion // Public Constructor
2306 #region Public Properties
2310 if (!owner.CheckBoxes)
2316 public bool IsReadOnly {
2317 get { return true; }
2320 public ListViewItem this [int index] {
2322 if (index < 0 || index >= Count)
2323 throw new ArgumentOutOfRangeException ("index");
2324 return (ListViewItem) list [index];
2328 bool ICollection.IsSynchronized {
2329 get { return list.IsSynchronized; }
2332 object ICollection.SyncRoot {
2333 get { return this; }
2336 bool IList.IsFixedSize {
2337 get { return true; }
2340 object IList.this [int index] {
2341 get { return this [index]; }
2342 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2344 #endregion // Public Properties
2346 #region Public Methods
2347 public bool Contains (ListViewItem item)
2349 if (!owner.CheckBoxes)
2351 return list.Contains (item);
2354 public void CopyTo (Array dest, int index)
2356 if (!owner.CheckBoxes)
2358 list.CopyTo (dest, index);
2361 public IEnumerator GetEnumerator ()
2363 if (!owner.CheckBoxes)
2364 return (new ListViewItem [0]).GetEnumerator ();
2365 return list.GetEnumerator ();
2368 int IList.Add (object value)
2370 throw new NotSupportedException ("Add operation is not supported.");
2375 throw new NotSupportedException ("Clear operation is not supported.");
2378 bool IList.Contains (object item)
2380 if (!(item is ListViewItem))
2382 return Contains ((ListViewItem) item);
2385 int IList.IndexOf (object item)
2387 if (!(item is ListViewItem))
2389 return IndexOf ((ListViewItem) item);
2392 void IList.Insert (int index, object value)
2394 throw new NotSupportedException ("Insert operation is not supported.");
2397 void IList.Remove (object value)
2399 throw new NotSupportedException ("Remove operation is not supported.");
2402 void IList.RemoveAt (int index)
2404 throw new NotSupportedException ("RemoveAt operation is not supported.");
2407 public int IndexOf (ListViewItem item)
2409 if (!owner.CheckBoxes)
2411 return list.IndexOf (item);
2413 #endregion // Public Methods
2414 } // CheckedListViewItemCollection
2416 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2418 internal ArrayList list;
2419 private ListView owner;
2421 #region Public Constructor
2422 public ColumnHeaderCollection (ListView owner)
2424 list = new ArrayList ();
2427 #endregion // Public Constructor
2429 #region Public Properties
2432 get { return list.Count; }
2435 public bool IsReadOnly {
2436 get { return false; }
2439 public virtual ColumnHeader this [int index] {
2441 if (index < 0 || index >= list.Count)
2442 throw new ArgumentOutOfRangeException ("index");
2443 return (ColumnHeader) list [index];
2447 bool ICollection.IsSynchronized {
2448 get { return true; }
2451 object ICollection.SyncRoot {
2452 get { return this; }
2455 bool IList.IsFixedSize {
2456 get { return list.IsFixedSize; }
2459 object IList.this [int index] {
2460 get { return this [index]; }
2461 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2463 #endregion // Public Properties
2465 #region Public Methods
2466 public virtual int Add (ColumnHeader value)
2469 value.owner = this.owner;
2470 idx = list.Add (value);
2471 if (owner.IsHandleCreated) {
2472 owner.Redraw (true);
2477 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2479 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2480 this.Add (colHeader);
2484 public virtual void AddRange (ColumnHeader [] values)
2486 foreach (ColumnHeader colHeader in values) {
2487 colHeader.owner = this.owner;
2491 owner.Redraw (true);
2494 public virtual void Clear ()
2497 owner.Redraw (true);
2500 public bool Contains (ColumnHeader value)
2502 return list.Contains (value);
2505 public IEnumerator GetEnumerator ()
2507 return list.GetEnumerator ();
2510 void ICollection.CopyTo (Array dest, int index)
2512 list.CopyTo (dest, index);
2515 int IList.Add (object value)
2517 if (! (value is ColumnHeader)) {
2518 throw new ArgumentException ("Not of type ColumnHeader", "value");
2521 return this.Add ((ColumnHeader) value);
2524 bool IList.Contains (object value)
2526 if (! (value is ColumnHeader)) {
2527 throw new ArgumentException ("Not of type ColumnHeader", "value");
2530 return this.Contains ((ColumnHeader) value);
2533 int IList.IndexOf (object value)
2535 if (! (value is ColumnHeader)) {
2536 throw new ArgumentException ("Not of type ColumnHeader", "value");
2539 return this.IndexOf ((ColumnHeader) value);
2542 void IList.Insert (int index, object value)
2544 if (! (value is ColumnHeader)) {
2545 throw new ArgumentException ("Not of type ColumnHeader", "value");
2548 this.Insert (index, (ColumnHeader) value);
2551 void IList.Remove (object value)
2553 if (! (value is ColumnHeader)) {
2554 throw new ArgumentException ("Not of type ColumnHeader", "value");
2557 this.Remove ((ColumnHeader) value);
2560 public int IndexOf (ColumnHeader value)
2562 return list.IndexOf (value);
2565 public void Insert (int index, ColumnHeader value)
2567 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2568 // but it's really only greater.
2569 if (index < 0 || index > list.Count)
2570 throw new ArgumentOutOfRangeException ("index");
2572 value.owner = this.owner;
2573 list.Insert (index, value);
2574 owner.Redraw (true);
2577 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2579 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2580 this.Insert (index, colHeader);
2583 public virtual void Remove (ColumnHeader column)
2585 // TODO: Update Column internal index ?
2586 list.Remove (column);
2587 owner.Redraw (true);
2590 public virtual void RemoveAt (int index)
2592 if (index < 0 || index >= list.Count)
2593 throw new ArgumentOutOfRangeException ("index");
2595 // TODO: Update Column internal index ?
2596 list.RemoveAt (index);
2597 owner.Redraw (true);
2599 #endregion // Public Methods
2602 } // ColumnHeaderCollection
2604 public class ListViewItemCollection : IList, ICollection, IEnumerable
2606 internal ArrayList list;
2607 private readonly ListView owner;
2609 #region Public Constructor
2610 public ListViewItemCollection (ListView owner)
2612 list = new ArrayList ();
2615 #endregion // Public Constructor
2617 #region Public Properties
2620 get { return list.Count; }
2623 public bool IsReadOnly {
2624 get { return false; }
2627 public virtual ListViewItem this [int displayIndex] {
2629 if (displayIndex < 0 || displayIndex >= list.Count)
2630 throw new ArgumentOutOfRangeException ("displayIndex");
2631 return (ListViewItem) list [displayIndex];
2635 if (displayIndex < 0 || displayIndex >= list.Count)
2636 throw new ArgumentOutOfRangeException ("displayIndex");
2638 if (list.Contains (value))
2639 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2641 value.Owner = owner;
2642 list [displayIndex] = value;
2644 owner.Redraw (true);
2648 bool ICollection.IsSynchronized {
2649 get { return true; }
2652 object ICollection.SyncRoot {
2653 get { return this; }
2656 bool IList.IsFixedSize {
2657 get { return list.IsFixedSize; }
2660 object IList.this [int index] {
2661 get { return this [index]; }
2663 if (value is ListViewItem)
2664 this [index] = (ListViewItem) value;
2666 this [index] = new ListViewItem (value.ToString ());
2669 #endregion // Public Properties
2671 #region Public Methods
2672 public virtual ListViewItem Add (ListViewItem value)
2674 if (list.Contains (value))
2675 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2677 value.Owner = owner;
2681 owner.Redraw (true);
2685 public virtual ListViewItem Add (string text)
2687 ListViewItem item = new ListViewItem (text);
2688 return this.Add (item);
2691 public virtual ListViewItem Add (string text, int imageIndex)
2693 ListViewItem item = new ListViewItem (text, imageIndex);
2694 return this.Add (item);
2697 public void AddRange (ListViewItem [] values)
2700 owner.SelectedItems.list.Clear ();
2701 owner.CheckedItems.list.Clear ();
2703 foreach (ListViewItem item in values) {
2709 owner.Redraw (true);
2712 public virtual void Clear ()
2714 owner.SetFocusedItem (null);
2715 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2717 owner.SelectedItems.list.Clear ();
2718 owner.CheckedItems.list.Clear ();
2719 owner.Redraw (true);
2722 public bool Contains (ListViewItem item)
2724 return list.Contains (item);
2727 public void CopyTo (Array dest, int index)
2729 list.CopyTo (dest, index);
2732 public IEnumerator GetEnumerator ()
2734 return list.GetEnumerator ();
2737 int IList.Add (object item)
2742 if (item is ListViewItem) {
2743 li = (ListViewItem) item;
2744 if (list.Contains (li))
2745 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2748 li = new ListViewItem (item.ToString ());
2751 result = list.Add (li);
2752 owner.Redraw (true);
2757 bool IList.Contains (object item)
2759 return list.Contains (item);
2762 int IList.IndexOf (object item)
2764 return list.IndexOf (item);
2767 void IList.Insert (int index, object item)
2769 if (item is ListViewItem)
2770 this.Insert (index, (ListViewItem) item);
2772 this.Insert (index, item.ToString ());
2775 void IList.Remove (object item)
2777 Remove ((ListViewItem) item);
2780 public int IndexOf (ListViewItem item)
2782 return list.IndexOf (item);
2785 public ListViewItem Insert (int index, ListViewItem item)
2787 if (index < 0 || index > list.Count)
2788 throw new ArgumentOutOfRangeException ("index");
2790 if (list.Contains (item))
2791 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2794 list.Insert (index, item);
2795 owner.Redraw (true);
2799 public ListViewItem Insert (int index, string text)
2801 return this.Insert (index, new ListViewItem (text));
2804 public ListViewItem Insert (int index, string text, int imageIndex)
2806 return this.Insert (index, new ListViewItem (text, imageIndex));
2809 public virtual void Remove (ListViewItem item)
2811 if (!list.Contains (item))
2814 owner.SelectedItems.list.Remove (item);
2815 owner.CheckedItems.list.Remove (item);
2817 owner.Redraw (true);
2820 public virtual void RemoveAt (int index)
2822 ListViewItem item = this [index];
2823 list.RemoveAt (index);
2824 owner.SelectedItems.list.Remove (item);
2825 owner.CheckedItems.list.Remove (item);
2826 owner.Redraw (false);
2828 #endregion // Public Methods
2830 } // ListViewItemCollection
2832 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2834 private readonly ListView owner;
2836 #region Public Constructor
2837 public SelectedIndexCollection (ListView owner)
2841 #endregion // Public Constructor
2843 #region Public Properties
2847 return owner.SelectedItems.Count;
2851 public bool IsReadOnly {
2852 get { return true; }
2855 public int this [int index] {
2857 int [] indices = GetIndices ();
2858 if (index < 0 || index >= indices.Length)
2859 throw new ArgumentOutOfRangeException ("index");
2860 return indices [index];
2864 bool ICollection.IsSynchronized {
2865 get { return false; }
2868 object ICollection.SyncRoot {
2869 get { return this; }
2872 bool IList.IsFixedSize {
2873 get { return true; }
2876 object IList.this [int index] {
2877 get { return this [index]; }
2878 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2880 #endregion // Public Properties
2882 #region Public Methods
2883 public bool Contains (int selectedIndex)
2885 int [] indices = GetIndices ();
2886 for (int i = 0; i < indices.Length; i++) {
2887 if (indices [i] == selectedIndex)
2893 public void CopyTo (Array dest, int index)
2895 int [] indices = GetIndices ();
2896 Array.Copy (indices, 0, dest, index, indices.Length);
2899 public IEnumerator GetEnumerator ()
2901 int [] indices = GetIndices ();
2902 return indices.GetEnumerator ();
2905 int IList.Add (object value)
2907 throw new NotSupportedException ("Add operation is not supported.");
2912 throw new NotSupportedException ("Clear operation is not supported.");
2915 bool IList.Contains (object selectedIndex)
2917 if (!(selectedIndex is int))
2919 return Contains ((int) selectedIndex);
2922 int IList.IndexOf (object selectedIndex)
2924 if (!(selectedIndex is int))
2926 return IndexOf ((int) selectedIndex);
2929 void IList.Insert (int index, object value)
2931 throw new NotSupportedException ("Insert operation is not supported.");
2934 void IList.Remove (object value)
2936 throw new NotSupportedException ("Remove operation is not supported.");
2939 void IList.RemoveAt (int index)
2941 throw new NotSupportedException ("RemoveAt operation is not supported.");
2944 public int IndexOf (int selectedIndex)
2946 int [] indices = GetIndices ();
2947 for (int i = 0; i < indices.Length; i++) {
2948 if (indices [i] == selectedIndex)
2953 #endregion // Public Methods
2955 private int [] GetIndices ()
2957 int [] indices = new int [Count];
2958 for (int i = 0; i < owner.SelectedItems.Count; i++) {
2959 ListViewItem item = owner.SelectedItems [i];
2960 indices [i] = item.Index;
2965 } // SelectedIndexCollection
2967 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2969 internal ArrayList list;
2970 private readonly ListView owner;
2972 #region Public Constructor
2973 public SelectedListViewItemCollection (ListView owner)
2975 list = new ArrayList ();
2978 #endregion // Public Constructor
2980 #region Public Properties
2984 if (!owner.IsHandleCreated)
2990 public bool IsReadOnly {
2991 get { return true; }
2994 public ListViewItem this [int index] {
2996 if (index < 0 || index >= Count)
2997 throw new ArgumentOutOfRangeException ("index");
2998 return (ListViewItem) list [index];
3002 bool ICollection.IsSynchronized {
3003 get { return list.IsSynchronized; }
3006 object ICollection.SyncRoot {
3007 get { return this; }
3010 bool IList.IsFixedSize {
3011 get { return true; }
3014 object IList.this [int index] {
3015 get { return this [index]; }
3016 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3018 #endregion // Public Properties
3020 #region Public Methods
3021 public void Clear ()
3023 if (!owner.IsHandleCreated)
3026 ArrayList copy = (ArrayList) list.Clone ();
3027 for (int i = 0; i < copy.Count; i++)
3028 ((ListViewItem) copy [i]).Selected = false;
3033 public bool Contains (ListViewItem item)
3035 if (!owner.IsHandleCreated)
3037 return list.Contains (item);
3040 public void CopyTo (Array dest, int index)
3042 if (!owner.IsHandleCreated)
3044 list.CopyTo (dest, index);
3047 public IEnumerator GetEnumerator ()
3049 if (!owner.IsHandleCreated)
3050 return (new ListViewItem [0]).GetEnumerator ();
3051 return list.GetEnumerator ();
3054 int IList.Add (object value)
3056 throw new NotSupportedException ("Add operation is not supported.");
3059 bool IList.Contains (object item)
3061 if (!(item is ListViewItem))
3063 return Contains ((ListViewItem) item);
3066 int IList.IndexOf (object item)
3068 if (!(item is ListViewItem))
3070 return IndexOf ((ListViewItem) item);
3073 void IList.Insert (int index, object value)
3075 throw new NotSupportedException ("Insert operation is not supported.");
3078 void IList.Remove (object value)
3080 throw new NotSupportedException ("Remove operation is not supported.");
3083 void IList.RemoveAt (int index)
3085 throw new NotSupportedException ("RemoveAt operation is not supported.");
3088 public int IndexOf (ListViewItem item)
3090 if (!owner.IsHandleCreated)
3092 return list.IndexOf (item);
3094 #endregion // Public Methods
3096 } // SelectedListViewItemCollection
3098 #endregion // Subclasses