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);
170 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
172 #endregion // Public Constructors
174 #region Private Internal Properties
175 internal Size CheckBoxSize {
177 if (this.check_boxes) {
178 if (this.state_image_list != null)
179 return this.state_image_list.ImageSize;
181 return ThemeEngine.Current.ListViewCheckBoxSize;
189 bool CanMultiselect {
193 else if (multiselect && (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0)
200 #endregion // Private Internal Properties
202 #region Protected Properties
203 protected override CreateParams CreateParams {
204 get { return base.CreateParams; }
207 protected override Size DefaultSize {
208 get { return ThemeEngine.Current.ListViewDefaultSize; }
210 #endregion // Protected Properties
212 #region Public Instance Properties
213 [DefaultValue (ItemActivation.Standard)]
214 public ItemActivation Activation {
215 get { return activation; }
217 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
218 value != ItemActivation.TwoClick) {
219 throw new InvalidEnumArgumentException (string.Format
220 ("Enum argument value '{0}' is not valid for Activation", value));
227 [DefaultValue (ListViewAlignment.Top)]
229 public ListViewAlignment Alignment {
230 get { return alignment; }
232 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
233 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
234 throw new InvalidEnumArgumentException (string.Format
235 ("Enum argument value '{0}' is not valid for Alignment", value));
238 if (this.alignment != value) {
240 // alignment does not matter in Details/List views
241 if (this.view == View.LargeIcon ||
242 this.View == View.SmallIcon)
248 [DefaultValue (false)]
249 public bool AllowColumnReorder {
250 get { return allow_column_reorder; }
251 set { allow_column_reorder = value; }
254 [DefaultValue (true)]
255 public bool AutoArrange {
256 get { return auto_arrange; }
258 if (auto_arrange != value) {
259 auto_arrange = value;
260 // autoarrange does not matter in Details/List views
261 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
267 public override Color BackColor {
269 if (background_color.IsEmpty)
270 return ThemeEngine.Current.ColorWindow;
272 return background_color;
274 set { background_color = value; }
278 [EditorBrowsable (EditorBrowsableState.Never)]
279 public override Image BackgroundImage {
280 get { return background_image; }
282 if (value == background_image)
285 background_image = value;
286 OnBackgroundImageChanged (EventArgs.Empty);
290 [DefaultValue (BorderStyle.Fixed3D)]
292 public BorderStyle BorderStyle {
293 get { return InternalBorderStyle; }
294 set { InternalBorderStyle = value; }
297 [DefaultValue (false)]
298 public bool CheckBoxes {
299 get { return check_boxes; }
301 if (check_boxes != value) {
309 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
310 public CheckedIndexCollection CheckedIndices {
311 get { return checked_indices; }
315 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
316 public CheckedListViewItemCollection CheckedItems {
317 get { return checked_items; }
320 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
322 [MergableProperty (false)]
323 public ColumnHeaderCollection Columns {
324 get { return columns; }
328 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
329 public ListViewItem FocusedItem {
335 public override Color ForeColor {
337 if (foreground_color.IsEmpty)
338 return ThemeEngine.Current.ColorWindowText;
340 return foreground_color;
342 set { foreground_color = value; }
345 [DefaultValue (false)]
346 public bool FullRowSelect {
347 get { return full_row_select; }
348 set { full_row_select = value; }
351 [DefaultValue (false)]
352 public bool GridLines {
353 get { return grid_lines; }
355 if (grid_lines != value) {
362 [DefaultValue (ColumnHeaderStyle.Clickable)]
363 public ColumnHeaderStyle HeaderStyle {
364 get { return header_style; }
366 if (header_style == value)
370 case ColumnHeaderStyle.Clickable:
371 case ColumnHeaderStyle.Nonclickable:
372 case ColumnHeaderStyle.None:
375 throw new InvalidEnumArgumentException (string.Format
376 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
379 header_style = value;
380 if (view == View.Details)
385 [DefaultValue (true)]
386 public bool HideSelection {
387 get { return hide_selection; }
389 if (hide_selection != value) {
390 hide_selection = value;
396 [DefaultValue (false)]
397 public bool HoverSelection {
398 get { return hover_selection; }
399 set { hover_selection = value; }
402 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
404 [MergableProperty (false)]
405 public ListViewItemCollection Items {
406 get { return items; }
409 [DefaultValue (false)]
410 public bool LabelEdit {
411 get { return label_edit; }
412 set { label_edit = value; }
415 [DefaultValue (true)]
417 public bool LabelWrap {
418 get { return label_wrap; }
420 if (label_wrap != value) {
427 [DefaultValue (null)]
428 public ImageList LargeImageList {
429 get { return large_image_list; }
431 large_image_list = value;
437 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
438 public IComparer ListViewItemSorter {
439 get { return item_sorter; }
440 set { item_sorter = value; }
443 [DefaultValue (true)]
444 public bool MultiSelect {
445 get { return multiselect; }
446 set { multiselect = value; }
449 [DefaultValue (true)]
450 public bool Scrollable {
451 get { return scrollable; }
453 if (scrollable != value) {
461 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
462 public SelectedIndexCollection SelectedIndices {
463 get { return selected_indices; }
467 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
468 public SelectedListViewItemCollection SelectedItems {
469 get { return selected_items; }
473 [MonoTODO("Implement")]
474 public bool ShowGroups {
484 [DefaultValue (null)]
485 public ImageList SmallImageList {
486 get { return small_image_list; }
488 small_image_list = value;
493 [DefaultValue (SortOrder.None)]
494 public SortOrder Sorting {
495 get { return sort_order; }
497 if (value != SortOrder.Ascending && value != SortOrder.Descending &&
498 value != SortOrder.None) {
499 throw new InvalidEnumArgumentException (string.Format
500 ("Enum argument value '{0}' is not valid for Sorting", value));
503 if (sort_order != value) {
510 [DefaultValue (null)]
511 public ImageList StateImageList {
512 get { return state_image_list; }
514 state_image_list = value;
521 [EditorBrowsable (EditorBrowsableState.Never)]
522 public override string Text {
531 OnTextChanged (EventArgs.Empty);
536 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
537 public ListViewItem TopItem {
540 if (this.items.Count == 0)
542 // if contents are not scrolled
543 // it is the first item
544 else if (h_marker == 0 && v_marker == 0)
545 return this.items [0];
546 // do a hit test for the scrolled position
548 foreach (ListViewItem item in this.items) {
549 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
558 [MonoTODO("Implement")]
559 public bool UseCompatibleStateImageBehavior {
569 [DefaultValue (View.LargeIcon)]
573 if (value != View.Details && value != View.LargeIcon &&
574 value != View.List && value != View.SmallIcon ) {
575 throw new InvalidEnumArgumentException (string.Format
576 ("Enum argument value '{0}' is not valid for View", value));
580 h_scroll.Value = v_scroll.Value = 0;
586 #endregion // Public Instance Properties
588 #region Internal Methods Properties
590 internal int FirstVisibleIndex {
593 if (this.items.Count == 0)
596 if (h_marker == 0 && v_marker == 0)
599 foreach (ListViewItem item in this.items) {
600 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
609 internal int LastVisibleIndex {
611 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
612 if (View == View.List || Alignment == ListViewAlignment.Left) {
613 if (Items[i].Bounds.X > ClientRectangle.Right)
616 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
621 return Items.Count - 1;
625 internal int TotalWidth {
626 get { return Math.Max (this.Width, this.layout_wd); }
629 internal int TotalHeight {
630 get { return Math.Max (this.Height, this.layout_ht); }
633 internal void Redraw (bool recalculate)
635 // Avoid calculations when control is being updated
640 CalculateListView (this.alignment);
645 internal Size GetChildColumnSize (int index)
647 Size ret_size = Size.Empty;
648 ColumnHeader col = this.columns [index];
650 if (col.Width == -2) { // autosize = max(items, columnheader)
651 Size size = Size.Ceiling (this.DeviceContext.MeasureString
652 (col.Text, this.Font));
653 ret_size = BiggestItem (index);
654 if (size.Width > ret_size.Width)
657 else { // -1 and all the values < -2 are put under one category
658 ret_size = BiggestItem (index);
659 // fall back to empty columns' width if no subitem is available for a column
660 if (ret_size.IsEmpty) {
661 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
662 if (col.Text.Length > 0)
663 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
664 (col.Text, this.Font)).Height;
666 ret_size.Height = this.Font.Height;
670 // adjust the size for icon and checkbox for 0th column
672 ret_size.Width += (this.CheckBoxSize.Width + 4);
673 if (this.small_image_list != null)
674 ret_size.Width += this.small_image_list.ImageSize.Width;
679 // Returns the size of biggest item text in a column.
680 private Size BiggestItem (int col)
682 Size temp = Size.Empty;
683 Size ret_size = Size.Empty;
685 // 0th column holds the item text, we check the size of
686 // the various subitems falling in that column and get
687 // the biggest one's size.
688 foreach (ListViewItem item in items) {
689 if (col >= item.SubItems.Count)
692 temp = Size.Ceiling (this.DeviceContext.MeasureString
693 (item.SubItems [col].Text, this.Font));
694 if (temp.Width > ret_size.Width)
698 // adjustment for space
699 if (!ret_size.IsEmpty)
705 // Sets the size of the biggest item text as per the view
706 private void CalcTextSize ()
708 // clear the old value
709 text_size = Size.Empty;
711 if (items.Count == 0)
714 text_size = BiggestItem (0);
716 if (view == View.LargeIcon && this.label_wrap) {
717 Size temp = Size.Empty;
718 if (this.check_boxes)
719 temp.Width += 2 * this.CheckBoxSize.Width;
720 if (large_image_list != null)
721 temp.Width += large_image_list.ImageSize.Width;
724 // wrapping is done for two lines only
725 if (text_size.Width > temp.Width) {
726 text_size.Width = temp.Width;
727 text_size.Height *= 2;
730 else if (view == View.List) {
731 // in list view max text shown in determined by the
732 // control width, even if scolling is enabled.
733 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
734 if (this.small_image_list != null)
735 max_wd -= this.small_image_list.ImageSize.Width;
737 if (text_size.Width > max_wd)
738 text_size.Width = max_wd;
741 // we do the default settings, if we have got 0's
742 if (text_size.Height <= 0)
743 text_size.Height = this.Font.Height;
744 if (text_size.Width <= 0)
745 text_size.Width = this.Width;
748 text_size.Width += 4;
749 text_size.Height += 2;
752 private void Scroll (ScrollBar scrollbar, int delta)
754 if (delta == 0 || !scrollbar.Visible)
758 if (scrollbar == h_scroll)
759 max = h_scroll.Maximum - item_control.Width;
761 max = v_scroll.Maximum - item_control.Height;
763 int val = scrollbar.Value + delta;
766 else if (val < scrollbar.Minimum)
767 val = scrollbar.Minimum;
768 scrollbar.Value = val;
771 private void CalculateScrollBars ()
773 Rectangle client_area = ClientRectangle;
775 if (!this.scrollable || this.items.Count <= 0) {
776 h_scroll.Visible = false;
777 v_scroll.Visible = false;
781 // making a scroll bar visible might make
782 // other scroll bar visible
783 if (layout_wd > client_area.Right) {
784 h_scroll.Visible = true;
785 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
786 v_scroll.Visible = true;
788 v_scroll.Visible = false;
789 } else if (layout_ht > client_area.Bottom) {
790 v_scroll.Visible = true;
791 if ((layout_wd + v_scroll.Width) > client_area.Right)
792 h_scroll.Visible = true;
794 h_scroll.Visible = false;
796 h_scroll.Visible = false;
797 v_scroll.Visible = false;
800 item_control.Height = ClientRectangle.Height - header_control.Height;
802 if (h_scroll.Visible) {
803 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
804 h_scroll.Minimum = 0;
806 // if v_scroll is visible, adjust the maximum of the
807 // h_scroll to account for the width of v_scroll
808 if (v_scroll.Visible) {
809 h_scroll.Maximum = layout_wd + v_scroll.Width;
810 h_scroll.Width = client_area.Width - v_scroll.Width;
813 h_scroll.Maximum = layout_wd;
814 h_scroll.Width = client_area.Width;
817 h_scroll.LargeChange = client_area.Width;
818 h_scroll.SmallChange = Font.Height;
819 item_control.Height -= h_scroll.Height;
822 if (header_control.Visible)
823 header_control.Width = ClientRectangle.Width;
824 item_control.Width = ClientRectangle.Width;
826 if (v_scroll.Visible) {
827 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
828 v_scroll.Minimum = 0;
830 // if h_scroll is visible, adjust the maximum of the
831 // v_scroll to account for the height of h_scroll
832 if (h_scroll.Visible) {
833 v_scroll.Maximum = layout_ht + h_scroll.Height;
834 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
836 v_scroll.Maximum = layout_ht;
837 v_scroll.Height = client_area.Height;
840 v_scroll.LargeChange = client_area.Height;
841 v_scroll.SmallChange = Font.Height;
842 if (header_control.Visible)
843 header_control.Width -= v_scroll.Width;
844 item_control.Width -= v_scroll.Width;
848 ColumnHeader GetReorderedColumn (int index)
850 if (reordered_column_indices == null)
851 return Columns [index];
853 return Columns [reordered_column_indices [index]];
856 void ReorderColumn (ColumnHeader col, int index)
858 if (reordered_column_indices == null) {
859 reordered_column_indices = new int [Columns.Count];
860 for (int i = 0; i < Columns.Count; i++)
861 reordered_column_indices [i] = i;
864 if (reordered_column_indices [index] == col.Index)
867 int[] curr = reordered_column_indices;
868 int[] result = new int [Columns.Count];
870 for (int i = 0; i < Columns.Count; i++) {
871 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
875 result [i] = col.Index;
877 result [i] = curr [curr_idx++];
880 reordered_column_indices = result;
882 header_control.Invalidate ();
883 item_control.Invalidate ();
886 Size LargeIconItemSize {
888 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
889 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
890 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
891 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
892 return new Size (w, h);
896 Size SmallIconItemSize {
898 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
899 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
900 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
901 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
902 return new Size (w, h);
908 ListViewItem[,] item_matrix;
910 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
912 header_control.Visible = false;
913 header_control.Size = Size.Empty;
914 item_control.Visible = true;
915 item_control.Location = Point.Empty;
917 if (items.Count == 0)
920 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
922 Rectangle area = ClientRectangle;
925 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
928 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
930 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
933 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
936 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
937 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
938 item_matrix = new ListViewItem [rows, cols];
941 foreach (ListViewItem item in items) {
942 int x = col * (sz.Width + x_spacing);
943 int y = row * (sz.Height + y_spacing);
944 item.Location = new Point (x, y);
948 item_matrix [row, col] = item;
962 item_control.Size = new Size (layout_wd, layout_ht);
967 if (header_style == ColumnHeaderStyle.None) {
968 header_control.Visible = false;
969 header_control.Size = Size.Empty;
974 for (int i = 0; i < Columns.Count; i++) {
975 ColumnHeader col = GetReorderedColumn (i);
978 col.CalcColumnHeader ();
982 if (x < ClientRectangle.Width)
983 x = ClientRectangle.Width;
985 header_control.Width = x;
986 header_control.Height = columns [0].Ht;
987 header_control.Visible = true;
990 void LayoutDetails ()
992 if (columns.Count == 0) {
993 header_control.Visible = false;
994 item_control.Visible = false;
1000 item_control.Visible = true;
1001 item_control.Location = new Point (0, header_control.Height);
1004 if (items.Count > 0) {
1005 foreach (ListViewItem item in items) {
1007 item.Location = new Point (0, y);
1008 y += item.Bounds.Height + 2;
1011 // some space for bottom gridline
1016 layout_wd = Math.Max (header_control.Width, item_control.Width);
1017 layout_ht = y + header_control.Height;
1020 private void CalculateListView (ListViewAlignment align)
1029 case View.SmallIcon:
1030 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1033 case View.LargeIcon:
1034 LayoutIcons (true, alignment == ListViewAlignment.Left,
1035 ThemeEngine.Current.ListViewHorizontalSpacing,
1036 ThemeEngine.Current.ListViewVerticalSpacing);
1040 LayoutIcons (false, true, 4, 2);
1044 CalculateScrollBars ();
1047 internal void UpdateSelection (ListViewItem item)
1049 if (item.Selected) {
1051 if (!CanMultiselect && SelectedItems.Count > 0) {
1052 SelectedItems.Clear ();
1053 SelectedIndices.list.Clear ();
1056 if (!SelectedItems.Contains (item)) {
1057 SelectedItems.list.Add (item);
1058 SelectedIndices.list.Add (item.Index);
1061 SelectedItems.list.Remove (item);
1062 SelectedIndices.list.Remove (item.Index);
1066 private bool KeySearchString (KeyEventArgs ke)
1068 int current_tickcnt = Environment.TickCount;
1069 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1070 keysearch_text = string.Empty;
1073 keysearch_text += (char) ke.KeyData;
1074 keysearch_tickcnt = current_tickcnt;
1076 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1079 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1080 CompareOptions.IgnoreCase)) {
1081 SetFocusedItem (Items [i]);
1082 items [i].Selected = true;
1086 i = (i + 1 < Items.Count) ? i+1 : 0;
1094 int GetAdjustedIndex (Keys key)
1098 if (View == View.Details) {
1100 result = FocusedItem.Index - 1;
1101 else if (key == Keys.Down) {
1102 result = FocusedItem.Index + 1;
1103 if (result == items.Count)
1109 int row = FocusedItem.row;
1110 int col = FocusedItem.col;
1116 return item_matrix [row, col - 1].Index;
1119 if (col == (cols - 1))
1121 while (item_matrix [row, col + 1] == null)
1123 return item_matrix [row, col + 1].Index;
1128 return item_matrix [row - 1, col].Index;
1131 if (row == (rows - 1))
1133 while (item_matrix [row + 1, col] == null)
1135 return item_matrix [row + 1, col].Index;
1142 ListViewItem selection_start;
1144 private bool SelectItems (ArrayList sel_items)
1146 bool changed = false;
1147 multiselecting = true;
1148 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1149 foreach (ListViewItem item in curr_items)
1150 if (!sel_items.Contains (item)) {
1151 item.Selected = false;
1154 foreach (ListViewItem item in sel_items)
1155 if (!item.Selected) {
1156 item.Selected = true;
1159 multiselecting = false;
1163 private void UpdateMultiSelection (int index)
1165 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1166 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1167 ListViewItem item = items [index];
1169 if (shift_pressed && selection_start != null) {
1170 ArrayList list = new ArrayList ();
1171 int start = Math.Min (selection_start.Index, index);
1172 int end = Math.Max (selection_start.Index, index);
1173 if (View == View.Details) {
1174 for (int i = start; i <= end; i++)
1175 list.Add (items [i]);
1177 int left = Math.Min (items [start].col, items [end].col);
1178 int right = Math.Max (items [start].col, items [end].col);
1179 int top = Math.Min (items [start].row, items [end].row);
1180 int bottom = Math.Max (items [start].row, items [end].row);
1181 foreach (ListViewItem curr in items)
1182 if (curr.row >= top && curr.row <= bottom &&
1183 curr.col >= left && curr.col <= right)
1186 if (SelectItems (list))
1187 OnSelectedIndexChanged (EventArgs.Empty);
1188 } else if (!ctrl_pressed) {
1189 SelectedItems.Clear ();
1190 SelectedIndices.list.Clear ();
1191 item.Selected = true;
1192 selection_start = item;
1193 OnSelectedIndexChanged (EventArgs.Empty);
1197 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1199 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1205 switch (ke.KeyCode) {
1208 index = Items.Count - 1;
1219 index = GetAdjustedIndex (ke.KeyCode);
1223 ke.Handled = KeySearchString (ke);
1231 UpdateMultiSelection (index);
1232 else if (!items [index].Selected) {
1233 items [index].Selected = true;
1234 OnSelectedIndexChanged (EventArgs.Empty);
1237 SetFocusedItem (items [index]);
1238 EnsureVisible (index);
1242 internal class ItemControl : Control {
1245 ListViewItem clicked_item;
1246 ListViewItem last_clicked_item;
1247 bool hover_processed = false;
1249 public ItemControl (ListView owner)
1252 DoubleClick += new EventHandler(ItemsDoubleClick);
1253 KeyDown += new KeyEventHandler (ItemsKeyDown);
1254 KeyUp += new KeyEventHandler (ItemsKeyUp);
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);
1260 GotFocus += new EventHandler (FocusChanged);
1261 LostFocus += new EventHandler (FocusChanged);
1264 void ItemsDoubleClick (object sender, EventArgs e)
1266 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1267 owner.ItemActivate (this, e);
1270 void ItemsKeyDown (object sender, KeyEventArgs args)
1272 owner.OnKeyDown (args);
1275 void ItemsKeyUp (object sender, KeyEventArgs args)
1277 owner.OnKeyUp (args);
1287 BoxSelect box_select_mode = BoxSelect.None;
1288 ArrayList prev_selection;
1289 Point box_select_start;
1291 Rectangle box_select_rect;
1292 internal Rectangle BoxSelectRectangle {
1293 get { return box_select_rect; }
1295 if (box_select_rect == value)
1298 InvalidateBoxSelectRect ();
1299 box_select_rect = value;
1300 InvalidateBoxSelectRect ();
1304 void InvalidateBoxSelectRect ()
1306 if (BoxSelectRectangle.Size.IsEmpty)
1309 Rectangle edge = BoxSelectRectangle;
1315 edge.Y = BoxSelectRectangle.Bottom - 1;
1317 edge.Y = BoxSelectRectangle.Y - 1;
1319 edge.Height = BoxSelectRectangle.Height + 2;
1321 edge.X = BoxSelectRectangle.Right - 1;
1325 private Rectangle CalculateBoxSelectRectangle (Point pt)
1327 int left = Math.Min (box_select_start.X, pt.X);
1328 int right = Math.Max (box_select_start.X, pt.X);
1329 int top = Math.Min (box_select_start.Y, pt.Y);
1330 int bottom = Math.Max (box_select_start.Y, pt.Y);
1331 return Rectangle.FromLTRB (left, top, right, bottom);
1334 ArrayList BoxSelectedItems {
1336 ArrayList result = new ArrayList ();
1337 foreach (ListViewItem item in owner.Items) {
1338 Rectangle r = item.Bounds;
1340 r.Y += r.Height / 4;
1343 if (BoxSelectRectangle.IntersectsWith (r))
1350 private bool PerformBoxSelection (Point pt)
1352 if (box_select_mode == BoxSelect.None)
1355 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1357 ArrayList box_items = BoxSelectedItems;
1361 switch (box_select_mode) {
1363 case BoxSelect.Normal:
1367 case BoxSelect.Control:
1368 items = new ArrayList ();
1369 foreach (ListViewItem item in prev_selection)
1370 if (!box_items.Contains (item))
1372 foreach (ListViewItem item in box_items)
1373 if (!prev_selection.Contains (item))
1377 case BoxSelect.Shift:
1379 foreach (ListViewItem item in box_items)
1380 prev_selection.Remove (item);
1381 foreach (ListViewItem item in prev_selection)
1386 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1390 owner.SelectItems (items);
1396 private void ItemsMouseDown (object sender, MouseEventArgs me)
1398 if (owner.items.Count == 0)
1401 Point pt = new Point (me.X, me.Y);
1402 foreach (ListViewItem item in owner.items) {
1403 if (item.CheckRectReal.Contains (pt)) {
1404 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1405 item.Checked = !item.Checked;
1407 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1409 // Raise the ItemCheck event
1410 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1411 owner.OnItemCheck (ice);
1415 if (owner.View == View.Details && !owner.FullRowSelect) {
1416 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1417 clicked_item = item;
1421 if (item.Bounds.Contains (pt)) {
1422 clicked_item = item;
1429 if (clicked_item != null) {
1430 owner.SetFocusedItem (clicked_item);
1431 bool changed = !clicked_item.Selected;
1432 if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0)
1433 owner.UpdateMultiSelection (clicked_item.Index);
1435 clicked_item.Selected = true;
1438 owner.OnSelectedIndexChanged (EventArgs.Empty);
1440 // Raise double click if the item was clicked. On MS the
1441 // double click is only raised if you double click an item
1442 if (me.Clicks > 1 && clicked_item != null)
1443 owner.OnDoubleClick (EventArgs.Empty);
1444 else if (me.Clicks == 1 && clicked_item != null)
1445 owner.OnClick (EventArgs.Empty);
1447 if (owner.MultiSelect) {
1448 Keys mods = XplatUI.State.ModifierKeys;
1449 if ((mods & Keys.Shift) != 0)
1450 box_select_mode = BoxSelect.Shift;
1451 else if ((mods & Keys.Control) != 0)
1452 box_select_mode = BoxSelect.Control;
1454 box_select_mode = BoxSelect.Normal;
1455 box_select_start = pt;
1456 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1457 } else if (owner.selected_indices.Count > 0) {
1458 owner.SelectedItems.Clear ();
1459 owner.SelectedIndices.list.Clear ();
1460 owner.OnSelectedIndexChanged (EventArgs.Empty);
1465 private void ItemsMouseMove (object sender, MouseEventArgs me)
1467 if (PerformBoxSelection (new Point (me.X, me.Y)))
1470 if (owner.HoverSelection && hover_processed) {
1472 Point pt = PointToClient (Control.MousePosition);
1473 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1474 if (item == null || item.Selected)
1477 hover_processed = false;
1478 XplatUI.ResetMouseHover (Handle);
1483 private void ItemsMouseHover (object sender, EventArgs e)
1485 if (Capture || !owner.HoverSelection)
1488 hover_processed = true;
1489 Point pt = PointToClient (Control.MousePosition);
1490 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1495 item.Selected = true;
1496 owner.OnSelectedIndexChanged (new EventArgs ());
1499 private void ItemsMouseUp (object sender, MouseEventArgs me)
1502 if (owner.Items.Count == 0)
1505 Point pt = new Point (me.X, me.Y);
1507 Rectangle rect = Rectangle.Empty;
1508 if (clicked_item != null) {
1509 if (owner.view == View.Details && !owner.full_row_select)
1510 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1512 rect = clicked_item.Bounds;
1514 if (rect.Contains (pt)) {
1515 switch (owner.activation) {
1516 case ItemActivation.OneClick:
1517 owner.OnItemActivate (EventArgs.Empty);
1520 case ItemActivation.TwoClick:
1521 if (last_clicked_item == clicked_item) {
1522 owner.OnItemActivate (EventArgs.Empty);
1523 last_clicked_item = null;
1525 last_clicked_item = clicked_item;
1528 // DoubleClick activation is handled in another handler
1532 } else if (owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1533 // Need this to clean up background clicks
1534 owner.SelectedItems.Clear ();
1535 owner.SelectedIndices.list.Clear ();
1536 owner.OnSelectedIndexChanged (EventArgs.Empty);
1539 clicked_item = null;
1540 box_select_start = Point.Empty;
1541 BoxSelectRectangle = Rectangle.Empty;
1542 prev_selection = null;
1543 box_select_mode = BoxSelect.None;
1546 private void ItemsMouseWheel (object sender, MouseEventArgs me)
1548 if (owner.Items.Count == 0)
1551 int lines = me.Delta / 120;
1556 switch (owner.View) {
1558 case View.SmallIcon:
1559 owner.Scroll (owner.v_scroll, -owner.Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1561 case View.LargeIcon:
1562 owner.Scroll (owner.v_scroll, -(owner.Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1565 owner.Scroll (owner.h_scroll, -owner.Items [0].Bounds.Width * lines);
1570 void FocusChanged (object o, EventArgs args)
1572 if (owner.Items.Count == 0)
1575 if (owner.FocusedItem == null)
1576 owner.SetFocusedItem (owner.Items [0]);
1578 Invalidate (owner.FocusedItem.Bounds);
1581 internal override void OnPaintInternal (PaintEventArgs pe)
1583 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1587 internal override void OnPaintInternal (PaintEventArgs pe)
1592 CalculateScrollBars ();
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);
2051 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2053 internal ArrayList list;
2054 private ListView owner;
2056 #region Public Constructor
2057 public CheckedIndexCollection (ListView owner)
2059 list = new ArrayList ();
2062 #endregion // Public Constructor
2064 #region Public Properties
2067 get { return list.Count; }
2070 public bool IsReadOnly {
2071 get { return true; }
2074 public int this [int index] {
2076 if (index < 0 || index >= list.Count)
2077 throw new ArgumentOutOfRangeException ("index");
2078 return (int) list [index];
2082 bool ICollection.IsSynchronized {
2083 get { return false; }
2086 object ICollection.SyncRoot {
2087 get { return this; }
2090 bool IList.IsFixedSize {
2091 get { return true; }
2094 object IList.this [int index] {
2095 get { return this [index]; }
2096 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2098 #endregion // Public Properties
2100 #region Public Methods
2101 public bool Contains (int checkedIndex)
2103 return list.Contains (checkedIndex);
2106 public IEnumerator GetEnumerator ()
2108 return list.GetEnumerator ();
2111 void ICollection.CopyTo (Array dest, int index)
2113 list.CopyTo (dest, index);
2116 int IList.Add (object value)
2118 throw new NotSupportedException ("Add operation is not supported.");
2123 throw new NotSupportedException ("Clear operation is not supported.");
2126 bool IList.Contains (object checkedIndex)
2128 return list.Contains (checkedIndex);
2131 int IList.IndexOf (object checkedIndex)
2133 return list.IndexOf (checkedIndex);
2136 void IList.Insert (int index, object value)
2138 throw new NotSupportedException ("Insert operation is not supported.");
2141 void IList.Remove (object value)
2143 throw new NotSupportedException ("Remove operation is not supported.");
2146 void IList.RemoveAt (int index)
2148 throw new NotSupportedException ("RemoveAt operation is not supported.");
2151 public int IndexOf (int checkedIndex)
2153 return list.IndexOf (checkedIndex);
2155 #endregion // Public Methods
2157 } // CheckedIndexCollection
2159 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2161 internal ArrayList list;
2162 private ListView owner;
2164 #region Public Constructor
2165 public CheckedListViewItemCollection (ListView owner)
2167 list = new ArrayList ();
2170 #endregion // Public Constructor
2172 #region Public Properties
2175 get { return list.Count; }
2178 public bool IsReadOnly {
2179 get { return true; }
2182 public ListViewItem this [int index] {
2184 if (index < 0 || index >= list.Count)
2185 throw new ArgumentOutOfRangeException ("index");
2186 return (ListViewItem) list [index];
2190 bool ICollection.IsSynchronized {
2191 get { return list.IsSynchronized; }
2194 object ICollection.SyncRoot {
2195 get { return this; }
2198 bool IList.IsFixedSize {
2199 get { return true; }
2202 object IList.this [int index] {
2203 get { return this [index]; }
2204 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2206 #endregion // Public Properties
2208 #region Public Methods
2209 public bool Contains (ListViewItem item)
2211 return list.Contains (item);
2214 public void CopyTo (Array dest, int index)
2216 list.CopyTo (dest, index);
2219 public IEnumerator GetEnumerator ()
2221 return list.GetEnumerator ();
2224 int IList.Add (object value)
2226 throw new NotSupportedException ("Add operation is not supported.");
2231 throw new NotSupportedException ("Clear operation is not supported.");
2234 bool IList.Contains (object item)
2236 return list.Contains (item);
2239 int IList.IndexOf (object item)
2241 return list.IndexOf (item);
2244 void IList.Insert (int index, object value)
2246 throw new NotSupportedException ("Insert operation is not supported.");
2249 void IList.Remove (object value)
2251 throw new NotSupportedException ("Remove operation is not supported.");
2254 void IList.RemoveAt (int index)
2256 throw new NotSupportedException ("RemoveAt operation is not supported.");
2259 public int IndexOf (ListViewItem item)
2261 return list.IndexOf (item);
2263 #endregion // Public Methods
2265 } // CheckedListViewItemCollection
2267 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2269 internal ArrayList list;
2270 private ListView owner;
2272 #region Public Constructor
2273 public ColumnHeaderCollection (ListView owner)
2275 list = new ArrayList ();
2278 #endregion // Public Constructor
2280 #region Public Properties
2283 get { return list.Count; }
2286 public bool IsReadOnly {
2287 get { return false; }
2290 public virtual ColumnHeader this [int index] {
2292 if (index < 0 || index >= list.Count)
2293 throw new ArgumentOutOfRangeException ("index");
2294 return (ColumnHeader) list [index];
2298 bool ICollection.IsSynchronized {
2299 get { return true; }
2302 object ICollection.SyncRoot {
2303 get { return this; }
2306 bool IList.IsFixedSize {
2307 get { return list.IsFixedSize; }
2310 object IList.this [int index] {
2311 get { return this [index]; }
2312 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2314 #endregion // Public Properties
2316 #region Public Methods
2317 public virtual int Add (ColumnHeader value)
2320 value.owner = this.owner;
2321 idx = list.Add (value);
2322 if (owner.IsHandleCreated) {
2323 owner.Redraw (true);
2328 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2330 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2331 this.Add (colHeader);
2335 public virtual void AddRange (ColumnHeader [] values)
2337 foreach (ColumnHeader colHeader in values) {
2338 colHeader.owner = this.owner;
2342 owner.Redraw (true);
2345 public virtual void Clear ()
2348 owner.Redraw (true);
2351 public bool Contains (ColumnHeader value)
2353 return list.Contains (value);
2356 public IEnumerator GetEnumerator ()
2358 return list.GetEnumerator ();
2361 void ICollection.CopyTo (Array dest, int index)
2363 list.CopyTo (dest, index);
2366 int IList.Add (object value)
2368 if (! (value is ColumnHeader)) {
2369 throw new ArgumentException ("Not of type ColumnHeader", "value");
2372 return this.Add ((ColumnHeader) value);
2375 bool IList.Contains (object value)
2377 if (! (value is ColumnHeader)) {
2378 throw new ArgumentException ("Not of type ColumnHeader", "value");
2381 return this.Contains ((ColumnHeader) value);
2384 int IList.IndexOf (object value)
2386 if (! (value is ColumnHeader)) {
2387 throw new ArgumentException ("Not of type ColumnHeader", "value");
2390 return this.IndexOf ((ColumnHeader) value);
2393 void IList.Insert (int index, object value)
2395 if (! (value is ColumnHeader)) {
2396 throw new ArgumentException ("Not of type ColumnHeader", "value");
2399 this.Insert (index, (ColumnHeader) value);
2402 void IList.Remove (object value)
2404 if (! (value is ColumnHeader)) {
2405 throw new ArgumentException ("Not of type ColumnHeader", "value");
2408 this.Remove ((ColumnHeader) value);
2411 public int IndexOf (ColumnHeader value)
2413 return list.IndexOf (value);
2416 public void Insert (int index, ColumnHeader value)
2418 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2419 // but it's really only greater.
2420 if (index < 0 || index > list.Count)
2421 throw new ArgumentOutOfRangeException ("index");
2423 value.owner = this.owner;
2424 list.Insert (index, value);
2425 owner.Redraw (true);
2428 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2430 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2431 this.Insert (index, colHeader);
2434 public virtual void Remove (ColumnHeader column)
2436 // TODO: Update Column internal index ?
2437 list.Remove (column);
2438 owner.Redraw (true);
2441 public virtual void RemoveAt (int index)
2443 if (index < 0 || index >= list.Count)
2444 throw new ArgumentOutOfRangeException ("index");
2446 // TODO: Update Column internal index ?
2447 list.RemoveAt (index);
2448 owner.Redraw (true);
2450 #endregion // Public Methods
2453 } // ColumnHeaderCollection
2455 public class ListViewItemCollection : IList, ICollection, IEnumerable
2457 internal ArrayList list;
2458 private ListView owner;
2460 #region Public Constructor
2461 public ListViewItemCollection (ListView owner)
2463 list = new ArrayList ();
2466 #endregion // Public Constructor
2468 #region Public Properties
2471 get { return list.Count; }
2474 public bool IsReadOnly {
2475 get { return false; }
2478 public virtual ListViewItem this [int displayIndex] {
2480 if (displayIndex < 0 || displayIndex >= list.Count)
2481 throw new ArgumentOutOfRangeException ("displayIndex");
2482 return (ListViewItem) list [displayIndex];
2486 if (displayIndex < 0 || displayIndex >= list.Count)
2487 throw new ArgumentOutOfRangeException ("displayIndex");
2489 if (list.Contains (value))
2490 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2492 value.Owner = owner;
2493 list [displayIndex] = value;
2495 owner.Redraw (true);
2499 bool ICollection.IsSynchronized {
2500 get { return true; }
2503 object ICollection.SyncRoot {
2504 get { return this; }
2507 bool IList.IsFixedSize {
2508 get { return list.IsFixedSize; }
2511 object IList.this [int index] {
2512 get { return this [index]; }
2514 if (value is ListViewItem)
2515 this [index] = (ListViewItem) value;
2517 this [index] = new ListViewItem (value.ToString ());
2520 #endregion // Public Properties
2522 #region Public Methods
2523 public virtual ListViewItem Add (ListViewItem value)
2525 if (list.Contains (value))
2526 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2528 value.Owner = owner;
2531 if (owner.Sorting != SortOrder.None)
2534 owner.Redraw (true);
2539 public virtual ListViewItem Add (string text)
2541 ListViewItem item = new ListViewItem (text);
2542 return this.Add (item);
2545 public virtual ListViewItem Add (string text, int imageIndex)
2547 ListViewItem item = new ListViewItem (text, imageIndex);
2548 return this.Add (item);
2551 public void AddRange (ListViewItem [] values)
2554 owner.SelectedItems.list.Clear ();
2555 owner.SelectedIndices.list.Clear ();
2556 owner.CheckedItems.list.Clear ();
2557 owner.CheckedIndices.list.Clear ();
2559 foreach (ListViewItem item in values) {
2564 if (owner.Sorting != SortOrder.None)
2567 owner.Redraw (true);
2570 public virtual void Clear ()
2572 owner.SetFocusedItem (null);
2573 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2575 owner.SelectedItems.list.Clear ();
2576 owner.SelectedIndices.list.Clear ();
2577 owner.CheckedItems.list.Clear ();
2578 owner.CheckedIndices.list.Clear ();
2579 owner.Redraw (true);
2582 public bool Contains (ListViewItem item)
2584 return list.Contains (item);
2587 public void CopyTo (Array dest, int index)
2589 list.CopyTo (dest, index);
2592 public IEnumerator GetEnumerator ()
2594 return list.GetEnumerator ();
2597 int IList.Add (object item)
2602 if (item is ListViewItem) {
2603 li = (ListViewItem) item;
2604 if (list.Contains (li))
2605 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2608 li = new ListViewItem (item.ToString ());
2611 result = list.Add (li);
2612 owner.Redraw (true);
2617 bool IList.Contains (object item)
2619 return list.Contains (item);
2622 int IList.IndexOf (object item)
2624 return list.IndexOf (item);
2627 void IList.Insert (int index, object item)
2629 if (item is ListViewItem)
2630 this.Insert (index, (ListViewItem) item);
2632 this.Insert (index, item.ToString ());
2635 void IList.Remove (object item)
2637 Remove ((ListViewItem) item);
2640 public int IndexOf (ListViewItem item)
2642 return list.IndexOf (item);
2645 public ListViewItem Insert (int index, ListViewItem item)
2647 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2648 // but it's really only greater.
2649 if (index < 0 || index > list.Count)
2650 throw new ArgumentOutOfRangeException ("index");
2652 if (list.Contains (item))
2653 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2656 list.Insert (index, item);
2657 owner.Redraw (true);
2661 public ListViewItem Insert (int index, string text)
2663 return this.Insert (index, new ListViewItem (text));
2666 public ListViewItem Insert (int index, string text, int imageIndex)
2668 return this.Insert (index, new ListViewItem (text, imageIndex));
2671 public virtual void Remove (ListViewItem item)
2673 if (!list.Contains (item))
2676 owner.SelectedItems.list.Remove (item);
2677 owner.SelectedIndices.list.Remove (item.Index);
2678 owner.CheckedItems.list.Remove (item);
2679 owner.CheckedIndices.list.Remove (item.Index);
2681 owner.Redraw (true);
2684 public virtual void RemoveAt (int index)
2686 if (index < 0 || index >= list.Count)
2687 throw new ArgumentOutOfRangeException ("index");
2689 list.RemoveAt (index);
2690 owner.SelectedItems.list.RemoveAt (index);
2691 owner.SelectedIndices.list.RemoveAt (index);
2692 owner.CheckedItems.list.RemoveAt (index);
2693 owner.CheckedIndices.list.RemoveAt (index);
2694 owner.Redraw (false);
2696 #endregion // Public Methods
2698 } // ListViewItemCollection
2700 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2702 internal ArrayList list;
2703 private ListView owner;
2705 #region Public Constructor
2706 public SelectedIndexCollection (ListView owner)
2708 list = new ArrayList ();
2711 #endregion // Public Constructor
2713 #region Public Properties
2716 get { return list.Count; }
2719 public bool IsReadOnly {
2720 get { return true; }
2723 public int this [int index] {
2725 if (index < 0 || index >= list.Count)
2726 throw new ArgumentOutOfRangeException ("index");
2727 return (int) list [index];
2731 bool ICollection.IsSynchronized {
2732 get { return list.IsSynchronized; }
2735 object ICollection.SyncRoot {
2736 get { return this; }
2739 bool IList.IsFixedSize {
2740 get { return true; }
2743 object IList.this [int index] {
2744 get { return this [index]; }
2745 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2747 #endregion // Public Properties
2749 #region Public Methods
2750 public bool Contains (int selectedIndex)
2752 return list.Contains (selectedIndex);
2755 public void CopyTo (Array dest, int index)
2757 list.CopyTo (dest, index);
2760 public IEnumerator GetEnumerator ()
2762 return list.GetEnumerator ();
2765 int IList.Add (object value)
2767 throw new NotSupportedException ("Add operation is not supported.");
2772 throw new NotSupportedException ("Clear operation is not supported.");
2775 bool IList.Contains (object selectedIndex)
2777 return list.Contains (selectedIndex);
2780 int IList.IndexOf (object selectedIndex)
2782 return list.IndexOf (selectedIndex);
2785 void IList.Insert (int index, object value)
2787 throw new NotSupportedException ("Insert operation is not supported.");
2790 void IList.Remove (object value)
2792 throw new NotSupportedException ("Remove operation is not supported.");
2795 void IList.RemoveAt (int index)
2797 throw new NotSupportedException ("RemoveAt operation is not supported.");
2800 public int IndexOf (int selectedIndex)
2802 return list.IndexOf (selectedIndex);
2804 #endregion // Public Methods
2806 } // SelectedIndexCollection
2808 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2810 internal ArrayList list;
2811 private ListView owner;
2813 #region Public Constructor
2814 public SelectedListViewItemCollection (ListView owner)
2816 list = new ArrayList ();
2819 #endregion // Public Constructor
2821 #region Public Properties
2824 get { return list.Count; }
2827 public bool IsReadOnly {
2828 get { return true; }
2831 public ListViewItem this [int index] {
2833 if (index < 0 || index >= list.Count)
2834 throw new ArgumentOutOfRangeException ("index");
2835 return (ListViewItem) list [index];
2839 bool ICollection.IsSynchronized {
2840 get { return list.IsSynchronized; }
2843 object ICollection.SyncRoot {
2844 get { return this; }
2847 bool IList.IsFixedSize {
2848 get { return true; }
2851 object IList.this [int index] {
2852 get { return this [index]; }
2853 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2855 #endregion // Public Properties
2857 #region Public Methods
2858 public void Clear ()
2860 ArrayList copy = (ArrayList) list.Clone ();
2861 for (int i = 0; i < copy.Count; i++)
2862 ((ListViewItem) copy [i]).Selected = false;
2867 public bool Contains (ListViewItem item)
2869 return list.Contains (item);
2872 public void CopyTo (Array dest, int index)
2874 list.CopyTo (dest, index);
2877 public IEnumerator GetEnumerator ()
2879 return list.GetEnumerator ();
2882 int IList.Add (object value)
2884 throw new NotSupportedException ("Add operation is not supported.");
2887 bool IList.Contains (object item)
2889 return list.Contains (item);
2892 int IList.IndexOf (object item)
2894 return list.IndexOf (item);
2897 void IList.Insert (int index, object value)
2899 throw new NotSupportedException ("Insert operation is not supported.");
2902 void IList.Remove (object value)
2904 throw new NotSupportedException ("Remove operation is not supported.");
2907 void IList.RemoveAt (int index)
2909 throw new NotSupportedException ("RemoveAt operation is not supported.");
2912 public int IndexOf (ListViewItem item)
2914 return list.IndexOf (item);
2916 #endregion // Public Methods
2918 } // SelectedListViewItemCollection
2920 #endregion // Subclasses