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.
37 using System.Collections;
38 using System.ComponentModel;
39 using System.ComponentModel.Design;
41 using System.Runtime.InteropServices;
42 using System.Globalization;
44 namespace System.Windows.Forms
46 [DefaultEvent ("SelectedIndexChanged")]
47 [DefaultProperty ("Items")]
48 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
49 public class ListView : Control
51 private ItemActivation activation = ItemActivation.Standard;
52 private ListViewAlignment alignment = ListViewAlignment.Top;
53 private bool allow_column_reorder = false;
54 private bool auto_arrange = true;
55 private bool check_boxes = false;
56 private CheckedIndexCollection checked_indices;
57 private CheckedListViewItemCollection checked_items;
58 private ColumnHeaderCollection columns;
59 internal ListViewItem focused_item;
60 private bool full_row_select = false;
61 private bool grid_lines = false;
62 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
63 private bool hide_selection = true;
64 private bool hover_selection = false;
65 private IComparer item_sorter = new ItemComparer ();
66 private ListViewItemCollection items;
67 private bool label_edit = false;
68 private bool label_wrap = true;
69 private bool multiselect = true;
70 private bool scrollable = true;
71 private SelectedIndexCollection selected_indices;
72 private SelectedListViewItemCollection selected_items;
73 private SortOrder sort_order = SortOrder.None;
74 private ImageList state_image_list;
75 private bool updating = false;
76 private View view = View.LargeIcon;
77 private int layout_wd; // We might draw more than our client area
78 private int layout_ht; // therefore we need to have these two.
79 //private TextBox editor; // Used for editing an item text
80 HeaderControl header_control;
81 internal ItemControl item_control;
82 internal ScrollBar h_scroll; // used for scrolling horizontally
83 internal ScrollBar v_scroll; // used for scrolling vertically
84 internal int h_marker; // Position markers for scrolling
85 internal int v_marker;
86 private int keysearch_tickcnt;
87 private string keysearch_text;
88 static private readonly int keysearch_keydelay = 1000;
89 private int[] reordered_column_indices;
92 internal ImageList large_image_list;
93 internal ImageList small_image_list;
94 internal Size text_size = Size.Empty;
97 public event LabelEditEventHandler AfterLabelEdit;
100 [EditorBrowsable (EditorBrowsableState.Never)]
101 public new event EventHandler BackgroundImageChanged {
102 add { base.BackgroundImageChanged += value; }
103 remove { base.BackgroundImageChanged -= value; }
106 public event LabelEditEventHandler BeforeLabelEdit;
107 public event ColumnClickEventHandler ColumnClick;
108 public event EventHandler ItemActivate;
109 public event ItemCheckEventHandler ItemCheck;
110 public event ItemDragEventHandler ItemDrag;
113 [EditorBrowsable (EditorBrowsableState.Never)]
114 public new event PaintEventHandler Paint {
115 add { base.Paint += value; }
116 remove { base.Paint -= value; }
119 public event EventHandler SelectedIndexChanged;
122 [EditorBrowsable (EditorBrowsableState.Never)]
123 public new event EventHandler TextChanged {
124 add { base.TextChanged += value; }
125 remove { base.TextChanged -= value; }
130 #region Public Constructors
133 background_color = ThemeEngine.Current.ColorWindow;
134 checked_indices = new CheckedIndexCollection (this);
135 checked_items = new CheckedListViewItemCollection (this);
136 columns = new ColumnHeaderCollection (this);
137 foreground_color = SystemColors.WindowText;
138 items = new ListViewItemCollection (this);
139 selected_indices = new SelectedIndexCollection (this);
140 selected_items = new SelectedListViewItemCollection (this);
142 border_style = BorderStyle.Fixed3D;
144 header_control = new HeaderControl (this);
145 header_control.Visible = false;
146 Controls.AddImplicit (header_control);
148 item_control = new ItemControl (this);
149 Controls.AddImplicit (item_control);
151 h_scroll = new HScrollBar ();
152 Controls.AddImplicit (this.h_scroll);
154 v_scroll = new VScrollBar ();
155 Controls.AddImplicit (this.v_scroll);
157 h_marker = v_marker = 0;
158 keysearch_tickcnt = 0;
160 // scroll bars are disabled initially
161 h_scroll.Visible = false;
162 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
163 v_scroll.Visible = false;
164 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
167 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
168 SizeChanged += new EventHandler (ListView_SizeChanged);
169 GotFocus += new EventHandler (FocusChanged);
170 LostFocus += new EventHandler (FocusChanged);
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) {
311 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
312 public CheckedIndexCollection CheckedIndices {
313 get { return checked_indices; }
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
318 public CheckedListViewItemCollection CheckedItems {
319 get { return checked_items; }
322 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
324 [MergableProperty (false)]
325 public ColumnHeaderCollection Columns {
326 get { return columns; }
330 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
331 public ListViewItem FocusedItem {
337 public override Color ForeColor {
339 if (foreground_color.IsEmpty)
340 return ThemeEngine.Current.ColorWindowText;
342 return foreground_color;
344 set { foreground_color = value; }
347 [DefaultValue (false)]
348 public bool FullRowSelect {
349 get { return full_row_select; }
350 set { full_row_select = value; }
353 [DefaultValue (false)]
354 public bool GridLines {
355 get { return grid_lines; }
357 if (grid_lines != value) {
364 [DefaultValue (ColumnHeaderStyle.Clickable)]
365 public ColumnHeaderStyle HeaderStyle {
366 get { return header_style; }
368 if (header_style == value)
372 case ColumnHeaderStyle.Clickable:
373 case ColumnHeaderStyle.Nonclickable:
374 case ColumnHeaderStyle.None:
377 throw new InvalidEnumArgumentException (string.Format
378 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
381 header_style = value;
382 if (view == View.Details)
387 [DefaultValue (true)]
388 public bool HideSelection {
389 get { return hide_selection; }
391 if (hide_selection != value) {
392 hide_selection = value;
398 [DefaultValue (false)]
399 public bool HoverSelection {
400 get { return hover_selection; }
401 set { hover_selection = value; }
404 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
406 [MergableProperty (false)]
407 public ListViewItemCollection Items {
408 get { return items; }
411 [DefaultValue (false)]
412 public bool LabelEdit {
413 get { return label_edit; }
414 set { label_edit = value; }
417 [DefaultValue (true)]
419 public bool LabelWrap {
420 get { return label_wrap; }
422 if (label_wrap != value) {
429 [DefaultValue (null)]
430 public ImageList LargeImageList {
431 get { return large_image_list; }
433 large_image_list = value;
439 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
440 public IComparer ListViewItemSorter {
441 get { return item_sorter; }
442 set { item_sorter = value; }
445 [DefaultValue (true)]
446 public bool MultiSelect {
447 get { return multiselect; }
448 set { multiselect = value; }
451 [DefaultValue (true)]
452 public bool Scrollable {
453 get { return scrollable; }
455 if (scrollable != value) {
463 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
464 public SelectedIndexCollection SelectedIndices {
465 get { return selected_indices; }
469 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
470 public SelectedListViewItemCollection SelectedItems {
471 get { return selected_items; }
475 [MonoTODO("Implement")]
476 public bool ShowGroups {
486 [DefaultValue (null)]
487 public ImageList SmallImageList {
488 get { return small_image_list; }
490 small_image_list = value;
495 [DefaultValue (SortOrder.None)]
496 public SortOrder Sorting {
497 get { return sort_order; }
499 if (value != SortOrder.Ascending && value != SortOrder.Descending &&
500 value != SortOrder.None) {
501 throw new InvalidEnumArgumentException (string.Format
502 ("Enum argument value '{0}' is not valid for Sorting", value));
505 if (sort_order != value) {
512 [DefaultValue (null)]
513 public ImageList StateImageList {
514 get { return state_image_list; }
516 state_image_list = value;
523 [EditorBrowsable (EditorBrowsableState.Never)]
524 public override string Text {
533 OnTextChanged (EventArgs.Empty);
538 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539 public ListViewItem TopItem {
542 if (this.items.Count == 0)
544 // if contents are not scrolled
545 // it is the first item
546 else if (h_marker == 0 && v_marker == 0)
547 return this.items [0];
548 // do a hit test for the scrolled position
550 foreach (ListViewItem item in this.items) {
551 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
560 [MonoTODO("Implement")]
561 public bool UseCompatibleStateImageBehavior {
571 [DefaultValue (View.LargeIcon)]
575 if (value != View.Details && value != View.LargeIcon &&
576 value != View.List && value != View.SmallIcon ) {
577 throw new InvalidEnumArgumentException (string.Format
578 ("Enum argument value '{0}' is not valid for View", value));
582 h_scroll.Value = v_scroll.Value = 0;
588 #endregion // Public Instance Properties
590 #region Internal Methods Properties
592 internal int FirstVisibleIndex {
595 if (this.items.Count == 0)
598 if (h_marker == 0 && v_marker == 0)
601 foreach (ListViewItem item in this.items) {
602 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
611 internal int LastVisibleIndex {
613 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
614 if (View == View.List || Alignment == ListViewAlignment.Left) {
615 if (Items[i].Bounds.X > ClientRectangle.Right)
618 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
623 return Items.Count - 1;
627 internal int TotalWidth {
628 get { return Math.Max (this.Width, this.layout_wd); }
631 internal int TotalHeight {
632 get { return Math.Max (this.Height, this.layout_ht); }
635 internal void Redraw (bool recalculate)
637 // Avoid calculations when control is being updated
642 CalculateListView (this.alignment);
647 internal Size GetChildColumnSize (int index)
649 Size ret_size = Size.Empty;
650 ColumnHeader col = this.columns [index];
652 if (col.Width == -2) { // autosize = max(items, columnheader)
653 Size size = Size.Ceiling (this.DeviceContext.MeasureString
654 (col.Text, this.Font));
655 ret_size = BiggestItem (index);
656 if (size.Width > ret_size.Width)
659 else { // -1 and all the values < -2 are put under one category
660 ret_size = BiggestItem (index);
661 // fall back to empty columns' width if no subitem is available for a column
662 if (ret_size.IsEmpty) {
663 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
664 if (col.Text.Length > 0)
665 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
666 (col.Text, this.Font)).Height;
668 ret_size.Height = this.Font.Height;
672 // adjust the size for icon and checkbox for 0th column
674 ret_size.Width += (this.CheckBoxSize.Width + 4);
675 if (this.small_image_list != null)
676 ret_size.Width += this.small_image_list.ImageSize.Width;
681 // Returns the size of biggest item text in a column.
682 private Size BiggestItem (int col)
684 Size temp = Size.Empty;
685 Size ret_size = Size.Empty;
687 // 0th column holds the item text, we check the size of
688 // the various subitems falling in that column and get
689 // the biggest one's size.
690 foreach (ListViewItem item in items) {
691 if (col >= item.SubItems.Count)
694 temp = Size.Ceiling (this.DeviceContext.MeasureString
695 (item.SubItems [col].Text, this.Font));
696 if (temp.Width > ret_size.Width)
700 // adjustment for space
701 if (!ret_size.IsEmpty)
707 // Sets the size of the biggest item text as per the view
708 private void CalcTextSize ()
710 // clear the old value
711 text_size = Size.Empty;
713 if (items.Count == 0)
716 text_size = BiggestItem (0);
718 if (view == View.LargeIcon && this.label_wrap) {
719 Size temp = Size.Empty;
720 if (this.check_boxes)
721 temp.Width += 2 * this.CheckBoxSize.Width;
722 if (large_image_list != null)
723 temp.Width += large_image_list.ImageSize.Width;
726 // wrapping is done for two lines only
727 if (text_size.Width > temp.Width) {
728 text_size.Width = temp.Width;
729 text_size.Height *= 2;
732 else if (view == View.List) {
733 // in list view max text shown in determined by the
734 // control width, even if scolling is enabled.
735 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
736 if (this.small_image_list != null)
737 max_wd -= this.small_image_list.ImageSize.Width;
739 if (text_size.Width > max_wd)
740 text_size.Width = max_wd;
743 // we do the default settings, if we have got 0's
744 if (text_size.Height <= 0)
745 text_size.Height = this.Font.Height;
746 if (text_size.Width <= 0)
747 text_size.Width = this.Width;
750 text_size.Width += 4;
751 text_size.Height += 2;
754 private void Scroll (ScrollBar scrollbar, int delta)
756 if (delta == 0 || !scrollbar.Visible)
760 if (scrollbar == h_scroll)
761 max = h_scroll.Maximum - item_control.Width;
763 max = v_scroll.Maximum - item_control.Height;
765 int val = scrollbar.Value + delta;
768 else if (val < scrollbar.Minimum)
769 val = scrollbar.Minimum;
770 scrollbar.Value = val;
773 private void CalculateScrollBars ()
775 Rectangle client_area = ClientRectangle;
777 if (!this.scrollable || this.items.Count <= 0) {
778 h_scroll.Visible = false;
779 v_scroll.Visible = false;
780 item_control.Location = new Point (0, header_control.Height);
781 item_control.Height = ClientRectangle.Width - header_control.Height;
782 item_control.Width = ClientRectangle.Width;
783 header_control.Width = ClientRectangle.Width;
787 // Don't calculate if the view is not displayable
788 if (client_area.Height < 0 || client_area.Width < 0)
791 // making a scroll bar visible might make
792 // other scroll bar visible
793 if (layout_wd > client_area.Right) {
794 h_scroll.Visible = true;
795 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
796 v_scroll.Visible = true;
798 v_scroll.Visible = false;
799 } else if (layout_ht > client_area.Bottom) {
800 v_scroll.Visible = true;
801 if ((layout_wd + v_scroll.Width) > client_area.Right)
802 h_scroll.Visible = true;
804 h_scroll.Visible = false;
806 h_scroll.Visible = false;
807 v_scroll.Visible = false;
810 item_control.Height = ClientRectangle.Height - header_control.Height;
812 if (h_scroll.Visible) {
813 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
814 h_scroll.Minimum = 0;
816 // if v_scroll is visible, adjust the maximum of the
817 // h_scroll to account for the width of v_scroll
818 if (v_scroll.Visible) {
819 h_scroll.Maximum = layout_wd + v_scroll.Width;
820 h_scroll.Width = client_area.Width - v_scroll.Width;
823 h_scroll.Maximum = layout_wd;
824 h_scroll.Width = client_area.Width;
827 h_scroll.LargeChange = client_area.Width;
828 h_scroll.SmallChange = Font.Height;
829 item_control.Height -= h_scroll.Height;
832 if (header_control.Visible)
833 header_control.Width = ClientRectangle.Width;
834 item_control.Width = ClientRectangle.Width;
836 if (v_scroll.Visible) {
837 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
838 v_scroll.Minimum = 0;
840 // if h_scroll is visible, adjust the maximum of the
841 // v_scroll to account for the height of h_scroll
842 if (h_scroll.Visible) {
843 v_scroll.Maximum = layout_ht + h_scroll.Height;
844 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
846 v_scroll.Maximum = layout_ht;
847 v_scroll.Height = client_area.Height;
850 v_scroll.LargeChange = client_area.Height;
851 v_scroll.SmallChange = Font.Height;
852 if (header_control.Visible)
853 header_control.Width -= v_scroll.Width;
854 item_control.Width -= v_scroll.Width;
858 ColumnHeader GetReorderedColumn (int index)
860 if (reordered_column_indices == null)
861 return Columns [index];
863 return Columns [reordered_column_indices [index]];
866 void ReorderColumn (ColumnHeader col, int index)
868 if (reordered_column_indices == null) {
869 reordered_column_indices = new int [Columns.Count];
870 for (int i = 0; i < Columns.Count; i++)
871 reordered_column_indices [i] = i;
874 if (reordered_column_indices [index] == col.Index)
877 int[] curr = reordered_column_indices;
878 int[] result = new int [Columns.Count];
880 for (int i = 0; i < Columns.Count; i++) {
881 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
885 result [i] = col.Index;
887 result [i] = curr [curr_idx++];
890 reordered_column_indices = result;
892 header_control.Invalidate ();
893 item_control.Invalidate ();
896 Size LargeIconItemSize {
898 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
899 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
900 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
901 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
902 return new Size (w, h);
906 Size SmallIconItemSize {
908 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
909 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
910 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
911 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
912 return new Size (w, h);
918 ListViewItem[,] item_matrix;
920 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
922 header_control.Visible = false;
923 header_control.Size = Size.Empty;
924 item_control.Visible = true;
925 item_control.Location = Point.Empty;
927 if (items.Count == 0)
930 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
932 Rectangle area = ClientRectangle;
935 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
938 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
940 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
943 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
946 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
947 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
948 item_matrix = new ListViewItem [rows, cols];
951 foreach (ListViewItem item in items) {
952 int x = col * (sz.Width + x_spacing);
953 int y = row * (sz.Height + y_spacing);
954 item.Location = new Point (x, y);
958 item_matrix [row, col] = item;
972 item_control.Size = new Size (layout_wd, layout_ht);
978 for (int i = 0; i < Columns.Count; i++) {
979 ColumnHeader col = GetReorderedColumn (i);
982 col.CalcColumnHeader ();
986 if (x < ClientRectangle.Width)
987 x = ClientRectangle.Width;
989 if (header_style == ColumnHeaderStyle.None) {
990 header_control.Visible = false;
991 header_control.Size = Size.Empty;
993 header_control.Width = x;
994 header_control.Height = columns [0].Ht;
995 header_control.Visible = true;
999 void LayoutDetails ()
1001 if (columns.Count == 0) {
1002 header_control.Visible = false;
1003 item_control.Visible = false;
1009 item_control.Visible = true;
1010 item_control.Location = new Point (0, header_control.Height);
1013 if (items.Count > 0) {
1014 foreach (ListViewItem item in items) {
1016 item.Location = new Point (0, y);
1017 y += item.Bounds.Height + 2;
1020 // some space for bottom gridline
1025 layout_wd = Math.Max (header_control.Width, item_control.Width);
1026 layout_ht = y + header_control.Height;
1029 private void CalculateListView (ListViewAlignment align)
1038 case View.SmallIcon:
1039 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1042 case View.LargeIcon:
1043 LayoutIcons (true, alignment == ListViewAlignment.Left,
1044 ThemeEngine.Current.ListViewHorizontalSpacing,
1045 ThemeEngine.Current.ListViewVerticalSpacing);
1049 LayoutIcons (false, true, 4, 2);
1053 CalculateScrollBars ();
1056 internal void UpdateSelection (ListViewItem item)
1058 if (item.Selected) {
1060 if (!CanMultiselect && SelectedItems.Count > 0) {
1061 SelectedItems.Clear ();
1062 SelectedIndices.list.Clear ();
1065 if (!SelectedItems.Contains (item)) {
1066 SelectedItems.list.Add (item);
1067 SelectedIndices.list.Add (item.Index);
1070 SelectedItems.list.Remove (item);
1071 SelectedIndices.list.Remove (item.Index);
1075 private bool KeySearchString (KeyEventArgs ke)
1077 int current_tickcnt = Environment.TickCount;
1078 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1079 keysearch_text = string.Empty;
1082 keysearch_text += (char) ke.KeyData;
1083 keysearch_tickcnt = current_tickcnt;
1085 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1088 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1089 CompareOptions.IgnoreCase)) {
1090 SetFocusedItem (Items [i]);
1091 items [i].Selected = true;
1095 i = (i + 1 < Items.Count) ? i+1 : 0;
1103 int GetAdjustedIndex (Keys key)
1107 if (View == View.Details) {
1109 result = FocusedItem.Index - 1;
1110 else if (key == Keys.Down) {
1111 result = FocusedItem.Index + 1;
1112 if (result == items.Count)
1118 int row = FocusedItem.row;
1119 int col = FocusedItem.col;
1125 return item_matrix [row, col - 1].Index;
1128 if (col == (cols - 1))
1130 while (item_matrix [row, col + 1] == null)
1132 return item_matrix [row, col + 1].Index;
1137 return item_matrix [row - 1, col].Index;
1140 if (row == (rows - 1) || row == Items.Count - 1)
1142 while (item_matrix [row + 1, col] == null)
1144 return item_matrix [row + 1, col].Index;
1151 ListViewItem selection_start;
1153 private bool SelectItems (ArrayList sel_items)
1155 bool changed = false;
1156 multiselecting = true;
1157 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1158 foreach (ListViewItem item in curr_items)
1159 if (!sel_items.Contains (item)) {
1160 item.Selected = false;
1163 foreach (ListViewItem item in sel_items)
1164 if (!item.Selected) {
1165 item.Selected = true;
1168 multiselecting = false;
1172 private void UpdateMultiSelection (int index)
1174 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1175 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1176 ListViewItem item = items [index];
1178 if (shift_pressed && selection_start != null) {
1179 ArrayList list = new ArrayList ();
1180 int start = Math.Min (selection_start.Index, index);
1181 int end = Math.Max (selection_start.Index, index);
1182 if (View == View.Details) {
1183 for (int i = start; i <= end; i++)
1184 list.Add (items [i]);
1186 int left = Math.Min (items [start].col, items [end].col);
1187 int right = Math.Max (items [start].col, items [end].col);
1188 int top = Math.Min (items [start].row, items [end].row);
1189 int bottom = Math.Max (items [start].row, items [end].row);
1190 foreach (ListViewItem curr in items)
1191 if (curr.row >= top && curr.row <= bottom &&
1192 curr.col >= left && curr.col <= right)
1195 if (SelectItems (list))
1196 OnSelectedIndexChanged (EventArgs.Empty);
1197 } else if (!ctrl_pressed) {
1198 SelectedItems.Clear ();
1199 SelectedIndices.list.Clear ();
1200 item.Selected = true;
1201 selection_start = item;
1202 OnSelectedIndexChanged (EventArgs.Empty);
1206 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1208 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1214 if (FocusedItem == null)
1215 SetFocusedItem (Items [0]);
1217 switch (ke.KeyCode) {
1220 index = Items.Count - 1;
1231 index = GetAdjustedIndex (ke.KeyCode);
1235 ke.Handled = KeySearchString (ke);
1243 UpdateMultiSelection (index);
1244 else if (!items [index].Selected) {
1245 items [index].Selected = true;
1246 OnSelectedIndexChanged (EventArgs.Empty);
1249 SetFocusedItem (items [index]);
1250 EnsureVisible (index);
1254 internal class ItemControl : Control {
1257 ListViewItem clicked_item;
1258 ListViewItem last_clicked_item;
1259 bool hover_processed = false;
1260 bool checking = false;
1262 public ItemControl (ListView owner)
1265 DoubleClick += new EventHandler(ItemsDoubleClick);
1266 MouseDown += new MouseEventHandler(ItemsMouseDown);
1267 MouseMove += new MouseEventHandler(ItemsMouseMove);
1268 MouseHover += new EventHandler(ItemsMouseHover);
1269 MouseUp += new MouseEventHandler(ItemsMouseUp);
1270 MouseWheel += new MouseEventHandler(ItemsMouseWheel);
1273 void ItemsDoubleClick (object sender, EventArgs e)
1275 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1276 owner.ItemActivate (this, e);
1286 BoxSelect box_select_mode = BoxSelect.None;
1287 ArrayList prev_selection;
1288 Point box_select_start;
1290 Rectangle box_select_rect;
1291 internal Rectangle BoxSelectRectangle {
1292 get { return box_select_rect; }
1294 if (box_select_rect == value)
1297 InvalidateBoxSelectRect ();
1298 box_select_rect = value;
1299 InvalidateBoxSelectRect ();
1303 void InvalidateBoxSelectRect ()
1305 if (BoxSelectRectangle.Size.IsEmpty)
1308 Rectangle edge = BoxSelectRectangle;
1314 edge.Y = BoxSelectRectangle.Bottom - 1;
1316 edge.Y = BoxSelectRectangle.Y - 1;
1318 edge.Height = BoxSelectRectangle.Height + 2;
1320 edge.X = BoxSelectRectangle.Right - 1;
1324 private Rectangle CalculateBoxSelectRectangle (Point pt)
1326 int left = Math.Min (box_select_start.X, pt.X);
1327 int right = Math.Max (box_select_start.X, pt.X);
1328 int top = Math.Min (box_select_start.Y, pt.Y);
1329 int bottom = Math.Max (box_select_start.Y, pt.Y);
1330 return Rectangle.FromLTRB (left, top, right, bottom);
1333 ArrayList BoxSelectedItems {
1335 ArrayList result = new ArrayList ();
1336 foreach (ListViewItem item in owner.Items) {
1337 Rectangle r = item.Bounds;
1339 r.Y += r.Height / 4;
1342 if (BoxSelectRectangle.IntersectsWith (r))
1349 private bool PerformBoxSelection (Point pt)
1351 if (box_select_mode == BoxSelect.None)
1354 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1356 ArrayList box_items = BoxSelectedItems;
1360 switch (box_select_mode) {
1362 case BoxSelect.Normal:
1366 case BoxSelect.Control:
1367 items = new ArrayList ();
1368 foreach (ListViewItem item in prev_selection)
1369 if (!box_items.Contains (item))
1371 foreach (ListViewItem item in box_items)
1372 if (!prev_selection.Contains (item))
1376 case BoxSelect.Shift:
1378 foreach (ListViewItem item in box_items)
1379 prev_selection.Remove (item);
1380 foreach (ListViewItem item in prev_selection)
1385 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1389 owner.SelectItems (items);
1395 private void ToggleCheckState (ListViewItem item)
1397 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1398 item.Checked = !item.Checked;
1399 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1401 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1402 owner.OnItemCheck (ice);
1405 private void ItemsMouseDown (object sender, MouseEventArgs me)
1407 if (owner.items.Count == 0)
1410 Point pt = new Point (me.X, me.Y);
1411 foreach (ListViewItem item in owner.items) {
1412 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1416 ToggleCheckState (item);
1420 if (owner.View == View.Details && !owner.FullRowSelect) {
1421 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1422 clicked_item = item;
1426 if (item.Bounds.Contains (pt)) {
1427 clicked_item = item;
1434 if (clicked_item != null) {
1435 owner.SetFocusedItem (clicked_item);
1436 bool changed = !clicked_item.Selected;
1437 if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0)
1438 owner.UpdateMultiSelection (clicked_item.Index);
1440 clicked_item.Selected = true;
1443 owner.OnSelectedIndexChanged (EventArgs.Empty);
1445 // Raise double click if the item was clicked. On MS the
1446 // double click is only raised if you double click an item
1447 if (me.Clicks > 1) {
1448 owner.OnDoubleClick (EventArgs.Empty);
1449 if (owner.CheckBoxes)
1450 ToggleCheckState (clicked_item);
1451 } else if (me.Clicks == 1)
1452 owner.OnClick (EventArgs.Empty);
1454 if (owner.MultiSelect) {
1455 Keys mods = XplatUI.State.ModifierKeys;
1456 if ((mods & Keys.Shift) != 0)
1457 box_select_mode = BoxSelect.Shift;
1458 else if ((mods & Keys.Control) != 0)
1459 box_select_mode = BoxSelect.Control;
1461 box_select_mode = BoxSelect.Normal;
1462 box_select_start = pt;
1463 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1464 } else if (owner.selected_indices.Count > 0) {
1465 owner.SelectedItems.Clear ();
1466 owner.SelectedIndices.list.Clear ();
1467 owner.OnSelectedIndexChanged (EventArgs.Empty);
1472 private void ItemsMouseMove (object sender, MouseEventArgs me)
1474 if (PerformBoxSelection (new Point (me.X, me.Y)))
1477 if (owner.HoverSelection && hover_processed) {
1479 Point pt = PointToClient (Control.MousePosition);
1480 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1481 if (item == null || item.Selected)
1484 hover_processed = false;
1485 XplatUI.ResetMouseHover (Handle);
1490 private void ItemsMouseHover (object sender, EventArgs e)
1492 if (Capture || !owner.HoverSelection)
1495 hover_processed = true;
1496 Point pt = PointToClient (Control.MousePosition);
1497 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1502 item.Selected = true;
1503 owner.OnSelectedIndexChanged (new EventArgs ());
1506 private void ItemsMouseUp (object sender, MouseEventArgs me)
1509 if (owner.Items.Count == 0)
1512 Point pt = new Point (me.X, me.Y);
1514 Rectangle rect = Rectangle.Empty;
1515 if (clicked_item != null) {
1516 if (owner.view == View.Details && !owner.full_row_select)
1517 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1519 rect = clicked_item.Bounds;
1521 if (rect.Contains (pt)) {
1522 switch (owner.activation) {
1523 case ItemActivation.OneClick:
1524 owner.OnItemActivate (EventArgs.Empty);
1527 case ItemActivation.TwoClick:
1528 if (last_clicked_item == clicked_item) {
1529 owner.OnItemActivate (EventArgs.Empty);
1530 last_clicked_item = null;
1532 last_clicked_item = clicked_item;
1535 // DoubleClick activation is handled in another handler
1539 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1540 // Need this to clean up background clicks
1541 owner.SelectedItems.Clear ();
1542 owner.SelectedIndices.list.Clear ();
1543 owner.OnSelectedIndexChanged (EventArgs.Empty);
1546 clicked_item = null;
1547 box_select_start = Point.Empty;
1548 BoxSelectRectangle = Rectangle.Empty;
1549 prev_selection = null;
1550 box_select_mode = BoxSelect.None;
1554 private void ItemsMouseWheel (object sender, MouseEventArgs me)
1556 if (owner.Items.Count == 0)
1559 int lines = me.Delta / 120;
1564 switch (owner.View) {
1566 case View.SmallIcon:
1567 owner.Scroll (owner.v_scroll, -owner.Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1569 case View.LargeIcon:
1570 owner.Scroll (owner.v_scroll, -(owner.Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1573 owner.Scroll (owner.h_scroll, -owner.Items [0].Bounds.Width * lines);
1578 internal override void OnPaintInternal (PaintEventArgs pe)
1580 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1583 internal override void OnGotFocusInternal (EventArgs e)
1589 internal override void OnPaintInternal (PaintEventArgs pe)
1594 CalculateScrollBars ();
1597 void FocusChanged (object o, EventArgs args)
1599 if (Items.Count == 0)
1602 if (FocusedItem == null)
1603 SetFocusedItem (Items [0]);
1605 item_control.Invalidate (FocusedItem.Bounds);
1608 private void ListView_SizeChanged (object sender, EventArgs e)
1610 CalculateListView (alignment);
1613 private void SetFocusedItem (ListViewItem item)
1615 if (focused_item != null)
1616 focused_item.Focused = false;
1619 item.Focused = true;
1621 focused_item = item;
1624 private void HorizontalScroller (object sender, EventArgs e)
1626 // Avoid unnecessary flickering, when button is
1627 // kept pressed at the end
1628 if (h_marker != h_scroll.Value) {
1630 int pixels = h_marker - h_scroll.Value;
1632 h_marker = h_scroll.Value;
1633 if (header_control.Visible)
1634 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1636 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1640 private void VerticalScroller (object sender, EventArgs e)
1642 // Avoid unnecessary flickering, when button is
1643 // kept pressed at the end
1644 if (v_marker != v_scroll.Value) {
1645 int pixels = v_marker - v_scroll.Value;
1646 Rectangle area = item_control.ClientRectangle;
1647 v_marker = v_scroll.Value;
1648 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1651 #endregion // Internal Methods Properties
1653 #region Protected Methods
1654 protected override void CreateHandle ()
1656 base.CreateHandle ();
1659 protected override void Dispose (bool disposing)
1662 h_scroll.Dispose ();
1663 v_scroll.Dispose ();
1665 large_image_list = null;
1666 small_image_list = null;
1667 state_image_list = null;
1670 base.Dispose (disposing);
1673 protected override bool IsInputKey (Keys keyData)
1690 return base.IsInputKey (keyData);
1693 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1695 if (AfterLabelEdit != null)
1696 AfterLabelEdit (this, e);
1699 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1701 if (BeforeLabelEdit != null)
1702 BeforeLabelEdit (this, e);
1705 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1707 if (ColumnClick != null)
1708 ColumnClick (this, e);
1711 protected override void OnEnabledChanged (EventArgs e)
1713 base.OnEnabledChanged (e);
1716 protected override void OnFontChanged (EventArgs e)
1718 base.OnFontChanged (e);
1722 protected override void OnHandleCreated (EventArgs e)
1724 base.OnHandleCreated (e);
1727 protected override void OnHandleDestroyed (EventArgs e)
1729 base.OnHandleDestroyed (e);
1732 protected virtual void OnItemActivate (EventArgs e)
1734 if (ItemActivate != null)
1735 ItemActivate (this, e);
1738 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1740 if (ItemCheck != null)
1741 ItemCheck (this, ice);
1744 protected virtual void OnItemDrag (ItemDragEventArgs e)
1746 if (ItemDrag != null)
1750 protected virtual void OnSelectedIndexChanged (EventArgs e)
1752 if (SelectedIndexChanged != null)
1753 SelectedIndexChanged (this, e);
1756 protected override void OnSystemColorsChanged (EventArgs e)
1758 base.OnSystemColorsChanged (e);
1761 protected void RealizeProperties ()
1766 protected void UpdateExtendedStyles ()
1771 protected override void WndProc (ref Message m)
1773 base.WndProc (ref m);
1775 #endregion // Protected Methods
1777 #region Public Instance Methods
1778 public void ArrangeIcons ()
1780 ArrangeIcons (this.alignment);
1783 public void ArrangeIcons (ListViewAlignment alignment)
1785 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1786 if (view == View.LargeIcon || view == View.SmallIcon) {
1787 this.CalculateListView (alignment);
1788 // we have done the calculations already
1789 this.Redraw (false);
1793 public void BeginUpdate ()
1795 // flag to avoid painting
1799 public void Clear ()
1802 items.Clear (); // Redraw (true) called here
1805 public void EndUpdate ()
1807 // flag to avoid painting
1810 // probably, now we need a redraw with recalculations
1814 public void EnsureVisible (int index)
1816 if (index < 0 || index >= items.Count || scrollable == false)
1819 Rectangle view_rect = item_control.ClientRectangle;
1820 Rectangle bounds = items [index].Bounds;
1822 if (view_rect.Contains (bounds))
1825 if (bounds.Left < 0)
1826 h_scroll.Value += bounds.Left;
1827 else if (bounds.Right > view_rect.Right)
1828 h_scroll.Value += (bounds.Right - view_rect.Right);
1831 v_scroll.Value += bounds.Top;
1832 else if (bounds.Bottom > view_rect.Bottom)
1833 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1836 public ListViewItem GetItemAt (int x, int y)
1838 foreach (ListViewItem item in items) {
1839 if (item.Bounds.Contains (x, y))
1845 public Rectangle GetItemRect (int index)
1847 return GetItemRect (index, ItemBoundsPortion.Entire);
1850 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1852 if (index < 0 || index >= items.Count)
1853 throw new IndexOutOfRangeException ("index");
1855 return items [index].GetBounds (portion);
1860 if (sort_order != SortOrder.None)
1861 items.list.Sort (item_sorter);
1863 if (sort_order == SortOrder.Descending)
1864 items.list.Reverse ();
1869 public override string ToString ()
1871 int count = this.Items.Count;
1874 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1876 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1878 #endregion // Public Instance Methods
1883 class HeaderControl : Control {
1886 bool column_resize_active = false;
1887 ColumnHeader resize_column;
1888 ColumnHeader clicked_column;
1889 ColumnHeader drag_column;
1891 int drag_to_index = -1;
1893 public HeaderControl (ListView owner)
1896 MouseDown += new MouseEventHandler (HeaderMouseDown);
1897 MouseMove += new MouseEventHandler (HeaderMouseMove);
1898 MouseUp += new MouseEventHandler (HeaderMouseUp);
1901 private ColumnHeader ColumnAtX (int x)
1903 Point pt = new Point (x, 0);
1904 ColumnHeader result = null;
1905 foreach (ColumnHeader col in owner.Columns) {
1906 if (col.Rect.Contains (pt)) {
1914 private int GetReorderedIndex (ColumnHeader col)
1916 if (owner.reordered_column_indices == null)
1919 for (int i = 0; i < owner.Columns.Count; i++)
1920 if (owner.reordered_column_indices [i] == col.Index)
1922 throw new Exception ("Column index missing from reordered array");
1925 private void HeaderMouseDown (object sender, MouseEventArgs me)
1927 if (resize_column != null) {
1928 column_resize_active = true;
1933 clicked_column = ColumnAtX (me.X + owner.h_marker);
1935 if (clicked_column != null) {
1937 if (owner.AllowColumnReorder) {
1939 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
1940 drag_column.column_rect = clicked_column.Rect;
1941 drag_to_index = GetReorderedIndex (clicked_column);
1943 clicked_column.pressed = true;
1944 Rectangle bounds = clicked_column.Rect;
1945 bounds.X -= owner.h_marker;
1946 Invalidate (bounds);
1951 private void HeaderMouseMove (object sender, MouseEventArgs me)
1953 Point pt = new Point (me.X + owner.h_marker, me.Y);
1955 if (column_resize_active) {
1956 resize_column.Width = pt.X - resize_column.X;
1957 if (resize_column.Width < 0)
1958 resize_column.Width = 0;
1962 resize_column = null;
1964 if (clicked_column != null) {
1965 if (owner.AllowColumnReorder) {
1968 r = drag_column.column_rect;
1969 r.X = clicked_column.Rect.X + me.X - drag_x;
1970 drag_column.column_rect = r;
1972 int x = me.X + owner.h_marker;
1973 ColumnHeader over = ColumnAtX (x);
1975 drag_to_index = owner.Columns.Count;
1976 else if (x < over.X + over.Width / 2)
1977 drag_to_index = GetReorderedIndex (over);
1979 drag_to_index = GetReorderedIndex (over) + 1;
1982 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
1983 bool pressed = clicked_column.pressed;
1984 clicked_column.pressed = over == clicked_column;
1985 if (clicked_column.pressed ^ pressed) {
1986 Rectangle bounds = clicked_column.Rect;
1987 bounds.X -= owner.h_marker;
1988 Invalidate (bounds);
1994 for (int i = 0; i < owner.Columns.Count; i++) {
1995 Rectangle zone = owner.Columns [i].Rect;
1996 zone.X = zone.Right - 5;
1998 if (zone.Contains (pt)) {
1999 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2001 resize_column = owner.Columns [i];
2006 if (resize_column == null)
2007 Cursor = Cursors.Default;
2009 Cursor = Cursors.VSplit;
2012 void HeaderMouseUp (object sender, MouseEventArgs me)
2016 if (column_resize_active) {
2017 column_resize_active = false;
2018 resize_column = null;
2019 Cursor = Cursors.Default;
2023 if (clicked_column != null && clicked_column.pressed) {
2024 clicked_column.pressed = false;
2025 Rectangle bounds = clicked_column.Rect;
2026 bounds.X -= owner.h_marker;
2027 Invalidate (bounds);
2028 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2031 if (drag_column != null && owner.AllowColumnReorder) {
2033 if (drag_to_index > GetReorderedIndex (clicked_column))
2035 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2036 owner.ReorderColumn (clicked_column, drag_to_index);
2041 clicked_column = null;
2044 internal override void OnPaintInternal (PaintEventArgs pe)
2049 Theme theme = ThemeEngine.Current;
2050 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2052 if (drag_column == null)
2056 if (drag_to_index == owner.Columns.Count)
2057 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2059 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2060 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2063 protected override void WndProc (ref Message m)
2065 switch ((Msg)m.Msg) {
2066 case Msg.WM_SETFOCUS:
2070 base.WndProc (ref m);
2076 private class ItemComparer : IComparer {
2078 public int Compare (object x, object y)
2080 ListViewItem item_x = x as ListViewItem;
2081 ListViewItem item_y = y as ListViewItem;
2082 return String.Compare (item_x.Text, item_y.Text);
2086 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2088 internal ArrayList list;
2089 private ListView owner;
2091 #region Public Constructor
2092 public CheckedIndexCollection (ListView owner)
2094 list = new ArrayList ();
2097 #endregion // Public Constructor
2099 #region Public Properties
2102 get { return list.Count; }
2105 public bool IsReadOnly {
2106 get { return true; }
2109 public int this [int index] {
2111 if (index < 0 || index >= list.Count)
2112 throw new ArgumentOutOfRangeException ("index");
2113 return (int) list [index];
2117 bool ICollection.IsSynchronized {
2118 get { return false; }
2121 object ICollection.SyncRoot {
2122 get { return this; }
2125 bool IList.IsFixedSize {
2126 get { return true; }
2129 object IList.this [int index] {
2130 get { return this [index]; }
2131 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2133 #endregion // Public Properties
2135 #region Public Methods
2136 public bool Contains (int checkedIndex)
2138 return list.Contains (checkedIndex);
2141 public IEnumerator GetEnumerator ()
2143 return list.GetEnumerator ();
2146 void ICollection.CopyTo (Array dest, int index)
2148 list.CopyTo (dest, index);
2151 int IList.Add (object value)
2153 throw new NotSupportedException ("Add operation is not supported.");
2158 throw new NotSupportedException ("Clear operation is not supported.");
2161 bool IList.Contains (object checkedIndex)
2163 return list.Contains (checkedIndex);
2166 int IList.IndexOf (object checkedIndex)
2168 return list.IndexOf (checkedIndex);
2171 void IList.Insert (int index, object value)
2173 throw new NotSupportedException ("Insert operation is not supported.");
2176 void IList.Remove (object value)
2178 throw new NotSupportedException ("Remove operation is not supported.");
2181 void IList.RemoveAt (int index)
2183 throw new NotSupportedException ("RemoveAt operation is not supported.");
2186 public int IndexOf (int checkedIndex)
2188 return list.IndexOf (checkedIndex);
2190 #endregion // Public Methods
2192 } // CheckedIndexCollection
2194 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2196 internal ArrayList list;
2197 private ListView owner;
2199 #region Public Constructor
2200 public CheckedListViewItemCollection (ListView owner)
2202 list = new ArrayList ();
2205 #endregion // Public Constructor
2207 #region Public Properties
2210 get { return list.Count; }
2213 public bool IsReadOnly {
2214 get { return true; }
2217 public ListViewItem this [int index] {
2219 if (index < 0 || index >= list.Count)
2220 throw new ArgumentOutOfRangeException ("index");
2221 return (ListViewItem) list [index];
2225 bool ICollection.IsSynchronized {
2226 get { return list.IsSynchronized; }
2229 object ICollection.SyncRoot {
2230 get { return this; }
2233 bool IList.IsFixedSize {
2234 get { return true; }
2237 object IList.this [int index] {
2238 get { return this [index]; }
2239 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2241 #endregion // Public Properties
2243 #region Public Methods
2244 public bool Contains (ListViewItem item)
2246 return list.Contains (item);
2249 public void CopyTo (Array dest, int index)
2251 list.CopyTo (dest, index);
2254 public IEnumerator GetEnumerator ()
2256 return list.GetEnumerator ();
2259 int IList.Add (object value)
2261 throw new NotSupportedException ("Add operation is not supported.");
2266 throw new NotSupportedException ("Clear operation is not supported.");
2269 bool IList.Contains (object item)
2271 return list.Contains (item);
2274 int IList.IndexOf (object item)
2276 return list.IndexOf (item);
2279 void IList.Insert (int index, object value)
2281 throw new NotSupportedException ("Insert operation is not supported.");
2284 void IList.Remove (object value)
2286 throw new NotSupportedException ("Remove operation is not supported.");
2289 void IList.RemoveAt (int index)
2291 throw new NotSupportedException ("RemoveAt operation is not supported.");
2294 public int IndexOf (ListViewItem item)
2296 return list.IndexOf (item);
2298 #endregion // Public Methods
2300 } // CheckedListViewItemCollection
2302 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2304 internal ArrayList list;
2305 private ListView owner;
2307 #region Public Constructor
2308 public ColumnHeaderCollection (ListView owner)
2310 list = new ArrayList ();
2313 #endregion // Public Constructor
2315 #region Public Properties
2318 get { return list.Count; }
2321 public bool IsReadOnly {
2322 get { return false; }
2325 public virtual ColumnHeader this [int index] {
2327 if (index < 0 || index >= list.Count)
2328 throw new ArgumentOutOfRangeException ("index");
2329 return (ColumnHeader) list [index];
2333 bool ICollection.IsSynchronized {
2334 get { return true; }
2337 object ICollection.SyncRoot {
2338 get { return this; }
2341 bool IList.IsFixedSize {
2342 get { return list.IsFixedSize; }
2345 object IList.this [int index] {
2346 get { return this [index]; }
2347 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2349 #endregion // Public Properties
2351 #region Public Methods
2352 public virtual int Add (ColumnHeader value)
2355 value.owner = this.owner;
2356 idx = list.Add (value);
2357 if (owner.IsHandleCreated) {
2358 owner.Redraw (true);
2363 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2365 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2366 this.Add (colHeader);
2370 public virtual void AddRange (ColumnHeader [] values)
2372 foreach (ColumnHeader colHeader in values) {
2373 colHeader.owner = this.owner;
2377 owner.Redraw (true);
2380 public virtual void Clear ()
2383 owner.Redraw (true);
2386 public bool Contains (ColumnHeader value)
2388 return list.Contains (value);
2391 public IEnumerator GetEnumerator ()
2393 return list.GetEnumerator ();
2396 void ICollection.CopyTo (Array dest, int index)
2398 list.CopyTo (dest, index);
2401 int IList.Add (object value)
2403 if (! (value is ColumnHeader)) {
2404 throw new ArgumentException ("Not of type ColumnHeader", "value");
2407 return this.Add ((ColumnHeader) value);
2410 bool IList.Contains (object value)
2412 if (! (value is ColumnHeader)) {
2413 throw new ArgumentException ("Not of type ColumnHeader", "value");
2416 return this.Contains ((ColumnHeader) value);
2419 int IList.IndexOf (object value)
2421 if (! (value is ColumnHeader)) {
2422 throw new ArgumentException ("Not of type ColumnHeader", "value");
2425 return this.IndexOf ((ColumnHeader) value);
2428 void IList.Insert (int index, object value)
2430 if (! (value is ColumnHeader)) {
2431 throw new ArgumentException ("Not of type ColumnHeader", "value");
2434 this.Insert (index, (ColumnHeader) value);
2437 void IList.Remove (object value)
2439 if (! (value is ColumnHeader)) {
2440 throw new ArgumentException ("Not of type ColumnHeader", "value");
2443 this.Remove ((ColumnHeader) value);
2446 public int IndexOf (ColumnHeader value)
2448 return list.IndexOf (value);
2451 public void Insert (int index, ColumnHeader value)
2453 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2454 // but it's really only greater.
2455 if (index < 0 || index > list.Count)
2456 throw new ArgumentOutOfRangeException ("index");
2458 value.owner = this.owner;
2459 list.Insert (index, value);
2460 owner.Redraw (true);
2463 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2465 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2466 this.Insert (index, colHeader);
2469 public virtual void Remove (ColumnHeader column)
2471 // TODO: Update Column internal index ?
2472 list.Remove (column);
2473 owner.Redraw (true);
2476 public virtual void RemoveAt (int index)
2478 if (index < 0 || index >= list.Count)
2479 throw new ArgumentOutOfRangeException ("index");
2481 // TODO: Update Column internal index ?
2482 list.RemoveAt (index);
2483 owner.Redraw (true);
2485 #endregion // Public Methods
2488 } // ColumnHeaderCollection
2490 public class ListViewItemCollection : IList, ICollection, IEnumerable
2492 internal ArrayList list;
2493 private ListView owner;
2495 #region Public Constructor
2496 public ListViewItemCollection (ListView owner)
2498 list = new ArrayList ();
2501 #endregion // Public Constructor
2503 #region Public Properties
2506 get { return list.Count; }
2509 public bool IsReadOnly {
2510 get { return false; }
2513 public virtual ListViewItem this [int displayIndex] {
2515 if (displayIndex < 0 || displayIndex >= list.Count)
2516 throw new ArgumentOutOfRangeException ("displayIndex");
2517 return (ListViewItem) list [displayIndex];
2521 if (displayIndex < 0 || displayIndex >= list.Count)
2522 throw new ArgumentOutOfRangeException ("displayIndex");
2524 if (list.Contains (value))
2525 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2527 value.Owner = owner;
2528 list [displayIndex] = value;
2530 owner.Redraw (true);
2534 bool ICollection.IsSynchronized {
2535 get { return true; }
2538 object ICollection.SyncRoot {
2539 get { return this; }
2542 bool IList.IsFixedSize {
2543 get { return list.IsFixedSize; }
2546 object IList.this [int index] {
2547 get { return this [index]; }
2549 if (value is ListViewItem)
2550 this [index] = (ListViewItem) value;
2552 this [index] = new ListViewItem (value.ToString ());
2555 #endregion // Public Properties
2557 #region Public Methods
2558 public virtual ListViewItem Add (ListViewItem value)
2560 if (list.Contains (value))
2561 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2563 value.Owner = owner;
2566 if (owner.Sorting != SortOrder.None)
2569 owner.Redraw (true);
2574 public virtual ListViewItem Add (string text)
2576 ListViewItem item = new ListViewItem (text);
2577 return this.Add (item);
2580 public virtual ListViewItem Add (string text, int imageIndex)
2582 ListViewItem item = new ListViewItem (text, imageIndex);
2583 return this.Add (item);
2586 public void AddRange (ListViewItem [] values)
2589 owner.SelectedItems.list.Clear ();
2590 owner.SelectedIndices.list.Clear ();
2591 owner.CheckedItems.list.Clear ();
2592 owner.CheckedIndices.list.Clear ();
2594 foreach (ListViewItem item in values) {
2599 if (owner.Sorting != SortOrder.None)
2602 owner.Redraw (true);
2605 public virtual void Clear ()
2607 owner.SetFocusedItem (null);
2608 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2610 owner.SelectedItems.list.Clear ();
2611 owner.SelectedIndices.list.Clear ();
2612 owner.CheckedItems.list.Clear ();
2613 owner.CheckedIndices.list.Clear ();
2614 owner.Redraw (true);
2617 public bool Contains (ListViewItem item)
2619 return list.Contains (item);
2622 public void CopyTo (Array dest, int index)
2624 list.CopyTo (dest, index);
2627 public IEnumerator GetEnumerator ()
2629 return list.GetEnumerator ();
2632 int IList.Add (object item)
2637 if (item is ListViewItem) {
2638 li = (ListViewItem) item;
2639 if (list.Contains (li))
2640 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2643 li = new ListViewItem (item.ToString ());
2646 result = list.Add (li);
2647 owner.Redraw (true);
2652 bool IList.Contains (object item)
2654 return list.Contains (item);
2657 int IList.IndexOf (object item)
2659 return list.IndexOf (item);
2662 void IList.Insert (int index, object item)
2664 if (item is ListViewItem)
2665 this.Insert (index, (ListViewItem) item);
2667 this.Insert (index, item.ToString ());
2670 void IList.Remove (object item)
2672 Remove ((ListViewItem) item);
2675 public int IndexOf (ListViewItem item)
2677 return list.IndexOf (item);
2680 public ListViewItem Insert (int index, ListViewItem item)
2682 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2683 // but it's really only greater.
2684 if (index < 0 || index > list.Count)
2685 throw new ArgumentOutOfRangeException ("index");
2687 if (list.Contains (item))
2688 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2691 list.Insert (index, item);
2692 owner.Redraw (true);
2696 public ListViewItem Insert (int index, string text)
2698 return this.Insert (index, new ListViewItem (text));
2701 public ListViewItem Insert (int index, string text, int imageIndex)
2703 return this.Insert (index, new ListViewItem (text, imageIndex));
2706 public virtual void Remove (ListViewItem item)
2708 if (!list.Contains (item))
2711 owner.SelectedItems.list.Remove (item);
2712 owner.SelectedIndices.list.Remove (item.Index);
2713 owner.CheckedItems.list.Remove (item);
2714 owner.CheckedIndices.list.Remove (item.Index);
2716 owner.Redraw (true);
2719 public virtual void RemoveAt (int index)
2721 if (index < 0 || index >= list.Count)
2722 throw new ArgumentOutOfRangeException ("index");
2724 list.RemoveAt (index);
2725 owner.SelectedItems.list.RemoveAt (index);
2726 owner.SelectedIndices.list.RemoveAt (index);
2727 owner.CheckedItems.list.RemoveAt (index);
2728 owner.CheckedIndices.list.RemoveAt (index);
2729 owner.Redraw (false);
2731 #endregion // Public Methods
2733 } // ListViewItemCollection
2735 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2737 internal ArrayList list;
2738 private ListView owner;
2740 #region Public Constructor
2741 public SelectedIndexCollection (ListView owner)
2743 list = new ArrayList ();
2746 #endregion // Public Constructor
2748 #region Public Properties
2751 get { return list.Count; }
2754 public bool IsReadOnly {
2755 get { return true; }
2758 public int this [int index] {
2760 if (index < 0 || index >= list.Count)
2761 throw new ArgumentOutOfRangeException ("index");
2762 return (int) list [index];
2766 bool ICollection.IsSynchronized {
2767 get { return list.IsSynchronized; }
2770 object ICollection.SyncRoot {
2771 get { return this; }
2774 bool IList.IsFixedSize {
2775 get { return true; }
2778 object IList.this [int index] {
2779 get { return this [index]; }
2780 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2782 #endregion // Public Properties
2784 #region Public Methods
2785 public bool Contains (int selectedIndex)
2787 return list.Contains (selectedIndex);
2790 public void CopyTo (Array dest, int index)
2792 list.CopyTo (dest, index);
2795 public IEnumerator GetEnumerator ()
2797 return list.GetEnumerator ();
2800 int IList.Add (object value)
2802 throw new NotSupportedException ("Add operation is not supported.");
2807 throw new NotSupportedException ("Clear operation is not supported.");
2810 bool IList.Contains (object selectedIndex)
2812 return list.Contains (selectedIndex);
2815 int IList.IndexOf (object selectedIndex)
2817 return list.IndexOf (selectedIndex);
2820 void IList.Insert (int index, object value)
2822 throw new NotSupportedException ("Insert operation is not supported.");
2825 void IList.Remove (object value)
2827 throw new NotSupportedException ("Remove operation is not supported.");
2830 void IList.RemoveAt (int index)
2832 throw new NotSupportedException ("RemoveAt operation is not supported.");
2835 public int IndexOf (int selectedIndex)
2837 return list.IndexOf (selectedIndex);
2839 #endregion // Public Methods
2841 } // SelectedIndexCollection
2843 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2845 internal ArrayList list;
2846 private ListView owner;
2848 #region Public Constructor
2849 public SelectedListViewItemCollection (ListView owner)
2851 list = new ArrayList ();
2854 #endregion // Public Constructor
2856 #region Public Properties
2859 get { return list.Count; }
2862 public bool IsReadOnly {
2863 get { return true; }
2866 public ListViewItem this [int index] {
2868 if (index < 0 || index >= list.Count)
2869 throw new ArgumentOutOfRangeException ("index");
2870 return (ListViewItem) list [index];
2874 bool ICollection.IsSynchronized {
2875 get { return list.IsSynchronized; }
2878 object ICollection.SyncRoot {
2879 get { return this; }
2882 bool IList.IsFixedSize {
2883 get { return true; }
2886 object IList.this [int index] {
2887 get { return this [index]; }
2888 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2890 #endregion // Public Properties
2892 #region Public Methods
2893 public void Clear ()
2895 ArrayList copy = (ArrayList) list.Clone ();
2896 for (int i = 0; i < copy.Count; i++)
2897 ((ListViewItem) copy [i]).Selected = false;
2902 public bool Contains (ListViewItem item)
2904 return list.Contains (item);
2907 public void CopyTo (Array dest, int index)
2909 list.CopyTo (dest, index);
2912 public IEnumerator GetEnumerator ()
2914 return list.GetEnumerator ();
2917 int IList.Add (object value)
2919 throw new NotSupportedException ("Add operation is not supported.");
2922 bool IList.Contains (object item)
2924 return list.Contains (item);
2927 int IList.IndexOf (object item)
2929 return list.IndexOf (item);
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 (ListViewItem item)
2949 return list.IndexOf (item);
2951 #endregion // Public Methods
2953 } // SelectedListViewItemCollection
2955 #endregion // Subclasses