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 resize_column = owner.Columns [i];
1991 if (resize_column == null)
1992 Cursor = Cursors.Default;
1994 Cursor = Cursors.VSplit;
1997 void HeaderMouseUp (object sender, MouseEventArgs me)
2001 if (column_resize_active) {
2002 column_resize_active = false;
2003 resize_column = null;
2004 Cursor = Cursors.Default;
2008 if (clicked_column != null && clicked_column.pressed) {
2009 clicked_column.pressed = false;
2010 Rectangle bounds = clicked_column.Rect;
2011 bounds.X -= owner.h_marker;
2012 Invalidate (bounds);
2013 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2016 if (drag_column != null && owner.AllowColumnReorder) {
2018 if (drag_to_index > GetReorderedIndex (clicked_column))
2020 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2021 owner.ReorderColumn (clicked_column, drag_to_index);
2026 clicked_column = null;
2029 internal override void OnPaintInternal (PaintEventArgs pe)
2034 Theme theme = ThemeEngine.Current;
2035 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2037 if (drag_column == null)
2041 if (drag_to_index == owner.Columns.Count)
2042 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2044 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2045 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2049 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2051 internal ArrayList list;
2052 private ListView owner;
2054 #region Public Constructor
2055 public CheckedIndexCollection (ListView owner)
2057 list = new ArrayList ();
2060 #endregion // Public Constructor
2062 #region Public Properties
2065 get { return list.Count; }
2068 public bool IsReadOnly {
2069 get { return true; }
2072 public int this [int index] {
2074 if (index < 0 || index >= list.Count)
2075 throw new ArgumentOutOfRangeException ("index");
2076 return (int) list [index];
2080 bool ICollection.IsSynchronized {
2081 get { return false; }
2084 object ICollection.SyncRoot {
2085 get { return this; }
2088 bool IList.IsFixedSize {
2089 get { return true; }
2092 object IList.this [int index] {
2093 get { return this [index]; }
2094 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2096 #endregion // Public Properties
2098 #region Public Methods
2099 public bool Contains (int checkedIndex)
2101 return list.Contains (checkedIndex);
2104 public IEnumerator GetEnumerator ()
2106 return list.GetEnumerator ();
2109 void ICollection.CopyTo (Array dest, int index)
2111 list.CopyTo (dest, index);
2114 int IList.Add (object value)
2116 throw new NotSupportedException ("Add operation is not supported.");
2121 throw new NotSupportedException ("Clear operation is not supported.");
2124 bool IList.Contains (object checkedIndex)
2126 return list.Contains (checkedIndex);
2129 int IList.IndexOf (object checkedIndex)
2131 return list.IndexOf (checkedIndex);
2134 void IList.Insert (int index, object value)
2136 throw new NotSupportedException ("Insert operation is not supported.");
2139 void IList.Remove (object value)
2141 throw new NotSupportedException ("Remove operation is not supported.");
2144 void IList.RemoveAt (int index)
2146 throw new NotSupportedException ("RemoveAt operation is not supported.");
2149 public int IndexOf (int checkedIndex)
2151 return list.IndexOf (checkedIndex);
2153 #endregion // Public Methods
2155 } // CheckedIndexCollection
2157 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2159 internal ArrayList list;
2160 private ListView owner;
2162 #region Public Constructor
2163 public CheckedListViewItemCollection (ListView owner)
2165 list = new ArrayList ();
2168 #endregion // Public Constructor
2170 #region Public Properties
2173 get { return list.Count; }
2176 public bool IsReadOnly {
2177 get { return true; }
2180 public ListViewItem this [int index] {
2182 if (index < 0 || index >= list.Count)
2183 throw new ArgumentOutOfRangeException ("index");
2184 return (ListViewItem) list [index];
2188 bool ICollection.IsSynchronized {
2189 get { return list.IsSynchronized; }
2192 object ICollection.SyncRoot {
2193 get { return this; }
2196 bool IList.IsFixedSize {
2197 get { return true; }
2200 object IList.this [int index] {
2201 get { return this [index]; }
2202 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2204 #endregion // Public Properties
2206 #region Public Methods
2207 public bool Contains (ListViewItem item)
2209 return list.Contains (item);
2212 public void CopyTo (Array dest, int index)
2214 list.CopyTo (dest, index);
2217 public IEnumerator GetEnumerator ()
2219 return list.GetEnumerator ();
2222 int IList.Add (object value)
2224 throw new NotSupportedException ("Add operation is not supported.");
2229 throw new NotSupportedException ("Clear operation is not supported.");
2232 bool IList.Contains (object item)
2234 return list.Contains (item);
2237 int IList.IndexOf (object item)
2239 return list.IndexOf (item);
2242 void IList.Insert (int index, object value)
2244 throw new NotSupportedException ("Insert operation is not supported.");
2247 void IList.Remove (object value)
2249 throw new NotSupportedException ("Remove operation is not supported.");
2252 void IList.RemoveAt (int index)
2254 throw new NotSupportedException ("RemoveAt operation is not supported.");
2257 public int IndexOf (ListViewItem item)
2259 return list.IndexOf (item);
2261 #endregion // Public Methods
2263 } // CheckedListViewItemCollection
2265 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2267 internal ArrayList list;
2268 private ListView owner;
2270 #region Public Constructor
2271 public ColumnHeaderCollection (ListView owner)
2273 list = new ArrayList ();
2276 #endregion // Public Constructor
2278 #region Public Properties
2281 get { return list.Count; }
2284 public bool IsReadOnly {
2285 get { return false; }
2288 public virtual ColumnHeader this [int index] {
2290 if (index < 0 || index >= list.Count)
2291 throw new ArgumentOutOfRangeException ("index");
2292 return (ColumnHeader) list [index];
2296 bool ICollection.IsSynchronized {
2297 get { return true; }
2300 object ICollection.SyncRoot {
2301 get { return this; }
2304 bool IList.IsFixedSize {
2305 get { return list.IsFixedSize; }
2308 object IList.this [int index] {
2309 get { return this [index]; }
2310 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2312 #endregion // Public Properties
2314 #region Public Methods
2315 public virtual int Add (ColumnHeader value)
2318 value.owner = this.owner;
2319 idx = list.Add (value);
2320 if (owner.IsHandleCreated) {
2321 owner.Redraw (true);
2326 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2328 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2329 this.Add (colHeader);
2333 public virtual void AddRange (ColumnHeader [] values)
2335 foreach (ColumnHeader colHeader in values) {
2336 colHeader.owner = this.owner;
2340 owner.Redraw (true);
2343 public virtual void Clear ()
2346 owner.Redraw (true);
2349 public bool Contains (ColumnHeader value)
2351 return list.Contains (value);
2354 public IEnumerator GetEnumerator ()
2356 return list.GetEnumerator ();
2359 void ICollection.CopyTo (Array dest, int index)
2361 list.CopyTo (dest, index);
2364 int IList.Add (object value)
2366 if (! (value is ColumnHeader)) {
2367 throw new ArgumentException ("Not of type ColumnHeader", "value");
2370 return this.Add ((ColumnHeader) value);
2373 bool IList.Contains (object value)
2375 if (! (value is ColumnHeader)) {
2376 throw new ArgumentException ("Not of type ColumnHeader", "value");
2379 return this.Contains ((ColumnHeader) value);
2382 int IList.IndexOf (object value)
2384 if (! (value is ColumnHeader)) {
2385 throw new ArgumentException ("Not of type ColumnHeader", "value");
2388 return this.IndexOf ((ColumnHeader) value);
2391 void IList.Insert (int index, object value)
2393 if (! (value is ColumnHeader)) {
2394 throw new ArgumentException ("Not of type ColumnHeader", "value");
2397 this.Insert (index, (ColumnHeader) value);
2400 void IList.Remove (object value)
2402 if (! (value is ColumnHeader)) {
2403 throw new ArgumentException ("Not of type ColumnHeader", "value");
2406 this.Remove ((ColumnHeader) value);
2409 public int IndexOf (ColumnHeader value)
2411 return list.IndexOf (value);
2414 public void Insert (int index, ColumnHeader value)
2416 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2417 // but it's really only greater.
2418 if (index < 0 || index > list.Count)
2419 throw new ArgumentOutOfRangeException ("index");
2421 value.owner = this.owner;
2422 list.Insert (index, value);
2423 owner.Redraw (true);
2426 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2428 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2429 this.Insert (index, colHeader);
2432 public virtual void Remove (ColumnHeader column)
2434 // TODO: Update Column internal index ?
2435 list.Remove (column);
2436 owner.Redraw (true);
2439 public virtual void RemoveAt (int index)
2441 if (index < 0 || index >= list.Count)
2442 throw new ArgumentOutOfRangeException ("index");
2444 // TODO: Update Column internal index ?
2445 list.RemoveAt (index);
2446 owner.Redraw (true);
2448 #endregion // Public Methods
2451 } // ColumnHeaderCollection
2453 public class ListViewItemCollection : IList, ICollection, IEnumerable
2455 internal ArrayList list;
2456 private ListView owner;
2458 #region Public Constructor
2459 public ListViewItemCollection (ListView owner)
2461 list = new ArrayList ();
2464 #endregion // Public Constructor
2466 #region Public Properties
2469 get { return list.Count; }
2472 public bool IsReadOnly {
2473 get { return false; }
2476 public virtual ListViewItem this [int displayIndex] {
2478 if (displayIndex < 0 || displayIndex >= list.Count)
2479 throw new ArgumentOutOfRangeException ("displayIndex");
2480 return (ListViewItem) list [displayIndex];
2484 if (displayIndex < 0 || displayIndex >= list.Count)
2485 throw new ArgumentOutOfRangeException ("displayIndex");
2487 if (list.Contains (value))
2488 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2490 value.Owner = owner;
2491 list [displayIndex] = value;
2493 owner.Redraw (true);
2497 bool ICollection.IsSynchronized {
2498 get { return true; }
2501 object ICollection.SyncRoot {
2502 get { return this; }
2505 bool IList.IsFixedSize {
2506 get { return list.IsFixedSize; }
2509 object IList.this [int index] {
2510 get { return this [index]; }
2512 if (value is ListViewItem)
2513 this [index] = (ListViewItem) value;
2515 this [index] = new ListViewItem (value.ToString ());
2518 #endregion // Public Properties
2520 #region Public Methods
2521 public virtual ListViewItem Add (ListViewItem value)
2523 if (list.Contains (value))
2524 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2526 value.Owner = owner;
2529 if (owner.Sorting != SortOrder.None)
2532 owner.Redraw (true);
2537 public virtual ListViewItem Add (string text)
2539 ListViewItem item = new ListViewItem (text);
2540 return this.Add (item);
2543 public virtual ListViewItem Add (string text, int imageIndex)
2545 ListViewItem item = new ListViewItem (text, imageIndex);
2546 return this.Add (item);
2549 public void AddRange (ListViewItem [] values)
2552 owner.SelectedItems.list.Clear ();
2553 owner.SelectedIndices.list.Clear ();
2554 owner.CheckedItems.list.Clear ();
2555 owner.CheckedIndices.list.Clear ();
2557 foreach (ListViewItem item in values) {
2562 if (owner.Sorting != SortOrder.None)
2565 owner.Redraw (true);
2568 public virtual void Clear ()
2570 owner.SetFocusedItem (null);
2571 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2573 owner.SelectedItems.list.Clear ();
2574 owner.SelectedIndices.list.Clear ();
2575 owner.CheckedItems.list.Clear ();
2576 owner.CheckedIndices.list.Clear ();
2577 owner.Redraw (true);
2580 public bool Contains (ListViewItem item)
2582 return list.Contains (item);
2585 public void CopyTo (Array dest, int index)
2587 list.CopyTo (dest, index);
2590 public IEnumerator GetEnumerator ()
2592 return list.GetEnumerator ();
2595 int IList.Add (object item)
2600 if (item is ListViewItem) {
2601 li = (ListViewItem) item;
2602 if (list.Contains (li))
2603 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2606 li = new ListViewItem (item.ToString ());
2609 result = list.Add (li);
2610 owner.Redraw (true);
2615 bool IList.Contains (object item)
2617 return list.Contains (item);
2620 int IList.IndexOf (object item)
2622 return list.IndexOf (item);
2625 void IList.Insert (int index, object item)
2627 if (item is ListViewItem)
2628 this.Insert (index, (ListViewItem) item);
2630 this.Insert (index, item.ToString ());
2633 void IList.Remove (object item)
2635 Remove ((ListViewItem) item);
2638 public int IndexOf (ListViewItem item)
2640 return list.IndexOf (item);
2643 public ListViewItem Insert (int index, ListViewItem item)
2645 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2646 // but it's really only greater.
2647 if (index < 0 || index > list.Count)
2648 throw new ArgumentOutOfRangeException ("index");
2650 if (list.Contains (item))
2651 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2654 list.Insert (index, item);
2655 owner.Redraw (true);
2659 public ListViewItem Insert (int index, string text)
2661 return this.Insert (index, new ListViewItem (text));
2664 public ListViewItem Insert (int index, string text, int imageIndex)
2666 return this.Insert (index, new ListViewItem (text, imageIndex));
2669 public virtual void Remove (ListViewItem item)
2671 if (!list.Contains (item))
2674 owner.SelectedItems.list.Remove (item);
2675 owner.SelectedIndices.list.Remove (item.Index);
2676 owner.CheckedItems.list.Remove (item);
2677 owner.CheckedIndices.list.Remove (item.Index);
2679 owner.Redraw (true);
2682 public virtual void RemoveAt (int index)
2684 if (index < 0 || index >= list.Count)
2685 throw new ArgumentOutOfRangeException ("index");
2687 list.RemoveAt (index);
2688 owner.SelectedItems.list.RemoveAt (index);
2689 owner.SelectedIndices.list.RemoveAt (index);
2690 owner.CheckedItems.list.RemoveAt (index);
2691 owner.CheckedIndices.list.RemoveAt (index);
2692 owner.Redraw (false);
2694 #endregion // Public Methods
2696 } // ListViewItemCollection
2698 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2700 internal ArrayList list;
2701 private ListView owner;
2703 #region Public Constructor
2704 public SelectedIndexCollection (ListView owner)
2706 list = new ArrayList ();
2709 #endregion // Public Constructor
2711 #region Public Properties
2714 get { return list.Count; }
2717 public bool IsReadOnly {
2718 get { return true; }
2721 public int this [int index] {
2723 if (index < 0 || index >= list.Count)
2724 throw new ArgumentOutOfRangeException ("index");
2725 return (int) list [index];
2729 bool ICollection.IsSynchronized {
2730 get { return list.IsSynchronized; }
2733 object ICollection.SyncRoot {
2734 get { return this; }
2737 bool IList.IsFixedSize {
2738 get { return true; }
2741 object IList.this [int index] {
2742 get { return this [index]; }
2743 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2745 #endregion // Public Properties
2747 #region Public Methods
2748 public bool Contains (int selectedIndex)
2750 return list.Contains (selectedIndex);
2753 public void CopyTo (Array dest, int index)
2755 list.CopyTo (dest, index);
2758 public IEnumerator GetEnumerator ()
2760 return list.GetEnumerator ();
2763 int IList.Add (object value)
2765 throw new NotSupportedException ("Add operation is not supported.");
2770 throw new NotSupportedException ("Clear operation is not supported.");
2773 bool IList.Contains (object selectedIndex)
2775 return list.Contains (selectedIndex);
2778 int IList.IndexOf (object selectedIndex)
2780 return list.IndexOf (selectedIndex);
2783 void IList.Insert (int index, object value)
2785 throw new NotSupportedException ("Insert operation is not supported.");
2788 void IList.Remove (object value)
2790 throw new NotSupportedException ("Remove operation is not supported.");
2793 void IList.RemoveAt (int index)
2795 throw new NotSupportedException ("RemoveAt operation is not supported.");
2798 public int IndexOf (int selectedIndex)
2800 return list.IndexOf (selectedIndex);
2802 #endregion // Public Methods
2804 } // SelectedIndexCollection
2806 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2808 internal ArrayList list;
2809 private ListView owner;
2811 #region Public Constructor
2812 public SelectedListViewItemCollection (ListView owner)
2814 list = new ArrayList ();
2817 #endregion // Public Constructor
2819 #region Public Properties
2822 get { return list.Count; }
2825 public bool IsReadOnly {
2826 get { return true; }
2829 public ListViewItem this [int index] {
2831 if (index < 0 || index >= list.Count)
2832 throw new ArgumentOutOfRangeException ("index");
2833 return (ListViewItem) list [index];
2837 bool ICollection.IsSynchronized {
2838 get { return list.IsSynchronized; }
2841 object ICollection.SyncRoot {
2842 get { return this; }
2845 bool IList.IsFixedSize {
2846 get { return true; }
2849 object IList.this [int index] {
2850 get { return this [index]; }
2851 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2853 #endregion // Public Properties
2855 #region Public Methods
2856 public void Clear ()
2858 ArrayList copy = (ArrayList) list.Clone ();
2859 for (int i = 0; i < copy.Count; i++)
2860 ((ListViewItem) copy [i]).Selected = false;
2865 public bool Contains (ListViewItem item)
2867 return list.Contains (item);
2870 public void CopyTo (Array dest, int index)
2872 list.CopyTo (dest, index);
2875 public IEnumerator GetEnumerator ()
2877 return list.GetEnumerator ();
2880 int IList.Add (object value)
2882 throw new NotSupportedException ("Add operation is not supported.");
2885 bool IList.Contains (object item)
2887 return list.Contains (item);
2890 int IList.IndexOf (object item)
2892 return list.IndexOf (item);
2895 void IList.Insert (int index, object value)
2897 throw new NotSupportedException ("Insert operation is not supported.");
2900 void IList.Remove (object value)
2902 throw new NotSupportedException ("Remove operation is not supported.");
2905 void IList.RemoveAt (int index)
2907 throw new NotSupportedException ("RemoveAt operation is not supported.");
2910 public int IndexOf (ListViewItem item)
2912 return list.IndexOf (item);
2914 #endregion // Public Methods
2916 } // SelectedListViewItemCollection
2918 #endregion // Subclasses