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 const int max_wrap_padding = 38;
748 // Sets the size of the biggest item text as per the view
749 private void CalcTextSize ()
751 // clear the old value
752 text_size = Size.Empty;
754 if (items.Count == 0)
757 text_size = BiggestItem (0);
759 if (view == View.LargeIcon && this.label_wrap) {
760 Size temp = Size.Empty;
761 if (this.check_boxes)
762 temp.Width += 2 * this.CheckBoxSize.Width;
763 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
764 temp.Width += icon_w + max_wrap_padding;
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.is_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.is_visible)
872 header_control.Width = ClientRectangle.Width;
873 item_control.Width = ClientRectangle.Width;
875 if (v_scroll.is_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);
1234 } else if (ctrl_pressed) {
1235 item.Selected = !item.Selected;
1236 selection_start = item;
1237 OnSelectedIndexChanged (EventArgs.Empty);
1239 SelectedItems.Clear ();
1240 item.Selected = true;
1241 selection_start = item;
1242 OnSelectedIndexChanged (EventArgs.Empty);
1246 internal override bool InternalPreProcessMessage (ref Message msg)
1248 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1249 Keys key_data = (Keys)msg.WParam.ToInt32();
1250 if (HandleNavKeys (key_data))
1253 return base.InternalPreProcessMessage (ref msg);
1256 bool HandleNavKeys (Keys key_data)
1258 if (Items.Count == 0 || !item_control.Visible)
1261 if (FocusedItem == null)
1262 SetFocusedItem (Items [0]);
1266 SelectIndex (Items.Count - 1);
1277 SelectIndex (GetAdjustedIndex (key_data));
1287 void SelectIndex (int index)
1293 UpdateMultiSelection (index);
1294 else if (!items [index].Selected) {
1295 items [index].Selected = true;
1296 OnSelectedIndexChanged (EventArgs.Empty);
1299 SetFocusedItem (items [index]);
1300 EnsureVisible (index);
1303 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1305 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1308 ke.Handled = KeySearchString (ke);
1311 internal class ItemControl : Control {
1314 ListViewItem clicked_item;
1315 ListViewItem last_clicked_item;
1316 bool hover_processed = false;
1317 bool checking = false;
1319 public ItemControl (ListView owner)
1322 DoubleClick += new EventHandler(ItemsDoubleClick);
1323 MouseDown += new MouseEventHandler(ItemsMouseDown);
1324 MouseMove += new MouseEventHandler(ItemsMouseMove);
1325 MouseHover += new EventHandler(ItemsMouseHover);
1326 MouseUp += new MouseEventHandler(ItemsMouseUp);
1329 void ItemsDoubleClick (object sender, EventArgs e)
1331 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1332 owner.ItemActivate (this, e);
1342 BoxSelect box_select_mode = BoxSelect.None;
1343 ArrayList prev_selection;
1344 Point box_select_start;
1346 Rectangle box_select_rect;
1347 internal Rectangle BoxSelectRectangle {
1348 get { return box_select_rect; }
1350 if (box_select_rect == value)
1353 InvalidateBoxSelectRect ();
1354 box_select_rect = value;
1355 InvalidateBoxSelectRect ();
1359 void InvalidateBoxSelectRect ()
1361 if (BoxSelectRectangle.Size.IsEmpty)
1364 Rectangle edge = BoxSelectRectangle;
1370 edge.Y = BoxSelectRectangle.Bottom - 1;
1372 edge.Y = BoxSelectRectangle.Y - 1;
1374 edge.Height = BoxSelectRectangle.Height + 2;
1376 edge.X = BoxSelectRectangle.Right - 1;
1380 private Rectangle CalculateBoxSelectRectangle (Point pt)
1382 int left = Math.Min (box_select_start.X, pt.X);
1383 int right = Math.Max (box_select_start.X, pt.X);
1384 int top = Math.Min (box_select_start.Y, pt.Y);
1385 int bottom = Math.Max (box_select_start.Y, pt.Y);
1386 return Rectangle.FromLTRB (left, top, right, bottom);
1389 ArrayList BoxSelectedItems {
1391 ArrayList result = new ArrayList ();
1392 foreach (ListViewItem item in owner.Items) {
1393 Rectangle r = item.Bounds;
1395 r.Y += r.Height / 4;
1398 if (BoxSelectRectangle.IntersectsWith (r))
1405 private bool PerformBoxSelection (Point pt)
1407 if (box_select_mode == BoxSelect.None)
1410 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1412 ArrayList box_items = BoxSelectedItems;
1416 switch (box_select_mode) {
1418 case BoxSelect.Normal:
1422 case BoxSelect.Control:
1423 items = new ArrayList ();
1424 foreach (ListViewItem item in prev_selection)
1425 if (!box_items.Contains (item))
1427 foreach (ListViewItem item in box_items)
1428 if (!prev_selection.Contains (item))
1432 case BoxSelect.Shift:
1434 foreach (ListViewItem item in box_items)
1435 prev_selection.Remove (item);
1436 foreach (ListViewItem item in prev_selection)
1441 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1445 owner.SelectItems (items);
1451 private void ToggleCheckState (ListViewItem item)
1453 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1454 item.Checked = !item.Checked;
1455 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1457 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1458 owner.OnItemCheck (ice);
1461 private void ItemsMouseDown (object sender, MouseEventArgs me)
1463 if (owner.items.Count == 0)
1466 Point pt = new Point (me.X, me.Y);
1467 foreach (ListViewItem item in owner.items) {
1468 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1472 ToggleCheckState (item);
1476 if (owner.View == View.Details && !owner.FullRowSelect) {
1477 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1478 clicked_item = item;
1482 if (item.Bounds.Contains (pt)) {
1483 clicked_item = item;
1490 if (clicked_item != null) {
1491 owner.SetFocusedItem (clicked_item);
1492 bool changed = !clicked_item.Selected;
1493 if (owner.MultiSelect)
1494 owner.UpdateMultiSelection (clicked_item.Index);
1496 clicked_item.Selected = true;
1499 owner.OnSelectedIndexChanged (EventArgs.Empty);
1501 // Raise double click if the item was clicked. On MS the
1502 // double click is only raised if you double click an item
1503 if (me.Clicks > 1) {
1504 owner.OnDoubleClick (EventArgs.Empty);
1505 if (owner.CheckBoxes)
1506 ToggleCheckState (clicked_item);
1507 } else if (me.Clicks == 1)
1508 owner.OnClick (EventArgs.Empty);
1510 if (owner.MultiSelect) {
1511 Keys mods = XplatUI.State.ModifierKeys;
1512 if ((mods & Keys.Shift) != 0)
1513 box_select_mode = BoxSelect.Shift;
1514 else if ((mods & Keys.Control) != 0)
1515 box_select_mode = BoxSelect.Control;
1517 box_select_mode = BoxSelect.Normal;
1518 box_select_start = pt;
1519 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1520 } else if (owner.selected_indices.Count > 0) {
1521 owner.SelectedItems.Clear ();
1522 owner.OnSelectedIndexChanged (EventArgs.Empty);
1527 private void ItemsMouseMove (object sender, MouseEventArgs me)
1529 if (PerformBoxSelection (new Point (me.X, me.Y)))
1532 if (owner.HoverSelection && hover_processed) {
1534 Point pt = PointToClient (Control.MousePosition);
1535 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1536 if (item == null || item.Selected)
1539 hover_processed = false;
1540 XplatUI.ResetMouseHover (Handle);
1545 private void ItemsMouseHover (object sender, EventArgs e)
1547 if (Capture || !owner.HoverSelection)
1550 hover_processed = true;
1551 Point pt = PointToClient (Control.MousePosition);
1552 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1557 item.Selected = true;
1558 owner.OnSelectedIndexChanged (new EventArgs ());
1561 private void ItemsMouseUp (object sender, MouseEventArgs me)
1564 if (owner.Items.Count == 0)
1567 Point pt = new Point (me.X, me.Y);
1569 Rectangle rect = Rectangle.Empty;
1570 if (clicked_item != null) {
1571 if (owner.view == View.Details && !owner.full_row_select)
1572 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1574 rect = clicked_item.Bounds;
1576 if (rect.Contains (pt)) {
1577 switch (owner.activation) {
1578 case ItemActivation.OneClick:
1579 owner.OnItemActivate (EventArgs.Empty);
1582 case ItemActivation.TwoClick:
1583 if (last_clicked_item == clicked_item) {
1584 owner.OnItemActivate (EventArgs.Empty);
1585 last_clicked_item = null;
1587 last_clicked_item = clicked_item;
1590 // DoubleClick activation is handled in another handler
1594 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1595 // Need this to clean up background clicks
1596 owner.SelectedItems.Clear ();
1597 owner.OnSelectedIndexChanged (EventArgs.Empty);
1600 clicked_item = null;
1601 box_select_start = Point.Empty;
1602 BoxSelectRectangle = Rectangle.Empty;
1603 prev_selection = null;
1604 box_select_mode = BoxSelect.None;
1608 internal override void OnPaintInternal (PaintEventArgs pe)
1610 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1613 internal override void OnGotFocusInternal (EventArgs e)
1619 internal override void OnPaintInternal (PaintEventArgs pe)
1624 CalculateScrollBars ();
1627 void FocusChanged (object o, EventArgs args)
1629 if (Items.Count == 0)
1632 if (FocusedItem == null)
1633 SetFocusedItem (Items [0]);
1635 item_control.Invalidate (FocusedItem.Bounds);
1638 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1640 if (Items.Count == 0)
1643 int lines = me.Delta / 120;
1650 case View.SmallIcon:
1651 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1653 case View.LargeIcon:
1654 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1657 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1662 private void ListView_SizeChanged (object sender, EventArgs e)
1664 CalculateListView (alignment);
1667 private void SetFocusedItem (ListViewItem item)
1669 if (focused_item != null)
1670 focused_item.Focused = false;
1673 item.Focused = true;
1675 focused_item = item;
1678 private void HorizontalScroller (object sender, EventArgs e)
1680 // Avoid unnecessary flickering, when button is
1681 // kept pressed at the end
1682 if (h_marker != h_scroll.Value) {
1684 int pixels = h_marker - h_scroll.Value;
1686 h_marker = h_scroll.Value;
1687 if (header_control.Visible)
1688 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1690 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1694 private void VerticalScroller (object sender, EventArgs e)
1696 // Avoid unnecessary flickering, when button is
1697 // kept pressed at the end
1698 if (v_marker != v_scroll.Value) {
1699 int pixels = v_marker - v_scroll.Value;
1700 Rectangle area = item_control.ClientRectangle;
1701 v_marker = v_scroll.Value;
1702 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1705 #endregion // Internal Methods Properties
1707 #region Protected Methods
1708 protected override void CreateHandle ()
1710 base.CreateHandle ();
1713 protected override void Dispose (bool disposing)
1716 h_scroll.Dispose ();
1717 v_scroll.Dispose ();
1719 large_image_list = null;
1720 small_image_list = null;
1721 state_image_list = null;
1724 base.Dispose (disposing);
1727 protected override bool IsInputKey (Keys keyData)
1744 return base.IsInputKey (keyData);
1747 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1749 if (AfterLabelEdit != null)
1750 AfterLabelEdit (this, e);
1753 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1755 if (BeforeLabelEdit != null)
1756 BeforeLabelEdit (this, e);
1759 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1761 if (ColumnClick != null)
1762 ColumnClick (this, e);
1765 protected override void OnEnabledChanged (EventArgs e)
1767 base.OnEnabledChanged (e);
1770 protected override void OnFontChanged (EventArgs e)
1772 base.OnFontChanged (e);
1776 protected override void OnHandleCreated (EventArgs e)
1778 base.OnHandleCreated (e);
1782 protected override void OnHandleDestroyed (EventArgs e)
1784 base.OnHandleDestroyed (e);
1787 protected virtual void OnItemActivate (EventArgs e)
1789 if (ItemActivate != null)
1790 ItemActivate (this, e);
1793 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1795 if (ItemCheck != null)
1796 ItemCheck (this, ice);
1799 protected virtual void OnItemDrag (ItemDragEventArgs e)
1801 if (ItemDrag != null)
1805 protected virtual void OnSelectedIndexChanged (EventArgs e)
1807 if (SelectedIndexChanged != null)
1808 SelectedIndexChanged (this, e);
1811 protected override void OnSystemColorsChanged (EventArgs e)
1813 base.OnSystemColorsChanged (e);
1816 protected void RealizeProperties ()
1821 protected void UpdateExtendedStyles ()
1826 protected override void WndProc (ref Message m)
1828 base.WndProc (ref m);
1830 #endregion // Protected Methods
1832 #region Public Instance Methods
1833 public void ArrangeIcons ()
1835 ArrangeIcons (this.alignment);
1838 public void ArrangeIcons (ListViewAlignment alignment)
1840 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1841 if (view == View.LargeIcon || view == View.SmallIcon) {
1842 this.CalculateListView (alignment);
1843 // we have done the calculations already
1844 this.Redraw (false);
1848 public void BeginUpdate ()
1850 // flag to avoid painting
1854 public void Clear ()
1857 items.Clear (); // Redraw (true) called here
1860 public void EndUpdate ()
1862 // flag to avoid painting
1865 // probably, now we need a redraw with recalculations
1869 public void EnsureVisible (int index)
1871 if (index < 0 || index >= items.Count || scrollable == false)
1874 Rectangle view_rect = item_control.ClientRectangle;
1875 Rectangle bounds = items [index].Bounds;
1877 if (view_rect.Contains (bounds))
1880 if (bounds.Left < 0)
1881 h_scroll.Value += bounds.Left;
1882 else if (bounds.Right > view_rect.Right)
1883 h_scroll.Value += (bounds.Right - view_rect.Right);
1886 v_scroll.Value += bounds.Top;
1887 else if (bounds.Bottom > view_rect.Bottom)
1888 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1891 public ListViewItem GetItemAt (int x, int y)
1893 foreach (ListViewItem item in items) {
1894 if (item.Bounds.Contains (x, y))
1900 public Rectangle GetItemRect (int index)
1902 return GetItemRect (index, ItemBoundsPortion.Entire);
1905 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1907 if (index < 0 || index >= items.Count)
1908 throw new IndexOutOfRangeException ("index");
1910 return items [index].GetBounds (portion);
1918 // we need this overload to reuse the logic for sorting, while allowing
1919 // redrawing to be done by caller or have it done by this method when
1920 // sorting is really performed
1922 // ListViewItemCollection's Add and AddRange methods call this overload
1923 // with redraw set to false, as they take care of redrawing themselves
1924 // (they even want to redraw the listview if no sort is performed, as
1925 // an item was added), while ListView.Sort () only wants to redraw if
1926 // sorting was actually performed
1927 private void Sort (bool redraw)
1929 if (!IsHandleCreated || item_sorter == null) {
1933 items.list.Sort (item_sorter);
1938 public override string ToString ()
1940 int count = this.Items.Count;
1943 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1945 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1947 #endregion // Public Instance Methods
1952 class HeaderControl : Control {
1955 bool column_resize_active = false;
1956 ColumnHeader resize_column;
1957 ColumnHeader clicked_column;
1958 ColumnHeader drag_column;
1960 int drag_to_index = -1;
1962 public HeaderControl (ListView owner)
1965 MouseDown += new MouseEventHandler (HeaderMouseDown);
1966 MouseMove += new MouseEventHandler (HeaderMouseMove);
1967 MouseUp += new MouseEventHandler (HeaderMouseUp);
1970 private ColumnHeader ColumnAtX (int x)
1972 Point pt = new Point (x, 0);
1973 ColumnHeader result = null;
1974 foreach (ColumnHeader col in owner.Columns) {
1975 if (col.Rect.Contains (pt)) {
1983 private int GetReorderedIndex (ColumnHeader col)
1985 if (owner.reordered_column_indices == null)
1988 for (int i = 0; i < owner.Columns.Count; i++)
1989 if (owner.reordered_column_indices [i] == col.Index)
1991 throw new Exception ("Column index missing from reordered array");
1994 private void HeaderMouseDown (object sender, MouseEventArgs me)
1996 if (resize_column != null) {
1997 column_resize_active = true;
2002 clicked_column = ColumnAtX (me.X + owner.h_marker);
2004 if (clicked_column != null) {
2006 if (owner.AllowColumnReorder) {
2008 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2009 drag_column.column_rect = clicked_column.Rect;
2010 drag_to_index = GetReorderedIndex (clicked_column);
2012 clicked_column.pressed = true;
2013 Rectangle bounds = clicked_column.Rect;
2014 bounds.X -= owner.h_marker;
2015 Invalidate (bounds);
2020 private void HeaderMouseMove (object sender, MouseEventArgs me)
2022 Point pt = new Point (me.X + owner.h_marker, me.Y);
2024 if (column_resize_active) {
2025 resize_column.Width = pt.X - resize_column.X;
2026 if (resize_column.Width < 0)
2027 resize_column.Width = 0;
2031 resize_column = null;
2033 if (clicked_column != null) {
2034 if (owner.AllowColumnReorder) {
2037 r = drag_column.column_rect;
2038 r.X = clicked_column.Rect.X + me.X - drag_x;
2039 drag_column.column_rect = r;
2041 int x = me.X + owner.h_marker;
2042 ColumnHeader over = ColumnAtX (x);
2044 drag_to_index = owner.Columns.Count;
2045 else if (x < over.X + over.Width / 2)
2046 drag_to_index = GetReorderedIndex (over);
2048 drag_to_index = GetReorderedIndex (over) + 1;
2051 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2052 bool pressed = clicked_column.pressed;
2053 clicked_column.pressed = over == clicked_column;
2054 if (clicked_column.pressed ^ pressed) {
2055 Rectangle bounds = clicked_column.Rect;
2056 bounds.X -= owner.h_marker;
2057 Invalidate (bounds);
2063 for (int i = 0; i < owner.Columns.Count; i++) {
2064 Rectangle zone = owner.Columns [i].Rect;
2065 zone.X = zone.Right - 5;
2067 if (zone.Contains (pt)) {
2068 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2070 resize_column = owner.Columns [i];
2075 if (resize_column == null)
2076 Cursor = Cursors.Default;
2078 Cursor = Cursors.VSplit;
2081 void HeaderMouseUp (object sender, MouseEventArgs me)
2085 if (column_resize_active) {
2086 column_resize_active = false;
2087 resize_column = null;
2088 Cursor = Cursors.Default;
2092 if (clicked_column != null && clicked_column.pressed) {
2093 clicked_column.pressed = false;
2094 Rectangle bounds = clicked_column.Rect;
2095 bounds.X -= owner.h_marker;
2096 Invalidate (bounds);
2097 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2100 if (drag_column != null && owner.AllowColumnReorder) {
2102 if (drag_to_index > GetReorderedIndex (clicked_column))
2104 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2105 owner.ReorderColumn (clicked_column, drag_to_index);
2110 clicked_column = null;
2113 internal override void OnPaintInternal (PaintEventArgs pe)
2118 Theme theme = ThemeEngine.Current;
2119 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2121 if (drag_column == null)
2125 if (drag_to_index == owner.Columns.Count)
2126 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2128 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2129 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2132 protected override void WndProc (ref Message m)
2134 switch ((Msg)m.Msg) {
2135 case Msg.WM_SETFOCUS:
2139 base.WndProc (ref m);
2145 private class ItemComparer : IComparer {
2146 readonly SortOrder sort_order;
2148 public ItemComparer (SortOrder sortOrder)
2150 sort_order = sortOrder;
2153 public int Compare (object x, object y)
2155 ListViewItem item_x = x as ListViewItem;
2156 ListViewItem item_y = y as ListViewItem;
2157 if (sort_order == SortOrder.Ascending)
2158 return String.Compare (item_x.Text, item_y.Text);
2160 return String.Compare (item_y.Text, item_x.Text);
2164 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2166 private readonly ListView owner;
2168 #region Public Constructor
2169 public CheckedIndexCollection (ListView owner)
2173 #endregion // Public Constructor
2175 #region Public Properties
2178 get { return owner.CheckedItems.Count; }
2181 public bool IsReadOnly {
2182 get { return true; }
2185 public int this [int index] {
2187 int [] indices = GetIndices ();
2188 if (index < 0 || index >= indices.Length)
2189 throw new ArgumentOutOfRangeException ("index");
2190 return indices [index];
2194 bool ICollection.IsSynchronized {
2195 get { return false; }
2198 object ICollection.SyncRoot {
2199 get { return this; }
2202 bool IList.IsFixedSize {
2203 get { return true; }
2206 object IList.this [int index] {
2207 get { return this [index]; }
2208 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2210 #endregion // Public Properties
2212 #region Public Methods
2213 public bool Contains (int checkedIndex)
2215 int [] indices = GetIndices ();
2216 for (int i = 0; i < indices.Length; i++) {
2217 if (indices [i] == checkedIndex)
2223 public IEnumerator GetEnumerator ()
2225 int [] indices = GetIndices ();
2226 return indices.GetEnumerator ();
2229 void ICollection.CopyTo (Array dest, int index)
2231 int [] indices = GetIndices ();
2232 Array.Copy (indices, 0, dest, index, indices.Length);
2235 int IList.Add (object value)
2237 throw new NotSupportedException ("Add operation is not supported.");
2242 throw new NotSupportedException ("Clear operation is not supported.");
2245 bool IList.Contains (object checkedIndex)
2247 if (!(checkedIndex is int))
2249 return Contains ((int) checkedIndex);
2252 int IList.IndexOf (object checkedIndex)
2254 if (!(checkedIndex is int))
2256 return IndexOf ((int) checkedIndex);
2259 void IList.Insert (int index, object value)
2261 throw new NotSupportedException ("Insert operation is not supported.");
2264 void IList.Remove (object value)
2266 throw new NotSupportedException ("Remove operation is not supported.");
2269 void IList.RemoveAt (int index)
2271 throw new NotSupportedException ("RemoveAt operation is not supported.");
2274 public int IndexOf (int checkedIndex)
2276 int [] indices = GetIndices ();
2277 for (int i = 0; i < indices.Length; i++) {
2278 if (indices [i] == checkedIndex)
2283 #endregion // Public Methods
2285 private int [] GetIndices ()
2287 int [] indices = new int [Count];
2288 for (int i = 0; i < owner.CheckedItems.Count; i++) {
2289 ListViewItem item = owner.CheckedItems [i];
2290 indices [i] = item.Index;
2294 } // CheckedIndexCollection
2296 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2298 internal readonly ArrayList list;
2299 private readonly ListView owner;
2301 #region Public Constructor
2302 public CheckedListViewItemCollection (ListView owner)
2304 list = new ArrayList ();
2307 #endregion // Public Constructor
2309 #region Public Properties
2313 if (!owner.CheckBoxes)
2319 public bool IsReadOnly {
2320 get { return true; }
2323 public ListViewItem this [int index] {
2325 if (index < 0 || index >= Count)
2326 throw new ArgumentOutOfRangeException ("index");
2327 return (ListViewItem) list [index];
2331 bool ICollection.IsSynchronized {
2332 get { return list.IsSynchronized; }
2335 object ICollection.SyncRoot {
2336 get { return this; }
2339 bool IList.IsFixedSize {
2340 get { return true; }
2343 object IList.this [int index] {
2344 get { return this [index]; }
2345 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2347 #endregion // Public Properties
2349 #region Public Methods
2350 public bool Contains (ListViewItem item)
2352 if (!owner.CheckBoxes)
2354 return list.Contains (item);
2357 public void CopyTo (Array dest, int index)
2359 if (!owner.CheckBoxes)
2361 list.CopyTo (dest, index);
2364 public IEnumerator GetEnumerator ()
2366 if (!owner.CheckBoxes)
2367 return (new ListViewItem [0]).GetEnumerator ();
2368 return list.GetEnumerator ();
2371 int IList.Add (object value)
2373 throw new NotSupportedException ("Add operation is not supported.");
2378 throw new NotSupportedException ("Clear operation is not supported.");
2381 bool IList.Contains (object item)
2383 if (!(item is ListViewItem))
2385 return Contains ((ListViewItem) item);
2388 int IList.IndexOf (object item)
2390 if (!(item is ListViewItem))
2392 return IndexOf ((ListViewItem) item);
2395 void IList.Insert (int index, object value)
2397 throw new NotSupportedException ("Insert operation is not supported.");
2400 void IList.Remove (object value)
2402 throw new NotSupportedException ("Remove operation is not supported.");
2405 void IList.RemoveAt (int index)
2407 throw new NotSupportedException ("RemoveAt operation is not supported.");
2410 public int IndexOf (ListViewItem item)
2412 if (!owner.CheckBoxes)
2414 return list.IndexOf (item);
2416 #endregion // Public Methods
2417 } // CheckedListViewItemCollection
2419 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2421 internal ArrayList list;
2422 private ListView owner;
2424 #region Public Constructor
2425 public ColumnHeaderCollection (ListView owner)
2427 list = new ArrayList ();
2430 #endregion // Public Constructor
2432 #region Public Properties
2435 get { return list.Count; }
2438 public bool IsReadOnly {
2439 get { return false; }
2442 public virtual ColumnHeader this [int index] {
2444 if (index < 0 || index >= list.Count)
2445 throw new ArgumentOutOfRangeException ("index");
2446 return (ColumnHeader) list [index];
2450 bool ICollection.IsSynchronized {
2451 get { return true; }
2454 object ICollection.SyncRoot {
2455 get { return this; }
2458 bool IList.IsFixedSize {
2459 get { return list.IsFixedSize; }
2462 object IList.this [int index] {
2463 get { return this [index]; }
2464 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2466 #endregion // Public Properties
2468 #region Public Methods
2469 public virtual int Add (ColumnHeader value)
2472 value.owner = this.owner;
2473 idx = list.Add (value);
2474 if (owner.IsHandleCreated) {
2475 owner.Redraw (true);
2480 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2482 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2483 this.Add (colHeader);
2487 public virtual void AddRange (ColumnHeader [] values)
2489 foreach (ColumnHeader colHeader in values) {
2490 colHeader.owner = this.owner;
2494 owner.Redraw (true);
2497 public virtual void Clear ()
2500 owner.Redraw (true);
2503 public bool Contains (ColumnHeader value)
2505 return list.Contains (value);
2508 public IEnumerator GetEnumerator ()
2510 return list.GetEnumerator ();
2513 void ICollection.CopyTo (Array dest, int index)
2515 list.CopyTo (dest, index);
2518 int IList.Add (object value)
2520 if (! (value is ColumnHeader)) {
2521 throw new ArgumentException ("Not of type ColumnHeader", "value");
2524 return this.Add ((ColumnHeader) value);
2527 bool IList.Contains (object value)
2529 if (! (value is ColumnHeader)) {
2530 throw new ArgumentException ("Not of type ColumnHeader", "value");
2533 return this.Contains ((ColumnHeader) value);
2536 int IList.IndexOf (object value)
2538 if (! (value is ColumnHeader)) {
2539 throw new ArgumentException ("Not of type ColumnHeader", "value");
2542 return this.IndexOf ((ColumnHeader) value);
2545 void IList.Insert (int index, object value)
2547 if (! (value is ColumnHeader)) {
2548 throw new ArgumentException ("Not of type ColumnHeader", "value");
2551 this.Insert (index, (ColumnHeader) value);
2554 void IList.Remove (object value)
2556 if (! (value is ColumnHeader)) {
2557 throw new ArgumentException ("Not of type ColumnHeader", "value");
2560 this.Remove ((ColumnHeader) value);
2563 public int IndexOf (ColumnHeader value)
2565 return list.IndexOf (value);
2568 public void Insert (int index, ColumnHeader value)
2570 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2571 // but it's really only greater.
2572 if (index < 0 || index > list.Count)
2573 throw new ArgumentOutOfRangeException ("index");
2575 value.owner = this.owner;
2576 list.Insert (index, value);
2577 owner.Redraw (true);
2580 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2582 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2583 this.Insert (index, colHeader);
2586 public virtual void Remove (ColumnHeader column)
2588 // TODO: Update Column internal index ?
2589 list.Remove (column);
2590 owner.Redraw (true);
2593 public virtual void RemoveAt (int index)
2595 if (index < 0 || index >= list.Count)
2596 throw new ArgumentOutOfRangeException ("index");
2598 // TODO: Update Column internal index ?
2599 list.RemoveAt (index);
2600 owner.Redraw (true);
2602 #endregion // Public Methods
2605 } // ColumnHeaderCollection
2607 public class ListViewItemCollection : IList, ICollection, IEnumerable
2609 internal ArrayList list;
2610 private readonly ListView owner;
2612 #region Public Constructor
2613 public ListViewItemCollection (ListView owner)
2615 list = new ArrayList ();
2618 #endregion // Public Constructor
2620 #region Public Properties
2623 get { return list.Count; }
2626 public bool IsReadOnly {
2627 get { return false; }
2630 public virtual ListViewItem this [int displayIndex] {
2632 if (displayIndex < 0 || displayIndex >= list.Count)
2633 throw new ArgumentOutOfRangeException ("displayIndex");
2634 return (ListViewItem) list [displayIndex];
2638 if (displayIndex < 0 || displayIndex >= list.Count)
2639 throw new ArgumentOutOfRangeException ("displayIndex");
2641 if (list.Contains (value))
2642 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2644 value.Owner = owner;
2645 list [displayIndex] = value;
2647 owner.Redraw (true);
2651 bool ICollection.IsSynchronized {
2652 get { return true; }
2655 object ICollection.SyncRoot {
2656 get { return this; }
2659 bool IList.IsFixedSize {
2660 get { return list.IsFixedSize; }
2663 object IList.this [int index] {
2664 get { return this [index]; }
2666 if (value is ListViewItem)
2667 this [index] = (ListViewItem) value;
2669 this [index] = new ListViewItem (value.ToString ());
2672 #endregion // Public Properties
2674 #region Public Methods
2675 public virtual ListViewItem Add (ListViewItem value)
2677 if (list.Contains (value))
2678 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2680 value.Owner = owner;
2684 owner.Redraw (true);
2688 public virtual ListViewItem Add (string text)
2690 ListViewItem item = new ListViewItem (text);
2691 return this.Add (item);
2694 public virtual ListViewItem Add (string text, int imageIndex)
2696 ListViewItem item = new ListViewItem (text, imageIndex);
2697 return this.Add (item);
2700 public void AddRange (ListViewItem [] values)
2703 owner.SelectedItems.list.Clear ();
2704 owner.CheckedItems.list.Clear ();
2706 foreach (ListViewItem item in values) {
2712 owner.Redraw (true);
2715 public virtual void Clear ()
2717 owner.SetFocusedItem (null);
2718 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2720 owner.SelectedItems.list.Clear ();
2721 owner.CheckedItems.list.Clear ();
2722 owner.Redraw (true);
2725 public bool Contains (ListViewItem item)
2727 return list.Contains (item);
2730 public void CopyTo (Array dest, int index)
2732 list.CopyTo (dest, index);
2735 public IEnumerator GetEnumerator ()
2737 return list.GetEnumerator ();
2740 int IList.Add (object item)
2745 if (item is ListViewItem) {
2746 li = (ListViewItem) item;
2747 if (list.Contains (li))
2748 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2751 li = new ListViewItem (item.ToString ());
2754 result = list.Add (li);
2755 owner.Redraw (true);
2760 bool IList.Contains (object item)
2762 return list.Contains (item);
2765 int IList.IndexOf (object item)
2767 return list.IndexOf (item);
2770 void IList.Insert (int index, object item)
2772 if (item is ListViewItem)
2773 this.Insert (index, (ListViewItem) item);
2775 this.Insert (index, item.ToString ());
2778 void IList.Remove (object item)
2780 Remove ((ListViewItem) item);
2783 public int IndexOf (ListViewItem item)
2785 return list.IndexOf (item);
2788 public ListViewItem Insert (int index, ListViewItem item)
2790 if (index < 0 || index > list.Count)
2791 throw new ArgumentOutOfRangeException ("index");
2793 if (list.Contains (item))
2794 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2797 list.Insert (index, item);
2798 owner.Redraw (true);
2802 public ListViewItem Insert (int index, string text)
2804 return this.Insert (index, new ListViewItem (text));
2807 public ListViewItem Insert (int index, string text, int imageIndex)
2809 return this.Insert (index, new ListViewItem (text, imageIndex));
2812 public virtual void Remove (ListViewItem item)
2814 if (!list.Contains (item))
2817 owner.SelectedItems.list.Remove (item);
2818 owner.CheckedItems.list.Remove (item);
2820 owner.Redraw (true);
2823 public virtual void RemoveAt (int index)
2825 ListViewItem item = this [index];
2826 list.RemoveAt (index);
2827 owner.SelectedItems.list.Remove (item);
2828 owner.CheckedItems.list.Remove (item);
2829 owner.Redraw (false);
2831 #endregion // Public Methods
2833 } // ListViewItemCollection
2835 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2837 private readonly ListView owner;
2839 #region Public Constructor
2840 public SelectedIndexCollection (ListView owner)
2844 #endregion // Public Constructor
2846 #region Public Properties
2850 return owner.SelectedItems.Count;
2854 public bool IsReadOnly {
2855 get { return true; }
2858 public int this [int index] {
2860 int [] indices = GetIndices ();
2861 if (index < 0 || index >= indices.Length)
2862 throw new ArgumentOutOfRangeException ("index");
2863 return indices [index];
2867 bool ICollection.IsSynchronized {
2868 get { return false; }
2871 object ICollection.SyncRoot {
2872 get { return this; }
2875 bool IList.IsFixedSize {
2876 get { return true; }
2879 object IList.this [int index] {
2880 get { return this [index]; }
2881 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2883 #endregion // Public Properties
2885 #region Public Methods
2886 public bool Contains (int selectedIndex)
2888 int [] indices = GetIndices ();
2889 for (int i = 0; i < indices.Length; i++) {
2890 if (indices [i] == selectedIndex)
2896 public void CopyTo (Array dest, int index)
2898 int [] indices = GetIndices ();
2899 Array.Copy (indices, 0, dest, index, indices.Length);
2902 public IEnumerator GetEnumerator ()
2904 int [] indices = GetIndices ();
2905 return indices.GetEnumerator ();
2908 int IList.Add (object value)
2910 throw new NotSupportedException ("Add operation is not supported.");
2915 throw new NotSupportedException ("Clear operation is not supported.");
2918 bool IList.Contains (object selectedIndex)
2920 if (!(selectedIndex is int))
2922 return Contains ((int) selectedIndex);
2925 int IList.IndexOf (object selectedIndex)
2927 if (!(selectedIndex is int))
2929 return IndexOf ((int) selectedIndex);
2932 void IList.Insert (int index, object value)
2934 throw new NotSupportedException ("Insert operation is not supported.");
2937 void IList.Remove (object value)
2939 throw new NotSupportedException ("Remove operation is not supported.");
2942 void IList.RemoveAt (int index)
2944 throw new NotSupportedException ("RemoveAt operation is not supported.");
2947 public int IndexOf (int selectedIndex)
2949 int [] indices = GetIndices ();
2950 for (int i = 0; i < indices.Length; i++) {
2951 if (indices [i] == selectedIndex)
2956 #endregion // Public Methods
2958 private int [] GetIndices ()
2960 int [] indices = new int [Count];
2961 for (int i = 0; i < owner.SelectedItems.Count; i++) {
2962 ListViewItem item = owner.SelectedItems [i];
2963 indices [i] = item.Index;
2968 } // SelectedIndexCollection
2970 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2972 internal ArrayList list;
2973 private readonly ListView owner;
2975 #region Public Constructor
2976 public SelectedListViewItemCollection (ListView owner)
2978 list = new ArrayList ();
2981 #endregion // Public Constructor
2983 #region Public Properties
2987 if (!owner.IsHandleCreated)
2993 public bool IsReadOnly {
2994 get { return true; }
2997 public ListViewItem this [int index] {
2999 if (index < 0 || index >= Count)
3000 throw new ArgumentOutOfRangeException ("index");
3001 return (ListViewItem) list [index];
3005 bool ICollection.IsSynchronized {
3006 get { return list.IsSynchronized; }
3009 object ICollection.SyncRoot {
3010 get { return this; }
3013 bool IList.IsFixedSize {
3014 get { return true; }
3017 object IList.this [int index] {
3018 get { return this [index]; }
3019 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3021 #endregion // Public Properties
3023 #region Public Methods
3024 public void Clear ()
3026 if (!owner.IsHandleCreated)
3029 ArrayList copy = (ArrayList) list.Clone ();
3030 for (int i = 0; i < copy.Count; i++)
3031 ((ListViewItem) copy [i]).Selected = false;
3036 public bool Contains (ListViewItem item)
3038 if (!owner.IsHandleCreated)
3040 return list.Contains (item);
3043 public void CopyTo (Array dest, int index)
3045 if (!owner.IsHandleCreated)
3047 list.CopyTo (dest, index);
3050 public IEnumerator GetEnumerator ()
3052 if (!owner.IsHandleCreated)
3053 return (new ListViewItem [0]).GetEnumerator ();
3054 return list.GetEnumerator ();
3057 int IList.Add (object value)
3059 throw new NotSupportedException ("Add operation is not supported.");
3062 bool IList.Contains (object item)
3064 if (!(item is ListViewItem))
3066 return Contains ((ListViewItem) item);
3069 int IList.IndexOf (object item)
3071 if (!(item is ListViewItem))
3073 return IndexOf ((ListViewItem) item);
3076 void IList.Insert (int index, object value)
3078 throw new NotSupportedException ("Insert operation is not supported.");
3081 void IList.Remove (object value)
3083 throw new NotSupportedException ("Remove operation is not supported.");
3086 void IList.RemoveAt (int index)
3088 throw new NotSupportedException ("RemoveAt operation is not supported.");
3091 public int IndexOf (ListViewItem item)
3093 if (!owner.IsHandleCreated)
3095 return list.IndexOf (item);
3097 #endregion // Public Methods
3099 } // SelectedListViewItemCollection
3101 #endregion // Subclasses