1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2006 Novell, Inc.
23 // Jordi Mas i Hernandez, jordi@ximian.com
24 // Mike Kestner <mkestner@novell.com>
31 using System.Collections;
32 using System.ComponentModel;
33 using System.ComponentModel.Design;
34 using System.ComponentModel.Design.Serialization;
35 using System.Globalization;
36 using System.Reflection;
37 using System.Runtime.InteropServices;
40 using System.Collections.Generic;
43 namespace System.Windows.Forms
45 [DefaultProperty("Items")]
46 [DefaultEvent("SelectedIndexChanged")]
47 [Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
49 [DefaultBindingProperty ("SelectedValue")]
50 [ClassInterface (ClassInterfaceType.AutoDispatch)]
53 public class ListBox : ListControl
55 public const int DefaultItemHeight = 13;
56 public const int NoMatches = -1;
58 internal enum ItemNavigation
70 Hashtable item_heights;
71 private int item_height = -1;
72 private int column_width = 0;
73 private int requested_height;
74 private DrawMode draw_mode = DrawMode.Normal;
75 private int horizontal_extent = 0;
76 private bool horizontal_scrollbar = false;
77 private bool integral_height = true;
78 private bool multicolumn = false;
79 private bool scroll_always_visible = false;
80 private SelectedIndexCollection selected_indices;
81 private SelectedObjectCollection selected_items;
82 private SelectionMode selection_mode = SelectionMode.One;
83 private bool sorted = false;
84 private bool use_tabstops = true;
85 private int column_width_internal = 120;
86 private ImplicitVScrollBar vscrollbar;
87 private ImplicitHScrollBar hscrollbar;
88 private int hbar_offset;
89 private bool suspend_layout;
90 private bool ctrl_pressed = false;
91 private bool shift_pressed = false;
92 private bool explicit_item_height = false;
93 private int top_index = 0;
94 private int last_visible_index = 0;
95 private Rectangle items_area;
96 private int focused_item = -1;
97 private ObjectCollection items;
99 private IntegerCollection custom_tab_offsets;
100 private Padding padding;
101 private bool use_custom_tab_offsets;
106 items = CreateItemCollection ();
107 selected_indices = new SelectedIndexCollection (this);
108 selected_items = new SelectedObjectCollection (this);
110 requested_height = bounds.Height;
111 InternalBorderStyle = BorderStyle.Fixed3D;
112 BackColor = ThemeEngine.Current.ColorWindow;
114 /* Vertical scrollbar */
115 vscrollbar = new ImplicitVScrollBar ();
116 vscrollbar.Minimum = 0;
117 vscrollbar.SmallChange = 1;
118 vscrollbar.LargeChange = 1;
119 vscrollbar.Maximum = 0;
120 vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
121 vscrollbar.Visible = false;
123 /* Horizontal scrollbar */
124 hscrollbar = new ImplicitHScrollBar ();
125 hscrollbar.Minimum = 0;
126 hscrollbar.SmallChange = 1;
127 hscrollbar.LargeChange = 1;
128 hscrollbar.Maximum = 0;
129 hscrollbar.Visible = false;
130 hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
132 Controls.AddImplicit (vscrollbar);
133 Controls.AddImplicit (hscrollbar);
136 MouseDown += new MouseEventHandler (OnMouseDownLB);
137 MouseMove += new MouseEventHandler (OnMouseMoveLB);
138 MouseUp += new MouseEventHandler (OnMouseUpLB);
139 MouseWheel += new MouseEventHandler (OnMouseWheelLB);
140 KeyDown += new KeyEventHandler (OnKeyDownLB);
141 KeyUp += new KeyEventHandler (OnKeyUpLB);
142 GotFocus += new EventHandler (OnGotFocus);
143 LostFocus += new EventHandler (OnLostFocus);
145 SetStyle (ControlStyles.UserPaint, false);
148 custom_tab_offsets = new IntegerCollection (this);
153 static object DrawItemEvent = new object ();
154 static object MeasureItemEvent = new object ();
155 static object SelectedIndexChangedEvent = new object ();
158 [EditorBrowsable (EditorBrowsableState.Never)]
159 public new event EventHandler BackgroundImageChanged {
160 add { base.BackgroundImageChanged += value; }
161 remove { base.BackgroundImageChanged -= value; }
166 [EditorBrowsable (EditorBrowsableState.Never)]
167 public new event EventHandler BackgroundImageLayoutChanged {
168 add { base.BackgroundImageLayoutChanged += value; }
169 remove { base.BackgroundImageLayoutChanged -= value; }
173 [EditorBrowsable (EditorBrowsableState.Always)]
176 [EditorBrowsable (EditorBrowsableState.Advanced)]
178 public new event EventHandler Click {
179 add { base.Click += value; }
180 remove { base.Click -= value; }
183 public event DrawItemEventHandler DrawItem {
184 add { Events.AddHandler (DrawItemEvent, value); }
185 remove { Events.RemoveHandler (DrawItemEvent, value); }
188 public event MeasureItemEventHandler MeasureItem {
189 add { Events.AddHandler (MeasureItemEvent, value); }
190 remove { Events.RemoveHandler (MeasureItemEvent, value); }
195 [EditorBrowsable (EditorBrowsableState.Always)]
196 public new event MouseEventHandler MouseClick {
197 add { base.MouseClick += value; }
198 remove { base.MouseClick -= value; }
202 [EditorBrowsable (EditorBrowsableState.Never)]
203 public new event EventHandler PaddingChanged {
204 add { base.PaddingChanged += value; }
205 remove { base.PaddingChanged -= value; }
210 [EditorBrowsable (EditorBrowsableState.Never)]
211 public new event PaintEventHandler Paint {
212 add { base.Paint += value; }
213 remove { base.Paint -= value; }
216 public event EventHandler SelectedIndexChanged {
217 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
218 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
222 [EditorBrowsable (EditorBrowsableState.Advanced)]
223 public new event EventHandler TextChanged {
224 add { base.TextChanged += value; }
225 remove { base.TextChanged -= value; }
229 #region Public Properties
230 public override Color BackColor {
231 get { return base.BackColor; }
233 if (base.BackColor == value)
236 base.BackColor = value;
237 base.Refresh (); // Careful. Calling the base method is not the same that calling
238 } // the overriden one that refresh also all the items
242 [EditorBrowsable (EditorBrowsableState.Never)]
243 public override Image BackgroundImage {
244 get { return base.BackgroundImage; }
246 base.BackgroundImage = value;
253 [EditorBrowsable (EditorBrowsableState.Never)]
254 public override ImageLayout BackgroundImageLayout {
255 get { return base.BackgroundImageLayout; }
256 set { base.BackgroundImageLayout = value; }
260 [DefaultValue (BorderStyle.Fixed3D)]
262 public BorderStyle BorderStyle {
263 get { return InternalBorderStyle; }
265 InternalBorderStyle = value;
266 UpdateListBoxBounds ();
272 public int ColumnWidth {
273 get { return column_width; }
276 throw new ArgumentException ("A value less than zero is assigned to the property.");
278 column_width = value;
281 ColumnWidthInternal = 120;
283 ColumnWidthInternal = value;
289 protected override CreateParams CreateParams {
290 get { return base.CreateParams;}
295 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
296 public IntegerCollection CustomTabOffsets {
297 get { return custom_tab_offsets; }
301 protected override Size DefaultSize {
302 get { return new Size (120, 96); }
305 [RefreshProperties(RefreshProperties.Repaint)]
306 [DefaultValue (DrawMode.Normal)]
307 public virtual DrawMode DrawMode {
308 get { return draw_mode; }
310 if (!Enum.IsDefined (typeof (DrawMode), value))
311 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
313 if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
314 throw new ArgumentException ("Cannot have variable height and multicolumn");
316 if (draw_mode == value)
321 if (draw_mode == DrawMode.OwnerDrawVariable)
322 item_heights = new Hashtable ();
327 Parent.PerformLayout (this, "DrawMode");
333 public override Font Font {
334 get { return base.Font; }
335 set { base.Font = value; }
339 public override Color ForeColor {
340 get { return base.ForeColor; }
342 if (base.ForeColor == value)
345 base.ForeColor = value;
352 public int HorizontalExtent {
353 get { return horizontal_extent; }
355 if (horizontal_extent == value)
358 horizontal_extent = value;
363 [DefaultValue (false)]
365 public bool HorizontalScrollbar {
366 get { return horizontal_scrollbar; }
368 if (horizontal_scrollbar == value)
371 horizontal_scrollbar = value;
377 [DefaultValue (true)]
379 [RefreshProperties(RefreshProperties.Repaint)]
380 public bool IntegralHeight {
381 get { return integral_height; }
383 if (integral_height == value)
386 integral_height = value;
387 UpdateListBoxBounds ();
393 [RefreshProperties(RefreshProperties.Repaint)]
394 public virtual int ItemHeight {
396 if (item_height == -1) {
397 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
398 item_height = (int) sz.Height;
404 throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
406 explicit_item_height = true;
407 if (item_height == value)
412 UpdateListBoxBounds ();
417 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
419 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
421 [MergableProperty (false)]
423 public ObjectCollection Items {
424 get { return items; }
427 [DefaultValue (false)]
428 public bool MultiColumn {
429 get { return multicolumn; }
431 if (multicolumn == value)
434 if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
435 throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
445 [EditorBrowsable (EditorBrowsableState.Never)]
446 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
447 public new Padding Padding {
448 get { return padding; }
449 set { padding = value; }
454 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
455 [EditorBrowsable (EditorBrowsableState.Advanced)]
456 public int PreferredHeight {
459 if (draw_mode == DrawMode.Normal)
460 itemsHeight = FontHeight * items.Count;
461 else if (draw_mode == DrawMode.OwnerDrawFixed)
462 itemsHeight = ItemHeight * items.Count;
463 else if (draw_mode == DrawMode.OwnerDrawVariable) {
464 for (int i = 0; i < items.Count; i++)
465 itemsHeight += (int) item_heights [Items [i]];
472 public override RightToLeft RightToLeft {
473 get { return base.RightToLeft; }
475 base.RightToLeft = value;
476 if (base.RightToLeft == RightToLeft.Yes)
477 StringFormat.Alignment = StringAlignment.Far;
479 StringFormat.Alignment = StringAlignment.Near;
484 // Only affects the Vertical ScrollBar
485 [DefaultValue (false)]
487 public bool ScrollAlwaysVisible {
488 get { return scroll_always_visible; }
490 if (scroll_always_visible == value)
493 scroll_always_visible = value;
500 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
501 public override int SelectedIndex {
503 if (selected_indices == null)
506 return selected_indices.Count > 0 ? selected_indices [0] : -1;
509 if (value < -1 || value >= Items.Count)
510 throw new ArgumentOutOfRangeException ("Index of out range");
512 if (SelectionMode == SelectionMode.None)
513 throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
516 selected_indices.Clear ();
518 selected_indices.Add (value);
523 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
524 public SelectedIndexCollection SelectedIndices {
525 get { return selected_indices; }
530 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
531 public object SelectedItem {
533 if (SelectedItems.Count > 0)
534 return SelectedItems[0];
539 if (value != null && !Items.Contains (value))
540 return; // FIXME: this is probably an exception
542 SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
547 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
548 public SelectedObjectCollection SelectedItems {
549 get {return selected_items;}
552 [DefaultValue (SelectionMode.One)]
553 public virtual SelectionMode SelectionMode {
554 get { return selection_mode; }
556 if (!Enum.IsDefined (typeof (SelectionMode), value))
557 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
559 if (selection_mode == value)
562 selection_mode = value;
564 switch (selection_mode) {
565 case SelectionMode.None:
566 SelectedIndices.Clear ();
569 case SelectionMode.One:
570 // FIXME: Probably this can be improved
571 ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
572 for (int i = 1; i < old_selection.Count; i++)
573 SelectedIndices.Remove ((int)old_selection [i]);
582 [DefaultValue (false)]
584 get { return sorted; }
597 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
598 [EditorBrowsable (EditorBrowsableState.Advanced)]
599 public override string Text {
601 if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
602 return GetItemText (SelectedItem);
610 if (SelectionMode == SelectionMode.None)
615 index = FindStringExact (value);
620 SelectedIndex = index;
625 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
626 public int TopIndex {
627 get { return top_index; }
629 if (value == top_index)
632 if (value < 0 || value >= Items.Count)
635 int page_size = (items_area.Height / ItemHeight);
637 if (Items.Count < page_size)
639 else if (!multicolumn)
640 top_index = Math.Min (value, Items.Count - page_size);
651 [DefaultValue (false)]
652 public bool UseCustomTabOffsets {
653 get { return use_custom_tab_offsets; }
655 if (use_custom_tab_offsets != value) {
656 use_custom_tab_offsets = value;
657 CalculateTabStops ();
662 [DefaultValue (true)]
663 public bool UseTabStops {
664 get { return use_tabstops; }
666 if (use_tabstops == value)
669 use_tabstops = value;
670 CalculateTabStops ();
675 protected override bool AllowSelection {
677 return SelectionMode != SelectionMode.None;
682 #endregion Public Properties
684 #region Private Properties
686 private int ColumnWidthInternal {
687 get { return column_width_internal; }
688 set { column_width_internal = value; }
691 private int row_count = 1;
692 private int RowCount {
694 return MultiColumn ? row_count : Items.Count;
698 #endregion Private Properties
700 #region UIA Framework Properties
702 internal ScrollBar UIAHScrollBar {
703 get { return hscrollbar; }
706 internal ScrollBar UIAVScrollBar {
707 get { return vscrollbar; }
710 #endregion UIA Framework Properties
712 #region Public Methods
714 [Obsolete ("this method has been deprecated")]
716 protected virtual void AddItemsCore (object[] value)
718 Items.AddRange (value);
721 public void BeginUpdate ()
723 suspend_layout = true;
726 public void ClearSelected ()
728 selected_indices.Clear ();
731 protected virtual ObjectCollection CreateItemCollection ()
733 return new ObjectCollection (this);
736 public void EndUpdate ()
738 suspend_layout = false;
743 public int FindString (String s)
745 return FindString (s, -1);
748 public int FindString (string s, int startIndex)
750 if (Items.Count == 0)
751 return -1; // No exception throwing if empty
753 if (startIndex < -1 || startIndex >= Items.Count)
754 throw new ArgumentOutOfRangeException ("Index of out range");
756 startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
760 string text = GetItemText (Items [i]);
761 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
762 CompareOptions.IgnoreCase))
765 i = (i == Items.Count - 1) ? 0 : i + 1;
773 public int FindStringExact (string s)
775 return FindStringExact (s, -1);
778 public int FindStringExact (string s, int startIndex)
780 if (Items.Count == 0)
781 return -1; // No exception throwing if empty
783 if (startIndex < -1 || startIndex >= Items.Count)
784 throw new ArgumentOutOfRangeException ("Index of out range");
786 startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
790 if (String.Compare (GetItemText (Items[i]), s, true) == 0)
793 i = (i + 1 == Items.Count) ? 0 : i + 1;
801 public int GetItemHeight (int index)
803 if (index < 0 || index >= Items.Count)
804 throw new ArgumentOutOfRangeException ("Index of out range");
806 if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
808 object o = Items [index];
809 if (item_heights.Contains (o))
810 return (int) item_heights [o];
812 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
813 OnMeasureItem (args);
814 item_heights [o] = args.ItemHeight;
815 return args.ItemHeight;
821 public Rectangle GetItemRectangle (int index)
823 if (index < 0 || index >= Items.Count)
824 throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
826 Rectangle rect = new Rectangle ();
829 int col = index / RowCount;
830 rect.Y = ((index - top_index) % RowCount) * ItemHeight;
831 rect.X = col * ColumnWidthInternal;
832 rect.Height = ItemHeight;
833 rect.Width = ColumnWidthInternal;
836 rect.Height = GetItemHeight (index);
837 rect.Width = items_area.Width;
839 if (DrawMode == DrawMode.OwnerDrawVariable) {
841 if (index >= top_index) {
842 for (int i = top_index; i < index; i++) {
843 rect.Y += GetItemHeight (i);
846 for (int i = index; i < top_index; i++) {
847 rect.Y -= GetItemHeight (i);
851 rect.Y = ItemHeight * (index - top_index);
855 if (this is CheckedListBox)
862 [EditorBrowsable (EditorBrowsableState.Advanced)]
863 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
865 bounds.Height = requested_height;
867 return base.GetScaledBounds (bounds, factor, specified);
871 public bool GetSelected (int index)
873 if (index < 0 || index >= Items.Count)
874 throw new ArgumentOutOfRangeException ("Index of out range");
876 return SelectedIndices.Contains (index);
879 public int IndexFromPoint (Point p)
881 return IndexFromPoint (p.X, p.Y);
884 // Only returns visible points
885 public int IndexFromPoint (int x, int y)
888 if (Items.Count == 0) {
892 for (int i = top_index; i <= last_visible_index; i++) {
893 if (GetItemRectangle (i).Contains (x,y) == true)
900 protected override void OnChangeUICues (UICuesEventArgs e)
902 base.OnChangeUICues (e);
905 protected override void OnDataSourceChanged (EventArgs e)
907 base.OnDataSourceChanged (e);
910 if (DataSource == null || DataManager == null) {
913 SelectedIndex = DataManager.Position;
917 protected override void OnDisplayMemberChanged (EventArgs e)
919 base.OnDisplayMemberChanged (e);
921 if (DataManager == null || !IsHandleCreated)
928 protected virtual void OnDrawItem (DrawItemEventArgs e)
931 case DrawMode.OwnerDrawFixed:
932 case DrawMode.OwnerDrawVariable:
933 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
940 ThemeEngine.Current.DrawListBoxItem (this, e);
945 protected override void OnFontChanged (EventArgs e)
947 base.OnFontChanged (e);
950 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
952 if (explicit_item_height) {
955 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
956 item_height = (int) sz.Height;
958 UpdateListBoxBounds ();
963 protected override void OnHandleCreated (EventArgs e)
965 base.OnHandleCreated (e);
968 UpdateListBoxBounds ();
971 EnsureVisible (focused_item);
974 protected override void OnHandleDestroyed (EventArgs e)
976 base.OnHandleDestroyed (e);
979 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
981 if (draw_mode != DrawMode.OwnerDrawVariable)
984 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
989 protected override void OnParentChanged (EventArgs e)
991 base.OnParentChanged (e);
994 protected override void OnResize (EventArgs e)
997 if (canvas_size.IsEmpty || MultiColumn)
1003 protected override void OnSelectedIndexChanged (EventArgs e)
1005 base.OnSelectedIndexChanged (e);
1007 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
1012 protected override void OnSelectedValueChanged (EventArgs e)
1014 base.OnSelectedValueChanged (e);
1017 public override void Refresh ()
1019 if (draw_mode == DrawMode.OwnerDrawVariable)
1020 item_heights.Clear ();
1025 protected override void RefreshItem (int index)
1027 if (index < 0 || index >= Items.Count)
1028 throw new ArgumentOutOfRangeException ("Index of out range");
1030 if (draw_mode == DrawMode.OwnerDrawVariable)
1031 item_heights.Remove (Items [index]);
1035 protected override void RefreshItems ()
1037 for (int i = 0; i < Items.Count; i++) {
1042 public override void ResetBackColor ()
1044 base.ResetBackColor ();
1047 public override void ResetForeColor ()
1049 base.ResetForeColor ();
1052 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
1054 base.ScaleControl (factor, specified);
1058 private int SnapHeightToIntegral (int height)
1062 switch (border_style) {
1063 case BorderStyle.Fixed3D:
1064 border = ThemeEngine.Current.Border3DSize.Height;
1066 case BorderStyle.FixedSingle:
1067 border = ThemeEngine.Current.BorderSize.Height;
1069 case BorderStyle.None:
1075 height -= (2 * border);
1076 height -= height % ItemHeight;
1077 height += (2 * border);
1082 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
1084 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
1085 requested_height = height;
1087 if (IntegralHeight && IsHandleCreated)
1088 height = SnapHeightToIntegral (height);
1090 base.SetBoundsCore (x, y, width, height, specified);
1091 UpdateScrollBars ();
1092 last_visible_index = LastVisibleItem ();
1095 protected override void SetItemCore (int index, object value)
1097 if (index < 0 || index >= Items.Count)
1100 Items[index] = value;
1103 protected override void SetItemsCore (IList value)
1108 Items.AddItems (value);
1114 public void SetSelected (int index, bool value)
1116 if (index < 0 || index >= Items.Count)
1117 throw new ArgumentOutOfRangeException ("Index of out range");
1119 if (SelectionMode == SelectionMode.None)
1120 throw new InvalidOperationException ();
1123 SelectedIndices.Add (index);
1125 SelectedIndices.Remove (index);
1128 protected virtual void Sort ()
1134 // Sometimes we could need to Sort, and request a Refresh
1135 // in a different place, to not have the painting done twice
1137 void Sort (bool paint)
1139 if (Items.Count == 0)
1148 public override string ToString ()
1150 return base.ToString ();
1153 protected virtual void WmReflectCommand (ref Message m)
1157 protected override void WndProc (ref Message m)
1159 base.WndProc (ref m);
1162 #endregion Public Methods
1164 #region Private Methods
1166 private void CalculateTabStops ()
1170 if (use_custom_tab_offsets) {
1171 float[] f = new float[custom_tab_offsets.Count];
1172 custom_tab_offsets.CopyTo (f, 0);
1173 StringFormat.SetTabStops (0, f);
1177 StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
1179 StringFormat.SetTabStops (0, new float[0]);
1184 private Size canvas_size;
1186 private void LayoutListBox ()
1188 if (!IsHandleCreated || suspend_layout)
1192 LayoutMultiColumn ();
1194 LayoutSingleColumn ();
1196 last_visible_index = LastVisibleItem ();
1197 UpdateScrollBars ();
1200 private void LayoutSingleColumn ()
1205 case DrawMode.OwnerDrawVariable:
1207 width = HorizontalExtent;
1208 for (int i = 0; i < Items.Count; i++) {
1209 height += GetItemHeight (i);
1213 case DrawMode.OwnerDrawFixed:
1214 height = Items.Count * ItemHeight;
1215 width = HorizontalExtent;
1218 case DrawMode.Normal:
1220 height = Items.Count * ItemHeight;
1222 for (int i = 0; i < Items.Count; i++) {
1223 SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
1224 int t = (int)sz.Width;
1226 if (this is CheckedListBox)
1235 canvas_size = new Size (width, height);
1238 private void LayoutMultiColumn ()
1240 int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
1241 row_count = Math.Max (1, usable_height / ItemHeight);
1243 int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1244 Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1245 if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
1246 usable_height = ClientRectangle.Height - hscrollbar.Height;
1247 row_count = Math.Max (1, usable_height / ItemHeight);
1248 cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1249 sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1254 internal void Draw (Rectangle clip, Graphics dc)
1256 Theme theme = ThemeEngine.Current;
1258 if (hscrollbar.Visible && vscrollbar.Visible) {
1259 // Paint the dead space in the bottom right corner
1260 Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
1261 if (rect.IntersectsWith (clip))
1262 dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
1265 dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
1267 if (Items.Count == 0)
1270 for (int i = top_index; i <= last_visible_index; i++) {
1271 Rectangle rect = GetItemDisplayRectangle (i, top_index);
1273 if (!clip.IntersectsWith (rect))
1276 DrawItemState state = DrawItemState.None;
1278 if (SelectedIndices.Contains (i))
1279 state |= DrawItemState.Selected;
1281 if (has_focus && FocusedItem == i)
1282 state |= DrawItemState.Focus;
1284 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
1285 rect.X -= hscrollbar.Value;
1286 rect.Width += hscrollbar.Value;
1289 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, ForeColor, BackColor));
1293 // Converts a GetItemRectangle to a one that we can display
1294 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
1296 Rectangle item_rect;
1297 Rectangle first_item_rect = GetItemRectangle (first_displayble);
1298 item_rect = GetItemRectangle (index);
1299 item_rect.X -= first_item_rect.X;
1300 item_rect.Y -= first_item_rect.Y;
1302 // Subtract the checkboxes from the width
1303 if (this is CheckedListBox)
1304 item_rect.Width -= 14;
1310 private void HorizontalScrollEvent (object sender, EventArgs e)
1313 int top_item = top_index;
1314 int last_item = last_visible_index;
1316 top_index = RowCount * hscrollbar.Value;
1317 last_visible_index = LastVisibleItem ();
1319 if (top_item != top_index || last_item != last_visible_index)
1320 Invalidate (items_area);
1323 int old_offset = hbar_offset;
1324 hbar_offset = hscrollbar.Value;
1326 if (hbar_offset < 0)
1329 if (IsHandleCreated)
1330 XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
1334 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
1335 // has screen coordinates
1336 private int IndexAtClientPoint (int x, int y)
1338 if (Items.Count == 0)
1343 else if (x > ClientRectangle.Right)
1344 x = ClientRectangle.Right;
1348 else if (y > ClientRectangle.Bottom)
1349 y = ClientRectangle.Bottom;
1351 for (int i = top_index; i <= last_visible_index; i++)
1352 if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
1358 internal override bool IsInputCharInternal (char charCode)
1363 private int LastVisibleItem ()
1365 Rectangle item_rect;
1366 int top_y = items_area.Y + items_area.Height;
1369 if (top_index >= Items.Count)
1372 for (i = top_index; i < Items.Count; i++) {
1373 item_rect = GetItemDisplayRectangle (i, top_index);
1375 if (item_rect.X > items_area.Width)
1378 if (item_rect.Y + item_rect.Height > top_y)
1385 private void UpdateTopItem ()
1388 int col = top_index / RowCount;
1390 if (col > hscrollbar.Maximum)
1391 hscrollbar.Value = hscrollbar.Maximum;
1393 hscrollbar.Value = col;
1395 int val = vscrollbar.Value;
1396 if (top_index > vscrollbar.Maximum)
1397 vscrollbar.Value = vscrollbar.Maximum;
1399 vscrollbar.Value = top_index;
1400 Scroll (vscrollbar, vscrollbar.Value - top_index);
1401 if (IsHandleCreated)
1402 XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * (val - vscrollbar.Value), false);
1406 // Navigates to the indicated item and returns the new item
1407 private int NavigateItemVisually (ItemNavigation navigation)
1409 int page_size, columns, selected_index = -1;
1412 columns = items_area.Width / ColumnWidthInternal;
1413 page_size = columns * RowCount;
1414 if (page_size == 0) {
1415 page_size = RowCount;
1418 page_size = items_area.Height / ItemHeight;
1421 switch (navigation) {
1423 case ItemNavigation.PreviousColumn: {
1424 if (SelectedIndex - RowCount < 0) {
1428 if (SelectedIndex - RowCount < top_index) {
1429 top_index = SelectedIndex - RowCount;
1433 selected_index = SelectedIndex - RowCount;
1437 case ItemNavigation.NextColumn: {
1438 if (SelectedIndex + RowCount >= Items.Count) {
1442 if (SelectedIndex + RowCount > last_visible_index) {
1443 top_index = SelectedIndex;
1447 selected_index = SelectedIndex + RowCount;
1451 case ItemNavigation.First: {
1458 case ItemNavigation.Last: {
1460 int rows = items_area.Height / ItemHeight;
1463 selected_index = Items.Count - 1;
1466 if (Items.Count < rows) {
1468 selected_index = Items.Count - 1;
1471 top_index = Items.Count - rows;
1472 selected_index = Items.Count - 1;
1478 case ItemNavigation.Next: {
1479 if (FocusedItem == Items.Count - 1)
1483 selected_index = FocusedItem + 1;
1488 ArrayList heights = new ArrayList ();
1489 if (draw_mode == DrawMode.OwnerDrawVariable) {
1490 for (int i = top_index; i <= FocusedItem + 1; i++) {
1491 int h = GetItemHeight (i);
1496 bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
1499 if (bottom >= items_area.Height) {
1500 int overhang = bottom - items_area.Height;
1503 if (draw_mode == DrawMode.OwnerDrawVariable)
1504 while (overhang > 0)
1505 overhang -= (int) heights [offset];
1507 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
1508 top_index += offset;
1511 selected_index = FocusedItem + 1;
1515 case ItemNavigation.Previous: {
1516 if (FocusedItem > 0) {
1517 if (FocusedItem - 1 < top_index) {
1521 selected_index = FocusedItem - 1;
1526 case ItemNavigation.NextPage: {
1527 if (Items.Count < page_size) {
1528 NavigateItemVisually (ItemNavigation.Last);
1532 if (FocusedItem + page_size - 1 >= Items.Count) {
1533 top_index = Items.Count - page_size;
1535 selected_index = Items.Count - 1;
1538 if (FocusedItem + page_size - 1 > last_visible_index) {
1539 top_index = FocusedItem;
1543 selected_index = FocusedItem + page_size - 1;
1549 case ItemNavigation.PreviousPage: {
1551 int rows = items_area.Height / ItemHeight;
1552 if (FocusedItem - (rows - 1) <= 0) {
1558 if (SelectedIndex - (rows - 1) < top_index) {
1559 top_index = FocusedItem - (rows - 1);
1563 selected_index = FocusedItem - (rows - 1);
1572 return selected_index;
1576 private void OnGotFocus (object sender, EventArgs e)
1578 if (Items.Count == 0)
1581 if (FocusedItem == -1)
1584 InvalidateItem (FocusedItem);
1587 private void OnLostFocus (object sender, EventArgs e)
1589 if (FocusedItem != -1)
1590 InvalidateItem (FocusedItem);
1593 private bool KeySearch (Keys key)
1595 char c = (char) key;
1596 if (!Char.IsLetterOrDigit (c))
1599 int idx = FindString (c.ToString (), SelectedIndex);
1600 if (idx != ListBox.NoMatches)
1601 SelectedIndex = idx;
1606 internal void OnKeyDownLB (object sender, KeyEventArgs e)
1610 if (Items.Count == 0)
1613 if (KeySearch (e.KeyCode))
1616 switch (e.KeyCode) {
1618 case Keys.ControlKey:
1619 ctrl_pressed = true;
1623 shift_pressed = true;
1627 new_item = NavigateItemVisually (ItemNavigation.First);
1631 new_item = NavigateItemVisually (ItemNavigation.Last);
1635 new_item = NavigateItemVisually (ItemNavigation.Previous);
1639 new_item = NavigateItemVisually (ItemNavigation.Next);
1643 new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
1647 new_item = NavigateItemVisually (ItemNavigation.NextPage);
1651 if (multicolumn == true) {
1652 new_item = NavigateItemVisually (ItemNavigation.NextColumn);
1657 if (multicolumn == true) {
1658 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
1663 if (selection_mode == SelectionMode.MultiSimple) {
1664 SelectedItemFromNavigation (FocusedItem);
1673 if (new_item != -1) {
1674 FocusedItem = new_item;
1676 if (selection_mode != SelectionMode.MultiSimple)
1677 SelectedItemFromNavigation (new_item);
1681 private void OnKeyUpLB (object sender, KeyEventArgs e)
1683 switch (e.KeyCode) {
1684 case Keys.ControlKey:
1685 ctrl_pressed = false;
1688 shift_pressed = false;
1695 internal void InvalidateItem (int index)
1697 if (!IsHandleCreated)
1699 Rectangle bounds = GetItemDisplayRectangle (index, top_index);
1700 if (ClientRectangle.IntersectsWith (bounds))
1701 Invalidate (bounds);
1704 internal virtual void OnItemClick (int index)
1706 OnSelectedIndexChanged (EventArgs.Empty);
1707 OnSelectedValueChanged (EventArgs.Empty);
1711 int[] prev_selection;
1712 bool button_pressed = false;
1713 Point button_pressed_loc = new Point (-1, -1);
1715 private void SelectExtended (int index)
1719 ArrayList new_selection = new ArrayList ();
1720 int start = anchor < index ? anchor : index;
1721 int end = anchor > index ? anchor : index;
1722 for (int i = start; i <= end; i++)
1723 new_selection.Add (i);
1726 foreach (int i in prev_selection)
1727 if (!new_selection.Contains (i))
1728 new_selection.Add (i);
1730 // Need to make a copy since we can't enumerate and modify the collection
1732 ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
1733 foreach (int i in sel_indices)
1734 if (!new_selection.Contains (i))
1735 selected_indices.Remove (i);
1737 foreach (int i in new_selection)
1738 if (!sel_indices.Contains (i))
1739 selected_indices.AddCore (i);
1743 private void OnMouseDownLB (object sender, MouseEventArgs e)
1745 // Only do stuff with the left mouse button
1746 if ((e.Button & MouseButtons.Left) == 0)
1749 int index = IndexAtClientPoint (e.X, e.Y);
1753 switch (SelectionMode) {
1754 case SelectionMode.One:
1755 SelectedIndices.AddCore (index); // Unselects previous one
1758 case SelectionMode.MultiSimple:
1759 if (SelectedIndices.Contains (index))
1760 SelectedIndices.RemoveCore (index);
1762 SelectedIndices.AddCore (index);
1765 case SelectionMode.MultiExtended:
1766 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1767 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1769 if (shift_pressed) {
1770 SelectedIndices.ClearCore ();
1771 SelectExtended (index);
1778 prev_selection = new int [SelectedIndices.Count];
1779 SelectedIndices.CopyTo (prev_selection, 0);
1781 if (SelectedIndices.Contains (index))
1782 SelectedIndices.RemoveCore (index);
1784 SelectedIndices.AddCore (index);
1789 SelectedIndices.ClearCore ();
1790 SelectedIndices.AddCore (index);
1793 case SelectionMode.None:
1799 button_pressed = true;
1800 button_pressed_loc = new Point (e.X, e.Y);
1801 FocusedItem = index;
1804 private void OnMouseMoveLB (object sender, MouseEventArgs e)
1806 // Don't take into account MouseMove events generated with MouseDown
1807 if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y))
1810 int index = IndexAtClientPoint (e.X, e.Y);
1814 switch (SelectionMode) {
1815 case SelectionMode.One:
1816 SelectedIndices.AddCore (index); // Unselects previous one
1819 case SelectionMode.MultiSimple:
1822 case SelectionMode.MultiExtended:
1823 SelectExtended (index);
1826 case SelectionMode.None:
1832 FocusedItem = index;
1835 internal override void OnDragDropEnd (DragDropEffects effects)
1837 // In the case of a DnD operation (started on MouseDown)
1838 // there will be no MouseUp event, so we need to reset
1840 button_pressed = false;
1843 private void OnMouseUpLB (object sender, MouseEventArgs e)
1845 // Only do stuff with the left mouse button
1846 if ((e.Button & MouseButtons.Left) == 0)
1850 OnDoubleClick (EventArgs.Empty);
1852 OnMouseDoubleClick (e);
1855 else if (e.Clicks == 1) {
1856 OnClick (EventArgs.Empty);
1862 if (!button_pressed)
1865 int index = IndexAtClientPoint (e.X, e.Y);
1866 OnItemClick (index);
1867 button_pressed = ctrl_pressed = shift_pressed = false;
1870 private void Scroll (ScrollBar scrollbar, int delta)
1872 if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
1876 if (scrollbar == hscrollbar)
1877 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
1879 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
1881 int val = scrollbar.Value + delta;
1884 else if (val < scrollbar.Minimum)
1885 val = scrollbar.Minimum;
1886 scrollbar.Value = val;
1889 private void OnMouseWheelLB (object sender, MouseEventArgs me)
1891 if (Items.Count == 0)
1894 int lines = me.Delta / 120;
1897 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
1899 Scroll (vscrollbar, -lines);
1902 internal override void OnPaintInternal (PaintEventArgs pevent)
1907 Draw (pevent.ClipRectangle, pevent.Graphics);
1910 internal void RepositionScrollBars ()
1912 if (vscrollbar.is_visible) {
1913 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
1914 vscrollbar.Location = new Point (items_area.Width, 0);
1917 if (hscrollbar.is_visible) {
1918 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
1919 hscrollbar.Location = new Point (0, items_area.Height);
1923 // An item navigation operation (mouse or keyboard) has caused to select a new item
1924 internal void SelectedItemFromNavigation (int index)
1926 switch (SelectionMode) {
1927 case SelectionMode.None:
1928 // .Net doesn't select the item, only ensures that it's visible
1929 // and fires the selection related events
1930 EnsureVisible (index);
1931 OnSelectedIndexChanged (EventArgs.Empty);
1932 OnSelectedValueChanged (EventArgs.Empty);
1934 case SelectionMode.One: {
1935 SelectedIndex = index;
1938 case SelectionMode.MultiSimple: {
1939 if (SelectedIndex == -1) {
1940 SelectedIndex = index;
1943 if (SelectedIndices.Contains (index))
1944 SelectedIndices.Remove (index);
1946 SelectedIndices.AddCore (index);
1948 OnSelectedIndexChanged (EventArgs.Empty);
1949 OnSelectedValueChanged (EventArgs.Empty);
1955 case SelectionMode.MultiExtended: {
1956 if (SelectedIndex == -1) {
1957 SelectedIndex = index;
1960 if (ctrl_pressed == false && shift_pressed == false) {
1961 SelectedIndices.Clear ();
1964 if (shift_pressed == true) {
1965 ShiftSelection (index);
1966 } else { // ctrl_pressed or single item
1967 SelectedIndices.AddCore (index);
1971 OnSelectedIndexChanged (EventArgs.Empty);
1972 OnSelectedValueChanged (EventArgs.Empty);
1982 private void ShiftSelection (int index)
1984 int shorter_item = -1, dist = Items.Count + 1, cur_dist;
1986 foreach (int idx in selected_indices) {
1988 cur_dist = idx - index;
1990 cur_dist = index - idx;
1993 if (cur_dist < dist) {
1999 if (shorter_item != -1) {
2002 if (shorter_item > index) {
2006 start = shorter_item;
2010 selected_indices.Clear ();
2011 for (int idx = start; idx <= end; idx++) {
2012 selected_indices.AddCore (idx);
2017 internal int FocusedItem {
2018 get { return focused_item; }
2020 if (focused_item == value)
2023 int prev = focused_item;
2025 focused_item = value;
2027 if (has_focus == false)
2031 InvalidateItem (prev);
2034 InvalidateItem (value);
2038 StringFormat string_format;
2039 internal StringFormat StringFormat {
2041 if (string_format == null) {
2042 string_format = new StringFormat ();
2043 if (RightToLeft == RightToLeft.Yes)
2044 string_format.Alignment = StringAlignment.Far;
2046 string_format.Alignment = StringAlignment.Near;
2047 CalculateTabStops ();
2049 return string_format;
2053 internal virtual void CollectionChanged ()
2058 if (Items.Count == 0) {
2059 selected_indices.List.Clear ();
2063 if (Items.Count <= focused_item)
2064 focused_item = Items.Count - 1;
2066 if (!IsHandleCreated || suspend_layout)
2074 void EnsureVisible (int index)
2076 if (!IsHandleCreated || index == -1)
2079 if (index < top_index) {
2083 } else if (!multicolumn) {
2084 int rows = items_area.Height / ItemHeight;
2085 if (index >= (top_index + rows))
2086 top_index = index - rows + 1;
2090 int rows = Math.Max (1, items_area.Height / ItemHeight);
2091 int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
2093 if (index >= (top_index + (rows * cols))) {
2094 int incolumn = index / rows;
2095 top_index = (incolumn - (cols - 1)) * rows;
2103 private void UpdateListBoxBounds ()
2105 if (IsHandleCreated)
2106 SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
2109 private void UpdateScrollBars ()
2111 items_area = ClientRectangle;
2112 if (UpdateHorizontalScrollBar ()) {
2113 items_area.Height -= hscrollbar.Height;
2114 if (UpdateVerticalScrollBar ()) {
2115 items_area.Width -= vscrollbar.Width;
2116 UpdateHorizontalScrollBar ();
2118 } else if (UpdateVerticalScrollBar ()) {
2119 items_area.Width -= vscrollbar.Width;
2120 if (UpdateHorizontalScrollBar ()) {
2121 items_area.Height -= hscrollbar.Height;
2122 UpdateVerticalScrollBar ();
2126 RepositionScrollBars ();
2129 /* Determines if the horizontal scrollbar has to be displyed */
2130 private bool UpdateHorizontalScrollBar ()
2133 bool enabled = true;
2136 if (canvas_size.Width > items_area.Width) {
2138 hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
2139 } else if (ScrollAlwaysVisible == true) {
2142 hscrollbar.Maximum = 0;
2144 } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
2146 hscrollbar.Maximum = canvas_size.Width;
2147 hscrollbar.LargeChange = Math.Max (0, items_area.Width);
2148 } else if (horizontal_scrollbar) {
2151 hscrollbar.Maximum = 0;
2154 hbar_offset = hscrollbar.Value;
2155 hscrollbar.Enabled = enabled;
2156 hscrollbar.Visible = show;
2161 /* Determines if the vertical scrollbar has to be displyed */
2162 private bool UpdateVerticalScrollBar ()
2164 if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) {
2165 vscrollbar.Visible = false;
2167 } else if (Items.Count == 0) {
2168 vscrollbar.Visible = true;
2169 vscrollbar.Enabled = false;
2170 vscrollbar.Maximum = 0;
2175 bool enabled = true;
2176 if (canvas_size.Height > items_area.Height) {
2178 vscrollbar.Maximum = Items.Count - 1;
2179 vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
2180 } else if (ScrollAlwaysVisible) {
2183 vscrollbar.Maximum = 0;
2186 vscrollbar.Enabled = enabled;
2187 vscrollbar.Visible = show;
2193 private void VerticalScrollEvent (object sender, EventArgs e)
2195 int top_item = top_index;
2197 top_index = /*row_count + */ vscrollbar.Value;
2198 last_visible_index = LastVisibleItem ();
2200 int delta = (top_item - top_index) * ItemHeight;
2201 if (DrawMode == DrawMode.OwnerDrawVariable) {
2204 if (top_index < top_item)
2205 for (int i = top_index; i < top_item; i++)
2206 delta += GetItemHeight (i);
2208 for (int i = top_item; i < top_index; i++)
2209 delta -= GetItemHeight (i);
2212 if (IsHandleCreated)
2213 XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
2216 #endregion Private Methods
2219 public class IntegerCollection : IList, ICollection, IEnumerable
2221 private ListBox owner;
2222 private List<int> list;
2224 #region Public Constructor
2225 public IntegerCollection (ListBox owner)
2228 list = new List<int> ();
2232 #region Public Properties
2235 get { return list.Count; }
2238 public int this [int index] {
2239 get { return list[index]; }
2240 set { list[index] = value; owner.CalculateTabStops (); }
2244 #region Public Methods
2245 public int Add (int item)
2247 // This collection does not allow duplicates
2248 if (!list.Contains (item)) {
2251 owner.CalculateTabStops ();
2254 return list.IndexOf (item);
2257 public void AddRange (int[] items)
2262 public void AddRange (IntegerCollection value)
2267 void AddItems (IList items)
2270 throw new ArgumentNullException ("items");
2272 foreach (int i in items)
2273 if (!list.Contains (i))
2279 public void Clear ()
2282 owner.CalculateTabStops ();
2285 public bool Contains (int item)
2287 return list.Contains (item);
2290 public void CopyTo (Array destination, int index)
2292 for (int i = 0; i < list.Count; i++)
2293 destination.SetValue (list[i], index++);
2296 public int IndexOf (int item)
2298 return list.IndexOf (item);
2301 public void Remove (int item)
2305 owner.CalculateTabStops ();
2308 public void RemoveAt (int index)
2311 throw new IndexOutOfRangeException ();
2313 list.RemoveAt (index);
2315 owner.CalculateTabStops ();
2319 #region IEnumerable Members
2320 IEnumerator IEnumerable.GetEnumerator ()
2322 return list.GetEnumerator ();
2326 #region IList Members
2327 int IList.Add (object item)
2329 int? intValue = item as int?;
2330 if (!intValue.HasValue)
2331 throw new ArgumentException ("item");
2332 return Add (intValue.Value);
2340 bool IList.Contains (object item)
2342 int? intValue = item as int?;
2343 if (!intValue.HasValue)
2345 return Contains (intValue.Value);
2348 int IList.IndexOf (object item)
2350 int? intValue = item as int?;
2351 if (!intValue.HasValue)
2353 return IndexOf (intValue.Value);
2356 void IList.Insert (int index, object value)
2358 throw new NotSupportedException (string.Format (
2359 CultureInfo.InvariantCulture, "No items "
2360 + "can be inserted into {0}, since it is"
2361 + " a sorted collection.", this.GetType ()));
2364 bool IList.IsFixedSize
2366 get { return false; }
2369 bool IList.IsReadOnly
2371 get { return false; }
2374 void IList.Remove (object value)
2376 int? intValue = value as int?;
2377 if (!intValue.HasValue)
2378 throw new ArgumentException ("value");
2380 Remove (intValue.Value);
2383 void IList.RemoveAt (int index)
2388 object IList.this[int index] {
2389 get { return this[index]; }
2390 set { this[index] = (int)value; }
2394 #region ICollection Members
2395 bool ICollection.IsSynchronized {
2396 get { return true; }
2399 object ICollection.SyncRoot {
2400 get { return this; }
2406 [ListBindable (false)]
2407 public class ObjectCollection : IList, ICollection, IEnumerable
2409 internal class ListObjectComparer : IComparer
2411 public int Compare (object a, object b)
2413 string str1 = a.ToString ();
2414 string str2 = b.ToString ();
2415 return str1.CompareTo (str2);
2419 private ListBox owner;
2420 internal ArrayList object_items = new ArrayList ();
2422 #region UIA Framework Events
2425 // We are using Reflection to add/remove internal events.
2426 // Class ListProvider uses the events.
2428 //Event used to generate UIA StructureChangedEvent
2429 static object UIACollectionChangedEvent = new object ();
2431 internal event CollectionChangeEventHandler UIACollectionChanged {
2432 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
2433 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
2436 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
2438 CollectionChangeEventHandler eh
2439 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
2445 #endregion UIA Framework Events
2447 public ObjectCollection (ListBox owner)
2452 public ObjectCollection (ListBox owner, object[] value)
2458 public ObjectCollection (ListBox owner, ObjectCollection value)
2464 #region Public Properties
2466 get { return object_items.Count; }
2469 public bool IsReadOnly {
2470 get { return false; }
2474 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2475 public virtual object this [int index] {
2477 if (index < 0 || index >= Count)
2478 throw new ArgumentOutOfRangeException ("Index of out range");
2480 return object_items[index];
2483 if (index < 0 || index >= Count)
2484 throw new ArgumentOutOfRangeException ("Index of out range");
2486 throw new ArgumentNullException ("value");
2489 //UIA Framework event: Item Removed
2490 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
2493 object_items[index] = value;
2496 //UIA Framework event: Item Added
2497 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
2500 owner.CollectionChanged ();
2504 bool ICollection.IsSynchronized {
2505 get { return false; }
2508 object ICollection.SyncRoot {
2509 get { return this; }
2512 bool IList.IsFixedSize {
2513 get { return false; }
2516 #endregion Public Properties
2518 #region Public Methods
2519 public int Add (object item)
2523 idx = AddItem (item);
2524 owner.CollectionChanged ();
2528 public void AddRange (object[] items)
2533 public void AddRange (ObjectCollection value)
2538 internal void AddItems (IList items)
2541 throw new ArgumentNullException ("items");
2544 foreach (object mi in items)
2546 throw new ArgumentNullException ("item");
2549 foreach (object mi in items)
2552 owner.CollectionChanged ();
2555 public virtual void Clear ()
2557 owner.selected_indices.ClearCore ();
2558 object_items.Clear ();
2559 owner.CollectionChanged ();
2562 //UIA Framework event: Items list cleared
2563 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
2567 public bool Contains (object value)
2570 throw new ArgumentNullException ("value");
2572 return object_items.Contains (value);
2576 public void CopyTo (object[] destination, int arrayIndex)
2578 object [] dest = destination;
2580 public void CopyTo (object [] dest, int arrayIndex)
2583 object_items.CopyTo (dest, arrayIndex);
2587 void ICollection.CopyTo (Array destination, int index)
2589 Array dest = destination;
2591 void ICollection.CopyTo (Array dest, int index)
2594 object_items.CopyTo (dest, index);
2597 public IEnumerator GetEnumerator ()
2599 return object_items.GetEnumerator ();
2602 int IList.Add (object item)
2607 public int IndexOf (object value)
2610 throw new ArgumentNullException ("value");
2612 return object_items.IndexOf (value);
2615 public void Insert (int index, object item)
2617 if (index < 0 || index > Count)
2618 throw new ArgumentOutOfRangeException ("Index of out range");
2620 throw new ArgumentNullException ("item");
2622 owner.BeginUpdate ();
2623 object_items.Insert (index, item);
2624 owner.CollectionChanged ();
2628 //UIA Framework event: Item Added
2629 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
2633 public void Remove (object value)
2638 int index = IndexOf (value);
2643 public void RemoveAt (int index)
2645 if (index < 0 || index >= Count)
2646 throw new ArgumentOutOfRangeException ("Index of out range");
2648 owner.selected_indices.Remove (index);
2649 object_items.RemoveAt (index);
2650 owner.CollectionChanged ();
2653 //UIA Framework event: Item Removed
2654 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
2657 #endregion Public Methods
2659 #region Private Methods
2660 internal int AddItem (object item)
2663 throw new ArgumentNullException ("item");
2665 int cnt = object_items.Count;
2666 object_items.Add (item);
2669 //UIA Framework event: Item Added
2670 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, cnt));
2676 internal void Sort ()
2678 object_items.Sort (new ListObjectComparer ());
2681 #endregion Private Methods
2684 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2686 private ListBox owner;
2687 ArrayList selection;
2688 bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
2690 public SelectedIndexCollection (ListBox owner)
2693 selection = new ArrayList ();
2696 #region Public Properties
2699 get { return selection.Count; }
2702 public bool IsReadOnly {
2703 get { return true; }
2706 public int this [int index] {
2708 if (index < 0 || index >= Count)
2709 throw new ArgumentOutOfRangeException ("Index of out range");
2712 return (int)selection [index];
2716 bool ICollection.IsSynchronized {
2717 get { return true; }
2720 bool IList.IsFixedSize{
2721 get { return true; }
2724 object ICollection.SyncRoot {
2725 get { return selection; }
2728 #endregion Public Properties
2730 #region Public Methods
2736 void Add (int index)
2738 if (AddCore (index)) {
2739 owner.OnSelectedIndexChanged (EventArgs.Empty);
2740 owner.OnSelectedValueChanged (EventArgs.Empty);
2744 // Need to separate selection logic from events,
2745 // since selection changes using keys/mouse handle them their own way
2746 internal bool AddCore (int index)
2748 if (selection.Contains (index))
2751 if (index == -1) // Weird MS behaviour
2753 if (index < -1 || index >= owner.Items.Count)
2754 throw new ArgumentOutOfRangeException ("index");
2755 if (owner.selection_mode == SelectionMode.None)
2756 throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
2758 if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
2759 RemoveCore ((int)selection [0]);
2761 selection.Add (index);
2762 sorting_needed = true;
2763 owner.EnsureVisible (index);
2764 owner.FocusedItem = index;
2765 owner.InvalidateItem (index);
2778 owner.OnSelectedIndexChanged (EventArgs.Empty);
2779 owner.OnSelectedValueChanged (EventArgs.Empty);
2783 internal bool ClearCore ()
2785 if (selection.Count == 0)
2788 foreach (int index in selection)
2789 owner.InvalidateItem (index);
2795 public bool Contains (int selectedIndex)
2797 foreach (int index in selection)
2798 if (index == selectedIndex)
2804 public void CopyTo (Array destination, int index)
2806 Array dest = destination;
2808 public void CopyTo (Array dest, int index)
2812 selection.CopyTo (dest, index);
2815 public IEnumerator GetEnumerator ()
2818 return selection.GetEnumerator ();
2821 // FIXME: Probably we can avoid sorting when calling
2822 // IndexOf (imagine a scenario where multiple removal of items
2829 void Remove (int index)
2831 // Separate logic from events here too
2832 if (RemoveCore (index)) {
2833 owner.OnSelectedIndexChanged (EventArgs.Empty);
2834 owner.OnSelectedValueChanged (EventArgs.Empty);
2838 internal bool RemoveCore (int index)
2840 int idx = IndexOf (index);
2844 selection.RemoveAt (idx);
2845 owner.InvalidateItem (index);
2850 int IList.Add (object value)
2852 throw new NotSupportedException ();
2857 throw new NotSupportedException ();
2860 bool IList.Contains (object selectedIndex)
2862 return Contains ((int)selectedIndex);
2865 int IList.IndexOf (object selectedIndex)
2867 return IndexOf ((int) selectedIndex);
2870 void IList.Insert (int index, object value)
2872 throw new NotSupportedException ();
2875 void IList.Remove (object value)
2877 throw new NotSupportedException ();
2880 void IList.RemoveAt (int index)
2882 throw new NotSupportedException ();
2885 object IList.this[int index]{
2886 get { return this [index]; }
2887 set {throw new NotImplementedException (); }
2890 public int IndexOf (int selectedIndex)
2894 for (int i = 0; i < selection.Count; i++)
2895 if ((int)selection [i] == selectedIndex)
2900 #endregion Public Methods
2901 internal ArrayList List {
2910 if (sorting_needed) {
2911 sorting_needed = false;
2917 public class SelectedObjectCollection : IList, ICollection, IEnumerable
2919 private ListBox owner;
2921 public SelectedObjectCollection (ListBox owner)
2926 #region Public Properties
2928 get { return owner.selected_indices.Count; }
2931 public bool IsReadOnly {
2932 get { return true; }
2936 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2937 public object this [int index] {
2939 if (index < 0 || index >= Count)
2940 throw new ArgumentOutOfRangeException ("Index of out range");
2942 return owner.items [owner.selected_indices [index]];
2944 set {throw new NotSupportedException ();}
2947 bool ICollection.IsSynchronized {
2948 get { return true; }
2951 object ICollection.SyncRoot {
2952 get { return this; }
2955 bool IList.IsFixedSize {
2956 get { return true; }
2959 #endregion Public Properties
2961 #region Public Methods
2963 public void Add (object value)
2965 if (owner.selection_mode == SelectionMode.None)
2966 throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
2968 int idx = owner.items.IndexOf (value);
2972 owner.selected_indices.Add (idx);
2975 public void Clear ()
2977 owner.selected_indices.Clear ();
2981 public bool Contains (object selectedObject)
2983 int idx = owner.items.IndexOf (selectedObject);
2984 return idx == -1 ? false : owner.selected_indices.Contains (idx);
2988 public void CopyTo (Array destination, int index)
2990 Array dest = destination;
2992 public void CopyTo (Array dest, int index)
2995 for (int i = 0; i < Count; i++)
2996 dest.SetValue (this [i], index++);
3000 public void Remove (object value)
3005 int idx = owner.items.IndexOf (value);
3009 owner.selected_indices.Remove (idx);
3013 int IList.Add (object value)
3015 throw new NotSupportedException ();
3020 throw new NotSupportedException ();
3023 void IList.Insert (int index, object value)
3025 throw new NotSupportedException ();
3028 void IList.Remove (object value)
3030 throw new NotSupportedException ();
3033 void IList.RemoveAt (int index)
3035 throw new NotSupportedException ();
3038 public int IndexOf (object selectedObject)
3040 int idx = owner.items.IndexOf (selectedObject);
3041 return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
3044 public IEnumerator GetEnumerator ()
3046 //FIXME: write an enumerator that uses selection.GetEnumerator
3047 // so that invalidation is write on selection changes
3048 object [] items = new object [Count];
3049 for (int i = 0; i < Count; i++) {
3050 items [i] = owner.items [owner.selected_indices [i]];
3053 return items.GetEnumerator ();
3056 #endregion Public Methods