1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com)
23 // Ravindra Kumar (rkumar@novell.com)
24 // Jordi Mas i Hernandez, jordi@ximian.com
25 // Mike Kestner (mkestner@novell.com)
28 // - Feedback for item activation, change in cursor types as mouse moves.
37 using System.Collections;
38 using System.ComponentModel;
39 using System.ComponentModel.Design;
41 using System.Runtime.InteropServices;
42 using System.Globalization;
44 namespace System.Windows.Forms
46 [DefaultEvent ("SelectedIndexChanged")]
47 [DefaultProperty ("Items")]
48 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
49 public class ListView : Control
51 private ItemActivation activation = ItemActivation.Standard;
52 private ListViewAlignment alignment = ListViewAlignment.Top;
53 private bool allow_column_reorder = false;
54 private bool auto_arrange = true;
55 private bool check_boxes = false;
56 private CheckedIndexCollection checked_indices;
57 private CheckedListViewItemCollection checked_items;
58 private ColumnHeaderCollection columns;
59 internal ListViewItem focused_item;
60 private bool full_row_select = false;
61 private bool grid_lines = false;
62 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
63 private bool hide_selection = true;
64 private bool hover_selection = false;
65 private IComparer item_sorter;
66 private ListViewItemCollection items;
67 private bool label_edit = false;
68 private bool label_wrap = true;
69 private bool multiselect = true;
70 private bool scrollable = true;
71 private SelectedIndexCollection selected_indices;
72 private SelectedListViewItemCollection selected_items;
73 private SortOrder sort_order = SortOrder.None;
74 private ImageList state_image_list;
75 private bool updating = false;
76 private View view = View.LargeIcon;
77 private int layout_wd; // We might draw more than our client area
78 private int layout_ht; // therefore we need to have these two.
79 //private TextBox editor; // Used for editing an item text
80 HeaderControl header_control;
81 internal ItemControl item_control;
82 internal ScrollBar h_scroll; // used for scrolling horizontally
83 internal ScrollBar v_scroll; // used for scrolling vertically
84 internal int h_marker; // Position markers for scrolling
85 internal int v_marker;
86 private int keysearch_tickcnt;
87 private string keysearch_text;
88 static private readonly int keysearch_keydelay = 1000;
89 private int[] reordered_column_indices;
92 internal ImageList large_image_list;
93 internal ImageList small_image_list;
94 internal Size text_size = Size.Empty;
97 public event LabelEditEventHandler AfterLabelEdit;
100 [EditorBrowsable (EditorBrowsableState.Never)]
101 public new event EventHandler BackgroundImageChanged {
102 add { base.BackgroundImageChanged += value; }
103 remove { base.BackgroundImageChanged -= value; }
106 public event LabelEditEventHandler BeforeLabelEdit;
107 public event ColumnClickEventHandler ColumnClick;
108 public event EventHandler ItemActivate;
109 public event ItemCheckEventHandler ItemCheck;
110 public event ItemDragEventHandler ItemDrag;
113 [EditorBrowsable (EditorBrowsableState.Never)]
114 public new event PaintEventHandler Paint {
115 add { base.Paint += value; }
116 remove { base.Paint -= value; }
119 public event EventHandler SelectedIndexChanged;
122 [EditorBrowsable (EditorBrowsableState.Never)]
123 public new event EventHandler TextChanged {
124 add { base.TextChanged += value; }
125 remove { base.TextChanged -= value; }
130 #region Public Constructors
133 background_color = ThemeEngine.Current.ColorWindow;
134 checked_indices = new CheckedIndexCollection (this);
135 checked_items = new CheckedListViewItemCollection (this);
136 columns = new ColumnHeaderCollection (this);
137 foreground_color = SystemColors.WindowText;
138 items = new ListViewItemCollection (this);
139 selected_indices = new SelectedIndexCollection (this);
140 selected_items = new SelectedListViewItemCollection (this);
142 border_style = BorderStyle.Fixed3D;
144 header_control = new HeaderControl (this);
145 header_control.Visible = false;
146 Controls.AddImplicit (header_control);
148 item_control = new ItemControl (this);
149 Controls.AddImplicit (item_control);
151 h_scroll = new HScrollBar ();
152 Controls.AddImplicit (this.h_scroll);
154 v_scroll = new VScrollBar ();
155 Controls.AddImplicit (this.v_scroll);
157 h_marker = v_marker = 0;
158 keysearch_tickcnt = 0;
160 // scroll bars are disabled initially
161 h_scroll.Visible = false;
162 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
163 v_scroll.Visible = false;
164 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
167 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
168 SizeChanged += new EventHandler (ListView_SizeChanged);
169 GotFocus += new EventHandler (FocusChanged);
170 LostFocus += new EventHandler (FocusChanged);
172 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
174 #endregion // Public Constructors
176 #region Private Internal Properties
177 internal Size CheckBoxSize {
179 if (this.check_boxes) {
180 if (this.state_image_list != null)
181 return this.state_image_list.ImageSize;
183 return ThemeEngine.Current.ListViewCheckBoxSize;
191 bool CanMultiselect {
195 else if (multiselect && (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0)
202 #endregion // Private Internal Properties
204 #region Protected Properties
205 protected override CreateParams CreateParams {
206 get { return base.CreateParams; }
209 protected override Size DefaultSize {
210 get { return ThemeEngine.Current.ListViewDefaultSize; }
212 #endregion // Protected Properties
214 #region Public Instance Properties
215 [DefaultValue (ItemActivation.Standard)]
216 public ItemActivation Activation {
217 get { return activation; }
219 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
220 value != ItemActivation.TwoClick) {
221 throw new InvalidEnumArgumentException (string.Format
222 ("Enum argument value '{0}' is not valid for Activation", value));
229 [DefaultValue (ListViewAlignment.Top)]
231 public ListViewAlignment Alignment {
232 get { return alignment; }
234 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
235 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
236 throw new InvalidEnumArgumentException (string.Format
237 ("Enum argument value '{0}' is not valid for Alignment", value));
240 if (this.alignment != value) {
242 // alignment does not matter in Details/List views
243 if (this.view == View.LargeIcon ||
244 this.View == View.SmallIcon)
250 [DefaultValue (false)]
251 public bool AllowColumnReorder {
252 get { return allow_column_reorder; }
253 set { allow_column_reorder = value; }
256 [DefaultValue (true)]
257 public bool AutoArrange {
258 get { return auto_arrange; }
260 if (auto_arrange != value) {
261 auto_arrange = value;
262 // autoarrange does not matter in Details/List views
263 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
269 public override Color BackColor {
271 if (background_color.IsEmpty)
272 return ThemeEngine.Current.ColorWindow;
274 return background_color;
276 set { background_color = value; }
280 [EditorBrowsable (EditorBrowsableState.Never)]
281 public override Image BackgroundImage {
282 get { return background_image; }
284 if (value == background_image)
287 background_image = value;
288 OnBackgroundImageChanged (EventArgs.Empty);
292 [DefaultValue (BorderStyle.Fixed3D)]
294 public BorderStyle BorderStyle {
295 get { return InternalBorderStyle; }
296 set { InternalBorderStyle = value; }
299 [DefaultValue (false)]
300 public bool CheckBoxes {
301 get { return check_boxes; }
303 if (check_boxes != value) {
311 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
312 public CheckedIndexCollection CheckedIndices {
313 get { return checked_indices; }
317 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
318 public CheckedListViewItemCollection CheckedItems {
319 get { return checked_items; }
322 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
324 [MergableProperty (false)]
325 public ColumnHeaderCollection Columns {
326 get { return columns; }
330 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
331 public ListViewItem FocusedItem {
337 public override Color ForeColor {
339 if (foreground_color.IsEmpty)
340 return ThemeEngine.Current.ColorWindowText;
342 return foreground_color;
344 set { foreground_color = value; }
347 [DefaultValue (false)]
348 public bool FullRowSelect {
349 get { return full_row_select; }
350 set { full_row_select = value; }
353 [DefaultValue (false)]
354 public bool GridLines {
355 get { return grid_lines; }
357 if (grid_lines != value) {
364 [DefaultValue (ColumnHeaderStyle.Clickable)]
365 public ColumnHeaderStyle HeaderStyle {
366 get { return header_style; }
368 if (header_style == value)
372 case ColumnHeaderStyle.Clickable:
373 case ColumnHeaderStyle.Nonclickable:
374 case ColumnHeaderStyle.None:
377 throw new InvalidEnumArgumentException (string.Format
378 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
381 header_style = value;
382 if (view == View.Details)
387 [DefaultValue (true)]
388 public bool HideSelection {
389 get { return hide_selection; }
391 if (hide_selection != value) {
392 hide_selection = value;
398 [DefaultValue (false)]
399 public bool HoverSelection {
400 get { return hover_selection; }
401 set { hover_selection = value; }
404 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
406 [MergableProperty (false)]
407 public ListViewItemCollection Items {
408 get { return items; }
411 [DefaultValue (false)]
412 public bool LabelEdit {
413 get { return label_edit; }
414 set { label_edit = value; }
417 [DefaultValue (true)]
419 public bool LabelWrap {
420 get { return label_wrap; }
422 if (label_wrap != value) {
429 [DefaultValue (null)]
430 public ImageList LargeImageList {
431 get { return large_image_list; }
433 large_image_list = value;
439 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
440 public IComparer ListViewItemSorter {
441 get { return item_sorter; }
442 set { item_sorter = value; }
445 [DefaultValue (true)]
446 public bool MultiSelect {
447 get { return multiselect; }
448 set { multiselect = value; }
451 [DefaultValue (true)]
452 public bool Scrollable {
453 get { return scrollable; }
455 if (scrollable != value) {
463 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
464 public SelectedIndexCollection SelectedIndices {
465 get { return selected_indices; }
469 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
470 public SelectedListViewItemCollection SelectedItems {
471 get { return selected_items; }
475 [MonoTODO("Implement")]
476 public bool ShowGroups {
486 [DefaultValue (null)]
487 public ImageList SmallImageList {
488 get { return small_image_list; }
490 small_image_list = value;
495 [DefaultValue (SortOrder.None)]
496 public SortOrder Sorting {
497 get { return sort_order; }
499 if (value != SortOrder.Ascending && value != SortOrder.Descending &&
500 value != SortOrder.None) {
501 throw new InvalidEnumArgumentException (string.Format
502 ("Enum argument value '{0}' is not valid for Sorting", value));
505 if (sort_order != value) {
512 [DefaultValue (null)]
513 public ImageList StateImageList {
514 get { return state_image_list; }
516 state_image_list = value;
523 [EditorBrowsable (EditorBrowsableState.Never)]
524 public override string Text {
533 OnTextChanged (EventArgs.Empty);
538 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539 public ListViewItem TopItem {
542 if (this.items.Count == 0)
544 // if contents are not scrolled
545 // it is the first item
546 else if (h_marker == 0 && v_marker == 0)
547 return this.items [0];
548 // do a hit test for the scrolled position
550 foreach (ListViewItem item in this.items) {
551 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
560 [MonoTODO("Implement")]
561 public bool UseCompatibleStateImageBehavior {
571 [DefaultValue (View.LargeIcon)]
575 if (value != View.Details && value != View.LargeIcon &&
576 value != View.List && value != View.SmallIcon ) {
577 throw new InvalidEnumArgumentException (string.Format
578 ("Enum argument value '{0}' is not valid for View", value));
582 h_scroll.Value = v_scroll.Value = 0;
588 #endregion // Public Instance Properties
590 #region Internal Methods Properties
592 internal int FirstVisibleIndex {
595 if (this.items.Count == 0)
598 if (h_marker == 0 && v_marker == 0)
601 foreach (ListViewItem item in this.items) {
602 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
611 internal int LastVisibleIndex {
613 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
614 if (View == View.List || Alignment == ListViewAlignment.Left) {
615 if (Items[i].Bounds.X > ClientRectangle.Right)
618 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
623 return Items.Count - 1;
627 internal int TotalWidth {
628 get { return Math.Max (this.Width, this.layout_wd); }
631 internal int TotalHeight {
632 get { return Math.Max (this.Height, this.layout_ht); }
635 internal void Redraw (bool recalculate)
637 // Avoid calculations when control is being updated
642 CalculateListView (this.alignment);
647 internal Size GetChildColumnSize (int index)
649 Size ret_size = Size.Empty;
650 ColumnHeader col = this.columns [index];
652 if (col.Width == -2) { // autosize = max(items, columnheader)
653 Size size = Size.Ceiling (this.DeviceContext.MeasureString
654 (col.Text, this.Font));
655 ret_size = BiggestItem (index);
656 if (size.Width > ret_size.Width)
659 else { // -1 and all the values < -2 are put under one category
660 ret_size = BiggestItem (index);
661 // fall back to empty columns' width if no subitem is available for a column
662 if (ret_size.IsEmpty) {
663 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
664 if (col.Text.Length > 0)
665 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
666 (col.Text, this.Font)).Height;
668 ret_size.Height = this.Font.Height;
672 // adjust the size for icon and checkbox for 0th column
674 ret_size.Width += (this.CheckBoxSize.Width + 4);
675 if (this.small_image_list != null)
676 ret_size.Width += this.small_image_list.ImageSize.Width;
681 // Returns the size of biggest item text in a column.
682 private Size BiggestItem (int col)
684 Size temp = Size.Empty;
685 Size ret_size = Size.Empty;
687 // 0th column holds the item text, we check the size of
688 // the various subitems falling in that column and get
689 // the biggest one's size.
690 foreach (ListViewItem item in items) {
691 if (col >= item.SubItems.Count)
694 temp = Size.Ceiling (this.DeviceContext.MeasureString
695 (item.SubItems [col].Text, this.Font));
696 if (temp.Width > ret_size.Width)
700 // adjustment for space
701 if (!ret_size.IsEmpty)
707 // Sets the size of the biggest item text as per the view
708 private void CalcTextSize ()
710 // clear the old value
711 text_size = Size.Empty;
713 if (items.Count == 0)
716 text_size = BiggestItem (0);
718 if (view == View.LargeIcon && this.label_wrap) {
719 Size temp = Size.Empty;
720 if (this.check_boxes)
721 temp.Width += 2 * this.CheckBoxSize.Width;
722 if (large_image_list != null)
723 temp.Width += large_image_list.ImageSize.Width;
726 // wrapping is done for two lines only
727 if (text_size.Width > temp.Width) {
728 text_size.Width = temp.Width;
729 text_size.Height *= 2;
732 else if (view == View.List) {
733 // in list view max text shown in determined by the
734 // control width, even if scolling is enabled.
735 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
736 if (this.small_image_list != null)
737 max_wd -= this.small_image_list.ImageSize.Width;
739 if (text_size.Width > max_wd)
740 text_size.Width = max_wd;
743 // we do the default settings, if we have got 0's
744 if (text_size.Height <= 0)
745 text_size.Height = this.Font.Height;
746 if (text_size.Width <= 0)
747 text_size.Width = this.Width;
750 text_size.Width += 4;
751 text_size.Height += 2;
754 private void Scroll (ScrollBar scrollbar, int delta)
756 if (delta == 0 || !scrollbar.Visible)
760 if (scrollbar == h_scroll)
761 max = h_scroll.Maximum - item_control.Width;
763 max = v_scroll.Maximum - item_control.Height;
765 int val = scrollbar.Value + delta;
768 else if (val < scrollbar.Minimum)
769 val = scrollbar.Minimum;
770 scrollbar.Value = val;
773 private void CalculateScrollBars ()
775 Rectangle client_area = ClientRectangle;
777 if (!this.scrollable || this.items.Count <= 0) {
778 h_scroll.Visible = false;
779 v_scroll.Visible = false;
783 // making a scroll bar visible might make
784 // other scroll bar visible
785 if (layout_wd > client_area.Right) {
786 h_scroll.Visible = true;
787 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
788 v_scroll.Visible = true;
790 v_scroll.Visible = false;
791 } else if (layout_ht > client_area.Bottom) {
792 v_scroll.Visible = true;
793 if ((layout_wd + v_scroll.Width) > client_area.Right)
794 h_scroll.Visible = true;
796 h_scroll.Visible = false;
798 h_scroll.Visible = false;
799 v_scroll.Visible = false;
802 item_control.Height = ClientRectangle.Height - header_control.Height;
804 if (h_scroll.Visible) {
805 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
806 h_scroll.Minimum = 0;
808 // if v_scroll is visible, adjust the maximum of the
809 // h_scroll to account for the width of v_scroll
810 if (v_scroll.Visible) {
811 h_scroll.Maximum = layout_wd + v_scroll.Width;
812 h_scroll.Width = client_area.Width - v_scroll.Width;
815 h_scroll.Maximum = layout_wd;
816 h_scroll.Width = client_area.Width;
819 h_scroll.LargeChange = client_area.Width;
820 h_scroll.SmallChange = Font.Height;
821 item_control.Height -= h_scroll.Height;
824 if (header_control.Visible)
825 header_control.Width = ClientRectangle.Width;
826 item_control.Width = ClientRectangle.Width;
828 if (v_scroll.Visible) {
829 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
830 v_scroll.Minimum = 0;
832 // if h_scroll is visible, adjust the maximum of the
833 // v_scroll to account for the height of h_scroll
834 if (h_scroll.Visible) {
835 v_scroll.Maximum = layout_ht + h_scroll.Height;
836 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
838 v_scroll.Maximum = layout_ht;
839 v_scroll.Height = client_area.Height;
842 v_scroll.LargeChange = client_area.Height;
843 v_scroll.SmallChange = Font.Height;
844 if (header_control.Visible)
845 header_control.Width -= v_scroll.Width;
846 item_control.Width -= v_scroll.Width;
850 ColumnHeader GetReorderedColumn (int index)
852 if (reordered_column_indices == null)
853 return Columns [index];
855 return Columns [reordered_column_indices [index]];
858 void ReorderColumn (ColumnHeader col, int index)
860 if (reordered_column_indices == null) {
861 reordered_column_indices = new int [Columns.Count];
862 for (int i = 0; i < Columns.Count; i++)
863 reordered_column_indices [i] = i;
866 if (reordered_column_indices [index] == col.Index)
869 int[] curr = reordered_column_indices;
870 int[] result = new int [Columns.Count];
872 for (int i = 0; i < Columns.Count; i++) {
873 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
877 result [i] = col.Index;
879 result [i] = curr [curr_idx++];
882 reordered_column_indices = result;
884 header_control.Invalidate ();
885 item_control.Invalidate ();
888 Size LargeIconItemSize {
890 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
891 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
892 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
893 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
894 return new Size (w, h);
898 Size SmallIconItemSize {
900 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
901 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
902 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
903 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
904 return new Size (w, h);
910 ListViewItem[,] item_matrix;
912 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
914 header_control.Visible = false;
915 header_control.Size = Size.Empty;
916 item_control.Visible = true;
917 item_control.Location = Point.Empty;
919 if (items.Count == 0)
922 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
924 Rectangle area = ClientRectangle;
927 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
930 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
932 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
935 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
938 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
939 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
940 item_matrix = new ListViewItem [rows, cols];
943 foreach (ListViewItem item in items) {
944 int x = col * (sz.Width + x_spacing);
945 int y = row * (sz.Height + y_spacing);
946 item.Location = new Point (x, y);
950 item_matrix [row, col] = item;
964 item_control.Size = new Size (layout_wd, layout_ht);
969 if (header_style == ColumnHeaderStyle.None) {
970 header_control.Visible = false;
971 header_control.Size = Size.Empty;
976 for (int i = 0; i < Columns.Count; i++) {
977 ColumnHeader col = GetReorderedColumn (i);
980 col.CalcColumnHeader ();
984 if (x < ClientRectangle.Width)
985 x = ClientRectangle.Width;
987 header_control.Width = x;
988 header_control.Height = columns [0].Ht;
989 header_control.Visible = true;
992 void LayoutDetails ()
994 if (columns.Count == 0) {
995 header_control.Visible = false;
996 item_control.Visible = false;
1002 item_control.Visible = true;
1003 item_control.Location = new Point (0, header_control.Height);
1006 if (items.Count > 0) {
1007 foreach (ListViewItem item in items) {
1009 item.Location = new Point (0, y);
1010 y += item.Bounds.Height + 2;
1013 // some space for bottom gridline
1018 layout_wd = Math.Max (header_control.Width, item_control.Width);
1019 layout_ht = y + header_control.Height;
1022 private void CalculateListView (ListViewAlignment align)
1031 case View.SmallIcon:
1032 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1035 case View.LargeIcon:
1036 LayoutIcons (true, alignment == ListViewAlignment.Left,
1037 ThemeEngine.Current.ListViewHorizontalSpacing,
1038 ThemeEngine.Current.ListViewVerticalSpacing);
1042 LayoutIcons (false, true, 4, 2);
1046 CalculateScrollBars ();
1049 internal void UpdateSelection (ListViewItem item)
1051 if (item.Selected) {
1053 if (!CanMultiselect && SelectedItems.Count > 0) {
1054 SelectedItems.Clear ();
1055 SelectedIndices.list.Clear ();
1058 if (!SelectedItems.Contains (item)) {
1059 SelectedItems.list.Add (item);
1060 SelectedIndices.list.Add (item.Index);
1063 SelectedItems.list.Remove (item);
1064 SelectedIndices.list.Remove (item.Index);
1068 private bool KeySearchString (KeyEventArgs ke)
1070 int current_tickcnt = Environment.TickCount;
1071 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1072 keysearch_text = string.Empty;
1075 keysearch_text += (char) ke.KeyData;
1076 keysearch_tickcnt = current_tickcnt;
1078 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1081 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1082 CompareOptions.IgnoreCase)) {
1083 SetFocusedItem (Items [i]);
1084 items [i].Selected = true;
1088 i = (i + 1 < Items.Count) ? i+1 : 0;
1096 int GetAdjustedIndex (Keys key)
1100 if (View == View.Details) {
1102 result = FocusedItem.Index - 1;
1103 else if (key == Keys.Down) {
1104 result = FocusedItem.Index + 1;
1105 if (result == items.Count)
1111 int row = FocusedItem.row;
1112 int col = FocusedItem.col;
1118 return item_matrix [row, col - 1].Index;
1121 if (col == (cols - 1))
1123 while (item_matrix [row, col + 1] == null)
1125 return item_matrix [row, col + 1].Index;
1130 return item_matrix [row - 1, col].Index;
1133 if (row == (rows - 1) || row == Items.Count - 1)
1135 while (item_matrix [row + 1, col] == null)
1137 return item_matrix [row + 1, col].Index;
1144 ListViewItem selection_start;
1146 private bool SelectItems (ArrayList sel_items)
1148 bool changed = false;
1149 multiselecting = true;
1150 ArrayList curr_items = (ArrayList) SelectedItems.list.Clone ();
1151 foreach (ListViewItem item in curr_items)
1152 if (!sel_items.Contains (item)) {
1153 item.Selected = false;
1156 foreach (ListViewItem item in sel_items)
1157 if (!item.Selected) {
1158 item.Selected = true;
1161 multiselecting = false;
1165 private void UpdateMultiSelection (int index)
1167 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1168 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1169 ListViewItem item = items [index];
1171 if (shift_pressed && selection_start != null) {
1172 ArrayList list = new ArrayList ();
1173 int start = Math.Min (selection_start.Index, index);
1174 int end = Math.Max (selection_start.Index, index);
1175 if (View == View.Details) {
1176 for (int i = start; i <= end; i++)
1177 list.Add (items [i]);
1179 int left = Math.Min (items [start].col, items [end].col);
1180 int right = Math.Max (items [start].col, items [end].col);
1181 int top = Math.Min (items [start].row, items [end].row);
1182 int bottom = Math.Max (items [start].row, items [end].row);
1183 foreach (ListViewItem curr in items)
1184 if (curr.row >= top && curr.row <= bottom &&
1185 curr.col >= left && curr.col <= right)
1188 if (SelectItems (list))
1189 OnSelectedIndexChanged (EventArgs.Empty);
1190 } else if (!ctrl_pressed) {
1191 SelectedItems.Clear ();
1192 SelectedIndices.list.Clear ();
1193 item.Selected = true;
1194 selection_start = item;
1195 OnSelectedIndexChanged (EventArgs.Empty);
1199 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1201 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1207 if (FocusedItem == null)
1208 SetFocusedItem (Items [0]);
1210 switch (ke.KeyCode) {
1213 index = Items.Count - 1;
1224 index = GetAdjustedIndex (ke.KeyCode);
1228 ke.Handled = KeySearchString (ke);
1236 UpdateMultiSelection (index);
1237 else if (!items [index].Selected) {
1238 items [index].Selected = true;
1239 OnSelectedIndexChanged (EventArgs.Empty);
1242 SetFocusedItem (items [index]);
1243 EnsureVisible (index);
1247 internal class ItemControl : Control {
1250 ListViewItem clicked_item;
1251 ListViewItem last_clicked_item;
1252 bool hover_processed = false;
1253 bool checking = false;
1255 public ItemControl (ListView owner)
1258 DoubleClick += new EventHandler(ItemsDoubleClick);
1259 MouseDown += new MouseEventHandler(ItemsMouseDown);
1260 MouseMove += new MouseEventHandler(ItemsMouseMove);
1261 MouseHover += new EventHandler(ItemsMouseHover);
1262 MouseUp += new MouseEventHandler(ItemsMouseUp);
1263 MouseWheel += new MouseEventHandler(ItemsMouseWheel);
1266 void ItemsDoubleClick (object sender, EventArgs e)
1268 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1269 owner.ItemActivate (this, e);
1279 BoxSelect box_select_mode = BoxSelect.None;
1280 ArrayList prev_selection;
1281 Point box_select_start;
1283 Rectangle box_select_rect;
1284 internal Rectangle BoxSelectRectangle {
1285 get { return box_select_rect; }
1287 if (box_select_rect == value)
1290 InvalidateBoxSelectRect ();
1291 box_select_rect = value;
1292 InvalidateBoxSelectRect ();
1296 void InvalidateBoxSelectRect ()
1298 if (BoxSelectRectangle.Size.IsEmpty)
1301 Rectangle edge = BoxSelectRectangle;
1307 edge.Y = BoxSelectRectangle.Bottom - 1;
1309 edge.Y = BoxSelectRectangle.Y - 1;
1311 edge.Height = BoxSelectRectangle.Height + 2;
1313 edge.X = BoxSelectRectangle.Right - 1;
1317 private Rectangle CalculateBoxSelectRectangle (Point pt)
1319 int left = Math.Min (box_select_start.X, pt.X);
1320 int right = Math.Max (box_select_start.X, pt.X);
1321 int top = Math.Min (box_select_start.Y, pt.Y);
1322 int bottom = Math.Max (box_select_start.Y, pt.Y);
1323 return Rectangle.FromLTRB (left, top, right, bottom);
1326 ArrayList BoxSelectedItems {
1328 ArrayList result = new ArrayList ();
1329 foreach (ListViewItem item in owner.Items) {
1330 Rectangle r = item.Bounds;
1332 r.Y += r.Height / 4;
1335 if (BoxSelectRectangle.IntersectsWith (r))
1342 private bool PerformBoxSelection (Point pt)
1344 if (box_select_mode == BoxSelect.None)
1347 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1349 ArrayList box_items = BoxSelectedItems;
1353 switch (box_select_mode) {
1355 case BoxSelect.Normal:
1359 case BoxSelect.Control:
1360 items = new ArrayList ();
1361 foreach (ListViewItem item in prev_selection)
1362 if (!box_items.Contains (item))
1364 foreach (ListViewItem item in box_items)
1365 if (!prev_selection.Contains (item))
1369 case BoxSelect.Shift:
1371 foreach (ListViewItem item in box_items)
1372 prev_selection.Remove (item);
1373 foreach (ListViewItem item in prev_selection)
1378 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1382 owner.SelectItems (items);
1388 private void ToggleCheckState (ListViewItem item)
1390 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1391 item.Checked = !item.Checked;
1392 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1394 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1395 owner.OnItemCheck (ice);
1398 private void ItemsMouseDown (object sender, MouseEventArgs me)
1400 if (owner.items.Count == 0)
1403 Point pt = new Point (me.X, me.Y);
1404 foreach (ListViewItem item in owner.items) {
1405 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1409 ToggleCheckState (item);
1413 if (owner.View == View.Details && !owner.FullRowSelect) {
1414 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1415 clicked_item = item;
1419 if (item.Bounds.Contains (pt)) {
1420 clicked_item = item;
1427 if (clicked_item != null) {
1428 owner.SetFocusedItem (clicked_item);
1429 bool changed = !clicked_item.Selected;
1430 if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0)
1431 owner.UpdateMultiSelection (clicked_item.Index);
1433 clicked_item.Selected = true;
1436 owner.OnSelectedIndexChanged (EventArgs.Empty);
1438 // Raise double click if the item was clicked. On MS the
1439 // double click is only raised if you double click an item
1440 if (me.Clicks > 1) {
1441 owner.OnDoubleClick (EventArgs.Empty);
1442 if (owner.CheckBoxes)
1443 ToggleCheckState (clicked_item);
1444 } else if (me.Clicks == 1)
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 (!checking && 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;
1547 private void ItemsMouseWheel (object sender, MouseEventArgs me)
1549 if (owner.Items.Count == 0)
1552 int lines = me.Delta / 120;
1557 switch (owner.View) {
1559 case View.SmallIcon:
1560 owner.Scroll (owner.v_scroll, -owner.Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1562 case View.LargeIcon:
1563 owner.Scroll (owner.v_scroll, -(owner.Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1566 owner.Scroll (owner.h_scroll, -owner.Items [0].Bounds.Width * lines);
1571 internal override void OnPaintInternal (PaintEventArgs pe)
1573 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1576 protected override void WndProc (ref Message m)
1578 switch ((Msg)m.Msg) {
1579 case Msg.WM_SETFOCUS:
1583 base.WndProc (ref m);
1589 internal override void OnPaintInternal (PaintEventArgs pe)
1594 CalculateScrollBars ();
1597 void FocusChanged (object o, EventArgs args)
1599 if (Items.Count == 0)
1602 if (FocusedItem == null)
1603 SetFocusedItem (Items [0]);
1605 item_control.Invalidate (FocusedItem.Bounds);
1608 private void ListView_SizeChanged (object sender, EventArgs e)
1610 CalculateListView (alignment);
1613 private void SetFocusedItem (ListViewItem item)
1615 if (focused_item != null)
1616 focused_item.Focused = false;
1619 item.Focused = true;
1621 focused_item = item;
1624 private void HorizontalScroller (object sender, EventArgs e)
1626 // Avoid unnecessary flickering, when button is
1627 // kept pressed at the end
1628 if (h_marker != h_scroll.Value) {
1630 int pixels = h_marker - h_scroll.Value;
1632 h_marker = h_scroll.Value;
1633 if (header_control.Visible)
1634 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1636 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1640 private void VerticalScroller (object sender, EventArgs e)
1642 // Avoid unnecessary flickering, when button is
1643 // kept pressed at the end
1644 if (v_marker != v_scroll.Value) {
1645 int pixels = v_marker - v_scroll.Value;
1646 Rectangle area = item_control.ClientRectangle;
1647 v_marker = v_scroll.Value;
1648 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1651 #endregion // Internal Methods Properties
1653 #region Protected Methods
1654 protected override void CreateHandle ()
1656 base.CreateHandle ();
1659 protected override void Dispose (bool disposing)
1662 h_scroll.Dispose ();
1663 v_scroll.Dispose ();
1665 large_image_list = null;
1666 small_image_list = null;
1667 state_image_list = null;
1670 base.Dispose (disposing);
1673 protected override bool IsInputKey (Keys keyData)
1690 return base.IsInputKey (keyData);
1693 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1695 if (AfterLabelEdit != null)
1696 AfterLabelEdit (this, e);
1699 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1701 if (BeforeLabelEdit != null)
1702 BeforeLabelEdit (this, e);
1705 protected virtual void OnColumnClick (ColumnClickEventArgs e)
1707 if (ColumnClick != null)
1708 ColumnClick (this, e);
1711 protected override void OnEnabledChanged (EventArgs e)
1713 base.OnEnabledChanged (e);
1716 protected override void OnFontChanged (EventArgs e)
1718 base.OnFontChanged (e);
1722 protected override void OnHandleCreated (EventArgs e)
1724 base.OnHandleCreated (e);
1727 protected override void OnHandleDestroyed (EventArgs e)
1729 base.OnHandleDestroyed (e);
1732 protected virtual void OnItemActivate (EventArgs e)
1734 if (ItemActivate != null)
1735 ItemActivate (this, e);
1738 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
1740 if (ItemCheck != null)
1741 ItemCheck (this, ice);
1744 protected virtual void OnItemDrag (ItemDragEventArgs e)
1746 if (ItemDrag != null)
1750 protected virtual void OnSelectedIndexChanged (EventArgs e)
1752 if (SelectedIndexChanged != null)
1753 SelectedIndexChanged (this, e);
1756 protected override void OnSystemColorsChanged (EventArgs e)
1758 base.OnSystemColorsChanged (e);
1761 protected void RealizeProperties ()
1766 protected void UpdateExtendedStyles ()
1771 protected override void WndProc (ref Message m)
1773 base.WndProc (ref m);
1775 #endregion // Protected Methods
1777 #region Public Instance Methods
1778 public void ArrangeIcons ()
1780 ArrangeIcons (this.alignment);
1783 public void ArrangeIcons (ListViewAlignment alignment)
1785 // Icons are arranged only if view is set to LargeIcon or SmallIcon
1786 if (view == View.LargeIcon || view == View.SmallIcon) {
1787 this.CalculateListView (alignment);
1788 // we have done the calculations already
1789 this.Redraw (false);
1793 public void BeginUpdate ()
1795 // flag to avoid painting
1799 public void Clear ()
1802 items.Clear (); // Redraw (true) called here
1805 public void EndUpdate ()
1807 // flag to avoid painting
1810 // probably, now we need a redraw with recalculations
1814 public void EnsureVisible (int index)
1816 if (index < 0 || index >= items.Count || scrollable == false)
1819 Rectangle view_rect = item_control.ClientRectangle;
1820 Rectangle bounds = items [index].Bounds;
1822 if (view_rect.Contains (bounds))
1825 if (bounds.Left < 0)
1826 h_scroll.Value += bounds.Left;
1827 else if (bounds.Right > view_rect.Right)
1828 h_scroll.Value += (bounds.Right - view_rect.Right);
1831 v_scroll.Value += bounds.Top;
1832 else if (bounds.Bottom > view_rect.Bottom)
1833 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
1836 public ListViewItem GetItemAt (int x, int y)
1838 foreach (ListViewItem item in items) {
1839 if (item.Bounds.Contains (x, y))
1845 public Rectangle GetItemRect (int index)
1847 return GetItemRect (index, ItemBoundsPortion.Entire);
1850 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
1852 if (index < 0 || index >= items.Count)
1853 throw new IndexOutOfRangeException ("index");
1855 return items [index].GetBounds (portion);
1860 if (sort_order != SortOrder.None)
1861 items.list.Sort (item_sorter);
1863 if (sort_order == SortOrder.Descending)
1864 items.list.Reverse ();
1869 public override string ToString ()
1871 int count = this.Items.Count;
1874 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
1876 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
1878 #endregion // Public Instance Methods
1883 class HeaderControl : Control {
1886 bool column_resize_active = false;
1887 ColumnHeader resize_column;
1888 ColumnHeader clicked_column;
1889 ColumnHeader drag_column;
1891 int drag_to_index = -1;
1893 public HeaderControl (ListView owner)
1896 MouseDown += new MouseEventHandler (HeaderMouseDown);
1897 MouseMove += new MouseEventHandler (HeaderMouseMove);
1898 MouseUp += new MouseEventHandler (HeaderMouseUp);
1901 private ColumnHeader ColumnAtX (int x)
1903 Point pt = new Point (x, 0);
1904 ColumnHeader result = null;
1905 foreach (ColumnHeader col in owner.Columns) {
1906 if (col.Rect.Contains (pt)) {
1914 private int GetReorderedIndex (ColumnHeader col)
1916 if (owner.reordered_column_indices == null)
1919 for (int i = 0; i < owner.Columns.Count; i++)
1920 if (owner.reordered_column_indices [i] == col.Index)
1922 throw new Exception ("Column index missing from reordered array");
1925 private void HeaderMouseDown (object sender, MouseEventArgs me)
1927 if (resize_column != null) {
1928 column_resize_active = true;
1933 clicked_column = ColumnAtX (me.X + owner.h_marker);
1935 if (clicked_column != null) {
1937 if (owner.AllowColumnReorder) {
1939 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
1940 drag_column.column_rect = clicked_column.Rect;
1941 drag_to_index = GetReorderedIndex (clicked_column);
1943 clicked_column.pressed = true;
1944 Rectangle bounds = clicked_column.Rect;
1945 bounds.X -= owner.h_marker;
1946 Invalidate (bounds);
1951 private void HeaderMouseMove (object sender, MouseEventArgs me)
1953 Point pt = new Point (me.X + owner.h_marker, me.Y);
1955 if (column_resize_active) {
1956 resize_column.Width = pt.X - resize_column.X;
1957 if (resize_column.Width < 0)
1958 resize_column.Width = 0;
1962 resize_column = null;
1964 if (clicked_column != null) {
1965 if (owner.AllowColumnReorder) {
1968 r = drag_column.column_rect;
1969 r.X = clicked_column.Rect.X + me.X - drag_x;
1970 drag_column.column_rect = r;
1972 int x = me.X + owner.h_marker;
1973 ColumnHeader over = ColumnAtX (x);
1975 drag_to_index = owner.Columns.Count;
1976 else if (x < over.X + over.Width / 2)
1977 drag_to_index = GetReorderedIndex (over);
1979 drag_to_index = GetReorderedIndex (over) + 1;
1982 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
1983 bool pressed = clicked_column.pressed;
1984 clicked_column.pressed = over == clicked_column;
1985 if (clicked_column.pressed ^ pressed) {
1986 Rectangle bounds = clicked_column.Rect;
1987 bounds.X -= owner.h_marker;
1988 Invalidate (bounds);
1994 for (int i = 0; i < owner.Columns.Count; i++) {
1995 Rectangle zone = owner.Columns [i].Rect;
1996 zone.X = zone.Right - 5;
1998 if (zone.Contains (pt)) {
1999 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2001 resize_column = owner.Columns [i];
2006 if (resize_column == null)
2007 Cursor = Cursors.Default;
2009 Cursor = Cursors.VSplit;
2012 void HeaderMouseUp (object sender, MouseEventArgs me)
2016 if (column_resize_active) {
2017 column_resize_active = false;
2018 resize_column = null;
2019 Cursor = Cursors.Default;
2023 if (clicked_column != null && clicked_column.pressed) {
2024 clicked_column.pressed = false;
2025 Rectangle bounds = clicked_column.Rect;
2026 bounds.X -= owner.h_marker;
2027 Invalidate (bounds);
2028 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2031 if (drag_column != null && owner.AllowColumnReorder) {
2033 if (drag_to_index > GetReorderedIndex (clicked_column))
2035 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2036 owner.ReorderColumn (clicked_column, drag_to_index);
2041 clicked_column = null;
2044 internal override void OnPaintInternal (PaintEventArgs pe)
2049 Theme theme = ThemeEngine.Current;
2050 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2052 if (drag_column == null)
2056 if (drag_to_index == owner.Columns.Count)
2057 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2059 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2060 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2063 protected override void WndProc (ref Message m)
2065 switch ((Msg)m.Msg) {
2066 case Msg.WM_SETFOCUS:
2070 base.WndProc (ref m);
2076 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2078 internal ArrayList list;
2079 private ListView owner;
2081 #region Public Constructor
2082 public CheckedIndexCollection (ListView owner)
2084 list = new ArrayList ();
2087 #endregion // Public Constructor
2089 #region Public Properties
2092 get { return list.Count; }
2095 public bool IsReadOnly {
2096 get { return true; }
2099 public int this [int index] {
2101 if (index < 0 || index >= list.Count)
2102 throw new ArgumentOutOfRangeException ("index");
2103 return (int) list [index];
2107 bool ICollection.IsSynchronized {
2108 get { return false; }
2111 object ICollection.SyncRoot {
2112 get { return this; }
2115 bool IList.IsFixedSize {
2116 get { return true; }
2119 object IList.this [int index] {
2120 get { return this [index]; }
2121 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2123 #endregion // Public Properties
2125 #region Public Methods
2126 public bool Contains (int checkedIndex)
2128 return list.Contains (checkedIndex);
2131 public IEnumerator GetEnumerator ()
2133 return list.GetEnumerator ();
2136 void ICollection.CopyTo (Array dest, int index)
2138 list.CopyTo (dest, index);
2141 int IList.Add (object value)
2143 throw new NotSupportedException ("Add operation is not supported.");
2148 throw new NotSupportedException ("Clear operation is not supported.");
2151 bool IList.Contains (object checkedIndex)
2153 return list.Contains (checkedIndex);
2156 int IList.IndexOf (object checkedIndex)
2158 return list.IndexOf (checkedIndex);
2161 void IList.Insert (int index, object value)
2163 throw new NotSupportedException ("Insert operation is not supported.");
2166 void IList.Remove (object value)
2168 throw new NotSupportedException ("Remove operation is not supported.");
2171 void IList.RemoveAt (int index)
2173 throw new NotSupportedException ("RemoveAt operation is not supported.");
2176 public int IndexOf (int checkedIndex)
2178 return list.IndexOf (checkedIndex);
2180 #endregion // Public Methods
2182 } // CheckedIndexCollection
2184 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2186 internal ArrayList list;
2187 private ListView owner;
2189 #region Public Constructor
2190 public CheckedListViewItemCollection (ListView owner)
2192 list = new ArrayList ();
2195 #endregion // Public Constructor
2197 #region Public Properties
2200 get { return list.Count; }
2203 public bool IsReadOnly {
2204 get { return true; }
2207 public ListViewItem this [int index] {
2209 if (index < 0 || index >= list.Count)
2210 throw new ArgumentOutOfRangeException ("index");
2211 return (ListViewItem) list [index];
2215 bool ICollection.IsSynchronized {
2216 get { return list.IsSynchronized; }
2219 object ICollection.SyncRoot {
2220 get { return this; }
2223 bool IList.IsFixedSize {
2224 get { return true; }
2227 object IList.this [int index] {
2228 get { return this [index]; }
2229 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2231 #endregion // Public Properties
2233 #region Public Methods
2234 public bool Contains (ListViewItem item)
2236 return list.Contains (item);
2239 public void CopyTo (Array dest, int index)
2241 list.CopyTo (dest, index);
2244 public IEnumerator GetEnumerator ()
2246 return list.GetEnumerator ();
2249 int IList.Add (object value)
2251 throw new NotSupportedException ("Add operation is not supported.");
2256 throw new NotSupportedException ("Clear operation is not supported.");
2259 bool IList.Contains (object item)
2261 return list.Contains (item);
2264 int IList.IndexOf (object item)
2266 return list.IndexOf (item);
2269 void IList.Insert (int index, object value)
2271 throw new NotSupportedException ("Insert operation is not supported.");
2274 void IList.Remove (object value)
2276 throw new NotSupportedException ("Remove operation is not supported.");
2279 void IList.RemoveAt (int index)
2281 throw new NotSupportedException ("RemoveAt operation is not supported.");
2284 public int IndexOf (ListViewItem item)
2286 return list.IndexOf (item);
2288 #endregion // Public Methods
2290 } // CheckedListViewItemCollection
2292 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2294 internal ArrayList list;
2295 private ListView owner;
2297 #region Public Constructor
2298 public ColumnHeaderCollection (ListView owner)
2300 list = new ArrayList ();
2303 #endregion // Public Constructor
2305 #region Public Properties
2308 get { return list.Count; }
2311 public bool IsReadOnly {
2312 get { return false; }
2315 public virtual ColumnHeader this [int index] {
2317 if (index < 0 || index >= list.Count)
2318 throw new ArgumentOutOfRangeException ("index");
2319 return (ColumnHeader) list [index];
2323 bool ICollection.IsSynchronized {
2324 get { return true; }
2327 object ICollection.SyncRoot {
2328 get { return this; }
2331 bool IList.IsFixedSize {
2332 get { return list.IsFixedSize; }
2335 object IList.this [int index] {
2336 get { return this [index]; }
2337 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2339 #endregion // Public Properties
2341 #region Public Methods
2342 public virtual int Add (ColumnHeader value)
2345 value.owner = this.owner;
2346 idx = list.Add (value);
2347 if (owner.IsHandleCreated) {
2348 owner.Redraw (true);
2353 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2355 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2356 this.Add (colHeader);
2360 public virtual void AddRange (ColumnHeader [] values)
2362 foreach (ColumnHeader colHeader in values) {
2363 colHeader.owner = this.owner;
2367 owner.Redraw (true);
2370 public virtual void Clear ()
2373 owner.Redraw (true);
2376 public bool Contains (ColumnHeader value)
2378 return list.Contains (value);
2381 public IEnumerator GetEnumerator ()
2383 return list.GetEnumerator ();
2386 void ICollection.CopyTo (Array dest, int index)
2388 list.CopyTo (dest, index);
2391 int IList.Add (object value)
2393 if (! (value is ColumnHeader)) {
2394 throw new ArgumentException ("Not of type ColumnHeader", "value");
2397 return this.Add ((ColumnHeader) value);
2400 bool IList.Contains (object value)
2402 if (! (value is ColumnHeader)) {
2403 throw new ArgumentException ("Not of type ColumnHeader", "value");
2406 return this.Contains ((ColumnHeader) value);
2409 int IList.IndexOf (object value)
2411 if (! (value is ColumnHeader)) {
2412 throw new ArgumentException ("Not of type ColumnHeader", "value");
2415 return this.IndexOf ((ColumnHeader) value);
2418 void IList.Insert (int index, object value)
2420 if (! (value is ColumnHeader)) {
2421 throw new ArgumentException ("Not of type ColumnHeader", "value");
2424 this.Insert (index, (ColumnHeader) value);
2427 void IList.Remove (object value)
2429 if (! (value is ColumnHeader)) {
2430 throw new ArgumentException ("Not of type ColumnHeader", "value");
2433 this.Remove ((ColumnHeader) value);
2436 public int IndexOf (ColumnHeader value)
2438 return list.IndexOf (value);
2441 public void Insert (int index, ColumnHeader value)
2443 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2444 // but it's really only greater.
2445 if (index < 0 || index > list.Count)
2446 throw new ArgumentOutOfRangeException ("index");
2448 value.owner = this.owner;
2449 list.Insert (index, value);
2450 owner.Redraw (true);
2453 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2455 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2456 this.Insert (index, colHeader);
2459 public virtual void Remove (ColumnHeader column)
2461 // TODO: Update Column internal index ?
2462 list.Remove (column);
2463 owner.Redraw (true);
2466 public virtual void RemoveAt (int index)
2468 if (index < 0 || index >= list.Count)
2469 throw new ArgumentOutOfRangeException ("index");
2471 // TODO: Update Column internal index ?
2472 list.RemoveAt (index);
2473 owner.Redraw (true);
2475 #endregion // Public Methods
2478 } // ColumnHeaderCollection
2480 public class ListViewItemCollection : IList, ICollection, IEnumerable
2482 internal ArrayList list;
2483 private ListView owner;
2485 #region Public Constructor
2486 public ListViewItemCollection (ListView owner)
2488 list = new ArrayList ();
2491 #endregion // Public Constructor
2493 #region Public Properties
2496 get { return list.Count; }
2499 public bool IsReadOnly {
2500 get { return false; }
2503 public virtual ListViewItem this [int displayIndex] {
2505 if (displayIndex < 0 || displayIndex >= list.Count)
2506 throw new ArgumentOutOfRangeException ("displayIndex");
2507 return (ListViewItem) list [displayIndex];
2511 if (displayIndex < 0 || displayIndex >= list.Count)
2512 throw new ArgumentOutOfRangeException ("displayIndex");
2514 if (list.Contains (value))
2515 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2517 value.Owner = owner;
2518 list [displayIndex] = value;
2520 owner.Redraw (true);
2524 bool ICollection.IsSynchronized {
2525 get { return true; }
2528 object ICollection.SyncRoot {
2529 get { return this; }
2532 bool IList.IsFixedSize {
2533 get { return list.IsFixedSize; }
2536 object IList.this [int index] {
2537 get { return this [index]; }
2539 if (value is ListViewItem)
2540 this [index] = (ListViewItem) value;
2542 this [index] = new ListViewItem (value.ToString ());
2545 #endregion // Public Properties
2547 #region Public Methods
2548 public virtual ListViewItem Add (ListViewItem value)
2550 if (list.Contains (value))
2551 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2553 value.Owner = owner;
2556 if (owner.Sorting != SortOrder.None)
2559 owner.Redraw (true);
2564 public virtual ListViewItem Add (string text)
2566 ListViewItem item = new ListViewItem (text);
2567 return this.Add (item);
2570 public virtual ListViewItem Add (string text, int imageIndex)
2572 ListViewItem item = new ListViewItem (text, imageIndex);
2573 return this.Add (item);
2576 public void AddRange (ListViewItem [] values)
2579 owner.SelectedItems.list.Clear ();
2580 owner.SelectedIndices.list.Clear ();
2581 owner.CheckedItems.list.Clear ();
2582 owner.CheckedIndices.list.Clear ();
2584 foreach (ListViewItem item in values) {
2589 if (owner.Sorting != SortOrder.None)
2592 owner.Redraw (true);
2595 public virtual void Clear ()
2597 owner.SetFocusedItem (null);
2598 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2600 owner.SelectedItems.list.Clear ();
2601 owner.SelectedIndices.list.Clear ();
2602 owner.CheckedItems.list.Clear ();
2603 owner.CheckedIndices.list.Clear ();
2604 owner.Redraw (true);
2607 public bool Contains (ListViewItem item)
2609 return list.Contains (item);
2612 public void CopyTo (Array dest, int index)
2614 list.CopyTo (dest, index);
2617 public IEnumerator GetEnumerator ()
2619 return list.GetEnumerator ();
2622 int IList.Add (object item)
2627 if (item is ListViewItem) {
2628 li = (ListViewItem) item;
2629 if (list.Contains (li))
2630 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2633 li = new ListViewItem (item.ToString ());
2636 result = list.Add (li);
2637 owner.Redraw (true);
2642 bool IList.Contains (object item)
2644 return list.Contains (item);
2647 int IList.IndexOf (object item)
2649 return list.IndexOf (item);
2652 void IList.Insert (int index, object item)
2654 if (item is ListViewItem)
2655 this.Insert (index, (ListViewItem) item);
2657 this.Insert (index, item.ToString ());
2660 void IList.Remove (object item)
2662 Remove ((ListViewItem) item);
2665 public int IndexOf (ListViewItem item)
2667 return list.IndexOf (item);
2670 public ListViewItem Insert (int index, ListViewItem item)
2672 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2673 // but it's really only greater.
2674 if (index < 0 || index > list.Count)
2675 throw new ArgumentOutOfRangeException ("index");
2677 if (list.Contains (item))
2678 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
2681 list.Insert (index, item);
2682 owner.Redraw (true);
2686 public ListViewItem Insert (int index, string text)
2688 return this.Insert (index, new ListViewItem (text));
2691 public ListViewItem Insert (int index, string text, int imageIndex)
2693 return this.Insert (index, new ListViewItem (text, imageIndex));
2696 public virtual void Remove (ListViewItem item)
2698 if (!list.Contains (item))
2701 owner.SelectedItems.list.Remove (item);
2702 owner.SelectedIndices.list.Remove (item.Index);
2703 owner.CheckedItems.list.Remove (item);
2704 owner.CheckedIndices.list.Remove (item.Index);
2706 owner.Redraw (true);
2709 public virtual void RemoveAt (int index)
2711 if (index < 0 || index >= list.Count)
2712 throw new ArgumentOutOfRangeException ("index");
2714 list.RemoveAt (index);
2715 owner.SelectedItems.list.RemoveAt (index);
2716 owner.SelectedIndices.list.RemoveAt (index);
2717 owner.CheckedItems.list.RemoveAt (index);
2718 owner.CheckedIndices.list.RemoveAt (index);
2719 owner.Redraw (false);
2721 #endregion // Public Methods
2723 } // ListViewItemCollection
2725 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2727 internal ArrayList list;
2728 private ListView owner;
2730 #region Public Constructor
2731 public SelectedIndexCollection (ListView owner)
2733 list = new ArrayList ();
2736 #endregion // Public Constructor
2738 #region Public Properties
2741 get { return list.Count; }
2744 public bool IsReadOnly {
2745 get { return true; }
2748 public int this [int index] {
2750 if (index < 0 || index >= list.Count)
2751 throw new ArgumentOutOfRangeException ("index");
2752 return (int) list [index];
2756 bool ICollection.IsSynchronized {
2757 get { return list.IsSynchronized; }
2760 object ICollection.SyncRoot {
2761 get { return this; }
2764 bool IList.IsFixedSize {
2765 get { return true; }
2768 object IList.this [int index] {
2769 get { return this [index]; }
2770 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2772 #endregion // Public Properties
2774 #region Public Methods
2775 public bool Contains (int selectedIndex)
2777 return list.Contains (selectedIndex);
2780 public void CopyTo (Array dest, int index)
2782 list.CopyTo (dest, index);
2785 public IEnumerator GetEnumerator ()
2787 return list.GetEnumerator ();
2790 int IList.Add (object value)
2792 throw new NotSupportedException ("Add operation is not supported.");
2797 throw new NotSupportedException ("Clear operation is not supported.");
2800 bool IList.Contains (object selectedIndex)
2802 return list.Contains (selectedIndex);
2805 int IList.IndexOf (object selectedIndex)
2807 return list.IndexOf (selectedIndex);
2810 void IList.Insert (int index, object value)
2812 throw new NotSupportedException ("Insert operation is not supported.");
2815 void IList.Remove (object value)
2817 throw new NotSupportedException ("Remove operation is not supported.");
2820 void IList.RemoveAt (int index)
2822 throw new NotSupportedException ("RemoveAt operation is not supported.");
2825 public int IndexOf (int selectedIndex)
2827 return list.IndexOf (selectedIndex);
2829 #endregion // Public Methods
2831 } // SelectedIndexCollection
2833 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
2835 internal ArrayList list;
2836 private ListView owner;
2838 #region Public Constructor
2839 public SelectedListViewItemCollection (ListView owner)
2841 list = new ArrayList ();
2844 #endregion // Public Constructor
2846 #region Public Properties
2849 get { return list.Count; }
2852 public bool IsReadOnly {
2853 get { return true; }
2856 public ListViewItem this [int index] {
2858 if (index < 0 || index >= list.Count)
2859 throw new ArgumentOutOfRangeException ("index");
2860 return (ListViewItem) list [index];
2864 bool ICollection.IsSynchronized {
2865 get { return list.IsSynchronized; }
2868 object ICollection.SyncRoot {
2869 get { return this; }
2872 bool IList.IsFixedSize {
2873 get { return true; }
2876 object IList.this [int index] {
2877 get { return this [index]; }
2878 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2880 #endregion // Public Properties
2882 #region Public Methods
2883 public void Clear ()
2885 ArrayList copy = (ArrayList) list.Clone ();
2886 for (int i = 0; i < copy.Count; i++)
2887 ((ListViewItem) copy [i]).Selected = false;
2892 public bool Contains (ListViewItem item)
2894 return list.Contains (item);
2897 public void CopyTo (Array dest, int index)
2899 list.CopyTo (dest, index);
2902 public IEnumerator GetEnumerator ()
2904 return list.GetEnumerator ();
2907 int IList.Add (object value)
2909 throw new NotSupportedException ("Add operation is not supported.");
2912 bool IList.Contains (object item)
2914 return list.Contains (item);
2917 int IList.IndexOf (object item)
2919 return list.IndexOf (item);
2922 void IList.Insert (int index, object value)
2924 throw new NotSupportedException ("Insert operation is not supported.");
2927 void IList.Remove (object value)
2929 throw new NotSupportedException ("Remove operation is not supported.");
2932 void IList.RemoveAt (int index)
2934 throw new NotSupportedException ("RemoveAt operation is not supported.");
2937 public int IndexOf (ListViewItem item)
2939 return list.IndexOf (item);
2941 #endregion // Public Methods
2943 } // SelectedListViewItemCollection
2945 #endregion // Subclasses