* ToolTip.cs: use Visible instead of is_visible.
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ListView.cs
blobd498fa5f5b3e3a23a2ac19f0c3d852c294b237ee
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 base.BackgroundImage; }
307 set { base.BackgroundImage = value; }
310 [DefaultValue (BorderStyle.Fixed3D)]
311 [DispId (-504)]
312 public BorderStyle BorderStyle {
313 get { return InternalBorderStyle; }
314 set { InternalBorderStyle = value; }
317 [DefaultValue (false)]
318 public bool CheckBoxes {
319 get { return check_boxes; }
320 set {
321 if (check_boxes != value) {
322 #if NET_2_0
323 if (value && View == View.Tile)
324 throw new NotSupportedException ("CheckBoxes are not"
325 + " supported in Tile view. Choose a different"
326 + " view or set CheckBoxes to false.");
327 #endif
329 check_boxes = value;
330 this.Redraw (true);
335 [Browsable (false)]
336 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
337 public CheckedIndexCollection CheckedIndices {
338 get { return checked_indices; }
341 [Browsable (false)]
342 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
343 public CheckedListViewItemCollection CheckedItems {
344 get { return checked_items; }
347 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
348 [Localizable (true)]
349 [MergableProperty (false)]
350 public ColumnHeaderCollection Columns {
351 get { return columns; }
354 [Browsable (false)]
355 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
356 public ListViewItem FocusedItem {
357 get {
358 return focused_item;
362 public override Color ForeColor {
363 get {
364 if (foreground_color.IsEmpty)
365 return ThemeEngine.Current.ColorWindowText;
366 else
367 return foreground_color;
369 set { foreground_color = value; }
372 [DefaultValue (false)]
373 public bool FullRowSelect {
374 get { return full_row_select; }
375 set { full_row_select = value; }
378 [DefaultValue (false)]
379 public bool GridLines {
380 get { return grid_lines; }
381 set {
382 if (grid_lines != value) {
383 grid_lines = value;
384 this.Redraw (false);
389 [DefaultValue (ColumnHeaderStyle.Clickable)]
390 public ColumnHeaderStyle HeaderStyle {
391 get { return header_style; }
392 set {
393 if (header_style == value)
394 return;
396 switch (value) {
397 case ColumnHeaderStyle.Clickable:
398 case ColumnHeaderStyle.Nonclickable:
399 case ColumnHeaderStyle.None:
400 break;
401 default:
402 throw new InvalidEnumArgumentException (string.Format
403 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
406 header_style = value;
407 if (view == View.Details)
408 Redraw (true);
412 [DefaultValue (true)]
413 public bool HideSelection {
414 get { return hide_selection; }
415 set {
416 if (hide_selection != value) {
417 hide_selection = value;
418 this.Redraw (false);
423 [DefaultValue (false)]
424 public bool HoverSelection {
425 get { return hover_selection; }
426 set { hover_selection = value; }
429 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
430 [Localizable (true)]
431 [MergableProperty (false)]
432 public ListViewItemCollection Items {
433 get { return items; }
436 [DefaultValue (false)]
437 public bool LabelEdit {
438 get { return label_edit; }
439 set { label_edit = value; }
442 [DefaultValue (true)]
443 [Localizable (true)]
444 public bool LabelWrap {
445 get { return label_wrap; }
446 set {
447 if (label_wrap != value) {
448 label_wrap = value;
449 this.Redraw (true);
454 [DefaultValue (null)]
455 public ImageList LargeImageList {
456 get { return large_image_list; }
457 set {
458 large_image_list = value;
459 this.Redraw (true);
463 [Browsable (false)]
464 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
465 public IComparer ListViewItemSorter {
466 get {
467 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
468 return null;
469 return item_sorter;
471 set {
472 if (item_sorter != value) {
473 item_sorter = value;
474 Sort ();
479 [DefaultValue (true)]
480 public bool MultiSelect {
481 get { return multiselect; }
482 set { multiselect = value; }
485 [DefaultValue (true)]
486 public bool Scrollable {
487 get { return scrollable; }
488 set {
489 if (scrollable != value) {
490 scrollable = value;
491 this.Redraw (true);
496 [Browsable (false)]
497 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
498 public SelectedIndexCollection SelectedIndices {
499 get { return selected_indices; }
502 [Browsable (false)]
503 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
504 public SelectedListViewItemCollection SelectedItems {
505 get { return selected_items; }
508 #if NET_2_0
509 [MonoTODO("Implement")]
510 public bool ShowGroups {
511 get {
512 return false;
515 set {
518 #endif
520 [DefaultValue (null)]
521 public ImageList SmallImageList {
522 get { return small_image_list; }
523 set {
524 small_image_list = value;
525 this.Redraw (true);
529 [DefaultValue (SortOrder.None)]
530 public SortOrder Sorting {
531 get { return sort_order; }
532 set {
533 if (!Enum.IsDefined (typeof (SortOrder), value)) {
534 throw new InvalidEnumArgumentException ("value", (int) value,
535 typeof (SortOrder));
538 if (sort_order == value)
539 return;
541 sort_order = value;
543 if (value == SortOrder.None) {
544 if (item_sorter != null) {
545 // ListViewItemSorter should never be reset for SmallIcon
546 // and LargeIcon view
547 if (View != View.SmallIcon && View != View.LargeIcon)
548 #if NET_2_0
549 item_sorter = null;
550 #else
551 // in .NET 1.1, only internal IComparer would be
552 // set to null
553 if (item_sorter is ItemComparer)
554 item_sorter = null;
555 #endif
557 this.Redraw (false);
558 } else {
559 if (item_sorter == null)
560 item_sorter = new ItemComparer (value);
561 if (item_sorter is ItemComparer) {
562 #if NET_2_0
563 item_sorter = new ItemComparer (value);
564 #else
565 // in .NET 1.1, the sort order is not updated for
566 // SmallIcon and LargeIcon views if no custom IComparer
567 // is set
568 if (View != View.SmallIcon && View != View.LargeIcon)
569 item_sorter = new ItemComparer (value);
570 #endif
572 Sort ();
577 [DefaultValue (null)]
578 public ImageList StateImageList {
579 get { return state_image_list; }
580 set {
581 state_image_list = value;
582 this.Redraw (true);
586 [Bindable (false)]
587 [Browsable (false)]
588 [EditorBrowsable (EditorBrowsableState.Never)]
589 public override string Text {
590 get { return base.Text; }
591 set {
592 if (value == base.Text)
593 return;
595 base.Text = value;
596 this.Redraw (true);
600 [Browsable (false)]
601 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
602 public ListViewItem TopItem {
603 get {
604 // there is no item
605 if (this.items.Count == 0)
606 return null;
607 // if contents are not scrolled
608 // it is the first item
609 else if (h_marker == 0 && v_marker == 0)
610 return this.items [0];
611 // do a hit test for the scrolled position
612 else {
613 foreach (ListViewItem item in this.items) {
614 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
615 return item;
617 return null;
622 #if NET_2_0
623 [MonoTODO("Implement")]
624 public bool UseCompatibleStateImageBehavior {
625 get {
626 return false;
629 set {
632 #endif
634 [DefaultValue (View.LargeIcon)]
635 public View View {
636 get { return view; }
637 set {
638 if (!Enum.IsDefined (typeof (View), value))
639 throw new InvalidEnumArgumentException ("value", (int) value,
640 typeof (View));
642 if (view != value) {
643 #if NET_2_0
644 if (CheckBoxes && value == View.Tile)
645 throw new NotSupportedException ("CheckBoxes are not"
646 + " supported in Tile view. Choose a different"
647 + " view or set CheckBoxes to false.");
648 #endif
650 h_scroll.Value = v_scroll.Value = 0;
651 view = value;
652 Redraw (true);
656 #endregion // Public Instance Properties
658 #region Internal Methods Properties
660 internal int FirstVisibleIndex {
661 get {
662 // there is no item
663 if (this.items.Count == 0)
664 return 0;
666 if (h_marker == 0 && v_marker == 0)
667 return 0;
669 foreach (ListViewItem item in this.items) {
670 if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
671 return item.Index;
673 return 0;
679 internal int LastVisibleIndex {
680 get {
681 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
682 if (View == View.List || Alignment == ListViewAlignment.Left) {
683 if (Items[i].Bounds.X > ClientRectangle.Right)
684 return i - 1;
685 } else {
686 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
687 return i - 1;
691 return Items.Count - 1;
695 internal void OnSelectedIndexChanged ()
697 if (IsHandleCreated)
698 OnSelectedIndexChanged (EventArgs.Empty);
701 internal int TotalWidth {
702 get { return Math.Max (this.Width, this.layout_wd); }
705 internal int TotalHeight {
706 get { return Math.Max (this.Height, this.layout_ht); }
709 internal void Redraw (bool recalculate)
711 // Avoid calculations when control is being updated
712 if (this.updating)
713 return;
715 if (recalculate)
716 CalculateListView (this.alignment);
718 Refresh ();
721 internal Size GetChildColumnSize (int index)
723 Size ret_size = Size.Empty;
724 ColumnHeader col = this.columns [index];
726 if (col.Width == -2) { // autosize = max(items, columnheader)
727 Size size = Size.Ceiling (this.DeviceContext.MeasureString
728 (col.Text, this.Font));
729 ret_size = BiggestItem (index);
730 if (size.Width > ret_size.Width)
731 ret_size = size;
733 else { // -1 and all the values < -2 are put under one category
734 ret_size = BiggestItem (index);
735 // fall back to empty columns' width if no subitem is available for a column
736 if (ret_size.IsEmpty) {
737 ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
738 if (col.Text.Length > 0)
739 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
740 (col.Text, this.Font)).Height;
741 else
742 ret_size.Height = this.Font.Height;
746 // adjust the size for icon and checkbox for 0th column
747 if (index == 0) {
748 ret_size.Width += (this.CheckBoxSize.Width + 4);
749 if (this.small_image_list != null)
750 ret_size.Width += this.small_image_list.ImageSize.Width;
752 return ret_size;
755 // Returns the size of biggest item text in a column.
756 private Size BiggestItem (int col)
758 Size temp = Size.Empty;
759 Size ret_size = Size.Empty;
761 // 0th column holds the item text, we check the size of
762 // the various subitems falling in that column and get
763 // the biggest one's size.
764 foreach (ListViewItem item in items) {
765 if (col >= item.SubItems.Count)
766 continue;
768 temp = Size.Ceiling (this.DeviceContext.MeasureString
769 (item.SubItems [col].Text, this.Font));
770 if (temp.Width > ret_size.Width)
771 ret_size = temp;
774 // adjustment for space
775 if (!ret_size.IsEmpty)
776 ret_size.Width += 4;
778 return ret_size;
781 const int max_wrap_padding = 38;
783 // Sets the size of the biggest item text as per the view
784 private void CalcTextSize ()
786 // clear the old value
787 text_size = Size.Empty;
789 if (items.Count == 0)
790 return;
792 text_size = BiggestItem (0);
794 if (view == View.LargeIcon && this.label_wrap) {
795 Size temp = Size.Empty;
796 if (this.check_boxes)
797 temp.Width += 2 * this.CheckBoxSize.Width;
798 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
799 temp.Width += icon_w + max_wrap_padding;
800 // wrapping is done for two lines only
801 if (text_size.Width > temp.Width) {
802 text_size.Width = temp.Width;
803 text_size.Height *= 2;
806 else if (view == View.List) {
807 // in list view max text shown in determined by the
808 // control width, even if scolling is enabled.
809 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
810 if (this.small_image_list != null)
811 max_wd -= this.small_image_list.ImageSize.Width;
813 if (text_size.Width > max_wd)
814 text_size.Width = max_wd;
817 // we do the default settings, if we have got 0's
818 if (text_size.Height <= 0)
819 text_size.Height = this.Font.Height;
820 if (text_size.Width <= 0)
821 text_size.Width = this.Width;
823 // little adjustment
824 text_size.Width += 4;
825 text_size.Height += 2;
828 private void Scroll (ScrollBar scrollbar, int delta)
830 if (delta == 0 || !scrollbar.Visible)
831 return;
833 int max;
834 if (scrollbar == h_scroll)
835 max = h_scroll.Maximum - item_control.Width;
836 else
837 max = v_scroll.Maximum - item_control.Height;
839 int val = scrollbar.Value + delta;
840 if (val > max)
841 val = max;
842 else if (val < scrollbar.Minimum)
843 val = scrollbar.Minimum;
844 scrollbar.Value = val;
847 private void CalculateScrollBars ()
849 Rectangle client_area = ClientRectangle;
851 if (!this.scrollable || this.items.Count <= 0) {
852 h_scroll.Visible = false;
853 v_scroll.Visible = false;
854 item_control.Location = new Point (0, header_control.Height);
855 item_control.Height = ClientRectangle.Width - header_control.Height;
856 item_control.Width = ClientRectangle.Width;
857 header_control.Width = ClientRectangle.Width;
858 return;
861 // Don't calculate if the view is not displayable
862 if (client_area.Height < 0 || client_area.Width < 0)
863 return;
865 // making a scroll bar visible might make
866 // other scroll bar visible
867 if (layout_wd > client_area.Right) {
868 h_scroll.Visible = true;
869 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
870 v_scroll.Visible = true;
871 else
872 v_scroll.Visible = false;
873 } else if (layout_ht > client_area.Bottom) {
874 v_scroll.Visible = true;
875 if ((layout_wd + v_scroll.Width) > client_area.Right)
876 h_scroll.Visible = true;
877 else
878 h_scroll.Visible = false;
879 } else {
880 h_scroll.Visible = false;
881 v_scroll.Visible = false;
884 item_control.Height = ClientRectangle.Height - header_control.Height;
886 if (h_scroll.Visible) {
887 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
888 h_scroll.Minimum = 0;
890 // if v_scroll is visible, adjust the maximum of the
891 // h_scroll to account for the width of v_scroll
892 if (v_scroll.Visible) {
893 h_scroll.Maximum = layout_wd + v_scroll.Width;
894 h_scroll.Width = client_area.Width - v_scroll.Width;
896 else {
897 h_scroll.Maximum = layout_wd;
898 h_scroll.Width = client_area.Width;
901 h_scroll.LargeChange = client_area.Width;
902 h_scroll.SmallChange = Font.Height;
903 item_control.Height -= h_scroll.Height;
906 if (header_control.Visible)
907 header_control.Width = ClientRectangle.Width;
908 item_control.Width = ClientRectangle.Width;
910 if (v_scroll.Visible) {
911 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
912 v_scroll.Minimum = 0;
914 // if h_scroll is visible, adjust the maximum of the
915 // v_scroll to account for the height of h_scroll
916 if (h_scroll.Visible) {
917 v_scroll.Maximum = layout_ht + h_scroll.Height;
918 v_scroll.Height = client_area.Height; // - h_scroll.Height already done
919 } else {
920 v_scroll.Maximum = layout_ht;
921 v_scroll.Height = client_area.Height;
924 v_scroll.LargeChange = client_area.Height;
925 v_scroll.SmallChange = Font.Height;
926 if (header_control.Visible)
927 header_control.Width -= v_scroll.Width;
928 item_control.Width -= v_scroll.Width;
932 ColumnHeader GetReorderedColumn (int index)
934 if (reordered_column_indices == null)
935 return Columns [index];
936 else
937 return Columns [reordered_column_indices [index]];
940 void ReorderColumn (ColumnHeader col, int index)
942 if (reordered_column_indices == null) {
943 reordered_column_indices = new int [Columns.Count];
944 for (int i = 0; i < Columns.Count; i++)
945 reordered_column_indices [i] = i;
948 if (reordered_column_indices [index] == col.Index)
949 return;
951 int[] curr = reordered_column_indices;
952 int[] result = new int [Columns.Count];
953 int curr_idx = 0;
954 for (int i = 0; i < Columns.Count; i++) {
955 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
956 curr_idx++;
958 if (i == index)
959 result [i] = col.Index;
960 else
961 result [i] = curr [curr_idx++];
964 reordered_column_indices = result;
965 LayoutDetails ();
966 header_control.Invalidate ();
967 item_control.Invalidate ();
970 Size LargeIconItemSize {
971 get {
972 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
973 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
974 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
975 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
976 return new Size (w, h);
980 Size SmallIconItemSize {
981 get {
982 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
983 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
984 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
985 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
986 return new Size (w, h);
990 int rows;
991 int cols;
992 ListViewItem[,] item_matrix;
994 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
996 header_control.Visible = false;
997 header_control.Size = Size.Empty;
998 item_control.Visible = true;
999 item_control.Location = Point.Empty;
1001 if (items.Count == 0)
1002 return;
1004 Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
1006 Rectangle area = ClientRectangle;
1008 if (left_aligned) {
1009 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
1010 if (rows <= 0)
1011 rows = 1;
1012 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
1013 } else {
1014 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
1015 if (cols <= 0)
1016 cols = 1;
1017 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
1020 layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
1021 layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
1022 item_matrix = new ListViewItem [rows, cols];
1023 int row = 0;
1024 int col = 0;
1025 foreach (ListViewItem item in items) {
1026 int x = col * (sz.Width + x_spacing);
1027 int y = row * (sz.Height + y_spacing);
1028 item.Location = new Point (x, y);
1029 item.Layout ();
1030 item.row = row;
1031 item.col = col;
1032 item_matrix [row, col] = item;
1033 if (left_aligned) {
1034 if (++row == rows) {
1035 row = 0;
1036 col++;
1038 } else {
1039 if (++col == cols) {
1040 col = 0;
1041 row++;
1046 item_control.Size = new Size (layout_wd, layout_ht);
1049 void LayoutHeader ()
1051 int x = 0;
1052 for (int i = 0; i < Columns.Count; i++) {
1053 ColumnHeader col = GetReorderedColumn (i);
1054 col.X = x;
1055 col.Y = 0;
1056 col.CalcColumnHeader ();
1057 x += col.Wd;
1060 if (x < ClientRectangle.Width)
1061 x = ClientRectangle.Width;
1063 if (header_style == ColumnHeaderStyle.None) {
1064 header_control.Visible = false;
1065 header_control.Size = Size.Empty;
1066 } else {
1067 header_control.Width = x;
1068 header_control.Height = columns [0].Ht;
1069 header_control.Visible = true;
1073 void LayoutDetails ()
1075 if (columns.Count == 0) {
1076 header_control.Visible = false;
1077 item_control.Visible = false;
1078 return;
1081 LayoutHeader ();
1083 item_control.Visible = true;
1084 item_control.Location = new Point (0, header_control.Height);
1086 int y = 0;
1087 if (items.Count > 0) {
1088 foreach (ListViewItem item in items) {
1089 item.Layout ();
1090 item.Location = new Point (0, y);
1091 y += item.Bounds.Height + 2;
1094 // some space for bottom gridline
1095 if (grid_lines)
1096 y += 2;
1099 layout_wd = Math.Max (header_control.Width, item_control.Width);
1100 layout_ht = y + header_control.Height;
1103 private void CalculateListView (ListViewAlignment align)
1105 CalcTextSize ();
1107 switch (view) {
1108 case View.Details:
1109 LayoutDetails ();
1110 break;
1112 case View.SmallIcon:
1113 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1114 break;
1116 case View.LargeIcon:
1117 LayoutIcons (true, alignment == ListViewAlignment.Left,
1118 ThemeEngine.Current.ListViewHorizontalSpacing,
1119 ThemeEngine.Current.ListViewVerticalSpacing);
1120 break;
1122 case View.List:
1123 LayoutIcons (false, true, 4, 2);
1124 break;
1127 CalculateScrollBars ();
1130 private bool KeySearchString (KeyEventArgs ke)
1132 int current_tickcnt = Environment.TickCount;
1133 if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1134 keysearch_text = string.Empty;
1137 keysearch_text += (char) ke.KeyData;
1138 keysearch_tickcnt = current_tickcnt;
1140 int start = FocusedItem == null ? 0 : FocusedItem.Index;
1141 int i = start;
1142 while (true) {
1143 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1144 CompareOptions.IgnoreCase)) {
1145 SetFocusedItem (Items [i]);
1146 items [i].Selected = true;
1147 EnsureVisible (i);
1148 break;
1150 i = (i + 1 < Items.Count) ? i+1 : 0;
1152 if (i == start)
1153 break;
1155 return true;
1158 int GetAdjustedIndex (Keys key)
1160 int result = -1;
1162 if (View == View.Details) {
1163 if (key == Keys.Up)
1164 result = FocusedItem.Index - 1;
1165 else if (key == Keys.Down) {
1166 result = FocusedItem.Index + 1;
1167 if (result == items.Count)
1168 result = -1;
1170 return result;
1173 int row = FocusedItem.row;
1174 int col = FocusedItem.col;
1176 switch (key) {
1177 case Keys.Left:
1178 if (col == 0)
1179 return -1;
1180 return item_matrix [row, col - 1].Index;
1182 case Keys.Right:
1183 if (col == (cols - 1))
1184 return -1;
1185 while (item_matrix [row, col + 1] == null)
1186 row--;
1187 return item_matrix [row, col + 1].Index;
1189 case Keys.Up:
1190 if (row == 0)
1191 return -1;
1192 return item_matrix [row - 1, col].Index;
1194 case Keys.Down:
1195 if (row == (rows - 1) || row == Items.Count - 1)
1196 return -1;
1197 while (item_matrix [row + 1, col] == null)
1198 col--;
1199 return item_matrix [row + 1, col].Index;
1201 default:
1202 return -1;
1206 ListViewItem selection_start;
1208 private bool SelectItems (ArrayList sel_items)
1210 bool changed = false;
1211 ArrayList curr_items = SelectedItems.List;
1212 foreach (ListViewItem item in curr_items)
1213 if (!sel_items.Contains (item)) {
1214 item.Selected = false;
1215 changed = true;
1217 foreach (ListViewItem item in sel_items)
1218 if (!item.Selected) {
1219 item.Selected = true;
1220 changed = true;
1222 return changed;
1225 private void UpdateMultiSelection (int index)
1227 bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1228 bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1229 ListViewItem item = items [index];
1231 if (shift_pressed && selection_start != null) {
1232 ArrayList list = new ArrayList ();
1233 int start = Math.Min (selection_start.Index, index);
1234 int end = Math.Max (selection_start.Index, index);
1235 if (View == View.Details) {
1236 for (int i = start; i <= end; i++)
1237 list.Add (items [i]);
1238 } else {
1239 int left = Math.Min (items [start].col, items [end].col);
1240 int right = Math.Max (items [start].col, items [end].col);
1241 int top = Math.Min (items [start].row, items [end].row);
1242 int bottom = Math.Max (items [start].row, items [end].row);
1243 foreach (ListViewItem curr in items)
1244 if (curr.row >= top && curr.row <= bottom &&
1245 curr.col >= left && curr.col <= right)
1246 list.Add (curr);
1248 if (SelectItems (list))
1249 OnSelectedIndexChanged (EventArgs.Empty);
1250 } else if (ctrl_pressed) {
1251 item.Selected = !item.Selected;
1252 selection_start = item;
1253 OnSelectedIndexChanged (EventArgs.Empty);
1254 } else {
1255 SelectedItems.Clear ();
1256 item.Selected = true;
1257 selection_start = item;
1258 OnSelectedIndexChanged (EventArgs.Empty);
1262 internal override bool InternalPreProcessMessage (ref Message msg)
1264 if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1265 Keys key_data = (Keys)msg.WParam.ToInt32();
1266 if (HandleNavKeys (key_data))
1267 return true;
1269 return base.InternalPreProcessMessage (ref msg);
1272 bool HandleNavKeys (Keys key_data)
1274 if (Items.Count == 0 || !item_control.Visible)
1275 return false;
1277 if (FocusedItem == null)
1278 SetFocusedItem (Items [0]);
1280 switch (key_data) {
1281 case Keys.End:
1282 SelectIndex (Items.Count - 1);
1283 break;
1285 case Keys.Home:
1286 SelectIndex (0);
1287 break;
1289 case Keys.Left:
1290 case Keys.Right:
1291 case Keys.Up:
1292 case Keys.Down:
1293 SelectIndex (GetAdjustedIndex (key_data));
1294 break;
1296 default:
1297 return false;
1300 return true;
1303 void SelectIndex (int index)
1305 if (index == -1)
1306 return;
1308 if (MultiSelect)
1309 UpdateMultiSelection (index);
1310 else if (!items [index].Selected) {
1311 items [index].Selected = true;
1312 OnSelectedIndexChanged (EventArgs.Empty);
1315 SetFocusedItem (items [index]);
1316 EnsureVisible (index);
1319 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1321 if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1322 return;
1324 ke.Handled = KeySearchString (ke);
1327 internal class ItemControl : Control {
1329 ListView owner;
1330 ListViewItem clicked_item;
1331 ListViewItem last_clicked_item;
1332 bool hover_processed = false;
1333 bool checking = false;
1335 ListViewLabelEditTextBox edit_text_box;
1336 internal ListViewItem edit_item;
1337 LabelEditEventArgs edit_args;
1339 public ItemControl (ListView owner)
1341 this.owner = owner;
1342 DoubleClick += new EventHandler(ItemsDoubleClick);
1343 MouseDown += new MouseEventHandler(ItemsMouseDown);
1344 MouseMove += new MouseEventHandler(ItemsMouseMove);
1345 MouseHover += new EventHandler(ItemsMouseHover);
1346 MouseUp += new MouseEventHandler(ItemsMouseUp);
1349 void ItemsDoubleClick (object sender, EventArgs e)
1351 if (owner.activation == ItemActivation.Standard)
1352 owner.OnItemActivate (EventArgs.Empty);
1355 enum BoxSelect {
1356 None,
1357 Normal,
1358 Shift,
1359 Control
1362 BoxSelect box_select_mode = BoxSelect.None;
1363 ArrayList prev_selection;
1364 Point box_select_start;
1366 Rectangle box_select_rect;
1367 internal Rectangle BoxSelectRectangle {
1368 get { return box_select_rect; }
1369 set {
1370 if (box_select_rect == value)
1371 return;
1373 InvalidateBoxSelectRect ();
1374 box_select_rect = value;
1375 InvalidateBoxSelectRect ();
1379 void InvalidateBoxSelectRect ()
1381 if (BoxSelectRectangle.Size.IsEmpty)
1382 return;
1384 Rectangle edge = BoxSelectRectangle;
1385 edge.X -= 1;
1386 edge.Y -= 1;
1387 edge.Width += 2;
1388 edge.Height = 2;
1389 Invalidate (edge);
1390 edge.Y = BoxSelectRectangle.Bottom - 1;
1391 Invalidate (edge);
1392 edge.Y = BoxSelectRectangle.Y - 1;
1393 edge.Width = 2;
1394 edge.Height = BoxSelectRectangle.Height + 2;
1395 Invalidate (edge);
1396 edge.X = BoxSelectRectangle.Right - 1;
1397 Invalidate (edge);
1400 private Rectangle CalculateBoxSelectRectangle (Point pt)
1402 int left = Math.Min (box_select_start.X, pt.X);
1403 int right = Math.Max (box_select_start.X, pt.X);
1404 int top = Math.Min (box_select_start.Y, pt.Y);
1405 int bottom = Math.Max (box_select_start.Y, pt.Y);
1406 return Rectangle.FromLTRB (left, top, right, bottom);
1409 ArrayList BoxSelectedItems {
1410 get {
1411 ArrayList result = new ArrayList ();
1412 foreach (ListViewItem item in owner.Items) {
1413 Rectangle r = item.Bounds;
1414 r.X += r.Width / 4;
1415 r.Y += r.Height / 4;
1416 r.Width /= 2;
1417 r.Height /= 2;
1418 if (BoxSelectRectangle.IntersectsWith (r))
1419 result.Add (item);
1421 return result;
1425 private bool PerformBoxSelection (Point pt)
1427 if (box_select_mode == BoxSelect.None)
1428 return false;
1430 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1432 ArrayList box_items = BoxSelectedItems;
1434 ArrayList items;
1436 switch (box_select_mode) {
1438 case BoxSelect.Normal:
1439 items = box_items;
1440 break;
1442 case BoxSelect.Control:
1443 items = new ArrayList ();
1444 foreach (ListViewItem item in prev_selection)
1445 if (!box_items.Contains (item))
1446 items.Add (item);
1447 foreach (ListViewItem item in box_items)
1448 if (!prev_selection.Contains (item))
1449 items.Add (item);
1450 break;
1452 case BoxSelect.Shift:
1453 items = box_items;
1454 foreach (ListViewItem item in box_items)
1455 prev_selection.Remove (item);
1456 foreach (ListViewItem item in prev_selection)
1457 items.Add (item);
1458 break;
1460 default:
1461 throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1464 SuspendLayout ();
1465 owner.SelectItems (items);
1466 ResumeLayout ();
1468 return true;
1471 private void ToggleCheckState (ListViewItem item)
1473 CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1474 item.Checked = !item.Checked;
1475 CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked;
1477 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1478 owner.OnItemCheck (ice);
1481 private void ItemsMouseDown (object sender, MouseEventArgs me)
1483 if (owner.items.Count == 0)
1484 return;
1486 Point pt = new Point (me.X, me.Y);
1487 foreach (ListViewItem item in owner.items) {
1488 if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1489 checking = true;
1490 if (me.Clicks > 1)
1491 return;
1492 ToggleCheckState (item);
1493 return;
1496 if (owner.View == View.Details && !owner.FullRowSelect) {
1497 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1498 clicked_item = item;
1499 break;
1501 } else {
1502 if (item.Bounds.Contains (pt)) {
1503 clicked_item = item;
1504 break;
1510 if (clicked_item != null) {
1511 owner.SetFocusedItem (clicked_item);
1512 bool changed = !clicked_item.Selected;
1513 if (owner.MultiSelect)
1514 owner.UpdateMultiSelection (clicked_item.Index);
1515 else
1516 clicked_item.Selected = true;
1518 if (changed)
1519 owner.OnSelectedIndexChanged (EventArgs.Empty);
1521 // Raise double click if the item was clicked. On MS the
1522 // double click is only raised if you double click an item
1523 if (me.Clicks > 1) {
1524 owner.OnDoubleClick (EventArgs.Empty);
1525 if (owner.CheckBoxes)
1526 ToggleCheckState (clicked_item);
1527 } else if (me.Clicks == 1) {
1528 owner.OnClick (EventArgs.Empty);
1529 if (owner.LabelEdit && !changed)
1530 BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
1532 } else {
1533 if (owner.MultiSelect) {
1534 Keys mods = XplatUI.State.ModifierKeys;
1535 if ((mods & Keys.Shift) != 0)
1536 box_select_mode = BoxSelect.Shift;
1537 else if ((mods & Keys.Control) != 0)
1538 box_select_mode = BoxSelect.Control;
1539 else
1540 box_select_mode = BoxSelect.Normal;
1541 box_select_start = pt;
1542 prev_selection = owner.SelectedItems.List;
1543 } else if (owner.SelectedItems.Count > 0) {
1544 owner.SelectedItems.Clear ();
1545 owner.OnSelectedIndexChanged (EventArgs.Empty);
1550 private void ItemsMouseMove (object sender, MouseEventArgs me)
1552 if (PerformBoxSelection (new Point (me.X, me.Y)))
1553 return;
1555 if (owner.HoverSelection && hover_processed) {
1557 Point pt = PointToClient (Control.MousePosition);
1558 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1559 if (item == null || item.Selected)
1560 return;
1562 hover_processed = false;
1563 XplatUI.ResetMouseHover (Handle);
1568 private void ItemsMouseHover (object sender, EventArgs e)
1570 if (Capture || !owner.HoverSelection)
1571 return;
1573 hover_processed = true;
1574 Point pt = PointToClient (Control.MousePosition);
1575 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1577 if (item == null)
1578 return;
1580 item.Selected = true;
1581 owner.OnSelectedIndexChanged (new EventArgs ());
1584 private void ItemsMouseUp (object sender, MouseEventArgs me)
1586 Capture = false;
1587 if (owner.Items.Count == 0)
1588 return;
1590 Point pt = new Point (me.X, me.Y);
1592 Rectangle rect = Rectangle.Empty;
1593 if (clicked_item != null) {
1594 if (owner.view == View.Details && !owner.full_row_select)
1595 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1596 else
1597 rect = clicked_item.Bounds;
1599 if (rect.Contains (pt)) {
1600 switch (owner.activation) {
1601 case ItemActivation.OneClick:
1602 owner.OnItemActivate (EventArgs.Empty);
1603 break;
1605 case ItemActivation.TwoClick:
1606 if (last_clicked_item == clicked_item) {
1607 owner.OnItemActivate (EventArgs.Empty);
1608 last_clicked_item = null;
1609 } else
1610 last_clicked_item = clicked_item;
1611 break;
1612 default:
1613 // DoubleClick activation is handled in another handler
1614 break;
1617 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1618 // Need this to clean up background clicks
1619 owner.SelectedItems.Clear ();
1620 owner.OnSelectedIndexChanged (EventArgs.Empty);
1623 clicked_item = null;
1624 box_select_start = Point.Empty;
1625 BoxSelectRectangle = Rectangle.Empty;
1626 prev_selection = null;
1627 box_select_mode = BoxSelect.None;
1628 checking = false;
1631 internal void LabelEditFinished (object sender, EventArgs e)
1633 EndEdit (edit_item);
1636 internal void BeginEdit (ListViewItem item)
1638 if (edit_item != null)
1639 EndEdit (edit_item);
1641 if (edit_text_box == null) {
1642 edit_text_box = new ListViewLabelEditTextBox ();
1643 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
1644 edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
1645 edit_text_box.Visible = false;
1646 Controls.Add (edit_text_box);
1649 item.EnsureVisible();
1651 edit_text_box.Reset ();
1653 switch (owner.view) {
1654 case View.List:
1655 case View.SmallIcon:
1656 case View.Details:
1657 edit_text_box.TextAlign = HorizontalAlignment.Left;
1658 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1659 SizeF sizef = DeviceContext.MeasureString (item.Text, item.Font);
1660 edit_text_box.Width = (int)sizef.Width + 4;
1661 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
1662 edit_text_box.WordWrap = false;
1663 edit_text_box.Multiline = false;
1664 break;
1665 case View.LargeIcon:
1666 edit_text_box.TextAlign = HorizontalAlignment.Center;
1667 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1668 sizef = DeviceContext.MeasureString (item.Text, item.Font);
1669 edit_text_box.Width = (int)sizef.Width + 4;
1670 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
1671 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
1672 edit_text_box.WordWrap = true;
1673 edit_text_box.Multiline = true;
1674 break;
1677 edit_text_box.Text = item.Text;
1678 edit_text_box.Font = item.Font;
1679 edit_text_box.Visible = true;
1680 edit_text_box.Focus ();
1681 edit_text_box.SelectAll ();
1683 edit_args = new LabelEditEventArgs (owner.Items.IndexOf(edit_item));
1684 owner.OnBeforeLabelEdit (edit_args);
1686 if (edit_args.CancelEdit)
1687 EndEdit (item);
1689 edit_item = item;
1692 internal void EndEdit (ListViewItem item)
1694 if (edit_text_box != null && edit_text_box.Visible) {
1695 edit_text_box.Visible = false;
1698 if (edit_item != null && edit_item == item) {
1699 owner.OnAfterLabelEdit (edit_args);
1701 if (!edit_args.CancelEdit) {
1702 if (edit_args.Label != null)
1703 edit_item.Text = edit_args.Label;
1704 else
1705 edit_item.Text = edit_text_box.Text;
1711 edit_item = null;
1714 internal override void OnPaintInternal (PaintEventArgs pe)
1716 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1719 internal override void OnGotFocusInternal (EventArgs e)
1721 owner.Focus ();
1725 internal class ListViewLabelEditTextBox : TextBox
1727 int max_width = -1;
1728 int min_width = -1;
1730 int max_height = -1;
1731 int min_height = -1;
1733 int old_number_lines = 1;
1735 SizeF text_size_one_char;
1737 public ListViewLabelEditTextBox ()
1739 min_height = DefaultSize.Height;
1740 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1743 public int MaxWidth {
1744 set {
1745 if (value < min_width)
1746 max_width = min_width;
1747 else
1748 max_width = value;
1752 public int MaxHeight {
1753 set {
1754 if (value < min_height)
1755 max_height = min_height;
1756 else
1757 max_height = value;
1761 public new int Width {
1762 get {
1763 return base.Width;
1765 set {
1766 min_width = value;
1767 base.Width = value;
1771 public override Font Font {
1772 get {
1773 return base.Font;
1775 set {
1776 base.Font = value;
1777 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1781 protected override void OnTextChanged (EventArgs e)
1783 SizeF text_size = DeviceContext.MeasureString (Text, Font);
1785 int new_width = (int)text_size.Width + 8;
1787 if (!Multiline)
1788 ResizeTextBoxWidth (new_width);
1789 else {
1790 if (Width != max_width)
1791 ResizeTextBoxWidth (new_width);
1793 int number_lines = Lines.Length;
1795 if (number_lines != old_number_lines) {
1796 int new_height = number_lines * (int)text_size_one_char.Height + 4;
1797 old_number_lines = number_lines;
1799 ResizeTextBoxHeight (new_height);
1803 base.OnTextChanged (e);
1806 protected override bool IsInputKey (Keys key_data)
1808 if ((key_data & Keys.Alt) == 0) {
1809 switch (key_data & Keys.KeyCode) {
1810 case Keys.Enter:
1811 return true;
1814 return base.IsInputKey (key_data);
1817 protected override void OnKeyDown (KeyEventArgs e)
1819 if (e.KeyCode == Keys.Return && Visible) {
1820 this.Visible = false;
1821 OnEditingFinished (e);
1825 protected override void OnLostFocus (EventArgs e)
1827 if (Visible) {
1828 OnEditingFinished (e);
1832 protected void OnEditingFinished (EventArgs e)
1834 EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
1835 if (eh != null)
1836 eh (this, e);
1839 private void ResizeTextBoxWidth (int new_width)
1841 if (new_width > max_width)
1842 base.Width = max_width;
1843 else
1844 if (new_width >= min_width)
1845 base.Width = new_width;
1846 else
1847 base.Width = min_width;
1850 private void ResizeTextBoxHeight (int new_height)
1852 if (new_height > max_height)
1853 base.Height = max_height;
1854 else
1855 if (new_height >= min_height)
1856 base.Height = new_height;
1857 else
1858 base.Height = min_height;
1861 public void Reset ()
1863 max_width = -1;
1864 min_width = -1;
1866 max_height = -1;
1868 old_number_lines = 1;
1870 Text = String.Empty;
1872 Size = DefaultSize;
1875 static object EditingFinishedEvent = new object ();
1876 public event EventHandler EditingFinished {
1877 add { Events.AddHandler (EditingFinishedEvent, value); }
1878 remove { Events.RemoveHandler (EditingFinishedEvent, value); }
1882 internal override void OnPaintInternal (PaintEventArgs pe)
1884 if (updating)
1885 return;
1887 CalculateScrollBars ();
1890 void FocusChanged (object o, EventArgs args)
1892 if (Items.Count == 0)
1893 return;
1895 if (FocusedItem == null)
1896 SetFocusedItem (Items [0]);
1898 item_control.Invalidate (FocusedItem.Bounds);
1901 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1903 if (Items.Count == 0)
1904 return;
1906 int lines = me.Delta / 120;
1908 if (lines == 0)
1909 return;
1911 switch (View) {
1912 case View.Details:
1913 case View.SmallIcon:
1914 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1915 break;
1916 case View.LargeIcon:
1917 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
1918 break;
1919 case View.List:
1920 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1921 break;
1925 private void ListView_SizeChanged (object sender, EventArgs e)
1927 CalculateListView (alignment);
1930 private void SetFocusedItem (ListViewItem item)
1932 if (focused_item != null)
1933 focused_item.Focused = false;
1935 if (item != null)
1936 item.Focused = true;
1938 focused_item = item;
1941 private void HorizontalScroller (object sender, EventArgs e)
1943 item_control.EndEdit (item_control.edit_item);
1945 // Avoid unnecessary flickering, when button is
1946 // kept pressed at the end
1947 if (h_marker != h_scroll.Value) {
1949 int pixels = h_marker - h_scroll.Value;
1951 h_marker = h_scroll.Value;
1952 if (header_control.Visible)
1953 XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1955 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1959 private void VerticalScroller (object sender, EventArgs e)
1961 item_control.EndEdit (item_control.edit_item);
1963 // Avoid unnecessary flickering, when button is
1964 // kept pressed at the end
1965 if (v_marker != v_scroll.Value) {
1966 int pixels = v_marker - v_scroll.Value;
1967 Rectangle area = item_control.ClientRectangle;
1968 v_marker = v_scroll.Value;
1969 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1972 #endregion // Internal Methods Properties
1974 #region Protected Methods
1975 protected override void CreateHandle ()
1977 base.CreateHandle ();
1978 for (int i = 0; i < SelectedItems.Count; i++)
1979 OnSelectedIndexChanged (EventArgs.Empty);
1982 protected override void Dispose (bool disposing)
1984 if (disposing) {
1985 h_scroll.Dispose ();
1986 v_scroll.Dispose ();
1988 large_image_list = null;
1989 small_image_list = null;
1990 state_image_list = null;
1993 base.Dispose (disposing);
1996 protected override bool IsInputKey (Keys keyData)
1998 switch (keyData) {
1999 case Keys.Up:
2000 case Keys.Down:
2001 case Keys.PageUp:
2002 case Keys.PageDown:
2003 case Keys.Right:
2004 case Keys.Left:
2005 case Keys.End:
2006 case Keys.Home:
2007 return true;
2009 default:
2010 break;
2013 return base.IsInputKey (keyData);
2016 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
2018 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
2019 if (eh != null)
2020 eh (this, e);
2023 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
2025 LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
2026 if (eh != null)
2027 eh (this, e);
2030 protected virtual void OnColumnClick (ColumnClickEventArgs e)
2032 ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
2033 if (eh != null)
2034 eh (this, e);
2037 protected override void OnEnabledChanged (EventArgs e)
2039 base.OnEnabledChanged (e);
2042 protected override void OnFontChanged (EventArgs e)
2044 base.OnFontChanged (e);
2045 Redraw (true);
2048 protected override void OnHandleCreated (EventArgs e)
2050 base.OnHandleCreated (e);
2051 Sort ();
2054 protected override void OnHandleDestroyed (EventArgs e)
2056 base.OnHandleDestroyed (e);
2059 protected virtual void OnItemActivate (EventArgs e)
2061 EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
2062 if (eh != null)
2063 eh (this, e);
2066 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
2068 EventHandler eh = (EventHandler)(Events [ItemCheckEvent]);
2069 if (eh != null)
2070 eh (this, ice);
2073 protected virtual void OnItemDrag (ItemDragEventArgs e)
2075 EventHandler eh = (EventHandler)(Events [ItemDragEvent]);
2076 if (eh != null)
2077 eh (this, e);
2080 protected virtual void OnSelectedIndexChanged (EventArgs e)
2082 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
2083 if (eh != null)
2084 eh (this, e);
2087 protected override void OnSystemColorsChanged (EventArgs e)
2089 base.OnSystemColorsChanged (e);
2092 protected void RealizeProperties ()
2094 // FIXME: TODO
2097 protected void UpdateExtendedStyles ()
2099 // FIXME: TODO
2102 protected override void WndProc (ref Message m)
2104 base.WndProc (ref m);
2106 #endregion // Protected Methods
2108 #region Public Instance Methods
2109 public void ArrangeIcons ()
2111 ArrangeIcons (this.alignment);
2114 public void ArrangeIcons (ListViewAlignment alignment)
2116 // Icons are arranged only if view is set to LargeIcon or SmallIcon
2117 if (view == View.LargeIcon || view == View.SmallIcon) {
2118 this.CalculateListView (alignment);
2119 // we have done the calculations already
2120 this.Redraw (false);
2124 public void BeginUpdate ()
2126 // flag to avoid painting
2127 updating = true;
2130 public void Clear ()
2132 columns.Clear ();
2133 items.Clear (); // Redraw (true) called here
2136 public void EndUpdate ()
2138 // flag to avoid painting
2139 updating = false;
2141 // probably, now we need a redraw with recalculations
2142 this.Redraw (true);
2145 public void EnsureVisible (int index)
2147 if (index < 0 || index >= items.Count || scrollable == false)
2148 return;
2150 Rectangle view_rect = item_control.ClientRectangle;
2151 Rectangle bounds = items [index].Bounds;
2153 if (view_rect.Contains (bounds))
2154 return;
2156 if (bounds.Left < 0)
2157 h_scroll.Value += bounds.Left;
2158 else if (bounds.Right > view_rect.Right)
2159 h_scroll.Value += (bounds.Right - view_rect.Right);
2161 if (bounds.Top < 0)
2162 v_scroll.Value += bounds.Top;
2163 else if (bounds.Bottom > view_rect.Bottom)
2164 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
2167 public ListViewItem GetItemAt (int x, int y)
2169 foreach (ListViewItem item in items) {
2170 if (item.Bounds.Contains (x, y))
2171 return item;
2173 return null;
2176 public Rectangle GetItemRect (int index)
2178 return GetItemRect (index, ItemBoundsPortion.Entire);
2181 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
2183 if (index < 0 || index >= items.Count)
2184 throw new IndexOutOfRangeException ("index");
2186 return items [index].GetBounds (portion);
2189 public void Sort ()
2191 Sort (true);
2194 // we need this overload to reuse the logic for sorting, while allowing
2195 // redrawing to be done by caller or have it done by this method when
2196 // sorting is really performed
2198 // ListViewItemCollection's Add and AddRange methods call this overload
2199 // with redraw set to false, as they take care of redrawing themselves
2200 // (they even want to redraw the listview if no sort is performed, as
2201 // an item was added), while ListView.Sort () only wants to redraw if
2202 // sorting was actually performed
2203 private void Sort (bool redraw)
2205 if (!IsHandleCreated || item_sorter == null) {
2206 return;
2209 items.Sort (item_sorter);
2210 if (redraw)
2211 this.Redraw (true);
2214 public override string ToString ()
2216 int count = this.Items.Count;
2218 if (count == 0)
2219 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
2220 else
2221 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
2223 #endregion // Public Instance Methods
2226 #region Subclasses
2228 class HeaderControl : Control {
2230 ListView owner;
2231 bool column_resize_active = false;
2232 ColumnHeader resize_column;
2233 ColumnHeader clicked_column;
2234 ColumnHeader drag_column;
2235 int drag_x;
2236 int drag_to_index = -1;
2238 public HeaderControl (ListView owner)
2240 this.owner = owner;
2241 MouseDown += new MouseEventHandler (HeaderMouseDown);
2242 MouseMove += new MouseEventHandler (HeaderMouseMove);
2243 MouseUp += new MouseEventHandler (HeaderMouseUp);
2246 private ColumnHeader ColumnAtX (int x)
2248 Point pt = new Point (x, 0);
2249 ColumnHeader result = null;
2250 foreach (ColumnHeader col in owner.Columns) {
2251 if (col.Rect.Contains (pt)) {
2252 result = col;
2253 break;
2256 return result;
2259 private int GetReorderedIndex (ColumnHeader col)
2261 if (owner.reordered_column_indices == null)
2262 return col.Index;
2263 else
2264 for (int i = 0; i < owner.Columns.Count; i++)
2265 if (owner.reordered_column_indices [i] == col.Index)
2266 return i;
2267 throw new Exception ("Column index missing from reordered array");
2270 private void HeaderMouseDown (object sender, MouseEventArgs me)
2272 if (resize_column != null) {
2273 column_resize_active = true;
2274 Capture = true;
2275 return;
2278 clicked_column = ColumnAtX (me.X + owner.h_marker);
2280 if (clicked_column != null) {
2281 Capture = true;
2282 if (owner.AllowColumnReorder) {
2283 drag_x = me.X;
2284 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2285 drag_column.column_rect = clicked_column.Rect;
2286 drag_to_index = GetReorderedIndex (clicked_column);
2288 clicked_column.pressed = true;
2289 Rectangle bounds = clicked_column.Rect;
2290 bounds.X -= owner.h_marker;
2291 Invalidate (bounds);
2292 return;
2296 private void HeaderMouseMove (object sender, MouseEventArgs me)
2298 Point pt = new Point (me.X + owner.h_marker, me.Y);
2300 if (column_resize_active) {
2301 resize_column.Width = pt.X - resize_column.X;
2302 if (resize_column.Width < 0)
2303 resize_column.Width = 0;
2304 return;
2307 resize_column = null;
2309 if (clicked_column != null) {
2310 if (owner.AllowColumnReorder) {
2311 Rectangle r;
2313 r = drag_column.column_rect;
2314 r.X = clicked_column.Rect.X + me.X - drag_x;
2315 drag_column.column_rect = r;
2317 int x = me.X + owner.h_marker;
2318 ColumnHeader over = ColumnAtX (x);
2319 if (over == null)
2320 drag_to_index = owner.Columns.Count;
2321 else if (x < over.X + over.Width / 2)
2322 drag_to_index = GetReorderedIndex (over);
2323 else
2324 drag_to_index = GetReorderedIndex (over) + 1;
2325 Invalidate ();
2326 } else {
2327 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2328 bool pressed = clicked_column.pressed;
2329 clicked_column.pressed = over == clicked_column;
2330 if (clicked_column.pressed ^ pressed) {
2331 Rectangle bounds = clicked_column.Rect;
2332 bounds.X -= owner.h_marker;
2333 Invalidate (bounds);
2336 return;
2339 for (int i = 0; i < owner.Columns.Count; i++) {
2340 Rectangle zone = owner.Columns [i].Rect;
2341 zone.X = zone.Right - 5;
2342 zone.Width = 10;
2343 if (zone.Contains (pt)) {
2344 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2345 i++;
2346 resize_column = owner.Columns [i];
2347 break;
2351 if (resize_column == null)
2352 Cursor = Cursors.Default;
2353 else
2354 Cursor = Cursors.VSplit;
2357 void HeaderMouseUp (object sender, MouseEventArgs me)
2359 Capture = false;
2361 if (column_resize_active) {
2362 column_resize_active = false;
2363 resize_column = null;
2364 Cursor = Cursors.Default;
2365 return;
2368 if (clicked_column != null && clicked_column.pressed) {
2369 clicked_column.pressed = false;
2370 Rectangle bounds = clicked_column.Rect;
2371 bounds.X -= owner.h_marker;
2372 Invalidate (bounds);
2373 owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2376 if (drag_column != null && owner.AllowColumnReorder) {
2377 drag_column = null;
2378 if (drag_to_index > GetReorderedIndex (clicked_column))
2379 drag_to_index--;
2380 if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2381 owner.ReorderColumn (clicked_column, drag_to_index);
2382 drag_to_index = -1;
2383 Invalidate ();
2386 clicked_column = null;
2389 internal override void OnPaintInternal (PaintEventArgs pe)
2391 if (owner.updating)
2392 return;
2394 Theme theme = ThemeEngine.Current;
2395 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2397 if (drag_column == null)
2398 return;
2400 int target_x;
2401 if (drag_to_index == owner.Columns.Count)
2402 target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2403 else
2404 target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2405 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2408 protected override void WndProc (ref Message m)
2410 switch ((Msg)m.Msg) {
2411 case Msg.WM_SETFOCUS:
2412 owner.Focus ();
2413 break;
2414 default:
2415 base.WndProc (ref m);
2416 break;
2421 private class ItemComparer : IComparer {
2422 readonly SortOrder sort_order;
2424 public ItemComparer (SortOrder sortOrder)
2426 sort_order = sortOrder;
2429 public int Compare (object x, object y)
2431 ListViewItem item_x = x as ListViewItem;
2432 ListViewItem item_y = y as ListViewItem;
2433 if (sort_order == SortOrder.Ascending)
2434 return String.Compare (item_x.Text, item_y.Text);
2435 else
2436 return String.Compare (item_y.Text, item_x.Text);
2440 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2442 private readonly ListView owner;
2444 #region Public Constructor
2445 public CheckedIndexCollection (ListView owner)
2447 this.owner = owner;
2449 #endregion // Public Constructor
2451 #region Public Properties
2452 [Browsable (false)]
2453 public int Count {
2454 get { return owner.CheckedItems.Count; }
2457 public bool IsReadOnly {
2458 get { return true; }
2461 public int this [int index] {
2462 get {
2463 int [] indices = GetIndices ();
2464 if (index < 0 || index >= indices.Length)
2465 throw new ArgumentOutOfRangeException ("index");
2466 return indices [index];
2470 bool ICollection.IsSynchronized {
2471 get { return false; }
2474 object ICollection.SyncRoot {
2475 get { return this; }
2478 bool IList.IsFixedSize {
2479 get { return true; }
2482 object IList.this [int index] {
2483 get { return this [index]; }
2484 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2486 #endregion // Public Properties
2488 #region Public Methods
2489 public bool Contains (int checkedIndex)
2491 int [] indices = GetIndices ();
2492 for (int i = 0; i < indices.Length; i++) {
2493 if (indices [i] == checkedIndex)
2494 return true;
2496 return false;
2499 public IEnumerator GetEnumerator ()
2501 int [] indices = GetIndices ();
2502 return indices.GetEnumerator ();
2505 void ICollection.CopyTo (Array dest, int index)
2507 int [] indices = GetIndices ();
2508 Array.Copy (indices, 0, dest, index, indices.Length);
2511 int IList.Add (object value)
2513 throw new NotSupportedException ("Add operation is not supported.");
2516 void IList.Clear ()
2518 throw new NotSupportedException ("Clear operation is not supported.");
2521 bool IList.Contains (object checkedIndex)
2523 if (!(checkedIndex is int))
2524 return false;
2525 return Contains ((int) checkedIndex);
2528 int IList.IndexOf (object checkedIndex)
2530 if (!(checkedIndex is int))
2531 return -1;
2532 return IndexOf ((int) checkedIndex);
2535 void IList.Insert (int index, object value)
2537 throw new NotSupportedException ("Insert operation is not supported.");
2540 void IList.Remove (object value)
2542 throw new NotSupportedException ("Remove operation is not supported.");
2545 void IList.RemoveAt (int index)
2547 throw new NotSupportedException ("RemoveAt operation is not supported.");
2550 public int IndexOf (int checkedIndex)
2552 int [] indices = GetIndices ();
2553 for (int i = 0; i < indices.Length; i++) {
2554 if (indices [i] == checkedIndex)
2555 return i;
2557 return -1;
2559 #endregion // Public Methods
2561 private int [] GetIndices ()
2563 ArrayList checked_items = owner.CheckedItems.List;
2564 int [] indices = new int [checked_items.Count];
2565 for (int i = 0; i < checked_items.Count; i++) {
2566 ListViewItem item = (ListViewItem) checked_items [i];
2567 indices [i] = item.Index;
2569 return indices;
2571 } // CheckedIndexCollection
2573 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2575 private readonly ListView owner;
2576 private ArrayList list;
2578 #region Public Constructor
2579 public CheckedListViewItemCollection (ListView owner)
2581 this.owner = owner;
2582 this.owner.Items.Changed += new CollectionChangedHandler (
2583 ItemsCollection_Changed);
2585 #endregion // Public Constructor
2587 #region Public Properties
2588 [Browsable (false)]
2589 public int Count {
2590 get {
2591 if (!owner.CheckBoxes)
2592 return 0;
2593 return List.Count;
2597 public bool IsReadOnly {
2598 get { return true; }
2601 public ListViewItem this [int index] {
2602 get {
2603 ArrayList checked_items = List;
2604 if (index < 0 || index >= checked_items.Count)
2605 throw new ArgumentOutOfRangeException ("index");
2606 return (ListViewItem) checked_items [index];
2610 bool ICollection.IsSynchronized {
2611 get { return false; }
2614 object ICollection.SyncRoot {
2615 get { return this; }
2618 bool IList.IsFixedSize {
2619 get { return true; }
2622 object IList.this [int index] {
2623 get { return this [index]; }
2624 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2626 #endregion // Public Properties
2628 #region Public Methods
2629 public bool Contains (ListViewItem item)
2631 if (!owner.CheckBoxes)
2632 return false;
2633 return List.Contains (item);
2636 public void CopyTo (Array dest, int index)
2638 if (!owner.CheckBoxes)
2639 return;
2640 List.CopyTo (dest, index);
2643 public IEnumerator GetEnumerator ()
2645 if (!owner.CheckBoxes)
2646 return (new ListViewItem [0]).GetEnumerator ();
2647 return List.GetEnumerator ();
2650 int IList.Add (object value)
2652 throw new NotSupportedException ("Add operation is not supported.");
2655 void IList.Clear ()
2657 throw new NotSupportedException ("Clear operation is not supported.");
2660 bool IList.Contains (object item)
2662 if (!(item is ListViewItem))
2663 return false;
2664 return Contains ((ListViewItem) item);
2667 int IList.IndexOf (object item)
2669 if (!(item is ListViewItem))
2670 return -1;
2671 return IndexOf ((ListViewItem) item);
2674 void IList.Insert (int index, object value)
2676 throw new NotSupportedException ("Insert operation is not supported.");
2679 void IList.Remove (object value)
2681 throw new NotSupportedException ("Remove operation is not supported.");
2684 void IList.RemoveAt (int index)
2686 throw new NotSupportedException ("RemoveAt operation is not supported.");
2689 public int IndexOf (ListViewItem item)
2691 if (!owner.CheckBoxes)
2692 return -1;
2693 return List.IndexOf (item);
2695 #endregion // Public Methods
2697 internal ArrayList List {
2698 get {
2699 if (list == null) {
2700 list = new ArrayList ();
2701 foreach (ListViewItem item in owner.Items) {
2702 if (item.Checked)
2703 list.Add (item);
2706 return list;
2710 internal void Reset ()
2712 // force re-population of list
2713 list = null;
2716 private void ItemsCollection_Changed ()
2718 Reset ();
2720 } // CheckedListViewItemCollection
2722 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2724 internal ArrayList list;
2725 private ListView owner;
2727 #region Public Constructor
2728 public ColumnHeaderCollection (ListView owner)
2730 list = new ArrayList ();
2731 this.owner = owner;
2733 #endregion // Public Constructor
2735 #region Public Properties
2736 [Browsable (false)]
2737 public int Count {
2738 get { return list.Count; }
2741 public bool IsReadOnly {
2742 get { return false; }
2745 public virtual ColumnHeader this [int index] {
2746 get {
2747 if (index < 0 || index >= list.Count)
2748 throw new ArgumentOutOfRangeException ("index");
2749 return (ColumnHeader) list [index];
2753 bool ICollection.IsSynchronized {
2754 get { return true; }
2757 object ICollection.SyncRoot {
2758 get { return this; }
2761 bool IList.IsFixedSize {
2762 get { return list.IsFixedSize; }
2765 object IList.this [int index] {
2766 get { return this [index]; }
2767 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2769 #endregion // Public Properties
2771 #region Public Methods
2772 public virtual int Add (ColumnHeader value)
2774 int idx;
2775 value.owner = this.owner;
2776 idx = list.Add (value);
2777 if (owner.IsHandleCreated) {
2778 owner.Redraw (true);
2780 return idx;
2783 public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2785 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2786 this.Add (colHeader);
2787 return colHeader;
2790 public virtual void AddRange (ColumnHeader [] values)
2792 foreach (ColumnHeader colHeader in values) {
2793 colHeader.owner = this.owner;
2794 Add (colHeader);
2797 owner.Redraw (true);
2800 public virtual void Clear ()
2802 list.Clear ();
2803 owner.Redraw (true);
2806 public bool Contains (ColumnHeader value)
2808 return list.Contains (value);
2811 public IEnumerator GetEnumerator ()
2813 return list.GetEnumerator ();
2816 void ICollection.CopyTo (Array dest, int index)
2818 list.CopyTo (dest, index);
2821 int IList.Add (object value)
2823 if (! (value is ColumnHeader)) {
2824 throw new ArgumentException ("Not of type ColumnHeader", "value");
2827 return this.Add ((ColumnHeader) value);
2830 bool IList.Contains (object value)
2832 if (! (value is ColumnHeader)) {
2833 throw new ArgumentException ("Not of type ColumnHeader", "value");
2836 return this.Contains ((ColumnHeader) value);
2839 int IList.IndexOf (object value)
2841 if (! (value is ColumnHeader)) {
2842 throw new ArgumentException ("Not of type ColumnHeader", "value");
2845 return this.IndexOf ((ColumnHeader) value);
2848 void IList.Insert (int index, object value)
2850 if (! (value is ColumnHeader)) {
2851 throw new ArgumentException ("Not of type ColumnHeader", "value");
2854 this.Insert (index, (ColumnHeader) value);
2857 void IList.Remove (object value)
2859 if (! (value is ColumnHeader)) {
2860 throw new ArgumentException ("Not of type ColumnHeader", "value");
2863 this.Remove ((ColumnHeader) value);
2866 public int IndexOf (ColumnHeader value)
2868 return list.IndexOf (value);
2871 public void Insert (int index, ColumnHeader value)
2873 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2874 // but it's really only greater.
2875 if (index < 0 || index > list.Count)
2876 throw new ArgumentOutOfRangeException ("index");
2878 value.owner = this.owner;
2879 list.Insert (index, value);
2880 owner.Redraw (true);
2883 public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2885 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2886 this.Insert (index, colHeader);
2889 public virtual void Remove (ColumnHeader column)
2891 // TODO: Update Column internal index ?
2892 list.Remove (column);
2893 owner.Redraw (true);
2896 public virtual void RemoveAt (int index)
2898 if (index < 0 || index >= list.Count)
2899 throw new ArgumentOutOfRangeException ("index");
2901 // TODO: Update Column internal index ?
2902 list.RemoveAt (index);
2903 owner.Redraw (true);
2905 #endregion // Public Methods
2908 } // ColumnHeaderCollection
2910 public class ListViewItemCollection : IList, ICollection, IEnumerable
2912 private readonly ArrayList list;
2913 private readonly ListView owner;
2915 #region Public Constructor
2916 public ListViewItemCollection (ListView owner)
2918 list = new ArrayList ();
2919 this.owner = owner;
2921 #endregion // Public Constructor
2923 #region Public Properties
2924 [Browsable (false)]
2925 public int Count {
2926 get { return list.Count; }
2929 public bool IsReadOnly {
2930 get { return false; }
2933 public virtual ListViewItem this [int displayIndex] {
2934 get {
2935 if (displayIndex < 0 || displayIndex >= list.Count)
2936 throw new ArgumentOutOfRangeException ("displayIndex");
2937 return (ListViewItem) list [displayIndex];
2940 set {
2941 if (displayIndex < 0 || displayIndex >= list.Count)
2942 throw new ArgumentOutOfRangeException ("displayIndex");
2944 if (list.Contains (value))
2945 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2947 value.Owner = owner;
2948 list [displayIndex] = value;
2949 OnChange ();
2951 owner.Redraw (true);
2955 bool ICollection.IsSynchronized {
2956 get { return true; }
2959 object ICollection.SyncRoot {
2960 get { return this; }
2963 bool IList.IsFixedSize {
2964 get { return list.IsFixedSize; }
2967 object IList.this [int index] {
2968 get { return this [index]; }
2969 set {
2970 if (value is ListViewItem)
2971 this [index] = (ListViewItem) value;
2972 else
2973 this [index] = new ListViewItem (value.ToString ());
2974 OnChange ();
2977 #endregion // Public Properties
2979 #region Public Methods
2980 public virtual ListViewItem Add (ListViewItem value)
2982 if (list.Contains (value))
2983 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2985 value.Owner = owner;
2986 list.Add (value);
2988 owner.Sort (false);
2989 OnChange ();
2990 owner.Redraw (true);
2991 return value;
2994 public virtual ListViewItem Add (string text)
2996 ListViewItem item = new ListViewItem (text);
2997 return this.Add (item);
3000 public virtual ListViewItem Add (string text, int imageIndex)
3002 ListViewItem item = new ListViewItem (text, imageIndex);
3003 return this.Add (item);
3006 public void AddRange (ListViewItem [] values)
3008 list.Clear ();
3010 foreach (ListViewItem item in values) {
3011 item.Owner = owner;
3012 list.Add (item);
3015 owner.Sort (false);
3016 OnChange ();
3017 owner.Redraw (true);
3020 public virtual void Clear ()
3022 owner.SetFocusedItem (null);
3023 owner.h_scroll.Value = owner.v_scroll.Value = 0;
3024 list.Clear ();
3025 OnChange ();
3026 owner.Redraw (true);
3029 public bool Contains (ListViewItem item)
3031 return list.Contains (item);
3034 public void CopyTo (Array dest, int index)
3036 list.CopyTo (dest, index);
3039 public IEnumerator GetEnumerator ()
3041 return list.GetEnumerator ();
3044 int IList.Add (object item)
3046 int result;
3047 ListViewItem li;
3049 if (item is ListViewItem) {
3050 li = (ListViewItem) item;
3051 if (list.Contains (li))
3052 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3054 else
3055 li = new ListViewItem (item.ToString ());
3057 li.Owner = owner;
3058 result = list.Add (li);
3059 OnChange ();
3060 owner.Redraw (true);
3062 return result;
3065 bool IList.Contains (object item)
3067 return list.Contains (item);
3070 int IList.IndexOf (object item)
3072 return list.IndexOf (item);
3075 void IList.Insert (int index, object item)
3077 if (item is ListViewItem)
3078 this.Insert (index, (ListViewItem) item);
3079 else
3080 this.Insert (index, item.ToString ());
3083 void IList.Remove (object item)
3085 Remove ((ListViewItem) item);
3088 public int IndexOf (ListViewItem item)
3090 return list.IndexOf (item);
3093 public ListViewItem Insert (int index, ListViewItem item)
3095 if (index < 0 || index > list.Count)
3096 throw new ArgumentOutOfRangeException ("index");
3098 if (list.Contains (item))
3099 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3101 item.Owner = owner;
3102 list.Insert (index, item);
3103 OnChange ();
3104 owner.Redraw (true);
3105 return item;
3108 public ListViewItem Insert (int index, string text)
3110 return this.Insert (index, new ListViewItem (text));
3113 public ListViewItem Insert (int index, string text, int imageIndex)
3115 return this.Insert (index, new ListViewItem (text, imageIndex));
3118 public virtual void Remove (ListViewItem item)
3120 if (!list.Contains (item))
3121 return;
3123 bool selection_changed = owner.SelectedItems.Contains (item);
3124 list.Remove (item);
3125 OnChange ();
3126 owner.Redraw (true);
3127 if (selection_changed)
3128 owner.OnSelectedIndexChanged (EventArgs.Empty);
3131 public virtual void RemoveAt (int index)
3133 if (index < 0 || index >= Count)
3134 throw new ArgumentOutOfRangeException ("index");
3135 bool selection_changed = owner.SelectedIndices.Contains (index);
3136 list.RemoveAt (index);
3137 OnChange ();
3138 owner.Redraw (false);
3139 if (selection_changed)
3140 owner.OnSelectedIndexChanged (EventArgs.Empty);
3142 #endregion // Public Methods
3144 internal event CollectionChangedHandler Changed;
3146 internal void Sort (IComparer comparer)
3148 list.Sort (comparer);
3149 OnChange ();
3152 internal void OnChange ()
3154 if (Changed != null)
3155 Changed ();
3157 } // ListViewItemCollection
3159 public class SelectedIndexCollection : IList, ICollection, IEnumerable
3161 private readonly ListView owner;
3163 #region Public Constructor
3164 public SelectedIndexCollection (ListView owner)
3166 this.owner = owner;
3168 #endregion // Public Constructor
3170 #region Public Properties
3171 [Browsable (false)]
3172 public int Count {
3173 get {
3174 return owner.SelectedItems.Count;
3178 public bool IsReadOnly {
3179 get { return true; }
3182 public int this [int index] {
3183 get {
3184 int [] indices = GetIndices ();
3185 if (index < 0 || index >= indices.Length)
3186 throw new ArgumentOutOfRangeException ("index");
3187 return indices [index];
3191 bool ICollection.IsSynchronized {
3192 get { return false; }
3195 object ICollection.SyncRoot {
3196 get { return this; }
3199 bool IList.IsFixedSize {
3200 get { return true; }
3203 object IList.this [int index] {
3204 get { return this [index]; }
3205 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3207 #endregion // Public Properties
3209 #region Public Methods
3210 public bool Contains (int selectedIndex)
3212 int [] indices = GetIndices ();
3213 for (int i = 0; i < indices.Length; i++) {
3214 if (indices [i] == selectedIndex)
3215 return true;
3217 return false;
3220 public void CopyTo (Array dest, int index)
3222 int [] indices = GetIndices ();
3223 Array.Copy (indices, 0, dest, index, indices.Length);
3226 public IEnumerator GetEnumerator ()
3228 int [] indices = GetIndices ();
3229 return indices.GetEnumerator ();
3232 int IList.Add (object value)
3234 throw new NotSupportedException ("Add operation is not supported.");
3237 void IList.Clear ()
3239 throw new NotSupportedException ("Clear operation is not supported.");
3242 bool IList.Contains (object selectedIndex)
3244 if (!(selectedIndex is int))
3245 return false;
3246 return Contains ((int) selectedIndex);
3249 int IList.IndexOf (object selectedIndex)
3251 if (!(selectedIndex is int))
3252 return -1;
3253 return IndexOf ((int) selectedIndex);
3256 void IList.Insert (int index, object value)
3258 throw new NotSupportedException ("Insert operation is not supported.");
3261 void IList.Remove (object value)
3263 throw new NotSupportedException ("Remove operation is not supported.");
3266 void IList.RemoveAt (int index)
3268 throw new NotSupportedException ("RemoveAt operation is not supported.");
3271 public int IndexOf (int selectedIndex)
3273 int [] indices = GetIndices ();
3274 for (int i = 0; i < indices.Length; i++) {
3275 if (indices [i] == selectedIndex)
3276 return i;
3278 return -1;
3280 #endregion // Public Methods
3282 private int [] GetIndices ()
3284 ArrayList selected_items = owner.SelectedItems.List;
3285 int [] indices = new int [selected_items.Count];
3286 for (int i = 0; i < selected_items.Count; i++) {
3287 ListViewItem item = (ListViewItem) selected_items [i];
3288 indices [i] = item.Index;
3290 return indices;
3292 } // SelectedIndexCollection
3294 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
3296 private readonly ListView owner;
3297 private ArrayList list;
3299 #region Public Constructor
3300 public SelectedListViewItemCollection (ListView owner)
3302 this.owner = owner;
3303 this.owner.Items.Changed += new CollectionChangedHandler (
3304 ItemsCollection_Changed);
3306 #endregion // Public Constructor
3308 #region Public Properties
3309 [Browsable (false)]
3310 public int Count {
3311 get {
3312 if (!owner.IsHandleCreated)
3313 return 0;
3314 return List.Count;
3318 public bool IsReadOnly {
3319 get { return true; }
3322 public ListViewItem this [int index] {
3323 get {
3324 ArrayList selected_items = List;
3325 if (!owner.IsHandleCreated || index < 0 || index >= selected_items.Count)
3326 throw new ArgumentOutOfRangeException ("index");
3327 return (ListViewItem) selected_items [index];
3331 bool ICollection.IsSynchronized {
3332 get { return false; }
3335 object ICollection.SyncRoot {
3336 get { return this; }
3339 bool IList.IsFixedSize {
3340 get { return true; }
3343 object IList.this [int index] {
3344 get { return this [index]; }
3345 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3347 #endregion // Public Properties
3349 #region Public Methods
3350 public void Clear ()
3352 if (!owner.IsHandleCreated)
3353 return;
3355 foreach (ListViewItem item in List)
3356 item.Selected = false;
3359 public bool Contains (ListViewItem item)
3361 if (!owner.IsHandleCreated)
3362 return false;
3363 return List.Contains (item);
3366 public void CopyTo (Array dest, int index)
3368 if (!owner.IsHandleCreated)
3369 return;
3370 List.CopyTo (dest, index);
3373 public IEnumerator GetEnumerator ()
3375 if (!owner.IsHandleCreated)
3376 return (new ListViewItem [0]).GetEnumerator ();
3377 return List.GetEnumerator ();
3380 int IList.Add (object value)
3382 throw new NotSupportedException ("Add operation is not supported.");
3385 bool IList.Contains (object item)
3387 if (!(item is ListViewItem))
3388 return false;
3389 return Contains ((ListViewItem) item);
3392 int IList.IndexOf (object item)
3394 if (!(item is ListViewItem))
3395 return -1;
3396 return IndexOf ((ListViewItem) item);
3399 void IList.Insert (int index, object value)
3401 throw new NotSupportedException ("Insert operation is not supported.");
3404 void IList.Remove (object value)
3406 throw new NotSupportedException ("Remove operation is not supported.");
3409 void IList.RemoveAt (int index)
3411 throw new NotSupportedException ("RemoveAt operation is not supported.");
3414 public int IndexOf (ListViewItem item)
3416 if (!owner.IsHandleCreated)
3417 return -1;
3418 return List.IndexOf (item);
3420 #endregion // Public Methods
3422 internal ArrayList List {
3423 get {
3424 if (list == null) {
3425 list = new ArrayList ();
3426 foreach (ListViewItem item in owner.Items) {
3427 if (item.Selected)
3428 list.Add (item);
3431 return list;
3435 internal void Reset ()
3437 // force re-population of list
3438 list = null;
3441 private void ItemsCollection_Changed ()
3443 Reset ();
3445 } // SelectedListViewItemCollection
3447 internal delegate void CollectionChangedHandler ();
3449 #endregion // Subclasses