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;
191 bool CanMultiselect {
195 else if (multiselect && (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0)
202 #endregion // Private Internal Properties
204 #region Protected Properties
205 protected override CreateParams CreateParams {
206 get { return base.CreateParams; }
209 protected override Size DefaultSize {
210 get { return ThemeEngine.Current.ListViewDefaultSize; }
212 #endregion // Protected Properties
214 #region Public Instance Properties
215 [DefaultValue (ItemActivation.Standard)]
216 public ItemActivation Activation {
217 get { return activation; }
219 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
220 value != ItemActivation.TwoClick) {
221 throw new InvalidEnumArgumentException (string.Format
222 ("Enum argument value '{0}' is not valid for Activation", value));
229 [DefaultValue (ListViewAlignment.Top)]
231 public ListViewAlignment Alignment {
232 get { return alignment; }
234 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
235 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
236 throw new InvalidEnumArgumentException (string.Format
237 ("Enum argument value '{0}' is not valid for Alignment", value));
240 if (this.alignment != value) {
242 // alignment does not matter in Details/List views
243 if (this.view == View.LargeIcon ||
244 this.View == View.SmallIcon)
250 [DefaultValue (false)]
251 public bool AllowColumnReorder {
252 get { return allow_column_reorder; }
253 set { allow_column_reorder = value; }
256 [DefaultValue (true)]
257 public bool AutoArrange {
258 get { return auto_arrange; }
260 if (auto_arrange != value) {
261 auto_arrange = value;
262 // autoarrange does not matter in Details/List views
263 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
269 public override Color BackColor {
271 if (background_color.IsEmpty)
272 return ThemeEngine.Current.ColorWindow;
274 return background_color;
276 set { background_color = value; }
280 [EditorBrowsable (EditorBrowsableState.Never)]
281 public override Image BackgroundImage {
282 get { return background_image; }
284 if (value == background_image)
287 background_image = value;
288 OnBackgroundImageChanged (EventArgs.Empty);
292 [DefaultValue (BorderStyle.Fixed3D)]
294 public BorderStyle BorderStyle {
295 get { return InternalBorderStyle; }
296 set { InternalBorderStyle = value; }
299 [DefaultValue (false)]
300 public bool CheckBoxes {
301 get { return check_boxes; }
303 if (check_boxes != value) {
305 if (value && View == View.Tile)
306 throw new NotSupportedException ("CheckBoxes are not"
307 + " supported in Tile view. Choose a different"
308 + " view or set CheckBoxes to false.");
318 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
319 public CheckedIndexCollection CheckedIndices {
320 get { return checked_indices; }
324 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
325 public CheckedListViewItemCollection CheckedItems {
326 get { return checked_items; }
329 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
331 [MergableProperty (false)]
332 public ColumnHeaderCollection Columns {
333 get { return columns; }
337 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
338 public ListViewItem FocusedItem {
344 public override Color ForeColor {
346 if (foreground_color.IsEmpty)
347 return ThemeEngine.Current.ColorWindowText;
349 return foreground_color;
351 set { foreground_color = value; }
354 [DefaultValue (false)]
355 public bool FullRowSelect {
356 get { return full_row_select; }
357 set { full_row_select = value; }
360 [DefaultValue (false)]
361 public bool GridLines {
362 get { return grid_lines; }
364 if (grid_lines != value) {
371 [DefaultValue (ColumnHeaderStyle.Clickable)]
372 public ColumnHeaderStyle HeaderStyle {
373 get { return header_style; }
375 if (header_style == value)
379 case ColumnHeaderStyle.Clickable:
380 case ColumnHeaderStyle.Nonclickable:
381 case ColumnHeaderStyle.None:
384 throw new InvalidEnumArgumentException (string.Format
385 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
388 header_style = value;
389 if (view == View.Details)
394 [DefaultValue (true)]
395 public bool HideSelection {
396 get { return hide_selection; }
398 if (hide_selection != value) {
399 hide_selection = value;
405 [DefaultValue (false)]
406 public bool HoverSelection {
407 get { return hover_selection; }
408 set { hover_selection = value; }
411 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
413 [MergableProperty (false)]
414 public ListViewItemCollection Items {
415 get { return items; }
418 [DefaultValue (false)]
419 public bool LabelEdit {
420 get { return label_edit; }
421 set { label_edit = value; }
424 [DefaultValue (true)]
426 public bool LabelWrap {
427 get { return label_wrap; }
429 if (label_wrap != value) {
436 [DefaultValue (null)]
437 public ImageList LargeImageList {
438 get { return large_image_list; }
440 large_image_list = value;
446 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
447 public IComparer ListViewItemSorter {
449 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
454 if (item_sorter != value) {
461 [DefaultValue (true)]
462 public bool MultiSelect {
463 get { return multiselect; }
464 set { multiselect = value; }
467 [DefaultValue (true)]
468 public bool Scrollable {
469 get { return scrollable; }
471 if (scrollable != value) {
479 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
480 public SelectedIndexCollection SelectedIndices {
481 get { return selected_indices; }
485 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
486 public SelectedListViewItemCollection SelectedItems {
487 get { return selected_items; }
491 [MonoTODO("Implement")]
492 public bool ShowGroups {
502 [DefaultValue (null)]
503 public ImageList SmallImageList {
504 get { return small_image_list; }
506 small_image_list = value;
511 [DefaultValue (SortOrder.None)]
512 public SortOrder Sorting {
513 get { return sort_order; }
515 if (!Enum.IsDefined (typeof (SortOrder), value)) {
516 throw new InvalidEnumArgumentException ("value", (int) value,
520 if (sort_order == value)
525 if (value == SortOrder.None) {
526 if (item_sorter != null) {
527 // ListViewItemSorter should never be reset for SmallIcon
528 // and LargeIcon view
529 if (View != View.SmallIcon && View != View.LargeIcon)
533 // in .NET 1.1, only internal IComparer would be
535 if (item_sorter is ItemComparer)
541 if (item_sorter == null)
542 item_sorter = new ItemComparer (value);
543 if (item_sorter is ItemComparer) {
545 item_sorter = new ItemComparer (value);
547 // in .NET 1.1, the sort order is not updated for
548 // SmallIcon and LargeIcon views if no custom IComparer
550 if (View != View.SmallIcon && View != View.LargeIcon)
551 item_sorter = new ItemComparer (value);
559 [DefaultValue (null)]
560 public ImageList StateImageList {
561 get { return state_image_list; }
563 state_image_list = value;
570 [EditorBrowsable (EditorBrowsableState.Never)]
571 public override string Text {
580 OnTextChanged (EventArgs.Empty);
585 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
586 public ListViewItem TopItem {
589 if (this.items.Count == 0)
591 // if contents are not scrolled
592 // it is the first item
593 else if (h_marker == 0 && v_marker == 0)
594 return this.items [0];
595 // do a hit test for the scrolled position
597 foreach (ListViewItem item in this.items) {
598 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
607 [MonoTODO("Implement")]
608 public bool UseCompatibleStateImageBehavior {
618 [DefaultValue (View.LargeIcon)]
622 if (!Enum.IsDefined (typeof (View), value))
623 throw new InvalidEnumArgumentException ("value", (int) value,
628 if (CheckBoxes && value == View.Tile)
629 throw new NotSupportedException ("CheckBoxes are not"
630 + " supported in Tile view. Choose a different"
631 + " view or set CheckBoxes to false.");
634 h_scroll.Value = v_scroll.Value = 0;
640 #endregion // Public Instance Properties
642 #region Internal Methods Properties
644 internal int FirstVisibleIndex {
647 if (this.items.Count == 0)
650 if (h_marker == 0 && v_marker == 0)
653 foreach (ListViewItem item in this.items) {
654 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
663 internal int LastVisibleIndex {
665 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
666 if (View == View.List || Alignment == ListViewAlignment.Left) {
667 if (Items[i].Bounds.X > ClientRectangle.Right)
670 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
675 return Items.Count - 1;
679 internal int TotalWidth {
680 get { return Math.Max (this.Width, this.layout_wd); }
683 internal int TotalHeight {
684 get { return Math.Max (this.Height, this.layout_ht); }
687 internal void Redraw (bool recalculate)
689 // Avoid calculations when control is being updated
694 CalculateListView (this.alignment);
699 internal Size GetChildColumnSize (int index)
701 Size ret_size = Size.Empty;
702 ColumnHeader col = this.columns [index];
704 if (col.Width == -2) { // autosize = max(items, columnheader)
705 Size size = Size.Ceiling (this.DeviceContext.MeasureString
706 (col.Text, this.Font));
707 ret_size = BiggestItem (index);
708 if (size.Width > ret_size.Width)
711 else { // -1 and all the values < -2 are put under one category
712 ret_size = BiggestItem (index);
713 // fall back to empty columns' width if no subitem is available for a column
714 if (ret_size.IsEmpty) {
715 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
716 if (col.Text.Length > 0)
717 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
718 (col.Text, this.Font)).Height;
720 ret_size.Height = this.Font.Height;
724 // adjust the size for icon and checkbox for 0th column
726 ret_size.Width += (this.CheckBoxSize.Width + 4);
727 if (this.small_image_list != null)
728 ret_size.Width += this.small_image_list.ImageSize.Width;
733 // Returns the size of biggest item text in a column.
734 private Size BiggestItem (int col)
736 Size temp = Size.Empty;
737 Size ret_size = Size.Empty;
739 // 0th column holds the item text, we check the size of
740 // the various subitems falling in that column and get
741 // the biggest one's size.
742 foreach (ListViewItem item in items) {
743 if (col >= item.SubItems.Count)
746 temp = Size.Ceiling (this.DeviceContext.MeasureString
747 (item.SubItems [col].Text, this.Font));
748 if (temp.Width > ret_size.Width)
752 // adjustment for space
753 if (!ret_size.IsEmpty)
759 // Sets the size of the biggest item text as per the view
760 private void CalcTextSize ()
762 // clear the old value
763 text_size = Size.Empty;
765 if (items.Count == 0)
768 text_size = BiggestItem (0);
770 if (view == View.LargeIcon && this.label_wrap) {
771 Size temp = Size.Empty;
772 if (this.check_boxes)
773 temp.Width += 2 * this.CheckBoxSize.Width;
774 if (large_image_list != null)
775 temp.Width += large_image_list.ImageSize.Width;
778 // wrapping is done for two lines only
779 if (text_size.Width > temp.Width) {
780 text_size.Width = temp.Width;
781 text_size.Height *= 2;
784 else if (view == View.List) {
785 // in list view max text shown in determined by the
786 // control width, even if scolling is enabled.
787 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
788 if (this.small_image_list != null)
789 max_wd -= this.small_image_list.ImageSize.Width;
791 if (text_size.Width > max_wd)
792 text_size.Width = max_wd;
795 // we do the default settings, if we have got 0's
796 if (text_size.Height <= 0)
797 text_size.Height = this.Font.Height;
798 if (text_size.Width <= 0)
799 text_size.Width = this.Width;
802 text_size.Width += 4;
803 text_size.Height += 2;
806 private void Scroll (ScrollBar scrollbar, int delta)
808 if (delta == 0 || !scrollbar.Visible)
812 if (scrollbar == h_scroll)
813 max = h_scroll.Maximum - item_control.Width;
815 max = v_scroll.Maximum - item_control.Height;
817 int val = scrollbar.Value + delta;
820 else if (val < scrollbar.Minimum)
821 val = scrollbar.Minimum;
822 scrollbar.Value = val;
825 private void CalculateScrollBars ()
827 Rectangle client_area = ClientRectangle;
829 if (!this.scrollable || this.items.Count <= 0) {
830 h_scroll.Visible = false;
831 v_scroll.Visible = false;
832 item_control.Location = new Point (0, header_control.Height);
833 item_control.Height = ClientRectangle.Width - header_control.Height;
834 item_control.Width = ClientRectangle.Width;
835 header_control.Width = ClientRectangle.Width;
839 // Don't calculate if the view is not displayable
840 if (client_area.Height < 0 || client_area.Width < 0)
843 // making a scroll bar visible might make
844 // other scroll bar visible
845 if (layout_wd > client_area.Right) {
846 h_scroll.Visible = true;
847 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
848 v_scroll.Visible = true;
850 v_scroll.Visible = false;
851 } else if (layout_ht > client_area.Bottom) {
852 v_scroll.Visible = true;
853 if ((layout_wd + v_scroll.Width) > client_area.Right)
854 h_scroll.Visible = true;
856 h_scroll.Visible = false;
858 h_scroll.Visible = false;
859 v_scroll.Visible = false;
862 item_control.Height = ClientRectangle.Height - header_control.Height;
864 if (h_scroll.Visible) {
865 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
866 h_scroll.Minimum = 0;
868 // if v_scroll is visible, adjust the maximum of the
869 // h_scroll to account for the width of v_scroll
870 if (v_scroll.Visible) {
871 h_scroll.Maximum = layout_wd + v_scroll.Width;
872 h_scroll.Width = client_area.Width - v_scroll.Width;
875 h_scroll.Maximum = layout_wd;
876 h_scroll.Width = client_area.Width;
879 h_scroll.LargeChange = client_area.Width;
880 h_scroll.SmallChange = Font.Height;
881 item_control.Height -= h_scroll.Height;
884 if (header_control.Visible)
885 header_control.Width = ClientRectangle.Width;
886 item_control.Width = ClientRectangle.Width;
888 if (v_scroll.Visible) {
889 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
890 v_scroll.Minimum = 0;
892 // if h_scroll is visible, adjust the maximum of the
893 // v_scroll to account for the height of h_scroll
894 if (h_scroll.Visible) {
895 v_scroll.Maximum = layout_ht + h_scroll.Height;
896 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
898 v_scroll.Maximum = layout_ht;
899 v_scroll.Height = client_area.Height;
902 v_scroll.LargeChange = client_area.Height;
903 v_scroll.SmallChange = Font.Height;
904 if (header_control.Visible)
905 header_control.Width -= v_scroll.Width;
906 item_control.Width -= v_scroll.Width;
910 ColumnHeader GetReorderedColumn (int index)
912 if (reordered_column_indices == null)
913 return Columns [index];
915 return Columns [reordered_column_indices [index]];
918 void ReorderColumn (ColumnHeader col, int index)
920 if (reordered_column_indices == null) {
921 reordered_column_indices = new int [Columns.Count];
922 for (int i = 0; i < Columns.Count; i++)
923 reordered_column_indices [i] = i;
926 if (reordered_column_indices [index] == col.Index)
929 int[] curr = reordered_column_indices;
930 int[] result = new int [Columns.Count];
932 for (int i = 0; i < Columns.Count; i++) {
933 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
937 result [i] = col.Index;
939 result [i] = curr [curr_idx++];
942 reordered_column_indices = result;
944 header_control.Invalidate ();
945 item_control.Invalidate ();
948 Size LargeIconItemSize {
950 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
951 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
952 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
953 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
954 return new Size (w, h);
958 Size SmallIconItemSize {
960 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
961 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
962 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
963 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
964 return new Size (w, h);
970 ListViewItem[,] item_matrix;
972 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
974 header_control.Visible = false;
975 header_control.Size = Size.Empty;
976 item_control.Visible = true;
977 item_control.Location = Point.Empty;
979 if (items.Count == 0)
982 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
984 Rectangle area = ClientRectangle;
987 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
990 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
992 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
995 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
998 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
999 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1000 item_matrix = new ListViewItem [rows, cols];
1003 foreach (ListViewItem item in items) {
1004 int x = col * (sz.Width + x_spacing);
1005 int y = row * (sz.Height + y_spacing);
1006 item.Location = new Point (x, y);
1010 item_matrix [row, col] = item;
1012 if (++row == rows) {
1017 if (++col == cols) {
1024 item_control.Size = new Size (layout_wd, layout_ht);
1027 void LayoutHeader ()
1030 for (int i = 0; i < Columns.Count; i++) {
1031 ColumnHeader col = GetReorderedColumn (i);
1034 col.CalcColumnHeader ();
1038 if (x < ClientRectangle.Width)
1039 x = ClientRectangle.Width;
1041 if (header_style == ColumnHeaderStyle.None) {
1042 header_control.Visible = false;
1043 header_control.Size = Size.Empty;
1045 header_control.Width = x;
1046 header_control.Height = columns [0].Ht;
1047 header_control.Visible = true;
1051 void LayoutDetails ()
1053 if (columns.Count == 0) {
1054 header_control.Visible = false;
1055 item_control.Visible = false;
1061 item_control.Visible = true;
1062 item_control.Location = new Point (0, header_control.Height);
1065 if (items.Count > 0) {
1066 foreach (ListViewItem item in items) {
1068 item.Location = new Point (0, y);
1069 y += item.Bounds.Height + 2;
1072 // some space for bottom gridline
1077 layout_wd = Math.Max (header_control.Width, item_control.Width);
1078 layout_ht = y + header_control.Height;
1081 private void CalculateListView (ListViewAlignment align)
1090 case View.SmallIcon:
1091 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1094 case View.LargeIcon:
1095 LayoutIcons (true, alignment == ListViewAlignment.Left,
1096 ThemeEngine.Current.ListViewHorizontalSpacing,
1097 ThemeEngine.Current.ListViewVerticalSpacing);
1101 LayoutIcons (false, true, 4, 2);
1105 CalculateScrollBars ();
1108 internal void UpdateSelection (ListViewItem item)
1110 if (item.Selected) {
1112 if (!CanMultiselect && SelectedItems.Count > 0) {
1113 SelectedItems.Clear ();
1116 if (!SelectedItems.list.Contains (item)) {
1117 SelectedItems.list.Add (item);
1120 SelectedItems.list.Remove (item);
1124 private bool KeySearchString (KeyEventArgs ke)
1126 int current_tickcnt = Environment.TickCount;
1127 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1128 keysearch_text = string.Empty;
1131 keysearch_text += (char) ke.KeyData;
1132 keysearch_tickcnt = current_tickcnt;
1134 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1137 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1138 CompareOptions.IgnoreCase)) {
1139 SetFocusedItem (Items [i]);
1140 items [i].Selected = true;
1144 i = (i + 1 < Items.Count) ? i+1 : 0;
1152 int GetAdjustedIndex (Keys key)
1156 if (View == View.Details) {
1158 result = FocusedItem.Index - 1;
1159 else if (key == Keys.Down) {
1160 result = FocusedItem.Index + 1;
1161 if (result == items.Count)
1167 int row = FocusedItem.row;
1168 int col = FocusedItem.col;
1174 return item_matrix [row, col - 1].Index;
1177 if (col == (cols - 1))
1179 while (item_matrix [row, col + 1] == null)
1181 return item_matrix [row, col + 1].Index;
1186 return item_matrix [row - 1, col].Index;
1189 if (row == (rows - 1) || row == Items.Count - 1)
1191 while (item_matrix [row + 1, col] == null)
1193 return item_matrix [row + 1, col].Index;
1200 ListViewItem selection_start;
1202 private bool SelectItems (ArrayList sel_items)
1204 bool changed = false;
1205 multiselecting = true;
1206 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1207 foreach (ListViewItem item in curr_items)
1208 if (!sel_items.Contains (item)) {
1209 item.Selected = false;
1212 foreach (ListViewItem item in sel_items)
1213 if (!item.Selected) {
1214 item.Selected = true;
1217 multiselecting = false;
1221 private void UpdateMultiSelection (int index)
1223 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1224 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1225 ListViewItem item = items [index];
1227 if (shift_pressed && selection_start != null) {
1228 ArrayList list = new ArrayList ();
1229 int start = Math.Min (selection_start.Index, index);
1230 int end = Math.Max (selection_start.Index, index);
1231 if (View == View.Details) {
1232 for (int i = start; i <= end; i++)
1233 list.Add (items [i]);
1235 int left = Math.Min (items [start].col, items [end].col);
1236 int right = Math.Max (items [start].col, items [end].col);
1237 int top = Math.Min (items [start].row, items [end].row);
1238 int bottom = Math.Max (items [start].row, items [end].row);
1239 foreach (ListViewItem curr in items)
1240 if (curr.row >= top && curr.row <= bottom &&
1241 curr.col >= left && curr.col <= right)
1244 if (SelectItems (list))
1245 OnSelectedIndexChanged (EventArgs.Empty);
1246 } else if (!ctrl_pressed) {
1247 SelectedItems.Clear ();
1248 item.Selected = true;
1249 selection_start = item;
1250 OnSelectedIndexChanged (EventArgs.Empty);
1254 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1256 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1262 if (FocusedItem == null)
1263 SetFocusedItem (Items [0]);
1265 switch (ke.KeyCode) {
1268 index = Items.Count - 1;
1279 index = GetAdjustedIndex (ke.KeyCode);
1283 ke.Handled = KeySearchString (ke);
1291 UpdateMultiSelection (index);
1292 else if (!items [index].Selected) {
1293 items [index].Selected = true;
1294 OnSelectedIndexChanged (EventArgs.Empty);
1297 SetFocusedItem (items [index]);
1298 EnsureVisible (index);
1302 internal class ItemControl : Control {
1305 ListViewItem clicked_item;
1306 ListViewItem last_clicked_item;
1307 bool hover_processed = false;
1308 bool checking = false;
1310 public ItemControl (ListView owner)
1313 DoubleClick += new EventHandler(ItemsDoubleClick);
1314 MouseDown += new MouseEventHandler(ItemsMouseDown);
1315 MouseMove += new MouseEventHandler(ItemsMouseMove);
1316 MouseHover += new EventHandler(ItemsMouseHover);
1317 MouseUp += new MouseEventHandler(ItemsMouseUp);
1320 void ItemsDoubleClick (object sender, EventArgs e)
1322 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1323 owner.ItemActivate (this, e);
1333 BoxSelect box_select_mode = BoxSelect.None;
1334 ArrayList prev_selection;
1335 Point box_select_start;
1337 Rectangle box_select_rect;
1338 internal Rectangle BoxSelectRectangle {
1339 get { return box_select_rect; }
1341 if (box_select_rect == value)
1344 InvalidateBoxSelectRect ();
1345 box_select_rect = value;
1346 InvalidateBoxSelectRect ();
1350 void InvalidateBoxSelectRect ()
1352 if (BoxSelectRectangle.Size.IsEmpty)
1355 Rectangle edge = BoxSelectRectangle;
1361 edge.Y = BoxSelectRectangle.Bottom - 1;
1363 edge.Y = BoxSelectRectangle.Y - 1;
1365 edge.Height = BoxSelectRectangle.Height + 2;
1367 edge.X = BoxSelectRectangle.Right - 1;
1371 private Rectangle CalculateBoxSelectRectangle (Point pt)
1373 int left = Math.Min (box_select_start.X, pt.X);
1374 int right = Math.Max (box_select_start.X, pt.X);
1375 int top = Math.Min (box_select_start.Y, pt.Y);
1376 int bottom = Math.Max (box_select_start.Y, pt.Y);
1377 return Rectangle.FromLTRB (left, top, right, bottom);
1380 ArrayList BoxSelectedItems {
1382 ArrayList result = new ArrayList ();
1383 foreach (ListViewItem item in owner.Items) {
1384 Rectangle r = item.Bounds;
1386 r.Y += r.Height / 4;
1389 if (BoxSelectRectangle.IntersectsWith (r))
1396 private bool PerformBoxSelection (Point pt)
1398 if (box_select_mode == BoxSelect.None)
1401 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1403 ArrayList box_items = BoxSelectedItems;
1407 switch (box_select_mode) {
1409 case BoxSelect.Normal:
1413 case BoxSelect.Control:
1414 items = new ArrayList ();
1415 foreach (ListViewItem item in prev_selection)
1416 if (!box_items.Contains (item))
1418 foreach (ListViewItem item in box_items)
1419 if (!prev_selection.Contains (item))
1423 case BoxSelect.Shift:
1425 foreach (ListViewItem item in box_items)
1426 prev_selection.Remove (item);
1427 foreach (ListViewItem item in prev_selection)
1432 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1436 owner.SelectItems (items);
1442 private void ToggleCheckState (ListViewItem item)
1444 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1445 item.Checked = !item.Checked;
1446 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1448 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1449 owner.OnItemCheck (ice);
1452 private void ItemsMouseDown (object sender, MouseEventArgs me)
1454 if (owner.items.Count == 0)
1457 Point pt = new Point (me.X, me.Y);
1458 foreach (ListViewItem item in owner.items) {
1459 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1463 ToggleCheckState (item);
1467 if (owner.View == View.Details && !owner.FullRowSelect) {
1468 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1469 clicked_item = item;
1473 if (item.Bounds.Contains (pt)) {
1474 clicked_item = item;
1481 if (clicked_item != null) {
1482 owner.SetFocusedItem (clicked_item);
1483 bool changed = !clicked_item.Selected;
1484 if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0)
1485 owner.UpdateMultiSelection (clicked_item.Index);
1487 clicked_item.Selected = true;
1490 owner.OnSelectedIndexChanged (EventArgs.Empty);
1492 // Raise double click if the item was clicked. On MS the
1493 // double click is only raised if you double click an item
1494 if (me.Clicks > 1) {
1495 owner.OnDoubleClick (EventArgs.Empty);
1496 if (owner.CheckBoxes)
1497 ToggleCheckState (clicked_item);
1498 } else if (me.Clicks == 1)
1499 owner.OnClick (EventArgs.Empty);
1501 if (owner.MultiSelect) {
1502 Keys mods = XplatUI.State.ModifierKeys;
1503 if ((mods & Keys.Shift) != 0)
1504 box_select_mode = BoxSelect.Shift;
1505 else if ((mods & Keys.Control) != 0)
1506 box_select_mode = BoxSelect.Control;
1508 box_select_mode = BoxSelect.Normal;
1509 box_select_start = pt;
1510 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1511 } else if (owner.selected_indices.Count > 0) {
1512 owner.SelectedItems.Clear ();
1513 owner.OnSelectedIndexChanged (EventArgs.Empty);
1518 private void ItemsMouseMove (object sender, MouseEventArgs me)
1520 if (PerformBoxSelection (new Point (me.X, me.Y)))
1523 if (owner.HoverSelection && hover_processed) {
1525 Point pt = PointToClient (Control.MousePosition);
1526 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1527 if (item == null || item.Selected)
1530 hover_processed = false;
1531 XplatUI.ResetMouseHover (Handle);
1536 private void ItemsMouseHover (object sender, EventArgs e)
1538 if (Capture || !owner.HoverSelection)
1541 hover_processed = true;
1542 Point pt = PointToClient (Control.MousePosition);
1543 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1548 item.Selected = true;
1549 owner.OnSelectedIndexChanged (new EventArgs ());
1552 private void ItemsMouseUp (object sender, MouseEventArgs me)
1555 if (owner.Items.Count == 0)
1558 Point pt = new Point (me.X, me.Y);
1560 Rectangle rect = Rectangle.Empty;
1561 if (clicked_item != null) {
1562 if (owner.view == View.Details && !owner.full_row_select)
1563 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1565 rect = clicked_item.Bounds;
1567 if (rect.Contains (pt)) {
1568 switch (owner.activation) {
1569 case ItemActivation.OneClick:
1570 owner.OnItemActivate (EventArgs.Empty);
1573 case ItemActivation.TwoClick:
1574 if (last_clicked_item == clicked_item) {
1575 owner.OnItemActivate (EventArgs.Empty);
1576 last_clicked_item = null;
1578 last_clicked_item = clicked_item;
1581 // DoubleClick activation is handled in another handler
1585 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1586 // Need this to clean up background clicks
1587 owner.SelectedItems.Clear ();
1588 owner.OnSelectedIndexChanged (EventArgs.Empty);
1591 clicked_item = null;
1592 box_select_start = Point.Empty;
1593 BoxSelectRectangle = Rectangle.Empty;
1594 prev_selection = null;
1595 box_select_mode = BoxSelect.None;
1599 internal override void OnPaintInternal (PaintEventArgs pe)
1601 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1604 internal override void OnGotFocusInternal (EventArgs e)
1610 internal override void OnPaintInternal (PaintEventArgs pe)
1615 CalculateScrollBars ();
1618 void FocusChanged (object o, EventArgs args)
1620 if (Items.Count == 0)
1623 if (FocusedItem == null)
1624 SetFocusedItem (Items [0]);
1626 item_control.Invalidate (FocusedItem.Bounds);
1629 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1631 if (Items.Count == 0)
1634 int lines = me.Delta / 120;
1641 case View.SmallIcon:
1642 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1644 case View.LargeIcon:
1645 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1648 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1653 private void ListView_SizeChanged (object sender, EventArgs e)
1655 CalculateListView (alignment);
1658 private void SetFocusedItem (ListViewItem item)
1660 if (focused_item != null)
1661 focused_item.Focused = false;
1664 item.Focused = true;
1666 focused_item = item;
1669 private void HorizontalScroller (object sender, EventArgs e)
1671 // Avoid unnecessary flickering, when button is
1672 // kept pressed at the end
1673 if (h_marker != h_scroll.Value) {
1675 int pixels = h_marker - h_scroll.Value;
1677 h_marker = h_scroll.Value;
1678 if (header_control.Visible)
1679 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1681 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1685 private void VerticalScroller (object sender, EventArgs e)
1687 // Avoid unnecessary flickering, when button is
1688 // kept pressed at the end
1689 if (v_marker != v_scroll.Value) {
1690 int pixels = v_marker - v_scroll.Value;
1691 Rectangle area = item_control.ClientRectangle;
1692 v_marker = v_scroll.Value;
1693 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1696 #endregion // Internal Methods Properties
1698 #region Protected Methods
1699 protected override void CreateHandle ()
1701 base.CreateHandle ();
1704 protected override void Dispose (bool disposing)
1707 h_scroll.Dispose ();
1708 v_scroll.Dispose ();
1710 large_image_list = null;
1711 small_image_list = null;
1712 state_image_list = null;
1715 base.Dispose (disposing);
1718 protected override bool IsInputKey (Keys keyData)
1735 return base.IsInputKey (keyData);
1738 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1740 if (AfterLabelEdit != null)
1741 AfterLabelEdit (this, e);
1744 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1746 if (BeforeLabelEdit != null)
1747 BeforeLabelEdit (this, e);
1750 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1752 if (ColumnClick != null)
1753 ColumnClick (this, e);
1756 protected override void OnEnabledChanged (EventArgs e)
1758 base.OnEnabledChanged (e);
1761 protected override void OnFontChanged (EventArgs e)
1763 base.OnFontChanged (e);
1767 protected override void OnHandleCreated (EventArgs e)
1769 base.OnHandleCreated (e);
1773 protected override void OnHandleDestroyed (EventArgs e)
1775 base.OnHandleDestroyed (e);
1778 protected virtual void OnItemActivate (EventArgs e)
1780 if (ItemActivate != null)
1781 ItemActivate (this, e);
1784 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1786 if (ItemCheck != null)
1787 ItemCheck (this, ice);
1790 protected virtual void OnItemDrag (ItemDragEventArgs e)
1792 if (ItemDrag != null)
1796 protected virtual void OnSelectedIndexChanged (EventArgs e)
1798 if (SelectedIndexChanged != null)
1799 SelectedIndexChanged (this, e);
1802 protected override void OnSystemColorsChanged (EventArgs e)
1804 base.OnSystemColorsChanged (e);
1807 protected void RealizeProperties ()
1812 protected void UpdateExtendedStyles ()
1817 protected override void WndProc (ref Message m)
1819 base.WndProc (ref m);
1821 #endregion // Protected Methods
1823 #region Public Instance Methods
1824 public void ArrangeIcons ()
1826 ArrangeIcons (this.alignment);
1829 public void ArrangeIcons (ListViewAlignment alignment)
1831 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1832 if (view == View.LargeIcon || view == View.SmallIcon) {
1833 this.CalculateListView (alignment);
1834 // we have done the calculations already
1835 this.Redraw (false);
1839 public void BeginUpdate ()
1841 // flag to avoid painting
1845 public void Clear ()
1848 items.Clear (); // Redraw (true) called here
1851 public void EndUpdate ()
1853 // flag to avoid painting
1856 // probably, now we need a redraw with recalculations
1860 public void EnsureVisible (int index)
1862 if (index < 0 || index >= items.Count || scrollable == false)
1865 Rectangle view_rect = item_control.ClientRectangle;
1866 Rectangle bounds = items [index].Bounds;
1868 if (view_rect.Contains (bounds))
1871 if (bounds.Left < 0)
1872 h_scroll.Value += bounds.Left;
1873 else if (bounds.Right > view_rect.Right)
1874 h_scroll.Value += (bounds.Right - view_rect.Right);
1877 v_scroll.Value += bounds.Top;
1878 else if (bounds.Bottom > view_rect.Bottom)
1879 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1882 public ListViewItem GetItemAt (int x, int y)
1884 foreach (ListViewItem item in items) {
1885 if (item.Bounds.Contains (x, y))
1891 public Rectangle GetItemRect (int index)
1893 return GetItemRect (index, ItemBoundsPortion.Entire);
1896 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1898 if (index < 0 || index >= items.Count)
1899 throw new IndexOutOfRangeException ("index");
1901 return items [index].GetBounds (portion);
1909 // we need this overload to reuse the logic for sorting, while allowing
1910 // redrawing to be done by caller or have it done by this method when
1911 // sorting is really performed
1913 // ListViewItemCollection's Add and AddRange methods call this overload
1914 // with redraw set to false, as they take care of redrawing themselves
1915 // (they even want to redraw the listview if no sort is performed, as
1916 // an item was added), while ListView.Sort () only wants to redraw if
1917 // sorting was actually performed
1918 private void Sort (bool redraw)
1920 if (!IsHandleCreated || item_sorter == null) {
1924 items.list.Sort (item_sorter);
1929 public override string ToString ()
1931 int count = this.Items.Count;
1934 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1936 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1938 #endregion // Public Instance Methods
1943 class HeaderControl : Control {
1946 bool column_resize_active = false;
1947 ColumnHeader resize_column;
1948 ColumnHeader clicked_column;
1949 ColumnHeader drag_column;
1951 int drag_to_index = -1;
1953 public HeaderControl (ListView owner)
1956 MouseDown += new MouseEventHandler (HeaderMouseDown);
1957 MouseMove += new MouseEventHandler (HeaderMouseMove);
1958 MouseUp += new MouseEventHandler (HeaderMouseUp);
1961 private ColumnHeader ColumnAtX (int x)
1963 Point pt = new Point (x, 0);
1964 ColumnHeader result = null;
1965 foreach (ColumnHeader col in owner.Columns) {
1966 if (col.Rect.Contains (pt)) {
1974 private int GetReorderedIndex (ColumnHeader col)
1976 if (owner.reordered_column_indices == null)
1979 for (int i = 0; i < owner.Columns.Count; i++)
1980 if (owner.reordered_column_indices [i] == col.Index)
1982 throw new Exception ("Column index missing from reordered array");
1985 private void HeaderMouseDown (object sender, MouseEventArgs me)
1987 if (resize_column != null) {
1988 column_resize_active = true;
1993 clicked_column = ColumnAtX (me.X + owner.h_marker);
1995 if (clicked_column != null) {
1997 if (owner.AllowColumnReorder) {
1999 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2000 drag_column.column_rect = clicked_column.Rect;
2001 drag_to_index = GetReorderedIndex (clicked_column);
2003 clicked_column.pressed = true;
2004 Rectangle bounds = clicked_column.Rect;
2005 bounds.X -= owner.h_marker;
2006 Invalidate (bounds);
2011 private void HeaderMouseMove (object sender, MouseEventArgs me)
2013 Point pt = new Point (me.X + owner.h_marker, me.Y);
2015 if (column_resize_active) {
2016 resize_column.Width = pt.X - resize_column.X;
2017 if (resize_column.Width < 0)
2018 resize_column.Width = 0;
2022 resize_column = null;
2024 if (clicked_column != null) {
2025 if (owner.AllowColumnReorder) {
2028 r = drag_column.column_rect;
2029 r.X = clicked_column.Rect.X + me.X - drag_x;
2030 drag_column.column_rect = r;
2032 int x = me.X + owner.h_marker;
2033 ColumnHeader over = ColumnAtX (x);
2035 drag_to_index = owner.Columns.Count;
2036 else if (x < over.X + over.Width / 2)
2037 drag_to_index = GetReorderedIndex (over);
2039 drag_to_index = GetReorderedIndex (over) + 1;
2042 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2043 bool pressed = clicked_column.pressed;
2044 clicked_column.pressed = over == clicked_column;
2045 if (clicked_column.pressed ^ pressed) {
2046 Rectangle bounds = clicked_column.Rect;
2047 bounds.X -= owner.h_marker;
2048 Invalidate (bounds);
2054 for (int i = 0; i < owner.Columns.Count; i++) {
2055 Rectangle zone = owner.Columns [i].Rect;
2056 zone.X = zone.Right - 5;
2058 if (zone.Contains (pt)) {
2059 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2061 resize_column = owner.Columns [i];
2066 if (resize_column == null)
2067 Cursor = Cursors.Default;
2069 Cursor = Cursors.VSplit;
2072 void HeaderMouseUp (object sender, MouseEventArgs me)
2076 if (column_resize_active) {
2077 column_resize_active = false;
2078 resize_column = null;
2079 Cursor = Cursors.Default;
2083 if (clicked_column != null && clicked_column.pressed) {
2084 clicked_column.pressed = false;
2085 Rectangle bounds = clicked_column.Rect;
2086 bounds.X -= owner.h_marker;
2087 Invalidate (bounds);
2088 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2091 if (drag_column != null && owner.AllowColumnReorder) {
2093 if (drag_to_index > GetReorderedIndex (clicked_column))
2095 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2096 owner.ReorderColumn (clicked_column, drag_to_index);
2101 clicked_column = null;
2104 internal override void OnPaintInternal (PaintEventArgs pe)
2109 Theme theme = ThemeEngine.Current;
2110 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2112 if (drag_column == null)
2116 if (drag_to_index == owner.Columns.Count)
2117 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2119 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2120 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2123 protected override void WndProc (ref Message m)
2125 switch ((Msg)m.Msg) {
2126 case Msg.WM_SETFOCUS:
2130 base.WndProc (ref m);
2136 private class ItemComparer : IComparer {
2137 readonly SortOrder sort_order;
2139 public ItemComparer (SortOrder sortOrder)
2141 sort_order = sortOrder;
2144 public int Compare (object x, object y)
2146 ListViewItem item_x = x as ListViewItem;
2147 ListViewItem item_y = y as ListViewItem;
2148 if (sort_order == SortOrder.Ascending)
2149 return String.Compare (item_x.Text, item_y.Text);
2151 return String.Compare (item_y.Text, item_x.Text);
2155 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2157 private readonly ListView owner;
2159 #region Public Constructor
2160 public CheckedIndexCollection (ListView owner)
2164 #endregion // Public Constructor
2166 #region Public Properties
2169 get { return owner.CheckedItems.Count; }
2172 public bool IsReadOnly {
2173 get { return true; }
2176 public int this [int index] {
2178 int [] indices = GetIndices ();
2179 if (index < 0 || index >= indices.Length)
2180 throw new ArgumentOutOfRangeException ("index");
2181 return indices [index];
2185 bool ICollection.IsSynchronized {
2186 get { return false; }
2189 object ICollection.SyncRoot {
2190 get { return this; }
2193 bool IList.IsFixedSize {
2194 get { return true; }
2197 object IList.this [int index] {
2198 get { return this [index]; }
2199 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2201 #endregion // Public Properties
2203 #region Public Methods
2204 public bool Contains (int checkedIndex)
2206 int [] indices = GetIndices ();
2207 for (int i = 0; i < indices.Length; i++) {
2208 if (indices [i] == checkedIndex)
2214 public IEnumerator GetEnumerator ()
2216 int [] indices = GetIndices ();
2217 return indices.GetEnumerator ();
2220 void ICollection.CopyTo (Array dest, int index)
2222 int [] indices = GetIndices ();
2223 Array.Copy (indices, 0, dest, index, indices.Length);
2226 int IList.Add (object value)
2228 throw new NotSupportedException ("Add operation is not supported.");
2233 throw new NotSupportedException ("Clear operation is not supported.");
2236 bool IList.Contains (object checkedIndex)
2238 if (!(checkedIndex is int))
2240 return Contains ((int) checkedIndex);
2243 int IList.IndexOf (object checkedIndex)
2245 if (!(checkedIndex is int))
2247 return IndexOf ((int) checkedIndex);
2250 void IList.Insert (int index, object value)
2252 throw new NotSupportedException ("Insert operation is not supported.");
2255 void IList.Remove (object value)
2257 throw new NotSupportedException ("Remove operation is not supported.");
2260 void IList.RemoveAt (int index)
2262 throw new NotSupportedException ("RemoveAt operation is not supported.");
2265 public int IndexOf (int checkedIndex)
2267 int [] indices = GetIndices ();
2268 for (int i = 0; i < indices.Length; i++) {
2269 if (indices [i] == checkedIndex)
2274 #endregion // Public Methods
2276 private int [] GetIndices ()
2278 int [] indices = new int [Count];
2279 for (int i = 0; i < owner.CheckedItems.Count; i++) {
2280 ListViewItem item = owner.CheckedItems [i];
2281 indices [i] = item.Index;
2285 } // CheckedIndexCollection
2287 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2289 internal readonly ArrayList list;
2290 private readonly ListView owner;
2292 #region Public Constructor
2293 public CheckedListViewItemCollection (ListView owner)
2295 list = new ArrayList ();
2298 #endregion // Public Constructor
2300 #region Public Properties
2304 if (!owner.CheckBoxes)
2310 public bool IsReadOnly {
2311 get { return true; }
2314 public ListViewItem this [int index] {
2316 if (index < 0 || index >= Count)
2317 throw new ArgumentOutOfRangeException ("index");
2318 return (ListViewItem) list [index];
2322 bool ICollection.IsSynchronized {
2323 get { return list.IsSynchronized; }
2326 object ICollection.SyncRoot {
2327 get { return this; }
2330 bool IList.IsFixedSize {
2331 get { return true; }
2334 object IList.this [int index] {
2335 get { return this [index]; }
2336 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2338 #endregion // Public Properties
2340 #region Public Methods
2341 public bool Contains (ListViewItem item)
2343 if (!owner.CheckBoxes)
2345 return list.Contains (item);
2348 public void CopyTo (Array dest, int index)
2350 if (!owner.CheckBoxes)
2352 list.CopyTo (dest, index);
2355 public IEnumerator GetEnumerator ()
2357 if (!owner.CheckBoxes)
2358 return (new ListViewItem [0]).GetEnumerator ();
2359 return list.GetEnumerator ();
2362 int IList.Add (object value)
2364 throw new NotSupportedException ("Add operation is not supported.");
2369 throw new NotSupportedException ("Clear operation is not supported.");
2372 bool IList.Contains (object item)
2374 if (!(item is ListViewItem))
2376 return Contains ((ListViewItem) item);
2379 int IList.IndexOf (object item)
2381 if (!(item is ListViewItem))
2383 return IndexOf ((ListViewItem) item);
2386 void IList.Insert (int index, object value)
2388 throw new NotSupportedException ("Insert operation is not supported.");
2391 void IList.Remove (object value)
2393 throw new NotSupportedException ("Remove operation is not supported.");
2396 void IList.RemoveAt (int index)
2398 throw new NotSupportedException ("RemoveAt operation is not supported.");
2401 public int IndexOf (ListViewItem item)
2403 if (!owner.CheckBoxes)
2405 return list.IndexOf (item);
2407 #endregion // Public Methods
2408 } // CheckedListViewItemCollection
2410 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2412 internal ArrayList list;
2413 private ListView owner;
2415 #region Public Constructor
2416 public ColumnHeaderCollection (ListView owner)
2418 list = new ArrayList ();
2421 #endregion // Public Constructor
2423 #region Public Properties
2426 get { return list.Count; }
2429 public bool IsReadOnly {
2430 get { return false; }
2433 public virtual ColumnHeader this [int index] {
2435 if (index < 0 || index >= list.Count)
2436 throw new ArgumentOutOfRangeException ("index");
2437 return (ColumnHeader) list [index];
2441 bool ICollection.IsSynchronized {
2442 get { return true; }
2445 object ICollection.SyncRoot {
2446 get { return this; }
2449 bool IList.IsFixedSize {
2450 get { return list.IsFixedSize; }
2453 object IList.this [int index] {
2454 get { return this [index]; }
2455 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2457 #endregion // Public Properties
2459 #region Public Methods
2460 public virtual int Add (ColumnHeader value)
2463 value.owner = this.owner;
2464 idx = list.Add (value);
2465 if (owner.IsHandleCreated) {
2466 owner.Redraw (true);
2471 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2473 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2474 this.Add (colHeader);
2478 public virtual void AddRange (ColumnHeader [] values)
2480 foreach (ColumnHeader colHeader in values) {
2481 colHeader.owner = this.owner;
2485 owner.Redraw (true);
2488 public virtual void Clear ()
2491 owner.Redraw (true);
2494 public bool Contains (ColumnHeader value)
2496 return list.Contains (value);
2499 public IEnumerator GetEnumerator ()
2501 return list.GetEnumerator ();
2504 void ICollection.CopyTo (Array dest, int index)
2506 list.CopyTo (dest, index);
2509 int IList.Add (object value)
2511 if (! (value is ColumnHeader)) {
2512 throw new ArgumentException ("Not of type ColumnHeader", "value");
2515 return this.Add ((ColumnHeader) value);
2518 bool IList.Contains (object value)
2520 if (! (value is ColumnHeader)) {
2521 throw new ArgumentException ("Not of type ColumnHeader", "value");
2524 return this.Contains ((ColumnHeader) value);
2527 int IList.IndexOf (object value)
2529 if (! (value is ColumnHeader)) {
2530 throw new ArgumentException ("Not of type ColumnHeader", "value");
2533 return this.IndexOf ((ColumnHeader) value);
2536 void IList.Insert (int index, object value)
2538 if (! (value is ColumnHeader)) {
2539 throw new ArgumentException ("Not of type ColumnHeader", "value");
2542 this.Insert (index, (ColumnHeader) value);
2545 void IList.Remove (object value)
2547 if (! (value is ColumnHeader)) {
2548 throw new ArgumentException ("Not of type ColumnHeader", "value");
2551 this.Remove ((ColumnHeader) value);
2554 public int IndexOf (ColumnHeader value)
2556 return list.IndexOf (value);
2559 public void Insert (int index, ColumnHeader value)
2561 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2562 // but it's really only greater.
2563 if (index < 0 || index > list.Count)
2564 throw new ArgumentOutOfRangeException ("index");
2566 value.owner = this.owner;
2567 list.Insert (index, value);
2568 owner.Redraw (true);
2571 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2573 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2574 this.Insert (index, colHeader);
2577 public virtual void Remove (ColumnHeader column)
2579 // TODO: Update Column internal index ?
2580 list.Remove (column);
2581 owner.Redraw (true);
2584 public virtual void RemoveAt (int index)
2586 if (index < 0 || index >= list.Count)
2587 throw new ArgumentOutOfRangeException ("index");
2589 // TODO: Update Column internal index ?
2590 list.RemoveAt (index);
2591 owner.Redraw (true);
2593 #endregion // Public Methods
2596 } // ColumnHeaderCollection
2598 public class ListViewItemCollection : IList, ICollection, IEnumerable
2600 internal ArrayList list;
2601 private readonly ListView owner;
2603 #region Public Constructor
2604 public ListViewItemCollection (ListView owner)
2606 list = new ArrayList ();
2609 #endregion // Public Constructor
2611 #region Public Properties
2614 get { return list.Count; }
2617 public bool IsReadOnly {
2618 get { return false; }
2621 public virtual ListViewItem this [int displayIndex] {
2623 if (displayIndex < 0 || displayIndex >= list.Count)
2624 throw new ArgumentOutOfRangeException ("displayIndex");
2625 return (ListViewItem) list [displayIndex];
2629 if (displayIndex < 0 || displayIndex >= list.Count)
2630 throw new ArgumentOutOfRangeException ("displayIndex");
2632 if (list.Contains (value))
2633 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2635 value.Owner = owner;
2636 list [displayIndex] = value;
2638 owner.Redraw (true);
2642 bool ICollection.IsSynchronized {
2643 get { return true; }
2646 object ICollection.SyncRoot {
2647 get { return this; }
2650 bool IList.IsFixedSize {
2651 get { return list.IsFixedSize; }
2654 object IList.this [int index] {
2655 get { return this [index]; }
2657 if (value is ListViewItem)
2658 this [index] = (ListViewItem) value;
2660 this [index] = new ListViewItem (value.ToString ());
2663 #endregion // Public Properties
2665 #region Public Methods
2666 public virtual ListViewItem Add (ListViewItem value)
2668 if (list.Contains (value))
2669 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2671 value.Owner = owner;
2675 owner.Redraw (true);
2679 public virtual ListViewItem Add (string text)
2681 ListViewItem item = new ListViewItem (text);
2682 return this.Add (item);
2685 public virtual ListViewItem Add (string text, int imageIndex)
2687 ListViewItem item = new ListViewItem (text, imageIndex);
2688 return this.Add (item);
2691 public void AddRange (ListViewItem [] values)
2694 owner.SelectedItems.list.Clear ();
2695 owner.CheckedItems.list.Clear ();
2697 foreach (ListViewItem item in values) {
2703 owner.Redraw (true);
2706 public virtual void Clear ()
2708 owner.SetFocusedItem (null);
2709 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2711 owner.SelectedItems.list.Clear ();
2712 owner.CheckedItems.list.Clear ();
2713 owner.Redraw (true);
2716 public bool Contains (ListViewItem item)
2718 return list.Contains (item);
2721 public void CopyTo (Array dest, int index)
2723 list.CopyTo (dest, index);
2726 public IEnumerator GetEnumerator ()
2728 return list.GetEnumerator ();
2731 int IList.Add (object item)
2736 if (item is ListViewItem) {
2737 li = (ListViewItem) item;
2738 if (list.Contains (li))
2739 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2742 li = new ListViewItem (item.ToString ());
2745 result = list.Add (li);
2746 owner.Redraw (true);
2751 bool IList.Contains (object item)
2753 return list.Contains (item);
2756 int IList.IndexOf (object item)
2758 return list.IndexOf (item);
2761 void IList.Insert (int index, object item)
2763 if (item is ListViewItem)
2764 this.Insert (index, (ListViewItem) item);
2766 this.Insert (index, item.ToString ());
2769 void IList.Remove (object item)
2771 Remove ((ListViewItem) item);
2774 public int IndexOf (ListViewItem item)
2776 return list.IndexOf (item);
2779 public ListViewItem Insert (int index, ListViewItem item)
2781 if (index < 0 || index > list.Count)
2782 throw new ArgumentOutOfRangeException ("index");
2784 if (list.Contains (item))
2785 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2788 list.Insert (index, item);
2789 owner.Redraw (true);
2793 public ListViewItem Insert (int index, string text)
2795 return this.Insert (index, new ListViewItem (text));
2798 public ListViewItem Insert (int index, string text, int imageIndex)
2800 return this.Insert (index, new ListViewItem (text, imageIndex));
2803 public virtual void Remove (ListViewItem item)
2805 if (!list.Contains (item))
2808 owner.SelectedItems.list.Remove (item);
2809 owner.CheckedItems.list.Remove (item);
2811 owner.Redraw (true);
2814 public virtual void RemoveAt (int index)
2816 ListViewItem item = this [index];
2817 list.RemoveAt (index);
2818 owner.SelectedItems.list.Remove (item);
2819 owner.CheckedItems.list.Remove (item);
2820 owner.Redraw (false);
2822 #endregion // Public Methods
2824 } // ListViewItemCollection
2826 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2828 private readonly ListView owner;
2830 #region Public Constructor
2831 public SelectedIndexCollection (ListView owner)
2835 #endregion // Public Constructor
2837 #region Public Properties
2841 return owner.SelectedItems.Count;
2845 public bool IsReadOnly {
2846 get { return true; }
2849 public int this [int index] {
2851 int [] indices = GetIndices ();
2852 if (index < 0 || index >= indices.Length)
2853 throw new ArgumentOutOfRangeException ("index");
2854 return indices [index];
2858 bool ICollection.IsSynchronized {
2859 get { return false; }
2862 object ICollection.SyncRoot {
2863 get { return this; }
2866 bool IList.IsFixedSize {
2867 get { return true; }
2870 object IList.this [int index] {
2871 get { return this [index]; }
2872 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2874 #endregion // Public Properties
2876 #region Public Methods
2877 public bool Contains (int selectedIndex)
2879 int [] indices = GetIndices ();
2880 for (int i = 0; i < indices.Length; i++) {
2881 if (indices [i] == selectedIndex)
2887 public void CopyTo (Array dest, int index)
2889 int [] indices = GetIndices ();
2890 Array.Copy (indices, 0, dest, index, indices.Length);
2893 public IEnumerator GetEnumerator ()
2895 int [] indices = GetIndices ();
2896 return indices.GetEnumerator ();
2899 int IList.Add (object value)
2901 throw new NotSupportedException ("Add operation is not supported.");
2906 throw new NotSupportedException ("Clear operation is not supported.");
2909 bool IList.Contains (object selectedIndex)
2911 if (!(selectedIndex is int))
2913 return Contains ((int) selectedIndex);
2916 int IList.IndexOf (object selectedIndex)
2918 if (!(selectedIndex is int))
2920 return IndexOf ((int) selectedIndex);
2923 void IList.Insert (int index, object value)
2925 throw new NotSupportedException ("Insert operation is not supported.");
2928 void IList.Remove (object value)
2930 throw new NotSupportedException ("Remove operation is not supported.");
2933 void IList.RemoveAt (int index)
2935 throw new NotSupportedException ("RemoveAt operation is not supported.");
2938 public int IndexOf (int selectedIndex)
2940 int [] indices = GetIndices ();
2941 for (int i = 0; i < indices.Length; i++) {
2942 if (indices [i] == selectedIndex)
2947 #endregion // Public Methods
2949 private int [] GetIndices ()
2951 int [] indices = new int [Count];
2952 for (int i = 0; i < owner.SelectedItems.Count; i++) {
2953 ListViewItem item = owner.SelectedItems [i];
2954 indices [i] = item.Index;
2959 } // SelectedIndexCollection
2961 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2963 internal ArrayList list;
2964 private readonly ListView owner;
2966 #region Public Constructor
2967 public SelectedListViewItemCollection (ListView owner)
2969 list = new ArrayList ();
2972 #endregion // Public Constructor
2974 #region Public Properties
2978 if (!owner.IsHandleCreated)
2984 public bool IsReadOnly {
2985 get { return true; }
2988 public ListViewItem this [int index] {
2990 if (index < 0 || index >= Count)
2991 throw new ArgumentOutOfRangeException ("index");
2992 return (ListViewItem) list [index];
2996 bool ICollection.IsSynchronized {
2997 get { return list.IsSynchronized; }
3000 object ICollection.SyncRoot {
3001 get { return this; }
3004 bool IList.IsFixedSize {
3005 get { return true; }
3008 object IList.this [int index] {
3009 get { return this [index]; }
3010 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3012 #endregion // Public Properties
3014 #region Public Methods
3015 public void Clear ()
3017 if (!owner.IsHandleCreated)
3020 ArrayList copy = (ArrayList) list.Clone ();
3021 for (int i = 0; i < copy.Count; i++)
3022 ((ListViewItem) copy [i]).Selected = false;
3027 public bool Contains (ListViewItem item)
3029 if (!owner.IsHandleCreated)
3031 return list.Contains (item);
3034 public void CopyTo (Array dest, int index)
3036 if (!owner.IsHandleCreated)
3038 list.CopyTo (dest, index);
3041 public IEnumerator GetEnumerator ()
3043 if (!owner.IsHandleCreated)
3044 return (new ListViewItem [0]).GetEnumerator ();
3045 return list.GetEnumerator ();
3048 int IList.Add (object value)
3050 throw new NotSupportedException ("Add operation is not supported.");
3053 bool IList.Contains (object item)
3055 if (!(item is ListViewItem))
3057 return Contains ((ListViewItem) item);
3060 int IList.IndexOf (object item)
3062 if (!(item is ListViewItem))
3064 return IndexOf ((ListViewItem) item);
3067 void IList.Insert (int index, object value)
3069 throw new NotSupportedException ("Insert operation is not supported.");
3072 void IList.Remove (object value)
3074 throw new NotSupportedException ("Remove operation is not supported.");
3077 void IList.RemoveAt (int index)
3079 throw new NotSupportedException ("RemoveAt operation is not supported.");
3082 public int IndexOf (ListViewItem item)
3084 if (!owner.IsHandleCreated)
3086 return list.IndexOf (item);
3088 #endregion // Public Methods
3090 } // SelectedListViewItemCollection
3092 #endregion // Subclasses