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;
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;
783 // making a scroll bar visible might make
784 // other scroll bar visible
785 if (layout_wd > client_area.Right) {
786 h_scroll.Visible = true;
787 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
788 v_scroll.Visible = true;
790 v_scroll.Visible = false;
791 } else if (layout_ht > client_area.Bottom) {
792 v_scroll.Visible = true;
793 if ((layout_wd + v_scroll.Width) > client_area.Right)
794 h_scroll.Visible = true;
796 h_scroll.Visible = false;
798 h_scroll.Visible = false;
799 v_scroll.Visible = false;
802 item_control.Height = ClientRectangle.Height - header_control.Height;
804 if (h_scroll.Visible) {
805 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
806 h_scroll.Minimum = 0;
808 // if v_scroll is visible, adjust the maximum of the
809 // h_scroll to account for the width of v_scroll
810 if (v_scroll.Visible) {
811 h_scroll.Maximum = layout_wd + v_scroll.Width;
812 h_scroll.Width = client_area.Width - v_scroll.Width;
815 h_scroll.Maximum = layout_wd;
816 h_scroll.Width = client_area.Width;
819 h_scroll.LargeChange = client_area.Width;
820 h_scroll.SmallChange = Font.Height;
821 item_control.Height -= h_scroll.Height;
824 if (header_control.Visible)
825 header_control.Width = ClientRectangle.Width;
826 item_control.Width = ClientRectangle.Width;
828 if (v_scroll.Visible) {
829 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
830 v_scroll.Minimum = 0;
832 // if h_scroll is visible, adjust the maximum of the
833 // v_scroll to account for the height of h_scroll
834 if (h_scroll.Visible) {
835 v_scroll.Maximum = layout_ht + h_scroll.Height;
836 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
838 v_scroll.Maximum = layout_ht;
839 v_scroll.Height = client_area.Height;
842 v_scroll.LargeChange = client_area.Height;
843 v_scroll.SmallChange = Font.Height;
844 if (header_control.Visible)
845 header_control.Width -= v_scroll.Width;
846 item_control.Width -= v_scroll.Width;
850 ColumnHeader GetReorderedColumn (int index)
852 if (reordered_column_indices == null)
853 return Columns [index];
855 return Columns [reordered_column_indices [index]];
858 void ReorderColumn (ColumnHeader col, int index)
860 if (reordered_column_indices == null) {
861 reordered_column_indices = new int [Columns.Count];
862 for (int i = 0; i < Columns.Count; i++)
863 reordered_column_indices [i] = i;
866 if (reordered_column_indices [index] == col.Index)
869 int[] curr = reordered_column_indices;
870 int[] result = new int [Columns.Count];
872 for (int i = 0; i < Columns.Count; i++) {
873 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
877 result [i] = col.Index;
879 result [i] = curr [curr_idx++];
882 reordered_column_indices = result;
884 header_control.Invalidate ();
885 item_control.Invalidate ();
888 Size LargeIconItemSize {
890 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
891 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
892 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
893 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
894 return new Size (w, h);
898 Size SmallIconItemSize {
900 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
901 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
902 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
903 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
904 return new Size (w, h);
910 ListViewItem[,] item_matrix;
912 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
914 header_control.Visible = false;
915 header_control.Size = Size.Empty;
916 item_control.Visible = true;
917 item_control.Location = Point.Empty;
919 if (items.Count == 0)
922 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
924 Rectangle area = ClientRectangle;
927 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
930 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
932 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
935 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
938 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
939 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
940 item_matrix = new ListViewItem [rows, cols];
943 foreach (ListViewItem item in items) {
944 int x = col * (sz.Width + x_spacing);
945 int y = row * (sz.Height + y_spacing);
946 item.Location = new Point (x, y);
950 item_matrix [row, col] = item;
964 item_control.Size = new Size (layout_wd, layout_ht);
969 if (header_style == ColumnHeaderStyle.None) {
970 header_control.Visible = false;
971 header_control.Size = Size.Empty;
976 for (int i = 0; i < Columns.Count; i++) {
977 ColumnHeader col = GetReorderedColumn (i);
980 col.CalcColumnHeader ();
984 if (x < ClientRectangle.Width)
985 x = ClientRectangle.Width;
987 header_control.Width = x;
988 header_control.Height = columns [0].Ht;
989 header_control.Visible = true;
992 void LayoutDetails ()
994 if (columns.Count == 0) {
995 header_control.Visible = false;
996 item_control.Visible = false;
1002 item_control.Visible = true;
1003 item_control.Location = new Point (0, header_control.Height);
1006 if (items.Count > 0) {
1007 foreach (ListViewItem item in items) {
1009 item.Location = new Point (0, y);
1010 y += item.Bounds.Height + 2;
1013 // some space for bottom gridline
1018 layout_wd = Math.Max (header_control.Width, item_control.Width);
1019 layout_ht = y + header_control.Height;
1022 private void CalculateListView (ListViewAlignment align)
1031 case View.SmallIcon:
1032 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1035 case View.LargeIcon:
1036 LayoutIcons (true, alignment == ListViewAlignment.Left,
1037 ThemeEngine.Current.ListViewHorizontalSpacing,
1038 ThemeEngine.Current.ListViewVerticalSpacing);
1042 LayoutIcons (false, true, 4, 2);
1046 CalculateScrollBars ();
1049 internal void UpdateSelection (ListViewItem item)
1051 if (item.Selected) {
1053 if (!CanMultiselect && SelectedItems.Count > 0) {
1054 SelectedItems.Clear ();
1055 SelectedIndices.list.Clear ();
1058 if (!SelectedItems.Contains (item)) {
1059 SelectedItems.list.Add (item);
1060 SelectedIndices.list.Add (item.Index);
1063 SelectedItems.list.Remove (item);
1064 SelectedIndices.list.Remove (item.Index);
1068 private bool KeySearchString (KeyEventArgs ke)
1070 int current_tickcnt = Environment.TickCount;
1071 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1072 keysearch_text = string.Empty;
1075 keysearch_text += (char) ke.KeyData;
1076 keysearch_tickcnt = current_tickcnt;
1078 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1081 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1082 CompareOptions.IgnoreCase)) {
1083 SetFocusedItem (Items [i]);
1084 items [i].Selected = true;
1088 i = (i + 1 < Items.Count) ? i+1 : 0;
1096 int GetAdjustedIndex (Keys key)
1100 if (View == View.Details) {
1102 result = FocusedItem.Index - 1;
1103 else if (key == Keys.Down) {
1104 result = FocusedItem.Index + 1;
1105 if (result == items.Count)
1111 int row = FocusedItem.row;
1112 int col = FocusedItem.col;
1118 return item_matrix [row, col - 1].Index;
1121 if (col == (cols - 1))
1123 while (item_matrix [row, col + 1] == null)
1125 return item_matrix [row, col + 1].Index;
1130 return item_matrix [row - 1, col].Index;
1133 if (row == (rows - 1))
1135 while (item_matrix [row + 1, col] == null)
1137 return item_matrix [row + 1, col].Index;
1144 ListViewItem selection_start;
1146 private bool SelectItems (ArrayList sel_items)
1148 bool changed = false;
1149 multiselecting = true;
1150 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1151 foreach (ListViewItem item in curr_items)
1152 if (!sel_items.Contains (item)) {
1153 item.Selected = false;
1156 foreach (ListViewItem item in sel_items)
1157 if (!item.Selected) {
1158 item.Selected = true;
1161 multiselecting = false;
1165 private void UpdateMultiSelection (int index)
1167 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1168 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1169 ListViewItem item = items [index];
1171 if (shift_pressed && selection_start != null) {
1172 ArrayList list = new ArrayList ();
1173 int start = Math.Min (selection_start.Index, index);
1174 int end = Math.Max (selection_start.Index, index);
1175 if (View == View.Details) {
1176 for (int i = start; i <= end; i++)
1177 list.Add (items [i]);
1179 int left = Math.Min (items [start].col, items [end].col);
1180 int right = Math.Max (items [start].col, items [end].col);
1181 int top = Math.Min (items [start].row, items [end].row);
1182 int bottom = Math.Max (items [start].row, items [end].row);
1183 foreach (ListViewItem curr in items)
1184 if (curr.row >= top && curr.row <= bottom &&
1185 curr.col >= left && curr.col <= right)
1188 if (SelectItems (list))
1189 OnSelectedIndexChanged (EventArgs.Empty);
1190 } else if (!ctrl_pressed) {
1191 SelectedItems.Clear ();
1192 SelectedIndices.list.Clear ();
1193 item.Selected = true;
1194 selection_start = item;
1195 OnSelectedIndexChanged (EventArgs.Empty);
1199 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1201 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1207 switch (ke.KeyCode) {
1210 index = Items.Count - 1;
1221 index = GetAdjustedIndex (ke.KeyCode);
1225 ke.Handled = KeySearchString (ke);
1233 UpdateMultiSelection (index);
1234 else if (!items [index].Selected) {
1235 items [index].Selected = true;
1236 OnSelectedIndexChanged (EventArgs.Empty);
1239 SetFocusedItem (items [index]);
1240 EnsureVisible (index);
1244 internal class ItemControl : Control {
1247 ListViewItem clicked_item;
1248 ListViewItem last_clicked_item;
1249 bool hover_processed = false;
1251 public ItemControl (ListView owner)
1254 DoubleClick += new EventHandler(ItemsDoubleClick);
1255 MouseDown += new MouseEventHandler(ItemsMouseDown);
1256 MouseMove += new MouseEventHandler(ItemsMouseMove);
1257 MouseHover += new EventHandler(ItemsMouseHover);
1258 MouseUp += new MouseEventHandler(ItemsMouseUp);
1259 MouseWheel += new MouseEventHandler(ItemsMouseWheel);
1262 void ItemsDoubleClick (object sender, EventArgs e)
1264 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1265 owner.ItemActivate (this, e);
1275 BoxSelect box_select_mode = BoxSelect.None;
1276 ArrayList prev_selection;
1277 Point box_select_start;
1279 Rectangle box_select_rect;
1280 internal Rectangle BoxSelectRectangle {
1281 get { return box_select_rect; }
1283 if (box_select_rect == value)
1286 InvalidateBoxSelectRect ();
1287 box_select_rect = value;
1288 InvalidateBoxSelectRect ();
1292 void InvalidateBoxSelectRect ()
1294 if (BoxSelectRectangle.Size.IsEmpty)
1297 Rectangle edge = BoxSelectRectangle;
1303 edge.Y = BoxSelectRectangle.Bottom - 1;
1305 edge.Y = BoxSelectRectangle.Y - 1;
1307 edge.Height = BoxSelectRectangle.Height + 2;
1309 edge.X = BoxSelectRectangle.Right - 1;
1313 private Rectangle CalculateBoxSelectRectangle (Point pt)
1315 int left = Math.Min (box_select_start.X, pt.X);
1316 int right = Math.Max (box_select_start.X, pt.X);
1317 int top = Math.Min (box_select_start.Y, pt.Y);
1318 int bottom = Math.Max (box_select_start.Y, pt.Y);
1319 return Rectangle.FromLTRB (left, top, right, bottom);
1322 ArrayList BoxSelectedItems {
1324 ArrayList result = new ArrayList ();
1325 foreach (ListViewItem item in owner.Items) {
1326 Rectangle r = item.Bounds;
1328 r.Y += r.Height / 4;
1331 if (BoxSelectRectangle.IntersectsWith (r))
1338 private bool PerformBoxSelection (Point pt)
1340 if (box_select_mode == BoxSelect.None)
1343 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1345 ArrayList box_items = BoxSelectedItems;
1349 switch (box_select_mode) {
1351 case BoxSelect.Normal:
1355 case BoxSelect.Control:
1356 items = new ArrayList ();
1357 foreach (ListViewItem item in prev_selection)
1358 if (!box_items.Contains (item))
1360 foreach (ListViewItem item in box_items)
1361 if (!prev_selection.Contains (item))
1365 case BoxSelect.Shift:
1367 foreach (ListViewItem item in box_items)
1368 prev_selection.Remove (item);
1369 foreach (ListViewItem item in prev_selection)
1374 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1378 owner.SelectItems (items);
1384 private void ItemsMouseDown (object sender, MouseEventArgs me)
1386 if (owner.items.Count == 0)
1389 Point pt = new Point (me.X, me.Y);
1390 foreach (ListViewItem item in owner.items) {
1391 if (item.CheckRectReal.Contains (pt)) {
1392 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1393 item.Checked = !item.Checked;
1395 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1397 // Raise the ItemCheck event
1398 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1399 owner.OnItemCheck (ice);
1403 if (owner.View == View.Details && !owner.FullRowSelect) {
1404 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1405 clicked_item = item;
1409 if (item.Bounds.Contains (pt)) {
1410 clicked_item = item;
1417 if (clicked_item != null) {
1418 owner.SetFocusedItem (clicked_item);
1419 bool changed = !clicked_item.Selected;
1420 if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0)
1421 owner.UpdateMultiSelection (clicked_item.Index);
1423 clicked_item.Selected = true;
1426 owner.OnSelectedIndexChanged (EventArgs.Empty);
1428 // Raise double click if the item was clicked. On MS the
1429 // double click is only raised if you double click an item
1430 if (me.Clicks > 1 && clicked_item != null)
1431 owner.OnDoubleClick (EventArgs.Empty);
1432 else if (me.Clicks == 1 && clicked_item != null)
1433 owner.OnClick (EventArgs.Empty);
1435 if (owner.MultiSelect) {
1436 Keys mods = XplatUI.State.ModifierKeys;
1437 if ((mods & Keys.Shift) != 0)
1438 box_select_mode = BoxSelect.Shift;
1439 else if ((mods & Keys.Control) != 0)
1440 box_select_mode = BoxSelect.Control;
1442 box_select_mode = BoxSelect.Normal;
1443 box_select_start = pt;
1444 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1445 } else if (owner.selected_indices.Count > 0) {
1446 owner.SelectedItems.Clear ();
1447 owner.SelectedIndices.list.Clear ();
1448 owner.OnSelectedIndexChanged (EventArgs.Empty);
1453 private void ItemsMouseMove (object sender, MouseEventArgs me)
1455 if (PerformBoxSelection (new Point (me.X, me.Y)))
1458 if (owner.HoverSelection && hover_processed) {
1460 Point pt = PointToClient (Control.MousePosition);
1461 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1462 if (item == null || item.Selected)
1465 hover_processed = false;
1466 XplatUI.ResetMouseHover (Handle);
1471 private void ItemsMouseHover (object sender, EventArgs e)
1473 if (Capture || !owner.HoverSelection)
1476 hover_processed = true;
1477 Point pt = PointToClient (Control.MousePosition);
1478 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1483 item.Selected = true;
1484 owner.OnSelectedIndexChanged (new EventArgs ());
1487 private void ItemsMouseUp (object sender, MouseEventArgs me)
1490 if (owner.Items.Count == 0)
1493 Point pt = new Point (me.X, me.Y);
1495 Rectangle rect = Rectangle.Empty;
1496 if (clicked_item != null) {
1497 if (owner.view == View.Details && !owner.full_row_select)
1498 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1500 rect = clicked_item.Bounds;
1502 if (rect.Contains (pt)) {
1503 switch (owner.activation) {
1504 case ItemActivation.OneClick:
1505 owner.OnItemActivate (EventArgs.Empty);
1508 case ItemActivation.TwoClick:
1509 if (last_clicked_item == clicked_item) {
1510 owner.OnItemActivate (EventArgs.Empty);
1511 last_clicked_item = null;
1513 last_clicked_item = clicked_item;
1516 // DoubleClick activation is handled in another handler
1520 } else if (owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1521 // Need this to clean up background clicks
1522 owner.SelectedItems.Clear ();
1523 owner.SelectedIndices.list.Clear ();
1524 owner.OnSelectedIndexChanged (EventArgs.Empty);
1527 clicked_item = null;
1528 box_select_start = Point.Empty;
1529 BoxSelectRectangle = Rectangle.Empty;
1530 prev_selection = null;
1531 box_select_mode = BoxSelect.None;
1534 private void ItemsMouseWheel (object sender, MouseEventArgs me)
1536 if (owner.Items.Count == 0)
1539 int lines = me.Delta / 120;
1544 switch (owner.View) {
1546 case View.SmallIcon:
1547 owner.Scroll (owner.v_scroll, -owner.Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1549 case View.LargeIcon:
1550 owner.Scroll (owner.v_scroll, -(owner.Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1553 owner.Scroll (owner.h_scroll, -owner.Items [0].Bounds.Width * lines);
1558 internal override void OnPaintInternal (PaintEventArgs pe)
1560 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1563 protected override void WndProc (ref Message m)
1565 switch ((Msg)m.Msg) {
1566 case Msg.WM_SETFOCUS:
1570 base.WndProc (ref m);
1576 internal override void OnPaintInternal (PaintEventArgs pe)
1581 CalculateScrollBars ();
1584 void FocusChanged (object o, EventArgs args)
1586 if (Items.Count == 0)
1589 if (FocusedItem == null)
1590 SetFocusedItem (Items [0]);
1592 item_control.Invalidate (FocusedItem.Bounds);
1595 private void ListView_SizeChanged (object sender, EventArgs e)
1597 CalculateListView (alignment);
1600 private void SetFocusedItem (ListViewItem item)
1602 if (focused_item != null)
1603 focused_item.Focused = false;
1606 item.Focused = true;
1608 focused_item = item;
1611 private void HorizontalScroller (object sender, EventArgs e)
1613 // Avoid unnecessary flickering, when button is
1614 // kept pressed at the end
1615 if (h_marker != h_scroll.Value) {
1617 int pixels = h_marker - h_scroll.Value;
1619 h_marker = h_scroll.Value;
1620 if (header_control.Visible)
1621 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1623 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1627 private void VerticalScroller (object sender, EventArgs e)
1629 // Avoid unnecessary flickering, when button is
1630 // kept pressed at the end
1631 if (v_marker != v_scroll.Value) {
1632 int pixels = v_marker - v_scroll.Value;
1633 Rectangle area = item_control.ClientRectangle;
1634 v_marker = v_scroll.Value;
1635 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1638 #endregion // Internal Methods Properties
1640 #region Protected Methods
1641 protected override void CreateHandle ()
1643 base.CreateHandle ();
1646 protected override void Dispose (bool disposing)
1649 h_scroll.Dispose ();
1650 v_scroll.Dispose ();
1652 large_image_list = null;
1653 small_image_list = null;
1654 state_image_list = null;
1657 base.Dispose (disposing);
1660 protected override bool IsInputKey (Keys keyData)
1677 return base.IsInputKey (keyData);
1680 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1682 if (AfterLabelEdit != null)
1683 AfterLabelEdit (this, e);
1686 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1688 if (BeforeLabelEdit != null)
1689 BeforeLabelEdit (this, e);
1692 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1694 if (ColumnClick != null)
1695 ColumnClick (this, e);
1698 protected override void OnEnabledChanged (EventArgs e)
1700 base.OnEnabledChanged (e);
1703 protected override void OnFontChanged (EventArgs e)
1705 base.OnFontChanged (e);
1709 protected override void OnHandleCreated (EventArgs e)
1711 base.OnHandleCreated (e);
1714 protected override void OnHandleDestroyed (EventArgs e)
1716 base.OnHandleDestroyed (e);
1719 protected virtual void OnItemActivate (EventArgs e)
1721 if (ItemActivate != null)
1722 ItemActivate (this, e);
1725 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1727 if (ItemCheck != null)
1728 ItemCheck (this, ice);
1731 protected virtual void OnItemDrag (ItemDragEventArgs e)
1733 if (ItemDrag != null)
1737 protected virtual void OnSelectedIndexChanged (EventArgs e)
1739 if (SelectedIndexChanged != null)
1740 SelectedIndexChanged (this, e);
1743 protected override void OnSystemColorsChanged (EventArgs e)
1745 base.OnSystemColorsChanged (e);
1748 protected void RealizeProperties ()
1753 protected void UpdateExtendedStyles ()
1758 protected override void WndProc (ref Message m)
1760 base.WndProc (ref m);
1762 #endregion // Protected Methods
1764 #region Public Instance Methods
1765 public void ArrangeIcons ()
1767 ArrangeIcons (this.alignment);
1770 public void ArrangeIcons (ListViewAlignment alignment)
1772 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1773 if (view == View.LargeIcon || view == View.SmallIcon) {
1774 this.CalculateListView (alignment);
1775 // we have done the calculations already
1776 this.Redraw (false);
1780 public void BeginUpdate ()
1782 // flag to avoid painting
1786 public void Clear ()
1789 items.Clear (); // Redraw (true) called here
1792 public void EndUpdate ()
1794 // flag to avoid painting
1797 // probably, now we need a redraw with recalculations
1801 public void EnsureVisible (int index)
1803 if (index < 0 || index >= items.Count || scrollable == false)
1806 Rectangle view_rect = item_control.ClientRectangle;
1807 Rectangle bounds = items [index].Bounds;
1809 if (view_rect.Contains (bounds))
1812 if (bounds.Left < 0)
1813 h_scroll.Value += bounds.Left;
1814 else if (bounds.Right > view_rect.Right)
1815 h_scroll.Value += (bounds.Right - view_rect.Right);
1818 v_scroll.Value += bounds.Top;
1819 else if (bounds.Bottom > view_rect.Bottom)
1820 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1823 public ListViewItem GetItemAt (int x, int y)
1825 foreach (ListViewItem item in items) {
1826 if (item.Bounds.Contains (x, y))
1832 public Rectangle GetItemRect (int index)
1834 return GetItemRect (index, ItemBoundsPortion.Entire);
1837 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1839 if (index < 0 || index >= items.Count)
1840 throw new IndexOutOfRangeException ("index");
1842 return items [index].GetBounds (portion);
1847 if (sort_order != SortOrder.None)
1848 items.list.Sort (item_sorter);
1850 if (sort_order == SortOrder.Descending)
1851 items.list.Reverse ();
1856 public override string ToString ()
1858 int count = this.Items.Count;
1861 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1863 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1865 #endregion // Public Instance Methods
1870 class HeaderControl : Control {
1873 bool column_resize_active = false;
1874 ColumnHeader resize_column;
1875 ColumnHeader clicked_column;
1876 ColumnHeader drag_column;
1878 int drag_to_index = -1;
1880 public HeaderControl (ListView owner)
1883 MouseDown += new MouseEventHandler (HeaderMouseDown);
1884 MouseMove += new MouseEventHandler (HeaderMouseMove);
1885 MouseUp += new MouseEventHandler (HeaderMouseUp);
1888 private ColumnHeader ColumnAtX (int x)
1890 Point pt = new Point (x, 0);
1891 ColumnHeader result = null;
1892 foreach (ColumnHeader col in owner.Columns) {
1893 if (col.Rect.Contains (pt)) {
1901 private int GetReorderedIndex (ColumnHeader col)
1903 if (owner.reordered_column_indices == null)
1906 for (int i = 0; i < owner.Columns.Count; i++)
1907 if (owner.reordered_column_indices [i] == col.Index)
1909 throw new Exception ("Column index missing from reordered array");
1912 private void HeaderMouseDown (object sender, MouseEventArgs me)
1914 if (resize_column != null) {
1915 column_resize_active = true;
1920 clicked_column = ColumnAtX (me.X + owner.h_marker);
1922 if (clicked_column != null) {
1924 if (owner.AllowColumnReorder) {
1926 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
1927 drag_column.column_rect = clicked_column.Rect;
1928 drag_to_index = GetReorderedIndex (clicked_column);
1930 clicked_column.pressed = true;
1931 Rectangle bounds = clicked_column.Rect;
1932 bounds.X -= owner.h_marker;
1933 Invalidate (bounds);
1938 private void HeaderMouseMove (object sender, MouseEventArgs me)
1940 Point pt = new Point (me.X + owner.h_marker, me.Y);
1942 if (column_resize_active) {
1943 resize_column.Width = pt.X - resize_column.X;
1944 if (resize_column.Width < 0)
1945 resize_column.Width = 0;
1949 resize_column = null;
1951 if (clicked_column != null) {
1952 if (owner.AllowColumnReorder) {
1955 r = drag_column.column_rect;
1956 r.X = clicked_column.Rect.X + me.X - drag_x;
1957 drag_column.column_rect = r;
1959 int x = me.X + owner.h_marker;
1960 ColumnHeader over = ColumnAtX (x);
1962 drag_to_index = owner.Columns.Count;
1963 else if (x < over.X + over.Width / 2)
1964 drag_to_index = GetReorderedIndex (over);
1966 drag_to_index = GetReorderedIndex (over) + 1;
1969 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
1970 bool pressed = clicked_column.pressed;
1971 clicked_column.pressed = over == clicked_column;
1972 if (clicked_column.pressed ^ pressed) {
1973 Rectangle bounds = clicked_column.Rect;
1974 bounds.X -= owner.h_marker;
1975 Invalidate (bounds);
1981 for (int i = 0; i < owner.Columns.Count; i++) {
1982 Rectangle zone = owner.Columns [i].Rect;
1983 zone.X = zone.Right - 5;
1985 if (zone.Contains (pt)) {
1986 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
1988 resize_column = owner.Columns [i];
1993 if (resize_column == null)
1994 Cursor = Cursors.Default;
1996 Cursor = Cursors.VSplit;
1999 void HeaderMouseUp (object sender, MouseEventArgs me)
2003 if (column_resize_active) {
2004 column_resize_active = false;
2005 resize_column = null;
2006 Cursor = Cursors.Default;
2010 if (clicked_column != null && clicked_column.pressed) {
2011 clicked_column.pressed = false;
2012 Rectangle bounds = clicked_column.Rect;
2013 bounds.X -= owner.h_marker;
2014 Invalidate (bounds);
2015 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2018 if (drag_column != null && owner.AllowColumnReorder) {
2020 if (drag_to_index > GetReorderedIndex (clicked_column))
2022 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2023 owner.ReorderColumn (clicked_column, drag_to_index);
2028 clicked_column = null;
2031 internal override void OnPaintInternal (PaintEventArgs pe)
2036 Theme theme = ThemeEngine.Current;
2037 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2039 if (drag_column == null)
2043 if (drag_to_index == owner.Columns.Count)
2044 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2046 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2047 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2050 protected override void WndProc (ref Message m)
2052 switch ((Msg)m.Msg) {
2053 case Msg.WM_SETFOCUS:
2057 base.WndProc (ref m);
2063 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2065 internal ArrayList list;
2066 private ListView owner;
2068 #region Public Constructor
2069 public CheckedIndexCollection (ListView owner)
2071 list = new ArrayList ();
2074 #endregion // Public Constructor
2076 #region Public Properties
2079 get { return list.Count; }
2082 public bool IsReadOnly {
2083 get { return true; }
2086 public int this [int index] {
2088 if (index < 0 || index >= list.Count)
2089 throw new ArgumentOutOfRangeException ("index");
2090 return (int) list [index];
2094 bool ICollection.IsSynchronized {
2095 get { return false; }
2098 object ICollection.SyncRoot {
2099 get { return this; }
2102 bool IList.IsFixedSize {
2103 get { return true; }
2106 object IList.this [int index] {
2107 get { return this [index]; }
2108 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2110 #endregion // Public Properties
2112 #region Public Methods
2113 public bool Contains (int checkedIndex)
2115 return list.Contains (checkedIndex);
2118 public IEnumerator GetEnumerator ()
2120 return list.GetEnumerator ();
2123 void ICollection.CopyTo (Array dest, int index)
2125 list.CopyTo (dest, index);
2128 int IList.Add (object value)
2130 throw new NotSupportedException ("Add operation is not supported.");
2135 throw new NotSupportedException ("Clear operation is not supported.");
2138 bool IList.Contains (object checkedIndex)
2140 return list.Contains (checkedIndex);
2143 int IList.IndexOf (object checkedIndex)
2145 return list.IndexOf (checkedIndex);
2148 void IList.Insert (int index, object value)
2150 throw new NotSupportedException ("Insert operation is not supported.");
2153 void IList.Remove (object value)
2155 throw new NotSupportedException ("Remove operation is not supported.");
2158 void IList.RemoveAt (int index)
2160 throw new NotSupportedException ("RemoveAt operation is not supported.");
2163 public int IndexOf (int checkedIndex)
2165 return list.IndexOf (checkedIndex);
2167 #endregion // Public Methods
2169 } // CheckedIndexCollection
2171 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2173 internal ArrayList list;
2174 private ListView owner;
2176 #region Public Constructor
2177 public CheckedListViewItemCollection (ListView owner)
2179 list = new ArrayList ();
2182 #endregion // Public Constructor
2184 #region Public Properties
2187 get { return list.Count; }
2190 public bool IsReadOnly {
2191 get { return true; }
2194 public ListViewItem this [int index] {
2196 if (index < 0 || index >= list.Count)
2197 throw new ArgumentOutOfRangeException ("index");
2198 return (ListViewItem) list [index];
2202 bool ICollection.IsSynchronized {
2203 get { return list.IsSynchronized; }
2206 object ICollection.SyncRoot {
2207 get { return this; }
2210 bool IList.IsFixedSize {
2211 get { return true; }
2214 object IList.this [int index] {
2215 get { return this [index]; }
2216 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2218 #endregion // Public Properties
2220 #region Public Methods
2221 public bool Contains (ListViewItem item)
2223 return list.Contains (item);
2226 public void CopyTo (Array dest, int index)
2228 list.CopyTo (dest, index);
2231 public IEnumerator GetEnumerator ()
2233 return list.GetEnumerator ();
2236 int IList.Add (object value)
2238 throw new NotSupportedException ("Add operation is not supported.");
2243 throw new NotSupportedException ("Clear operation is not supported.");
2246 bool IList.Contains (object item)
2248 return list.Contains (item);
2251 int IList.IndexOf (object item)
2253 return list.IndexOf (item);
2256 void IList.Insert (int index, object value)
2258 throw new NotSupportedException ("Insert operation is not supported.");
2261 void IList.Remove (object value)
2263 throw new NotSupportedException ("Remove operation is not supported.");
2266 void IList.RemoveAt (int index)
2268 throw new NotSupportedException ("RemoveAt operation is not supported.");
2271 public int IndexOf (ListViewItem item)
2273 return list.IndexOf (item);
2275 #endregion // Public Methods
2277 } // CheckedListViewItemCollection
2279 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2281 internal ArrayList list;
2282 private ListView owner;
2284 #region Public Constructor
2285 public ColumnHeaderCollection (ListView owner)
2287 list = new ArrayList ();
2290 #endregion // Public Constructor
2292 #region Public Properties
2295 get { return list.Count; }
2298 public bool IsReadOnly {
2299 get { return false; }
2302 public virtual ColumnHeader this [int index] {
2304 if (index < 0 || index >= list.Count)
2305 throw new ArgumentOutOfRangeException ("index");
2306 return (ColumnHeader) list [index];
2310 bool ICollection.IsSynchronized {
2311 get { return true; }
2314 object ICollection.SyncRoot {
2315 get { return this; }
2318 bool IList.IsFixedSize {
2319 get { return list.IsFixedSize; }
2322 object IList.this [int index] {
2323 get { return this [index]; }
2324 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2326 #endregion // Public Properties
2328 #region Public Methods
2329 public virtual int Add (ColumnHeader value)
2332 value.owner = this.owner;
2333 idx = list.Add (value);
2334 if (owner.IsHandleCreated) {
2335 owner.Redraw (true);
2340 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2342 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2343 this.Add (colHeader);
2347 public virtual void AddRange (ColumnHeader [] values)
2349 foreach (ColumnHeader colHeader in values) {
2350 colHeader.owner = this.owner;
2354 owner.Redraw (true);
2357 public virtual void Clear ()
2360 owner.Redraw (true);
2363 public bool Contains (ColumnHeader value)
2365 return list.Contains (value);
2368 public IEnumerator GetEnumerator ()
2370 return list.GetEnumerator ();
2373 void ICollection.CopyTo (Array dest, int index)
2375 list.CopyTo (dest, index);
2378 int IList.Add (object value)
2380 if (! (value is ColumnHeader)) {
2381 throw new ArgumentException ("Not of type ColumnHeader", "value");
2384 return this.Add ((ColumnHeader) value);
2387 bool IList.Contains (object value)
2389 if (! (value is ColumnHeader)) {
2390 throw new ArgumentException ("Not of type ColumnHeader", "value");
2393 return this.Contains ((ColumnHeader) value);
2396 int IList.IndexOf (object value)
2398 if (! (value is ColumnHeader)) {
2399 throw new ArgumentException ("Not of type ColumnHeader", "value");
2402 return this.IndexOf ((ColumnHeader) value);
2405 void IList.Insert (int index, object value)
2407 if (! (value is ColumnHeader)) {
2408 throw new ArgumentException ("Not of type ColumnHeader", "value");
2411 this.Insert (index, (ColumnHeader) value);
2414 void IList.Remove (object value)
2416 if (! (value is ColumnHeader)) {
2417 throw new ArgumentException ("Not of type ColumnHeader", "value");
2420 this.Remove ((ColumnHeader) value);
2423 public int IndexOf (ColumnHeader value)
2425 return list.IndexOf (value);
2428 public void Insert (int index, ColumnHeader value)
2430 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2431 // but it's really only greater.
2432 if (index < 0 || index > list.Count)
2433 throw new ArgumentOutOfRangeException ("index");
2435 value.owner = this.owner;
2436 list.Insert (index, value);
2437 owner.Redraw (true);
2440 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2442 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2443 this.Insert (index, colHeader);
2446 public virtual void Remove (ColumnHeader column)
2448 // TODO: Update Column internal index ?
2449 list.Remove (column);
2450 owner.Redraw (true);
2453 public virtual void RemoveAt (int index)
2455 if (index < 0 || index >= list.Count)
2456 throw new ArgumentOutOfRangeException ("index");
2458 // TODO: Update Column internal index ?
2459 list.RemoveAt (index);
2460 owner.Redraw (true);
2462 #endregion // Public Methods
2465 } // ColumnHeaderCollection
2467 public class ListViewItemCollection : IList, ICollection, IEnumerable
2469 internal ArrayList list;
2470 private ListView owner;
2472 #region Public Constructor
2473 public ListViewItemCollection (ListView owner)
2475 list = new ArrayList ();
2478 #endregion // Public Constructor
2480 #region Public Properties
2483 get { return list.Count; }
2486 public bool IsReadOnly {
2487 get { return false; }
2490 public virtual ListViewItem this [int displayIndex] {
2492 if (displayIndex < 0 || displayIndex >= list.Count)
2493 throw new ArgumentOutOfRangeException ("displayIndex");
2494 return (ListViewItem) list [displayIndex];
2498 if (displayIndex < 0 || displayIndex >= list.Count)
2499 throw new ArgumentOutOfRangeException ("displayIndex");
2501 if (list.Contains (value))
2502 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2504 value.Owner = owner;
2505 list [displayIndex] = value;
2507 owner.Redraw (true);
2511 bool ICollection.IsSynchronized {
2512 get { return true; }
2515 object ICollection.SyncRoot {
2516 get { return this; }
2519 bool IList.IsFixedSize {
2520 get { return list.IsFixedSize; }
2523 object IList.this [int index] {
2524 get { return this [index]; }
2526 if (value is ListViewItem)
2527 this [index] = (ListViewItem) value;
2529 this [index] = new ListViewItem (value.ToString ());
2532 #endregion // Public Properties
2534 #region Public Methods
2535 public virtual ListViewItem Add (ListViewItem value)
2537 if (list.Contains (value))
2538 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2540 value.Owner = owner;
2543 if (owner.Sorting != SortOrder.None)
2546 owner.Redraw (true);
2551 public virtual ListViewItem Add (string text)
2553 ListViewItem item = new ListViewItem (text);
2554 return this.Add (item);
2557 public virtual ListViewItem Add (string text, int imageIndex)
2559 ListViewItem item = new ListViewItem (text, imageIndex);
2560 return this.Add (item);
2563 public void AddRange (ListViewItem [] values)
2566 owner.SelectedItems.list.Clear ();
2567 owner.SelectedIndices.list.Clear ();
2568 owner.CheckedItems.list.Clear ();
2569 owner.CheckedIndices.list.Clear ();
2571 foreach (ListViewItem item in values) {
2576 if (owner.Sorting != SortOrder.None)
2579 owner.Redraw (true);
2582 public virtual void Clear ()
2584 owner.SetFocusedItem (null);
2585 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2587 owner.SelectedItems.list.Clear ();
2588 owner.SelectedIndices.list.Clear ();
2589 owner.CheckedItems.list.Clear ();
2590 owner.CheckedIndices.list.Clear ();
2591 owner.Redraw (true);
2594 public bool Contains (ListViewItem item)
2596 return list.Contains (item);
2599 public void CopyTo (Array dest, int index)
2601 list.CopyTo (dest, index);
2604 public IEnumerator GetEnumerator ()
2606 return list.GetEnumerator ();
2609 int IList.Add (object item)
2614 if (item is ListViewItem) {
2615 li = (ListViewItem) item;
2616 if (list.Contains (li))
2617 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2620 li = new ListViewItem (item.ToString ());
2623 result = list.Add (li);
2624 owner.Redraw (true);
2629 bool IList.Contains (object item)
2631 return list.Contains (item);
2634 int IList.IndexOf (object item)
2636 return list.IndexOf (item);
2639 void IList.Insert (int index, object item)
2641 if (item is ListViewItem)
2642 this.Insert (index, (ListViewItem) item);
2644 this.Insert (index, item.ToString ());
2647 void IList.Remove (object item)
2649 Remove ((ListViewItem) item);
2652 public int IndexOf (ListViewItem item)
2654 return list.IndexOf (item);
2657 public ListViewItem Insert (int index, ListViewItem item)
2659 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2660 // but it's really only greater.
2661 if (index < 0 || index > list.Count)
2662 throw new ArgumentOutOfRangeException ("index");
2664 if (list.Contains (item))
2665 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2668 list.Insert (index, item);
2669 owner.Redraw (true);
2673 public ListViewItem Insert (int index, string text)
2675 return this.Insert (index, new ListViewItem (text));
2678 public ListViewItem Insert (int index, string text, int imageIndex)
2680 return this.Insert (index, new ListViewItem (text, imageIndex));
2683 public virtual void Remove (ListViewItem item)
2685 if (!list.Contains (item))
2688 owner.SelectedItems.list.Remove (item);
2689 owner.SelectedIndices.list.Remove (item.Index);
2690 owner.CheckedItems.list.Remove (item);
2691 owner.CheckedIndices.list.Remove (item.Index);
2693 owner.Redraw (true);
2696 public virtual void RemoveAt (int index)
2698 if (index < 0 || index >= list.Count)
2699 throw new ArgumentOutOfRangeException ("index");
2701 list.RemoveAt (index);
2702 owner.SelectedItems.list.RemoveAt (index);
2703 owner.SelectedIndices.list.RemoveAt (index);
2704 owner.CheckedItems.list.RemoveAt (index);
2705 owner.CheckedIndices.list.RemoveAt (index);
2706 owner.Redraw (false);
2708 #endregion // Public Methods
2710 } // ListViewItemCollection
2712 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2714 internal ArrayList list;
2715 private ListView owner;
2717 #region Public Constructor
2718 public SelectedIndexCollection (ListView owner)
2720 list = new ArrayList ();
2723 #endregion // Public Constructor
2725 #region Public Properties
2728 get { return list.Count; }
2731 public bool IsReadOnly {
2732 get { return true; }
2735 public int this [int index] {
2737 if (index < 0 || index >= list.Count)
2738 throw new ArgumentOutOfRangeException ("index");
2739 return (int) list [index];
2743 bool ICollection.IsSynchronized {
2744 get { return list.IsSynchronized; }
2747 object ICollection.SyncRoot {
2748 get { return this; }
2751 bool IList.IsFixedSize {
2752 get { return true; }
2755 object IList.this [int index] {
2756 get { return this [index]; }
2757 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2759 #endregion // Public Properties
2761 #region Public Methods
2762 public bool Contains (int selectedIndex)
2764 return list.Contains (selectedIndex);
2767 public void CopyTo (Array dest, int index)
2769 list.CopyTo (dest, index);
2772 public IEnumerator GetEnumerator ()
2774 return list.GetEnumerator ();
2777 int IList.Add (object value)
2779 throw new NotSupportedException ("Add operation is not supported.");
2784 throw new NotSupportedException ("Clear operation is not supported.");
2787 bool IList.Contains (object selectedIndex)
2789 return list.Contains (selectedIndex);
2792 int IList.IndexOf (object selectedIndex)
2794 return list.IndexOf (selectedIndex);
2797 void IList.Insert (int index, object value)
2799 throw new NotSupportedException ("Insert operation is not supported.");
2802 void IList.Remove (object value)
2804 throw new NotSupportedException ("Remove operation is not supported.");
2807 void IList.RemoveAt (int index)
2809 throw new NotSupportedException ("RemoveAt operation is not supported.");
2812 public int IndexOf (int selectedIndex)
2814 return list.IndexOf (selectedIndex);
2816 #endregion // Public Methods
2818 } // SelectedIndexCollection
2820 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2822 internal ArrayList list;
2823 private ListView owner;
2825 #region Public Constructor
2826 public SelectedListViewItemCollection (ListView owner)
2828 list = new ArrayList ();
2831 #endregion // Public Constructor
2833 #region Public Properties
2836 get { return list.Count; }
2839 public bool IsReadOnly {
2840 get { return true; }
2843 public ListViewItem this [int index] {
2845 if (index < 0 || index >= list.Count)
2846 throw new ArgumentOutOfRangeException ("index");
2847 return (ListViewItem) list [index];
2851 bool ICollection.IsSynchronized {
2852 get { return list.IsSynchronized; }
2855 object ICollection.SyncRoot {
2856 get { return this; }
2859 bool IList.IsFixedSize {
2860 get { return true; }
2863 object IList.this [int index] {
2864 get { return this [index]; }
2865 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2867 #endregion // Public Properties
2869 #region Public Methods
2870 public void Clear ()
2872 ArrayList copy = (ArrayList) list.Clone ();
2873 for (int i = 0; i < copy.Count; i++)
2874 ((ListViewItem) copy [i]).Selected = false;
2879 public bool Contains (ListViewItem item)
2881 return list.Contains (item);
2884 public void CopyTo (Array dest, int index)
2886 list.CopyTo (dest, index);
2889 public IEnumerator GetEnumerator ()
2891 return list.GetEnumerator ();
2894 int IList.Add (object value)
2896 throw new NotSupportedException ("Add operation is not supported.");
2899 bool IList.Contains (object item)
2901 return list.Contains (item);
2904 int IList.IndexOf (object item)
2906 return list.IndexOf (item);
2909 void IList.Insert (int index, object value)
2911 throw new NotSupportedException ("Insert operation is not supported.");
2914 void IList.Remove (object value)
2916 throw new NotSupportedException ("Remove operation is not supported.");
2919 void IList.RemoveAt (int index)
2921 throw new NotSupportedException ("RemoveAt operation is not supported.");
2924 public int IndexOf (ListViewItem item)
2926 return list.IndexOf (item);
2928 #endregion // Public Methods
2930 } // SelectedListViewItemCollection
2932 #endregion // Subclasses