1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com)
23 // Ravindra Kumar (rkumar@novell.com)
24 // Jordi Mas i Hernandez, jordi@ximian.com
25 // Mike Kestner (mkestner@novell.com)
28 // - Feedback for item activation, change in cursor types as mouse moves.
37 using System.Collections;
38 using System.ComponentModel;
39 using System.ComponentModel.Design;
41 using System.Runtime.InteropServices;
42 using System.Globalization;
44 namespace System.Windows.Forms
46 [DefaultEvent ("SelectedIndexChanged")]
47 [DefaultProperty ("Items")]
48 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
49 public class ListView : Control
51 private ItemActivation activation = ItemActivation.Standard;
52 private ListViewAlignment alignment = ListViewAlignment.Top;
53 private bool allow_column_reorder = false;
54 private bool auto_arrange = true;
55 private bool check_boxes = false;
56 private CheckedIndexCollection checked_indices;
57 private CheckedListViewItemCollection checked_items;
58 private ColumnHeaderCollection columns;
59 internal ListViewItem focused_item;
60 private bool full_row_select = false;
61 private bool grid_lines = false;
62 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
63 private bool hide_selection = true;
64 private bool hover_selection = false;
65 private IComparer item_sorter = new ItemComparer ();
66 private ListViewItemCollection items;
67 private bool label_edit = false;
68 private bool label_wrap = true;
69 private bool multiselect = true;
70 private bool scrollable = true;
71 private SelectedIndexCollection selected_indices;
72 private SelectedListViewItemCollection selected_items;
73 private SortOrder sort_order = SortOrder.None;
74 private ImageList state_image_list;
75 private bool updating = false;
76 private View view = View.LargeIcon;
77 private int layout_wd; // We might draw more than our client area
78 private int layout_ht; // therefore we need to have these two.
79 //private TextBox editor; // Used for editing an item text
80 HeaderControl header_control;
81 internal ItemControl item_control;
82 internal ScrollBar h_scroll; // used for scrolling horizontally
83 internal ScrollBar v_scroll; // used for scrolling vertically
84 internal int h_marker; // Position markers for scrolling
85 internal int v_marker;
86 private int keysearch_tickcnt;
87 private string keysearch_text;
88 static private readonly int keysearch_keydelay = 1000;
89 private int[] reordered_column_indices;
92 internal ImageList large_image_list;
93 internal ImageList small_image_list;
94 internal Size text_size = Size.Empty;
97 public event LabelEditEventHandler AfterLabelEdit;
100 [EditorBrowsable (EditorBrowsableState.Never)]
101 public new event EventHandler BackgroundImageChanged {
102 add { base.BackgroundImageChanged += value; }
103 remove { base.BackgroundImageChanged -= value; }
106 public event LabelEditEventHandler BeforeLabelEdit;
107 public event ColumnClickEventHandler ColumnClick;
108 public event EventHandler ItemActivate;
109 public event ItemCheckEventHandler ItemCheck;
110 public event ItemDragEventHandler ItemDrag;
113 [EditorBrowsable (EditorBrowsableState.Never)]
114 public new event PaintEventHandler Paint {
115 add { base.Paint += value; }
116 remove { base.Paint -= value; }
119 public event EventHandler SelectedIndexChanged;
122 [EditorBrowsable (EditorBrowsableState.Never)]
123 public new event EventHandler TextChanged {
124 add { base.TextChanged += value; }
125 remove { base.TextChanged -= value; }
130 #region Public Constructors
133 background_color = ThemeEngine.Current.ColorWindow;
134 checked_indices = new CheckedIndexCollection (this);
135 checked_items = new CheckedListViewItemCollection (this);
136 columns = new ColumnHeaderCollection (this);
137 foreground_color = SystemColors.WindowText;
138 items = new ListViewItemCollection (this);
139 selected_indices = new SelectedIndexCollection (this);
140 selected_items = new SelectedListViewItemCollection (this);
142 border_style = BorderStyle.Fixed3D;
144 header_control = new HeaderControl (this);
145 header_control.Visible = false;
146 Controls.AddImplicit (header_control);
148 item_control = new ItemControl (this);
149 Controls.AddImplicit (item_control);
151 h_scroll = new HScrollBar ();
152 Controls.AddImplicit (this.h_scroll);
154 v_scroll = new VScrollBar ();
155 Controls.AddImplicit (this.v_scroll);
157 h_marker = v_marker = 0;
158 keysearch_tickcnt = 0;
160 // scroll bars are disabled initially
161 h_scroll.Visible = false;
162 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
163 v_scroll.Visible = false;
164 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
167 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
168 SizeChanged += new EventHandler (ListView_SizeChanged);
169 GotFocus += new EventHandler (FocusChanged);
170 LostFocus += new EventHandler (FocusChanged);
172 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
174 #endregion // Public Constructors
176 #region Private Internal Properties
177 internal Size CheckBoxSize {
179 if (this.check_boxes) {
180 if (this.state_image_list != null)
181 return this.state_image_list.ImageSize;
183 return ThemeEngine.Current.ListViewCheckBoxSize;
191 bool CanMultiselect {
195 else if (multiselect && (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0)
202 #endregion // Private Internal Properties
204 #region Protected Properties
205 protected override CreateParams CreateParams {
206 get { return base.CreateParams; }
209 protected override Size DefaultSize {
210 get { return ThemeEngine.Current.ListViewDefaultSize; }
212 #endregion // Protected Properties
214 #region Public Instance Properties
215 [DefaultValue (ItemActivation.Standard)]
216 public ItemActivation Activation {
217 get { return activation; }
219 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
220 value != ItemActivation.TwoClick) {
221 throw new InvalidEnumArgumentException (string.Format
222 ("Enum argument value '{0}' is not valid for Activation", value));
229 [DefaultValue (ListViewAlignment.Top)]
231 public ListViewAlignment Alignment {
232 get { return alignment; }
234 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
235 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
236 throw new InvalidEnumArgumentException (string.Format
237 ("Enum argument value '{0}' is not valid for Alignment", value));
240 if (this.alignment != value) {
242 // alignment does not matter in Details/List views
243 if (this.view == View.LargeIcon ||
244 this.View == View.SmallIcon)
250 [DefaultValue (false)]
251 public bool AllowColumnReorder {
252 get { return allow_column_reorder; }
253 set { allow_column_reorder = value; }
256 [DefaultValue (true)]
257 public bool AutoArrange {
258 get { return auto_arrange; }
260 if (auto_arrange != value) {
261 auto_arrange = value;
262 // autoarrange does not matter in Details/List views
263 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
269 public override Color BackColor {
271 if (background_color.IsEmpty)
272 return ThemeEngine.Current.ColorWindow;
274 return background_color;
276 set { background_color = value; }
280 [EditorBrowsable (EditorBrowsableState.Never)]
281 public override Image BackgroundImage {
282 get { return background_image; }
284 if (value == background_image)
287 background_image = value;
288 OnBackgroundImageChanged (EventArgs.Empty);
292 [DefaultValue (BorderStyle.Fixed3D)]
294 public BorderStyle BorderStyle {
295 get { return InternalBorderStyle; }
296 set { InternalBorderStyle = value; }
299 [DefaultValue (false)]
300 public bool CheckBoxes {
301 get { return check_boxes; }
303 if (check_boxes != value) {
311 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
312 public CheckedIndexCollection CheckedIndices {
313 get { return checked_indices; }
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
318 public CheckedListViewItemCollection CheckedItems {
319 get { return checked_items; }
322 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
324 [MergableProperty (false)]
325 public ColumnHeaderCollection Columns {
326 get { return columns; }
330 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
331 public ListViewItem FocusedItem {
337 public override Color ForeColor {
339 if (foreground_color.IsEmpty)
340 return ThemeEngine.Current.ColorWindowText;
342 return foreground_color;
344 set { foreground_color = value; }
347 [DefaultValue (false)]
348 public bool FullRowSelect {
349 get { return full_row_select; }
350 set { full_row_select = value; }
353 [DefaultValue (false)]
354 public bool GridLines {
355 get { return grid_lines; }
357 if (grid_lines != value) {
364 [DefaultValue (ColumnHeaderStyle.Clickable)]
365 public ColumnHeaderStyle HeaderStyle {
366 get { return header_style; }
368 if (header_style == value)
372 case ColumnHeaderStyle.Clickable:
373 case ColumnHeaderStyle.Nonclickable:
374 case ColumnHeaderStyle.None:
377 throw new InvalidEnumArgumentException (string.Format
378 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
381 header_style = value;
382 if (view == View.Details)
387 [DefaultValue (true)]
388 public bool HideSelection {
389 get { return hide_selection; }
391 if (hide_selection != value) {
392 hide_selection = value;
398 [DefaultValue (false)]
399 public bool HoverSelection {
400 get { return hover_selection; }
401 set { hover_selection = value; }
404 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
406 [MergableProperty (false)]
407 public ListViewItemCollection Items {
408 get { return items; }
411 [DefaultValue (false)]
412 public bool LabelEdit {
413 get { return label_edit; }
414 set { label_edit = value; }
417 [DefaultValue (true)]
419 public bool LabelWrap {
420 get { return label_wrap; }
422 if (label_wrap != value) {
429 [DefaultValue (null)]
430 public ImageList LargeImageList {
431 get { return large_image_list; }
433 large_image_list = value;
439 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
440 public IComparer ListViewItemSorter {
441 get { return item_sorter; }
442 set { item_sorter = value; }
445 [DefaultValue (true)]
446 public bool MultiSelect {
447 get { return multiselect; }
448 set { multiselect = value; }
451 [DefaultValue (true)]
452 public bool Scrollable {
453 get { return scrollable; }
455 if (scrollable != value) {
463 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
464 public SelectedIndexCollection SelectedIndices {
465 get { return selected_indices; }
469 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
470 public SelectedListViewItemCollection SelectedItems {
471 get { return selected_items; }
475 [MonoTODO("Implement")]
476 public bool ShowGroups {
486 [DefaultValue (null)]
487 public ImageList SmallImageList {
488 get { return small_image_list; }
490 small_image_list = value;
495 [DefaultValue (SortOrder.None)]
496 public SortOrder Sorting {
497 get { return sort_order; }
499 if (value != SortOrder.Ascending && value != SortOrder.Descending &&
500 value != SortOrder.None) {
501 throw new InvalidEnumArgumentException (string.Format
502 ("Enum argument value '{0}' is not valid for Sorting", value));
505 if (sort_order != value) {
512 [DefaultValue (null)]
513 public ImageList StateImageList {
514 get { return state_image_list; }
516 state_image_list = value;
523 [EditorBrowsable (EditorBrowsableState.Never)]
524 public override string Text {
533 OnTextChanged (EventArgs.Empty);
538 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539 public ListViewItem TopItem {
542 if (this.items.Count == 0)
544 // if contents are not scrolled
545 // it is the first item
546 else if (h_marker == 0 && v_marker == 0)
547 return this.items [0];
548 // do a hit test for the scrolled position
550 foreach (ListViewItem item in this.items) {
551 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
560 [MonoTODO("Implement")]
561 public bool UseCompatibleStateImageBehavior {
571 [DefaultValue (View.LargeIcon)]
575 if (value != View.Details && value != View.LargeIcon &&
576 value != View.List && value != View.SmallIcon ) {
577 throw new InvalidEnumArgumentException (string.Format
578 ("Enum argument value '{0}' is not valid for View", value));
582 h_scroll.Value = v_scroll.Value = 0;
588 #endregion // Public Instance Properties
590 #region Internal Methods Properties
592 internal int FirstVisibleIndex {
595 if (this.items.Count == 0)
598 if (h_marker == 0 && v_marker == 0)
601 foreach (ListViewItem item in this.items) {
602 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
611 internal int LastVisibleIndex {
613 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
614 if (View == View.List || Alignment == ListViewAlignment.Left) {
615 if (Items[i].Bounds.X > ClientRectangle.Right)
618 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
623 return Items.Count - 1;
627 internal int TotalWidth {
628 get { return Math.Max (this.Width, this.layout_wd); }
631 internal int TotalHeight {
632 get { return Math.Max (this.Height, this.layout_ht); }
635 internal void Redraw (bool recalculate)
637 // Avoid calculations when control is being updated
642 CalculateListView (this.alignment);
647 internal Size GetChildColumnSize (int index)
649 Size ret_size = Size.Empty;
650 ColumnHeader col = this.columns [index];
652 if (col.Width == -2) { // autosize = max(items, columnheader)
653 Size size = Size.Ceiling (this.DeviceContext.MeasureString
654 (col.Text, this.Font));
655 ret_size = BiggestItem (index);
656 if (size.Width > ret_size.Width)
659 else { // -1 and all the values < -2 are put under one category
660 ret_size = BiggestItem (index);
661 // fall back to empty columns' width if no subitem is available for a column
662 if (ret_size.IsEmpty) {
663 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
664 if (col.Text.Length > 0)
665 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
666 (col.Text, this.Font)).Height;
668 ret_size.Height = this.Font.Height;
672 // adjust the size for icon and checkbox for 0th column
674 ret_size.Width += (this.CheckBoxSize.Width + 4);
675 if (this.small_image_list != null)
676 ret_size.Width += this.small_image_list.ImageSize.Width;
681 // Returns the size of biggest item text in a column.
682 private Size BiggestItem (int col)
684 Size temp = Size.Empty;
685 Size ret_size = Size.Empty;
687 // 0th column holds the item text, we check the size of
688 // the various subitems falling in that column and get
689 // the biggest one's size.
690 foreach (ListViewItem item in items) {
691 if (col >= item.SubItems.Count)
694 temp = Size.Ceiling (this.DeviceContext.MeasureString
695 (item.SubItems [col].Text, this.Font));
696 if (temp.Width > ret_size.Width)
700 // adjustment for space
701 if (!ret_size.IsEmpty)
707 // Sets the size of the biggest item text as per the view
708 private void CalcTextSize ()
710 // clear the old value
711 text_size = Size.Empty;
713 if (items.Count == 0)
716 text_size = BiggestItem (0);
718 if (view == View.LargeIcon && this.label_wrap) {
719 Size temp = Size.Empty;
720 if (this.check_boxes)
721 temp.Width += 2 * this.CheckBoxSize.Width;
722 if (large_image_list != null)
723 temp.Width += large_image_list.ImageSize.Width;
726 // wrapping is done for two lines only
727 if (text_size.Width > temp.Width) {
728 text_size.Width = temp.Width;
729 text_size.Height *= 2;
732 else if (view == View.List) {
733 // in list view max text shown in determined by the
734 // control width, even if scolling is enabled.
735 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
736 if (this.small_image_list != null)
737 max_wd -= this.small_image_list.ImageSize.Width;
739 if (text_size.Width > max_wd)
740 text_size.Width = max_wd;
743 // we do the default settings, if we have got 0's
744 if (text_size.Height <= 0)
745 text_size.Height = this.Font.Height;
746 if (text_size.Width <= 0)
747 text_size.Width = this.Width;
750 text_size.Width += 4;
751 text_size.Height += 2;
754 private void Scroll (ScrollBar scrollbar, int delta)
756 if (delta == 0 || !scrollbar.Visible)
760 if (scrollbar == h_scroll)
761 max = h_scroll.Maximum - item_control.Width;
763 max = v_scroll.Maximum - item_control.Height;
765 int val = scrollbar.Value + delta;
768 else if (val < scrollbar.Minimum)
769 val = scrollbar.Minimum;
770 scrollbar.Value = val;
773 private void CalculateScrollBars ()
775 Rectangle client_area = ClientRectangle;
777 if (!this.scrollable || this.items.Count <= 0) {
778 h_scroll.Visible = false;
779 v_scroll.Visible = false;
783 // Don't calculate if the view is not displayable
784 if (client_area.Height < 0 || client_area.Width < 0)
787 // making a scroll bar visible might make
788 // other scroll bar visible
789 if (layout_wd > client_area.Right) {
790 h_scroll.Visible = true;
791 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
792 v_scroll.Visible = true;
794 v_scroll.Visible = false;
795 } else if (layout_ht > client_area.Bottom) {
796 v_scroll.Visible = true;
797 if ((layout_wd + v_scroll.Width) > client_area.Right)
798 h_scroll.Visible = true;
800 h_scroll.Visible = false;
802 h_scroll.Visible = false;
803 v_scroll.Visible = false;
806 item_control.Height = ClientRectangle.Height - header_control.Height;
808 if (h_scroll.Visible) {
809 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
810 h_scroll.Minimum = 0;
812 // if v_scroll is visible, adjust the maximum of the
813 // h_scroll to account for the width of v_scroll
814 if (v_scroll.Visible) {
815 h_scroll.Maximum = layout_wd + v_scroll.Width;
816 h_scroll.Width = client_area.Width - v_scroll.Width;
819 h_scroll.Maximum = layout_wd;
820 h_scroll.Width = client_area.Width;
823 h_scroll.LargeChange = client_area.Width;
824 h_scroll.SmallChange = Font.Height;
825 item_control.Height -= h_scroll.Height;
828 if (header_control.Visible)
829 header_control.Width = ClientRectangle.Width;
830 item_control.Width = ClientRectangle.Width;
832 if (v_scroll.Visible) {
833 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
834 v_scroll.Minimum = 0;
836 // if h_scroll is visible, adjust the maximum of the
837 // v_scroll to account for the height of h_scroll
838 if (h_scroll.Visible) {
839 v_scroll.Maximum = layout_ht + h_scroll.Height;
840 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
842 v_scroll.Maximum = layout_ht;
843 v_scroll.Height = client_area.Height;
846 v_scroll.LargeChange = client_area.Height;
847 v_scroll.SmallChange = Font.Height;
848 if (header_control.Visible)
849 header_control.Width -= v_scroll.Width;
850 item_control.Width -= v_scroll.Width;
854 ColumnHeader GetReorderedColumn (int index)
856 if (reordered_column_indices == null)
857 return Columns [index];
859 return Columns [reordered_column_indices [index]];
862 void ReorderColumn (ColumnHeader col, int index)
864 if (reordered_column_indices == null) {
865 reordered_column_indices = new int [Columns.Count];
866 for (int i = 0; i < Columns.Count; i++)
867 reordered_column_indices [i] = i;
870 if (reordered_column_indices [index] == col.Index)
873 int[] curr = reordered_column_indices;
874 int[] result = new int [Columns.Count];
876 for (int i = 0; i < Columns.Count; i++) {
877 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
881 result [i] = col.Index;
883 result [i] = curr [curr_idx++];
886 reordered_column_indices = result;
888 header_control.Invalidate ();
889 item_control.Invalidate ();
892 Size LargeIconItemSize {
894 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
895 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
896 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
897 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
898 return new Size (w, h);
902 Size SmallIconItemSize {
904 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
905 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
906 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
907 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
908 return new Size (w, h);
914 ListViewItem[,] item_matrix;
916 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
918 header_control.Visible = false;
919 header_control.Size = Size.Empty;
920 item_control.Visible = true;
921 item_control.Location = Point.Empty;
923 if (items.Count == 0)
926 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
928 Rectangle area = ClientRectangle;
931 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
934 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
936 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
939 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
942 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
943 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
944 item_matrix = new ListViewItem [rows, cols];
947 foreach (ListViewItem item in items) {
948 int x = col * (sz.Width + x_spacing);
949 int y = row * (sz.Height + y_spacing);
950 item.Location = new Point (x, y);
954 item_matrix [row, col] = item;
968 item_control.Size = new Size (layout_wd, layout_ht);
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 if (header_style == ColumnHeaderStyle.None) {
986 header_control.Visible = false;
987 header_control.Size = Size.Empty;
989 header_control.Width = x;
990 header_control.Height = columns [0].Ht;
991 header_control.Visible = true;
995 void LayoutDetails ()
997 if (columns.Count == 0) {
998 header_control.Visible = false;
999 item_control.Visible = false;
1005 item_control.Visible = true;
1006 item_control.Location = new Point (0, header_control.Height);
1009 if (items.Count > 0) {
1010 foreach (ListViewItem item in items) {
1012 item.Location = new Point (0, y);
1013 y += item.Bounds.Height + 2;
1016 // some space for bottom gridline
1021 layout_wd = Math.Max (header_control.Width, item_control.Width);
1022 layout_ht = y + header_control.Height;
1025 private void CalculateListView (ListViewAlignment align)
1034 case View.SmallIcon:
1035 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1038 case View.LargeIcon:
1039 LayoutIcons (true, alignment == ListViewAlignment.Left,
1040 ThemeEngine.Current.ListViewHorizontalSpacing,
1041 ThemeEngine.Current.ListViewVerticalSpacing);
1045 LayoutIcons (false, true, 4, 2);
1049 CalculateScrollBars ();
1052 internal void UpdateSelection (ListViewItem item)
1054 if (item.Selected) {
1056 if (!CanMultiselect && SelectedItems.Count > 0) {
1057 SelectedItems.Clear ();
1058 SelectedIndices.list.Clear ();
1061 if (!SelectedItems.Contains (item)) {
1062 SelectedItems.list.Add (item);
1063 SelectedIndices.list.Add (item.Index);
1066 SelectedItems.list.Remove (item);
1067 SelectedIndices.list.Remove (item.Index);
1071 private bool KeySearchString (KeyEventArgs ke)
1073 int current_tickcnt = Environment.TickCount;
1074 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1075 keysearch_text = string.Empty;
1078 keysearch_text += (char) ke.KeyData;
1079 keysearch_tickcnt = current_tickcnt;
1081 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1084 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1085 CompareOptions.IgnoreCase)) {
1086 SetFocusedItem (Items [i]);
1087 items [i].Selected = true;
1091 i = (i + 1 < Items.Count) ? i+1 : 0;
1099 int GetAdjustedIndex (Keys key)
1103 if (View == View.Details) {
1105 result = FocusedItem.Index - 1;
1106 else if (key == Keys.Down) {
1107 result = FocusedItem.Index + 1;
1108 if (result == items.Count)
1114 int row = FocusedItem.row;
1115 int col = FocusedItem.col;
1121 return item_matrix [row, col - 1].Index;
1124 if (col == (cols - 1))
1126 while (item_matrix [row, col + 1] == null)
1128 return item_matrix [row, col + 1].Index;
1133 return item_matrix [row - 1, col].Index;
1136 if (row == (rows - 1) || row == Items.Count - 1)
1138 while (item_matrix [row + 1, col] == null)
1140 return item_matrix [row + 1, col].Index;
1147 ListViewItem selection_start;
1149 private bool SelectItems (ArrayList sel_items)
1151 bool changed = false;
1152 multiselecting = true;
1153 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1154 foreach (ListViewItem item in curr_items)
1155 if (!sel_items.Contains (item)) {
1156 item.Selected = false;
1159 foreach (ListViewItem item in sel_items)
1160 if (!item.Selected) {
1161 item.Selected = true;
1164 multiselecting = false;
1168 private void UpdateMultiSelection (int index)
1170 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1171 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1172 ListViewItem item = items [index];
1174 if (shift_pressed && selection_start != null) {
1175 ArrayList list = new ArrayList ();
1176 int start = Math.Min (selection_start.Index, index);
1177 int end = Math.Max (selection_start.Index, index);
1178 if (View == View.Details) {
1179 for (int i = start; i <= end; i++)
1180 list.Add (items [i]);
1182 int left = Math.Min (items [start].col, items [end].col);
1183 int right = Math.Max (items [start].col, items [end].col);
1184 int top = Math.Min (items [start].row, items [end].row);
1185 int bottom = Math.Max (items [start].row, items [end].row);
1186 foreach (ListViewItem curr in items)
1187 if (curr.row >= top && curr.row <= bottom &&
1188 curr.col >= left && curr.col <= right)
1191 if (SelectItems (list))
1192 OnSelectedIndexChanged (EventArgs.Empty);
1193 } else if (!ctrl_pressed) {
1194 SelectedItems.Clear ();
1195 SelectedIndices.list.Clear ();
1196 item.Selected = true;
1197 selection_start = item;
1198 OnSelectedIndexChanged (EventArgs.Empty);
1202 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1204 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1210 if (FocusedItem == null)
1211 SetFocusedItem (Items [0]);
1213 switch (ke.KeyCode) {
1216 index = Items.Count - 1;
1227 index = GetAdjustedIndex (ke.KeyCode);
1231 ke.Handled = KeySearchString (ke);
1239 UpdateMultiSelection (index);
1240 else if (!items [index].Selected) {
1241 items [index].Selected = true;
1242 OnSelectedIndexChanged (EventArgs.Empty);
1245 SetFocusedItem (items [index]);
1246 EnsureVisible (index);
1250 internal class ItemControl : Control {
1253 ListViewItem clicked_item;
1254 ListViewItem last_clicked_item;
1255 bool hover_processed = false;
1256 bool checking = false;
1258 public ItemControl (ListView owner)
1261 DoubleClick += new EventHandler(ItemsDoubleClick);
1262 MouseDown += new MouseEventHandler(ItemsMouseDown);
1263 MouseMove += new MouseEventHandler(ItemsMouseMove);
1264 MouseHover += new EventHandler(ItemsMouseHover);
1265 MouseUp += new MouseEventHandler(ItemsMouseUp);
1266 MouseWheel += new MouseEventHandler(ItemsMouseWheel);
1269 void ItemsDoubleClick (object sender, EventArgs e)
1271 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1272 owner.ItemActivate (this, e);
1282 BoxSelect box_select_mode = BoxSelect.None;
1283 ArrayList prev_selection;
1284 Point box_select_start;
1286 Rectangle box_select_rect;
1287 internal Rectangle BoxSelectRectangle {
1288 get { return box_select_rect; }
1290 if (box_select_rect == value)
1293 InvalidateBoxSelectRect ();
1294 box_select_rect = value;
1295 InvalidateBoxSelectRect ();
1299 void InvalidateBoxSelectRect ()
1301 if (BoxSelectRectangle.Size.IsEmpty)
1304 Rectangle edge = BoxSelectRectangle;
1310 edge.Y = BoxSelectRectangle.Bottom - 1;
1312 edge.Y = BoxSelectRectangle.Y - 1;
1314 edge.Height = BoxSelectRectangle.Height + 2;
1316 edge.X = BoxSelectRectangle.Right - 1;
1320 private Rectangle CalculateBoxSelectRectangle (Point pt)
1322 int left = Math.Min (box_select_start.X, pt.X);
1323 int right = Math.Max (box_select_start.X, pt.X);
1324 int top = Math.Min (box_select_start.Y, pt.Y);
1325 int bottom = Math.Max (box_select_start.Y, pt.Y);
1326 return Rectangle.FromLTRB (left, top, right, bottom);
1329 ArrayList BoxSelectedItems {
1331 ArrayList result = new ArrayList ();
1332 foreach (ListViewItem item in owner.Items) {
1333 Rectangle r = item.Bounds;
1335 r.Y += r.Height / 4;
1338 if (BoxSelectRectangle.IntersectsWith (r))
1345 private bool PerformBoxSelection (Point pt)
1347 if (box_select_mode == BoxSelect.None)
1350 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1352 ArrayList box_items = BoxSelectedItems;
1356 switch (box_select_mode) {
1358 case BoxSelect.Normal:
1362 case BoxSelect.Control:
1363 items = new ArrayList ();
1364 foreach (ListViewItem item in prev_selection)
1365 if (!box_items.Contains (item))
1367 foreach (ListViewItem item in box_items)
1368 if (!prev_selection.Contains (item))
1372 case BoxSelect.Shift:
1374 foreach (ListViewItem item in box_items)
1375 prev_selection.Remove (item);
1376 foreach (ListViewItem item in prev_selection)
1381 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1385 owner.SelectItems (items);
1391 private void ToggleCheckState (ListViewItem item)
1393 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1394 item.Checked = !item.Checked;
1395 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1397 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1398 owner.OnItemCheck (ice);
1401 private void ItemsMouseDown (object sender, MouseEventArgs me)
1403 if (owner.items.Count == 0)
1406 Point pt = new Point (me.X, me.Y);
1407 foreach (ListViewItem item in owner.items) {
1408 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1412 ToggleCheckState (item);
1416 if (owner.View == View.Details && !owner.FullRowSelect) {
1417 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1418 clicked_item = item;
1422 if (item.Bounds.Contains (pt)) {
1423 clicked_item = item;
1430 if (clicked_item != null) {
1431 owner.SetFocusedItem (clicked_item);
1432 bool changed = !clicked_item.Selected;
1433 if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0)
1434 owner.UpdateMultiSelection (clicked_item.Index);
1436 clicked_item.Selected = true;
1439 owner.OnSelectedIndexChanged (EventArgs.Empty);
1441 // Raise double click if the item was clicked. On MS the
1442 // double click is only raised if you double click an item
1443 if (me.Clicks > 1) {
1444 owner.OnDoubleClick (EventArgs.Empty);
1445 if (owner.CheckBoxes)
1446 ToggleCheckState (clicked_item);
1447 } else if (me.Clicks == 1)
1448 owner.OnClick (EventArgs.Empty);
1450 if (owner.MultiSelect) {
1451 Keys mods = XplatUI.State.ModifierKeys;
1452 if ((mods & Keys.Shift) != 0)
1453 box_select_mode = BoxSelect.Shift;
1454 else if ((mods & Keys.Control) != 0)
1455 box_select_mode = BoxSelect.Control;
1457 box_select_mode = BoxSelect.Normal;
1458 box_select_start = pt;
1459 prev_selection = (ArrayList) owner.SelectedItems.list.Clone ();
1460 } else if (owner.selected_indices.Count > 0) {
1461 owner.SelectedItems.Clear ();
1462 owner.SelectedIndices.list.Clear ();
1463 owner.OnSelectedIndexChanged (EventArgs.Empty);
1468 private void ItemsMouseMove (object sender, MouseEventArgs me)
1470 if (PerformBoxSelection (new Point (me.X, me.Y)))
1473 if (owner.HoverSelection && hover_processed) {
1475 Point pt = PointToClient (Control.MousePosition);
1476 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1477 if (item == null || item.Selected)
1480 hover_processed = false;
1481 XplatUI.ResetMouseHover (Handle);
1486 private void ItemsMouseHover (object sender, EventArgs e)
1488 if (Capture || !owner.HoverSelection)
1491 hover_processed = true;
1492 Point pt = PointToClient (Control.MousePosition);
1493 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1498 item.Selected = true;
1499 owner.OnSelectedIndexChanged (new EventArgs ());
1502 private void ItemsMouseUp (object sender, MouseEventArgs me)
1505 if (owner.Items.Count == 0)
1508 Point pt = new Point (me.X, me.Y);
1510 Rectangle rect = Rectangle.Empty;
1511 if (clicked_item != null) {
1512 if (owner.view == View.Details && !owner.full_row_select)
1513 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1515 rect = clicked_item.Bounds;
1517 if (rect.Contains (pt)) {
1518 switch (owner.activation) {
1519 case ItemActivation.OneClick:
1520 owner.OnItemActivate (EventArgs.Empty);
1523 case ItemActivation.TwoClick:
1524 if (last_clicked_item == clicked_item) {
1525 owner.OnItemActivate (EventArgs.Empty);
1526 last_clicked_item = null;
1528 last_clicked_item = clicked_item;
1531 // DoubleClick activation is handled in another handler
1535 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1536 // Need this to clean up background clicks
1537 owner.SelectedItems.Clear ();
1538 owner.SelectedIndices.list.Clear ();
1539 owner.OnSelectedIndexChanged (EventArgs.Empty);
1542 clicked_item = null;
1543 box_select_start = Point.Empty;
1544 BoxSelectRectangle = Rectangle.Empty;
1545 prev_selection = null;
1546 box_select_mode = BoxSelect.None;
1550 private void ItemsMouseWheel (object sender, MouseEventArgs me)
1552 if (owner.Items.Count == 0)
1555 int lines = me.Delta / 120;
1560 switch (owner.View) {
1562 case View.SmallIcon:
1563 owner.Scroll (owner.v_scroll, -owner.Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1565 case View.LargeIcon:
1566 owner.Scroll (owner.v_scroll, -(owner.Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1569 owner.Scroll (owner.h_scroll, -owner.Items [0].Bounds.Width * lines);
1574 internal override void OnPaintInternal (PaintEventArgs pe)
1576 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1579 internal override void OnGotFocusInternal (EventArgs e)
1585 internal override void OnPaintInternal (PaintEventArgs pe)
1590 CalculateScrollBars ();
1593 void FocusChanged (object o, EventArgs args)
1595 if (Items.Count == 0)
1598 if (FocusedItem == null)
1599 SetFocusedItem (Items [0]);
1601 item_control.Invalidate (FocusedItem.Bounds);
1604 private void ListView_SizeChanged (object sender, EventArgs e)
1606 CalculateListView (alignment);
1609 private void SetFocusedItem (ListViewItem item)
1611 if (focused_item != null)
1612 focused_item.Focused = false;
1615 item.Focused = true;
1617 focused_item = item;
1620 private void HorizontalScroller (object sender, EventArgs e)
1622 // Avoid unnecessary flickering, when button is
1623 // kept pressed at the end
1624 if (h_marker != h_scroll.Value) {
1626 int pixels = h_marker - h_scroll.Value;
1628 h_marker = h_scroll.Value;
1629 if (header_control.Visible)
1630 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1632 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1636 private void VerticalScroller (object sender, EventArgs e)
1638 // Avoid unnecessary flickering, when button is
1639 // kept pressed at the end
1640 if (v_marker != v_scroll.Value) {
1641 int pixels = v_marker - v_scroll.Value;
1642 Rectangle area = item_control.ClientRectangle;
1643 v_marker = v_scroll.Value;
1644 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1647 #endregion // Internal Methods Properties
1649 #region Protected Methods
1650 protected override void CreateHandle ()
1652 base.CreateHandle ();
1655 protected override void Dispose (bool disposing)
1658 h_scroll.Dispose ();
1659 v_scroll.Dispose ();
1661 large_image_list = null;
1662 small_image_list = null;
1663 state_image_list = null;
1666 base.Dispose (disposing);
1669 protected override bool IsInputKey (Keys keyData)
1686 return base.IsInputKey (keyData);
1689 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1691 if (AfterLabelEdit != null)
1692 AfterLabelEdit (this, e);
1695 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1697 if (BeforeLabelEdit != null)
1698 BeforeLabelEdit (this, e);
1701 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1703 if (ColumnClick != null)
1704 ColumnClick (this, e);
1707 protected override void OnEnabledChanged (EventArgs e)
1709 base.OnEnabledChanged (e);
1712 protected override void OnFontChanged (EventArgs e)
1714 base.OnFontChanged (e);
1718 protected override void OnHandleCreated (EventArgs e)
1720 base.OnHandleCreated (e);
1723 protected override void OnHandleDestroyed (EventArgs e)
1725 base.OnHandleDestroyed (e);
1728 protected virtual void OnItemActivate (EventArgs e)
1730 if (ItemActivate != null)
1731 ItemActivate (this, e);
1734 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1736 if (ItemCheck != null)
1737 ItemCheck (this, ice);
1740 protected virtual void OnItemDrag (ItemDragEventArgs e)
1742 if (ItemDrag != null)
1746 protected virtual void OnSelectedIndexChanged (EventArgs e)
1748 if (SelectedIndexChanged != null)
1749 SelectedIndexChanged (this, e);
1752 protected override void OnSystemColorsChanged (EventArgs e)
1754 base.OnSystemColorsChanged (e);
1757 protected void RealizeProperties ()
1762 protected void UpdateExtendedStyles ()
1767 protected override void WndProc (ref Message m)
1769 base.WndProc (ref m);
1771 #endregion // Protected Methods
1773 #region Public Instance Methods
1774 public void ArrangeIcons ()
1776 ArrangeIcons (this.alignment);
1779 public void ArrangeIcons (ListViewAlignment alignment)
1781 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1782 if (view == View.LargeIcon || view == View.SmallIcon) {
1783 this.CalculateListView (alignment);
1784 // we have done the calculations already
1785 this.Redraw (false);
1789 public void BeginUpdate ()
1791 // flag to avoid painting
1795 public void Clear ()
1798 items.Clear (); // Redraw (true) called here
1801 public void EndUpdate ()
1803 // flag to avoid painting
1806 // probably, now we need a redraw with recalculations
1810 public void EnsureVisible (int index)
1812 if (index < 0 || index >= items.Count || scrollable == false)
1815 Rectangle view_rect = item_control.ClientRectangle;
1816 Rectangle bounds = items [index].Bounds;
1818 if (view_rect.Contains (bounds))
1821 if (bounds.Left < 0)
1822 h_scroll.Value += bounds.Left;
1823 else if (bounds.Right > view_rect.Right)
1824 h_scroll.Value += (bounds.Right - view_rect.Right);
1827 v_scroll.Value += bounds.Top;
1828 else if (bounds.Bottom > view_rect.Bottom)
1829 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1832 public ListViewItem GetItemAt (int x, int y)
1834 foreach (ListViewItem item in items) {
1835 if (item.Bounds.Contains (x, y))
1841 public Rectangle GetItemRect (int index)
1843 return GetItemRect (index, ItemBoundsPortion.Entire);
1846 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1848 if (index < 0 || index >= items.Count)
1849 throw new IndexOutOfRangeException ("index");
1851 return items [index].GetBounds (portion);
1856 if (sort_order != SortOrder.None)
1857 items.list.Sort (item_sorter);
1859 if (sort_order == SortOrder.Descending)
1860 items.list.Reverse ();
1865 public override string ToString ()
1867 int count = this.Items.Count;
1870 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1872 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1874 #endregion // Public Instance Methods
1879 class HeaderControl : Control {
1882 bool column_resize_active = false;
1883 ColumnHeader resize_column;
1884 ColumnHeader clicked_column;
1885 ColumnHeader drag_column;
1887 int drag_to_index = -1;
1889 public HeaderControl (ListView owner)
1892 MouseDown += new MouseEventHandler (HeaderMouseDown);
1893 MouseMove += new MouseEventHandler (HeaderMouseMove);
1894 MouseUp += new MouseEventHandler (HeaderMouseUp);
1897 private ColumnHeader ColumnAtX (int x)
1899 Point pt = new Point (x, 0);
1900 ColumnHeader result = null;
1901 foreach (ColumnHeader col in owner.Columns) {
1902 if (col.Rect.Contains (pt)) {
1910 private int GetReorderedIndex (ColumnHeader col)
1912 if (owner.reordered_column_indices == null)
1915 for (int i = 0; i < owner.Columns.Count; i++)
1916 if (owner.reordered_column_indices [i] == col.Index)
1918 throw new Exception ("Column index missing from reordered array");
1921 private void HeaderMouseDown (object sender, MouseEventArgs me)
1923 if (resize_column != null) {
1924 column_resize_active = true;
1929 clicked_column = ColumnAtX (me.X + owner.h_marker);
1931 if (clicked_column != null) {
1933 if (owner.AllowColumnReorder) {
1935 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
1936 drag_column.column_rect = clicked_column.Rect;
1937 drag_to_index = GetReorderedIndex (clicked_column);
1939 clicked_column.pressed = true;
1940 Rectangle bounds = clicked_column.Rect;
1941 bounds.X -= owner.h_marker;
1942 Invalidate (bounds);
1947 private void HeaderMouseMove (object sender, MouseEventArgs me)
1949 Point pt = new Point (me.X + owner.h_marker, me.Y);
1951 if (column_resize_active) {
1952 resize_column.Width = pt.X - resize_column.X;
1953 if (resize_column.Width < 0)
1954 resize_column.Width = 0;
1958 resize_column = null;
1960 if (clicked_column != null) {
1961 if (owner.AllowColumnReorder) {
1964 r = drag_column.column_rect;
1965 r.X = clicked_column.Rect.X + me.X - drag_x;
1966 drag_column.column_rect = r;
1968 int x = me.X + owner.h_marker;
1969 ColumnHeader over = ColumnAtX (x);
1971 drag_to_index = owner.Columns.Count;
1972 else if (x < over.X + over.Width / 2)
1973 drag_to_index = GetReorderedIndex (over);
1975 drag_to_index = GetReorderedIndex (over) + 1;
1978 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
1979 bool pressed = clicked_column.pressed;
1980 clicked_column.pressed = over == clicked_column;
1981 if (clicked_column.pressed ^ pressed) {
1982 Rectangle bounds = clicked_column.Rect;
1983 bounds.X -= owner.h_marker;
1984 Invalidate (bounds);
1990 for (int i = 0; i < owner.Columns.Count; i++) {
1991 Rectangle zone = owner.Columns [i].Rect;
1992 zone.X = zone.Right - 5;
1994 if (zone.Contains (pt)) {
1995 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
1997 resize_column = owner.Columns [i];
2002 if (resize_column == null)
2003 Cursor = Cursors.Default;
2005 Cursor = Cursors.VSplit;
2008 void HeaderMouseUp (object sender, MouseEventArgs me)
2012 if (column_resize_active) {
2013 column_resize_active = false;
2014 resize_column = null;
2015 Cursor = Cursors.Default;
2019 if (clicked_column != null && clicked_column.pressed) {
2020 clicked_column.pressed = false;
2021 Rectangle bounds = clicked_column.Rect;
2022 bounds.X -= owner.h_marker;
2023 Invalidate (bounds);
2024 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2027 if (drag_column != null && owner.AllowColumnReorder) {
2029 if (drag_to_index > GetReorderedIndex (clicked_column))
2031 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2032 owner.ReorderColumn (clicked_column, drag_to_index);
2037 clicked_column = null;
2040 internal override void OnPaintInternal (PaintEventArgs pe)
2045 Theme theme = ThemeEngine.Current;
2046 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2048 if (drag_column == null)
2052 if (drag_to_index == owner.Columns.Count)
2053 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2055 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2056 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2059 protected override void WndProc (ref Message m)
2061 switch ((Msg)m.Msg) {
2062 case Msg.WM_SETFOCUS:
2066 base.WndProc (ref m);
2072 private class ItemComparer : IComparer {
2074 public int Compare (object x, object y)
2076 ListViewItem item_x = x as ListViewItem;
2077 ListViewItem item_y = y as ListViewItem;
2078 return String.Compare (item_x.Text, item_y.Text);
2082 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2084 internal ArrayList list;
2085 private ListView owner;
2087 #region Public Constructor
2088 public CheckedIndexCollection (ListView owner)
2090 list = new ArrayList ();
2093 #endregion // Public Constructor
2095 #region Public Properties
2098 get { return list.Count; }
2101 public bool IsReadOnly {
2102 get { return true; }
2105 public int this [int index] {
2107 if (index < 0 || index >= list.Count)
2108 throw new ArgumentOutOfRangeException ("index");
2109 return (int) list [index];
2113 bool ICollection.IsSynchronized {
2114 get { return false; }
2117 object ICollection.SyncRoot {
2118 get { return this; }
2121 bool IList.IsFixedSize {
2122 get { return true; }
2125 object IList.this [int index] {
2126 get { return this [index]; }
2127 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2129 #endregion // Public Properties
2131 #region Public Methods
2132 public bool Contains (int checkedIndex)
2134 return list.Contains (checkedIndex);
2137 public IEnumerator GetEnumerator ()
2139 return list.GetEnumerator ();
2142 void ICollection.CopyTo (Array dest, int index)
2144 list.CopyTo (dest, index);
2147 int IList.Add (object value)
2149 throw new NotSupportedException ("Add operation is not supported.");
2154 throw new NotSupportedException ("Clear operation is not supported.");
2157 bool IList.Contains (object checkedIndex)
2159 return list.Contains (checkedIndex);
2162 int IList.IndexOf (object checkedIndex)
2164 return list.IndexOf (checkedIndex);
2167 void IList.Insert (int index, object value)
2169 throw new NotSupportedException ("Insert operation is not supported.");
2172 void IList.Remove (object value)
2174 throw new NotSupportedException ("Remove operation is not supported.");
2177 void IList.RemoveAt (int index)
2179 throw new NotSupportedException ("RemoveAt operation is not supported.");
2182 public int IndexOf (int checkedIndex)
2184 return list.IndexOf (checkedIndex);
2186 #endregion // Public Methods
2188 } // CheckedIndexCollection
2190 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2192 internal ArrayList list;
2193 private ListView owner;
2195 #region Public Constructor
2196 public CheckedListViewItemCollection (ListView owner)
2198 list = new ArrayList ();
2201 #endregion // Public Constructor
2203 #region Public Properties
2206 get { return list.Count; }
2209 public bool IsReadOnly {
2210 get { return true; }
2213 public ListViewItem this [int index] {
2215 if (index < 0 || index >= list.Count)
2216 throw new ArgumentOutOfRangeException ("index");
2217 return (ListViewItem) list [index];
2221 bool ICollection.IsSynchronized {
2222 get { return list.IsSynchronized; }
2225 object ICollection.SyncRoot {
2226 get { return this; }
2229 bool IList.IsFixedSize {
2230 get { return true; }
2233 object IList.this [int index] {
2234 get { return this [index]; }
2235 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2237 #endregion // Public Properties
2239 #region Public Methods
2240 public bool Contains (ListViewItem item)
2242 return list.Contains (item);
2245 public void CopyTo (Array dest, int index)
2247 list.CopyTo (dest, index);
2250 public IEnumerator GetEnumerator ()
2252 return list.GetEnumerator ();
2255 int IList.Add (object value)
2257 throw new NotSupportedException ("Add operation is not supported.");
2262 throw new NotSupportedException ("Clear operation is not supported.");
2265 bool IList.Contains (object item)
2267 return list.Contains (item);
2270 int IList.IndexOf (object item)
2272 return list.IndexOf (item);
2275 void IList.Insert (int index, object value)
2277 throw new NotSupportedException ("Insert operation is not supported.");
2280 void IList.Remove (object value)
2282 throw new NotSupportedException ("Remove operation is not supported.");
2285 void IList.RemoveAt (int index)
2287 throw new NotSupportedException ("RemoveAt operation is not supported.");
2290 public int IndexOf (ListViewItem item)
2292 return list.IndexOf (item);
2294 #endregion // Public Methods
2296 } // CheckedListViewItemCollection
2298 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2300 internal ArrayList list;
2301 private ListView owner;
2303 #region Public Constructor
2304 public ColumnHeaderCollection (ListView owner)
2306 list = new ArrayList ();
2309 #endregion // Public Constructor
2311 #region Public Properties
2314 get { return list.Count; }
2317 public bool IsReadOnly {
2318 get { return false; }
2321 public virtual ColumnHeader this [int index] {
2323 if (index < 0 || index >= list.Count)
2324 throw new ArgumentOutOfRangeException ("index");
2325 return (ColumnHeader) list [index];
2329 bool ICollection.IsSynchronized {
2330 get { return true; }
2333 object ICollection.SyncRoot {
2334 get { return this; }
2337 bool IList.IsFixedSize {
2338 get { return list.IsFixedSize; }
2341 object IList.this [int index] {
2342 get { return this [index]; }
2343 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2345 #endregion // Public Properties
2347 #region Public Methods
2348 public virtual int Add (ColumnHeader value)
2351 value.owner = this.owner;
2352 idx = list.Add (value);
2353 if (owner.IsHandleCreated) {
2354 owner.Redraw (true);
2359 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2361 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2362 this.Add (colHeader);
2366 public virtual void AddRange (ColumnHeader [] values)
2368 foreach (ColumnHeader colHeader in values) {
2369 colHeader.owner = this.owner;
2373 owner.Redraw (true);
2376 public virtual void Clear ()
2379 owner.Redraw (true);
2382 public bool Contains (ColumnHeader value)
2384 return list.Contains (value);
2387 public IEnumerator GetEnumerator ()
2389 return list.GetEnumerator ();
2392 void ICollection.CopyTo (Array dest, int index)
2394 list.CopyTo (dest, index);
2397 int IList.Add (object value)
2399 if (! (value is ColumnHeader)) {
2400 throw new ArgumentException ("Not of type ColumnHeader", "value");
2403 return this.Add ((ColumnHeader) value);
2406 bool IList.Contains (object value)
2408 if (! (value is ColumnHeader)) {
2409 throw new ArgumentException ("Not of type ColumnHeader", "value");
2412 return this.Contains ((ColumnHeader) value);
2415 int IList.IndexOf (object value)
2417 if (! (value is ColumnHeader)) {
2418 throw new ArgumentException ("Not of type ColumnHeader", "value");
2421 return this.IndexOf ((ColumnHeader) value);
2424 void IList.Insert (int index, object value)
2426 if (! (value is ColumnHeader)) {
2427 throw new ArgumentException ("Not of type ColumnHeader", "value");
2430 this.Insert (index, (ColumnHeader) value);
2433 void IList.Remove (object value)
2435 if (! (value is ColumnHeader)) {
2436 throw new ArgumentException ("Not of type ColumnHeader", "value");
2439 this.Remove ((ColumnHeader) value);
2442 public int IndexOf (ColumnHeader value)
2444 return list.IndexOf (value);
2447 public void Insert (int index, ColumnHeader value)
2449 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2450 // but it's really only greater.
2451 if (index < 0 || index > list.Count)
2452 throw new ArgumentOutOfRangeException ("index");
2454 value.owner = this.owner;
2455 list.Insert (index, value);
2456 owner.Redraw (true);
2459 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2461 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2462 this.Insert (index, colHeader);
2465 public virtual void Remove (ColumnHeader column)
2467 // TODO: Update Column internal index ?
2468 list.Remove (column);
2469 owner.Redraw (true);
2472 public virtual void RemoveAt (int index)
2474 if (index < 0 || index >= list.Count)
2475 throw new ArgumentOutOfRangeException ("index");
2477 // TODO: Update Column internal index ?
2478 list.RemoveAt (index);
2479 owner.Redraw (true);
2481 #endregion // Public Methods
2484 } // ColumnHeaderCollection
2486 public class ListViewItemCollection : IList, ICollection, IEnumerable
2488 internal ArrayList list;
2489 private ListView owner;
2491 #region Public Constructor
2492 public ListViewItemCollection (ListView owner)
2494 list = new ArrayList ();
2497 #endregion // Public Constructor
2499 #region Public Properties
2502 get { return list.Count; }
2505 public bool IsReadOnly {
2506 get { return false; }
2509 public virtual ListViewItem this [int displayIndex] {
2511 if (displayIndex < 0 || displayIndex >= list.Count)
2512 throw new ArgumentOutOfRangeException ("displayIndex");
2513 return (ListViewItem) list [displayIndex];
2517 if (displayIndex < 0 || displayIndex >= list.Count)
2518 throw new ArgumentOutOfRangeException ("displayIndex");
2520 if (list.Contains (value))
2521 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2523 value.Owner = owner;
2524 list [displayIndex] = value;
2526 owner.Redraw (true);
2530 bool ICollection.IsSynchronized {
2531 get { return true; }
2534 object ICollection.SyncRoot {
2535 get { return this; }
2538 bool IList.IsFixedSize {
2539 get { return list.IsFixedSize; }
2542 object IList.this [int index] {
2543 get { return this [index]; }
2545 if (value is ListViewItem)
2546 this [index] = (ListViewItem) value;
2548 this [index] = new ListViewItem (value.ToString ());
2551 #endregion // Public Properties
2553 #region Public Methods
2554 public virtual ListViewItem Add (ListViewItem value)
2556 if (list.Contains (value))
2557 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2559 value.Owner = owner;
2562 if (owner.Sorting != SortOrder.None)
2565 owner.Redraw (true);
2570 public virtual ListViewItem Add (string text)
2572 ListViewItem item = new ListViewItem (text);
2573 return this.Add (item);
2576 public virtual ListViewItem Add (string text, int imageIndex)
2578 ListViewItem item = new ListViewItem (text, imageIndex);
2579 return this.Add (item);
2582 public void AddRange (ListViewItem [] values)
2585 owner.SelectedItems.list.Clear ();
2586 owner.SelectedIndices.list.Clear ();
2587 owner.CheckedItems.list.Clear ();
2588 owner.CheckedIndices.list.Clear ();
2590 foreach (ListViewItem item in values) {
2595 if (owner.Sorting != SortOrder.None)
2598 owner.Redraw (true);
2601 public virtual void Clear ()
2603 owner.SetFocusedItem (null);
2604 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2606 owner.SelectedItems.list.Clear ();
2607 owner.SelectedIndices.list.Clear ();
2608 owner.CheckedItems.list.Clear ();
2609 owner.CheckedIndices.list.Clear ();
2610 owner.Redraw (true);
2613 public bool Contains (ListViewItem item)
2615 return list.Contains (item);
2618 public void CopyTo (Array dest, int index)
2620 list.CopyTo (dest, index);
2623 public IEnumerator GetEnumerator ()
2625 return list.GetEnumerator ();
2628 int IList.Add (object item)
2633 if (item is ListViewItem) {
2634 li = (ListViewItem) item;
2635 if (list.Contains (li))
2636 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2639 li = new ListViewItem (item.ToString ());
2642 result = list.Add (li);
2643 owner.Redraw (true);
2648 bool IList.Contains (object item)
2650 return list.Contains (item);
2653 int IList.IndexOf (object item)
2655 return list.IndexOf (item);
2658 void IList.Insert (int index, object item)
2660 if (item is ListViewItem)
2661 this.Insert (index, (ListViewItem) item);
2663 this.Insert (index, item.ToString ());
2666 void IList.Remove (object item)
2668 Remove ((ListViewItem) item);
2671 public int IndexOf (ListViewItem item)
2673 return list.IndexOf (item);
2676 public ListViewItem Insert (int index, ListViewItem item)
2678 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2679 // but it's really only greater.
2680 if (index < 0 || index > list.Count)
2681 throw new ArgumentOutOfRangeException ("index");
2683 if (list.Contains (item))
2684 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2687 list.Insert (index, item);
2688 owner.Redraw (true);
2692 public ListViewItem Insert (int index, string text)
2694 return this.Insert (index, new ListViewItem (text));
2697 public ListViewItem Insert (int index, string text, int imageIndex)
2699 return this.Insert (index, new ListViewItem (text, imageIndex));
2702 public virtual void Remove (ListViewItem item)
2704 if (!list.Contains (item))
2707 owner.SelectedItems.list.Remove (item);
2708 owner.SelectedIndices.list.Remove (item.Index);
2709 owner.CheckedItems.list.Remove (item);
2710 owner.CheckedIndices.list.Remove (item.Index);
2712 owner.Redraw (true);
2715 public virtual void RemoveAt (int index)
2717 if (index < 0 || index >= list.Count)
2718 throw new ArgumentOutOfRangeException ("index");
2720 list.RemoveAt (index);
2721 owner.SelectedItems.list.RemoveAt (index);
2722 owner.SelectedIndices.list.RemoveAt (index);
2723 owner.CheckedItems.list.RemoveAt (index);
2724 owner.CheckedIndices.list.RemoveAt (index);
2725 owner.Redraw (false);
2727 #endregion // Public Methods
2729 } // ListViewItemCollection
2731 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2733 internal ArrayList list;
2734 private ListView owner;
2736 #region Public Constructor
2737 public SelectedIndexCollection (ListView owner)
2739 list = new ArrayList ();
2742 #endregion // Public Constructor
2744 #region Public Properties
2747 get { return list.Count; }
2750 public bool IsReadOnly {
2751 get { return true; }
2754 public int this [int index] {
2756 if (index < 0 || index >= list.Count)
2757 throw new ArgumentOutOfRangeException ("index");
2758 return (int) list [index];
2762 bool ICollection.IsSynchronized {
2763 get { return list.IsSynchronized; }
2766 object ICollection.SyncRoot {
2767 get { return this; }
2770 bool IList.IsFixedSize {
2771 get { return true; }
2774 object IList.this [int index] {
2775 get { return this [index]; }
2776 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2778 #endregion // Public Properties
2780 #region Public Methods
2781 public bool Contains (int selectedIndex)
2783 return list.Contains (selectedIndex);
2786 public void CopyTo (Array dest, int index)
2788 list.CopyTo (dest, index);
2791 public IEnumerator GetEnumerator ()
2793 return list.GetEnumerator ();
2796 int IList.Add (object value)
2798 throw new NotSupportedException ("Add operation is not supported.");
2803 throw new NotSupportedException ("Clear operation is not supported.");
2806 bool IList.Contains (object selectedIndex)
2808 return list.Contains (selectedIndex);
2811 int IList.IndexOf (object selectedIndex)
2813 return list.IndexOf (selectedIndex);
2816 void IList.Insert (int index, object value)
2818 throw new NotSupportedException ("Insert operation is not supported.");
2821 void IList.Remove (object value)
2823 throw new NotSupportedException ("Remove operation is not supported.");
2826 void IList.RemoveAt (int index)
2828 throw new NotSupportedException ("RemoveAt operation is not supported.");
2831 public int IndexOf (int selectedIndex)
2833 return list.IndexOf (selectedIndex);
2835 #endregion // Public Methods
2837 } // SelectedIndexCollection
2839 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2841 internal ArrayList list;
2842 private ListView owner;
2844 #region Public Constructor
2845 public SelectedListViewItemCollection (ListView owner)
2847 list = new ArrayList ();
2850 #endregion // Public Constructor
2852 #region Public Properties
2855 get { return list.Count; }
2858 public bool IsReadOnly {
2859 get { return true; }
2862 public ListViewItem this [int index] {
2864 if (index < 0 || index >= list.Count)
2865 throw new ArgumentOutOfRangeException ("index");
2866 return (ListViewItem) list [index];
2870 bool ICollection.IsSynchronized {
2871 get { return list.IsSynchronized; }
2874 object ICollection.SyncRoot {
2875 get { return this; }
2878 bool IList.IsFixedSize {
2879 get { return true; }
2882 object IList.this [int index] {
2883 get { return this [index]; }
2884 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2886 #endregion // Public Properties
2888 #region Public Methods
2889 public void Clear ()
2891 ArrayList copy = (ArrayList) list.Clone ();
2892 for (int i = 0; i < copy.Count; i++)
2893 ((ListViewItem) copy [i]).Selected = false;
2898 public bool Contains (ListViewItem item)
2900 return list.Contains (item);
2903 public void CopyTo (Array dest, int index)
2905 list.CopyTo (dest, index);
2908 public IEnumerator GetEnumerator ()
2910 return list.GetEnumerator ();
2913 int IList.Add (object value)
2915 throw new NotSupportedException ("Add operation is not supported.");
2918 bool IList.Contains (object item)
2920 return list.Contains (item);
2923 int IList.IndexOf (object item)
2925 return list.IndexOf (item);
2928 void IList.Insert (int index, object value)
2930 throw new NotSupportedException ("Insert operation is not supported.");
2933 void IList.Remove (object value)
2935 throw new NotSupportedException ("Remove operation is not supported.");
2938 void IList.RemoveAt (int index)
2940 throw new NotSupportedException ("RemoveAt operation is not supported.");
2943 public int IndexOf (ListViewItem item)
2945 return list.IndexOf (item);
2947 #endregion // Public Methods
2949 } // SelectedListViewItemCollection
2951 #endregion // Subclasses