2006-12-05 Chris Toshok <toshok@ximian.com>
[mcs.git] / class / Managed.Windows.Forms / System.Windows.Forms / ListView.cs
blob6d9eceb6e6b951c1d8cafcb7df405ce49e551d32
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:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
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)
22 // Authors:
23 // Ravindra Kumar (rkumar@novell.com)
24 // Jordi Mas i Hernandez, jordi@ximian.com
25 // Mike Kestner (mkestner@novell.com)
27 // TODO:
28 // - Feedback for item activation, change in cursor types as mouse moves.
29 // - LabelEdit
30 // - Drag and drop
33 // NOT COMPLETE
36 using System.Collections;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Drawing;
40 using System.Runtime.InteropServices;
41 using System.Globalization;
43 namespace System.Windows.Forms
45 [DefaultEvent ("SelectedIndexChanged")]
46 [DefaultProperty ("Items")]
47 [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
48 public class ListView : Control
50 private ItemActivation activation = ItemActivation.Standard;
51 private ListViewAlignment alignment = ListViewAlignment.Top;
52 private bool allow_column_reorder = false;
53 private bool auto_arrange = true;
54 private bool check_boxes = false;
55 private readonly CheckedIndexCollection checked_indices;
56 private readonly CheckedListViewItemCollection checked_items;
57 private readonly ColumnHeaderCollection columns;
58 internal ListViewItem focused_item;
59 private bool full_row_select = false;
60 private bool grid_lines = false;
61 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
62 private bool hide_selection = true;
63 private bool hover_selection = false;
64 private IComparer item_sorter;
65 private readonly ListViewItemCollection items;
66 private bool label_edit = false;
67 private bool label_wrap = true;
68 private bool multiselect = true;
69 private bool scrollable = true;
70 private readonly SelectedIndexCollection selected_indices;
71 private readonly SelectedListViewItemCollection selected_items;
72 private SortOrder sort_order = SortOrder.None;
73 private ImageList state_image_list;
74 private bool updating = false;
75 private View view = View.LargeIcon;
76 private int layout_wd; // We might draw more than our client area
77 private int layout_ht; // therefore we need to have these two.
78 //private TextBox editor; // Used for editing an item text
79 HeaderControl header_control;
80 internal ItemControl item_control;
81 internal ScrollBar h_scroll; // used for scrolling horizontally
82 internal ScrollBar v_scroll; // used for scrolling vertically
83 internal int h_marker; // Position markers for scrolling
84 internal int v_marker;
85 private int keysearch_tickcnt;
86 private string keysearch_text;
87 static private readonly int keysearch_keydelay = 1000;
88 private int[] reordered_column_indices;
90 // internal variables
91 internal ImageList large_image_list;
92 internal ImageList small_image_list;
93 internal Size text_size = Size.Empty;
95 #region Events
96 static object AfterLabelEditEvent = new object ();
97 static object BeforeLabelEditEvent = new object ();
98 static object ColumnClickEvent = new object ();
99 static object ItemActivateEvent = new object ();
100 static object ItemCheckEvent = new object ();
101 static object ItemDragEvent = new object ();
102 static object SelectedIndexChangedEvent = new object ();
104 public event LabelEditEventHandler AfterLabelEdit {
105 add { Events.AddHandler (AfterLabelEditEvent, value); }
106 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
109 [Browsable (false)]
110 [EditorBrowsable (EditorBrowsableState.Never)]
111 public new event EventHandler BackgroundImageChanged {
112 add { base.BackgroundImageChanged += value; }
113 remove { base.BackgroundImageChanged -= value; }
116 public event LabelEditEventHandler BeforeLabelEdit {
117 add { Events.AddHandler (BeforeLabelEditEvent, value); }
118 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
121 public event ColumnClickEventHandler ColumnClick {
122 add { Events.AddHandler (ColumnClickEvent, value); }
123 remove { Events.RemoveHandler (ColumnClickEvent, value); }
126 public event EventHandler ItemActivate {
127 add { Events.AddHandler (ItemActivateEvent, value); }
128 remove { Events.RemoveHandler (ItemActivateEvent, value); }
131 public event ItemCheckEventHandler ItemCheck {
132 add { Events.AddHandler (ItemCheckEvent, value); }
133 remove { Events.RemoveHandler (ItemCheckEvent, value); }
136 public event ItemDragEventHandler ItemDrag {
137 add { Events.AddHandler (ItemDragEvent, value); }
138 remove { Events.RemoveHandler (ItemDragEvent, value); }
141 [Browsable (false)]
142 [EditorBrowsable (EditorBrowsableState.Never)]
143 public new event PaintEventHandler Paint {
144 add { base.Paint += value; }
145 remove { base.Paint -= value; }
148 public event EventHandler SelectedIndexChanged {
149 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
150 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
153 [Browsable (false)]
154 [EditorBrowsable (EditorBrowsableState.Never)]
155 public new event EventHandler TextChanged {
156 add { base.TextChanged += value; }
157 remove { base.TextChanged -= value; }
160 #endregion // Events
162 #region Public Constructors
163 public ListView ()
165 background_color = ThemeEngine.Current.ColorWindow;
166 items = new ListViewItemCollection (this);
167 checked_indices = new CheckedIndexCollection (this);
168 checked_items = new CheckedListViewItemCollection (this);
169 columns = new ColumnHeaderCollection (this);
170 foreground_color = SystemColors.WindowText;
171 selected_indices = new SelectedIndexCollection (this);
172 selected_items = new SelectedListViewItemCollection (this);
174 border_style = BorderStyle.Fixed3D;
176 header_control = new HeaderControl (this);
177 header_control.Visible = false;
178 Controls.AddImplicit (header_control);
180 item_control = new ItemControl (this);
181 Controls.AddImplicit (item_control);
183 h_scroll = new ImplicitHScrollBar ();
184 Controls.AddImplicit (this.h_scroll);
186 v_scroll = new ImplicitVScrollBar ();
187 Controls.AddImplicit (this.v_scroll);
189 h_marker = v_marker = 0;
190 keysearch_tickcnt = 0;
192 // scroll bars are disabled initially
193 h_scroll.Visible = false;
194 h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
195 v_scroll.Visible = false;
196 v_scroll.ValueChanged += new EventHandler(VerticalScroller);
198 // event handlers
199 base.KeyDown += new KeyEventHandler(ListView_KeyDown);
200 SizeChanged += new EventHandler (ListView_SizeChanged);
201 GotFocus += new EventHandler (FocusChanged);
202 LostFocus += new EventHandler (FocusChanged);
203 MouseWheel += new MouseEventHandler(ListView_MouseWheel);
205 this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
206 #if NET_2_0
207 | ControlStyles.UseTextForAccessibility
208 #endif
209 , false);
211 #endregion // Public Constructors
213 #region Private Internal Properties
214 internal Size CheckBoxSize {
215 get {
216 if (this.check_boxes) {
217 if (this.state_image_list != null)
218 return this.state_image_list.ImageSize;
219 else
220 return ThemeEngine.Current.ListViewCheckBoxSize;
222 return Size.Empty;
226 #endregion // Private Internal Properties
228 #region Protected Properties
229 protected override CreateParams CreateParams {
230 get { return base.CreateParams; }
233 protected override Size DefaultSize {
234 get { return ThemeEngine.Current.ListViewDefaultSize; }
236 #endregion // Protected Properties
238 #region Public Instance Properties
239 [DefaultValue (ItemActivation.Standard)]
240 public ItemActivation Activation {
241 get { return activation; }
242 set {
243 if (value != ItemActivation.Standard && value != ItemActivation.OneClick &&
244 value != ItemActivation.TwoClick) {
245 throw new InvalidEnumArgumentException (string.Format
246 ("Enum argument value '{0}' is not valid for Activation", value));
249 activation = value;
253 [DefaultValue (ListViewAlignment.Top)]
254 [Localizable (true)]
255 public ListViewAlignment Alignment {
256 get { return alignment; }
257 set {
258 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left &&
259 value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
260 throw new InvalidEnumArgumentException (string.Format
261 ("Enum argument value '{0}' is not valid for Alignment", value));
264 if (this.alignment != value) {
265 alignment = value;
266 // alignment does not matter in Details/List views
267 if (this.view == View.LargeIcon ||
268 this.View == View.SmallIcon)
269 this.Redraw (true);
274 [DefaultValue (false)]
275 public bool AllowColumnReorder {
276 get { return allow_column_reorder; }
277 set { allow_column_reorder = value; }
280 [DefaultValue (true)]
281 public bool AutoArrange {
282 get { return auto_arrange; }
283 set {
284 if (auto_arrange != value) {
285 auto_arrange = value;
286 // autoarrange does not matter in Details/List views
287 if (this.view == View.LargeIcon || this.View == View.SmallIcon)
288 this.Redraw (true);
293 public override Color BackColor {
294 get {
295 if (background_color.IsEmpty)
296 return ThemeEngine.Current.ColorWindow;
297 else
298 return background_color;
300 set { background_color = value; }
303 [Browsable (false)]
304 [EditorBrowsable (EditorBrowsableState.Never)]
305 public override Image BackgroundImage {
306 get { return background_image; }
307 set {
308 if (value == background_image)
309 return;
311 background_image = value;
312 OnBackgroundImageChanged (EventArgs.Empty);
316 [DefaultValue (BorderStyle.Fixed3D)]
317 [DispId (-504)]
318 public BorderStyle BorderStyle {
319 get { return InternalBorderStyle; }
320 set { InternalBorderStyle = value; }
323 [DefaultValue (false)]
324 public bool CheckBoxes {
325 get { return check_boxes; }
326 set {
327 if (check_boxes != value) {
328 #if NET_2_0
329 if (value && View == View.Tile)
330 throw new NotSupportedException ("CheckBoxes are not"
331 + " supported in Tile view. Choose a different"
332 + " view or set CheckBoxes to false.");
333 #endif
335 check_boxes = value;
336 this.Redraw (true);
341 [Browsable (false)]
342 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
343 public CheckedIndexCollection CheckedIndices {
344 get { return checked_indices; }
347 [Browsable (false)]
348 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
349 public CheckedListViewItemCollection CheckedItems {
350 get { return checked_items; }
353 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
354 [Localizable (true)]
355 [MergableProperty (false)]
356 public ColumnHeaderCollection Columns {
357 get { return columns; }
360 [Browsable (false)]
361 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
362 public ListViewItem FocusedItem {
363 get {
364 return focused_item;
368 public override Color ForeColor {
369 get {
370 if (foreground_color.IsEmpty)
371 return ThemeEngine.Current.ColorWindowText;
372 else
373 return foreground_color;
375 set { foreground_color = value; }
378 [DefaultValue (false)]
379 public bool FullRowSelect {
380 get { return full_row_select; }
381 set { full_row_select = value; }
384 [DefaultValue (false)]
385 public bool GridLines {
386 get { return grid_lines; }
387 set {
388 if (grid_lines != value) {
389 grid_lines = value;
390 this.Redraw (false);
395 [DefaultValue (ColumnHeaderStyle.Clickable)]
396 public ColumnHeaderStyle HeaderStyle {
397 get { return header_style; }
398 set {
399 if (header_style == value)
400 return;
402 switch (value) {
403 case ColumnHeaderStyle.Clickable:
404 case ColumnHeaderStyle.Nonclickable:
405 case ColumnHeaderStyle.None:
406 break;
407 default:
408 throw new InvalidEnumArgumentException (string.Format
409 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
412 header_style = value;
413 if (view == View.Details)
414 Redraw (true);
418 [DefaultValue (true)]
419 public bool HideSelection {
420 get { return hide_selection; }
421 set {
422 if (hide_selection != value) {
423 hide_selection = value;
424 this.Redraw (false);
429 [DefaultValue (false)]
430 public bool HoverSelection {
431 get { return hover_selection; }
432 set { hover_selection = value; }
435 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
436 [Localizable (true)]
437 [MergableProperty (false)]
438 public ListViewItemCollection Items {
439 get { return items; }
442 [DefaultValue (false)]
443 public bool LabelEdit {
444 get { return label_edit; }
445 set { label_edit = value; }
448 [DefaultValue (true)]
449 [Localizable (true)]
450 public bool LabelWrap {
451 get { return label_wrap; }
452 set {
453 if (label_wrap != value) {
454 label_wrap = value;
455 this.Redraw (true);
460 [DefaultValue (null)]
461 public ImageList LargeImageList {
462 get { return large_image_list; }
463 set {
464 large_image_list = value;
465 this.Redraw (true);
469 [Browsable (false)]
470 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
471 public IComparer ListViewItemSorter {
472 get {
473 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
474 return null;
475 return item_sorter;
477 set {
478 if (item_sorter != value) {
479 item_sorter = value;
480 Sort ();
485 [DefaultValue (true)]
486 public bool MultiSelect {
487 get { return multiselect; }
488 set { multiselect = value; }
491 [DefaultValue (true)]
492 public bool Scrollable {
493 get { return scrollable; }
494 set {
495 if (scrollable != value) {
496 scrollable = value;
497 this.Redraw (true);
502 [Browsable (false)]
503 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
504 public SelectedIndexCollection SelectedIndices {
505 get { return selected_indices; }
508 [Browsable (false)]
509 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
510 public SelectedListViewItemCollection SelectedItems {
511 get { return selected_items; }
514 #if NET_2_0
515 [MonoTODO("Implement")]
516 public bool ShowGroups {
517 get {
518 return false;
521 set {
524 #endif
526 [DefaultValue (null)]
527 public ImageList SmallImageList {
528 get { return small_image_list; }
529 set {
530 small_image_list = value;
531 this.Redraw (true);
535 [DefaultValue (SortOrder.None)]
536 public SortOrder Sorting {
537 get { return sort_order; }
538 set {
539 if (!Enum.IsDefined (typeof (SortOrder), value)) {
540 throw new InvalidEnumArgumentException ("value", (int) value,
541 typeof (SortOrder));
544 if (sort_order == value)
545 return;
547 sort_order = value;
549 if (value == SortOrder.None) {
550 if (item_sorter != null) {
551 // ListViewItemSorter should never be reset for SmallIcon
552 // and LargeIcon view
553 if (View != View.SmallIcon && View != View.LargeIcon)
554 #if NET_2_0
555 item_sorter = null;
556 #else
557 // in .NET 1.1, only internal IComparer would be
558 // set to null
559 if (item_sorter is ItemComparer)
560 item_sorter = null;
561 #endif
563 this.Redraw (false);
564 } else {
565 if (item_sorter == null)
566 item_sorter = new ItemComparer (value);
567 if (item_sorter is ItemComparer) {
568 #if NET_2_0
569 item_sorter = new ItemComparer (value);
570 #else
571 // in .NET 1.1, the sort order is not updated for
572 // SmallIcon and LargeIcon views if no custom IComparer
573 // is set
574 if (View != View.SmallIcon && View != View.LargeIcon)
575 item_sorter = new ItemComparer (value);
576 #endif
578 Sort ();
583 [DefaultValue (null)]
584 public ImageList StateImageList {
585 get { return state_image_list; }
586 set {
587 state_image_list = value;
588 this.Redraw (true);
592 [Bindable (false)]
593 [Browsable (false)]
594 [EditorBrowsable (EditorBrowsableState.Never)]
595 public override string Text {
596 get { return base.Text; }
597 set {
598 if (value == base.Text)
599 return;
601 base.Text = value;
602 this.Redraw (true);
606 [Browsable (false)]
607 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
608 public ListViewItem TopItem {
609 get {
610 // there is no item
611 if (this.items.Count == 0)
612 return null;
613 // if contents are not scrolled
614 // it is the first item
615 else if (h_marker == 0 && v_marker == 0)
616 return this.items [0];
617 // do a hit test for the scrolled position
618 else {
619 foreach (ListViewItem item in this.items) {
620 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
621 return item;
623 return null;
628 #if NET_2_0
629 [MonoTODO("Implement")]
630 public bool UseCompatibleStateImageBehavior {
631 get {
632 return false;
635 set {
638 #endif
640 [DefaultValue (View.LargeIcon)]
641 public View View {
642 get { return view; }
643 set {
644 if (!Enum.IsDefined (typeof (View), value))
645 throw new InvalidEnumArgumentException ("value", (int) value,
646 typeof (View));
648 if (view != value) {
649 #if NET_2_0
650 if (CheckBoxes && value == View.Tile)
651 throw new NotSupportedException ("CheckBoxes are not"
652 + " supported in Tile view. Choose a different"
653 + " view or set CheckBoxes to false.");
654 #endif
656 h_scroll.Value = v_scroll.Value = 0;
657 view = value;
658 Redraw (true);
662 #endregion // Public Instance Properties
664 #region Internal Methods Properties
666 internal int FirstVisibleIndex {
667 get {
668 // there is no item
669 if (this.items.Count == 0)
670 return 0;
672 if (h_marker == 0 && v_marker == 0)
673 return 0;
675 foreach (ListViewItem item in this.items) {
676 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
677 return item.Index;
679 return 0;
685 internal int LastVisibleIndex {
686 get {
687 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
688 if (View == View.List || Alignment == ListViewAlignment.Left) {
689 if (Items[i].Bounds.X > ClientRectangle.Right)
690 return i - 1;
691 } else {
692 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
693 return i - 1;
697 return Items.Count - 1;
701 internal void OnSelectedIndexChanged ()
703 if (IsHandleCreated)
704 OnSelectedIndexChanged (EventArgs.Empty);
707 internal int TotalWidth {
708 get { return Math.Max (this.Width, this.layout_wd); }
711 internal int TotalHeight {
712 get { return Math.Max (this.Height, this.layout_ht); }
715 internal void Redraw (bool recalculate)
717 // Avoid calculations when control is being updated
718 if (this.updating)
719 return;
721 if (recalculate)
722 CalculateListView (this.alignment);
724 Refresh ();
727 internal Size GetChildColumnSize (int index)
729 Size ret_size = Size.Empty;
730 ColumnHeader col = this.columns [index];
732 if (col.Width == -2) { // autosize = max(items, columnheader)
733 Size size = Size.Ceiling (this.DeviceContext.MeasureString
734 (col.Text, this.Font));
735 ret_size = BiggestItem (index);
736 if (size.Width > ret_size.Width)
737 ret_size = size;
739 else { // -1 and all the values < -2 are put under one category
740 ret_size = BiggestItem (index);
741 // fall back to empty columns' width if no subitem is available for a column
742 if (ret_size.IsEmpty) {
743 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
744 if (col.Text.Length > 0)
745 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
746 (col.Text, this.Font)).Height;
747 else
748 ret_size.Height = this.Font.Height;
752 // adjust the size for icon and checkbox for 0th column
753 if (index == 0) {
754 ret_size.Width += (this.CheckBoxSize.Width + 4);
755 if (this.small_image_list != null)
756 ret_size.Width += this.small_image_list.ImageSize.Width;
758 return ret_size;
761 // Returns the size of biggest item text in a column.
762 private Size BiggestItem (int col)
764 Size temp = Size.Empty;
765 Size ret_size = Size.Empty;
767 // 0th column holds the item text, we check the size of
768 // the various subitems falling in that column and get
769 // the biggest one's size.
770 foreach (ListViewItem item in items) {
771 if (col >= item.SubItems.Count)
772 continue;
774 temp = Size.Ceiling (this.DeviceContext.MeasureString
775 (item.SubItems [col].Text, this.Font));
776 if (temp.Width > ret_size.Width)
777 ret_size = temp;
780 // adjustment for space
781 if (!ret_size.IsEmpty)
782 ret_size.Width += 4;
784 return ret_size;
787 const int max_wrap_padding = 38;
789 // Sets the size of the biggest item text as per the view
790 private void CalcTextSize ()
792 // clear the old value
793 text_size = Size.Empty;
795 if (items.Count == 0)
796 return;
798 text_size = BiggestItem (0);
800 if (view == View.LargeIcon && this.label_wrap) {
801 Size temp = Size.Empty;
802 if (this.check_boxes)
803 temp.Width += 2 * this.CheckBoxSize.Width;
804 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
805 temp.Width += icon_w + max_wrap_padding;
806 // wrapping is done for two lines only
807 if (text_size.Width > temp.Width) {
808 text_size.Width = temp.Width;
809 text_size.Height *= 2;
812 else if (view == View.List) {
813 // in list view max text shown in determined by the
814 // control width, even if scolling is enabled.
815 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
816 if (this.small_image_list != null)
817 max_wd -= this.small_image_list.ImageSize.Width;
819 if (text_size.Width > max_wd)
820 text_size.Width = max_wd;
823 // we do the default settings, if we have got 0's
824 if (text_size.Height <= 0)
825 text_size.Height = this.Font.Height;
826 if (text_size.Width <= 0)
827 text_size.Width = this.Width;
829 // little adjustment
830 text_size.Width += 4;
831 text_size.Height += 2;
834 private void Scroll (ScrollBar scrollbar, int delta)
836 if (delta == 0 || !scrollbar.Visible)
837 return;
839 int max;
840 if (scrollbar == h_scroll)
841 max = h_scroll.Maximum - item_control.Width;
842 else
843 max = v_scroll.Maximum - item_control.Height;
845 int val = scrollbar.Value + delta;
846 if (val > max)
847 val = max;
848 else if (val < scrollbar.Minimum)
849 val = scrollbar.Minimum;
850 scrollbar.Value = val;
853 private void CalculateScrollBars ()
855 Rectangle client_area = ClientRectangle;
857 if (!this.scrollable || this.items.Count <= 0) {
858 h_scroll.Visible = false;
859 v_scroll.Visible = false;
860 item_control.Location = new Point (0, header_control.Height);
861 item_control.Height = ClientRectangle.Width - header_control.Height;
862 item_control.Width = ClientRectangle.Width;
863 header_control.Width = ClientRectangle.Width;
864 return;
867 // Don't calculate if the view is not displayable
868 if (client_area.Height < 0 || client_area.Width < 0)
869 return;
871 // making a scroll bar visible might make
872 // other scroll bar visible
873 if (layout_wd > client_area.Right) {
874 h_scroll.Visible = true;
875 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
876 v_scroll.Visible = true;
877 else
878 v_scroll.Visible = false;
879 } else if (layout_ht > client_area.Bottom) {
880 v_scroll.Visible = true;
881 if ((layout_wd + v_scroll.Width) > client_area.Right)
882 h_scroll.Visible = true;
883 else
884 h_scroll.Visible = false;
885 } else {
886 h_scroll.Visible = false;
887 v_scroll.Visible = false;
890 item_control.Height = ClientRectangle.Height - header_control.Height;
892 if (h_scroll.is_visible) {
893 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
894 h_scroll.Minimum = 0;
896 // if v_scroll is visible, adjust the maximum of the
897 // h_scroll to account for the width of v_scroll
898 if (v_scroll.Visible) {
899 h_scroll.Maximum = layout_wd + v_scroll.Width;
900 h_scroll.Width = client_area.Width - v_scroll.Width;
902 else {
903 h_scroll.Maximum = layout_wd;
904 h_scroll.Width = client_area.Width;
907 h_scroll.LargeChange = client_area.Width;
908 h_scroll.SmallChange = Font.Height;
909 item_control.Height -= h_scroll.Height;
912 if (header_control.is_visible)
913 header_control.Width = ClientRectangle.Width;
914 item_control.Width = ClientRectangle.Width;
916 if (v_scroll.is_visible) {
917 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
918 v_scroll.Minimum = 0;
920 // if h_scroll is visible, adjust the maximum of the
921 // v_scroll to account for the height of h_scroll
922 if (h_scroll.Visible) {
923 v_scroll.Maximum = layout_ht + h_scroll.Height;
924 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
925 } else {
926 v_scroll.Maximum = layout_ht;
927 v_scroll.Height = client_area.Height;
930 v_scroll.LargeChange = client_area.Height;
931 v_scroll.SmallChange = Font.Height;
932 if (header_control.Visible)
933 header_control.Width -= v_scroll.Width;
934 item_control.Width -= v_scroll.Width;
938 ColumnHeader GetReorderedColumn (int index)
940 if (reordered_column_indices == null)
941 return Columns [index];
942 else
943 return Columns [reordered_column_indices [index]];
946 void ReorderColumn (ColumnHeader col, int index)
948 if (reordered_column_indices == null) {
949 reordered_column_indices = new int [Columns.Count];
950 for (int i = 0; i < Columns.Count; i++)
951 reordered_column_indices [i] = i;
954 if (reordered_column_indices [index] == col.Index)
955 return;
957 int[] curr = reordered_column_indices;
958 int[] result = new int [Columns.Count];
959 int curr_idx = 0;
960 for (int i = 0; i < Columns.Count; i++) {
961 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
962 curr_idx++;
964 if (i == index)
965 result [i] = col.Index;
966 else
967 result [i] = curr [curr_idx++];
970 reordered_column_indices = result;
971 LayoutDetails ();
972 header_control.Invalidate ();
973 item_control.Invalidate ();
976 Size LargeIconItemSize {
977 get {
978 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
979 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
980 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
981 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
982 return new Size (w, h);
986 Size SmallIconItemSize {
987 get {
988 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
989 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
990 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
991 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
992 return new Size (w, h);
996 int rows;
997 int cols;
998 ListViewItem[,] item_matrix;
1000 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
1002 header_control.Visible = false;
1003 header_control.Size = Size.Empty;
1004 item_control.Visible = true;
1005 item_control.Location = Point.Empty;
1007 if (items.Count == 0)
1008 return;
1010 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
1012 Rectangle area = ClientRectangle;
1014 if (left_aligned) {
1015 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
1016 if (rows <= 0)
1017 rows = 1;
1018 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1019 } else {
1020 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
1021 if (cols <= 0)
1022 cols = 1;
1023 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1026 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1027 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1028 item_matrix = new ListViewItem [rows, cols];
1029 int row = 0;
1030 int col = 0;
1031 foreach (ListViewItem item in items) {
1032 int x = col * (sz.Width + x_spacing);
1033 int y = row * (sz.Height + y_spacing);
1034 item.Location = new Point (x, y);
1035 item.Layout ();
1036 item.row = row;
1037 item.col = col;
1038 item_matrix [row, col] = item;
1039 if (left_aligned) {
1040 if (++row == rows) {
1041 row = 0;
1042 col++;
1044 } else {
1045 if (++col == cols) {
1046 col = 0;
1047 row++;
1052 item_control.Size = new Size (layout_wd, layout_ht);
1055 void LayoutHeader ()
1057 int x = 0;
1058 for (int i = 0; i < Columns.Count; i++) {
1059 ColumnHeader col = GetReorderedColumn (i);
1060 col.X = x;
1061 col.Y = 0;
1062 col.CalcColumnHeader ();
1063 x += col.Wd;
1066 if (x < ClientRectangle.Width)
1067 x = ClientRectangle.Width;
1069 if (header_style == ColumnHeaderStyle.None) {
1070 header_control.Visible = false;
1071 header_control.Size = Size.Empty;
1072 } else {
1073 header_control.Width = x;
1074 header_control.Height = columns [0].Ht;
1075 header_control.Visible = true;
1079 void LayoutDetails ()
1081 if (columns.Count == 0) {
1082 header_control.Visible = false;
1083 item_control.Visible = false;
1084 return;
1087 LayoutHeader ();
1089 item_control.Visible = true;
1090 item_control.Location = new Point (0, header_control.Height);
1092 int y = 0;
1093 if (items.Count > 0) {
1094 foreach (ListViewItem item in items) {
1095 item.Layout ();
1096 item.Location = new Point (0, y);
1097 y += item.Bounds.Height + 2;
1100 // some space for bottom gridline
1101 if (grid_lines)
1102 y += 2;
1105 layout_wd = Math.Max (header_control.Width, item_control.Width);
1106 layout_ht = y + header_control.Height;
1109 private void CalculateListView (ListViewAlignment align)
1111 CalcTextSize ();
1113 switch (view) {
1114 case View.Details:
1115 LayoutDetails ();
1116 break;
1118 case View.SmallIcon:
1119 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1120 break;
1122 case View.LargeIcon:
1123 LayoutIcons (true, alignment == ListViewAlignment.Left,
1124 ThemeEngine.Current.ListViewHorizontalSpacing,
1125 ThemeEngine.Current.ListViewVerticalSpacing);
1126 break;
1128 case View.List:
1129 LayoutIcons (false, true, 4, 2);
1130 break;
1133 CalculateScrollBars ();
1136 private bool KeySearchString (KeyEventArgs ke)
1138 int current_tickcnt = Environment.TickCount;
1139 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1140 keysearch_text = string.Empty;
1143 keysearch_text += (char) ke.KeyData;
1144 keysearch_tickcnt = current_tickcnt;
1146 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1147 int i = start;
1148 while (true) {
1149 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1150 CompareOptions.IgnoreCase)) {
1151 SetFocusedItem (Items [i]);
1152 items [i].Selected = true;
1153 EnsureVisible (i);
1154 break;
1156 i = (i + 1 < Items.Count) ? i+1 : 0;
1158 if (i == start)
1159 break;
1161 return true;
1164 int GetAdjustedIndex (Keys key)
1166 int result = -1;
1168 if (View == View.Details) {
1169 if (key == Keys.Up)
1170 result = FocusedItem.Index - 1;
1171 else if (key == Keys.Down) {
1172 result = FocusedItem.Index + 1;
1173 if (result == items.Count)
1174 result = -1;
1176 return result;
1179 int row = FocusedItem.row;
1180 int col = FocusedItem.col;
1182 switch (key) {
1183 case Keys.Left:
1184 if (col == 0)
1185 return -1;
1186 return item_matrix [row, col - 1].Index;
1188 case Keys.Right:
1189 if (col == (cols - 1))
1190 return -1;
1191 while (item_matrix [row, col + 1] == null)
1192 row--;
1193 return item_matrix [row, col + 1].Index;
1195 case Keys.Up:
1196 if (row == 0)
1197 return -1;
1198 return item_matrix [row - 1, col].Index;
1200 case Keys.Down:
1201 if (row == (rows - 1) || row == Items.Count - 1)
1202 return -1;
1203 while (item_matrix [row + 1, col] == null)
1204 col--;
1205 return item_matrix [row + 1, col].Index;
1207 default:
1208 return -1;
1212 ListViewItem selection_start;
1214 private bool SelectItems (ArrayList sel_items)
1216 bool changed = false;
1217 ArrayList curr_items = SelectedItems.List;
1218 foreach (ListViewItem item in curr_items)
1219 if (!sel_items.Contains (item)) {
1220 item.Selected = false;
1221 changed = true;
1223 foreach (ListViewItem item in sel_items)
1224 if (!item.Selected) {
1225 item.Selected = true;
1226 changed = true;
1228 return changed;
1231 private void UpdateMultiSelection (int index)
1233 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1234 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1235 ListViewItem item = items [index];
1237 if (shift_pressed && selection_start != null) {
1238 ArrayList list = new ArrayList ();
1239 int start = Math.Min (selection_start.Index, index);
1240 int end = Math.Max (selection_start.Index, index);
1241 if (View == View.Details) {
1242 for (int i = start; i <= end; i++)
1243 list.Add (items [i]);
1244 } else {
1245 int left = Math.Min (items [start].col, items [end].col);
1246 int right = Math.Max (items [start].col, items [end].col);
1247 int top = Math.Min (items [start].row, items [end].row);
1248 int bottom = Math.Max (items [start].row, items [end].row);
1249 foreach (ListViewItem curr in items)
1250 if (curr.row >= top && curr.row <= bottom &&
1251 curr.col >= left && curr.col <= right)
1252 list.Add (curr);
1254 if (SelectItems (list))
1255 OnSelectedIndexChanged (EventArgs.Empty);
1256 } else if (ctrl_pressed) {
1257 item.Selected = !item.Selected;
1258 selection_start = item;
1259 OnSelectedIndexChanged (EventArgs.Empty);
1260 } else {
1261 SelectedItems.Clear ();
1262 item.Selected = true;
1263 selection_start = item;
1264 OnSelectedIndexChanged (EventArgs.Empty);
1268 internal override bool InternalPreProcessMessage (ref Message msg)
1270 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1271 Keys key_data = (Keys)msg.WParam.ToInt32();
1272 if (HandleNavKeys (key_data))
1273 return true;
1275 return base.InternalPreProcessMessage (ref msg);
1278 bool HandleNavKeys (Keys key_data)
1280 if (Items.Count == 0 || !item_control.Visible)
1281 return false;
1283 if (FocusedItem == null)
1284 SetFocusedItem (Items [0]);
1286 switch (key_data) {
1287 case Keys.End:
1288 SelectIndex (Items.Count - 1);
1289 break;
1291 case Keys.Home:
1292 SelectIndex (0);
1293 break;
1295 case Keys.Left:
1296 case Keys.Right:
1297 case Keys.Up:
1298 case Keys.Down:
1299 SelectIndex (GetAdjustedIndex (key_data));
1300 break;
1302 default:
1303 return false;
1306 return true;
1309 void SelectIndex (int index)
1311 if (index == -1)
1312 return;
1314 if (MultiSelect)
1315 UpdateMultiSelection (index);
1316 else if (!items [index].Selected) {
1317 items [index].Selected = true;
1318 OnSelectedIndexChanged (EventArgs.Empty);
1321 SetFocusedItem (items [index]);
1322 EnsureVisible (index);
1325 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1327 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1328 return;
1330 ke.Handled = KeySearchString (ke);
1333 internal class ItemControl : Control {
1335 ListView owner;
1336 ListViewItem clicked_item;
1337 ListViewItem last_clicked_item;
1338 bool hover_processed = false;
1339 bool checking = false;
1341 ListViewLabelEditTextBox edit_text_box;
1342 internal ListViewItem edit_item;
1343 LabelEditEventArgs edit_args;
1345 public ItemControl (ListView owner)
1347 this.owner = owner;
1348 DoubleClick += new EventHandler(ItemsDoubleClick);
1349 MouseDown += new MouseEventHandler(ItemsMouseDown);
1350 MouseMove += new MouseEventHandler(ItemsMouseMove);
1351 MouseHover += new EventHandler(ItemsMouseHover);
1352 MouseUp += new MouseEventHandler(ItemsMouseUp);
1355 void ItemsDoubleClick (object sender, EventArgs e)
1357 if (owner.activation == ItemActivation.Standard)
1358 owner.OnItemActivate (EventArgs.Empty);
1361 enum BoxSelect {
1362 None,
1363 Normal,
1364 Shift,
1365 Control
1368 BoxSelect box_select_mode = BoxSelect.None;
1369 ArrayList prev_selection;
1370 Point box_select_start;
1372 Rectangle box_select_rect;
1373 internal Rectangle BoxSelectRectangle {
1374 get { return box_select_rect; }
1375 set {
1376 if (box_select_rect == value)
1377 return;
1379 InvalidateBoxSelectRect ();
1380 box_select_rect = value;
1381 InvalidateBoxSelectRect ();
1385 void InvalidateBoxSelectRect ()
1387 if (BoxSelectRectangle.Size.IsEmpty)
1388 return;
1390 Rectangle edge = BoxSelectRectangle;
1391 edge.X -= 1;
1392 edge.Y -= 1;
1393 edge.Width += 2;
1394 edge.Height = 2;
1395 Invalidate (edge);
1396 edge.Y = BoxSelectRectangle.Bottom - 1;
1397 Invalidate (edge);
1398 edge.Y = BoxSelectRectangle.Y - 1;
1399 edge.Width = 2;
1400 edge.Height = BoxSelectRectangle.Height + 2;
1401 Invalidate (edge);
1402 edge.X = BoxSelectRectangle.Right - 1;
1403 Invalidate (edge);
1406 private Rectangle CalculateBoxSelectRectangle (Point pt)
1408 int left = Math.Min (box_select_start.X, pt.X);
1409 int right = Math.Max (box_select_start.X, pt.X);
1410 int top = Math.Min (box_select_start.Y, pt.Y);
1411 int bottom = Math.Max (box_select_start.Y, pt.Y);
1412 return Rectangle.FromLTRB (left, top, right, bottom);
1415 ArrayList BoxSelectedItems {
1416 get {
1417 ArrayList result = new ArrayList ();
1418 foreach (ListViewItem item in owner.Items) {
1419 Rectangle r = item.Bounds;
1420 r.X += r.Width / 4;
1421 r.Y += r.Height / 4;
1422 r.Width /= 2;
1423 r.Height /= 2;
1424 if (BoxSelectRectangle.IntersectsWith (r))
1425 result.Add (item);
1427 return result;
1431 private bool PerformBoxSelection (Point pt)
1433 if (box_select_mode == BoxSelect.None)
1434 return false;
1436 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1438 ArrayList box_items = BoxSelectedItems;
1440 ArrayList items;
1442 switch (box_select_mode) {
1444 case BoxSelect.Normal:
1445 items = box_items;
1446 break;
1448 case BoxSelect.Control:
1449 items = new ArrayList ();
1450 foreach (ListViewItem item in prev_selection)
1451 if (!box_items.Contains (item))
1452 items.Add (item);
1453 foreach (ListViewItem item in box_items)
1454 if (!prev_selection.Contains (item))
1455 items.Add (item);
1456 break;
1458 case BoxSelect.Shift:
1459 items = box_items;
1460 foreach (ListViewItem item in box_items)
1461 prev_selection.Remove (item);
1462 foreach (ListViewItem item in prev_selection)
1463 items.Add (item);
1464 break;
1466 default:
1467 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1470 SuspendLayout ();
1471 owner.SelectItems (items);
1472 ResumeLayout ();
1474 return true;
1477 private void ToggleCheckState (ListViewItem item)
1479 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1480 item.Checked = !item.Checked;
1481 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1483 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1484 owner.OnItemCheck (ice);
1487 private void ItemsMouseDown (object sender, MouseEventArgs me)
1489 if (owner.items.Count == 0)
1490 return;
1492 Point pt = new Point (me.X, me.Y);
1493 foreach (ListViewItem item in owner.items) {
1494 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1495 checking = true;
1496 if (me.Clicks > 1)
1497 return;
1498 ToggleCheckState (item);
1499 return;
1502 if (owner.View == View.Details && !owner.FullRowSelect) {
1503 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1504 clicked_item = item;
1505 break;
1507 } else {
1508 if (item.Bounds.Contains (pt)) {
1509 clicked_item = item;
1510 break;
1516 if (clicked_item != null) {
1517 owner.SetFocusedItem (clicked_item);
1518 bool changed = !clicked_item.Selected;
1519 if (owner.MultiSelect)
1520 owner.UpdateMultiSelection (clicked_item.Index);
1521 else
1522 clicked_item.Selected = true;
1524 if (changed)
1525 owner.OnSelectedIndexChanged (EventArgs.Empty);
1527 // Raise double click if the item was clicked. On MS the
1528 // double click is only raised if you double click an item
1529 if (me.Clicks > 1) {
1530 owner.OnDoubleClick (EventArgs.Empty);
1531 if (owner.CheckBoxes)
1532 ToggleCheckState (clicked_item);
1533 } else if (me.Clicks == 1) {
1534 owner.OnClick (EventArgs.Empty);
1535 if (owner.LabelEdit && !changed)
1536 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
1538 } else {
1539 if (owner.MultiSelect) {
1540 Keys mods = XplatUI.State.ModifierKeys;
1541 if ((mods & Keys.Shift) != 0)
1542 box_select_mode = BoxSelect.Shift;
1543 else if ((mods & Keys.Control) != 0)
1544 box_select_mode = BoxSelect.Control;
1545 else
1546 box_select_mode = BoxSelect.Normal;
1547 box_select_start = pt;
1548 prev_selection = owner.SelectedItems.List;
1549 } else if (owner.SelectedItems.Count > 0) {
1550 owner.SelectedItems.Clear ();
1551 owner.OnSelectedIndexChanged (EventArgs.Empty);
1556 private void ItemsMouseMove (object sender, MouseEventArgs me)
1558 if (PerformBoxSelection (new Point (me.X, me.Y)))
1559 return;
1561 if (owner.HoverSelection && hover_processed) {
1563 Point pt = PointToClient (Control.MousePosition);
1564 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1565 if (item == null || item.Selected)
1566 return;
1568 hover_processed = false;
1569 XplatUI.ResetMouseHover (Handle);
1574 private void ItemsMouseHover (object sender, EventArgs e)
1576 if (Capture || !owner.HoverSelection)
1577 return;
1579 hover_processed = true;
1580 Point pt = PointToClient (Control.MousePosition);
1581 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1583 if (item == null)
1584 return;
1586 item.Selected = true;
1587 owner.OnSelectedIndexChanged (new EventArgs ());
1590 private void ItemsMouseUp (object sender, MouseEventArgs me)
1592 Capture = false;
1593 if (owner.Items.Count == 0)
1594 return;
1596 Point pt = new Point (me.X, me.Y);
1598 Rectangle rect = Rectangle.Empty;
1599 if (clicked_item != null) {
1600 if (owner.view == View.Details && !owner.full_row_select)
1601 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1602 else
1603 rect = clicked_item.Bounds;
1605 if (rect.Contains (pt)) {
1606 switch (owner.activation) {
1607 case ItemActivation.OneClick:
1608 owner.OnItemActivate (EventArgs.Empty);
1609 break;
1611 case ItemActivation.TwoClick:
1612 if (last_clicked_item == clicked_item) {
1613 owner.OnItemActivate (EventArgs.Empty);
1614 last_clicked_item = null;
1615 } else
1616 last_clicked_item = clicked_item;
1617 break;
1618 default:
1619 // DoubleClick activation is handled in another handler
1620 break;
1623 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1624 // Need this to clean up background clicks
1625 owner.SelectedItems.Clear ();
1626 owner.OnSelectedIndexChanged (EventArgs.Empty);
1629 clicked_item = null;
1630 box_select_start = Point.Empty;
1631 BoxSelectRectangle = Rectangle.Empty;
1632 prev_selection = null;
1633 box_select_mode = BoxSelect.None;
1634 checking = false;
1637 internal void LabelEditFinished (object sender, EventArgs e)
1639 EndEdit (edit_item);
1642 internal void BeginEdit (ListViewItem item)
1644 if (edit_item != null)
1645 EndEdit (edit_item);
1647 if (edit_text_box == null) {
1648 edit_text_box = new ListViewLabelEditTextBox ();
1649 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
1650 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
1651 edit_text_box.Visible = false;
1652 Controls.Add (edit_text_box);
1655 item.EnsureVisible();
1657 edit_text_box.Reset ();
1659 switch (owner.view) {
1660 case View.List:
1661 case View.SmallIcon:
1662 case View.Details:
1663 edit_text_box.TextAlign = HorizontalAlignment.Left;
1664 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1665 SizeF sizef = DeviceContext.MeasureString (item.Text, item.Font);
1666 edit_text_box.Width = (int)sizef.Width + 4;
1667 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
1668 edit_text_box.WordWrap = false;
1669 edit_text_box.Multiline = false;
1670 break;
1671 case View.LargeIcon:
1672 edit_text_box.TextAlign = HorizontalAlignment.Center;
1673 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1674 sizef = DeviceContext.MeasureString (item.Text, item.Font);
1675 edit_text_box.Width = (int)sizef.Width + 4;
1676 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
1677 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
1678 edit_text_box.WordWrap = true;
1679 edit_text_box.Multiline = true;
1680 break;
1683 edit_text_box.Text = item.Text;
1684 edit_text_box.Font = item.Font;
1685 edit_text_box.Visible = true;
1686 edit_text_box.Focus ();
1687 edit_text_box.SelectAll ();
1689 edit_args = new LabelEditEventArgs (owner.Items.IndexOf(edit_item));
1690 owner.OnBeforeLabelEdit (edit_args);
1692 if (edit_args.CancelEdit)
1693 EndEdit (item);
1695 edit_item = item;
1698 internal void EndEdit (ListViewItem item)
1700 if (edit_text_box != null && edit_text_box.Visible) {
1701 edit_text_box.Visible = false;
1704 if (edit_item != null && edit_item == item) {
1705 owner.OnAfterLabelEdit (edit_args);
1707 if (!edit_args.CancelEdit) {
1708 if (edit_args.Label != null)
1709 edit_item.Text = edit_args.Label;
1710 else
1711 edit_item.Text = edit_text_box.Text;
1717 edit_item = null;
1720 internal override void OnPaintInternal (PaintEventArgs pe)
1722 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1725 internal override void OnGotFocusInternal (EventArgs e)
1727 owner.Focus ();
1731 internal class ListViewLabelEditTextBox : TextBox
1733 int max_width = -1;
1734 int min_width = -1;
1736 int max_height = -1;
1737 int min_height = -1;
1739 int old_number_lines = 1;
1741 SizeF text_size_one_char;
1743 public ListViewLabelEditTextBox ()
1745 min_height = DefaultSize.Height;
1746 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1749 public int MaxWidth {
1750 set {
1751 if (value < min_width)
1752 max_width = min_width;
1753 else
1754 max_width = value;
1758 public int MaxHeight {
1759 set {
1760 if (value < min_height)
1761 max_height = min_height;
1762 else
1763 max_height = value;
1767 public new int Width {
1768 get {
1769 return base.Width;
1771 set {
1772 min_width = value;
1773 base.Width = value;
1777 public override Font Font {
1778 get {
1779 return base.Font;
1781 set {
1782 base.Font = value;
1783 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1787 protected override void OnTextChanged (EventArgs e)
1789 SizeF text_size = DeviceContext.MeasureString (Text, Font);
1791 int new_width = (int)text_size.Width + 8;
1793 if (!Multiline)
1794 ResizeTextBoxWidth (new_width);
1795 else {
1796 if (Width != max_width)
1797 ResizeTextBoxWidth (new_width);
1799 int number_lines = Lines.Length;
1801 if (number_lines != old_number_lines) {
1802 int new_height = number_lines * (int)text_size_one_char.Height + 4;
1803 old_number_lines = number_lines;
1805 ResizeTextBoxHeight (new_height);
1809 base.OnTextChanged (e);
1812 protected override bool IsInputKey (Keys key_data)
1814 if ((key_data & Keys.Alt) == 0) {
1815 switch (key_data & Keys.KeyCode) {
1816 case Keys.Enter:
1817 return true;
1820 return base.IsInputKey (key_data);
1823 protected override void OnKeyDown (KeyEventArgs e)
1825 if (e.KeyCode == Keys.Return && Visible) {
1826 this.Visible = false;
1827 OnEditingFinished (e);
1831 protected override void OnLostFocus (EventArgs e)
1833 if (Visible) {
1834 OnEditingFinished (e);
1838 protected void OnEditingFinished (EventArgs e)
1840 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
1841 if (eh != null)
1842 eh (this, e);
1845 private void ResizeTextBoxWidth (int new_width)
1847 if (new_width > max_width)
1848 base.Width = max_width;
1849 else
1850 if (new_width >= min_width)
1851 base.Width = new_width;
1852 else
1853 base.Width = min_width;
1856 private void ResizeTextBoxHeight (int new_height)
1858 if (new_height > max_height)
1859 base.Height = max_height;
1860 else
1861 if (new_height >= min_height)
1862 base.Height = new_height;
1863 else
1864 base.Height = min_height;
1867 public void Reset ()
1869 max_width = -1;
1870 min_width = -1;
1872 max_height = -1;
1874 old_number_lines = 1;
1876 Text = String.Empty;
1878 Size = DefaultSize;
1881 static object EditingFinishedEvent = new object ();
1882 public event EventHandler EditingFinished {
1883 add { Events.AddHandler (EditingFinishedEvent, value); }
1884 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
1888 internal override void OnPaintInternal (PaintEventArgs pe)
1890 if (updating)
1891 return;
1893 CalculateScrollBars ();
1896 void FocusChanged (object o, EventArgs args)
1898 if (Items.Count == 0)
1899 return;
1901 if (FocusedItem == null)
1902 SetFocusedItem (Items [0]);
1904 item_control.Invalidate (FocusedItem.Bounds);
1907 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1909 if (Items.Count == 0)
1910 return;
1912 int lines = me.Delta / 120;
1914 if (lines == 0)
1915 return;
1917 switch (View) {
1918 case View.Details:
1919 case View.SmallIcon:
1920 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1921 break;
1922 case View.LargeIcon:
1923 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1924 break;
1925 case View.List:
1926 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1927 break;
1931 private void ListView_SizeChanged (object sender, EventArgs e)
1933 CalculateListView (alignment);
1936 private void SetFocusedItem (ListViewItem item)
1938 if (focused_item != null)
1939 focused_item.Focused = false;
1941 if (item != null)
1942 item.Focused = true;
1944 focused_item = item;
1947 private void HorizontalScroller (object sender, EventArgs e)
1949 item_control.EndEdit (item_control.edit_item);
1951 // Avoid unnecessary flickering, when button is
1952 // kept pressed at the end
1953 if (h_marker != h_scroll.Value) {
1955 int pixels = h_marker - h_scroll.Value;
1957 h_marker = h_scroll.Value;
1958 if (header_control.Visible)
1959 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1961 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1965 private void VerticalScroller (object sender, EventArgs e)
1967 item_control.EndEdit (item_control.edit_item);
1969 // Avoid unnecessary flickering, when button is
1970 // kept pressed at the end
1971 if (v_marker != v_scroll.Value) {
1972 int pixels = v_marker - v_scroll.Value;
1973 Rectangle area = item_control.ClientRectangle;
1974 v_marker = v_scroll.Value;
1975 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1978 #endregion // Internal Methods Properties
1980 #region Protected Methods
1981 protected override void CreateHandle ()
1983 base.CreateHandle ();
1984 for (int i = 0; i < SelectedItems.Count; i++)
1985 OnSelectedIndexChanged (EventArgs.Empty);
1988 protected override void Dispose (bool disposing)
1990 if (disposing) {
1991 h_scroll.Dispose ();
1992 v_scroll.Dispose ();
1994 large_image_list = null;
1995 small_image_list = null;
1996 state_image_list = null;
1999 base.Dispose (disposing);
2002 protected override bool IsInputKey (Keys keyData)
2004 switch (keyData) {
2005 case Keys.Up:
2006 case Keys.Down:
2007 case Keys.PageUp:
2008 case Keys.PageDown:
2009 case Keys.Right:
2010 case Keys.Left:
2011 case Keys.End:
2012 case Keys.Home:
2013 return true;
2015 default:
2016 break;
2019 return base.IsInputKey (keyData);
2022 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
2024 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
2025 if (eh != null)
2026 eh (this, e);
2029 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
2031 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
2032 if (eh != null)
2033 eh (this, e);
2036 protected virtual void OnColumnClick (ColumnClickEventArgs e)
2038 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
2039 if (eh != null)
2040 eh (this, e);
2043 protected override void OnEnabledChanged (EventArgs e)
2045 base.OnEnabledChanged (e);
2048 protected override void OnFontChanged (EventArgs e)
2050 base.OnFontChanged (e);
2051 Redraw (true);
2054 protected override void OnHandleCreated (EventArgs e)
2056 base.OnHandleCreated (e);
2057 Sort ();
2060 protected override void OnHandleDestroyed (EventArgs e)
2062 base.OnHandleDestroyed (e);
2065 protected virtual void OnItemActivate (EventArgs e)
2067 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
2068 if (eh != null)
2069 eh (this, e);
2072 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
2074 EventHandler eh = (EventHandler)(Events [ItemCheckEvent]);
2075 if (eh != null)
2076 eh (this, ice);
2079 protected virtual void OnItemDrag (ItemDragEventArgs e)
2081 EventHandler eh = (EventHandler)(Events [ItemDragEvent]);
2082 if (eh != null)
2083 eh (this, e);
2086 protected virtual void OnSelectedIndexChanged (EventArgs e)
2088 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
2089 if (eh != null)
2090 eh (this, e);
2093 protected override void OnSystemColorsChanged (EventArgs e)
2095 base.OnSystemColorsChanged (e);
2098 protected void RealizeProperties ()
2100 // FIXME: TODO
2103 protected void UpdateExtendedStyles ()
2105 // FIXME: TODO
2108 protected override void WndProc (ref Message m)
2110 base.WndProc (ref m);
2112 #endregion // Protected Methods
2114 #region Public Instance Methods
2115 public void ArrangeIcons ()
2117 ArrangeIcons (this.alignment);
2120 public void ArrangeIcons (ListViewAlignment alignment)
2122 // Icons are arranged only if view is set to LargeIcon or SmallIcon
2123 if (view == View.LargeIcon || view == View.SmallIcon) {
2124 this.CalculateListView (alignment);
2125 // we have done the calculations already
2126 this.Redraw (false);
2130 public void BeginUpdate ()
2132 // flag to avoid painting
2133 updating = true;
2136 public void Clear ()
2138 columns.Clear ();
2139 items.Clear (); // Redraw (true) called here
2142 public void EndUpdate ()
2144 // flag to avoid painting
2145 updating = false;
2147 // probably, now we need a redraw with recalculations
2148 this.Redraw (true);
2151 public void EnsureVisible (int index)
2153 if (index < 0 || index >= items.Count || scrollable == false)
2154 return;
2156 Rectangle view_rect = item_control.ClientRectangle;
2157 Rectangle bounds = items [index].Bounds;
2159 if (view_rect.Contains (bounds))
2160 return;
2162 if (bounds.Left < 0)
2163 h_scroll.Value += bounds.Left;
2164 else if (bounds.Right > view_rect.Right)
2165 h_scroll.Value += (bounds.Right - view_rect.Right);
2167 if (bounds.Top < 0)
2168 v_scroll.Value += bounds.Top;
2169 else if (bounds.Bottom > view_rect.Bottom)
2170 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
2173 public ListViewItem GetItemAt (int x, int y)
2175 foreach (ListViewItem item in items) {
2176 if (item.Bounds.Contains (x, y))
2177 return item;
2179 return null;
2182 public Rectangle GetItemRect (int index)
2184 return GetItemRect (index, ItemBoundsPortion.Entire);
2187 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
2189 if (index < 0 || index >= items.Count)
2190 throw new IndexOutOfRangeException ("index");
2192 return items [index].GetBounds (portion);
2195 public void Sort ()
2197 Sort (true);
2200 // we need this overload to reuse the logic for sorting, while allowing
2201 // redrawing to be done by caller or have it done by this method when
2202 // sorting is really performed
2204 // ListViewItemCollection's Add and AddRange methods call this overload
2205 // with redraw set to false, as they take care of redrawing themselves
2206 // (they even want to redraw the listview if no sort is performed, as
2207 // an item was added), while ListView.Sort () only wants to redraw if
2208 // sorting was actually performed
2209 private void Sort (bool redraw)
2211 if (!IsHandleCreated || item_sorter == null) {
2212 return;
2215 items.Sort (item_sorter);
2216 if (redraw)
2217 this.Redraw (true);
2220 public override string ToString ()
2222 int count = this.Items.Count;
2224 if (count == 0)
2225 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
2226 else
2227 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
2229 #endregion // Public Instance Methods
2232 #region Subclasses
2234 class HeaderControl : Control {
2236 ListView owner;
2237 bool column_resize_active = false;
2238 ColumnHeader resize_column;
2239 ColumnHeader clicked_column;
2240 ColumnHeader drag_column;
2241 int drag_x;
2242 int drag_to_index = -1;
2244 public HeaderControl (ListView owner)
2246 this.owner = owner;
2247 MouseDown += new MouseEventHandler (HeaderMouseDown);
2248 MouseMove += new MouseEventHandler (HeaderMouseMove);
2249 MouseUp += new MouseEventHandler (HeaderMouseUp);
2252 private ColumnHeader ColumnAtX (int x)
2254 Point pt = new Point (x, 0);
2255 ColumnHeader result = null;
2256 foreach (ColumnHeader col in owner.Columns) {
2257 if (col.Rect.Contains (pt)) {
2258 result = col;
2259 break;
2262 return result;
2265 private int GetReorderedIndex (ColumnHeader col)
2267 if (owner.reordered_column_indices == null)
2268 return col.Index;
2269 else
2270 for (int i = 0; i < owner.Columns.Count; i++)
2271 if (owner.reordered_column_indices [i] == col.Index)
2272 return i;
2273 throw new Exception ("Column index missing from reordered array");
2276 private void HeaderMouseDown (object sender, MouseEventArgs me)
2278 if (resize_column != null) {
2279 column_resize_active = true;
2280 Capture = true;
2281 return;
2284 clicked_column = ColumnAtX (me.X + owner.h_marker);
2286 if (clicked_column != null) {
2287 Capture = true;
2288 if (owner.AllowColumnReorder) {
2289 drag_x = me.X;
2290 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2291 drag_column.column_rect = clicked_column.Rect;
2292 drag_to_index = GetReorderedIndex (clicked_column);
2294 clicked_column.pressed = true;
2295 Rectangle bounds = clicked_column.Rect;
2296 bounds.X -= owner.h_marker;
2297 Invalidate (bounds);
2298 return;
2302 private void HeaderMouseMove (object sender, MouseEventArgs me)
2304 Point pt = new Point (me.X + owner.h_marker, me.Y);
2306 if (column_resize_active) {
2307 resize_column.Width = pt.X - resize_column.X;
2308 if (resize_column.Width < 0)
2309 resize_column.Width = 0;
2310 return;
2313 resize_column = null;
2315 if (clicked_column != null) {
2316 if (owner.AllowColumnReorder) {
2317 Rectangle r;
2319 r = drag_column.column_rect;
2320 r.X = clicked_column.Rect.X + me.X - drag_x;
2321 drag_column.column_rect = r;
2323 int x = me.X + owner.h_marker;
2324 ColumnHeader over = ColumnAtX (x);
2325 if (over == null)
2326 drag_to_index = owner.Columns.Count;
2327 else if (x < over.X + over.Width / 2)
2328 drag_to_index = GetReorderedIndex (over);
2329 else
2330 drag_to_index = GetReorderedIndex (over) + 1;
2331 Invalidate ();
2332 } else {
2333 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2334 bool pressed = clicked_column.pressed;
2335 clicked_column.pressed = over == clicked_column;
2336 if (clicked_column.pressed ^ pressed) {
2337 Rectangle bounds = clicked_column.Rect;
2338 bounds.X -= owner.h_marker;
2339 Invalidate (bounds);
2342 return;
2345 for (int i = 0; i < owner.Columns.Count; i++) {
2346 Rectangle zone = owner.Columns [i].Rect;
2347 zone.X = zone.Right - 5;
2348 zone.Width = 10;
2349 if (zone.Contains (pt)) {
2350 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2351 i++;
2352 resize_column = owner.Columns [i];
2353 break;
2357 if (resize_column == null)
2358 Cursor = Cursors.Default;
2359 else
2360 Cursor = Cursors.VSplit;
2363 void HeaderMouseUp (object sender, MouseEventArgs me)
2365 Capture = false;
2367 if (column_resize_active) {
2368 column_resize_active = false;
2369 resize_column = null;
2370 Cursor = Cursors.Default;
2371 return;
2374 if (clicked_column != null && clicked_column.pressed) {
2375 clicked_column.pressed = false;
2376 Rectangle bounds = clicked_column.Rect;
2377 bounds.X -= owner.h_marker;
2378 Invalidate (bounds);
2379 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2382 if (drag_column != null && owner.AllowColumnReorder) {
2383 drag_column = null;
2384 if (drag_to_index > GetReorderedIndex (clicked_column))
2385 drag_to_index--;
2386 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2387 owner.ReorderColumn (clicked_column, drag_to_index);
2388 drag_to_index = -1;
2389 Invalidate ();
2392 clicked_column = null;
2395 internal override void OnPaintInternal (PaintEventArgs pe)
2397 if (owner.updating)
2398 return;
2400 Theme theme = ThemeEngine.Current;
2401 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2403 if (drag_column == null)
2404 return;
2406 int target_x;
2407 if (drag_to_index == owner.Columns.Count)
2408 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2409 else
2410 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2411 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2414 protected override void WndProc (ref Message m)
2416 switch ((Msg)m.Msg) {
2417 case Msg.WM_SETFOCUS:
2418 owner.Focus ();
2419 break;
2420 default:
2421 base.WndProc (ref m);
2422 break;
2427 private class ItemComparer : IComparer {
2428 readonly SortOrder sort_order;
2430 public ItemComparer (SortOrder sortOrder)
2432 sort_order = sortOrder;
2435 public int Compare (object x, object y)
2437 ListViewItem item_x = x as ListViewItem;
2438 ListViewItem item_y = y as ListViewItem;
2439 if (sort_order == SortOrder.Ascending)
2440 return String.Compare (item_x.Text, item_y.Text);
2441 else
2442 return String.Compare (item_y.Text, item_x.Text);
2446 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2448 private readonly ListView owner;
2450 #region Public Constructor
2451 public CheckedIndexCollection (ListView owner)
2453 this.owner = owner;
2455 #endregion // Public Constructor
2457 #region Public Properties
2458 [Browsable (false)]
2459 public int Count {
2460 get { return owner.CheckedItems.Count; }
2463 public bool IsReadOnly {
2464 get { return true; }
2467 public int this [int index] {
2468 get {
2469 int [] indices = GetIndices ();
2470 if (index < 0 || index >= indices.Length)
2471 throw new ArgumentOutOfRangeException ("index");
2472 return indices [index];
2476 bool ICollection.IsSynchronized {
2477 get { return false; }
2480 object ICollection.SyncRoot {
2481 get { return this; }
2484 bool IList.IsFixedSize {
2485 get { return true; }
2488 object IList.this [int index] {
2489 get { return this [index]; }
2490 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2492 #endregion // Public Properties
2494 #region Public Methods
2495 public bool Contains (int checkedIndex)
2497 int [] indices = GetIndices ();
2498 for (int i = 0; i < indices.Length; i++) {
2499 if (indices [i] == checkedIndex)
2500 return true;
2502 return false;
2505 public IEnumerator GetEnumerator ()
2507 int [] indices = GetIndices ();
2508 return indices.GetEnumerator ();
2511 void ICollection.CopyTo (Array dest, int index)
2513 int [] indices = GetIndices ();
2514 Array.Copy (indices, 0, dest, index, indices.Length);
2517 int IList.Add (object value)
2519 throw new NotSupportedException ("Add operation is not supported.");
2522 void IList.Clear ()
2524 throw new NotSupportedException ("Clear operation is not supported.");
2527 bool IList.Contains (object checkedIndex)
2529 if (!(checkedIndex is int))
2530 return false;
2531 return Contains ((int) checkedIndex);
2534 int IList.IndexOf (object checkedIndex)
2536 if (!(checkedIndex is int))
2537 return -1;
2538 return IndexOf ((int) checkedIndex);
2541 void IList.Insert (int index, object value)
2543 throw new NotSupportedException ("Insert operation is not supported.");
2546 void IList.Remove (object value)
2548 throw new NotSupportedException ("Remove operation is not supported.");
2551 void IList.RemoveAt (int index)
2553 throw new NotSupportedException ("RemoveAt operation is not supported.");
2556 public int IndexOf (int checkedIndex)
2558 int [] indices = GetIndices ();
2559 for (int i = 0; i < indices.Length; i++) {
2560 if (indices [i] == checkedIndex)
2561 return i;
2563 return -1;
2565 #endregion // Public Methods
2567 private int [] GetIndices ()
2569 ArrayList checked_items = owner.CheckedItems.List;
2570 int [] indices = new int [checked_items.Count];
2571 for (int i = 0; i < checked_items.Count; i++) {
2572 ListViewItem item = (ListViewItem) checked_items [i];
2573 indices [i] = item.Index;
2575 return indices;
2577 } // CheckedIndexCollection
2579 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2581 private readonly ListView owner;
2582 private ArrayList list;
2584 #region Public Constructor
2585 public CheckedListViewItemCollection (ListView owner)
2587 this.owner = owner;
2588 this.owner.Items.Changed += new CollectionChangedHandler (
2589 ItemsCollection_Changed);
2591 #endregion // Public Constructor
2593 #region Public Properties
2594 [Browsable (false)]
2595 public int Count {
2596 get {
2597 if (!owner.CheckBoxes)
2598 return 0;
2599 return List.Count;
2603 public bool IsReadOnly {
2604 get { return true; }
2607 public ListViewItem this [int index] {
2608 get {
2609 ArrayList checked_items = List;
2610 if (index < 0 || index >= checked_items.Count)
2611 throw new ArgumentOutOfRangeException ("index");
2612 return (ListViewItem) checked_items [index];
2616 bool ICollection.IsSynchronized {
2617 get { return false; }
2620 object ICollection.SyncRoot {
2621 get { return this; }
2624 bool IList.IsFixedSize {
2625 get { return true; }
2628 object IList.this [int index] {
2629 get { return this [index]; }
2630 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2632 #endregion // Public Properties
2634 #region Public Methods
2635 public bool Contains (ListViewItem item)
2637 if (!owner.CheckBoxes)
2638 return false;
2639 return List.Contains (item);
2642 public void CopyTo (Array dest, int index)
2644 if (!owner.CheckBoxes)
2645 return;
2646 List.CopyTo (dest, index);
2649 public IEnumerator GetEnumerator ()
2651 if (!owner.CheckBoxes)
2652 return (new ListViewItem [0]).GetEnumerator ();
2653 return List.GetEnumerator ();
2656 int IList.Add (object value)
2658 throw new NotSupportedException ("Add operation is not supported.");
2661 void IList.Clear ()
2663 throw new NotSupportedException ("Clear operation is not supported.");
2666 bool IList.Contains (object item)
2668 if (!(item is ListViewItem))
2669 return false;
2670 return Contains ((ListViewItem) item);
2673 int IList.IndexOf (object item)
2675 if (!(item is ListViewItem))
2676 return -1;
2677 return IndexOf ((ListViewItem) item);
2680 void IList.Insert (int index, object value)
2682 throw new NotSupportedException ("Insert operation is not supported.");
2685 void IList.Remove (object value)
2687 throw new NotSupportedException ("Remove operation is not supported.");
2690 void IList.RemoveAt (int index)
2692 throw new NotSupportedException ("RemoveAt operation is not supported.");
2695 public int IndexOf (ListViewItem item)
2697 if (!owner.CheckBoxes)
2698 return -1;
2699 return List.IndexOf (item);
2701 #endregion // Public Methods
2703 internal ArrayList List {
2704 get {
2705 if (list == null) {
2706 list = new ArrayList ();
2707 foreach (ListViewItem item in owner.Items) {
2708 if (item.Checked)
2709 list.Add (item);
2712 return list;
2716 internal void Reset ()
2718 // force re-population of list
2719 list = null;
2722 private void ItemsCollection_Changed ()
2724 Reset ();
2726 } // CheckedListViewItemCollection
2728 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2730 internal ArrayList list;
2731 private ListView owner;
2733 #region Public Constructor
2734 public ColumnHeaderCollection (ListView owner)
2736 list = new ArrayList ();
2737 this.owner = owner;
2739 #endregion // Public Constructor
2741 #region Public Properties
2742 [Browsable (false)]
2743 public int Count {
2744 get { return list.Count; }
2747 public bool IsReadOnly {
2748 get { return false; }
2751 public virtual ColumnHeader this [int index] {
2752 get {
2753 if (index < 0 || index >= list.Count)
2754 throw new ArgumentOutOfRangeException ("index");
2755 return (ColumnHeader) list [index];
2759 bool ICollection.IsSynchronized {
2760 get { return true; }
2763 object ICollection.SyncRoot {
2764 get { return this; }
2767 bool IList.IsFixedSize {
2768 get { return list.IsFixedSize; }
2771 object IList.this [int index] {
2772 get { return this [index]; }
2773 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2775 #endregion // Public Properties
2777 #region Public Methods
2778 public virtual int Add (ColumnHeader value)
2780 int idx;
2781 value.owner = this.owner;
2782 idx = list.Add (value);
2783 if (owner.IsHandleCreated) {
2784 owner.Redraw (true);
2786 return idx;
2789 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2791 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2792 this.Add (colHeader);
2793 return colHeader;
2796 public virtual void AddRange (ColumnHeader [] values)
2798 foreach (ColumnHeader colHeader in values) {
2799 colHeader.owner = this.owner;
2800 Add (colHeader);
2803 owner.Redraw (true);
2806 public virtual void Clear ()
2808 list.Clear ();
2809 owner.Redraw (true);
2812 public bool Contains (ColumnHeader value)
2814 return list.Contains (value);
2817 public IEnumerator GetEnumerator ()
2819 return list.GetEnumerator ();
2822 void ICollection.CopyTo (Array dest, int index)
2824 list.CopyTo (dest, index);
2827 int IList.Add (object value)
2829 if (! (value is ColumnHeader)) {
2830 throw new ArgumentException ("Not of type ColumnHeader", "value");
2833 return this.Add ((ColumnHeader) value);
2836 bool IList.Contains (object value)
2838 if (! (value is ColumnHeader)) {
2839 throw new ArgumentException ("Not of type ColumnHeader", "value");
2842 return this.Contains ((ColumnHeader) value);
2845 int IList.IndexOf (object value)
2847 if (! (value is ColumnHeader)) {
2848 throw new ArgumentException ("Not of type ColumnHeader", "value");
2851 return this.IndexOf ((ColumnHeader) value);
2854 void IList.Insert (int index, object value)
2856 if (! (value is ColumnHeader)) {
2857 throw new ArgumentException ("Not of type ColumnHeader", "value");
2860 this.Insert (index, (ColumnHeader) value);
2863 void IList.Remove (object value)
2865 if (! (value is ColumnHeader)) {
2866 throw new ArgumentException ("Not of type ColumnHeader", "value");
2869 this.Remove ((ColumnHeader) value);
2872 public int IndexOf (ColumnHeader value)
2874 return list.IndexOf (value);
2877 public void Insert (int index, ColumnHeader value)
2879 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2880 // but it's really only greater.
2881 if (index < 0 || index > list.Count)
2882 throw new ArgumentOutOfRangeException ("index");
2884 value.owner = this.owner;
2885 list.Insert (index, value);
2886 owner.Redraw (true);
2889 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2891 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2892 this.Insert (index, colHeader);
2895 public virtual void Remove (ColumnHeader column)
2897 // TODO: Update Column internal index ?
2898 list.Remove (column);
2899 owner.Redraw (true);
2902 public virtual void RemoveAt (int index)
2904 if (index < 0 || index >= list.Count)
2905 throw new ArgumentOutOfRangeException ("index");
2907 // TODO: Update Column internal index ?
2908 list.RemoveAt (index);
2909 owner.Redraw (true);
2911 #endregion // Public Methods
2914 } // ColumnHeaderCollection
2916 public class ListViewItemCollection : IList, ICollection, IEnumerable
2918 private readonly ArrayList list;
2919 private readonly ListView owner;
2921 #region Public Constructor
2922 public ListViewItemCollection (ListView owner)
2924 list = new ArrayList ();
2925 this.owner = owner;
2927 #endregion // Public Constructor
2929 #region Public Properties
2930 [Browsable (false)]
2931 public int Count {
2932 get { return list.Count; }
2935 public bool IsReadOnly {
2936 get { return false; }
2939 public virtual ListViewItem this [int displayIndex] {
2940 get {
2941 if (displayIndex < 0 || displayIndex >= list.Count)
2942 throw new ArgumentOutOfRangeException ("displayIndex");
2943 return (ListViewItem) list [displayIndex];
2946 set {
2947 if (displayIndex < 0 || displayIndex >= list.Count)
2948 throw new ArgumentOutOfRangeException ("displayIndex");
2950 if (list.Contains (value))
2951 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2953 value.Owner = owner;
2954 list [displayIndex] = value;
2955 OnChange ();
2957 owner.Redraw (true);
2961 bool ICollection.IsSynchronized {
2962 get { return true; }
2965 object ICollection.SyncRoot {
2966 get { return this; }
2969 bool IList.IsFixedSize {
2970 get { return list.IsFixedSize; }
2973 object IList.this [int index] {
2974 get { return this [index]; }
2975 set {
2976 if (value is ListViewItem)
2977 this [index] = (ListViewItem) value;
2978 else
2979 this [index] = new ListViewItem (value.ToString ());
2980 OnChange ();
2983 #endregion // Public Properties
2985 #region Public Methods
2986 public virtual ListViewItem Add (ListViewItem value)
2988 if (list.Contains (value))
2989 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2991 value.Owner = owner;
2992 list.Add (value);
2994 owner.Sort (false);
2995 OnChange ();
2996 owner.Redraw (true);
2997 return value;
3000 public virtual ListViewItem Add (string text)
3002 ListViewItem item = new ListViewItem (text);
3003 return this.Add (item);
3006 public virtual ListViewItem Add (string text, int imageIndex)
3008 ListViewItem item = new ListViewItem (text, imageIndex);
3009 return this.Add (item);
3012 public void AddRange (ListViewItem [] values)
3014 list.Clear ();
3016 foreach (ListViewItem item in values) {
3017 item.Owner = owner;
3018 list.Add (item);
3021 owner.Sort (false);
3022 OnChange ();
3023 owner.Redraw (true);
3026 public virtual void Clear ()
3028 owner.SetFocusedItem (null);
3029 owner.h_scroll.Value = owner.v_scroll.Value = 0;
3030 list.Clear ();
3031 OnChange ();
3032 owner.Redraw (true);
3035 public bool Contains (ListViewItem item)
3037 return list.Contains (item);
3040 public void CopyTo (Array dest, int index)
3042 list.CopyTo (dest, index);
3045 public IEnumerator GetEnumerator ()
3047 return list.GetEnumerator ();
3050 int IList.Add (object item)
3052 int result;
3053 ListViewItem li;
3055 if (item is ListViewItem) {
3056 li = (ListViewItem) item;
3057 if (list.Contains (li))
3058 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3060 else
3061 li = new ListViewItem (item.ToString ());
3063 li.Owner = owner;
3064 result = list.Add (li);
3065 OnChange ();
3066 owner.Redraw (true);
3068 return result;
3071 bool IList.Contains (object item)
3073 return list.Contains (item);
3076 int IList.IndexOf (object item)
3078 return list.IndexOf (item);
3081 void IList.Insert (int index, object item)
3083 if (item is ListViewItem)
3084 this.Insert (index, (ListViewItem) item);
3085 else
3086 this.Insert (index, item.ToString ());
3089 void IList.Remove (object item)
3091 Remove ((ListViewItem) item);
3094 public int IndexOf (ListViewItem item)
3096 return list.IndexOf (item);
3099 public ListViewItem Insert (int index, ListViewItem item)
3101 if (index < 0 || index > list.Count)
3102 throw new ArgumentOutOfRangeException ("index");
3104 if (list.Contains (item))
3105 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3107 item.Owner = owner;
3108 list.Insert (index, item);
3109 OnChange ();
3110 owner.Redraw (true);
3111 return item;
3114 public ListViewItem Insert (int index, string text)
3116 return this.Insert (index, new ListViewItem (text));
3119 public ListViewItem Insert (int index, string text, int imageIndex)
3121 return this.Insert (index, new ListViewItem (text, imageIndex));
3124 public virtual void Remove (ListViewItem item)
3126 if (!list.Contains (item))
3127 return;
3129 bool selection_changed = owner.SelectedItems.Contains (item);
3130 list.Remove (item);
3131 OnChange ();
3132 owner.Redraw (true);
3133 if (selection_changed)
3134 owner.OnSelectedIndexChanged (EventArgs.Empty);
3137 public virtual void RemoveAt (int index)
3139 if (index < 0 || index >= Count)
3140 throw new ArgumentOutOfRangeException ("index");
3141 bool selection_changed = owner.SelectedIndices.Contains (index);
3142 list.RemoveAt (index);
3143 OnChange ();
3144 owner.Redraw (false);
3145 if (selection_changed)
3146 owner.OnSelectedIndexChanged (EventArgs.Empty);
3148 #endregion // Public Methods
3150 internal event CollectionChangedHandler Changed;
3152 internal void Sort (IComparer comparer)
3154 list.Sort (comparer);
3155 OnChange ();
3158 internal void OnChange ()
3160 if (Changed != null)
3161 Changed ();
3163 } // ListViewItemCollection
3165 public class SelectedIndexCollection : IList, ICollection, IEnumerable
3167 private readonly ListView owner;
3169 #region Public Constructor
3170 public SelectedIndexCollection (ListView owner)
3172 this.owner = owner;
3174 #endregion // Public Constructor
3176 #region Public Properties
3177 [Browsable (false)]
3178 public int Count {
3179 get {
3180 return owner.SelectedItems.Count;
3184 public bool IsReadOnly {
3185 get { return true; }
3188 public int this [int index] {
3189 get {
3190 int [] indices = GetIndices ();
3191 if (index < 0 || index >= indices.Length)
3192 throw new ArgumentOutOfRangeException ("index");
3193 return indices [index];
3197 bool ICollection.IsSynchronized {
3198 get { return false; }
3201 object ICollection.SyncRoot {
3202 get { return this; }
3205 bool IList.IsFixedSize {
3206 get { return true; }
3209 object IList.this [int index] {
3210 get { return this [index]; }
3211 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3213 #endregion // Public Properties
3215 #region Public Methods
3216 public bool Contains (int selectedIndex)
3218 int [] indices = GetIndices ();
3219 for (int i = 0; i < indices.Length; i++) {
3220 if (indices [i] == selectedIndex)
3221 return true;
3223 return false;
3226 public void CopyTo (Array dest, int index)
3228 int [] indices = GetIndices ();
3229 Array.Copy (indices, 0, dest, index, indices.Length);
3232 public IEnumerator GetEnumerator ()
3234 int [] indices = GetIndices ();
3235 return indices.GetEnumerator ();
3238 int IList.Add (object value)
3240 throw new NotSupportedException ("Add operation is not supported.");
3243 void IList.Clear ()
3245 throw new NotSupportedException ("Clear operation is not supported.");
3248 bool IList.Contains (object selectedIndex)
3250 if (!(selectedIndex is int))
3251 return false;
3252 return Contains ((int) selectedIndex);
3255 int IList.IndexOf (object selectedIndex)
3257 if (!(selectedIndex is int))
3258 return -1;
3259 return IndexOf ((int) selectedIndex);
3262 void IList.Insert (int index, object value)
3264 throw new NotSupportedException ("Insert operation is not supported.");
3267 void IList.Remove (object value)
3269 throw new NotSupportedException ("Remove operation is not supported.");
3272 void IList.RemoveAt (int index)
3274 throw new NotSupportedException ("RemoveAt operation is not supported.");
3277 public int IndexOf (int selectedIndex)
3279 int [] indices = GetIndices ();
3280 for (int i = 0; i < indices.Length; i++) {
3281 if (indices [i] == selectedIndex)
3282 return i;
3284 return -1;
3286 #endregion // Public Methods
3288 private int [] GetIndices ()
3290 ArrayList selected_items = owner.SelectedItems.List;
3291 int [] indices = new int [selected_items.Count];
3292 for (int i = 0; i < selected_items.Count; i++) {
3293 ListViewItem item = (ListViewItem) selected_items [i];
3294 indices [i] = item.Index;
3296 return indices;
3298 } // SelectedIndexCollection
3300 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
3302 private readonly ListView owner;
3303 private ArrayList list;
3305 #region Public Constructor
3306 public SelectedListViewItemCollection (ListView owner)
3308 this.owner = owner;
3309 this.owner.Items.Changed += new CollectionChangedHandler (
3310 ItemsCollection_Changed);
3312 #endregion // Public Constructor
3314 #region Public Properties
3315 [Browsable (false)]
3316 public int Count {
3317 get {
3318 if (!owner.IsHandleCreated)
3319 return 0;
3320 return List.Count;
3324 public bool IsReadOnly {
3325 get { return true; }
3328 public ListViewItem this [int index] {
3329 get {
3330 ArrayList selected_items = List;
3331 if (!owner.IsHandleCreated || index < 0 || index >= selected_items.Count)
3332 throw new ArgumentOutOfRangeException ("index");
3333 return (ListViewItem) selected_items [index];
3337 bool ICollection.IsSynchronized {
3338 get { return false; }
3341 object ICollection.SyncRoot {
3342 get { return this; }
3345 bool IList.IsFixedSize {
3346 get { return true; }
3349 object IList.this [int index] {
3350 get { return this [index]; }
3351 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3353 #endregion // Public Properties
3355 #region Public Methods
3356 public void Clear ()
3358 if (!owner.IsHandleCreated)
3359 return;
3361 foreach (ListViewItem item in List)
3362 item.Selected = false;
3365 public bool Contains (ListViewItem item)
3367 if (!owner.IsHandleCreated)
3368 return false;
3369 return List.Contains (item);
3372 public void CopyTo (Array dest, int index)
3374 if (!owner.IsHandleCreated)
3375 return;
3376 List.CopyTo (dest, index);
3379 public IEnumerator GetEnumerator ()
3381 if (!owner.IsHandleCreated)
3382 return (new ListViewItem [0]).GetEnumerator ();
3383 return List.GetEnumerator ();
3386 int IList.Add (object value)
3388 throw new NotSupportedException ("Add operation is not supported.");
3391 bool IList.Contains (object item)
3393 if (!(item is ListViewItem))
3394 return false;
3395 return Contains ((ListViewItem) item);
3398 int IList.IndexOf (object item)
3400 if (!(item is ListViewItem))
3401 return -1;
3402 return IndexOf ((ListViewItem) item);
3405 void IList.Insert (int index, object value)
3407 throw new NotSupportedException ("Insert operation is not supported.");
3410 void IList.Remove (object value)
3412 throw new NotSupportedException ("Remove operation is not supported.");
3415 void IList.RemoveAt (int index)
3417 throw new NotSupportedException ("RemoveAt operation is not supported.");
3420 public int IndexOf (ListViewItem item)
3422 if (!owner.IsHandleCreated)
3423 return -1;
3424 return List.IndexOf (item);
3426 #endregion // Public Methods
3428 internal ArrayList List {
3429 get {
3430 if (list == null) {
3431 list = new ArrayList ();
3432 foreach (ListViewItem item in owner.Items) {
3433 if (item.Selected)
3434 list.Add (item);
3437 return list;
3441 internal void Reset ()
3443 // force re-population of list
3444 list = null;
3447 private void ItemsCollection_Changed ()
3449 Reset ();
3451 } // SelectedListViewItemCollection
3453 internal delegate void CollectionChangedHandler ();
3455 #endregion // Subclasses