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)
26 // Daniel Nauck (dna(at)mono-project(dot)de)
29 // - 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 using System.Collections.Generic;
47 namespace System.Windows.Forms
49 [DefaultEvent ("SelectedIndexChanged")]
50 [DefaultProperty ("Items")]
51 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
53 [ClassInterface (ClassInterfaceType.AutoDispatch)]
55 [Docking (DockingBehavior.Ask)]
57 public class ListView : Control
59 private ItemActivation activation = ItemActivation.Standard;
60 private ListViewAlignment alignment = ListViewAlignment.Top;
61 private bool allow_column_reorder = false;
62 private bool auto_arrange = true;
63 private bool check_boxes = false;
64 private readonly CheckedIndexCollection checked_indices;
65 private readonly CheckedListViewItemCollection checked_items;
66 private readonly ColumnHeaderCollection columns;
67 internal ListViewItem focused_item;
68 private bool full_row_select = false;
69 private bool grid_lines = false;
70 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
71 private bool hide_selection = true;
72 private bool hover_selection = false;
73 private IComparer item_sorter;
74 private readonly ListViewItemCollection items;
76 private readonly ListViewGroupCollection groups;
77 private bool show_groups = true;
79 private bool label_edit = false;
80 private bool label_wrap = true;
81 private bool multiselect = true;
82 private bool scrollable = true;
83 private readonly SelectedIndexCollection selected_indices;
84 private readonly SelectedListViewItemCollection selected_items;
85 private SortOrder sort_order = SortOrder.None;
86 private ImageList state_image_list;
87 private bool updating = false;
88 private View view = View.LargeIcon;
89 private int layout_wd; // We might draw more than our client area
90 private int layout_ht; // therefore we need to have these two.
91 //private TextBox editor; // Used for editing an item text
92 HeaderControl header_control;
93 internal ItemControl item_control;
94 internal ScrollBar h_scroll; // used for scrolling horizontally
95 internal ScrollBar v_scroll; // used for scrolling vertically
96 internal int h_marker; // Position markers for scrolling
97 internal int v_marker;
98 private int keysearch_tickcnt;
99 private string keysearch_text;
100 static private readonly int keysearch_keydelay = 1000;
101 private int[] reordered_column_indices;
103 private Size tile_size;
106 // internal variables
107 internal ImageList large_image_list;
108 internal ImageList small_image_list;
109 internal Size text_size = Size.Empty;
112 static object AfterLabelEditEvent = new object ();
113 static object BeforeLabelEditEvent = new object ();
114 static object ColumnClickEvent = new object ();
115 static object ItemActivateEvent = new object ();
116 static object ItemCheckEvent = new object ();
117 static object ItemDragEvent = new object ();
118 static object SelectedIndexChangedEvent = new object ();
120 public event LabelEditEventHandler AfterLabelEdit {
121 add { Events.AddHandler (AfterLabelEditEvent, value); }
122 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
126 [EditorBrowsable (EditorBrowsableState.Never)]
127 public new event EventHandler BackgroundImageChanged {
128 add { base.BackgroundImageChanged += value; }
129 remove { base.BackgroundImageChanged -= value; }
132 public event LabelEditEventHandler BeforeLabelEdit {
133 add { Events.AddHandler (BeforeLabelEditEvent, value); }
134 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
137 public event ColumnClickEventHandler ColumnClick {
138 add { Events.AddHandler (ColumnClickEvent, value); }
139 remove { Events.RemoveHandler (ColumnClickEvent, value); }
142 public event EventHandler ItemActivate {
143 add { Events.AddHandler (ItemActivateEvent, value); }
144 remove { Events.RemoveHandler (ItemActivateEvent, value); }
147 public event ItemCheckEventHandler ItemCheck {
148 add { Events.AddHandler (ItemCheckEvent, value); }
149 remove { Events.RemoveHandler (ItemCheckEvent, value); }
152 public event ItemDragEventHandler ItemDrag {
153 add { Events.AddHandler (ItemDragEvent, value); }
154 remove { Events.RemoveHandler (ItemDragEvent, value); }
158 [EditorBrowsable (EditorBrowsableState.Never)]
159 public new event PaintEventHandler Paint {
160 add { base.Paint += value; }
161 remove { base.Paint -= value; }
164 public event EventHandler SelectedIndexChanged {
165 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
166 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
170 [EditorBrowsable (EditorBrowsableState.Never)]
171 public new event EventHandler TextChanged {
172 add { base.TextChanged += value; }
173 remove { base.TextChanged -= value; }
178 #region Public Constructors
181 background_color = ThemeEngine.Current.ColorWindow;
182 items = new ListViewItemCollection (this);
184 groups = new ListViewGroupCollection (this);
186 checked_indices = new CheckedIndexCollection (this);
187 checked_items = new CheckedListViewItemCollection (this);
188 columns = new ColumnHeaderCollection (this);
189 foreground_color = SystemColors.WindowText;
190 selected_indices = new SelectedIndexCollection (this);
191 selected_items = new SelectedListViewItemCollection (this);
193 border_style = BorderStyle.Fixed3D;
195 header_control = new HeaderControl (this);
196 header_control.Visible = false;
197 Controls.AddImplicit (header_control);
199 item_control = new ItemControl (this);
200 Controls.AddImplicit (item_control);
202 h_scroll = new ImplicitHScrollBar ();
203 Controls.AddImplicit (this.h_scroll);
205 v_scroll = new ImplicitVScrollBar ();
206 Controls.AddImplicit (this.v_scroll);
208 h_marker = v_marker = 0;
209 keysearch_tickcnt = 0;
211 // scroll bars are disabled initially
212 h_scroll.Visible = false;
213 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
214 v_scroll.Visible = false;
215 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
218 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
219 SizeChanged += new EventHandler (ListView_SizeChanged);
220 GotFocus += new EventHandler (FocusChanged);
221 LostFocus += new EventHandler (FocusChanged);
222 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
224 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
226 | ControlStyles.UseTextForAccessibility
230 #endregion // Public Constructors
232 #region Private Internal Properties
233 internal Size CheckBoxSize {
235 if (this.check_boxes) {
236 if (this.state_image_list != null)
237 return this.state_image_list.ImageSize;
239 return ThemeEngine.Current.ListViewCheckBoxSize;
245 #endregion // Private Internal Properties
247 #region Protected Properties
248 protected override CreateParams CreateParams {
249 get { return base.CreateParams; }
252 protected override Size DefaultSize {
253 get { return ThemeEngine.Current.ListViewDefaultSize; }
255 #endregion // Protected Properties
257 #region Public Instance Properties
258 [DefaultValue (ItemActivation.Standard)]
259 public ItemActivation Activation {
260 get { return activation; }
262 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
263 value != ItemActivation.TwoClick) {
264 throw new InvalidEnumArgumentException (string.Format
265 ("Enum argument value '{0}' is not valid for Activation", value));
272 [DefaultValue (ListViewAlignment.Top)]
274 public ListViewAlignment Alignment {
275 get { return alignment; }
277 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
278 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
279 throw new InvalidEnumArgumentException (string.Format
280 ("Enum argument value '{0}' is not valid for Alignment", value));
283 if (this.alignment != value) {
285 // alignment does not matter in Details/List views
286 if (this.view == View.LargeIcon ||
287 this.View == View.SmallIcon)
293 [DefaultValue (false)]
294 public bool AllowColumnReorder {
295 get { return allow_column_reorder; }
296 set { allow_column_reorder = value; }
299 [DefaultValue (true)]
300 public bool AutoArrange {
301 get { return auto_arrange; }
303 if (auto_arrange != value) {
304 auto_arrange = value;
305 // autoarrange does not matter in Details/List views
306 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
312 public override Color BackColor {
314 if (background_color.IsEmpty)
315 return ThemeEngine.Current.ColorWindow;
317 return background_color;
319 set { background_color = value; }
323 [EditorBrowsable (EditorBrowsableState.Never)]
324 public override Image BackgroundImage {
325 get { return base.BackgroundImage; }
326 set { base.BackgroundImage = value; }
329 [DefaultValue (BorderStyle.Fixed3D)]
331 public BorderStyle BorderStyle {
332 get { return InternalBorderStyle; }
333 set { InternalBorderStyle = value; }
336 [DefaultValue (false)]
337 public bool CheckBoxes {
338 get { return check_boxes; }
340 if (check_boxes != value) {
342 if (value && View == View.Tile)
343 throw new NotSupportedException ("CheckBoxes are not"
344 + " supported in Tile view. Choose a different"
345 + " view or set CheckBoxes to false.");
355 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
356 public CheckedIndexCollection CheckedIndices {
357 get { return checked_indices; }
361 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
362 public CheckedListViewItemCollection CheckedItems {
363 get { return checked_items; }
366 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
368 [MergableProperty (false)]
369 public ColumnHeaderCollection Columns {
370 get { return columns; }
374 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
375 public ListViewItem FocusedItem {
381 public override Color ForeColor {
383 if (foreground_color.IsEmpty)
384 return ThemeEngine.Current.ColorWindowText;
386 return foreground_color;
388 set { foreground_color = value; }
391 [DefaultValue (false)]
392 public bool FullRowSelect {
393 get { return full_row_select; }
394 set { full_row_select = value; }
397 [DefaultValue (false)]
398 public bool GridLines {
399 get { return grid_lines; }
401 if (grid_lines != value) {
408 [DefaultValue (ColumnHeaderStyle.Clickable)]
409 public ColumnHeaderStyle HeaderStyle {
410 get { return header_style; }
412 if (header_style == value)
416 case ColumnHeaderStyle.Clickable:
417 case ColumnHeaderStyle.Nonclickable:
418 case ColumnHeaderStyle.None:
421 throw new InvalidEnumArgumentException (string.Format
422 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
425 header_style = value;
426 if (view == View.Details)
431 [DefaultValue (true)]
432 public bool HideSelection {
433 get { return hide_selection; }
435 if (hide_selection != value) {
436 hide_selection = value;
442 [DefaultValue (false)]
443 public bool HoverSelection {
444 get { return hover_selection; }
445 set { hover_selection = value; }
448 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
450 [MergableProperty (false)]
451 public ListViewItemCollection Items {
452 get { return items; }
455 [DefaultValue (false)]
456 public bool LabelEdit {
457 get { return label_edit; }
458 set { label_edit = value; }
461 [DefaultValue (true)]
463 public bool LabelWrap {
464 get { return label_wrap; }
466 if (label_wrap != value) {
473 [DefaultValue (null)]
474 public ImageList LargeImageList {
475 get { return large_image_list; }
477 large_image_list = value;
483 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
484 public IComparer ListViewItemSorter {
486 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
491 if (item_sorter != value) {
498 [DefaultValue (true)]
499 public bool MultiSelect {
500 get { return multiselect; }
501 set { multiselect = value; }
504 [DefaultValue (true)]
505 public bool Scrollable {
506 get { return scrollable; }
508 if (scrollable != value) {
516 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
517 public SelectedIndexCollection SelectedIndices {
518 get { return selected_indices; }
522 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
523 public SelectedListViewItemCollection SelectedItems {
524 get { return selected_items; }
529 public bool ShowGroups {
530 get { return show_groups; }
533 if (show_groups != value)
541 [LocalizableAttribute(true)]
542 public ListViewGroupCollection Groups {
543 get { return groups; }
547 [DefaultValue (null)]
548 public ImageList SmallImageList {
549 get { return small_image_list; }
551 small_image_list = value;
556 [DefaultValue (SortOrder.None)]
557 public SortOrder Sorting {
558 get { return sort_order; }
560 if (!Enum.IsDefined (typeof (SortOrder), value)) {
561 throw new InvalidEnumArgumentException ("value", (int) value,
565 if (sort_order == value)
570 if (value == SortOrder.None) {
571 if (item_sorter != null) {
572 // ListViewItemSorter should never be reset for SmallIcon
573 // and LargeIcon view
574 if (View != View.SmallIcon && View != View.LargeIcon)
578 // in .NET 1.1, only internal IComparer would be
580 if (item_sorter is ItemComparer)
586 if (item_sorter == null)
587 item_sorter = new ItemComparer (value);
588 if (item_sorter is ItemComparer) {
590 item_sorter = new ItemComparer (value);
592 // in .NET 1.1, the sort order is not updated for
593 // SmallIcon and LargeIcon views if no custom IComparer
595 if (View != View.SmallIcon && View != View.LargeIcon)
596 item_sorter = new ItemComparer (value);
604 [DefaultValue (null)]
605 public ImageList StateImageList {
606 get { return state_image_list; }
608 state_image_list = value;
615 [EditorBrowsable (EditorBrowsableState.Never)]
616 public override string Text {
617 get { return base.Text; }
619 if (value == base.Text)
628 public Size TileSize {
633 if (value.Width <= 0 || value.Height <= 0)
634 throw new ArgumentOutOfRangeException ("value");
643 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
644 public ListViewItem TopItem {
647 if (this.items.Count == 0)
649 // if contents are not scrolled
650 // it is the first item
651 else if (h_marker == 0 && v_marker == 0)
652 return this.items [0];
653 // do a hit test for the scrolled position
655 foreach (ListViewItem item in this.items) {
656 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
665 [MonoTODO("Implement")]
666 public bool UseCompatibleStateImageBehavior {
676 [DefaultValue (View.LargeIcon)]
680 if (!Enum.IsDefined (typeof (View), value))
681 throw new InvalidEnumArgumentException ("value", (int) value,
686 if (CheckBoxes && value == View.Tile)
687 throw new NotSupportedException ("CheckBoxes are not"
688 + " supported in Tile view. Choose a different"
689 + " view or set CheckBoxes to false.");
692 h_scroll.Value = v_scroll.Value = 0;
698 #endregion // Public Instance Properties
700 #region Internal Methods Properties
702 internal int FirstVisibleIndex {
705 if (this.items.Count == 0)
708 if (h_marker == 0 && v_marker == 0)
711 foreach (ListViewItem item in this.items) {
712 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
721 internal int LastVisibleIndex {
723 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
724 if (View == View.List || Alignment == ListViewAlignment.Left) {
725 if (Items[i].Bounds.X > item_control.ClientRectangle.Right)
728 if (Items[i].Bounds.Y > item_control.ClientRectangle.Bottom)
733 return Items.Count - 1;
737 internal void OnSelectedIndexChanged ()
740 OnSelectedIndexChanged (EventArgs.Empty);
743 internal int TotalWidth {
744 get { return Math.Max (this.Width, this.layout_wd); }
747 internal int TotalHeight {
748 get { return Math.Max (this.Height, this.layout_ht); }
751 internal void Redraw (bool recalculate)
753 // Avoid calculations when control is being updated
758 CalculateListView (this.alignment);
763 const int text_padding = 5;
765 internal Size GetChildColumnSize (int index)
767 Size ret_size = Size.Empty;
768 ColumnHeader col = this.columns [index];
770 if (col.Width == -2) { // autosize = max(items, columnheader)
771 Size size = Size.Ceiling (this.DeviceContext.MeasureString
772 (col.Text, this.Font));
773 size.Width += text_padding;
774 ret_size = BiggestItem (index);
775 if (size.Width > ret_size.Width)
778 else { // -1 and all the values < -2 are put under one category
779 ret_size = BiggestItem (index);
780 // fall back to empty columns' width if no subitem is available for a column
781 if (ret_size.IsEmpty) {
782 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
783 if (col.Text.Length > 0)
784 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
785 (col.Text, this.Font)).Height;
787 ret_size.Height = this.Font.Height;
791 ret_size.Height += text_padding;
793 // adjust the size for icon and checkbox for 0th column
795 ret_size.Width += (this.CheckBoxSize.Width + 4);
796 if (this.small_image_list != null)
797 ret_size.Width += this.small_image_list.ImageSize.Width;
802 // Returns the size of biggest item text in a column.
803 private Size BiggestItem (int col)
805 Size temp = Size.Empty;
806 Size ret_size = Size.Empty;
808 // 0th column holds the item text, we check the size of
809 // the various subitems falling in that column and get
810 // the biggest one's size.
811 foreach (ListViewItem item in items) {
812 if (col >= item.SubItems.Count)
815 temp = Size.Ceiling (this.DeviceContext.MeasureString
816 (item.SubItems [col].Text, this.Font));
817 if (temp.Width > ret_size.Width)
821 // adjustment for space
822 if (!ret_size.IsEmpty)
828 const int max_wrap_padding = 38;
830 // Sets the size of the biggest item text as per the view
831 private void CalcTextSize ()
833 // clear the old value
834 text_size = Size.Empty;
836 if (items.Count == 0)
839 text_size = BiggestItem (0);
841 if (view == View.LargeIcon && this.label_wrap) {
842 Size temp = Size.Empty;
843 if (this.check_boxes)
844 temp.Width += 2 * this.CheckBoxSize.Width;
845 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
846 temp.Width += icon_w + max_wrap_padding;
847 // wrapping is done for two lines only
848 if (text_size.Width > temp.Width) {
849 text_size.Width = temp.Width;
850 text_size.Height *= 2;
853 else if (view == View.List) {
854 // in list view max text shown in determined by the
855 // control width, even if scolling is enabled.
856 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
857 if (this.small_image_list != null)
858 max_wd -= this.small_image_list.ImageSize.Width;
860 if (text_size.Width > max_wd)
861 text_size.Width = max_wd;
864 // we do the default settings, if we have got 0's
865 if (text_size.Height <= 0)
866 text_size.Height = this.Font.Height;
867 if (text_size.Width <= 0)
868 text_size.Width = this.Width;
871 text_size.Width += 4;
872 text_size.Height += 2;
875 private void Scroll (ScrollBar scrollbar, int delta)
877 if (delta == 0 || !scrollbar.Visible)
881 if (scrollbar == h_scroll)
882 max = h_scroll.Maximum - item_control.Width;
884 max = v_scroll.Maximum - item_control.Height;
886 int val = scrollbar.Value + delta;
889 else if (val < scrollbar.Minimum)
890 val = scrollbar.Minimum;
891 scrollbar.Value = val;
894 private void CalculateScrollBars ()
896 Rectangle client_area = ClientRectangle;
898 if (!this.scrollable || this.items.Count <= 0) {
899 h_scroll.Visible = false;
900 v_scroll.Visible = false;
901 item_control.Location = new Point (0, header_control.Height);
902 item_control.Height = ClientRectangle.Width - header_control.Height;
903 item_control.Width = ClientRectangle.Width;
904 header_control.Width = ClientRectangle.Width;
908 // Don't calculate if the view is not displayable
909 if (client_area.Height < 0 || client_area.Width < 0)
912 // making a scroll bar visible might make
913 // other scroll bar visible
914 if (layout_wd > client_area.Right) {
915 h_scroll.Visible = true;
916 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
917 v_scroll.Visible = true;
919 v_scroll.Visible = false;
920 } else if (layout_ht > client_area.Bottom) {
921 v_scroll.Visible = true;
922 if ((layout_wd + v_scroll.Width) > client_area.Right)
923 h_scroll.Visible = true;
925 h_scroll.Visible = false;
927 h_scroll.Visible = false;
928 v_scroll.Visible = false;
931 item_control.Height = ClientRectangle.Height - header_control.Height;
933 if (h_scroll.is_visible) {
934 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
935 h_scroll.Minimum = 0;
937 // if v_scroll is visible, adjust the maximum of the
938 // h_scroll to account for the width of v_scroll
939 if (v_scroll.Visible) {
940 h_scroll.Maximum = layout_wd + v_scroll.Width;
941 h_scroll.Width = client_area.Width - v_scroll.Width;
944 h_scroll.Maximum = layout_wd;
945 h_scroll.Width = client_area.Width;
948 h_scroll.LargeChange = client_area.Width;
949 h_scroll.SmallChange = Font.Height;
950 item_control.Height -= h_scroll.Height;
953 if (header_control.is_visible)
954 header_control.Width = ClientRectangle.Width;
955 item_control.Width = ClientRectangle.Width;
957 if (v_scroll.is_visible) {
958 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
959 v_scroll.Minimum = 0;
961 // if h_scroll is visible, adjust the maximum of the
962 // v_scroll to account for the height of h_scroll
963 if (h_scroll.Visible) {
964 v_scroll.Maximum = layout_ht + h_scroll.Height;
965 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
967 v_scroll.Maximum = layout_ht;
968 v_scroll.Height = client_area.Height;
971 v_scroll.LargeChange = client_area.Height;
972 v_scroll.SmallChange = Font.Height;
973 if (header_control.Visible)
974 header_control.Width -= v_scroll.Width;
975 item_control.Width -= v_scroll.Width;
980 internal int GetReorderedColumnIndex (ColumnHeader column)
982 if (reordered_column_indices == null)
985 for (int i = 0; i < Columns.Count; i++)
986 if (reordered_column_indices [i] == column.Index)
993 ColumnHeader GetReorderedColumn (int index)
995 if (reordered_column_indices == null)
996 return Columns [index];
998 return Columns [reordered_column_indices [index]];
1001 internal void ReorderColumn (ColumnHeader col, int index)
1004 ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
1006 ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
1010 header_control.Invalidate ();
1011 item_control.Invalidate ();
1016 if (reordered_column_indices == null) {
1017 reordered_column_indices = new int [Columns.Count];
1018 for (int i = 0; i < Columns.Count; i++)
1019 reordered_column_indices [i] = i;
1022 if (reordered_column_indices [index] == col.Index)
1025 int[] curr = reordered_column_indices;
1026 int[] result = new int [Columns.Count];
1028 for (int i = 0; i < Columns.Count; i++) {
1029 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
1033 result [i] = col.Index;
1035 result [i] = curr [curr_idx++];
1038 reordered_column_indices = result;
1040 header_control.Invalidate ();
1041 item_control.Invalidate ();
1044 Size LargeIconItemSize {
1046 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
1047 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
1048 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
1049 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
1050 return new Size (w, h);
1054 Size SmallIconItemSize {
1056 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
1057 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
1058 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
1059 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
1060 return new Size (w, h);
1067 // Calculate tile size if needed
1068 // It appears that using Font.Size instead of a SizeF value can give us
1069 // a slightly better approach to the proportions defined in .Net
1070 if (tile_size == Size.Empty) {
1071 int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
1072 int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
1073 int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
1074 int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
1076 tile_size = new Size (w, h);
1086 ListViewItem[,] item_matrix;
1088 void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
1090 header_control.Visible = false;
1091 header_control.Size = Size.Empty;
1092 item_control.Visible = true;
1093 item_control.Location = Point.Empty;
1095 if (items.Count == 0)
1098 Size sz = item_size;
1099 Rectangle area = ClientRectangle;
1102 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
1105 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1107 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
1110 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1113 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1114 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1115 item_matrix = new ListViewItem [rows, cols];
1118 foreach (ListViewItem item in items) {
1119 int x = col * (sz.Width + x_spacing);
1120 int y = row * (sz.Height + y_spacing);
1121 item.Location = new Point (x, y);
1125 item_matrix [row, col] = item;
1127 if (++row == rows) {
1132 if (++col == cols) {
1139 item_control.Size = new Size (layout_wd, layout_ht);
1142 void LayoutHeader ()
1145 for (int i = 0; i < Columns.Count; i++) {
1146 ColumnHeader col = GetReorderedColumn (i);
1149 col.CalcColumnHeader ();
1153 if (x < ClientRectangle.Width)
1154 x = ClientRectangle.Width;
1156 if (header_style == ColumnHeaderStyle.None) {
1157 header_control.Visible = false;
1158 header_control.Size = Size.Empty;
1160 header_control.Width = x;
1161 header_control.Height = columns [0].Ht;
1162 header_control.Visible = true;
1166 void LayoutDetails ()
1168 if (columns.Count == 0) {
1169 header_control.Visible = false;
1170 item_control.Visible = false;
1176 item_control.Visible = true;
1177 item_control.Location = new Point (0, header_control.Height);
1180 if (items.Count > 0) {
1181 foreach (ListViewItem item in items) {
1183 item.Location = new Point (0, y);
1184 y += item.Bounds.Height + 2;
1187 // some space for bottom gridline
1192 layout_wd = Math.Max (header_control.Width, item_control.Width);
1193 layout_ht = y + header_control.Height;
1196 private void CalculateListView (ListViewAlignment align)
1205 case View.SmallIcon:
1206 LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left, 4, 2);
1209 case View.LargeIcon:
1210 LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
1211 ThemeEngine.Current.ListViewHorizontalSpacing,
1212 ThemeEngine.Current.ListViewVerticalSpacing);
1216 LayoutIcons (SmallIconItemSize, true, 4, 2);
1220 LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
1221 ThemeEngine.Current.ListViewHorizontalSpacing,
1222 ThemeEngine.Current.ListViewVerticalSpacing);
1227 CalculateScrollBars ();
1230 private bool KeySearchString (KeyEventArgs ke)
1232 int current_tickcnt = Environment.TickCount;
1233 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1234 keysearch_text = string.Empty;
1237 keysearch_text += (char) ke.KeyData;
1238 keysearch_tickcnt = current_tickcnt;
1240 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1243 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1244 CompareOptions.IgnoreCase)) {
1245 SetFocusedItem (Items [i]);
1246 items [i].Selected = true;
1250 i = (i + 1 < Items.Count) ? i+1 : 0;
1258 int GetAdjustedIndex (Keys key)
1262 if (View == View.Details) {
1265 result = FocusedItem.Index - 1;
1268 result = FocusedItem.Index + 1;
1269 if (result == items.Count)
1273 int last_index = LastVisibleIndex;
1274 if (Items [last_index].Bounds.Bottom > item_control.ClientRectangle.Bottom)
1276 if (FocusedItem.Index == last_index) {
1277 if (FocusedItem.Index < Items.Count - 1) {
1278 int page_size = item_control.Height / items [0].Bounds.Height - 1;
1279 result = FocusedItem.Index + page_size - 1;
1280 if (result >= Items.Count)
1281 result = Items.Count - 1;
1284 result = last_index;
1287 int first_index = FirstVisibleIndex;
1288 if (Items [first_index].Bounds.Y < 0)
1290 if (FocusedItem.Index == first_index) {
1291 if (first_index > 0) {
1292 int page_size = item_control.Height / items [0].Bounds.Height - 1;
1293 result = first_index - page_size + 1;
1298 result = first_index;
1304 int row = FocusedItem.row;
1305 int col = FocusedItem.col;
1311 return item_matrix [row, col - 1].Index;
1314 if (col == (cols - 1))
1316 while (item_matrix [row, col + 1] == null) {
1321 return item_matrix [row, col + 1].Index;
1326 return item_matrix [row - 1, col].Index;
1329 if (row == (rows - 1) || row == Items.Count - 1)
1331 while (item_matrix [row + 1, col] == null) {
1336 return item_matrix [row + 1, col].Index;
1343 ListViewItem selection_start;
1345 private bool SelectItems (ArrayList sel_items)
1347 bool changed = false;
1348 ArrayList curr_items = SelectedItems.List;
1349 foreach (ListViewItem item in curr_items)
1350 if (!sel_items.Contains (item)) {
1351 item.Selected = false;
1354 foreach (ListViewItem item in sel_items)
1355 if (!item.Selected) {
1356 item.Selected = true;
1362 private void UpdateMultiSelection (int index)
1364 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1365 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1366 ListViewItem item = items [index];
1368 if (shift_pressed && selection_start != null) {
1369 ArrayList list = new ArrayList ();
1370 int start = Math.Min (selection_start.Index, index);
1371 int end = Math.Max (selection_start.Index, index);
1372 if (View == View.Details) {
1373 for (int i = start; i <= end; i++)
1374 list.Add (items [i]);
1376 int left = Math.Min (items [start].col, items [end].col);
1377 int right = Math.Max (items [start].col, items [end].col);
1378 int top = Math.Min (items [start].row, items [end].row);
1379 int bottom = Math.Max (items [start].row, items [end].row);
1380 foreach (ListViewItem curr in items)
1381 if (curr.row >= top && curr.row <= bottom &&
1382 curr.col >= left && curr.col <= right)
1385 if (SelectItems (list))
1386 OnSelectedIndexChanged (EventArgs.Empty);
1387 } else if (ctrl_pressed) {
1388 item.Selected = !item.Selected;
1389 selection_start = item;
1390 OnSelectedIndexChanged (EventArgs.Empty);
1392 SelectedItems.Clear ();
1393 item.Selected = true;
1394 selection_start = item;
1395 OnSelectedIndexChanged (EventArgs.Empty);
1399 internal override bool InternalPreProcessMessage (ref Message msg)
1401 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1402 Keys key_data = (Keys)msg.WParam.ToInt32();
1403 if (HandleNavKeys (key_data))
1406 return base.InternalPreProcessMessage (ref msg);
1409 bool HandleNavKeys (Keys key_data)
1411 if (Items.Count == 0 || !item_control.Visible)
1414 if (FocusedItem == null)
1415 SetFocusedItem (Items [0]);
1419 SelectIndex (Items.Count - 1);
1432 SelectIndex (GetAdjustedIndex (key_data));
1442 void SelectIndex (int index)
1448 UpdateMultiSelection (index);
1449 else if (!items [index].Selected) {
1450 items [index].Selected = true;
1451 OnSelectedIndexChanged (EventArgs.Empty);
1454 SetFocusedItem (items [index]);
1455 EnsureVisible (index);
1458 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1460 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1463 ke.Handled = KeySearchString (ke);
1466 private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
1468 Point loc = PointToClient (Control.MousePosition);
1469 return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
1472 internal class ItemControl : Control {
1475 ListViewItem clicked_item;
1476 ListViewItem last_clicked_item;
1477 bool hover_processed = false;
1478 bool checking = false;
1480 ListViewLabelEditTextBox edit_text_box;
1481 internal ListViewItem edit_item;
1482 LabelEditEventArgs edit_args;
1484 public ItemControl (ListView owner)
1487 DoubleClick += new EventHandler(ItemsDoubleClick);
1488 MouseDown += new MouseEventHandler(ItemsMouseDown);
1489 MouseMove += new MouseEventHandler(ItemsMouseMove);
1490 MouseHover += new EventHandler(ItemsMouseHover);
1491 MouseUp += new MouseEventHandler(ItemsMouseUp);
1494 void ItemsDoubleClick (object sender, EventArgs e)
1496 if (owner.activation == ItemActivation.Standard)
1497 owner.OnItemActivate (EventArgs.Empty);
1507 BoxSelect box_select_mode = BoxSelect.None;
1508 ArrayList prev_selection;
1509 Point box_select_start;
1511 Rectangle box_select_rect;
1512 internal Rectangle BoxSelectRectangle {
1513 get { return box_select_rect; }
1515 if (box_select_rect == value)
1518 InvalidateBoxSelectRect ();
1519 box_select_rect = value;
1520 InvalidateBoxSelectRect ();
1524 void InvalidateBoxSelectRect ()
1526 if (BoxSelectRectangle.Size.IsEmpty)
1529 Rectangle edge = BoxSelectRectangle;
1535 edge.Y = BoxSelectRectangle.Bottom - 1;
1537 edge.Y = BoxSelectRectangle.Y - 1;
1539 edge.Height = BoxSelectRectangle.Height + 2;
1541 edge.X = BoxSelectRectangle.Right - 1;
1545 private Rectangle CalculateBoxSelectRectangle (Point pt)
1547 int left = Math.Min (box_select_start.X, pt.X);
1548 int right = Math.Max (box_select_start.X, pt.X);
1549 int top = Math.Min (box_select_start.Y, pt.Y);
1550 int bottom = Math.Max (box_select_start.Y, pt.Y);
1551 return Rectangle.FromLTRB (left, top, right, bottom);
1554 ArrayList BoxSelectedItems {
1556 ArrayList result = new ArrayList ();
1557 foreach (ListViewItem item in owner.Items) {
1558 Rectangle r = item.Bounds;
1560 r.Y += r.Height / 4;
1563 if (BoxSelectRectangle.IntersectsWith (r))
1570 private bool PerformBoxSelection (Point pt)
1572 if (box_select_mode == BoxSelect.None)
1575 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1577 ArrayList box_items = BoxSelectedItems;
1581 switch (box_select_mode) {
1583 case BoxSelect.Normal:
1587 case BoxSelect.Control:
1588 items = new ArrayList ();
1589 foreach (ListViewItem item in prev_selection)
1590 if (!box_items.Contains (item))
1592 foreach (ListViewItem item in box_items)
1593 if (!prev_selection.Contains (item))
1597 case BoxSelect.Shift:
1599 foreach (ListViewItem item in box_items)
1600 prev_selection.Remove (item);
1601 foreach (ListViewItem item in prev_selection)
1606 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1610 owner.SelectItems (items);
1616 private void ToggleCheckState (ListViewItem item)
1618 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1619 item.Checked = !item.Checked;
1620 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1622 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1623 owner.OnItemCheck (ice);
1626 private void ItemsMouseDown (object sender, MouseEventArgs me)
1628 if (owner.items.Count == 0) {
1629 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
1633 Point pt = new Point (me.X, me.Y);
1634 foreach (ListViewItem item in owner.items) {
1635 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1637 ToggleCheckState (item);
1638 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
1642 if (owner.View == View.Details && !owner.FullRowSelect) {
1643 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1644 clicked_item = item;
1648 if (item.Bounds.Contains (pt)) {
1649 clicked_item = item;
1656 if (clicked_item != null) {
1657 owner.SetFocusedItem (clicked_item);
1658 bool changed = !clicked_item.Selected;
1659 if (owner.MultiSelect)
1660 owner.UpdateMultiSelection (clicked_item.Index);
1662 clicked_item.Selected = true;
1665 owner.OnSelectedIndexChanged (EventArgs.Empty);
1667 // Raise double click if the item was clicked. On MS the
1668 // double click is only raised if you double click an item
1669 if (me.Clicks > 1) {
1670 owner.OnDoubleClick (EventArgs.Empty);
1671 if (owner.CheckBoxes)
1672 ToggleCheckState (clicked_item);
1673 } else if (me.Clicks == 1) {
1674 owner.OnClick (EventArgs.Empty);
1675 if (owner.LabelEdit && !changed)
1676 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
1679 if (owner.MultiSelect) {
1680 Keys mods = XplatUI.State.ModifierKeys;
1681 if ((mods & Keys.Shift) != 0)
1682 box_select_mode = BoxSelect.Shift;
1683 else if ((mods & Keys.Control) != 0)
1684 box_select_mode = BoxSelect.Control;
1686 box_select_mode = BoxSelect.Normal;
1687 box_select_start = pt;
1688 prev_selection = owner.SelectedItems.List;
1689 } else if (owner.SelectedItems.Count > 0) {
1690 owner.SelectedItems.Clear ();
1691 owner.OnSelectedIndexChanged (EventArgs.Empty);
1695 owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
1698 private void ItemsMouseMove (object sender, MouseEventArgs me)
1700 bool done = PerformBoxSelection (new Point (me.X, me.Y));
1702 if (!done && owner.HoverSelection && hover_processed) {
1704 Point pt = PointToClient (Control.MousePosition);
1705 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1706 if (item != null && !item.Selected) {
1707 hover_processed = false;
1708 XplatUI.ResetMouseHover (Handle);
1712 owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
1716 private void ItemsMouseHover (object sender, EventArgs e)
1718 owner.OnMouseHover(e);
1720 if (Capture || !owner.HoverSelection)
1723 hover_processed = true;
1724 Point pt = PointToClient (Control.MousePosition);
1725 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1730 item.Selected = true;
1731 owner.OnSelectedIndexChanged (new EventArgs ());
1734 private void ItemsMouseUp (object sender, MouseEventArgs me)
1737 if (owner.Items.Count == 0) {
1738 owner.OnMouseUp (owner.TranslateMouseEventArgs (me));
1742 Point pt = new Point (me.X, me.Y);
1744 Rectangle rect = Rectangle.Empty;
1745 if (clicked_item != null) {
1746 if (owner.view == View.Details && !owner.full_row_select)
1747 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1749 rect = clicked_item.Bounds;
1751 if (rect.Contains (pt)) {
1752 switch (owner.activation) {
1753 case ItemActivation.OneClick:
1754 owner.OnItemActivate (EventArgs.Empty);
1757 case ItemActivation.TwoClick:
1758 if (last_clicked_item == clicked_item) {
1759 owner.OnItemActivate (EventArgs.Empty);
1760 last_clicked_item = null;
1762 last_clicked_item = clicked_item;
1765 // DoubleClick activation is handled in another handler
1769 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1770 // Need this to clean up background clicks
1771 owner.SelectedItems.Clear ();
1772 owner.OnSelectedIndexChanged (EventArgs.Empty);
1775 clicked_item = null;
1776 box_select_start = Point.Empty;
1777 BoxSelectRectangle = Rectangle.Empty;
1778 prev_selection = null;
1779 box_select_mode = BoxSelect.None;
1781 owner.OnMouseUp (owner.TranslateMouseEventArgs (me));
1784 internal void LabelEditFinished (object sender, EventArgs e)
1786 EndEdit (edit_item);
1789 internal void BeginEdit (ListViewItem item)
1791 if (edit_item != null)
1792 EndEdit (edit_item);
1794 if (edit_text_box == null) {
1795 edit_text_box = new ListViewLabelEditTextBox ();
1796 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
1797 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
1798 edit_text_box.Visible = false;
1799 Controls.Add (edit_text_box);
1802 item.EnsureVisible();
1804 edit_text_box.Reset ();
1806 switch (owner.view) {
1808 case View.SmallIcon:
1810 edit_text_box.TextAlign = HorizontalAlignment.Left;
1811 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1812 SizeF sizef = DeviceContext.MeasureString (item.Text, item.Font);
1813 edit_text_box.Width = (int)sizef.Width + 4;
1814 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
1815 edit_text_box.WordWrap = false;
1816 edit_text_box.Multiline = false;
1818 case View.LargeIcon:
1819 edit_text_box.TextAlign = HorizontalAlignment.Center;
1820 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1821 sizef = DeviceContext.MeasureString (item.Text, item.Font);
1822 edit_text_box.Width = (int)sizef.Width + 4;
1823 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
1824 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
1825 edit_text_box.WordWrap = true;
1826 edit_text_box.Multiline = true;
1830 edit_text_box.Text = item.Text;
1831 edit_text_box.Font = item.Font;
1832 edit_text_box.Visible = true;
1833 edit_text_box.Focus ();
1834 edit_text_box.SelectAll ();
1836 edit_args = new LabelEditEventArgs (owner.Items.IndexOf(edit_item));
1837 owner.OnBeforeLabelEdit (edit_args);
1839 if (edit_args.CancelEdit)
1845 internal void EndEdit (ListViewItem item)
1847 if (edit_text_box != null && edit_text_box.Visible) {
1848 edit_text_box.Visible = false;
1851 if (edit_item != null && edit_item == item) {
1852 owner.OnAfterLabelEdit (edit_args);
1854 if (!edit_args.CancelEdit) {
1855 if (edit_args.Label != null)
1856 edit_item.Text = edit_args.Label;
1858 edit_item.Text = edit_text_box.Text;
1867 internal override void OnPaintInternal (PaintEventArgs pe)
1869 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1872 internal override void OnGotFocusInternal (EventArgs e)
1874 owner.Select (false, true);
1877 internal override void OnLostFocusInternal (EventArgs e)
1879 owner.Select (false, true);
1883 internal class ListViewLabelEditTextBox : TextBox
1888 int max_height = -1;
1889 int min_height = -1;
1891 int old_number_lines = 1;
1893 SizeF text_size_one_char;
1895 public ListViewLabelEditTextBox ()
1897 min_height = DefaultSize.Height;
1898 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1901 public int MaxWidth {
1903 if (value < min_width)
1904 max_width = min_width;
1910 public int MaxHeight {
1912 if (value < min_height)
1913 max_height = min_height;
1919 public new int Width {
1929 public override Font Font {
1935 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1939 protected override void OnTextChanged (EventArgs e)
1941 SizeF text_size = DeviceContext.MeasureString (Text, Font);
1943 int new_width = (int)text_size.Width + 8;
1946 ResizeTextBoxWidth (new_width);
1948 if (Width != max_width)
1949 ResizeTextBoxWidth (new_width);
1951 int number_lines = Lines.Length;
1953 if (number_lines != old_number_lines) {
1954 int new_height = number_lines * (int)text_size_one_char.Height + 4;
1955 old_number_lines = number_lines;
1957 ResizeTextBoxHeight (new_height);
1961 base.OnTextChanged (e);
1964 protected override bool IsInputKey (Keys key_data)
1966 if ((key_data & Keys.Alt) == 0) {
1967 switch (key_data & Keys.KeyCode) {
1972 return base.IsInputKey (key_data);
1975 protected override void OnKeyDown (KeyEventArgs e)
1977 if (e.KeyCode == Keys.Return && Visible) {
1978 this.Visible = false;
1979 OnEditingFinished (e);
1983 protected override void OnLostFocus (EventArgs e)
1986 OnEditingFinished (e);
1990 protected void OnEditingFinished (EventArgs e)
1992 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
1997 private void ResizeTextBoxWidth (int new_width)
1999 if (new_width > max_width)
2000 base.Width = max_width;
2002 if (new_width >= min_width)
2003 base.Width = new_width;
2005 base.Width = min_width;
2008 private void ResizeTextBoxHeight (int new_height)
2010 if (new_height > max_height)
2011 base.Height = max_height;
2013 if (new_height >= min_height)
2014 base.Height = new_height;
2016 base.Height = min_height;
2019 public void Reset ()
2026 old_number_lines = 1;
2028 Text = String.Empty;
2033 static object EditingFinishedEvent = new object ();
2034 public event EventHandler EditingFinished {
2035 add { Events.AddHandler (EditingFinishedEvent, value); }
2036 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
2040 internal override void OnPaintInternal (PaintEventArgs pe)
2045 CalculateScrollBars ();
2048 void FocusChanged (object o, EventArgs args)
2050 if (Items.Count == 0)
2053 if (FocusedItem == null)
2054 SetFocusedItem (Items [0]);
2056 item_control.Invalidate (FocusedItem.Bounds);
2059 private void ListView_MouseWheel (object sender, MouseEventArgs me)
2061 if (Items.Count == 0)
2064 int lines = me.Delta / 120;
2071 case View.SmallIcon:
2072 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
2074 case View.LargeIcon:
2075 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
2078 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
2083 private void ListView_SizeChanged (object sender, EventArgs e)
2085 CalculateListView (alignment);
2088 private void SetFocusedItem (ListViewItem item)
2090 if (focused_item != null)
2091 focused_item.Focused = false;
2094 item.Focused = true;
2096 focused_item = item;
2099 private void HorizontalScroller (object sender, EventArgs e)
2101 item_control.EndEdit (item_control.edit_item);
2103 // Avoid unnecessary flickering, when button is
2104 // kept pressed at the end
2105 if (h_marker != h_scroll.Value) {
2107 int pixels = h_marker - h_scroll.Value;
2109 h_marker = h_scroll.Value;
2110 if (header_control.Visible)
2111 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
2113 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
2117 private void VerticalScroller (object sender, EventArgs e)
2119 item_control.EndEdit (item_control.edit_item);
2121 // Avoid unnecessary flickering, when button is
2122 // kept pressed at the end
2123 if (v_marker != v_scroll.Value) {
2124 int pixels = v_marker - v_scroll.Value;
2125 Rectangle area = item_control.ClientRectangle;
2126 v_marker = v_scroll.Value;
2127 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
2130 #endregion // Internal Methods Properties
2132 #region Protected Methods
2133 protected override void CreateHandle ()
2135 base.CreateHandle ();
2136 for (int i = 0; i < SelectedItems.Count; i++)
2137 OnSelectedIndexChanged (EventArgs.Empty);
2140 protected override void Dispose (bool disposing)
2143 h_scroll.Dispose ();
2144 v_scroll.Dispose ();
2146 large_image_list = null;
2147 small_image_list = null;
2148 state_image_list = null;
2151 base.Dispose (disposing);
2154 protected override bool IsInputKey (Keys keyData)
2171 return base.IsInputKey (keyData);
2174 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
2176 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
2181 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
2183 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
2188 protected virtual void OnColumnClick (ColumnClickEventArgs e)
2190 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
2195 protected override void OnEnabledChanged (EventArgs e)
2197 base.OnEnabledChanged (e);
2200 protected override void OnFontChanged (EventArgs e)
2202 base.OnFontChanged (e);
2206 protected override void OnHandleCreated (EventArgs e)
2208 base.OnHandleCreated (e);
2212 protected override void OnHandleDestroyed (EventArgs e)
2214 base.OnHandleDestroyed (e);
2217 protected virtual void OnItemActivate (EventArgs e)
2219 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
2224 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
2226 EventHandler eh = (EventHandler)(Events [ItemCheckEvent]);
2231 protected virtual void OnItemDrag (ItemDragEventArgs e)
2233 EventHandler eh = (EventHandler)(Events [ItemDragEvent]);
2238 protected virtual void OnSelectedIndexChanged (EventArgs e)
2240 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
2245 protected override void OnSystemColorsChanged (EventArgs e)
2247 base.OnSystemColorsChanged (e);
2250 protected void RealizeProperties ()
2255 protected void UpdateExtendedStyles ()
2260 bool refocusing = false;
2262 protected override void WndProc (ref Message m)
2264 switch ((Msg)m.Msg) {
2265 case Msg.WM_KILLFOCUS:
2266 Control receiver = Control.FromHandle (m.WParam);
2267 if (receiver == item_control) {
2273 case Msg.WM_SETFOCUS:
2283 base.WndProc (ref m);
2285 #endregion // Protected Methods
2287 #region Public Instance Methods
2288 public void ArrangeIcons ()
2290 ArrangeIcons (this.alignment);
2293 public void ArrangeIcons (ListViewAlignment alignment)
2295 // Icons are arranged only if view is set to LargeIcon or SmallIcon
2296 if (view == View.LargeIcon || view == View.SmallIcon) {
2297 this.CalculateListView (alignment);
2298 // we have done the calculations already
2299 this.Redraw (false);
2303 public void BeginUpdate ()
2305 // flag to avoid painting
2309 public void Clear ()
2312 items.Clear (); // Redraw (true) called here
2315 public void EndUpdate ()
2317 // flag to avoid painting
2320 // probably, now we need a redraw with recalculations
2324 public void EnsureVisible (int index)
2326 if (index < 0 || index >= items.Count || scrollable == false)
2329 Rectangle view_rect = item_control.ClientRectangle;
2330 Rectangle bounds = items [index].Bounds;
2332 if (view_rect.Contains (bounds))
2335 if (View != View.Details) {
2336 if (bounds.Left < 0)
2337 h_scroll.Value += bounds.Left;
2338 else if (bounds.Right > view_rect.Right)
2339 h_scroll.Value += (bounds.Right - view_rect.Right);
2343 v_scroll.Value += bounds.Top;
2344 else if (bounds.Bottom > view_rect.Bottom)
2345 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
2348 public ListViewItem GetItemAt (int x, int y)
2350 foreach (ListViewItem item in items) {
2351 if (item.Bounds.Contains (x, y))
2357 public Rectangle GetItemRect (int index)
2359 return GetItemRect (index, ItemBoundsPortion.Entire);
2362 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
2364 if (index < 0 || index >= items.Count)
2365 throw new IndexOutOfRangeException ("index");
2367 return items [index].GetBounds (portion);
2375 // we need this overload to reuse the logic for sorting, while allowing
2376 // redrawing to be done by caller or have it done by this method when
2377 // sorting is really performed
2379 // ListViewItemCollection's Add and AddRange methods call this overload
2380 // with redraw set to false, as they take care of redrawing themselves
2381 // (they even want to redraw the listview if no sort is performed, as
2382 // an item was added), while ListView.Sort () only wants to redraw if
2383 // sorting was actually performed
2384 private void Sort (bool redraw)
2386 if (!IsHandleCreated || item_sorter == null) {
2390 items.Sort (item_sorter);
2395 public override string ToString ()
2397 int count = this.Items.Count;
2400 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
2402 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
2404 #endregion // Public Instance Methods
2409 class HeaderControl : Control {
2412 bool column_resize_active = false;
2413 ColumnHeader resize_column;
2414 ColumnHeader clicked_column;
2415 ColumnHeader drag_column;
2417 int drag_to_index = -1;
2419 public HeaderControl (ListView owner)
2422 MouseDown += new MouseEventHandler (HeaderMouseDown);
2423 MouseMove += new MouseEventHandler (HeaderMouseMove);
2424 MouseUp += new MouseEventHandler (HeaderMouseUp);
2427 private ColumnHeader ColumnAtX (int x)
2429 Point pt = new Point (x, 0);
2430 ColumnHeader result = null;
2431 foreach (ColumnHeader col in owner.Columns) {
2432 if (col.Rect.Contains (pt)) {
2440 private int GetReorderedIndex (ColumnHeader col)
2442 if (owner.reordered_column_indices == null)
2445 for (int i = 0; i < owner.Columns.Count; i++)
2446 if (owner.reordered_column_indices [i] == col.Index)
2448 throw new Exception ("Column index missing from reordered array");
2451 private void HeaderMouseDown (object sender, MouseEventArgs me)
2453 if (resize_column != null) {
2454 column_resize_active = true;
2459 clicked_column = ColumnAtX (me.X + owner.h_marker);
2461 if (clicked_column != null) {
2463 if (owner.AllowColumnReorder) {
2465 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2466 drag_column.Rect = clicked_column.Rect;
2467 drag_to_index = GetReorderedIndex (clicked_column);
2469 clicked_column.Pressed = true;
2470 Rectangle bounds = clicked_column.Rect;
2471 bounds.X -= owner.h_marker;
2472 Invalidate (bounds);
2479 column_resize_active = false;
2480 resize_column = null;
2482 Cursor = Cursors.Default;
2485 private void HeaderMouseMove (object sender, MouseEventArgs me)
2487 Point pt = new Point (me.X + owner.h_marker, me.Y);
2489 if (column_resize_active) {
2490 int width = pt.X - resize_column.X;
2494 if (!owner.CanProceedWithResize (resize_column, width)){
2498 resize_column.Width = width;
2502 resize_column = null;
2504 if (clicked_column != null) {
2505 if (owner.AllowColumnReorder) {
2508 r = drag_column.Rect;
2509 r.X = clicked_column.Rect.X + me.X - drag_x;
2510 drag_column.Rect = r;
2512 int x = me.X + owner.h_marker;
2513 ColumnHeader over = ColumnAtX (x);
2515 drag_to_index = owner.Columns.Count;
2516 else if (x < over.X + over.Width / 2)
2517 drag_to_index = GetReorderedIndex (over);
2519 drag_to_index = GetReorderedIndex (over) + 1;
2522 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2523 bool pressed = clicked_column.Pressed;
2524 clicked_column.Pressed = over == clicked_column;
2525 if (clicked_column.Pressed ^ pressed) {
2526 Rectangle bounds = clicked_column.Rect;
2527 bounds.X -= owner.h_marker;
2528 Invalidate (bounds);
2534 for (int i = 0; i < owner.Columns.Count; i++) {
2535 Rectangle zone = owner.Columns [i].Rect;
2536 zone.X = zone.Right - 5;
2538 if (zone.Contains (pt)) {
2539 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2541 resize_column = owner.Columns [i];
2546 if (resize_column == null)
2547 Cursor = Cursors.Default;
2549 Cursor = Cursors.VSplit;
2552 void HeaderMouseUp (object sender, MouseEventArgs me)
2556 if (column_resize_active) {
2557 int column_idx = resize_column.Index;
2559 owner.RaiseColumnWidthChanged (column_idx);
2563 if (clicked_column != null && clicked_column.Pressed) {
2564 clicked_column.Pressed = false;
2565 Rectangle bounds = clicked_column.Rect;
2566 bounds.X -= owner.h_marker;
2567 Invalidate (bounds);
2568 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2571 if (drag_column != null && owner.AllowColumnReorder) {
2573 if (drag_to_index > GetReorderedIndex (clicked_column))
2575 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2576 owner.ReorderColumn (clicked_column, drag_to_index);
2581 clicked_column = null;
2584 internal override void OnPaintInternal (PaintEventArgs pe)
2589 Theme theme = ThemeEngine.Current;
2590 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2592 if (drag_column == null)
2596 if (drag_to_index == owner.Columns.Count)
2597 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2599 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2600 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2603 protected override void WndProc (ref Message m)
2605 switch ((Msg)m.Msg) {
2606 case Msg.WM_SETFOCUS:
2610 base.WndProc (ref m);
2616 private class ItemComparer : IComparer {
2617 readonly SortOrder sort_order;
2619 public ItemComparer (SortOrder sortOrder)
2621 sort_order = sortOrder;
2624 public int Compare (object x, object y)
2626 ListViewItem item_x = x as ListViewItem;
2627 ListViewItem item_y = y as ListViewItem;
2628 if (sort_order == SortOrder.Ascending)
2629 return String.Compare (item_x.Text, item_y.Text);
2631 return String.Compare (item_y.Text, item_x.Text);
2635 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2637 private readonly ListView owner;
2639 #region Public Constructor
2640 public CheckedIndexCollection (ListView owner)
2644 #endregion // Public Constructor
2646 #region Public Properties
2649 get { return owner.CheckedItems.Count; }
2652 public bool IsReadOnly {
2653 get { return true; }
2656 public int this [int index] {
2658 int [] indices = GetIndices ();
2659 if (index < 0 || index >= indices.Length)
2660 throw new ArgumentOutOfRangeException ("index");
2661 return indices [index];
2665 bool ICollection.IsSynchronized {
2666 get { return false; }
2669 object ICollection.SyncRoot {
2670 get { return this; }
2673 bool IList.IsFixedSize {
2674 get { return true; }
2677 object IList.this [int index] {
2678 get { return this [index]; }
2679 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2681 #endregion // Public Properties
2683 #region Public Methods
2684 public bool Contains (int checkedIndex)
2686 int [] indices = GetIndices ();
2687 for (int i = 0; i < indices.Length; i++) {
2688 if (indices [i] == checkedIndex)
2694 public IEnumerator GetEnumerator ()
2696 int [] indices = GetIndices ();
2697 return indices.GetEnumerator ();
2700 void ICollection.CopyTo (Array dest, int index)
2702 int [] indices = GetIndices ();
2703 Array.Copy (indices, 0, dest, index, indices.Length);
2706 int IList.Add (object value)
2708 throw new NotSupportedException ("Add operation is not supported.");
2713 throw new NotSupportedException ("Clear operation is not supported.");
2716 bool IList.Contains (object checkedIndex)
2718 if (!(checkedIndex is int))
2720 return Contains ((int) checkedIndex);
2723 int IList.IndexOf (object checkedIndex)
2725 if (!(checkedIndex is int))
2727 return IndexOf ((int) checkedIndex);
2730 void IList.Insert (int index, object value)
2732 throw new NotSupportedException ("Insert operation is not supported.");
2735 void IList.Remove (object value)
2737 throw new NotSupportedException ("Remove operation is not supported.");
2740 void IList.RemoveAt (int index)
2742 throw new NotSupportedException ("RemoveAt operation is not supported.");
2745 public int IndexOf (int checkedIndex)
2747 int [] indices = GetIndices ();
2748 for (int i = 0; i < indices.Length; i++) {
2749 if (indices [i] == checkedIndex)
2754 #endregion // Public Methods
2756 private int [] GetIndices ()
2758 ArrayList checked_items = owner.CheckedItems.List;
2759 int [] indices = new int [checked_items.Count];
2760 for (int i = 0; i < checked_items.Count; i++) {
2761 ListViewItem item = (ListViewItem) checked_items [i];
2762 indices [i] = item.Index;
2766 } // CheckedIndexCollection
2768 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2770 private readonly ListView owner;
2771 private ArrayList list;
2773 #region Public Constructor
2774 public CheckedListViewItemCollection (ListView owner)
2777 this.owner.Items.Changed += new CollectionChangedHandler (
2778 ItemsCollection_Changed);
2780 #endregion // Public Constructor
2782 #region Public Properties
2786 if (!owner.CheckBoxes)
2792 public bool IsReadOnly {
2793 get { return true; }
2796 public ListViewItem this [int index] {
2798 ArrayList checked_items = List;
2799 if (index < 0 || index >= checked_items.Count)
2800 throw new ArgumentOutOfRangeException ("index");
2801 return (ListViewItem) checked_items [index];
2806 public virtual ListViewItem this [string key] {
2808 int idx = IndexOfKey (key);
2809 return idx == -1 ? null : (ListViewItem) List [idx];
2814 bool ICollection.IsSynchronized {
2815 get { return false; }
2818 object ICollection.SyncRoot {
2819 get { return this; }
2822 bool IList.IsFixedSize {
2823 get { return true; }
2826 object IList.this [int index] {
2827 get { return this [index]; }
2828 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2830 #endregion // Public Properties
2832 #region Public Methods
2833 public bool Contains (ListViewItem item)
2835 if (!owner.CheckBoxes)
2837 return List.Contains (item);
2841 public virtual bool ContainsKey (string key)
2843 return IndexOfKey (key) != -1;
2847 public void CopyTo (Array dest, int index)
2849 if (!owner.CheckBoxes)
2851 List.CopyTo (dest, index);
2854 public IEnumerator GetEnumerator ()
2856 if (!owner.CheckBoxes)
2857 return (new ListViewItem [0]).GetEnumerator ();
2858 return List.GetEnumerator ();
2861 int IList.Add (object value)
2863 throw new NotSupportedException ("Add operation is not supported.");
2868 throw new NotSupportedException ("Clear operation is not supported.");
2871 bool IList.Contains (object item)
2873 if (!(item is ListViewItem))
2875 return Contains ((ListViewItem) item);
2878 int IList.IndexOf (object item)
2880 if (!(item is ListViewItem))
2882 return IndexOf ((ListViewItem) item);
2885 void IList.Insert (int index, object value)
2887 throw new NotSupportedException ("Insert operation is not supported.");
2890 void IList.Remove (object value)
2892 throw new NotSupportedException ("Remove operation is not supported.");
2895 void IList.RemoveAt (int index)
2897 throw new NotSupportedException ("RemoveAt operation is not supported.");
2900 public int IndexOf (ListViewItem item)
2902 if (!owner.CheckBoxes)
2904 return List.IndexOf (item);
2908 public virtual int IndexOfKey (string key)
2910 if (key == null || key.Length == 0)
2913 ArrayList checked_items = List;
2914 for (int i = 0; i < checked_items.Count; i++) {
2915 ListViewItem item = (ListViewItem) checked_items [i];
2916 if (String.Compare (key, item.Name, true) == 0)
2923 #endregion // Public Methods
2925 internal ArrayList List {
2928 list = new ArrayList ();
2929 foreach (ListViewItem item in owner.Items) {
2938 internal void Reset ()
2940 // force re-population of list
2944 private void ItemsCollection_Changed ()
2948 } // CheckedListViewItemCollection
2950 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2952 internal ArrayList list;
2953 private ListView owner;
2955 #region Public Constructor
2956 public ColumnHeaderCollection (ListView owner)
2958 list = new ArrayList ();
2961 #endregion // Public Constructor
2963 #region Public Properties
2966 get { return list.Count; }
2969 public bool IsReadOnly {
2970 get { return false; }
2973 public virtual ColumnHeader this [int index] {
2975 if (index < 0 || index >= list.Count)
2976 throw new ArgumentOutOfRangeException ("index");
2977 return (ColumnHeader) list [index];
2982 public virtual ColumnHeader this [string key] {
2984 int idx = IndexOfKey (key);
2988 return (ColumnHeader) list [idx];
2993 bool ICollection.IsSynchronized {
2994 get { return true; }
2997 object ICollection.SyncRoot {
2998 get { return this; }
3001 bool IList.IsFixedSize {
3002 get { return list.IsFixedSize; }
3005 object IList.this [int index] {
3006 get { return this [index]; }
3007 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3009 #endregion // Public Properties
3011 #region Public Methods
3012 public virtual int Add (ColumnHeader value)
3015 value.SetListView (this.owner);
3016 idx = list.Add (value);
3017 if (owner.IsHandleCreated) {
3018 owner.Redraw (true);
3023 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
3025 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
3026 this.Add (colHeader);
3031 public virtual ColumnHeader Add (string text)
3033 return Add (String.Empty, text);
3036 public virtual ColumnHeader Add (string text, int iwidth)
3038 return Add (String.Empty, text, iwidth);
3041 public virtual ColumnHeader Add (string key, string text)
3043 ColumnHeader colHeader = new ColumnHeader ();
3044 colHeader.Name = key;
3045 colHeader.Text = text;
3050 public virtual ColumnHeader Add (string key, string text, int iwidth)
3052 return Add (key, text, iwidth, HorizontalAlignment.Left, -1);
3055 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, int imageIndex)
3057 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
3058 colHeader.ImageIndex = imageIndex;
3063 public virtual ColumnHeader Add (string key, string text, int iwidth, HorizontalAlignment textAlign, string imageKey)
3065 ColumnHeader colHeader = new ColumnHeader (key, text, iwidth, textAlign);
3066 colHeader.ImageKey = imageKey;
3072 public virtual void AddRange (ColumnHeader [] values)
3074 foreach (ColumnHeader colHeader in values) {
3075 colHeader.SetListView (this.owner);
3079 owner.Redraw (true);
3082 public virtual void Clear ()
3085 owner.Redraw (true);
3088 public bool Contains (ColumnHeader value)
3090 return list.Contains (value);
3094 public virtual bool ContainsKey (string key)
3096 return IndexOfKey (key) != -1;
3100 public IEnumerator GetEnumerator ()
3102 return list.GetEnumerator ();
3105 void ICollection.CopyTo (Array dest, int index)
3107 list.CopyTo (dest, index);
3110 int IList.Add (object value)
3112 if (! (value is ColumnHeader)) {
3113 throw new ArgumentException ("Not of type ColumnHeader", "value");
3116 return this.Add ((ColumnHeader) value);
3119 bool IList.Contains (object value)
3121 if (! (value is ColumnHeader)) {
3122 throw new ArgumentException ("Not of type ColumnHeader", "value");
3125 return this.Contains ((ColumnHeader) value);
3128 int IList.IndexOf (object value)
3130 if (! (value is ColumnHeader)) {
3131 throw new ArgumentException ("Not of type ColumnHeader", "value");
3134 return this.IndexOf ((ColumnHeader) value);
3137 void IList.Insert (int index, object value)
3139 if (! (value is ColumnHeader)) {
3140 throw new ArgumentException ("Not of type ColumnHeader", "value");
3143 this.Insert (index, (ColumnHeader) value);
3146 void IList.Remove (object value)
3148 if (! (value is ColumnHeader)) {
3149 throw new ArgumentException ("Not of type ColumnHeader", "value");
3152 this.Remove ((ColumnHeader) value);
3155 public int IndexOf (ColumnHeader value)
3157 return list.IndexOf (value);
3161 public virtual int IndexOfKey (string key)
3163 if (key == null || key.Length == 0)
3166 for (int i = 0; i < list.Count; i++) {
3167 ColumnHeader col = (ColumnHeader) list [i];
3168 if (String.Compare (key, col.Name, true) == 0)
3176 public void Insert (int index, ColumnHeader value)
3178 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
3179 // but it's really only greater.
3180 if (index < 0 || index > list.Count)
3181 throw new ArgumentOutOfRangeException ("index");
3183 value.SetListView (owner);
3184 list.Insert (index, value);
3185 owner.Redraw (true);
3189 public void Insert (int index, string text)
3191 Insert (index, String.Empty, text);
3194 public void Insert (int index, string text, int width)
3196 Insert (index, String.Empty, text, width);
3199 public void Insert (int index, string key, string text)
3201 ColumnHeader colHeader = new ColumnHeader ();
3202 colHeader.Name = key;
3203 colHeader.Text = text;
3204 Insert (index, colHeader);
3207 public void Insert (int index, string key, string text, int width)
3209 ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
3210 Insert (index, colHeader);
3213 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
3215 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
3216 colHeader.ImageIndex = imageIndex;
3217 Insert (index, colHeader);
3220 public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
3222 ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
3223 colHeader.ImageKey = imageKey;
3224 Insert (index, colHeader);
3228 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
3230 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
3231 this.Insert (index, colHeader);
3234 public virtual void Remove (ColumnHeader column)
3236 // TODO: Update Column internal index ?
3237 list.Remove (column);
3238 owner.Redraw (true);
3242 public virtual void RemoveByKey (string key)
3244 int idx = IndexOfKey (key);
3250 public virtual void RemoveAt (int index)
3252 if (index < 0 || index >= list.Count)
3253 throw new ArgumentOutOfRangeException ("index");
3255 // TODO: Update Column internal index ?
3256 list.RemoveAt (index);
3257 owner.Redraw (true);
3259 #endregion // Public Methods
3262 } // ColumnHeaderCollection
3264 public class ListViewItemCollection : IList, ICollection, IEnumerable
3266 private readonly ArrayList list;
3267 private readonly ListView owner;
3269 #region Public Constructor
3270 public ListViewItemCollection (ListView owner)
3272 list = new ArrayList ();
3275 #endregion // Public Constructor
3277 #region Public Properties
3280 get { return list.Count; }
3283 public bool IsReadOnly {
3284 get { return false; }
3287 public virtual ListViewItem this [int displayIndex] {
3289 if (displayIndex < 0 || displayIndex >= list.Count)
3290 throw new ArgumentOutOfRangeException ("displayIndex");
3291 return (ListViewItem) list [displayIndex];
3295 if (displayIndex < 0 || displayIndex >= list.Count)
3296 throw new ArgumentOutOfRangeException ("displayIndex");
3298 if (list.Contains (value))
3299 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
3301 if (value.ListView != null && value.ListView != owner)
3302 throw new ArgumentException ("Cannot add or insert the item '" + value.Text + "' in more than one place. You must first remove it from its current location or clone it.", "value");
3304 value.Owner = owner;
3305 list [displayIndex] = value;
3308 owner.Redraw (true);
3313 public virtual ListViewItem this [string key] {
3315 int idx = IndexOfKey (key);
3319 return (ListViewItem) list [idx];
3324 bool ICollection.IsSynchronized {
3325 get { return true; }
3328 object ICollection.SyncRoot {
3329 get { return this; }
3332 bool IList.IsFixedSize {
3333 get { return list.IsFixedSize; }
3336 object IList.this [int index] {
3337 get { return this [index]; }
3339 if (value is ListViewItem)
3340 this [index] = (ListViewItem) value;
3342 this [index] = new ListViewItem (value.ToString ());
3346 #endregion // Public Properties
3348 #region Public Methods
3349 public virtual ListViewItem Add (ListViewItem value)
3351 if (list.Contains (value))
3352 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
3354 if (value.ListView != null && value.ListView != owner)
3355 throw new ArgumentException ("Cannot add or insert the item '" + value.Text + "' in more than one place. You must first remove it from its current location or clone it.", "value");
3357 value.Owner = owner;
3360 if (this.owner != null)
3364 owner.Redraw (true);
3370 public virtual ListViewItem Add (string text)
3372 ListViewItem item = new ListViewItem (text);
3373 return this.Add (item);
3376 public virtual ListViewItem Add (string text, int imageIndex)
3378 ListViewItem item = new ListViewItem (text, imageIndex);
3379 return this.Add (item);
3383 public virtual ListViewItem Add (string text, string imageKey)
3385 ListViewItem item = new ListViewItem (text, imageKey);
3386 return this.Add (item);
3389 public virtual ListViewItem Add (string key, string text, int imageIndex)
3391 ListViewItem item = new ListViewItem (text, imageIndex);
3393 return this.Add (item);
3396 public virtual ListViewItem Add (string key, string text, string imageKey)
3398 ListViewItem item = new ListViewItem (text, imageKey);
3400 return this.Add (item);
3404 public void AddRange (ListViewItem [] values)
3407 throw new ArgumentNullException ("Argument cannot be null!", "values");
3409 foreach (ListViewItem item in values) {
3415 public void AddRange (ListViewItemCollection items)
3418 throw new ArgumentNullException ("Argument cannot be null!", "items");
3420 ListViewItem[] itemArray = new ListViewItem[items.Count];
3421 items.CopyTo (itemArray,0);
3422 this.AddRange (itemArray);
3426 public virtual void Clear ()
3428 owner.SetFocusedItem (null);
3429 owner.h_scroll.Value = owner.v_scroll.Value = 0;
3432 owner.Redraw (true);
3435 public bool Contains (ListViewItem item)
3437 return list.Contains (item);
3441 public virtual bool ContainsKey (string key)
3443 return IndexOfKey (key) != -1;
3447 public void CopyTo (Array dest, int index)
3449 list.CopyTo (dest, index);
3453 public ListViewItem [] Find (string key, bool searchAllSubitems)
3456 return new ListViewItem [0];
3458 List<ListViewItem> temp_list = new List<ListViewItem> ();
3460 for (int i = 0; i < list.Count; i++) {
3461 ListViewItem lvi = (ListViewItem) list [i];
3462 if (String.Compare (key, lvi.Name, true) == 0)
3463 temp_list.Add (lvi);
3466 ListViewItem [] retval = new ListViewItem [temp_list.Count];
3467 temp_list.CopyTo (retval);
3473 public IEnumerator GetEnumerator ()
3475 return list.GetEnumerator ();
3478 int IList.Add (object item)
3483 if (item is ListViewItem) {
3484 li = (ListViewItem) item;
3485 if (list.Contains (li))
3486 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3488 if (li.ListView != null && li.ListView != owner)
3489 throw new ArgumentException ("Cannot add or insert the item '" + li.Text + "' in more than one place. You must first remove it from its current location or clone it.", "item");
3492 li = new ListViewItem (item.ToString ());
3495 result = list.Add (li);
3497 owner.Redraw (true);
3502 bool IList.Contains (object item)
3504 return list.Contains (item);
3507 int IList.IndexOf (object item)
3509 return list.IndexOf (item);
3512 void IList.Insert (int index, object item)
3514 if (item is ListViewItem)
3515 this.Insert (index, (ListViewItem) item);
3517 this.Insert (index, item.ToString ());
3520 void IList.Remove (object item)
3522 Remove ((ListViewItem) item);
3525 public int IndexOf (ListViewItem item)
3527 return list.IndexOf (item);
3531 public virtual int IndexOfKey (string key)
3533 if (key == null || key.Length == 0)
3536 for (int i = 0; i < list.Count; i++) {
3537 ListViewItem lvi = (ListViewItem) list [i];
3538 if (String.Compare (key, lvi.Name, true) == 0)
3546 public ListViewItem Insert (int index, ListViewItem item)
3548 if (index < 0 || index > list.Count)
3549 throw new ArgumentOutOfRangeException ("index");
3551 if (list.Contains (item))
3552 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3554 if (item.ListView != null && item.ListView != owner)
3555 throw new ArgumentException ("Cannot add or insert the item '" + item.Text + "' in more than one place. You must first remove it from its current location or clone it.", "item");
3558 list.Insert (index, item);
3560 owner.Redraw (true);
3564 public ListViewItem Insert (int index, string text)
3566 return this.Insert (index, new ListViewItem (text));
3569 public ListViewItem Insert (int index, string text, int imageIndex)
3571 return this.Insert (index, new ListViewItem (text, imageIndex));
3575 public ListViewItem Insert (int index, string key, string text, int imageIndex)
3577 ListViewItem lvi = new ListViewItem (text, imageIndex);
3579 return Insert (index, lvi);
3583 public virtual void Remove (ListViewItem item)
3585 if (!list.Contains (item))
3588 bool selection_changed = owner.SelectedItems.Contains (item);
3591 owner.Redraw (true);
3592 if (selection_changed)
3593 owner.OnSelectedIndexChanged (EventArgs.Empty);
3596 public virtual void RemoveAt (int index)
3598 if (index < 0 || index >= Count)
3599 throw new ArgumentOutOfRangeException ("index");
3600 bool selection_changed = owner.SelectedIndices.Contains (index);
3601 list.RemoveAt (index);
3603 owner.Redraw (false);
3604 if (selection_changed)
3605 owner.OnSelectedIndexChanged (EventArgs.Empty);
3609 public virtual void RemoveByKey (string key)
3611 int idx = IndexOfKey (key);
3617 #endregion // Public Methods
3619 internal event CollectionChangedHandler Changed;
3621 internal void Sort (IComparer comparer)
3623 list.Sort (comparer);
3627 internal void OnChange ()
3629 if (Changed != null)
3632 } // ListViewItemCollection
3634 public class SelectedIndexCollection : IList, ICollection, IEnumerable
3636 private readonly ListView owner;
3638 #region Public Constructor
3639 public SelectedIndexCollection (ListView owner)
3643 #endregion // Public Constructor
3645 #region Public Properties
3649 return owner.SelectedItems.Count;
3653 public bool IsReadOnly {
3663 public int this [int index] {
3665 int [] indices = GetIndices ();
3666 if (index < 0 || index >= indices.Length)
3667 throw new ArgumentOutOfRangeException ("index");
3668 return indices [index];
3672 bool ICollection.IsSynchronized {
3673 get { return false; }
3676 object ICollection.SyncRoot {
3677 get { return this; }
3680 bool IList.IsFixedSize {
3690 object IList.this [int index] {
3691 get { return this [index]; }
3692 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3694 #endregion // Public Properties
3696 #region Public Methods
3698 public int Add (int itemIndex)
3700 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
3701 throw new ArgumentOutOfRangeException ("index");
3703 owner.Items [itemIndex].Selected = true;
3704 if (!owner.IsHandleCreated)
3707 return owner.SelectedItems.Count;
3710 public void Clear ()
3712 owner.SelectedItems.Clear ();
3715 public bool Contains (int selectedIndex)
3717 int [] indices = GetIndices ();
3718 for (int i = 0; i < indices.Length; i++) {
3719 if (indices [i] == selectedIndex)
3725 public void CopyTo (Array dest, int index)
3727 int [] indices = GetIndices ();
3728 Array.Copy (indices, 0, dest, index, indices.Length);
3731 public IEnumerator GetEnumerator ()
3733 int [] indices = GetIndices ();
3734 return indices.GetEnumerator ();
3737 int IList.Add (object value)
3739 throw new NotSupportedException ("Add operation is not supported.");
3744 throw new NotSupportedException ("Clear operation is not supported.");
3747 bool IList.Contains (object selectedIndex)
3749 if (!(selectedIndex is int))
3751 return Contains ((int) selectedIndex);
3754 int IList.IndexOf (object selectedIndex)
3756 if (!(selectedIndex is int))
3758 return IndexOf ((int) selectedIndex);
3761 void IList.Insert (int index, object value)
3763 throw new NotSupportedException ("Insert operation is not supported.");
3766 void IList.Remove (object value)
3768 throw new NotSupportedException ("Remove operation is not supported.");
3771 void IList.RemoveAt (int index)
3773 throw new NotSupportedException ("RemoveAt operation is not supported.");
3776 public int IndexOf (int selectedIndex)
3778 int [] indices = GetIndices ();
3779 for (int i = 0; i < indices.Length; i++) {
3780 if (indices [i] == selectedIndex)
3787 public void Remove (int itemIndex)
3789 if (itemIndex < 0 || itemIndex >= owner.Items.Count)
3790 throw new ArgumentOutOfRangeException ("itemIndex");
3792 owner.Items [itemIndex].Selected = false;
3795 #endregion // Public Methods
3797 private int [] GetIndices ()
3799 ArrayList selected_items = owner.SelectedItems.List;
3800 int [] indices = new int [selected_items.Count];
3801 for (int i = 0; i < selected_items.Count; i++) {
3802 ListViewItem item = (ListViewItem) selected_items [i];
3803 indices [i] = item.Index;
3807 } // SelectedIndexCollection
3809 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
3811 private readonly ListView owner;
3812 private ArrayList list;
3814 #region Public Constructor
3815 public SelectedListViewItemCollection (ListView owner)
3818 this.owner.Items.Changed += new CollectionChangedHandler (
3819 ItemsCollection_Changed);
3821 #endregion // Public Constructor
3823 #region Public Properties
3827 if (!owner.IsHandleCreated)
3833 public bool IsReadOnly {
3834 get { return true; }
3837 public ListViewItem this [int index] {
3839 ArrayList selected_items = List;
3840 if (!owner.IsHandleCreated || index < 0 || index >= selected_items.Count)
3841 throw new ArgumentOutOfRangeException ("index");
3842 return (ListViewItem) selected_items [index];
3847 public virtual ListViewItem this [string key] {
3849 int idx = IndexOfKey (key);
3853 return (ListViewItem) List [idx];
3858 bool ICollection.IsSynchronized {
3859 get { return false; }
3862 object ICollection.SyncRoot {
3863 get { return this; }
3866 bool IList.IsFixedSize {
3867 get { return true; }
3870 object IList.this [int index] {
3871 get { return this [index]; }
3872 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3874 #endregion // Public Properties
3876 #region Public Methods
3877 public void Clear ()
3879 if (!owner.IsHandleCreated)
3882 foreach (ListViewItem item in List)
3883 item.Selected = false;
3886 public bool Contains (ListViewItem item)
3888 if (!owner.IsHandleCreated)
3890 return List.Contains (item);
3894 public virtual bool ContainsKey (string key)
3896 return IndexOfKey (key) != -1;
3900 public void CopyTo (Array dest, int index)
3902 if (!owner.IsHandleCreated)
3904 List.CopyTo (dest, index);
3907 public IEnumerator GetEnumerator ()
3909 if (!owner.IsHandleCreated)
3910 return (new ListViewItem [0]).GetEnumerator ();
3911 return List.GetEnumerator ();
3914 int IList.Add (object value)
3916 throw new NotSupportedException ("Add operation is not supported.");
3919 bool IList.Contains (object item)
3921 if (!(item is ListViewItem))
3923 return Contains ((ListViewItem) item);
3926 int IList.IndexOf (object item)
3928 if (!(item is ListViewItem))
3930 return IndexOf ((ListViewItem) item);
3933 void IList.Insert (int index, object value)
3935 throw new NotSupportedException ("Insert operation is not supported.");
3938 void IList.Remove (object value)
3940 throw new NotSupportedException ("Remove operation is not supported.");
3943 void IList.RemoveAt (int index)
3945 throw new NotSupportedException ("RemoveAt operation is not supported.");
3948 public int IndexOf (ListViewItem item)
3950 if (!owner.IsHandleCreated)
3952 return List.IndexOf (item);
3956 public virtual int IndexOfKey (string key)
3958 if (!owner.IsHandleCreated || key == null || key.Length == 0)
3961 ArrayList selected_items = List;
3962 for (int i = 0; i < selected_items.Count; i++) {
3963 ListViewItem item = (ListViewItem) selected_items [i];
3964 if (String.Compare (item.Name, key, true) == 0)
3971 #endregion // Public Methods
3973 internal ArrayList List {
3976 list = new ArrayList ();
3977 foreach (ListViewItem item in owner.Items) {
3986 internal void Reset ()
3988 // force re-population of list
3992 private void ItemsCollection_Changed ()
3996 } // SelectedListViewItemCollection
3998 internal delegate void CollectionChangedHandler ();
4000 #endregion // Subclasses
4002 protected override void OnResize (EventArgs e)
4007 protected override void OnMouseLeave (EventArgs e)
4009 base.OnMouseLeave (e);
4013 // ColumnReorder event
4015 static object ColumnReorderedEvent = new object ();
4016 public event ColumnReorderedEventHandler ColumnReordered {
4017 add { Events.AddHandler (ColumnReorderedEvent, value); }
4018 remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
4021 protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
4023 ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
4030 // ColumnWidthChanged
4032 static object ColumnWidthChangedEvent = new object ();
4033 public event ColumnWidthChangedEventHandler ColumnWidthChanged {
4034 add { Events.AddHandler (ColumnWidthChangedEvent, value); }
4035 remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
4038 protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
4040 ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
4045 void RaiseColumnWidthChanged (int resize_column)
4047 ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
4049 OnColumnWidthChanged (n);
4053 // ColumnWidthChanging
4055 static object ColumnWidthChangingEvent = new object ();
4056 public event ColumnWidthChangingEventHandler ColumnWidthChanging {
4057 add { Events.AddHandler (ColumnWidthChangingEvent, value); }
4058 remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
4061 protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
4063 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
4069 // 2.0 profile based implementation
4071 bool CanProceedWithResize (ColumnHeader col, int width)
4073 ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
4077 ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
4078 cwceh (this, changing);
4079 return !changing.Cancel;
4083 // 1.0 profile based implementation
4085 bool CanProceedWithResize (ColumnHeader col, int width)
4090 void RaiseColumnWidthChanged (int resize_column)